@themoltnet/agent-daemon 0.50.0 → 0.51.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +387 -7
- package/dist/main.js +1 -68
- package/package.json +8 -8
package/dist/cli.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AAGzD,wBAAsB,iBAAiB,CAAC,OAAO,EAAE;IAC/C,OAAO,EAAE,oBAAoB,CAAC;IAC9B,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;CACjB,GAAG,OAAO,CAAC,MAAM,CAAC,CA2ClB"}
|
package/dist/cli.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { a as cryptoService, defaultPiDaemonAdapter, i as compileExecutionPlan, n as createExecutionPlanSnapshot, r as parseCredentialRequirements, t as runtimeExecutionOffer } from "./pi.js";
|
|
3
3
|
import { assertRuntimeAdapterSupportsProfile } from "./runtime.js";
|
|
4
|
+
import { createRequire } from "node:module";
|
|
5
|
+
import { pino, transport } from "pino";
|
|
4
6
|
import { Type } from "typebox";
|
|
5
7
|
import "multiformats/cid";
|
|
6
8
|
import "multiformats/codecs/json";
|
|
@@ -16,7 +18,6 @@ import { constants, createReadStream, createWriteStream, existsSync, lstatSync,
|
|
|
16
18
|
import { AuthenticationError, MoltNetError, agentKeyKey, assertTrustedConfigApiUrl, createExecutorAttestor, deriveMcpUrl, formatSecretReferenceString, identitySeedKey, parseSecretReferenceString, readConfig, register, requireSecureCredentialApiUrl, resolveAgentKey, resolveEnvSecretReference, resolveIdentitySeed, resolveOAuth2ClientSecret } from "@themoltnet/sdk";
|
|
17
19
|
import { createHash, randomBytes, randomUUID, timingSafeEqual } from "node:crypto";
|
|
18
20
|
import { once } from "node:events";
|
|
19
|
-
import { pino, transport } from "pino";
|
|
20
21
|
import { metrics } from "@opentelemetry/api";
|
|
21
22
|
import { OTLPMetricExporter } from "@opentelemetry/exporter-metrics-otlp-proto";
|
|
22
23
|
import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-proto";
|
|
@@ -26,7 +27,7 @@ import { BatchSpanProcessor } from "@opentelemetry/sdk-trace-base";
|
|
|
26
27
|
import { NodeTracerProvider } from "@opentelemetry/sdk-trace-node";
|
|
27
28
|
import { ATTR_SERVICE_NAME, ATTR_SERVICE_VERSION } from "@opentelemetry/semantic-conventions";
|
|
28
29
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
29
|
-
import { mkdir, open, realpath, stat } from "node:fs/promises";
|
|
30
|
+
import { mkdir, open, readFile, realpath, stat, writeFile } from "node:fs/promises";
|
|
30
31
|
import { pipeline } from "node:stream/promises";
|
|
31
32
|
import cors from "@fastify/cors";
|
|
32
33
|
import helmet from "@fastify/helmet";
|
|
@@ -34,7 +35,8 @@ import { lock, lockSync } from "proper-lockfile";
|
|
|
34
35
|
import { ModelRuntime, readStoredCredential } from "@earendil-works/pi-coding-agent";
|
|
35
36
|
import { Transform, Writable } from "node:stream";
|
|
36
37
|
import { writePiConfig } from "@themoltnet/pi-runtime/pi-config";
|
|
37
|
-
import { homedir } from "node:os";
|
|
38
|
+
import { homedir, platform } from "node:os";
|
|
39
|
+
import { pathToFileURL } from "node:url";
|
|
38
40
|
import { StringDecoder } from "node:string_decoder";
|
|
39
41
|
import rateLimit from "@fastify/rate-limit";
|
|
40
42
|
import Fastify from "fastify";
|
|
@@ -3431,6 +3433,8 @@ Commands:
|
|
|
3431
3433
|
child processes. Binds 127.0.0.1 only.
|
|
3432
3434
|
sync-sessions
|
|
3433
3435
|
Repair durable runtime-session checkpoints from local slot files.
|
|
3436
|
+
update check
|
|
3437
|
+
Check the stable MoltNet agent release without reading credentials.
|
|
3434
3438
|
|
|
3435
3439
|
Run \`agent-daemon <command> --help\` for command-specific flags.
|
|
3436
3440
|
|
|
@@ -3798,6 +3802,12 @@ function loadAgentServerEnvConfig() {
|
|
|
3798
3802
|
function processEnvSnapshot() {
|
|
3799
3803
|
return process.env;
|
|
3800
3804
|
}
|
|
3805
|
+
function loadUpdateEnvConfig() {
|
|
3806
|
+
return {
|
|
3807
|
+
xdgCacheHome: process.env["XDG_CACHE_HOME"] ?? "",
|
|
3808
|
+
localAppData: process.env["LOCALAPPDATA"] ?? ""
|
|
3809
|
+
};
|
|
3810
|
+
}
|
|
3801
3811
|
//#endregion
|
|
3802
3812
|
//#region src/lib/abort-active-attempt.ts
|
|
3803
3813
|
/** Best-effort signal cleanup; lease expiry remains the final backstop. */
|
|
@@ -8475,7 +8485,7 @@ var RunManager = class {
|
|
|
8475
8485
|
};
|
|
8476
8486
|
}
|
|
8477
8487
|
/** Assemble child env + args for a run. Exposed for tests. */
|
|
8478
|
-
async prepare(spec, agent, piDir, providers = this.store.readProviders()) {
|
|
8488
|
+
async prepare(spec, agent, piDir, providers = this.store.readProviders(), runtimeModule) {
|
|
8479
8489
|
const { activation, config } = agent;
|
|
8480
8490
|
const homeDir = join(dirname(piDir), "home");
|
|
8481
8491
|
const env = {
|
|
@@ -8499,6 +8509,7 @@ var RunManager = class {
|
|
|
8499
8509
|
};
|
|
8500
8510
|
})();
|
|
8501
8511
|
const args = [
|
|
8512
|
+
...runtimeModule ? ["--runtime", runtimeModule] : [],
|
|
8502
8513
|
spec.mode,
|
|
8503
8514
|
"--agent",
|
|
8504
8515
|
target.agentName,
|
|
@@ -8569,7 +8580,8 @@ var RunManager = class {
|
|
|
8569
8580
|
const runDir = this.store.runDir(id);
|
|
8570
8581
|
const piDir = join(runDir, "pi");
|
|
8571
8582
|
const providers = this.store.readProviders();
|
|
8572
|
-
const
|
|
8583
|
+
const runtimeModule = this.options.resolveRuntimeModule ? await this.options.resolveRuntimeModule(spec, agent, dirname(piDir)) : this.options.runtimeRegistry ? await this.resolveRuntimeModule(spec, agent, dirname(piDir)) : void 0;
|
|
8584
|
+
const { args, env, cwd } = await this.prepare(spec, agent, piDir, providers, runtimeModule);
|
|
8573
8585
|
this.assertStartOpen(signal);
|
|
8574
8586
|
let child;
|
|
8575
8587
|
let logStream;
|
|
@@ -8700,6 +8712,46 @@ var RunManager = class {
|
|
|
8700
8712
|
throw cause;
|
|
8701
8713
|
}
|
|
8702
8714
|
}
|
|
8715
|
+
async resolveRuntimeModule(spec, activated, cwd) {
|
|
8716
|
+
const profiles = await resolveRuntimeProfiles({
|
|
8717
|
+
agent: await this.connectActivatedAgent(activated),
|
|
8718
|
+
profiles: spec.profiles,
|
|
8719
|
+
teamId: spec.teamId,
|
|
8720
|
+
cwd
|
|
8721
|
+
});
|
|
8722
|
+
const kinds = [...new Set(profiles.map((profile) => profile.runtimeKind))];
|
|
8723
|
+
if (kinds.length !== 1) throw new AgentServerRunError("invalid_spec", "All profiles in a server-managed run must use the same runtime kind.");
|
|
8724
|
+
const kind = kinds[0];
|
|
8725
|
+
let registration;
|
|
8726
|
+
try {
|
|
8727
|
+
registration = this.options.runtimeRegistry?.resolve(kind);
|
|
8728
|
+
} catch (cause) {
|
|
8729
|
+
this.log("warn", "registered runtime unavailable locally", {
|
|
8730
|
+
runtimeKind: kind,
|
|
8731
|
+
...runtimeRegistryFailure(cause)
|
|
8732
|
+
});
|
|
8733
|
+
throw new AgentServerRunError("invalid_spec", `Registered runtime "${kind}" is unavailable locally; re-register it before starting a run.`);
|
|
8734
|
+
}
|
|
8735
|
+
if (registration) return registration.moduleUrl;
|
|
8736
|
+
if (kind === "gondolin_pi") return void 0;
|
|
8737
|
+
throw new AgentServerRunError("invalid_spec", `No local runtime is registered for profile kind "${kind}".`);
|
|
8738
|
+
}
|
|
8739
|
+
async connectActivatedAgent(activated) {
|
|
8740
|
+
const { activation, config } = activated;
|
|
8741
|
+
if (activation.source === "managed") {
|
|
8742
|
+
const agentKey = await resolveAgentKey(config, this.options.secretProviders);
|
|
8743
|
+
if (!agentKey) throw new AgentServerRunError("invalid_spec", `managed agent "${activation.alias}" has no agent key`);
|
|
8744
|
+
return connect({
|
|
8745
|
+
agentKey,
|
|
8746
|
+
apiUrl: activation.apiUrl
|
|
8747
|
+
});
|
|
8748
|
+
}
|
|
8749
|
+
return connect({
|
|
8750
|
+
configDir: dirname(activation.configPath),
|
|
8751
|
+
apiUrl: activation.apiUrl ?? activation.configApiUrl,
|
|
8752
|
+
secretProviders: this.options.externalSecretProviders
|
|
8753
|
+
});
|
|
8754
|
+
}
|
|
8703
8755
|
stop(id) {
|
|
8704
8756
|
const record = this.store.readRun(id);
|
|
8705
8757
|
if (!record) throw new AgentServerRunError("run_not_found", `run "${id}" was not found`);
|
|
@@ -8882,6 +8934,14 @@ function safeRunError(error) {
|
|
|
8882
8934
|
...typeof code === "string" ? { errorCode: code } : {}
|
|
8883
8935
|
};
|
|
8884
8936
|
}
|
|
8937
|
+
function runtimeRegistryFailure(error) {
|
|
8938
|
+
const code = error?.code;
|
|
8939
|
+
return {
|
|
8940
|
+
errorType: error instanceof Error ? error.name : typeof error,
|
|
8941
|
+
...typeof code === "string" ? { errorCode: code } : {},
|
|
8942
|
+
...error instanceof Error ? { errorMessage: error.message.slice(0, 512) } : {}
|
|
8943
|
+
};
|
|
8944
|
+
}
|
|
8885
8945
|
function sanitizeChildBaseEnv(baseEnv) {
|
|
8886
8946
|
return Object.fromEntries(Object.entries(baseEnv).filter(([name]) => INHERITED_ENV_NAMES.has(name) || INHERITED_MOLTNET_ENV_NAMES.has(name)));
|
|
8887
8947
|
}
|
|
@@ -8897,6 +8957,168 @@ async function terminateChild(child) {
|
|
|
8897
8957
|
child.kill("SIGKILL");
|
|
8898
8958
|
});
|
|
8899
8959
|
}
|
|
8960
|
+
//#endregion
|
|
8961
|
+
//#region src/runtime-loader.ts
|
|
8962
|
+
function extractRuntimeModule(argv) {
|
|
8963
|
+
const remaining = [];
|
|
8964
|
+
let specifier;
|
|
8965
|
+
let parsingOptions = true;
|
|
8966
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
8967
|
+
const argument = argv[index];
|
|
8968
|
+
if (argument === "--") {
|
|
8969
|
+
parsingOptions = false;
|
|
8970
|
+
remaining.push(argument);
|
|
8971
|
+
continue;
|
|
8972
|
+
}
|
|
8973
|
+
if (!parsingOptions) {
|
|
8974
|
+
remaining.push(argument);
|
|
8975
|
+
continue;
|
|
8976
|
+
}
|
|
8977
|
+
let candidate;
|
|
8978
|
+
if (argument === "--runtime") {
|
|
8979
|
+
candidate = argv[index + 1];
|
|
8980
|
+
if (!candidate) throw new Error("Missing value for --runtime");
|
|
8981
|
+
index += 1;
|
|
8982
|
+
} else if (argument.startsWith("--runtime=")) {
|
|
8983
|
+
candidate = argument.slice(10);
|
|
8984
|
+
if (!candidate) throw new Error("Missing value for --runtime");
|
|
8985
|
+
} else {
|
|
8986
|
+
remaining.push(argument);
|
|
8987
|
+
continue;
|
|
8988
|
+
}
|
|
8989
|
+
if (specifier) throw new Error("--runtime may be specified only once");
|
|
8990
|
+
specifier = candidate;
|
|
8991
|
+
}
|
|
8992
|
+
return {
|
|
8993
|
+
argv: remaining,
|
|
8994
|
+
specifier
|
|
8995
|
+
};
|
|
8996
|
+
}
|
|
8997
|
+
function resolveRuntimeModuleUrl(specifier, cwd = process.cwd()) {
|
|
8998
|
+
if (specifier.startsWith("file:")) return new URL(specifier).href;
|
|
8999
|
+
if (isAbsolute(specifier) || specifier.startsWith("./") || specifier.startsWith("../")) return pathToFileURL(resolve(cwd, specifier)).href;
|
|
9000
|
+
return pathToFileURL(createRequire(resolve(cwd, "package.json")).resolve(specifier)).href;
|
|
9001
|
+
}
|
|
9002
|
+
async function loadDaemonRuntimeAdapter(specifier, options = {}) {
|
|
9003
|
+
let moduleUrl;
|
|
9004
|
+
try {
|
|
9005
|
+
moduleUrl = resolveRuntimeModuleUrl(specifier, options.cwd);
|
|
9006
|
+
} catch (cause) {
|
|
9007
|
+
throw new Error(`Unable to resolve runtime module "${specifier}" from ${options.cwd ?? process.cwd()}`, { cause });
|
|
9008
|
+
}
|
|
9009
|
+
let namespace;
|
|
9010
|
+
try {
|
|
9011
|
+
namespace = await import(moduleUrl);
|
|
9012
|
+
} catch (cause) {
|
|
9013
|
+
throw new Error(`Unable to load runtime module "${specifier}"`, { cause });
|
|
9014
|
+
}
|
|
9015
|
+
const adapter = namespace.default;
|
|
9016
|
+
if (!isDaemonRuntimeAdapter(adapter)) throw new Error(`Runtime module "${specifier}" must default-export a DaemonRuntimeAdapter`);
|
|
9017
|
+
return adapter;
|
|
9018
|
+
}
|
|
9019
|
+
function isDaemonRuntimeAdapter(value) {
|
|
9020
|
+
if (!value || typeof value !== "object") return false;
|
|
9021
|
+
const candidate = value;
|
|
9022
|
+
return typeof candidate.runtimeKind === "string" && candidate.runtimeKind.length > 0 && typeof candidate.prepare === "function";
|
|
9023
|
+
}
|
|
9024
|
+
//#endregion
|
|
9025
|
+
//#region src/lib/agent-server/runtime-registry.ts
|
|
9026
|
+
var LOCKFILE_NAMES = [
|
|
9027
|
+
"pnpm-lock.yaml",
|
|
9028
|
+
"package-lock.json",
|
|
9029
|
+
"yarn.lock",
|
|
9030
|
+
"bun.lockb"
|
|
9031
|
+
];
|
|
9032
|
+
/** Local, operator-owned allowlist for executable daemon runtime modules. */
|
|
9033
|
+
var RuntimeRegistry = class {
|
|
9034
|
+
constructor(root) {
|
|
9035
|
+
this.root = root;
|
|
9036
|
+
}
|
|
9037
|
+
get path() {
|
|
9038
|
+
return join(this.root, "runtime-registry.json");
|
|
9039
|
+
}
|
|
9040
|
+
list() {
|
|
9041
|
+
if (!existsSync(this.path)) return [];
|
|
9042
|
+
const value = JSON.parse(readFileSync(this.path, "utf8"));
|
|
9043
|
+
if (!Array.isArray(value) || !value.every(isRegistration)) throw new AgentServerStoreError("invalid_state", "runtime registry contains an invalid registration");
|
|
9044
|
+
return value;
|
|
9045
|
+
}
|
|
9046
|
+
async register(kind, specifier, cwd = process.cwd()) {
|
|
9047
|
+
kind = assertStoreName("runtime kind", kind);
|
|
9048
|
+
mkdirSync(this.root, {
|
|
9049
|
+
recursive: true,
|
|
9050
|
+
mode: 448
|
|
9051
|
+
});
|
|
9052
|
+
const moduleUrl = resolveRuntimeModuleUrl(specifier, cwd);
|
|
9053
|
+
const adapter = await loadDaemonRuntimeAdapter(specifier, { cwd });
|
|
9054
|
+
if (adapter.runtimeKind !== kind) throw new Error(`Runtime module provides "${adapter.runtimeKind}", not registered kind "${kind}".`);
|
|
9055
|
+
if (!moduleUrl.startsWith("file:")) throw new Error("Runtime registration must resolve to a local file URL.");
|
|
9056
|
+
const entryHash = hashFile(new URL(moduleUrl));
|
|
9057
|
+
const lockfilePath = isPackageSpecifier(specifier) ? findLockfile(cwd) : void 0;
|
|
9058
|
+
if (isPackageSpecifier(specifier) && !lockfilePath) throw new Error("Package runtime registration requires a pnpm, npm, Yarn, or Bun lockfile in the current project or a parent directory.");
|
|
9059
|
+
const entry = {
|
|
9060
|
+
kind,
|
|
9061
|
+
moduleUrl,
|
|
9062
|
+
entryHash,
|
|
9063
|
+
...lockfilePath ? {
|
|
9064
|
+
lockfilePath,
|
|
9065
|
+
lockfileHash: hashPath(lockfilePath)
|
|
9066
|
+
} : {},
|
|
9067
|
+
registeredAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
9068
|
+
};
|
|
9069
|
+
const entries = this.list().filter((candidate) => candidate.kind !== kind);
|
|
9070
|
+
entries.push(entry);
|
|
9071
|
+
writeRegistry(this.path, entries);
|
|
9072
|
+
return entry;
|
|
9073
|
+
}
|
|
9074
|
+
unregister(kind) {
|
|
9075
|
+
kind = assertStoreName("runtime kind", kind);
|
|
9076
|
+
const entries = this.list();
|
|
9077
|
+
const next = entries.filter((entry) => entry.kind !== kind);
|
|
9078
|
+
if (next.length === entries.length) return false;
|
|
9079
|
+
writeRegistry(this.path, next);
|
|
9080
|
+
return true;
|
|
9081
|
+
}
|
|
9082
|
+
resolve(kind) {
|
|
9083
|
+
kind = assertStoreName("runtime kind", kind);
|
|
9084
|
+
const entry = this.list().find((candidate) => candidate.kind === kind);
|
|
9085
|
+
if (!entry) return void 0;
|
|
9086
|
+
if (hashFile(new URL(entry.moduleUrl)) !== entry.entryHash) throw new Error(`Registered runtime "${kind}" has changed; re-register it before starting a run.`);
|
|
9087
|
+
if (entry.lockfilePath && (!existsSync(entry.lockfilePath) || hashPath(entry.lockfilePath) !== entry.lockfileHash)) throw new Error(`Registered runtime "${kind}" lockfile has changed; re-register it before starting a run.`);
|
|
9088
|
+
return entry;
|
|
9089
|
+
}
|
|
9090
|
+
};
|
|
9091
|
+
function hashFile(url) {
|
|
9092
|
+
return createHash("sha256").update(readFileSync(url)).digest("hex");
|
|
9093
|
+
}
|
|
9094
|
+
function hashPath(path) {
|
|
9095
|
+
return createHash("sha256").update(readFileSync(path)).digest("hex");
|
|
9096
|
+
}
|
|
9097
|
+
function isPackageSpecifier(specifier) {
|
|
9098
|
+
return !specifier.startsWith("file:") && !specifier.startsWith(".") && !specifier.startsWith("/");
|
|
9099
|
+
}
|
|
9100
|
+
function findLockfile(cwd) {
|
|
9101
|
+
let current = resolve(cwd);
|
|
9102
|
+
for (;;) {
|
|
9103
|
+
for (const name of LOCKFILE_NAMES) {
|
|
9104
|
+
const candidate = join(current, name);
|
|
9105
|
+
if (existsSync(candidate)) return candidate;
|
|
9106
|
+
}
|
|
9107
|
+
const parent = dirname(current);
|
|
9108
|
+
if (parent === current) return void 0;
|
|
9109
|
+
current = parent;
|
|
9110
|
+
}
|
|
9111
|
+
}
|
|
9112
|
+
function isRegistration(value) {
|
|
9113
|
+
if (!value || typeof value !== "object") return false;
|
|
9114
|
+
const entry = value;
|
|
9115
|
+
return typeof entry.kind === "string" && typeof entry.moduleUrl === "string" && entry.moduleUrl.startsWith("file:") && typeof entry.entryHash === "string" && typeof entry.registeredAt === "string" && (entry.lockfilePath === void 0 || typeof entry.lockfilePath === "string") && (entry.lockfileHash === void 0 || typeof entry.lockfileHash === "string") && entry.lockfilePath === void 0 === (entry.lockfileHash === void 0);
|
|
9116
|
+
}
|
|
9117
|
+
function writeRegistry(path, entries) {
|
|
9118
|
+
const temp = `${path}.${process.pid}.tmp`;
|
|
9119
|
+
writeFileSync(temp, `${JSON.stringify(entries, null, 2)}\n`, { mode: 384 });
|
|
9120
|
+
renameSync(temp, path);
|
|
9121
|
+
}
|
|
8900
9122
|
var AgentServerModelDiscoveryError = class extends Error {
|
|
8901
9123
|
name = "AgentServerModelDiscoveryError";
|
|
8902
9124
|
constructor(code, message, statusCode, options) {
|
|
@@ -9968,7 +10190,8 @@ async function runAgentServer(argv) {
|
|
|
9968
10190
|
secretProviders,
|
|
9969
10191
|
externalSecretProviders,
|
|
9970
10192
|
baseEnv: processEnvSnapshot(),
|
|
9971
|
-
logger
|
|
10193
|
+
logger,
|
|
10194
|
+
runtimeRegistry: new RuntimeRegistry(store.root)
|
|
9972
10195
|
});
|
|
9973
10196
|
const app = buildAgentServer({
|
|
9974
10197
|
store,
|
|
@@ -10233,15 +10456,128 @@ function parseLimit(raw) {
|
|
|
10233
10456
|
return value;
|
|
10234
10457
|
}
|
|
10235
10458
|
//#endregion
|
|
10459
|
+
//#region src/lib/update.ts
|
|
10460
|
+
var UPDATE_MANIFEST_URL = "https://themolt.net/download/manifest.json";
|
|
10461
|
+
var UPDATE_CACHE_TTL_MS = 1440 * 60 * 1e3;
|
|
10462
|
+
var UPDATE_ERROR_CACHE_TTL_MS = 300 * 1e3;
|
|
10463
|
+
function detectDaemonInstallMethod(executable = process.argv[1] ?? "") {
|
|
10464
|
+
const path = executable.replaceAll("\\", "/");
|
|
10465
|
+
if (path.includes("/node_modules/@themoltnet/agent-daemon/")) return "npm";
|
|
10466
|
+
if (path.includes("/.local/share/moltnet/") || path.includes("/opt/moltnet/")) return "bundle";
|
|
10467
|
+
return "direct";
|
|
10468
|
+
}
|
|
10469
|
+
function daemonUpdateCommand(method) {
|
|
10470
|
+
if (method === "npm") return "npm install -g @themoltnet/agent-daemon@latest";
|
|
10471
|
+
return "curl -fsSL https://themolt.net/install/agent | sh";
|
|
10472
|
+
}
|
|
10473
|
+
async function checkDaemonUpdate(input) {
|
|
10474
|
+
const installMethod = detectDaemonInstallMethod(input.executable ?? process.argv[1] ?? "moltnet-agent");
|
|
10475
|
+
const result = {
|
|
10476
|
+
currentVersion: normalizeVersion(input.currentVersion),
|
|
10477
|
+
updateAvailable: false,
|
|
10478
|
+
installMethod,
|
|
10479
|
+
releaseUrl: "https://themolt.net/download",
|
|
10480
|
+
command: daemonUpdateCommand(installMethod)
|
|
10481
|
+
};
|
|
10482
|
+
const now = input.now ?? /* @__PURE__ */ new Date();
|
|
10483
|
+
if (!input.force) {
|
|
10484
|
+
const cached = await readCache();
|
|
10485
|
+
const ttl = cached?.latest ? UPDATE_CACHE_TTL_MS : UPDATE_ERROR_CACHE_TTL_MS;
|
|
10486
|
+
if (cached && now.getTime() - Date.parse(cached.checkedAt) < ttl) {
|
|
10487
|
+
result.latestVersion = cached.latest;
|
|
10488
|
+
result.updateAvailable = compareVersions(cached.latest, result.currentVersion) > 0;
|
|
10489
|
+
return result;
|
|
10490
|
+
}
|
|
10491
|
+
}
|
|
10492
|
+
try {
|
|
10493
|
+
const response = await (input.fetchFn ?? fetch)(UPDATE_MANIFEST_URL, { signal: AbortSignal.timeout(5e3) });
|
|
10494
|
+
if (!response.ok) throw new Error(`manifest returned HTTP ${response.status}`);
|
|
10495
|
+
const latest = manifestVersion(await response.json());
|
|
10496
|
+
if (!latest) throw new Error("manifest has no valid agent version");
|
|
10497
|
+
await writeCache({
|
|
10498
|
+
checkedAt: now.toISOString(),
|
|
10499
|
+
latest
|
|
10500
|
+
}).catch(() => void 0);
|
|
10501
|
+
result.latestVersion = latest;
|
|
10502
|
+
result.updateAvailable = compareVersions(latest, result.currentVersion) > 0;
|
|
10503
|
+
return result;
|
|
10504
|
+
} catch (error) {
|
|
10505
|
+
await writeCache({
|
|
10506
|
+
checkedAt: now.toISOString(),
|
|
10507
|
+
error: error instanceof Error ? error.message : String(error)
|
|
10508
|
+
}).catch(() => void 0);
|
|
10509
|
+
throw new Error(`could not check for MoltNet agent updates: ${error instanceof Error ? error.message : String(error)}`);
|
|
10510
|
+
}
|
|
10511
|
+
}
|
|
10512
|
+
function manifestVersion(value) {
|
|
10513
|
+
if (!value || typeof value !== "object") return void 0;
|
|
10514
|
+
const agent = value.agent;
|
|
10515
|
+
if (!agent || typeof agent !== "object") return void 0;
|
|
10516
|
+
const version = agent.version;
|
|
10517
|
+
return typeof version === "string" && validVersion(version) ? normalizeVersion(version) : void 0;
|
|
10518
|
+
}
|
|
10519
|
+
function normalizeVersion(value) {
|
|
10520
|
+
return value.trim().replace(/^v/, "");
|
|
10521
|
+
}
|
|
10522
|
+
function validVersion(value) {
|
|
10523
|
+
return /^v?\d+\.\d+\.\d+$/.test(value);
|
|
10524
|
+
}
|
|
10525
|
+
function compareVersions(a, b) {
|
|
10526
|
+
if (!a || !validVersion(a) || !validVersion(b)) return 0;
|
|
10527
|
+
const left = normalizeVersion(a).split(".").map(Number);
|
|
10528
|
+
const right = normalizeVersion(b).split(".").map(Number);
|
|
10529
|
+
for (let i = 0; i < 3; i += 1) {
|
|
10530
|
+
const leftPart = left[i] ?? 0;
|
|
10531
|
+
const rightPart = right[i] ?? 0;
|
|
10532
|
+
if (leftPart > rightPart) return 1;
|
|
10533
|
+
if (leftPart < rightPart) return -1;
|
|
10534
|
+
}
|
|
10535
|
+
return 0;
|
|
10536
|
+
}
|
|
10537
|
+
function cachePath() {
|
|
10538
|
+
const env = loadUpdateEnvConfig();
|
|
10539
|
+
return join(env.xdgCacheHome || (platform() === "win32" ? env.localAppData || join(homedir(), "AppData", "Local") : join(homedir(), ".cache")), "moltnet", "updates", "agent.json");
|
|
10540
|
+
}
|
|
10541
|
+
async function readCache() {
|
|
10542
|
+
try {
|
|
10543
|
+
return JSON.parse(await readFile(cachePath(), "utf8"));
|
|
10544
|
+
} catch {
|
|
10545
|
+
return;
|
|
10546
|
+
}
|
|
10547
|
+
}
|
|
10548
|
+
async function writeCache(cache) {
|
|
10549
|
+
const path = cachePath();
|
|
10550
|
+
await mkdir(dirname(path), {
|
|
10551
|
+
recursive: true,
|
|
10552
|
+
mode: 448
|
|
10553
|
+
});
|
|
10554
|
+
await writeFile(path, JSON.stringify(cache), { mode: 384 });
|
|
10555
|
+
}
|
|
10556
|
+
//#endregion
|
|
10557
|
+
//#region src/version.ts
|
|
10558
|
+
var DAEMON_VERSION = "0.51.0";
|
|
10559
|
+
//#endregion
|
|
10236
10560
|
//#region src/cli.ts
|
|
10237
10561
|
async function runAgentDaemonCli(options) {
|
|
10238
10562
|
const [subcommand, ...rest] = options.argv ?? process.argv.slice(2);
|
|
10563
|
+
if ([
|
|
10564
|
+
"poll",
|
|
10565
|
+
"drain",
|
|
10566
|
+
"serve"
|
|
10567
|
+
].includes(subcommand ?? "") && !isWorkspaceInvocation()) checkDaemonUpdate({ currentVersion: DAEMON_VERSION }).then((update) => {
|
|
10568
|
+
if (update.updateAvailable) pino({ name: "moltnet.agent-daemon" }).info({
|
|
10569
|
+
event: "agent-daemon.update_available",
|
|
10570
|
+
update
|
|
10571
|
+
}, "A MoltNet agent update is available");
|
|
10572
|
+
}).catch(() => void 0);
|
|
10239
10573
|
switch (subcommand) {
|
|
10240
10574
|
case "poll": return runPoll(rest, options.runtime);
|
|
10241
10575
|
case "once": return runOnce(rest, options.runtime);
|
|
10242
10576
|
case "drain": return runDrain(rest, options.runtime);
|
|
10243
10577
|
case "server": return runAgentServer(rest);
|
|
10244
10578
|
case "sync-sessions": return runSyncSessions(rest);
|
|
10579
|
+
case "update": return runUpdate(rest);
|
|
10580
|
+
case "runtime": return runRuntime(rest);
|
|
10245
10581
|
case "-h":
|
|
10246
10582
|
case "--help":
|
|
10247
10583
|
console.log(ROOT_USAGE);
|
|
@@ -10254,5 +10590,49 @@ async function runAgentDaemonCli(options) {
|
|
|
10254
10590
|
return 1;
|
|
10255
10591
|
}
|
|
10256
10592
|
}
|
|
10593
|
+
function isWorkspaceInvocation() {
|
|
10594
|
+
return (process.argv[1] ?? "").replaceAll("\\", "/").includes("/apps/agent-daemon/src/");
|
|
10595
|
+
}
|
|
10596
|
+
async function runUpdate(argv) {
|
|
10597
|
+
if (argv[0] !== "check" || argv.slice(1).some((arg) => arg !== "--json")) {
|
|
10598
|
+
console.error("Usage: moltnet-agent update check [--json]");
|
|
10599
|
+
return 1;
|
|
10600
|
+
}
|
|
10601
|
+
try {
|
|
10602
|
+
const update = await checkDaemonUpdate({
|
|
10603
|
+
currentVersion: DAEMON_VERSION,
|
|
10604
|
+
force: true
|
|
10605
|
+
});
|
|
10606
|
+
if (argv.includes("--json")) console.log(JSON.stringify(update));
|
|
10607
|
+
else if (update.updateAvailable) console.log(`MoltNet agent ${update.latestVersion} is available (you have ${update.currentVersion}). Run: ${update.command}`);
|
|
10608
|
+
else console.log(`MoltNet agent ${update.currentVersion} is up to date.`);
|
|
10609
|
+
return 0;
|
|
10610
|
+
} catch (error) {
|
|
10611
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
10612
|
+
return 1;
|
|
10613
|
+
}
|
|
10614
|
+
}
|
|
10615
|
+
async function runRuntime(argv) {
|
|
10616
|
+
const config = loadAgentServerEnvConfig();
|
|
10617
|
+
const registry = new RuntimeRegistry(resolveAgentServerRoot({
|
|
10618
|
+
root: config.root,
|
|
10619
|
+
xdgConfigHome: config.xdgConfigHome
|
|
10620
|
+
}));
|
|
10621
|
+
const [command, ...rest] = argv;
|
|
10622
|
+
if (command === "list") {
|
|
10623
|
+
console.log(JSON.stringify(registry.list(), null, 2));
|
|
10624
|
+
return 0;
|
|
10625
|
+
}
|
|
10626
|
+
if (command === "unregister" && rest.length === 1) {
|
|
10627
|
+
if (!registry.unregister(rest[0])) throw new Error(`No runtime registered for "${rest[0]}".`);
|
|
10628
|
+
return 0;
|
|
10629
|
+
}
|
|
10630
|
+
if (command === "register" && rest.length === 2) {
|
|
10631
|
+
console.log(JSON.stringify(await registry.register(rest[0], rest[1]), null, 2));
|
|
10632
|
+
return 0;
|
|
10633
|
+
}
|
|
10634
|
+
console.error("Usage: moltnet-agent runtime <list|register <kind> <module>|unregister <kind>>");
|
|
10635
|
+
return 1;
|
|
10636
|
+
}
|
|
10257
10637
|
//#endregion
|
|
10258
|
-
export { runAgentDaemonCli };
|
|
10638
|
+
export { loadDaemonRuntimeAdapter as n, runAgentDaemonCli, extractRuntimeModule as t };
|
package/dist/main.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { defaultPiDaemonAdapter } from "./pi.js";
|
|
3
|
-
import { runAgentDaemonCli } from "./cli.js";
|
|
4
|
-
import { createRequire } from "node:module";
|
|
3
|
+
import { n as loadDaemonRuntimeAdapter, runAgentDaemonCli, t as extractRuntimeModule } from "./cli.js";
|
|
5
4
|
import { registerInstrumentations } from "@opentelemetry/instrumentation";
|
|
6
5
|
import { DnsInstrumentation } from "@opentelemetry/instrumentation-dns";
|
|
7
6
|
import { HttpInstrumentation } from "@opentelemetry/instrumentation-http";
|
|
@@ -9,8 +8,6 @@ import { NetInstrumentation } from "@opentelemetry/instrumentation-net";
|
|
|
9
8
|
import { PgInstrumentation } from "@opentelemetry/instrumentation-pg";
|
|
10
9
|
import { PinoInstrumentation } from "@opentelemetry/instrumentation-pino";
|
|
11
10
|
import { UndiciInstrumentation } from "@opentelemetry/instrumentation-undici";
|
|
12
|
-
import { isAbsolute, resolve } from "node:path";
|
|
13
|
-
import { pathToFileURL } from "node:url";
|
|
14
11
|
//#region ../../libs/observability/src/instrumentation.ts
|
|
15
12
|
/**
|
|
16
13
|
* Register OTel auto-instrumentation for common Node.js modules.
|
|
@@ -101,70 +98,6 @@ function installSupervisorParentGuard(proc = process) {
|
|
|
101
98
|
return true;
|
|
102
99
|
}
|
|
103
100
|
//#endregion
|
|
104
|
-
//#region src/runtime-loader.ts
|
|
105
|
-
function extractRuntimeModule(argv) {
|
|
106
|
-
const remaining = [];
|
|
107
|
-
let specifier;
|
|
108
|
-
let parsingOptions = true;
|
|
109
|
-
for (let index = 0; index < argv.length; index += 1) {
|
|
110
|
-
const argument = argv[index];
|
|
111
|
-
if (argument === "--") {
|
|
112
|
-
parsingOptions = false;
|
|
113
|
-
remaining.push(argument);
|
|
114
|
-
continue;
|
|
115
|
-
}
|
|
116
|
-
if (!parsingOptions) {
|
|
117
|
-
remaining.push(argument);
|
|
118
|
-
continue;
|
|
119
|
-
}
|
|
120
|
-
let candidate;
|
|
121
|
-
if (argument === "--runtime") {
|
|
122
|
-
candidate = argv[index + 1];
|
|
123
|
-
if (!candidate) throw new Error("Missing value for --runtime");
|
|
124
|
-
index += 1;
|
|
125
|
-
} else if (argument.startsWith("--runtime=")) {
|
|
126
|
-
candidate = argument.slice(10);
|
|
127
|
-
if (!candidate) throw new Error("Missing value for --runtime");
|
|
128
|
-
} else {
|
|
129
|
-
remaining.push(argument);
|
|
130
|
-
continue;
|
|
131
|
-
}
|
|
132
|
-
if (specifier) throw new Error("--runtime may be specified only once");
|
|
133
|
-
specifier = candidate;
|
|
134
|
-
}
|
|
135
|
-
return {
|
|
136
|
-
argv: remaining,
|
|
137
|
-
specifier
|
|
138
|
-
};
|
|
139
|
-
}
|
|
140
|
-
function resolveRuntimeModuleUrl(specifier, cwd = process.cwd()) {
|
|
141
|
-
if (specifier.startsWith("file:")) return new URL(specifier).href;
|
|
142
|
-
if (isAbsolute(specifier) || specifier.startsWith("./") || specifier.startsWith("../")) return pathToFileURL(resolve(cwd, specifier)).href;
|
|
143
|
-
return pathToFileURL(createRequire(resolve(cwd, "package.json")).resolve(specifier)).href;
|
|
144
|
-
}
|
|
145
|
-
async function loadDaemonRuntimeAdapter(specifier, options = {}) {
|
|
146
|
-
let moduleUrl;
|
|
147
|
-
try {
|
|
148
|
-
moduleUrl = resolveRuntimeModuleUrl(specifier, options.cwd);
|
|
149
|
-
} catch (cause) {
|
|
150
|
-
throw new Error(`Unable to resolve runtime module "${specifier}" from ${options.cwd ?? process.cwd()}`, { cause });
|
|
151
|
-
}
|
|
152
|
-
let namespace;
|
|
153
|
-
try {
|
|
154
|
-
namespace = await import(moduleUrl);
|
|
155
|
-
} catch (cause) {
|
|
156
|
-
throw new Error(`Unable to load runtime module "${specifier}"`, { cause });
|
|
157
|
-
}
|
|
158
|
-
const adapter = namespace.default;
|
|
159
|
-
if (!isDaemonRuntimeAdapter(adapter)) throw new Error(`Runtime module "${specifier}" must default-export a DaemonRuntimeAdapter`);
|
|
160
|
-
return adapter;
|
|
161
|
-
}
|
|
162
|
-
function isDaemonRuntimeAdapter(value) {
|
|
163
|
-
if (!value || typeof value !== "object") return false;
|
|
164
|
-
const candidate = value;
|
|
165
|
-
return typeof candidate.runtimeKind === "string" && candidate.runtimeKind.length > 0 && typeof candidate.prepare === "function";
|
|
166
|
-
}
|
|
167
|
-
//#endregion
|
|
168
101
|
//#region src/main.ts
|
|
169
102
|
installSupervisorParentGuard();
|
|
170
103
|
async function main() {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@themoltnet/agent-daemon",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.51.0",
|
|
4
4
|
"license": "AGPL-3.0-only",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "Universal MoltNet agent daemon host with a built-in Pi/Gondolin runtime and support for trusted operator-owned runtime modules. CLI: moltnet-agent.",
|
|
@@ -84,9 +84,9 @@
|
|
|
84
84
|
"proper-lockfile": "4.1.2",
|
|
85
85
|
"typebox": "^1.2.8",
|
|
86
86
|
"@themoltnet/agent-runtime": "0.45.2",
|
|
87
|
+
"@themoltnet/pi-runtime": "0.14.1",
|
|
87
88
|
"@themoltnet/os-keyring": "0.3.0",
|
|
88
|
-
"@themoltnet/sdk": "0.140.0"
|
|
89
|
-
"@themoltnet/pi-runtime": "0.14.1"
|
|
89
|
+
"@themoltnet/sdk": "0.140.0"
|
|
90
90
|
},
|
|
91
91
|
"devDependencies": {
|
|
92
92
|
"@fastify/swagger": "^9.6.1",
|
|
@@ -97,15 +97,15 @@
|
|
|
97
97
|
"vite-plugin-dts": "^4.5.4",
|
|
98
98
|
"vitest": "^3.0.0",
|
|
99
99
|
"@moltnet/agent-eval": "0.1.0",
|
|
100
|
-
"@moltnet/crypto-service": "0.1.0",
|
|
101
100
|
"@moltnet/bootstrap": "0.1.0",
|
|
101
|
+
"@moltnet/crypto-service": "0.1.0",
|
|
102
|
+
"@moltnet/execution-plan": "0.1.0",
|
|
102
103
|
"@moltnet/execution-integrations": "0.1.0",
|
|
104
|
+
"@moltnet/models": "0.1.0",
|
|
103
105
|
"@moltnet/loopback-companion": "0.1.0",
|
|
104
|
-
"@moltnet/
|
|
106
|
+
"@moltnet/tasks": "0.1.0",
|
|
105
107
|
"@moltnet/observability": "0.1.0",
|
|
106
|
-
"@moltnet/
|
|
107
|
-
"@moltnet/runtime-profiles": "0.1.0",
|
|
108
|
-
"@moltnet/tasks": "0.1.0"
|
|
108
|
+
"@moltnet/runtime-profiles": "0.1.0"
|
|
109
109
|
},
|
|
110
110
|
"nx": {
|
|
111
111
|
"projectType": "application",
|