@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/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 = "machines") {
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 = "machines", options = {}) {
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
  };
@@ -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;
@@ -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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAaxB,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"}
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"}