@moxt-ai/mobius 0.0.386 → 0.0.388

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.
Files changed (149) hide show
  1. package/dist/{chunk-FVYSQLQL.js → chunk-7BSIM5AK.js} +132 -136
  2. package/dist/{chunk-FVYSQLQL.js.map → chunk-7BSIM5AK.js.map} +3 -3
  3. package/dist/{chunk-Z6JG65YI.js → chunk-Z4WUPVBL.js} +2 -2
  4. package/dist/{chunk-Z6JG65YI.js.map → chunk-Z4WUPVBL.js.map} +1 -1
  5. package/dist/cli/handler/daemon/index.d.ts.map +1 -1
  6. package/dist/cli/handler/daemon/index.js +9 -4
  7. package/dist/cli/handler/daemon/index.js.map +1 -1
  8. package/dist/cli/handler/daemon/quit.d.ts.map +1 -1
  9. package/dist/cli/handler/daemon/quit.js +5 -4
  10. package/dist/cli/handler/daemon/quit.js.map +1 -1
  11. package/dist/cli/handler/daemon/release.js +1 -1
  12. package/dist/cli/handler/daemon/start.d.ts.map +1 -1
  13. package/dist/cli/handler/daemon/start.js +3 -3
  14. package/dist/cli/handler/daemon/start.js.map +1 -1
  15. package/dist/cli/handler/daemon/upgrade.d.ts.map +1 -1
  16. package/dist/cli/handler/daemon/upgrade.js +8 -24
  17. package/dist/cli/handler/daemon/upgrade.js.map +1 -1
  18. package/dist/cli/handler/pair/pair.js +3 -3
  19. package/dist/cli/handler/pair/pair.js.map +1 -1
  20. package/dist/cli/handler/pair/remove.js +1 -1
  21. package/dist/cli/handler/pair/stop.js +1 -1
  22. package/dist/cli/handler/version/index.d.ts.map +1 -1
  23. package/dist/cli/handler/version/index.js +6 -2
  24. package/dist/cli/handler/version/index.js.map +1 -1
  25. package/dist/{cli-NLIGRBEL.js → cli-O57WJIFN.js} +412 -1143
  26. package/dist/cli-O57WJIFN.js.map +7 -0
  27. package/dist/cli.js +1998 -6
  28. package/dist/cli.js.map +4 -4
  29. package/dist/daemon/control/protocol.d.ts +1 -133
  30. package/dist/daemon/control/protocol.d.ts.map +1 -1
  31. package/dist/daemon/control/protocol.js +4 -63
  32. package/dist/daemon/control/protocol.js.map +1 -1
  33. package/dist/daemon/lifecycle/daemon-client.d.ts +8 -0
  34. package/dist/daemon/lifecycle/daemon-client.d.ts.map +1 -0
  35. package/dist/daemon/lifecycle/daemon-client.js +154 -0
  36. package/dist/daemon/lifecycle/daemon-client.js.map +1 -0
  37. package/dist/daemon/lifecycle/{worker.d.ts → daemon.d.ts} +2 -2
  38. package/dist/daemon/lifecycle/daemon.d.ts.map +1 -0
  39. package/dist/daemon/lifecycle/daemon.js +266 -0
  40. package/dist/daemon/lifecycle/daemon.js.map +1 -0
  41. package/dist/daemon/lifecycle/unhandled-rejection.d.ts +1 -2
  42. package/dist/daemon/lifecycle/unhandled-rejection.d.ts.map +1 -1
  43. package/dist/daemon/lifecycle/unhandled-rejection.js +4 -7
  44. package/dist/daemon/lifecycle/unhandled-rejection.js.map +1 -1
  45. package/dist/daemon/upgrades/data.d.ts +0 -1
  46. package/dist/daemon/upgrades/data.d.ts.map +1 -1
  47. package/dist/daemon/upgrades/data.js +0 -5
  48. package/dist/daemon/upgrades/data.js.map +1 -1
  49. package/dist/engine/runtime/command/registry/prompt.d.ts.map +1 -1
  50. package/dist/engine/runtime/command/registry/prompt.js +2 -3
  51. package/dist/engine/runtime/command/registry/prompt.js.map +1 -1
  52. package/dist/engine/runtime/relay/session-event-publisher.d.ts.map +1 -1
  53. package/dist/engine/runtime/relay/session-event-publisher.js +2 -4
  54. package/dist/engine/runtime/relay/session-event-publisher.js.map +1 -1
  55. package/dist/engine/session/api/session-layer.js +3 -3
  56. package/dist/engine/session/api/session-layer.js.map +1 -1
  57. package/dist/engine/session/live/agent-session.d.ts.map +1 -1
  58. package/dist/engine/session/live/agent-session.js +4 -3
  59. package/dist/engine/session/live/agent-session.js.map +1 -1
  60. package/dist/engine/session/prompt/execute-prompt.d.ts +8 -8
  61. package/dist/engine/session/prompt/execute-prompt.d.ts.map +1 -1
  62. package/dist/engine/session/prompt/execute-prompt.js +10 -10
  63. package/dist/engine/session/prompt/execute-prompt.js.map +1 -1
  64. package/dist/engine/session/prompt/prompt-lifecycle.d.ts +8 -4
  65. package/dist/engine/session/prompt/prompt-lifecycle.d.ts.map +1 -1
  66. package/dist/engine/session/prompt/prompt-lifecycle.js +18 -17
  67. package/dist/engine/session/prompt/prompt-lifecycle.js.map +1 -1
  68. package/dist/engine/session/turn/active-executions.d.ts +4 -2
  69. package/dist/engine/session/turn/active-executions.d.ts.map +1 -1
  70. package/dist/engine/session/turn/active-executions.js +4 -6
  71. package/dist/engine/session/turn/active-executions.js.map +1 -1
  72. package/dist/engine/session/turn/active-turn.d.ts +12 -8
  73. package/dist/engine/session/turn/active-turn.d.ts.map +1 -1
  74. package/dist/engine/session/turn/active-turn.js +47 -36
  75. package/dist/engine/session/turn/active-turn.js.map +1 -1
  76. package/dist/engine/session/turn/execution-receipts.d.ts +6 -2
  77. package/dist/engine/session/turn/execution-receipts.d.ts.map +1 -1
  78. package/dist/engine/session/turn/execution-receipts.js +18 -32
  79. package/dist/engine/session/turn/execution-receipts.js.map +1 -1
  80. package/dist/engine/session/turn/session-execution.d.ts +6 -2
  81. package/dist/engine/session/turn/session-execution.d.ts.map +1 -1
  82. package/dist/engine/session/turn/session-execution.js +10 -7
  83. package/dist/engine/session/turn/session-execution.js.map +1 -1
  84. package/dist/engine/session/turn/session-turns.d.ts +1 -1
  85. package/dist/engine/session/turn/session-turns.d.ts.map +1 -1
  86. package/dist/engine/session/turn/session-turns.js +1 -1
  87. package/dist/engine/session/turn/session-turns.js.map +1 -1
  88. package/dist/engine/util/process/platform/windows.d.ts.map +1 -1
  89. package/dist/engine/util/process/platform/windows.js +7 -4
  90. package/dist/engine/util/process/platform/windows.js.map +1 -1
  91. package/dist/engine.js +2 -2
  92. package/dist/storage/daemon/daemon-lock.d.ts +9 -0
  93. package/dist/storage/daemon/daemon-lock.d.ts.map +1 -0
  94. package/dist/storage/daemon/{manager-lock.js → daemon-lock.js} +22 -7
  95. package/dist/storage/daemon/daemon-lock.js.map +1 -0
  96. package/dist/storage/state-root-layout.d.ts +0 -2
  97. package/dist/storage/state-root-layout.d.ts.map +1 -1
  98. package/dist/storage/state-root-layout.js +0 -2
  99. package/dist/storage/state-root-layout.js.map +1 -1
  100. package/package.json +2 -2
  101. package/dist/chunk-T2GPD4RK.js +0 -2002
  102. package/dist/chunk-T2GPD4RK.js.map +0 -7
  103. package/dist/cli/handler/daemon/manager.d.ts +0 -4
  104. package/dist/cli/handler/daemon/manager.d.ts.map +0 -1
  105. package/dist/cli/handler/daemon/manager.js +0 -9
  106. package/dist/cli/handler/daemon/manager.js.map +0 -1
  107. package/dist/cli/handler/daemon/worker.d.ts +0 -4
  108. package/dist/cli/handler/daemon/worker.d.ts.map +0 -1
  109. package/dist/cli/handler/daemon/worker.js +0 -7
  110. package/dist/cli/handler/daemon/worker.js.map +0 -1
  111. package/dist/cli-NLIGRBEL.js.map +0 -7
  112. package/dist/daemon/control/switch-status.d.ts +0 -4
  113. package/dist/daemon/control/switch-status.d.ts.map +0 -1
  114. package/dist/daemon/control/switch-status.js +0 -10
  115. package/dist/daemon/control/switch-status.js.map +0 -1
  116. package/dist/daemon/lifecycle/manager-client.d.ts +0 -8
  117. package/dist/daemon/lifecycle/manager-client.d.ts.map +0 -1
  118. package/dist/daemon/lifecycle/manager-client.js +0 -130
  119. package/dist/daemon/lifecycle/manager-client.js.map +0 -1
  120. package/dist/daemon/lifecycle/manager.d.ts +0 -3
  121. package/dist/daemon/lifecycle/manager.d.ts.map +0 -1
  122. package/dist/daemon/lifecycle/manager.js +0 -215
  123. package/dist/daemon/lifecycle/manager.js.map +0 -1
  124. package/dist/daemon/lifecycle/worker-lifecycle-log.d.ts +0 -14
  125. package/dist/daemon/lifecycle/worker-lifecycle-log.d.ts.map +0 -1
  126. package/dist/daemon/lifecycle/worker-lifecycle-log.js +0 -63
  127. package/dist/daemon/lifecycle/worker-lifecycle-log.js.map +0 -1
  128. package/dist/daemon/lifecycle/worker-process.d.ts +0 -26
  129. package/dist/daemon/lifecycle/worker-process.d.ts.map +0 -1
  130. package/dist/daemon/lifecycle/worker-process.js +0 -145
  131. package/dist/daemon/lifecycle/worker-process.js.map +0 -1
  132. package/dist/daemon/lifecycle/worker.d.ts.map +0 -1
  133. package/dist/daemon/lifecycle/worker.js +0 -213
  134. package/dist/daemon/lifecycle/worker.js.map +0 -1
  135. package/dist/daemon/upgrades/package.d.ts +0 -9
  136. package/dist/daemon/upgrades/package.d.ts.map +0 -1
  137. package/dist/daemon/upgrades/package.js +0 -157
  138. package/dist/daemon/upgrades/package.js.map +0 -1
  139. package/dist/daemon/upgrades/upgrade.d.ts +0 -21
  140. package/dist/daemon/upgrades/upgrade.d.ts.map +0 -1
  141. package/dist/daemon/upgrades/upgrade.js +0 -242
  142. package/dist/daemon/upgrades/upgrade.js.map +0 -1
  143. package/dist/storage/daemon/manager-lock.d.ts +0 -9
  144. package/dist/storage/daemon/manager-lock.d.ts.map +0 -1
  145. package/dist/storage/daemon/manager-lock.js.map +0 -1
  146. package/dist/storage/daemon/release-files.d.ts +0 -9
  147. package/dist/storage/daemon/release-files.d.ts.map +0 -1
  148. package/dist/storage/daemon/release-files.js +0 -14
  149. package/dist/storage/daemon/release-files.js.map +0 -1
@@ -1,12 +1,8 @@
1
- import {
2
- require_semver
3
- } from "./chunk-T2GPD4RK.js";
4
1
  import {
5
2
  BoundedLineLog,
6
3
  MissingAdapter,
7
4
  MobiusRuntimeEngine,
8
5
  PairingClientError,
9
- RuntimeDiagnosticLog,
10
6
  RuntimeInstallationStore,
11
7
  RuntimeInstallationStoreError,
12
8
  RuntimeLocalContext,
@@ -14,35 +10,26 @@ import {
14
10
  RuntimeTurnActivity,
15
11
  UnsupportedAgentVersion,
16
12
  WORKER_LIFECYCLE_LOG_FILENAME,
17
- canonicalPath,
18
- decodeIdentifier,
19
13
  decodeRuntimeUpgradeVersion,
20
14
  describeRelayFailure,
21
15
  discoverLocalAgentInventory,
22
16
  ensurePrivateDirectory,
23
17
  external_exports,
24
- findExecutableOnPath,
25
18
  installationPointerPath,
26
19
  isNodeError,
27
- isRecord,
28
20
  logDirectory,
29
21
  parsePairingCode,
30
22
  parseRelayOrigin,
31
23
  platformSupportFor,
32
24
  prepareRuntimeConnection,
33
25
  refreshAgentCatalog,
34
- resolveLaunchFiles,
35
- runBoundedProcess,
36
26
  unhandledRejectionLogFilename,
37
27
  validateLocalPath,
38
28
  validatePairingId
39
- } from "./chunk-FVYSQLQL.js";
29
+ } from "./chunk-7BSIM5AK.js";
40
30
  import {
41
- __callDispose,
42
- __toESM,
43
- __using,
44
31
  package_default
45
- } from "./chunk-Z6JG65YI.js";
32
+ } from "./chunk-Z4WUPVBL.js";
46
33
 
47
34
  // ../../node_modules/.pnpm/commander@15.0.0/node_modules/commander/lib/error.js
48
35
  var CommanderError = class extends Error {
@@ -3433,8 +3420,6 @@ var STATE_ROOT_LAYOUT = Object.freeze({
3433
3420
  currentData: "current-data.txt",
3434
3421
  /** Applications' installations: `installations/<application>/<installation>`. */
3435
3422
  installations: "installations",
3436
- /** Managed CLI releases: `npm/<version>`. */
3437
- releases: "npm",
3438
3423
  /** Each CLI version's data: `versions/<version>`. */
3439
3424
  versions: "versions"
3440
3425
  });
@@ -3882,14 +3867,29 @@ function registerAgentsHandler(program2, context) {
3882
3867
  });
3883
3868
  }
3884
3869
 
3885
- // src/daemon/lifecycle/manager.ts
3886
- import { setTimeout as setTimeout2 } from "node:timers/promises";
3870
+ // src/daemon/lifecycle/daemon.ts
3871
+ import { setTimeout as delay } from "node:timers/promises";
3872
+
3873
+ // src/engine/session/process/runtime-agent-processes.ts
3874
+ var RuntimeAgentProcesses = class {
3875
+ #pools = /* @__PURE__ */ new Set();
3876
+ /** Tracks a Runtime's pool until the returned function removes it. */
3877
+ add = (pool) => {
3878
+ this.#pools.add(pool);
3879
+ return () => {
3880
+ this.#pools.delete(pool);
3881
+ };
3882
+ };
3883
+ retireIdleProcesses = async () => {
3884
+ await Promise.all([...this.#pools].map(async (pool) => await pool.retireIdleProcesses()));
3885
+ };
3886
+ };
3887
3887
 
3888
- // src/storage/daemon/manager-lock.ts
3888
+ // src/storage/daemon/daemon-lock.ts
3889
3889
  import { readFile as readFile3, unlink, writeFile as writeFile2 } from "node:fs/promises";
3890
3890
  import { join as join7 } from "node:path";
3891
- async function acquireManagerLock(stateDirectory, name = "manager.lock") {
3892
- const path2 = join7(stateDirectory, name);
3891
+ async function acquireDaemonLock(stateDirectory, filename = "manager.lock") {
3892
+ const path2 = join7(stateDirectory, filename);
3893
3893
  for (let attempt = 0; attempt < 2; attempt += 1) {
3894
3894
  try {
3895
3895
  await writeFile2(path2, `${process.pid.toString()}
@@ -3916,9 +3916,22 @@ async function acquireManagerLock(stateDirectory, name = "manager.lock") {
3916
3916
  }
3917
3917
  return null;
3918
3918
  }
3919
- async function managerLockExists(stateDirectory) {
3919
+ async function daemonLockExists(stateDirectory, filename = "manager.lock") {
3920
3920
  try {
3921
- await readFile3(join7(stateDirectory, "manager.lock"), "utf8");
3921
+ const pid = Number((await readFile3(join7(stateDirectory, filename), "utf8")).trim());
3922
+ if (!Number.isSafeInteger(pid) || pid <= 1) {
3923
+ return true;
3924
+ }
3925
+ try {
3926
+ process.kill(pid, 0);
3927
+ } catch (cause) {
3928
+ if (isNodeError(cause, "ESRCH")) {
3929
+ return false;
3930
+ }
3931
+ if (!isNodeError(cause, "EPERM")) {
3932
+ throw cause;
3933
+ }
3934
+ }
3922
3935
  return true;
3923
3936
  } catch (cause) {
3924
3937
  if (isNodeError(cause, "ENOENT")) {
@@ -3928,8 +3941,9 @@ async function managerLockExists(stateDirectory) {
3928
3941
  }
3929
3942
  }
3930
3943
 
3931
- // src/daemon/control/local-server.ts
3932
- import { createServer } from "node:net";
3944
+ // src/daemon/control/local-client.ts
3945
+ import { createHash } from "node:crypto";
3946
+ import { createConnection } from "node:net";
3933
3947
 
3934
3948
  // src/storage/daemon/control-token.ts
3935
3949
  import { randomBytes } from "node:crypto";
@@ -3961,8 +3975,6 @@ async function readOrCreateControlToken(stateDirectory) {
3961
3975
  }
3962
3976
 
3963
3977
  // src/daemon/control/local-client.ts
3964
- import { createHash } from "node:crypto";
3965
- import { createConnection } from "node:net";
3966
3978
  var CONTROL_TIMEOUT_MILLISECONDS = 13e4;
3967
3979
  var MAXIMUM_MESSAGE_BYTES = 64 * 1024;
3968
3980
  var managerPlatform = platformSupportFor(process.platform);
@@ -4037,6 +4049,7 @@ async function sendLocalRequest(stateDirectory, endpoint, command, signal) {
4037
4049
  }
4038
4050
 
4039
4051
  // src/daemon/control/local-server.ts
4052
+ import { createServer } from "node:net";
4040
4053
  async function serveLocalControl(stateDirectory, endpoint, signal, handle, onListening = () => void 0, onResponse = () => void 0) {
4041
4054
  signal.throwIfAborted();
4042
4055
  const token = await readOrCreateControlToken(stateDirectory);
@@ -4097,71 +4110,6 @@ async function serveLocalControl(stateDirectory, endpoint, signal, handle, onLis
4097
4110
  }
4098
4111
  }
4099
4112
 
4100
- // src/daemon/control/protocol.ts
4101
- var workerLaunch = external_exports.object({
4102
- executable: external_exports.string().min(1),
4103
- entry: external_exports.string().min(1),
4104
- version: external_exports.string().min(1).max(128)
4105
- }).readonly();
4106
- var transition = {
4107
- id: external_exports.string().min(1).max(256),
4108
- context: external_exports.string().max(256),
4109
- previous: workerLaunch,
4110
- next: workerLaunch
4111
- };
4112
- var daemonSwitchState = external_exports.discriminatedUnion("kind", [
4113
- external_exports.object({ ...transition, kind: external_exports.literal("switching") }),
4114
- external_exports.object({ ...transition, kind: external_exports.literal("switched") }),
4115
- external_exports.object({
4116
- ...transition,
4117
- kind: external_exports.literal("rolled-back"),
4118
- code: external_exports.string(),
4119
- message: external_exports.string(),
4120
- stdout: external_exports.string().max(4096),
4121
- stderr: external_exports.string().max(4096),
4122
- retryAfter: external_exports.number().int().nonnegative()
4123
- })
4124
- ]).readonly();
4125
- var lifecycleCommand = external_exports.discriminatedUnion("kind", [
4126
- external_exports.object({ kind: external_exports.literal("status") }),
4127
- external_exports.object({ kind: external_exports.literal("ensure"), launch: workerLaunch }),
4128
- external_exports.object({ kind: external_exports.literal("stop") }),
4129
- external_exports.object({
4130
- kind: external_exports.literal("switch"),
4131
- id: external_exports.string().min(1).max(256),
4132
- context: external_exports.string().max(256),
4133
- launch: workerLaunch
4134
- }),
4135
- external_exports.object({ kind: external_exports.literal("switch-status"), wait: external_exports.boolean() })
4136
- ]).readonly();
4137
- var lifecycleResponse = external_exports.discriminatedUnion("kind", [
4138
- external_exports.object({
4139
- kind: external_exports.literal("status"),
4140
- protocolVersion: external_exports.literal(2),
4141
- worker: external_exports.object({ pid: external_exports.number().int().positive(), version: external_exports.string() }).readonly().nullable()
4142
- }),
4143
- external_exports.object({ kind: external_exports.literal("switch-status"), state: daemonSwitchState.nullable() }),
4144
- external_exports.object({ kind: external_exports.literal("stopped") }),
4145
- external_exports.object({ kind: external_exports.literal("switching") }),
4146
- external_exports.object({ kind: external_exports.literal("error"), code: external_exports.string(), message: external_exports.string() })
4147
- ]).readonly();
4148
- var lifecycleCommandKinds = /* @__PURE__ */ new Set(["ensure", "status", "stop", "switch", "switch-status"]);
4149
- var lifecycleResponseKinds = /* @__PURE__ */ new Set(["error", "status", "stopped", "switching", "switch-status"]);
4150
- var daemonSwitchStateKinds = /* @__PURE__ */ new Set(["rolled-back", "switched", "switching"]);
4151
- function hasUnknownKind(value, known) {
4152
- return typeof value === "object" && value !== null && "kind" in value && typeof value.kind === "string" && !known.has(value.kind);
4153
- }
4154
- var decodeKnownLifecycleCommand = (value) => hasUnknownKind(value, lifecycleCommandKinds) ? null : lifecycleCommand.parse(value);
4155
- var decodeKnownLifecycleResponse = (value) => {
4156
- if (hasUnknownKind(value, lifecycleResponseKinds)) {
4157
- return null;
4158
- }
4159
- if (typeof value === "object" && value !== null && "kind" in value && value.kind === "switch-status" && "state" in value && hasUnknownKind(value.state, daemonSwitchStateKinds)) {
4160
- return lifecycleResponse.parse({ ...value, state: null });
4161
- }
4162
- return lifecycleResponse.parse(value);
4163
- };
4164
-
4165
4113
  // src/daemon/upgrades/data.ts
4166
4114
  async function prepareVersionData(version, signal) {
4167
4115
  signal.throwIfAborted();
@@ -4174,9 +4122,6 @@ async function prepareVersionData(version, signal) {
4174
4122
  await discardVersionData(root, targetVersion, signal);
4175
4123
  await copyVersionData(root, current, targetVersion, signal);
4176
4124
  }
4177
- async function discardRolledBackData(version, signal) {
4178
- await discardVersionData(readStateDirectory(), decodeRuntimeUpgradeVersion(version), signal);
4179
- }
4180
4125
  async function recordCurrentData(version, signal) {
4181
4126
  await writeCurrentDataVersion(readStateDirectory(), decodeRuntimeUpgradeVersion(version), signal);
4182
4127
  }
@@ -4200,7 +4145,7 @@ function describeRejection(reason) {
4200
4145
  }).join("");
4201
4146
  return printable.slice(0, MAXIMUM_REJECTION_TEXT_BYTES);
4202
4147
  }
4203
- function surviveUnhandledRejections(role) {
4148
+ function surviveUnhandledRejections() {
4204
4149
  let log = null;
4205
4150
  const pending = [];
4206
4151
  const record = (reason) => {
@@ -4231,7 +4176,7 @@ function surviveUnhandledRejections(role) {
4231
4176
  }
4232
4177
  log = new BoundedLineLog(
4233
4178
  logDirectory(stateDirectory),
4234
- unhandledRejectionLogFilename(role),
4179
+ unhandledRejectionLogFilename("worker"),
4235
4180
  MAXIMUM_REJECTION_LOG_BYTES,
4236
4181
  (cause) => {
4237
4182
  process.stderr.write(`The unhandled rejection log could not be written: ${String(cause)}
@@ -4248,606 +4193,309 @@ function surviveUnhandledRejections(role) {
4248
4193
  };
4249
4194
  }
4250
4195
 
4251
- // src/daemon/lifecycle/worker-lifecycle-log.ts
4252
- var MAXIMUM_LIFECYCLE_LOG_BYTES = 512 * 1024;
4253
- var MAXIMUM_TEXT_BYTES = 2048;
4254
- function toLogText(value) {
4255
- const singleLine = Array.from(value, (character) => {
4256
- const code = character.charCodeAt(0);
4257
- return code < 32 || code === 127 ? " " : character;
4258
- }).join("");
4259
- return singleLine.slice(-MAXIMUM_TEXT_BYTES);
4260
- }
4261
- var WorkerLifecycleLog = class {
4262
- #log;
4263
- constructor(stateDirectory) {
4264
- this.#log = new BoundedLineLog(
4265
- logDirectory(stateDirectory),
4266
- WORKER_LIFECYCLE_LOG_FILENAME,
4267
- MAXIMUM_LIFECYCLE_LOG_BYTES,
4268
- (cause) => {
4269
- process.stderr.write(`The worker lifecycle log could not be written: ${String(cause)}
4270
- `);
4271
- }
4272
- );
4196
+ // src/daemon/lifecycle/daemon.ts
4197
+ var DaemonRuntimeError = class extends Error {
4198
+ code = "worker_runtime_failed";
4199
+ constructor(connectionId, cause) {
4200
+ super(`Runtime ${connectionId} failed`, { cause });
4273
4201
  }
4274
- started = (pid, version) => {
4275
- this.#append({ kind: "worker_started", pid, version });
4276
- };
4277
- ready = (pid, version) => {
4278
- this.#append({ kind: "worker_ready", pid, version });
4279
- };
4280
- exited = (pid, version, exit, stderr) => {
4281
- this.#append({
4282
- kind: "worker_exited",
4283
- pid,
4284
- version,
4285
- exitCode: exit.code,
4286
- signal: exit.signal,
4287
- stderr: toLogText(stderr)
4288
- });
4289
- };
4290
- startFailed = (version, pid, code, message, stderr) => {
4291
- this.#append({
4292
- kind: "worker_start_failed",
4293
- pid,
4294
- version,
4295
- code,
4296
- message: toLogText(message),
4297
- stderr: toLogText(stderr)
4298
- });
4299
- };
4300
- restartFailed = (version, message) => {
4301
- this.#append({ kind: "worker_restart_failed", version, message: toLogText(message) });
4302
- };
4303
- #append = (entry) => {
4304
- this.#log.record(
4305
- `Mobius worker lifecycle ${JSON.stringify({ ...entry, timestamp: (/* @__PURE__ */ new Date()).toISOString() })}`
4306
- );
4307
- };
4308
4202
  };
4309
-
4310
- // src/daemon/lifecycle/worker-process.ts
4311
- import { spawn } from "node:child_process";
4312
- import { isAbsolute } from "node:path";
4313
- async function decodeWorkerLaunch(value) {
4314
- const { entry, executable, version } = workerLaunch.parse(value);
4315
- if (!isAbsolute(entry) || !isAbsolute(executable)) {
4316
- throw new Error("Worker launch paths must be absolute");
4317
- }
4318
- const resolved = await resolveLaunchFiles(entry, executable);
4319
- if (resolved === null) {
4320
- throw new Error("Worker launch paths must be files");
4321
- }
4322
- return { entry: resolved.entry, executable: resolved.executable, version };
4203
+ async function sendDaemonCommand(directory, kind, signal) {
4204
+ const response = await sendLocalRequest(directory, "manager", { kind }, signal);
4205
+ return response === null ? await sendLocalRequest(directory, "worker", { kind }, signal) : response;
4323
4206
  }
4324
- var WorkerStartupError = class extends Error {
4325
- code;
4326
- stdout;
4327
- stderr;
4328
- constructor(code, message, options, stdout = "", stderr = "") {
4329
- super(message, options);
4330
- this.code = code;
4331
- this.stdout = stdout;
4332
- this.stderr = stderr;
4333
- }
4334
- };
4335
- var WorkerProcess = class {
4336
- #child;
4337
- #stdout = "";
4338
- #stderr = "";
4339
- #started = Promise.withResolvers();
4340
- #closed = Promise.withResolvers();
4341
- #drained = Promise.withResolvers();
4342
- #exit = null;
4343
- exited = this.#closed.promise;
4344
- // Resolves once the child's stdio streams are closed, so the captured stderr
4345
- // tail is final. Bounded: if an orphaned grandchild inherits the pipes and
4346
- // keeps them open, the fallback timer still resolves it.
4347
- streamsSettled = this.#drained.promise;
4348
- launch;
4349
- ready;
4350
- constructor(launch, signal, environment) {
4351
- signal.throwIfAborted();
4352
- this.launch = launch;
4353
- this.#child = spawn(launch.executable, [launch.entry, "__worker"], {
4354
- env: environment,
4355
- windowsHide: true,
4356
- stdio: ["ignore", "pipe", "pipe", "ipc"]
4357
- });
4358
- this.#child.stdout?.setEncoding("utf8").on("data", (chunk) => {
4359
- this.#stdout = (this.#stdout + chunk).slice(-4096);
4360
- process.stdout.write(chunk);
4361
- });
4362
- this.#child.stderr?.setEncoding("utf8").on("data", (chunk) => {
4363
- this.#stderr = (this.#stderr + chunk).slice(-4096);
4364
- process.stderr.write(chunk);
4365
- });
4366
- this.#child.once("close", () => {
4367
- this.#drained.resolve();
4368
- });
4369
- this.#child.on("message", (value) => {
4370
- if (isRecord(value) && value["kind"] === "worker-ready") {
4371
- if (value["version"] !== launch.version) {
4372
- this.#started.reject(
4373
- new WorkerStartupError(
4374
- "worker_version_mismatch",
4375
- "The new daemon reported a different version"
4376
- )
4377
- );
4378
- } else {
4379
- this.#started.resolve(value);
4380
- }
4381
- return;
4382
- }
4383
- });
4384
- this.#child.once("error", (cause) => {
4385
- this.#started.reject(
4386
- new WorkerStartupError("worker_spawn_failed", "The new daemon process could not be launched", {
4387
- cause
4388
- })
4389
- );
4390
- if (this.#child.pid === void 0) {
4391
- this.#exit = { code: null, signal: null };
4392
- this.#boundStreamsSettled();
4393
- this.#closed.resolve();
4394
- }
4395
- });
4396
- this.#child.once("exit", (code, signal2) => {
4397
- this.#exit = { code, signal: signal2 };
4398
- this.#boundStreamsSettled();
4399
- this.#started.reject(
4400
- new WorkerStartupError(
4401
- "worker_exited",
4402
- "The new daemon exited before becoming ready",
4403
- void 0,
4404
- this.#stdout,
4405
- this.#stderr
4406
- )
4407
- );
4408
- this.#closed.resolve();
4409
- });
4410
- this.ready = this.#waitForStart(signal);
4411
- }
4412
- #boundStreamsSettled() {
4413
- const giveUp = setTimeout(() => this.#drained.resolve(), 2e3);
4414
- void this.#drained.promise.then(() => clearTimeout(giveUp));
4415
- }
4416
- get running() {
4417
- return this.#child.exitCode === null && this.#child.signalCode === null && this.#child.connected;
4418
- }
4419
- get pid() {
4420
- const pid = this.#child.pid;
4421
- if (pid === void 0) {
4422
- throw new Error("The working process did not start");
4207
+ async function reloadDaemon(stateDirectory, signal) {
4208
+ try {
4209
+ const response = await sendDaemonCommand(stateDirectory, "reload", signal);
4210
+ if (typeof response !== "object" || response === null || !("kind" in response) || response.kind !== "reloaded") {
4211
+ throw new Error("Saved configuration could not be applied to the running daemon");
4423
4212
  }
4424
- return pid;
4425
- }
4426
- // How the child terminated, available once `exited` settles. `code` and `signal`
4427
- // are null when the process could not be launched at all.
4428
- get exit() {
4429
- if (this.#exit === null) {
4430
- throw new Error("The working process has not exited");
4213
+ } catch (cause) {
4214
+ if (!isNodeError(cause, "ENOENT") && !isNodeError(cause, "ECONNREFUSED")) {
4215
+ throw cause;
4431
4216
  }
4432
- return this.#exit;
4433
4217
  }
4434
- get stderr() {
4435
- return this.#stderr;
4436
- }
4437
- close = async () => {
4438
- if (this.#child.connected) {
4439
- this.#child.disconnect();
4440
- }
4441
- const timeout = setTimeout(() => this.#child.kill("SIGKILL"), 1e4);
4442
- try {
4443
- await this.#closed.promise;
4444
- } finally {
4445
- clearTimeout(timeout);
4218
+ }
4219
+ async function releaseIdleAgentProcesses(stateDirectory, signal) {
4220
+ try {
4221
+ const response = await sendDaemonCommand(stateDirectory, "release", signal);
4222
+ if (typeof response === "object" && response !== null && "kind" in response && response.kind === "released") {
4223
+ return;
4446
4224
  }
4447
- };
4448
- async #waitForStart(signal) {
4449
- const deadline = AbortSignal.any([signal, AbortSignal.timeout(12e4)]);
4450
- const abort = () => this.#started.reject(
4451
- signal.aborted ? signal.reason : new WorkerStartupError(
4452
- "worker_start_timeout",
4453
- "The new daemon did not become ready within 120 seconds",
4454
- { cause: deadline.reason },
4455
- this.#stdout,
4456
- this.#stderr
4457
- )
4225
+ throw new Error(
4226
+ "The running daemon cannot release Agent sessions. Run `npx --yes @moxt-ai/mobius@latest restart` to update it first."
4458
4227
  );
4459
- deadline.addEventListener("abort", abort, { once: true });
4460
- if (deadline.aborted) {
4461
- abort();
4462
- }
4463
- try {
4464
- return await this.#started.promise;
4465
- } finally {
4466
- deadline.removeEventListener("abort", abort);
4228
+ } catch (cause) {
4229
+ if (!isNodeError(cause, "ENOENT") && !isNodeError(cause, "ECONNREFUSED")) {
4230
+ throw cause;
4467
4231
  }
4468
4232
  }
4469
- };
4470
-
4471
- // src/daemon/lifecycle/manager.ts
4472
- async function runManager(stateDirectory, signal) {
4473
- const release = await acquireManagerLock(stateDirectory);
4474
- if (release === null) {
4475
- return;
4233
+ }
4234
+ async function runDaemon(version, signal, owner = "standalone") {
4235
+ if (owner === "legacy-manager" && !process.connected) {
4236
+ throw new Error("The legacy worker entrypoint requires a manager IPC connection");
4476
4237
  }
4477
- const rejectionGuard = surviveUnhandledRejections("manager");
4478
- rejectionGuard.bindStateDirectory(stateDirectory);
4238
+ const rejectionGuard = surviveUnhandledRejections();
4479
4239
  const shutdown = new AbortController();
4480
4240
  const combined = AbortSignal.any([signal, shutdown.signal]);
4481
- const lifecycle = new WorkerLifecycleLog(stateDirectory);
4482
- const workerExits = /* @__PURE__ */ new Set();
4483
- let worker;
4484
- let switchState = null;
4485
- let queue = Promise.resolve();
4486
- const status = () => ({
4487
- kind: "status",
4488
- protocolVersion: 2,
4489
- worker: worker?.running ? { pid: worker.pid, version: worker.launch.version } : null
4490
- });
4491
- const start = async (launch) => {
4492
- combined.throwIfAborted();
4493
- await worker?.close();
4494
- const replacement = new WorkerProcess(launch, combined, process.env);
4495
- worker = replacement;
4496
- let pid = null;
4497
- try {
4498
- pid = replacement.pid;
4499
- } catch {
4500
- }
4501
- if (pid !== null) {
4502
- lifecycle.started(pid, launch.version);
4503
- }
4504
- try {
4505
- await replacement.ready;
4506
- } catch (cause) {
4507
- await replacement.close();
4508
- await replacement.streamsSettled;
4509
- const code = cause instanceof WorkerStartupError ? cause.code : "worker_start_failed";
4510
- const message = cause instanceof WorkerStartupError ? cause.message : "The new daemon could not start";
4511
- lifecycle.startFailed(launch.version, pid, code, message, replacement.stderr);
4512
- throw cause;
4241
+ const disconnect = () => shutdown.abort();
4242
+ if (owner === "legacy-manager") {
4243
+ process.once("disconnect", disconnect);
4244
+ }
4245
+ try {
4246
+ await runDaemonRuntime(version, combined, shutdown, rejectionGuard, owner);
4247
+ } catch (cause) {
4248
+ const control = await openMobiusControlInstallation(AbortSignal.timeout(5e3), process.env);
4249
+ new BoundedLineLog(logDirectory(control.stateDirectory), WORKER_LIFECYCLE_LOG_FILENAME, 512 * 1024).record(
4250
+ JSON.stringify({
4251
+ kind: "daemon_failed",
4252
+ message: cause instanceof Error ? cause.message : "Daemon failed"
4253
+ })
4254
+ );
4255
+ throw cause;
4256
+ } finally {
4257
+ shutdown.abort();
4258
+ rejectionGuard.release();
4259
+ if (owner === "legacy-manager") {
4260
+ process.off("disconnect", disconnect);
4261
+ if (process.connected) {
4262
+ process.disconnect();
4263
+ }
4513
4264
  }
4514
- if (pid !== null) {
4515
- lifecycle.ready(pid, launch.version);
4265
+ }
4266
+ }
4267
+ async function runDaemonRuntime(version, combined, shutdown, rejectionGuard, owner) {
4268
+ const control = await openMobiusControlInstallation(combined, process.env);
4269
+ const releaseDaemon = await acquireDaemonLock(
4270
+ control.stateDirectory,
4271
+ owner === "legacy-manager" ? "worker.lock" : "manager.lock"
4272
+ );
4273
+ if (releaseDaemon === null) {
4274
+ return;
4275
+ }
4276
+ const lifecycleLog = new BoundedLineLog(
4277
+ logDirectory(control.stateDirectory),
4278
+ WORKER_LIFECYCLE_LOG_FILENAME,
4279
+ 512 * 1024
4280
+ );
4281
+ lifecycleLog.record(
4282
+ JSON.stringify({ kind: "daemon_started", pid: process.pid, version, timestamp: (/* @__PURE__ */ new Date()).toISOString() })
4283
+ );
4284
+ try {
4285
+ if (owner === "standalone") {
4286
+ const deadline = AbortSignal.any([combined, AbortSignal.timeout(3e4)]);
4287
+ while (await daemonLockExists(control.stateDirectory, "worker.lock")) {
4288
+ await delay(50, void 0, { signal: deadline });
4289
+ }
4290
+ deadline.throwIfAborted();
4516
4291
  }
4517
- const exited = replacement.exited.then(async () => {
4518
- await replacement.streamsSettled;
4519
- if (pid !== null) {
4520
- lifecycle.exited(pid, launch.version, replacement.exit, replacement.stderr);
4292
+ await prepareVersionData(version, combined);
4293
+ const engineDataDir = await prepareMobiusEngineDataDir(version, combined, process.env);
4294
+ const installation = await RuntimeInstallationStore.open(engineDataDir, combined);
4295
+ rejectionGuard.bindStateDirectory(control.stateDirectory);
4296
+ const turnActivity = new RuntimeTurnActivity();
4297
+ const agentProcesses = new RuntimeAgentProcesses();
4298
+ const active = /* @__PURE__ */ new Map();
4299
+ const fail = (connectionId, cause) => {
4300
+ console.error(`Runtime ${connectionId} failed; stopping the daemon`, cause);
4301
+ shutdown.abort(new DaemonRuntimeError(connectionId, cause));
4302
+ };
4303
+ const reconcile = async () => {
4304
+ const connections = await installation.listConnections(combined);
4305
+ const enabled = new Set(
4306
+ connections.filter((connection) => connection.activation === "active").map((connection) => connection.connectionId)
4307
+ );
4308
+ for (const [id, running] of active) {
4309
+ if (enabled.has(id)) {
4310
+ continue;
4311
+ }
4312
+ running.controller.abort();
4313
+ await running.completion;
4314
+ active.delete(id);
4521
4315
  }
4522
- try {
4523
- await setTimeout2(1e3, void 0, { signal: combined });
4524
- const restart = queue.then(async () => {
4525
- if (worker === replacement && !combined.aborted) {
4526
- await start(replacement.launch);
4527
- }
4528
- });
4529
- queue = restart.catch(() => void 0);
4530
- await restart;
4531
- } catch {
4532
- if (!combined.aborted) {
4533
- const message = "The working daemon could not be restarted; run mobius start to retry";
4534
- process.stderr.write(`${message}
4535
- `);
4536
- lifecycle.restartFailed(launch.version, message);
4316
+ for (const connection of connections) {
4317
+ if (!enabled.has(connection.connectionId) || active.has(connection.connectionId)) {
4318
+ continue;
4319
+ }
4320
+ try {
4321
+ const config = await prepareRuntimeConnection(
4322
+ installation,
4323
+ connection,
4324
+ combined,
4325
+ process.env,
4326
+ resolveMaxAcpProcesses(process.env)
4327
+ );
4328
+ const controller = new AbortController();
4329
+ const loop = new RuntimeLoop(async () => config, {
4330
+ processLogsDirectory: control.stateDirectory,
4331
+ turnActivity,
4332
+ agentProcesses
4333
+ });
4334
+ const completion = loop.run(AbortSignal.any([combined, controller.signal])).then(
4335
+ () => {
4336
+ if (!combined.aborted && !controller.signal.aborted) {
4337
+ fail(connection.connectionId, new Error("The Runtime stopped unexpectedly"));
4338
+ }
4339
+ },
4340
+ (cause) => {
4341
+ if (!combined.aborted && !controller.signal.aborted) {
4342
+ fail(connection.connectionId, cause);
4343
+ }
4344
+ }
4345
+ ).finally(() => active.delete(connection.connectionId));
4346
+ active.set(connection.connectionId, { config, controller, completion });
4347
+ } catch (cause) {
4348
+ combined.throwIfAborted();
4349
+ fail(connection.connectionId, cause);
4350
+ throw cause;
4537
4351
  }
4538
4352
  }
4539
- });
4540
- workerExits.add(exited);
4541
- void exited.finally(() => workerExits.delete(exited));
4542
- };
4543
- const switchWorker = async (transition2) => {
4544
- try {
4545
- await start(transition2.next);
4546
- switchState = { ...transition2, kind: "switched" };
4547
- } catch (cause) {
4548
- combined.throwIfAborted();
4549
- const code = cause instanceof WorkerStartupError ? cause.code : "worker_start_failed";
4550
- const message = cause instanceof WorkerStartupError ? cause.message : "The new daemon could not start";
4551
- switchState = {
4552
- ...transition2,
4553
- kind: "rolled-back",
4554
- code,
4555
- message,
4556
- stdout: cause instanceof WorkerStartupError ? cause.stdout : "",
4557
- stderr: cause instanceof WorkerStartupError ? cause.stderr : "",
4558
- retryAfter: Date.now() + 8 * 36e5
4559
- };
4560
- await start(transition2.previous);
4561
- await discardRolledBackData(transition2.next.version, combined);
4562
- }
4563
- };
4564
- const handle = async (value, requestSignal) => {
4565
- const command = decodeKnownLifecycleCommand(value);
4566
- if (command === null) {
4567
- return null;
4568
- }
4569
- if (command.kind === "switch-status" && !command.wait) {
4570
- return { kind: "switch-status", state: switchState };
4571
- }
4572
- const result = queue.then(async () => {
4573
- requestSignal.throwIfAborted();
4574
- switch (command.kind) {
4575
- case "switch-status":
4576
- return { kind: "switch-status", state: switchState };
4577
- case "status":
4578
- return status();
4579
- case "stop":
4580
- return { kind: "stopped" };
4581
- case "switch": {
4582
- const state = switchState;
4583
- if (state?.kind === "switching" || state?.kind === "rolled-back" && state.retryAfter > Date.now()) {
4584
- return {
4585
- kind: "error",
4586
- code: "switch_blocked",
4587
- message: "A switch is in progress or cooling down"
4588
- };
4589
- }
4590
- if (!worker?.running) {
4591
- return {
4592
- kind: "error",
4593
- code: "worker_unavailable",
4594
- message: "There is no working daemon to replace"
4595
- };
4353
+ };
4354
+ const refreshRunningCatalogs = async (connectionIds) => {
4355
+ await Promise.all(
4356
+ [...connectionIds].map(async (connectionId) => {
4357
+ const config = active.get(connectionId)?.config;
4358
+ if (config?.rediscoverAgents === void 0) {
4359
+ return;
4596
4360
  }
4597
- const next = await decodeWorkerLaunch(command.launch);
4598
- if (next.version === worker.launch.version) {
4599
- return {
4600
- kind: "error",
4601
- code: "already_current",
4602
- message: "The requested version is already running"
4603
- };
4361
+ try {
4362
+ await refreshAgentCatalog(
4363
+ config,
4364
+ config.rediscoverAgents,
4365
+ AbortSignal.any([combined, AbortSignal.timeout(9e4)])
4366
+ );
4367
+ } catch (cause) {
4368
+ combined.throwIfAborted();
4369
+ console.error(`Runtime ${connectionId} could not refresh its Agent catalog`, cause);
4604
4370
  }
4605
- switchState = {
4606
- kind: "switching",
4607
- id: command.id,
4608
- context: command.context,
4609
- previous: worker.launch,
4610
- next
4611
- };
4612
- return { kind: "switching" };
4371
+ })
4372
+ );
4373
+ };
4374
+ let reload = Promise.resolve();
4375
+ const handle = async (request) => {
4376
+ if (typeof request === "object" && request !== null && "kind" in request) {
4377
+ if (request.kind === "status" || request.kind === "ensure") {
4378
+ return { kind: "status", protocolVersion: 2, worker: { pid: process.pid, version } };
4613
4379
  }
4614
- case "ensure": {
4615
- const launch = await decodeWorkerLaunch(command.launch);
4616
- if (!worker?.running || worker.launch.version !== launch.version || worker.launch.entry !== launch.entry || worker.launch.executable !== launch.executable) {
4617
- await start(launch);
4618
- }
4619
- await worker?.ready;
4620
- return status();
4380
+ if (request.kind === "stop") {
4381
+ return { kind: "stopped" };
4621
4382
  }
4622
4383
  }
4623
- });
4624
- queue = result.catch(() => void 0);
4625
- return await result;
4626
- };
4627
- try {
4628
- await serveLocalControl(stateDirectory, "manager", combined, handle, void 0, (response) => {
4629
- if (typeof response === "object" && response !== null && "kind" in response && response.kind === "switching") {
4630
- const pending = queue.then(async () => {
4631
- const state = switchState;
4632
- if (state?.kind === "switching") {
4633
- await switchWorker(state);
4634
- }
4635
- });
4636
- queue = pending.catch(() => {
4637
- if (!combined.aborted) {
4638
- process.stderr.write("Daemon switch or rollback failed; run mobius start to retry.\n");
4384
+ if (typeof request === "object" && request !== null && "kind" in request && request.kind === "release") {
4385
+ await agentProcesses.retireIdleProcesses();
4386
+ return { kind: "released" };
4387
+ }
4388
+ if (typeof request !== "object" || request === null || !("kind" in request) || request.kind !== "reload") {
4389
+ return { kind: "error", code: "invalid_reload", message: "Expected a configuration reload" };
4390
+ }
4391
+ const running = new Set(active.keys());
4392
+ const pending = reload.then(reconcile).then(async () => await refreshRunningCatalogs(running));
4393
+ reload = pending.catch(() => void 0);
4394
+ await pending;
4395
+ return { kind: "reloaded" };
4396
+ };
4397
+ try {
4398
+ await reconcile();
4399
+ await recordCurrentData(version, combined);
4400
+ lifecycleLog.record(
4401
+ JSON.stringify({
4402
+ kind: "daemon_ready",
4403
+ pid: process.pid,
4404
+ version,
4405
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
4406
+ })
4407
+ );
4408
+ const servers = owner === "legacy-manager" ? ["worker"] : ["manager", "worker"];
4409
+ const results = await Promise.allSettled(
4410
+ servers.map(async (endpoint) => {
4411
+ try {
4412
+ await serveLocalControl(
4413
+ control.stateDirectory,
4414
+ endpoint,
4415
+ combined,
4416
+ handle,
4417
+ () => {
4418
+ if (owner === "legacy-manager") {
4419
+ combined.throwIfAborted();
4420
+ process.send?.({ kind: "worker-ready", version }, () => void 0);
4421
+ }
4422
+ },
4423
+ (response) => {
4424
+ if (typeof response === "object" && response !== null && "kind" in response && response.kind === "stopped") {
4425
+ shutdown.abort();
4426
+ }
4427
+ }
4428
+ );
4429
+ } finally {
4430
+ shutdown.abort();
4639
4431
  }
4640
- });
4432
+ })
4433
+ );
4434
+ for (const result of results) {
4435
+ if (result.status === "rejected") {
4436
+ throw result.reason;
4437
+ }
4641
4438
  }
4642
- if (typeof response === "object" && response !== null && "kind" in response && response.kind === "stopped") {
4643
- shutdown.abort();
4439
+ if (shutdown.signal.reason instanceof DaemonRuntimeError) {
4440
+ throw shutdown.signal.reason;
4644
4441
  }
4645
- });
4442
+ } finally {
4443
+ shutdown.abort();
4444
+ await reload;
4445
+ await Promise.all([...active.values()].map((running) => running.completion));
4446
+ }
4646
4447
  } finally {
4647
- shutdown.abort();
4648
- await queue;
4649
- await worker?.close();
4650
- await Promise.all(workerExits);
4651
- rejectionGuard.release();
4652
- await release();
4448
+ await releaseDaemon();
4653
4449
  }
4654
4450
  }
4655
4451
 
4656
- // src/cli/handler/daemon/manager.ts
4657
- function registerManagerCommand(program2, context) {
4658
- program2.command("__manager", { hidden: true }).action(async () => {
4659
- const installation = await openMobiusControlInstallation(context.shutdownSignal, process.env);
4660
- await runManager(installation.stateDirectory, context.shutdownSignal);
4661
- });
4662
- }
4663
-
4664
- // src/daemon/lifecycle/manager-client.ts
4665
- var import_semver2 = __toESM(require_semver(), 1);
4666
- import { spawn as spawn2 } from "node:child_process";
4667
-
4668
- // src/daemon/upgrades/package.ts
4669
- import { isAbsolute as isAbsolute2, join as join10, relative } from "node:path";
4670
- var import_semver = __toESM(require_semver(), 1);
4452
+ // src/daemon/lifecycle/daemon-client.ts
4453
+ import { spawn } from "node:child_process";
4454
+ import { once } from "node:events";
4455
+ import { setTimeout as delay2 } from "node:timers/promises";
4671
4456
 
4672
- // src/storage/daemon/release-files.ts
4673
- import { readFile as readFile5, realpath } from "node:fs/promises";
4674
- import { join as join9 } from "node:path";
4675
- function releasePrefix(version) {
4676
- return join9(readStateDirectory(), STATE_ROOT_LAYOUT.releases, version);
4677
- }
4678
- async function readInstalledRelease(prefix) {
4679
- const packageRoot = await realpath(join9(prefix, "node_modules", "@moxt-ai", "mobius"));
4680
- return { manifest: await readFile5(join9(packageRoot, "package.json"), "utf8"), packageRoot };
4457
+ // src/daemon/control/protocol.ts
4458
+ var lifecycleResponse = external_exports.discriminatedUnion("kind", [
4459
+ external_exports.object({
4460
+ kind: external_exports.literal("status"),
4461
+ protocolVersion: external_exports.literal(2),
4462
+ worker: external_exports.object({ pid: external_exports.number().int().positive(), version: external_exports.string() }).readonly().nullable()
4463
+ }),
4464
+ external_exports.object({ kind: external_exports.literal("stopped") }),
4465
+ external_exports.object({ kind: external_exports.literal("error"), code: external_exports.string(), message: external_exports.string() })
4466
+ ]).readonly();
4467
+ function decodeKnownLifecycleResponse(value) {
4468
+ if (typeof value === "object" && value !== null && "kind" in value && typeof value.kind === "string" && !["status", "stopped", "error"].includes(value.kind)) {
4469
+ return null;
4470
+ }
4471
+ return lifecycleResponse.parse(value);
4681
4472
  }
4682
4473
 
4683
- // src/daemon/upgrades/package.ts
4684
- var DaemonReleaseUpgradeError = class extends Error {
4685
- code;
4686
- constructor(code, message, options) {
4687
- super(message, options);
4688
- this.code = code;
4474
+ // src/daemon/lifecycle/daemon-client.ts
4475
+ async function spawnDaemon() {
4476
+ const entry = process.argv[1];
4477
+ if (entry === void 0) {
4478
+ throw new Error("The CLI entrypoint is missing");
4689
4479
  }
4690
- };
4691
- async function prepareDaemonReleaseVersion(version, signal) {
4692
- decodeRuntimeUpgradeVersion(version);
4693
- signal.throwIfAborted();
4694
- const prefix = releasePrefix(version);
4695
- let installed;
4696
- try {
4697
- installed = await readInstalledRelease(prefix);
4698
- } catch (cause) {
4699
- signal.throwIfAborted();
4700
- if (!isNodeError(cause, "ENOENT")) {
4701
- throw new DaemonReleaseUpgradeError(
4702
- "release_read_failed",
4703
- "The installed Mobius release could not be read",
4704
- { cause }
4705
- );
4706
- }
4707
- return await installDaemonReleaseVersion(version, signal);
4708
- }
4709
- try {
4710
- return await verifyInstalledDaemonRelease(installed, prefix, version, signal);
4711
- } catch (cause) {
4712
- signal.throwIfAborted();
4713
- if (cause instanceof DaemonReleaseUpgradeError) {
4714
- throw cause;
4715
- }
4716
- throw new DaemonReleaseUpgradeError("release_invalid", "The installed Mobius release could not be verified", {
4717
- cause
4718
- });
4719
- }
4720
- }
4721
- async function installDaemonReleaseVersion(version, signal) {
4722
- decodeRuntimeUpgradeVersion(version);
4723
- const npm = await findExecutableOnPath("npm", process.env);
4724
- if (npm === null) {
4725
- throw new DaemonReleaseUpgradeError("npm_unavailable", "npm is required to download the new daemon");
4726
- }
4727
- const prefix = releasePrefix(version);
4728
- await ensurePrivateDirectory(prefix);
4729
- try {
4730
- await validateLocalPath(prefix, true, true, signal);
4731
- await runBoundedProcess({
4732
- command: npm.path,
4733
- arguments: [
4734
- "install",
4735
- "--prefix",
4736
- prefix,
4737
- "--ignore-scripts",
4738
- "--no-audit",
4739
- "--no-fund",
4740
- "--save-exact",
4741
- "--",
4742
- `@moxt-ai/mobius@${version}`
4743
- ],
4744
- cwd: prefix,
4745
- environment: process.env,
4746
- signal,
4747
- timeoutMilliseconds: 3e5,
4748
- outputLimitBytes: 64 * 1024
4749
- });
4750
- return await verifyInstalledDaemonRelease(await readInstalledRelease(prefix), prefix, version, signal);
4751
- } catch (cause) {
4752
- signal.throwIfAborted();
4753
- if (cause instanceof DaemonReleaseUpgradeError) {
4754
- throw cause;
4755
- }
4756
- throw new DaemonReleaseUpgradeError(
4757
- "npm_install_failed",
4758
- "npm could not install and verify the requested Mobius version",
4759
- { cause }
4760
- );
4761
- }
4762
- }
4763
- async function verifyInstalledDaemonRelease(installed, prefix, version, signal) {
4764
- await validateLocalPath(prefix, true, true, signal);
4765
- const metadata = external_exports.object({
4766
- name: external_exports.literal("@moxt-ai/mobius"),
4767
- version: external_exports.literal(version),
4768
- bin: external_exports.object({ mobius: external_exports.literal("dist/cli.js") }),
4769
- engines: external_exports.object({ node: external_exports.string().refine((value) => (0, import_semver.validRange)(value) !== null) })
4770
- }).parse(JSON.parse(installed.manifest));
4771
- const entry = await canonicalPath(join10(installed.packageRoot, metadata.bin.mobius));
4772
- const resolved = relative(await canonicalPath(prefix), entry);
4773
- if (resolved.startsWith("..") || isAbsolute2(resolved)) {
4774
- throw new Error("Installed entry escaped npm prefix");
4775
- }
4776
- if (!(0, import_semver.satisfies)(process.versions.node, metadata.engines.node)) {
4777
- throw new DaemonReleaseUpgradeError(
4778
- "node_incompatible",
4779
- `Mobius ${version} requires Node.js ${metadata.engines.node}; the running daemon uses ${process.versions.node} at ${process.execPath}. Update Node.js before retrying.`
4780
- );
4781
- }
4782
- try {
4783
- const result = await runBoundedProcess({
4784
- command: process.execPath,
4785
- arguments: [entry, "--version"],
4786
- cwd: prefix,
4787
- environment: process.env,
4788
- signal,
4789
- timeoutMilliseconds: 3e4,
4790
- outputLimitBytes: 16 * 1024
4791
- });
4792
- if (!result.stdout.split(/\r?\n/u).includes(`CLI: ${version}`)) {
4793
- throw new Error("Candidate did not report the requested CLI version");
4794
- }
4795
- } catch (cause) {
4796
- signal.throwIfAborted();
4797
- throw new DaemonReleaseUpgradeError(
4798
- "release_startup_failed",
4799
- `Mobius ${version} startup check failed; the current daemon is unchanged.`,
4800
- { cause }
4801
- );
4802
- }
4803
- return await decodeWorkerLaunch({ executable: process.execPath, entry, version });
4804
- }
4805
- var npmViewVersionSchema = external_exports.union([external_exports.string(), external_exports.tuple([external_exports.string()])]).transform((value) => typeof value === "string" ? value : value[0]);
4806
- async function findNewDaemonReleaseVersion(currentVersion, signal) {
4807
- const npm = await findExecutableOnPath("npm", process.env);
4808
- if (npm === null) {
4809
- throw new DaemonReleaseUpgradeError("npm_unavailable", "npm is required to check for a new daemon");
4810
- }
4811
- try {
4812
- const result = await runBoundedProcess({
4813
- command: npm.path,
4814
- arguments: ["view", "@moxt-ai/mobius", "dist-tags.latest", "--json", "--prefer-online"],
4815
- cwd: process.cwd(),
4816
- environment: process.env,
4817
- signal,
4818
- timeoutMilliseconds: 3e4,
4819
- outputLimitBytes: 4096
4820
- });
4821
- const version = decodeRuntimeUpgradeVersion(npmViewVersionSchema.parse(JSON.parse(result.stdout)));
4822
- return (0, import_semver.gt)(version, currentVersion) ? version : null;
4823
- } catch (cause) {
4824
- signal.throwIfAborted();
4825
- throw new DaemonReleaseUpgradeError("npm_check_failed", "npm could not return a valid latest Mobius version", {
4826
- cause
4827
- });
4828
- }
4829
- }
4830
-
4831
- // src/daemon/lifecycle/manager-client.ts
4832
- async function spawnManager(launch) {
4833
- const child = spawn2(launch.executable, [launch.entry, "__manager"], {
4480
+ const child = spawn(process.execPath, [entry, "__daemon"], {
4834
4481
  detached: true,
4835
4482
  windowsHide: true,
4836
4483
  env: process.env,
4837
4484
  stdio: "ignore"
4838
4485
  });
4486
+ await once(child, "spawn");
4839
4487
  child.unref();
4840
4488
  return child;
4841
4489
  }
4842
- async function launchManager(launch) {
4843
- process.stdout.write(`Starting mobius daemon ${launch.version}
4490
+ async function launchDaemon(version) {
4491
+ process.stdout.write(`Starting mobius daemon ${version}
4844
4492
  `);
4845
- await spawnManager(launch);
4493
+ return await spawnDaemon();
4846
4494
  }
4847
- async function waitForManagerExit(stateDirectory, signal) {
4495
+ async function waitForDaemonExit(stateDirectory, signal) {
4848
4496
  const deadline = AbortSignal.any([signal, AbortSignal.timeout(3e4)]);
4849
4497
  while (!deadline.aborted) {
4850
- if (!await managerLockExists(stateDirectory)) {
4498
+ if (!await daemonLockExists(stateDirectory)) {
4851
4499
  return;
4852
4500
  }
4853
4501
  await new Promise((resolve6, reject) => {
@@ -4863,24 +4511,15 @@ async function waitForManagerExit(stateDirectory, signal) {
4863
4511
  }
4864
4512
  throw deadline.reason;
4865
4513
  }
4866
- async function ensureWorkingProcess(stateDirectory, cliVersion, signal) {
4867
- const current = await readCurrentDataVersion(readStateDirectory(), signal);
4868
- const version = current !== null && (0, import_semver2.gt)(current, cliVersion) ? current : cliVersion;
4869
- const launch = await prepareDaemonReleaseVersion(version, signal);
4870
- const deadline = AbortSignal.any([signal, AbortSignal.timeout(CONTROL_TIMEOUT_MILLISECONDS)]);
4871
- await ensureManager(stateDirectory, launch, deadline);
4872
- const response = decodeKnownLifecycleResponse(
4873
- await sendLocalRequest(stateDirectory, "manager", { kind: "ensure", launch }, deadline)
4874
- );
4875
- if (response?.kind === "error") {
4876
- throw new Error(response.message);
4514
+ async function ensureDaemon(stateDirectory, version, signal) {
4515
+ const running = await readDaemonVersion(stateDirectory, signal);
4516
+ if (running !== null) {
4517
+ return running;
4877
4518
  }
4878
- if (response?.kind !== "status" || response.worker?.version !== version) {
4879
- throw new Error("The working daemon did not start");
4880
- }
4881
- return version;
4519
+ const deadline = AbortSignal.any([signal, AbortSignal.timeout(CONTROL_TIMEOUT_MILLISECONDS)]);
4520
+ return await waitForDaemon(stateDirectory, version, deadline);
4882
4521
  }
4883
- async function readWorkingProcessVersion(stateDirectory, signal) {
4522
+ async function readDaemonVersion(stateDirectory, signal) {
4884
4523
  try {
4885
4524
  const response = decodeKnownLifecycleResponse(
4886
4525
  await sendLocalRequest(stateDirectory, "manager", { kind: "status" }, signal)
@@ -4895,28 +4534,49 @@ async function readWorkingProcessVersion(stateDirectory, signal) {
4895
4534
  }
4896
4535
  return null;
4897
4536
  }
4898
- async function ensureManager(stateDirectory, launch, signal) {
4899
- let managerStarted = false;
4537
+ async function waitForDaemon(stateDirectory, version, signal) {
4538
+ let daemonStarted = false;
4539
+ let child;
4900
4540
  while (!signal.aborted) {
4901
4541
  try {
4902
- const response = decodeKnownLifecycleResponse(
4542
+ let response = decodeKnownLifecycleResponse(
4903
4543
  await sendLocalRequest(stateDirectory, "manager", { kind: "status" }, signal)
4904
4544
  );
4905
- if (response?.kind === "status") {
4906
- if (managerStarted) {
4907
- process.stdout.write(`Mobius daemon ${launch.version} started
4545
+ if (response?.kind === "status" && response.worker === null) {
4546
+ const entry = process.argv[1];
4547
+ if (entry === void 0) {
4548
+ throw new Error("The CLI entrypoint is missing");
4549
+ }
4550
+ response = decodeKnownLifecycleResponse(
4551
+ await sendLocalRequest(
4552
+ stateDirectory,
4553
+ "manager",
4554
+ { kind: "ensure", launch: { entry, executable: process.execPath, version } },
4555
+ signal
4556
+ )
4557
+ );
4558
+ }
4559
+ if (response?.kind === "error") {
4560
+ throw new Error(response.message);
4561
+ }
4562
+ if (response?.kind === "status" && response.worker !== null) {
4563
+ if (daemonStarted) {
4564
+ process.stdout.write(`Mobius daemon ${version} started
4908
4565
  `);
4909
4566
  }
4910
- return;
4567
+ return response.worker.version;
4911
4568
  }
4912
- throw new Error("The manager could not return its status");
4569
+ throw new Error("The daemon could not return its status");
4913
4570
  } catch (cause) {
4914
4571
  if (!isNodeError(cause, "ENOENT") && !isNodeError(cause, "ECONNREFUSED")) {
4915
4572
  throw cause;
4916
4573
  }
4917
- if (!managerStarted) {
4918
- await launchManager(launch);
4919
- managerStarted = true;
4574
+ if (child !== void 0 && (child.signalCode !== null || child.exitCode !== null && (child.exitCode !== 0 || !await daemonLockExists(stateDirectory)))) {
4575
+ throw new Error("The daemon exited before becoming ready");
4576
+ }
4577
+ if (!daemonStarted) {
4578
+ child = await launchDaemon(version);
4579
+ daemonStarted = true;
4920
4580
  }
4921
4581
  await new Promise((resolve6, reject) => {
4922
4582
  const timeout = setTimeout(finish, 50);
@@ -4932,414 +4592,42 @@ async function ensureManager(stateDirectory, launch, signal) {
4932
4592
  }
4933
4593
  throw signal.reason;
4934
4594
  }
4935
- async function stopManager(stateDirectory, signal) {
4936
- try {
4937
- const response = decodeKnownLifecycleResponse(
4938
- await sendLocalRequest(stateDirectory, "manager", { kind: "stop" }, signal)
4939
- );
4940
- if (response?.kind === "error") {
4941
- throw new Error(response.message);
4942
- }
4943
- if (response?.kind !== "stopped") {
4944
- throw new Error("The Mobius manager returned an unexpected response");
4945
- }
4946
- await waitForManagerExit(stateDirectory, signal);
4947
- return response;
4948
- } catch (cause) {
4949
- if (isNodeError(cause, "ENOENT") || isNodeError(cause, "ECONNREFUSED")) {
4950
- return { kind: "stopped" };
4951
- }
4952
- throw cause;
4953
- }
4954
- }
4955
-
4956
- // src/cli/handler/daemon/quit.ts
4957
- function registerQuitCommand(program2, context) {
4958
- program2.command("quit").description("stop the manager and every active Runtime").action(async () => {
4959
- const installation = await openMobiusControlInstallation(context.shutdownSignal, process.env);
4960
- await stopManager(installation.stateDirectory, context.shutdownSignal);
4961
- process.stdout.write("Mobius daemon stopped.\n");
4962
- });
4963
- }
4964
-
4965
- // src/daemon/lifecycle/worker.ts
4966
- import { setTimeout as setTimeout4 } from "node:timers/promises";
4967
-
4968
- // src/engine/session/process/runtime-agent-processes.ts
4969
- var RuntimeAgentProcesses = class {
4970
- #pools = /* @__PURE__ */ new Set();
4971
- /** Tracks a Runtime's pool until the returned function removes it. */
4972
- add = (pool) => {
4973
- this.#pools.add(pool);
4974
- return () => {
4975
- this.#pools.delete(pool);
4976
- };
4977
- };
4978
- retireIdleProcesses = async () => {
4979
- await Promise.all([...this.#pools].map(async (pool) => await pool.retireIdleProcesses()));
4980
- };
4981
- };
4982
-
4983
- // src/daemon/upgrades/upgrade.ts
4984
- import { randomUUID as randomUUID2 } from "node:crypto";
4985
- import { setInterval, setTimeout as setTimeout3 } from "node:timers/promises";
4986
-
4987
- // src/daemon/control/switch-status.ts
4988
- async function readSwitchStatus(directory, signal, wait = false) {
4989
- const response = decodeKnownLifecycleResponse(
4990
- await sendLocalRequest(directory, "manager", { kind: "switch-status", wait }, signal)
4991
- );
4992
- if (response?.kind !== "switch-status") {
4993
- throw new Error("The manager could not return the switch status");
4994
- }
4995
- return response.state;
4996
- }
4997
-
4998
- // src/daemon/upgrades/upgrade.ts
4999
- var SWITCH_COMPLETION_MILLISECONDS = 3e5;
5000
- var SWITCH_POLL_MILLISECONDS = 200;
5001
- var UPGRADE_QUIET_MILLISECONDS = 30 * 6e4;
5002
- var UPGRADE_QUIET_CHECK_MILLISECONDS = 6e4;
5003
- async function reportDaemonUpgradeAvailability(send, signal) {
5004
- if (!process.connected) {
5005
- return;
5006
- }
5007
- const control = await openMobiusControlInstallation(signal, process.env);
5008
- const transition2 = await readSwitchStatus(control.stateDirectory, signal, true);
5009
- if (transition2 === null) {
5010
- return;
5011
- }
5012
- const base = {
5013
- kind: "runtime.upgrade.result",
5014
- requestId: decodeIdentifier(transition2.id),
5015
- version: package_default.version,
5016
- targetVersion: transition2.next.version
5017
- };
5018
- if (transition2.kind === "rolled-back" && transition2.previous.version === package_default.version) {
5019
- send({
5020
- ...base,
5021
- kind: "runtime.upgrade.result",
5022
- outcome: {
5023
- status: "failed",
5024
- code: transition2.code,
5025
- message: transition2.message,
5026
- retryAfter: transition2.retryAfter,
5027
- stdout: transition2.stdout,
5028
- stderr: transition2.stderr
4595
+ async function stopDaemon(stateDirectory, signal) {
4596
+ const deadline = AbortSignal.any([signal, AbortSignal.timeout(CONTROL_TIMEOUT_MILLISECONDS)]);
4597
+ while (true) {
4598
+ deadline.throwIfAborted();
4599
+ try {
4600
+ const response = decodeKnownLifecycleResponse(
4601
+ await sendLocalRequest(stateDirectory, "manager", { kind: "stop" }, deadline)
4602
+ );
4603
+ if (response?.kind === "error") {
4604
+ throw new Error(response.message);
5029
4605
  }
5030
- });
5031
- } else if (transition2.next.version === package_default.version) {
5032
- send({ ...base, kind: "runtime.upgrade.result", outcome: { status: "succeeded" } });
5033
- }
5034
- }
5035
- async function runDaemonReleaseUpgrades(signal, activity) {
5036
- try {
5037
- for await (const _tick of setInterval(36e5, void 0, { signal })) {
5038
- try {
5039
- await checkDaemonReleaseUpgrade(signal, activity);
5040
- } catch (cause) {
5041
- signal.throwIfAborted();
5042
- console.error("Daemon npm upgrade check failed", cause);
4606
+ if (response?.kind !== "stopped") {
4607
+ throw new Error("The Mobius daemon returned an unexpected response");
5043
4608
  }
5044
- }
5045
- } catch (cause) {
5046
- if (!signal.aborted) {
5047
- throw cause;
5048
- }
5049
- }
5050
- }
5051
- async function requestDaemonReleaseUpgrade(stateDirectory, currentVersion, signal) {
5052
- const version = await findNewDaemonReleaseVersion(currentVersion, signal);
5053
- if (version === null) {
5054
- return { kind: "current" };
5055
- }
5056
- const launch = await installDaemonReleaseVersion(version, signal);
5057
- return await requestInstalledDaemonReleaseSwitch(stateDirectory, launch, signal);
5058
- }
5059
- async function requestInstalledDaemonReleaseSwitch(stateDirectory, launch, signal) {
5060
- const id = randomUUID2();
5061
- const response = decodeKnownLifecycleResponse(
5062
- await sendLocalRequest(stateDirectory, "manager", { kind: "switch", id, context: "npm", launch }, signal)
5063
- );
5064
- if (response?.kind === "error") {
5065
- throw new DaemonReleaseUpgradeError(response.code, response.message);
5066
- }
5067
- if (response?.kind !== "switching") {
5068
- throw new DaemonReleaseUpgradeError("manager_incompatible", "The manager does not support daemon switching");
5069
- }
5070
- return { kind: "requested", id, version: launch.version };
5071
- }
5072
- async function awaitDaemonReleaseSwitch(stateDirectory, id, signal) {
5073
- const deadline = AbortSignal.any([signal, AbortSignal.timeout(SWITCH_COMPLETION_MILLISECONDS)]);
5074
- let outcome;
5075
- while (!deadline.aborted) {
5076
- let state;
5077
- try {
5078
- state = await readSwitchStatus(stateDirectory, deadline);
4609
+ await waitForDaemonExit(stateDirectory, deadline);
4610
+ return response;
5079
4611
  } catch (cause) {
5080
- if (isNodeError(cause, "ENOENT") || isNodeError(cause, "ECONNREFUSED")) {
5081
- throw cause;
5082
- }
5083
4612
  deadline.throwIfAborted();
5084
- state = null;
5085
- }
5086
- if (state !== null && state.id === id && state.kind !== "switching") {
5087
- outcome = state;
5088
- break;
5089
- }
5090
- await setTimeout3(SWITCH_POLL_MILLISECONDS, void 0, { signal: deadline });
5091
- }
5092
- if (outcome === void 0) {
5093
- throw deadline.reason;
5094
- }
5095
- if (outcome.kind === "rolled-back") {
5096
- deadline.throwIfAborted();
5097
- await readSwitchStatus(stateDirectory, deadline, true).catch(() => void 0);
5098
- }
5099
- return outcome;
5100
- }
5101
- async function reserveWhenQuiet(activity, signal) {
5102
- for (; ; ) {
5103
- await activity.whenIdle(signal);
5104
- for await (const _tick of setInterval(UPGRADE_QUIET_CHECK_MILLISECONDS, void 0, { signal })) {
5105
- const reservation = activity.reserveIfQuiet(UPGRADE_QUIET_MILLISECONDS);
5106
- if (reservation !== null) {
5107
- return reservation;
4613
+ if (!isNodeError(cause, "ENOENT") && !isNodeError(cause, "ECONNREFUSED")) {
4614
+ throw cause;
5108
4615
  }
5109
- if (activity.count() > 0) {
5110
- break;
4616
+ if (!await daemonLockExists(stateDirectory)) {
4617
+ return { kind: "stopped" };
5111
4618
  }
4619
+ await delay2(50, void 0, { signal: deadline });
5112
4620
  }
5113
- signal.throwIfAborted();
5114
- }
5115
- }
5116
- async function checkDaemonReleaseUpgrade(signal, activity) {
5117
- var _stack = [];
5118
- try {
5119
- const control = await openMobiusControlInstallation(signal, process.env);
5120
- const transition2 = await readSwitchStatus(control.stateDirectory, signal);
5121
- if (transition2?.kind === "switching" || transition2?.kind === "rolled-back" && transition2.retryAfter > Date.now()) {
5122
- return;
5123
- }
5124
- const version = await findNewDaemonReleaseVersion(package_default.version, signal);
5125
- if (version === null) {
5126
- return;
5127
- }
5128
- const launch = await installDaemonReleaseVersion(version, signal);
5129
- let reservation = activity.reserveIfQuiet(UPGRADE_QUIET_MILLISECONDS);
5130
- if (reservation === null) {
5131
- const diagnostics = new RuntimeDiagnosticLog(control.stateDirectory);
5132
- const record = (event, details = {}) => diagnostics.record(
5133
- `Mobius daemon diagnostic ${JSON.stringify({ timestamp: (/* @__PURE__ */ new Date()).toISOString(), event, version, ...details })}`
5134
- );
5135
- record("daemon_upgrade_deferred", { activeWork: activity.count(), ...activity.describe() });
5136
- reservation = await reserveWhenQuiet(activity, signal);
5137
- record("daemon_upgrade_released");
5138
- }
5139
- const _reservation = __using(_stack, reservation);
5140
- signal.throwIfAborted();
5141
- await requestInstalledDaemonReleaseSwitch(control.stateDirectory, launch, signal);
5142
- await new Promise((resolve6) => {
5143
- if (signal.aborted) {
5144
- resolve6();
5145
- } else {
5146
- signal.addEventListener("abort", () => resolve6(), { once: true });
5147
- }
5148
- });
5149
- } catch (_) {
5150
- var _error = _, _hasError = true;
5151
- } finally {
5152
- __callDispose(_stack, _error, _hasError);
5153
4621
  }
5154
4622
  }
5155
4623
 
5156
- // src/daemon/lifecycle/worker.ts
5157
- var WorkerRuntimeError = class extends Error {
5158
- code = "worker_runtime_failed";
5159
- constructor(connectionId, cause) {
5160
- super(`Runtime ${connectionId} failed`, { cause });
5161
- }
5162
- };
5163
- async function reloadDaemon(stateDirectory, signal) {
5164
- try {
5165
- const response = await sendLocalRequest(stateDirectory, "worker", { kind: "reload" }, signal);
5166
- if (typeof response !== "object" || response === null || !("kind" in response) || response.kind !== "reloaded") {
5167
- throw new Error("Saved configuration could not be applied to the running daemon");
5168
- }
5169
- } catch (cause) {
5170
- if (!isNodeError(cause, "ENOENT") && !isNodeError(cause, "ECONNREFUSED")) {
5171
- throw cause;
5172
- }
5173
- }
5174
- }
5175
- async function releaseIdleAgentProcesses(stateDirectory, signal) {
5176
- try {
5177
- const response = await sendLocalRequest(stateDirectory, "worker", { kind: "release" }, signal);
5178
- if (typeof response === "object" && response !== null && "kind" in response && response.kind === "released") {
5179
- return;
5180
- }
5181
- throw new Error("The running daemon cannot release Agent sessions. Run `mobius start` to update it first.");
5182
- } catch (cause) {
5183
- if (!isNodeError(cause, "ENOENT") && !isNodeError(cause, "ECONNREFUSED")) {
5184
- throw cause;
5185
- }
5186
- }
5187
- }
5188
- async function runWorker(version, signal) {
5189
- if (!process.connected) {
5190
- throw new Error("The daemon must be started by its manager");
5191
- }
5192
- const rejectionGuard = surviveUnhandledRejections("worker");
5193
- const shutdown = new AbortController();
5194
- const combined = AbortSignal.any([signal, shutdown.signal]);
5195
- const disconnect = () => shutdown.abort();
5196
- process.once("disconnect", disconnect);
5197
- try {
5198
- await runWorkerRuntime(version, combined, shutdown, rejectionGuard);
5199
- } finally {
5200
- shutdown.abort();
5201
- process.off("disconnect", disconnect);
5202
- rejectionGuard.release();
5203
- if (process.connected) {
5204
- process.disconnect();
5205
- }
5206
- }
5207
- }
5208
- async function runWorkerRuntime(version, combined, shutdown, rejectionGuard) {
5209
- const control = await openMobiusControlInstallation(combined, process.env);
5210
- await prepareVersionData(version, combined);
5211
- const engineDataDir = await prepareMobiusEngineDataDir(version, combined, process.env);
5212
- const installation = await RuntimeInstallationStore.open(engineDataDir, combined);
5213
- const deadline = AbortSignal.any([combined, AbortSignal.timeout(15e3)]);
5214
- let release = null;
5215
- while (release === null) {
5216
- deadline.throwIfAborted();
5217
- release = await acquireManagerLock(control.stateDirectory, "worker.lock");
5218
- if (release === null) {
5219
- await setTimeout4(25, void 0, { signal: deadline });
5220
- }
5221
- }
5222
- rejectionGuard.bindStateDirectory(control.stateDirectory);
5223
- const turnActivity = new RuntimeTurnActivity();
5224
- const agentProcesses = new RuntimeAgentProcesses();
5225
- const active = /* @__PURE__ */ new Map();
5226
- const fail = (connectionId, cause) => {
5227
- console.error(`Runtime ${connectionId} failed; stopping the daemon`, cause);
5228
- shutdown.abort(new WorkerRuntimeError(connectionId, cause));
5229
- };
5230
- const reconcile = async () => {
5231
- const connections = await installation.listConnections(combined);
5232
- const enabled = new Set(
5233
- connections.filter((connection) => connection.activation === "active").map((connection) => connection.connectionId)
5234
- );
5235
- for (const [id, running] of active) {
5236
- if (enabled.has(id)) {
5237
- continue;
5238
- }
5239
- running.controller.abort();
5240
- await running.completion;
5241
- active.delete(id);
5242
- }
5243
- for (const connection of connections) {
5244
- if (!enabled.has(connection.connectionId) || active.has(connection.connectionId)) {
5245
- continue;
5246
- }
5247
- try {
5248
- const config = await prepareRuntimeConnection(
5249
- installation,
5250
- connection,
5251
- combined,
5252
- process.env,
5253
- resolveMaxAcpProcesses(process.env)
5254
- );
5255
- const controller = new AbortController();
5256
- const loop = new RuntimeLoop(async () => config, {
5257
- processLogsDirectory: control.stateDirectory,
5258
- turnActivity,
5259
- agentProcesses,
5260
- reportUpgradeAvailability: reportDaemonUpgradeAvailability
5261
- });
5262
- const completion = loop.run(AbortSignal.any([combined, controller.signal])).then(
5263
- () => {
5264
- if (!combined.aborted && !controller.signal.aborted) {
5265
- fail(connection.connectionId, new Error("The Runtime stopped unexpectedly"));
5266
- }
5267
- },
5268
- (cause) => {
5269
- if (!combined.aborted && !controller.signal.aborted) {
5270
- fail(connection.connectionId, cause);
5271
- }
5272
- }
5273
- ).finally(() => active.delete(connection.connectionId));
5274
- active.set(connection.connectionId, { config, controller, completion });
5275
- } catch (cause) {
5276
- combined.throwIfAborted();
5277
- fail(connection.connectionId, cause);
5278
- throw cause;
5279
- }
5280
- }
5281
- };
5282
- const refreshRunningCatalogs = async (connectionIds) => {
5283
- await Promise.all(
5284
- [...connectionIds].map(async (connectionId) => {
5285
- const config = active.get(connectionId)?.config;
5286
- if (config?.rediscoverAgents === void 0) {
5287
- return;
5288
- }
5289
- try {
5290
- await refreshAgentCatalog(
5291
- config,
5292
- config.rediscoverAgents,
5293
- AbortSignal.any([combined, AbortSignal.timeout(9e4)])
5294
- );
5295
- } catch (cause) {
5296
- combined.throwIfAborted();
5297
- console.error(`Runtime ${connectionId} could not refresh its Agent catalog`, cause);
5298
- }
5299
- })
5300
- );
5301
- };
5302
- let reload = Promise.resolve();
5303
- let upgrades = Promise.resolve();
5304
- try {
5305
- await reconcile();
5306
- await serveLocalControl(
5307
- control.stateDirectory,
5308
- "worker",
5309
- combined,
5310
- async (request) => {
5311
- if (typeof request === "object" && request !== null && "kind" in request && request.kind === "release") {
5312
- await agentProcesses.retireIdleProcesses();
5313
- return { kind: "released" };
5314
- }
5315
- if (typeof request !== "object" || request === null || !("kind" in request) || request.kind !== "reload") {
5316
- return { kind: "error", code: "invalid_reload", message: "Expected a configuration reload" };
5317
- }
5318
- const running = new Set(active.keys());
5319
- const pending = reload.then(reconcile).then(async () => await refreshRunningCatalogs(running));
5320
- reload = pending.catch(() => void 0);
5321
- await pending;
5322
- return { kind: "reloaded" };
5323
- },
5324
- async () => {
5325
- combined.throwIfAborted();
5326
- await recordCurrentData(version, combined);
5327
- upgrades = runDaemonReleaseUpgrades(combined, turnActivity);
5328
- if (process.connected) {
5329
- process.send?.({ kind: "worker-ready", version }, () => void 0);
5330
- }
5331
- }
5332
- );
5333
- if (shutdown.signal.reason instanceof WorkerRuntimeError) {
5334
- throw shutdown.signal.reason;
5335
- }
5336
- } finally {
5337
- shutdown.abort();
5338
- await upgrades;
5339
- await reload;
5340
- await Promise.all([...active.values()].map((running) => running.completion));
5341
- await release();
5342
- }
4624
+ // src/cli/handler/daemon/quit.ts
4625
+ function registerQuitCommand(program2, context) {
4626
+ program2.command("stop").alias("quit").description("stop the daemon and every active Runtime").action(async () => {
4627
+ const installation = await openMobiusControlInstallation(context.shutdownSignal, process.env);
4628
+ await stopDaemon(installation.stateDirectory, context.shutdownSignal);
4629
+ process.stdout.write("Mobius daemon stopped.\n");
4630
+ });
5343
4631
  }
5344
4632
 
5345
4633
  // src/cli/handler/daemon/release.ts
@@ -5355,7 +4643,7 @@ function registerReleaseCommand(program2, context) {
5355
4643
  function registerStartCommand(program2, context) {
5356
4644
  program2.command("start").description("ensure the daemon is running and optionally activate a saved connection").addOption(valueOption("--connection <connection-id>", "saved Runtime connection identifier")).action(async (options) => {
5357
4645
  const control = await openMobiusControlInstallation(context.shutdownSignal, process.env);
5358
- await ensureWorkingProcess(control.stateDirectory, context.version, context.shutdownSignal);
4646
+ await ensureDaemon(control.stateDirectory, context.version, context.shutdownSignal);
5359
4647
  const installation = await openMobiusCliInstallation(context.shutdownSignal, process.env);
5360
4648
  if (options.connection !== void 0) {
5361
4649
  await installation.setConnectionActivation(options.connection, "active", context.shutdownSignal);
@@ -5367,43 +4655,23 @@ function registerStartCommand(program2, context) {
5367
4655
 
5368
4656
  // src/cli/handler/daemon/upgrade.ts
5369
4657
  function registerUpgradeCommand(program2, context) {
5370
- program2.command("upgrade").description("start the daemon if needed and upgrade it to the newest npm release").action(async () => {
5371
- const signal = context.shutdownSignal;
5372
- const control = await openMobiusControlInstallation(signal, process.env);
5373
- const current = await readWorkingProcessVersion(control.stateDirectory, signal) ?? await ensureWorkingProcess(control.stateDirectory, context.version, signal);
5374
- await reloadDaemon(control.stateDirectory, signal);
5375
- const attempt = await requestDaemonReleaseUpgrade(control.stateDirectory, current, signal);
5376
- if (attempt.kind === "current") {
5377
- process.stdout.write(`Mobius daemon ${current} is already the newest release.
5378
- `);
5379
- return;
5380
- }
5381
- process.stdout.write(`Upgrading Mobius daemon to ${attempt.version}...
5382
- `);
5383
- const outcome = await awaitDaemonReleaseSwitch(control.stateDirectory, attempt.id, signal);
5384
- if (outcome.kind === "switched") {
5385
- process.stdout.write(`Mobius daemon upgraded to ${attempt.version}.
4658
+ program2.command("restart").alias("upgrade").description("restart the daemon using the latest release").action(async () => {
4659
+ const control = await openMobiusControlInstallation(context.shutdownSignal, process.env);
4660
+ await stopDaemon(control.stateDirectory, context.shutdownSignal);
4661
+ const version = await ensureDaemon(control.stateDirectory, context.version, context.shutdownSignal);
4662
+ process.stdout.write(`Mobius daemon ${version} restarted.
5386
4663
  `);
5387
- return;
5388
- }
5389
- throw new DaemonReleaseUpgradeError(
5390
- outcome.code,
5391
- `The upgrade to ${attempt.version} failed and ${outcome.previous.version} was restored: ${outcome.message}`
5392
- );
5393
- });
5394
- }
5395
-
5396
- // src/cli/handler/daemon/worker.ts
5397
- function registerWorkerCommand(program2, context) {
5398
- program2.command("__worker", { hidden: true }).action(async () => {
5399
- await runWorker(context.version, context.shutdownSignal);
5400
4664
  });
5401
4665
  }
5402
4666
 
5403
4667
  // src/cli/handler/daemon/index.ts
5404
4668
  function registerInternalRuntimeHandlers(program2, context) {
5405
- registerManagerCommand(program2, context);
5406
- registerWorkerCommand(program2, context);
4669
+ program2.command("__daemon", { hidden: true }).action(async () => {
4670
+ await runDaemon(context.version, context.shutdownSignal);
4671
+ });
4672
+ program2.command("__worker", { hidden: true }).action(async () => {
4673
+ await runDaemon(context.version, context.shutdownSignal, "legacy-manager");
4674
+ });
5407
4675
  }
5408
4676
  function registerDaemonProcessLifecycleHandlers(program2, context) {
5409
4677
  registerStartCommand(program2, context);
@@ -5414,11 +4682,11 @@ function registerDaemonProcessLifecycleHandlers(program2, context) {
5414
4682
 
5415
4683
  // src/cli/handler/diagnose/index.ts
5416
4684
  import { fork } from "node:child_process";
5417
- import { join as join13, resolve as resolve3 } from "node:path";
4685
+ import { join as join11, resolve as resolve3 } from "node:path";
5418
4686
 
5419
4687
  // src/storage/diagnostics/relay-probe-files.ts
5420
- import { mkdir, readFile as readFile6, rm as rm2, stat as stat2, writeFile as writeFile4 } from "node:fs/promises";
5421
- import { join as join11 } from "node:path";
4688
+ import { mkdir, readFile as readFile5, rm as rm2, stat as stat2, writeFile as writeFile4 } from "node:fs/promises";
4689
+ import { join as join9 } from "node:path";
5422
4690
  var ProbeConfigurationFileError = class extends Error {
5423
4691
  code = "probe_config_invalid";
5424
4692
  };
@@ -5431,7 +4699,7 @@ async function readPrivateProbeConfiguration(path2, signal) {
5431
4699
  );
5432
4700
  }
5433
4701
  try {
5434
- return JSON.parse(await readFile6(path2, { encoding: "utf8", signal }));
4702
+ return JSON.parse(await readFile5(path2, { encoding: "utf8", signal }));
5435
4703
  } catch {
5436
4704
  signal.throwIfAborted();
5437
4705
  throw new ProbeConfigurationFileError("The pairing configuration is not valid JSON");
@@ -5444,16 +4712,16 @@ async function ensureProbeDirectory(directory) {
5444
4712
  await mkdir(directory, { recursive: true, mode: 448 });
5445
4713
  }
5446
4714
  async function writeProbeReport(directory, report) {
5447
- await writeFile4(join11(directory, "report.json"), `${JSON.stringify(report, null, 2)}
4715
+ await writeFile4(join9(directory, "report.json"), `${JSON.stringify(report, null, 2)}
5448
4716
  `, { mode: 384 });
5449
4717
  }
5450
4718
  async function writeProbeEvents(directory, events) {
5451
- await writeFile4(join11(directory, "transport.jsonl"), events.map((event) => JSON.stringify(event)).join("\n"), {
4719
+ await writeFile4(join9(directory, "transport.jsonl"), events.map((event) => JSON.stringify(event)).join("\n"), {
5452
4720
  mode: 384
5453
4721
  });
5454
4722
  }
5455
4723
  async function removeProbeState(directory) {
5456
- await rm2(join11(directory, "state"), { recursive: true, force: true });
4724
+ await rm2(join9(directory, "state"), { recursive: true, force: true });
5457
4725
  }
5458
4726
 
5459
4727
  // src/cli/handler/diagnose/config.ts
@@ -5599,7 +4867,7 @@ async function collectRouteEvidence(hostname, cancellation) {
5599
4867
  }
5600
4868
 
5601
4869
  // src/cli/handler/diagnose/worker.ts
5602
- import { join as join12 } from "node:path";
4870
+ import { join as join10 } from "node:path";
5603
4871
  async function runRelayProbeWorker(configPath, role, directory, signal) {
5604
4872
  if (!process.send) {
5605
4873
  throw new Error("The relay probe worker requires its coordinator");
@@ -5643,11 +4911,11 @@ async function runRelayProbeWorker(configPath, role, directory, signal) {
5643
4911
  collectRouteEvidence(origin.hostname, stopped),
5644
4912
  ...proxy === null ? [] : [collectRouteEvidence(proxy.hostname, stopped)]
5645
4913
  ]);
5646
- const workspace = join12(directory, "workspace");
4914
+ const workspace = join10(directory, "workspace");
5647
4915
  await ensureProbeDirectory(workspace);
5648
4916
  const engine = new MobiusRuntimeEngine({
5649
- engineDataDir: join12(directory, "state"),
5650
- agentLaunchEnvironment: { HOME: join12(directory, "home"), PATH: "" }
4917
+ engineDataDir: join10(directory, "state"),
4918
+ agentLaunchEnvironment: { HOME: join10(directory, "home"), PATH: "" }
5651
4919
  });
5652
4920
  const pairing2 = config[role];
5653
4921
  const connection = await engine.pair(
@@ -5739,9 +5007,9 @@ var ProbeLeg = class {
5739
5007
  throw new RelayProbeError("probe_entrypoint_missing", "Cannot locate the Mobius CLI entrypoint");
5740
5008
  }
5741
5009
  const environment = {
5742
- HOME: join13(directory, "home"),
5010
+ HOME: join11(directory, "home"),
5743
5011
  PATH: process.env["PATH"] ?? "",
5744
- MOBIUS_STATE_DIRECTORY: join13(directory, "state")
5012
+ MOBIUS_STATE_DIRECTORY: join11(directory, "state")
5745
5013
  };
5746
5014
  for (const name of [
5747
5015
  "SystemRoot",
@@ -5884,8 +5152,8 @@ async function compare(configPath, output, duration, context) {
5884
5152
  try {
5885
5153
  for (const role of ["direct", "proxy"]) {
5886
5154
  context.shutdownSignal.throwIfAborted();
5887
- const legDirectory = join13(directory, role);
5888
- await ensureProbeDirectory(join13(legDirectory, "home"));
5155
+ const legDirectory = join11(directory, role);
5156
+ await ensureProbeDirectory(join11(legDirectory, "home"));
5889
5157
  legs.push(new ProbeLeg(role, resolve3(configPath), legDirectory, proxy));
5890
5158
  }
5891
5159
  const cancelled = Promise.withResolvers();
@@ -5969,11 +5237,11 @@ async function compare(configPath, output, duration, context) {
5969
5237
  legs: reports
5970
5238
  };
5971
5239
  await writeProbeReport(directory, report);
5972
- process.stdout.write(`${join13(directory, "report.json")}
5240
+ process.stdout.write(`${join11(directory, "report.json")}
5973
5241
  `);
5974
5242
  } finally {
5975
5243
  clearTimeout(force);
5976
- await Promise.all(legs.map((leg) => removeProbeState(join13(directory, leg.role))));
5244
+ await Promise.all(legs.map((leg) => removeProbeState(join11(directory, leg.role))));
5977
5245
  }
5978
5246
  }
5979
5247
  }
@@ -5998,11 +5266,11 @@ function registerDiagnoseCommand(program2, context) {
5998
5266
  }
5999
5267
 
6000
5268
  // src/cli/handler/logs/logic/collect-local-logs.ts
6001
- import { join as join15, relative as relative3, resolve as resolve4 } from "node:path";
5269
+ import { join as join13, relative as relative2, resolve as resolve4 } from "node:path";
6002
5270
 
6003
5271
  // src/storage/logs/log-files.ts
6004
- import { lstat as lstat2, readdir, readFile as readFile7 } from "node:fs/promises";
6005
- import { join as join14, relative as relative2 } from "node:path";
5272
+ import { lstat as lstat2, readdir, readFile as readFile6 } from "node:fs/promises";
5273
+ import { join as join12, relative } from "node:path";
6006
5274
  var LOG_FILE_NAME_PATTERN = /^[\w.-]{1,128}\.log$/u;
6007
5275
  var INSTALLATIONS_DIRECTORY = "installations";
6008
5276
  var VERSIONS_DIRECTORY = "versions";
@@ -6010,22 +5278,22 @@ function isAbsent(cause) {
6010
5278
  return typeof cause === "object" && cause !== null && Reflect.get(cause, "code") === "ENOENT";
6011
5279
  }
6012
5280
  async function findLogDirectories(root, unreadable, signal) {
6013
- const versions = join14(root, VERSIONS_DIRECTORY);
6014
- const installationsRoots = [join14(root, INSTALLATIONS_DIRECTORY)];
5281
+ const versions = join12(root, VERSIONS_DIRECTORY);
5282
+ const installationsRoots = [join12(root, INSTALLATIONS_DIRECTORY)];
6015
5283
  for (const version of await listDirectories(versions, root, unreadable, signal)) {
6016
- installationsRoots.push(join14(versions, version, INSTALLATIONS_DIRECTORY));
5284
+ installationsRoots.push(join12(versions, version, INSTALLATIONS_DIRECTORY));
6017
5285
  }
6018
5286
  const found = [];
6019
5287
  for (const installations of installationsRoots) {
6020
5288
  for (const dataDirectory of await listDirectories(installations, root, unreadable, signal)) {
6021
- const application = join14(installations, dataDirectory);
5289
+ const application = join12(installations, dataDirectory);
6022
5290
  for (const installationId of await listDirectories(application, root, unreadable, signal)) {
6023
- const logs = join14(application, installationId, "logs");
5291
+ const logs = join12(application, installationId, "logs");
6024
5292
  const status = await directoryStatus(logs);
6025
5293
  if (status === "listed") {
6026
5294
  found.push(logs);
6027
5295
  } else if (status === "unreadable") {
6028
- unreadable.push(relative2(root, logs));
5296
+ unreadable.push(relative(root, logs));
6029
5297
  }
6030
5298
  }
6031
5299
  }
@@ -6039,7 +5307,7 @@ async function listDirectories(path2, root, unreadable, signal) {
6039
5307
  return entries.filter((entry) => entry.isDirectory() && !entry.isSymbolicLink()).map((entry) => entry.name);
6040
5308
  } catch (cause) {
6041
5309
  if (!isAbsent(cause)) {
6042
- unreadable.push(relative2(root, path2));
5310
+ unreadable.push(relative(root, path2));
6043
5311
  }
6044
5312
  return [];
6045
5313
  }
@@ -6050,7 +5318,7 @@ async function listLogFiles(directory, root, unreadable) {
6050
5318
  return entries.filter((entry) => entry.isFile() && !entry.isSymbolicLink() && LOG_FILE_NAME_PATTERN.test(entry.name)).map((entry) => entry.name).sort();
6051
5319
  } catch (cause) {
6052
5320
  if (!isAbsent(cause)) {
6053
- unreadable.push(relative2(root, directory));
5321
+ unreadable.push(relative(root, directory));
6054
5322
  }
6055
5323
  return [];
6056
5324
  }
@@ -6078,7 +5346,7 @@ async function readOwnedLogFile(path2, signal) {
6078
5346
  return { kind: "skipped" };
6079
5347
  }
6080
5348
  try {
6081
- return { kind: "read", content: await readFile7(path2, { encoding: "utf8", signal }) };
5349
+ return { kind: "read", content: await readFile6(path2, { encoding: "utf8", signal }) };
6082
5350
  } catch (cause) {
6083
5351
  if (signal.aborted) {
6084
5352
  throw cause;
@@ -6100,8 +5368,8 @@ async function collectLocalDaemonLogs(stateDirectory, tailLines, signal) {
6100
5368
  for (const directory of directories) {
6101
5369
  const names = await listLogFiles(directory, root, unreadable);
6102
5370
  for (const name of names) {
6103
- const path2 = join15(directory, name);
6104
- const reported = relative3(root, path2);
5371
+ const path2 = join13(directory, name);
5372
+ const reported = relative2(root, path2);
6105
5373
  const read = await readOwnedLogFile(path2, signal);
6106
5374
  if (read.kind === "unreadable") {
6107
5375
  unreadable.push(reported);
@@ -6282,7 +5550,7 @@ function registerPairAction(command, context) {
6282
5550
  command.addOption(valueOption("--url <relay-url>", "relay HTTP or HTTPS URL")).addOption(valueOption("--code <pairing-code>", "pairing code shown in the browser")).addOption(valueOption("--pairing <runtime-pairing-id>", "Runtime pairing identifier")).addOption(valueOption("--workspace <directory>", "default workspace directory")).action(async (options) => {
6283
5551
  const requested = requirePairOptions(options, command);
6284
5552
  const control = await openMobiusControlInstallation(context.shutdownSignal, process.env);
6285
- await ensureWorkingProcess(control.stateDirectory, context.version, context.shutdownSignal);
5553
+ await ensureDaemon(control.stateDirectory, context.version, context.shutdownSignal);
6286
5554
  const installation = await openMobiusCliInstallation(context.shutdownSignal, process.env);
6287
5555
  process.stdout.write(`mobius version: ${context.version}
6288
5556
  `);
@@ -6340,13 +5608,14 @@ function registerPairHandlers(program2, context) {
6340
5608
  // src/cli/handler/version/index.ts
6341
5609
  async function reportInstallationVersion(context) {
6342
5610
  const control = await openMobiusControlInstallation(context.shutdownSignal, process.env);
6343
- const daemonVersion = await readWorkingProcessVersion(control.stateDirectory, context.shutdownSignal);
5611
+ const daemonVersion = await readDaemonVersion(control.stateDirectory, context.shutdownSignal);
6344
5612
  process.stdout.write(`CLI: ${context.version}
6345
5613
  `);
6346
5614
  process.stdout.write(`Daemon: ${daemonVersion ?? "not running"}
6347
5615
  `);
6348
5616
  }
6349
5617
  function registerVersionHandler(program2, context) {
5618
+ program2.command("status").description("show CLI and daemon versions").action(async () => await reportInstallationVersion(context));
6350
5619
  program2.option("-v, --version", "print the CLI and running daemon versions");
6351
5620
  program2.action(() => {
6352
5621
  if (program2.opts()["version"]) {
@@ -6406,4 +5675,4 @@ async function runCLI() {
6406
5675
  export {
6407
5676
  runCLI
6408
5677
  };
6409
- //# sourceMappingURL=cli-NLIGRBEL.js.map
5678
+ //# sourceMappingURL=cli-O57WJIFN.js.map