@moxt-ai/mobius 0.0.386 → 0.0.387
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/{chunk-FVYSQLQL.js → chunk-A62FEGIN.js} +11 -24
- package/dist/{chunk-FVYSQLQL.js.map → chunk-A62FEGIN.js.map} +3 -3
- package/dist/{chunk-Z6JG65YI.js → chunk-S7PKNDIS.js} +2 -2
- package/dist/{chunk-Z6JG65YI.js.map → chunk-S7PKNDIS.js.map} +1 -1
- package/dist/cli/handler/daemon/index.d.ts.map +1 -1
- package/dist/cli/handler/daemon/index.js +9 -4
- package/dist/cli/handler/daemon/index.js.map +1 -1
- package/dist/cli/handler/daemon/quit.d.ts.map +1 -1
- package/dist/cli/handler/daemon/quit.js +5 -4
- package/dist/cli/handler/daemon/quit.js.map +1 -1
- package/dist/cli/handler/daemon/release.js +1 -1
- package/dist/cli/handler/daemon/start.d.ts.map +1 -1
- package/dist/cli/handler/daemon/start.js +3 -3
- package/dist/cli/handler/daemon/start.js.map +1 -1
- package/dist/cli/handler/daemon/upgrade.d.ts.map +1 -1
- package/dist/cli/handler/daemon/upgrade.js +8 -24
- package/dist/cli/handler/daemon/upgrade.js.map +1 -1
- package/dist/cli/handler/pair/pair.js +3 -3
- package/dist/cli/handler/pair/pair.js.map +1 -1
- package/dist/cli/handler/pair/remove.js +1 -1
- package/dist/cli/handler/pair/stop.js +1 -1
- package/dist/cli/handler/version/index.d.ts.map +1 -1
- package/dist/cli/handler/version/index.js +6 -2
- package/dist/cli/handler/version/index.js.map +1 -1
- package/dist/{cli-NLIGRBEL.js → cli-43LPJ5ID.js} +412 -1143
- package/dist/cli-43LPJ5ID.js.map +7 -0
- package/dist/cli.js +1998 -6
- package/dist/cli.js.map +4 -4
- package/dist/daemon/control/protocol.d.ts +1 -133
- package/dist/daemon/control/protocol.d.ts.map +1 -1
- package/dist/daemon/control/protocol.js +4 -63
- package/dist/daemon/control/protocol.js.map +1 -1
- package/dist/daemon/lifecycle/daemon-client.d.ts +8 -0
- package/dist/daemon/lifecycle/daemon-client.d.ts.map +1 -0
- package/dist/daemon/lifecycle/daemon-client.js +154 -0
- package/dist/daemon/lifecycle/daemon-client.js.map +1 -0
- package/dist/daemon/lifecycle/{worker.d.ts → daemon.d.ts} +2 -2
- package/dist/daemon/lifecycle/daemon.d.ts.map +1 -0
- package/dist/daemon/lifecycle/daemon.js +266 -0
- package/dist/daemon/lifecycle/daemon.js.map +1 -0
- package/dist/daemon/lifecycle/unhandled-rejection.d.ts +1 -2
- package/dist/daemon/lifecycle/unhandled-rejection.d.ts.map +1 -1
- package/dist/daemon/lifecycle/unhandled-rejection.js +4 -7
- package/dist/daemon/lifecycle/unhandled-rejection.js.map +1 -1
- package/dist/daemon/upgrades/data.d.ts +0 -1
- package/dist/daemon/upgrades/data.d.ts.map +1 -1
- package/dist/daemon/upgrades/data.js +0 -5
- package/dist/daemon/upgrades/data.js.map +1 -1
- package/dist/engine/util/process/platform/windows.d.ts.map +1 -1
- package/dist/engine/util/process/platform/windows.js +7 -4
- package/dist/engine/util/process/platform/windows.js.map +1 -1
- package/dist/engine.js +2 -2
- package/dist/storage/daemon/daemon-lock.d.ts +9 -0
- package/dist/storage/daemon/daemon-lock.d.ts.map +1 -0
- package/dist/storage/daemon/{manager-lock.js → daemon-lock.js} +22 -7
- package/dist/storage/daemon/daemon-lock.js.map +1 -0
- package/dist/storage/state-root-layout.d.ts +0 -2
- package/dist/storage/state-root-layout.d.ts.map +1 -1
- package/dist/storage/state-root-layout.js +0 -2
- package/dist/storage/state-root-layout.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-T2GPD4RK.js +0 -2002
- package/dist/chunk-T2GPD4RK.js.map +0 -7
- package/dist/cli/handler/daemon/manager.d.ts +0 -4
- package/dist/cli/handler/daemon/manager.d.ts.map +0 -1
- package/dist/cli/handler/daemon/manager.js +0 -9
- package/dist/cli/handler/daemon/manager.js.map +0 -1
- package/dist/cli/handler/daemon/worker.d.ts +0 -4
- package/dist/cli/handler/daemon/worker.d.ts.map +0 -1
- package/dist/cli/handler/daemon/worker.js +0 -7
- package/dist/cli/handler/daemon/worker.js.map +0 -1
- package/dist/cli-NLIGRBEL.js.map +0 -7
- package/dist/daemon/control/switch-status.d.ts +0 -4
- package/dist/daemon/control/switch-status.d.ts.map +0 -1
- package/dist/daemon/control/switch-status.js +0 -10
- package/dist/daemon/control/switch-status.js.map +0 -1
- package/dist/daemon/lifecycle/manager-client.d.ts +0 -8
- package/dist/daemon/lifecycle/manager-client.d.ts.map +0 -1
- package/dist/daemon/lifecycle/manager-client.js +0 -130
- package/dist/daemon/lifecycle/manager-client.js.map +0 -1
- package/dist/daemon/lifecycle/manager.d.ts +0 -3
- package/dist/daemon/lifecycle/manager.d.ts.map +0 -1
- package/dist/daemon/lifecycle/manager.js +0 -215
- package/dist/daemon/lifecycle/manager.js.map +0 -1
- package/dist/daemon/lifecycle/worker-lifecycle-log.d.ts +0 -14
- package/dist/daemon/lifecycle/worker-lifecycle-log.d.ts.map +0 -1
- package/dist/daemon/lifecycle/worker-lifecycle-log.js +0 -63
- package/dist/daemon/lifecycle/worker-lifecycle-log.js.map +0 -1
- package/dist/daemon/lifecycle/worker-process.d.ts +0 -26
- package/dist/daemon/lifecycle/worker-process.d.ts.map +0 -1
- package/dist/daemon/lifecycle/worker-process.js +0 -145
- package/dist/daemon/lifecycle/worker-process.js.map +0 -1
- package/dist/daemon/lifecycle/worker.d.ts.map +0 -1
- package/dist/daemon/lifecycle/worker.js +0 -213
- package/dist/daemon/lifecycle/worker.js.map +0 -1
- package/dist/daemon/upgrades/package.d.ts +0 -9
- package/dist/daemon/upgrades/package.d.ts.map +0 -1
- package/dist/daemon/upgrades/package.js +0 -157
- package/dist/daemon/upgrades/package.js.map +0 -1
- package/dist/daemon/upgrades/upgrade.d.ts +0 -21
- package/dist/daemon/upgrades/upgrade.d.ts.map +0 -1
- package/dist/daemon/upgrades/upgrade.js +0 -242
- package/dist/daemon/upgrades/upgrade.js.map +0 -1
- package/dist/storage/daemon/manager-lock.d.ts +0 -9
- package/dist/storage/daemon/manager-lock.d.ts.map +0 -1
- package/dist/storage/daemon/manager-lock.js.map +0 -1
- package/dist/storage/daemon/release-files.d.ts +0 -9
- package/dist/storage/daemon/release-files.d.ts.map +0 -1
- package/dist/storage/daemon/release-files.js +0 -14
- 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-
|
|
29
|
+
} from "./chunk-A62FEGIN.js";
|
|
40
30
|
import {
|
|
41
|
-
__callDispose,
|
|
42
|
-
__toESM,
|
|
43
|
-
__using,
|
|
44
31
|
package_default
|
|
45
|
-
} from "./chunk-
|
|
32
|
+
} from "./chunk-S7PKNDIS.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/
|
|
3886
|
-
import { setTimeout as
|
|
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/
|
|
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
|
|
3892
|
-
const path2 = join7(stateDirectory,
|
|
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
|
|
3919
|
+
async function daemonLockExists(stateDirectory, filename = "manager.lock") {
|
|
3920
3920
|
try {
|
|
3921
|
-
await readFile3(join7(stateDirectory,
|
|
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-
|
|
3932
|
-
import {
|
|
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(
|
|
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(
|
|
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/
|
|
4252
|
-
var
|
|
4253
|
-
|
|
4254
|
-
|
|
4255
|
-
|
|
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
|
-
|
|
4311
|
-
|
|
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
|
-
|
|
4325
|
-
|
|
4326
|
-
|
|
4327
|
-
|
|
4328
|
-
|
|
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
|
-
|
|
4425
|
-
|
|
4426
|
-
|
|
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
|
-
|
|
4435
|
-
|
|
4436
|
-
|
|
4437
|
-
|
|
4438
|
-
if (
|
|
4439
|
-
|
|
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
|
-
|
|
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
|
-
|
|
4460
|
-
if (
|
|
4461
|
-
|
|
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
|
-
|
|
4472
|
-
|
|
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(
|
|
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
|
|
4482
|
-
|
|
4483
|
-
|
|
4484
|
-
|
|
4485
|
-
|
|
4486
|
-
|
|
4487
|
-
|
|
4488
|
-
|
|
4489
|
-
|
|
4490
|
-
|
|
4491
|
-
|
|
4492
|
-
|
|
4493
|
-
|
|
4494
|
-
|
|
4495
|
-
|
|
4496
|
-
|
|
4497
|
-
|
|
4498
|
-
|
|
4499
|
-
|
|
4500
|
-
|
|
4501
|
-
|
|
4502
|
-
|
|
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
|
-
|
|
4515
|
-
|
|
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
|
-
|
|
4518
|
-
|
|
4519
|
-
|
|
4520
|
-
|
|
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
|
-
|
|
4523
|
-
|
|
4524
|
-
|
|
4525
|
-
|
|
4526
|
-
|
|
4527
|
-
|
|
4528
|
-
|
|
4529
|
-
|
|
4530
|
-
|
|
4531
|
-
|
|
4532
|
-
|
|
4533
|
-
|
|
4534
|
-
|
|
4535
|
-
|
|
4536
|
-
|
|
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
|
-
|
|
4541
|
-
|
|
4542
|
-
|
|
4543
|
-
|
|
4544
|
-
|
|
4545
|
-
|
|
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
|
-
|
|
4598
|
-
|
|
4599
|
-
|
|
4600
|
-
|
|
4601
|
-
|
|
4602
|
-
|
|
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
|
-
|
|
4606
|
-
|
|
4607
|
-
|
|
4608
|
-
|
|
4609
|
-
|
|
4610
|
-
|
|
4611
|
-
|
|
4612
|
-
return { kind: "
|
|
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
|
-
|
|
4615
|
-
|
|
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
|
-
|
|
4625
|
-
|
|
4626
|
-
|
|
4627
|
-
|
|
4628
|
-
|
|
4629
|
-
|
|
4630
|
-
|
|
4631
|
-
|
|
4632
|
-
|
|
4633
|
-
|
|
4634
|
-
|
|
4635
|
-
|
|
4636
|
-
|
|
4637
|
-
|
|
4638
|
-
|
|
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 (
|
|
4643
|
-
shutdown.
|
|
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
|
-
|
|
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/
|
|
4657
|
-
|
|
4658
|
-
|
|
4659
|
-
|
|
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/
|
|
4673
|
-
|
|
4674
|
-
|
|
4675
|
-
|
|
4676
|
-
|
|
4677
|
-
}
|
|
4678
|
-
|
|
4679
|
-
|
|
4680
|
-
|
|
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/
|
|
4684
|
-
|
|
4685
|
-
|
|
4686
|
-
|
|
4687
|
-
|
|
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
|
|
4843
|
-
process.stdout.write(`Starting mobius daemon ${
|
|
4490
|
+
async function launchDaemon(version) {
|
|
4491
|
+
process.stdout.write(`Starting mobius daemon ${version}
|
|
4844
4492
|
`);
|
|
4845
|
-
await
|
|
4493
|
+
return await spawnDaemon();
|
|
4846
4494
|
}
|
|
4847
|
-
async function
|
|
4495
|
+
async function waitForDaemonExit(stateDirectory, signal) {
|
|
4848
4496
|
const deadline = AbortSignal.any([signal, AbortSignal.timeout(3e4)]);
|
|
4849
4497
|
while (!deadline.aborted) {
|
|
4850
|
-
if (!await
|
|
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
|
|
4867
|
-
const
|
|
4868
|
-
|
|
4869
|
-
|
|
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
|
-
|
|
4879
|
-
|
|
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
|
|
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
|
|
4899
|
-
let
|
|
4537
|
+
async function waitForDaemon(stateDirectory, version, signal) {
|
|
4538
|
+
let daemonStarted = false;
|
|
4539
|
+
let child;
|
|
4900
4540
|
while (!signal.aborted) {
|
|
4901
4541
|
try {
|
|
4902
|
-
|
|
4542
|
+
let response = decodeKnownLifecycleResponse(
|
|
4903
4543
|
await sendLocalRequest(stateDirectory, "manager", { kind: "status" }, signal)
|
|
4904
4544
|
);
|
|
4905
|
-
if (response?.kind === "status") {
|
|
4906
|
-
|
|
4907
|
-
|
|
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
|
|
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 (!
|
|
4918
|
-
|
|
4919
|
-
|
|
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
|
|
4936
|
-
|
|
4937
|
-
|
|
4938
|
-
|
|
4939
|
-
|
|
4940
|
-
|
|
4941
|
-
|
|
4942
|
-
|
|
4943
|
-
|
|
4944
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 (
|
|
5110
|
-
|
|
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/
|
|
5157
|
-
|
|
5158
|
-
|
|
5159
|
-
|
|
5160
|
-
|
|
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
|
|
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("
|
|
5371
|
-
const
|
|
5372
|
-
|
|
5373
|
-
const
|
|
5374
|
-
|
|
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
|
-
|
|
5406
|
-
|
|
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
|
|
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
|
|
5421
|
-
import { join as
|
|
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
|
|
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(
|
|
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(
|
|
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(
|
|
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
|
|
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 =
|
|
4914
|
+
const workspace = join10(directory, "workspace");
|
|
5647
4915
|
await ensureProbeDirectory(workspace);
|
|
5648
4916
|
const engine = new MobiusRuntimeEngine({
|
|
5649
|
-
engineDataDir:
|
|
5650
|
-
agentLaunchEnvironment: { HOME:
|
|
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:
|
|
5010
|
+
HOME: join11(directory, "home"),
|
|
5743
5011
|
PATH: process.env["PATH"] ?? "",
|
|
5744
|
-
MOBIUS_STATE_DIRECTORY:
|
|
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 =
|
|
5888
|
-
await ensureProbeDirectory(
|
|
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(`${
|
|
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(
|
|
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
|
|
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
|
|
6005
|
-
import { join as
|
|
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 =
|
|
6014
|
-
const installationsRoots = [
|
|
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(
|
|
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 =
|
|
5289
|
+
const application = join12(installations, dataDirectory);
|
|
6022
5290
|
for (const installationId of await listDirectories(application, root, unreadable, signal)) {
|
|
6023
|
-
const 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(
|
|
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(
|
|
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(
|
|
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
|
|
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 =
|
|
6104
|
-
const reported =
|
|
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
|
|
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
|
|
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-
|
|
5678
|
+
//# sourceMappingURL=cli-43LPJ5ID.js.map
|