@hasna/machines 0.0.9 → 0.0.11

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/mcp/index.js CHANGED
@@ -1,26 +1,41 @@
1
1
  #!/usr/bin/env bun
2
2
  // @bun
3
3
  var __defProp = Object.defineProperty;
4
- var __returnValue = (v) => v;
5
- function __exportSetter(name, newValue) {
6
- this[name] = __returnValue.bind(null, newValue);
7
- }
8
4
  var __export = (target, all) => {
9
5
  for (var name in all)
10
6
  __defProp(target, name, {
11
7
  get: all[name],
12
8
  enumerable: true,
13
9
  configurable: true,
14
- set: __exportSetter.bind(all, name)
10
+ set: (newValue) => all[name] = () => newValue
15
11
  });
16
12
  };
17
13
 
18
14
  // src/mcp/index.ts
19
- import { readFileSync as readFileSync7 } from "fs";
20
- import { dirname as dirname5, join as join9 } from "path";
21
- import { fileURLToPath as fileURLToPath2 } from "url";
22
15
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
23
16
 
17
+ // src/version.ts
18
+ import { existsSync, readFileSync } from "fs";
19
+ import { dirname, join } from "path";
20
+ import { fileURLToPath } from "url";
21
+ function getPackageVersion() {
22
+ try {
23
+ const here = dirname(fileURLToPath(import.meta.url));
24
+ const candidates = [join(here, "..", "package.json"), join(here, "..", "..", "package.json")];
25
+ const pkgPath = candidates.find((candidate) => existsSync(candidate));
26
+ if (!pkgPath) {
27
+ return "0.0.0";
28
+ }
29
+ return JSON.parse(readFileSync(pkgPath, "utf8")).version || "0.0.0";
30
+ } catch {
31
+ return "0.0.0";
32
+ }
33
+ }
34
+
35
+ // src/mcp/http.ts
36
+ import { createServer } from "http";
37
+ import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
38
+
24
39
  // src/mcp/server.ts
25
40
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
26
41
 
@@ -3999,20 +4014,20 @@ var coerce = {
3999
4014
  var NEVER = INVALID;
4000
4015
  // src/commands/backup.ts
4001
4016
  import { homedir } from "os";
4002
- import { join } from "path";
4017
+ import { join as join2 } from "path";
4003
4018
  function quote(value) {
4004
4019
  return `'${value.replace(/'/g, `'\\''`)}'`;
4005
4020
  }
4006
4021
  function defaultBackupSources() {
4007
4022
  const home = homedir();
4008
4023
  return [
4009
- join(home, ".hasna"),
4010
- join(home, ".ssh"),
4011
- join(home, ".secrets")
4024
+ join2(home, ".hasna"),
4025
+ join2(home, ".ssh"),
4026
+ join2(home, ".secrets")
4012
4027
  ];
4013
4028
  }
4014
4029
  function buildBackupPlan(bucket, prefix = "machines") {
4015
- const archivePath = join(homedir(), ".hasna", "machines", "backup.tgz");
4030
+ const archivePath = join2(homedir(), ".hasna", "machines", "backup.tgz");
4016
4031
  const sources = defaultBackupSources();
4017
4032
  const steps = [
4018
4033
  {
@@ -4063,33 +4078,33 @@ function runBackup(bucket, prefix = "machines", options = {}) {
4063
4078
  }
4064
4079
 
4065
4080
  // src/manifests.ts
4066
- import { existsSync as existsSync2, readFileSync, writeFileSync } from "fs";
4081
+ import { existsSync as existsSync3, readFileSync as readFileSync2, writeFileSync } from "fs";
4067
4082
  import { arch, homedir as homedir2, hostname, platform, userInfo } from "os";
4068
- import { dirname as dirname2 } from "path";
4083
+ import { dirname as dirname3 } from "path";
4069
4084
 
4070
4085
  // src/paths.ts
4071
- import { existsSync, mkdirSync } from "fs";
4072
- import { dirname, join as join2, resolve } from "path";
4086
+ import { existsSync as existsSync2, mkdirSync } from "fs";
4087
+ import { dirname as dirname2, join as join3, resolve } from "path";
4073
4088
  function homeDir() {
4074
4089
  return process.env["HOME"] || process.env["USERPROFILE"] || "~";
4075
4090
  }
4076
4091
  function getDataDir() {
4077
- return process.env["HASNA_MACHINES_DIR"] || join2(homeDir(), ".hasna", "machines");
4092
+ return process.env["HASNA_MACHINES_DIR"] || join3(homeDir(), ".hasna", "machines");
4078
4093
  }
4079
4094
  function getDbPath() {
4080
- return process.env["HASNA_MACHINES_DB_PATH"] || join2(getDataDir(), "machines.db");
4095
+ return process.env["HASNA_MACHINES_DB_PATH"] || join3(getDataDir(), "machines.db");
4081
4096
  }
4082
4097
  function getManifestPath() {
4083
- return process.env["HASNA_MACHINES_MANIFEST_PATH"] || join2(getDataDir(), "machines.json");
4098
+ return process.env["HASNA_MACHINES_MANIFEST_PATH"] || join3(getDataDir(), "machines.json");
4084
4099
  }
4085
4100
  function getNotificationsPath() {
4086
- return process.env["HASNA_MACHINES_NOTIFICATIONS_PATH"] || join2(getDataDir(), "notifications.json");
4101
+ return process.env["HASNA_MACHINES_NOTIFICATIONS_PATH"] || join3(getDataDir(), "notifications.json");
4087
4102
  }
4088
4103
  function ensureParentDir(filePath) {
4089
4104
  if (filePath === ":memory:")
4090
4105
  return;
4091
- const dir = dirname(resolve(filePath));
4092
- if (!existsSync(dir)) {
4106
+ const dir = dirname2(resolve(filePath));
4107
+ if (!existsSync2(dir)) {
4093
4108
  mkdirSync(dir, { recursive: true });
4094
4109
  }
4095
4110
  }
@@ -4154,10 +4169,10 @@ function getDefaultManifest() {
4154
4169
  };
4155
4170
  }
4156
4171
  function readManifest(path = getManifestPath()) {
4157
- if (!existsSync2(path)) {
4172
+ if (!existsSync3(path)) {
4158
4173
  return getDefaultManifest();
4159
4174
  }
4160
- const raw = JSON.parse(readFileSync(path, "utf8"));
4175
+ const raw = JSON.parse(readFileSync2(path, "utf8"));
4161
4176
  return fleetSchema.parse(raw);
4162
4177
  }
4163
4178
  function validateManifest(path = getManifestPath()) {
@@ -4181,7 +4196,7 @@ function getManifestMachine(machineId, path = getManifestPath()) {
4181
4196
  function detectCurrentMachineManifest() {
4182
4197
  const machineId = process.env["HASNA_MACHINES_MACHINE_ID"] || hostname();
4183
4198
  const user = userInfo().username;
4184
- const bunDir = dirname2(process.execPath);
4199
+ const bunDir = dirname3(process.execPath);
4185
4200
  return {
4186
4201
  id: machineId,
4187
4202
  hostname: hostname(),
@@ -4207,22 +4222,22 @@ import { hostname as hostname2 } from "os";
4207
4222
  import { createRequire } from "module";
4208
4223
  import { Database } from "bun:sqlite";
4209
4224
  import {
4210
- existsSync as existsSync3,
4225
+ existsSync as existsSync4,
4211
4226
  mkdirSync as mkdirSync2,
4212
4227
  readdirSync,
4213
4228
  copyFileSync
4214
4229
  } from "fs";
4215
4230
  import { homedir as homedir3 } from "os";
4216
- import { join as join3, relative } from "path";
4217
- import { existsSync as existsSync22, mkdirSync as mkdirSync22, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
4231
+ import { join as join4, relative } from "path";
4232
+ import { existsSync as existsSync22, mkdirSync as mkdirSync22, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "fs";
4218
4233
  import { homedir as homedir22 } from "os";
4219
4234
  import { join as join22 } from "path";
4220
4235
  import { readdirSync as readdirSync2, existsSync as existsSync32 } from "fs";
4221
4236
  import { join as join32 } from "path";
4222
4237
  import { homedir as homedir32 } from "os";
4223
4238
  import { homedir as homedir4 } from "os";
4224
- import { join as join4 } from "path";
4225
- import { join as join6, dirname as dirname3 } from "path";
4239
+ import { join as join42 } from "path";
4240
+ import { join as join6, dirname as dirname4 } from "path";
4226
4241
  import { homedir as homedir5, platform as platform2 } from "os";
4227
4242
  var __create = Object.create;
4228
4243
  var __getProtoOf = Object.getPrototypeOf;
@@ -4255,9 +4270,9 @@ var __toESM = (mod, isNodeMode, target) => {
4255
4270
  return to;
4256
4271
  };
4257
4272
  var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
4258
- var __returnValue2 = (v) => v;
4259
- function __exportSetter2(name, newValue) {
4260
- this[name] = __returnValue2.bind(null, newValue);
4273
+ var __returnValue = (v) => v;
4274
+ function __exportSetter(name, newValue) {
4275
+ this[name] = __returnValue.bind(null, newValue);
4261
4276
  }
4262
4277
  var __export2 = (target, all) => {
4263
4278
  for (var name in all)
@@ -4265,7 +4280,7 @@ var __export2 = (target, all) => {
4265
4280
  get: all[name],
4266
4281
  enumerable: true,
4267
4282
  configurable: true,
4268
- set: __exportSetter2.bind(all, name)
4283
+ set: __exportSetter.bind(all, name)
4269
4284
  });
4270
4285
  };
4271
4286
  var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
@@ -13387,17 +13402,17 @@ var init_zod = __esm(() => {
13387
13402
  init_external();
13388
13403
  });
13389
13404
  function getDataDir2(serviceName) {
13390
- const dir = join3(HASNA_DIR, serviceName);
13405
+ const dir = join4(HASNA_DIR, serviceName);
13391
13406
  mkdirSync2(dir, { recursive: true });
13392
13407
  return dir;
13393
13408
  }
13394
13409
  function getDbPath2(serviceName) {
13395
13410
  const dir = getDataDir2(serviceName);
13396
- return join3(dir, `${serviceName}.db`);
13411
+ return join4(dir, `${serviceName}.db`);
13397
13412
  }
13398
13413
  var HASNA_DIR;
13399
13414
  var init_dotfile = __esm(() => {
13400
- HASNA_DIR = join3(homedir3(), ".hasna");
13415
+ HASNA_DIR = join4(homedir3(), ".hasna");
13401
13416
  });
13402
13417
  var exports_config = {};
13403
13418
  __export2(exports_config, {
@@ -13420,7 +13435,7 @@ function getCloudConfig() {
13420
13435
  return CloudConfigSchema.parse({});
13421
13436
  }
13422
13437
  try {
13423
- const raw = readFileSync2(CONFIG_PATH, "utf-8");
13438
+ const raw = readFileSync3(CONFIG_PATH, "utf-8");
13424
13439
  return CloudConfigSchema.parse(JSON.parse(raw));
13425
13440
  } catch {
13426
13441
  return CloudConfigSchema.parse({});
@@ -13711,7 +13726,7 @@ class SyncProgressTracker {
13711
13726
  init_adapter();
13712
13727
  init_config();
13713
13728
  init_discover();
13714
- var AUTO_SYNC_CONFIG_PATH = join4(homedir4(), ".hasna", "cloud", "config.json");
13729
+ var AUTO_SYNC_CONFIG_PATH = join42(homedir4(), ".hasna", "cloud", "config.json");
13715
13730
  init_config();
13716
13731
  init_adapter();
13717
13732
  init_dotfile();
@@ -14089,16 +14104,16 @@ function runCertPlan(domains, options = {}) {
14089
14104
  }
14090
14105
 
14091
14106
  // src/commands/dns.ts
14092
- import { existsSync as existsSync4, readFileSync as readFileSync3, writeFileSync as writeFileSync3 } from "fs";
14107
+ import { existsSync as existsSync5, readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "fs";
14093
14108
  import { join as join7 } from "path";
14094
14109
  function getDnsPath() {
14095
14110
  return join7(getDataDir(), "dns.json");
14096
14111
  }
14097
14112
  function readMappings() {
14098
14113
  const path = getDnsPath();
14099
- if (!existsSync4(path))
14114
+ if (!existsSync5(path))
14100
14115
  return [];
14101
- return JSON.parse(readFileSync3(path, "utf8"));
14116
+ return JSON.parse(readFileSync4(path, "utf8"));
14102
14117
  }
14103
14118
  function writeMappings(mappings) {
14104
14119
  const path = getDnsPath();
@@ -14400,7 +14415,7 @@ function runTailscaleInstall(machineId, options = {}) {
14400
14415
  }
14401
14416
 
14402
14417
  // src/commands/notifications.ts
14403
- import { existsSync as existsSync5, readFileSync as readFileSync4, writeFileSync as writeFileSync4 } from "fs";
14418
+ import { existsSync as existsSync6, readFileSync as readFileSync5, writeFileSync as writeFileSync4 } from "fs";
14404
14419
  var notificationChannelSchema = exports_external.object({
14405
14420
  id: exports_external.string(),
14406
14421
  type: exports_external.enum(["email", "webhook", "command"]),
@@ -14556,10 +14571,10 @@ function getDefaultNotificationConfig() {
14556
14571
  };
14557
14572
  }
14558
14573
  function readNotificationConfig(path = getNotificationsPath()) {
14559
- if (!existsSync5(path)) {
14574
+ if (!existsSync6(path)) {
14560
14575
  return getDefaultNotificationConfig();
14561
14576
  }
14562
- return notificationConfigSchema.parse(JSON.parse(readFileSync4(path, "utf8")));
14577
+ return notificationConfigSchema.parse(JSON.parse(readFileSync5(path, "utf8")));
14563
14578
  }
14564
14579
  function writeNotificationConfig(config, path = getNotificationsPath()) {
14565
14580
  ensureParentDir(path);
@@ -14731,24 +14746,6 @@ function manifestValidate() {
14731
14746
  return validateManifest(getManifestPath());
14732
14747
  }
14733
14748
 
14734
- // src/version.ts
14735
- import { existsSync as existsSync6, readFileSync as readFileSync5 } from "fs";
14736
- import { dirname as dirname4, join as join8 } from "path";
14737
- import { fileURLToPath } from "url";
14738
- function getPackageVersion() {
14739
- try {
14740
- const here = dirname4(fileURLToPath(import.meta.url));
14741
- const candidates = [join8(here, "..", "package.json"), join8(here, "..", "..", "package.json")];
14742
- const pkgPath = candidates.find((candidate) => existsSync6(candidate));
14743
- if (!pkgPath) {
14744
- return "0.0.0";
14745
- }
14746
- return JSON.parse(readFileSync5(pkgPath, "utf8")).version || "0.0.0";
14747
- } catch {
14748
- return "0.0.0";
14749
- }
14750
- }
14751
-
14752
14749
  // src/commands/status.ts
14753
14750
  function getStatus() {
14754
14751
  const manifest = readManifest();
@@ -15268,6 +15265,9 @@ function getAgentStatus(machineId = getLocalMachineId()) {
15268
15265
  }
15269
15266
 
15270
15267
  // src/mcp/server.ts
15268
+ function buildServer(version = getPackageVersion()) {
15269
+ return createMcpServer(version);
15270
+ }
15271
15271
  function createMcpServer(version) {
15272
15272
  const server = new McpServer({ name: "machines", version });
15273
15273
  server.tool("machines_status", "Return local machine fleet status paths and machine identity.", {}, async () => ({
@@ -15364,21 +15364,107 @@ function createMcpServer(version) {
15364
15364
  return server;
15365
15365
  }
15366
15366
 
15367
- // src/mcp/index.ts
15368
- function getPkgVersion() {
15367
+ // src/mcp/http.ts
15368
+ var DEFAULT_HTTP_PORT = 8821;
15369
+ var HTTP_NAME = "machines";
15370
+ function isHttpMode(args = process.argv.slice(2)) {
15371
+ return args.includes("--http") || process.env.MCP_HTTP === "1";
15372
+ }
15373
+ function resolveHttpPort(args = process.argv.slice(2)) {
15374
+ for (let i = 0;i < args.length; i++) {
15375
+ const arg = args[i];
15376
+ if (arg === "--port" && args[i + 1]) {
15377
+ return parsePort(args[i + 1]);
15378
+ }
15379
+ if (arg.startsWith("--port=")) {
15380
+ return parsePort(arg.slice("--port=".length));
15381
+ }
15382
+ }
15383
+ const envPort = process.env.MCP_HTTP_PORT;
15384
+ if (envPort) {
15385
+ return parsePort(envPort);
15386
+ }
15387
+ return DEFAULT_HTTP_PORT;
15388
+ }
15389
+ function parsePort(raw) {
15390
+ const port = Number.parseInt(raw, 10);
15391
+ if (!Number.isInteger(port) || port < 1 || port > 65535) {
15392
+ throw new Error(`Invalid port: ${raw}`);
15393
+ }
15394
+ return port;
15395
+ }
15396
+ function pathnameFromRequest(req) {
15397
+ return new URL(req.url ?? "/", "http://127.0.0.1").pathname;
15398
+ }
15399
+ async function readRequestBody(req) {
15400
+ if (req.method !== "POST" && req.method !== "DELETE") {
15401
+ return;
15402
+ }
15403
+ const chunks = [];
15404
+ for await (const chunk of req) {
15405
+ chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
15406
+ }
15407
+ const text = Buffer.concat(chunks).toString("utf8");
15408
+ if (!text) {
15409
+ return;
15410
+ }
15411
+ return JSON.parse(text);
15412
+ }
15413
+ async function handleMcpRequest(req, res) {
15414
+ const server = buildServer();
15415
+ const transport = new StreamableHTTPServerTransport({
15416
+ sessionIdGenerator: undefined
15417
+ });
15418
+ await server.connect(transport);
15369
15419
  try {
15370
- const pkgPath = join9(dirname5(fileURLToPath2(import.meta.url)), "..", "..", "package.json");
15371
- return JSON.parse(readFileSync7(pkgPath, "utf8")).version || "0.0.0";
15372
- } catch {
15373
- return "0.0.0";
15420
+ const body = await readRequestBody(req);
15421
+ await transport.handleRequest(req, res, body);
15422
+ } finally {
15423
+ res.on("close", () => {
15424
+ transport.close().catch(() => {
15425
+ return;
15426
+ });
15427
+ server.close().catch(() => {
15428
+ return;
15429
+ });
15430
+ });
15374
15431
  }
15375
15432
  }
15433
+ function startHttpServer(options = {}) {
15434
+ const host = options.host ?? "127.0.0.1";
15435
+ const port = options.port ?? resolveHttpPort();
15436
+ const name = options.name ?? HTTP_NAME;
15437
+ const httpServer = createServer(async (req, res) => {
15438
+ const path = pathnameFromRequest(req);
15439
+ if (req.method === "GET" && path === "/health") {
15440
+ res.writeHead(200, { "content-type": "application/json" });
15441
+ res.end(JSON.stringify({ status: "ok", name }));
15442
+ return;
15443
+ }
15444
+ if (path === "/mcp") {
15445
+ await handleMcpRequest(req, res);
15446
+ return;
15447
+ }
15448
+ res.writeHead(404, { "content-type": "application/json" });
15449
+ res.end(JSON.stringify({ error: "Not found" }));
15450
+ });
15451
+ httpServer.listen(port, host, () => {
15452
+ const address = httpServer.address();
15453
+ const boundPort = typeof address === "object" && address ? address.port : port;
15454
+ console.error(`machines-mcp HTTP listening on http://${host}:${boundPort}`);
15455
+ });
15456
+ return httpServer;
15457
+ }
15458
+
15459
+ // src/mcp/index.ts
15376
15460
  function printHelp() {
15377
15461
  console.log(`Usage: machines-mcp [options]
15378
15462
 
15379
- MCP server for machine fleet management tools (stdio transport)
15463
+ MCP server for machine fleet management tools (stdio transport by default)
15380
15464
 
15381
15465
  Options:
15466
+ --http Start Streamable HTTP transport on 127.0.0.1 (or MCP_HTTP=1)
15467
+ --port <n> HTTP port (default: 8821, or MCP_HTTP_PORT env)
15382
15468
  -V, --version output the version number
15383
15469
  -h, --help display help for command`);
15384
15470
  }
@@ -15388,9 +15474,13 @@ if (args.includes("--help") || args.includes("-h")) {
15388
15474
  process.exit(0);
15389
15475
  }
15390
15476
  if (args.includes("--version") || args.includes("-V")) {
15391
- console.log(getPkgVersion());
15477
+ console.log(getPackageVersion());
15392
15478
  process.exit(0);
15393
15479
  }
15394
- var server = createMcpServer(getPkgVersion());
15395
- var transport = new StdioServerTransport;
15396
- await server.connect(transport);
15480
+ if (isHttpMode(args)) {
15481
+ startHttpServer({ port: resolveHttpPort(args) });
15482
+ } else {
15483
+ const server = buildServer();
15484
+ const transport = new StdioServerTransport;
15485
+ await server.connect(transport);
15486
+ }
@@ -1,4 +1,5 @@
1
1
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
2
  export declare const MACHINE_MCP_TOOL_NAMES: readonly ["machines_status", "machines_doctor", "machines_self_test", "machines_apps_list", "machines_apps_status", "machines_apps_diff", "machines_apps_plan", "machines_apps_apply", "machines_manifest", "machines_manifest_validate", "machines_manifest_bootstrap", "machines_manifest_get", "machines_manifest_remove", "machines_agent_status", "machines_setup_preview", "machines_setup_apply", "machines_sync_preview", "machines_sync_apply", "machines_diff", "machines_install_tailscale_preview", "machines_install_tailscale_apply", "machines_install_claude_status", "machines_install_claude_diff", "machines_install_claude_preview", "machines_install_claude_apply", "machines_ssh_resolve", "machines_ports", "machines_backup_preview", "machines_backup_apply", "machines_cert_preview", "machines_cert_apply", "machines_dns_add", "machines_dns_list", "machines_dns_render", "machines_notifications_add", "machines_notifications_list", "machines_notifications_test", "machines_notifications_dispatch", "machines_notifications_remove", "machines_serve_info", "machines_serve_dashboard"];
3
+ export declare function buildServer(version?: string): McpServer;
3
4
  export declare function createMcpServer(version: string): McpServer;
4
5
  //# sourceMappingURL=server.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/mcp/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AA2BpE,eAAO,MAAM,sBAAsB,4jCA0CzB,CAAC;AAEX,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,CA6R1D"}
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/mcp/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AA4BpE,eAAO,MAAM,sBAAsB,4jCA0CzB,CAAC;AAEX,wBAAgB,WAAW,CAAC,OAAO,GAAE,MAA4B,GAAG,SAAS,CAE5E;AAED,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,CA6R1D"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hasna/machines",
3
- "version": "0.0.9",
3
+ "version": "0.0.11",
4
4
  "description": "Machine fleet management CLI + MCP for developers",
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",