adhdev 0.9.82-rc.3 → 0.9.82-rc.31

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/cli/index.js CHANGED
@@ -784,8 +784,14 @@ async function getGitRepoStatus(workspace, options = {}) {
784
784
  const includeSubmodules = options.includeSubmodules !== false;
785
785
  try {
786
786
  const repo = await resolveGitRepository(workspace, options);
787
- const statusOutput = await runGit(repo, ["status", "--porcelain=v2", "--branch"], options);
788
- const parsed = parsePorcelainV2Status(statusOutput.stdout);
787
+ let parsed = await readPorcelainStatus(repo, options);
788
+ let upstreamProbe = getInitialUpstreamProbe(parsed);
789
+ if (options.refreshUpstream) {
790
+ upstreamProbe = await refreshTrackedUpstream(repo, parsed, options);
791
+ if (upstreamProbe.upstreamStatus === "fresh") {
792
+ parsed = await readPorcelainStatus(repo, options);
793
+ }
794
+ }
789
795
  const head = await readHead(repo, options);
790
796
  const stashCount = await readStashCount(repo, options);
791
797
  let submodules;
@@ -800,6 +806,9 @@ async function getGitRepoStatus(workspace, options = {}) {
800
806
  headCommit: head.commit,
801
807
  headMessage: head.message,
802
808
  upstream: parsed.upstream,
809
+ upstreamStatus: parsed.upstream ? upstreamProbe.upstreamStatus : "no_upstream",
810
+ upstreamFetchedAt: upstreamProbe.upstreamFetchedAt,
811
+ upstreamFetchError: upstreamProbe.upstreamFetchError,
803
812
  ahead: parsed.ahead,
804
813
  behind: parsed.behind,
805
814
  staged: parsed.staged,
@@ -824,6 +833,60 @@ async function getGitRepoStatus(workspace, options = {}) {
824
833
  );
825
834
  }
826
835
  }
836
+ async function readPorcelainStatus(repo, options) {
837
+ const statusOutput = await runGit(repo, ["status", "--porcelain=v2", "--branch"], options);
838
+ return parsePorcelainV2Status(statusOutput.stdout);
839
+ }
840
+ function getInitialUpstreamProbe(parsed) {
841
+ return {
842
+ upstreamStatus: parsed.upstream ? "unchecked" : "no_upstream"
843
+ };
844
+ }
845
+ async function refreshTrackedUpstream(repo, parsed, options) {
846
+ if (!parsed.upstream || !parsed.branch) {
847
+ return { upstreamStatus: "no_upstream" };
848
+ }
849
+ const remoteName = await readBranchRemote(repo, parsed.branch, options) ?? inferRemoteName(parsed.upstream);
850
+ if (!remoteName) {
851
+ return {
852
+ upstreamStatus: "stale",
853
+ upstreamFetchError: `Unable to resolve remote for upstream '${parsed.upstream}'`
854
+ };
855
+ }
856
+ try {
857
+ await runGit(repo, ["fetch", "--quiet", "--prune", "--no-tags", remoteName], options);
858
+ return {
859
+ upstreamStatus: "fresh",
860
+ upstreamFetchedAt: Date.now()
861
+ };
862
+ } catch (error48) {
863
+ return {
864
+ upstreamStatus: "stale",
865
+ upstreamFetchError: formatGitError(error48)
866
+ };
867
+ }
868
+ }
869
+ async function readBranchRemote(repo, branch, options) {
870
+ try {
871
+ const result = await runGit(repo, ["config", "--get", `branch.${branch}.remote`], options);
872
+ return result.stdout.trim() || null;
873
+ } catch {
874
+ return null;
875
+ }
876
+ }
877
+ function inferRemoteName(upstream) {
878
+ const [remoteName] = upstream.split("/");
879
+ return remoteName?.trim() || null;
880
+ }
881
+ function formatGitError(error48) {
882
+ if (error48 instanceof GitCommandError) {
883
+ return error48.stderr || error48.message;
884
+ }
885
+ if (error48 instanceof Error) {
886
+ return error48.message;
887
+ }
888
+ return String(error48);
889
+ }
827
890
  function parsePorcelainV2Status(output) {
828
891
  const parsed = {
829
892
  branch: null,
@@ -918,6 +981,7 @@ function emptyStatus(workspace, lastCheckedAt, error48) {
918
981
  headCommit: null,
919
982
  headMessage: null,
920
983
  upstream: null,
984
+ upstreamStatus: "unavailable",
921
985
  ahead: 0,
922
986
  behind: 0,
923
987
  staged: 0,
@@ -1210,6 +1274,9 @@ function createGitCompactSummary(status, diffSummary) {
1210
1274
  isGitRepo: status.isGitRepo,
1211
1275
  repoRoot: status.repoRoot,
1212
1276
  branch: status.branch,
1277
+ upstreamStatus: status.upstreamStatus,
1278
+ upstreamFetchedAt: status.upstreamFetchedAt,
1279
+ upstreamFetchError: status.upstreamFetchError,
1213
1280
  dirty: status.staged > 0 || status.modified > 0 || status.untracked > 0 || status.deleted > 0 || status.renamed > 0 || conflictCount > 0 || changedFiles > 0,
1214
1281
  changedFiles,
1215
1282
  ahead: status.ahead,
@@ -1537,7 +1604,7 @@ function serviceNotImplemented(command) {
1537
1604
  }
1538
1605
  function createDefaultGitCommandServices() {
1539
1606
  return {
1540
- getStatus: ({ workspace }) => getGitRepoStatus(workspace),
1607
+ getStatus: ({ workspace, refreshUpstream }) => getGitRepoStatus(workspace, { refreshUpstream }),
1541
1608
  getDiffSummary: ({ workspace }) => getGitDiffSummary(workspace),
1542
1609
  getDiffFile: ({ workspace, path: filePath }) => getGitFileDiff(workspace, filePath),
1543
1610
  createSnapshot: ({ workspace, reason, sessionId, turnId }) => defaultSnapshotStore.create({
@@ -1622,7 +1689,7 @@ async function handleGitCommand(command, args, services = defaultGitCommandServi
1622
1689
  switch (command) {
1623
1690
  case "git_status": {
1624
1691
  if (!services.getStatus) return serviceNotImplemented(command);
1625
- const status = await runService(() => services.getStatus({ workspace }));
1692
+ const status = await runService(() => services.getStatus({ workspace, refreshUpstream: optionalBoolean(args?.refreshUpstream) }));
1626
1693
  return "success" in status ? status : { success: true, status };
1627
1694
  }
1628
1695
  case "git_diff_summary": {
@@ -3743,6 +3810,36 @@ function getQueuePath(meshId) {
3743
3810
  const safe = meshId.replace(/[^a-zA-Z0-9_-]/g, "_");
3744
3811
  return (0, import_path4.join)(getLedgerDir(), `${safe}.queue.json`);
3745
3812
  }
3813
+ function getLockPath(meshId) {
3814
+ const safe = meshId.replace(/[^a-zA-Z0-9_-]/g, "_");
3815
+ return (0, import_path4.join)(getLedgerDir(), `${safe}.queue.lock`);
3816
+ }
3817
+ function withQueueLock(meshId, fn) {
3818
+ const lockPath = getLockPath(meshId);
3819
+ let fd = -1;
3820
+ for (let i = 0; i < 10; i++) {
3821
+ try {
3822
+ fd = (0, import_fs4.openSync)(lockPath, "wx");
3823
+ break;
3824
+ } catch {
3825
+ const deadline = Date.now() + 30;
3826
+ while (Date.now() < deadline) {
3827
+ }
3828
+ }
3829
+ }
3830
+ try {
3831
+ return fn();
3832
+ } finally {
3833
+ if (fd !== -1) try {
3834
+ (0, import_fs4.closeSync)(fd);
3835
+ } catch {
3836
+ }
3837
+ try {
3838
+ (0, import_fs4.unlinkSync)(lockPath);
3839
+ } catch {
3840
+ }
3841
+ }
3842
+ }
3746
3843
  function readQueue(meshId) {
3747
3844
  const path42 = getQueuePath(meshId);
3748
3845
  if (!(0, import_fs4.existsSync)(path42)) return [];
@@ -3758,20 +3855,22 @@ function writeQueue(meshId, queue) {
3758
3855
  (0, import_fs4.writeFileSync)(path42, JSON.stringify(queue, null, 2), "utf-8");
3759
3856
  }
3760
3857
  function enqueueTask(meshId, message, opts) {
3761
- const queue = readQueue(meshId);
3762
- const entry = {
3763
- id: (0, import_crypto5.randomUUID)(),
3764
- meshId,
3765
- message,
3766
- status: "pending",
3767
- targetNodeId: opts?.targetNodeId,
3768
- targetSessionId: opts?.targetSessionId,
3769
- createdAt: (/* @__PURE__ */ new Date()).toISOString(),
3770
- updatedAt: (/* @__PURE__ */ new Date()).toISOString()
3771
- };
3772
- queue.push(entry);
3773
- writeQueue(meshId, queue);
3774
- return entry;
3858
+ return withQueueLock(meshId, () => {
3859
+ const queue = readQueue(meshId);
3860
+ const entry = {
3861
+ id: (0, import_crypto5.randomUUID)(),
3862
+ meshId,
3863
+ message,
3864
+ status: "pending",
3865
+ targetNodeId: opts?.targetNodeId,
3866
+ targetSessionId: opts?.targetSessionId,
3867
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
3868
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
3869
+ };
3870
+ queue.push(entry);
3871
+ writeQueue(meshId, queue);
3872
+ return entry;
3873
+ });
3775
3874
  }
3776
3875
  function getQueue(meshId, opts) {
3777
3876
  let queue = readQueue(meshId);
@@ -3782,100 +3881,111 @@ function getQueue(meshId, opts) {
3782
3881
  return queue;
3783
3882
  }
3784
3883
  function claimNextTask(meshId, nodeId, sessionId) {
3785
- const queue = readQueue(meshId);
3786
- const hasActiveAssignment = queue.some((q) => q.status === "assigned" && (q.assignedSessionId === sessionId || q.assignedNodeId === nodeId));
3787
- if (hasActiveAssignment) return null;
3788
- let targetIdx = queue.findIndex((q) => q.status === "pending" && q.targetSessionId === sessionId);
3789
- if (targetIdx === -1) {
3790
- targetIdx = queue.findIndex((q) => q.status === "pending" && q.targetNodeId === nodeId && !q.targetSessionId);
3791
- }
3792
- if (targetIdx === -1) {
3793
- targetIdx = queue.findIndex((q) => q.status === "pending" && !q.targetNodeId && !q.targetSessionId);
3794
- }
3795
- if (targetIdx === -1) return null;
3796
- const entry = queue[targetIdx];
3797
- entry.status = "assigned";
3798
- entry.assignedNodeId = nodeId;
3799
- entry.assignedSessionId = sessionId;
3800
- entry.dispatchTimestamp = (/* @__PURE__ */ new Date()).toISOString();
3801
- entry.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
3802
- writeQueue(meshId, queue);
3803
- return entry;
3884
+ return withQueueLock(meshId, () => {
3885
+ const queue = readQueue(meshId);
3886
+ const hasActiveAssignment = queue.some((q) => q.status === "assigned" && (q.assignedSessionId === sessionId || q.assignedNodeId === nodeId));
3887
+ if (hasActiveAssignment) return null;
3888
+ let targetIdx = queue.findIndex((q) => q.status === "pending" && q.targetSessionId === sessionId);
3889
+ if (targetIdx === -1) {
3890
+ targetIdx = queue.findIndex((q) => q.status === "pending" && q.targetNodeId === nodeId && !q.targetSessionId);
3891
+ }
3892
+ if (targetIdx === -1) {
3893
+ targetIdx = queue.findIndex((q) => q.status === "pending" && !q.targetNodeId && !q.targetSessionId);
3894
+ }
3895
+ if (targetIdx === -1) return null;
3896
+ const entry = queue[targetIdx];
3897
+ entry.status = "assigned";
3898
+ entry.assignedNodeId = nodeId;
3899
+ entry.assignedSessionId = sessionId;
3900
+ entry.dispatchTimestamp = (/* @__PURE__ */ new Date()).toISOString();
3901
+ entry.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
3902
+ writeQueue(meshId, queue);
3903
+ return entry;
3904
+ });
3804
3905
  }
3805
3906
  function updateTaskStatus(meshId, taskId, status) {
3806
- const queue = readQueue(meshId);
3807
- const idx = queue.findIndex((q) => q.id === taskId);
3808
- if (idx === -1) return null;
3809
- queue[idx].status = status;
3810
- queue[idx].updatedAt = (/* @__PURE__ */ new Date()).toISOString();
3811
- writeQueue(meshId, queue);
3812
- return queue[idx];
3907
+ return withQueueLock(meshId, () => {
3908
+ const queue = readQueue(meshId);
3909
+ const idx = queue.findIndex((q) => q.id === taskId);
3910
+ if (idx === -1) return null;
3911
+ queue[idx].status = status;
3912
+ queue[idx].updatedAt = (/* @__PURE__ */ new Date()).toISOString();
3913
+ writeQueue(meshId, queue);
3914
+ return queue[idx];
3915
+ });
3813
3916
  }
3814
3917
  function recordTaskAutoLaunch(meshId, taskId, autoLaunch) {
3815
- const queue = readQueue(meshId);
3816
- const idx = queue.findIndex((q) => q.id === taskId);
3817
- if (idx === -1) return null;
3818
- const now = (/* @__PURE__ */ new Date()).toISOString();
3819
- queue[idx].autoLaunch = {
3820
- ...autoLaunch,
3821
- updatedAt: now
3822
- };
3823
- queue[idx].updatedAt = now;
3824
- writeQueue(meshId, queue);
3825
- return queue[idx];
3918
+ return withQueueLock(meshId, () => {
3919
+ const queue = readQueue(meshId);
3920
+ const idx = queue.findIndex((q) => q.id === taskId);
3921
+ if (idx === -1) return null;
3922
+ const now = (/* @__PURE__ */ new Date()).toISOString();
3923
+ queue[idx].autoLaunch = { ...autoLaunch, updatedAt: now };
3924
+ queue[idx].updatedAt = now;
3925
+ writeQueue(meshId, queue);
3926
+ return queue[idx];
3927
+ });
3826
3928
  }
3827
3929
  function cancelTask(meshId, taskId, opts) {
3828
- const queue = readQueue(meshId);
3829
- const idx = queue.findIndex((q) => q.id === taskId);
3830
- if (idx === -1) return null;
3831
- const now = (/* @__PURE__ */ new Date()).toISOString();
3832
- queue[idx].status = "cancelled";
3833
- queue[idx].updatedAt = now;
3834
- queue[idx].cancelledAt = now;
3835
- if (opts?.reason) queue[idx].cancelReason = opts.reason;
3836
- writeQueue(meshId, queue);
3837
- return queue[idx];
3930
+ return withQueueLock(meshId, () => {
3931
+ const queue = readQueue(meshId);
3932
+ const idx = queue.findIndex((q) => q.id === taskId);
3933
+ if (idx === -1) return null;
3934
+ const now = (/* @__PURE__ */ new Date()).toISOString();
3935
+ queue[idx].status = "cancelled";
3936
+ queue[idx].updatedAt = now;
3937
+ queue[idx].cancelledAt = now;
3938
+ if (opts?.reason) queue[idx].cancelReason = opts.reason;
3939
+ writeQueue(meshId, queue);
3940
+ return queue[idx];
3941
+ });
3838
3942
  }
3839
3943
  function requeueTask(meshId, taskId, opts) {
3840
- const queue = readQueue(meshId);
3841
- const idx = queue.findIndex((q) => q.id === taskId);
3842
- if (idx === -1) return null;
3843
- const entry = queue[idx];
3844
- const now = (/* @__PURE__ */ new Date()).toISOString();
3845
- entry.status = "pending";
3846
- delete entry.assignedNodeId;
3847
- delete entry.assignedSessionId;
3848
- delete entry.cancelledAt;
3849
- delete entry.cancelReason;
3850
- if (opts?.clearTargetNode) delete entry.targetNodeId;
3851
- if (typeof opts?.targetNodeId === "string") entry.targetNodeId = opts.targetNodeId;
3852
- if (opts?.clearTargetSession !== false) delete entry.targetSessionId;
3853
- if (typeof opts?.targetSessionId === "string") entry.targetSessionId = opts.targetSessionId;
3854
- entry.updatedAt = now;
3855
- entry.requeuedAt = now;
3856
- entry.requeueCount = (entry.requeueCount || 0) + 1;
3857
- if (opts?.reason) entry.requeueReason = opts.reason;
3858
- writeQueue(meshId, queue);
3859
- return entry;
3860
- }
3861
- function updateSessionTaskStatus(meshId, sessionId, status) {
3862
- const queue = readQueue(meshId);
3863
- let bestIdx = -1;
3864
- let bestTime = 0;
3865
- for (let i = queue.length - 1; i >= 0; i--) {
3866
- if (queue[i].assignedSessionId === sessionId && queue[i].status === "assigned") {
3944
+ return withQueueLock(meshId, () => {
3945
+ const queue = readQueue(meshId);
3946
+ const idx = queue.findIndex((q) => q.id === taskId);
3947
+ if (idx === -1) return null;
3948
+ const entry = queue[idx];
3949
+ const now = (/* @__PURE__ */ new Date()).toISOString();
3950
+ entry.status = "pending";
3951
+ delete entry.assignedNodeId;
3952
+ delete entry.assignedSessionId;
3953
+ delete entry.cancelledAt;
3954
+ delete entry.cancelReason;
3955
+ if (opts?.clearTargetNode) delete entry.targetNodeId;
3956
+ if (typeof opts?.targetNodeId === "string") entry.targetNodeId = opts.targetNodeId;
3957
+ if (opts?.clearTargetSession !== false) delete entry.targetSessionId;
3958
+ if (typeof opts?.targetSessionId === "string") entry.targetSessionId = opts.targetSessionId;
3959
+ entry.updatedAt = now;
3960
+ entry.requeuedAt = now;
3961
+ entry.requeueCount = (entry.requeueCount || 0) + 1;
3962
+ if (opts?.reason) entry.requeueReason = opts.reason;
3963
+ writeQueue(meshId, queue);
3964
+ return entry;
3965
+ });
3966
+ }
3967
+ function updateSessionTaskStatus(meshId, sessionId, status, opts) {
3968
+ return withQueueLock(meshId, () => {
3969
+ const queue = readQueue(meshId);
3970
+ const occurredAtTime = opts?.occurredAt ? new Date(opts.occurredAt).getTime() : Number.NaN;
3971
+ const hasOccurredAt = Number.isFinite(occurredAtTime);
3972
+ let bestIdx = -1;
3973
+ let bestTime = 0;
3974
+ for (let i = queue.length - 1; i >= 0; i--) {
3975
+ if (queue[i].assignedSessionId !== sessionId || queue[i].status !== "assigned") continue;
3867
3976
  const time3 = new Date(queue[i].dispatchTimestamp || queue[i].updatedAt).getTime();
3977
+ if (hasOccurredAt && Number.isFinite(time3) && time3 > occurredAtTime) continue;
3868
3978
  if (time3 > bestTime) {
3869
3979
  bestTime = time3;
3870
3980
  bestIdx = i;
3871
3981
  }
3872
3982
  }
3873
- }
3874
- if (bestIdx === -1) return null;
3875
- queue[bestIdx].status = status;
3876
- queue[bestIdx].updatedAt = (/* @__PURE__ */ new Date()).toISOString();
3877
- writeQueue(meshId, queue);
3878
- return queue[bestIdx];
3983
+ if (bestIdx === -1) return null;
3984
+ queue[bestIdx].status = status;
3985
+ queue[bestIdx].updatedAt = (/* @__PURE__ */ new Date()).toISOString();
3986
+ writeQueue(meshId, queue);
3987
+ return queue[bestIdx];
3988
+ });
3879
3989
  }
3880
3990
  function getMeshQueueStats(meshId) {
3881
3991
  const queue = readQueue(meshId);
@@ -4279,18 +4389,75 @@ __export(mesh_events_exports, {
4279
4389
  drainPendingMeshCoordinatorEvents: () => drainPendingMeshCoordinatorEvents,
4280
4390
  getPendingMeshCoordinatorEvents: () => getPendingMeshCoordinatorEvents,
4281
4391
  handleMeshForwardEvent: () => handleMeshForwardEvent,
4392
+ queuePendingMeshCoordinatorEvent: () => queuePendingMeshCoordinatorEvent,
4282
4393
  setupMeshEventForwarding: () => setupMeshEventForwarding,
4283
4394
  triggerMeshQueue: () => triggerMeshQueue,
4284
4395
  tryAssignQueueTask: () => tryAssignQueueTask
4285
4396
  });
4286
- function drainPendingMeshCoordinatorEvents() {
4287
- return pendingMeshCoordinatorEvents.splice(0);
4397
+ function sweepExpiredRemoteIdleSessions() {
4398
+ const now = Date.now();
4399
+ for (const [key, session] of remoteIdleSessions) {
4400
+ if (session.expiresAt <= now) remoteIdleSessions.delete(key);
4401
+ }
4402
+ }
4403
+ function getPendingEventsPath(meshId) {
4404
+ const safe = meshId.replace(/[^a-zA-Z0-9_-]/g, "_");
4405
+ return (0, import_path5.join)(getLedgerDir(), `${safe}.pending-events.jsonl`);
4406
+ }
4407
+ function queuePendingMeshCoordinatorEvent(event) {
4408
+ try {
4409
+ (0, import_fs6.appendFileSync)(getPendingEventsPath(event.meshId), JSON.stringify(event) + "\n", "utf-8");
4410
+ return true;
4411
+ } catch (e) {
4412
+ LOG.warn("MeshEvents", `Failed to persist pending coordinator event: ${e?.message || e}`);
4413
+ return false;
4414
+ }
4415
+ }
4416
+ function drainPendingMeshCoordinatorEvents(meshId) {
4417
+ if (!meshId) return [];
4418
+ const path42 = getPendingEventsPath(meshId);
4419
+ if (!(0, import_fs6.existsSync)(path42)) return [];
4420
+ try {
4421
+ const raw = (0, import_fs6.readFileSync)(path42, "utf-8");
4422
+ try {
4423
+ (0, import_fs6.unlinkSync)(path42);
4424
+ } catch {
4425
+ }
4426
+ return raw.split("\n").filter(Boolean).flatMap((line) => {
4427
+ try {
4428
+ return [JSON.parse(line)];
4429
+ } catch {
4430
+ return [];
4431
+ }
4432
+ });
4433
+ } catch {
4434
+ return [];
4435
+ }
4288
4436
  }
4289
- function getPendingMeshCoordinatorEvents() {
4290
- return pendingMeshCoordinatorEvents.slice();
4437
+ function getPendingMeshCoordinatorEvents(meshId) {
4438
+ if (!meshId) return [];
4439
+ const path42 = getPendingEventsPath(meshId);
4440
+ if (!(0, import_fs6.existsSync)(path42)) return [];
4441
+ try {
4442
+ const raw = (0, import_fs6.readFileSync)(path42, "utf-8");
4443
+ return raw.split("\n").filter(Boolean).flatMap((line) => {
4444
+ try {
4445
+ return [JSON.parse(line)];
4446
+ } catch {
4447
+ return [];
4448
+ }
4449
+ });
4450
+ } catch {
4451
+ return [];
4452
+ }
4291
4453
  }
4292
- function clearPendingMeshCoordinatorEvents() {
4293
- pendingMeshCoordinatorEvents.splice(0);
4454
+ function clearPendingMeshCoordinatorEvents(meshId) {
4455
+ if (!meshId) return;
4456
+ const path42 = getPendingEventsPath(meshId);
4457
+ if ((0, import_fs6.existsSync)(path42)) try {
4458
+ (0, import_fs6.unlinkSync)(path42);
4459
+ } catch {
4460
+ }
4294
4461
  }
4295
4462
  function readNonEmptyString(value) {
4296
4463
  return typeof value === "string" && value.trim() ? value.trim() : "";
@@ -4336,6 +4503,38 @@ function shouldSuppressIntentionalCleanupStop(args) {
4336
4503
  if (isIntentionalCleanupStopMetadata(args.metadataEvent)) return true;
4337
4504
  return hasRecentIntentionalCleanupStop(args.meshId, args.sessionId, args.nodeId);
4338
4505
  }
4506
+ function readEventTimestamp(value) {
4507
+ if (typeof value === "number" && Number.isFinite(value)) return value;
4508
+ if (typeof value === "string" && value.trim()) {
4509
+ const numeric = Number(value);
4510
+ if (Number.isFinite(numeric)) return numeric;
4511
+ const parsed = Date.parse(value);
4512
+ if (Number.isFinite(parsed)) return parsed;
4513
+ }
4514
+ return null;
4515
+ }
4516
+ function buildMeshCompletionFingerprint(args) {
4517
+ const timestampPart = Number.isFinite(args.timestamp) ? String(args.timestamp) : readNonEmptyString(args.finalSummary).slice(0, 200);
4518
+ return [
4519
+ args.meshId,
4520
+ args.event,
4521
+ args.sessionId,
4522
+ args.providerType || "",
4523
+ args.providerSessionId || "",
4524
+ timestampPart
4525
+ ].join("::");
4526
+ }
4527
+ function isDuplicateMeshCompletionEvent(args) {
4528
+ const fingerprint = buildMeshCompletionFingerprint(args);
4529
+ if (!fingerprint) return false;
4530
+ const now = Date.now();
4531
+ for (const [key, seenAt] of recentCompletionFingerprints.entries()) {
4532
+ if (now - seenAt > RECENT_COMPLETION_FINGERPRINT_TTL_MS) recentCompletionFingerprints.delete(key);
4533
+ }
4534
+ if (recentCompletionFingerprints.has(fingerprint)) return true;
4535
+ recentCompletionFingerprints.set(fingerprint, now);
4536
+ return false;
4537
+ }
4339
4538
  function tryAssignQueueTask(components, meshId, nodeId, sessionId, providerType) {
4340
4539
  const task = claimNextTask(meshId, nodeId, sessionId);
4341
4540
  if (!task) {
@@ -4354,7 +4553,16 @@ function tryAssignQueueTask(components, meshId, nodeId, sessionId, providerType)
4354
4553
  message: task.message
4355
4554
  }).catch((e) => {
4356
4555
  LOG.error("MeshQueue", `Failed to dispatch task via P2P to remote node ${nodeId}: ${e?.message}`);
4357
- updateTaskStatus(meshId, task.id, "failed");
4556
+ updateTaskStatus(meshId, task.id, "pending");
4557
+ try {
4558
+ appendLedgerEntry(meshId, {
4559
+ kind: "dispatch_failed",
4560
+ nodeId,
4561
+ sessionId,
4562
+ payload: { taskId: task.id, error: e?.message, retryable: true }
4563
+ });
4564
+ } catch {
4565
+ }
4358
4566
  });
4359
4567
  return true;
4360
4568
  }
@@ -4688,18 +4896,36 @@ function injectMeshSystemMessage(components, args) {
4688
4896
  LOG.info("MeshEvents", `Suppressed ${args.event} for intentionally cleanup-stopped session ${eventSessionId || "(unknown session)"}`);
4689
4897
  return { success: true, forwarded: 0, suppressed: true, intentionalCleanupStop: true };
4690
4898
  }
4899
+ const eventTimestamp = readEventTimestamp(args.metadataEvent.timestamp);
4900
+ if (args.event === "agent:generating_completed" && eventSessionId) {
4901
+ const duplicateCompletion = isDuplicateMeshCompletionEvent({
4902
+ meshId: args.meshId,
4903
+ event: args.event,
4904
+ sessionId: eventSessionId,
4905
+ providerType: readNonEmptyString(args.metadataEvent.providerType) || void 0,
4906
+ providerSessionId: readNonEmptyString(args.metadataEvent.providerSessionId) || void 0,
4907
+ timestamp: eventTimestamp,
4908
+ finalSummary: readNonEmptyString(args.metadataEvent.finalSummary) || void 0
4909
+ });
4910
+ if (duplicateCompletion) {
4911
+ LOG.info("MeshEvents", `Suppressed duplicate completion for mesh ${args.meshId} session ${eventSessionId}`);
4912
+ return { success: true, forwarded: 0, suppressed: true, duplicateCompletion: true };
4913
+ }
4914
+ }
4691
4915
  let completedTaskForLedger = null;
4692
4916
  if (args.event === "agent:generating_completed") {
4693
4917
  const sessionId = resolveEventSessionId(args.metadataEvent, args.sourceInstanceId);
4694
4918
  const nodeId = readNonEmptyString(args.nodeId) || readNonEmptyString(args.metadataEvent.meshNodeId);
4695
4919
  const providerType = readNonEmptyString(args.metadataEvent.providerType);
4696
4920
  if (sessionId) {
4697
- const completedTask = updateSessionTaskStatus(args.meshId, sessionId, "completed");
4921
+ const completedTask = updateSessionTaskStatus(args.meshId, sessionId, "completed", {
4922
+ occurredAt: eventTimestamp !== null ? new Date(eventTimestamp).toISOString() : void 0
4923
+ });
4698
4924
  completedTaskForLedger = completedTask ? { id: completedTask.id } : null;
4699
4925
  if (nodeId && providerType) {
4700
- setTimeout(() => {
4926
+ setImmediate(() => {
4701
4927
  tryAssignQueueTask(components, args.meshId, nodeId, sessionId, providerType);
4702
- }, 500);
4928
+ });
4703
4929
  }
4704
4930
  }
4705
4931
  } else if (args.event === "agent:ready") {
@@ -4737,13 +4963,17 @@ function injectMeshSystemMessage(components, args) {
4737
4963
  }
4738
4964
  }
4739
4965
  if (sessionId && nodeId && providerType) {
4740
- remoteIdleSessions.set(`${nodeId}:${sessionId}`, { nodeId, sessionId, providerType });
4741
- setTimeout(() => {
4966
+ sweepExpiredRemoteIdleSessions();
4967
+ remoteIdleSessions.set(`${nodeId}:${sessionId}`, {
4968
+ nodeId,
4969
+ sessionId,
4970
+ providerType,
4971
+ expiresAt: Date.now() + REMOTE_IDLE_SESSION_TTL_MS
4972
+ });
4973
+ setImmediate(() => {
4742
4974
  const assigned = tryAssignQueueTask(components, args.meshId, nodeId, sessionId, providerType);
4743
- if (assigned) {
4744
- remoteIdleSessions.delete(`${nodeId}:${sessionId}`);
4745
- }
4746
- }, 500);
4975
+ if (assigned) remoteIdleSessions.delete(`${nodeId}:${sessionId}`);
4976
+ });
4747
4977
  }
4748
4978
  } else if (args.event === "agent:generating_started") {
4749
4979
  const sessionId = resolveEventSessionId(args.metadataEvent, args.sourceInstanceId);
@@ -4854,17 +5084,18 @@ function injectMeshSystemMessage(components, args) {
4854
5084
  return true;
4855
5085
  });
4856
5086
  if (coordinatorInstances.length === 0) {
4857
- if (pendingMeshCoordinatorEvents.length < MAX_PENDING_EVENTS) {
4858
- pendingMeshCoordinatorEvents.push({
4859
- event: args.event,
4860
- meshId: args.meshId,
4861
- nodeLabel: args.nodeLabel,
4862
- metadataEvent: {
4863
- ...args.metadataEvent,
4864
- ...recoveryContext ? { recoveryContext } : {}
4865
- },
4866
- queuedAt: Date.now()
4867
- });
5087
+ if (queuePendingMeshCoordinatorEvent({
5088
+ event: args.event,
5089
+ meshId: args.meshId,
5090
+ nodeLabel: args.nodeLabel,
5091
+ nodeId: args.nodeId || void 0,
5092
+ workspace: readNonEmptyString(args.metadataEvent.workspace),
5093
+ metadataEvent: {
5094
+ ...args.metadataEvent,
5095
+ ...recoveryContext ? { recoveryContext } : {}
5096
+ },
5097
+ queuedAt: Date.now()
5098
+ })) {
4868
5099
  LOG.info("MeshEvents", `Queued ${args.event} for MCP coordinator (mesh ${args.meshId})`);
4869
5100
  }
4870
5101
  return { success: true, forwarded: 0 };
@@ -4903,6 +5134,7 @@ function handleMeshForwardEvent(components, payload) {
4903
5134
  providerType: readNonEmptyString(payload.providerType),
4904
5135
  providerSessionId: readNonEmptyString(payload.providerSessionId),
4905
5136
  finalSummary: readNonEmptyString(payload.finalSummary) || readNonEmptyString(payload.summary),
5137
+ ...payload.timestamp !== void 0 ? { timestamp: payload.timestamp } : {},
4906
5138
  intentional: payload.intentional === true,
4907
5139
  intentionalStop: payload.intentionalStop === true,
4908
5140
  operatorCleanup: payload.operatorCleanup === true,
@@ -4945,19 +5177,20 @@ function setupMeshEventForwarding(components) {
4945
5177
  });
4946
5178
  });
4947
5179
  }
4948
- var remoteIdleSessions, MAX_PENDING_EVENTS, pendingMeshCoordinatorEvents, MESH_COORDINATOR_EVENTS, EVENT_TO_LEDGER_KIND, INTENTIONAL_CLEANUP_STOP_SUPPRESSION_MS, autoLaunchInProgress, autoLaunchCooldownUntil, AUTO_LAUNCH_COOLDOWN_MS;
5180
+ var import_fs6, import_path5, REMOTE_IDLE_SESSION_TTL_MS, remoteIdleSessions, MESH_COORDINATOR_EVENTS, EVENT_TO_LEDGER_KIND, INTENTIONAL_CLEANUP_STOP_SUPPRESSION_MS, RECENT_COMPLETION_FINGERPRINT_TTL_MS, recentCompletionFingerprints, autoLaunchInProgress, autoLaunchCooldownUntil, AUTO_LAUNCH_COOLDOWN_MS;
4949
5181
  var init_mesh_events = __esm({
4950
5182
  "../../oss/packages/daemon-core/src/mesh/mesh-events.ts"() {
4951
5183
  "use strict";
5184
+ import_fs6 = require("fs");
5185
+ import_path5 = require("path");
4952
5186
  init_config();
4953
5187
  init_mesh_config();
4954
5188
  init_cli_detector();
4955
5189
  init_logger();
4956
5190
  init_mesh_ledger();
4957
5191
  init_mesh_work_queue();
5192
+ REMOTE_IDLE_SESSION_TTL_MS = 5 * 60 * 1e3;
4958
5193
  remoteIdleSessions = /* @__PURE__ */ new Map();
4959
- MAX_PENDING_EVENTS = 50;
4960
- pendingMeshCoordinatorEvents = [];
4961
5194
  MESH_COORDINATOR_EVENTS = /* @__PURE__ */ new Set([
4962
5195
  "agent:generating_started",
4963
5196
  "agent:generating_completed",
@@ -4973,6 +5206,8 @@ var init_mesh_events = __esm({
4973
5206
  "monitor:long_generating": "task_stalled"
4974
5207
  };
4975
5208
  INTENTIONAL_CLEANUP_STOP_SUPPRESSION_MS = 30 * 60 * 1e3;
5209
+ RECENT_COMPLETION_FINGERPRINT_TTL_MS = 10 * 60 * 1e3;
5210
+ recentCompletionFingerprints = /* @__PURE__ */ new Map();
4976
5211
  autoLaunchInProgress = /* @__PURE__ */ new Set();
4977
5212
  autoLaunchCooldownUntil = /* @__PURE__ */ new Map();
4978
5213
  AUTO_LAUNCH_COOLDOWN_MS = 5e3;
@@ -5100,7 +5335,7 @@ function isPlainObject2(value) {
5100
5335
  return !!value && typeof value === "object" && !Array.isArray(value);
5101
5336
  }
5102
5337
  function getStatePath() {
5103
- return (0, import_path5.join)(getConfigDir(), "state.json");
5338
+ return (0, import_path6.join)(getConfigDir(), "state.json");
5104
5339
  }
5105
5340
  function normalizeState(raw) {
5106
5341
  const parsed = isPlainObject2(raw) ? raw : {};
@@ -5136,11 +5371,11 @@ function normalizeState(raw) {
5136
5371
  }
5137
5372
  function loadState() {
5138
5373
  const statePath = getStatePath();
5139
- if (!(0, import_fs6.existsSync)(statePath)) {
5374
+ if (!(0, import_fs7.existsSync)(statePath)) {
5140
5375
  return { ...DEFAULT_STATE };
5141
5376
  }
5142
5377
  try {
5143
- const raw = (0, import_fs6.readFileSync)(statePath, "utf-8");
5378
+ const raw = (0, import_fs7.readFileSync)(statePath, "utf-8");
5144
5379
  return normalizeState(JSON.parse(raw));
5145
5380
  } catch {
5146
5381
  return { ...DEFAULT_STATE };
@@ -5149,17 +5384,17 @@ function loadState() {
5149
5384
  function saveState(state) {
5150
5385
  const statePath = getStatePath();
5151
5386
  const normalized = normalizeState(state);
5152
- (0, import_fs6.writeFileSync)(statePath, JSON.stringify(normalized, null, 2), { encoding: "utf-8", mode: 384 });
5387
+ (0, import_fs7.writeFileSync)(statePath, JSON.stringify(normalized, null, 2), { encoding: "utf-8", mode: 384 });
5153
5388
  }
5154
5389
  function resetState() {
5155
5390
  saveState({ ...DEFAULT_STATE });
5156
5391
  }
5157
- var import_fs6, import_path5, DEFAULT_STATE;
5392
+ var import_fs7, import_path6, DEFAULT_STATE;
5158
5393
  var init_state_store = __esm({
5159
5394
  "../../oss/packages/daemon-core/src/config/state-store.ts"() {
5160
5395
  "use strict";
5161
- import_fs6 = require("fs");
5162
- import_path5 = require("path");
5396
+ import_fs7 = require("fs");
5397
+ import_path6 = require("path");
5163
5398
  init_config();
5164
5399
  DEFAULT_STATE = {
5165
5400
  recentActivity: [],
@@ -5192,7 +5427,7 @@ function findCliCommand(command) {
5192
5427
  if (path10.isAbsolute(trimmed) || trimmed.includes("/") || trimmed.includes("\\") || trimmed.startsWith("~")) {
5193
5428
  const candidate = trimmed.startsWith("~") ? path10.join((0, import_os2.homedir)(), trimmed.slice(1)) : trimmed;
5194
5429
  const resolved = path10.isAbsolute(candidate) ? candidate : path10.resolve(candidate);
5195
- return (0, import_fs7.existsSync)(resolved) ? resolved : null;
5430
+ return (0, import_fs8.existsSync)(resolved) ? resolved : null;
5196
5431
  }
5197
5432
  try {
5198
5433
  const result = (0, import_child_process2.execSync)(
@@ -5223,9 +5458,9 @@ function checkPathExists(paths) {
5223
5458
  if (normalized.includes("*")) {
5224
5459
  const username = home.split(/[\\/]/).pop() || "";
5225
5460
  const resolved = normalized.replace("*", username);
5226
- if ((0, import_fs7.existsSync)(resolved)) return resolved;
5461
+ if ((0, import_fs8.existsSync)(resolved)) return resolved;
5227
5462
  } else {
5228
- if ((0, import_fs7.existsSync)(normalized)) return normalized;
5463
+ if ((0, import_fs8.existsSync)(normalized)) return normalized;
5229
5464
  }
5230
5465
  }
5231
5466
  return null;
@@ -5239,7 +5474,7 @@ async function detectIDEs(providerLoader) {
5239
5474
  let resolvedCli = cliPath;
5240
5475
  if (!resolvedCli && appPath && os32 === "darwin") {
5241
5476
  const bundledCli = `${appPath}/Contents/Resources/app/bin/${def.cli}`;
5242
- if ((0, import_fs7.existsSync)(bundledCli)) resolvedCli = bundledCli;
5477
+ if ((0, import_fs8.existsSync)(bundledCli)) resolvedCli = bundledCli;
5243
5478
  }
5244
5479
  if (!resolvedCli && appPath && os32 === "win32") {
5245
5480
  const { dirname: dirname13 } = await import("path");
@@ -5252,7 +5487,7 @@ async function detectIDEs(providerLoader) {
5252
5487
  `${appDir}\\\\resources\\\\app\\\\bin\\\\${def.cli}.cmd`
5253
5488
  ];
5254
5489
  for (const c of candidates) {
5255
- if ((0, import_fs7.existsSync)(c)) {
5490
+ if ((0, import_fs8.existsSync)(c)) {
5256
5491
  resolvedCli = c;
5257
5492
  break;
5258
5493
  }
@@ -5273,12 +5508,12 @@ async function detectIDEs(providerLoader) {
5273
5508
  }
5274
5509
  return results;
5275
5510
  }
5276
- var import_child_process2, import_fs7, import_os2, path10, BUILTIN_IDE_DEFINITIONS, registeredIDEs;
5511
+ var import_child_process2, import_fs8, import_os2, path10, BUILTIN_IDE_DEFINITIONS, registeredIDEs;
5277
5512
  var init_ide_detector = __esm({
5278
5513
  "../../oss/packages/daemon-core/src/detection/ide-detector.ts"() {
5279
5514
  "use strict";
5280
5515
  import_child_process2 = require("child_process");
5281
- import_fs7 = require("fs");
5516
+ import_fs8 = require("fs");
5282
5517
  import_os2 = require("os");
5283
5518
  path10 = __toESM(require("path"));
5284
5519
  BUILTIN_IDE_DEFINITIONS = [];
@@ -7199,7 +7434,7 @@ var init_status_monitor = __esm({
7199
7434
  });
7200
7435
 
7201
7436
  // ../../oss/packages/daemon-core/src/providers/chat-message-normalization.ts
7202
- function extractFinalSummaryFromMessages(messages, maxChars = 500) {
7437
+ function extractFinalSummaryFromMessages(messages, maxChars = DEFAULT_FINAL_SUMMARY_MAX_CHARS) {
7203
7438
  if (!Array.isArray(messages) || messages.length === 0) return "";
7204
7439
  for (let i = messages.length - 1; i >= 0; i--) {
7205
7440
  const msg = messages[i];
@@ -7501,11 +7736,12 @@ function filterInternalChatMessages(messages) {
7501
7736
  function filterChatMessagesByVisibility(messages, surface) {
7502
7737
  return (Array.isArray(messages) ? messages : []).filter((message) => classifyChatMessageVisibility(message).surface === surface);
7503
7738
  }
7504
- var BUILTIN_CHAT_MESSAGE_KINDS, CHAT_MESSAGE_VISIBILITIES, CHAT_MESSAGE_TRANSCRIPT_VISIBILITIES, CHAT_MESSAGE_AUDIENCES, CHAT_MESSAGE_SOURCES, CHAT_MESSAGE_ACTIVITY_SOURCES, CHAT_MESSAGE_INTERNAL_SOURCES, KNOWN_CHAT_MESSAGE_KINDS, CHAT_MESSAGE_KIND_ALIASES, EXPLICIT_HIDDEN_VISIBILITIES, EXPLICIT_VISIBLE_VISIBILITIES, HIDDEN_AUDIENCES, ACTIVITY_SOURCE_SET, INTERNAL_SOURCE_SET;
7739
+ var DEFAULT_FINAL_SUMMARY_MAX_CHARS, BUILTIN_CHAT_MESSAGE_KINDS, CHAT_MESSAGE_VISIBILITIES, CHAT_MESSAGE_TRANSCRIPT_VISIBILITIES, CHAT_MESSAGE_AUDIENCES, CHAT_MESSAGE_SOURCES, CHAT_MESSAGE_ACTIVITY_SOURCES, CHAT_MESSAGE_INTERNAL_SOURCES, KNOWN_CHAT_MESSAGE_KINDS, CHAT_MESSAGE_KIND_ALIASES, EXPLICIT_HIDDEN_VISIBILITIES, EXPLICIT_VISIBLE_VISIBILITIES, HIDDEN_AUDIENCES, ACTIVITY_SOURCE_SET, INTERNAL_SOURCE_SET;
7505
7740
  var init_chat_message_normalization = __esm({
7506
7741
  "../../oss/packages/daemon-core/src/providers/chat-message-normalization.ts"() {
7507
7742
  "use strict";
7508
7743
  init_contracts();
7744
+ DEFAULT_FINAL_SUMMARY_MAX_CHARS = 4e3;
7509
7745
  BUILTIN_CHAT_MESSAGE_KINDS = ["standard", "thought", "tool", "terminal", "system"];
7510
7746
  CHAT_MESSAGE_VISIBILITIES = ["user", "debug", "internal", "hidden"];
7511
7747
  CHAT_MESSAGE_TRANSCRIPT_VISIBILITIES = ["visible", "chat", "user", "debug", "internal", "hidden"];
@@ -37725,7 +37961,7 @@ function commandExists(command) {
37725
37961
  const trimmed = command.trim();
37726
37962
  if (!trimmed) return false;
37727
37963
  if (isExplicitCommand(trimmed)) {
37728
- return (0, import_fs8.existsSync)(expandExecutable(trimmed));
37964
+ return (0, import_fs9.existsSync)(expandExecutable(trimmed));
37729
37965
  }
37730
37966
  try {
37731
37967
  (0, import_child_process6.execFileSync)(process.platform === "win32" ? "where" : "which", [trimmed], {
@@ -37746,10 +37982,10 @@ function hasCliArg(args, flag) {
37746
37982
  }
37747
37983
  function ensureEmptyDelegatedMcpConfig(workspace) {
37748
37984
  const baseDir = path19.join(os16.tmpdir(), "adhdev-delegated-agent-empty-mcp");
37749
- (0, import_fs8.mkdirSync)(baseDir, { recursive: true });
37985
+ (0, import_fs9.mkdirSync)(baseDir, { recursive: true });
37750
37986
  const workspaceHash = crypto4.createHash("sha256").update(path19.resolve(workspace || os16.tmpdir())).digest("hex").slice(0, 16);
37751
37987
  const filePath = path19.join(baseDir, `${workspaceHash}.json`);
37752
- (0, import_fs8.writeFileSync)(filePath, JSON.stringify({ mcpServers: {} }, null, 2), "utf-8");
37988
+ (0, import_fs9.writeFileSync)(filePath, JSON.stringify({ mcpServers: {} }, null, 2), "utf-8");
37753
37989
  return filePath;
37754
37990
  }
37755
37991
  function buildCoordinatorDelegatedCliLaunchOptions(input) {
@@ -37876,14 +38112,14 @@ function resolveCliSessionBinding(provider, normalizedType, cliArgs, requestedRe
37876
38112
  launchMode: "new"
37877
38113
  };
37878
38114
  }
37879
- var os16, path19, crypto4, import_fs8, import_child_process6, chalkModule, chalkApi, COORDINATOR_DELEGATED_ENV_UNSETS, DaemonCliManager;
38115
+ var os16, path19, crypto4, import_fs9, import_child_process6, chalkModule, chalkApi, COORDINATOR_DELEGATED_ENV_UNSETS, DaemonCliManager;
37880
38116
  var init_cli_manager = __esm({
37881
38117
  "../../oss/packages/daemon-core/src/commands/cli-manager.ts"() {
37882
38118
  "use strict";
37883
38119
  os16 = __toESM(require("os"));
37884
38120
  path19 = __toESM(require("path"));
37885
38121
  crypto4 = __toESM(require("crypto"));
37886
- import_fs8 = require("fs");
38122
+ import_fs9 = require("fs");
37887
38123
  import_child_process6 = require("child_process");
37888
38124
  init_source2();
37889
38125
  init_provider_cli_adapter();
@@ -38797,7 +39033,7 @@ function createFsWatchInstance(path42, options, listener, errHandler, emitRaw) {
38797
39033
  }
38798
39034
  };
38799
39035
  try {
38800
- return (0, import_fs9.watch)(path42, {
39036
+ return (0, import_fs10.watch)(path42, {
38801
39037
  persistent: options.persistent
38802
39038
  }, handleEvent);
38803
39039
  } catch (error48) {
@@ -38805,11 +39041,11 @@ function createFsWatchInstance(path42, options, listener, errHandler, emitRaw) {
38805
39041
  return void 0;
38806
39042
  }
38807
39043
  }
38808
- var import_fs9, import_promises5, sysPath, import_os3, STR_DATA, STR_END, STR_CLOSE, EMPTY_FN, pl, isWindows, isMacos, isLinux, isFreeBSD, isIBMi, EVENTS, EV, THROTTLE_MODE_WATCH, statMethods, KEY_LISTENERS, KEY_ERR, KEY_RAW, HANDLER_KEYS, binaryExtensions, isBinaryPath, foreach, addAndConvert, clearItem, delFromSet, isEmptySet, FsWatchInstances, fsWatchBroadcast, setFsWatchListener, FsWatchFileInstances, setFsWatchFileListener, NodeFsHandler;
39044
+ var import_fs10, import_promises5, sysPath, import_os3, STR_DATA, STR_END, STR_CLOSE, EMPTY_FN, pl, isWindows, isMacos, isLinux, isFreeBSD, isIBMi, EVENTS, EV, THROTTLE_MODE_WATCH, statMethods, KEY_LISTENERS, KEY_ERR, KEY_RAW, HANDLER_KEYS, binaryExtensions, isBinaryPath, foreach, addAndConvert, clearItem, delFromSet, isEmptySet, FsWatchInstances, fsWatchBroadcast, setFsWatchListener, FsWatchFileInstances, setFsWatchFileListener, NodeFsHandler;
38809
39045
  var init_handler2 = __esm({
38810
39046
  "../../oss/node_modules/chokidar/esm/handler.js"() {
38811
39047
  "use strict";
38812
- import_fs9 = require("fs");
39048
+ import_fs10 = require("fs");
38813
39049
  import_promises5 = require("fs/promises");
38814
39050
  sysPath = __toESM(require("path"), 1);
38815
39051
  import_os3 = require("os");
@@ -39213,7 +39449,7 @@ var init_handler2 = __esm({
39213
39449
  let cont = FsWatchFileInstances.get(fullPath);
39214
39450
  const copts = cont && cont.options;
39215
39451
  if (copts && (copts.persistent < options.persistent || copts.interval > options.interval)) {
39216
- (0, import_fs9.unwatchFile)(fullPath);
39452
+ (0, import_fs10.unwatchFile)(fullPath);
39217
39453
  cont = void 0;
39218
39454
  }
39219
39455
  if (cont) {
@@ -39224,7 +39460,7 @@ var init_handler2 = __esm({
39224
39460
  listeners: listener,
39225
39461
  rawEmitters: rawEmitter,
39226
39462
  options,
39227
- watcher: (0, import_fs9.watchFile)(fullPath, options, (curr, prev) => {
39463
+ watcher: (0, import_fs10.watchFile)(fullPath, options, (curr, prev) => {
39228
39464
  foreach(cont.rawEmitters, (rawEmitter2) => {
39229
39465
  rawEmitter2(EV.CHANGE, fullPath, { curr, prev });
39230
39466
  });
@@ -39241,7 +39477,7 @@ var init_handler2 = __esm({
39241
39477
  delFromSet(cont, KEY_RAW, rawEmitter);
39242
39478
  if (isEmptySet(cont.listeners)) {
39243
39479
  FsWatchFileInstances.delete(fullPath);
39244
- (0, import_fs9.unwatchFile)(fullPath);
39480
+ (0, import_fs10.unwatchFile)(fullPath);
39245
39481
  cont.options = cont.watcher = void 0;
39246
39482
  Object.freeze(cont);
39247
39483
  }
@@ -39619,11 +39855,11 @@ function watch(paths, options = {}) {
39619
39855
  watcher.add(paths);
39620
39856
  return watcher;
39621
39857
  }
39622
- var import_fs10, import_promises6, import_events2, sysPath2, SLASH, SLASH_SLASH, ONE_DOT, TWO_DOTS, STRING_TYPE, BACK_SLASH_RE, DOUBLE_SLASH_RE, DOT_RE, REPLACER_RE, isMatcherObject, unifyPaths, toUnix, normalizePathToUnix, normalizeIgnored, getAbsolutePath, EMPTY_SET, DirEntry, STAT_METHOD_F, STAT_METHOD_L, WatchHelper, FSWatcher;
39858
+ var import_fs11, import_promises6, import_events2, sysPath2, SLASH, SLASH_SLASH, ONE_DOT, TWO_DOTS, STRING_TYPE, BACK_SLASH_RE, DOUBLE_SLASH_RE, DOT_RE, REPLACER_RE, isMatcherObject, unifyPaths, toUnix, normalizePathToUnix, normalizeIgnored, getAbsolutePath, EMPTY_SET, DirEntry, STAT_METHOD_F, STAT_METHOD_L, WatchHelper, FSWatcher;
39623
39859
  var init_esm2 = __esm({
39624
39860
  "../../oss/node_modules/chokidar/esm/index.js"() {
39625
39861
  "use strict";
39626
- import_fs10 = require("fs");
39862
+ import_fs11 = require("fs");
39627
39863
  import_promises6 = require("fs/promises");
39628
39864
  import_events2 = require("events");
39629
39865
  sysPath2 = __toESM(require("path"), 1);
@@ -40101,7 +40337,7 @@ var init_esm2 = __esm({
40101
40337
  const now = /* @__PURE__ */ new Date();
40102
40338
  const writes = this._pendingWrites;
40103
40339
  function awaitWriteFinishFn(prevStat) {
40104
- (0, import_fs10.stat)(fullPath, (err, curStat) => {
40340
+ (0, import_fs11.stat)(fullPath, (err, curStat) => {
40105
40341
  if (err || !writes.has(path42)) {
40106
40342
  if (err && err.code !== "ENOENT")
40107
40343
  awfEmit(err);
@@ -46402,55 +46638,45 @@ function readBooleanValue(...values) {
46402
46638
  }
46403
46639
  return void 0;
46404
46640
  }
46405
- function buildCachedInlineMeshGitStatus(node) {
46406
- const cachedStatus = readObjectRecord(node?.cachedStatus);
46407
- const cachedGit = readObjectRecord(cachedStatus.git);
46408
- if (Object.keys(cachedGit).length) {
46409
- const conflictFiles2 = Array.isArray(cachedGit.conflictFiles) ? cachedGit.conflictFiles.filter((value) => typeof value === "string") : [];
46410
- const conflictCount2 = readNumberValue(cachedGit.conflicts) ?? conflictFiles2.length;
46411
- const hasConflicts2 = readBooleanValue(cachedGit.hasConflicts) ?? conflictCount2 > 0;
46412
- const isGitRepo2 = readBooleanValue(cachedGit.isGitRepo);
46413
- if (isGitRepo2 !== void 0) {
46414
- return {
46415
- workspace: readStringValue(cachedGit.workspace, node?.workspace) || "",
46416
- repoRoot: readStringValue(cachedGit.repoRoot, node?.repoRoot, node?.workspace) || null,
46417
- isGitRepo: isGitRepo2,
46418
- branch: readStringValue(cachedGit.branch) ?? null,
46419
- headCommit: readStringValue(cachedGit.headCommit) ?? null,
46420
- headMessage: readStringValue(cachedGit.headMessage) ?? null,
46421
- upstream: readStringValue(cachedGit.upstream) ?? null,
46422
- ahead: readNumberValue(cachedGit.ahead) ?? 0,
46423
- behind: readNumberValue(cachedGit.behind) ?? 0,
46424
- staged: readNumberValue(cachedGit.staged) ?? 0,
46425
- modified: readNumberValue(cachedGit.modified) ?? 0,
46426
- untracked: readNumberValue(cachedGit.untracked) ?? 0,
46427
- deleted: readNumberValue(cachedGit.deleted) ?? 0,
46428
- renamed: readNumberValue(cachedGit.renamed) ?? 0,
46429
- hasConflicts: hasConflicts2,
46430
- conflictFiles: conflictFiles2,
46431
- stashCount: readNumberValue(cachedGit.stashCount) ?? 0,
46432
- lastCheckedAt: readNumberValue(cachedGit.lastCheckedAt) ?? Date.now()
46433
- };
46434
- }
46435
- }
46436
- const rawGit = readObjectRecord(node?.lastGit ?? node?.last_git);
46437
- const gitResult = readObjectRecord(rawGit.result);
46438
- const directStatus = readObjectRecord(rawGit.status);
46439
- const nestedStatus = readObjectRecord(gitResult.status);
46440
- const rawProbe = readObjectRecord(node?.lastProbe ?? node?.last_probe);
46441
- const probeGit = readObjectRecord(rawProbe.git);
46442
- const probeGitResult = readObjectRecord(probeGit.result);
46443
- const probeDirectStatus = readObjectRecord(probeGit.status);
46444
- const probeNestedStatus = readObjectRecord(probeGitResult.status);
46445
- const status = Object.keys(directStatus).length ? directStatus : Object.keys(nestedStatus).length ? nestedStatus : Object.keys(probeDirectStatus).length ? probeDirectStatus : Object.keys(probeNestedStatus).length ? probeNestedStatus : {};
46641
+ function joinRepoPath(root, relativePath) {
46642
+ const normalizedRoot = typeof root === "string" ? root.trim().replace(/[\\/]+$/, "") : "";
46643
+ const normalizedPath = typeof relativePath === "string" ? relativePath.trim() : "";
46644
+ if (!normalizedPath) return void 0;
46645
+ if (/^(?:[A-Za-z]:[\\/]|\/)/.test(normalizedPath)) return normalizedPath;
46646
+ if (!normalizedRoot) return void 0;
46647
+ return `${normalizedRoot}/${normalizedPath.replace(/^[\\/]+/, "")}`;
46648
+ }
46649
+ function readGitSubmodules(value, parentRepoRoot) {
46650
+ if (!Array.isArray(value)) return void 0;
46651
+ const submodules = value.map((entry) => {
46652
+ const submodule = readObjectRecord(entry);
46653
+ const path42 = readStringValue(submodule.path);
46654
+ const commit = readStringValue(submodule.commit);
46655
+ const repoPath = readStringValue(submodule.repoPath, submodule.repo_root) ?? joinRepoPath(parentRepoRoot, path42);
46656
+ if (!path42 || !commit || !repoPath) return null;
46657
+ return {
46658
+ path: path42,
46659
+ commit,
46660
+ repoPath,
46661
+ dirty: readBooleanValue(submodule.dirty) ?? false,
46662
+ outOfSync: readBooleanValue(submodule.outOfSync, submodule.out_of_sync) ?? false,
46663
+ lastCheckedAt: readNumberValue(submodule.lastCheckedAt, submodule.last_checked_at) ?? Date.now(),
46664
+ ...readStringValue(submodule.error) ? { error: readStringValue(submodule.error) } : {}
46665
+ };
46666
+ }).filter((entry) => entry !== null);
46667
+ return submodules.length > 0 ? submodules : void 0;
46668
+ }
46669
+ function normalizeInlineMeshGitStatus(status, node, options) {
46446
46670
  const isGitRepo = readBooleanValue(status.isGitRepo);
46447
46671
  if (!Object.keys(status).length || isGitRepo === void 0) return void 0;
46448
46672
  const conflictFiles = Array.isArray(status.conflictFiles) ? status.conflictFiles.filter((value) => typeof value === "string") : [];
46449
46673
  const conflictCount = readNumberValue(status.conflicts) ?? conflictFiles.length;
46450
46674
  const hasConflicts = readBooleanValue(status.hasConflicts) ?? conflictCount > 0;
46675
+ const repoRoot = readStringValue(status.repoRoot, status.repo_root, node?.repoRoot, node?.repo_root, status.workspace, node?.workspace) || void 0;
46676
+ const submodules = readGitSubmodules(status.submodules, repoRoot);
46451
46677
  return {
46452
46678
  workspace: readStringValue(status.workspace, node?.workspace) || "",
46453
- repoRoot: readStringValue(status.repoRoot, node?.repoRoot, node?.workspace) || null,
46679
+ repoRoot: repoRoot ?? null,
46454
46680
  isGitRepo,
46455
46681
  branch: readStringValue(status.branch) ?? null,
46456
46682
  headCommit: readStringValue(status.headCommit) ?? null,
@@ -46466,29 +46692,407 @@ function buildCachedInlineMeshGitStatus(node) {
46466
46692
  hasConflicts,
46467
46693
  conflictFiles,
46468
46694
  stashCount: readNumberValue(status.stashCount) ?? 0,
46469
- lastCheckedAt: Date.now()
46695
+ lastCheckedAt: options?.lastCheckedAt ?? readNumberValue(status.lastCheckedAt) ?? Date.now(),
46696
+ ...submodules ? { submodules } : {}
46697
+ };
46698
+ }
46699
+ function buildInlineMeshTransitGitStatus(node) {
46700
+ const rawGit = readObjectRecord(node?.lastGit ?? node?.last_git);
46701
+ const gitResult = readObjectRecord(rawGit.result);
46702
+ const directStatus = readObjectRecord(rawGit.status);
46703
+ const nestedStatus = readObjectRecord(gitResult.status);
46704
+ const rawProbe = readObjectRecord(node?.lastProbe ?? node?.last_probe);
46705
+ const probeGit = readObjectRecord(rawProbe.git);
46706
+ const probeGitResult = readObjectRecord(probeGit.result);
46707
+ const probeDirectStatus = readObjectRecord(probeGit.status);
46708
+ const probeNestedStatus = readObjectRecord(probeGitResult.status);
46709
+ const status = Object.keys(directStatus).length ? directStatus : Object.keys(nestedStatus).length ? nestedStatus : Object.keys(probeDirectStatus).length ? probeDirectStatus : Object.keys(probeNestedStatus).length ? probeNestedStatus : {};
46710
+ return normalizeInlineMeshGitStatus(status, node, { lastCheckedAt: Date.now() });
46711
+ }
46712
+ function recordInlineMeshDirectGitTruth(node, git, source) {
46713
+ if (!node || typeof node !== "object" || Array.isArray(node)) return;
46714
+ const checkedAt = readNumberValue(git.lastCheckedAt) ?? Date.now();
46715
+ const updatedAt = new Date(checkedAt).toISOString();
46716
+ const nextGit = {
46717
+ ...git,
46718
+ lastCheckedAt: checkedAt
46719
+ };
46720
+ node.lastGit = {
46721
+ source,
46722
+ checkedAt,
46723
+ status: nextGit
46470
46724
  };
46725
+ node.last_git = node.lastGit;
46726
+ node.machineStatus = "online";
46727
+ node.updatedAt = updatedAt;
46728
+ node.lastSeenAt = updatedAt;
46729
+ const repoRoot = readStringValue(nextGit.repoRoot);
46730
+ if (repoRoot && !readStringValue(node.repoRoot)) node.repoRoot = repoRoot;
46731
+ }
46732
+ function buildCachedInlineMeshGitStatus(node) {
46733
+ const liveGit = buildInlineMeshTransitGitStatus(node);
46734
+ if (liveGit) return liveGit;
46735
+ const cachedStatus = readObjectRecord(node?.cachedStatus);
46736
+ const cachedGit = readObjectRecord(cachedStatus.git);
46737
+ if (!Object.keys(cachedGit).length) return void 0;
46738
+ return normalizeInlineMeshGitStatus(cachedGit, node);
46471
46739
  }
46472
- function applyCachedInlineMeshNodeStatus(status, node) {
46740
+ function shouldDiscardCachedInlineMeshStatus(node) {
46473
46741
  const cachedStatus = readObjectRecord(node?.cachedStatus);
46474
- const git = buildCachedInlineMeshGitStatus(node);
46475
- const error48 = readStringValue(cachedStatus.error, node?.error);
46476
- const health = readStringValue(cachedStatus.health, node?.health);
46742
+ if (!Object.keys(cachedStatus).length) return false;
46743
+ const cachedGit = readObjectRecord(cachedStatus.git);
46744
+ const workspaceError = readStringValue(cachedStatus.error, node?.error);
46745
+ if (workspaceError && /workspace must be an existing directory/i.test(workspaceError)) return true;
46746
+ const isGitRepo = readBooleanValue(cachedGit.isGitRepo);
46747
+ const branch = readStringValue(cachedGit.branch);
46748
+ const headCommit = readStringValue(cachedGit.headCommit);
46749
+ return isGitRepo === false && !branch && !headCommit;
46750
+ }
46751
+ function stripInlineMeshTransientNodeState(node) {
46752
+ if (!node || typeof node !== "object" || Array.isArray(node)) return node;
46753
+ const {
46754
+ cachedStatus,
46755
+ lastGit: _lastGit,
46756
+ last_git: _lastGitLegacy,
46757
+ lastProbe: _lastProbe,
46758
+ last_probe: _lastProbeLegacy,
46759
+ error: _error,
46760
+ health: _health,
46761
+ machineStatus: _machineStatus,
46762
+ lastSeenAt: _lastSeenAt,
46763
+ last_seen_at: _lastSeenAtLegacy,
46764
+ updatedAt: _updatedAt,
46765
+ updated_at: _updatedAtLegacy,
46766
+ activeSession: _activeSession,
46767
+ active_session: _activeSessionLegacy,
46768
+ activeSessionId: _activeSessionId,
46769
+ active_session_id: _activeSessionIdLegacy,
46770
+ sessionId: _sessionId,
46771
+ session_id: _sessionIdLegacy,
46772
+ providerType: _providerType,
46773
+ provider_type: _providerTypeLegacy,
46774
+ ...rest
46775
+ } = node;
46776
+ if (cachedStatus && !shouldDiscardCachedInlineMeshStatus(node)) {
46777
+ return { ...rest, cachedStatus };
46778
+ }
46779
+ return rest;
46780
+ }
46781
+ function hasInlineMeshTransientNodeState(node) {
46782
+ if (!node || typeof node !== "object" || Array.isArray(node)) return false;
46783
+ return "cachedStatus" in node || "lastGit" in node || "last_git" in node || "lastProbe" in node || "last_probe" in node || "error" in node || "health" in node || "machineStatus" in node || "lastSeenAt" in node || "last_seen_at" in node || "updatedAt" in node || "updated_at" in node || "activeSession" in node || "active_session" in node || "activeSessionId" in node || "active_session_id" in node || "sessionId" in node || "session_id" in node || "providerType" in node || "provider_type" in node;
46784
+ }
46785
+ function readInlineMeshNodeId(node) {
46786
+ return readStringValue(node?.id, node?.nodeId) || "";
46787
+ }
46788
+ function sanitizeInlineMesh(inlineMesh) {
46789
+ if (!inlineMesh || typeof inlineMesh !== "object" || Array.isArray(inlineMesh)) return inlineMesh;
46790
+ if (!Array.isArray(inlineMesh.nodes)) return inlineMesh;
46791
+ let changed = false;
46792
+ const nodes = inlineMesh.nodes.map((node) => {
46793
+ if (!hasInlineMeshTransientNodeState(node)) return node;
46794
+ changed = true;
46795
+ return stripInlineMeshTransientNodeState(node);
46796
+ });
46797
+ if (!changed) return inlineMesh;
46798
+ return {
46799
+ ...inlineMesh,
46800
+ nodes
46801
+ };
46802
+ }
46803
+ function reconcileInlineMeshCache(cached2, incoming) {
46804
+ if (!cached2 || typeof cached2 !== "object" || Array.isArray(cached2)) return incoming;
46805
+ if (!incoming || typeof incoming !== "object" || Array.isArray(incoming)) return cached2;
46806
+ const cachedNodes = Array.isArray(cached2.nodes) ? cached2.nodes : [];
46807
+ const incomingNodes = Array.isArray(incoming.nodes) ? incoming.nodes : [];
46808
+ if (!cachedNodes.length || !incomingNodes.length) return { ...cached2, ...incoming };
46809
+ const incomingById = /* @__PURE__ */ new Map();
46810
+ for (const node of incomingNodes) {
46811
+ const nodeId = readInlineMeshNodeId(node);
46812
+ if (nodeId) incomingById.set(nodeId, node);
46813
+ }
46814
+ const nodes = cachedNodes.map((cachedNode) => {
46815
+ const nodeId = readInlineMeshNodeId(cachedNode);
46816
+ const incomingNode = nodeId ? incomingById.get(nodeId) : void 0;
46817
+ if (!incomingNode) return cachedNode;
46818
+ if (hasInlineMeshTransientNodeState(incomingNode)) {
46819
+ return { ...cachedNode, ...incomingNode };
46820
+ }
46821
+ return { ...stripInlineMeshTransientNodeState(cachedNode), ...incomingNode };
46822
+ });
46823
+ return {
46824
+ ...cached2,
46825
+ ...incoming,
46826
+ nodes
46827
+ };
46828
+ }
46829
+ function hasGitWorktreeChanges(git) {
46830
+ if (!git) return false;
46831
+ return Number(git.staged || 0) + Number(git.modified || 0) + Number(git.untracked || 0) + Number(git.deleted || 0) + Number(git.renamed || 0) > 0;
46832
+ }
46833
+ function getGitSubmoduleDriftState(git) {
46834
+ const submodules = Array.isArray(git?.submodules) ? git.submodules : [];
46835
+ let dirty = false;
46836
+ let outOfSync = false;
46837
+ for (const entry of submodules) {
46838
+ const submodule = readObjectRecord(entry);
46839
+ if (readBooleanValue(submodule.dirty) === true) dirty = true;
46840
+ if (readBooleanValue(submodule.outOfSync) === true || !!readStringValue(submodule.error)) outOfSync = true;
46841
+ }
46842
+ return { dirty, outOfSync };
46843
+ }
46844
+ function deriveMeshNodeHealthFromGit(git) {
46845
+ if (!git || readBooleanValue(git.isGitRepo) === false) return "degraded";
46846
+ const branch = readStringValue(git.branch);
46847
+ if (!branch) return "degraded";
46848
+ const submoduleDrift = getGitSubmoduleDriftState(git);
46849
+ if (submoduleDrift.outOfSync) return "degraded";
46850
+ if (submoduleDrift.dirty || hasGitWorktreeChanges(git)) return "dirty";
46851
+ return "online";
46852
+ }
46853
+ function readCachedInlineMeshActiveSessions(node) {
46854
+ const cachedStatus = readObjectRecord(node?.cachedStatus);
46855
+ const activeSession = readObjectRecord(cachedStatus.activeSession);
46856
+ const fallbackSession = Object.keys(activeSession).length ? activeSession : readObjectRecord(node?.activeSession ?? node?.active_session);
46857
+ const sessionId = readStringValue(fallbackSession.id, fallbackSession.sessionId, fallbackSession.session_id, node?.activeSessionId, node?.active_session_id, node?.sessionId, node?.session_id);
46858
+ return sessionId ? [sessionId] : [];
46859
+ }
46860
+ function readCachedInlineMeshActiveSessionDetails(node) {
46861
+ const cachedStatus = readObjectRecord(node?.cachedStatus);
46862
+ const activeSession = readObjectRecord(cachedStatus.activeSession);
46863
+ const fallbackSession = Object.keys(activeSession).length ? activeSession : readObjectRecord(node?.activeSession ?? node?.active_session);
46864
+ const sessionId = readStringValue(
46865
+ fallbackSession.id,
46866
+ fallbackSession.sessionId,
46867
+ fallbackSession.session_id,
46868
+ node?.activeSessionId,
46869
+ node?.active_session_id,
46870
+ node?.sessionId,
46871
+ node?.session_id
46872
+ );
46873
+ if (!sessionId) return [];
46874
+ return [{
46875
+ sessionId,
46876
+ providerType: readStringValue(
46877
+ fallbackSession.providerType,
46878
+ fallbackSession.provider_type,
46879
+ fallbackSession.cliType,
46880
+ fallbackSession.cli_type,
46881
+ fallbackSession.provider,
46882
+ node?.providerType,
46883
+ node?.provider_type
46884
+ ),
46885
+ state: readStringValue(fallbackSession.status, fallbackSession.state, fallbackSession.lifecycle),
46886
+ lifecycle: readStringValue(fallbackSession.lifecycle),
46887
+ title: readStringValue(fallbackSession.title, fallbackSession.displayName, fallbackSession.display_name) ?? null,
46888
+ workspace: readStringValue(fallbackSession.workspace, node?.workspace) ?? null,
46889
+ lastActivityAt: readStringValue(fallbackSession.lastActivityAt, fallbackSession.last_activity_at) ?? null,
46890
+ recoveryState: readStringValue(fallbackSession.recoveryState, fallbackSession.recovery_state) ?? null,
46891
+ isCached: true
46892
+ }];
46893
+ }
46894
+ function readLiveMeshSessionState(record2) {
46895
+ return readStringValue(
46896
+ record2?.meta?.sessionStatus,
46897
+ record2?.meta?.status,
46898
+ record2?.meta?.providerStatus,
46899
+ record2?.status,
46900
+ record2?.state,
46901
+ record2?.lifecycle
46902
+ );
46903
+ }
46904
+ function toIsoTimestamp(value) {
46905
+ if (typeof value === "number" && Number.isFinite(value)) return new Date(value).toISOString();
46906
+ const stringValue = readStringValue(value);
46907
+ return stringValue || null;
46908
+ }
46909
+ function synthesizeMeshNodeFreshnessFromConnection(status) {
46910
+ const connection = readObjectRecord(status.connection);
46911
+ const connectionFreshAt = toIsoTimestamp(connection.lastCommandAt ?? connection.lastConnectedAt ?? connection.lastStateChangeAt);
46912
+ const git = readObjectRecord(status.git);
46913
+ const gitCheckedAt = toIsoTimestamp(git.lastCheckedAt);
46914
+ if (!status.lastSeenAt && connectionFreshAt) status.lastSeenAt = connectionFreshAt;
46915
+ if (!status.updatedAt && (gitCheckedAt || connectionFreshAt)) {
46916
+ status.updatedAt = gitCheckedAt ?? connectionFreshAt;
46917
+ }
46918
+ }
46919
+ function finalizeMeshNodeStatus(args) {
46920
+ const { status, node, daemonId, isSelfNode } = args;
46921
+ if (!readStringValue(status.machineStatus)) {
46922
+ const cachedStatus = readObjectRecord(node?.cachedStatus);
46923
+ const machineStatus = readStringValue(cachedStatus.machineStatus, cachedStatus.machine_status, node?.machineStatus);
46924
+ if (machineStatus) status.machineStatus = machineStatus;
46925
+ }
46926
+ synthesizeMeshNodeFreshnessFromConnection(status);
46927
+ const connectionState = readStringValue(readObjectRecord(status.connection).state);
46928
+ status.launchReady = !!daemonId && (readStringValue(status.machineStatus) === "online" || connectionState === "connected" || isSelfNode);
46929
+ }
46930
+ async function probeRemoteMeshGitStatus(args) {
46931
+ if (!args.dispatchMeshCommand) return null;
46932
+ const remoteResult = await Promise.race([
46933
+ args.dispatchMeshCommand(args.daemonId, "git_status", { workspace: args.workspace }),
46934
+ new Promise((_2, reject) => setTimeout(() => reject(new Error("timeout")), args.timeoutMs))
46935
+ ]);
46936
+ const remoteGit = remoteResult?.status ?? remoteResult?.git ?? remoteResult;
46937
+ return remoteGit && typeof remoteGit === "object" && typeof remoteGit.isGitRepo === "boolean" ? remoteGit : null;
46938
+ }
46939
+ async function hydrateInlineMeshDirectTruth(args) {
46940
+ const nodes = Array.isArray(args.mesh?.nodes) ? args.mesh.nodes : [];
46941
+ if (!nodes.length) {
46942
+ return {
46943
+ directEvidenceCount: 0,
46944
+ localConfirmedCount: 0,
46945
+ peerAttemptedCount: 0,
46946
+ peerConfirmedCount: 0,
46947
+ unavailableNodeIds: []
46948
+ };
46949
+ }
46950
+ const selectedCoordinatorNodeId = readStringValue(
46951
+ args.mesh?.coordinator?.preferredNodeId,
46952
+ nodes[0]?.id,
46953
+ nodes[0]?.nodeId
46954
+ );
46955
+ let localConfirmedCount = 0;
46956
+ let peerAttemptedCount = 0;
46957
+ let peerConfirmedCount = 0;
46958
+ const unavailableNodeIds = [];
46959
+ for (const [nodeIndex, node] of nodes.entries()) {
46960
+ const nodeId = readStringValue(node?.id, node?.nodeId) || `node_${nodeIndex}`;
46961
+ const workspace = readStringValue(node?.workspace);
46962
+ const daemonId = readStringValue(node?.daemonId);
46963
+ const isSelfNode = Boolean(
46964
+ nodeId && selectedCoordinatorNodeId && nodeId === selectedCoordinatorNodeId
46965
+ ) || Boolean(
46966
+ daemonId && (daemonId === args.localMachineId || daemonId === args.statusInstanceId)
46967
+ ) || Boolean(args.meshSource !== "local_config" && nodeIndex === 0);
46968
+ if (!workspace) {
46969
+ if (!isSelfNode && daemonId) unavailableNodeIds.push(nodeId);
46970
+ continue;
46971
+ }
46972
+ if (isSelfNode && fs10.existsSync(workspace)) {
46973
+ try {
46974
+ const localGit = await getGitRepoStatus(workspace, { timeoutMs: 1e4, refreshUpstream: true });
46975
+ if (localGit?.isGitRepo) {
46976
+ recordInlineMeshDirectGitTruth(node, localGit, "selected_coordinator_local_git");
46977
+ localConfirmedCount += 1;
46978
+ continue;
46979
+ }
46980
+ } catch {
46981
+ }
46982
+ }
46983
+ if (!daemonId || !args.dispatchMeshCommand) {
46984
+ if (!isSelfNode) unavailableNodeIds.push(nodeId);
46985
+ continue;
46986
+ }
46987
+ peerAttemptedCount += 1;
46988
+ try {
46989
+ const remoteGit = await probeRemoteMeshGitStatus({
46990
+ dispatchMeshCommand: args.dispatchMeshCommand,
46991
+ daemonId,
46992
+ workspace,
46993
+ timeoutMs: 8e3
46994
+ });
46995
+ if (remoteGit) {
46996
+ recordInlineMeshDirectGitTruth(node, remoteGit, "selected_coordinator_mesh_p2p_git");
46997
+ peerConfirmedCount += 1;
46998
+ continue;
46999
+ }
47000
+ } catch {
47001
+ }
47002
+ unavailableNodeIds.push(nodeId);
47003
+ }
47004
+ return {
47005
+ directEvidenceCount: localConfirmedCount + peerConfirmedCount,
47006
+ localConfirmedCount,
47007
+ peerAttemptedCount,
47008
+ peerConfirmedCount,
47009
+ unavailableNodeIds
47010
+ };
47011
+ }
47012
+ function summarizeMeshSessionRecord(record2) {
47013
+ return {
47014
+ sessionId: readStringValue(record2?.sessionId) || "unknown",
47015
+ providerType: readStringValue(record2?.providerType),
47016
+ state: readLiveMeshSessionState(record2),
47017
+ lifecycle: readStringValue(record2?.lifecycle),
47018
+ surfaceKind: getSessionHostSurfaceKind(record2),
47019
+ recoveryState: readStringValue(record2?.meta?.runtimeRecoveryState) ?? null,
47020
+ workspace: readStringValue(record2?.workspace) ?? null,
47021
+ title: readStringValue(record2?.displayName, record2?.workspaceLabel) ?? null,
47022
+ lastActivityAt: toIsoTimestamp(record2?.updatedAt ?? record2?.lastActivityAt ?? record2?.last_activity_at),
47023
+ isCached: false
47024
+ };
47025
+ }
47026
+ function liveSessionRecordMatchesMeshNode(record2, meshId, nodeId) {
47027
+ const recordNodeId = readStringValue(record2?.meta?.meshNodeId);
47028
+ if (!recordNodeId || recordNodeId !== nodeId) return false;
47029
+ const recordMeshId = readStringValue(record2?.meta?.meshNodeFor);
47030
+ return !recordMeshId || recordMeshId === meshId;
47031
+ }
47032
+ function liveSessionRecordMatchesMeshWorkspace(record2, meshId, workspace) {
47033
+ const recordWorkspace = readStringValue(record2?.workspace);
47034
+ if (!recordWorkspace || !workspace || recordWorkspace !== workspace) return false;
47035
+ const recordMeshId = readStringValue(record2?.meta?.meshNodeFor);
47036
+ if (recordMeshId) return recordMeshId === meshId;
47037
+ return record2?.meta?.launchedByCoordinator === true || !!readStringValue(record2?.meta?.meshNodeId);
47038
+ }
47039
+ function readLiveMeshNodeWorkspace(args) {
47040
+ const directNodeWorkspace = args.liveSessionRecords.find((record2) => liveSessionRecordMatchesMeshNode(record2, args.meshId, args.nodeId) && readStringValue(record2?.workspace));
47041
+ if (directNodeWorkspace) {
47042
+ return readStringValue(directNodeWorkspace.workspace) || "";
47043
+ }
47044
+ if (args.allowCoordinatorSession) {
47045
+ const coordinatorWorkspace = args.liveSessionRecords.find((record2) => readStringValue(record2?.meta?.meshCoordinatorFor) === args.meshId && readStringValue(record2?.workspace));
47046
+ if (coordinatorWorkspace) {
47047
+ return readStringValue(coordinatorWorkspace.workspace) || "";
47048
+ }
47049
+ }
47050
+ return "";
47051
+ }
47052
+ function collectLiveMeshSessionRecords(args) {
47053
+ const matches = args.liveSessionRecords.filter((record2) => {
47054
+ const nodeWorkspace = readStringValue(args.node?.workspace);
47055
+ if (liveSessionRecordMatchesMeshNode(record2, args.meshId, args.nodeId)) return true;
47056
+ return !!nodeWorkspace && liveSessionRecordMatchesMeshWorkspace(record2, args.meshId, nodeWorkspace);
47057
+ });
47058
+ if (args.allowCoordinatorSession) {
47059
+ for (const record2 of args.liveSessionRecords) {
47060
+ if (readStringValue(record2?.meta?.meshCoordinatorFor) !== args.meshId) continue;
47061
+ const sessionId = readStringValue(record2?.sessionId);
47062
+ if (sessionId && matches.some((entry) => readStringValue(entry?.sessionId) === sessionId)) continue;
47063
+ matches.push(record2);
47064
+ }
47065
+ }
47066
+ return matches;
47067
+ }
47068
+ function applyCachedInlineMeshNodeStatus(status, node, options) {
47069
+ const cachedStatus = readObjectRecord(node?.cachedStatus);
47070
+ const liveGit = buildInlineMeshTransitGitStatus(node);
47071
+ const git = options?.skipGit ? void 0 : liveGit ?? buildCachedInlineMeshGitStatus(node);
47072
+ const error48 = options?.skipError ? void 0 : liveGit ? void 0 : readStringValue(cachedStatus.error, node?.error);
47073
+ const health = options?.skipHealth ? void 0 : liveGit ? void 0 : readStringValue(cachedStatus.health, node?.health);
46477
47074
  const machineStatus = readStringValue(cachedStatus.machineStatus, node?.machineStatus);
46478
- if (!git && !error48 && !health) return false;
46479
- if (!machineStatus && !git && !error48) return false;
47075
+ const lastSeenAt = toIsoTimestamp(cachedStatus.lastSeenAt ?? cachedStatus.last_seen_at ?? node?.lastSeenAt ?? node?.last_seen_at);
47076
+ const updatedAt = toIsoTimestamp(cachedStatus.updatedAt ?? cachedStatus.updated_at ?? node?.updatedAt ?? node?.updated_at);
47077
+ const activeSessions = readCachedInlineMeshActiveSessions(node);
47078
+ const activeSessionDetails = readCachedInlineMeshActiveSessionDetails(node);
47079
+ if (!git && !error48 && !health && !machineStatus && !lastSeenAt && !updatedAt && activeSessions.length === 0) return false;
46480
47080
  if (git) status.git = git;
46481
47081
  if (error48) status.error = error48;
47082
+ if (machineStatus) status.machineStatus = machineStatus;
47083
+ if (lastSeenAt) status.lastSeenAt = lastSeenAt;
47084
+ if (updatedAt) status.updatedAt = updatedAt;
47085
+ if (activeSessions.length > 0) status.activeSessions = activeSessions;
47086
+ if (activeSessionDetails.length > 0) status.activeSessionDetails = activeSessionDetails;
46482
47087
  if (health) {
46483
47088
  status.health = health;
46484
47089
  return true;
46485
47090
  }
46486
47091
  if (git) {
46487
- const dirty = Number(git.staged || 0) + Number(git.modified || 0) + Number(git.untracked || 0) + Number(git.deleted || 0) + Number(git.renamed || 0) > 0;
46488
- status.health = git.isGitRepo === false ? "degraded" : dirty ? "dirty" : "online";
47092
+ status.health = deriveMeshNodeHealthFromGit(git);
46489
47093
  return true;
46490
47094
  }
46491
- return false;
47095
+ return activeSessions.length > 0 || !!machineStatus || !!lastSeenAt || !!updatedAt;
46492
47096
  }
46493
47097
  async function resolveProviderTypeFromPriority(args) {
46494
47098
  if (!args.providerPriority.length) {
@@ -46521,7 +47125,7 @@ function truncateValidationOutput(value) {
46521
47125
  }
46522
47126
  function readPackageScripts(workspace) {
46523
47127
  try {
46524
- const packageJsonPath = (0, import_path6.join)(workspace, "package.json");
47128
+ const packageJsonPath = (0, import_path7.join)(workspace, "package.json");
46525
47129
  const parsed = JSON.parse(fs10.readFileSync(packageJsonPath, "utf-8"));
46526
47130
  return parsed?.scripts && typeof parsed.scripts === "object" && !Array.isArray(parsed.scripts) ? parsed.scripts : {};
46527
47131
  } catch {
@@ -46729,13 +47333,13 @@ function serializeMeshCoordinatorMcpConfig(config2, format) {
46729
47333
  }
46730
47334
  function resolveHermesUserHome() {
46731
47335
  const explicitHome = process.env.HERMES_HOME?.trim();
46732
- return explicitHome || (0, import_path6.join)((0, import_os4.homedir)(), ".hermes");
47336
+ return explicitHome || (0, import_path7.join)((0, import_os4.homedir)(), ".hermes");
46733
47337
  }
46734
47338
  function loadHermesCoordinatorBaseConfig(targetConfigPath) {
46735
47339
  const sourceHome = resolveHermesUserHome();
46736
- const sourceConfigPath = (0, import_path6.join)(sourceHome, "config.yaml");
47340
+ const sourceConfigPath = (0, import_path7.join)(sourceHome, "config.yaml");
46737
47341
  if (!fs10.existsSync(sourceConfigPath)) return { config: {}, sourceHome, sourceConfigPath };
46738
- if ((0, import_path6.resolve)(sourceConfigPath) === (0, import_path6.resolve)(targetConfigPath)) return { config: {}, sourceHome, sourceConfigPath };
47342
+ if ((0, import_path7.resolve)(sourceConfigPath) === (0, import_path7.resolve)(targetConfigPath)) return { config: {}, sourceHome, sourceConfigPath };
46739
47343
  const parsed = parseMeshCoordinatorMcpConfig(fs10.readFileSync(sourceConfigPath, "utf-8"), "hermes_config_yaml");
46740
47344
  const { mcp_servers: _mcpServers, ...baseConfig } = parsed;
46741
47345
  return { config: baseConfig, sourceHome, sourceConfigPath };
@@ -46769,10 +47373,10 @@ function stripHermesCoordinatorTempModelProviderOverrides(config2) {
46769
47373
  return sanitized;
46770
47374
  }
46771
47375
  function copyHermesCoordinatorCredentialFiles(sourceHome, targetHome) {
46772
- if ((0, import_path6.resolve)(sourceHome) === (0, import_path6.resolve)(targetHome)) return;
47376
+ if ((0, import_path7.resolve)(sourceHome) === (0, import_path7.resolve)(targetHome)) return;
46773
47377
  for (const fileName of [".env", "auth.json"]) {
46774
- const sourcePath = (0, import_path6.join)(sourceHome, fileName);
46775
- const targetPath = (0, import_path6.join)(targetHome, fileName);
47378
+ const sourcePath = (0, import_path7.join)(sourceHome, fileName);
47379
+ const targetPath = (0, import_path7.join)(targetHome, fileName);
46776
47380
  if (!fs10.existsSync(sourcePath)) continue;
46777
47381
  try {
46778
47382
  fs10.copyFileSync(sourcePath, targetPath);
@@ -46862,7 +47466,7 @@ function summarizeSessionHostPruneResult(result) {
46862
47466
  keptCount: Array.isArray(value.keptSessionIds) ? value.keptSessionIds.length : void 0
46863
47467
  };
46864
47468
  }
46865
- var import_os4, import_path6, fs10, CHANNEL_NPM_TAG, CHANNEL_SERVER_URL, REFINE_VALIDATION_CATEGORIES, REFINE_VALIDATION_TIMEOUT_MS, REFINE_VALIDATION_OUTPUT_LIMIT_BYTES, REFINE_VALIDATION_SUMMARY_CHARS, REFINE_VALIDATION_MAX_COMMANDS, CHAT_COMMANDS, READ_DEBUG_ENABLED2, DaemonCommandRouter;
47469
+ var import_os4, import_path7, fs10, CHANNEL_NPM_TAG, CHANNEL_SERVER_URL, REFINE_VALIDATION_CATEGORIES, REFINE_VALIDATION_TIMEOUT_MS, REFINE_VALIDATION_OUTPUT_LIMIT_BYTES, REFINE_VALIDATION_SUMMARY_CHARS, REFINE_VALIDATION_MAX_COMMANDS, CHAT_COMMANDS, READ_DEBUG_ENABLED2, DaemonCommandRouter;
46866
47470
  var init_router = __esm({
46867
47471
  "../../oss/packages/daemon-core/src/commands/router.ts"() {
46868
47472
  "use strict";
@@ -46878,6 +47482,7 @@ var init_router = __esm({
46878
47482
  init_chat_history();
46879
47483
  init_ide_detector();
46880
47484
  init_cli_detector();
47485
+ init_git_status();
46881
47486
  init_logger();
46882
47487
  init_command_log();
46883
47488
  init_js_yaml();
@@ -46891,7 +47496,7 @@ var init_router = __esm({
46891
47496
  init_snapshot();
46892
47497
  init_upgrade_helper();
46893
47498
  import_os4 = require("os");
46894
- import_path6 = require("path");
47499
+ import_path7 = require("path");
46895
47500
  fs10 = __toESM(require("fs"));
46896
47501
  CHANNEL_NPM_TAG = { stable: "latest", preview: "next" };
46897
47502
  CHANNEL_SERVER_URL = {
@@ -46922,25 +47527,40 @@ var init_router = __esm({
46922
47527
  }
46923
47528
  getCachedInlineMesh(meshId, inlineMesh) {
46924
47529
  if (inlineMesh && typeof inlineMesh === "object") {
46925
- this.inlineMeshCache.set(meshId, inlineMesh);
46926
- return inlineMesh;
47530
+ return this.warmInlineMeshCache(meshId, inlineMesh);
46927
47531
  }
46928
47532
  return this.inlineMeshCache.get(meshId);
46929
47533
  }
47534
+ warmInlineMeshCache(meshId, inlineMesh) {
47535
+ if (!inlineMesh || typeof inlineMesh !== "object") return void 0;
47536
+ const sanitizedInlineMesh = sanitizeInlineMesh(inlineMesh);
47537
+ const cached2 = this.inlineMeshCache.get(meshId);
47538
+ if (cached2) {
47539
+ const merged = reconcileInlineMeshCache(cached2, sanitizedInlineMesh);
47540
+ this.inlineMeshCache.set(meshId, merged);
47541
+ return merged;
47542
+ }
47543
+ this.inlineMeshCache.set(meshId, sanitizedInlineMesh);
47544
+ return sanitizedInlineMesh;
47545
+ }
46930
47546
  async getMeshForCommand(meshId, inlineMesh, options) {
46931
47547
  const preferInline = options?.preferInline === true;
46932
47548
  if (preferInline) {
46933
- const cached3 = this.getCachedInlineMesh(meshId, inlineMesh);
46934
- if (cached3) return { mesh: cached3, inline: true };
47549
+ const cached3 = this.getCachedInlineMesh(meshId);
47550
+ if (cached3) return { mesh: cached3, inline: true, source: "inline_cache" };
47551
+ const warmedInline2 = this.warmInlineMeshCache(meshId, inlineMesh);
47552
+ if (warmedInline2) return { mesh: warmedInline2, inline: true, source: "inline_bootstrap" };
46935
47553
  }
46936
47554
  try {
46937
47555
  const { getMesh: getMesh3 } = await Promise.resolve().then(() => (init_mesh_config(), mesh_config_exports));
46938
47556
  const mesh = getMesh3(meshId);
46939
- if (mesh) return { mesh, inline: false };
47557
+ if (mesh) return { mesh, inline: false, source: "local_config" };
46940
47558
  } catch {
46941
47559
  }
46942
- const cached2 = this.getCachedInlineMesh(meshId, inlineMesh);
46943
- return cached2 ? { mesh: cached2, inline: true } : null;
47560
+ const cached2 = this.getCachedInlineMesh(meshId);
47561
+ if (cached2) return { mesh: cached2, inline: true, source: "inline_cache" };
47562
+ const warmedInline = this.warmInlineMeshCache(meshId, inlineMesh);
47563
+ return warmedInline ? { mesh: warmedInline, inline: true, source: "inline_bootstrap" } : null;
46944
47564
  }
46945
47565
  updateInlineMeshNode(meshId, mesh, node) {
46946
47566
  if (!mesh || !Array.isArray(mesh.nodes) || !node?.id) return;
@@ -47005,7 +47625,7 @@ var init_router = __esm({
47005
47625
  }
47006
47626
  const { resolveWorktreePath: resolveWorktreePath2, listWorktrees: listWorktrees2, removeWorktree: removeWorktree2 } = await Promise.resolve().then(() => (init_git_worktree(), git_worktree_exports));
47007
47627
  const normalizePath3 = (value) => {
47008
- const resolved = (0, import_path6.resolve)(value);
47628
+ const resolved = (0, import_path7.resolve)(value);
47009
47629
  try {
47010
47630
  return fs10.realpathSync(resolved);
47011
47631
  } catch {
@@ -47169,6 +47789,7 @@ var init_router = __esm({
47169
47789
  const deletedSessionIds = [];
47170
47790
  const skippedSessionIds = [];
47171
47791
  const skippedLiveSessionIds = [];
47792
+ const skippedCoordinatorSessionIds = [];
47172
47793
  const deleteUnsupportedSessionIds = [];
47173
47794
  const recordsRemainSessionIds = [];
47174
47795
  const errors = [];
@@ -47201,6 +47822,12 @@ var init_router = __esm({
47201
47822
  const completed = this.isCompletedHostedSession(record2);
47202
47823
  const surfaceKind = getSessionHostSurfaceKind(record2);
47203
47824
  const liveRuntime = surfaceKind === "live_runtime";
47825
+ const coordinatorSession = readStringValue(record2?.meta?.meshCoordinatorFor) === args.meshId;
47826
+ if (!hasExplicitSessionIds && coordinatorSession) {
47827
+ skippedSessionIds.push(sessionId);
47828
+ skippedCoordinatorSessionIds.push(sessionId);
47829
+ continue;
47830
+ }
47204
47831
  if (!hasExplicitSessionIds && liveRuntime) {
47205
47832
  skippedSessionIds.push(sessionId);
47206
47833
  skippedLiveSessionIds.push(sessionId);
@@ -47266,6 +47893,7 @@ var init_router = __esm({
47266
47893
  deletedSessionIds,
47267
47894
  skippedSessionIds,
47268
47895
  skippedLiveSessionIds,
47896
+ skippedCoordinatorSessionIds,
47269
47897
  ...deleteUnsupported ? {
47270
47898
  deleteUnsupported: true,
47271
47899
  effectiveCleanup: args.mode === "stop_and_delete" ? "stopped_only_records_remain" : "delete_unsupported_records_remain",
@@ -47398,7 +48026,8 @@ var init_router = __esm({
47398
48026
  return handleMeshForwardEvent({ instanceManager: this.deps.instanceManager }, args);
47399
48027
  }
47400
48028
  case "get_pending_mesh_events": {
47401
- const events = drainPendingMeshCoordinatorEvents();
48029
+ const meshId = typeof args?.meshId === "string" ? args.meshId.trim() : "";
48030
+ const events = drainPendingMeshCoordinatorEvents(meshId || void 0);
47402
48031
  return { success: true, events };
47403
48032
  }
47404
48033
  case "launch_cli":
@@ -47927,15 +48556,39 @@ var init_router = __esm({
47927
48556
  case "get_mesh": {
47928
48557
  const meshId = typeof args?.meshId === "string" ? args.meshId.trim() : "";
47929
48558
  if (!meshId) return { success: false, error: "meshId required" };
47930
- try {
47931
- const { getMesh: getMesh3 } = await Promise.resolve().then(() => (init_mesh_config(), mesh_config_exports));
47932
- const mesh = getMesh3(meshId);
47933
- if (mesh) return { success: true, mesh };
47934
- } catch {
48559
+ const meshRecord = await this.getMeshForCommand(meshId, args?.inlineMesh, { preferInline: true });
48560
+ if (!meshRecord?.mesh) return { success: false, error: "Mesh not found" };
48561
+ const requireDirectPeerTruth = args?.requireDirectPeerTruth === true;
48562
+ const directTruth = await hydrateInlineMeshDirectTruth({
48563
+ mesh: meshRecord.mesh,
48564
+ meshSource: meshRecord.source,
48565
+ dispatchMeshCommand: this.deps.dispatchMeshCommand,
48566
+ statusInstanceId: this.deps.statusInstanceId,
48567
+ localMachineId: loadConfig().machineId || ""
48568
+ });
48569
+ const directTruthSatisfied = meshRecord.source !== "inline_bootstrap" || directTruth.directEvidenceCount > 0;
48570
+ const sourceOfTruth = {
48571
+ membership: meshRecord.source === "inline_cache" ? "coordinator_inline_mesh_cache" : meshRecord.source === "local_config" ? "local_mesh_config" : "inline_bootstrap_snapshot",
48572
+ coordinatorOwnsLiveTruth: directTruthSatisfied,
48573
+ directPeerTruth: {
48574
+ required: requireDirectPeerTruth,
48575
+ satisfied: directTruthSatisfied,
48576
+ directEvidenceCount: directTruth.directEvidenceCount,
48577
+ localConfirmedCount: directTruth.localConfirmedCount,
48578
+ peerAttemptedCount: directTruth.peerAttemptedCount,
48579
+ peerConfirmedCount: directTruth.peerConfirmedCount,
48580
+ unavailableNodeIds: directTruth.unavailableNodeIds
48581
+ }
48582
+ };
48583
+ if (requireDirectPeerTruth && !directTruthSatisfied) {
48584
+ return {
48585
+ success: false,
48586
+ code: "mesh_direct_peer_truth_unavailable",
48587
+ error: "Selected coordinator could not confirm direct mesh truth yet. Bootstrap inventory stays unavailable until direct get_mesh probes succeed.",
48588
+ sourceOfTruth
48589
+ };
47935
48590
  }
47936
- const cached2 = this.inlineMeshCache.get(meshId);
47937
- if (cached2) return { success: true, mesh: cached2 };
47938
- return { success: false, error: "Mesh not found" };
48591
+ return { success: true, mesh: meshRecord.mesh, sourceOfTruth };
47939
48592
  }
47940
48593
  case "create_mesh": {
47941
48594
  const name = typeof args?.name === "string" ? args.name.trim() : "";
@@ -48456,7 +49109,14 @@ var init_router = __esm({
48456
49109
  cliType
48457
49110
  };
48458
49111
  }
48459
- const workspace = typeof coordinatorNode.workspace === "string" ? coordinatorNode.workspace.trim() : "";
49112
+ const sessionHostRecords = this.deps.sessionHostControl?.listSessions ? await this.deps.sessionHostControl.listSessions().catch(() => []) : [];
49113
+ const liveMeshSessions = partitionSessionHostRecords(Array.isArray(sessionHostRecords) ? sessionHostRecords : []).liveRuntimes;
49114
+ const workspace = readLiveMeshNodeWorkspace({
49115
+ meshId,
49116
+ nodeId: String(coordinatorNode.id || coordinatorNode.nodeId || preferredCoordinatorNodeId || ""),
49117
+ liveSessionRecords: liveMeshSessions,
49118
+ allowCoordinatorSession: true
49119
+ }) || (typeof coordinatorNode.workspace === "string" ? coordinatorNode.workspace.trim() : "");
48460
49120
  if (!workspace) return { success: false, error: "Coordinator node workspace required", meshId, cliType };
48461
49121
  if (!cliType) {
48462
49122
  const resolved = await resolveProviderTypeFromPriority({
@@ -48618,7 +49278,7 @@ ${block}`);
48618
49278
  workspace
48619
49279
  };
48620
49280
  }
48621
- const { existsSync: existsSync32, readFileSync: readFileSync23, writeFileSync: writeFileSync18, copyFileSync: copyFileSync5, mkdirSync: mkdirSync22 } = await import("fs");
49281
+ const { existsSync: existsSync33, readFileSync: readFileSync24, writeFileSync: writeFileSync18, copyFileSync: copyFileSync5, mkdirSync: mkdirSync22 } = await import("fs");
48622
49282
  const { dirname: dirname13 } = await import("path");
48623
49283
  const mcpConfigPath = coordinatorSetup.configPath;
48624
49284
  const hermesManualFallback = cliType === "hermes-cli" && configFormat === "hermes_config_yaml" ? createHermesManualMeshCoordinatorSetup(meshId, workspace) : null;
@@ -48661,14 +49321,14 @@ ${block}`);
48661
49321
  if (hermesManualFallback) return returnManualFallback(message);
48662
49322
  return { success: false, code: "mesh_coordinator_config_write_failed", error: message, meshId, cliType, workspace };
48663
49323
  }
48664
- const hadExistingMcpConfig = existsSync32(mcpConfigPath);
49324
+ const hadExistingMcpConfig = existsSync33(mcpConfigPath);
48665
49325
  let existingMcpConfig = hermesBaseConfig?.config || {};
48666
49326
  if (hermesBaseConfig) {
48667
49327
  copyHermesCoordinatorCredentialFiles(hermesBaseConfig.sourceHome, dirname13(mcpConfigPath));
48668
49328
  }
48669
49329
  if (hadExistingMcpConfig) {
48670
49330
  try {
48671
- const parsedExistingMcpConfig = parseMeshCoordinatorMcpConfig(readFileSync23(mcpConfigPath, "utf-8"), configFormat);
49331
+ const parsedExistingMcpConfig = parseMeshCoordinatorMcpConfig(readFileSync24(mcpConfigPath, "utf-8"), configFormat);
48672
49332
  const existingCoordinatorConfig = hermesManualFallback ? stripHermesCoordinatorTempModelProviderOverrides(parsedExistingMcpConfig) : parsedExistingMcpConfig;
48673
49333
  existingMcpConfig = { ...existingMcpConfig, ...existingCoordinatorConfig };
48674
49334
  copyFileSync5(mcpConfigPath, mcpConfigPath + ".backup");
@@ -48764,92 +49424,184 @@ ${block}`);
48764
49424
  const { readLedgerEntries: readLedgerEntries2, getLedgerSummary: getLedgerSummary2 } = await Promise.resolve().then(() => (init_mesh_ledger(), mesh_ledger_exports));
48765
49425
  const ledgerEntries = readLedgerEntries2(meshId, { tail: 20 });
48766
49426
  const ledgerSummary = getLedgerSummary2(meshId);
49427
+ const sessionHostRecords = this.deps.sessionHostControl?.listSessions ? await this.deps.sessionHostControl.listSessions().catch(() => []) : [];
49428
+ const liveMeshSessions = partitionSessionHostRecords(Array.isArray(sessionHostRecords) ? sessionHostRecords : []).liveRuntimes;
49429
+ const localMachineId = loadConfig().machineId || "";
49430
+ const selectedCoordinatorNodeId = readStringValue(
49431
+ mesh.coordinator?.preferredNodeId,
49432
+ mesh.nodes?.[0]?.id,
49433
+ mesh.nodes?.[0]?.nodeId
49434
+ );
49435
+ const inlineCoordinatorNodeId = meshRecord?.inline && Array.isArray(mesh.nodes) ? selectedCoordinatorNodeId : void 0;
49436
+ const refreshedAt = (/* @__PURE__ */ new Date()).toISOString();
48767
49437
  const nodeStatuses = [];
48768
- for (const node of mesh.nodes || []) {
49438
+ for (const [nodeIndex, node] of (mesh.nodes || []).entries()) {
49439
+ const nodeId = String(node.id || node.nodeId || "");
49440
+ const daemonId = readStringValue(node.daemonId);
49441
+ const providerPriority = readProviderPriorityFromPolicy(node.policy);
49442
+ const isSelfNode = Boolean(
49443
+ nodeId && inlineCoordinatorNodeId && nodeId === inlineCoordinatorNodeId
49444
+ ) || Boolean(
49445
+ daemonId && (daemonId === localMachineId || daemonId === this.deps.statusInstanceId)
49446
+ ) || Boolean(meshRecord?.inline && nodeIndex === 0);
48769
49447
  const status = {
48770
- nodeId: node.id || node.nodeId,
49448
+ nodeId,
48771
49449
  machineLabel: node.machineLabel || node.id || node.nodeId,
48772
49450
  workspace: node.workspace,
48773
49451
  repoRoot: node.repoRoot,
48774
49452
  isLocalWorktree: node.isLocalWorktree,
48775
49453
  worktreeBranch: node.worktreeBranch,
48776
- daemonId: node.daemonId,
49454
+ daemonId,
48777
49455
  machineId: node.machineId,
49456
+ machineStatus: node.machineStatus,
48778
49457
  health: "unknown",
48779
49458
  providers: node.providers || [],
48780
- activeSessions: []
49459
+ providerPriority,
49460
+ activeSessions: [],
49461
+ activeSessionDetails: [],
49462
+ launchReady: false
48781
49463
  };
48782
- if (node.workspace && typeof node.workspace === "string") {
48783
- if (!fs10.existsSync(node.workspace) && applyCachedInlineMeshNodeStatus(status, node)) {
48784
- nodeStatuses.push(status);
48785
- continue;
49464
+ if (isSelfNode) {
49465
+ status.connection = {
49466
+ perspective: "selected_coordinator",
49467
+ source: "mesh_peer_status",
49468
+ state: "self",
49469
+ transport: "local",
49470
+ reported: true,
49471
+ reason: "Selected coordinator daemon",
49472
+ lastStateChangeAt: refreshedAt
49473
+ };
49474
+ } else if (daemonId) {
49475
+ const connection = this.deps.getMeshPeerConnectionStatus?.(daemonId);
49476
+ status.connection = connection ?? {
49477
+ perspective: "selected_coordinator",
49478
+ source: "not_reported",
49479
+ state: "unknown",
49480
+ transport: "unknown",
49481
+ reported: false,
49482
+ reason: "No live mesh peer telemetry reported by the selected coordinator yet."
49483
+ };
49484
+ } else {
49485
+ status.connection = {
49486
+ perspective: "selected_coordinator",
49487
+ source: "not_reported",
49488
+ state: "unknown",
49489
+ transport: "unknown",
49490
+ reported: false,
49491
+ reason: "Node has no daemon id, so mesh transport cannot be reported from the selected coordinator."
49492
+ };
49493
+ }
49494
+ const matchedLiveSessionRecords = collectLiveMeshSessionRecords({
49495
+ meshId,
49496
+ node,
49497
+ nodeId,
49498
+ liveSessionRecords: liveMeshSessions,
49499
+ allowCoordinatorSession: nodeId === selectedCoordinatorNodeId
49500
+ });
49501
+ const workspace = readLiveMeshNodeWorkspace({
49502
+ meshId,
49503
+ nodeId,
49504
+ liveSessionRecords: matchedLiveSessionRecords,
49505
+ allowCoordinatorSession: nodeId === selectedCoordinatorNodeId
49506
+ }) || (typeof node.workspace === "string" ? node.workspace : "");
49507
+ status.workspace = workspace || node.workspace;
49508
+ if (matchedLiveSessionRecords.length > 0) {
49509
+ const sessionIds = matchedLiveSessionRecords.map((record2) => typeof record2?.sessionId === "string" ? record2.sessionId : "").filter(Boolean);
49510
+ const providerTypes = matchedLiveSessionRecords.map((record2) => readStringValue(record2?.providerType)).filter(Boolean);
49511
+ status.activeSessions = sessionIds;
49512
+ status.activeSessionDetails = matchedLiveSessionRecords.map(summarizeMeshSessionRecord);
49513
+ if (providerTypes.length > 0) {
49514
+ status.providers = Array.from(/* @__PURE__ */ new Set([...Array.isArray(status.providers) ? status.providers : [], ...providerTypes]));
48786
49515
  }
48787
- try {
48788
- const { execFile: execFile3 } = await import("child_process");
48789
- const { promisify: promisify3 } = await import("util");
48790
- const execFileAsync3 = promisify3(execFile3);
48791
- const runGit2 = async (args2) => {
48792
- const result = await execFileAsync3("git", ["-C", node.workspace, ...args2], {
48793
- encoding: "utf8",
48794
- timeout: 1e4
48795
- });
48796
- return result.stdout.trim();
48797
- };
48798
- const branch = await runGit2(["branch", "--show-current"]).catch(() => "");
48799
- const porc = await runGit2(["status", "--porcelain"]).catch(() => "");
48800
- const headCommit = await runGit2(["rev-parse", "--short", "HEAD"]).catch(() => null);
48801
- const headMessage = await runGit2(["log", "-1", "--format=%s"]).catch(() => null);
48802
- const upstream = await runGit2(["rev-parse", "--abbrev-ref", "@{upstream}"]).catch(() => null);
48803
- const aheadBehind = await runGit2(["rev-list", "--left-right", "--count", "@{upstream}...HEAD"]).catch(() => "");
48804
- const stashCount = await runGit2(["stash", "list"]).catch(() => "");
48805
- let ahead = 0, behind = 0;
48806
- if (aheadBehind) {
48807
- const parts = aheadBehind.split(/\s+/);
48808
- if (parts.length >= 2) {
48809
- behind = parseInt(parts[0], 10) || 0;
48810
- ahead = parseInt(parts[1], 10) || 0;
49516
+ }
49517
+ if (workspace) {
49518
+ if (!fs10.existsSync(workspace)) {
49519
+ const inlineTransitGit = buildInlineMeshTransitGitStatus(node);
49520
+ let remoteProbeApplied = false;
49521
+ if (inlineTransitGit) {
49522
+ status.git = inlineTransitGit;
49523
+ status.health = inlineTransitGit.isGitRepo ? deriveMeshNodeHealthFromGit(inlineTransitGit) : "degraded";
49524
+ remoteProbeApplied = true;
49525
+ } else if (!isSelfNode && daemonId && this.deps.dispatchMeshCommand) {
49526
+ try {
49527
+ const remoteGit = await probeRemoteMeshGitStatus({
49528
+ dispatchMeshCommand: this.deps.dispatchMeshCommand,
49529
+ daemonId,
49530
+ workspace,
49531
+ timeoutMs: 8e3
49532
+ });
49533
+ if (remoteGit) {
49534
+ status.git = remoteGit;
49535
+ status.health = remoteGit.isGitRepo ? deriveMeshNodeHealthFromGit(remoteGit) : "degraded";
49536
+ recordInlineMeshDirectGitTruth(node, remoteGit, "selected_coordinator_mesh_p2p_git");
49537
+ remoteProbeApplied = true;
49538
+ }
49539
+ } catch {
49540
+ const refreshedConnection = this.deps.getMeshPeerConnectionStatus?.(daemonId);
49541
+ const refreshedConnectionState = readStringValue(refreshedConnection?.state);
49542
+ if (refreshedConnection && refreshedConnectionState === "connected") {
49543
+ status.connection = refreshedConnection;
49544
+ try {
49545
+ const remoteGit = await probeRemoteMeshGitStatus({
49546
+ dispatchMeshCommand: this.deps.dispatchMeshCommand,
49547
+ daemonId,
49548
+ workspace,
49549
+ timeoutMs: 12e3
49550
+ });
49551
+ if (remoteGit) {
49552
+ status.git = remoteGit;
49553
+ status.health = remoteGit.isGitRepo ? deriveMeshNodeHealthFromGit(remoteGit) : "degraded";
49554
+ recordInlineMeshDirectGitTruth(node, remoteGit, "selected_coordinator_mesh_p2p_git");
49555
+ remoteProbeApplied = true;
49556
+ }
49557
+ } catch {
49558
+ }
49559
+ }
48811
49560
  }
48812
49561
  }
48813
- const dirty = porc.length > 0;
48814
- const lines = porc ? porc.split("\n").filter(Boolean) : [];
48815
- let staged = 0, modified = 0, untracked = 0, deleted = 0, renamed2 = 0;
48816
- for (const line of lines) {
48817
- const xy = line.slice(0, 2);
48818
- if (xy[0] !== " " && xy[0] !== "?") staged++;
48819
- if (xy[1] === "M") modified++;
48820
- if (xy[1] === "D") deleted++;
48821
- if (xy[0] === "R" || xy[1] === "R") renamed2++;
48822
- if (xy === "??") untracked++;
49562
+ if (!remoteProbeApplied) {
49563
+ const connectionState = readStringValue(status.connection?.state);
49564
+ const pendingPeerGitProbe = !inlineTransitGit && !isSelfNode && !!daemonId && (readStringValue(status.machineStatus) === "online" || readStringValue(status.health) === "online" || connectionState === "connecting" || connectionState === "connected" || connectionState === "unknown");
49565
+ if (pendingPeerGitProbe) {
49566
+ status.gitProbePending = true;
49567
+ status.health = "unknown";
49568
+ }
49569
+ if (applyCachedInlineMeshNodeStatus(
49570
+ status,
49571
+ node,
49572
+ pendingPeerGitProbe ? { skipGit: true, skipError: true, skipHealth: true } : void 0
49573
+ )) {
49574
+ finalizeMeshNodeStatus({ status, node, daemonId, isSelfNode });
49575
+ nodeStatuses.push(status);
49576
+ continue;
49577
+ }
49578
+ if (meshRecord?.source === "inline_cache" && !isSelfNode) {
49579
+ finalizeMeshNodeStatus({ status, node, daemonId, isSelfNode });
49580
+ nodeStatuses.push(status);
49581
+ continue;
49582
+ }
48823
49583
  }
48824
- status.git = {
48825
- workspace: node.workspace,
48826
- repoRoot: node.workspace,
48827
- isGitRepo: true,
48828
- branch: branch || null,
48829
- headCommit,
48830
- headMessage,
48831
- upstream,
48832
- ahead,
48833
- behind,
48834
- staged,
48835
- modified,
48836
- untracked,
48837
- deleted,
48838
- renamed: renamed2,
48839
- hasConflicts: false,
48840
- conflictFiles: [],
48841
- stashCount: stashCount ? stashCount.split("\n").filter(Boolean).length : 0,
48842
- lastCheckedAt: Date.now()
48843
- };
48844
- status.health = branch ? dirty ? "dirty" : "online" : "degraded";
48845
- } catch {
48846
- if (!applyCachedInlineMeshNodeStatus(status, node)) {
48847
- status.health = "degraded";
49584
+ } else {
49585
+ try {
49586
+ const gitStatus = await getGitRepoStatus(workspace, { timeoutMs: 1e4, refreshUpstream: true });
49587
+ status.git = gitStatus;
49588
+ recordInlineMeshDirectGitTruth(node, gitStatus, "selected_coordinator_local_git");
49589
+ if (gitStatus.isGitRepo) {
49590
+ status.health = deriveMeshNodeHealthFromGit(gitStatus);
49591
+ } else {
49592
+ status.health = "degraded";
49593
+ if (gitStatus.error && !status.error) status.error = gitStatus.error;
49594
+ }
49595
+ } catch {
49596
+ if (!applyCachedInlineMeshNodeStatus(status, node)) {
49597
+ status.health = "degraded";
49598
+ }
48848
49599
  }
48849
49600
  }
48850
49601
  } else {
48851
49602
  applyCachedInlineMeshNodeStatus(status, node);
48852
49603
  }
49604
+ finalizeMeshNodeStatus({ status, node, daemonId, isSelfNode });
48853
49605
  nodeStatuses.push(status);
48854
49606
  }
48855
49607
  return {
@@ -48858,6 +49610,12 @@ ${block}`);
48858
49610
  meshName: mesh.name,
48859
49611
  repoIdentity: mesh.repoIdentity,
48860
49612
  defaultBranch: mesh.defaultBranch,
49613
+ refreshedAt: (/* @__PURE__ */ new Date()).toISOString(),
49614
+ sourceOfTruth: {
49615
+ membership: meshRecord?.source === "inline_cache" ? "coordinator_inline_mesh_cache" : meshRecord?.source === "local_config" ? "local_mesh_config" : "inline_bootstrap_snapshot",
49616
+ coordinatorOwnsLiveTruth: meshRecord?.source !== "inline_bootstrap",
49617
+ historicalEvidenceOnly: ["recoveryHints", "ledger.summary", "queue.summary"]
49618
+ },
48861
49619
  nodes: nodeStatuses,
48862
49620
  queue: { tasks: queue, summary: queueSummary },
48863
49621
  ledger: { entries: ledgerEntries, summary: ledgerSummary }
@@ -56936,6 +57694,7 @@ async function initDaemonComponents(config2) {
56936
57694
  sessionHostControl: config2.sessionHostControl,
56937
57695
  statusInstanceId: config2.statusInstanceId,
56938
57696
  statusVersion: config2.statusVersion,
57697
+ getMeshPeerConnectionStatus: config2.getMeshPeerConnectionStatus,
56939
57698
  getCdpLogFn: config2.getCdpLogFn || ((ideType) => LOG.forComponent(`CDP:${ideType}`).asLogFn())
56940
57699
  });
56941
57700
  poller = new AgentStreamPoller({
@@ -57237,6 +57996,7 @@ __export(src_exports, {
57237
57996
  prepareSessionChatTailUpdate: () => prepareSessionChatTailUpdate,
57238
57997
  prepareSessionModalUpdate: () => prepareSessionModalUpdate,
57239
57998
  probeCdpPort: () => probeCdpPort,
57999
+ queuePendingMeshCoordinatorEvent: () => queuePendingMeshCoordinatorEvent,
57240
58000
  readChatHistory: () => readChatHistory,
57241
58001
  readLedgerEntries: () => readLedgerEntries,
57242
58002
  readLedgerSlice: () => readLedgerSlice,
@@ -72139,7 +72899,7 @@ var require_buffer_list = __commonJS({
72139
72899
  }
72140
72900
  }, {
72141
72901
  key: "join",
72142
- value: function join39(s) {
72902
+ value: function join40(s) {
72143
72903
  if (this.length === 0) return "";
72144
72904
  var p = this.head;
72145
72905
  var ret = "" + p.data;
@@ -86198,13 +86958,13 @@ function splitStringBySpace(str2) {
86198
86958
  }
86199
86959
  return pieces;
86200
86960
  }
86201
- var import_chardet, import_child_process12, import_fs11, import_node_path3, import_node_os3, import_node_crypto3, import_iconv_lite, ExternalEditor;
86961
+ var import_chardet, import_child_process12, import_fs12, import_node_path3, import_node_os3, import_node_crypto3, import_iconv_lite, ExternalEditor;
86202
86962
  var init_esm4 = __esm({
86203
86963
  "../../node_modules/@inquirer/external-editor/dist/esm/index.js"() {
86204
86964
  "use strict";
86205
86965
  import_chardet = __toESM(require_lib(), 1);
86206
86966
  import_child_process12 = require("child_process");
86207
- import_fs11 = require("fs");
86967
+ import_fs12 = require("fs");
86208
86968
  import_node_path3 = __toESM(require("path"), 1);
86209
86969
  import_node_os3 = __toESM(require("os"), 1);
86210
86970
  import_node_crypto3 = require("crypto");
@@ -86280,14 +87040,14 @@ var init_esm4 = __esm({
86280
87040
  if (Object.prototype.hasOwnProperty.call(this.fileOptions, "mode")) {
86281
87041
  opt.mode = this.fileOptions.mode;
86282
87042
  }
86283
- (0, import_fs11.writeFileSync)(this.tempFile, this.text, opt);
87043
+ (0, import_fs12.writeFileSync)(this.tempFile, this.text, opt);
86284
87044
  } catch (createFileError) {
86285
87045
  throw new CreateFileError(createFileError);
86286
87046
  }
86287
87047
  }
86288
87048
  readTemporaryFile() {
86289
87049
  try {
86290
- const tempFileBuffer = (0, import_fs11.readFileSync)(this.tempFile);
87050
+ const tempFileBuffer = (0, import_fs12.readFileSync)(this.tempFile);
86291
87051
  if (tempFileBuffer.length === 0) {
86292
87052
  this.text = "";
86293
87053
  } else {
@@ -86303,7 +87063,7 @@ var init_esm4 = __esm({
86303
87063
  }
86304
87064
  removeTemporaryFile() {
86305
87065
  try {
86306
- (0, import_fs11.unlinkSync)(this.tempFile);
87066
+ (0, import_fs12.unlinkSync)(this.tempFile);
86307
87067
  } catch (removeFileError) {
86308
87068
  throw new RemoveFileError(removeFileError);
86309
87069
  }
@@ -88005,25 +88765,25 @@ function resolvePackageVersion(options) {
88005
88765
  const injectedVersion = options?.injectedVersion || "unknown";
88006
88766
  const dir = options?.dirname || __dirname;
88007
88767
  const possiblePaths = [
88008
- (0, import_path7.join)(dir, "..", "..", "package.json"),
88009
- (0, import_path7.join)(dir, "..", "package.json"),
88010
- (0, import_path7.join)(dir, "package.json")
88768
+ (0, import_path8.join)(dir, "..", "..", "package.json"),
88769
+ (0, import_path8.join)(dir, "..", "package.json"),
88770
+ (0, import_path8.join)(dir, "package.json")
88011
88771
  ];
88012
88772
  for (const p of possiblePaths) {
88013
88773
  try {
88014
- const data = JSON.parse((0, import_fs12.readFileSync)(p, "utf-8"));
88774
+ const data = JSON.parse((0, import_fs13.readFileSync)(p, "utf-8"));
88015
88775
  if (data.version) return data.version;
88016
88776
  } catch {
88017
88777
  }
88018
88778
  }
88019
88779
  return injectedVersion;
88020
88780
  }
88021
- var import_fs12, import_path7;
88781
+ var import_fs13, import_path8;
88022
88782
  var init_version = __esm({
88023
88783
  "src/version.ts"() {
88024
88784
  "use strict";
88025
- import_fs12 = require("fs");
88026
- import_path7 = require("path");
88785
+ import_fs13 = require("fs");
88786
+ import_path8 = require("path");
88027
88787
  }
88028
88788
  });
88029
88789
 
@@ -89959,7 +90719,7 @@ var require_filesystem = __commonJS({
89959
90719
  var LDD_PATH = "/usr/bin/ldd";
89960
90720
  var SELF_PATH = "/proc/self/exe";
89961
90721
  var MAX_LENGTH = 2048;
89962
- var readFileSync23 = (path42) => {
90722
+ var readFileSync24 = (path42) => {
89963
90723
  const fd = fs24.openSync(path42, "r");
89964
90724
  const buffer = Buffer.alloc(MAX_LENGTH);
89965
90725
  const bytesRead = fs24.readSync(fd, buffer, 0, MAX_LENGTH, 0);
@@ -89984,7 +90744,7 @@ var require_filesystem = __commonJS({
89984
90744
  module2.exports = {
89985
90745
  LDD_PATH,
89986
90746
  SELF_PATH,
89987
- readFileSync: readFileSync23,
90747
+ readFileSync: readFileSync24,
89988
90748
  readFile: readFile2
89989
90749
  };
89990
90750
  }
@@ -90033,7 +90793,7 @@ var require_detect_libc = __commonJS({
90033
90793
  "use strict";
90034
90794
  var childProcess = require("child_process");
90035
90795
  var { isLinux: isLinux2, getReport } = require_process();
90036
- var { LDD_PATH, SELF_PATH, readFile: readFile2, readFileSync: readFileSync23 } = require_filesystem();
90796
+ var { LDD_PATH, SELF_PATH, readFile: readFile2, readFileSync: readFileSync24 } = require_filesystem();
90037
90797
  var { interpreterPath } = require_elf();
90038
90798
  var cachedFamilyInterpreter;
90039
90799
  var cachedFamilyFilesystem;
@@ -90125,7 +90885,7 @@ var require_detect_libc = __commonJS({
90125
90885
  }
90126
90886
  cachedFamilyFilesystem = null;
90127
90887
  try {
90128
- const lddContent = readFileSync23(LDD_PATH);
90888
+ const lddContent = readFileSync24(LDD_PATH);
90129
90889
  cachedFamilyFilesystem = getFamilyFromLddContent(lddContent);
90130
90890
  } catch (e) {
90131
90891
  }
@@ -90150,7 +90910,7 @@ var require_detect_libc = __commonJS({
90150
90910
  }
90151
90911
  cachedFamilyInterpreter = null;
90152
90912
  try {
90153
- const selfContent = readFileSync23(SELF_PATH);
90913
+ const selfContent = readFileSync24(SELF_PATH);
90154
90914
  const path42 = interpreterPath(selfContent);
90155
90915
  cachedFamilyInterpreter = familyFromInterpreterPath(path42);
90156
90916
  } catch (e) {
@@ -90214,7 +90974,7 @@ var require_detect_libc = __commonJS({
90214
90974
  }
90215
90975
  cachedVersionFilesystem = null;
90216
90976
  try {
90217
- const lddContent = readFileSync23(LDD_PATH);
90977
+ const lddContent = readFileSync24(LDD_PATH);
90218
90978
  const versionMatch = lddContent.match(RE_GLIBC_VERSION);
90219
90979
  if (versionMatch) {
90220
90980
  cachedVersionFilesystem = versionMatch[1];
@@ -97213,11 +97973,30 @@ var init_daemon_mesh_manager = __esm({
97213
97973
  nodeDatachannel = null;
97214
97974
  peers = /* @__PURE__ */ new Map();
97215
97975
  // Map<targetDaemonId, PeerEntry>
97976
+ peerSnapshots = /* @__PURE__ */ new Map();
97216
97977
  pendingRequests = /* @__PURE__ */ new Map();
97217
97978
  commandCallback;
97218
97979
  p2pFailure(message, command, targetDaemonId) {
97219
97980
  return new P2pRelayFailureError(message, { command, targetDaemonId });
97220
97981
  }
97982
+ updatePeerSnapshot(targetDaemonId, state, patch = {}) {
97983
+ const previous = this.peerSnapshots.get(targetDaemonId);
97984
+ const now = (/* @__PURE__ */ new Date()).toISOString();
97985
+ this.peerSnapshots.set(targetDaemonId, {
97986
+ perspective: "selected_coordinator",
97987
+ source: "mesh_peer_status",
97988
+ reported: true,
97989
+ state,
97990
+ transport: patch.transport ?? previous?.transport ?? "unknown",
97991
+ reason: patch.reason ?? previous?.reason,
97992
+ lastStateChangeAt: now,
97993
+ lastConnectedAt: patch.lastConnectedAt ?? previous?.lastConnectedAt,
97994
+ lastCommandAt: patch.lastCommandAt ?? previous?.lastCommandAt
97995
+ });
97996
+ }
97997
+ getPeerConnectionStatus(targetDaemonId) {
97998
+ return this.peerSnapshots.get(targetDaemonId) ?? null;
97999
+ }
97221
98000
  invalidatePeer(targetDaemonId, reason, options = {}) {
97222
98001
  const peer = this.peers.get(targetDaemonId);
97223
98002
  if (peer?.commandQueue) {
@@ -97232,6 +98011,11 @@ var init_daemon_mesh_manager = __esm({
97232
98011
  pending.reject(this.p2pFailure(reason, pending.command, targetDaemonId));
97233
98012
  }
97234
98013
  }
98014
+ const snapshotState = peer?.state === "closed" ? "closed" : peer?.state === "disconnected" ? "disconnected" : "failed";
98015
+ this.updatePeerSnapshot(targetDaemonId, snapshotState, {
98016
+ reason,
98017
+ transport: peer?.isRelay === true ? "relay" : peer?.isRelay === false ? "direct" : "unknown"
98018
+ });
97235
98019
  if (options.closeResources !== false && peer) {
97236
98020
  try {
97237
98021
  peer.dataChannel?.close?.();
@@ -97264,6 +98048,7 @@ var init_daemon_mesh_manager = __esm({
97264
98048
  "send_chat",
97265
98049
  "read_chat",
97266
98050
  "get_chat_debug_bundle",
98051
+ "get_pending_mesh_events",
97267
98052
  "git_status",
97268
98053
  "git_diff_summary",
97269
98054
  "launch_cli",
@@ -97351,6 +98136,20 @@ var init_daemon_mesh_manager = __esm({
97351
98136
  if (!peer) {
97352
98137
  throw this.p2pFailure("Failed to initiate P2P connection entry", command, targetDaemonId);
97353
98138
  }
98139
+ const lastCommandAt = (/* @__PURE__ */ new Date()).toISOString();
98140
+ if (peer.state === "connected") {
98141
+ this.updatePeerSnapshot(targetDaemonId, "connected", {
98142
+ transport: peer.isRelay === true ? "relay" : peer.isRelay === false ? "direct" : "unknown",
98143
+ lastConnectedAt: this.peerSnapshots.get(targetDaemonId)?.lastConnectedAt,
98144
+ lastCommandAt
98145
+ });
98146
+ } else {
98147
+ this.updatePeerSnapshot(targetDaemonId, "connecting", {
98148
+ transport: peer.isRelay === true ? "relay" : peer.isRelay === false ? "direct" : "unknown",
98149
+ reason: "Waiting for mesh DataChannel to open.",
98150
+ lastCommandAt
98151
+ });
98152
+ }
97354
98153
  return new Promise((resolve23, reject) => {
97355
98154
  const requestId = `req_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
97356
98155
  const timer = setTimeout(() => {
@@ -97494,6 +98293,9 @@ var init_daemon_mesh_manager = __esm({
97494
98293
  remoteDescriptionSet: false
97495
98294
  };
97496
98295
  this.peers.set(targetDaemonId, entry);
98296
+ this.updatePeerSnapshot(targetDaemonId, "connecting", {
98297
+ reason: isInitiator ? "P2P mesh connection initiated by the selected coordinator." : "Waiting for the remote daemon to finish the mesh DataChannel handshake."
98298
+ });
97497
98299
  pc.onLocalDescription((sdp, type2) => {
97498
98300
  this.serverConn.sendMeshCommand(targetDaemonId, type2 === "offer" ? "mesh_p2p_offer" : "mesh_p2p_answer", { sdp, type: type2 });
97499
98301
  });
@@ -97504,7 +98306,26 @@ var init_daemon_mesh_manager = __esm({
97504
98306
  LOG.info("Mesh", `[Mesh] P2P state with ${targetDaemonId.slice(0, 12)}: ${state}`);
97505
98307
  if (state === "connected") {
97506
98308
  entry.state = "connected";
98309
+ let transport = "unknown";
98310
+ try {
98311
+ const pair = pc.getSelectedCandidatePair?.();
98312
+ if (pair) {
98313
+ const localType = pair.local?.type || "unknown";
98314
+ const remoteType = pair.remote?.type || "unknown";
98315
+ entry.isRelay = localType === "relay" || remoteType === "relay";
98316
+ transport = entry.isRelay ? "relay" : "direct";
98317
+ LOG.info("Mesh", `[Mesh] Candidate pair with ${targetDaemonId.slice(0, 12)}: local=${localType} remote=${remoteType} \u2192 ${transport}`);
98318
+ }
98319
+ } catch {
98320
+ transport = entry.isRelay === true ? "relay" : entry.isRelay === false ? "direct" : "unknown";
98321
+ }
98322
+ this.updatePeerSnapshot(targetDaemonId, "connected", {
98323
+ transport,
98324
+ reason: transport === "relay" ? "Connected over TURN relay." : transport === "direct" ? "Connected directly peer-to-peer." : "Connected, but selected candidate pair details are unavailable.",
98325
+ lastConnectedAt: (/* @__PURE__ */ new Date()).toISOString()
98326
+ });
97507
98327
  } else if (state === "failed" || state === "closed" || state === "disconnected") {
98328
+ entry.state = state;
97508
98329
  this.invalidatePeer(targetDaemonId, `P2P state changed to ${state}`, { rejectPending: true, closeResources: false });
97509
98330
  }
97510
98331
  });
@@ -97522,6 +98343,11 @@ var init_daemon_mesh_manager = __esm({
97522
98343
  dc.onOpen(() => {
97523
98344
  LOG.info("Mesh", `[Mesh] DataChannel OPEN with ${targetDaemonId.slice(0, 12)}`);
97524
98345
  entry.state = "connected";
98346
+ this.updatePeerSnapshot(targetDaemonId, "connected", {
98347
+ transport: entry.isRelay === true ? "relay" : entry.isRelay === false ? "direct" : "unknown",
98348
+ reason: entry.isRelay === true ? "Connected over TURN relay." : entry.isRelay === false ? "Connected directly peer-to-peer." : "DataChannel open; transport details not reported yet.",
98349
+ lastConnectedAt: (/* @__PURE__ */ new Date()).toISOString()
98350
+ });
97525
98351
  if (entry.commandQueue) {
97526
98352
  const queue = entry.commandQueue;
97527
98353
  entry.commandQueue = [];
@@ -97801,6 +98627,7 @@ var init_adhdev_daemon = __esm({
97801
98627
  "use strict";
97802
98628
  init_server_connection();
97803
98629
  init_src();
98630
+ init_mesh_events();
97804
98631
  init_daemon_p2p2();
97805
98632
  init_screenshot_controller();
97806
98633
  init_session_host();
@@ -97817,7 +98644,7 @@ var init_adhdev_daemon = __esm({
97817
98644
  init_version();
97818
98645
  init_src();
97819
98646
  init_runtime_defaults();
97820
- pkgVersion = resolvePackageVersion({ injectedVersion: "0.9.82-rc.3" });
98647
+ pkgVersion = resolvePackageVersion({ injectedVersion: "0.9.82-rc.31" });
97821
98648
  AdhdevDaemon = class _AdhdevDaemon {
97822
98649
  localHttpServer = null;
97823
98650
  localWss = null;
@@ -98341,6 +99168,7 @@ ${err?.stack || ""}`);
98341
99168
  if (!this.meshManager) throw new Error("Mesh manager not initialized");
98342
99169
  return this.meshManager.sendCommand(daemonId, command, args);
98343
99170
  },
99171
+ getMeshPeerConnectionStatus: (daemonId) => this.meshManager?.getPeerConnectionStatus(daemonId) ?? null,
98344
99172
  onStatusChange: () => {
98345
99173
  this.invalidateHotChatSnapshotCache();
98346
99174
  this.statusReporter?.onStatusChange();
@@ -98710,6 +99538,7 @@ ${err?.stack || ""}`);
98710
99538
  const meshId = this.readMeshString(settings.meshNodeFor);
98711
99539
  const coordinatorDaemonId = this.readMeshString(settings.meshCoordinatorDaemonId);
98712
99540
  if (!meshId || !coordinatorDaemonId) return;
99541
+ const relayTimestamp = typeof event.timestamp === "number" && Number.isFinite(event.timestamp) ? event.timestamp : this.readMeshString(event.timestamp) || void 0;
98713
99542
  const payload = {
98714
99543
  event: this.readMeshString(event.event),
98715
99544
  meshId,
@@ -98718,7 +99547,8 @@ ${err?.stack || ""}`);
98718
99547
  targetSessionId: this.readMeshString(event.targetSessionId) || instanceId,
98719
99548
  providerType: this.readMeshString(event.providerType),
98720
99549
  providerSessionId: this.readMeshString(event.providerSessionId),
98721
- finalSummary: this.readMeshString(event.finalSummary) || this.readMeshString(event.summary)
99550
+ finalSummary: this.readMeshString(event.finalSummary) || this.readMeshString(event.summary),
99551
+ ...relayTimestamp !== void 0 ? { timestamp: relayTimestamp } : {}
98722
99552
  };
98723
99553
  if (coordinatorDaemonId === localDaemonId) {
98724
99554
  try {
@@ -98733,6 +99563,22 @@ ${err?.stack || ""}`);
98733
99563
  await this.meshManager.sendCommand(coordinatorDaemonId, "mesh_forward_event", payload);
98734
99564
  LOG.info("MeshEvents", `Relayed ${payload.event} for mesh ${meshId} to coordinator daemon ${coordinatorDaemonId.slice(0, 12)}\u2026`);
98735
99565
  } catch (error48) {
99566
+ queuePendingMeshCoordinatorEvent({
99567
+ event: payload.event,
99568
+ meshId,
99569
+ nodeLabel: payload.nodeId ? `Node '${payload.nodeId}'` : payload.workspace ? `Agent at ${payload.workspace}` : "Remote agent",
99570
+ nodeId: payload.nodeId || void 0,
99571
+ workspace: payload.workspace || void 0,
99572
+ metadataEvent: {
99573
+ targetSessionId: payload.targetSessionId,
99574
+ providerType: payload.providerType,
99575
+ providerSessionId: payload.providerSessionId,
99576
+ finalSummary: payload.finalSummary,
99577
+ workspace: payload.workspace,
99578
+ ...payload.timestamp !== void 0 ? { timestamp: payload.timestamp } : {}
99579
+ },
99580
+ queuedAt: Date.now()
99581
+ });
98736
99582
  LOG.warn("MeshEvents", `Failed to relay ${payload.event} for mesh ${meshId}: ${error48?.message || error48}`);
98737
99583
  }
98738
99584
  }
@@ -99639,6 +100485,420 @@ var init_runtime_target_trace = __esm({
99639
100485
  }
99640
100486
  });
99641
100487
 
100488
+ // src/cli/service-commands.ts
100489
+ var service_commands_exports = {};
100490
+ __export(service_commands_exports, {
100491
+ buildPlist: () => buildPlist,
100492
+ installAutoStartServiceForCurrentProcess: () => installAutoStartServiceForCurrentProcess,
100493
+ isAutoStartServiceInstalled: () => isAutoStartServiceInstalled,
100494
+ registerServiceCommands: () => registerServiceCommands
100495
+ });
100496
+ function getDarwinPlistPath() {
100497
+ return import_node_path5.default.join(import_node_os4.default.homedir(), "Library", "LaunchAgents", `${LAUNCHD_LABEL}.plist`);
100498
+ }
100499
+ function getWindowsStartupDir() {
100500
+ const appData = process.env.APPDATA || import_node_path5.default.join(import_node_os4.default.homedir(), "AppData", "Roaming");
100501
+ return import_node_path5.default.join(appData, "Microsoft", "Windows", "Start Menu", "Programs", "Startup");
100502
+ }
100503
+ function getWindowsVbsPath() {
100504
+ return import_node_path5.default.join(getWindowsStartupDir(), "adhdev-daemon.vbs");
100505
+ }
100506
+ function resolveCliPath() {
100507
+ return import_node_fs4.default.realpathSync(process.argv[1]);
100508
+ }
100509
+ function ensureDir(dir) {
100510
+ if (!import_node_fs4.default.existsSync(dir)) import_node_fs4.default.mkdirSync(dir, { recursive: true });
100511
+ }
100512
+ async function fetchHealth() {
100513
+ const controller = new AbortController();
100514
+ const timer = setTimeout(() => controller.abort(), DEFAULT_LOCAL_DAEMON_HEALTH_TIMEOUT_MS2);
100515
+ try {
100516
+ const res = await fetch(`http://127.0.0.1:${DEFAULT_DAEMON_PORT}/health`, { signal: controller.signal });
100517
+ if (!res.ok) return null;
100518
+ return await res.json();
100519
+ } catch {
100520
+ return null;
100521
+ } finally {
100522
+ clearTimeout(timer);
100523
+ }
100524
+ }
100525
+ function getProcessInfo(pid) {
100526
+ try {
100527
+ if (process.platform === "win32") {
100528
+ const out = (0, import_node_child_process4.execSync)(`tasklist /FI "PID eq ${pid}" /FO CSV /NH`, { encoding: "utf-8" });
100529
+ const match = out.match(/"(\d[\d,]+)\sK"/);
100530
+ const memKB = match ? parseInt(match[1].replace(/,/g, ""), 10) : 0;
100531
+ return { uptime: "-", memMB: Math.round(memKB / 1024) };
100532
+ } else {
100533
+ const out = (0, import_node_child_process4.execSync)(`ps -o etime=,rss= -p ${pid}`, { encoding: "utf-8" }).trim();
100534
+ const parts = out.split(/\s+/);
100535
+ const etime = parts[0] || "-";
100536
+ const rssKB = parseInt(parts[1] || "0", 10);
100537
+ return { uptime: formatElapsed(etime), memMB: Math.round(rssKB / 1024) };
100538
+ }
100539
+ } catch {
100540
+ return null;
100541
+ }
100542
+ }
100543
+ function formatElapsed(etime) {
100544
+ const parts = etime.replace("-", ":").split(":").map(Number);
100545
+ if (parts.length === 4) return `${parts[0]}d ${parts[1]}h ${parts[2]}m`;
100546
+ if (parts.length === 3) return `${parts[0]}h ${parts[1]}m`;
100547
+ if (parts.length === 2) return `${parts[0]}m ${parts[1]}s`;
100548
+ return etime;
100549
+ }
100550
+ function rotateLogIfNeeded(logPath) {
100551
+ try {
100552
+ if (!import_node_fs4.default.existsSync(logPath)) return;
100553
+ const stat5 = import_node_fs4.default.statSync(logPath);
100554
+ if (stat5.size > MAX_LOG_SIZE2) {
100555
+ const rotated = logPath + ".old";
100556
+ if (import_node_fs4.default.existsSync(rotated)) import_node_fs4.default.unlinkSync(rotated);
100557
+ import_node_fs4.default.renameSync(logPath, rotated);
100558
+ import_node_fs4.default.writeFileSync(logPath, `[log rotated at ${(/* @__PURE__ */ new Date()).toISOString()}]
100559
+ `, "utf-8");
100560
+ }
100561
+ } catch {
100562
+ }
100563
+ }
100564
+ function rotateLogs() {
100565
+ rotateLogIfNeeded(LOG_OUT);
100566
+ rotateLogIfNeeded(LOG_ERR);
100567
+ }
100568
+ function normalizeLaunchdPathEntry(entry) {
100569
+ const trimmed = String(entry || "").trim();
100570
+ if (!trimmed) return null;
100571
+ if (trimmed.startsWith("~")) {
100572
+ return import_node_path5.default.join(import_node_os4.default.homedir(), trimmed.slice(1));
100573
+ }
100574
+ return import_node_path5.default.isAbsolute(trimmed) ? trimmed : null;
100575
+ }
100576
+ function buildLaunchdPath(nodeExe, currentPath = process.env.PATH || "") {
100577
+ const brewPrefix = import_node_fs4.default.existsSync("/opt/homebrew/bin") ? "/opt/homebrew/bin" : "/usr/local/bin";
100578
+ const entries = [];
100579
+ const seen = /* @__PURE__ */ new Set();
100580
+ const addEntry = (value) => {
100581
+ if (!value) return;
100582
+ const normalized = normalizeLaunchdPathEntry(value);
100583
+ if (!normalized || seen.has(normalized)) return;
100584
+ seen.add(normalized);
100585
+ entries.push(normalized);
100586
+ };
100587
+ addEntry(import_node_path5.default.dirname(nodeExe));
100588
+ for (const entry of String(currentPath || "").split(import_node_path5.default.delimiter)) {
100589
+ addEntry(entry);
100590
+ }
100591
+ for (const entry of [brewPrefix, "/usr/local/bin", "/usr/bin", "/bin", "/usr/sbin", "/sbin"]) {
100592
+ addEntry(entry);
100593
+ }
100594
+ return entries.join(":");
100595
+ }
100596
+ function buildPlist(nodeExe, cliExe, currentPath = process.env.PATH || "") {
100597
+ const pathValue = buildLaunchdPath(nodeExe, currentPath);
100598
+ return `<?xml version="1.0" encoding="UTF-8"?>
100599
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
100600
+ <plist version="1.0">
100601
+ <dict>
100602
+ <key>Label</key>
100603
+ <string>${LAUNCHD_LABEL}</string>
100604
+ <key>ProgramArguments</key>
100605
+ <array>
100606
+ <string>${nodeExe}</string>
100607
+ <string>${cliExe}</string>
100608
+ <string>daemon</string>
100609
+ </array>
100610
+ <key>RunAtLoad</key>
100611
+ <true/>
100612
+ <key>KeepAlive</key>
100613
+ <dict>
100614
+ <key>SuccessfulExit</key>
100615
+ <false/>
100616
+ </dict>
100617
+ <key>ThrottleInterval</key>
100618
+ <integer>30</integer>
100619
+ <key>StandardOutPath</key>
100620
+ <string>${LOG_OUT}</string>
100621
+ <key>StandardErrorPath</key>
100622
+ <string>${LOG_ERR}</string>
100623
+ <key>EnvironmentVariables</key>
100624
+ <dict>
100625
+ <key>PATH</key>
100626
+ <string>${pathValue}</string>
100627
+ </dict>
100628
+ </dict>
100629
+ </plist>`;
100630
+ }
100631
+ function installDarwin(nodeExe, cliExe) {
100632
+ const plistPath = getDarwinPlistPath();
100633
+ ensureDir(ADHDEV_DIR);
100634
+ ensureDir(import_node_path5.default.dirname(plistPath));
100635
+ import_node_fs4.default.writeFileSync(plistPath, buildPlist(nodeExe, cliExe), "utf-8");
100636
+ console.log(source_default.gray(` Plist: ${plistPath}`));
100637
+ try {
100638
+ (0, import_node_child_process4.execSync)(`launchctl unload "${plistPath}" 2>/dev/null`, { stdio: "ignore" });
100639
+ } catch {
100640
+ }
100641
+ try {
100642
+ (0, import_node_child_process4.execSync)(`launchctl load -w "${plistPath}"`, { stdio: "ignore" });
100643
+ console.log(source_default.green("\n \u2713 Registered as LaunchAgent \u2014 daemon will start on login."));
100644
+ console.log(source_default.gray(` Logs: ~/.adhdev/daemon-launchd.{out,err}`));
100645
+ } catch (e) {
100646
+ console.log(source_default.red(`
100647
+ \u2717 launchctl load failed: ${e.message}`));
100648
+ }
100649
+ }
100650
+ function uninstallDarwin() {
100651
+ const plistPath = getDarwinPlistPath();
100652
+ if (!import_node_fs4.default.existsSync(plistPath)) {
100653
+ console.log(source_default.yellow("\n \u26A0 Service is not installed."));
100654
+ return;
100655
+ }
100656
+ try {
100657
+ (0, import_node_child_process4.execSync)(`launchctl unload "${plistPath}" 2>/dev/null`, { stdio: "ignore" });
100658
+ } catch {
100659
+ }
100660
+ import_node_fs4.default.unlinkSync(plistPath);
100661
+ console.log(source_default.green("\n \u2713 Removed LaunchAgent. Daemon will no longer auto-start."));
100662
+ }
100663
+ function isInstalledDarwin() {
100664
+ return import_node_fs4.default.existsSync(getDarwinPlistPath());
100665
+ }
100666
+ function buildVbs(nodeExe, cliExe) {
100667
+ const logFile = import_node_path5.default.join(ADHDEV_DIR, "daemon-service.log").replace(/\\/g, "\\\\");
100668
+ const escapedNodeExe = nodeExe.replace(/\\/g, "\\\\");
100669
+ const escapedCliExe = cliExe.replace(/\\/g, "\\\\");
100670
+ return `' ADHDev Daemon Auto-Start (generated by adhdev service install)
100671
+ Set WshShell = CreateObject("WScript.Shell")
100672
+ WshShell.Run "cmd.exe /c """"${escapedNodeExe}"""" """"${escapedCliExe}"""" daemon >> """"${logFile}"""" 2>&1", 0, False
100673
+ `;
100674
+ }
100675
+ function installWindows(nodeExe, cliExe) {
100676
+ const vbsPath = getWindowsVbsPath();
100677
+ ensureDir(ADHDEV_DIR);
100678
+ ensureDir(import_node_path5.default.dirname(vbsPath));
100679
+ import_node_fs4.default.writeFileSync(vbsPath, buildVbs(nodeExe, cliExe), "utf-8");
100680
+ console.log(source_default.gray(` Startup script: ${vbsPath}`));
100681
+ console.log(source_default.green("\n \u2713 Registered in Startup folder \u2014 daemon will start on login (hidden)."));
100682
+ console.log(source_default.gray(` Logs: ${import_node_path5.default.join(ADHDEV_DIR, "daemon-service.log")}`));
100683
+ console.log(source_default.gray(" To start now without rebooting, run: adhdev daemon"));
100684
+ }
100685
+ function uninstallWindows() {
100686
+ const vbsPath = getWindowsVbsPath();
100687
+ if (!import_node_fs4.default.existsSync(vbsPath)) {
100688
+ console.log(source_default.yellow("\n \u26A0 Service is not installed."));
100689
+ return;
100690
+ }
100691
+ import_node_fs4.default.unlinkSync(vbsPath);
100692
+ console.log(source_default.green("\n \u2713 Removed Startup script. Daemon will no longer auto-start."));
100693
+ console.log(source_default.gray(" Note: a currently running daemon is not affected. Stop with: adhdev daemon:stop"));
100694
+ }
100695
+ function isInstalledWindows() {
100696
+ return import_node_fs4.default.existsSync(getWindowsVbsPath());
100697
+ }
100698
+ function isAutoStartServiceInstalled(platform12 = import_node_os4.default.platform()) {
100699
+ if (platform12 === "darwin") return isInstalledDarwin();
100700
+ if (platform12 === "win32") return isInstalledWindows();
100701
+ return false;
100702
+ }
100703
+ function installAutoStartServiceForCurrentProcess(platform12 = import_node_os4.default.platform()) {
100704
+ const nodeExe = process.execPath;
100705
+ const cliExe = resolveCliPath();
100706
+ if (platform12 === "darwin") {
100707
+ installDarwin(nodeExe, cliExe);
100708
+ return true;
100709
+ }
100710
+ if (platform12 === "win32") {
100711
+ installWindows(nodeExe, cliExe);
100712
+ return true;
100713
+ }
100714
+ return false;
100715
+ }
100716
+ function registerServiceCommands(program2) {
100717
+ const svc = program2.command("service").description("\u{1F50C} Manage ADHDev as an OS background auto-start service");
100718
+ svc.command("install").description("Register ADHDev daemon to start automatically on login").action(async () => {
100719
+ console.log(source_default.bold("\n \u{1F680} Installing ADHDev Background Service"));
100720
+ const platform12 = import_node_os4.default.platform();
100721
+ const nodeExe = process.execPath;
100722
+ const cliExe = resolveCliPath();
100723
+ console.log(source_default.gray(` Node: ${nodeExe}`));
100724
+ console.log(source_default.gray(` CLI: ${cliExe}`));
100725
+ console.log(source_default.gray(` Platform: ${platform12}`));
100726
+ if (!installAutoStartServiceForCurrentProcess(platform12)) {
100727
+ console.log(source_default.yellow("\n \u26A0 Auto-start service install is not supported on this platform."));
100728
+ console.log(source_default.gray(" On Linux, create a systemd user unit manually:"));
100729
+ console.log(source_default.gray(" ~/.config/systemd/user/adhdev-daemon.service"));
100730
+ }
100731
+ console.log();
100732
+ });
100733
+ svc.command("uninstall").description("Remove the OS background service").action(async () => {
100734
+ console.log(source_default.bold("\n \u{1F5D1}\uFE0F Removing ADHDev Background Service"));
100735
+ const platform12 = import_node_os4.default.platform();
100736
+ if (platform12 === "darwin") {
100737
+ uninstallDarwin();
100738
+ } else if (platform12 === "win32") {
100739
+ uninstallWindows();
100740
+ } else {
100741
+ console.log(source_default.yellow("\n \u26A0 Not supported on this platform."));
100742
+ }
100743
+ console.log();
100744
+ });
100745
+ svc.command("status").description("Show service installation state and live daemon health").action(async () => {
100746
+ const platform12 = import_node_os4.default.platform();
100747
+ const installed = isAutoStartServiceInstalled(platform12);
100748
+ if (installed) {
100749
+ console.log(source_default.green("\n \u2713 Service is installed."));
100750
+ if (platform12 === "darwin") console.log(source_default.gray(` Plist: ${getDarwinPlistPath()}`));
100751
+ else console.log(source_default.gray(` Script: ${getWindowsVbsPath()}`));
100752
+ } else {
100753
+ console.log(source_default.gray("\n \u2717 Service is not installed. Run: adhdev service install"));
100754
+ }
100755
+ const health = await fetchHealth();
100756
+ if (health?.ok && health.pid) {
100757
+ const info = getProcessInfo(health.pid);
100758
+ if (info) {
100759
+ console.log(source_default.green(` \u2713 Daemon running \u2014 PID ${health.pid}, uptime ${info.uptime}, ${info.memMB} MB`));
100760
+ } else {
100761
+ console.log(source_default.green(` \u2713 Daemon running \u2014 PID ${health.pid}`));
100762
+ }
100763
+ } else {
100764
+ console.log(source_default.yellow(" \u2717 Daemon is not running."));
100765
+ }
100766
+ const outSize = import_node_fs4.default.existsSync(LOG_OUT) ? import_node_fs4.default.statSync(LOG_OUT).size : 0;
100767
+ const errSize = import_node_fs4.default.existsSync(LOG_ERR) ? import_node_fs4.default.statSync(LOG_ERR).size : 0;
100768
+ if (outSize > 0 || errSize > 0) {
100769
+ console.log(source_default.gray(` Logs: stdout ${formatBytes(outSize)}, stderr ${formatBytes(errSize)}`));
100770
+ }
100771
+ console.log();
100772
+ });
100773
+ svc.command("logs").description("View daemon service logs").option("--err", "Show stderr log instead of stdout").option("--clear", "Truncate all log files").option("-n, --lines <count>", "Number of lines to show", "30").action(async (options) => {
100774
+ if (options.clear) {
100775
+ for (const f of [LOG_OUT, LOG_ERR]) {
100776
+ if (import_node_fs4.default.existsSync(f)) import_node_fs4.default.writeFileSync(f, "", "utf-8");
100777
+ }
100778
+ console.log(source_default.green("\n \u2713 Logs cleared.\n"));
100779
+ return;
100780
+ }
100781
+ const logFile = options.err ? LOG_ERR : LOG_OUT;
100782
+ if (!import_node_fs4.default.existsSync(logFile)) {
100783
+ console.log(source_default.gray(`
100784
+ No log file found: ${logFile}
100785
+ `));
100786
+ return;
100787
+ }
100788
+ const lines = parseInt(options.lines, 10) || 30;
100789
+ console.log(source_default.gray(`
100790
+ \u2500\u2500 ${options.err ? "stderr" : "stdout"}: ${logFile} (last ${lines} lines) \u2500\u2500
100791
+ `));
100792
+ const content = import_node_fs4.default.readFileSync(logFile, "utf-8");
100793
+ const allLines = content.split("\n");
100794
+ const lastLines = allLines.slice(-lines).join("\n");
100795
+ if (lastLines.trim()) console.log(lastLines);
100796
+ console.log(source_default.gray("\n (watching for new output, Ctrl+C to stop)\n"));
100797
+ let offset = Buffer.byteLength(content, "utf-8");
100798
+ const watcher = import_node_fs4.default.watchFile(logFile, { interval: 500 }, () => {
100799
+ try {
100800
+ const stat5 = import_node_fs4.default.statSync(logFile);
100801
+ if (stat5.size > offset) {
100802
+ const fd = import_node_fs4.default.openSync(logFile, "r");
100803
+ const buf = Buffer.alloc(stat5.size - offset);
100804
+ import_node_fs4.default.readSync(fd, buf, 0, buf.length, offset);
100805
+ import_node_fs4.default.closeSync(fd);
100806
+ process.stdout.write(buf.toString("utf-8"));
100807
+ offset = stat5.size;
100808
+ } else if (stat5.size < offset) {
100809
+ offset = 0;
100810
+ }
100811
+ } catch {
100812
+ }
100813
+ });
100814
+ const cleanup = () => {
100815
+ import_node_fs4.default.unwatchFile(logFile);
100816
+ process.exit(0);
100817
+ };
100818
+ process.on("SIGINT", cleanup);
100819
+ process.on("SIGTERM", cleanup);
100820
+ });
100821
+ svc.command("restart").description("Restart the daemon process (service will auto-relaunch)").action(async () => {
100822
+ const platform12 = import_node_os4.default.platform();
100823
+ const installed = isAutoStartServiceInstalled(platform12);
100824
+ if (!installed) {
100825
+ console.log(source_default.yellow("\n \u26A0 Service is not installed. Use `adhdev daemon:restart` for manual restart."));
100826
+ console.log(source_default.gray(" Or install the service first: adhdev service install\n"));
100827
+ return;
100828
+ }
100829
+ rotateLogs();
100830
+ if (platform12 === "darwin") {
100831
+ console.log(source_default.cyan("\n Refreshing LaunchAgent definition and reloading service..."));
100832
+ installAutoStartServiceForCurrentProcess(platform12);
100833
+ console.log();
100834
+ return;
100835
+ }
100836
+ const health = await fetchHealth();
100837
+ if (!health?.pid) {
100838
+ console.log(source_default.yellow("\n \u26A0 Daemon is not currently running."));
100839
+ console.log(source_default.gray(" Start with: adhdev daemon\n"));
100840
+ return;
100841
+ }
100842
+ console.log(source_default.cyan(`
100843
+ Stopping daemon (PID ${health.pid})...`));
100844
+ try {
100845
+ process.kill(health.pid, "SIGTERM");
100846
+ } catch {
100847
+ console.log(source_default.yellow(" Could not send SIGTERM. Process may have already exited."));
100848
+ }
100849
+ await new Promise((r) => setTimeout(r, 2e3));
100850
+ if (platform12 === "win32") {
100851
+ const vbsPath = getWindowsVbsPath();
100852
+ if (import_node_fs4.default.existsSync(vbsPath)) {
100853
+ try {
100854
+ (0, import_node_child_process4.execSync)(`wscript.exe "${vbsPath}"`, { stdio: "ignore", windowsHide: true });
100855
+ } catch {
100856
+ }
100857
+ }
100858
+ }
100859
+ let restarted = false;
100860
+ for (let i = 0; i < 8; i++) {
100861
+ await new Promise((r) => setTimeout(r, 1e3));
100862
+ const newHealth = await fetchHealth();
100863
+ if (newHealth?.ok && newHealth.pid !== health.pid) {
100864
+ restarted = true;
100865
+ const info = getProcessInfo(newHealth.pid);
100866
+ console.log(source_default.green(` \u2713 Daemon restarted \u2014 new PID ${newHealth.pid}${info ? `, ${info.memMB} MB` : ""}`));
100867
+ break;
100868
+ }
100869
+ }
100870
+ if (!restarted) {
100871
+ console.log(source_default.yellow(" \u26A0 Daemon did not restart within 10s."));
100872
+ console.log(source_default.gray(" Check: adhdev service logs --err"));
100873
+ }
100874
+ console.log();
100875
+ });
100876
+ }
100877
+ function formatBytes(bytes) {
100878
+ if (bytes === 0) return "0 B";
100879
+ if (bytes < 1024) return `${bytes} B`;
100880
+ if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
100881
+ return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
100882
+ }
100883
+ var import_node_fs4, import_node_path5, import_node_os4, import_node_child_process4, DEFAULT_LOCAL_DAEMON_HEALTH_TIMEOUT_MS2, LAUNCHD_LABEL, ADHDEV_DIR, LOG_OUT, LOG_ERR, MAX_LOG_SIZE2;
100884
+ var init_service_commands = __esm({
100885
+ "src/cli/service-commands.ts"() {
100886
+ "use strict";
100887
+ import_node_fs4 = __toESM(require("fs"));
100888
+ import_node_path5 = __toESM(require("path"));
100889
+ import_node_os4 = __toESM(require("os"));
100890
+ import_node_child_process4 = require("child_process");
100891
+ init_source();
100892
+ init_src();
100893
+ DEFAULT_LOCAL_DAEMON_HEALTH_TIMEOUT_MS2 = 1500;
100894
+ LAUNCHD_LABEL = "dev.adhf.daemon";
100895
+ ADHDEV_DIR = import_node_path5.default.join(import_node_os4.default.homedir(), ".adhdev");
100896
+ LOG_OUT = import_node_path5.default.join(ADHDEV_DIR, "daemon-launchd.out");
100897
+ LOG_ERR = import_node_path5.default.join(ADHDEV_DIR, "daemon-launchd.err");
100898
+ MAX_LOG_SIZE2 = 10 * 1024 * 1024;
100899
+ }
100900
+ });
100901
+
99642
100902
  // ../../oss/packages/web-core/src/constants/supported.ts
99643
100903
  var supported_exports = {};
99644
100904
  __export(supported_exports, {
@@ -101612,7 +102872,7 @@ function formatDebugTraceEntryLine(entry) {
101612
102872
  }
101613
102873
 
101614
102874
  // src/cli/daemon-commands.ts
101615
- var DEFAULT_LOCAL_DAEMON_HEALTH_TIMEOUT_MS2 = 1500;
102875
+ var DEFAULT_LOCAL_DAEMON_HEALTH_TIMEOUT_MS3 = 1500;
101616
102876
  var DEFAULT_TRACE_FOLLOW_INTERVAL_MS = 1500;
101617
102877
  var DEFAULT_DAEMON_PORT_TEXT = String(DEFAULT_DAEMON_PORT);
101618
102878
  var DEV_SERVER_PORT2 = 19280;
@@ -101640,6 +102900,15 @@ async function resolveConfiguredUpdateChannel() {
101640
102900
  function releaseChannelLabel(channel) {
101641
102901
  return `${channel} (${CHANNEL_NPM_TAG3[channel]})`;
101642
102902
  }
102903
+ function buildDaemonRestartCommandArgv(options = {}) {
102904
+ const cliPath = options.cliPath || process.argv[1];
102905
+ const daemonPort = options.daemonPort || DEFAULT_DAEMON_PORT_TEXT;
102906
+ const platform12 = options.platform || process.platform;
102907
+ if (platform12 === "darwin" && options.serviceInstalled) {
102908
+ return [cliPath, "service", "install"];
102909
+ }
102910
+ return [cliPath, "daemon", "-p", daemonPort];
102911
+ }
101643
102912
  async function persistReleaseChannel(channel) {
101644
102913
  const { updateConfig: updateConfig2 } = await Promise.resolve().then(() => (init_src(), src_exports));
101645
102914
  updateConfig2({ updateChannel: channel, serverUrl: CHANNEL_SERVER_URL3[channel] });
@@ -101650,7 +102919,7 @@ function hideCommand(command) {
101650
102919
  }
101651
102920
  async function fetchLocalDaemonHealth(port) {
101652
102921
  const controller = new AbortController();
101653
- const timer = setTimeout(() => controller.abort(), DEFAULT_LOCAL_DAEMON_HEALTH_TIMEOUT_MS2);
102922
+ const timer = setTimeout(() => controller.abort(), DEFAULT_LOCAL_DAEMON_HEALTH_TIMEOUT_MS3);
101654
102923
  try {
101655
102924
  const res = await fetch(`http://127.0.0.1:${port}/health`, { signal: controller.signal });
101656
102925
  if (!res.ok) return null;
@@ -102016,6 +103285,9 @@ async function runDaemonUpgrade(options, pkgVersion3) {
102016
103285
  } else {
102017
103286
  console.log(source_default.cyan(`
102018
103287
  Upgrading v${currentVersion} \u2192 v${latest}...`));
103288
+ const { isAutoStartServiceInstalled: isAutoStartServiceInstalled2 } = await Promise.resolve().then(() => (init_service_commands(), service_commands_exports));
103289
+ const serviceInstalled = isAutoStartServiceInstalled2();
103290
+ const shouldRefreshInstalledService = options.restart !== false && process.platform === "darwin" && serviceInstalled;
102019
103291
  const daemonWasRunning = options.restart !== false && isDaemonRunning2();
102020
103292
  if (daemonWasRunning) {
102021
103293
  stopDaemon2();
@@ -102027,10 +103299,22 @@ async function runDaemonUpgrade(options, pkgVersion3) {
102027
103299
  packageName: "adhdev",
102028
103300
  targetVersion: latest,
102029
103301
  parentPid: process.pid,
102030
- restartArgv: daemonWasRunning ? [process.argv[1], "daemon", "-p", DEFAULT_DAEMON_PORT_TEXT] : [],
103302
+ restartArgv: shouldRefreshInstalledService ? buildDaemonRestartCommandArgv({
103303
+ cliPath: process.argv[1],
103304
+ daemonPort: DEFAULT_DAEMON_PORT_TEXT,
103305
+ platform: process.platform,
103306
+ serviceInstalled
103307
+ }) : daemonWasRunning ? buildDaemonRestartCommandArgv({
103308
+ cliPath: process.argv[1],
103309
+ daemonPort: DEFAULT_DAEMON_PORT_TEXT,
103310
+ platform: process.platform,
103311
+ serviceInstalled
103312
+ }) : [],
102031
103313
  cwd: process.cwd()
102032
103314
  });
102033
- if (daemonWasRunning) {
103315
+ if (shouldRefreshInstalledService) {
103316
+ console.log(source_default.cyan(" Upgrading and refreshing background service in background..."));
103317
+ } else if (daemonWasRunning) {
102034
103318
  console.log(source_default.cyan(" Upgrading and restarting daemon in background..."));
102035
103319
  } else {
102036
103320
  console.log(source_default.cyan(" Upgrading in background..."));
@@ -102042,29 +103326,44 @@ async function runDaemonUpgrade(options, pkgVersion3) {
102042
103326
  return;
102043
103327
  }
102044
103328
  }
102045
- if (options.restart !== false && isDaemonRunning2()) {
102046
- console.log(source_default.yellow("\n Restarting daemon..."));
102047
- stopDaemon2();
102048
- await new Promise((r) => setTimeout(r, 2e3));
102049
- stopManagedSessionHostProcess2();
102050
- await new Promise((r) => setTimeout(r, 500));
102051
- const child = spawn7(process.execPath, [process.argv[1], "daemon", "-p", DEFAULT_DAEMON_PORT_TEXT], {
102052
- detached: true,
102053
- stdio: "ignore",
102054
- windowsHide: true,
102055
- env: { ...process.env }
102056
- });
102057
- child.unref();
102058
- await new Promise((r) => setTimeout(r, 3e3));
103329
+ if (options.restart !== false) {
103330
+ const { installAutoStartServiceForCurrentProcess: installAutoStartServiceForCurrentProcess2, isAutoStartServiceInstalled: isAutoStartServiceInstalled2 } = await Promise.resolve().then(() => (init_service_commands(), service_commands_exports));
103331
+ const serviceInstalled = isAutoStartServiceInstalled2();
103332
+ if (process.platform === "darwin" && serviceInstalled) {
103333
+ console.log(source_default.yellow("\n Refreshing LaunchAgent definition..."));
103334
+ installAutoStartServiceForCurrentProcess2(process.platform);
103335
+ console.log();
103336
+ return;
103337
+ }
102059
103338
  if (isDaemonRunning2()) {
102060
- console.log(source_default.green(` \u2713 Daemon restarted with new version
103339
+ console.log(source_default.yellow("\n Restarting daemon..."));
103340
+ stopDaemon2();
103341
+ await new Promise((r) => setTimeout(r, 2e3));
103342
+ stopManagedSessionHostProcess2();
103343
+ await new Promise((r) => setTimeout(r, 500));
103344
+ const child = spawn7(process.execPath, buildDaemonRestartCommandArgv({
103345
+ cliPath: process.argv[1],
103346
+ daemonPort: DEFAULT_DAEMON_PORT_TEXT,
103347
+ platform: process.platform,
103348
+ serviceInstalled
103349
+ }), {
103350
+ detached: true,
103351
+ stdio: "ignore",
103352
+ windowsHide: true,
103353
+ env: { ...process.env }
103354
+ });
103355
+ child.unref();
103356
+ await new Promise((r) => setTimeout(r, 3e3));
103357
+ if (isDaemonRunning2()) {
103358
+ console.log(source_default.green(` \u2713 Daemon restarted with new version
102061
103359
  `));
102062
- } else {
102063
- console.log(source_default.yellow(` \u26A0 Daemon not detected. Start manually: adhdev daemon
103360
+ } else {
103361
+ console.log(source_default.yellow(` \u26A0 Daemon not detected. Start manually: adhdev daemon
102064
103362
  `));
103363
+ }
103364
+ } else {
103365
+ console.log(source_default.gray("\n Daemon was not running. Start with: adhdev daemon\n"));
102065
103366
  }
102066
- } else if (options.restart !== false) {
102067
- console.log(source_default.gray("\n Daemon was not running. Start with: adhdev daemon\n"));
102068
103367
  } else {
102069
103368
  console.log(source_default.green("\n \u2713 Upgrade complete (daemon not restarted)\n"));
102070
103369
  }
@@ -102235,7 +103534,7 @@ function registerDaemonCommands(program2, pkgVersion3) {
102235
103534
  }
102236
103535
  try {
102237
103536
  const controller = new AbortController();
102238
- const timer = setTimeout(() => controller.abort(), DEFAULT_LOCAL_DAEMON_HEALTH_TIMEOUT_MS2);
103537
+ const timer = setTimeout(() => controller.abort(), DEFAULT_LOCAL_DAEMON_HEALTH_TIMEOUT_MS3);
102239
103538
  const res = await fetch(`${DEV_SERVER_BASE_URL}/api/status`, { signal: controller.signal });
102240
103539
  clearTimeout(timer);
102241
103540
  if (res.ok) {
@@ -102782,373 +104081,8 @@ function buildDoctorAdvice(input) {
102782
104081
  return advice;
102783
104082
  }
102784
104083
 
102785
- // src/cli/service-commands.ts
102786
- var import_node_fs4 = __toESM(require("fs"));
102787
- var import_node_path5 = __toESM(require("path"));
102788
- var import_node_os4 = __toESM(require("os"));
102789
- var import_node_child_process4 = require("child_process");
102790
- init_source();
102791
- init_src();
102792
- var DEFAULT_LOCAL_DAEMON_HEALTH_TIMEOUT_MS3 = 1500;
102793
- var LAUNCHD_LABEL = "dev.adhf.daemon";
102794
- var ADHDEV_DIR = import_node_path5.default.join(import_node_os4.default.homedir(), ".adhdev");
102795
- var LOG_OUT = import_node_path5.default.join(ADHDEV_DIR, "daemon-launchd.out");
102796
- var LOG_ERR = import_node_path5.default.join(ADHDEV_DIR, "daemon-launchd.err");
102797
- var MAX_LOG_SIZE2 = 10 * 1024 * 1024;
102798
- function getDarwinPlistPath() {
102799
- return import_node_path5.default.join(import_node_os4.default.homedir(), "Library", "LaunchAgents", `${LAUNCHD_LABEL}.plist`);
102800
- }
102801
- function getWindowsStartupDir() {
102802
- const appData = process.env.APPDATA || import_node_path5.default.join(import_node_os4.default.homedir(), "AppData", "Roaming");
102803
- return import_node_path5.default.join(appData, "Microsoft", "Windows", "Start Menu", "Programs", "Startup");
102804
- }
102805
- function getWindowsVbsPath() {
102806
- return import_node_path5.default.join(getWindowsStartupDir(), "adhdev-daemon.vbs");
102807
- }
102808
- function resolveCliPath() {
102809
- return import_node_fs4.default.realpathSync(process.argv[1]);
102810
- }
102811
- function ensureDir(dir) {
102812
- if (!import_node_fs4.default.existsSync(dir)) import_node_fs4.default.mkdirSync(dir, { recursive: true });
102813
- }
102814
- async function fetchHealth() {
102815
- const controller = new AbortController();
102816
- const timer = setTimeout(() => controller.abort(), DEFAULT_LOCAL_DAEMON_HEALTH_TIMEOUT_MS3);
102817
- try {
102818
- const res = await fetch(`http://127.0.0.1:${DEFAULT_DAEMON_PORT}/health`, { signal: controller.signal });
102819
- if (!res.ok) return null;
102820
- return await res.json();
102821
- } catch {
102822
- return null;
102823
- } finally {
102824
- clearTimeout(timer);
102825
- }
102826
- }
102827
- function getProcessInfo(pid) {
102828
- try {
102829
- if (process.platform === "win32") {
102830
- const out = (0, import_node_child_process4.execSync)(`tasklist /FI "PID eq ${pid}" /FO CSV /NH`, { encoding: "utf-8" });
102831
- const match = out.match(/"(\d[\d,]+)\sK"/);
102832
- const memKB = match ? parseInt(match[1].replace(/,/g, ""), 10) : 0;
102833
- return { uptime: "-", memMB: Math.round(memKB / 1024) };
102834
- } else {
102835
- const out = (0, import_node_child_process4.execSync)(`ps -o etime=,rss= -p ${pid}`, { encoding: "utf-8" }).trim();
102836
- const parts = out.split(/\s+/);
102837
- const etime = parts[0] || "-";
102838
- const rssKB = parseInt(parts[1] || "0", 10);
102839
- return { uptime: formatElapsed(etime), memMB: Math.round(rssKB / 1024) };
102840
- }
102841
- } catch {
102842
- return null;
102843
- }
102844
- }
102845
- function formatElapsed(etime) {
102846
- const parts = etime.replace("-", ":").split(":").map(Number);
102847
- if (parts.length === 4) return `${parts[0]}d ${parts[1]}h ${parts[2]}m`;
102848
- if (parts.length === 3) return `${parts[0]}h ${parts[1]}m`;
102849
- if (parts.length === 2) return `${parts[0]}m ${parts[1]}s`;
102850
- return etime;
102851
- }
102852
- function rotateLogIfNeeded(logPath) {
102853
- try {
102854
- if (!import_node_fs4.default.existsSync(logPath)) return;
102855
- const stat5 = import_node_fs4.default.statSync(logPath);
102856
- if (stat5.size > MAX_LOG_SIZE2) {
102857
- const rotated = logPath + ".old";
102858
- if (import_node_fs4.default.existsSync(rotated)) import_node_fs4.default.unlinkSync(rotated);
102859
- import_node_fs4.default.renameSync(logPath, rotated);
102860
- import_node_fs4.default.writeFileSync(logPath, `[log rotated at ${(/* @__PURE__ */ new Date()).toISOString()}]
102861
- `, "utf-8");
102862
- }
102863
- } catch {
102864
- }
102865
- }
102866
- function rotateLogs() {
102867
- rotateLogIfNeeded(LOG_OUT);
102868
- rotateLogIfNeeded(LOG_ERR);
102869
- }
102870
- function buildPlist(nodeExe, cliExe) {
102871
- const brewPrefix = import_node_fs4.default.existsSync("/opt/homebrew/bin") ? "/opt/homebrew/bin" : "/usr/local/bin";
102872
- const nodeDir = import_node_path5.default.dirname(nodeExe);
102873
- const pathEntries = /* @__PURE__ */ new Set([nodeDir, brewPrefix, "/usr/local/bin", "/usr/bin", "/bin", "/usr/sbin", "/sbin"]);
102874
- const pathValue = Array.from(pathEntries).join(":");
102875
- return `<?xml version="1.0" encoding="UTF-8"?>
102876
- <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
102877
- <plist version="1.0">
102878
- <dict>
102879
- <key>Label</key>
102880
- <string>${LAUNCHD_LABEL}</string>
102881
- <key>ProgramArguments</key>
102882
- <array>
102883
- <string>${nodeExe}</string>
102884
- <string>${cliExe}</string>
102885
- <string>daemon</string>
102886
- </array>
102887
- <key>RunAtLoad</key>
102888
- <true/>
102889
- <key>KeepAlive</key>
102890
- <dict>
102891
- <key>SuccessfulExit</key>
102892
- <false/>
102893
- </dict>
102894
- <key>ThrottleInterval</key>
102895
- <integer>30</integer>
102896
- <key>StandardOutPath</key>
102897
- <string>${LOG_OUT}</string>
102898
- <key>StandardErrorPath</key>
102899
- <string>${LOG_ERR}</string>
102900
- <key>EnvironmentVariables</key>
102901
- <dict>
102902
- <key>PATH</key>
102903
- <string>${pathValue}</string>
102904
- </dict>
102905
- </dict>
102906
- </plist>`;
102907
- }
102908
- function installDarwin(nodeExe, cliExe) {
102909
- const plistPath = getDarwinPlistPath();
102910
- ensureDir(ADHDEV_DIR);
102911
- ensureDir(import_node_path5.default.dirname(plistPath));
102912
- import_node_fs4.default.writeFileSync(plistPath, buildPlist(nodeExe, cliExe), "utf-8");
102913
- console.log(source_default.gray(` Plist: ${plistPath}`));
102914
- try {
102915
- (0, import_node_child_process4.execSync)(`launchctl unload "${plistPath}" 2>/dev/null`, { stdio: "ignore" });
102916
- } catch {
102917
- }
102918
- try {
102919
- (0, import_node_child_process4.execSync)(`launchctl load -w "${plistPath}"`, { stdio: "ignore" });
102920
- console.log(source_default.green("\n \u2713 Registered as LaunchAgent \u2014 daemon will start on login."));
102921
- console.log(source_default.gray(` Logs: ~/.adhdev/daemon-launchd.{out,err}`));
102922
- } catch (e) {
102923
- console.log(source_default.red(`
102924
- \u2717 launchctl load failed: ${e.message}`));
102925
- }
102926
- }
102927
- function uninstallDarwin() {
102928
- const plistPath = getDarwinPlistPath();
102929
- if (!import_node_fs4.default.existsSync(plistPath)) {
102930
- console.log(source_default.yellow("\n \u26A0 Service is not installed."));
102931
- return;
102932
- }
102933
- try {
102934
- (0, import_node_child_process4.execSync)(`launchctl unload "${plistPath}" 2>/dev/null`, { stdio: "ignore" });
102935
- } catch {
102936
- }
102937
- import_node_fs4.default.unlinkSync(plistPath);
102938
- console.log(source_default.green("\n \u2713 Removed LaunchAgent. Daemon will no longer auto-start."));
102939
- }
102940
- function isInstalledDarwin() {
102941
- return import_node_fs4.default.existsSync(getDarwinPlistPath());
102942
- }
102943
- function buildVbs(nodeExe, cliExe) {
102944
- const logFile = import_node_path5.default.join(ADHDEV_DIR, "daemon-service.log").replace(/\\/g, "\\\\");
102945
- const escapedNodeExe = nodeExe.replace(/\\/g, "\\\\");
102946
- const escapedCliExe = cliExe.replace(/\\/g, "\\\\");
102947
- return `' ADHDev Daemon Auto-Start (generated by adhdev service install)
102948
- Set WshShell = CreateObject("WScript.Shell")
102949
- WshShell.Run "cmd.exe /c """"${escapedNodeExe}"""" """"${escapedCliExe}"""" daemon >> """"${logFile}"""" 2>&1", 0, False
102950
- `;
102951
- }
102952
- function installWindows(nodeExe, cliExe) {
102953
- const vbsPath = getWindowsVbsPath();
102954
- ensureDir(ADHDEV_DIR);
102955
- ensureDir(import_node_path5.default.dirname(vbsPath));
102956
- import_node_fs4.default.writeFileSync(vbsPath, buildVbs(nodeExe, cliExe), "utf-8");
102957
- console.log(source_default.gray(` Startup script: ${vbsPath}`));
102958
- console.log(source_default.green("\n \u2713 Registered in Startup folder \u2014 daemon will start on login (hidden)."));
102959
- console.log(source_default.gray(` Logs: ${import_node_path5.default.join(ADHDEV_DIR, "daemon-service.log")}`));
102960
- console.log(source_default.gray(" To start now without rebooting, run: adhdev daemon"));
102961
- }
102962
- function uninstallWindows() {
102963
- const vbsPath = getWindowsVbsPath();
102964
- if (!import_node_fs4.default.existsSync(vbsPath)) {
102965
- console.log(source_default.yellow("\n \u26A0 Service is not installed."));
102966
- return;
102967
- }
102968
- import_node_fs4.default.unlinkSync(vbsPath);
102969
- console.log(source_default.green("\n \u2713 Removed Startup script. Daemon will no longer auto-start."));
102970
- console.log(source_default.gray(" Note: a currently running daemon is not affected. Stop with: adhdev daemon:stop"));
102971
- }
102972
- function isInstalledWindows() {
102973
- return import_node_fs4.default.existsSync(getWindowsVbsPath());
102974
- }
102975
- function registerServiceCommands(program2) {
102976
- const svc = program2.command("service").description("\u{1F50C} Manage ADHDev as an OS background auto-start service");
102977
- svc.command("install").description("Register ADHDev daemon to start automatically on login").action(async () => {
102978
- console.log(source_default.bold("\n \u{1F680} Installing ADHDev Background Service"));
102979
- const platform12 = import_node_os4.default.platform();
102980
- const nodeExe = process.execPath;
102981
- const cliExe = resolveCliPath();
102982
- console.log(source_default.gray(` Node: ${nodeExe}`));
102983
- console.log(source_default.gray(` CLI: ${cliExe}`));
102984
- console.log(source_default.gray(` Platform: ${platform12}`));
102985
- if (platform12 === "darwin") {
102986
- installDarwin(nodeExe, cliExe);
102987
- } else if (platform12 === "win32") {
102988
- installWindows(nodeExe, cliExe);
102989
- } else {
102990
- console.log(source_default.yellow("\n \u26A0 Auto-start service install is not supported on this platform."));
102991
- console.log(source_default.gray(" On Linux, create a systemd user unit manually:"));
102992
- console.log(source_default.gray(" ~/.config/systemd/user/adhdev-daemon.service"));
102993
- }
102994
- console.log();
102995
- });
102996
- svc.command("uninstall").description("Remove the OS background service").action(async () => {
102997
- console.log(source_default.bold("\n \u{1F5D1}\uFE0F Removing ADHDev Background Service"));
102998
- const platform12 = import_node_os4.default.platform();
102999
- if (platform12 === "darwin") {
103000
- uninstallDarwin();
103001
- } else if (platform12 === "win32") {
103002
- uninstallWindows();
103003
- } else {
103004
- console.log(source_default.yellow("\n \u26A0 Not supported on this platform."));
103005
- }
103006
- console.log();
103007
- });
103008
- svc.command("status").description("Show service installation state and live daemon health").action(async () => {
103009
- const platform12 = import_node_os4.default.platform();
103010
- const installed = platform12 === "darwin" ? isInstalledDarwin() : platform12 === "win32" ? isInstalledWindows() : false;
103011
- if (installed) {
103012
- console.log(source_default.green("\n \u2713 Service is installed."));
103013
- if (platform12 === "darwin") console.log(source_default.gray(` Plist: ${getDarwinPlistPath()}`));
103014
- else console.log(source_default.gray(` Script: ${getWindowsVbsPath()}`));
103015
- } else {
103016
- console.log(source_default.gray("\n \u2717 Service is not installed. Run: adhdev service install"));
103017
- }
103018
- const health = await fetchHealth();
103019
- if (health?.ok && health.pid) {
103020
- const info = getProcessInfo(health.pid);
103021
- if (info) {
103022
- console.log(source_default.green(` \u2713 Daemon running \u2014 PID ${health.pid}, uptime ${info.uptime}, ${info.memMB} MB`));
103023
- } else {
103024
- console.log(source_default.green(` \u2713 Daemon running \u2014 PID ${health.pid}`));
103025
- }
103026
- } else {
103027
- console.log(source_default.yellow(" \u2717 Daemon is not running."));
103028
- }
103029
- const outSize = import_node_fs4.default.existsSync(LOG_OUT) ? import_node_fs4.default.statSync(LOG_OUT).size : 0;
103030
- const errSize = import_node_fs4.default.existsSync(LOG_ERR) ? import_node_fs4.default.statSync(LOG_ERR).size : 0;
103031
- if (outSize > 0 || errSize > 0) {
103032
- console.log(source_default.gray(` Logs: stdout ${formatBytes(outSize)}, stderr ${formatBytes(errSize)}`));
103033
- }
103034
- console.log();
103035
- });
103036
- svc.command("logs").description("View daemon service logs").option("--err", "Show stderr log instead of stdout").option("--clear", "Truncate all log files").option("-n, --lines <count>", "Number of lines to show", "30").action(async (options) => {
103037
- if (options.clear) {
103038
- for (const f of [LOG_OUT, LOG_ERR]) {
103039
- if (import_node_fs4.default.existsSync(f)) import_node_fs4.default.writeFileSync(f, "", "utf-8");
103040
- }
103041
- console.log(source_default.green("\n \u2713 Logs cleared.\n"));
103042
- return;
103043
- }
103044
- const logFile = options.err ? LOG_ERR : LOG_OUT;
103045
- if (!import_node_fs4.default.existsSync(logFile)) {
103046
- console.log(source_default.gray(`
103047
- No log file found: ${logFile}
103048
- `));
103049
- return;
103050
- }
103051
- const lines = parseInt(options.lines, 10) || 30;
103052
- console.log(source_default.gray(`
103053
- \u2500\u2500 ${options.err ? "stderr" : "stdout"}: ${logFile} (last ${lines} lines) \u2500\u2500
103054
- `));
103055
- const content = import_node_fs4.default.readFileSync(logFile, "utf-8");
103056
- const allLines = content.split("\n");
103057
- const lastLines = allLines.slice(-lines).join("\n");
103058
- if (lastLines.trim()) console.log(lastLines);
103059
- console.log(source_default.gray("\n (watching for new output, Ctrl+C to stop)\n"));
103060
- let offset = Buffer.byteLength(content, "utf-8");
103061
- const watcher = import_node_fs4.default.watchFile(logFile, { interval: 500 }, () => {
103062
- try {
103063
- const stat5 = import_node_fs4.default.statSync(logFile);
103064
- if (stat5.size > offset) {
103065
- const fd = import_node_fs4.default.openSync(logFile, "r");
103066
- const buf = Buffer.alloc(stat5.size - offset);
103067
- import_node_fs4.default.readSync(fd, buf, 0, buf.length, offset);
103068
- import_node_fs4.default.closeSync(fd);
103069
- process.stdout.write(buf.toString("utf-8"));
103070
- offset = stat5.size;
103071
- } else if (stat5.size < offset) {
103072
- offset = 0;
103073
- }
103074
- } catch {
103075
- }
103076
- });
103077
- const cleanup = () => {
103078
- import_node_fs4.default.unwatchFile(logFile);
103079
- process.exit(0);
103080
- };
103081
- process.on("SIGINT", cleanup);
103082
- process.on("SIGTERM", cleanup);
103083
- });
103084
- svc.command("restart").description("Restart the daemon process (service will auto-relaunch)").action(async () => {
103085
- const platform12 = import_node_os4.default.platform();
103086
- const installed = platform12 === "darwin" ? isInstalledDarwin() : platform12 === "win32" ? isInstalledWindows() : false;
103087
- if (!installed) {
103088
- console.log(source_default.yellow("\n \u26A0 Service is not installed. Use `adhdev daemon:restart` for manual restart."));
103089
- console.log(source_default.gray(" Or install the service first: adhdev service install\n"));
103090
- return;
103091
- }
103092
- rotateLogs();
103093
- const health = await fetchHealth();
103094
- if (!health?.pid) {
103095
- console.log(source_default.yellow("\n \u26A0 Daemon is not currently running."));
103096
- if (platform12 === "darwin") {
103097
- console.log(source_default.gray(" Starting via launchctl..."));
103098
- try {
103099
- (0, import_node_child_process4.execSync)(`launchctl start ${LAUNCHD_LABEL}`, { stdio: "ignore" });
103100
- console.log(source_default.green(" \u2713 Started.\n"));
103101
- } catch {
103102
- console.log(source_default.red(" \u2717 Failed to start. Check: adhdev service logs --err\n"));
103103
- }
103104
- } else {
103105
- console.log(source_default.gray(" Start with: adhdev daemon\n"));
103106
- }
103107
- return;
103108
- }
103109
- console.log(source_default.cyan(`
103110
- Stopping daemon (PID ${health.pid})...`));
103111
- try {
103112
- process.kill(health.pid, "SIGTERM");
103113
- } catch {
103114
- console.log(source_default.yellow(" Could not send SIGTERM. Process may have already exited."));
103115
- }
103116
- await new Promise((r) => setTimeout(r, 2e3));
103117
- if (platform12 === "win32") {
103118
- const vbsPath = getWindowsVbsPath();
103119
- if (import_node_fs4.default.existsSync(vbsPath)) {
103120
- try {
103121
- (0, import_node_child_process4.execSync)(`wscript.exe "${vbsPath}"`, { stdio: "ignore", windowsHide: true });
103122
- } catch {
103123
- }
103124
- }
103125
- }
103126
- let restarted = false;
103127
- for (let i = 0; i < 8; i++) {
103128
- await new Promise((r) => setTimeout(r, 1e3));
103129
- const newHealth = await fetchHealth();
103130
- if (newHealth?.ok && newHealth.pid !== health.pid) {
103131
- restarted = true;
103132
- const info = getProcessInfo(newHealth.pid);
103133
- console.log(source_default.green(` \u2713 Daemon restarted \u2014 new PID ${newHealth.pid}${info ? `, ${info.memMB} MB` : ""}`));
103134
- break;
103135
- }
103136
- }
103137
- if (!restarted) {
103138
- console.log(source_default.yellow(" \u26A0 Daemon did not restart within 10s."));
103139
- console.log(source_default.gray(" Check: adhdev service logs --err"));
103140
- }
103141
- console.log();
103142
- });
103143
- }
103144
- function formatBytes(bytes) {
103145
- if (bytes === 0) return "0 B";
103146
- if (bytes < 1024) return `${bytes} B`;
103147
- if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
103148
- return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
103149
- }
103150
-
103151
104084
  // src/cli/doctor-commands.ts
104085
+ init_service_commands();
103152
104086
  function resolvePackageRoot() {
103153
104087
  return path39.resolve(__dirname, "..", "..");
103154
104088
  }
@@ -104640,6 +105574,9 @@ function registerCdpCommands(program2) {
104640
105574
  });
104641
105575
  }
104642
105576
 
105577
+ // src/cli/index.ts
105578
+ init_service_commands();
105579
+
104643
105580
  // src/cli/mcp-commands.ts
104644
105581
  var import_node_child_process5 = require("child_process");
104645
105582
  var fs23 = __toESM(require("fs"));