chatroom-cli 1.36.0 → 1.37.1
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 +726 -83
- package/dist/index.js.map +17 -13
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -13426,6 +13426,10 @@ function startSessionEventForwarder(client4, options) {
|
|
|
13426
13426
|
const errorTarget = options.errorTarget ?? process.stderr;
|
|
13427
13427
|
let cancelled = false;
|
|
13428
13428
|
let doneResolve;
|
|
13429
|
+
let sessionStarted = false;
|
|
13430
|
+
const seenToolStates = new Map;
|
|
13431
|
+
let lastStatus;
|
|
13432
|
+
const agentEndCallbacks = [];
|
|
13429
13433
|
const donePromise = new Promise((resolve) => {
|
|
13430
13434
|
doneResolve = resolve;
|
|
13431
13435
|
});
|
|
@@ -13445,31 +13449,76 @@ function startSessionEventForwarder(client4, options) {
|
|
|
13445
13449
|
const eventSession = eventSessionId(event);
|
|
13446
13450
|
if (eventSession && eventSession !== options.sessionId)
|
|
13447
13451
|
continue;
|
|
13448
|
-
if (eventSession === undefined && event.type !== "session.idle" && event.type !== "session.compacted" && event.type !== "session.error" && event.type !== "session.status" && event.type !== "file.edited")
|
|
13452
|
+
if (eventSession === undefined && event.type !== "message.part.updated" && event.type !== "session.idle" && event.type !== "session.compacted" && event.type !== "session.error" && event.type !== "session.status" && event.type !== "file.edited")
|
|
13449
13453
|
continue;
|
|
13454
|
+
if (!sessionStarted) {
|
|
13455
|
+
sessionStarted = true;
|
|
13456
|
+
target.write(formatLogLine(options, "session] Started", `role: ${options.role}`) + `
|
|
13457
|
+
`);
|
|
13458
|
+
}
|
|
13450
13459
|
switch (event.type) {
|
|
13451
13460
|
case "message.part.updated": {
|
|
13452
13461
|
const props = event.properties;
|
|
13453
13462
|
const part = props?.part;
|
|
13454
|
-
if (part?.type === "text"
|
|
13455
|
-
|
|
13463
|
+
if (part?.type === "text") {
|
|
13464
|
+
const chunk = props?.delta !== undefined && props.delta !== "" ? props.delta : part.text;
|
|
13465
|
+
if (chunk) {
|
|
13466
|
+
target.write(formatLogLine(options, "text", chunk) + `
|
|
13456
13467
|
`);
|
|
13468
|
+
}
|
|
13469
|
+
} else if (part?.type === "reasoning") {
|
|
13470
|
+
const chunk = props?.delta !== undefined && props.delta !== "" ? props.delta : part.text;
|
|
13471
|
+
if (chunk) {
|
|
13472
|
+
target.write(formatLogLine(options, "thinking", chunk) + `
|
|
13473
|
+
`);
|
|
13474
|
+
}
|
|
13457
13475
|
} else if (part?.type === "tool" && part.tool) {
|
|
13458
|
-
|
|
13459
|
-
|
|
13476
|
+
let appendInput = function(base, input, tool) {
|
|
13477
|
+
if (!input || typeof input === "object" && Object.keys(input).length === 0) {
|
|
13478
|
+
return base;
|
|
13479
|
+
}
|
|
13480
|
+
const inp = input;
|
|
13481
|
+
if (tool === "bash" && typeof inp.command === "string") {
|
|
13482
|
+
return `${base}: ${inp.command}`;
|
|
13483
|
+
}
|
|
13484
|
+
const inputStr = typeof inp === "string" ? inp : JSON.stringify(inp);
|
|
13485
|
+
return `${base}: ${inputStr}`;
|
|
13486
|
+
};
|
|
13487
|
+
const state = typeof props?.state === "string" ? props.state : typeof part.state?.status === "string" ? part.state.status : "started";
|
|
13488
|
+
let payload = state;
|
|
13489
|
+
if (part.state?.input) {
|
|
13490
|
+
payload = appendInput(payload, part.state.input, part.tool);
|
|
13491
|
+
}
|
|
13492
|
+
if (state === "completed" && part.state?.time?.start !== undefined && part.state?.time?.end !== undefined) {
|
|
13493
|
+
const duration = ((part.state.time.end - part.state.time.start) / 1000).toFixed(1);
|
|
13494
|
+
payload = appendInput(`${state} (${duration}s)`, part.state.input, part.tool);
|
|
13495
|
+
}
|
|
13496
|
+
const callID = part.callID ?? "unknown";
|
|
13497
|
+
const seenKey = `${callID}:${state}`;
|
|
13498
|
+
if (!seenToolStates.has(seenKey)) {
|
|
13499
|
+
seenToolStates.set(seenKey, payload);
|
|
13500
|
+
target.write(formatLogLine(options, "tool: " + part.tool, payload) + `
|
|
13460
13501
|
`);
|
|
13502
|
+
}
|
|
13503
|
+
if (state === "completed" || state === "error") {
|
|
13504
|
+
seenToolStates.delete(seenKey);
|
|
13505
|
+
}
|
|
13461
13506
|
}
|
|
13462
13507
|
break;
|
|
13463
13508
|
}
|
|
13464
13509
|
case "file.edited": {
|
|
13465
13510
|
const props = event.properties;
|
|
13466
|
-
|
|
13511
|
+
const kind = props?.action ?? props?.kind;
|
|
13512
|
+
const filePayload = kind ? `${props?.file} (${kind})` : props?.file;
|
|
13513
|
+
target.write(formatLogLine(options, "file", filePayload) + `
|
|
13467
13514
|
`);
|
|
13468
13515
|
break;
|
|
13469
13516
|
}
|
|
13470
13517
|
case "session.idle": {
|
|
13471
13518
|
target.write(formatLogLine(options, "agent_end") + `
|
|
13472
13519
|
`);
|
|
13520
|
+
for (const cb of agentEndCallbacks)
|
|
13521
|
+
cb();
|
|
13473
13522
|
break;
|
|
13474
13523
|
}
|
|
13475
13524
|
case "session.compacted": {
|
|
@@ -13479,15 +13528,25 @@ function startSessionEventForwarder(client4, options) {
|
|
|
13479
13528
|
}
|
|
13480
13529
|
case "session.status": {
|
|
13481
13530
|
const props = event.properties;
|
|
13482
|
-
|
|
13531
|
+
const currentStatus = props?.status?.type;
|
|
13532
|
+
if (currentStatus !== lastStatus) {
|
|
13533
|
+
lastStatus = currentStatus;
|
|
13534
|
+
target.write(formatLogLine(options, "status", currentStatus) + `
|
|
13483
13535
|
`);
|
|
13536
|
+
}
|
|
13484
13537
|
break;
|
|
13485
13538
|
}
|
|
13486
13539
|
case "session.error": {
|
|
13487
13540
|
const props = event.properties;
|
|
13488
13541
|
const err = props?.error;
|
|
13489
13542
|
const errMsg = err?.name ? `${err.name}${err?.data?.message ? ": " + err.data.message : ""}` : String(err ?? "unknown");
|
|
13490
|
-
|
|
13543
|
+
let payload = errMsg;
|
|
13544
|
+
if (props?.tool) {
|
|
13545
|
+
payload += ` [tool: ${props.tool}]`;
|
|
13546
|
+
} else if (props?.command) {
|
|
13547
|
+
payload += ` [command: ${props.command}]`;
|
|
13548
|
+
}
|
|
13549
|
+
errorTarget.write(formatLogLine(options, "error", payload) + `
|
|
13491
13550
|
`);
|
|
13492
13551
|
break;
|
|
13493
13552
|
}
|
|
@@ -13508,7 +13567,10 @@ function startSessionEventForwarder(client4, options) {
|
|
|
13508
13567
|
stop: () => {
|
|
13509
13568
|
cancelled = true;
|
|
13510
13569
|
},
|
|
13511
|
-
done: donePromise
|
|
13570
|
+
done: donePromise,
|
|
13571
|
+
onAgentEnd: (cb) => {
|
|
13572
|
+
agentEndCallbacks.push(cb);
|
|
13573
|
+
}
|
|
13512
13574
|
};
|
|
13513
13575
|
}
|
|
13514
13576
|
|
|
@@ -13629,7 +13691,12 @@ var init_opencode_sdk_agent_service = __esm(() => {
|
|
|
13629
13691
|
agent: selected.name,
|
|
13630
13692
|
...composedSystem ? { system: composedSystem } : {},
|
|
13631
13693
|
parts: [{ type: "text", text: prompt }],
|
|
13632
|
-
...modelParts ? { model: modelParts } : {}
|
|
13694
|
+
...modelParts ? { model: modelParts } : {},
|
|
13695
|
+
tools: {
|
|
13696
|
+
task: false,
|
|
13697
|
+
question: false,
|
|
13698
|
+
external_directory: false
|
|
13699
|
+
}
|
|
13633
13700
|
}
|
|
13634
13701
|
}), PROMPT_ASYNC_TIMEOUT_MS, "session.promptAsync");
|
|
13635
13702
|
} catch (err) {
|
|
@@ -13688,6 +13755,9 @@ var init_opencode_sdk_agent_service = __esm(() => {
|
|
|
13688
13755
|
},
|
|
13689
13756
|
onOutput: (cb) => {
|
|
13690
13757
|
outputCallbacks.push(cb);
|
|
13758
|
+
},
|
|
13759
|
+
onAgentEnd: (cb) => {
|
|
13760
|
+
forwarder?.onAgentEnd(cb);
|
|
13691
13761
|
}
|
|
13692
13762
|
};
|
|
13693
13763
|
}
|
|
@@ -56809,7 +56879,7 @@ var init_telegram = __esm(() => {
|
|
|
56809
56879
|
});
|
|
56810
56880
|
|
|
56811
56881
|
// ../../services/backend/config/reliability.ts
|
|
56812
|
-
var DAEMON_HEARTBEAT_INTERVAL_MS = 30000, AGENT_REQUEST_DEADLINE_MS = 120000;
|
|
56882
|
+
var DAEMON_HEARTBEAT_INTERVAL_MS = 30000, AGENT_REQUEST_DEADLINE_MS = 120000, OBSERVATION_TTL_MS = 60000, OBSERVED_SAFETY_POLL_MS = 30000;
|
|
56813
56883
|
|
|
56814
56884
|
// src/events/daemon/agent/on-request-start-agent.ts
|
|
56815
56885
|
async function onRequestStartAgent(ctx, event) {
|
|
@@ -56974,6 +57044,55 @@ var init_pid = __esm(() => {
|
|
|
56974
57044
|
CHATROOM_DIR4 = join8(homedir5(), ".chatroom");
|
|
56975
57045
|
});
|
|
56976
57046
|
|
|
57047
|
+
// src/infrastructure/git/git-state-pipeline.ts
|
|
57048
|
+
import { createHash as createHash3 } from "node:crypto";
|
|
57049
|
+
|
|
57050
|
+
class GitStatePipeline {
|
|
57051
|
+
fields;
|
|
57052
|
+
constructor(fields) {
|
|
57053
|
+
this.fields = fields;
|
|
57054
|
+
}
|
|
57055
|
+
async collect(workingDir, preCollected) {
|
|
57056
|
+
const results = new Map(preCollected);
|
|
57057
|
+
if (this.fields.length === 0)
|
|
57058
|
+
return results;
|
|
57059
|
+
const entries = await Promise.all(this.fields.filter((f) => !results.has(f.key)).map(async (field) => {
|
|
57060
|
+
try {
|
|
57061
|
+
const raw = await field.collect(workingDir);
|
|
57062
|
+
return { key: field.key, raw };
|
|
57063
|
+
} catch {
|
|
57064
|
+
return { key: field.key, raw: field.defaultValue };
|
|
57065
|
+
}
|
|
57066
|
+
}));
|
|
57067
|
+
for (const { key, raw } of entries) {
|
|
57068
|
+
results.set(key, raw);
|
|
57069
|
+
}
|
|
57070
|
+
return results;
|
|
57071
|
+
}
|
|
57072
|
+
computeHash(values, slim) {
|
|
57073
|
+
const hashInput = {};
|
|
57074
|
+
for (const field of this.fields) {
|
|
57075
|
+
if (slim && !field.includeInSlim)
|
|
57076
|
+
continue;
|
|
57077
|
+
const raw = values.get(field.key) ?? field.defaultValue;
|
|
57078
|
+
hashInput[field.key] = field.toHashable(raw);
|
|
57079
|
+
}
|
|
57080
|
+
return createHash3("md5").update(JSON.stringify(hashInput)).digest("hex");
|
|
57081
|
+
}
|
|
57082
|
+
toMutationArgs(values, slim) {
|
|
57083
|
+
const args = {};
|
|
57084
|
+
for (const field of this.fields) {
|
|
57085
|
+
if (slim && !field.includeInSlim)
|
|
57086
|
+
continue;
|
|
57087
|
+
const raw = values.get(field.key) ?? field.defaultValue;
|
|
57088
|
+
Object.assign(args, field.toMutationPartial(raw));
|
|
57089
|
+
}
|
|
57090
|
+
args.pipelineMode = slim ? "slim" : "full";
|
|
57091
|
+
return args;
|
|
57092
|
+
}
|
|
57093
|
+
}
|
|
57094
|
+
var init_git_state_pipeline = () => {};
|
|
57095
|
+
|
|
56977
57096
|
// src/commands/machine/daemon-start/utils.ts
|
|
56978
57097
|
function formatTimestamp() {
|
|
56979
57098
|
return new Date().toISOString().replace("T", " ").substring(0, 19);
|
|
@@ -57613,6 +57732,28 @@ async function processMoreCommits(ctx, req) {
|
|
|
57613
57732
|
});
|
|
57614
57733
|
console.log(`[${formatTimestamp()}] \uD83D\uDCDC More commits appended: ${req.workingDir} (+${commits.length} commits, offset=${offset})`);
|
|
57615
57734
|
}
|
|
57735
|
+
async function processAllPullRequests(ctx, req) {
|
|
57736
|
+
const pullRequests = await getAllPRs(req.workingDir);
|
|
57737
|
+
await ctx.deps.backend.mutation(api.workspaces.upsertAllPullRequests, {
|
|
57738
|
+
sessionId: ctx.sessionId,
|
|
57739
|
+
machineId: ctx.machineId,
|
|
57740
|
+
workingDir: req.workingDir,
|
|
57741
|
+
pullRequests
|
|
57742
|
+
});
|
|
57743
|
+
console.log(`[${formatTimestamp()}] \uD83D\uDCCB All pull requests pushed: ${req.workingDir} (${pullRequests.length} PRs)`);
|
|
57744
|
+
}
|
|
57745
|
+
async function processRecentCommits(ctx, req) {
|
|
57746
|
+
const commits = await getRecentCommits(req.workingDir, COMMITS_PER_PAGE, 0);
|
|
57747
|
+
const hasMoreCommits = commits.length >= COMMITS_PER_PAGE;
|
|
57748
|
+
await ctx.deps.backend.mutation(api.workspaces.upsertRecentCommits, {
|
|
57749
|
+
sessionId: ctx.sessionId,
|
|
57750
|
+
machineId: ctx.machineId,
|
|
57751
|
+
workingDir: req.workingDir,
|
|
57752
|
+
commits,
|
|
57753
|
+
hasMoreCommits
|
|
57754
|
+
});
|
|
57755
|
+
console.log(`[${formatTimestamp()}] \uD83D\uDCDC Recent commits pushed: ${req.workingDir} (${commits.length} commits)`);
|
|
57756
|
+
}
|
|
57616
57757
|
async function processRequests(ctx, requests, processedRequestIds, dedupTtlMs) {
|
|
57617
57758
|
const evictBefore = Date.now() - dedupTtlMs;
|
|
57618
57759
|
for (const [id, ts] of processedRequestIds) {
|
|
@@ -57649,6 +57790,12 @@ async function processRequests(ctx, requests, processedRequestIds, dedupTtlMs) {
|
|
|
57649
57790
|
case "pr_commits":
|
|
57650
57791
|
await processPRCommits(ctx, req);
|
|
57651
57792
|
break;
|
|
57793
|
+
case "all_pull_requests":
|
|
57794
|
+
await processAllPullRequests(ctx, req);
|
|
57795
|
+
break;
|
|
57796
|
+
case "recent_commits":
|
|
57797
|
+
await processRecentCommits(ctx, req);
|
|
57798
|
+
break;
|
|
57652
57799
|
}
|
|
57653
57800
|
await ctx.deps.backend.mutation(api.workspaces.updateRequestStatus, {
|
|
57654
57801
|
sessionId: ctx.sessionId,
|
|
@@ -57673,7 +57820,26 @@ var init_git_subscription = __esm(() => {
|
|
|
57673
57820
|
});
|
|
57674
57821
|
|
|
57675
57822
|
// src/commands/machine/daemon-start/git-heartbeat.ts
|
|
57676
|
-
|
|
57823
|
+
function makeBranchDependentFields(branch) {
|
|
57824
|
+
return [
|
|
57825
|
+
{
|
|
57826
|
+
key: "openPullRequests",
|
|
57827
|
+
includeInSlim: true,
|
|
57828
|
+
collect: (wd) => getOpenPRsForBranch(wd, branch),
|
|
57829
|
+
toHashable: (raw) => raw.map((pr) => pr.prNumber),
|
|
57830
|
+
toMutationPartial: (raw) => ({ openPullRequests: raw }),
|
|
57831
|
+
defaultValue: []
|
|
57832
|
+
},
|
|
57833
|
+
{
|
|
57834
|
+
key: "headCommitStatus",
|
|
57835
|
+
includeInSlim: true,
|
|
57836
|
+
collect: (wd) => getCommitStatusChecks(wd, branch),
|
|
57837
|
+
toHashable: (raw) => raw,
|
|
57838
|
+
toMutationPartial: (raw) => ({ headCommitStatus: raw }),
|
|
57839
|
+
defaultValue: null
|
|
57840
|
+
}
|
|
57841
|
+
];
|
|
57842
|
+
}
|
|
57677
57843
|
async function pushGitState(ctx) {
|
|
57678
57844
|
let workspaces;
|
|
57679
57845
|
try {
|
|
@@ -57700,8 +57866,8 @@ async function pushSingleWorkspaceGitState(ctx, workingDir) {
|
|
|
57700
57866
|
const stateKey = makeGitStateKey(ctx.machineId, workingDir);
|
|
57701
57867
|
const isRepo = await isGitRepo(workingDir);
|
|
57702
57868
|
if (!isRepo) {
|
|
57703
|
-
const
|
|
57704
|
-
if (ctx.lastPushedGitState.get(stateKey) ===
|
|
57869
|
+
const stateHash = "not_found";
|
|
57870
|
+
if (ctx.lastPushedGitState.get(stateKey) === stateHash)
|
|
57705
57871
|
return;
|
|
57706
57872
|
await ctx.deps.backend.mutation(api.workspaces.upsertWorkspaceGitState, {
|
|
57707
57873
|
sessionId: ctx.sessionId,
|
|
@@ -57709,20 +57875,13 @@ async function pushSingleWorkspaceGitState(ctx, workingDir) {
|
|
|
57709
57875
|
workingDir,
|
|
57710
57876
|
status: "not_found"
|
|
57711
57877
|
});
|
|
57712
|
-
ctx.lastPushedGitState.set(stateKey,
|
|
57878
|
+
ctx.lastPushedGitState.set(stateKey, stateHash);
|
|
57713
57879
|
return;
|
|
57714
57880
|
}
|
|
57715
|
-
const
|
|
57716
|
-
getBranch(workingDir),
|
|
57717
|
-
isDirty(workingDir),
|
|
57718
|
-
getDiffStat(workingDir),
|
|
57719
|
-
getRecentCommits(workingDir, COMMITS_PER_PAGE),
|
|
57720
|
-
getCommitsAhead(workingDir)
|
|
57721
|
-
]);
|
|
57722
|
-
const remotes = await getRemotes(workingDir);
|
|
57881
|
+
const branchResult = await getBranch(workingDir);
|
|
57723
57882
|
if (branchResult.status === "error") {
|
|
57724
|
-
const
|
|
57725
|
-
if (ctx.lastPushedGitState.get(stateKey) ===
|
|
57883
|
+
const stateHash = `error:${branchResult.message}`;
|
|
57884
|
+
if (ctx.lastPushedGitState.get(stateKey) === stateHash)
|
|
57726
57885
|
return;
|
|
57727
57886
|
await ctx.deps.backend.mutation(api.workspaces.upsertWorkspaceGitState, {
|
|
57728
57887
|
sessionId: ctx.sessionId,
|
|
@@ -57731,21 +57890,21 @@ async function pushSingleWorkspaceGitState(ctx, workingDir) {
|
|
|
57731
57890
|
status: "error",
|
|
57732
57891
|
errorMessage: branchResult.message
|
|
57733
57892
|
});
|
|
57734
|
-
ctx.lastPushedGitState.set(stateKey,
|
|
57893
|
+
ctx.lastPushedGitState.set(stateKey, stateHash);
|
|
57735
57894
|
return;
|
|
57736
57895
|
}
|
|
57737
57896
|
if (branchResult.status === "not_found") {
|
|
57738
57897
|
return;
|
|
57739
57898
|
}
|
|
57740
|
-
const openPRs = await getOpenPRsForBranch(workingDir, branchResult.branch);
|
|
57741
|
-
const allPRs = await getAllPRs(workingDir);
|
|
57742
|
-
const headCommitStatus = await getCommitStatusChecks(workingDir, branchResult.branch);
|
|
57743
57899
|
const branch = branchResult.branch;
|
|
57744
|
-
const
|
|
57745
|
-
const
|
|
57900
|
+
const allFields = [branchField, ...GIT_STATE_FIELDS, ...makeBranchDependentFields(branch)];
|
|
57901
|
+
const pipeline2 = new GitStatePipeline(allFields);
|
|
57902
|
+
const preCollected = new Map([["branch", branchResult]]);
|
|
57903
|
+
const values = await pipeline2.collect(workingDir, preCollected);
|
|
57904
|
+
const commits = values.get("commits");
|
|
57746
57905
|
const hasMoreCommits = commits.length >= COMMITS_PER_PAGE;
|
|
57747
|
-
const
|
|
57748
|
-
if (ctx.lastPushedGitState.get(stateKey) ===
|
|
57906
|
+
const hash = pipeline2.computeHash(values, false);
|
|
57907
|
+
if (ctx.lastPushedGitState.get(stateKey) === hash) {
|
|
57749
57908
|
return;
|
|
57750
57909
|
}
|
|
57751
57910
|
await ctx.deps.backend.mutation(api.workspaces.upsertWorkspaceGitState, {
|
|
@@ -57753,23 +57912,73 @@ async function pushSingleWorkspaceGitState(ctx, workingDir) {
|
|
|
57753
57912
|
machineId: ctx.machineId,
|
|
57754
57913
|
workingDir,
|
|
57755
57914
|
status: "available",
|
|
57756
|
-
|
|
57757
|
-
isDirty: isDirty2,
|
|
57758
|
-
diffStat,
|
|
57915
|
+
...pipeline2.toMutationArgs(values, false),
|
|
57759
57916
|
recentCommits: commits,
|
|
57760
|
-
hasMoreCommits
|
|
57761
|
-
openPullRequests: openPRs,
|
|
57762
|
-
allPullRequests: allPRs,
|
|
57763
|
-
remotes,
|
|
57764
|
-
commitsAhead,
|
|
57765
|
-
headCommitStatus
|
|
57917
|
+
hasMoreCommits
|
|
57766
57918
|
});
|
|
57767
|
-
ctx.lastPushedGitState.set(stateKey,
|
|
57768
|
-
console.log(`[${formatTimestamp()}] \uD83D\uDD00 Git state pushed: ${workingDir} (${branch}${
|
|
57919
|
+
ctx.lastPushedGitState.set(stateKey, hash);
|
|
57920
|
+
console.log(`[${formatTimestamp()}] \uD83D\uDD00 Git state pushed: ${workingDir} (${branch}${values.get("isDirty") ? ", dirty" : ", clean"})`);
|
|
57769
57921
|
prefetchMissingCommitDetails(ctx, workingDir, commits).catch((err) => {
|
|
57770
57922
|
console.warn(`[${formatTimestamp()}] ⚠️ Commit pre-fetch failed for ${workingDir}: ${getErrorMessage(err)}`);
|
|
57771
57923
|
});
|
|
57772
57924
|
}
|
|
57925
|
+
async function pushSingleWorkspaceGitSummaryForObserved(ctx, workingDir, reason = "safety-poll") {
|
|
57926
|
+
const stateKey = makeGitStateKey(ctx.machineId, workingDir);
|
|
57927
|
+
const isRepo = await isGitRepo(workingDir);
|
|
57928
|
+
if (!isRepo) {
|
|
57929
|
+
const stateHash = "not_found";
|
|
57930
|
+
if (reason !== "refresh" && ctx.lastPushedGitState.get(stateKey) === stateHash)
|
|
57931
|
+
return;
|
|
57932
|
+
await ctx.deps.backend.mutation(api.workspaces.upsertWorkspaceGitState, {
|
|
57933
|
+
sessionId: ctx.sessionId,
|
|
57934
|
+
machineId: ctx.machineId,
|
|
57935
|
+
workingDir,
|
|
57936
|
+
status: "not_found"
|
|
57937
|
+
});
|
|
57938
|
+
ctx.lastPushedGitState.set(stateKey, stateHash);
|
|
57939
|
+
return;
|
|
57940
|
+
}
|
|
57941
|
+
const branchResult = await getBranch(workingDir);
|
|
57942
|
+
if (branchResult.status === "error") {
|
|
57943
|
+
const stateHash = `error:${branchResult.message}`;
|
|
57944
|
+
if (reason !== "refresh" && ctx.lastPushedGitState.get(stateKey) === stateHash)
|
|
57945
|
+
return;
|
|
57946
|
+
await ctx.deps.backend.mutation(api.workspaces.upsertWorkspaceGitState, {
|
|
57947
|
+
sessionId: ctx.sessionId,
|
|
57948
|
+
machineId: ctx.machineId,
|
|
57949
|
+
workingDir,
|
|
57950
|
+
status: "error",
|
|
57951
|
+
errorMessage: branchResult.message
|
|
57952
|
+
});
|
|
57953
|
+
ctx.lastPushedGitState.set(stateKey, stateHash);
|
|
57954
|
+
return;
|
|
57955
|
+
}
|
|
57956
|
+
if (branchResult.status === "not_found") {
|
|
57957
|
+
return;
|
|
57958
|
+
}
|
|
57959
|
+
const branch = branchResult.branch;
|
|
57960
|
+
const slimFields = [
|
|
57961
|
+
branchField,
|
|
57962
|
+
...GIT_STATE_FIELDS.filter((f) => f.includeInSlim),
|
|
57963
|
+
...makeBranchDependentFields(branch)
|
|
57964
|
+
];
|
|
57965
|
+
const pipeline2 = new GitStatePipeline(slimFields);
|
|
57966
|
+
const preCollected = new Map([["branch", branchResult]]);
|
|
57967
|
+
const values = await pipeline2.collect(workingDir, preCollected);
|
|
57968
|
+
const hash = pipeline2.computeHash(values, true);
|
|
57969
|
+
if (reason !== "refresh" && ctx.lastPushedGitState.get(stateKey) === hash) {
|
|
57970
|
+
return;
|
|
57971
|
+
}
|
|
57972
|
+
await ctx.deps.backend.mutation(api.workspaces.upsertWorkspaceGitState, {
|
|
57973
|
+
sessionId: ctx.sessionId,
|
|
57974
|
+
machineId: ctx.machineId,
|
|
57975
|
+
workingDir,
|
|
57976
|
+
status: "available",
|
|
57977
|
+
...pipeline2.toMutationArgs(values, true)
|
|
57978
|
+
});
|
|
57979
|
+
ctx.lastPushedGitState.set(stateKey, hash);
|
|
57980
|
+
console.log(`[${formatTimestamp()}] \uD83D\uDC41️ Observed git summary pushed: ${workingDir} (${branch}${values.get("isDirty") ? ", dirty" : ", clean"})${reason === "refresh" ? " [refresh]" : ""}`);
|
|
57981
|
+
}
|
|
57773
57982
|
async function prefetchMissingCommitDetails(ctx, workingDir, commits) {
|
|
57774
57983
|
if (commits.length === 0)
|
|
57775
57984
|
return;
|
|
@@ -57840,11 +58049,87 @@ async function prefetchSingleCommit(ctx, workingDir, sha, commits) {
|
|
|
57840
58049
|
});
|
|
57841
58050
|
console.log(`[${formatTimestamp()}] ✅ Pre-fetched: ${sha.slice(0, 7)} in ${workingDir}`);
|
|
57842
58051
|
}
|
|
58052
|
+
var branchField, GIT_STATE_FIELDS;
|
|
57843
58053
|
var init_git_heartbeat = __esm(() => {
|
|
58054
|
+
init_git_state_pipeline();
|
|
57844
58055
|
init_git_subscription();
|
|
57845
58056
|
init_api3();
|
|
57846
58057
|
init_git_reader();
|
|
57847
58058
|
init_convex_error();
|
|
58059
|
+
branchField = {
|
|
58060
|
+
key: "branch",
|
|
58061
|
+
includeInSlim: true,
|
|
58062
|
+
collect: () => {
|
|
58063
|
+
throw new Error("branch must be pre-collected");
|
|
58064
|
+
},
|
|
58065
|
+
toHashable: (raw) => {
|
|
58066
|
+
const r = raw;
|
|
58067
|
+
return r.status === "available" ? r.branch : "unknown";
|
|
58068
|
+
},
|
|
58069
|
+
toMutationPartial: (raw) => {
|
|
58070
|
+
const r = raw;
|
|
58071
|
+
return r.status === "available" ? { branch: r.branch } : {};
|
|
58072
|
+
},
|
|
58073
|
+
defaultValue: { status: "not_found" }
|
|
58074
|
+
};
|
|
58075
|
+
GIT_STATE_FIELDS = [
|
|
58076
|
+
{
|
|
58077
|
+
key: "isDirty",
|
|
58078
|
+
includeInSlim: true,
|
|
58079
|
+
collect: (wd) => isDirty(wd),
|
|
58080
|
+
toHashable: (raw) => raw,
|
|
58081
|
+
toMutationPartial: (raw) => ({ isDirty: raw }),
|
|
58082
|
+
defaultValue: false
|
|
58083
|
+
},
|
|
58084
|
+
{
|
|
58085
|
+
key: "diffStat",
|
|
58086
|
+
includeInSlim: false,
|
|
58087
|
+
collect: (wd) => getDiffStat(wd),
|
|
58088
|
+
toHashable: (raw) => {
|
|
58089
|
+
const r = raw;
|
|
58090
|
+
return r.status === "available" ? r.diffStat : { filesChanged: 0, insertions: 0, deletions: 0 };
|
|
58091
|
+
},
|
|
58092
|
+
toMutationPartial: (raw) => {
|
|
58093
|
+
const r = raw;
|
|
58094
|
+
return {
|
|
58095
|
+
diffStat: r.status === "available" ? r.diffStat : { filesChanged: 0, insertions: 0, deletions: 0 }
|
|
58096
|
+
};
|
|
58097
|
+
},
|
|
58098
|
+
defaultValue: { status: "not_found" }
|
|
58099
|
+
},
|
|
58100
|
+
{
|
|
58101
|
+
key: "commits",
|
|
58102
|
+
includeInSlim: false,
|
|
58103
|
+
collect: (wd) => getRecentCommits(wd, COMMITS_PER_PAGE),
|
|
58104
|
+
toHashable: (raw) => raw.map((c) => c.sha),
|
|
58105
|
+
toMutationPartial: () => ({}),
|
|
58106
|
+
defaultValue: []
|
|
58107
|
+
},
|
|
58108
|
+
{
|
|
58109
|
+
key: "commitsAhead",
|
|
58110
|
+
includeInSlim: false,
|
|
58111
|
+
collect: (wd) => getCommitsAhead(wd),
|
|
58112
|
+
toHashable: (raw) => raw,
|
|
58113
|
+
toMutationPartial: (raw) => ({ commitsAhead: raw }),
|
|
58114
|
+
defaultValue: 0
|
|
58115
|
+
},
|
|
58116
|
+
{
|
|
58117
|
+
key: "remotes",
|
|
58118
|
+
includeInSlim: false,
|
|
58119
|
+
collect: (wd) => getRemotes(wd),
|
|
58120
|
+
toHashable: (raw) => raw.map((r) => `${r.name}:${r.url}`),
|
|
58121
|
+
toMutationPartial: (raw) => ({ remotes: raw }),
|
|
58122
|
+
defaultValue: []
|
|
58123
|
+
},
|
|
58124
|
+
{
|
|
58125
|
+
key: "allPullRequests",
|
|
58126
|
+
includeInSlim: false,
|
|
58127
|
+
collect: (wd) => getAllPRs(wd),
|
|
58128
|
+
toHashable: (raw) => raw.map((pr) => `${pr.prNumber}:${pr.state}`),
|
|
58129
|
+
toMutationPartial: (raw) => ({ allPullRequests: raw }),
|
|
58130
|
+
defaultValue: []
|
|
58131
|
+
}
|
|
58132
|
+
];
|
|
57848
58133
|
});
|
|
57849
58134
|
|
|
57850
58135
|
// src/infrastructure/services/workspace/workspace-resolver.ts
|
|
@@ -58485,6 +58770,176 @@ var init_file_tree_subscription = __esm(() => {
|
|
|
58485
58770
|
init_convex_error();
|
|
58486
58771
|
});
|
|
58487
58772
|
|
|
58773
|
+
// src/commands/machine/daemon-start/observed-sync.ts
|
|
58774
|
+
function startObservedSyncSubscription(ctx, wsClient2) {
|
|
58775
|
+
console.log(`[${formatTimestamp()}] \uD83D\uDC41️ Starting observed-sync subscription (reactive)`);
|
|
58776
|
+
const observedWorkingDirs = new Map;
|
|
58777
|
+
const chatroomRefreshState = new Map;
|
|
58778
|
+
const skippedPushCount = new Map;
|
|
58779
|
+
const pendingRefresh = new Map;
|
|
58780
|
+
let stopped = false;
|
|
58781
|
+
let reconcileInFlight = false;
|
|
58782
|
+
const unsubscribe = wsClient2.onUpdate(api.machines.getObservedChatroomsForMachine, {
|
|
58783
|
+
sessionId: ctx.sessionId,
|
|
58784
|
+
machineId: ctx.machineId
|
|
58785
|
+
}, (observed) => {
|
|
58786
|
+
if (stopped)
|
|
58787
|
+
return;
|
|
58788
|
+
handleObservedChange(observed ?? []);
|
|
58789
|
+
}, (err) => {
|
|
58790
|
+
console.warn(`[${formatTimestamp()}] ⚠️ Observed-sync subscription error: ${getErrorMessage(err)}`);
|
|
58791
|
+
});
|
|
58792
|
+
const reconcileIntervalMs = Math.max(OBSERVATION_TTL_MS / 2, OBSERVED_SAFETY_POLL_MS);
|
|
58793
|
+
const reconcileTimer = setInterval(() => {
|
|
58794
|
+
if (stopped || reconcileInFlight)
|
|
58795
|
+
return;
|
|
58796
|
+
reconcileInFlight = true;
|
|
58797
|
+
ctx.deps.backend.query(api.machines.getObservedChatroomsForMachine, {
|
|
58798
|
+
sessionId: ctx.sessionId,
|
|
58799
|
+
machineId: ctx.machineId
|
|
58800
|
+
}).then((observed) => {
|
|
58801
|
+
if (!stopped)
|
|
58802
|
+
handleObservedChange(observed ?? []);
|
|
58803
|
+
}).catch((err) => {
|
|
58804
|
+
console.warn(`[${formatTimestamp()}] ⚠️ Observed-sync reconcile query failed: ${getErrorMessage(err)}`);
|
|
58805
|
+
}).finally(() => {
|
|
58806
|
+
reconcileInFlight = false;
|
|
58807
|
+
});
|
|
58808
|
+
}, reconcileIntervalMs);
|
|
58809
|
+
console.log(`[${formatTimestamp()}] \uD83D\uDC41️ Observed-sync subscription started`);
|
|
58810
|
+
return {
|
|
58811
|
+
stop: () => {
|
|
58812
|
+
stopped = true;
|
|
58813
|
+
unsubscribe();
|
|
58814
|
+
clearInterval(reconcileTimer);
|
|
58815
|
+
for (const [wd, state] of observedWorkingDirs) {
|
|
58816
|
+
clearInterval(state.intervalHandle);
|
|
58817
|
+
const skips = skippedPushCount.get(wd) ?? 0;
|
|
58818
|
+
if (skips > 0) {
|
|
58819
|
+
console.log(`[${formatTimestamp()}] \uD83D\uDC41️ Stopped observing ${wd} (skipped ${skips} overlapping pushes)`);
|
|
58820
|
+
} else {
|
|
58821
|
+
console.log(`[${formatTimestamp()}] \uD83D\uDC41️ Stopped observing ${wd}`);
|
|
58822
|
+
}
|
|
58823
|
+
}
|
|
58824
|
+
observedWorkingDirs.clear();
|
|
58825
|
+
skippedPushCount.clear();
|
|
58826
|
+
pendingRefresh.clear();
|
|
58827
|
+
console.log(`[${formatTimestamp()}] \uD83D\uDC41️ Observed-sync subscription stopped`);
|
|
58828
|
+
}
|
|
58829
|
+
};
|
|
58830
|
+
function handleObservedChange(observed) {
|
|
58831
|
+
const newWorkingDirs = new Set;
|
|
58832
|
+
const refreshedWorkingDirs = new Set;
|
|
58833
|
+
for (const chatroom of observed) {
|
|
58834
|
+
const chatroomId = chatroom.chatroomId;
|
|
58835
|
+
const currentRefresh = chatroom.lastRefreshedAt;
|
|
58836
|
+
const previous = chatroomRefreshState.get(chatroomId);
|
|
58837
|
+
const wasRefreshed = currentRefresh !== null && currentRefresh !== undefined && (previous === undefined || (previous.lastRefreshedAt ?? 0) < currentRefresh);
|
|
58838
|
+
if (wasRefreshed) {
|
|
58839
|
+
chatroomRefreshState.set(chatroomId, { lastRefreshedAt: currentRefresh });
|
|
58840
|
+
for (const wd of chatroom.workingDirs) {
|
|
58841
|
+
refreshedWorkingDirs.add(wd);
|
|
58842
|
+
}
|
|
58843
|
+
}
|
|
58844
|
+
for (const wd of chatroom.workingDirs) {
|
|
58845
|
+
newWorkingDirs.add(wd);
|
|
58846
|
+
}
|
|
58847
|
+
}
|
|
58848
|
+
for (const [chatroomId] of chatroomRefreshState) {
|
|
58849
|
+
const stillObserved = observed.some((c) => c.chatroomId === chatroomId);
|
|
58850
|
+
if (!stillObserved) {
|
|
58851
|
+
chatroomRefreshState.delete(chatroomId);
|
|
58852
|
+
}
|
|
58853
|
+
}
|
|
58854
|
+
const currentWorkingDirs = new Set(observedWorkingDirs.keys());
|
|
58855
|
+
let addedCount = 0;
|
|
58856
|
+
let removedCount = 0;
|
|
58857
|
+
for (const wd of currentWorkingDirs) {
|
|
58858
|
+
if (!newWorkingDirs.has(wd)) {
|
|
58859
|
+
const state = observedWorkingDirs.get(wd);
|
|
58860
|
+
if (state) {
|
|
58861
|
+
clearInterval(state.intervalHandle);
|
|
58862
|
+
observedWorkingDirs.delete(wd);
|
|
58863
|
+
const skips = skippedPushCount.get(wd) ?? 0;
|
|
58864
|
+
if (skips > 0) {
|
|
58865
|
+
console.log(`[${formatTimestamp()}] \uD83D\uDC41️ Stopped observing ${wd} (skipped ${skips} overlapping pushes)`);
|
|
58866
|
+
} else {
|
|
58867
|
+
console.log(`[${formatTimestamp()}] \uD83D\uDC41️ Stopped observing ${wd}`);
|
|
58868
|
+
}
|
|
58869
|
+
skippedPushCount.delete(wd);
|
|
58870
|
+
pendingRefresh.delete(wd);
|
|
58871
|
+
removedCount++;
|
|
58872
|
+
}
|
|
58873
|
+
}
|
|
58874
|
+
}
|
|
58875
|
+
for (const wd of newWorkingDirs) {
|
|
58876
|
+
if (!observedWorkingDirs.has(wd)) {
|
|
58877
|
+
observedWorkingDirs.set(wd, {
|
|
58878
|
+
intervalHandle: setInterval(() => {
|
|
58879
|
+
schedulePushForWorkingDir(wd, "safety-poll");
|
|
58880
|
+
}, OBSERVED_SAFETY_POLL_MS),
|
|
58881
|
+
pushInFlight: false
|
|
58882
|
+
});
|
|
58883
|
+
console.log(`[${formatTimestamp()}] \uD83D\uDC41️ Started observing ${wd}`);
|
|
58884
|
+
schedulePushForWorkingDir(wd, "safety-poll");
|
|
58885
|
+
addedCount++;
|
|
58886
|
+
}
|
|
58887
|
+
}
|
|
58888
|
+
if (addedCount > 0 || removedCount > 0) {
|
|
58889
|
+
console.log(`[${formatTimestamp()}] \uD83D\uDC41️ Observing ${observedWorkingDirs.size} working dir(s)`);
|
|
58890
|
+
}
|
|
58891
|
+
for (const wd of refreshedWorkingDirs) {
|
|
58892
|
+
if (observedWorkingDirs.has(wd)) {
|
|
58893
|
+
console.log(`[${formatTimestamp()}] \uD83D\uDD04 Refresh triggered for ${wd}`);
|
|
58894
|
+
schedulePushForWorkingDir(wd, "refresh");
|
|
58895
|
+
}
|
|
58896
|
+
}
|
|
58897
|
+
}
|
|
58898
|
+
function schedulePushForWorkingDir(workingDir, reason = "safety-poll") {
|
|
58899
|
+
if (stopped)
|
|
58900
|
+
return;
|
|
58901
|
+
const state = observedWorkingDirs.get(workingDir);
|
|
58902
|
+
if (!state)
|
|
58903
|
+
return;
|
|
58904
|
+
if (state.pushInFlight) {
|
|
58905
|
+
if (reason === "refresh") {
|
|
58906
|
+
pendingRefresh.set(workingDir, true);
|
|
58907
|
+
}
|
|
58908
|
+
const current = skippedPushCount.get(workingDir) ?? 0;
|
|
58909
|
+
skippedPushCount.set(workingDir, current + 1);
|
|
58910
|
+
console.log(`[${formatTimestamp()}] \uD83D\uDC41️ Skipping observed push for ${workingDir} (${reason}, in flight)`);
|
|
58911
|
+
return;
|
|
58912
|
+
}
|
|
58913
|
+
state.pushInFlight = true;
|
|
58914
|
+
pushForWorkingDir(workingDir, reason).catch((err) => {
|
|
58915
|
+
console.warn(`[${formatTimestamp()}] ⚠️ Push failed for ${workingDir}: ${getErrorMessage(err)}`);
|
|
58916
|
+
}).finally(() => {
|
|
58917
|
+
const s = observedWorkingDirs.get(workingDir);
|
|
58918
|
+
if (s) {
|
|
58919
|
+
s.pushInFlight = false;
|
|
58920
|
+
if (pendingRefresh.get(workingDir)) {
|
|
58921
|
+
pendingRefresh.delete(workingDir);
|
|
58922
|
+
schedulePushForWorkingDir(workingDir, "refresh");
|
|
58923
|
+
}
|
|
58924
|
+
}
|
|
58925
|
+
});
|
|
58926
|
+
}
|
|
58927
|
+
async function pushForWorkingDir(workingDir, reason = "safety-poll") {
|
|
58928
|
+
await pushSingleWorkspaceGitSummaryForObserved(ctx, workingDir, reason).catch((err) => {
|
|
58929
|
+
console.warn(`[${formatTimestamp()}] ⚠️ Observed git summary push failed for ${workingDir}: ${getErrorMessage(err)}`);
|
|
58930
|
+
});
|
|
58931
|
+
await pushSingleWorkspaceCommands(ctx, workingDir).catch((err) => {
|
|
58932
|
+
console.warn(`[${formatTimestamp()}] ⚠️ Command sync failed for ${workingDir}: ${getErrorMessage(err)}`);
|
|
58933
|
+
});
|
|
58934
|
+
}
|
|
58935
|
+
}
|
|
58936
|
+
var init_observed_sync = __esm(() => {
|
|
58937
|
+
init_api3();
|
|
58938
|
+
init_git_heartbeat();
|
|
58939
|
+
init_command_sync_heartbeat();
|
|
58940
|
+
init_convex_error();
|
|
58941
|
+
});
|
|
58942
|
+
|
|
58488
58943
|
// src/commands/machine/daemon-start/handlers/ping.ts
|
|
58489
58944
|
function handlePing() {
|
|
58490
58945
|
console.log(` ↪ Responding: pong`);
|
|
@@ -58494,6 +58949,13 @@ function handlePing() {
|
|
|
58494
58949
|
// src/commands/machine/daemon-start/handlers/command-runner.ts
|
|
58495
58950
|
import { spawn as spawn3 } from "node:child_process";
|
|
58496
58951
|
import { access as access3 } from "node:fs/promises";
|
|
58952
|
+
function evictStalePendingStops() {
|
|
58953
|
+
const evictBefore = Date.now() - PENDING_STOP_TTL_MS;
|
|
58954
|
+
for (const [runId, ts] of pendingStops) {
|
|
58955
|
+
if (ts < evictBefore)
|
|
58956
|
+
pendingStops.delete(runId);
|
|
58957
|
+
}
|
|
58958
|
+
}
|
|
58497
58959
|
async function reportRunFailed(ctx, runId, reason) {
|
|
58498
58960
|
try {
|
|
58499
58961
|
await ctx.deps.backend.mutation(api.commands.updateRunStatus, {
|
|
@@ -58538,6 +59000,21 @@ async function onCommandRun(ctx, event) {
|
|
|
58538
59000
|
console.log(`[${formatTimestamp()}] ⚠️ Command already running: ${runIdStr}`);
|
|
58539
59001
|
return;
|
|
58540
59002
|
}
|
|
59003
|
+
if (pendingStops.has(runIdStr)) {
|
|
59004
|
+
pendingStops.delete(runIdStr);
|
|
59005
|
+
console.log(`[${formatTimestamp()}] ⏭️ Skipping command run due to pending stop: ${commandName} (${runIdStr})`);
|
|
59006
|
+
try {
|
|
59007
|
+
await ctx.deps.backend.mutation(api.commands.updateRunStatus, {
|
|
59008
|
+
sessionId: ctx.sessionId,
|
|
59009
|
+
machineId: ctx.machineId,
|
|
59010
|
+
runId,
|
|
59011
|
+
status: "stopped"
|
|
59012
|
+
});
|
|
59013
|
+
} catch (err) {
|
|
59014
|
+
console.warn(`[${formatTimestamp()}] ⚠️ Failed to update status to stopped for pending-stop skip: ${getErrorMessage(err)}`);
|
|
59015
|
+
}
|
|
59016
|
+
return;
|
|
59017
|
+
}
|
|
58541
59018
|
console.log(`[${formatTimestamp()}] \uD83D\uDE80 Running command: ${commandName} → ${script}`);
|
|
58542
59019
|
if (!workingDir.startsWith("/")) {
|
|
58543
59020
|
console.error(`[${formatTimestamp()}] ❌ Rejected command: workingDir is not absolute: ${workingDir}`);
|
|
@@ -58557,6 +59034,34 @@ async function onCommandRun(ctx, event) {
|
|
|
58557
59034
|
stdio: ["ignore", "pipe", "pipe"],
|
|
58558
59035
|
detached: true
|
|
58559
59036
|
});
|
|
59037
|
+
const timeoutTimer = setTimeout(() => {
|
|
59038
|
+
console.log(`[${formatTimestamp()}] ⏰ Command timed out after ${DEFAULT_COMMAND_TIMEOUT_MS / 60000} minutes: ${commandName} (runId: ${runIdStr})`);
|
|
59039
|
+
const pid = child.pid;
|
|
59040
|
+
if (pid) {
|
|
59041
|
+
try {
|
|
59042
|
+
process.kill(-pid, "SIGTERM");
|
|
59043
|
+
} catch {
|
|
59044
|
+
child.kill("SIGTERM");
|
|
59045
|
+
}
|
|
59046
|
+
} else {
|
|
59047
|
+
child.kill("SIGTERM");
|
|
59048
|
+
}
|
|
59049
|
+
setTimeout(() => {
|
|
59050
|
+
if (!runningProcesses.has(runIdStr))
|
|
59051
|
+
return;
|
|
59052
|
+
console.log(`[${formatTimestamp()}] \uD83D\uDD2A Force-killing timed-out process: ${runIdStr}`);
|
|
59053
|
+
if (pid) {
|
|
59054
|
+
try {
|
|
59055
|
+
process.kill(-pid, "SIGKILL");
|
|
59056
|
+
} catch {
|
|
59057
|
+
child.kill("SIGKILL");
|
|
59058
|
+
}
|
|
59059
|
+
} else {
|
|
59060
|
+
child.kill("SIGKILL");
|
|
59061
|
+
}
|
|
59062
|
+
}, FORCE_KILL_DELAY_MS);
|
|
59063
|
+
}, DEFAULT_COMMAND_TIMEOUT_MS);
|
|
59064
|
+
timeoutTimer.unref?.();
|
|
58560
59065
|
const tracked = {
|
|
58561
59066
|
process: child,
|
|
58562
59067
|
runId: runIdStr,
|
|
@@ -58564,7 +59069,8 @@ async function onCommandRun(ctx, event) {
|
|
|
58564
59069
|
chunkIndex: 0,
|
|
58565
59070
|
flushTimer: setInterval(() => {
|
|
58566
59071
|
flushOutput(ctx, tracked).catch(() => {});
|
|
58567
|
-
}, OUTPUT_FLUSH_INTERVAL_MS)
|
|
59072
|
+
}, OUTPUT_FLUSH_INTERVAL_MS),
|
|
59073
|
+
timeoutTimer
|
|
58568
59074
|
};
|
|
58569
59075
|
tracked.flushTimer.unref?.();
|
|
58570
59076
|
runningProcesses.set(runIdStr, tracked);
|
|
@@ -58589,6 +59095,8 @@ async function onCommandRun(ctx, event) {
|
|
|
58589
59095
|
console.log(`[${formatTimestamp()}] \uD83C\uDFC1 Command exited: ${commandName} (code=${code2}, signal=${signal})`);
|
|
58590
59096
|
await flushOutput(ctx, tracked).catch(() => {});
|
|
58591
59097
|
clearInterval(tracked.flushTimer);
|
|
59098
|
+
if (tracked.timeoutTimer)
|
|
59099
|
+
clearTimeout(tracked.timeoutTimer);
|
|
58592
59100
|
runningProcesses.delete(runIdStr);
|
|
58593
59101
|
const status = code2 === 0 ? "completed" : signal ? "stopped" : "failed";
|
|
58594
59102
|
try {
|
|
@@ -58606,6 +59114,8 @@ async function onCommandRun(ctx, event) {
|
|
|
58606
59114
|
child.on("error", async (err) => {
|
|
58607
59115
|
console.error(`[${formatTimestamp()}] ❌ Command spawn failed: ${commandName}: ${err.message}`);
|
|
58608
59116
|
clearInterval(tracked.flushTimer);
|
|
59117
|
+
if (tracked.timeoutTimer)
|
|
59118
|
+
clearTimeout(tracked.timeoutTimer);
|
|
58609
59119
|
runningProcesses.delete(runIdStr);
|
|
58610
59120
|
try {
|
|
58611
59121
|
await ctx.deps.backend.mutation(api.commands.updateRunStatus, {
|
|
@@ -58624,6 +59134,8 @@ async function onCommandStop(ctx, event) {
|
|
|
58624
59134
|
const tracked = runningProcesses.get(runIdStr);
|
|
58625
59135
|
if (!tracked) {
|
|
58626
59136
|
console.log(`[${formatTimestamp()}] ⚠️ No running process found for run: ${runIdStr}`);
|
|
59137
|
+
pendingStops.set(runIdStr, Date.now());
|
|
59138
|
+
console.log(`[${formatTimestamp()}] \uD83D\uDCDD Registered pending stop for run: ${runIdStr}`);
|
|
58627
59139
|
try {
|
|
58628
59140
|
await ctx.deps.backend.mutation(api.commands.updateRunStatus, {
|
|
58629
59141
|
sessionId: ctx.sessionId,
|
|
@@ -58633,11 +59145,16 @@ async function onCommandStop(ctx, event) {
|
|
|
58633
59145
|
});
|
|
58634
59146
|
console.log(`[${formatTimestamp()}] \uD83D\uDCDD Marked orphaned run as stopped: ${runIdStr}`);
|
|
58635
59147
|
} catch (err) {
|
|
58636
|
-
console.warn(`[${formatTimestamp()}] ⚠️ Failed to mark orphaned run as stopped: ${getErrorMessage(err)}`);
|
|
59148
|
+
console.warn(`[${formatTimestamp()}] ⚠️ Failed to mark orphaned run as stopped (will retry): ${getErrorMessage(err)}`);
|
|
59149
|
+
throw err;
|
|
58637
59150
|
}
|
|
58638
59151
|
return;
|
|
58639
59152
|
}
|
|
58640
59153
|
console.log(`[${formatTimestamp()}] \uD83D\uDED1 Stopping command run: ${runIdStr}`);
|
|
59154
|
+
if (tracked.timeoutTimer) {
|
|
59155
|
+
clearTimeout(tracked.timeoutTimer);
|
|
59156
|
+
tracked.timeoutTimer = null;
|
|
59157
|
+
}
|
|
58641
59158
|
const pid = tracked.process.pid;
|
|
58642
59159
|
if (pid) {
|
|
58643
59160
|
try {
|
|
@@ -58649,7 +59166,7 @@ async function onCommandStop(ctx, event) {
|
|
|
58649
59166
|
tracked.process.kill("SIGTERM");
|
|
58650
59167
|
}
|
|
58651
59168
|
setTimeout(() => {
|
|
58652
|
-
if (
|
|
59169
|
+
if (!runningProcesses.has(runIdStr))
|
|
58653
59170
|
return;
|
|
58654
59171
|
console.log(`[${formatTimestamp()}] \uD83D\uDD2A Force-killing process: ${runIdStr}`);
|
|
58655
59172
|
if (pid) {
|
|
@@ -58661,14 +59178,55 @@ async function onCommandStop(ctx, event) {
|
|
|
58661
59178
|
} else {
|
|
58662
59179
|
tracked.process.kill("SIGKILL");
|
|
58663
59180
|
}
|
|
58664
|
-
},
|
|
59181
|
+
}, FORCE_KILL_DELAY_MS);
|
|
59182
|
+
}
|
|
59183
|
+
async function shutdownAllCommands(ctx) {
|
|
59184
|
+
if (runningProcesses.size === 0)
|
|
59185
|
+
return;
|
|
59186
|
+
console.log(`[${formatTimestamp()}] Shutting down ${runningProcesses.size} running command(s)...`);
|
|
59187
|
+
for (const [, tracked] of runningProcesses) {
|
|
59188
|
+
clearInterval(tracked.flushTimer);
|
|
59189
|
+
if (tracked.timeoutTimer)
|
|
59190
|
+
clearTimeout(tracked.timeoutTimer);
|
|
59191
|
+
await flushOutput(ctx, tracked).catch(() => {});
|
|
59192
|
+
const pid = tracked.process.pid;
|
|
59193
|
+
if (pid) {
|
|
59194
|
+
try {
|
|
59195
|
+
process.kill(-pid, "SIGTERM");
|
|
59196
|
+
} catch {
|
|
59197
|
+
tracked.process.kill("SIGTERM");
|
|
59198
|
+
}
|
|
59199
|
+
} else {
|
|
59200
|
+
tracked.process.kill("SIGTERM");
|
|
59201
|
+
}
|
|
59202
|
+
}
|
|
59203
|
+
await new Promise((resolve5) => {
|
|
59204
|
+
const t = setTimeout(resolve5, 3000);
|
|
59205
|
+
t.unref?.();
|
|
59206
|
+
});
|
|
59207
|
+
for (const [, tracked] of runningProcesses) {
|
|
59208
|
+
const pid = tracked.process.pid;
|
|
59209
|
+
if (pid) {
|
|
59210
|
+
try {
|
|
59211
|
+
process.kill(-pid, "SIGKILL");
|
|
59212
|
+
} catch {}
|
|
59213
|
+
} else {
|
|
59214
|
+
try {
|
|
59215
|
+
tracked.process.kill("SIGKILL");
|
|
59216
|
+
} catch {}
|
|
59217
|
+
}
|
|
59218
|
+
}
|
|
59219
|
+
runningProcesses.clear();
|
|
59220
|
+
console.log(`[${formatTimestamp()}] All commands stopped`);
|
|
58665
59221
|
}
|
|
58666
|
-
var runningProcesses, OUTPUT_FLUSH_INTERVAL_MS = 3000, MAX_BUFFER_SIZE;
|
|
59222
|
+
var runningProcesses, pendingStops, PENDING_STOP_TTL_MS = 60000, OUTPUT_FLUSH_INTERVAL_MS = 3000, MAX_BUFFER_SIZE, DEFAULT_COMMAND_TIMEOUT_MS, FORCE_KILL_DELAY_MS = 5000;
|
|
58667
59223
|
var init_command_runner = __esm(() => {
|
|
58668
59224
|
init_api3();
|
|
58669
59225
|
init_convex_error();
|
|
58670
59226
|
runningProcesses = new Map;
|
|
59227
|
+
pendingStops = new Map;
|
|
58671
59228
|
MAX_BUFFER_SIZE = 100 * 1024;
|
|
59229
|
+
DEFAULT_COMMAND_TIMEOUT_MS = 30 * 60 * 1000;
|
|
58672
59230
|
});
|
|
58673
59231
|
|
|
58674
59232
|
// src/commands/machine/daemon-start/handlers/state-recovery.ts
|
|
@@ -58712,6 +59270,14 @@ var init_state_recovery = __esm(() => {
|
|
|
58712
59270
|
init_api3();
|
|
58713
59271
|
});
|
|
58714
59272
|
|
|
59273
|
+
// src/commands/machine/daemon-start/capabilities-snapshot.ts
|
|
59274
|
+
function harnessCapabilitiesFingerprint(harnesses, versions) {
|
|
59275
|
+
const h = [...harnesses].sort().join("\x01");
|
|
59276
|
+
const keys = Object.keys(versions).sort();
|
|
59277
|
+
const v3 = keys.map((k) => `${k}:${JSON.stringify(versions[k] ?? null)}`).join("\x02");
|
|
59278
|
+
return `${h}::${v3}`;
|
|
59279
|
+
}
|
|
59280
|
+
|
|
58715
59281
|
// src/infrastructure/local-api/routes/identity.ts
|
|
58716
59282
|
async function handleIdentity(_req, ctx) {
|
|
58717
59283
|
const identity = {
|
|
@@ -59727,6 +60293,15 @@ var init_agent_process_manager = __esm(() => {
|
|
|
59727
60293
|
init_api3();
|
|
59728
60294
|
});
|
|
59729
60295
|
|
|
60296
|
+
// ../../services/backend/config/featureFlags.ts
|
|
60297
|
+
var featureFlags;
|
|
60298
|
+
var init_featureFlags = __esm(() => {
|
|
60299
|
+
featureFlags = {
|
|
60300
|
+
observedSyncEnabled: false,
|
|
60301
|
+
disableLogin: false
|
|
60302
|
+
};
|
|
60303
|
+
});
|
|
60304
|
+
|
|
59730
60305
|
// src/commands/machine/daemon-start/init.ts
|
|
59731
60306
|
import { stat as stat2 } from "node:fs/promises";
|
|
59732
60307
|
async function discoverModels(agentServices) {
|
|
@@ -59900,6 +60475,17 @@ async function recoverState(ctx) {
|
|
|
59900
60475
|
} catch (e) {
|
|
59901
60476
|
console.log(` ⚠️ Failed to clear stale PIDs: ${getErrorMessage(e)}`);
|
|
59902
60477
|
}
|
|
60478
|
+
try {
|
|
60479
|
+
const runResult = await ctx.deps.backend.mutation(api.commands.clearStaleCommandRuns, {
|
|
60480
|
+
sessionId: ctx.sessionId,
|
|
60481
|
+
machineId: ctx.machineId
|
|
60482
|
+
});
|
|
60483
|
+
if (runResult.clearedCount > 0) {
|
|
60484
|
+
console.log(` \uD83E\uDDF9 Cleared ${runResult.clearedCount} stale command run(s) from backend`);
|
|
60485
|
+
}
|
|
60486
|
+
} catch (e) {
|
|
60487
|
+
console.log(` ⚠️ Failed to clear stale command runs: ${getErrorMessage(e)}`);
|
|
60488
|
+
}
|
|
59903
60489
|
}
|
|
59904
60490
|
async function initDaemon() {
|
|
59905
60491
|
if (!acquireLock()) {
|
|
@@ -59949,7 +60535,9 @@ async function initDaemon() {
|
|
|
59949
60535
|
events,
|
|
59950
60536
|
agentServices,
|
|
59951
60537
|
lastPushedGitState: new Map,
|
|
59952
|
-
lastPushedModels: availableModels
|
|
60538
|
+
lastPushedModels: availableModels,
|
|
60539
|
+
lastPushedHarnessFingerprint: harnessCapabilitiesFingerprint(config3.availableHarnesses, config3.harnessVersions),
|
|
60540
|
+
observedSyncEnabled: featureFlags.observedSyncEnabled ?? false
|
|
59953
60541
|
};
|
|
59954
60542
|
registerEventListeners(ctx);
|
|
59955
60543
|
logStartup(ctx, availableModels);
|
|
@@ -59991,11 +60579,13 @@ var init_init2 = __esm(() => {
|
|
|
59991
60579
|
init_convex_error();
|
|
59992
60580
|
init_version();
|
|
59993
60581
|
init_pid();
|
|
60582
|
+
init_featureFlags();
|
|
59994
60583
|
AUTH_WAIT_TIMEOUT_MS = 5 * 60 * 1000;
|
|
59995
60584
|
});
|
|
59996
60585
|
|
|
59997
60586
|
// src/events/lifecycle/on-daemon-shutdown.ts
|
|
59998
60587
|
async function onDaemonShutdown(ctx) {
|
|
60588
|
+
await shutdownAllCommands(ctx);
|
|
59999
60589
|
const activeAgents = ctx.deps.agentProcessManager.listActive();
|
|
60000
60590
|
if (activeAgents.length > 0) {
|
|
60001
60591
|
console.log(`[${formatTimestamp()}] Stopping ${activeAgents.length} agent(s)...`);
|
|
@@ -60024,6 +60614,7 @@ async function onDaemonShutdown(ctx) {
|
|
|
60024
60614
|
}
|
|
60025
60615
|
var init_on_daemon_shutdown = __esm(() => {
|
|
60026
60616
|
init_api3();
|
|
60617
|
+
init_command_runner();
|
|
60027
60618
|
});
|
|
60028
60619
|
|
|
60029
60620
|
// src/infrastructure/git/git-writer.ts
|
|
@@ -60215,15 +60806,18 @@ function formatModelMap(map) {
|
|
|
60215
60806
|
return Object.entries(map).map(([harness, models]) => `${harness}: ${models.join(", ")}`).join("; ");
|
|
60216
60807
|
}
|
|
60217
60808
|
async function refreshModels(ctx) {
|
|
60218
|
-
if (!ctx.config)
|
|
60219
|
-
return;
|
|
60809
|
+
if (!ctx.config) {
|
|
60810
|
+
return { kind: "noop" };
|
|
60811
|
+
}
|
|
60220
60812
|
const models = await discoverModels(ctx.agentServices);
|
|
60221
60813
|
const freshConfig = ensureMachineRegistered();
|
|
60222
60814
|
ctx.config.availableHarnesses = freshConfig.availableHarnesses;
|
|
60223
60815
|
ctx.config.harnessVersions = freshConfig.harnessVersions;
|
|
60224
|
-
const
|
|
60225
|
-
|
|
60226
|
-
|
|
60816
|
+
const modelDiff = diffModels(ctx.lastPushedModels, models);
|
|
60817
|
+
const nextHarnessFingerprint = harnessCapabilitiesFingerprint(ctx.config.availableHarnesses, ctx.config.harnessVersions);
|
|
60818
|
+
const harnessFingerprintChanged = ctx.lastPushedHarnessFingerprint !== null && nextHarnessFingerprint !== ctx.lastPushedHarnessFingerprint;
|
|
60819
|
+
if (!modelDiff.hasChanges && !harnessFingerprintChanged) {
|
|
60820
|
+
return { kind: "skipped_no_changes" };
|
|
60227
60821
|
}
|
|
60228
60822
|
const totalCount = Object.values(models).flat().length;
|
|
60229
60823
|
try {
|
|
@@ -60235,15 +60829,19 @@ async function refreshModels(ctx) {
|
|
|
60235
60829
|
availableModels: models
|
|
60236
60830
|
});
|
|
60237
60831
|
ctx.lastPushedModels = models;
|
|
60238
|
-
|
|
60239
|
-
|
|
60832
|
+
ctx.lastPushedHarnessFingerprint = nextHarnessFingerprint;
|
|
60833
|
+
if (Object.keys(modelDiff.added).length > 0) {
|
|
60834
|
+
console.log(`[${formatTimestamp()}] ➕ New models detected — ${formatModelMap(modelDiff.added)}`);
|
|
60240
60835
|
}
|
|
60241
|
-
if (Object.keys(
|
|
60242
|
-
console.log(`[${formatTimestamp()}] ➖ Models no longer available — ${formatModelMap(
|
|
60836
|
+
if (Object.keys(modelDiff.removed).length > 0) {
|
|
60837
|
+
console.log(`[${formatTimestamp()}] ➖ Models no longer available — ${formatModelMap(modelDiff.removed)}`);
|
|
60243
60838
|
}
|
|
60244
60839
|
console.log(`[${formatTimestamp()}] \uD83D\uDD04 Model refresh pushed: ${totalCount > 0 ? `${totalCount} models` : "none discovered"}`);
|
|
60840
|
+
return { kind: "pushed" };
|
|
60245
60841
|
} catch (error) {
|
|
60246
|
-
|
|
60842
|
+
const message = getErrorMessage(error);
|
|
60843
|
+
console.warn(`[${formatTimestamp()}] ⚠️ Model refresh failed: ${message}`);
|
|
60844
|
+
return { kind: "failed", message };
|
|
60247
60845
|
}
|
|
60248
60846
|
}
|
|
60249
60847
|
function evictStaleDedupEntries(tracker) {
|
|
@@ -60260,6 +60858,10 @@ function evictStaleDedupEntries(tracker) {
|
|
|
60260
60858
|
if (ts2 < evictBefore)
|
|
60261
60859
|
tracker.gitRefreshIds.delete(id);
|
|
60262
60860
|
}
|
|
60861
|
+
for (const [id, ts2] of tracker.capabilitiesRefreshIds) {
|
|
60862
|
+
if (ts2 < evictBefore)
|
|
60863
|
+
tracker.capabilitiesRefreshIds.delete(id);
|
|
60864
|
+
}
|
|
60263
60865
|
for (const [id, ts2] of tracker.localActionIds) {
|
|
60264
60866
|
if (ts2 < evictBefore)
|
|
60265
60867
|
tracker.localActionIds.delete(id);
|
|
@@ -60272,6 +60874,7 @@ function evictStaleDedupEntries(tracker) {
|
|
|
60272
60874
|
if (ts2 < evictBefore)
|
|
60273
60875
|
tracker.commandStopIds.delete(id);
|
|
60274
60876
|
}
|
|
60877
|
+
evictStalePendingStops();
|
|
60275
60878
|
}
|
|
60276
60879
|
async function dispatchCommandEvent(ctx, event, tracker) {
|
|
60277
60880
|
const eventId = event._id.toString();
|
|
@@ -60279,40 +60882,40 @@ async function dispatchCommandEvent(ctx, event, tracker) {
|
|
|
60279
60882
|
if (event.type === "agent.requestStart") {
|
|
60280
60883
|
if (tracker.commandIds.has(eventId))
|
|
60281
60884
|
return;
|
|
60282
|
-
tracker.commandIds.set(eventId, Date.now());
|
|
60283
60885
|
await onRequestStartAgent(ctx, event);
|
|
60886
|
+
tracker.commandIds.set(eventId, Date.now());
|
|
60284
60887
|
} else if (event.type === "agent.requestStop") {
|
|
60285
60888
|
if (tracker.commandIds.has(eventId))
|
|
60286
60889
|
return;
|
|
60287
|
-
tracker.commandIds.set(eventId, Date.now());
|
|
60288
60890
|
await onRequestStopAgent(ctx, event);
|
|
60891
|
+
tracker.commandIds.set(eventId, Date.now());
|
|
60289
60892
|
} else if (event.type === "daemon.ping") {
|
|
60290
60893
|
if (tracker.pingIds.has(eventId))
|
|
60291
60894
|
return;
|
|
60292
|
-
tracker.pingIds.set(eventId, Date.now());
|
|
60293
60895
|
handlePing();
|
|
60294
60896
|
await ctx.deps.backend.mutation(api.machines.ackPing, {
|
|
60295
60897
|
sessionId: ctx.sessionId,
|
|
60296
60898
|
machineId: ctx.machineId,
|
|
60297
60899
|
pingEventId: event._id
|
|
60298
60900
|
});
|
|
60901
|
+
tracker.pingIds.set(eventId, Date.now());
|
|
60299
60902
|
} else if (event.type === "daemon.gitRefresh") {
|
|
60300
60903
|
if (tracker.gitRefreshIds.has(eventId))
|
|
60301
60904
|
return;
|
|
60302
|
-
tracker.gitRefreshIds.set(eventId, Date.now());
|
|
60303
60905
|
const stateKey = makeGitStateKey(ctx.machineId, event.workingDir);
|
|
60304
60906
|
ctx.lastPushedGitState.delete(stateKey);
|
|
60305
60907
|
console.log(`[${formatTimestamp()}] \uD83D\uDD04 Git refresh requested for ${event.workingDir}`);
|
|
60306
60908
|
await pushGitState(ctx);
|
|
60909
|
+
tracker.gitRefreshIds.set(eventId, Date.now());
|
|
60307
60910
|
} else if (event.type === "daemon.localAction") {
|
|
60308
60911
|
if (tracker.localActionIds.has(eventId))
|
|
60309
60912
|
return;
|
|
60310
|
-
tracker.localActionIds.set(eventId, Date.now());
|
|
60311
60913
|
console.log(`[${formatTimestamp()}] \uD83D\uDDA5️ Local action: ${event.action} → ${event.workingDir}`);
|
|
60312
60914
|
const result = await executeLocalAction(event.action, event.workingDir);
|
|
60313
60915
|
if (!result.success) {
|
|
60314
60916
|
console.warn(`[${formatTimestamp()}] ⚠️ Local action failed: ${result.error}`);
|
|
60315
60917
|
}
|
|
60918
|
+
tracker.localActionIds.set(eventId, Date.now());
|
|
60316
60919
|
} else if (eventType === "command.run") {
|
|
60317
60920
|
if (tracker.commandRunIds.has(eventId))
|
|
60318
60921
|
return;
|
|
@@ -60321,8 +60924,42 @@ async function dispatchCommandEvent(ctx, event, tracker) {
|
|
|
60321
60924
|
} else if (eventType === "command.stop") {
|
|
60322
60925
|
if (tracker.commandStopIds.has(eventId))
|
|
60323
60926
|
return;
|
|
60324
|
-
tracker.commandStopIds.set(eventId, Date.now());
|
|
60325
60927
|
await onCommandStop(ctx, event);
|
|
60928
|
+
tracker.commandStopIds.set(eventId, Date.now());
|
|
60929
|
+
} else if (event.type === "daemon.refreshCapabilities") {
|
|
60930
|
+
if (tracker.capabilitiesRefreshIds.has(eventId))
|
|
60931
|
+
return;
|
|
60932
|
+
console.log(`[${formatTimestamp()}] \uD83D\uDD04 Manual capabilities refresh requested`);
|
|
60933
|
+
const outcome = await refreshModels(ctx);
|
|
60934
|
+
tracker.capabilitiesRefreshIds.set(eventId, Date.now());
|
|
60935
|
+
const batchId = "batchId" in event && event.batchId !== undefined ? event.batchId : undefined;
|
|
60936
|
+
if (!batchId) {
|
|
60937
|
+
return;
|
|
60938
|
+
}
|
|
60939
|
+
let status;
|
|
60940
|
+
let errorMessage;
|
|
60941
|
+
if (outcome.kind === "pushed") {
|
|
60942
|
+
status = "completed";
|
|
60943
|
+
} else if (outcome.kind === "skipped_no_changes") {
|
|
60944
|
+
status = "skipped_no_changes";
|
|
60945
|
+
} else if (outcome.kind === "failed") {
|
|
60946
|
+
status = "failed";
|
|
60947
|
+
errorMessage = outcome.message;
|
|
60948
|
+
} else {
|
|
60949
|
+
status = "failed";
|
|
60950
|
+
errorMessage = "Daemon configuration unavailable";
|
|
60951
|
+
}
|
|
60952
|
+
try {
|
|
60953
|
+
await ctx.deps.backend.mutation(api.machines.reportCapabilitiesRefreshResult, {
|
|
60954
|
+
sessionId: ctx.sessionId,
|
|
60955
|
+
batchId,
|
|
60956
|
+
machineId: ctx.machineId,
|
|
60957
|
+
status,
|
|
60958
|
+
errorMessage
|
|
60959
|
+
});
|
|
60960
|
+
} catch (error) {
|
|
60961
|
+
console.warn(`[${formatTimestamp()}] ⚠️ Capabilities refresh report failed: ${getErrorMessage(error)}`);
|
|
60962
|
+
}
|
|
60326
60963
|
}
|
|
60327
60964
|
}
|
|
60328
60965
|
async function startCommandLoop(ctx) {
|
|
@@ -60334,12 +60971,14 @@ async function startCommandLoop(ctx) {
|
|
|
60334
60971
|
}).then(() => {
|
|
60335
60972
|
heartbeatCount++;
|
|
60336
60973
|
console.log(`[${formatTimestamp()}] \uD83D\uDC93 Daemon heartbeat #${heartbeatCount} OK`);
|
|
60337
|
-
|
|
60338
|
-
|
|
60339
|
-
|
|
60340
|
-
|
|
60341
|
-
|
|
60342
|
-
|
|
60974
|
+
if (!ctx.observedSyncEnabled) {
|
|
60975
|
+
pushGitState(ctx).catch((err) => {
|
|
60976
|
+
console.warn(`[${formatTimestamp()}] ⚠️ Git state push failed: ${getErrorMessage(err)}`);
|
|
60977
|
+
});
|
|
60978
|
+
pushCommands(ctx).catch((err) => {
|
|
60979
|
+
console.warn(`[${formatTimestamp()}] ⚠️ Command sync failed: ${getErrorMessage(err)}`);
|
|
60980
|
+
});
|
|
60981
|
+
}
|
|
60343
60982
|
}).catch((err) => {
|
|
60344
60983
|
console.warn(`[${formatTimestamp()}] ⚠️ Daemon heartbeat failed: ${getErrorMessage(err)}`);
|
|
60345
60984
|
});
|
|
@@ -60348,8 +60987,13 @@ async function startCommandLoop(ctx) {
|
|
|
60348
60987
|
let gitSubscriptionHandle = null;
|
|
60349
60988
|
let fileContentSubscriptionHandle = null;
|
|
60350
60989
|
let fileTreeSubscriptionHandle = null;
|
|
60351
|
-
|
|
60352
|
-
|
|
60990
|
+
let observedSyncSubscriptionHandle = null;
|
|
60991
|
+
if (ctx.observedSyncEnabled) {
|
|
60992
|
+
console.log(`[${formatTimestamp()}] \uD83D\uDC41️ Observed-sync enabled, skipping immediate push`);
|
|
60993
|
+
} else {
|
|
60994
|
+
pushGitState(ctx).catch(() => {});
|
|
60995
|
+
pushCommands(ctx).catch(() => {});
|
|
60996
|
+
}
|
|
60353
60997
|
const shutdown = async () => {
|
|
60354
60998
|
console.log(`
|
|
60355
60999
|
[${formatTimestamp()}] Shutting down...`);
|
|
@@ -60360,6 +61004,8 @@ async function startCommandLoop(ctx) {
|
|
|
60360
61004
|
fileContentSubscriptionHandle.stop();
|
|
60361
61005
|
if (fileTreeSubscriptionHandle)
|
|
60362
61006
|
fileTreeSubscriptionHandle.stop();
|
|
61007
|
+
if (observedSyncSubscriptionHandle)
|
|
61008
|
+
observedSyncSubscriptionHandle.stop();
|
|
60363
61009
|
await onDaemonShutdown(ctx);
|
|
60364
61010
|
if (ctx.stopLocalApi) {
|
|
60365
61011
|
await ctx.stopLocalApi().catch(() => {});
|
|
@@ -60374,6 +61020,9 @@ async function startCommandLoop(ctx) {
|
|
|
60374
61020
|
gitSubscriptionHandle = startGitRequestSubscription(ctx, wsClient2);
|
|
60375
61021
|
fileContentSubscriptionHandle = startFileContentSubscription(ctx, wsClient2);
|
|
60376
61022
|
fileTreeSubscriptionHandle = startFileTreeSubscription(ctx, wsClient2);
|
|
61023
|
+
if (ctx.observedSyncEnabled) {
|
|
61024
|
+
observedSyncSubscriptionHandle = startObservedSyncSubscription(ctx, wsClient2);
|
|
61025
|
+
}
|
|
60377
61026
|
console.log(`
|
|
60378
61027
|
Listening for commands...`);
|
|
60379
61028
|
console.log(`Press Ctrl+C to stop
|
|
@@ -60382,6 +61031,7 @@ Listening for commands...`);
|
|
|
60382
61031
|
commandIds: new Map,
|
|
60383
61032
|
pingIds: new Map,
|
|
60384
61033
|
gitRefreshIds: new Map,
|
|
61034
|
+
capabilitiesRefreshIds: new Map,
|
|
60385
61035
|
localActionIds: new Map,
|
|
60386
61036
|
commandRunIds: new Map,
|
|
60387
61037
|
commandStopIds: new Map
|
|
@@ -60402,15 +61052,8 @@ Listening for commands...`);
|
|
|
60402
61052
|
}
|
|
60403
61053
|
}
|
|
60404
61054
|
});
|
|
60405
|
-
const modelRefreshTimer = setInterval(() => {
|
|
60406
|
-
refreshModels(ctx).catch((err) => {
|
|
60407
|
-
console.warn(`[${formatTimestamp()}] ⚠️ Model refresh error: ${getErrorMessage(err)}`);
|
|
60408
|
-
});
|
|
60409
|
-
}, MODEL_REFRESH_INTERVAL_MS);
|
|
60410
|
-
modelRefreshTimer.unref();
|
|
60411
61055
|
return await new Promise(() => {});
|
|
60412
61056
|
}
|
|
60413
|
-
var MODEL_REFRESH_INTERVAL_MS;
|
|
60414
61057
|
var init_command_loop = __esm(() => {
|
|
60415
61058
|
init_on_request_start_agent();
|
|
60416
61059
|
init_on_request_stop_agent();
|
|
@@ -60420,6 +61063,7 @@ var init_command_loop = __esm(() => {
|
|
|
60420
61063
|
init_file_content_subscription();
|
|
60421
61064
|
init_file_tree_subscription();
|
|
60422
61065
|
init_git_subscription();
|
|
61066
|
+
init_observed_sync();
|
|
60423
61067
|
init_command_runner();
|
|
60424
61068
|
init_init2();
|
|
60425
61069
|
init_api3();
|
|
@@ -60428,7 +61072,6 @@ var init_command_loop = __esm(() => {
|
|
|
60428
61072
|
init_local_actions();
|
|
60429
61073
|
init_machine();
|
|
60430
61074
|
init_convex_error();
|
|
60431
|
-
MODEL_REFRESH_INTERVAL_MS = 10 * 1000;
|
|
60432
61075
|
});
|
|
60433
61076
|
|
|
60434
61077
|
// src/commands/machine/daemon-start/index.ts
|
|
@@ -61525,5 +62168,5 @@ program2.hook("preAction", async (_thisCommand, actionCommand) => {
|
|
|
61525
62168
|
});
|
|
61526
62169
|
program2.parse();
|
|
61527
62170
|
|
|
61528
|
-
//# debugId=
|
|
62171
|
+
//# debugId=1C43E3169710F16B64756E2164756E21
|
|
61529
62172
|
//# sourceMappingURL=index.js.map
|