@hasna/machines 0.0.9 → 0.0.10

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/LICENSE CHANGED
@@ -1,3 +1,4 @@
1
+
1
2
  Apache License
2
3
  Version 2.0, January 2004
3
4
  http://www.apache.org/licenses/
@@ -175,7 +176,7 @@
175
176
 
176
177
  END OF TERMS AND CONDITIONS
177
178
 
178
- Copyright 2026 hasna
179
+ Copyright 2026 Hasna, Inc.
179
180
 
180
181
  Licensed under the Apache License, Version 2.0 (the "License");
181
182
  you may not use this file except in compliance with the License.
package/README.md CHANGED
@@ -8,6 +8,21 @@ Machine fleet management for developers — provision, sync, inspect, and operat
8
8
  - `machines-mcp`: MCP server exposing fleet tools to AI agents
9
9
  - `machines-agent`: lightweight local daemon for heartbeats and runtime reporting
10
10
 
11
+ ## HTTP mode
12
+
13
+ Long-lived Streamable HTTP transport for shared agent connections (stdio remains the default):
14
+
15
+ ```bash
16
+ machines-mcp --http
17
+ # or: MCP_HTTP=1 machines-mcp
18
+ # default port: 8821 (override with --port or MCP_HTTP_PORT)
19
+ ```
20
+
21
+ Endpoints on `127.0.0.1` only:
22
+
23
+ - `GET /health` → `{"status":"ok","name":"machines"}`
24
+ - `POST /mcp` → MCP Streamable HTTP
25
+
11
26
  ## Manifest
12
27
 
13
28
  `machines.json` is the desired fleet declaration.
package/dist/cli/index.js CHANGED
@@ -17959,6 +17959,28 @@ function readHistory(historyPath) {
17959
17959
  return [];
17960
17960
  }
17961
17961
  }
17962
+ function writeHistory(entries, historyPath) {
17963
+ const path = resolveHistoryPath(historyPath);
17964
+ ensureParentDir(path);
17965
+ writeFileSync5(path, `${JSON.stringify(entries, null, 2)}
17966
+ `, "utf8");
17967
+ }
17968
+ function computeHash(content) {
17969
+ return createHash("sha256").update(content).digest("hex").slice(0, 16);
17970
+ }
17971
+ function shouldSkipContent(content, skipPatterns) {
17972
+ const lower = content.toLowerCase();
17973
+ return skipPatterns.some((pattern) => lower.includes(pattern.toLowerCase()));
17974
+ }
17975
+ function sanitizeClipboardForRead(content, maxSizeBytes, skipPatterns) {
17976
+ if (Buffer.byteLength(content, "utf8") > maxSizeBytes) {
17977
+ return { ok: false, reason: "content exceeds size limit" };
17978
+ }
17979
+ if (shouldSkipContent(content, skipPatterns)) {
17980
+ return { ok: false, reason: "content matches skip pattern" };
17981
+ }
17982
+ return { ok: true };
17983
+ }
17962
17984
  function getOrCreateClipboardKey() {
17963
17985
  const keyPath = getClipboardKeyPath();
17964
17986
  if (existsSync8(keyPath)) {
@@ -17985,6 +18007,20 @@ function writeClipboardConfig(config, configPath) {
17985
18007
  function readClipboardHistory(historyPath) {
17986
18008
  return readHistory(historyPath);
17987
18009
  }
18010
+ function addClipboardEntry(entry, historyPath) {
18011
+ const entries = readHistory(historyPath);
18012
+ const existing = entries.find((e) => e.hash === entry.hash);
18013
+ if (existing) {
18014
+ existing.timestamp = entry.timestamp;
18015
+ } else {
18016
+ entries.unshift(entry);
18017
+ }
18018
+ const config = readConfig();
18019
+ if (entries.length > config.maxHistory) {
18020
+ entries.length = config.maxHistory;
18021
+ }
18022
+ writeHistory(entries, historyPath);
18023
+ }
17988
18024
  function clearClipboardHistory(historyPath) {
17989
18025
  const path = resolveHistoryPath(historyPath);
17990
18026
  if (existsSync8(path)) {
@@ -18001,6 +18037,337 @@ function getClipboardStatus(historyPath) {
18001
18037
  };
18002
18038
  }
18003
18039
 
18040
+ // src/commands/clipboard-daemon.ts
18041
+ import { readFileSync as readFileSync9, writeFileSync as writeFileSync6 } from "fs";
18042
+ import { join as join10 } from "path";
18043
+ import { createHash as createHash3 } from "crypto";
18044
+
18045
+ // src/commands/clipboard-server.ts
18046
+ import { createServer } from "http";
18047
+ import { createHash as createHash2 } from "crypto";
18048
+ import { readFileSync as readFileSync8 } from "fs";
18049
+ function readLocalClipboardSync() {
18050
+ const platform4 = process.platform;
18051
+ if (platform4 === "darwin") {
18052
+ const result = Bun.spawnSync(["pbpaste"], { stdout: "pipe", stderr: "pipe" });
18053
+ return result.exitCode === 0 ? result.stdout.toString("utf8").trim() : "";
18054
+ }
18055
+ if (platform4 === "linux") {
18056
+ if (hasCommand2("wl-paste")) {
18057
+ const result = Bun.spawnSync(["wl-paste"], { stdout: "pipe", stderr: "pipe" });
18058
+ return result.exitCode === 0 ? result.stdout.toString("utf8").trim() : "";
18059
+ }
18060
+ if (hasCommand2("xclip")) {
18061
+ const result = Bun.spawnSync(["xclip", "-selection", "clipboard", "-o"], { stdout: "pipe", stderr: "pipe" });
18062
+ return result.exitCode === 0 ? result.stdout.toString("utf8").trim() : "";
18063
+ }
18064
+ return "";
18065
+ }
18066
+ return "";
18067
+ }
18068
+ function writeLocalClipboardSync(content) {
18069
+ const platform4 = process.platform;
18070
+ if (platform4 === "darwin") {
18071
+ const result = Bun.spawnSync(["pbcopy"], { stdin: new TextEncoder().encode(content), stdout: "ignore", stderr: "ignore" });
18072
+ return result.exitCode === 0;
18073
+ }
18074
+ if (platform4 === "linux") {
18075
+ if (hasCommand2("wl-copy")) {
18076
+ const result = Bun.spawnSync(["wl-copy"], { stdin: new TextEncoder().encode(content), stdout: "ignore", stderr: "ignore" });
18077
+ return result.exitCode === 0;
18078
+ }
18079
+ if (hasCommand2("xclip")) {
18080
+ const result = Bun.spawnSync(["xclip", "-selection", "clipboard"], { stdin: new TextEncoder().encode(content), stdout: "ignore", stderr: "ignore" });
18081
+ return result.exitCode === 0;
18082
+ }
18083
+ return false;
18084
+ }
18085
+ return false;
18086
+ }
18087
+ function hasCommand2(binary) {
18088
+ const result = Bun.spawnSync(["bash", "-lc", `command -v ${binary} >/dev/null 2>&1`], { stdout: "ignore", stderr: "ignore", env: process.env });
18089
+ return result.exitCode === 0;
18090
+ }
18091
+ function loadSharedSecret() {
18092
+ const keyPath = getClipboardKeyPath();
18093
+ try {
18094
+ return readFileSync8(keyPath, "utf8").trim();
18095
+ } catch {
18096
+ return "";
18097
+ }
18098
+ }
18099
+ function authenticate(request) {
18100
+ const authHeader = request.headers["authorization"];
18101
+ if (!authHeader || !authHeader.startsWith("Bearer ")) {
18102
+ return false;
18103
+ }
18104
+ const token = authHeader.slice(7);
18105
+ const secret = loadSharedSecret();
18106
+ if (!secret)
18107
+ return false;
18108
+ return createHash2("sha256").update(token).digest("hex") === createHash2("sha256").update(secret).digest("hex");
18109
+ }
18110
+ function jsonResponse(response, status, data) {
18111
+ response.writeHead(status, { "content-type": "application/json" });
18112
+ response.end(JSON.stringify(data));
18113
+ }
18114
+ var currentContentHash = null;
18115
+ function getCurrentContentHash() {
18116
+ return currentContentHash;
18117
+ }
18118
+ function setCurrentContentHash(hash) {
18119
+ currentContentHash = hash;
18120
+ }
18121
+ function startClipboardServer(options = {}) {
18122
+ const config = options.config || readClipboardConfig();
18123
+ const port = options.port || config.port;
18124
+ const server = createServer(async (request, response) => {
18125
+ if (!authenticate(request)) {
18126
+ return jsonResponse(response, 401, { error: "unauthorized" });
18127
+ }
18128
+ const url = new URL(request.url || "/", `http://${request.headers.host || "localhost"}`);
18129
+ if (url.pathname === "/clipboard" && request.method === "POST") {
18130
+ return handleReceiveClipboard(request, response, config);
18131
+ }
18132
+ if (url.pathname === "/clipboard" && request.method === "GET") {
18133
+ return handleGetClipboard(response, config);
18134
+ }
18135
+ if (url.pathname === "/health" && request.method === "GET") {
18136
+ return jsonResponse(response, 200, { ok: true, machineId: process.env["HASNA_MACHINES_MACHINE_ID"] || "unknown" });
18137
+ }
18138
+ jsonResponse(response, 404, { error: "not found" });
18139
+ });
18140
+ server.listen(port, "0.0.0.0", () => {});
18141
+ server.on("error", (error) => {
18142
+ console.error(`clipboard server error: ${error.message}`);
18143
+ });
18144
+ return {
18145
+ server,
18146
+ port,
18147
+ close: async () => {
18148
+ await new Promise((resolve2) => server.close(() => resolve2()));
18149
+ }
18150
+ };
18151
+ }
18152
+ function handleReceiveClipboard(request, response, config) {
18153
+ let body = "";
18154
+ request.on("data", (chunk) => {
18155
+ body += chunk;
18156
+ });
18157
+ request.on("end", () => {
18158
+ try {
18159
+ const parsed = JSON.parse(body);
18160
+ const content = parsed.content || "";
18161
+ const contentType = parsed.contentType || "text";
18162
+ const sourceMachine = parsed.sourceMachine || "unknown";
18163
+ if (!content) {
18164
+ return jsonResponse(response, 400, { error: "empty content" });
18165
+ }
18166
+ const hash = computeHash(content);
18167
+ if (hash === currentContentHash) {
18168
+ return jsonResponse(response, 200, { received: false, reason: "loop detected" });
18169
+ }
18170
+ const check2 = sanitizeClipboardForRead(content, config.maxSizeBytes, config.skipPatterns);
18171
+ if (!check2.ok) {
18172
+ return jsonResponse(response, 200, { received: false, reason: check2.reason });
18173
+ }
18174
+ writeLocalClipboardSync(content);
18175
+ currentContentHash = hash;
18176
+ addClipboardEntry({
18177
+ hash,
18178
+ content,
18179
+ contentType,
18180
+ sourceMachine,
18181
+ timestamp: new Date().toISOString()
18182
+ });
18183
+ return jsonResponse(response, 200, { received: true, hash });
18184
+ } catch {
18185
+ return jsonResponse(response, 400, { error: "invalid JSON" });
18186
+ }
18187
+ });
18188
+ }
18189
+ function handleGetClipboard(response, config) {
18190
+ const content = readLocalClipboardSync();
18191
+ if (!content) {
18192
+ return jsonResponse(response, 200, { content: "", hash: null });
18193
+ }
18194
+ const hash = computeHash(content);
18195
+ return jsonResponse(response, 200, { content, hash, contentType: "text" });
18196
+ }
18197
+
18198
+ // src/commands/clipboard-daemon.ts
18199
+ var DAEMON_PID_PATH = join10(getDataDir(), "clipboard-daemon.pid");
18200
+ function readLocalClipboardSync2() {
18201
+ const platform4 = process.platform;
18202
+ if (platform4 === "darwin") {
18203
+ const result = Bun.spawnSync(["pbpaste"], { stdout: "pipe", stderr: "pipe" });
18204
+ return result.exitCode === 0 ? result.stdout.toString("utf8").trim() : "";
18205
+ }
18206
+ if (platform4 === "linux") {
18207
+ if (hasCommand3("wl-paste")) {
18208
+ const result = Bun.spawnSync(["wl-paste"], { stdout: "pipe", stderr: "pipe" });
18209
+ return result.exitCode === 0 ? result.stdout.toString("utf8").trim() : "";
18210
+ }
18211
+ if (hasCommand3("xclip")) {
18212
+ const result = Bun.spawnSync(["xclip", "-selection", "clipboard", "-o"], { stdout: "pipe", stderr: "pipe" });
18213
+ return result.exitCode === 0 ? result.stdout.toString("utf8").trim() : "";
18214
+ }
18215
+ return "";
18216
+ }
18217
+ return "";
18218
+ }
18219
+ function hasCommand3(binary) {
18220
+ const result = Bun.spawnSync(["bash", "-lc", `command -v ${binary} >/dev/null 2>&1`], { stdout: "ignore", stderr: "ignore", env: process.env });
18221
+ return result.exitCode === 0;
18222
+ }
18223
+ function hasDisplayServer() {
18224
+ const display = process.env["DISPLAY"] || "";
18225
+ const wayland = process.env["WAYLAND_DISPLAY"] || "";
18226
+ if (display || wayland)
18227
+ return true;
18228
+ if (process.platform === "darwin")
18229
+ return true;
18230
+ if (process.platform === "linux") {
18231
+ try {
18232
+ const result = Bun.spawnSync(["loginctl", "list-sessions", "--no-legend"], { stdout: "pipe", stderr: "pipe", env: process.env, timeout: 2000 });
18233
+ if (result.exitCode === 0) {
18234
+ const sessions = result.stdout.toString("utf8").trim();
18235
+ if (sessions) {
18236
+ for (const line of sessions.split(`
18237
+ `)) {
18238
+ const sessionId = line.trim().split(/\s+/)[0];
18239
+ if (sessionId) {
18240
+ const typeResult = Bun.spawnSync(["loginctl", "show-session", sessionId, "-p", "Type", "--value"], { stdout: "pipe", stderr: "pipe", env: process.env, timeout: 2000 });
18241
+ if (typeResult.exitCode === 0) {
18242
+ const sessionType = typeResult.stdout.toString("utf8").trim();
18243
+ if (sessionType === "wayland" || sessionType === "x11") {
18244
+ return true;
18245
+ }
18246
+ }
18247
+ }
18248
+ }
18249
+ }
18250
+ }
18251
+ } catch {}
18252
+ return false;
18253
+ }
18254
+ return false;
18255
+ }
18256
+ function computeHash2(content) {
18257
+ return createHash3("sha256").update(content).digest("hex").slice(0, 16);
18258
+ }
18259
+ function loadSharedSecret2() {
18260
+ try {
18261
+ return readFileSync9(getClipboardKeyPath(), "utf8").trim();
18262
+ } catch {
18263
+ return "";
18264
+ }
18265
+ }
18266
+ function writePid(pid) {
18267
+ writeFileSync6(DAEMON_PID_PATH, `${pid}
18268
+ `);
18269
+ }
18270
+ function readPid() {
18271
+ try {
18272
+ const pid = Number.parseInt(readFileSync9(DAEMON_PID_PATH, "utf8").trim());
18273
+ return Number.isFinite(pid) ? pid : null;
18274
+ } catch {
18275
+ return null;
18276
+ }
18277
+ }
18278
+ function isProcessRunning(pid) {
18279
+ try {
18280
+ process.kill(pid, 0);
18281
+ return true;
18282
+ } catch {
18283
+ return false;
18284
+ }
18285
+ }
18286
+ function stopClipboardDaemon() {
18287
+ const pid = readPid();
18288
+ if (pid && isProcessRunning(pid)) {
18289
+ process.kill(pid, "SIGTERM");
18290
+ return { stopped: true, pid };
18291
+ }
18292
+ return { stopped: false, pid };
18293
+ }
18294
+ function startClipboardDaemon(port) {
18295
+ const config = readClipboardConfig();
18296
+ const daemonPort = port || config.port;
18297
+ const { server, close } = startClipboardServer({ port: daemonPort });
18298
+ server.on("listening", () => {
18299
+ console.log(`clipboard daemon started on port ${daemonPort} (pid ${process.pid})`);
18300
+ writePid(process.pid);
18301
+ });
18302
+ const secret = loadSharedSecret2();
18303
+ const machineId = process.env["HASNA_MACHINES_MACHINE_ID"] || "unknown";
18304
+ const hasDisplay = hasDisplayServer();
18305
+ if (!hasDisplay) {
18306
+ console.log("clipboard daemon running in receive-only mode (no display server)");
18307
+ }
18308
+ setInterval(async () => {
18309
+ if (!hasDisplay)
18310
+ return;
18311
+ const content = readLocalClipboardSync2();
18312
+ if (!content)
18313
+ return;
18314
+ const hash = computeHash2(content);
18315
+ const currentContentHash2 = getCurrentContentHash();
18316
+ if (hash === currentContentHash2)
18317
+ return;
18318
+ setCurrentContentHash(hash);
18319
+ const peers = await discoverPeers();
18320
+ for (const peer of peers) {
18321
+ try {
18322
+ const res = await fetch(`http://${peer.host}:${peer.port}/clipboard`, {
18323
+ method: "POST",
18324
+ headers: {
18325
+ "content-type": "application/json",
18326
+ authorization: `Bearer ${secret}`
18327
+ },
18328
+ body: JSON.stringify({
18329
+ content,
18330
+ contentType: "text",
18331
+ sourceMachine: machineId
18332
+ }),
18333
+ signal: AbortSignal.timeout(2000)
18334
+ });
18335
+ if (res.ok) {
18336
+ const data = await res.json();
18337
+ if (data["received"] === true) {
18338
+ console.log(`clipboard sent to ${peer.host}`);
18339
+ }
18340
+ }
18341
+ } catch {}
18342
+ }
18343
+ }, 500);
18344
+ }
18345
+ async function discoverPeers() {
18346
+ const config = readClipboardConfig();
18347
+ const peers = [];
18348
+ try {
18349
+ const result = Bun.spawnSync(["tailscale", "status", "--json"], { stdout: "pipe", stderr: "pipe", env: process.env });
18350
+ if (result.exitCode === 0) {
18351
+ const status = JSON.parse(result.stdout.toString("utf8"));
18352
+ const peers_map = status["Peer"] || {};
18353
+ for (const [, peerInfo] of Object.entries(peers_map)) {
18354
+ for (const ip of peerInfo.TailscaleIPs) {
18355
+ if (ip.includes(".") && !ip.endsWith(".1")) {
18356
+ peers.push({ host: ip, port: config.port });
18357
+ }
18358
+ }
18359
+ }
18360
+ }
18361
+ } catch {}
18362
+ const knownPeers = ["100.82.44.120", "100.100.226.69", "100.71.123.34", "100.85.234.92"];
18363
+ for (const ip of knownPeers) {
18364
+ if (!peers.some((p) => p.host === ip)) {
18365
+ peers.push({ host: ip, port: config.port });
18366
+ }
18367
+ }
18368
+ return peers;
18369
+ }
18370
+
18004
18371
  // src/cli-utils.ts
18005
18372
  function parseIntegerOption(value, label, constraints = {}) {
18006
18373
  const parsed = Number.parseInt(value, 10);
@@ -18032,7 +18399,7 @@ ${items.map((item) => `- ${item}`).join(`
18032
18399
 
18033
18400
  // src/cli/index.ts
18034
18401
  import { rmSync as rmSync2 } from "fs";
18035
- import { readFileSync as readFileSync8 } from "fs";
18402
+ import { readFileSync as readFileSync10 } from "fs";
18036
18403
  var program2 = new Command;
18037
18404
  function printJsonOrText(data, text, json = false) {
18038
18405
  if (json || program2.opts().quiet) {
@@ -18179,7 +18546,7 @@ manifestCommand.command("add").description("Add or replace a machine in the flee
18179
18546
  console.error("error: --from-stdin requires piped input");
18180
18547
  process.exit(1);
18181
18548
  }
18182
- const input = readFileSync8(0, "utf8");
18549
+ const input = readFileSync10(0, "utf8");
18183
18550
  const machine2 = JSON.parse(input);
18184
18551
  console.log(JSON.stringify(manifestAdd(machine2), null, 2));
18185
18552
  return;
@@ -18358,6 +18725,14 @@ clipboardCommand.command("key").description("Show or rotate the shared secret ke
18358
18725
  const key = getOrCreateClipboardKey();
18359
18726
  printJsonOrText({ key }, key, options.json);
18360
18727
  });
18728
+ clipboardCommand.command("start").description("Start clipboard sync daemon").option("--port <port>", "Port to listen on").action((options) => {
18729
+ const port = options.port ? Number(options.port) : undefined;
18730
+ startClipboardDaemon(port);
18731
+ });
18732
+ clipboardCommand.command("stop").description("Stop clipboard sync daemon").action(() => {
18733
+ const result = stopClipboardDaemon();
18734
+ console.log(result.stopped ? `daemon stopped (pid ${result.pid})` : "daemon not running");
18735
+ });
18361
18736
  installClaudeCommand.command("status").description("Check installed state for Claude, Codex, and Gemini CLIs").option("--machine <id>", "Machine identifier").option("--tool <name...>", "CLI tools to inspect (claude, codex, gemini)").option("-j, --json", "Print JSON output", false).action((options) => {
18362
18737
  const result = getClaudeCliStatus(options.machine, options.tool);
18363
18738
  printJsonOrText(result, renderClaudeStatusResult(result), options.json);
@@ -0,0 +1,6 @@
1
+ export declare function stopClipboardDaemon(): {
2
+ stopped: boolean;
3
+ pid: number | null;
4
+ };
5
+ export declare function startClipboardDaemon(port?: number): void;
6
+ //# sourceMappingURL=clipboard-daemon.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"clipboard-daemon.d.ts","sourceRoot":"","sources":["../../src/commands/clipboard-daemon.ts"],"names":[],"mappings":"AA2HA,wBAAgB,mBAAmB,IAAI;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAO9E;AAED,wBAAgB,oBAAoB,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CA6DxD"}
@@ -9,6 +9,8 @@ export interface ClipboardServerHandle {
9
9
  port: number;
10
10
  close: () => Promise<void>;
11
11
  }
12
+ export declare function getCurrentContentHash(): string | null;
13
+ export declare function setCurrentContentHash(hash: string): void;
12
14
  export declare function startClipboardServer(options?: ClipboardServerOptions): ClipboardServerHandle;
13
15
  export declare function pushClipboardToPeer(host: string, port: number, token: string): Promise<{
14
16
  sent: boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"clipboard-server.d.ts","sourceRoot":"","sources":["../../src/commands/clipboard-server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAA6C,MAAM,WAAW,CAAC;AAKpF,OAAO,KAAK,EAAE,eAAe,EAAkB,MAAM,aAAa,CAAC;AAuFnE,MAAM,WAAW,sBAAsB;IACrC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,eAAe,CAAC;CAC1B;AAED,MAAM,WAAW,qBAAqB;IACpC,MAAM,EAAE,UAAU,CAAC,OAAO,YAAY,CAAC,CAAC;IACxC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC5B;AAID,wBAAgB,oBAAoB,CAAC,OAAO,GAAE,sBAA2B,GAAG,qBAAqB,CA6ChG;AA6DD,wBAAsB,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CA4ChI"}
1
+ {"version":3,"file":"clipboard-server.d.ts","sourceRoot":"","sources":["../../src/commands/clipboard-server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAA6C,MAAM,WAAW,CAAC;AAKpF,OAAO,KAAK,EAAE,eAAe,EAAkB,MAAM,aAAa,CAAC;AAuFnE,MAAM,WAAW,sBAAsB;IACrC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,eAAe,CAAC;CAC1B;AAED,MAAM,WAAW,qBAAqB;IACpC,MAAM,EAAE,UAAU,CAAC,OAAO,YAAY,CAAC,CAAC;IACxC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC5B;AAID,wBAAgB,qBAAqB,IAAI,MAAM,GAAG,IAAI,CAA+B;AACrF,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAA+B;AAExF,wBAAgB,oBAAoB,CAAC,OAAO,GAAE,sBAA2B,GAAG,qBAAqB,CA6ChG;AA6DD,wBAAsB,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CA4ChI"}
package/dist/index.js CHANGED
@@ -30811,6 +30811,9 @@ var MACHINE_MCP_TOOL_NAMES = [
30811
30811
  "machines_serve_info",
30812
30812
  "machines_serve_dashboard"
30813
30813
  ];
30814
+ function buildServer(version2 = getPackageVersion()) {
30815
+ return createMcpServer(version2);
30816
+ }
30814
30817
  function createMcpServer(version2) {
30815
30818
  const server = new McpServer({ name: "machines", version: version2 });
30816
30819
  server.tool("machines_status", "Return local machine fleet status paths and machine identity.", {}, async () => ({
@@ -30979,6 +30982,7 @@ export {
30979
30982
  buildSyncPlan,
30980
30983
  buildSshCommand,
30981
30984
  buildSetupPlan,
30985
+ buildServer,
30982
30986
  buildClaudeInstallPlan,
30983
30987
  buildCertPlan,
30984
30988
  buildBackupPlan,
@@ -0,0 +1,12 @@
1
+ import { type Server } from "node:http";
2
+ export declare const DEFAULT_HTTP_PORT = 8821;
3
+ export declare const HTTP_NAME = "machines";
4
+ export interface StartHttpServerOptions {
5
+ port?: number;
6
+ host?: string;
7
+ name?: string;
8
+ }
9
+ export declare function isHttpMode(args?: string[]): boolean;
10
+ export declare function resolveHttpPort(args?: string[]): number;
11
+ export declare function startHttpServer(options?: StartHttpServerOptions): Server;
12
+ //# sourceMappingURL=http.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../../src/mcp/http.ts"],"names":[],"mappings":"AAAA,OAAO,EAAsC,KAAK,MAAM,EAAuB,MAAM,WAAW,CAAC;AAIjG,eAAO,MAAM,iBAAiB,OAAO,CAAC;AACtC,eAAO,MAAM,SAAS,aAAa,CAAC;AAEpC,MAAM,WAAW,sBAAsB;IACrC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,wBAAgB,UAAU,CAAC,IAAI,GAAE,MAAM,EAA0B,GAAG,OAAO,CAE1E;AAED,wBAAgB,eAAe,CAAC,IAAI,GAAE,MAAM,EAA0B,GAAG,MAAM,CAiB9E;AAmDD,wBAAgB,eAAe,CAAC,OAAO,GAAE,sBAA2B,GAAG,MAAM,CA8B5E"}
package/dist/mcp/index.js CHANGED
@@ -16,11 +16,30 @@ var __export = (target, all) => {
16
16
  };
17
17
 
18
18
  // 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
19
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
23
20
 
21
+ // src/version.ts
22
+ import { existsSync, readFileSync } from "fs";
23
+ import { dirname, join } from "path";
24
+ import { fileURLToPath } from "url";
25
+ function getPackageVersion() {
26
+ try {
27
+ const here = dirname(fileURLToPath(import.meta.url));
28
+ const candidates = [join(here, "..", "package.json"), join(here, "..", "..", "package.json")];
29
+ const pkgPath = candidates.find((candidate) => existsSync(candidate));
30
+ if (!pkgPath) {
31
+ return "0.0.0";
32
+ }
33
+ return JSON.parse(readFileSync(pkgPath, "utf8")).version || "0.0.0";
34
+ } catch {
35
+ return "0.0.0";
36
+ }
37
+ }
38
+
39
+ // src/mcp/http.ts
40
+ import { createServer } from "http";
41
+ import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
42
+
24
43
  // src/mcp/server.ts
25
44
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
26
45
 
@@ -3999,20 +4018,20 @@ var coerce = {
3999
4018
  var NEVER = INVALID;
4000
4019
  // src/commands/backup.ts
4001
4020
  import { homedir } from "os";
4002
- import { join } from "path";
4021
+ import { join as join2 } from "path";
4003
4022
  function quote(value) {
4004
4023
  return `'${value.replace(/'/g, `'\\''`)}'`;
4005
4024
  }
4006
4025
  function defaultBackupSources() {
4007
4026
  const home = homedir();
4008
4027
  return [
4009
- join(home, ".hasna"),
4010
- join(home, ".ssh"),
4011
- join(home, ".secrets")
4028
+ join2(home, ".hasna"),
4029
+ join2(home, ".ssh"),
4030
+ join2(home, ".secrets")
4012
4031
  ];
4013
4032
  }
4014
4033
  function buildBackupPlan(bucket, prefix = "machines") {
4015
- const archivePath = join(homedir(), ".hasna", "machines", "backup.tgz");
4034
+ const archivePath = join2(homedir(), ".hasna", "machines", "backup.tgz");
4016
4035
  const sources = defaultBackupSources();
4017
4036
  const steps = [
4018
4037
  {
@@ -4063,33 +4082,33 @@ function runBackup(bucket, prefix = "machines", options = {}) {
4063
4082
  }
4064
4083
 
4065
4084
  // src/manifests.ts
4066
- import { existsSync as existsSync2, readFileSync, writeFileSync } from "fs";
4085
+ import { existsSync as existsSync3, readFileSync as readFileSync2, writeFileSync } from "fs";
4067
4086
  import { arch, homedir as homedir2, hostname, platform, userInfo } from "os";
4068
- import { dirname as dirname2 } from "path";
4087
+ import { dirname as dirname3 } from "path";
4069
4088
 
4070
4089
  // src/paths.ts
4071
- import { existsSync, mkdirSync } from "fs";
4072
- import { dirname, join as join2, resolve } from "path";
4090
+ import { existsSync as existsSync2, mkdirSync } from "fs";
4091
+ import { dirname as dirname2, join as join3, resolve } from "path";
4073
4092
  function homeDir() {
4074
4093
  return process.env["HOME"] || process.env["USERPROFILE"] || "~";
4075
4094
  }
4076
4095
  function getDataDir() {
4077
- return process.env["HASNA_MACHINES_DIR"] || join2(homeDir(), ".hasna", "machines");
4096
+ return process.env["HASNA_MACHINES_DIR"] || join3(homeDir(), ".hasna", "machines");
4078
4097
  }
4079
4098
  function getDbPath() {
4080
- return process.env["HASNA_MACHINES_DB_PATH"] || join2(getDataDir(), "machines.db");
4099
+ return process.env["HASNA_MACHINES_DB_PATH"] || join3(getDataDir(), "machines.db");
4081
4100
  }
4082
4101
  function getManifestPath() {
4083
- return process.env["HASNA_MACHINES_MANIFEST_PATH"] || join2(getDataDir(), "machines.json");
4102
+ return process.env["HASNA_MACHINES_MANIFEST_PATH"] || join3(getDataDir(), "machines.json");
4084
4103
  }
4085
4104
  function getNotificationsPath() {
4086
- return process.env["HASNA_MACHINES_NOTIFICATIONS_PATH"] || join2(getDataDir(), "notifications.json");
4105
+ return process.env["HASNA_MACHINES_NOTIFICATIONS_PATH"] || join3(getDataDir(), "notifications.json");
4087
4106
  }
4088
4107
  function ensureParentDir(filePath) {
4089
4108
  if (filePath === ":memory:")
4090
4109
  return;
4091
- const dir = dirname(resolve(filePath));
4092
- if (!existsSync(dir)) {
4110
+ const dir = dirname2(resolve(filePath));
4111
+ if (!existsSync2(dir)) {
4093
4112
  mkdirSync(dir, { recursive: true });
4094
4113
  }
4095
4114
  }
@@ -4154,10 +4173,10 @@ function getDefaultManifest() {
4154
4173
  };
4155
4174
  }
4156
4175
  function readManifest(path = getManifestPath()) {
4157
- if (!existsSync2(path)) {
4176
+ if (!existsSync3(path)) {
4158
4177
  return getDefaultManifest();
4159
4178
  }
4160
- const raw = JSON.parse(readFileSync(path, "utf8"));
4179
+ const raw = JSON.parse(readFileSync2(path, "utf8"));
4161
4180
  return fleetSchema.parse(raw);
4162
4181
  }
4163
4182
  function validateManifest(path = getManifestPath()) {
@@ -4181,7 +4200,7 @@ function getManifestMachine(machineId, path = getManifestPath()) {
4181
4200
  function detectCurrentMachineManifest() {
4182
4201
  const machineId = process.env["HASNA_MACHINES_MACHINE_ID"] || hostname();
4183
4202
  const user = userInfo().username;
4184
- const bunDir = dirname2(process.execPath);
4203
+ const bunDir = dirname3(process.execPath);
4185
4204
  return {
4186
4205
  id: machineId,
4187
4206
  hostname: hostname(),
@@ -4207,22 +4226,22 @@ import { hostname as hostname2 } from "os";
4207
4226
  import { createRequire } from "module";
4208
4227
  import { Database } from "bun:sqlite";
4209
4228
  import {
4210
- existsSync as existsSync3,
4229
+ existsSync as existsSync4,
4211
4230
  mkdirSync as mkdirSync2,
4212
4231
  readdirSync,
4213
4232
  copyFileSync
4214
4233
  } from "fs";
4215
4234
  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";
4235
+ import { join as join4, relative } from "path";
4236
+ import { existsSync as existsSync22, mkdirSync as mkdirSync22, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "fs";
4218
4237
  import { homedir as homedir22 } from "os";
4219
4238
  import { join as join22 } from "path";
4220
4239
  import { readdirSync as readdirSync2, existsSync as existsSync32 } from "fs";
4221
4240
  import { join as join32 } from "path";
4222
4241
  import { homedir as homedir32 } from "os";
4223
4242
  import { homedir as homedir4 } from "os";
4224
- import { join as join4 } from "path";
4225
- import { join as join6, dirname as dirname3 } from "path";
4243
+ import { join as join42 } from "path";
4244
+ import { join as join6, dirname as dirname4 } from "path";
4226
4245
  import { homedir as homedir5, platform as platform2 } from "os";
4227
4246
  var __create = Object.create;
4228
4247
  var __getProtoOf = Object.getPrototypeOf;
@@ -13387,17 +13406,17 @@ var init_zod = __esm(() => {
13387
13406
  init_external();
13388
13407
  });
13389
13408
  function getDataDir2(serviceName) {
13390
- const dir = join3(HASNA_DIR, serviceName);
13409
+ const dir = join4(HASNA_DIR, serviceName);
13391
13410
  mkdirSync2(dir, { recursive: true });
13392
13411
  return dir;
13393
13412
  }
13394
13413
  function getDbPath2(serviceName) {
13395
13414
  const dir = getDataDir2(serviceName);
13396
- return join3(dir, `${serviceName}.db`);
13415
+ return join4(dir, `${serviceName}.db`);
13397
13416
  }
13398
13417
  var HASNA_DIR;
13399
13418
  var init_dotfile = __esm(() => {
13400
- HASNA_DIR = join3(homedir3(), ".hasna");
13419
+ HASNA_DIR = join4(homedir3(), ".hasna");
13401
13420
  });
13402
13421
  var exports_config = {};
13403
13422
  __export2(exports_config, {
@@ -13420,7 +13439,7 @@ function getCloudConfig() {
13420
13439
  return CloudConfigSchema.parse({});
13421
13440
  }
13422
13441
  try {
13423
- const raw = readFileSync2(CONFIG_PATH, "utf-8");
13442
+ const raw = readFileSync3(CONFIG_PATH, "utf-8");
13424
13443
  return CloudConfigSchema.parse(JSON.parse(raw));
13425
13444
  } catch {
13426
13445
  return CloudConfigSchema.parse({});
@@ -13711,7 +13730,7 @@ class SyncProgressTracker {
13711
13730
  init_adapter();
13712
13731
  init_config();
13713
13732
  init_discover();
13714
- var AUTO_SYNC_CONFIG_PATH = join4(homedir4(), ".hasna", "cloud", "config.json");
13733
+ var AUTO_SYNC_CONFIG_PATH = join42(homedir4(), ".hasna", "cloud", "config.json");
13715
13734
  init_config();
13716
13735
  init_adapter();
13717
13736
  init_dotfile();
@@ -14089,16 +14108,16 @@ function runCertPlan(domains, options = {}) {
14089
14108
  }
14090
14109
 
14091
14110
  // src/commands/dns.ts
14092
- import { existsSync as existsSync4, readFileSync as readFileSync3, writeFileSync as writeFileSync3 } from "fs";
14111
+ import { existsSync as existsSync5, readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "fs";
14093
14112
  import { join as join7 } from "path";
14094
14113
  function getDnsPath() {
14095
14114
  return join7(getDataDir(), "dns.json");
14096
14115
  }
14097
14116
  function readMappings() {
14098
14117
  const path = getDnsPath();
14099
- if (!existsSync4(path))
14118
+ if (!existsSync5(path))
14100
14119
  return [];
14101
- return JSON.parse(readFileSync3(path, "utf8"));
14120
+ return JSON.parse(readFileSync4(path, "utf8"));
14102
14121
  }
14103
14122
  function writeMappings(mappings) {
14104
14123
  const path = getDnsPath();
@@ -14400,7 +14419,7 @@ function runTailscaleInstall(machineId, options = {}) {
14400
14419
  }
14401
14420
 
14402
14421
  // src/commands/notifications.ts
14403
- import { existsSync as existsSync5, readFileSync as readFileSync4, writeFileSync as writeFileSync4 } from "fs";
14422
+ import { existsSync as existsSync6, readFileSync as readFileSync5, writeFileSync as writeFileSync4 } from "fs";
14404
14423
  var notificationChannelSchema = exports_external.object({
14405
14424
  id: exports_external.string(),
14406
14425
  type: exports_external.enum(["email", "webhook", "command"]),
@@ -14556,10 +14575,10 @@ function getDefaultNotificationConfig() {
14556
14575
  };
14557
14576
  }
14558
14577
  function readNotificationConfig(path = getNotificationsPath()) {
14559
- if (!existsSync5(path)) {
14578
+ if (!existsSync6(path)) {
14560
14579
  return getDefaultNotificationConfig();
14561
14580
  }
14562
- return notificationConfigSchema.parse(JSON.parse(readFileSync4(path, "utf8")));
14581
+ return notificationConfigSchema.parse(JSON.parse(readFileSync5(path, "utf8")));
14563
14582
  }
14564
14583
  function writeNotificationConfig(config, path = getNotificationsPath()) {
14565
14584
  ensureParentDir(path);
@@ -14731,24 +14750,6 @@ function manifestValidate() {
14731
14750
  return validateManifest(getManifestPath());
14732
14751
  }
14733
14752
 
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
14753
  // src/commands/status.ts
14753
14754
  function getStatus() {
14754
14755
  const manifest = readManifest();
@@ -15268,6 +15269,9 @@ function getAgentStatus(machineId = getLocalMachineId()) {
15268
15269
  }
15269
15270
 
15270
15271
  // src/mcp/server.ts
15272
+ function buildServer(version = getPackageVersion()) {
15273
+ return createMcpServer(version);
15274
+ }
15271
15275
  function createMcpServer(version) {
15272
15276
  const server = new McpServer({ name: "machines", version });
15273
15277
  server.tool("machines_status", "Return local machine fleet status paths and machine identity.", {}, async () => ({
@@ -15364,21 +15368,107 @@ function createMcpServer(version) {
15364
15368
  return server;
15365
15369
  }
15366
15370
 
15367
- // src/mcp/index.ts
15368
- function getPkgVersion() {
15371
+ // src/mcp/http.ts
15372
+ var DEFAULT_HTTP_PORT = 8821;
15373
+ var HTTP_NAME = "machines";
15374
+ function isHttpMode(args = process.argv.slice(2)) {
15375
+ return args.includes("--http") || process.env.MCP_HTTP === "1";
15376
+ }
15377
+ function resolveHttpPort(args = process.argv.slice(2)) {
15378
+ for (let i = 0;i < args.length; i++) {
15379
+ const arg = args[i];
15380
+ if (arg === "--port" && args[i + 1]) {
15381
+ return parsePort(args[i + 1]);
15382
+ }
15383
+ if (arg.startsWith("--port=")) {
15384
+ return parsePort(arg.slice("--port=".length));
15385
+ }
15386
+ }
15387
+ const envPort = process.env.MCP_HTTP_PORT;
15388
+ if (envPort) {
15389
+ return parsePort(envPort);
15390
+ }
15391
+ return DEFAULT_HTTP_PORT;
15392
+ }
15393
+ function parsePort(raw) {
15394
+ const port = Number.parseInt(raw, 10);
15395
+ if (!Number.isInteger(port) || port < 1 || port > 65535) {
15396
+ throw new Error(`Invalid port: ${raw}`);
15397
+ }
15398
+ return port;
15399
+ }
15400
+ function pathnameFromRequest(req) {
15401
+ return new URL(req.url ?? "/", "http://127.0.0.1").pathname;
15402
+ }
15403
+ async function readRequestBody(req) {
15404
+ if (req.method !== "POST" && req.method !== "DELETE") {
15405
+ return;
15406
+ }
15407
+ const chunks = [];
15408
+ for await (const chunk of req) {
15409
+ chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
15410
+ }
15411
+ const text = Buffer.concat(chunks).toString("utf8");
15412
+ if (!text) {
15413
+ return;
15414
+ }
15415
+ return JSON.parse(text);
15416
+ }
15417
+ async function handleMcpRequest(req, res) {
15418
+ const server = buildServer();
15419
+ const transport = new StreamableHTTPServerTransport({
15420
+ sessionIdGenerator: undefined
15421
+ });
15422
+ await server.connect(transport);
15369
15423
  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";
15424
+ const body = await readRequestBody(req);
15425
+ await transport.handleRequest(req, res, body);
15426
+ } finally {
15427
+ res.on("close", () => {
15428
+ transport.close().catch(() => {
15429
+ return;
15430
+ });
15431
+ server.close().catch(() => {
15432
+ return;
15433
+ });
15434
+ });
15374
15435
  }
15375
15436
  }
15437
+ function startHttpServer(options = {}) {
15438
+ const host = options.host ?? "127.0.0.1";
15439
+ const port = options.port ?? resolveHttpPort();
15440
+ const name = options.name ?? HTTP_NAME;
15441
+ const httpServer = createServer(async (req, res) => {
15442
+ const path = pathnameFromRequest(req);
15443
+ if (req.method === "GET" && path === "/health") {
15444
+ res.writeHead(200, { "content-type": "application/json" });
15445
+ res.end(JSON.stringify({ status: "ok", name }));
15446
+ return;
15447
+ }
15448
+ if (path === "/mcp") {
15449
+ await handleMcpRequest(req, res);
15450
+ return;
15451
+ }
15452
+ res.writeHead(404, { "content-type": "application/json" });
15453
+ res.end(JSON.stringify({ error: "Not found" }));
15454
+ });
15455
+ httpServer.listen(port, host, () => {
15456
+ const address = httpServer.address();
15457
+ const boundPort = typeof address === "object" && address ? address.port : port;
15458
+ console.error(`machines-mcp HTTP listening on http://${host}:${boundPort}`);
15459
+ });
15460
+ return httpServer;
15461
+ }
15462
+
15463
+ // src/mcp/index.ts
15376
15464
  function printHelp() {
15377
15465
  console.log(`Usage: machines-mcp [options]
15378
15466
 
15379
- MCP server for machine fleet management tools (stdio transport)
15467
+ MCP server for machine fleet management tools (stdio transport by default)
15380
15468
 
15381
15469
  Options:
15470
+ --http Start Streamable HTTP transport on 127.0.0.1 (or MCP_HTTP=1)
15471
+ --port <n> HTTP port (default: 8821, or MCP_HTTP_PORT env)
15382
15472
  -V, --version output the version number
15383
15473
  -h, --help display help for command`);
15384
15474
  }
@@ -15388,9 +15478,13 @@ if (args.includes("--help") || args.includes("-h")) {
15388
15478
  process.exit(0);
15389
15479
  }
15390
15480
  if (args.includes("--version") || args.includes("-V")) {
15391
- console.log(getPkgVersion());
15481
+ console.log(getPackageVersion());
15392
15482
  process.exit(0);
15393
15483
  }
15394
- var server = createMcpServer(getPkgVersion());
15395
- var transport = new StdioServerTransport;
15396
- await server.connect(transport);
15484
+ if (isHttpMode(args)) {
15485
+ startHttpServer({ port: resolveHttpPort(args) });
15486
+ } else {
15487
+ const server = buildServer();
15488
+ const transport = new StdioServerTransport;
15489
+ await server.connect(transport);
15490
+ }
@@ -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.10",
4
4
  "description": "Machine fleet management CLI + MCP for developers",
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",