@hasna/machines 0.0.43 → 0.0.45
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/README.md +6 -0
- package/dist/cli/index.js +89 -42
- package/dist/commands/self-test.d.ts.map +1 -1
- package/dist/commands/status.d.ts +4 -1
- package/dist/commands/status.d.ts.map +1 -1
- package/dist/index.js +82 -30
- package/dist/mcp/index.js +81 -29
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/types.d.ts +1 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -144,6 +144,7 @@ CLI and MCP expose the same topology view:
|
|
|
144
144
|
machines topology --json
|
|
145
145
|
machines topology --no-tailscale --json
|
|
146
146
|
machines route --machine linux-dev-01 --json
|
|
147
|
+
machines ssh --machine linux-dev-01 --private-metadata
|
|
147
148
|
```
|
|
148
149
|
|
|
149
150
|
## Screen sharing
|
|
@@ -322,6 +323,11 @@ set `HASNA_MACHINES_ALLOW_PRIVATE_OUTPUT=1` and pass the explicit
|
|
|
322
323
|
`privateMetadata=true` query parameter or MCP `private_metadata` argument. The
|
|
323
324
|
caller flag alone is ignored.
|
|
324
325
|
|
|
326
|
+
Default status and SSH-resolution output is public-safe: local paths, machine
|
|
327
|
+
identifiers, route targets, and generated SSH commands are redacted unless
|
|
328
|
+
private output is explicitly requested. CLI commands that print raw SSH targets
|
|
329
|
+
require `--private-metadata`.
|
|
330
|
+
|
|
325
331
|
Doctor summaries are also opt-in with `--doctor-summary` or
|
|
326
332
|
`HASNA_MACHINES_AGENT_DOCTOR_SUMMARY=1`. The daemon records a compact
|
|
327
333
|
ok/warn/fail count plus redacted blockers and avoids optional private adapters
|
package/dist/cli/index.js
CHANGED
|
@@ -9972,7 +9972,8 @@ function parseJsonObject(value) {
|
|
|
9972
9972
|
return null;
|
|
9973
9973
|
}
|
|
9974
9974
|
}
|
|
9975
|
-
function getStatus() {
|
|
9975
|
+
function getStatus(options = {}) {
|
|
9976
|
+
const privateMetadata = options.privateMetadata === true;
|
|
9976
9977
|
const manifest = readManifest();
|
|
9977
9978
|
const heartbeats = listHeartbeats();
|
|
9978
9979
|
const heartbeatByMachine = latestHeartbeatByMachine(heartbeats);
|
|
@@ -9981,17 +9982,17 @@ function getStatus() {
|
|
|
9981
9982
|
...heartbeats.map((heartbeat) => heartbeat.machine_id)
|
|
9982
9983
|
]);
|
|
9983
9984
|
return {
|
|
9984
|
-
machineId: getLocalMachineId(),
|
|
9985
|
-
manifestPath: getManifestPath(),
|
|
9986
|
-
dbPath: getDbPath(),
|
|
9987
|
-
notificationsPath: getNotificationsPath(),
|
|
9985
|
+
machineId: privateMetadata ? getLocalMachineId() : REDACTED_VALUE,
|
|
9986
|
+
manifestPath: privateMetadata ? getManifestPath() : REDACTED_VALUE,
|
|
9987
|
+
dbPath: privateMetadata ? getDbPath() : REDACTED_VALUE,
|
|
9988
|
+
notificationsPath: privateMetadata ? getNotificationsPath() : REDACTED_VALUE,
|
|
9988
9989
|
manifestMachineCount: manifest.machines.length,
|
|
9989
9990
|
heartbeatCount: heartbeats.length,
|
|
9990
9991
|
machines: [...machineIds].sort().map((machineId) => {
|
|
9991
9992
|
const declared = manifest.machines.find((machine) => machine.id === machineId);
|
|
9992
9993
|
const heartbeat = heartbeatByMachine.get(machineId);
|
|
9993
9994
|
return {
|
|
9994
|
-
machineId,
|
|
9995
|
+
machineId: privateMetadata ? machineId : REDACTED_VALUE,
|
|
9995
9996
|
platform: declared?.platform,
|
|
9996
9997
|
manifestDeclared: Boolean(declared),
|
|
9997
9998
|
heartbeatStatus: heartbeat?.status || "unknown",
|
|
@@ -9999,7 +10000,7 @@ function getStatus() {
|
|
|
9999
10000
|
daemonVersion: heartbeat?.daemon_version ?? null,
|
|
10000
10001
|
agentMode: heartbeat?.agent_mode ?? null,
|
|
10001
10002
|
storageSyncStatus: heartbeat?.storage_sync_status ?? null,
|
|
10002
|
-
doctorSummary: parseJsonObject(heartbeat?.doctor_summary_json),
|
|
10003
|
+
doctorSummary: privateMetadata ? parseJsonObject(heartbeat?.doctor_summary_json) : null,
|
|
10003
10004
|
privateMetadata: Boolean(heartbeat?.private_metadata)
|
|
10004
10005
|
};
|
|
10005
10006
|
}),
|
|
@@ -10588,8 +10589,8 @@ function runDoctor(machineId, options = {}) {
|
|
|
10588
10589
|
|
|
10589
10590
|
// src/commands/daemon.ts
|
|
10590
10591
|
import { execFileSync } from "child_process";
|
|
10591
|
-
import { chmodSync, existsSync as existsSync8, mkdirSync as mkdirSync2, writeFileSync as writeFileSync4 } from "fs";
|
|
10592
|
-
import { dirname as dirname4 } from "path";
|
|
10592
|
+
import { chmodSync, existsSync as existsSync8, readFileSync as readFileSync6, statSync, mkdirSync as mkdirSync2, writeFileSync as writeFileSync4 } from "fs";
|
|
10593
|
+
import { delimiter, dirname as dirname4 } from "path";
|
|
10593
10594
|
import { platform as osPlatform } from "os";
|
|
10594
10595
|
var DEFAULT_SERVICE_NAME = "machines-agent";
|
|
10595
10596
|
var DEFAULT_EXECUTABLE = "/usr/local/bin/machines-agent";
|
|
@@ -10979,13 +10980,46 @@ WantedBy=${options.mode === "system" ? "multi-user.target" : "default.target"}
|
|
|
10979
10980
|
`;
|
|
10980
10981
|
}
|
|
10981
10982
|
function daemonProgramArguments(options) {
|
|
10982
|
-
const bunRuntime =
|
|
10983
|
+
const bunRuntime = bunRuntimeForExecutable(options.executable);
|
|
10983
10984
|
const base = bunRuntime ? [bunRuntime, options.executable] : [options.executable];
|
|
10984
10985
|
return [...base, "--interval-ms", String(options.intervalMs)];
|
|
10985
10986
|
}
|
|
10986
|
-
function
|
|
10987
|
-
|
|
10988
|
-
|
|
10987
|
+
function bunRuntimeForExecutable(executable) {
|
|
10988
|
+
if (!isBunShebangScript(executable))
|
|
10989
|
+
return null;
|
|
10990
|
+
for (const candidate of bunRuntimeCandidates(executable)) {
|
|
10991
|
+
if (isExecutableFile(candidate))
|
|
10992
|
+
return candidate;
|
|
10993
|
+
}
|
|
10994
|
+
return null;
|
|
10995
|
+
}
|
|
10996
|
+
function bunRuntimeCandidates(executable) {
|
|
10997
|
+
const candidates = [
|
|
10998
|
+
`${dirname4(executable)}/bun`,
|
|
10999
|
+
process.env["BUN_INSTALL"] ? `${process.env["BUN_INSTALL"]}/bin/bun` : null,
|
|
11000
|
+
process.env["HOME"] ? `${process.env["HOME"]}/.bun/bin/bun` : null,
|
|
11001
|
+
...(process.env["PATH"] ?? "").split(delimiter).filter(Boolean).map((entry) => `${entry}/bun`)
|
|
11002
|
+
].filter((value) => Boolean(value));
|
|
11003
|
+
return [...new Set(candidates)];
|
|
11004
|
+
}
|
|
11005
|
+
function isBunShebangScript(executable) {
|
|
11006
|
+
try {
|
|
11007
|
+
const content = readFileSync6(executable, "utf8").slice(0, 256);
|
|
11008
|
+
const firstLine2 = content.split(/\r?\n/, 1)[0] ?? "";
|
|
11009
|
+
return /^#!.*\bbun\b/.test(firstLine2);
|
|
11010
|
+
} catch {
|
|
11011
|
+
return false;
|
|
11012
|
+
}
|
|
11013
|
+
}
|
|
11014
|
+
function isExecutableFile(path) {
|
|
11015
|
+
if (!existsSync8(path))
|
|
11016
|
+
return false;
|
|
11017
|
+
try {
|
|
11018
|
+
const stats = statSync(path);
|
|
11019
|
+
return stats.isFile() && (stats.mode & 73) !== 0;
|
|
11020
|
+
} catch {
|
|
11021
|
+
return false;
|
|
11022
|
+
}
|
|
10989
11023
|
}
|
|
10990
11024
|
function launchdDomain(options) {
|
|
10991
11025
|
return options.mode === "system" ? "system" : "gui/$UID";
|
|
@@ -11343,7 +11377,7 @@ function startDashboardServer(options = {}) {
|
|
|
11343
11377
|
return Response.json({ ok: true, ...getServeInfo(options) });
|
|
11344
11378
|
}
|
|
11345
11379
|
if (url.pathname === "/api/status") {
|
|
11346
|
-
return Response.json(getStatus());
|
|
11380
|
+
return Response.json(appendWarnings(getStatus({ privateMetadata }), privateWarnings));
|
|
11347
11381
|
}
|
|
11348
11382
|
if (url.pathname === "/api/topology") {
|
|
11349
11383
|
const topology = discoverMachineTopology({ includeTailscale: url.searchParams.get("tailscale") !== "false" });
|
|
@@ -11481,15 +11515,16 @@ function check(id, status, summary, detail) {
|
|
|
11481
11515
|
function runSelfTest() {
|
|
11482
11516
|
const version = getPackageVersion();
|
|
11483
11517
|
const status = getStatus();
|
|
11518
|
+
const machineId = getLocalMachineId();
|
|
11484
11519
|
const doctor = runDoctor();
|
|
11485
11520
|
const serveInfo = getServeInfo();
|
|
11486
11521
|
const html = renderDashboardHtml();
|
|
11487
11522
|
const notifications = listNotificationChannels();
|
|
11488
|
-
const apps = listApps(
|
|
11489
|
-
const appsDiff = diffApps(
|
|
11490
|
-
const cliPlan = buildClaudeInstallPlan(
|
|
11523
|
+
const apps = listApps(machineId);
|
|
11524
|
+
const appsDiff = diffApps(machineId);
|
|
11525
|
+
const cliPlan = buildClaudeInstallPlan(machineId);
|
|
11491
11526
|
return {
|
|
11492
|
-
machineId
|
|
11527
|
+
machineId,
|
|
11493
11528
|
checks: [
|
|
11494
11529
|
check("package-version", version === "0.0.0" ? "fail" : "ok", "Package version resolves", version),
|
|
11495
11530
|
check("status", "ok", "Status loads", JSON.stringify({ machines: status.manifestMachineCount, heartbeats: status.heartbeatCount })),
|
|
@@ -11507,7 +11542,7 @@ function runSelfTest() {
|
|
|
11507
11542
|
// src/commands/clipboard.ts
|
|
11508
11543
|
init_paths();
|
|
11509
11544
|
import { createHash } from "crypto";
|
|
11510
|
-
import { existsSync as existsSync9, readFileSync as
|
|
11545
|
+
import { existsSync as existsSync9, readFileSync as readFileSync7, rmSync, writeFileSync as writeFileSync5 } from "fs";
|
|
11511
11546
|
import { join as join6 } from "path";
|
|
11512
11547
|
var DEFAULT_CONFIG = {
|
|
11513
11548
|
version: 1,
|
|
@@ -11541,7 +11576,7 @@ function readConfig(configPath) {
|
|
|
11541
11576
|
if (!existsSync9(path)) {
|
|
11542
11577
|
return getDefaultConfig();
|
|
11543
11578
|
}
|
|
11544
|
-
const parsed = JSON.parse(
|
|
11579
|
+
const parsed = JSON.parse(readFileSync7(path, "utf8"));
|
|
11545
11580
|
return { ...getDefaultConfig(), ...parsed };
|
|
11546
11581
|
}
|
|
11547
11582
|
function writeConfig(config, configPath) {
|
|
@@ -11556,7 +11591,7 @@ function readHistory(historyPath) {
|
|
|
11556
11591
|
return [];
|
|
11557
11592
|
}
|
|
11558
11593
|
try {
|
|
11559
|
-
return JSON.parse(
|
|
11594
|
+
return JSON.parse(readFileSync7(path, "utf8"));
|
|
11560
11595
|
} catch {
|
|
11561
11596
|
return [];
|
|
11562
11597
|
}
|
|
@@ -11586,7 +11621,7 @@ function sanitizeClipboardForRead(content, maxSizeBytes, skipPatterns) {
|
|
|
11586
11621
|
function getOrCreateClipboardKey() {
|
|
11587
11622
|
const keyPath = getClipboardKeyPath();
|
|
11588
11623
|
if (existsSync9(keyPath)) {
|
|
11589
|
-
return
|
|
11624
|
+
return readFileSync7(keyPath, "utf8").trim();
|
|
11590
11625
|
}
|
|
11591
11626
|
const key = createHash("sha256").update(crypto.randomUUID()).digest("hex").slice(0, 32);
|
|
11592
11627
|
ensureParentDir(keyPath);
|
|
@@ -11641,7 +11676,7 @@ function getClipboardStatus(historyPath) {
|
|
|
11641
11676
|
|
|
11642
11677
|
// src/commands/clipboard-daemon.ts
|
|
11643
11678
|
init_paths();
|
|
11644
|
-
import { readFileSync as
|
|
11679
|
+
import { readFileSync as readFileSync9, writeFileSync as writeFileSync6 } from "fs";
|
|
11645
11680
|
import { join as join7 } from "path";
|
|
11646
11681
|
import { createHash as createHash3 } from "crypto";
|
|
11647
11682
|
|
|
@@ -11649,7 +11684,7 @@ import { createHash as createHash3 } from "crypto";
|
|
|
11649
11684
|
init_paths();
|
|
11650
11685
|
import { createServer } from "http";
|
|
11651
11686
|
import { createHash as createHash2 } from "crypto";
|
|
11652
|
-
import { readFileSync as
|
|
11687
|
+
import { readFileSync as readFileSync8 } from "fs";
|
|
11653
11688
|
function readLocalClipboardSync() {
|
|
11654
11689
|
const platform5 = process.platform;
|
|
11655
11690
|
if (platform5 === "darwin") {
|
|
@@ -11695,7 +11730,7 @@ function hasCommand3(binary) {
|
|
|
11695
11730
|
function loadSharedSecret() {
|
|
11696
11731
|
const keyPath = getClipboardKeyPath();
|
|
11697
11732
|
try {
|
|
11698
|
-
return
|
|
11733
|
+
return readFileSync8(keyPath, "utf8").trim();
|
|
11699
11734
|
} catch {
|
|
11700
11735
|
return "";
|
|
11701
11736
|
}
|
|
@@ -11862,7 +11897,7 @@ function computeHash2(content) {
|
|
|
11862
11897
|
}
|
|
11863
11898
|
function loadSharedSecret2() {
|
|
11864
11899
|
try {
|
|
11865
|
-
return
|
|
11900
|
+
return readFileSync9(getClipboardKeyPath(), "utf8").trim();
|
|
11866
11901
|
} catch {
|
|
11867
11902
|
return "";
|
|
11868
11903
|
}
|
|
@@ -11873,7 +11908,7 @@ function writePid(pid) {
|
|
|
11873
11908
|
}
|
|
11874
11909
|
function readPid() {
|
|
11875
11910
|
try {
|
|
11876
|
-
const pid = Number.parseInt(
|
|
11911
|
+
const pid = Number.parseInt(readFileSync9(DAEMON_PID_PATH, "utf8").trim());
|
|
11877
11912
|
return Number.isFinite(pid) ? pid : null;
|
|
11878
11913
|
} catch {
|
|
11879
11914
|
return null;
|
|
@@ -11974,7 +12009,7 @@ async function discoverPeers() {
|
|
|
11974
12009
|
|
|
11975
12010
|
// src/commands/heal.ts
|
|
11976
12011
|
init_paths();
|
|
11977
|
-
import { existsSync as existsSync10, readFileSync as
|
|
12012
|
+
import { existsSync as existsSync10, readFileSync as readFileSync10, writeFileSync as writeFileSync7 } from "fs";
|
|
11978
12013
|
import { join as join8 } from "path";
|
|
11979
12014
|
var DEFAULT_THRESHOLDS = {
|
|
11980
12015
|
reconnect: 3,
|
|
@@ -12028,7 +12063,7 @@ function readHealConfig(path) {
|
|
|
12028
12063
|
const p = path || getHealConfigPath();
|
|
12029
12064
|
if (!existsSync10(p))
|
|
12030
12065
|
return { ...DEFAULT_HEAL_CONFIG, thresholds: { ...DEFAULT_THRESHOLDS } };
|
|
12031
|
-
const parsed = JSON.parse(
|
|
12066
|
+
const parsed = JSON.parse(readFileSync10(p, "utf8"));
|
|
12032
12067
|
return {
|
|
12033
12068
|
...DEFAULT_HEAL_CONFIG,
|
|
12034
12069
|
...parsed,
|
|
@@ -12047,7 +12082,7 @@ function readHealState(path) {
|
|
|
12047
12082
|
if (!existsSync10(p))
|
|
12048
12083
|
return defaultHealState();
|
|
12049
12084
|
try {
|
|
12050
|
-
return { ...defaultHealState(), ...JSON.parse(
|
|
12085
|
+
return { ...defaultHealState(), ...JSON.parse(readFileSync10(p, "utf8")) };
|
|
12051
12086
|
} catch {
|
|
12052
12087
|
return defaultHealState();
|
|
12053
12088
|
}
|
|
@@ -12174,7 +12209,7 @@ function sh(cmd, timeoutMs = 8000) {
|
|
|
12174
12209
|
}
|
|
12175
12210
|
function getCurrentBootId() {
|
|
12176
12211
|
try {
|
|
12177
|
-
return
|
|
12212
|
+
return readFileSync10("/proc/sys/kernel/random/boot_id", "utf8").trim();
|
|
12178
12213
|
} catch {
|
|
12179
12214
|
return "";
|
|
12180
12215
|
}
|
|
@@ -12260,7 +12295,7 @@ function executeAction(action, config) {
|
|
|
12260
12295
|
|
|
12261
12296
|
// src/commands/heal-daemon.ts
|
|
12262
12297
|
init_paths();
|
|
12263
|
-
import { existsSync as existsSync11, readFileSync as
|
|
12298
|
+
import { existsSync as existsSync11, readFileSync as readFileSync11, writeFileSync as writeFileSync8 } from "fs";
|
|
12264
12299
|
import { join as join9 } from "path";
|
|
12265
12300
|
var DAEMON_PID_PATH2 = join9(getDataDir(), "heal-daemon.pid");
|
|
12266
12301
|
var SERVICE_PATH = "/etc/systemd/system/machines-heal.service";
|
|
@@ -12308,7 +12343,7 @@ function writePid2(pid) {
|
|
|
12308
12343
|
}
|
|
12309
12344
|
function readPid2() {
|
|
12310
12345
|
try {
|
|
12311
|
-
const pid = Number.parseInt(
|
|
12346
|
+
const pid = Number.parseInt(readFileSync11(DAEMON_PID_PATH2, "utf8").trim());
|
|
12312
12347
|
return Number.isFinite(pid) ? pid : null;
|
|
12313
12348
|
} catch {
|
|
12314
12349
|
return null;
|
|
@@ -12382,7 +12417,7 @@ function enableHardwareWatchdog() {
|
|
|
12382
12417
|
const log2 = [];
|
|
12383
12418
|
if (!existsSync11(SYSTEM_CONF))
|
|
12384
12419
|
return ["/etc/systemd/system.conf not found; skipping hardware watchdog"];
|
|
12385
|
-
let conf =
|
|
12420
|
+
let conf = readFileSync11(SYSTEM_CONF, "utf8");
|
|
12386
12421
|
const set = (key, value) => {
|
|
12387
12422
|
const re = new RegExp(`^#?\\s*${key}=.*$`, "m");
|
|
12388
12423
|
if (re.test(conf))
|
|
@@ -12499,7 +12534,7 @@ ${items.map((item) => `- ${item}`).join(`
|
|
|
12499
12534
|
|
|
12500
12535
|
// src/cli/index.ts
|
|
12501
12536
|
import { rmSync as rmSync2 } from "fs";
|
|
12502
|
-
import { readFileSync as
|
|
12537
|
+
import { readFileSync as readFileSync12 } from "fs";
|
|
12503
12538
|
var program2 = new Command;
|
|
12504
12539
|
function printJsonOrText(data, text, json = false) {
|
|
12505
12540
|
if (json || program2.opts().quiet) {
|
|
@@ -12831,7 +12866,7 @@ manifestCommand.command("add").description("Add or replace a machine in the flee
|
|
|
12831
12866
|
console.error("error: --from-stdin requires piped input");
|
|
12832
12867
|
process.exit(1);
|
|
12833
12868
|
}
|
|
12834
|
-
const input =
|
|
12869
|
+
const input = readFileSync12(0, "utf8");
|
|
12835
12870
|
const machine2 = JSON.parse(input);
|
|
12836
12871
|
console.log(JSON.stringify(manifestAdd(machine2), null, 2));
|
|
12837
12872
|
return;
|
|
@@ -13160,13 +13195,25 @@ program2.command("route").description("Resolve the best route for a machine").re
|
|
|
13160
13195
|
}
|
|
13161
13196
|
console.log(options.privateMetadata ? command2 ?? `${resolved.route}:${resolved.target}` : `${publicResolved.route}:${publicResolved.target ?? "unresolved"}`);
|
|
13162
13197
|
});
|
|
13163
|
-
program2.command("ssh").description("Choose the best SSH route for a machine").requiredOption("--machine <id>", "Machine identifier").option("--cmd <command>", "Remote command to run").option("-j, --json", "Print JSON output", false).action((options) => {
|
|
13198
|
+
program2.command("ssh").description("Choose the best SSH route for a machine").requiredOption("--machine <id>", "Machine identifier").option("--cmd <command>", "Remote command to run").option("--private-metadata", "Print private SSH target and command", false).option("-j, --json", "Print JSON output", false).action((options) => {
|
|
13199
|
+
const resolved = resolveMachineRoute(options.machine);
|
|
13200
|
+
const publicResolved = redactRouteForOutput(resolved, { privateMetadata: options.privateMetadata });
|
|
13201
|
+
const command2 = resolved.ok && options.privateMetadata ? buildSshCommand(options.machine, options.cmd) : resolved.ok ? REDACTED_VALUE : null;
|
|
13164
13202
|
if (options.json) {
|
|
13165
|
-
|
|
13166
|
-
console.log(JSON.stringify({ resolved, command: resolved.ok ? buildSshCommand(options.machine, options.cmd) : null }, null, 2));
|
|
13203
|
+
console.log(JSON.stringify({ resolved: publicResolved, command: command2 }, null, 2));
|
|
13167
13204
|
return;
|
|
13168
13205
|
}
|
|
13169
|
-
|
|
13206
|
+
if (!resolved.ok) {
|
|
13207
|
+
console.error(source_default.red(resolved.warnings.join("; ") || `No route found for ${options.machine}`));
|
|
13208
|
+
process.exitCode = 1;
|
|
13209
|
+
return;
|
|
13210
|
+
}
|
|
13211
|
+
if (!options.privateMetadata) {
|
|
13212
|
+
console.error(source_default.red("Refusing to print private SSH target; rerun with --private-metadata."));
|
|
13213
|
+
process.exitCode = 1;
|
|
13214
|
+
return;
|
|
13215
|
+
}
|
|
13216
|
+
console.log(command2);
|
|
13170
13217
|
});
|
|
13171
13218
|
program2.command("screen").description("Open Screen Sharing (VNC) to a machine using its best live route").argument("[machine]", "Machine identifier").option("--machine <id>", "Machine identifier (alternative to positional arg)").option("--all", "Open every reachable machine", false).option("--print", "Print the vnc:// URL instead of opening it", false).option("-j, --json", "Print JSON output", false).action((machineArg, options) => {
|
|
13172
13219
|
if (options.all) {
|
|
@@ -13314,8 +13361,8 @@ storageCommand.command("sync").description("Bidirectional storage sync: pull the
|
|
|
13314
13361
|
printStorageError(error);
|
|
13315
13362
|
}
|
|
13316
13363
|
});
|
|
13317
|
-
program2.command("status").description("Print local machine and storage status").option("-j, --json", "Print JSON output", false).action((options) => {
|
|
13318
|
-
const status = getStatus();
|
|
13364
|
+
program2.command("status").description("Print local machine and storage status").option("--private-metadata", "Print private local paths and machine identifiers", false).option("-j, --json", "Print JSON output", false).action((options) => {
|
|
13365
|
+
const status = getStatus({ privateMetadata: options.privateMetadata });
|
|
13319
13366
|
printJsonOrText(status, renderFleetStatus(status), options.json);
|
|
13320
13367
|
});
|
|
13321
13368
|
program2.command("doctor").description("Run machine preflight checks").option("--machine <id>", "Machine identifier").option("-j, --json", "Print JSON output", false).action((options) => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"self-test.d.ts","sourceRoot":"","sources":["../../src/commands/self-test.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAiB,cAAc,EAAE,MAAM,aAAa,CAAC;AAMjE,wBAAgB,WAAW,IAAI,cAAc,
|
|
1
|
+
{"version":3,"file":"self-test.d.ts","sourceRoot":"","sources":["../../src/commands/self-test.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAiB,cAAc,EAAE,MAAM,aAAa,CAAC;AAMjE,wBAAgB,WAAW,IAAI,cAAc,CAmD5C"}
|
|
@@ -1,3 +1,6 @@
|
|
|
1
1
|
import type { FleetStatus } from "../types.js";
|
|
2
|
-
export
|
|
2
|
+
export interface FleetStatusOptions {
|
|
3
|
+
privateMetadata?: boolean;
|
|
4
|
+
}
|
|
5
|
+
export declare function getStatus(options?: FleetStatusOptions): FleetStatus;
|
|
3
6
|
//# sourceMappingURL=status.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../../src/commands/status.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../../src/commands/status.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAY/C,MAAM,WAAW,kBAAkB;IACjC,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;AAED,wBAAgB,SAAS,CAAC,OAAO,GAAE,kBAAuB,GAAG,WAAW,CAoCvE"}
|
package/dist/index.js
CHANGED
|
@@ -13728,8 +13728,8 @@ function renderDomainMapping(domain) {
|
|
|
13728
13728
|
}
|
|
13729
13729
|
// src/commands/daemon.ts
|
|
13730
13730
|
import { execFileSync as execFileSync2 } from "child_process";
|
|
13731
|
-
import { chmodSync, existsSync as existsSync6, mkdirSync as mkdirSync2, writeFileSync as writeFileSync3 } from "fs";
|
|
13732
|
-
import { dirname as dirname4 } from "path";
|
|
13731
|
+
import { chmodSync, existsSync as existsSync6, readFileSync as readFileSync4, statSync, mkdirSync as mkdirSync2, writeFileSync as writeFileSync3 } from "fs";
|
|
13732
|
+
import { delimiter, dirname as dirname4 } from "path";
|
|
13733
13733
|
import { platform as osPlatform } from "os";
|
|
13734
13734
|
var DEFAULT_SERVICE_NAME = "machines-agent";
|
|
13735
13735
|
var DEFAULT_EXECUTABLE = "/usr/local/bin/machines-agent";
|
|
@@ -14142,13 +14142,46 @@ WantedBy=${options.mode === "system" ? "multi-user.target" : "default.target"}
|
|
|
14142
14142
|
`;
|
|
14143
14143
|
}
|
|
14144
14144
|
function daemonProgramArguments(options) {
|
|
14145
|
-
const bunRuntime =
|
|
14145
|
+
const bunRuntime = bunRuntimeForExecutable(options.executable);
|
|
14146
14146
|
const base = bunRuntime ? [bunRuntime, options.executable] : [options.executable];
|
|
14147
14147
|
return [...base, "--interval-ms", String(options.intervalMs)];
|
|
14148
14148
|
}
|
|
14149
|
-
function
|
|
14150
|
-
|
|
14151
|
-
|
|
14149
|
+
function bunRuntimeForExecutable(executable) {
|
|
14150
|
+
if (!isBunShebangScript(executable))
|
|
14151
|
+
return null;
|
|
14152
|
+
for (const candidate of bunRuntimeCandidates(executable)) {
|
|
14153
|
+
if (isExecutableFile(candidate))
|
|
14154
|
+
return candidate;
|
|
14155
|
+
}
|
|
14156
|
+
return null;
|
|
14157
|
+
}
|
|
14158
|
+
function bunRuntimeCandidates(executable) {
|
|
14159
|
+
const candidates = [
|
|
14160
|
+
`${dirname4(executable)}/bun`,
|
|
14161
|
+
process.env["BUN_INSTALL"] ? `${process.env["BUN_INSTALL"]}/bin/bun` : null,
|
|
14162
|
+
process.env["HOME"] ? `${process.env["HOME"]}/.bun/bin/bun` : null,
|
|
14163
|
+
...(process.env["PATH"] ?? "").split(delimiter).filter(Boolean).map((entry) => `${entry}/bun`)
|
|
14164
|
+
].filter((value) => Boolean(value));
|
|
14165
|
+
return [...new Set(candidates)];
|
|
14166
|
+
}
|
|
14167
|
+
function isBunShebangScript(executable) {
|
|
14168
|
+
try {
|
|
14169
|
+
const content = readFileSync4(executable, "utf8").slice(0, 256);
|
|
14170
|
+
const firstLine2 = content.split(/\r?\n/, 1)[0] ?? "";
|
|
14171
|
+
return /^#!.*\bbun\b/.test(firstLine2);
|
|
14172
|
+
} catch {
|
|
14173
|
+
return false;
|
|
14174
|
+
}
|
|
14175
|
+
}
|
|
14176
|
+
function isExecutableFile(path) {
|
|
14177
|
+
if (!existsSync6(path))
|
|
14178
|
+
return false;
|
|
14179
|
+
try {
|
|
14180
|
+
const stats = statSync(path);
|
|
14181
|
+
return stats.isFile() && (stats.mode & 73) !== 0;
|
|
14182
|
+
} catch {
|
|
14183
|
+
return false;
|
|
14184
|
+
}
|
|
14152
14185
|
}
|
|
14153
14186
|
function launchdDomain(options) {
|
|
14154
14187
|
return options.mode === "system" ? "system" : "gui/$UID";
|
|
@@ -14430,7 +14463,7 @@ function runTailscaleInstall(machineId, options = {}, runner = runMachineCommand
|
|
|
14430
14463
|
};
|
|
14431
14464
|
}
|
|
14432
14465
|
// src/commands/notifications.ts
|
|
14433
|
-
import { existsSync as existsSync7, readFileSync as
|
|
14466
|
+
import { existsSync as existsSync7, readFileSync as readFileSync5, writeFileSync as writeFileSync4 } from "fs";
|
|
14434
14467
|
var notificationChannelSchema = exports_external.object({
|
|
14435
14468
|
id: exports_external.string(),
|
|
14436
14469
|
type: exports_external.enum(["email", "webhook", "command"]),
|
|
@@ -14589,7 +14622,7 @@ function readNotificationConfig(path = getNotificationsPath()) {
|
|
|
14589
14622
|
if (!existsSync7(path)) {
|
|
14590
14623
|
return getDefaultNotificationConfig();
|
|
14591
14624
|
}
|
|
14592
|
-
return notificationConfigSchema.parse(JSON.parse(
|
|
14625
|
+
return notificationConfigSchema.parse(JSON.parse(readFileSync5(path, "utf8")));
|
|
14593
14626
|
}
|
|
14594
14627
|
function writeNotificationConfig(config, path = getNotificationsPath()) {
|
|
14595
14628
|
ensureParentDir(path);
|
|
@@ -14830,7 +14863,8 @@ function parseJsonObject2(value) {
|
|
|
14830
14863
|
return null;
|
|
14831
14864
|
}
|
|
14832
14865
|
}
|
|
14833
|
-
function getStatus() {
|
|
14866
|
+
function getStatus(options = {}) {
|
|
14867
|
+
const privateMetadata = options.privateMetadata === true;
|
|
14834
14868
|
const manifest = readManifest();
|
|
14835
14869
|
const heartbeats = listHeartbeats();
|
|
14836
14870
|
const heartbeatByMachine = latestHeartbeatByMachine(heartbeats);
|
|
@@ -14839,17 +14873,17 @@ function getStatus() {
|
|
|
14839
14873
|
...heartbeats.map((heartbeat) => heartbeat.machine_id)
|
|
14840
14874
|
]);
|
|
14841
14875
|
return {
|
|
14842
|
-
machineId: getLocalMachineId(),
|
|
14843
|
-
manifestPath: getManifestPath(),
|
|
14844
|
-
dbPath: getDbPath(),
|
|
14845
|
-
notificationsPath: getNotificationsPath(),
|
|
14876
|
+
machineId: privateMetadata ? getLocalMachineId() : REDACTED_VALUE,
|
|
14877
|
+
manifestPath: privateMetadata ? getManifestPath() : REDACTED_VALUE,
|
|
14878
|
+
dbPath: privateMetadata ? getDbPath() : REDACTED_VALUE,
|
|
14879
|
+
notificationsPath: privateMetadata ? getNotificationsPath() : REDACTED_VALUE,
|
|
14846
14880
|
manifestMachineCount: manifest.machines.length,
|
|
14847
14881
|
heartbeatCount: heartbeats.length,
|
|
14848
14882
|
machines: [...machineIds].sort().map((machineId) => {
|
|
14849
14883
|
const declared = manifest.machines.find((machine) => machine.id === machineId);
|
|
14850
14884
|
const heartbeat = heartbeatByMachine.get(machineId);
|
|
14851
14885
|
return {
|
|
14852
|
-
machineId,
|
|
14886
|
+
machineId: privateMetadata ? machineId : REDACTED_VALUE,
|
|
14853
14887
|
platform: declared?.platform,
|
|
14854
14888
|
manifestDeclared: Boolean(declared),
|
|
14855
14889
|
heartbeatStatus: heartbeat?.status || "unknown",
|
|
@@ -14857,7 +14891,7 @@ function getStatus() {
|
|
|
14857
14891
|
daemonVersion: heartbeat?.daemon_version ?? null,
|
|
14858
14892
|
agentMode: heartbeat?.agent_mode ?? null,
|
|
14859
14893
|
storageSyncStatus: heartbeat?.storage_sync_status ?? null,
|
|
14860
|
-
doctorSummary: parseJsonObject2(heartbeat?.doctor_summary_json),
|
|
14894
|
+
doctorSummary: privateMetadata ? parseJsonObject2(heartbeat?.doctor_summary_json) : null,
|
|
14861
14895
|
privateMetadata: Boolean(heartbeat?.private_metadata)
|
|
14862
14896
|
};
|
|
14863
14897
|
}),
|
|
@@ -15099,7 +15133,7 @@ function startDashboardServer(options = {}) {
|
|
|
15099
15133
|
return Response.json({ ok: true, ...getServeInfo(options) });
|
|
15100
15134
|
}
|
|
15101
15135
|
if (url.pathname === "/api/status") {
|
|
15102
|
-
return Response.json(getStatus());
|
|
15136
|
+
return Response.json(appendWarnings(getStatus({ privateMetadata }), privateWarnings));
|
|
15103
15137
|
}
|
|
15104
15138
|
if (url.pathname === "/api/topology") {
|
|
15105
15139
|
const topology = discoverMachineTopology({ includeTailscale: url.searchParams.get("tailscale") !== "false" });
|
|
@@ -15237,15 +15271,16 @@ function check(id, status, summary, detail) {
|
|
|
15237
15271
|
function runSelfTest() {
|
|
15238
15272
|
const version = getPackageVersion();
|
|
15239
15273
|
const status = getStatus();
|
|
15274
|
+
const machineId = getLocalMachineId();
|
|
15240
15275
|
const doctor = runDoctor();
|
|
15241
15276
|
const serveInfo = getServeInfo();
|
|
15242
15277
|
const html = renderDashboardHtml();
|
|
15243
15278
|
const notifications = listNotificationChannels();
|
|
15244
|
-
const apps = listApps(
|
|
15245
|
-
const appsDiff = diffApps(
|
|
15246
|
-
const cliPlan = buildClaudeInstallPlan(
|
|
15279
|
+
const apps = listApps(machineId);
|
|
15280
|
+
const appsDiff = diffApps(machineId);
|
|
15281
|
+
const cliPlan = buildClaudeInstallPlan(machineId);
|
|
15247
15282
|
return {
|
|
15248
|
-
machineId
|
|
15283
|
+
machineId,
|
|
15249
15284
|
checks: [
|
|
15250
15285
|
check("package-version", version === "0.0.0" ? "fail" : "ok", "Package version resolves", version),
|
|
15251
15286
|
check("status", "ok", "Status loads", JSON.stringify({ machines: status.manifestMachineCount, heartbeats: status.heartbeatCount })),
|
|
@@ -15526,7 +15561,7 @@ function buildScreenEnableCommand(machineId, options = {}) {
|
|
|
15526
15561
|
};
|
|
15527
15562
|
}
|
|
15528
15563
|
// src/commands/sync.ts
|
|
15529
|
-
import { existsSync as existsSync8, lstatSync, readFileSync as
|
|
15564
|
+
import { existsSync as existsSync8, lstatSync, readFileSync as readFileSync6, symlinkSync, copyFileSync } from "fs";
|
|
15530
15565
|
import { homedir as homedir5 } from "os";
|
|
15531
15566
|
function quote4(value) {
|
|
15532
15567
|
return `'${value.replace(/'/g, `'\\''`)}'`;
|
|
@@ -15586,8 +15621,8 @@ function detectFileActions(machine) {
|
|
|
15586
15621
|
if (file.mode === "symlink") {
|
|
15587
15622
|
status = lstatSync(file.target).isSymbolicLink() ? "ok" : "drifted";
|
|
15588
15623
|
} else {
|
|
15589
|
-
const source =
|
|
15590
|
-
const target =
|
|
15624
|
+
const source = readFileSync6(file.source, "utf8");
|
|
15625
|
+
const target = readFileSync6(file.target, "utf8");
|
|
15591
15626
|
status = source === target ? "ok" : "drifted";
|
|
15592
15627
|
}
|
|
15593
15628
|
}
|
|
@@ -24815,14 +24850,17 @@ function privateOutputWarnings2(requested, allowed) {
|
|
|
24815
24850
|
function appendWarnings2(payload, warnings) {
|
|
24816
24851
|
if (warnings.length === 0)
|
|
24817
24852
|
return payload;
|
|
24818
|
-
|
|
24853
|
+
const currentWarnings = typeof payload === "object" && payload && "warnings" in payload && Array.isArray(payload.warnings) ? payload.warnings : [];
|
|
24854
|
+
return { ...payload, warnings: [...currentWarnings, ...warnings] };
|
|
24819
24855
|
}
|
|
24820
24856
|
function createMcpServer(version2) {
|
|
24821
24857
|
const server = new McpServer({ name: "machines", version: version2 });
|
|
24822
24858
|
const events = new EventsClient3;
|
|
24823
|
-
server.tool("machines_status", "Return local machine fleet status paths and machine identity.", {}, async () =>
|
|
24824
|
-
|
|
24825
|
-
|
|
24859
|
+
server.tool("machines_status", "Return local machine fleet status paths and machine identity.", { private_metadata: exports_external.boolean().optional().describe("Include private local paths and machine identifiers") }, async ({ private_metadata }) => {
|
|
24860
|
+
const privateMetadata = privateMetadataAllowed(private_metadata);
|
|
24861
|
+
const warnings = privateOutputWarnings2(private_metadata, privateMetadata);
|
|
24862
|
+
return { content: [{ type: "text", text: JSON.stringify(appendWarnings2(getStatus({ privateMetadata }), warnings), null, 2) }] };
|
|
24863
|
+
});
|
|
24826
24864
|
server.tool("machines_doctor", "Run machine preflight checks.", { machine_id: exports_external.string().optional().describe("Machine identifier") }, async ({ machine_id }) => ({ content: [{ type: "text", text: JSON.stringify(runDoctor(machine_id), null, 2) }] }));
|
|
24827
24865
|
server.tool("machines_self_test", "Run local package smoke checks.", {}, async () => ({
|
|
24828
24866
|
content: [{ type: "text", text: JSON.stringify(runSelfTest(), null, 2) }]
|
|
@@ -25003,9 +25041,23 @@ function createMcpServer(version2) {
|
|
|
25003
25041
|
}), null, 2)
|
|
25004
25042
|
}]
|
|
25005
25043
|
}));
|
|
25006
|
-
server.tool("machines_ssh_resolve", "Resolve the best SSH route for a machine.", {
|
|
25007
|
-
|
|
25008
|
-
|
|
25044
|
+
server.tool("machines_ssh_resolve", "Resolve the best SSH route for a machine.", {
|
|
25045
|
+
machine_id: exports_external.string().describe("Machine identifier"),
|
|
25046
|
+
remote_command: exports_external.string().optional().describe("Optional remote command"),
|
|
25047
|
+
private_metadata: exports_external.boolean().optional().describe("Include private SSH target and command")
|
|
25048
|
+
}, async ({ machine_id, remote_command, private_metadata }) => {
|
|
25049
|
+
const privateMetadata = privateMetadataAllowed(private_metadata);
|
|
25050
|
+
const warnings = privateOutputWarnings2(private_metadata, privateMetadata);
|
|
25051
|
+
const resolved = resolveMachineRoute(machine_id);
|
|
25052
|
+
const publicResolved = redactRouteForOutput(resolved, { privateMetadata });
|
|
25053
|
+
const command2 = resolved.ok && privateMetadata ? buildSshCommand(machine_id, remote_command) : resolved.ok ? "[redacted]" : null;
|
|
25054
|
+
return {
|
|
25055
|
+
content: [{
|
|
25056
|
+
type: "text",
|
|
25057
|
+
text: JSON.stringify(appendWarnings2({ resolved: publicResolved, command: command2 }, warnings), null, 2)
|
|
25058
|
+
}]
|
|
25059
|
+
};
|
|
25060
|
+
});
|
|
25009
25061
|
server.tool("machines_ports", "List listening ports on a machine.", { machine_id: exports_external.string().optional().describe("Machine identifier") }, async ({ machine_id }) => ({
|
|
25010
25062
|
content: [{ type: "text", text: JSON.stringify(listPorts(machine_id), null, 2) }]
|
|
25011
25063
|
}));
|
package/dist/mcp/index.js
CHANGED
|
@@ -5954,8 +5954,8 @@ function diffMachines(leftMachineId, rightMachineId) {
|
|
|
5954
5954
|
}
|
|
5955
5955
|
|
|
5956
5956
|
// src/commands/daemon.ts
|
|
5957
|
-
import { chmodSync, existsSync as existsSync6, mkdirSync as mkdirSync2, writeFileSync as writeFileSync3 } from "fs";
|
|
5958
|
-
import { dirname as dirname4 } from "path";
|
|
5957
|
+
import { chmodSync, existsSync as existsSync6, readFileSync as readFileSync4, statSync, mkdirSync as mkdirSync2, writeFileSync as writeFileSync3 } from "fs";
|
|
5958
|
+
import { delimiter, dirname as dirname4 } from "path";
|
|
5959
5959
|
import { platform as osPlatform } from "os";
|
|
5960
5960
|
var DEFAULT_SERVICE_NAME = "machines-agent";
|
|
5961
5961
|
var DEFAULT_EXECUTABLE = "/usr/local/bin/machines-agent";
|
|
@@ -6219,13 +6219,46 @@ WantedBy=${options.mode === "system" ? "multi-user.target" : "default.target"}
|
|
|
6219
6219
|
`;
|
|
6220
6220
|
}
|
|
6221
6221
|
function daemonProgramArguments(options) {
|
|
6222
|
-
const bunRuntime =
|
|
6222
|
+
const bunRuntime = bunRuntimeForExecutable(options.executable);
|
|
6223
6223
|
const base = bunRuntime ? [bunRuntime, options.executable] : [options.executable];
|
|
6224
6224
|
return [...base, "--interval-ms", String(options.intervalMs)];
|
|
6225
6225
|
}
|
|
6226
|
-
function
|
|
6227
|
-
|
|
6228
|
-
|
|
6226
|
+
function bunRuntimeForExecutable(executable) {
|
|
6227
|
+
if (!isBunShebangScript(executable))
|
|
6228
|
+
return null;
|
|
6229
|
+
for (const candidate of bunRuntimeCandidates(executable)) {
|
|
6230
|
+
if (isExecutableFile(candidate))
|
|
6231
|
+
return candidate;
|
|
6232
|
+
}
|
|
6233
|
+
return null;
|
|
6234
|
+
}
|
|
6235
|
+
function bunRuntimeCandidates(executable) {
|
|
6236
|
+
const candidates = [
|
|
6237
|
+
`${dirname4(executable)}/bun`,
|
|
6238
|
+
process.env["BUN_INSTALL"] ? `${process.env["BUN_INSTALL"]}/bin/bun` : null,
|
|
6239
|
+
process.env["HOME"] ? `${process.env["HOME"]}/.bun/bin/bun` : null,
|
|
6240
|
+
...(process.env["PATH"] ?? "").split(delimiter).filter(Boolean).map((entry) => `${entry}/bun`)
|
|
6241
|
+
].filter((value) => Boolean(value));
|
|
6242
|
+
return [...new Set(candidates)];
|
|
6243
|
+
}
|
|
6244
|
+
function isBunShebangScript(executable) {
|
|
6245
|
+
try {
|
|
6246
|
+
const content = readFileSync4(executable, "utf8").slice(0, 256);
|
|
6247
|
+
const firstLine = content.split(/\r?\n/, 1)[0] ?? "";
|
|
6248
|
+
return /^#!.*\bbun\b/.test(firstLine);
|
|
6249
|
+
} catch {
|
|
6250
|
+
return false;
|
|
6251
|
+
}
|
|
6252
|
+
}
|
|
6253
|
+
function isExecutableFile(path) {
|
|
6254
|
+
if (!existsSync6(path))
|
|
6255
|
+
return false;
|
|
6256
|
+
try {
|
|
6257
|
+
const stats = statSync(path);
|
|
6258
|
+
return stats.isFile() && (stats.mode & 73) !== 0;
|
|
6259
|
+
} catch {
|
|
6260
|
+
return false;
|
|
6261
|
+
}
|
|
6229
6262
|
}
|
|
6230
6263
|
function launchdDomain(options) {
|
|
6231
6264
|
return options.mode === "system" ? "system" : "gui/$UID";
|
|
@@ -6621,7 +6654,7 @@ function runTailscaleInstall(machineId, options = {}, runner = runMachineCommand
|
|
|
6621
6654
|
}
|
|
6622
6655
|
|
|
6623
6656
|
// src/commands/notifications.ts
|
|
6624
|
-
import { existsSync as existsSync7, readFileSync as
|
|
6657
|
+
import { existsSync as existsSync7, readFileSync as readFileSync5, writeFileSync as writeFileSync4 } from "fs";
|
|
6625
6658
|
var notificationChannelSchema = exports_external.object({
|
|
6626
6659
|
id: exports_external.string(),
|
|
6627
6660
|
type: exports_external.enum(["email", "webhook", "command"]),
|
|
@@ -6780,7 +6813,7 @@ function readNotificationConfig(path = getNotificationsPath()) {
|
|
|
6780
6813
|
if (!existsSync7(path)) {
|
|
6781
6814
|
return getDefaultNotificationConfig();
|
|
6782
6815
|
}
|
|
6783
|
-
return notificationConfigSchema.parse(JSON.parse(
|
|
6816
|
+
return notificationConfigSchema.parse(JSON.parse(readFileSync5(path, "utf8")));
|
|
6784
6817
|
}
|
|
6785
6818
|
function writeNotificationConfig(config, path = getNotificationsPath()) {
|
|
6786
6819
|
ensureParentDir(path);
|
|
@@ -7408,7 +7441,8 @@ function parseJsonObject2(value) {
|
|
|
7408
7441
|
return null;
|
|
7409
7442
|
}
|
|
7410
7443
|
}
|
|
7411
|
-
function getStatus() {
|
|
7444
|
+
function getStatus(options = {}) {
|
|
7445
|
+
const privateMetadata = options.privateMetadata === true;
|
|
7412
7446
|
const manifest = readManifest();
|
|
7413
7447
|
const heartbeats = listHeartbeats();
|
|
7414
7448
|
const heartbeatByMachine = latestHeartbeatByMachine(heartbeats);
|
|
@@ -7417,17 +7451,17 @@ function getStatus() {
|
|
|
7417
7451
|
...heartbeats.map((heartbeat) => heartbeat.machine_id)
|
|
7418
7452
|
]);
|
|
7419
7453
|
return {
|
|
7420
|
-
machineId: getLocalMachineId(),
|
|
7421
|
-
manifestPath: getManifestPath(),
|
|
7422
|
-
dbPath: getDbPath(),
|
|
7423
|
-
notificationsPath: getNotificationsPath(),
|
|
7454
|
+
machineId: privateMetadata ? getLocalMachineId() : REDACTED_VALUE,
|
|
7455
|
+
manifestPath: privateMetadata ? getManifestPath() : REDACTED_VALUE,
|
|
7456
|
+
dbPath: privateMetadata ? getDbPath() : REDACTED_VALUE,
|
|
7457
|
+
notificationsPath: privateMetadata ? getNotificationsPath() : REDACTED_VALUE,
|
|
7424
7458
|
manifestMachineCount: manifest.machines.length,
|
|
7425
7459
|
heartbeatCount: heartbeats.length,
|
|
7426
7460
|
machines: [...machineIds].sort().map((machineId) => {
|
|
7427
7461
|
const declared = manifest.machines.find((machine) => machine.id === machineId);
|
|
7428
7462
|
const heartbeat = heartbeatByMachine.get(machineId);
|
|
7429
7463
|
return {
|
|
7430
|
-
machineId,
|
|
7464
|
+
machineId: privateMetadata ? machineId : REDACTED_VALUE,
|
|
7431
7465
|
platform: declared?.platform,
|
|
7432
7466
|
manifestDeclared: Boolean(declared),
|
|
7433
7467
|
heartbeatStatus: heartbeat?.status || "unknown",
|
|
@@ -7435,7 +7469,7 @@ function getStatus() {
|
|
|
7435
7469
|
daemonVersion: heartbeat?.daemon_version ?? null,
|
|
7436
7470
|
agentMode: heartbeat?.agent_mode ?? null,
|
|
7437
7471
|
storageSyncStatus: heartbeat?.storage_sync_status ?? null,
|
|
7438
|
-
doctorSummary: parseJsonObject2(heartbeat?.doctor_summary_json),
|
|
7472
|
+
doctorSummary: privateMetadata ? parseJsonObject2(heartbeat?.doctor_summary_json) : null,
|
|
7439
7473
|
privateMetadata: Boolean(heartbeat?.private_metadata)
|
|
7440
7474
|
};
|
|
7441
7475
|
}),
|
|
@@ -7451,15 +7485,16 @@ function check(id, status, summary, detail) {
|
|
|
7451
7485
|
function runSelfTest() {
|
|
7452
7486
|
const version = getPackageVersion();
|
|
7453
7487
|
const status = getStatus();
|
|
7488
|
+
const machineId = getLocalMachineId();
|
|
7454
7489
|
const doctor = runDoctor();
|
|
7455
7490
|
const serveInfo = getServeInfo();
|
|
7456
7491
|
const html = renderDashboardHtml();
|
|
7457
7492
|
const notifications = listNotificationChannels();
|
|
7458
|
-
const apps = listApps(
|
|
7459
|
-
const appsDiff = diffApps(
|
|
7460
|
-
const cliPlan = buildClaudeInstallPlan(
|
|
7493
|
+
const apps = listApps(machineId);
|
|
7494
|
+
const appsDiff = diffApps(machineId);
|
|
7495
|
+
const cliPlan = buildClaudeInstallPlan(machineId);
|
|
7461
7496
|
return {
|
|
7462
|
-
machineId
|
|
7497
|
+
machineId,
|
|
7463
7498
|
checks: [
|
|
7464
7499
|
check("package-version", version === "0.0.0" ? "fail" : "ok", "Package version resolves", version),
|
|
7465
7500
|
check("status", "ok", "Status loads", JSON.stringify({ machines: status.manifestMachineCount, heartbeats: status.heartbeatCount })),
|
|
@@ -7818,7 +7853,7 @@ function runSetup(machineId, options = {}, runner = runMachineCommand) {
|
|
|
7818
7853
|
}
|
|
7819
7854
|
|
|
7820
7855
|
// src/commands/sync.ts
|
|
7821
|
-
import { existsSync as existsSync8, lstatSync, readFileSync as
|
|
7856
|
+
import { existsSync as existsSync8, lstatSync, readFileSync as readFileSync6, symlinkSync, copyFileSync } from "fs";
|
|
7822
7857
|
import { homedir as homedir5 } from "os";
|
|
7823
7858
|
function quote4(value) {
|
|
7824
7859
|
return `'${value.replace(/'/g, `'\\''`)}'`;
|
|
@@ -7878,8 +7913,8 @@ function detectFileActions(machine) {
|
|
|
7878
7913
|
if (file.mode === "symlink") {
|
|
7879
7914
|
status = lstatSync(file.target).isSymbolicLink() ? "ok" : "drifted";
|
|
7880
7915
|
} else {
|
|
7881
|
-
const source =
|
|
7882
|
-
const target =
|
|
7916
|
+
const source = readFileSync6(file.source, "utf8");
|
|
7917
|
+
const target = readFileSync6(file.target, "utf8");
|
|
7883
7918
|
status = source === target ? "ok" : "drifted";
|
|
7884
7919
|
}
|
|
7885
7920
|
}
|
|
@@ -8229,14 +8264,17 @@ function privateOutputWarnings(requested, allowed) {
|
|
|
8229
8264
|
function appendWarnings(payload, warnings) {
|
|
8230
8265
|
if (warnings.length === 0)
|
|
8231
8266
|
return payload;
|
|
8232
|
-
|
|
8267
|
+
const currentWarnings = typeof payload === "object" && payload && "warnings" in payload && Array.isArray(payload.warnings) ? payload.warnings : [];
|
|
8268
|
+
return { ...payload, warnings: [...currentWarnings, ...warnings] };
|
|
8233
8269
|
}
|
|
8234
8270
|
function createMcpServer(version) {
|
|
8235
8271
|
const server = new McpServer({ name: "machines", version });
|
|
8236
8272
|
const events = new EventsClient2;
|
|
8237
|
-
server.tool("machines_status", "Return local machine fleet status paths and machine identity.", {}, async () =>
|
|
8238
|
-
|
|
8239
|
-
|
|
8273
|
+
server.tool("machines_status", "Return local machine fleet status paths and machine identity.", { private_metadata: exports_external.boolean().optional().describe("Include private local paths and machine identifiers") }, async ({ private_metadata }) => {
|
|
8274
|
+
const privateMetadata = privateMetadataAllowed(private_metadata);
|
|
8275
|
+
const warnings = privateOutputWarnings(private_metadata, privateMetadata);
|
|
8276
|
+
return { content: [{ type: "text", text: JSON.stringify(appendWarnings(getStatus({ privateMetadata }), warnings), null, 2) }] };
|
|
8277
|
+
});
|
|
8240
8278
|
server.tool("machines_doctor", "Run machine preflight checks.", { machine_id: exports_external.string().optional().describe("Machine identifier") }, async ({ machine_id }) => ({ content: [{ type: "text", text: JSON.stringify(runDoctor(machine_id), null, 2) }] }));
|
|
8241
8279
|
server.tool("machines_self_test", "Run local package smoke checks.", {}, async () => ({
|
|
8242
8280
|
content: [{ type: "text", text: JSON.stringify(runSelfTest(), null, 2) }]
|
|
@@ -8417,9 +8455,23 @@ function createMcpServer(version) {
|
|
|
8417
8455
|
}), null, 2)
|
|
8418
8456
|
}]
|
|
8419
8457
|
}));
|
|
8420
|
-
server.tool("machines_ssh_resolve", "Resolve the best SSH route for a machine.", {
|
|
8421
|
-
|
|
8422
|
-
|
|
8458
|
+
server.tool("machines_ssh_resolve", "Resolve the best SSH route for a machine.", {
|
|
8459
|
+
machine_id: exports_external.string().describe("Machine identifier"),
|
|
8460
|
+
remote_command: exports_external.string().optional().describe("Optional remote command"),
|
|
8461
|
+
private_metadata: exports_external.boolean().optional().describe("Include private SSH target and command")
|
|
8462
|
+
}, async ({ machine_id, remote_command, private_metadata }) => {
|
|
8463
|
+
const privateMetadata = privateMetadataAllowed(private_metadata);
|
|
8464
|
+
const warnings = privateOutputWarnings(private_metadata, privateMetadata);
|
|
8465
|
+
const resolved = resolveMachineRoute(machine_id);
|
|
8466
|
+
const publicResolved = redactRouteForOutput(resolved, { privateMetadata });
|
|
8467
|
+
const command2 = resolved.ok && privateMetadata ? buildSshCommand(machine_id, remote_command) : resolved.ok ? "[redacted]" : null;
|
|
8468
|
+
return {
|
|
8469
|
+
content: [{
|
|
8470
|
+
type: "text",
|
|
8471
|
+
text: JSON.stringify(appendWarnings({ resolved: publicResolved, command: command2 }, warnings), null, 2)
|
|
8472
|
+
}]
|
|
8473
|
+
};
|
|
8474
|
+
});
|
|
8423
8475
|
server.tool("machines_ports", "List listening ports on a machine.", { machine_id: exports_external.string().optional().describe("Machine identifier") }, async ({ machine_id }) => ({
|
|
8424
8476
|
content: [{ type: "text", text: JSON.stringify(listPorts(machine_id), null, 2) }]
|
|
8425
8477
|
}));
|
package/dist/mcp/server.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/mcp/server.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAiCpE,eAAO,MAAM,sBAAsB,k9CA2DzB,CAAC;AAEX,wBAAgB,WAAW,CAAC,OAAO,GAAE,MAA4B,GAAG,SAAS,CAE5E;
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/mcp/server.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAiCpE,eAAO,MAAM,sBAAsB,k9CA2DzB,CAAC;AAEX,wBAAgB,WAAW,CAAC,OAAO,GAAE,MAA4B,GAAG,SAAS,CAE5E;AAkBD,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,CAykB1D"}
|
package/dist/types.d.ts
CHANGED
|
@@ -116,6 +116,7 @@ export interface FleetStatus {
|
|
|
116
116
|
machines: FleetStatusMachine[];
|
|
117
117
|
recentSetupRuns: number;
|
|
118
118
|
recentSyncRuns: number;
|
|
119
|
+
warnings?: string[];
|
|
119
120
|
}
|
|
120
121
|
export type NotificationChannelType = "email" | "webhook" | "command";
|
|
121
122
|
export interface NotificationChannel {
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,eAAe,GAAG,OAAO,GAAG,OAAO,GAAG,SAAS,CAAC;AAC5D,MAAM,MAAM,iBAAiB,GAAG,OAAO,GAAG,KAAK,GAAG,WAAW,CAAC;AAE9D,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,KAAK,GAAG,MAAM,GAAG,KAAK,GAAG,QAAQ,CAAC;IAC5C,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,KAAK,GAAG,QAAQ,GAAG,QAAQ,CAAC;IACxD,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC3B;AAED,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,EAAE,eAAe,CAAC;IAC1B,UAAU,CAAC,EAAE,iBAAiB,CAAC;IAC/B,aAAa,EAAE,MAAM,CAAC;IACtB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACnC,QAAQ,CAAC,EAAE,mBAAmB,EAAE,CAAC;IACjC,IAAI,CAAC,EAAE,eAAe,EAAE,CAAC;IACzB,KAAK,CAAC,EAAE,oBAAoB,EAAE,CAAC;CAChC;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,CAAC,CAAC;IACX,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,eAAe,EAAE,CAAC;CAC7B;AAED,MAAM,MAAM,kBAAkB,GAAG,MAAM,GAAG,aAAa,CAAC;AAExD,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,kBAAkB,CAAC;IACzB,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,OAAO,EAAE,OAAO,CAAC;IACjB,UAAU,EAAE,IAAI,CAAC;CAClB;AAED,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,iBAAiB,CAAC;IAC1B,UAAU,EAAE,kBAAkB,GAAG,SAAS,GAAG,UAAU,CAAC;IACxD,cAAc,CAAC,EAAE,iBAAiB,CAAC;IACnC,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,QAAQ,GAAG,SAAS,CAAC;IAC7B,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,WAAW;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;IACvB,KAAK,EAAE,SAAS,EAAE,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,SAAS;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,OAAO,GAAG,KAAK,GAAG,MAAM,GAAG,KAAK,GAAG,QAAQ,CAAC;IACrD,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,MAAM,WAAW,UAAU;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;IACvB,OAAO,EAAE,UAAU,EAAE,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,IAAI,GAAG,SAAS,GAAG,SAAS,CAAC;IACrC,IAAI,EAAE,SAAS,GAAG,MAAM,GAAG,SAAS,CAAC;CACtC;AAED,MAAM,WAAW,WAAW;IAC1B,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,MAAM,CAAC;IACvB,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,eAAe,EAAE;QACf,QAAQ,EAAE,MAAM,EAAE,CAAC;QACnB,SAAS,EAAE,MAAM,EAAE,CAAC;KACrB,CAAC;IACF,YAAY,EAAE;QACZ,QAAQ,EAAE,MAAM,EAAE,CAAC;QACnB,SAAS,EAAE,MAAM,EAAE,CAAC;KACrB,CAAC;CACH;AAED,MAAM,WAAW,kBAAkB;IACjC,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,gBAAgB,EAAE,OAAO,CAAC;IAC1B,eAAe,EAAE,QAAQ,GAAG,SAAS,GAAG,SAAS,CAAC;IAClD,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,iBAAiB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IAC/C,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;AAED,MAAM,WAAW,WAAW;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,iBAAiB,EAAE,MAAM,CAAC;IAC1B,oBAAoB,EAAE,MAAM,CAAC;IAC7B,cAAc,EAAE,MAAM,CAAC;IACvB,QAAQ,EAAE,kBAAkB,EAAE,CAAC;IAC/B,eAAe,EAAE,MAAM,CAAC;IACxB,cAAc,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|