@hasna/machines 0.0.16 → 0.0.17
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 +17 -5
- package/dist/cli/index.js +387 -217
- package/dist/commands/ssh.d.ts +6 -3
- package/dist/commands/ssh.d.ts.map +1 -1
- package/dist/compatibility.d.ts +4 -0
- package/dist/compatibility.d.ts.map +1 -1
- package/dist/consumer.d.ts +10 -0
- package/dist/consumer.d.ts.map +1 -0
- package/dist/consumer.js +4983 -0
- package/dist/index.js +223 -64
- package/dist/mcp/index.js +370 -217
- package/dist/mcp/server.d.ts +1 -1
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/remote.d.ts.map +1 -1
- package/dist/topology.d.ts +45 -1
- package/dist/topology.d.ts.map +1 -1
- package/package.json +6 -2
package/dist/index.js
CHANGED
|
@@ -11085,9 +11085,31 @@ function detectCurrentMachineManifest() {
|
|
|
11085
11085
|
};
|
|
11086
11086
|
}
|
|
11087
11087
|
// src/topology.ts
|
|
11088
|
-
import { existsSync as
|
|
11088
|
+
import { existsSync as existsSync4 } from "fs";
|
|
11089
11089
|
import { arch as arch2, hostname as hostname3, platform as platform2, userInfo as userInfo2 } from "os";
|
|
11090
11090
|
import { spawnSync } from "child_process";
|
|
11091
|
+
|
|
11092
|
+
// src/version.ts
|
|
11093
|
+
import { existsSync as existsSync3, readFileSync as readFileSync2 } from "fs";
|
|
11094
|
+
import { dirname as dirname3, join as join2 } from "path";
|
|
11095
|
+
import { fileURLToPath } from "url";
|
|
11096
|
+
function getPackageVersion() {
|
|
11097
|
+
try {
|
|
11098
|
+
const here = dirname3(fileURLToPath(import.meta.url));
|
|
11099
|
+
const candidates = [join2(here, "..", "package.json"), join2(here, "..", "..", "package.json")];
|
|
11100
|
+
const pkgPath = candidates.find((candidate) => existsSync3(candidate));
|
|
11101
|
+
if (!pkgPath) {
|
|
11102
|
+
return "0.0.0";
|
|
11103
|
+
}
|
|
11104
|
+
return JSON.parse(readFileSync2(pkgPath, "utf8")).version || "0.0.0";
|
|
11105
|
+
} catch {
|
|
11106
|
+
return "0.0.0";
|
|
11107
|
+
}
|
|
11108
|
+
}
|
|
11109
|
+
|
|
11110
|
+
// src/topology.ts
|
|
11111
|
+
var MACHINES_CONSUMER_CONTRACT_VERSION = 1;
|
|
11112
|
+
var MACHINES_PACKAGE_NAME = "@hasna/machines";
|
|
11091
11113
|
function normalizePlatform2(value = platform2()) {
|
|
11092
11114
|
const normalized = value.toLowerCase();
|
|
11093
11115
|
if (normalized === "darwin" || normalized === "macos")
|
|
@@ -11169,16 +11191,26 @@ function findTailscalePeer(machine, machineId, peers) {
|
|
|
11169
11191
|
}
|
|
11170
11192
|
return peers.get(machineId) ?? null;
|
|
11171
11193
|
}
|
|
11194
|
+
function envReachableHosts() {
|
|
11195
|
+
const raw = process.env["HASNA_MACHINES_REACHABLE_HOSTS"];
|
|
11196
|
+
return new Set((raw || "").split(",").map((value) => value.trim()).filter(Boolean));
|
|
11197
|
+
}
|
|
11198
|
+
function manifestHostReachable(target) {
|
|
11199
|
+
const overrides = envReachableHosts();
|
|
11200
|
+
if (overrides.size === 0)
|
|
11201
|
+
return null;
|
|
11202
|
+
return overrides.has(target);
|
|
11203
|
+
}
|
|
11172
11204
|
function routeHints(input) {
|
|
11173
11205
|
const hints = [];
|
|
11174
11206
|
if (input.machineId === input.localMachineId) {
|
|
11175
11207
|
hints.push({ kind: "local", target: "localhost", reachable: true });
|
|
11176
11208
|
}
|
|
11177
11209
|
if (input.manifest?.sshAddress) {
|
|
11178
|
-
hints.push({ kind: "ssh", target: input.manifest.sshAddress, reachable:
|
|
11210
|
+
hints.push({ kind: "ssh", target: input.manifest.sshAddress, reachable: manifestHostReachable(input.manifest.sshAddress) });
|
|
11179
11211
|
}
|
|
11180
11212
|
if (input.manifest?.hostname) {
|
|
11181
|
-
hints.push({ kind: "lan", target: input.manifest.hostname, reachable:
|
|
11213
|
+
hints.push({ kind: "lan", target: input.manifest.hostname, reachable: manifestHostReachable(input.manifest.hostname) });
|
|
11182
11214
|
}
|
|
11183
11215
|
const tailscaleTarget = input.manifest?.tailscaleName ?? input.peer?.DNSName ?? input.peer?.TailscaleIPs?.[0];
|
|
11184
11216
|
if (tailscaleTarget) {
|
|
@@ -11186,6 +11218,28 @@ function routeHints(input) {
|
|
|
11186
11218
|
}
|
|
11187
11219
|
return hints;
|
|
11188
11220
|
}
|
|
11221
|
+
function routeRank(hint) {
|
|
11222
|
+
if (hint.kind === "local")
|
|
11223
|
+
return 0;
|
|
11224
|
+
if (hint.reachable === true && hint.kind === "ssh")
|
|
11225
|
+
return 1;
|
|
11226
|
+
if (hint.reachable === true && hint.kind === "lan")
|
|
11227
|
+
return 2;
|
|
11228
|
+
if (hint.reachable === true && hint.kind === "tailscale")
|
|
11229
|
+
return 3;
|
|
11230
|
+
if (hint.reachable === false)
|
|
11231
|
+
return 8;
|
|
11232
|
+
if (hint.kind === "ssh")
|
|
11233
|
+
return 4;
|
|
11234
|
+
if (hint.kind === "lan")
|
|
11235
|
+
return 5;
|
|
11236
|
+
if (hint.kind === "tailscale")
|
|
11237
|
+
return 6;
|
|
11238
|
+
return 9;
|
|
11239
|
+
}
|
|
11240
|
+
function selectRouteHint(hints) {
|
|
11241
|
+
return [...hints].sort((left, right) => routeRank(left) - routeRank(right))[0] ?? null;
|
|
11242
|
+
}
|
|
11189
11243
|
function buildEntry(input) {
|
|
11190
11244
|
const manifest = input.manifest;
|
|
11191
11245
|
const peer = input.peer;
|
|
@@ -11195,8 +11249,8 @@ function buildEntry(input) {
|
|
|
11195
11249
|
manifest,
|
|
11196
11250
|
peer
|
|
11197
11251
|
});
|
|
11198
|
-
const selectedRoute =
|
|
11199
|
-
const route = selectedRoute?.kind === "ssh" ? "
|
|
11252
|
+
const selectedRoute = selectRouteHint(hints);
|
|
11253
|
+
const route = selectedRoute?.kind === "ssh" ? "ssh" : selectedRoute?.kind ?? "unknown";
|
|
11200
11254
|
return {
|
|
11201
11255
|
machine_id: input.machineId,
|
|
11202
11256
|
hostname: manifest?.hostname ?? peer?.HostName ?? null,
|
|
@@ -11251,15 +11305,140 @@ function discoverMachineTopology(options = {}) {
|
|
|
11251
11305
|
});
|
|
11252
11306
|
});
|
|
11253
11307
|
return {
|
|
11308
|
+
schema_version: MACHINES_CONSUMER_CONTRACT_VERSION,
|
|
11309
|
+
package: {
|
|
11310
|
+
name: MACHINES_PACKAGE_NAME,
|
|
11311
|
+
version: getPackageVersion()
|
|
11312
|
+
},
|
|
11313
|
+
capabilities: {
|
|
11314
|
+
topology: true,
|
|
11315
|
+
compatibility: true,
|
|
11316
|
+
route_resolution: true,
|
|
11317
|
+
cli_json_fallback: true
|
|
11318
|
+
},
|
|
11254
11319
|
generated_at: now.toISOString(),
|
|
11255
11320
|
local_machine_id: localMachineId,
|
|
11256
11321
|
local_hostname: hostname3(),
|
|
11257
11322
|
current_platform: normalizePlatform2(),
|
|
11258
|
-
manifest_path_known:
|
|
11323
|
+
manifest_path_known: existsSync4(getManifestPath()),
|
|
11259
11324
|
machines,
|
|
11260
11325
|
warnings
|
|
11261
11326
|
};
|
|
11262
11327
|
}
|
|
11328
|
+
function normalizeMachineAlias(value) {
|
|
11329
|
+
return value.trim().replace(/\.$/, "").toLowerCase();
|
|
11330
|
+
}
|
|
11331
|
+
function routeTargetMatches(machine, requested) {
|
|
11332
|
+
const normalized = normalizeMachineAlias(requested);
|
|
11333
|
+
const values = [
|
|
11334
|
+
machine.ssh.address,
|
|
11335
|
+
machine.ssh.command_target,
|
|
11336
|
+
machine.tailscale.dns_name,
|
|
11337
|
+
machine.tailscale.dns_name?.split(".")[0],
|
|
11338
|
+
...machine.tailscale.ips,
|
|
11339
|
+
...machine.route_hints.map((hint) => hint.target),
|
|
11340
|
+
...machine.route_hints.map((hint) => hint.target.split("@").pop() ?? hint.target)
|
|
11341
|
+
].filter((value) => Boolean(value));
|
|
11342
|
+
return values.some((value) => normalizeMachineAlias(value) === normalized);
|
|
11343
|
+
}
|
|
11344
|
+
function findRouteMachine(topology, requestedMachineId) {
|
|
11345
|
+
const requested = normalizeMachineAlias(requestedMachineId);
|
|
11346
|
+
if (requested === "local" || requested === "localhost" || requested === normalizeMachineAlias(hostname3()) || requested === normalizeMachineAlias(topology.local_machine_id)) {
|
|
11347
|
+
return {
|
|
11348
|
+
machine: topology.machines.find((machine) => machine.machine_id === topology.local_machine_id) ?? null,
|
|
11349
|
+
matchedBy: "local_alias"
|
|
11350
|
+
};
|
|
11351
|
+
}
|
|
11352
|
+
const machineIdMatch = topology.machines.find((machine) => normalizeMachineAlias(machine.machine_id) === requested);
|
|
11353
|
+
if (machineIdMatch)
|
|
11354
|
+
return { machine: machineIdMatch, matchedBy: "machine_id" };
|
|
11355
|
+
const hostnameMatch = topology.machines.find((machine) => machine.hostname && normalizeMachineAlias(machine.hostname) === requested);
|
|
11356
|
+
if (hostnameMatch)
|
|
11357
|
+
return { machine: hostnameMatch, matchedBy: "hostname" };
|
|
11358
|
+
const tailscaleMatch = topology.machines.find((machine) => {
|
|
11359
|
+
if (!machine.tailscale.dns_name)
|
|
11360
|
+
return false;
|
|
11361
|
+
const dns = normalizeMachineAlias(machine.tailscale.dns_name);
|
|
11362
|
+
return dns === requested || dns.split(".")[0] === requested;
|
|
11363
|
+
});
|
|
11364
|
+
if (tailscaleMatch)
|
|
11365
|
+
return { machine: tailscaleMatch, matchedBy: "tailscale" };
|
|
11366
|
+
const routeMatch = topology.machines.find((machine) => routeTargetMatches(machine, requestedMachineId));
|
|
11367
|
+
if (routeMatch)
|
|
11368
|
+
return { machine: routeMatch, matchedBy: "route_target" };
|
|
11369
|
+
return { machine: null, matchedBy: null };
|
|
11370
|
+
}
|
|
11371
|
+
function routeConfidence(input) {
|
|
11372
|
+
if (input.matchedBy === "local_alias")
|
|
11373
|
+
return "exact";
|
|
11374
|
+
if (input.hint?.kind === "local")
|
|
11375
|
+
return "exact";
|
|
11376
|
+
if (input.hint?.reachable === true)
|
|
11377
|
+
return "high";
|
|
11378
|
+
if (input.machine.manifest_declared && (input.hint?.kind === "ssh" || input.hint?.kind === "lan"))
|
|
11379
|
+
return "medium";
|
|
11380
|
+
if (input.hint)
|
|
11381
|
+
return "low";
|
|
11382
|
+
return "none";
|
|
11383
|
+
}
|
|
11384
|
+
function resolveMachineRoute(machineId, options = {}) {
|
|
11385
|
+
const topology = options.topology ?? discoverMachineTopology(options);
|
|
11386
|
+
const warnings = [...topology.warnings];
|
|
11387
|
+
const { machine, matchedBy } = findRouteMachine(topology, machineId);
|
|
11388
|
+
const generatedAt = (options.now ?? new Date).toISOString();
|
|
11389
|
+
if (!machine) {
|
|
11390
|
+
warnings.push(`machine_not_found:${machineId}`);
|
|
11391
|
+
return {
|
|
11392
|
+
schema_version: MACHINES_CONSUMER_CONTRACT_VERSION,
|
|
11393
|
+
package: { name: MACHINES_PACKAGE_NAME, version: getPackageVersion() },
|
|
11394
|
+
ok: false,
|
|
11395
|
+
machine_id: null,
|
|
11396
|
+
requested_machine_id: machineId,
|
|
11397
|
+
generated_at: generatedAt,
|
|
11398
|
+
route: "unknown",
|
|
11399
|
+
source: "unknown",
|
|
11400
|
+
target: null,
|
|
11401
|
+
command_target: null,
|
|
11402
|
+
confidence: "none",
|
|
11403
|
+
local: false,
|
|
11404
|
+
evidence: {
|
|
11405
|
+
topology: true,
|
|
11406
|
+
matched_by: null,
|
|
11407
|
+
manifest_declared: null,
|
|
11408
|
+
heartbeat_status: null,
|
|
11409
|
+
tailscale_online: null,
|
|
11410
|
+
selected_hint: null
|
|
11411
|
+
},
|
|
11412
|
+
warnings
|
|
11413
|
+
};
|
|
11414
|
+
}
|
|
11415
|
+
const selectedHint = selectRouteHint(machine.route_hints);
|
|
11416
|
+
const route = selectedHint?.kind ?? machine.ssh.route ?? "unknown";
|
|
11417
|
+
const local = route === "local" || machine.machine_id === topology.local_machine_id;
|
|
11418
|
+
return {
|
|
11419
|
+
schema_version: MACHINES_CONSUMER_CONTRACT_VERSION,
|
|
11420
|
+
package: topology.package,
|
|
11421
|
+
ok: Boolean(selectedHint?.target),
|
|
11422
|
+
machine_id: machine.machine_id,
|
|
11423
|
+
requested_machine_id: machineId,
|
|
11424
|
+
generated_at: generatedAt,
|
|
11425
|
+
route,
|
|
11426
|
+
source: route,
|
|
11427
|
+
target: selectedHint?.target ?? null,
|
|
11428
|
+
command_target: selectedHint?.target ?? null,
|
|
11429
|
+
confidence: routeConfidence({ machine, hint: selectedHint, matchedBy }),
|
|
11430
|
+
local,
|
|
11431
|
+
evidence: {
|
|
11432
|
+
topology: true,
|
|
11433
|
+
matched_by: matchedBy,
|
|
11434
|
+
manifest_declared: machine.manifest_declared,
|
|
11435
|
+
heartbeat_status: machine.heartbeat_status,
|
|
11436
|
+
tailscale_online: machine.tailscale.online,
|
|
11437
|
+
selected_hint: selectedHint
|
|
11438
|
+
},
|
|
11439
|
+
warnings
|
|
11440
|
+
};
|
|
11441
|
+
}
|
|
11263
11442
|
function getLocalMachineTopology(options = {}) {
|
|
11264
11443
|
const topology = discoverMachineTopology(options);
|
|
11265
11444
|
return topology.machines.find((machine) => machine.machine_id === topology.local_machine_id) ?? {
|
|
@@ -11280,49 +11459,28 @@ function getLocalMachineTopology(options = {}) {
|
|
|
11280
11459
|
};
|
|
11281
11460
|
}
|
|
11282
11461
|
// src/remote.ts
|
|
11283
|
-
import { spawnSync as
|
|
11462
|
+
import { spawnSync as spawnSync2 } from "child_process";
|
|
11284
11463
|
import { hostname as hostname4 } from "os";
|
|
11285
11464
|
|
|
11286
11465
|
// src/commands/ssh.ts
|
|
11287
|
-
|
|
11288
|
-
|
|
11289
|
-
|
|
11290
|
-
|
|
11291
|
-
}
|
|
11292
|
-
function isReachable(host) {
|
|
11293
|
-
const overrides = envReachableHosts();
|
|
11294
|
-
if (overrides.size > 0) {
|
|
11295
|
-
return overrides.has(host);
|
|
11296
|
-
}
|
|
11297
|
-
const probe = spawnSync2("bash", ["-lc", `getent hosts ${host} >/dev/null 2>&1 || ping -c 1 -W 1 ${host} >/dev/null 2>&1`], {
|
|
11298
|
-
stdio: "ignore"
|
|
11299
|
-
});
|
|
11300
|
-
return probe.status === 0;
|
|
11301
|
-
}
|
|
11302
|
-
function resolveSshTarget(machineId) {
|
|
11303
|
-
const machine = getManifestMachine(machineId);
|
|
11304
|
-
if (!machine) {
|
|
11305
|
-
throw new Error(`Machine not found in manifest: ${machineId}`);
|
|
11466
|
+
function resolveSshTarget(machineId, options = {}) {
|
|
11467
|
+
const resolved = resolveMachineRoute(machineId, options);
|
|
11468
|
+
if (!resolved.ok || !resolved.target) {
|
|
11469
|
+
throw new Error(`Machine route not found: ${machineId}`);
|
|
11306
11470
|
}
|
|
11307
|
-
|
|
11308
|
-
|
|
11309
|
-
return {
|
|
11310
|
-
machineId,
|
|
11311
|
-
target: "localhost",
|
|
11312
|
-
route: "local"
|
|
11313
|
-
};
|
|
11471
|
+
if (resolved.route !== "local" && resolved.route !== "lan" && resolved.route !== "tailscale" && resolved.route !== "ssh") {
|
|
11472
|
+
throw new Error(`Machine route is not SSH-capable: ${machineId}`);
|
|
11314
11473
|
}
|
|
11315
|
-
const lanTarget = machine.sshAddress || machine.hostname || machine.id;
|
|
11316
|
-
const tailscaleTarget = machine.tailscaleName || machine.hostname || machine.id;
|
|
11317
|
-
const route = isReachable(lanTarget) ? "lan" : "tailscale";
|
|
11318
11474
|
return {
|
|
11319
|
-
machineId,
|
|
11320
|
-
target:
|
|
11321
|
-
route
|
|
11475
|
+
machineId: resolved.machine_id ?? machineId,
|
|
11476
|
+
target: resolved.target,
|
|
11477
|
+
route: resolved.route,
|
|
11478
|
+
confidence: resolved.confidence,
|
|
11479
|
+
warnings: resolved.warnings
|
|
11322
11480
|
};
|
|
11323
11481
|
}
|
|
11324
|
-
function buildSshCommand(machineId, remoteCommand) {
|
|
11325
|
-
const resolved = resolveSshTarget(machineId);
|
|
11482
|
+
function buildSshCommand(machineId, remoteCommand, options = {}) {
|
|
11483
|
+
const resolved = resolveSshTarget(machineId, options);
|
|
11326
11484
|
return remoteCommand ? `ssh ${resolved.target} ${JSON.stringify(remoteCommand)}` : `ssh ${resolved.target}`;
|
|
11327
11485
|
}
|
|
11328
11486
|
|
|
@@ -11343,7 +11501,8 @@ function resolveMachineCommand(machineId, command, localMachineId = getLocalMach
|
|
|
11343
11501
|
shellCommand: buildSshCommand(machineId, command)
|
|
11344
11502
|
};
|
|
11345
11503
|
} catch (error) {
|
|
11346
|
-
|
|
11504
|
+
const message = String(error.message ?? error);
|
|
11505
|
+
if (message.includes("Machine route not found") || message.includes("Machine not found in manifest")) {
|
|
11347
11506
|
return { source: "ssh", shellCommand: `ssh ${shellQuote(machineId)} ${shellQuote(command)}` };
|
|
11348
11507
|
}
|
|
11349
11508
|
throw error;
|
|
@@ -11351,7 +11510,7 @@ function resolveMachineCommand(machineId, command, localMachineId = getLocalMach
|
|
|
11351
11510
|
}
|
|
11352
11511
|
function runMachineCommand(machineId, command) {
|
|
11353
11512
|
const resolved = resolveMachineCommand(machineId, command);
|
|
11354
|
-
const result =
|
|
11513
|
+
const result = spawnSync2("bash", ["-c", resolved.shellCommand], {
|
|
11355
11514
|
encoding: "utf8",
|
|
11356
11515
|
env: process.env
|
|
11357
11516
|
});
|
|
@@ -11364,24 +11523,6 @@ function runMachineCommand(machineId, command) {
|
|
|
11364
11523
|
};
|
|
11365
11524
|
}
|
|
11366
11525
|
|
|
11367
|
-
// src/version.ts
|
|
11368
|
-
import { existsSync as existsSync4, readFileSync as readFileSync2 } from "fs";
|
|
11369
|
-
import { dirname as dirname3, join as join2 } from "path";
|
|
11370
|
-
import { fileURLToPath } from "url";
|
|
11371
|
-
function getPackageVersion() {
|
|
11372
|
-
try {
|
|
11373
|
-
const here = dirname3(fileURLToPath(import.meta.url));
|
|
11374
|
-
const candidates = [join2(here, "..", "package.json"), join2(here, "..", "..", "package.json")];
|
|
11375
|
-
const pkgPath = candidates.find((candidate) => existsSync4(candidate));
|
|
11376
|
-
if (!pkgPath) {
|
|
11377
|
-
return "0.0.0";
|
|
11378
|
-
}
|
|
11379
|
-
return JSON.parse(readFileSync2(pkgPath, "utf8")).version || "0.0.0";
|
|
11380
|
-
} catch {
|
|
11381
|
-
return "0.0.0";
|
|
11382
|
-
}
|
|
11383
|
-
}
|
|
11384
|
-
|
|
11385
11526
|
// src/compatibility.ts
|
|
11386
11527
|
var DEFAULT_COMMANDS = [
|
|
11387
11528
|
{ command: "bun", required: true },
|
|
@@ -11608,6 +11749,17 @@ function checkMachineCompatibility(options = {}) {
|
|
|
11608
11749
|
fail: checks.filter((check) => check.status === "fail").length
|
|
11609
11750
|
};
|
|
11610
11751
|
return {
|
|
11752
|
+
schema_version: MACHINES_CONSUMER_CONTRACT_VERSION,
|
|
11753
|
+
package: {
|
|
11754
|
+
name: MACHINES_PACKAGE_NAME,
|
|
11755
|
+
version: getPackageVersion()
|
|
11756
|
+
},
|
|
11757
|
+
capabilities: {
|
|
11758
|
+
topology: true,
|
|
11759
|
+
compatibility: true,
|
|
11760
|
+
route_resolution: true,
|
|
11761
|
+
cli_json_fallback: true
|
|
11762
|
+
},
|
|
11611
11763
|
ok: summary.fail === 0,
|
|
11612
11764
|
machine_id: machineId,
|
|
11613
11765
|
source: checks[0]?.source ?? "local",
|
|
@@ -12502,7 +12654,7 @@ async function testNotificationChannel(channelId, event = "manual.test", message
|
|
|
12502
12654
|
};
|
|
12503
12655
|
}
|
|
12504
12656
|
// src/commands/ports.ts
|
|
12505
|
-
import { spawnSync as
|
|
12657
|
+
import { spawnSync as spawnSync3 } from "child_process";
|
|
12506
12658
|
function parseSsOutput(output) {
|
|
12507
12659
|
return output.trim().split(`
|
|
12508
12660
|
`).map((line) => line.trim()).filter(Boolean).map((line) => {
|
|
@@ -12544,7 +12696,7 @@ function listPorts(machineId) {
|
|
|
12544
12696
|
const isLocal = targetMachineId === getLocalMachineId();
|
|
12545
12697
|
const localCommand = "if command -v ss >/dev/null 2>&1; then ss -ltnpH; else lsof -nP -iTCP -sTCP:LISTEN; fi";
|
|
12546
12698
|
const command = isLocal ? localCommand : buildSshCommand(targetMachineId, localCommand);
|
|
12547
|
-
const result =
|
|
12699
|
+
const result = spawnSync3("bash", ["-lc", command], { encoding: "utf8" });
|
|
12548
12700
|
if (result.status !== 0) {
|
|
12549
12701
|
throw new Error(result.stderr || `Failed to list ports for ${targetMachineId}`);
|
|
12550
12702
|
}
|
|
@@ -22072,6 +22224,7 @@ var MACHINE_MCP_TOOL_NAMES = [
|
|
|
22072
22224
|
"machines_install_claude_diff",
|
|
22073
22225
|
"machines_install_claude_preview",
|
|
22074
22226
|
"machines_install_claude_apply",
|
|
22227
|
+
"machines_route_resolve",
|
|
22075
22228
|
"machines_ssh_resolve",
|
|
22076
22229
|
"machines_ports",
|
|
22077
22230
|
"machines_backup_preview",
|
|
@@ -22182,8 +22335,11 @@ function createMcpServer(version2) {
|
|
|
22182
22335
|
}));
|
|
22183
22336
|
server.tool("machines_install_tailscale_preview", "Preview Tailscale install steps for a machine.", { machine_id: exports_external.string().optional().describe("Machine identifier") }, async ({ machine_id }) => ({ content: [{ type: "text", text: JSON.stringify(buildTailscaleInstallPlan(machine_id), null, 2) }] }));
|
|
22184
22337
|
server.tool("machines_install_tailscale_apply", "Execute Tailscale install steps for a machine.", { machine_id: exports_external.string().optional().describe("Machine identifier"), yes: exports_external.boolean().describe("Confirmation flag for execution") }, async ({ machine_id, yes }) => ({ content: [{ type: "text", text: JSON.stringify(runTailscaleInstall(machine_id, { apply: true, yes }), null, 2) }] }));
|
|
22338
|
+
server.tool("machines_route_resolve", "Resolve the best route for a machine using manifest, heartbeat, SSH, LAN, and Tailscale topology.", { machine_id: exports_external.string().describe("Machine identifier"), include_tailscale: exports_external.boolean().optional().describe("Whether to probe tailscale status --json") }, async ({ machine_id, include_tailscale }) => ({
|
|
22339
|
+
content: [{ type: "text", text: JSON.stringify(resolveMachineRoute(machine_id, { includeTailscale: include_tailscale !== false }), null, 2) }]
|
|
22340
|
+
}));
|
|
22185
22341
|
server.tool("machines_ssh_resolve", "Resolve the best SSH route for a machine.", { machine_id: exports_external.string().describe("Machine identifier"), remote_command: exports_external.string().optional().describe("Optional remote command") }, async ({ machine_id, remote_command }) => ({
|
|
22186
|
-
content: [{ type: "text", text: JSON.stringify({ resolved:
|
|
22342
|
+
content: [{ type: "text", text: JSON.stringify({ resolved: resolveMachineRoute(machine_id), command: buildSshCommand(machine_id, remote_command) }, null, 2) }]
|
|
22187
22343
|
}));
|
|
22188
22344
|
server.tool("machines_ports", "List listening ports on a machine.", { machine_id: exports_external.string().optional().describe("Machine identifier") }, async ({ machine_id }) => ({
|
|
22189
22345
|
content: [{ type: "text", text: JSON.stringify(listPorts(machine_id), null, 2) }]
|
|
@@ -22248,6 +22404,7 @@ export {
|
|
|
22248
22404
|
runAppsInstall,
|
|
22249
22405
|
resolveTables,
|
|
22250
22406
|
resolveSshTarget,
|
|
22407
|
+
resolveMachineRoute,
|
|
22251
22408
|
renderDomainMapping,
|
|
22252
22409
|
renderDashboardHtml,
|
|
22253
22410
|
removeNotificationChannel,
|
|
@@ -22331,5 +22488,7 @@ export {
|
|
|
22331
22488
|
MACHINES_STORAGE_MODE_ENV,
|
|
22332
22489
|
MACHINES_STORAGE_FALLBACK_ENV,
|
|
22333
22490
|
MACHINES_STORAGE_ENV,
|
|
22491
|
+
MACHINES_PACKAGE_NAME,
|
|
22492
|
+
MACHINES_CONSUMER_CONTRACT_VERSION,
|
|
22334
22493
|
CROSSREFS_KEY
|
|
22335
22494
|
};
|