@alook/daemon 0.1.12 → 0.1.13
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/README.md +36 -0
- package/dist/cli/index.js +677 -96
- package/dist/index.js +928 -521
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -98,7 +98,7 @@ var init_nanoid = () => {};
|
|
|
98
98
|
// src/cli/index.ts
|
|
99
99
|
import { Command, CommanderError } from "commander";
|
|
100
100
|
import { realpathSync as realpathSync2 } from "node:fs";
|
|
101
|
-
import { randomUUID as
|
|
101
|
+
import { randomUUID as randomUUID7 } from "node:crypto";
|
|
102
102
|
import { pathToFileURL as pathToFileURL2 } from "node:url";
|
|
103
103
|
|
|
104
104
|
// ../../node_modules/.pnpm/zod@4.4.3/node_modules/zod/v4/classic/external.js
|
|
@@ -16742,6 +16742,7 @@ var OwnerDiagnosticReportSchema = exports_external.discriminatedUnion("status",
|
|
|
16742
16742
|
|
|
16743
16743
|
// ../shared/src/community-cli-contract.ts
|
|
16744
16744
|
var DM_SERVER = ".dm";
|
|
16745
|
+
var CONTROL_HEARTBEAT_CAPABILITY = "control-heartbeat-v1";
|
|
16745
16746
|
function parseRef(ref) {
|
|
16746
16747
|
if (!ref.startsWith("/"))
|
|
16747
16748
|
throw new Error(`ref must start with "/": ${ref}`);
|
|
@@ -16817,6 +16818,10 @@ function parseSeq(s) {
|
|
|
16817
16818
|
return n;
|
|
16818
16819
|
}
|
|
16819
16820
|
var HostCommandSchema = exports_external.discriminatedUnion("type", [
|
|
16821
|
+
exports_external.strictObject({
|
|
16822
|
+
type: exports_external.literal("machine:heartbeat"),
|
|
16823
|
+
nonce: exports_external.string().min(1).max(128)
|
|
16824
|
+
}),
|
|
16820
16825
|
exports_external.object({
|
|
16821
16826
|
type: exports_external.literal("agent:wake"),
|
|
16822
16827
|
agentId: exports_external.string().min(1),
|
|
@@ -17456,6 +17461,7 @@ var CommunityMachineSummarySchema = exports_external.object({
|
|
|
17456
17461
|
var HostReadyMessageSchema = exports_external.object({
|
|
17457
17462
|
type: exports_external.literal("ready"),
|
|
17458
17463
|
runtimeReport: CommunityMachineRuntimeListSchema,
|
|
17464
|
+
capabilities: exports_external.array(exports_external.string().min(1).max(64)).max(16).optional().default([]),
|
|
17459
17465
|
runningAgents: exports_external.array(exports_external.string()).default([]),
|
|
17460
17466
|
hostname: exports_external.string().optional(),
|
|
17461
17467
|
platform: exports_external.string().optional(),
|
|
@@ -17507,6 +17513,14 @@ var AgentWakeAckMessageSchema = exports_external.object({
|
|
|
17507
17513
|
status: exports_external.enum(["ok", "error"]),
|
|
17508
17514
|
error: exports_external.object({ code: exports_external.string().optional(), message: exports_external.string().optional() }).optional()
|
|
17509
17515
|
});
|
|
17516
|
+
var MachineHeartbeatAckMessageSchema = exports_external.strictObject({
|
|
17517
|
+
type: exports_external.literal("machine_heartbeat_ack"),
|
|
17518
|
+
nonce: exports_external.string().min(1).max(128)
|
|
17519
|
+
});
|
|
17520
|
+
var DiagnosticCommandAckMessageSchema = exports_external.strictObject({
|
|
17521
|
+
type: exports_external.literal("diagnostics_ack"),
|
|
17522
|
+
reportId: DiagnosticReportIdSchema
|
|
17523
|
+
});
|
|
17510
17524
|
var CommunityPairTokenResponseSchema = exports_external.object({
|
|
17511
17525
|
tokenId: exports_external.string(),
|
|
17512
17526
|
expiresAt: exports_external.string()
|
|
@@ -17515,6 +17529,7 @@ var CommunityDaemonActivateRequestSchema = exports_external.object({
|
|
|
17515
17529
|
hostname: exports_external.string(),
|
|
17516
17530
|
platform: exports_external.string(),
|
|
17517
17531
|
arch: exports_external.string(),
|
|
17532
|
+
expectedMachineId: exports_external.string().min(1).optional(),
|
|
17518
17533
|
osRelease: exports_external.string().optional(),
|
|
17519
17534
|
daemonVersion: exports_external.string().optional(),
|
|
17520
17535
|
runtimeReport: CommunityMachineRuntimeListSchema.optional()
|
|
@@ -17522,7 +17537,12 @@ var CommunityDaemonActivateRequestSchema = exports_external.object({
|
|
|
17522
17537
|
var CommunityDaemonActivateResponseSchema = exports_external.object({
|
|
17523
17538
|
credential: exports_external.string(),
|
|
17524
17539
|
machineId: exports_external.string(),
|
|
17525
|
-
expiresAt: exports_external.string().nullable()
|
|
17540
|
+
expiresAt: exports_external.string().nullable(),
|
|
17541
|
+
sessionOutcome: exports_external.literal("committed")
|
|
17542
|
+
});
|
|
17543
|
+
var CommunityDaemonActivateErrorResponseSchema = exports_external.object({
|
|
17544
|
+
error: exports_external.string(),
|
|
17545
|
+
sessionOutcome: exports_external.enum(["not_committed", "unknown"])
|
|
17526
17546
|
});
|
|
17527
17547
|
var CommunityDaemonEnrollAgentRequestSchema = exports_external.object({
|
|
17528
17548
|
agentId: exports_external.string().min(1).max(128)
|
|
@@ -18162,26 +18182,6 @@ var channelJoinBaselineGuard = sql`${communityMessage.createdAt} > COALESCE(${co
|
|
|
18162
18182
|
|
|
18163
18183
|
// ../shared/src/db/queries/community/mention.ts
|
|
18164
18184
|
var MENTION_INSERT_MAX_ROWS = maxRowsPerInsert(5);
|
|
18165
|
-
// ../shared/src/community/bot-activity-presets.ts
|
|
18166
|
-
var BOT_ACTIVITY_PRESETS = {
|
|
18167
|
-
idle: { emoji: "\uD83D\uDCA4", text: "Idle" },
|
|
18168
|
-
starting: { emoji: "\uD83C\uDF00", text: "Waking up" },
|
|
18169
|
-
stopping: { emoji: "\uD83C\uDF19", text: "Wrapping up" }
|
|
18170
|
-
};
|
|
18171
|
-
var RUNNING_PRESETS = [
|
|
18172
|
-
{ emoji: "⚡", text: "Working on it" },
|
|
18173
|
-
{ emoji: "\uD83D\uDEE0️", text: "Cooking" },
|
|
18174
|
-
{ emoji: "\uD83E\uDDE0", text: "Thinking hard" },
|
|
18175
|
-
{ emoji: "\uD83D\uDD27", text: "Tinkering" },
|
|
18176
|
-
{ emoji: "\uD83D\uDE80", text: "On it" },
|
|
18177
|
-
{ emoji: "\uD83D\uDD25", text: "In the zone" }
|
|
18178
|
-
];
|
|
18179
|
-
var BOT_ACTIVITY_STATUS_PAIRS = [
|
|
18180
|
-
BOT_ACTIVITY_PRESETS.idle,
|
|
18181
|
-
BOT_ACTIVITY_PRESETS.starting,
|
|
18182
|
-
BOT_ACTIVITY_PRESETS.stopping,
|
|
18183
|
-
...RUNNING_PRESETS
|
|
18184
|
-
];
|
|
18185
18185
|
// ../shared/src/community-ws-events.ts
|
|
18186
18186
|
var string4 = exports_external.string();
|
|
18187
18187
|
var nullableString = string4.nullable();
|
|
@@ -18638,6 +18638,68 @@ var WS_EVENTS = {
|
|
|
18638
18638
|
BOT_AUDIT_EVENT: "community:bot.audit_event"
|
|
18639
18639
|
};
|
|
18640
18640
|
var COMMUNITY_EVENT_TYPES = new Set(Object.values(WS_EVENTS));
|
|
18641
|
+
var COMMUNITY_USER_TARGET_MAX_BYTES = 128;
|
|
18642
|
+
function utf8ByteLength(value) {
|
|
18643
|
+
return new TextEncoder().encode(value).byteLength;
|
|
18644
|
+
}
|
|
18645
|
+
function isWellFormedUnicode(value) {
|
|
18646
|
+
for (let index2 = 0;index2 < value.length; index2 += 1) {
|
|
18647
|
+
const codeUnit = value.charCodeAt(index2);
|
|
18648
|
+
if (codeUnit >= 55296 && codeUnit <= 56319) {
|
|
18649
|
+
const next = value.charCodeAt(index2 + 1);
|
|
18650
|
+
if (!Number.isInteger(next) || next < 56320 || next > 57343)
|
|
18651
|
+
return false;
|
|
18652
|
+
index2 += 1;
|
|
18653
|
+
} else if (codeUnit >= 56320 && codeUnit <= 57343) {
|
|
18654
|
+
return false;
|
|
18655
|
+
}
|
|
18656
|
+
}
|
|
18657
|
+
return true;
|
|
18658
|
+
}
|
|
18659
|
+
function isValidCommunityUserTarget(value) {
|
|
18660
|
+
return typeof value === "string" && value.length > 0 && isWellFormedUnicode(value) && utf8ByteLength(value) <= COMMUNITY_USER_TARGET_MAX_BYTES;
|
|
18661
|
+
}
|
|
18662
|
+
|
|
18663
|
+
// ../shared/src/community-message-delivery.ts
|
|
18664
|
+
var MESSAGE_DELIVERY_MAX_USERS = 1000;
|
|
18665
|
+
var target = exports_external.string().refine(isValidCommunityUserTarget);
|
|
18666
|
+
var targetList = exports_external.array(target).max(MESSAGE_DELIVERY_MAX_USERS);
|
|
18667
|
+
var memberAddedSchema = exports_external.strictObject({
|
|
18668
|
+
userId: target,
|
|
18669
|
+
serverId: exports_external.string().min(1),
|
|
18670
|
+
channelId: exports_external.string().min(1)
|
|
18671
|
+
});
|
|
18672
|
+
var batchShape = exports_external.strictObject({
|
|
18673
|
+
messageId: exports_external.string().min(1),
|
|
18674
|
+
messageEvent: exports_external.unknown(),
|
|
18675
|
+
contentUserIds: targetList,
|
|
18676
|
+
unreadPlainUserIds: targetList,
|
|
18677
|
+
unreadMentionUserIds: targetList,
|
|
18678
|
+
mentionUserIds: targetList,
|
|
18679
|
+
memberAdded: memberAddedSchema.optional(),
|
|
18680
|
+
parentProjection: exports_external.unknown().optional(),
|
|
18681
|
+
parentProjectionUserIds: targetList.optional()
|
|
18682
|
+
});
|
|
18683
|
+
// ../shared/src/community/bot-activity-presets.ts
|
|
18684
|
+
var BOT_ACTIVITY_PRESETS = {
|
|
18685
|
+
idle: { emoji: "\uD83D\uDCA4", text: "Idle" },
|
|
18686
|
+
starting: { emoji: "\uD83C\uDF00", text: "Waking up" },
|
|
18687
|
+
stopping: { emoji: "\uD83C\uDF19", text: "Wrapping up" }
|
|
18688
|
+
};
|
|
18689
|
+
var RUNNING_PRESETS = [
|
|
18690
|
+
{ emoji: "⚡", text: "Working on it" },
|
|
18691
|
+
{ emoji: "\uD83D\uDEE0️", text: "Cooking" },
|
|
18692
|
+
{ emoji: "\uD83E\uDDE0", text: "Thinking hard" },
|
|
18693
|
+
{ emoji: "\uD83D\uDD27", text: "Tinkering" },
|
|
18694
|
+
{ emoji: "\uD83D\uDE80", text: "On it" },
|
|
18695
|
+
{ emoji: "\uD83D\uDD25", text: "In the zone" }
|
|
18696
|
+
];
|
|
18697
|
+
var BOT_ACTIVITY_STATUS_PAIRS = [
|
|
18698
|
+
BOT_ACTIVITY_PRESETS.idle,
|
|
18699
|
+
BOT_ACTIVITY_PRESETS.starting,
|
|
18700
|
+
BOT_ACTIVITY_PRESETS.stopping,
|
|
18701
|
+
...RUNNING_PRESETS
|
|
18702
|
+
];
|
|
18641
18703
|
// ../shared/src/db/index.ts
|
|
18642
18704
|
var allSchema = { ...exports_schema, ...exports_community_schema, ...exports_community_machine_schema };
|
|
18643
18705
|
// ../shared/src/db/queries/task.ts
|
|
@@ -19649,8 +19711,8 @@ function ensureSymlinks(workDir) {
|
|
|
19649
19711
|
try {
|
|
19650
19712
|
const stat = lstatSync(aliasPath);
|
|
19651
19713
|
if (stat.isSymbolicLink()) {
|
|
19652
|
-
const
|
|
19653
|
-
if (
|
|
19714
|
+
const target2 = readlinkSync(aliasPath);
|
|
19715
|
+
if (target2 === CANONICAL_FILE)
|
|
19654
19716
|
continue;
|
|
19655
19717
|
unlinkSync2(aliasPath);
|
|
19656
19718
|
} else {
|
|
@@ -19789,11 +19851,11 @@ function buildClaudeProviderIsolationEnv(ctx) {
|
|
|
19789
19851
|
fs4.mkdirSync(configDir, { recursive: true });
|
|
19790
19852
|
const hostClaude = path4.join(process.env.HOME || ".", ".claude");
|
|
19791
19853
|
for (const sub of ["skills", "commands"]) {
|
|
19792
|
-
const
|
|
19854
|
+
const target2 = path4.join(hostClaude, sub);
|
|
19793
19855
|
const link = path4.join(configDir, sub);
|
|
19794
19856
|
try {
|
|
19795
|
-
if (fs4.existsSync(
|
|
19796
|
-
fs4.symlinkSync(
|
|
19857
|
+
if (fs4.existsSync(target2) && !fs4.existsSync(link))
|
|
19858
|
+
fs4.symlinkSync(target2, link);
|
|
19797
19859
|
} catch {}
|
|
19798
19860
|
}
|
|
19799
19861
|
return {
|
|
@@ -20027,9 +20089,9 @@ function resolveHomePath(relativePath, deps = {}) {
|
|
|
20027
20089
|
}
|
|
20028
20090
|
function resolveSpawnSpec(command, args, override, deps = {}, platform = process.platform) {
|
|
20029
20091
|
const trimmed = override?.trim();
|
|
20030
|
-
const
|
|
20092
|
+
const target2 = trimmed && trimmed.length > 0 ? trimmed : command;
|
|
20031
20093
|
const looksLikePath = trimmed !== undefined && trimmed.length > 0 && /[\\/]/.test(trimmed);
|
|
20032
|
-
const resolved = looksLikePath ?
|
|
20094
|
+
const resolved = looksLikePath ? target2 : resolveCommandOnPath(target2, deps) ?? target2;
|
|
20033
20095
|
return { command: resolved, args, shell: needsWindowsShimShell(resolved, platform) };
|
|
20034
20096
|
}
|
|
20035
20097
|
function resolveClaudeCommand(deps = {}) {
|
|
@@ -21366,8 +21428,8 @@ function listRuntimeIds() {
|
|
|
21366
21428
|
var SELECTABLE_RUNTIMES = new Set(["claude", "codex", "opencode", "pi", "cursor"]);
|
|
21367
21429
|
function resolveAlookCliPath(moduleDir) {
|
|
21368
21430
|
const thisDir = moduleDir ?? path8.dirname(fileURLToPath(import.meta.url));
|
|
21369
|
-
const
|
|
21370
|
-
return fs6.existsSync(
|
|
21431
|
+
const target2 = path8.basename(thisDir) === "dist" ? path8.resolve(thisDir, "cli", "index.js") : path8.resolve(thisDir, "..", "scripts", "alook-shim.mjs");
|
|
21432
|
+
return fs6.existsSync(target2) ? target2 : null;
|
|
21371
21433
|
}
|
|
21372
21434
|
function deriveCliFallbackCandidates(cliPath) {
|
|
21373
21435
|
if (!cliPath)
|
|
@@ -21771,6 +21833,236 @@ function writeStatusFile(path9, snapshot) {
|
|
|
21771
21833
|
} catch {}
|
|
21772
21834
|
}
|
|
21773
21835
|
|
|
21836
|
+
// src/manager/wakeCoordinator.ts
|
|
21837
|
+
class WakeCoordinator {
|
|
21838
|
+
agents = new Map;
|
|
21839
|
+
async run(command, dispatch, onDesiredAdvance) {
|
|
21840
|
+
const state = this.agent(command.agentId);
|
|
21841
|
+
const channel2 = command.unreadNotice.channel;
|
|
21842
|
+
const seq = command.unreadNotice.latestSeq;
|
|
21843
|
+
const scope = state.scopes.get(channel2);
|
|
21844
|
+
const coveredSeq = Math.max(scope?.modelSeenSeq ?? 0, scope?.admittedSeq ?? 0);
|
|
21845
|
+
if (seq <= coveredSeq) {
|
|
21846
|
+
this.rememberReplacement(state, command);
|
|
21847
|
+
return { state: "suppressed", coveredSeq };
|
|
21848
|
+
}
|
|
21849
|
+
const desiredAdvanced = !scope || seq > scope.desiredSeq;
|
|
21850
|
+
if (!scope || seq >= scope.desiredSeq) {
|
|
21851
|
+
state.scopes.set(channel2, {
|
|
21852
|
+
desiredSeq: Math.max(scope?.desiredSeq ?? 0, seq),
|
|
21853
|
+
admittedSeq: scope?.admittedSeq ?? 0,
|
|
21854
|
+
modelSeenSeq: scope?.modelSeenSeq ?? 0,
|
|
21855
|
+
command
|
|
21856
|
+
});
|
|
21857
|
+
}
|
|
21858
|
+
if (desiredAdvanced)
|
|
21859
|
+
onDesiredAdvance?.(command);
|
|
21860
|
+
state.dispatch = dispatch;
|
|
21861
|
+
if (state.active || state.admitting) {
|
|
21862
|
+
this.rememberReplacement(state, command);
|
|
21863
|
+
return { state: "suppressed", coveredSeq: seq };
|
|
21864
|
+
}
|
|
21865
|
+
await this.admit(command.agentId, command);
|
|
21866
|
+
return { state: "accepted" };
|
|
21867
|
+
}
|
|
21868
|
+
modelSeenGeneration(agentId) {
|
|
21869
|
+
return this.agent(agentId).observationGeneration;
|
|
21870
|
+
}
|
|
21871
|
+
recordModelSeen(agentId, messages, generation) {
|
|
21872
|
+
const state = this.agent(agentId);
|
|
21873
|
+
if (generation !== state.observationGeneration)
|
|
21874
|
+
return false;
|
|
21875
|
+
for (const message2 of messages) {
|
|
21876
|
+
const seq = Number(message2.seq.startsWith("#") ? message2.seq.slice(1) : message2.seq);
|
|
21877
|
+
if (!Number.isSafeInteger(seq) || seq <= 0)
|
|
21878
|
+
continue;
|
|
21879
|
+
const scope = state.scopes.get(message2.channel);
|
|
21880
|
+
if (scope) {
|
|
21881
|
+
scope.modelSeenSeq = Math.max(scope.modelSeenSeq, seq);
|
|
21882
|
+
} else {
|
|
21883
|
+
state.scopes.set(message2.channel, {
|
|
21884
|
+
desiredSeq: 0,
|
|
21885
|
+
admittedSeq: 0,
|
|
21886
|
+
modelSeenSeq: seq,
|
|
21887
|
+
command: null
|
|
21888
|
+
});
|
|
21889
|
+
}
|
|
21890
|
+
}
|
|
21891
|
+
return true;
|
|
21892
|
+
}
|
|
21893
|
+
recordAgentActivity(agentId, activity) {
|
|
21894
|
+
const state = this.agent(agentId);
|
|
21895
|
+
if (activity === "starting" || activity === "running") {
|
|
21896
|
+
state.active = true;
|
|
21897
|
+
return;
|
|
21898
|
+
}
|
|
21899
|
+
if (activity !== "idle")
|
|
21900
|
+
return;
|
|
21901
|
+
state.active = false;
|
|
21902
|
+
state.activeAdmission = null;
|
|
21903
|
+
const next = this.nextPending(state);
|
|
21904
|
+
if (next && state.dispatch)
|
|
21905
|
+
this.admit(agentId, next);
|
|
21906
|
+
}
|
|
21907
|
+
recordDeliveryAck(agentId, launchId, status) {
|
|
21908
|
+
const state = this.agents.get(agentId);
|
|
21909
|
+
const admission = state?.activeAdmission;
|
|
21910
|
+
if (!state || !admission || admission.launchId !== launchId)
|
|
21911
|
+
return;
|
|
21912
|
+
if (status === "ok")
|
|
21913
|
+
return;
|
|
21914
|
+
for (const [channel2, coverage] of admission.coverage) {
|
|
21915
|
+
const scope = state.scopes.get(channel2);
|
|
21916
|
+
if (scope?.admittedSeq === coverage.admittedSeq) {
|
|
21917
|
+
scope.admittedSeq = coverage.previousSeq;
|
|
21918
|
+
}
|
|
21919
|
+
}
|
|
21920
|
+
state.active = false;
|
|
21921
|
+
state.failedAdmissionLaunchId = launchId;
|
|
21922
|
+
state.retryAfterFailure = state.coalescedReplacement;
|
|
21923
|
+
state.coalescedReplacement = null;
|
|
21924
|
+
state.activeAdmission = null;
|
|
21925
|
+
if (!state.admitting && state.retryAfterFailure) {
|
|
21926
|
+
this.retryFailedAdmission(agentId, state).catch(() => {});
|
|
21927
|
+
}
|
|
21928
|
+
}
|
|
21929
|
+
invalidate(agentId, clearModelSeen) {
|
|
21930
|
+
const state = this.agent(agentId);
|
|
21931
|
+
state.observationGeneration += 1;
|
|
21932
|
+
state.active = false;
|
|
21933
|
+
state.admitting = false;
|
|
21934
|
+
state.activeAdmission = null;
|
|
21935
|
+
state.failedAdmissionLaunchId = null;
|
|
21936
|
+
state.coalescedReplacement = null;
|
|
21937
|
+
state.retryAfterFailure = null;
|
|
21938
|
+
if (clearModelSeen) {
|
|
21939
|
+
state.scopes.clear();
|
|
21940
|
+
return;
|
|
21941
|
+
}
|
|
21942
|
+
for (const scope of state.scopes.values()) {
|
|
21943
|
+
scope.desiredSeq = scope.modelSeenSeq;
|
|
21944
|
+
scope.admittedSeq = scope.modelSeenSeq;
|
|
21945
|
+
}
|
|
21946
|
+
}
|
|
21947
|
+
agent(agentId) {
|
|
21948
|
+
let state = this.agents.get(agentId);
|
|
21949
|
+
if (!state) {
|
|
21950
|
+
state = {
|
|
21951
|
+
active: false,
|
|
21952
|
+
admitting: false,
|
|
21953
|
+
activeAdmission: null,
|
|
21954
|
+
failedAdmissionLaunchId: null,
|
|
21955
|
+
coalescedReplacement: null,
|
|
21956
|
+
retryAfterFailure: null,
|
|
21957
|
+
observationGeneration: 0,
|
|
21958
|
+
scopes: new Map,
|
|
21959
|
+
dispatch: null
|
|
21960
|
+
};
|
|
21961
|
+
this.agents.set(agentId, state);
|
|
21962
|
+
}
|
|
21963
|
+
return state;
|
|
21964
|
+
}
|
|
21965
|
+
nextPending(state) {
|
|
21966
|
+
let next = null;
|
|
21967
|
+
for (const scope of state.scopes.values()) {
|
|
21968
|
+
if (scope.desiredSeq <= Math.max(scope.modelSeenSeq, scope.admittedSeq))
|
|
21969
|
+
continue;
|
|
21970
|
+
if (!scope.command)
|
|
21971
|
+
continue;
|
|
21972
|
+
if (scope.command.launchId === state.failedAdmissionLaunchId)
|
|
21973
|
+
continue;
|
|
21974
|
+
if (!next || scope.desiredSeq > next.desiredSeq)
|
|
21975
|
+
next = scope;
|
|
21976
|
+
}
|
|
21977
|
+
return next?.command ?? null;
|
|
21978
|
+
}
|
|
21979
|
+
async admit(agentId, preferred) {
|
|
21980
|
+
const state = this.agent(agentId);
|
|
21981
|
+
if (state.active || state.admitting || !state.dispatch)
|
|
21982
|
+
return;
|
|
21983
|
+
const admissionGeneration = state.observationGeneration;
|
|
21984
|
+
const command = preferred ?? this.nextPending(state);
|
|
21985
|
+
if (!command)
|
|
21986
|
+
return;
|
|
21987
|
+
const commandScope = state.scopes.get(command.unreadNotice.channel);
|
|
21988
|
+
if (commandScope && command.unreadNotice.latestSeq >= commandScope.desiredSeq) {
|
|
21989
|
+
commandScope.command = command;
|
|
21990
|
+
}
|
|
21991
|
+
const coverage = new Map;
|
|
21992
|
+
for (const [channel2, scope] of state.scopes) {
|
|
21993
|
+
if (scope.desiredSeq <= Math.max(scope.modelSeenSeq, scope.admittedSeq))
|
|
21994
|
+
continue;
|
|
21995
|
+
coverage.set(channel2, {
|
|
21996
|
+
previousSeq: scope.admittedSeq,
|
|
21997
|
+
admittedSeq: scope.desiredSeq
|
|
21998
|
+
});
|
|
21999
|
+
scope.admittedSeq = scope.desiredSeq;
|
|
22000
|
+
scope.command = command;
|
|
22001
|
+
}
|
|
22002
|
+
if (coverage.size === 0)
|
|
22003
|
+
return;
|
|
22004
|
+
state.admitting = true;
|
|
22005
|
+
state.active = true;
|
|
22006
|
+
state.failedAdmissionLaunchId = null;
|
|
22007
|
+
state.coalescedReplacement = null;
|
|
22008
|
+
state.activeAdmission = { launchId: command.launchId, coverage };
|
|
22009
|
+
let dispatchError;
|
|
22010
|
+
try {
|
|
22011
|
+
await state.dispatch(command);
|
|
22012
|
+
} catch (error51) {
|
|
22013
|
+
if (state.observationGeneration === admissionGeneration) {
|
|
22014
|
+
for (const [channel2, admitted] of coverage) {
|
|
22015
|
+
const scope = state.scopes.get(channel2);
|
|
22016
|
+
if (scope?.admittedSeq === admitted.admittedSeq) {
|
|
22017
|
+
scope.admittedSeq = admitted.previousSeq;
|
|
22018
|
+
}
|
|
22019
|
+
}
|
|
22020
|
+
state.active = false;
|
|
22021
|
+
state.failedAdmissionLaunchId = command.launchId;
|
|
22022
|
+
state.retryAfterFailure = state.coalescedReplacement;
|
|
22023
|
+
state.coalescedReplacement = null;
|
|
22024
|
+
state.activeAdmission = null;
|
|
22025
|
+
}
|
|
22026
|
+
dispatchError = error51;
|
|
22027
|
+
} finally {
|
|
22028
|
+
if (state.observationGeneration === admissionGeneration) {
|
|
22029
|
+
state.admitting = false;
|
|
22030
|
+
}
|
|
22031
|
+
}
|
|
22032
|
+
if (state.observationGeneration === admissionGeneration) {
|
|
22033
|
+
await this.retryFailedAdmission(agentId, state);
|
|
22034
|
+
if (!state.active && !state.admitting) {
|
|
22035
|
+
const next = this.nextPending(state);
|
|
22036
|
+
if (next)
|
|
22037
|
+
await this.admit(agentId, next);
|
|
22038
|
+
}
|
|
22039
|
+
}
|
|
22040
|
+
if (dispatchError)
|
|
22041
|
+
throw dispatchError;
|
|
22042
|
+
}
|
|
22043
|
+
rememberReplacement(state, command) {
|
|
22044
|
+
const failedOrActiveLaunchId = state.activeAdmission?.launchId ?? state.failedAdmissionLaunchId;
|
|
22045
|
+
if (!failedOrActiveLaunchId || command.launchId === failedOrActiveLaunchId)
|
|
22046
|
+
return;
|
|
22047
|
+
const scope = state.scopes.get(command.unreadNotice.channel);
|
|
22048
|
+
if (!scope || command.unreadNotice.latestSeq < scope.desiredSeq)
|
|
22049
|
+
return;
|
|
22050
|
+
state.coalescedReplacement = command;
|
|
22051
|
+
if (state.failedAdmissionLaunchId)
|
|
22052
|
+
state.retryAfterFailure = command;
|
|
22053
|
+
}
|
|
22054
|
+
async retryFailedAdmission(agentId, state) {
|
|
22055
|
+
if (state.active || state.admitting)
|
|
22056
|
+
return;
|
|
22057
|
+
const replacement = state.retryAfterFailure;
|
|
22058
|
+
state.retryAfterFailure = null;
|
|
22059
|
+
if (!replacement)
|
|
22060
|
+
return;
|
|
22061
|
+
state.failedAdmissionLaunchId = null;
|
|
22062
|
+
await this.admit(agentId, replacement);
|
|
22063
|
+
}
|
|
22064
|
+
}
|
|
22065
|
+
|
|
21774
22066
|
// src/server/wsControlChannel.ts
|
|
21775
22067
|
var WS_CONTROL_COMMAND_CONSUMED = Symbol("ws-control-command-consumed");
|
|
21776
22068
|
var DEFAULT_PING_INTERVAL_MS = 15000;
|
|
@@ -21785,6 +22077,7 @@ class WsControlChannel {
|
|
|
21785
22077
|
opts;
|
|
21786
22078
|
statusValue = "idle";
|
|
21787
22079
|
commandCbs = [];
|
|
22080
|
+
wakeDesiredCbs = [];
|
|
21788
22081
|
resyncHooks = [];
|
|
21789
22082
|
ws = null;
|
|
21790
22083
|
attempt = 0;
|
|
@@ -21794,6 +22087,7 @@ class WsControlChannel {
|
|
|
21794
22087
|
pongDeadline = 0;
|
|
21795
22088
|
resyncProvider = null;
|
|
21796
22089
|
log;
|
|
22090
|
+
wakeCoordinator = new WakeCoordinator;
|
|
21797
22091
|
constructor(opts) {
|
|
21798
22092
|
this.opts = opts;
|
|
21799
22093
|
this.log = opts.logger ?? createLogger2({ header: "@alook/daemon:ws" });
|
|
@@ -21816,6 +22110,30 @@ class WsControlChannel {
|
|
|
21816
22110
|
onCommand(cb) {
|
|
21817
22111
|
this.commandCbs.push(cb);
|
|
21818
22112
|
}
|
|
22113
|
+
onWakeDesiredAdvance(cb) {
|
|
22114
|
+
this.wakeDesiredCbs.push(cb);
|
|
22115
|
+
}
|
|
22116
|
+
async ingestCommand(frame) {
|
|
22117
|
+
const parsed = HostCommandSchema.safeParse(frame);
|
|
22118
|
+
if (!parsed.success) {
|
|
22119
|
+
this.log.warn("dropped malformed HostCommand frame", {
|
|
22120
|
+
type: typeof frame === "object" && frame !== null && "type" in frame ? String(frame.type) : "unknown",
|
|
22121
|
+
issues: parsed.error.issues.map((issue3) => ({
|
|
22122
|
+
path: issue3.path.join("."),
|
|
22123
|
+
code: issue3.code
|
|
22124
|
+
}))
|
|
22125
|
+
});
|
|
22126
|
+
return false;
|
|
22127
|
+
}
|
|
22128
|
+
await this.dispatchIngressCommand(parsed.data);
|
|
22129
|
+
return true;
|
|
22130
|
+
}
|
|
22131
|
+
modelSeenGeneration(agentId) {
|
|
22132
|
+
return this.wakeCoordinator.modelSeenGeneration(agentId);
|
|
22133
|
+
}
|
|
22134
|
+
recordModelSeen(agentId, messages, generation = this.wakeCoordinator.modelSeenGeneration(agentId)) {
|
|
22135
|
+
return this.wakeCoordinator.recordModelSeen(agentId, messages, generation);
|
|
22136
|
+
}
|
|
21819
22137
|
onResync(provider) {
|
|
21820
22138
|
this.resyncProvider = provider;
|
|
21821
22139
|
}
|
|
@@ -21832,6 +22150,7 @@ class WsControlChannel {
|
|
|
21832
22150
|
this.sendFrame({ type: "agent_session", ...info });
|
|
21833
22151
|
}
|
|
21834
22152
|
async reportAgentActivity(info) {
|
|
22153
|
+
this.wakeCoordinator.recordAgentActivity(info.agentId, info.state);
|
|
21835
22154
|
this.sendFrame({ type: "agent_activity", ...info });
|
|
21836
22155
|
}
|
|
21837
22156
|
reportAgentTyping(info) {
|
|
@@ -21844,6 +22163,7 @@ class WsControlChannel {
|
|
|
21844
22163
|
this.sendFrame(frame);
|
|
21845
22164
|
}
|
|
21846
22165
|
async reportWakeAck(info) {
|
|
22166
|
+
this.wakeCoordinator.recordDeliveryAck(info.agentId, info.launchId, info.status);
|
|
21847
22167
|
this.sendFrame({ type: "agent_wake_ack", ...info });
|
|
21848
22168
|
}
|
|
21849
22169
|
async reportStoppedAck(info) {
|
|
@@ -21915,27 +22235,75 @@ class WsControlChannel {
|
|
|
21915
22235
|
return;
|
|
21916
22236
|
}
|
|
21917
22237
|
this.attempt = 0;
|
|
21918
|
-
|
|
21919
|
-
|
|
21920
|
-
|
|
21921
|
-
|
|
21922
|
-
|
|
21923
|
-
|
|
21924
|
-
return;
|
|
21925
|
-
}
|
|
21926
|
-
const cmd = parsed.data;
|
|
22238
|
+
this.ingestCommand(frame).catch((err) => {
|
|
22239
|
+
this.log.warn("command ingress failed", { type: frame.type, err: describeErr(err) });
|
|
22240
|
+
});
|
|
22241
|
+
}
|
|
22242
|
+
async dispatchListeners(cmd) {
|
|
22243
|
+
const pending = [];
|
|
21927
22244
|
for (const cb of this.commandCbs) {
|
|
21928
22245
|
try {
|
|
21929
22246
|
const result = cb(cmd);
|
|
21930
22247
|
if (result === WS_CONTROL_COMMAND_CONSUMED)
|
|
21931
22248
|
break;
|
|
21932
|
-
Promise.resolve(result).catch((err) => {
|
|
22249
|
+
pending.push(Promise.resolve(result).catch((err) => {
|
|
21933
22250
|
this.log.warn("command listener threw", { type: cmd.type, err: describeErr(err) });
|
|
21934
|
-
});
|
|
22251
|
+
}));
|
|
21935
22252
|
} catch (err) {
|
|
21936
22253
|
this.log.warn("command listener threw synchronously", { type: cmd.type, err: describeErr(err) });
|
|
21937
22254
|
}
|
|
21938
22255
|
}
|
|
22256
|
+
await Promise.all(pending);
|
|
22257
|
+
}
|
|
22258
|
+
async dispatchIngressCommand(cmd) {
|
|
22259
|
+
if (cmd.type === "machine:heartbeat") {
|
|
22260
|
+
this.sendFrame({ type: "machine_heartbeat_ack", nonce: cmd.nonce });
|
|
22261
|
+
return;
|
|
22262
|
+
}
|
|
22263
|
+
if (cmd.type === "diagnostics:collect") {
|
|
22264
|
+
this.sendFrame({ type: "diagnostics_ack", reportId: cmd.reportId });
|
|
22265
|
+
await this.dispatchListeners(cmd);
|
|
22266
|
+
return;
|
|
22267
|
+
}
|
|
22268
|
+
if (cmd.type === "agent:wake") {
|
|
22269
|
+
const result = await this.wakeCoordinator.run(cmd, (accepted) => this.dispatchListeners(accepted), (advanced) => {
|
|
22270
|
+
for (const cb of this.wakeDesiredCbs) {
|
|
22271
|
+
try {
|
|
22272
|
+
cb(advanced);
|
|
22273
|
+
} catch (err) {
|
|
22274
|
+
this.log.warn("wake desired observer threw", { err: describeErr(err) });
|
|
22275
|
+
}
|
|
22276
|
+
}
|
|
22277
|
+
});
|
|
22278
|
+
if (result.state === "suppressed") {
|
|
22279
|
+
await this.reportWakeAck({
|
|
22280
|
+
agentId: cmd.agentId,
|
|
22281
|
+
launchId: cmd.launchId,
|
|
22282
|
+
status: "ok"
|
|
22283
|
+
});
|
|
22284
|
+
this.log.debug("duplicate wake suppressed", {
|
|
22285
|
+
agentId: cmd.agentId,
|
|
22286
|
+
channel: cmd.unreadNotice.channel,
|
|
22287
|
+
latestSeq: cmd.unreadNotice.latestSeq,
|
|
22288
|
+
coveredSeq: result.coveredSeq
|
|
22289
|
+
});
|
|
22290
|
+
}
|
|
22291
|
+
return;
|
|
22292
|
+
}
|
|
22293
|
+
if (cmd.type === "machine:reset_all") {
|
|
22294
|
+
for (const reset of cmd.resets)
|
|
22295
|
+
this.wakeCoordinator.invalidate(reset.agentId, true);
|
|
22296
|
+
await this.dispatchListeners(cmd);
|
|
22297
|
+
return;
|
|
22298
|
+
}
|
|
22299
|
+
if (cmd.type === "agent:stop" || cmd.type === "agent:model_switch") {
|
|
22300
|
+
this.wakeCoordinator.invalidate(cmd.agentId, false);
|
|
22301
|
+
} else if (cmd.type === "agent:reset" || cmd.type === "agent:nap") {
|
|
22302
|
+
this.wakeCoordinator.invalidate(cmd.agentId, true);
|
|
22303
|
+
} else if (cmd.type === "bot:removed") {
|
|
22304
|
+
this.wakeCoordinator.invalidate(cmd.botId, true);
|
|
22305
|
+
}
|
|
22306
|
+
await this.dispatchListeners(cmd);
|
|
21939
22307
|
}
|
|
21940
22308
|
onSocketClosed(code, reason) {
|
|
21941
22309
|
this.log.warn("control channel closed", { code, reason: reason ? String(reason) : "" });
|
|
@@ -21970,7 +22338,10 @@ class WsControlChannel {
|
|
|
21970
22338
|
this.pingTimer = setInterval(() => {
|
|
21971
22339
|
if (this.now() > this.pongDeadline) {
|
|
21972
22340
|
this.log.warn("heartbeat pong timeout — forcing reconnect");
|
|
21973
|
-
this.ws?.
|
|
22341
|
+
if (this.ws?.terminate)
|
|
22342
|
+
this.ws.terminate();
|
|
22343
|
+
else
|
|
22344
|
+
this.ws?.close();
|
|
21974
22345
|
return;
|
|
21975
22346
|
}
|
|
21976
22347
|
this.log.debug("heartbeat ping");
|
|
@@ -22122,6 +22493,14 @@ function writeJson(res, status, body) {
|
|
|
22122
22493
|
res.writeHead(status, { "content-type": "application/json" });
|
|
22123
22494
|
res.end(JSON.stringify(body));
|
|
22124
22495
|
}
|
|
22496
|
+
function normalizeContentEncoding(value) {
|
|
22497
|
+
const normalized = (Array.isArray(value) ? value.join(",") : value ?? "").trim().toLowerCase();
|
|
22498
|
+
if (!normalized || normalized === "identity")
|
|
22499
|
+
return "identity";
|
|
22500
|
+
if (normalized === "gzip" || normalized === "deflate" || normalized === "br")
|
|
22501
|
+
return normalized;
|
|
22502
|
+
return "other";
|
|
22503
|
+
}
|
|
22125
22504
|
function isCanonicalChannelScope(channel2) {
|
|
22126
22505
|
try {
|
|
22127
22506
|
const parsed = parseRef(channel2);
|
|
@@ -22218,6 +22597,11 @@ async function startCredentialProxy(broker, options = {}) {
|
|
|
22218
22597
|
const upstreamClient = upstream.protocol === "https:" ? https : http;
|
|
22219
22598
|
const onPull = options.onInboxPullResponse;
|
|
22220
22599
|
const onProxyRequest = options.onProxyRequest;
|
|
22600
|
+
const reportInboxPullObservationError = (failure) => {
|
|
22601
|
+
try {
|
|
22602
|
+
options.onInboxPullObservationError?.(failure);
|
|
22603
|
+
} catch {}
|
|
22604
|
+
};
|
|
22221
22605
|
const server = http.createServer((req, res) => {
|
|
22222
22606
|
const pathname = new URL2(req.url ?? "/", "http://placeholder").pathname;
|
|
22223
22607
|
if (pathname.startsWith("/__alook/local/")) {
|
|
@@ -22256,7 +22640,20 @@ async function startCredentialProxy(broker, options = {}) {
|
|
|
22256
22640
|
return;
|
|
22257
22641
|
}
|
|
22258
22642
|
const reg = verdict.reg;
|
|
22259
|
-
const isInboxPull =
|
|
22643
|
+
const isInboxPull = req.method === "POST" && pathname === "/api/community/users/me/inbox/pull";
|
|
22644
|
+
const shouldObserveInboxPull = Boolean(isInboxPull && onPull);
|
|
22645
|
+
let inboxPullObservationToken;
|
|
22646
|
+
if (shouldObserveInboxPull) {
|
|
22647
|
+
try {
|
|
22648
|
+
inboxPullObservationToken = options.onInboxPullStart?.(reg.agentId);
|
|
22649
|
+
} catch {
|
|
22650
|
+
reportInboxPullObservationError({
|
|
22651
|
+
agentId: reg.agentId,
|
|
22652
|
+
reason: "observer_failed",
|
|
22653
|
+
contentEncoding: "identity"
|
|
22654
|
+
});
|
|
22655
|
+
}
|
|
22656
|
+
}
|
|
22260
22657
|
if (onProxyRequest) {
|
|
22261
22658
|
try {
|
|
22262
22659
|
onProxyRequest(reg.agentId, req.method ?? "GET", pathname);
|
|
@@ -22269,6 +22666,8 @@ async function startCredentialProxy(broker, options = {}) {
|
|
|
22269
22666
|
outHeaders[broker.headerNames.agentId.toLowerCase()] = reg.agentId;
|
|
22270
22667
|
outHeaders[broker.headerNames.client.toLowerCase()] = broker.clientLabel;
|
|
22271
22668
|
outHeaders[broker.headerNames.capabilities.toLowerCase()] = [...reg.capabilities].join(",");
|
|
22669
|
+
if (isInboxPull)
|
|
22670
|
+
outHeaders["accept-encoding"] = "identity";
|
|
22272
22671
|
let responded = false;
|
|
22273
22672
|
let upstreamRes;
|
|
22274
22673
|
const upstreamReq = upstreamClient.request({
|
|
@@ -22287,18 +22686,54 @@ async function startCredentialProxy(broker, options = {}) {
|
|
|
22287
22686
|
};
|
|
22288
22687
|
res_.on("error", destroyResIfIncomplete);
|
|
22289
22688
|
res_.on("close", destroyResIfIncomplete);
|
|
22290
|
-
if (
|
|
22689
|
+
if (shouldObserveInboxPull && res_.statusCode !== undefined && res_.statusCode >= 200 && res_.statusCode < 300) {
|
|
22291
22690
|
const chunks = [];
|
|
22292
22691
|
res_.on("data", (chunk2) => chunks.push(chunk2));
|
|
22293
22692
|
res_.on("end", () => {
|
|
22294
22693
|
const body = Buffer.concat(chunks);
|
|
22694
|
+
const contentEncoding = normalizeContentEncoding(res_.headers["content-encoding"]);
|
|
22695
|
+
if (contentEncoding !== "identity") {
|
|
22696
|
+
reportInboxPullObservationError({
|
|
22697
|
+
agentId: reg.agentId,
|
|
22698
|
+
reason: "unexpected_content_encoding",
|
|
22699
|
+
contentEncoding
|
|
22700
|
+
});
|
|
22701
|
+
} else {
|
|
22702
|
+
let parsed;
|
|
22703
|
+
try {
|
|
22704
|
+
parsed = JSON.parse(body.toString("utf8"));
|
|
22705
|
+
} catch {
|
|
22706
|
+
reportInboxPullObservationError({
|
|
22707
|
+
agentId: reg.agentId,
|
|
22708
|
+
reason: "invalid_json",
|
|
22709
|
+
contentEncoding
|
|
22710
|
+
});
|
|
22711
|
+
}
|
|
22712
|
+
if (parsed !== undefined) {
|
|
22713
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed) || !Array.isArray(parsed.messages)) {
|
|
22714
|
+
reportInboxPullObservationError({
|
|
22715
|
+
agentId: reg.agentId,
|
|
22716
|
+
reason: "invalid_inbox_shape",
|
|
22717
|
+
contentEncoding
|
|
22718
|
+
});
|
|
22719
|
+
} else {
|
|
22720
|
+
const messages = parsed.messages;
|
|
22721
|
+
if (messages.length > 0) {
|
|
22722
|
+
try {
|
|
22723
|
+
onPull(reg.agentId, messages, inboxPullObservationToken);
|
|
22724
|
+
} catch {
|
|
22725
|
+
reportInboxPullObservationError({
|
|
22726
|
+
agentId: reg.agentId,
|
|
22727
|
+
reason: "observer_failed",
|
|
22728
|
+
contentEncoding
|
|
22729
|
+
});
|
|
22730
|
+
}
|
|
22731
|
+
}
|
|
22732
|
+
}
|
|
22733
|
+
}
|
|
22734
|
+
}
|
|
22295
22735
|
res.writeHead(res_.statusCode, res_.headers);
|
|
22296
22736
|
res.end(body);
|
|
22297
|
-
try {
|
|
22298
|
-
const parsed = JSON.parse(body.toString());
|
|
22299
|
-
if (parsed.messages)
|
|
22300
|
-
onPull(reg.agentId, parsed.messages);
|
|
22301
|
-
} catch {}
|
|
22302
22737
|
});
|
|
22303
22738
|
} else {
|
|
22304
22739
|
res.writeHead(res_.statusCode ?? 502, res_.headers);
|
|
@@ -23217,17 +23652,17 @@ function extractToolAudit(rawName, rawInput) {
|
|
|
23217
23652
|
return { name, suppressed: false };
|
|
23218
23653
|
return { name, target: truncateTargetToCodeUnits(firstLine), suppressed: false };
|
|
23219
23654
|
}
|
|
23220
|
-
let
|
|
23655
|
+
let target2;
|
|
23221
23656
|
if (cls === "file_target")
|
|
23222
|
-
|
|
23657
|
+
target2 = pickFileTarget(rawInput);
|
|
23223
23658
|
else if (cls === "pattern")
|
|
23224
|
-
|
|
23659
|
+
target2 = pickPatternTarget(rawInput);
|
|
23225
23660
|
else
|
|
23226
|
-
|
|
23227
|
-
if (typeof
|
|
23661
|
+
target2 = pickFallthroughTarget(rawInput);
|
|
23662
|
+
if (typeof target2 !== "string" || target2.length === 0) {
|
|
23228
23663
|
return { name, suppressed: false };
|
|
23229
23664
|
}
|
|
23230
|
-
return { name, target: truncateTargetToCodeUnits(
|
|
23665
|
+
return { name, target: truncateTargetToCodeUnits(target2), suppressed: false };
|
|
23231
23666
|
}
|
|
23232
23667
|
function truncateThinking(text2) {
|
|
23233
23668
|
const chars = [...text2].length;
|
|
@@ -24060,6 +24495,7 @@ class AgentRouter {
|
|
|
24060
24495
|
buildReady() {
|
|
24061
24496
|
return {
|
|
24062
24497
|
runtimeReport: [...this.runtimes.values()],
|
|
24498
|
+
capabilities: [CONTROL_HEARTBEAT_CAPABILITY],
|
|
24063
24499
|
runningAgents: [...this.running],
|
|
24064
24500
|
hostname: this.opts.hostname,
|
|
24065
24501
|
platform: this.opts.platform,
|
|
@@ -24902,6 +25338,8 @@ function createTimelineRecorder(opts) {
|
|
|
24902
25338
|
sessionByAgent.set(agentId, sessionId);
|
|
24903
25339
|
},
|
|
24904
25340
|
appendEntryForAgent(agentId, messages) {
|
|
25341
|
+
if (messages.length === 0)
|
|
25342
|
+
return;
|
|
24905
25343
|
const dir = dirFor(agentId);
|
|
24906
25344
|
if (!prepareTimelineDirectory(dir))
|
|
24907
25345
|
return;
|
|
@@ -25321,7 +25759,16 @@ async function createDaemon(opts) {
|
|
|
25321
25759
|
const typingTracker = createTypingScopeTracker();
|
|
25322
25760
|
const broker = new CredentialBroker({ upstreamBaseUrl: opts.serverUrl });
|
|
25323
25761
|
const proxy = await startCredentialProxy(broker, {
|
|
25324
|
-
|
|
25762
|
+
onInboxPullStart: (agentId) => channelRef?.modelSeenGeneration(agentId),
|
|
25763
|
+
onInboxPullResponse: (agentId, messages, observationToken) => {
|
|
25764
|
+
timeline2.appendEntryForAgent(agentId, messages);
|
|
25765
|
+
if (typeof observationToken === "number") {
|
|
25766
|
+
channelRef?.recordModelSeen(agentId, messages, observationToken);
|
|
25767
|
+
}
|
|
25768
|
+
},
|
|
25769
|
+
onInboxPullObservationError: ({ agentId, reason, contentEncoding }) => {
|
|
25770
|
+
log2.warn("inbox pull timeline observation failed", { agentId, reason, contentEncoding });
|
|
25771
|
+
},
|
|
25325
25772
|
onMessageReminderArm: (input) => reminderSchedulerRef?.arm(input) ?? { armed: false, reason: "reminder_scheduler_unavailable" },
|
|
25326
25773
|
onProxyRequest: (agentId, method, pathname) => {
|
|
25327
25774
|
const subcommand = deriveAuditLogSubcommand(pathname, method);
|
|
@@ -25422,11 +25869,29 @@ async function createDaemon(opts) {
|
|
|
25422
25869
|
if (!res.ok)
|
|
25423
25870
|
throw new Error(`resync-wakes ${res.status}`);
|
|
25424
25871
|
const json2 = await res.json();
|
|
25425
|
-
log2.info("wake resync completed", {
|
|
25872
|
+
log2.info("wake resync completed", { attempted: json2.attempted ?? 0 });
|
|
25426
25873
|
} catch (err) {
|
|
25427
25874
|
log2.warn("wake resync failed", { err: err instanceof Error ? err.message : String(err) });
|
|
25428
25875
|
}
|
|
25429
25876
|
}
|
|
25877
|
+
async function resyncPendingDiagnostics() {
|
|
25878
|
+
try {
|
|
25879
|
+
const res = await fetch(`${opts.serverUrl}/api/community/daemon/resync-diagnostics`, {
|
|
25880
|
+
method: "POST",
|
|
25881
|
+
headers: { authorization: `Bearer ${opts.machineKey}` }
|
|
25882
|
+
});
|
|
25883
|
+
if (!res.ok)
|
|
25884
|
+
throw new Error(`resync-diagnostics ${res.status}`);
|
|
25885
|
+
const json2 = await res.json();
|
|
25886
|
+
log2.info("diagnostics resync completed", {
|
|
25887
|
+
pending: json2.pending ?? 0,
|
|
25888
|
+
attempted: json2.attempted ?? 0,
|
|
25889
|
+
ambiguous: json2.ambiguous ?? 0
|
|
25890
|
+
});
|
|
25891
|
+
} catch (err) {
|
|
25892
|
+
log2.warn("diagnostics resync failed", { err: err instanceof Error ? err.message : String(err) });
|
|
25893
|
+
}
|
|
25894
|
+
}
|
|
25430
25895
|
const enrollAgent = async (agentId) => {
|
|
25431
25896
|
const existing = enrolledKeys.get(agentId);
|
|
25432
25897
|
if (existing)
|
|
@@ -25652,11 +26117,11 @@ async function createDaemon(opts) {
|
|
|
25652
26117
|
handleDiagnosticCommand: opts.handleDiagnosticCommand,
|
|
25653
26118
|
reportDiagnosticFailure: opts.reportDiagnosticFailure
|
|
25654
26119
|
}));
|
|
26120
|
+
channel2.onWakeDesiredAdvance((cmd) => {
|
|
26121
|
+
reminderSchedulerRef?.observe(cmd.agentId, cmd.unreadNotice.channel, cmd.unreadNotice.latestSeq);
|
|
26122
|
+
});
|
|
25655
26123
|
channel2.onCommand((cmd) => {
|
|
25656
26124
|
switch (cmd.type) {
|
|
25657
|
-
case "agent:wake":
|
|
25658
|
-
reminderSchedulerRef?.observe(cmd.agentId, cmd.unreadNotice.channel, cmd.unreadNotice.latestSeq);
|
|
25659
|
-
break;
|
|
25660
26125
|
case "agent:stop":
|
|
25661
26126
|
reminderSchedulerRef?.clearAgent(cmd.agentId);
|
|
25662
26127
|
break;
|
|
@@ -25673,6 +26138,7 @@ async function createDaemon(opts) {
|
|
|
25673
26138
|
channel2.onOpen(() => {
|
|
25674
26139
|
coldStartWarmup();
|
|
25675
26140
|
resyncPendingWakes();
|
|
26141
|
+
resyncPendingDiagnostics();
|
|
25676
26142
|
});
|
|
25677
26143
|
channel2.connect();
|
|
25678
26144
|
await router.start();
|
|
@@ -27504,6 +27970,7 @@ var STOP_GRACE_MS = 5000;
|
|
|
27504
27970
|
var STOP_KILL_GRACE_MS = 2000;
|
|
27505
27971
|
var POLL_MS2 = 100;
|
|
27506
27972
|
var START_RECEIPT_TIMEOUT_MS = 15000;
|
|
27973
|
+
var PAIRING_ACTIVATION_TIMEOUT_MS = 30000;
|
|
27507
27974
|
var RUNNER_TERM_GRACE_MS = 2000;
|
|
27508
27975
|
var RUNNER_KILL_GRACE_MS = 2000;
|
|
27509
27976
|
var MACHINE_ID_PATTERN = /^cm_[A-Za-z0-9_-]{8,64}$/;
|
|
@@ -28046,20 +28513,58 @@ function findExistingCredentialForBearer(baseDir, bearer) {
|
|
|
28046
28513
|
}
|
|
28047
28514
|
return null;
|
|
28048
28515
|
}
|
|
28049
|
-
async function activatePairingToken(serverUrl, tokenId, hostname4, platform, arch, osRelease, daemonVersion, runtimeReport) {
|
|
28050
|
-
|
|
28051
|
-
|
|
28052
|
-
|
|
28053
|
-
|
|
28054
|
-
|
|
28055
|
-
|
|
28056
|
-
|
|
28057
|
-
});
|
|
28058
|
-
|
|
28059
|
-
|
|
28060
|
-
|
|
28516
|
+
async function activatePairingToken(serverUrl, tokenId, hostname4, platform, arch, osRelease, daemonVersion, runtimeReport, expectedMachineId) {
|
|
28517
|
+
let res;
|
|
28518
|
+
let json2;
|
|
28519
|
+
let timedOut = false;
|
|
28520
|
+
const controller = new AbortController;
|
|
28521
|
+
const timeout = setTimeout(() => {
|
|
28522
|
+
timedOut = true;
|
|
28523
|
+
controller.abort();
|
|
28524
|
+
}, PAIRING_ACTIVATION_TIMEOUT_MS);
|
|
28525
|
+
timeout.unref?.();
|
|
28526
|
+
try {
|
|
28527
|
+
res = await fetch(`${serverUrl}/api/community/daemon/activate`, {
|
|
28528
|
+
method: "POST",
|
|
28529
|
+
headers: {
|
|
28530
|
+
"content-type": "application/json",
|
|
28531
|
+
authorization: `Bearer ${tokenId}`
|
|
28532
|
+
},
|
|
28533
|
+
body: JSON.stringify({
|
|
28534
|
+
hostname: hostname4,
|
|
28535
|
+
platform,
|
|
28536
|
+
arch,
|
|
28537
|
+
osRelease,
|
|
28538
|
+
daemonVersion,
|
|
28539
|
+
runtimeReport,
|
|
28540
|
+
...expectedMachineId ? { expectedMachineId } : {}
|
|
28541
|
+
}),
|
|
28542
|
+
signal: controller.signal
|
|
28543
|
+
});
|
|
28544
|
+
json2 = await res.json().catch(() => null);
|
|
28545
|
+
} catch (error51) {
|
|
28546
|
+
throw new PairingActivationError(timedOut ? "activation timed out before commit status was known" : error51 instanceof Error ? error51.message : String(error51), "unknown");
|
|
28547
|
+
} finally {
|
|
28548
|
+
clearTimeout(timeout);
|
|
28549
|
+
}
|
|
28550
|
+
if (!res.ok) {
|
|
28551
|
+
const parsed2 = CommunityDaemonActivateErrorResponseSchema.safeParse(json2);
|
|
28552
|
+
throw new PairingActivationError(parsed2.success ? parsed2.data.error : `activate failed (${res.status})`, res.status >= 400 && res.status < 500 && parsed2.success ? parsed2.data.sessionOutcome : "unknown");
|
|
28553
|
+
}
|
|
28554
|
+
const parsed = CommunityDaemonActivateResponseSchema.safeParse(json2);
|
|
28555
|
+
if (!parsed.success) {
|
|
28556
|
+
throw new PairingActivationError("activate returned an invalid response", "unknown");
|
|
28557
|
+
}
|
|
28558
|
+
return { credential: parsed.data.credential, machineId: parsed.data.machineId };
|
|
28559
|
+
}
|
|
28560
|
+
|
|
28561
|
+
class PairingActivationError extends Error {
|
|
28562
|
+
sessionOutcome;
|
|
28563
|
+
constructor(message2, sessionOutcome) {
|
|
28564
|
+
super(message2);
|
|
28565
|
+
this.sessionOutcome = sessionOutcome;
|
|
28566
|
+
this.name = "PairingActivationError";
|
|
28061
28567
|
}
|
|
28062
|
-
return { credential: json2.credential, machineId: json2.machineId };
|
|
28063
28568
|
}
|
|
28064
28569
|
async function resolveMachineIdentity(serverUrl, credential) {
|
|
28065
28570
|
const response = await fetch(`${serverUrl}/api/community/daemon/identity`, {
|
|
@@ -28087,12 +28592,13 @@ async function prepareDaemonStart(opts) {
|
|
|
28087
28592
|
throw new Error("daemon package version is not a strict release version");
|
|
28088
28593
|
const ownerToken = crypto5.randomBytes(24).toString("base64url");
|
|
28089
28594
|
const persisted = opts.machineKey.startsWith("cmk_") ? findExistingCredentialForBearer(baseDir, opts.machineKey) : null;
|
|
28595
|
+
const reconnectMachineId = opts.machineKey.startsWith("cmt_") && opts.expectedMachineId && opts.resumeRequestId ? validateMachineId(opts.expectedMachineId) : undefined;
|
|
28090
28596
|
let coarseLockPath = null;
|
|
28091
28597
|
let launchLockPath = null;
|
|
28092
28598
|
try {
|
|
28093
28599
|
let machineKey = persisted?.credential;
|
|
28094
|
-
let machineId = persisted ? validateMachineId(persisted.machineId) :
|
|
28095
|
-
if (
|
|
28600
|
+
let machineId = persisted ? validateMachineId(persisted.machineId) : reconnectMachineId;
|
|
28601
|
+
if (machineId) {
|
|
28096
28602
|
launchLockPath = acquireLaunchLock(baseDir, machineId, ownerToken, opts.resumeRequestId);
|
|
28097
28603
|
} else {
|
|
28098
28604
|
coarseLockPath = acquireCoarseLock(baseDir, ownerToken);
|
|
@@ -28103,9 +28609,13 @@ async function prepareDaemonStart(opts) {
|
|
|
28103
28609
|
const runtimeReport = await detectRuntimes();
|
|
28104
28610
|
const healthyRuntimeIds = runtimeReport.filter((runtime) => runtime.status === "healthy").map((runtime) => runtime.id);
|
|
28105
28611
|
if (opts.machineKey.startsWith("cmt_")) {
|
|
28106
|
-
|
|
28612
|
+
opts.onActivationAttempt?.();
|
|
28613
|
+
const activated = await activatePairingToken(serverUrl, opts.machineKey, os3.hostname(), process.platform, process.arch, os3.release(), daemonVersion, runtimeReport, opts.expectedMachineId);
|
|
28107
28614
|
machineKey = activated.credential;
|
|
28108
28615
|
machineId = validateMachineId(activated.machineId);
|
|
28616
|
+
if (opts.expectedMachineId && machineId !== validateMachineId(opts.expectedMachineId)) {
|
|
28617
|
+
throw new Error("activate returned a different machine identity");
|
|
28618
|
+
}
|
|
28109
28619
|
} else {
|
|
28110
28620
|
machineKey ??= opts.machineKey;
|
|
28111
28621
|
machineId ??= validateMachineId(await resolveMachineIdentity(serverUrl, opts.machineKey));
|
|
@@ -28119,6 +28629,7 @@ async function prepareDaemonStart(opts) {
|
|
|
28119
28629
|
wsUrl,
|
|
28120
28630
|
daemonVersion
|
|
28121
28631
|
});
|
|
28632
|
+
if (false) {}
|
|
28122
28633
|
if (coarseLockPath)
|
|
28123
28634
|
removeOwnedFile(coarseLockPath, process.pid, ownerToken);
|
|
28124
28635
|
const startedAt = new Date().toISOString();
|
|
@@ -28177,6 +28688,66 @@ async function daemonStartById(opts) {
|
|
|
28177
28688
|
foreground: opts.foreground
|
|
28178
28689
|
});
|
|
28179
28690
|
}
|
|
28691
|
+
function pidOwnershipEqual(a, b) {
|
|
28692
|
+
if (!a || !b)
|
|
28693
|
+
return a === b;
|
|
28694
|
+
return a.pid === b.pid && a.machineId === b.machineId && a.startedAt === b.startedAt && a.ownerToken === b.ownerToken;
|
|
28695
|
+
}
|
|
28696
|
+
async function daemonReconnect(opts, deps = {}) {
|
|
28697
|
+
if (!opts.machineKey.startsWith("cmt_")) {
|
|
28698
|
+
throw new Error("daemon reconnect requires a cmt_ pairing token");
|
|
28699
|
+
}
|
|
28700
|
+
const machineId = validateMachineId(opts.id);
|
|
28701
|
+
const baseDir = opts.baseDir || process.env.ALOOK_DATA_DIR || DEFAULT_BASE_DIR;
|
|
28702
|
+
const launch = readDaemonLaunchRecord(baseDir, machineId);
|
|
28703
|
+
const pidPath = pidfilePathById(baseDir, machineId);
|
|
28704
|
+
const before = readPidFile(pidPath);
|
|
28705
|
+
if (before && (before.machineId !== machineId || !before.startedAt || !before.ownerToken)) {
|
|
28706
|
+
throw new Error("daemon reconnect cannot verify the current pid ownership tuple");
|
|
28707
|
+
}
|
|
28708
|
+
const requestId = crypto5.randomUUID();
|
|
28709
|
+
const replacement = acquireDaemonReplacementLock({ baseDir, machineId, requestId });
|
|
28710
|
+
let oldStopped = false;
|
|
28711
|
+
let activationAttempted = false;
|
|
28712
|
+
try {
|
|
28713
|
+
const owned = readPidFile(pidPath);
|
|
28714
|
+
if (!pidOwnershipEqual(before, owned)) {
|
|
28715
|
+
throw new Error("daemon ownership changed after reconnect lock acquisition");
|
|
28716
|
+
}
|
|
28717
|
+
if (owned && (deps.isProcessAlive ?? isProcessAlive)(owned.pid)) {
|
|
28718
|
+
await (deps.stopExactDaemonPid ?? stopExactDaemonPid)(owned.pid);
|
|
28719
|
+
oldStopped = true;
|
|
28720
|
+
removePidFileIfMatches(pidPath, owned);
|
|
28721
|
+
} else if (owned) {
|
|
28722
|
+
removePidFileIfMatches(pidPath, owned);
|
|
28723
|
+
}
|
|
28724
|
+
try {
|
|
28725
|
+
await (deps.start ?? daemonStart)({
|
|
28726
|
+
machineKey: opts.machineKey,
|
|
28727
|
+
serverUrl: opts.serverUrl ?? launch.serverUrl,
|
|
28728
|
+
wsUrl: opts.wsUrl ?? launch.wsUrl,
|
|
28729
|
+
baseDir,
|
|
28730
|
+
resumeRequestId: requestId,
|
|
28731
|
+
expectedMachineId: machineId,
|
|
28732
|
+
onActivationAttempt: () => {
|
|
28733
|
+
activationAttempted = true;
|
|
28734
|
+
}
|
|
28735
|
+
});
|
|
28736
|
+
} catch (error51) {
|
|
28737
|
+
const canRestoreOldEpoch = !activationAttempted || error51 instanceof PairingActivationError && error51.sessionOutcome === "not_committed";
|
|
28738
|
+
if (oldStopped && canRestoreOldEpoch) {
|
|
28739
|
+
try {
|
|
28740
|
+
await (deps.resume ?? daemonResume)({ id: machineId, baseDir, requestId });
|
|
28741
|
+
} catch (rollbackError) {
|
|
28742
|
+
throw new AggregateError([error51, rollbackError], "reconnect failed before rotation and the prior daemon could not be resumed");
|
|
28743
|
+
}
|
|
28744
|
+
}
|
|
28745
|
+
throw error51;
|
|
28746
|
+
}
|
|
28747
|
+
} finally {
|
|
28748
|
+
removeReplacementLockIfMatches(replacement.path, replacement.lock);
|
|
28749
|
+
}
|
|
28750
|
+
}
|
|
28180
28751
|
function runnerArguments() {
|
|
28181
28752
|
const command = process.env.ALOOK_DAEMON_PACKAGE_WRAPPER === "1" ? ["run"] : ["daemon", "run"];
|
|
28182
28753
|
return [...process.execArgv, process.argv[1], ...command];
|
|
@@ -28633,7 +29204,7 @@ async function cmdMessageSend(opts, stdin) {
|
|
|
28633
29204
|
}
|
|
28634
29205
|
replyToSeq = n;
|
|
28635
29206
|
}
|
|
28636
|
-
const nonce =
|
|
29207
|
+
const nonce = randomUUID7();
|
|
28637
29208
|
const res = await sendWithRetry(api2, {
|
|
28638
29209
|
agentId: agent2,
|
|
28639
29210
|
channel: channel2,
|
|
@@ -28666,30 +29237,30 @@ async function cmdMessageSend(opts, stdin) {
|
|
|
28666
29237
|
}
|
|
28667
29238
|
async function cmdMessageEmoji(opts) {
|
|
28668
29239
|
const api2 = getApi();
|
|
28669
|
-
const
|
|
29240
|
+
const target2 = opts.target;
|
|
28670
29241
|
const emoji3 = opts.emoji;
|
|
28671
|
-
if (!
|
|
29242
|
+
if (!target2)
|
|
28672
29243
|
throw new CliError("message emoji: --target <ref> is required (e.g. /demo#1234/general#42)");
|
|
28673
29244
|
if (!emoji3)
|
|
28674
29245
|
throw new CliError("message emoji: --emoji <string> is required");
|
|
28675
|
-
const { channel: channel2, seq } = parseMessageTarget("message emoji",
|
|
29246
|
+
const { channel: channel2, seq } = parseMessageTarget("message emoji", target2);
|
|
28676
29247
|
if (Buffer.byteLength(emoji3, "utf8") > MAX_EMOJI_BYTES) {
|
|
28677
29248
|
const err = new CliError("emoji is too long");
|
|
28678
29249
|
err.hint = "use a single emoji, not a phrase";
|
|
28679
29250
|
throw err;
|
|
28680
29251
|
}
|
|
28681
29252
|
const res = await api2.reactAdd({ channel: channel2, seq, emoji: emoji3 });
|
|
28682
|
-
return { target, emoji: emoji3, duplicate: res.duplicate === true };
|
|
29253
|
+
return { target: target2, emoji: emoji3, duplicate: res.duplicate === true };
|
|
28683
29254
|
}
|
|
28684
|
-
function parseMessageTarget(command,
|
|
29255
|
+
function parseMessageTarget(command, target2) {
|
|
28685
29256
|
let parsed;
|
|
28686
29257
|
try {
|
|
28687
|
-
parsed = parseRef(
|
|
29258
|
+
parsed = parseRef(target2);
|
|
28688
29259
|
} catch (err) {
|
|
28689
29260
|
throw new CliError(`${command}: ${err.message}`);
|
|
28690
29261
|
}
|
|
28691
29262
|
if (parsed.seq === undefined) {
|
|
28692
|
-
const err = new CliError(`${command} needs a ref with a seq (e.g. ${
|
|
29263
|
+
const err = new CliError(`${command} needs a ref with a seq (e.g. ${target2}#42)`);
|
|
28693
29264
|
err.hint = "pass --target /<server>/<channel>#N, /<server>/<channel>/#N#M for thread reply, or /.dm/<peer>#N";
|
|
28694
29265
|
throw err;
|
|
28695
29266
|
}
|
|
@@ -28698,21 +29269,21 @@ function parseMessageTarget(command, target) {
|
|
|
28698
29269
|
}
|
|
28699
29270
|
async function cmdMessageMarkSet(opts) {
|
|
28700
29271
|
const api2 = getApi();
|
|
28701
|
-
const
|
|
28702
|
-
if (!
|
|
29272
|
+
const target2 = opts.target;
|
|
29273
|
+
if (!target2)
|
|
28703
29274
|
throw new CliError("message mark set: --target <ref> is required");
|
|
28704
|
-
const request = parseMessageTarget("message mark set",
|
|
29275
|
+
const request = parseMessageTarget("message mark set", target2);
|
|
28705
29276
|
await withTransientMutationRetry(() => api2.markSet(request));
|
|
28706
|
-
return { target, marked: true };
|
|
29277
|
+
return { target: target2, marked: true };
|
|
28707
29278
|
}
|
|
28708
29279
|
async function cmdMessageMarkRemove(opts) {
|
|
28709
29280
|
const api2 = getApi();
|
|
28710
|
-
const
|
|
28711
|
-
if (!
|
|
29281
|
+
const target2 = opts.target;
|
|
29282
|
+
if (!target2)
|
|
28712
29283
|
throw new CliError("message mark remove: --target <ref> is required");
|
|
28713
|
-
const request = parseMessageTarget("message mark remove",
|
|
29284
|
+
const request = parseMessageTarget("message mark remove", target2);
|
|
28714
29285
|
await withTransientMutationRetry(() => api2.markRemove(request));
|
|
28715
|
-
return { target, marked: false };
|
|
29286
|
+
return { target: target2, marked: false };
|
|
28716
29287
|
}
|
|
28717
29288
|
async function cmdMessageMarkList(opts) {
|
|
28718
29289
|
const api2 = getApi();
|
|
@@ -28722,9 +29293,9 @@ async function cmdMessageMarkList(opts) {
|
|
|
28722
29293
|
async function cmdAttachmentUpload(opts) {
|
|
28723
29294
|
const api2 = getApi();
|
|
28724
29295
|
const agent2 = agentId(opts);
|
|
28725
|
-
const
|
|
29296
|
+
const target2 = opts.target;
|
|
28726
29297
|
const filePath = opts.file;
|
|
28727
|
-
if (!
|
|
29298
|
+
if (!target2)
|
|
28728
29299
|
throw new CliError("message attachment upload: --target <ref> is required");
|
|
28729
29300
|
if (!filePath)
|
|
28730
29301
|
throw new CliError("message attachment upload: --file <path> is required");
|
|
@@ -28765,7 +29336,7 @@ async function cmdAttachmentUpload(opts) {
|
|
|
28765
29336
|
}
|
|
28766
29337
|
const result = await api2.attachmentUpload({
|
|
28767
29338
|
agentId: agent2,
|
|
28768
|
-
target,
|
|
29339
|
+
target: target2,
|
|
28769
29340
|
file: { data: new Uint8Array(bytes), filename, contentType },
|
|
28770
29341
|
...thumbnail ? { thumbnail } : {},
|
|
28771
29342
|
...width !== undefined ? { width } : {},
|
|
@@ -29123,6 +29694,16 @@ function buildProgram(stdin) {
|
|
|
29123
29694
|
daemon.command("run", { hidden: true }).exitOverride().configureOutput({ writeOut: () => {}, writeErr: () => {} }).action(async () => {
|
|
29124
29695
|
await daemonRunFromIpc();
|
|
29125
29696
|
});
|
|
29697
|
+
daemon.command("reconnect").description("rotate and reconnect one previously paired machine").requiredOption("--id <machineId>", "machine id shown by daemon list").requiredOption("--machine-key <key>", "cmt_ reconnect token").option("--server-url <url>", "server HTTP URL (defaults to the saved launch record)").option("--ws-url <url>", "server WebSocket URL (defaults to the saved launch record)").option("--base-dir <path>", "data directory for agent workspaces and pidfile").exitOverride().configureOutput({ writeOut: () => {}, writeErr: () => {} }).action(async function() {
|
|
29698
|
+
const localOpts = this.opts();
|
|
29699
|
+
await daemonReconnect({
|
|
29700
|
+
id: localOpts.id,
|
|
29701
|
+
machineKey: localOpts.machineKey,
|
|
29702
|
+
serverUrl: localOpts.serverUrl,
|
|
29703
|
+
wsUrl: localOpts.wsUrl,
|
|
29704
|
+
baseDir: localOpts.baseDir
|
|
29705
|
+
});
|
|
29706
|
+
});
|
|
29126
29707
|
daemon.command("resume", { hidden: true }).requiredOption("--id <machineId>").requiredOption("--base-dir <path>").requiredOption("--request-id <id>").exitOverride().configureOutput({ writeOut: () => {}, writeErr: () => {} }).action(async function() {
|
|
29127
29708
|
const localOpts = this.opts();
|
|
29128
29709
|
await daemonResume({
|