@hasna/machines 0.0.18 → 0.0.20
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 +56 -8
- package/dist/cli/index.js +307 -20
- package/dist/commands/backup.d.ts +17 -2
- package/dist/commands/backup.d.ts.map +1 -1
- package/dist/compatibility.d.ts.map +1 -1
- package/dist/consumer.d.ts +2 -2
- package/dist/consumer.d.ts.map +1 -1
- package/dist/consumer.js +252 -13
- package/dist/index.js +343 -17
- package/dist/manifests.d.ts +8 -0
- package/dist/manifests.d.ts.map +1 -1
- package/dist/mcp/index.js +311 -21
- package/dist/mcp/server.d.ts +1 -1
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/topology.d.ts +65 -0
- package/dist/topology.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -11008,6 +11008,7 @@ var machineSchema = exports_external.object({
|
|
|
11008
11008
|
workspacePath: exports_external.string(),
|
|
11009
11009
|
bunPath: exports_external.string().optional(),
|
|
11010
11010
|
tags: exports_external.array(exports_external.string()).optional(),
|
|
11011
|
+
metadata: exports_external.record(exports_external.unknown()).optional(),
|
|
11011
11012
|
packages: exports_external.array(packageSchema).optional(),
|
|
11012
11013
|
apps: exports_external.array(appSchema).optional(),
|
|
11013
11014
|
files: exports_external.array(fileSchema).optional()
|
|
@@ -11110,6 +11111,40 @@ function getPackageVersion() {
|
|
|
11110
11111
|
// src/topology.ts
|
|
11111
11112
|
var MACHINES_CONSUMER_CONTRACT_VERSION = 1;
|
|
11112
11113
|
var MACHINES_PACKAGE_NAME = "@hasna/machines";
|
|
11114
|
+
var MACHINES_CONSUMER_ENTRYPOINT = "@hasna/machines/consumer";
|
|
11115
|
+
var MACHINES_CONSUMER_CAPABILITIES = {
|
|
11116
|
+
topology: true,
|
|
11117
|
+
compatibility: true,
|
|
11118
|
+
route_resolution: true,
|
|
11119
|
+
cli_json_fallback: true,
|
|
11120
|
+
workspace_path_mapping: true
|
|
11121
|
+
};
|
|
11122
|
+
var MACHINES_CONSUMER_CONTRACT = {
|
|
11123
|
+
schema_version: MACHINES_CONSUMER_CONTRACT_VERSION,
|
|
11124
|
+
package_name: MACHINES_PACKAGE_NAME,
|
|
11125
|
+
entrypoint: MACHINES_CONSUMER_ENTRYPOINT,
|
|
11126
|
+
capabilities: MACHINES_CONSUMER_CAPABILITIES,
|
|
11127
|
+
envelopes: ["topology", "route", "workspace", "compatibility"],
|
|
11128
|
+
stable_exports: [
|
|
11129
|
+
"MACHINES_CONSUMER_CONTRACT",
|
|
11130
|
+
"MACHINES_CONSUMER_CONTRACT_VERSION",
|
|
11131
|
+
"MACHINES_CONSUMER_CAPABILITIES",
|
|
11132
|
+
"MACHINES_PACKAGE_NAME",
|
|
11133
|
+
"discoverMachineTopology",
|
|
11134
|
+
"getLocalMachineTopology",
|
|
11135
|
+
"resolveMachineRoute",
|
|
11136
|
+
"resolveMachineWorkspace",
|
|
11137
|
+
"checkMachineCompatibility",
|
|
11138
|
+
"resolveMachineCommand",
|
|
11139
|
+
"runMachineCommand",
|
|
11140
|
+
"buildSshCommand",
|
|
11141
|
+
"resolveSshTarget",
|
|
11142
|
+
"getPackageVersion"
|
|
11143
|
+
]
|
|
11144
|
+
};
|
|
11145
|
+
function getMachinesConsumerCapabilities() {
|
|
11146
|
+
return { ...MACHINES_CONSUMER_CAPABILITIES };
|
|
11147
|
+
}
|
|
11113
11148
|
function normalizePlatform2(value = platform2()) {
|
|
11114
11149
|
const normalized = value.toLowerCase();
|
|
11115
11150
|
if (normalized === "darwin" || normalized === "macos")
|
|
@@ -11310,12 +11345,7 @@ function discoverMachineTopology(options = {}) {
|
|
|
11310
11345
|
name: MACHINES_PACKAGE_NAME,
|
|
11311
11346
|
version: getPackageVersion()
|
|
11312
11347
|
},
|
|
11313
|
-
capabilities:
|
|
11314
|
-
topology: true,
|
|
11315
|
-
compatibility: true,
|
|
11316
|
-
route_resolution: true,
|
|
11317
|
-
cli_json_fallback: true
|
|
11318
|
-
},
|
|
11348
|
+
capabilities: getMachinesConsumerCapabilities(),
|
|
11319
11349
|
generated_at: now.toISOString(),
|
|
11320
11350
|
local_machine_id: localMachineId,
|
|
11321
11351
|
local_hostname: hostname3(),
|
|
@@ -11439,6 +11469,215 @@ function resolveMachineRoute(machineId, options = {}) {
|
|
|
11439
11469
|
warnings
|
|
11440
11470
|
};
|
|
11441
11471
|
}
|
|
11472
|
+
function isRecord(value) {
|
|
11473
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
11474
|
+
}
|
|
11475
|
+
function metadataString(metadata, keys) {
|
|
11476
|
+
for (const key of keys) {
|
|
11477
|
+
const value = metadata[key];
|
|
11478
|
+
if (typeof value === "string" && value.trim())
|
|
11479
|
+
return value.trim();
|
|
11480
|
+
}
|
|
11481
|
+
return null;
|
|
11482
|
+
}
|
|
11483
|
+
function metadataBoolean(metadata, keys) {
|
|
11484
|
+
for (const key of keys) {
|
|
11485
|
+
const value = metadata[key];
|
|
11486
|
+
if (typeof value === "boolean")
|
|
11487
|
+
return value;
|
|
11488
|
+
}
|
|
11489
|
+
return null;
|
|
11490
|
+
}
|
|
11491
|
+
function metadataStringArray(metadata, keys) {
|
|
11492
|
+
for (const key of keys) {
|
|
11493
|
+
const value = metadata[key];
|
|
11494
|
+
if (Array.isArray(value))
|
|
11495
|
+
return value.filter((entry) => typeof entry === "string");
|
|
11496
|
+
}
|
|
11497
|
+
return [];
|
|
11498
|
+
}
|
|
11499
|
+
function readMappedPath(input) {
|
|
11500
|
+
for (const containerName of input.containers) {
|
|
11501
|
+
const container = input.metadata[containerName];
|
|
11502
|
+
if (!isRecord(container))
|
|
11503
|
+
continue;
|
|
11504
|
+
for (const key of input.keys) {
|
|
11505
|
+
const value = container[key];
|
|
11506
|
+
if (typeof value === "string" && value.trim())
|
|
11507
|
+
return value.trim();
|
|
11508
|
+
if (isRecord(value)) {
|
|
11509
|
+
const path = metadataString(value, ["path", "root", "workspacePath", "workspace_path"]);
|
|
11510
|
+
if (path)
|
|
11511
|
+
return path;
|
|
11512
|
+
}
|
|
11513
|
+
}
|
|
11514
|
+
}
|
|
11515
|
+
return null;
|
|
11516
|
+
}
|
|
11517
|
+
function trimTrailingSlash(value) {
|
|
11518
|
+
return value.replace(/\/+$/, "");
|
|
11519
|
+
}
|
|
11520
|
+
function joinPath(left, right) {
|
|
11521
|
+
return `${trimTrailingSlash(left)}/${right.replace(/^\/+/, "")}`;
|
|
11522
|
+
}
|
|
11523
|
+
function inferRepoRoot(workspaceRoot, repoName) {
|
|
11524
|
+
if (!workspaceRoot || !repoName)
|
|
11525
|
+
return null;
|
|
11526
|
+
const root = trimTrailingSlash(workspaceRoot);
|
|
11527
|
+
if (root.endsWith(`/${repoName}`) || root === repoName)
|
|
11528
|
+
return root;
|
|
11529
|
+
if (root.endsWith("/workspace") || root.endsWith("/Workspace")) {
|
|
11530
|
+
return joinPath(root, `hasna/opensource/${repoName}`);
|
|
11531
|
+
}
|
|
11532
|
+
return joinPath(root, repoName);
|
|
11533
|
+
}
|
|
11534
|
+
function projectPathFromMetadata(metadata, projectId, repoName) {
|
|
11535
|
+
const keys = [projectId, repoName].filter((value) => Boolean(value));
|
|
11536
|
+
return readMappedPath({
|
|
11537
|
+
metadata,
|
|
11538
|
+
containers: ["workspace_paths", "workspacePaths", "repo_paths", "repoPaths", "project_paths", "projectPaths", "projects"],
|
|
11539
|
+
keys
|
|
11540
|
+
});
|
|
11541
|
+
}
|
|
11542
|
+
function openFilesPathFromMetadata(metadata, projectId, repoName) {
|
|
11543
|
+
const direct = metadataString(metadata, ["open_files_root", "openFilesRoot", "open_files_path", "openFilesPath"]);
|
|
11544
|
+
if (direct)
|
|
11545
|
+
return direct;
|
|
11546
|
+
const keys = [projectId, repoName, "open-files", "open_files", "default"].filter((value) => Boolean(value));
|
|
11547
|
+
return readMappedPath({
|
|
11548
|
+
metadata,
|
|
11549
|
+
containers: ["open_files_roots", "openFilesRoots", "open_files_paths", "openFilesPaths"],
|
|
11550
|
+
keys
|
|
11551
|
+
});
|
|
11552
|
+
}
|
|
11553
|
+
function trustStatus(machine) {
|
|
11554
|
+
if (!machine)
|
|
11555
|
+
return "unknown";
|
|
11556
|
+
const explicit = metadataString(machine.metadata, ["trust_status", "trustStatus"]);
|
|
11557
|
+
if (explicit === "trusted" || explicit === "untrusted" || explicit === "unknown")
|
|
11558
|
+
return explicit;
|
|
11559
|
+
const trusted = metadataBoolean(machine.metadata, ["trusted", "syncTrusted", "sync_trusted"]);
|
|
11560
|
+
if (trusted === true)
|
|
11561
|
+
return "trusted";
|
|
11562
|
+
if (trusted === false)
|
|
11563
|
+
return "untrusted";
|
|
11564
|
+
if (machine.route_hints.some((hint) => hint.kind === "local"))
|
|
11565
|
+
return "trusted";
|
|
11566
|
+
if (machine.tags.includes("trusted"))
|
|
11567
|
+
return "trusted";
|
|
11568
|
+
return "unknown";
|
|
11569
|
+
}
|
|
11570
|
+
function authStatus(machine) {
|
|
11571
|
+
if (!machine)
|
|
11572
|
+
return "unknown";
|
|
11573
|
+
const explicit = metadataString(machine.metadata, ["auth_status", "authStatus"]);
|
|
11574
|
+
if (explicit === "authenticated" || explicit === "unauthenticated" || explicit === "unknown")
|
|
11575
|
+
return explicit;
|
|
11576
|
+
const authenticated = metadataBoolean(machine.metadata, ["authenticated", "sshAuthorized", "ssh_authorized"]);
|
|
11577
|
+
if (authenticated === true)
|
|
11578
|
+
return "authenticated";
|
|
11579
|
+
if (authenticated === false)
|
|
11580
|
+
return "unauthenticated";
|
|
11581
|
+
if (machine.route_hints.some((hint) => hint.kind === "local"))
|
|
11582
|
+
return "authenticated";
|
|
11583
|
+
return "unknown";
|
|
11584
|
+
}
|
|
11585
|
+
function primaryMachine(machine, projectId, primaryMachineId) {
|
|
11586
|
+
if (!machine)
|
|
11587
|
+
return false;
|
|
11588
|
+
if (primaryMachineId)
|
|
11589
|
+
return machine.machine_id === primaryMachineId;
|
|
11590
|
+
if (metadataBoolean(machine.metadata, ["primary", "primary_machine", "primaryMachine"]) === true)
|
|
11591
|
+
return true;
|
|
11592
|
+
const primaryProjects = metadataStringArray(machine.metadata, ["primary_projects", "primaryProjects"]);
|
|
11593
|
+
if (primaryProjects.includes(projectId))
|
|
11594
|
+
return true;
|
|
11595
|
+
return machine.tags.includes("primary");
|
|
11596
|
+
}
|
|
11597
|
+
function metadataKeysForDiagnostics(metadata) {
|
|
11598
|
+
return Object.keys(metadata).filter((key) => !/(secret|token|key|password|credential)/i.test(key)).sort();
|
|
11599
|
+
}
|
|
11600
|
+
function resolveMachineWorkspace(options) {
|
|
11601
|
+
const topology = options.topology ?? discoverMachineTopology(options);
|
|
11602
|
+
const warnings = [...topology.warnings];
|
|
11603
|
+
const { machine, matchedBy } = findRouteMachine(topology, options.machineId);
|
|
11604
|
+
const generatedAt = (options.now ?? new Date).toISOString();
|
|
11605
|
+
const repoName = options.repoName ?? options.projectId;
|
|
11606
|
+
const openFilesRepoName = options.openFilesRepoName ?? "open-files";
|
|
11607
|
+
if (!machine) {
|
|
11608
|
+
warnings.push(`machine_not_found:${options.machineId}`);
|
|
11609
|
+
return {
|
|
11610
|
+
schema_version: MACHINES_CONSUMER_CONTRACT_VERSION,
|
|
11611
|
+
package: topology.package,
|
|
11612
|
+
ok: false,
|
|
11613
|
+
requested_machine_id: options.machineId,
|
|
11614
|
+
machine_id: null,
|
|
11615
|
+
generated_at: generatedAt,
|
|
11616
|
+
project: { project_id: options.projectId, repo_name: repoName, canonical: Boolean(options.projectId) },
|
|
11617
|
+
machine: { current: false, primary: false, trust_status: "unknown", auth_status: "unknown" },
|
|
11618
|
+
paths: {
|
|
11619
|
+
workspace_root: { path: null, source: "unresolved" },
|
|
11620
|
+
project_root: { path: null, source: "unresolved" },
|
|
11621
|
+
open_files_root: { path: null, source: "unresolved" }
|
|
11622
|
+
},
|
|
11623
|
+
evidence: {
|
|
11624
|
+
topology: true,
|
|
11625
|
+
matched_by: matchedBy,
|
|
11626
|
+
manifest_declared: null,
|
|
11627
|
+
metadata_keys: []
|
|
11628
|
+
},
|
|
11629
|
+
warnings
|
|
11630
|
+
};
|
|
11631
|
+
}
|
|
11632
|
+
const metadata = machine.metadata;
|
|
11633
|
+
const workspaceRootPath = options.workspaceRoot ?? machine.workspace_path;
|
|
11634
|
+
const workspaceRootSource = options.workspaceRoot ? "argument" : machine.workspace_path ? "manifest" : "unresolved";
|
|
11635
|
+
const metadataProjectRoot = projectPathFromMetadata(metadata, options.projectId, repoName);
|
|
11636
|
+
const inferredProjectRoot = inferRepoRoot(workspaceRootPath, repoName);
|
|
11637
|
+
const projectRootPath = options.projectRoot ?? metadataProjectRoot ?? inferredProjectRoot;
|
|
11638
|
+
const projectRootSource = options.projectRoot ? "argument" : metadataProjectRoot ? "manifest_metadata" : inferredProjectRoot ? "inferred" : "unresolved";
|
|
11639
|
+
const metadataOpenFilesRoot = openFilesPathFromMetadata(metadata, options.projectId, openFilesRepoName);
|
|
11640
|
+
const inferredOpenFilesRoot = inferRepoRoot(workspaceRootPath, openFilesRepoName);
|
|
11641
|
+
const openFilesRootPath = options.openFilesRoot ?? metadataOpenFilesRoot ?? inferredOpenFilesRoot;
|
|
11642
|
+
const openFilesRootSource = options.openFilesRoot ? "argument" : metadataOpenFilesRoot ? "manifest_metadata" : inferredOpenFilesRoot ? "inferred" : "unresolved";
|
|
11643
|
+
if (projectRootSource === "inferred")
|
|
11644
|
+
warnings.push(`project_root_inferred:${options.projectId}`);
|
|
11645
|
+
if (openFilesRootSource === "inferred")
|
|
11646
|
+
warnings.push(`open_files_root_inferred:${options.projectId}`);
|
|
11647
|
+
if (!projectRootPath)
|
|
11648
|
+
warnings.push(`project_root_unresolved:${options.projectId}`);
|
|
11649
|
+
return {
|
|
11650
|
+
schema_version: MACHINES_CONSUMER_CONTRACT_VERSION,
|
|
11651
|
+
package: topology.package,
|
|
11652
|
+
ok: Boolean(projectRootPath),
|
|
11653
|
+
requested_machine_id: options.machineId,
|
|
11654
|
+
machine_id: machine.machine_id,
|
|
11655
|
+
generated_at: generatedAt,
|
|
11656
|
+
project: {
|
|
11657
|
+
project_id: options.projectId,
|
|
11658
|
+
repo_name: repoName,
|
|
11659
|
+
canonical: Boolean(options.projectId && repoName)
|
|
11660
|
+
},
|
|
11661
|
+
machine: {
|
|
11662
|
+
current: machine.machine_id === topology.local_machine_id,
|
|
11663
|
+
primary: primaryMachine(machine, options.projectId, options.primaryMachineId),
|
|
11664
|
+
trust_status: trustStatus(machine),
|
|
11665
|
+
auth_status: authStatus(machine)
|
|
11666
|
+
},
|
|
11667
|
+
paths: {
|
|
11668
|
+
workspace_root: { path: workspaceRootPath, source: workspaceRootSource },
|
|
11669
|
+
project_root: { path: projectRootPath, source: projectRootSource },
|
|
11670
|
+
open_files_root: { path: openFilesRootPath, source: openFilesRootSource }
|
|
11671
|
+
},
|
|
11672
|
+
evidence: {
|
|
11673
|
+
topology: true,
|
|
11674
|
+
matched_by: matchedBy,
|
|
11675
|
+
manifest_declared: machine.manifest_declared,
|
|
11676
|
+
metadata_keys: metadataKeysForDiagnostics(metadata)
|
|
11677
|
+
},
|
|
11678
|
+
warnings
|
|
11679
|
+
};
|
|
11680
|
+
}
|
|
11442
11681
|
function getLocalMachineTopology(options = {}) {
|
|
11443
11682
|
const topology = discoverMachineTopology(options);
|
|
11444
11683
|
return topology.machines.find((machine) => machine.machine_id === topology.local_machine_id) ?? {
|
|
@@ -11757,12 +11996,7 @@ function checkMachineCompatibility(options = {}) {
|
|
|
11757
11996
|
name: MACHINES_PACKAGE_NAME,
|
|
11758
11997
|
version: getPackageVersion()
|
|
11759
11998
|
},
|
|
11760
|
-
capabilities:
|
|
11761
|
-
topology: true,
|
|
11762
|
-
compatibility: true,
|
|
11763
|
-
route_resolution: true,
|
|
11764
|
-
cli_json_fallback: true
|
|
11765
|
-
},
|
|
11999
|
+
capabilities: getMachinesConsumerCapabilities(),
|
|
11766
12000
|
ok: summary.fail === 0,
|
|
11767
12001
|
machine_id: machineId,
|
|
11768
12002
|
source: checks[0]?.source ?? "local",
|
|
@@ -11796,9 +12030,52 @@ function getAgentStatus(machineId = getLocalMachineId()) {
|
|
|
11796
12030
|
// src/commands/backup.ts
|
|
11797
12031
|
import { homedir as homedir2 } from "os";
|
|
11798
12032
|
import { join as join3 } from "path";
|
|
12033
|
+
var MACHINES_BACKUP_BUCKET_ENV = "HASNA_MACHINES_S3_BUCKET";
|
|
12034
|
+
var MACHINES_BACKUP_BUCKET_FALLBACK_ENV = "MACHINES_S3_BUCKET";
|
|
12035
|
+
var MACHINES_BACKUP_PREFIX_ENV = "HASNA_MACHINES_S3_PREFIX";
|
|
12036
|
+
var MACHINES_BACKUP_PREFIX_FALLBACK_ENV = "MACHINES_S3_PREFIX";
|
|
12037
|
+
var DEFAULT_BACKUP_PREFIX = "machines";
|
|
11799
12038
|
function quote(value) {
|
|
11800
12039
|
return `'${value.replace(/'/g, `'\\''`)}'`;
|
|
11801
12040
|
}
|
|
12041
|
+
function readEnv2(name) {
|
|
12042
|
+
const value = process.env[name]?.trim();
|
|
12043
|
+
return value || undefined;
|
|
12044
|
+
}
|
|
12045
|
+
function readBackupBucketEnv() {
|
|
12046
|
+
const primary = readEnv2(MACHINES_BACKUP_BUCKET_ENV);
|
|
12047
|
+
if (primary)
|
|
12048
|
+
return { bucket: primary, bucketSource: MACHINES_BACKUP_BUCKET_ENV };
|
|
12049
|
+
const fallback = readEnv2(MACHINES_BACKUP_BUCKET_FALLBACK_ENV);
|
|
12050
|
+
if (fallback)
|
|
12051
|
+
return { bucket: fallback, bucketSource: MACHINES_BACKUP_BUCKET_FALLBACK_ENV };
|
|
12052
|
+
return null;
|
|
12053
|
+
}
|
|
12054
|
+
function readBackupPrefixEnv() {
|
|
12055
|
+
const primary = readEnv2(MACHINES_BACKUP_PREFIX_ENV);
|
|
12056
|
+
if (primary)
|
|
12057
|
+
return { prefix: primary, prefixSource: MACHINES_BACKUP_PREFIX_ENV };
|
|
12058
|
+
const fallback = readEnv2(MACHINES_BACKUP_PREFIX_FALLBACK_ENV);
|
|
12059
|
+
if (fallback)
|
|
12060
|
+
return { prefix: fallback, prefixSource: MACHINES_BACKUP_PREFIX_FALLBACK_ENV };
|
|
12061
|
+
return null;
|
|
12062
|
+
}
|
|
12063
|
+
function resolveBackupTarget(options = {}) {
|
|
12064
|
+
const explicitBucket = options.bucket?.trim();
|
|
12065
|
+
const envBucket = explicitBucket ? null : readBackupBucketEnv();
|
|
12066
|
+
const bucket = explicitBucket || envBucket?.bucket;
|
|
12067
|
+
if (!bucket) {
|
|
12068
|
+
throw new Error(`Missing S3 backup bucket. Pass --bucket or set ${MACHINES_BACKUP_BUCKET_ENV} or ${MACHINES_BACKUP_BUCKET_FALLBACK_ENV}.`);
|
|
12069
|
+
}
|
|
12070
|
+
const explicitPrefix = options.prefix?.trim();
|
|
12071
|
+
const envPrefix = explicitPrefix ? null : readBackupPrefixEnv();
|
|
12072
|
+
return {
|
|
12073
|
+
bucket,
|
|
12074
|
+
prefix: explicitPrefix || envPrefix?.prefix || DEFAULT_BACKUP_PREFIX,
|
|
12075
|
+
bucketSource: explicitBucket ? "argument" : envBucket.bucketSource,
|
|
12076
|
+
prefixSource: explicitPrefix ? "argument" : envPrefix?.prefixSource || "default"
|
|
12077
|
+
};
|
|
12078
|
+
}
|
|
11802
12079
|
function defaultBackupSources() {
|
|
11803
12080
|
const home = homedir2();
|
|
11804
12081
|
return [
|
|
@@ -11807,7 +12084,8 @@ function defaultBackupSources() {
|
|
|
11807
12084
|
join3(home, ".secrets")
|
|
11808
12085
|
];
|
|
11809
12086
|
}
|
|
11810
|
-
function buildBackupPlan(bucket, prefix
|
|
12087
|
+
function buildBackupPlan(bucket, prefix) {
|
|
12088
|
+
const target = resolveBackupTarget({ bucket, prefix });
|
|
11811
12089
|
const archivePath = join3(homedir2(), ".hasna", "machines", "backup.tgz");
|
|
11812
12090
|
const sources = defaultBackupSources();
|
|
11813
12091
|
const steps = [
|
|
@@ -11820,7 +12098,7 @@ function buildBackupPlan(bucket, prefix = "machines") {
|
|
|
11820
12098
|
{
|
|
11821
12099
|
id: "backup-upload",
|
|
11822
12100
|
title: "Upload archive to S3",
|
|
11823
|
-
command: `aws s3 cp ${quote(archivePath)} s3://${bucket}/${prefix}/$(hostname)-backup.tgz`,
|
|
12101
|
+
command: `aws s3 cp ${quote(archivePath)} s3://${target.bucket}/${target.prefix}/$(hostname)-backup.tgz`,
|
|
11824
12102
|
manager: "custom"
|
|
11825
12103
|
}
|
|
11826
12104
|
];
|
|
@@ -11831,7 +12109,7 @@ function buildBackupPlan(bucket, prefix = "machines") {
|
|
|
11831
12109
|
executed: 0
|
|
11832
12110
|
};
|
|
11833
12111
|
}
|
|
11834
|
-
function runBackup(bucket, prefix
|
|
12112
|
+
function runBackup(bucket, prefix, options = {}) {
|
|
11835
12113
|
const plan = buildBackupPlan(bucket, prefix);
|
|
11836
12114
|
if (!options.apply)
|
|
11837
12115
|
return plan;
|
|
@@ -22228,6 +22506,7 @@ var MACHINE_MCP_TOOL_NAMES = [
|
|
|
22228
22506
|
"machines_install_claude_preview",
|
|
22229
22507
|
"machines_install_claude_apply",
|
|
22230
22508
|
"machines_route_resolve",
|
|
22509
|
+
"machines_workspace_resolve",
|
|
22231
22510
|
"machines_ssh_resolve",
|
|
22232
22511
|
"machines_ports",
|
|
22233
22512
|
"machines_backup_preview",
|
|
@@ -22341,14 +22620,50 @@ function createMcpServer(version2) {
|
|
|
22341
22620
|
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 }) => ({
|
|
22342
22621
|
content: [{ type: "text", text: JSON.stringify(resolveMachineRoute(machine_id, { includeTailscale: include_tailscale !== false }), null, 2) }]
|
|
22343
22622
|
}));
|
|
22623
|
+
server.tool("machines_workspace_resolve", "Resolve sync-safe repo and open-files roots for an open-* consumer.", {
|
|
22624
|
+
machine_id: exports_external.string().describe("Machine identifier"),
|
|
22625
|
+
project_id: exports_external.string().describe("Canonical project id"),
|
|
22626
|
+
repo_name: exports_external.string().optional().describe("Repository name; defaults to project id"),
|
|
22627
|
+
open_files_repo_name: exports_external.string().optional().describe("Open-files repository name"),
|
|
22628
|
+
primary_machine_id: exports_external.string().optional().describe("Primary machine id for this project"),
|
|
22629
|
+
workspace_root: exports_external.string().optional().describe("Override the machine workspace root"),
|
|
22630
|
+
project_root: exports_external.string().optional().describe("Override the resolved project root"),
|
|
22631
|
+
open_files_root: exports_external.string().optional().describe("Override the resolved open-files root"),
|
|
22632
|
+
include_tailscale: exports_external.boolean().optional().describe("Whether to probe tailscale status --json")
|
|
22633
|
+
}, async ({
|
|
22634
|
+
machine_id,
|
|
22635
|
+
project_id,
|
|
22636
|
+
repo_name,
|
|
22637
|
+
open_files_repo_name,
|
|
22638
|
+
primary_machine_id,
|
|
22639
|
+
workspace_root,
|
|
22640
|
+
project_root,
|
|
22641
|
+
open_files_root,
|
|
22642
|
+
include_tailscale
|
|
22643
|
+
}) => ({
|
|
22644
|
+
content: [{
|
|
22645
|
+
type: "text",
|
|
22646
|
+
text: JSON.stringify(resolveMachineWorkspace({
|
|
22647
|
+
machineId: machine_id,
|
|
22648
|
+
projectId: project_id,
|
|
22649
|
+
repoName: repo_name,
|
|
22650
|
+
openFilesRepoName: open_files_repo_name,
|
|
22651
|
+
primaryMachineId: primary_machine_id,
|
|
22652
|
+
workspaceRoot: workspace_root,
|
|
22653
|
+
projectRoot: project_root,
|
|
22654
|
+
openFilesRoot: open_files_root,
|
|
22655
|
+
includeTailscale: include_tailscale !== false
|
|
22656
|
+
}), null, 2)
|
|
22657
|
+
}]
|
|
22658
|
+
}));
|
|
22344
22659
|
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 }) => ({
|
|
22345
22660
|
content: [{ type: "text", text: JSON.stringify({ resolved: resolveMachineRoute(machine_id), command: buildSshCommand(machine_id, remote_command) }, null, 2) }]
|
|
22346
22661
|
}));
|
|
22347
22662
|
server.tool("machines_ports", "List listening ports on a machine.", { machine_id: exports_external.string().optional().describe("Machine identifier") }, async ({ machine_id }) => ({
|
|
22348
22663
|
content: [{ type: "text", text: JSON.stringify(listPorts(machine_id), null, 2) }]
|
|
22349
22664
|
}));
|
|
22350
|
-
server.tool("machines_backup_preview", "Preview backup steps for the current machine.", { bucket: exports_external.string().describe("S3 bucket name"), prefix: exports_external.string().optional().describe("S3 key prefix") }, async ({ bucket, prefix }) => ({ content: [{ type: "text", text: JSON.stringify(buildBackupPlan(bucket, prefix), null, 2) }] }));
|
|
22351
|
-
server.tool("machines_backup_apply", "Execute backup steps for the current machine.", { bucket: exports_external.string().describe("S3 bucket name"), prefix: exports_external.string().optional().describe("S3 key prefix"), yes: exports_external.boolean().describe("Confirmation flag for execution") }, async ({ bucket, prefix, yes }) => ({ content: [{ type: "text", text: JSON.stringify(runBackup(bucket, prefix, { apply: true, yes }), null, 2) }] }));
|
|
22665
|
+
server.tool("machines_backup_preview", "Preview backup steps for the current machine.", { bucket: exports_external.string().optional().describe("S3 bucket name; defaults to HASNA_MACHINES_S3_BUCKET or MACHINES_S3_BUCKET"), prefix: exports_external.string().optional().describe("S3 key prefix; defaults to HASNA_MACHINES_S3_PREFIX, MACHINES_S3_PREFIX, or machines") }, async ({ bucket, prefix }) => ({ content: [{ type: "text", text: JSON.stringify(buildBackupPlan(bucket, prefix), null, 2) }] }));
|
|
22666
|
+
server.tool("machines_backup_apply", "Execute backup steps for the current machine.", { bucket: exports_external.string().optional().describe("S3 bucket name; defaults to HASNA_MACHINES_S3_BUCKET or MACHINES_S3_BUCKET"), prefix: exports_external.string().optional().describe("S3 key prefix; defaults to HASNA_MACHINES_S3_PREFIX, MACHINES_S3_PREFIX, or machines"), yes: exports_external.boolean().describe("Confirmation flag for execution") }, async ({ bucket, prefix, yes }) => ({ content: [{ type: "text", text: JSON.stringify(runBackup(bucket, prefix, { apply: true, yes }), null, 2) }] }));
|
|
22352
22667
|
server.tool("machines_cert_preview", "Preview mkcert steps for one or more domains.", { domains: exports_external.array(exports_external.string()).describe("Domains to issue certificates for") }, async ({ domains }) => ({ content: [{ type: "text", text: JSON.stringify(buildCertPlan(domains), null, 2) }] }));
|
|
22353
22668
|
server.tool("machines_cert_apply", "Execute mkcert steps for one or more domains.", { domains: exports_external.array(exports_external.string()).describe("Domains to issue certificates for"), yes: exports_external.boolean().describe("Confirmation flag for execution") }, async ({ domains, yes }) => ({ content: [{ type: "text", text: JSON.stringify(runCertPlan(domains, { apply: true, yes }), null, 2) }] }));
|
|
22354
22669
|
server.tool("machines_dns_add", "Add or replace a local domain mapping.", { domain: exports_external.string().describe("Domain name"), port: exports_external.number().describe("Target port"), target_host: exports_external.string().optional().describe("Target host") }, async ({ domain, port, target_host }) => ({ content: [{ type: "text", text: JSON.stringify(addDomainMapping(domain, port, target_host), null, 2) }] }));
|
|
@@ -22407,7 +22722,9 @@ export {
|
|
|
22407
22722
|
runAppsInstall,
|
|
22408
22723
|
resolveTables,
|
|
22409
22724
|
resolveSshTarget,
|
|
22725
|
+
resolveMachineWorkspace,
|
|
22410
22726
|
resolveMachineRoute,
|
|
22727
|
+
resolveBackupTarget,
|
|
22411
22728
|
renderDomainMapping,
|
|
22412
22729
|
renderDashboardHtml,
|
|
22413
22730
|
removeNotificationChannel,
|
|
@@ -22444,6 +22761,7 @@ export {
|
|
|
22444
22761
|
getNotificationsPath,
|
|
22445
22762
|
getManifestPath,
|
|
22446
22763
|
getManifestMachine,
|
|
22764
|
+
getMachinesConsumerCapabilities,
|
|
22447
22765
|
getLocalMachineTopology,
|
|
22448
22766
|
getLocalMachineId,
|
|
22449
22767
|
getDefaultNotificationConfig,
|
|
@@ -22492,6 +22810,14 @@ export {
|
|
|
22492
22810
|
MACHINES_STORAGE_FALLBACK_ENV,
|
|
22493
22811
|
MACHINES_STORAGE_ENV,
|
|
22494
22812
|
MACHINES_PACKAGE_NAME,
|
|
22813
|
+
MACHINES_CONSUMER_ENTRYPOINT,
|
|
22495
22814
|
MACHINES_CONSUMER_CONTRACT_VERSION,
|
|
22815
|
+
MACHINES_CONSUMER_CONTRACT,
|
|
22816
|
+
MACHINES_CONSUMER_CAPABILITIES,
|
|
22817
|
+
MACHINES_BACKUP_PREFIX_FALLBACK_ENV,
|
|
22818
|
+
MACHINES_BACKUP_PREFIX_ENV,
|
|
22819
|
+
MACHINES_BACKUP_BUCKET_FALLBACK_ENV,
|
|
22820
|
+
MACHINES_BACKUP_BUCKET_ENV,
|
|
22821
|
+
DEFAULT_BACKUP_PREFIX,
|
|
22496
22822
|
CROSSREFS_KEY
|
|
22497
22823
|
};
|
package/dist/manifests.d.ts
CHANGED
|
@@ -10,6 +10,7 @@ export declare const machineSchema: z.ZodObject<{
|
|
|
10
10
|
workspacePath: z.ZodString;
|
|
11
11
|
bunPath: z.ZodOptional<z.ZodString>;
|
|
12
12
|
tags: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
13
|
+
metadata: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
13
14
|
packages: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
14
15
|
name: z.ZodString;
|
|
15
16
|
manager: z.ZodOptional<z.ZodEnum<["bun", "brew", "apt", "custom"]>>;
|
|
@@ -59,6 +60,7 @@ export declare const machineSchema: z.ZodObject<{
|
|
|
59
60
|
connection?: "local" | "ssh" | "tailscale" | undefined;
|
|
60
61
|
bunPath?: string | undefined;
|
|
61
62
|
tags?: string[] | undefined;
|
|
63
|
+
metadata?: Record<string, unknown> | undefined;
|
|
62
64
|
packages?: {
|
|
63
65
|
name: string;
|
|
64
66
|
manager?: "bun" | "brew" | "apt" | "custom" | undefined;
|
|
@@ -84,6 +86,7 @@ export declare const machineSchema: z.ZodObject<{
|
|
|
84
86
|
connection?: "local" | "ssh" | "tailscale" | undefined;
|
|
85
87
|
bunPath?: string | undefined;
|
|
86
88
|
tags?: string[] | undefined;
|
|
89
|
+
metadata?: Record<string, unknown> | undefined;
|
|
87
90
|
packages?: {
|
|
88
91
|
name: string;
|
|
89
92
|
manager?: "bun" | "brew" | "apt" | "custom" | undefined;
|
|
@@ -113,6 +116,7 @@ export declare const fleetSchema: z.ZodObject<{
|
|
|
113
116
|
workspacePath: z.ZodString;
|
|
114
117
|
bunPath: z.ZodOptional<z.ZodString>;
|
|
115
118
|
tags: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
119
|
+
metadata: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
116
120
|
packages: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
117
121
|
name: z.ZodString;
|
|
118
122
|
manager: z.ZodOptional<z.ZodEnum<["bun", "brew", "apt", "custom"]>>;
|
|
@@ -162,6 +166,7 @@ export declare const fleetSchema: z.ZodObject<{
|
|
|
162
166
|
connection?: "local" | "ssh" | "tailscale" | undefined;
|
|
163
167
|
bunPath?: string | undefined;
|
|
164
168
|
tags?: string[] | undefined;
|
|
169
|
+
metadata?: Record<string, unknown> | undefined;
|
|
165
170
|
packages?: {
|
|
166
171
|
name: string;
|
|
167
172
|
manager?: "bun" | "brew" | "apt" | "custom" | undefined;
|
|
@@ -187,6 +192,7 @@ export declare const fleetSchema: z.ZodObject<{
|
|
|
187
192
|
connection?: "local" | "ssh" | "tailscale" | undefined;
|
|
188
193
|
bunPath?: string | undefined;
|
|
189
194
|
tags?: string[] | undefined;
|
|
195
|
+
metadata?: Record<string, unknown> | undefined;
|
|
190
196
|
packages?: {
|
|
191
197
|
name: string;
|
|
192
198
|
manager?: "bun" | "brew" | "apt" | "custom" | undefined;
|
|
@@ -214,6 +220,7 @@ export declare const fleetSchema: z.ZodObject<{
|
|
|
214
220
|
connection?: "local" | "ssh" | "tailscale" | undefined;
|
|
215
221
|
bunPath?: string | undefined;
|
|
216
222
|
tags?: string[] | undefined;
|
|
223
|
+
metadata?: Record<string, unknown> | undefined;
|
|
217
224
|
packages?: {
|
|
218
225
|
name: string;
|
|
219
226
|
manager?: "bun" | "brew" | "apt" | "custom" | undefined;
|
|
@@ -243,6 +250,7 @@ export declare const fleetSchema: z.ZodObject<{
|
|
|
243
250
|
connection?: "local" | "ssh" | "tailscale" | undefined;
|
|
244
251
|
bunPath?: string | undefined;
|
|
245
252
|
tags?: string[] | undefined;
|
|
253
|
+
metadata?: Record<string, unknown> | undefined;
|
|
246
254
|
packages?: {
|
|
247
255
|
name: string;
|
|
248
256
|
manager?: "bun" | "brew" | "apt" | "custom" | undefined;
|
package/dist/manifests.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"manifests.d.ts","sourceRoot":"","sources":["../src/manifests.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,KAAK,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAoBjE,eAAO,MAAM,aAAa
|
|
1
|
+
{"version":3,"file":"manifests.d.ts","sourceRoot":"","sources":["../src/manifests.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,KAAK,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAoBjE,eAAO,MAAM,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAcxB,CAAC;AAEH,eAAO,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAItB,CAAC;AAoBH,wBAAgB,kBAAkB,IAAI,aAAa,CAMlD;AAED,wBAAgB,YAAY,CAAC,IAAI,SAAoB,GAAG,aAAa,CAMpE;AAED,wBAAgB,gBAAgB,CAAC,IAAI,SAAoB,GAAG,aAAa,CAExE;AAED,wBAAgB,aAAa,CAAC,QAAQ,EAAE,aAAa,EAAE,IAAI,SAAoB,GAAG,MAAM,CAUvF;AAED,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,SAAoB,GAAG,eAAe,GAAG,IAAI,CAEtG;AAED,wBAAgB,4BAA4B,IAAI,eAAe,CAiB9D"}
|