adhdev 0.9.82-rc.3 → 0.9.82-rc.30

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`);
4288
4406
  }
4289
- function getPendingMeshCoordinatorEvents() {
4290
- return pendingMeshCoordinatorEvents.slice();
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
+ }
4291
4415
  }
4292
- function clearPendingMeshCoordinatorEvents() {
4293
- pendingMeshCoordinatorEvents.splice(0);
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
+ }
4436
+ }
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
+ }
4453
+ }
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,52 +46638,33 @@ 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 readGitSubmodules(value) {
46642
+ if (!Array.isArray(value)) return void 0;
46643
+ const submodules = value.map((entry) => {
46644
+ const submodule = readObjectRecord(entry);
46645
+ const path42 = readStringValue(submodule.path);
46646
+ const commit = readStringValue(submodule.commit);
46647
+ const repoPath = readStringValue(submodule.repoPath, submodule.repo_root);
46648
+ if (!path42 || !commit || !repoPath) return null;
46649
+ return {
46650
+ path: path42,
46651
+ commit,
46652
+ repoPath,
46653
+ dirty: readBooleanValue(submodule.dirty) ?? false,
46654
+ outOfSync: readBooleanValue(submodule.outOfSync, submodule.out_of_sync) ?? false,
46655
+ lastCheckedAt: readNumberValue(submodule.lastCheckedAt, submodule.last_checked_at) ?? Date.now(),
46656
+ ...readStringValue(submodule.error) ? { error: readStringValue(submodule.error) } : {}
46657
+ };
46658
+ }).filter((entry) => entry !== null);
46659
+ return submodules.length > 0 ? submodules : void 0;
46660
+ }
46661
+ function normalizeInlineMeshGitStatus(status, node, options) {
46446
46662
  const isGitRepo = readBooleanValue(status.isGitRepo);
46447
46663
  if (!Object.keys(status).length || isGitRepo === void 0) return void 0;
46448
46664
  const conflictFiles = Array.isArray(status.conflictFiles) ? status.conflictFiles.filter((value) => typeof value === "string") : [];
46449
46665
  const conflictCount = readNumberValue(status.conflicts) ?? conflictFiles.length;
46450
46666
  const hasConflicts = readBooleanValue(status.hasConflicts) ?? conflictCount > 0;
46667
+ const submodules = readGitSubmodules(status.submodules);
46451
46668
  return {
46452
46669
  workspace: readStringValue(status.workspace, node?.workspace) || "",
46453
46670
  repoRoot: readStringValue(status.repoRoot, node?.repoRoot, node?.workspace) || null,
@@ -46466,29 +46683,285 @@ function buildCachedInlineMeshGitStatus(node) {
46466
46683
  hasConflicts,
46467
46684
  conflictFiles,
46468
46685
  stashCount: readNumberValue(status.stashCount) ?? 0,
46469
- lastCheckedAt: Date.now()
46686
+ lastCheckedAt: options?.lastCheckedAt ?? readNumberValue(status.lastCheckedAt) ?? Date.now(),
46687
+ ...submodules ? { submodules } : {}
46688
+ };
46689
+ }
46690
+ function buildInlineMeshTransitGitStatus(node) {
46691
+ const rawGit = readObjectRecord(node?.lastGit ?? node?.last_git);
46692
+ const gitResult = readObjectRecord(rawGit.result);
46693
+ const directStatus = readObjectRecord(rawGit.status);
46694
+ const nestedStatus = readObjectRecord(gitResult.status);
46695
+ const rawProbe = readObjectRecord(node?.lastProbe ?? node?.last_probe);
46696
+ const probeGit = readObjectRecord(rawProbe.git);
46697
+ const probeGitResult = readObjectRecord(probeGit.result);
46698
+ const probeDirectStatus = readObjectRecord(probeGit.status);
46699
+ const probeNestedStatus = readObjectRecord(probeGitResult.status);
46700
+ const status = Object.keys(directStatus).length ? directStatus : Object.keys(nestedStatus).length ? nestedStatus : Object.keys(probeDirectStatus).length ? probeDirectStatus : Object.keys(probeNestedStatus).length ? probeNestedStatus : {};
46701
+ return normalizeInlineMeshGitStatus(status, node, { lastCheckedAt: Date.now() });
46702
+ }
46703
+ function buildCachedInlineMeshGitStatus(node) {
46704
+ const liveGit = buildInlineMeshTransitGitStatus(node);
46705
+ if (liveGit) return liveGit;
46706
+ const cachedStatus = readObjectRecord(node?.cachedStatus);
46707
+ const cachedGit = readObjectRecord(cachedStatus.git);
46708
+ if (!Object.keys(cachedGit).length) return void 0;
46709
+ return normalizeInlineMeshGitStatus(cachedGit, node);
46710
+ }
46711
+ function shouldDiscardCachedInlineMeshStatus(node) {
46712
+ const cachedStatus = readObjectRecord(node?.cachedStatus);
46713
+ if (!Object.keys(cachedStatus).length) return false;
46714
+ const cachedGit = readObjectRecord(cachedStatus.git);
46715
+ const workspaceError = readStringValue(cachedStatus.error, node?.error);
46716
+ if (workspaceError && /workspace must be an existing directory/i.test(workspaceError)) return true;
46717
+ const isGitRepo = readBooleanValue(cachedGit.isGitRepo);
46718
+ const branch = readStringValue(cachedGit.branch);
46719
+ const headCommit = readStringValue(cachedGit.headCommit);
46720
+ return isGitRepo === false && !branch && !headCommit;
46721
+ }
46722
+ function stripInlineMeshTransientNodeState(node) {
46723
+ if (!node || typeof node !== "object" || Array.isArray(node)) return node;
46724
+ const {
46725
+ cachedStatus,
46726
+ lastGit: _lastGit,
46727
+ last_git: _lastGitLegacy,
46728
+ lastProbe: _lastProbe,
46729
+ last_probe: _lastProbeLegacy,
46730
+ error: _error,
46731
+ health: _health,
46732
+ machineStatus: _machineStatus,
46733
+ lastSeenAt: _lastSeenAt,
46734
+ last_seen_at: _lastSeenAtLegacy,
46735
+ updatedAt: _updatedAt,
46736
+ updated_at: _updatedAtLegacy,
46737
+ activeSession: _activeSession,
46738
+ active_session: _activeSessionLegacy,
46739
+ activeSessionId: _activeSessionId,
46740
+ active_session_id: _activeSessionIdLegacy,
46741
+ sessionId: _sessionId,
46742
+ session_id: _sessionIdLegacy,
46743
+ providerType: _providerType,
46744
+ provider_type: _providerTypeLegacy,
46745
+ providers: _providers,
46746
+ ...rest
46747
+ } = node;
46748
+ if (cachedStatus && !shouldDiscardCachedInlineMeshStatus(node)) {
46749
+ return { ...rest, cachedStatus };
46750
+ }
46751
+ return rest;
46752
+ }
46753
+ function hasInlineMeshTransientNodeState(node) {
46754
+ if (!node || typeof node !== "object" || Array.isArray(node)) return false;
46755
+ 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 || "providers" in node;
46756
+ }
46757
+ function readInlineMeshNodeId(node) {
46758
+ return readStringValue(node?.id, node?.nodeId) || "";
46759
+ }
46760
+ function sanitizeInlineMesh(inlineMesh) {
46761
+ if (!inlineMesh || typeof inlineMesh !== "object" || Array.isArray(inlineMesh)) return inlineMesh;
46762
+ if (!Array.isArray(inlineMesh.nodes)) return inlineMesh;
46763
+ let changed = false;
46764
+ const nodes = inlineMesh.nodes.map((node) => {
46765
+ if (!hasInlineMeshTransientNodeState(node)) return node;
46766
+ changed = true;
46767
+ return stripInlineMeshTransientNodeState(node);
46768
+ });
46769
+ if (!changed) return inlineMesh;
46770
+ return {
46771
+ ...inlineMesh,
46772
+ nodes
46773
+ };
46774
+ }
46775
+ function reconcileInlineMeshCache(cached2, incoming) {
46776
+ if (!cached2 || typeof cached2 !== "object" || Array.isArray(cached2)) return incoming;
46777
+ if (!incoming || typeof incoming !== "object" || Array.isArray(incoming)) return cached2;
46778
+ const cachedNodes = Array.isArray(cached2.nodes) ? cached2.nodes : [];
46779
+ const incomingNodes = Array.isArray(incoming.nodes) ? incoming.nodes : [];
46780
+ if (!cachedNodes.length || !incomingNodes.length) return { ...cached2, ...incoming };
46781
+ const incomingById = /* @__PURE__ */ new Map();
46782
+ for (const node of incomingNodes) {
46783
+ const nodeId = readInlineMeshNodeId(node);
46784
+ if (nodeId) incomingById.set(nodeId, node);
46785
+ }
46786
+ const nodes = cachedNodes.map((cachedNode) => {
46787
+ const nodeId = readInlineMeshNodeId(cachedNode);
46788
+ const incomingNode = nodeId ? incomingById.get(nodeId) : void 0;
46789
+ if (!incomingNode) return cachedNode;
46790
+ if (hasInlineMeshTransientNodeState(incomingNode)) {
46791
+ return { ...cachedNode, ...incomingNode };
46792
+ }
46793
+ return { ...stripInlineMeshTransientNodeState(cachedNode), ...incomingNode };
46794
+ });
46795
+ return {
46796
+ ...cached2,
46797
+ ...incoming,
46798
+ nodes
46470
46799
  };
46471
46800
  }
46472
- function applyCachedInlineMeshNodeStatus(status, node) {
46801
+ function hasGitWorktreeChanges(git) {
46802
+ if (!git) return false;
46803
+ return Number(git.staged || 0) + Number(git.modified || 0) + Number(git.untracked || 0) + Number(git.deleted || 0) + Number(git.renamed || 0) > 0;
46804
+ }
46805
+ function getGitSubmoduleDriftState(git) {
46806
+ const submodules = Array.isArray(git?.submodules) ? git.submodules : [];
46807
+ let dirty = false;
46808
+ let outOfSync = false;
46809
+ for (const entry of submodules) {
46810
+ const submodule = readObjectRecord(entry);
46811
+ if (readBooleanValue(submodule.dirty) === true) dirty = true;
46812
+ if (readBooleanValue(submodule.outOfSync) === true || !!readStringValue(submodule.error)) outOfSync = true;
46813
+ }
46814
+ return { dirty, outOfSync };
46815
+ }
46816
+ function deriveMeshNodeHealthFromGit(git) {
46817
+ if (!git || readBooleanValue(git.isGitRepo) === false) return "degraded";
46818
+ const branch = readStringValue(git.branch);
46819
+ if (!branch) return "degraded";
46820
+ const submoduleDrift = getGitSubmoduleDriftState(git);
46821
+ if (submoduleDrift.outOfSync) return "degraded";
46822
+ if (submoduleDrift.dirty || hasGitWorktreeChanges(git)) return "dirty";
46823
+ return "online";
46824
+ }
46825
+ function readCachedInlineMeshActiveSessions(node) {
46473
46826
  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);
46827
+ const activeSession = readObjectRecord(cachedStatus.activeSession);
46828
+ const fallbackSession = Object.keys(activeSession).length ? activeSession : readObjectRecord(node?.activeSession ?? node?.active_session);
46829
+ const sessionId = readStringValue(fallbackSession.id, fallbackSession.sessionId, fallbackSession.session_id, node?.activeSessionId, node?.active_session_id, node?.sessionId, node?.session_id);
46830
+ return sessionId ? [sessionId] : [];
46831
+ }
46832
+ function readCachedInlineMeshActiveSessionDetails(node) {
46833
+ const cachedStatus = readObjectRecord(node?.cachedStatus);
46834
+ const activeSession = readObjectRecord(cachedStatus.activeSession);
46835
+ const fallbackSession = Object.keys(activeSession).length ? activeSession : readObjectRecord(node?.activeSession ?? node?.active_session);
46836
+ const sessionId = readStringValue(
46837
+ fallbackSession.id,
46838
+ fallbackSession.sessionId,
46839
+ fallbackSession.session_id,
46840
+ node?.activeSessionId,
46841
+ node?.active_session_id,
46842
+ node?.sessionId,
46843
+ node?.session_id
46844
+ );
46845
+ if (!sessionId) return [];
46846
+ return [{
46847
+ sessionId,
46848
+ providerType: readStringValue(
46849
+ fallbackSession.providerType,
46850
+ fallbackSession.provider_type,
46851
+ fallbackSession.cliType,
46852
+ fallbackSession.cli_type,
46853
+ fallbackSession.provider,
46854
+ node?.providerType,
46855
+ node?.provider_type
46856
+ ),
46857
+ state: readStringValue(fallbackSession.status, fallbackSession.state, fallbackSession.lifecycle),
46858
+ lifecycle: readStringValue(fallbackSession.lifecycle),
46859
+ title: readStringValue(fallbackSession.title, fallbackSession.displayName, fallbackSession.display_name) ?? null,
46860
+ workspace: readStringValue(fallbackSession.workspace, node?.workspace) ?? null,
46861
+ lastActivityAt: readStringValue(fallbackSession.lastActivityAt, fallbackSession.last_activity_at) ?? null,
46862
+ recoveryState: readStringValue(fallbackSession.recoveryState, fallbackSession.recovery_state) ?? null,
46863
+ isCached: true
46864
+ }];
46865
+ }
46866
+ function readLiveMeshSessionState(record2) {
46867
+ return readStringValue(
46868
+ record2?.meta?.sessionStatus,
46869
+ record2?.meta?.status,
46870
+ record2?.meta?.providerStatus,
46871
+ record2?.status,
46872
+ record2?.state,
46873
+ record2?.lifecycle
46874
+ );
46875
+ }
46876
+ function toIsoTimestamp(value) {
46877
+ if (typeof value === "number" && Number.isFinite(value)) return new Date(value).toISOString();
46878
+ const stringValue = readStringValue(value);
46879
+ return stringValue || null;
46880
+ }
46881
+ function summarizeMeshSessionRecord(record2) {
46882
+ return {
46883
+ sessionId: readStringValue(record2?.sessionId) || "unknown",
46884
+ providerType: readStringValue(record2?.providerType),
46885
+ state: readLiveMeshSessionState(record2),
46886
+ lifecycle: readStringValue(record2?.lifecycle),
46887
+ surfaceKind: getSessionHostSurfaceKind(record2),
46888
+ recoveryState: readStringValue(record2?.meta?.runtimeRecoveryState) ?? null,
46889
+ workspace: readStringValue(record2?.workspace) ?? null,
46890
+ title: readStringValue(record2?.displayName, record2?.workspaceLabel) ?? null,
46891
+ lastActivityAt: toIsoTimestamp(record2?.updatedAt ?? record2?.lastActivityAt ?? record2?.last_activity_at),
46892
+ isCached: false
46893
+ };
46894
+ }
46895
+ function liveSessionRecordMatchesMeshNode(record2, meshId, nodeId) {
46896
+ const recordNodeId = readStringValue(record2?.meta?.meshNodeId);
46897
+ if (!recordNodeId || recordNodeId !== nodeId) return false;
46898
+ const recordMeshId = readStringValue(record2?.meta?.meshNodeFor);
46899
+ return !recordMeshId || recordMeshId === meshId;
46900
+ }
46901
+ function liveSessionRecordMatchesMeshWorkspace(record2, meshId, workspace) {
46902
+ const recordWorkspace = readStringValue(record2?.workspace);
46903
+ if (!recordWorkspace || !workspace || recordWorkspace !== workspace) return false;
46904
+ const recordMeshId = readStringValue(record2?.meta?.meshNodeFor);
46905
+ if (recordMeshId) return recordMeshId === meshId;
46906
+ return record2?.meta?.launchedByCoordinator === true || !!readStringValue(record2?.meta?.meshNodeId);
46907
+ }
46908
+ function readLiveMeshNodeWorkspace(args) {
46909
+ const directNodeWorkspace = args.liveSessionRecords.find((record2) => liveSessionRecordMatchesMeshNode(record2, args.meshId, args.nodeId) && readStringValue(record2?.workspace));
46910
+ if (directNodeWorkspace) {
46911
+ return readStringValue(directNodeWorkspace.workspace) || "";
46912
+ }
46913
+ if (args.allowCoordinatorSession) {
46914
+ const coordinatorWorkspace = args.liveSessionRecords.find((record2) => readStringValue(record2?.meta?.meshCoordinatorFor) === args.meshId && readStringValue(record2?.workspace));
46915
+ if (coordinatorWorkspace) {
46916
+ return readStringValue(coordinatorWorkspace.workspace) || "";
46917
+ }
46918
+ }
46919
+ return "";
46920
+ }
46921
+ function collectLiveMeshSessionRecords(args) {
46922
+ const matches = args.liveSessionRecords.filter((record2) => {
46923
+ const nodeWorkspace = readStringValue(args.node?.workspace);
46924
+ if (liveSessionRecordMatchesMeshNode(record2, args.meshId, args.nodeId)) return true;
46925
+ return !!nodeWorkspace && liveSessionRecordMatchesMeshWorkspace(record2, args.meshId, nodeWorkspace);
46926
+ });
46927
+ if (args.allowCoordinatorSession) {
46928
+ for (const record2 of args.liveSessionRecords) {
46929
+ if (readStringValue(record2?.meta?.meshCoordinatorFor) !== args.meshId) continue;
46930
+ const sessionId = readStringValue(record2?.sessionId);
46931
+ if (sessionId && matches.some((entry) => readStringValue(entry?.sessionId) === sessionId)) continue;
46932
+ matches.push(record2);
46933
+ }
46934
+ }
46935
+ return matches;
46936
+ }
46937
+ function applyCachedInlineMeshNodeStatus(status, node, options) {
46938
+ const cachedStatus = readObjectRecord(node?.cachedStatus);
46939
+ const liveGit = buildInlineMeshTransitGitStatus(node);
46940
+ const git = options?.skipGit ? void 0 : liveGit ?? buildCachedInlineMeshGitStatus(node);
46941
+ const error48 = options?.skipError ? void 0 : liveGit ? void 0 : readStringValue(cachedStatus.error, node?.error);
46942
+ const health = options?.skipHealth ? void 0 : liveGit ? void 0 : readStringValue(cachedStatus.health, node?.health);
46477
46943
  const machineStatus = readStringValue(cachedStatus.machineStatus, node?.machineStatus);
46478
- if (!git && !error48 && !health) return false;
46479
- if (!machineStatus && !git && !error48) return false;
46944
+ const lastSeenAt = toIsoTimestamp(cachedStatus.lastSeenAt ?? cachedStatus.last_seen_at ?? node?.lastSeenAt ?? node?.last_seen_at);
46945
+ const updatedAt = toIsoTimestamp(cachedStatus.updatedAt ?? cachedStatus.updated_at ?? node?.updatedAt ?? node?.updated_at);
46946
+ const activeSessions = readCachedInlineMeshActiveSessions(node);
46947
+ const activeSessionDetails = readCachedInlineMeshActiveSessionDetails(node);
46948
+ if (!git && !error48 && !health && !machineStatus && !lastSeenAt && !updatedAt && activeSessions.length === 0) return false;
46480
46949
  if (git) status.git = git;
46481
46950
  if (error48) status.error = error48;
46951
+ if (machineStatus) status.machineStatus = machineStatus;
46952
+ if (lastSeenAt) status.lastSeenAt = lastSeenAt;
46953
+ if (updatedAt) status.updatedAt = updatedAt;
46954
+ if (activeSessions.length > 0) status.activeSessions = activeSessions;
46955
+ if (activeSessionDetails.length > 0) status.activeSessionDetails = activeSessionDetails;
46482
46956
  if (health) {
46483
46957
  status.health = health;
46484
46958
  return true;
46485
46959
  }
46486
46960
  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";
46961
+ status.health = deriveMeshNodeHealthFromGit(git);
46489
46962
  return true;
46490
46963
  }
46491
- return false;
46964
+ return activeSessions.length > 0 || !!machineStatus || !!lastSeenAt || !!updatedAt;
46492
46965
  }
46493
46966
  async function resolveProviderTypeFromPriority(args) {
46494
46967
  if (!args.providerPriority.length) {
@@ -46521,7 +46994,7 @@ function truncateValidationOutput(value) {
46521
46994
  }
46522
46995
  function readPackageScripts(workspace) {
46523
46996
  try {
46524
- const packageJsonPath = (0, import_path6.join)(workspace, "package.json");
46997
+ const packageJsonPath = (0, import_path7.join)(workspace, "package.json");
46525
46998
  const parsed = JSON.parse(fs10.readFileSync(packageJsonPath, "utf-8"));
46526
46999
  return parsed?.scripts && typeof parsed.scripts === "object" && !Array.isArray(parsed.scripts) ? parsed.scripts : {};
46527
47000
  } catch {
@@ -46729,13 +47202,13 @@ function serializeMeshCoordinatorMcpConfig(config2, format) {
46729
47202
  }
46730
47203
  function resolveHermesUserHome() {
46731
47204
  const explicitHome = process.env.HERMES_HOME?.trim();
46732
- return explicitHome || (0, import_path6.join)((0, import_os4.homedir)(), ".hermes");
47205
+ return explicitHome || (0, import_path7.join)((0, import_os4.homedir)(), ".hermes");
46733
47206
  }
46734
47207
  function loadHermesCoordinatorBaseConfig(targetConfigPath) {
46735
47208
  const sourceHome = resolveHermesUserHome();
46736
- const sourceConfigPath = (0, import_path6.join)(sourceHome, "config.yaml");
47209
+ const sourceConfigPath = (0, import_path7.join)(sourceHome, "config.yaml");
46737
47210
  if (!fs10.existsSync(sourceConfigPath)) return { config: {}, sourceHome, sourceConfigPath };
46738
- if ((0, import_path6.resolve)(sourceConfigPath) === (0, import_path6.resolve)(targetConfigPath)) return { config: {}, sourceHome, sourceConfigPath };
47211
+ if ((0, import_path7.resolve)(sourceConfigPath) === (0, import_path7.resolve)(targetConfigPath)) return { config: {}, sourceHome, sourceConfigPath };
46739
47212
  const parsed = parseMeshCoordinatorMcpConfig(fs10.readFileSync(sourceConfigPath, "utf-8"), "hermes_config_yaml");
46740
47213
  const { mcp_servers: _mcpServers, ...baseConfig } = parsed;
46741
47214
  return { config: baseConfig, sourceHome, sourceConfigPath };
@@ -46769,10 +47242,10 @@ function stripHermesCoordinatorTempModelProviderOverrides(config2) {
46769
47242
  return sanitized;
46770
47243
  }
46771
47244
  function copyHermesCoordinatorCredentialFiles(sourceHome, targetHome) {
46772
- if ((0, import_path6.resolve)(sourceHome) === (0, import_path6.resolve)(targetHome)) return;
47245
+ if ((0, import_path7.resolve)(sourceHome) === (0, import_path7.resolve)(targetHome)) return;
46773
47246
  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);
47247
+ const sourcePath = (0, import_path7.join)(sourceHome, fileName);
47248
+ const targetPath = (0, import_path7.join)(targetHome, fileName);
46776
47249
  if (!fs10.existsSync(sourcePath)) continue;
46777
47250
  try {
46778
47251
  fs10.copyFileSync(sourcePath, targetPath);
@@ -46862,7 +47335,7 @@ function summarizeSessionHostPruneResult(result) {
46862
47335
  keptCount: Array.isArray(value.keptSessionIds) ? value.keptSessionIds.length : void 0
46863
47336
  };
46864
47337
  }
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;
47338
+ 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
47339
  var init_router = __esm({
46867
47340
  "../../oss/packages/daemon-core/src/commands/router.ts"() {
46868
47341
  "use strict";
@@ -46878,6 +47351,7 @@ var init_router = __esm({
46878
47351
  init_chat_history();
46879
47352
  init_ide_detector();
46880
47353
  init_cli_detector();
47354
+ init_git_status();
46881
47355
  init_logger();
46882
47356
  init_command_log();
46883
47357
  init_js_yaml();
@@ -46891,7 +47365,7 @@ var init_router = __esm({
46891
47365
  init_snapshot();
46892
47366
  init_upgrade_helper();
46893
47367
  import_os4 = require("os");
46894
- import_path6 = require("path");
47368
+ import_path7 = require("path");
46895
47369
  fs10 = __toESM(require("fs"));
46896
47370
  CHANNEL_NPM_TAG = { stable: "latest", preview: "next" };
46897
47371
  CHANNEL_SERVER_URL = {
@@ -46922,25 +47396,40 @@ var init_router = __esm({
46922
47396
  }
46923
47397
  getCachedInlineMesh(meshId, inlineMesh) {
46924
47398
  if (inlineMesh && typeof inlineMesh === "object") {
46925
- this.inlineMeshCache.set(meshId, inlineMesh);
46926
- return inlineMesh;
47399
+ return this.warmInlineMeshCache(meshId, inlineMesh);
46927
47400
  }
46928
47401
  return this.inlineMeshCache.get(meshId);
46929
47402
  }
47403
+ warmInlineMeshCache(meshId, inlineMesh) {
47404
+ if (!inlineMesh || typeof inlineMesh !== "object") return void 0;
47405
+ const sanitizedInlineMesh = sanitizeInlineMesh(inlineMesh);
47406
+ const cached2 = this.inlineMeshCache.get(meshId);
47407
+ if (cached2) {
47408
+ const merged = reconcileInlineMeshCache(cached2, sanitizedInlineMesh);
47409
+ this.inlineMeshCache.set(meshId, merged);
47410
+ return merged;
47411
+ }
47412
+ this.inlineMeshCache.set(meshId, sanitizedInlineMesh);
47413
+ return sanitizedInlineMesh;
47414
+ }
46930
47415
  async getMeshForCommand(meshId, inlineMesh, options) {
46931
47416
  const preferInline = options?.preferInline === true;
46932
47417
  if (preferInline) {
46933
- const cached3 = this.getCachedInlineMesh(meshId, inlineMesh);
46934
- if (cached3) return { mesh: cached3, inline: true };
47418
+ const cached3 = this.getCachedInlineMesh(meshId);
47419
+ if (cached3) return { mesh: cached3, inline: true, source: "inline_cache" };
47420
+ const warmedInline2 = this.warmInlineMeshCache(meshId, inlineMesh);
47421
+ if (warmedInline2) return { mesh: warmedInline2, inline: true, source: "inline_bootstrap" };
46935
47422
  }
46936
47423
  try {
46937
47424
  const { getMesh: getMesh3 } = await Promise.resolve().then(() => (init_mesh_config(), mesh_config_exports));
46938
47425
  const mesh = getMesh3(meshId);
46939
- if (mesh) return { mesh, inline: false };
47426
+ if (mesh) return { mesh, inline: false, source: "local_config" };
46940
47427
  } catch {
46941
47428
  }
46942
- const cached2 = this.getCachedInlineMesh(meshId, inlineMesh);
46943
- return cached2 ? { mesh: cached2, inline: true } : null;
47429
+ const cached2 = this.getCachedInlineMesh(meshId);
47430
+ if (cached2) return { mesh: cached2, inline: true, source: "inline_cache" };
47431
+ const warmedInline = this.warmInlineMeshCache(meshId, inlineMesh);
47432
+ return warmedInline ? { mesh: warmedInline, inline: true, source: "inline_bootstrap" } : null;
46944
47433
  }
46945
47434
  updateInlineMeshNode(meshId, mesh, node) {
46946
47435
  if (!mesh || !Array.isArray(mesh.nodes) || !node?.id) return;
@@ -47005,7 +47494,7 @@ var init_router = __esm({
47005
47494
  }
47006
47495
  const { resolveWorktreePath: resolveWorktreePath2, listWorktrees: listWorktrees2, removeWorktree: removeWorktree2 } = await Promise.resolve().then(() => (init_git_worktree(), git_worktree_exports));
47007
47496
  const normalizePath3 = (value) => {
47008
- const resolved = (0, import_path6.resolve)(value);
47497
+ const resolved = (0, import_path7.resolve)(value);
47009
47498
  try {
47010
47499
  return fs10.realpathSync(resolved);
47011
47500
  } catch {
@@ -47169,6 +47658,7 @@ var init_router = __esm({
47169
47658
  const deletedSessionIds = [];
47170
47659
  const skippedSessionIds = [];
47171
47660
  const skippedLiveSessionIds = [];
47661
+ const skippedCoordinatorSessionIds = [];
47172
47662
  const deleteUnsupportedSessionIds = [];
47173
47663
  const recordsRemainSessionIds = [];
47174
47664
  const errors = [];
@@ -47201,6 +47691,12 @@ var init_router = __esm({
47201
47691
  const completed = this.isCompletedHostedSession(record2);
47202
47692
  const surfaceKind = getSessionHostSurfaceKind(record2);
47203
47693
  const liveRuntime = surfaceKind === "live_runtime";
47694
+ const coordinatorSession = readStringValue(record2?.meta?.meshCoordinatorFor) === args.meshId;
47695
+ if (!hasExplicitSessionIds && coordinatorSession) {
47696
+ skippedSessionIds.push(sessionId);
47697
+ skippedCoordinatorSessionIds.push(sessionId);
47698
+ continue;
47699
+ }
47204
47700
  if (!hasExplicitSessionIds && liveRuntime) {
47205
47701
  skippedSessionIds.push(sessionId);
47206
47702
  skippedLiveSessionIds.push(sessionId);
@@ -47266,6 +47762,7 @@ var init_router = __esm({
47266
47762
  deletedSessionIds,
47267
47763
  skippedSessionIds,
47268
47764
  skippedLiveSessionIds,
47765
+ skippedCoordinatorSessionIds,
47269
47766
  ...deleteUnsupported ? {
47270
47767
  deleteUnsupported: true,
47271
47768
  effectiveCleanup: args.mode === "stop_and_delete" ? "stopped_only_records_remain" : "delete_unsupported_records_remain",
@@ -47398,7 +47895,8 @@ var init_router = __esm({
47398
47895
  return handleMeshForwardEvent({ instanceManager: this.deps.instanceManager }, args);
47399
47896
  }
47400
47897
  case "get_pending_mesh_events": {
47401
- const events = drainPendingMeshCoordinatorEvents();
47898
+ const meshId = typeof args?.meshId === "string" ? args.meshId.trim() : "";
47899
+ const events = drainPendingMeshCoordinatorEvents(meshId || void 0);
47402
47900
  return { success: true, events };
47403
47901
  }
47404
47902
  case "launch_cli":
@@ -47927,14 +48425,8 @@ var init_router = __esm({
47927
48425
  case "get_mesh": {
47928
48426
  const meshId = typeof args?.meshId === "string" ? args.meshId.trim() : "";
47929
48427
  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 {
47935
- }
47936
- const cached2 = this.inlineMeshCache.get(meshId);
47937
- if (cached2) return { success: true, mesh: cached2 };
48428
+ const meshRecord = await this.getMeshForCommand(meshId, args?.inlineMesh, { preferInline: true });
48429
+ if (meshRecord?.mesh) return { success: true, mesh: meshRecord.mesh };
47938
48430
  return { success: false, error: "Mesh not found" };
47939
48431
  }
47940
48432
  case "create_mesh": {
@@ -48456,7 +48948,14 @@ var init_router = __esm({
48456
48948
  cliType
48457
48949
  };
48458
48950
  }
48459
- const workspace = typeof coordinatorNode.workspace === "string" ? coordinatorNode.workspace.trim() : "";
48951
+ const sessionHostRecords = this.deps.sessionHostControl?.listSessions ? await this.deps.sessionHostControl.listSessions().catch(() => []) : [];
48952
+ const liveMeshSessions = partitionSessionHostRecords(Array.isArray(sessionHostRecords) ? sessionHostRecords : []).liveRuntimes;
48953
+ const workspace = readLiveMeshNodeWorkspace({
48954
+ meshId,
48955
+ nodeId: String(coordinatorNode.id || coordinatorNode.nodeId || preferredCoordinatorNodeId || ""),
48956
+ liveSessionRecords: liveMeshSessions,
48957
+ allowCoordinatorSession: true
48958
+ }) || (typeof coordinatorNode.workspace === "string" ? coordinatorNode.workspace.trim() : "");
48460
48959
  if (!workspace) return { success: false, error: "Coordinator node workspace required", meshId, cliType };
48461
48960
  if (!cliType) {
48462
48961
  const resolved = await resolveProviderTypeFromPriority({
@@ -48618,7 +49117,7 @@ ${block}`);
48618
49117
  workspace
48619
49118
  };
48620
49119
  }
48621
- const { existsSync: existsSync32, readFileSync: readFileSync23, writeFileSync: writeFileSync18, copyFileSync: copyFileSync5, mkdirSync: mkdirSync22 } = await import("fs");
49120
+ const { existsSync: existsSync33, readFileSync: readFileSync24, writeFileSync: writeFileSync18, copyFileSync: copyFileSync5, mkdirSync: mkdirSync22 } = await import("fs");
48622
49121
  const { dirname: dirname13 } = await import("path");
48623
49122
  const mcpConfigPath = coordinatorSetup.configPath;
48624
49123
  const hermesManualFallback = cliType === "hermes-cli" && configFormat === "hermes_config_yaml" ? createHermesManualMeshCoordinatorSetup(meshId, workspace) : null;
@@ -48661,14 +49160,14 @@ ${block}`);
48661
49160
  if (hermesManualFallback) return returnManualFallback(message);
48662
49161
  return { success: false, code: "mesh_coordinator_config_write_failed", error: message, meshId, cliType, workspace };
48663
49162
  }
48664
- const hadExistingMcpConfig = existsSync32(mcpConfigPath);
49163
+ const hadExistingMcpConfig = existsSync33(mcpConfigPath);
48665
49164
  let existingMcpConfig = hermesBaseConfig?.config || {};
48666
49165
  if (hermesBaseConfig) {
48667
49166
  copyHermesCoordinatorCredentialFiles(hermesBaseConfig.sourceHome, dirname13(mcpConfigPath));
48668
49167
  }
48669
49168
  if (hadExistingMcpConfig) {
48670
49169
  try {
48671
- const parsedExistingMcpConfig = parseMeshCoordinatorMcpConfig(readFileSync23(mcpConfigPath, "utf-8"), configFormat);
49170
+ const parsedExistingMcpConfig = parseMeshCoordinatorMcpConfig(readFileSync24(mcpConfigPath, "utf-8"), configFormat);
48672
49171
  const existingCoordinatorConfig = hermesManualFallback ? stripHermesCoordinatorTempModelProviderOverrides(parsedExistingMcpConfig) : parsedExistingMcpConfig;
48673
49172
  existingMcpConfig = { ...existingMcpConfig, ...existingCoordinatorConfig };
48674
49173
  copyFileSync5(mcpConfigPath, mcpConfigPath + ".backup");
@@ -48764,92 +49263,157 @@ ${block}`);
48764
49263
  const { readLedgerEntries: readLedgerEntries2, getLedgerSummary: getLedgerSummary2 } = await Promise.resolve().then(() => (init_mesh_ledger(), mesh_ledger_exports));
48765
49264
  const ledgerEntries = readLedgerEntries2(meshId, { tail: 20 });
48766
49265
  const ledgerSummary = getLedgerSummary2(meshId);
49266
+ const sessionHostRecords = this.deps.sessionHostControl?.listSessions ? await this.deps.sessionHostControl.listSessions().catch(() => []) : [];
49267
+ const liveMeshSessions = partitionSessionHostRecords(Array.isArray(sessionHostRecords) ? sessionHostRecords : []).liveRuntimes;
49268
+ const localMachineId = loadConfig().machineId || "";
49269
+ const selectedCoordinatorNodeId = readStringValue(
49270
+ mesh.coordinator?.preferredNodeId,
49271
+ mesh.nodes?.[0]?.id,
49272
+ mesh.nodes?.[0]?.nodeId
49273
+ );
49274
+ const inlineCoordinatorNodeId = meshRecord?.inline && Array.isArray(mesh.nodes) ? selectedCoordinatorNodeId : void 0;
49275
+ const refreshedAt = (/* @__PURE__ */ new Date()).toISOString();
48767
49276
  const nodeStatuses = [];
48768
- for (const node of mesh.nodes || []) {
49277
+ for (const [nodeIndex, node] of (mesh.nodes || []).entries()) {
49278
+ const nodeId = String(node.id || node.nodeId || "");
49279
+ const daemonId = readStringValue(node.daemonId);
49280
+ const providerPriority = readProviderPriorityFromPolicy(node.policy);
49281
+ const isSelfNode = Boolean(
49282
+ nodeId && inlineCoordinatorNodeId && nodeId === inlineCoordinatorNodeId
49283
+ ) || Boolean(
49284
+ daemonId && (daemonId === localMachineId || daemonId === this.deps.statusInstanceId)
49285
+ ) || Boolean(meshRecord?.inline && nodeIndex === 0);
48769
49286
  const status = {
48770
- nodeId: node.id || node.nodeId,
49287
+ nodeId,
48771
49288
  machineLabel: node.machineLabel || node.id || node.nodeId,
48772
49289
  workspace: node.workspace,
48773
49290
  repoRoot: node.repoRoot,
48774
49291
  isLocalWorktree: node.isLocalWorktree,
48775
49292
  worktreeBranch: node.worktreeBranch,
48776
- daemonId: node.daemonId,
49293
+ daemonId,
48777
49294
  machineId: node.machineId,
49295
+ machineStatus: node.machineStatus,
48778
49296
  health: "unknown",
48779
49297
  providers: node.providers || [],
48780
- activeSessions: []
49298
+ providerPriority,
49299
+ activeSessions: [],
49300
+ activeSessionDetails: [],
49301
+ launchReady: false
48781
49302
  };
48782
- if (node.workspace && typeof node.workspace === "string") {
48783
- if (!fs10.existsSync(node.workspace) && applyCachedInlineMeshNodeStatus(status, node)) {
48784
- nodeStatuses.push(status);
48785
- continue;
49303
+ if (isSelfNode) {
49304
+ status.connection = {
49305
+ perspective: "selected_coordinator",
49306
+ source: "mesh_peer_status",
49307
+ state: "self",
49308
+ transport: "local",
49309
+ reported: true,
49310
+ reason: "Selected coordinator daemon",
49311
+ lastStateChangeAt: refreshedAt
49312
+ };
49313
+ } else if (daemonId) {
49314
+ const connection = this.deps.getMeshPeerConnectionStatus?.(daemonId);
49315
+ status.connection = connection ?? {
49316
+ perspective: "selected_coordinator",
49317
+ source: "not_reported",
49318
+ state: "unknown",
49319
+ transport: "unknown",
49320
+ reported: false,
49321
+ reason: "No live mesh peer telemetry reported by the selected coordinator yet."
49322
+ };
49323
+ } else {
49324
+ status.connection = {
49325
+ perspective: "selected_coordinator",
49326
+ source: "not_reported",
49327
+ state: "unknown",
49328
+ transport: "unknown",
49329
+ reported: false,
49330
+ reason: "Node has no daemon id, so mesh transport cannot be reported from the selected coordinator."
49331
+ };
49332
+ }
49333
+ const matchedLiveSessionRecords = collectLiveMeshSessionRecords({
49334
+ meshId,
49335
+ node,
49336
+ nodeId,
49337
+ liveSessionRecords: liveMeshSessions,
49338
+ allowCoordinatorSession: nodeId === selectedCoordinatorNodeId
49339
+ });
49340
+ const workspace = readLiveMeshNodeWorkspace({
49341
+ meshId,
49342
+ nodeId,
49343
+ liveSessionRecords: matchedLiveSessionRecords,
49344
+ allowCoordinatorSession: nodeId === selectedCoordinatorNodeId
49345
+ }) || (typeof node.workspace === "string" ? node.workspace : "");
49346
+ status.workspace = workspace || node.workspace;
49347
+ if (matchedLiveSessionRecords.length > 0) {
49348
+ const sessionIds = matchedLiveSessionRecords.map((record2) => typeof record2?.sessionId === "string" ? record2.sessionId : "").filter(Boolean);
49349
+ const providerTypes = matchedLiveSessionRecords.map((record2) => readStringValue(record2?.providerType)).filter(Boolean);
49350
+ status.activeSessions = sessionIds;
49351
+ status.activeSessionDetails = matchedLiveSessionRecords.map(summarizeMeshSessionRecord);
49352
+ if (providerTypes.length > 0) {
49353
+ status.providers = Array.from(/* @__PURE__ */ new Set([...Array.isArray(status.providers) ? status.providers : [], ...providerTypes]));
48786
49354
  }
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;
49355
+ }
49356
+ if (workspace) {
49357
+ if (!fs10.existsSync(workspace)) {
49358
+ let remoteProbeApplied = false;
49359
+ if (!isSelfNode && daemonId && this.deps.dispatchMeshCommand) {
49360
+ try {
49361
+ const remoteResult = await Promise.race([
49362
+ this.deps.dispatchMeshCommand(daemonId, "git_status", { workspace }),
49363
+ new Promise((_2, reject) => setTimeout(() => reject(new Error("timeout")), 8e3))
49364
+ ]);
49365
+ const remoteGit = remoteResult?.status ?? remoteResult?.git ?? remoteResult;
49366
+ if (remoteGit && typeof remoteGit === "object" && typeof remoteGit.isGitRepo === "boolean") {
49367
+ status.git = remoteGit;
49368
+ status.health = remoteGit.isGitRepo ? deriveMeshNodeHealthFromGit(remoteGit) : "degraded";
49369
+ remoteProbeApplied = true;
49370
+ }
49371
+ } catch {
48811
49372
  }
48812
49373
  }
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++;
49374
+ if (!remoteProbeApplied) {
49375
+ const connectionState = readStringValue(status.connection?.state);
49376
+ const inlineTransitGit = buildInlineMeshTransitGitStatus(node);
49377
+ const pendingPeerGitProbe = !inlineTransitGit && !isSelfNode && !!daemonId && (readStringValue(status.machineStatus) === "online" || readStringValue(status.health) === "online" || connectionState === "connecting" || connectionState === "connected" || connectionState === "unknown");
49378
+ if (pendingPeerGitProbe) {
49379
+ status.gitProbePending = true;
49380
+ status.health = "unknown";
49381
+ }
49382
+ if (applyCachedInlineMeshNodeStatus(
49383
+ status,
49384
+ node,
49385
+ pendingPeerGitProbe ? { skipGit: true, skipError: true, skipHealth: true } : void 0
49386
+ )) {
49387
+ status.launchReady = !!daemonId && (readStringValue(status.machineStatus) === "online" || isSelfNode);
49388
+ nodeStatuses.push(status);
49389
+ continue;
49390
+ }
49391
+ if (meshRecord?.source === "inline_cache" && !isSelfNode) {
49392
+ status.launchReady = !!daemonId && (readStringValue(status.machineStatus) === "online" || isSelfNode);
49393
+ nodeStatuses.push(status);
49394
+ continue;
49395
+ }
48823
49396
  }
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";
49397
+ } else {
49398
+ try {
49399
+ const gitStatus = await getGitRepoStatus(workspace, { timeoutMs: 1e4, refreshUpstream: true });
49400
+ status.git = gitStatus;
49401
+ if (gitStatus.isGitRepo) {
49402
+ status.health = deriveMeshNodeHealthFromGit(gitStatus);
49403
+ } else {
49404
+ status.health = "degraded";
49405
+ if (gitStatus.error && !status.error) status.error = gitStatus.error;
49406
+ }
49407
+ } catch {
49408
+ if (!applyCachedInlineMeshNodeStatus(status, node)) {
49409
+ status.health = "degraded";
49410
+ }
48848
49411
  }
48849
49412
  }
48850
49413
  } else {
48851
49414
  applyCachedInlineMeshNodeStatus(status, node);
48852
49415
  }
49416
+ status.launchReady = !!daemonId && (readStringValue(status.machineStatus) === "online" || isSelfNode);
48853
49417
  nodeStatuses.push(status);
48854
49418
  }
48855
49419
  return {
@@ -48858,6 +49422,12 @@ ${block}`);
48858
49422
  meshName: mesh.name,
48859
49423
  repoIdentity: mesh.repoIdentity,
48860
49424
  defaultBranch: mesh.defaultBranch,
49425
+ refreshedAt: (/* @__PURE__ */ new Date()).toISOString(),
49426
+ sourceOfTruth: {
49427
+ membership: meshRecord?.source === "inline_cache" ? "coordinator_inline_mesh_cache" : meshRecord?.source === "local_config" ? "local_mesh_config" : "inline_bootstrap_snapshot",
49428
+ coordinatorOwnsLiveTruth: meshRecord?.source !== "inline_bootstrap",
49429
+ historicalEvidenceOnly: ["recoveryHints", "ledger.summary", "queue.summary"]
49430
+ },
48861
49431
  nodes: nodeStatuses,
48862
49432
  queue: { tasks: queue, summary: queueSummary },
48863
49433
  ledger: { entries: ledgerEntries, summary: ledgerSummary }
@@ -56936,6 +57506,7 @@ async function initDaemonComponents(config2) {
56936
57506
  sessionHostControl: config2.sessionHostControl,
56937
57507
  statusInstanceId: config2.statusInstanceId,
56938
57508
  statusVersion: config2.statusVersion,
57509
+ getMeshPeerConnectionStatus: config2.getMeshPeerConnectionStatus,
56939
57510
  getCdpLogFn: config2.getCdpLogFn || ((ideType) => LOG.forComponent(`CDP:${ideType}`).asLogFn())
56940
57511
  });
56941
57512
  poller = new AgentStreamPoller({
@@ -57237,6 +57808,7 @@ __export(src_exports, {
57237
57808
  prepareSessionChatTailUpdate: () => prepareSessionChatTailUpdate,
57238
57809
  prepareSessionModalUpdate: () => prepareSessionModalUpdate,
57239
57810
  probeCdpPort: () => probeCdpPort,
57811
+ queuePendingMeshCoordinatorEvent: () => queuePendingMeshCoordinatorEvent,
57240
57812
  readChatHistory: () => readChatHistory,
57241
57813
  readLedgerEntries: () => readLedgerEntries,
57242
57814
  readLedgerSlice: () => readLedgerSlice,
@@ -72139,7 +72711,7 @@ var require_buffer_list = __commonJS({
72139
72711
  }
72140
72712
  }, {
72141
72713
  key: "join",
72142
- value: function join39(s) {
72714
+ value: function join40(s) {
72143
72715
  if (this.length === 0) return "";
72144
72716
  var p = this.head;
72145
72717
  var ret = "" + p.data;
@@ -86198,13 +86770,13 @@ function splitStringBySpace(str2) {
86198
86770
  }
86199
86771
  return pieces;
86200
86772
  }
86201
- var import_chardet, import_child_process12, import_fs11, import_node_path3, import_node_os3, import_node_crypto3, import_iconv_lite, ExternalEditor;
86773
+ var import_chardet, import_child_process12, import_fs12, import_node_path3, import_node_os3, import_node_crypto3, import_iconv_lite, ExternalEditor;
86202
86774
  var init_esm4 = __esm({
86203
86775
  "../../node_modules/@inquirer/external-editor/dist/esm/index.js"() {
86204
86776
  "use strict";
86205
86777
  import_chardet = __toESM(require_lib(), 1);
86206
86778
  import_child_process12 = require("child_process");
86207
- import_fs11 = require("fs");
86779
+ import_fs12 = require("fs");
86208
86780
  import_node_path3 = __toESM(require("path"), 1);
86209
86781
  import_node_os3 = __toESM(require("os"), 1);
86210
86782
  import_node_crypto3 = require("crypto");
@@ -86280,14 +86852,14 @@ var init_esm4 = __esm({
86280
86852
  if (Object.prototype.hasOwnProperty.call(this.fileOptions, "mode")) {
86281
86853
  opt.mode = this.fileOptions.mode;
86282
86854
  }
86283
- (0, import_fs11.writeFileSync)(this.tempFile, this.text, opt);
86855
+ (0, import_fs12.writeFileSync)(this.tempFile, this.text, opt);
86284
86856
  } catch (createFileError) {
86285
86857
  throw new CreateFileError(createFileError);
86286
86858
  }
86287
86859
  }
86288
86860
  readTemporaryFile() {
86289
86861
  try {
86290
- const tempFileBuffer = (0, import_fs11.readFileSync)(this.tempFile);
86862
+ const tempFileBuffer = (0, import_fs12.readFileSync)(this.tempFile);
86291
86863
  if (tempFileBuffer.length === 0) {
86292
86864
  this.text = "";
86293
86865
  } else {
@@ -86303,7 +86875,7 @@ var init_esm4 = __esm({
86303
86875
  }
86304
86876
  removeTemporaryFile() {
86305
86877
  try {
86306
- (0, import_fs11.unlinkSync)(this.tempFile);
86878
+ (0, import_fs12.unlinkSync)(this.tempFile);
86307
86879
  } catch (removeFileError) {
86308
86880
  throw new RemoveFileError(removeFileError);
86309
86881
  }
@@ -88005,25 +88577,25 @@ function resolvePackageVersion(options) {
88005
88577
  const injectedVersion = options?.injectedVersion || "unknown";
88006
88578
  const dir = options?.dirname || __dirname;
88007
88579
  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")
88580
+ (0, import_path8.join)(dir, "..", "..", "package.json"),
88581
+ (0, import_path8.join)(dir, "..", "package.json"),
88582
+ (0, import_path8.join)(dir, "package.json")
88011
88583
  ];
88012
88584
  for (const p of possiblePaths) {
88013
88585
  try {
88014
- const data = JSON.parse((0, import_fs12.readFileSync)(p, "utf-8"));
88586
+ const data = JSON.parse((0, import_fs13.readFileSync)(p, "utf-8"));
88015
88587
  if (data.version) return data.version;
88016
88588
  } catch {
88017
88589
  }
88018
88590
  }
88019
88591
  return injectedVersion;
88020
88592
  }
88021
- var import_fs12, import_path7;
88593
+ var import_fs13, import_path8;
88022
88594
  var init_version = __esm({
88023
88595
  "src/version.ts"() {
88024
88596
  "use strict";
88025
- import_fs12 = require("fs");
88026
- import_path7 = require("path");
88597
+ import_fs13 = require("fs");
88598
+ import_path8 = require("path");
88027
88599
  }
88028
88600
  });
88029
88601
 
@@ -89959,7 +90531,7 @@ var require_filesystem = __commonJS({
89959
90531
  var LDD_PATH = "/usr/bin/ldd";
89960
90532
  var SELF_PATH = "/proc/self/exe";
89961
90533
  var MAX_LENGTH = 2048;
89962
- var readFileSync23 = (path42) => {
90534
+ var readFileSync24 = (path42) => {
89963
90535
  const fd = fs24.openSync(path42, "r");
89964
90536
  const buffer = Buffer.alloc(MAX_LENGTH);
89965
90537
  const bytesRead = fs24.readSync(fd, buffer, 0, MAX_LENGTH, 0);
@@ -89984,7 +90556,7 @@ var require_filesystem = __commonJS({
89984
90556
  module2.exports = {
89985
90557
  LDD_PATH,
89986
90558
  SELF_PATH,
89987
- readFileSync: readFileSync23,
90559
+ readFileSync: readFileSync24,
89988
90560
  readFile: readFile2
89989
90561
  };
89990
90562
  }
@@ -90033,7 +90605,7 @@ var require_detect_libc = __commonJS({
90033
90605
  "use strict";
90034
90606
  var childProcess = require("child_process");
90035
90607
  var { isLinux: isLinux2, getReport } = require_process();
90036
- var { LDD_PATH, SELF_PATH, readFile: readFile2, readFileSync: readFileSync23 } = require_filesystem();
90608
+ var { LDD_PATH, SELF_PATH, readFile: readFile2, readFileSync: readFileSync24 } = require_filesystem();
90037
90609
  var { interpreterPath } = require_elf();
90038
90610
  var cachedFamilyInterpreter;
90039
90611
  var cachedFamilyFilesystem;
@@ -90125,7 +90697,7 @@ var require_detect_libc = __commonJS({
90125
90697
  }
90126
90698
  cachedFamilyFilesystem = null;
90127
90699
  try {
90128
- const lddContent = readFileSync23(LDD_PATH);
90700
+ const lddContent = readFileSync24(LDD_PATH);
90129
90701
  cachedFamilyFilesystem = getFamilyFromLddContent(lddContent);
90130
90702
  } catch (e) {
90131
90703
  }
@@ -90150,7 +90722,7 @@ var require_detect_libc = __commonJS({
90150
90722
  }
90151
90723
  cachedFamilyInterpreter = null;
90152
90724
  try {
90153
- const selfContent = readFileSync23(SELF_PATH);
90725
+ const selfContent = readFileSync24(SELF_PATH);
90154
90726
  const path42 = interpreterPath(selfContent);
90155
90727
  cachedFamilyInterpreter = familyFromInterpreterPath(path42);
90156
90728
  } catch (e) {
@@ -90214,7 +90786,7 @@ var require_detect_libc = __commonJS({
90214
90786
  }
90215
90787
  cachedVersionFilesystem = null;
90216
90788
  try {
90217
- const lddContent = readFileSync23(LDD_PATH);
90789
+ const lddContent = readFileSync24(LDD_PATH);
90218
90790
  const versionMatch = lddContent.match(RE_GLIBC_VERSION);
90219
90791
  if (versionMatch) {
90220
90792
  cachedVersionFilesystem = versionMatch[1];
@@ -97213,11 +97785,30 @@ var init_daemon_mesh_manager = __esm({
97213
97785
  nodeDatachannel = null;
97214
97786
  peers = /* @__PURE__ */ new Map();
97215
97787
  // Map<targetDaemonId, PeerEntry>
97788
+ peerSnapshots = /* @__PURE__ */ new Map();
97216
97789
  pendingRequests = /* @__PURE__ */ new Map();
97217
97790
  commandCallback;
97218
97791
  p2pFailure(message, command, targetDaemonId) {
97219
97792
  return new P2pRelayFailureError(message, { command, targetDaemonId });
97220
97793
  }
97794
+ updatePeerSnapshot(targetDaemonId, state, patch = {}) {
97795
+ const previous = this.peerSnapshots.get(targetDaemonId);
97796
+ const now = (/* @__PURE__ */ new Date()).toISOString();
97797
+ this.peerSnapshots.set(targetDaemonId, {
97798
+ perspective: "selected_coordinator",
97799
+ source: "mesh_peer_status",
97800
+ reported: true,
97801
+ state,
97802
+ transport: patch.transport ?? previous?.transport ?? "unknown",
97803
+ reason: patch.reason ?? previous?.reason,
97804
+ lastStateChangeAt: now,
97805
+ lastConnectedAt: patch.lastConnectedAt ?? previous?.lastConnectedAt,
97806
+ lastCommandAt: patch.lastCommandAt ?? previous?.lastCommandAt
97807
+ });
97808
+ }
97809
+ getPeerConnectionStatus(targetDaemonId) {
97810
+ return this.peerSnapshots.get(targetDaemonId) ?? null;
97811
+ }
97221
97812
  invalidatePeer(targetDaemonId, reason, options = {}) {
97222
97813
  const peer = this.peers.get(targetDaemonId);
97223
97814
  if (peer?.commandQueue) {
@@ -97232,6 +97823,11 @@ var init_daemon_mesh_manager = __esm({
97232
97823
  pending.reject(this.p2pFailure(reason, pending.command, targetDaemonId));
97233
97824
  }
97234
97825
  }
97826
+ const snapshotState = peer?.state === "closed" ? "closed" : peer?.state === "disconnected" ? "disconnected" : "failed";
97827
+ this.updatePeerSnapshot(targetDaemonId, snapshotState, {
97828
+ reason,
97829
+ transport: peer?.isRelay === true ? "relay" : peer?.isRelay === false ? "direct" : "unknown"
97830
+ });
97235
97831
  if (options.closeResources !== false && peer) {
97236
97832
  try {
97237
97833
  peer.dataChannel?.close?.();
@@ -97264,6 +97860,7 @@ var init_daemon_mesh_manager = __esm({
97264
97860
  "send_chat",
97265
97861
  "read_chat",
97266
97862
  "get_chat_debug_bundle",
97863
+ "get_pending_mesh_events",
97267
97864
  "git_status",
97268
97865
  "git_diff_summary",
97269
97866
  "launch_cli",
@@ -97351,6 +97948,20 @@ var init_daemon_mesh_manager = __esm({
97351
97948
  if (!peer) {
97352
97949
  throw this.p2pFailure("Failed to initiate P2P connection entry", command, targetDaemonId);
97353
97950
  }
97951
+ const lastCommandAt = (/* @__PURE__ */ new Date()).toISOString();
97952
+ if (peer.state === "connected") {
97953
+ this.updatePeerSnapshot(targetDaemonId, "connected", {
97954
+ transport: peer.isRelay === true ? "relay" : peer.isRelay === false ? "direct" : "unknown",
97955
+ lastConnectedAt: this.peerSnapshots.get(targetDaemonId)?.lastConnectedAt,
97956
+ lastCommandAt
97957
+ });
97958
+ } else {
97959
+ this.updatePeerSnapshot(targetDaemonId, "connecting", {
97960
+ transport: peer.isRelay === true ? "relay" : peer.isRelay === false ? "direct" : "unknown",
97961
+ reason: "Waiting for mesh DataChannel to open.",
97962
+ lastCommandAt
97963
+ });
97964
+ }
97354
97965
  return new Promise((resolve23, reject) => {
97355
97966
  const requestId = `req_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
97356
97967
  const timer = setTimeout(() => {
@@ -97494,6 +98105,9 @@ var init_daemon_mesh_manager = __esm({
97494
98105
  remoteDescriptionSet: false
97495
98106
  };
97496
98107
  this.peers.set(targetDaemonId, entry);
98108
+ this.updatePeerSnapshot(targetDaemonId, "connecting", {
98109
+ reason: isInitiator ? "P2P mesh connection initiated by the selected coordinator." : "Waiting for the remote daemon to finish the mesh DataChannel handshake."
98110
+ });
97497
98111
  pc.onLocalDescription((sdp, type2) => {
97498
98112
  this.serverConn.sendMeshCommand(targetDaemonId, type2 === "offer" ? "mesh_p2p_offer" : "mesh_p2p_answer", { sdp, type: type2 });
97499
98113
  });
@@ -97504,7 +98118,26 @@ var init_daemon_mesh_manager = __esm({
97504
98118
  LOG.info("Mesh", `[Mesh] P2P state with ${targetDaemonId.slice(0, 12)}: ${state}`);
97505
98119
  if (state === "connected") {
97506
98120
  entry.state = "connected";
98121
+ let transport = "unknown";
98122
+ try {
98123
+ const pair = pc.getSelectedCandidatePair?.();
98124
+ if (pair) {
98125
+ const localType = pair.local?.type || "unknown";
98126
+ const remoteType = pair.remote?.type || "unknown";
98127
+ entry.isRelay = localType === "relay" || remoteType === "relay";
98128
+ transport = entry.isRelay ? "relay" : "direct";
98129
+ LOG.info("Mesh", `[Mesh] Candidate pair with ${targetDaemonId.slice(0, 12)}: local=${localType} remote=${remoteType} \u2192 ${transport}`);
98130
+ }
98131
+ } catch {
98132
+ transport = entry.isRelay === true ? "relay" : entry.isRelay === false ? "direct" : "unknown";
98133
+ }
98134
+ this.updatePeerSnapshot(targetDaemonId, "connected", {
98135
+ transport,
98136
+ reason: transport === "relay" ? "Connected over TURN relay." : transport === "direct" ? "Connected directly peer-to-peer." : "Connected, but selected candidate pair details are unavailable.",
98137
+ lastConnectedAt: (/* @__PURE__ */ new Date()).toISOString()
98138
+ });
97507
98139
  } else if (state === "failed" || state === "closed" || state === "disconnected") {
98140
+ entry.state = state;
97508
98141
  this.invalidatePeer(targetDaemonId, `P2P state changed to ${state}`, { rejectPending: true, closeResources: false });
97509
98142
  }
97510
98143
  });
@@ -97522,6 +98155,11 @@ var init_daemon_mesh_manager = __esm({
97522
98155
  dc.onOpen(() => {
97523
98156
  LOG.info("Mesh", `[Mesh] DataChannel OPEN with ${targetDaemonId.slice(0, 12)}`);
97524
98157
  entry.state = "connected";
98158
+ this.updatePeerSnapshot(targetDaemonId, "connected", {
98159
+ transport: entry.isRelay === true ? "relay" : entry.isRelay === false ? "direct" : "unknown",
98160
+ reason: entry.isRelay === true ? "Connected over TURN relay." : entry.isRelay === false ? "Connected directly peer-to-peer." : "DataChannel open; transport details not reported yet.",
98161
+ lastConnectedAt: (/* @__PURE__ */ new Date()).toISOString()
98162
+ });
97525
98163
  if (entry.commandQueue) {
97526
98164
  const queue = entry.commandQueue;
97527
98165
  entry.commandQueue = [];
@@ -97801,6 +98439,7 @@ var init_adhdev_daemon = __esm({
97801
98439
  "use strict";
97802
98440
  init_server_connection();
97803
98441
  init_src();
98442
+ init_mesh_events();
97804
98443
  init_daemon_p2p2();
97805
98444
  init_screenshot_controller();
97806
98445
  init_session_host();
@@ -97817,7 +98456,7 @@ var init_adhdev_daemon = __esm({
97817
98456
  init_version();
97818
98457
  init_src();
97819
98458
  init_runtime_defaults();
97820
- pkgVersion = resolvePackageVersion({ injectedVersion: "0.9.82-rc.3" });
98459
+ pkgVersion = resolvePackageVersion({ injectedVersion: "0.9.82-rc.30" });
97821
98460
  AdhdevDaemon = class _AdhdevDaemon {
97822
98461
  localHttpServer = null;
97823
98462
  localWss = null;
@@ -98341,6 +98980,7 @@ ${err?.stack || ""}`);
98341
98980
  if (!this.meshManager) throw new Error("Mesh manager not initialized");
98342
98981
  return this.meshManager.sendCommand(daemonId, command, args);
98343
98982
  },
98983
+ getMeshPeerConnectionStatus: (daemonId) => this.meshManager?.getPeerConnectionStatus(daemonId) ?? null,
98344
98984
  onStatusChange: () => {
98345
98985
  this.invalidateHotChatSnapshotCache();
98346
98986
  this.statusReporter?.onStatusChange();
@@ -98710,6 +99350,7 @@ ${err?.stack || ""}`);
98710
99350
  const meshId = this.readMeshString(settings.meshNodeFor);
98711
99351
  const coordinatorDaemonId = this.readMeshString(settings.meshCoordinatorDaemonId);
98712
99352
  if (!meshId || !coordinatorDaemonId) return;
99353
+ const relayTimestamp = typeof event.timestamp === "number" && Number.isFinite(event.timestamp) ? event.timestamp : this.readMeshString(event.timestamp) || void 0;
98713
99354
  const payload = {
98714
99355
  event: this.readMeshString(event.event),
98715
99356
  meshId,
@@ -98718,7 +99359,8 @@ ${err?.stack || ""}`);
98718
99359
  targetSessionId: this.readMeshString(event.targetSessionId) || instanceId,
98719
99360
  providerType: this.readMeshString(event.providerType),
98720
99361
  providerSessionId: this.readMeshString(event.providerSessionId),
98721
- finalSummary: this.readMeshString(event.finalSummary) || this.readMeshString(event.summary)
99362
+ finalSummary: this.readMeshString(event.finalSummary) || this.readMeshString(event.summary),
99363
+ ...relayTimestamp !== void 0 ? { timestamp: relayTimestamp } : {}
98722
99364
  };
98723
99365
  if (coordinatorDaemonId === localDaemonId) {
98724
99366
  try {
@@ -98733,6 +99375,22 @@ ${err?.stack || ""}`);
98733
99375
  await this.meshManager.sendCommand(coordinatorDaemonId, "mesh_forward_event", payload);
98734
99376
  LOG.info("MeshEvents", `Relayed ${payload.event} for mesh ${meshId} to coordinator daemon ${coordinatorDaemonId.slice(0, 12)}\u2026`);
98735
99377
  } catch (error48) {
99378
+ queuePendingMeshCoordinatorEvent({
99379
+ event: payload.event,
99380
+ meshId,
99381
+ nodeLabel: payload.nodeId ? `Node '${payload.nodeId}'` : payload.workspace ? `Agent at ${payload.workspace}` : "Remote agent",
99382
+ nodeId: payload.nodeId || void 0,
99383
+ workspace: payload.workspace || void 0,
99384
+ metadataEvent: {
99385
+ targetSessionId: payload.targetSessionId,
99386
+ providerType: payload.providerType,
99387
+ providerSessionId: payload.providerSessionId,
99388
+ finalSummary: payload.finalSummary,
99389
+ workspace: payload.workspace,
99390
+ ...payload.timestamp !== void 0 ? { timestamp: payload.timestamp } : {}
99391
+ },
99392
+ queuedAt: Date.now()
99393
+ });
98736
99394
  LOG.warn("MeshEvents", `Failed to relay ${payload.event} for mesh ${meshId}: ${error48?.message || error48}`);
98737
99395
  }
98738
99396
  }
@@ -99639,6 +100297,420 @@ var init_runtime_target_trace = __esm({
99639
100297
  }
99640
100298
  });
99641
100299
 
100300
+ // src/cli/service-commands.ts
100301
+ var service_commands_exports = {};
100302
+ __export(service_commands_exports, {
100303
+ buildPlist: () => buildPlist,
100304
+ installAutoStartServiceForCurrentProcess: () => installAutoStartServiceForCurrentProcess,
100305
+ isAutoStartServiceInstalled: () => isAutoStartServiceInstalled,
100306
+ registerServiceCommands: () => registerServiceCommands
100307
+ });
100308
+ function getDarwinPlistPath() {
100309
+ return import_node_path5.default.join(import_node_os4.default.homedir(), "Library", "LaunchAgents", `${LAUNCHD_LABEL}.plist`);
100310
+ }
100311
+ function getWindowsStartupDir() {
100312
+ const appData = process.env.APPDATA || import_node_path5.default.join(import_node_os4.default.homedir(), "AppData", "Roaming");
100313
+ return import_node_path5.default.join(appData, "Microsoft", "Windows", "Start Menu", "Programs", "Startup");
100314
+ }
100315
+ function getWindowsVbsPath() {
100316
+ return import_node_path5.default.join(getWindowsStartupDir(), "adhdev-daemon.vbs");
100317
+ }
100318
+ function resolveCliPath() {
100319
+ return import_node_fs4.default.realpathSync(process.argv[1]);
100320
+ }
100321
+ function ensureDir(dir) {
100322
+ if (!import_node_fs4.default.existsSync(dir)) import_node_fs4.default.mkdirSync(dir, { recursive: true });
100323
+ }
100324
+ async function fetchHealth() {
100325
+ const controller = new AbortController();
100326
+ const timer = setTimeout(() => controller.abort(), DEFAULT_LOCAL_DAEMON_HEALTH_TIMEOUT_MS2);
100327
+ try {
100328
+ const res = await fetch(`http://127.0.0.1:${DEFAULT_DAEMON_PORT}/health`, { signal: controller.signal });
100329
+ if (!res.ok) return null;
100330
+ return await res.json();
100331
+ } catch {
100332
+ return null;
100333
+ } finally {
100334
+ clearTimeout(timer);
100335
+ }
100336
+ }
100337
+ function getProcessInfo(pid) {
100338
+ try {
100339
+ if (process.platform === "win32") {
100340
+ const out = (0, import_node_child_process4.execSync)(`tasklist /FI "PID eq ${pid}" /FO CSV /NH`, { encoding: "utf-8" });
100341
+ const match = out.match(/"(\d[\d,]+)\sK"/);
100342
+ const memKB = match ? parseInt(match[1].replace(/,/g, ""), 10) : 0;
100343
+ return { uptime: "-", memMB: Math.round(memKB / 1024) };
100344
+ } else {
100345
+ const out = (0, import_node_child_process4.execSync)(`ps -o etime=,rss= -p ${pid}`, { encoding: "utf-8" }).trim();
100346
+ const parts = out.split(/\s+/);
100347
+ const etime = parts[0] || "-";
100348
+ const rssKB = parseInt(parts[1] || "0", 10);
100349
+ return { uptime: formatElapsed(etime), memMB: Math.round(rssKB / 1024) };
100350
+ }
100351
+ } catch {
100352
+ return null;
100353
+ }
100354
+ }
100355
+ function formatElapsed(etime) {
100356
+ const parts = etime.replace("-", ":").split(":").map(Number);
100357
+ if (parts.length === 4) return `${parts[0]}d ${parts[1]}h ${parts[2]}m`;
100358
+ if (parts.length === 3) return `${parts[0]}h ${parts[1]}m`;
100359
+ if (parts.length === 2) return `${parts[0]}m ${parts[1]}s`;
100360
+ return etime;
100361
+ }
100362
+ function rotateLogIfNeeded(logPath) {
100363
+ try {
100364
+ if (!import_node_fs4.default.existsSync(logPath)) return;
100365
+ const stat5 = import_node_fs4.default.statSync(logPath);
100366
+ if (stat5.size > MAX_LOG_SIZE2) {
100367
+ const rotated = logPath + ".old";
100368
+ if (import_node_fs4.default.existsSync(rotated)) import_node_fs4.default.unlinkSync(rotated);
100369
+ import_node_fs4.default.renameSync(logPath, rotated);
100370
+ import_node_fs4.default.writeFileSync(logPath, `[log rotated at ${(/* @__PURE__ */ new Date()).toISOString()}]
100371
+ `, "utf-8");
100372
+ }
100373
+ } catch {
100374
+ }
100375
+ }
100376
+ function rotateLogs() {
100377
+ rotateLogIfNeeded(LOG_OUT);
100378
+ rotateLogIfNeeded(LOG_ERR);
100379
+ }
100380
+ function normalizeLaunchdPathEntry(entry) {
100381
+ const trimmed = String(entry || "").trim();
100382
+ if (!trimmed) return null;
100383
+ if (trimmed.startsWith("~")) {
100384
+ return import_node_path5.default.join(import_node_os4.default.homedir(), trimmed.slice(1));
100385
+ }
100386
+ return import_node_path5.default.isAbsolute(trimmed) ? trimmed : null;
100387
+ }
100388
+ function buildLaunchdPath(nodeExe, currentPath = process.env.PATH || "") {
100389
+ const brewPrefix = import_node_fs4.default.existsSync("/opt/homebrew/bin") ? "/opt/homebrew/bin" : "/usr/local/bin";
100390
+ const entries = [];
100391
+ const seen = /* @__PURE__ */ new Set();
100392
+ const addEntry = (value) => {
100393
+ if (!value) return;
100394
+ const normalized = normalizeLaunchdPathEntry(value);
100395
+ if (!normalized || seen.has(normalized)) return;
100396
+ seen.add(normalized);
100397
+ entries.push(normalized);
100398
+ };
100399
+ addEntry(import_node_path5.default.dirname(nodeExe));
100400
+ for (const entry of String(currentPath || "").split(import_node_path5.default.delimiter)) {
100401
+ addEntry(entry);
100402
+ }
100403
+ for (const entry of [brewPrefix, "/usr/local/bin", "/usr/bin", "/bin", "/usr/sbin", "/sbin"]) {
100404
+ addEntry(entry);
100405
+ }
100406
+ return entries.join(":");
100407
+ }
100408
+ function buildPlist(nodeExe, cliExe, currentPath = process.env.PATH || "") {
100409
+ const pathValue = buildLaunchdPath(nodeExe, currentPath);
100410
+ return `<?xml version="1.0" encoding="UTF-8"?>
100411
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
100412
+ <plist version="1.0">
100413
+ <dict>
100414
+ <key>Label</key>
100415
+ <string>${LAUNCHD_LABEL}</string>
100416
+ <key>ProgramArguments</key>
100417
+ <array>
100418
+ <string>${nodeExe}</string>
100419
+ <string>${cliExe}</string>
100420
+ <string>daemon</string>
100421
+ </array>
100422
+ <key>RunAtLoad</key>
100423
+ <true/>
100424
+ <key>KeepAlive</key>
100425
+ <dict>
100426
+ <key>SuccessfulExit</key>
100427
+ <false/>
100428
+ </dict>
100429
+ <key>ThrottleInterval</key>
100430
+ <integer>30</integer>
100431
+ <key>StandardOutPath</key>
100432
+ <string>${LOG_OUT}</string>
100433
+ <key>StandardErrorPath</key>
100434
+ <string>${LOG_ERR}</string>
100435
+ <key>EnvironmentVariables</key>
100436
+ <dict>
100437
+ <key>PATH</key>
100438
+ <string>${pathValue}</string>
100439
+ </dict>
100440
+ </dict>
100441
+ </plist>`;
100442
+ }
100443
+ function installDarwin(nodeExe, cliExe) {
100444
+ const plistPath = getDarwinPlistPath();
100445
+ ensureDir(ADHDEV_DIR);
100446
+ ensureDir(import_node_path5.default.dirname(plistPath));
100447
+ import_node_fs4.default.writeFileSync(plistPath, buildPlist(nodeExe, cliExe), "utf-8");
100448
+ console.log(source_default.gray(` Plist: ${plistPath}`));
100449
+ try {
100450
+ (0, import_node_child_process4.execSync)(`launchctl unload "${plistPath}" 2>/dev/null`, { stdio: "ignore" });
100451
+ } catch {
100452
+ }
100453
+ try {
100454
+ (0, import_node_child_process4.execSync)(`launchctl load -w "${plistPath}"`, { stdio: "ignore" });
100455
+ console.log(source_default.green("\n \u2713 Registered as LaunchAgent \u2014 daemon will start on login."));
100456
+ console.log(source_default.gray(` Logs: ~/.adhdev/daemon-launchd.{out,err}`));
100457
+ } catch (e) {
100458
+ console.log(source_default.red(`
100459
+ \u2717 launchctl load failed: ${e.message}`));
100460
+ }
100461
+ }
100462
+ function uninstallDarwin() {
100463
+ const plistPath = getDarwinPlistPath();
100464
+ if (!import_node_fs4.default.existsSync(plistPath)) {
100465
+ console.log(source_default.yellow("\n \u26A0 Service is not installed."));
100466
+ return;
100467
+ }
100468
+ try {
100469
+ (0, import_node_child_process4.execSync)(`launchctl unload "${plistPath}" 2>/dev/null`, { stdio: "ignore" });
100470
+ } catch {
100471
+ }
100472
+ import_node_fs4.default.unlinkSync(plistPath);
100473
+ console.log(source_default.green("\n \u2713 Removed LaunchAgent. Daemon will no longer auto-start."));
100474
+ }
100475
+ function isInstalledDarwin() {
100476
+ return import_node_fs4.default.existsSync(getDarwinPlistPath());
100477
+ }
100478
+ function buildVbs(nodeExe, cliExe) {
100479
+ const logFile = import_node_path5.default.join(ADHDEV_DIR, "daemon-service.log").replace(/\\/g, "\\\\");
100480
+ const escapedNodeExe = nodeExe.replace(/\\/g, "\\\\");
100481
+ const escapedCliExe = cliExe.replace(/\\/g, "\\\\");
100482
+ return `' ADHDev Daemon Auto-Start (generated by adhdev service install)
100483
+ Set WshShell = CreateObject("WScript.Shell")
100484
+ WshShell.Run "cmd.exe /c """"${escapedNodeExe}"""" """"${escapedCliExe}"""" daemon >> """"${logFile}"""" 2>&1", 0, False
100485
+ `;
100486
+ }
100487
+ function installWindows(nodeExe, cliExe) {
100488
+ const vbsPath = getWindowsVbsPath();
100489
+ ensureDir(ADHDEV_DIR);
100490
+ ensureDir(import_node_path5.default.dirname(vbsPath));
100491
+ import_node_fs4.default.writeFileSync(vbsPath, buildVbs(nodeExe, cliExe), "utf-8");
100492
+ console.log(source_default.gray(` Startup script: ${vbsPath}`));
100493
+ console.log(source_default.green("\n \u2713 Registered in Startup folder \u2014 daemon will start on login (hidden)."));
100494
+ console.log(source_default.gray(` Logs: ${import_node_path5.default.join(ADHDEV_DIR, "daemon-service.log")}`));
100495
+ console.log(source_default.gray(" To start now without rebooting, run: adhdev daemon"));
100496
+ }
100497
+ function uninstallWindows() {
100498
+ const vbsPath = getWindowsVbsPath();
100499
+ if (!import_node_fs4.default.existsSync(vbsPath)) {
100500
+ console.log(source_default.yellow("\n \u26A0 Service is not installed."));
100501
+ return;
100502
+ }
100503
+ import_node_fs4.default.unlinkSync(vbsPath);
100504
+ console.log(source_default.green("\n \u2713 Removed Startup script. Daemon will no longer auto-start."));
100505
+ console.log(source_default.gray(" Note: a currently running daemon is not affected. Stop with: adhdev daemon:stop"));
100506
+ }
100507
+ function isInstalledWindows() {
100508
+ return import_node_fs4.default.existsSync(getWindowsVbsPath());
100509
+ }
100510
+ function isAutoStartServiceInstalled(platform12 = import_node_os4.default.platform()) {
100511
+ if (platform12 === "darwin") return isInstalledDarwin();
100512
+ if (platform12 === "win32") return isInstalledWindows();
100513
+ return false;
100514
+ }
100515
+ function installAutoStartServiceForCurrentProcess(platform12 = import_node_os4.default.platform()) {
100516
+ const nodeExe = process.execPath;
100517
+ const cliExe = resolveCliPath();
100518
+ if (platform12 === "darwin") {
100519
+ installDarwin(nodeExe, cliExe);
100520
+ return true;
100521
+ }
100522
+ if (platform12 === "win32") {
100523
+ installWindows(nodeExe, cliExe);
100524
+ return true;
100525
+ }
100526
+ return false;
100527
+ }
100528
+ function registerServiceCommands(program2) {
100529
+ const svc = program2.command("service").description("\u{1F50C} Manage ADHDev as an OS background auto-start service");
100530
+ svc.command("install").description("Register ADHDev daemon to start automatically on login").action(async () => {
100531
+ console.log(source_default.bold("\n \u{1F680} Installing ADHDev Background Service"));
100532
+ const platform12 = import_node_os4.default.platform();
100533
+ const nodeExe = process.execPath;
100534
+ const cliExe = resolveCliPath();
100535
+ console.log(source_default.gray(` Node: ${nodeExe}`));
100536
+ console.log(source_default.gray(` CLI: ${cliExe}`));
100537
+ console.log(source_default.gray(` Platform: ${platform12}`));
100538
+ if (!installAutoStartServiceForCurrentProcess(platform12)) {
100539
+ console.log(source_default.yellow("\n \u26A0 Auto-start service install is not supported on this platform."));
100540
+ console.log(source_default.gray(" On Linux, create a systemd user unit manually:"));
100541
+ console.log(source_default.gray(" ~/.config/systemd/user/adhdev-daemon.service"));
100542
+ }
100543
+ console.log();
100544
+ });
100545
+ svc.command("uninstall").description("Remove the OS background service").action(async () => {
100546
+ console.log(source_default.bold("\n \u{1F5D1}\uFE0F Removing ADHDev Background Service"));
100547
+ const platform12 = import_node_os4.default.platform();
100548
+ if (platform12 === "darwin") {
100549
+ uninstallDarwin();
100550
+ } else if (platform12 === "win32") {
100551
+ uninstallWindows();
100552
+ } else {
100553
+ console.log(source_default.yellow("\n \u26A0 Not supported on this platform."));
100554
+ }
100555
+ console.log();
100556
+ });
100557
+ svc.command("status").description("Show service installation state and live daemon health").action(async () => {
100558
+ const platform12 = import_node_os4.default.platform();
100559
+ const installed = isAutoStartServiceInstalled(platform12);
100560
+ if (installed) {
100561
+ console.log(source_default.green("\n \u2713 Service is installed."));
100562
+ if (platform12 === "darwin") console.log(source_default.gray(` Plist: ${getDarwinPlistPath()}`));
100563
+ else console.log(source_default.gray(` Script: ${getWindowsVbsPath()}`));
100564
+ } else {
100565
+ console.log(source_default.gray("\n \u2717 Service is not installed. Run: adhdev service install"));
100566
+ }
100567
+ const health = await fetchHealth();
100568
+ if (health?.ok && health.pid) {
100569
+ const info = getProcessInfo(health.pid);
100570
+ if (info) {
100571
+ console.log(source_default.green(` \u2713 Daemon running \u2014 PID ${health.pid}, uptime ${info.uptime}, ${info.memMB} MB`));
100572
+ } else {
100573
+ console.log(source_default.green(` \u2713 Daemon running \u2014 PID ${health.pid}`));
100574
+ }
100575
+ } else {
100576
+ console.log(source_default.yellow(" \u2717 Daemon is not running."));
100577
+ }
100578
+ const outSize = import_node_fs4.default.existsSync(LOG_OUT) ? import_node_fs4.default.statSync(LOG_OUT).size : 0;
100579
+ const errSize = import_node_fs4.default.existsSync(LOG_ERR) ? import_node_fs4.default.statSync(LOG_ERR).size : 0;
100580
+ if (outSize > 0 || errSize > 0) {
100581
+ console.log(source_default.gray(` Logs: stdout ${formatBytes(outSize)}, stderr ${formatBytes(errSize)}`));
100582
+ }
100583
+ console.log();
100584
+ });
100585
+ 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) => {
100586
+ if (options.clear) {
100587
+ for (const f of [LOG_OUT, LOG_ERR]) {
100588
+ if (import_node_fs4.default.existsSync(f)) import_node_fs4.default.writeFileSync(f, "", "utf-8");
100589
+ }
100590
+ console.log(source_default.green("\n \u2713 Logs cleared.\n"));
100591
+ return;
100592
+ }
100593
+ const logFile = options.err ? LOG_ERR : LOG_OUT;
100594
+ if (!import_node_fs4.default.existsSync(logFile)) {
100595
+ console.log(source_default.gray(`
100596
+ No log file found: ${logFile}
100597
+ `));
100598
+ return;
100599
+ }
100600
+ const lines = parseInt(options.lines, 10) || 30;
100601
+ console.log(source_default.gray(`
100602
+ \u2500\u2500 ${options.err ? "stderr" : "stdout"}: ${logFile} (last ${lines} lines) \u2500\u2500
100603
+ `));
100604
+ const content = import_node_fs4.default.readFileSync(logFile, "utf-8");
100605
+ const allLines = content.split("\n");
100606
+ const lastLines = allLines.slice(-lines).join("\n");
100607
+ if (lastLines.trim()) console.log(lastLines);
100608
+ console.log(source_default.gray("\n (watching for new output, Ctrl+C to stop)\n"));
100609
+ let offset = Buffer.byteLength(content, "utf-8");
100610
+ const watcher = import_node_fs4.default.watchFile(logFile, { interval: 500 }, () => {
100611
+ try {
100612
+ const stat5 = import_node_fs4.default.statSync(logFile);
100613
+ if (stat5.size > offset) {
100614
+ const fd = import_node_fs4.default.openSync(logFile, "r");
100615
+ const buf = Buffer.alloc(stat5.size - offset);
100616
+ import_node_fs4.default.readSync(fd, buf, 0, buf.length, offset);
100617
+ import_node_fs4.default.closeSync(fd);
100618
+ process.stdout.write(buf.toString("utf-8"));
100619
+ offset = stat5.size;
100620
+ } else if (stat5.size < offset) {
100621
+ offset = 0;
100622
+ }
100623
+ } catch {
100624
+ }
100625
+ });
100626
+ const cleanup = () => {
100627
+ import_node_fs4.default.unwatchFile(logFile);
100628
+ process.exit(0);
100629
+ };
100630
+ process.on("SIGINT", cleanup);
100631
+ process.on("SIGTERM", cleanup);
100632
+ });
100633
+ svc.command("restart").description("Restart the daemon process (service will auto-relaunch)").action(async () => {
100634
+ const platform12 = import_node_os4.default.platform();
100635
+ const installed = isAutoStartServiceInstalled(platform12);
100636
+ if (!installed) {
100637
+ console.log(source_default.yellow("\n \u26A0 Service is not installed. Use `adhdev daemon:restart` for manual restart."));
100638
+ console.log(source_default.gray(" Or install the service first: adhdev service install\n"));
100639
+ return;
100640
+ }
100641
+ rotateLogs();
100642
+ if (platform12 === "darwin") {
100643
+ console.log(source_default.cyan("\n Refreshing LaunchAgent definition and reloading service..."));
100644
+ installAutoStartServiceForCurrentProcess(platform12);
100645
+ console.log();
100646
+ return;
100647
+ }
100648
+ const health = await fetchHealth();
100649
+ if (!health?.pid) {
100650
+ console.log(source_default.yellow("\n \u26A0 Daemon is not currently running."));
100651
+ console.log(source_default.gray(" Start with: adhdev daemon\n"));
100652
+ return;
100653
+ }
100654
+ console.log(source_default.cyan(`
100655
+ Stopping daemon (PID ${health.pid})...`));
100656
+ try {
100657
+ process.kill(health.pid, "SIGTERM");
100658
+ } catch {
100659
+ console.log(source_default.yellow(" Could not send SIGTERM. Process may have already exited."));
100660
+ }
100661
+ await new Promise((r) => setTimeout(r, 2e3));
100662
+ if (platform12 === "win32") {
100663
+ const vbsPath = getWindowsVbsPath();
100664
+ if (import_node_fs4.default.existsSync(vbsPath)) {
100665
+ try {
100666
+ (0, import_node_child_process4.execSync)(`wscript.exe "${vbsPath}"`, { stdio: "ignore", windowsHide: true });
100667
+ } catch {
100668
+ }
100669
+ }
100670
+ }
100671
+ let restarted = false;
100672
+ for (let i = 0; i < 8; i++) {
100673
+ await new Promise((r) => setTimeout(r, 1e3));
100674
+ const newHealth = await fetchHealth();
100675
+ if (newHealth?.ok && newHealth.pid !== health.pid) {
100676
+ restarted = true;
100677
+ const info = getProcessInfo(newHealth.pid);
100678
+ console.log(source_default.green(` \u2713 Daemon restarted \u2014 new PID ${newHealth.pid}${info ? `, ${info.memMB} MB` : ""}`));
100679
+ break;
100680
+ }
100681
+ }
100682
+ if (!restarted) {
100683
+ console.log(source_default.yellow(" \u26A0 Daemon did not restart within 10s."));
100684
+ console.log(source_default.gray(" Check: adhdev service logs --err"));
100685
+ }
100686
+ console.log();
100687
+ });
100688
+ }
100689
+ function formatBytes(bytes) {
100690
+ if (bytes === 0) return "0 B";
100691
+ if (bytes < 1024) return `${bytes} B`;
100692
+ if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
100693
+ return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
100694
+ }
100695
+ 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;
100696
+ var init_service_commands = __esm({
100697
+ "src/cli/service-commands.ts"() {
100698
+ "use strict";
100699
+ import_node_fs4 = __toESM(require("fs"));
100700
+ import_node_path5 = __toESM(require("path"));
100701
+ import_node_os4 = __toESM(require("os"));
100702
+ import_node_child_process4 = require("child_process");
100703
+ init_source();
100704
+ init_src();
100705
+ DEFAULT_LOCAL_DAEMON_HEALTH_TIMEOUT_MS2 = 1500;
100706
+ LAUNCHD_LABEL = "dev.adhf.daemon";
100707
+ ADHDEV_DIR = import_node_path5.default.join(import_node_os4.default.homedir(), ".adhdev");
100708
+ LOG_OUT = import_node_path5.default.join(ADHDEV_DIR, "daemon-launchd.out");
100709
+ LOG_ERR = import_node_path5.default.join(ADHDEV_DIR, "daemon-launchd.err");
100710
+ MAX_LOG_SIZE2 = 10 * 1024 * 1024;
100711
+ }
100712
+ });
100713
+
99642
100714
  // ../../oss/packages/web-core/src/constants/supported.ts
99643
100715
  var supported_exports = {};
99644
100716
  __export(supported_exports, {
@@ -101612,7 +102684,7 @@ function formatDebugTraceEntryLine(entry) {
101612
102684
  }
101613
102685
 
101614
102686
  // src/cli/daemon-commands.ts
101615
- var DEFAULT_LOCAL_DAEMON_HEALTH_TIMEOUT_MS2 = 1500;
102687
+ var DEFAULT_LOCAL_DAEMON_HEALTH_TIMEOUT_MS3 = 1500;
101616
102688
  var DEFAULT_TRACE_FOLLOW_INTERVAL_MS = 1500;
101617
102689
  var DEFAULT_DAEMON_PORT_TEXT = String(DEFAULT_DAEMON_PORT);
101618
102690
  var DEV_SERVER_PORT2 = 19280;
@@ -101640,6 +102712,15 @@ async function resolveConfiguredUpdateChannel() {
101640
102712
  function releaseChannelLabel(channel) {
101641
102713
  return `${channel} (${CHANNEL_NPM_TAG3[channel]})`;
101642
102714
  }
102715
+ function buildDaemonRestartCommandArgv(options = {}) {
102716
+ const cliPath = options.cliPath || process.argv[1];
102717
+ const daemonPort = options.daemonPort || DEFAULT_DAEMON_PORT_TEXT;
102718
+ const platform12 = options.platform || process.platform;
102719
+ if (platform12 === "darwin" && options.serviceInstalled) {
102720
+ return [cliPath, "service", "install"];
102721
+ }
102722
+ return [cliPath, "daemon", "-p", daemonPort];
102723
+ }
101643
102724
  async function persistReleaseChannel(channel) {
101644
102725
  const { updateConfig: updateConfig2 } = await Promise.resolve().then(() => (init_src(), src_exports));
101645
102726
  updateConfig2({ updateChannel: channel, serverUrl: CHANNEL_SERVER_URL3[channel] });
@@ -101650,7 +102731,7 @@ function hideCommand(command) {
101650
102731
  }
101651
102732
  async function fetchLocalDaemonHealth(port) {
101652
102733
  const controller = new AbortController();
101653
- const timer = setTimeout(() => controller.abort(), DEFAULT_LOCAL_DAEMON_HEALTH_TIMEOUT_MS2);
102734
+ const timer = setTimeout(() => controller.abort(), DEFAULT_LOCAL_DAEMON_HEALTH_TIMEOUT_MS3);
101654
102735
  try {
101655
102736
  const res = await fetch(`http://127.0.0.1:${port}/health`, { signal: controller.signal });
101656
102737
  if (!res.ok) return null;
@@ -102016,6 +103097,9 @@ async function runDaemonUpgrade(options, pkgVersion3) {
102016
103097
  } else {
102017
103098
  console.log(source_default.cyan(`
102018
103099
  Upgrading v${currentVersion} \u2192 v${latest}...`));
103100
+ const { isAutoStartServiceInstalled: isAutoStartServiceInstalled2 } = await Promise.resolve().then(() => (init_service_commands(), service_commands_exports));
103101
+ const serviceInstalled = isAutoStartServiceInstalled2();
103102
+ const shouldRefreshInstalledService = options.restart !== false && process.platform === "darwin" && serviceInstalled;
102019
103103
  const daemonWasRunning = options.restart !== false && isDaemonRunning2();
102020
103104
  if (daemonWasRunning) {
102021
103105
  stopDaemon2();
@@ -102027,10 +103111,22 @@ async function runDaemonUpgrade(options, pkgVersion3) {
102027
103111
  packageName: "adhdev",
102028
103112
  targetVersion: latest,
102029
103113
  parentPid: process.pid,
102030
- restartArgv: daemonWasRunning ? [process.argv[1], "daemon", "-p", DEFAULT_DAEMON_PORT_TEXT] : [],
103114
+ restartArgv: shouldRefreshInstalledService ? buildDaemonRestartCommandArgv({
103115
+ cliPath: process.argv[1],
103116
+ daemonPort: DEFAULT_DAEMON_PORT_TEXT,
103117
+ platform: process.platform,
103118
+ serviceInstalled
103119
+ }) : daemonWasRunning ? buildDaemonRestartCommandArgv({
103120
+ cliPath: process.argv[1],
103121
+ daemonPort: DEFAULT_DAEMON_PORT_TEXT,
103122
+ platform: process.platform,
103123
+ serviceInstalled
103124
+ }) : [],
102031
103125
  cwd: process.cwd()
102032
103126
  });
102033
- if (daemonWasRunning) {
103127
+ if (shouldRefreshInstalledService) {
103128
+ console.log(source_default.cyan(" Upgrading and refreshing background service in background..."));
103129
+ } else if (daemonWasRunning) {
102034
103130
  console.log(source_default.cyan(" Upgrading and restarting daemon in background..."));
102035
103131
  } else {
102036
103132
  console.log(source_default.cyan(" Upgrading in background..."));
@@ -102042,29 +103138,44 @@ async function runDaemonUpgrade(options, pkgVersion3) {
102042
103138
  return;
102043
103139
  }
102044
103140
  }
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));
103141
+ if (options.restart !== false) {
103142
+ const { installAutoStartServiceForCurrentProcess: installAutoStartServiceForCurrentProcess2, isAutoStartServiceInstalled: isAutoStartServiceInstalled2 } = await Promise.resolve().then(() => (init_service_commands(), service_commands_exports));
103143
+ const serviceInstalled = isAutoStartServiceInstalled2();
103144
+ if (process.platform === "darwin" && serviceInstalled) {
103145
+ console.log(source_default.yellow("\n Refreshing LaunchAgent definition..."));
103146
+ installAutoStartServiceForCurrentProcess2(process.platform);
103147
+ console.log();
103148
+ return;
103149
+ }
102059
103150
  if (isDaemonRunning2()) {
102060
- console.log(source_default.green(` \u2713 Daemon restarted with new version
103151
+ console.log(source_default.yellow("\n Restarting daemon..."));
103152
+ stopDaemon2();
103153
+ await new Promise((r) => setTimeout(r, 2e3));
103154
+ stopManagedSessionHostProcess2();
103155
+ await new Promise((r) => setTimeout(r, 500));
103156
+ const child = spawn7(process.execPath, buildDaemonRestartCommandArgv({
103157
+ cliPath: process.argv[1],
103158
+ daemonPort: DEFAULT_DAEMON_PORT_TEXT,
103159
+ platform: process.platform,
103160
+ serviceInstalled
103161
+ }), {
103162
+ detached: true,
103163
+ stdio: "ignore",
103164
+ windowsHide: true,
103165
+ env: { ...process.env }
103166
+ });
103167
+ child.unref();
103168
+ await new Promise((r) => setTimeout(r, 3e3));
103169
+ if (isDaemonRunning2()) {
103170
+ console.log(source_default.green(` \u2713 Daemon restarted with new version
102061
103171
  `));
102062
- } else {
102063
- console.log(source_default.yellow(` \u26A0 Daemon not detected. Start manually: adhdev daemon
103172
+ } else {
103173
+ console.log(source_default.yellow(` \u26A0 Daemon not detected. Start manually: adhdev daemon
102064
103174
  `));
103175
+ }
103176
+ } else {
103177
+ console.log(source_default.gray("\n Daemon was not running. Start with: adhdev daemon\n"));
102065
103178
  }
102066
- } else if (options.restart !== false) {
102067
- console.log(source_default.gray("\n Daemon was not running. Start with: adhdev daemon\n"));
102068
103179
  } else {
102069
103180
  console.log(source_default.green("\n \u2713 Upgrade complete (daemon not restarted)\n"));
102070
103181
  }
@@ -102235,7 +103346,7 @@ function registerDaemonCommands(program2, pkgVersion3) {
102235
103346
  }
102236
103347
  try {
102237
103348
  const controller = new AbortController();
102238
- const timer = setTimeout(() => controller.abort(), DEFAULT_LOCAL_DAEMON_HEALTH_TIMEOUT_MS2);
103349
+ const timer = setTimeout(() => controller.abort(), DEFAULT_LOCAL_DAEMON_HEALTH_TIMEOUT_MS3);
102239
103350
  const res = await fetch(`${DEV_SERVER_BASE_URL}/api/status`, { signal: controller.signal });
102240
103351
  clearTimeout(timer);
102241
103352
  if (res.ok) {
@@ -102782,373 +103893,8 @@ function buildDoctorAdvice(input) {
102782
103893
  return advice;
102783
103894
  }
102784
103895
 
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
103896
  // src/cli/doctor-commands.ts
103897
+ init_service_commands();
103152
103898
  function resolvePackageRoot() {
103153
103899
  return path39.resolve(__dirname, "..", "..");
103154
103900
  }
@@ -104640,6 +105386,9 @@ function registerCdpCommands(program2) {
104640
105386
  });
104641
105387
  }
104642
105388
 
105389
+ // src/cli/index.ts
105390
+ init_service_commands();
105391
+
104643
105392
  // src/cli/mcp-commands.ts
104644
105393
  var import_node_child_process5 = require("child_process");
104645
105394
  var fs23 = __toESM(require("fs"));