@hasna/machines 0.0.3 → 0.0.5

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/cli/index.js CHANGED
@@ -6599,6 +6599,12 @@ function getManifestPath() {
6599
6599
  function getNotificationsPath() {
6600
6600
  return process.env["HASNA_MACHINES_NOTIFICATIONS_PATH"] || join2(getDataDir(), "notifications.json");
6601
6601
  }
6602
+ function getClipboardKeyPath() {
6603
+ return process.env["HASNA_MACHINES_CLIPBOARD_KEY_PATH"] || join2(getDataDir(), "clipboard.key");
6604
+ }
6605
+ function getClipboardHistoryPath() {
6606
+ return process.env["HASNA_MACHINES_CLIPBOARD_HISTORY_PATH"] || join2(getDataDir(), "clipboard-history.json");
6607
+ }
6602
6608
  function ensureParentDir(filePath) {
6603
6609
  if (filePath === ":memory:")
6604
6610
  return;
@@ -17667,11 +17673,13 @@ function renderDashboardHtml() {
17667
17673
  .unknown, .warn { background: #2f2b16; color: #ffd76a; }
17668
17674
  ul { margin: 8px 0 0; padding-left: 18px; }
17669
17675
  .muted { color: #9fb0d9; }
17676
+ .refresh { font-size: 12px; color: #6b7fa3; margin-left: auto; }
17677
+ .updated { transition: opacity 0.3s; }
17670
17678
  </style>
17671
17679
  </head>
17672
17680
  <body>
17673
17681
  <main>
17674
- <h1>Machines Dashboard</h1>
17682
+ <h1>Machines Dashboard <span class="refresh" id="last-updated"></span></h1>
17675
17683
  <div class="grid">
17676
17684
  <section class="card"><div>Manifest machines</div><div class="stat">${status.manifestMachineCount}</div></section>
17677
17685
  <section class="card"><div>Heartbeats</div><div class="stat">${status.heartbeatCount}</div></section>
@@ -17698,7 +17706,7 @@ function renderDashboardHtml() {
17698
17706
  <h2>Doctor</h2>
17699
17707
  <table>
17700
17708
  <thead><tr><th>Check</th><th>Status</th><th>Detail</th></tr></thead>
17701
- <tbody>
17709
+ <tbody id="doctor-tbody">
17702
17710
  ${doctor.checks.map((entry) => `<tr>
17703
17711
  <td>${escapeHtml(entry.summary)}</td>
17704
17712
  <td><span class="badge ${escapeHtml(entry.status)}">${escapeHtml(entry.status)}</span></td>
@@ -17708,6 +17716,16 @@ function renderDashboardHtml() {
17708
17716
  </table>
17709
17717
  </section>
17710
17718
 
17719
+ <section class="card" style="margin-top:16px">
17720
+ <h2>Apps</h2>
17721
+ <p class="muted">Use <code>/api/apps/status</code> for the full app inventory payload.</p>
17722
+ </section>
17723
+
17724
+ <section class="card" style="margin-top:16px">
17725
+ <h2>AI CLIs</h2>
17726
+ <p class="muted">Use <code>/api/install-claude/status</code> for the full CLI inventory payload.</p>
17727
+ </section>
17728
+
17711
17729
  <section class="card" style="margin-top:16px">
17712
17730
  <h2>Self Test</h2>
17713
17731
  <p class="muted">Use <code>/api/self-test</code> for the full smoke-check payload.</p>
@@ -17715,9 +17733,66 @@ function renderDashboardHtml() {
17715
17733
 
17716
17734
  <section class="card" style="margin-top:16px">
17717
17735
  <h2>Manifest</h2>
17718
- <pre>${escapeHtml(JSON.stringify(manifest, null, 2))}</pre>
17736
+ <pre id="manifest-json">${escapeHtml(JSON.stringify(manifest, null, 2))}</pre>
17719
17737
  </section>
17720
17738
  </main>
17739
+ <script>
17740
+ // Auto-refresh dashboard data every 15s
17741
+ const REFRESH_INTERVAL = 15000;
17742
+ async function refreshData() {
17743
+ try {
17744
+ const [statusRes, doctorRes] = await Promise.all([
17745
+ fetch("/api/status"),
17746
+ fetch("/api/doctor"),
17747
+ ]);
17748
+ const status = await statusRes.json();
17749
+ const doctor = await doctorRes.json();
17750
+
17751
+ // Update stat cards
17752
+ const stats = document.querySelectorAll(".stat");
17753
+ if (stats[0]) stats[0].textContent = status.manifestMachineCount;
17754
+ if (stats[1]) stats[1].textContent = status.heartbeatCount;
17755
+
17756
+ // Update machine table
17757
+ const tbody = document.querySelector("tbody");
17758
+ if (tbody && status.machines) {
17759
+ tbody.innerHTML = status.machines
17760
+ .map((m) =>
17761
+ "<tr>" +
17762
+ "<td><code>" + m.machineId + "</code></td>" +
17763
+ "<td>" + (m.platform || "unknown") + "</td>" +
17764
+ '<td><span class="badge ' + m.heartbeatStatus + '">' + m.heartbeatStatus + '</span></td>' +
17765
+ "<td>" + (m.lastHeartbeatAt || "\\u2014") + "</td>" +
17766
+ "</tr>"
17767
+ )
17768
+ .join("");
17769
+ }
17770
+
17771
+ // Update doctor table
17772
+ const doctorTbody = document.getElementById("doctor-tbody");
17773
+ if (doctorTbody && doctor.checks) {
17774
+ doctorTbody.innerHTML = doctor.checks
17775
+ .map((c) =>
17776
+ "<tr>" +
17777
+ "<td>" + c.summary + "</td>" +
17778
+ '<td><span class="badge ' + c.status + '">' + c.status + '</span></td>' +
17779
+ '<td class="muted">' + c.detail + "</td>" +
17780
+ "</tr>"
17781
+ )
17782
+ .join("");
17783
+ }
17784
+
17785
+ // Update timestamp
17786
+ document.getElementById("last-updated").textContent =
17787
+ "updated " + new Date().toLocaleTimeString();
17788
+ } catch (e) {
17789
+ // Silently ignore fetch errors during page unload
17790
+ }
17791
+ }
17792
+ document.getElementById("last-updated").textContent =
17793
+ "updated " + new Date().toLocaleTimeString();
17794
+ setInterval(refreshData, REFRESH_INTERVAL);
17795
+ </script>
17721
17796
  </body>
17722
17797
  </html>`;
17723
17798
  }
@@ -17828,6 +17903,426 @@ function runSelfTest() {
17828
17903
  };
17829
17904
  }
17830
17905
 
17906
+ // src/commands/clipboard.ts
17907
+ import { createHash } from "crypto";
17908
+ import { existsSync as existsSync8, readFileSync as readFileSync7, rmSync, writeFileSync as writeFileSync5 } from "fs";
17909
+ import { join as join9 } from "path";
17910
+ var DEFAULT_CONFIG = {
17911
+ version: 1,
17912
+ enabled: true,
17913
+ port: 19452,
17914
+ maxHistory: 500,
17915
+ maxSizeBytes: 10 * 1024 * 1024,
17916
+ skipPatterns: [
17917
+ "password",
17918
+ "secret",
17919
+ "token",
17920
+ "-----BEGIN",
17921
+ "AKIA"
17922
+ ]
17923
+ };
17924
+ function resolveConfigPath(configPath) {
17925
+ if (configPath)
17926
+ return configPath;
17927
+ return join9(getDataDir(), "clipboard-config.json");
17928
+ }
17929
+ function resolveHistoryPath(historyPath) {
17930
+ if (historyPath)
17931
+ return historyPath;
17932
+ return getClipboardHistoryPath();
17933
+ }
17934
+ function getDefaultConfig() {
17935
+ return { ...DEFAULT_CONFIG, skipPatterns: [...DEFAULT_CONFIG.skipPatterns] };
17936
+ }
17937
+ function readConfig(configPath) {
17938
+ const path = resolveConfigPath(configPath);
17939
+ if (!existsSync8(path)) {
17940
+ return getDefaultConfig();
17941
+ }
17942
+ const parsed = JSON.parse(readFileSync7(path, "utf8"));
17943
+ return { ...getDefaultConfig(), ...parsed };
17944
+ }
17945
+ function writeConfig(config, configPath) {
17946
+ const path = resolveConfigPath(configPath);
17947
+ ensureParentDir(path);
17948
+ writeFileSync5(path, `${JSON.stringify(config, null, 2)}
17949
+ `, "utf8");
17950
+ }
17951
+ function readHistory(historyPath) {
17952
+ const path = resolveHistoryPath(historyPath);
17953
+ if (!existsSync8(path)) {
17954
+ return [];
17955
+ }
17956
+ try {
17957
+ return JSON.parse(readFileSync7(path, "utf8"));
17958
+ } catch {
17959
+ return [];
17960
+ }
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
+ }
17984
+ function getOrCreateClipboardKey() {
17985
+ const keyPath = getClipboardKeyPath();
17986
+ if (existsSync8(keyPath)) {
17987
+ return readFileSync7(keyPath, "utf8").trim();
17988
+ }
17989
+ const key = createHash("sha256").update(crypto.randomUUID()).digest("hex").slice(0, 32);
17990
+ ensureParentDir(keyPath);
17991
+ writeFileSync5(keyPath, `${key}
17992
+ `, "utf8");
17993
+ return key;
17994
+ }
17995
+ function getDefaultClipboardConfig() {
17996
+ return getDefaultConfig();
17997
+ }
17998
+ function getConfigPath2(configPath) {
17999
+ return resolveConfigPath(configPath);
18000
+ }
18001
+ function readClipboardConfig(configPath) {
18002
+ return readConfig(configPath);
18003
+ }
18004
+ function writeClipboardConfig(config, configPath) {
18005
+ writeConfig(config, configPath);
18006
+ }
18007
+ function readClipboardHistory(historyPath) {
18008
+ return readHistory(historyPath);
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
+ }
18024
+ function clearClipboardHistory(historyPath) {
18025
+ const path = resolveHistoryPath(historyPath);
18026
+ if (existsSync8(path)) {
18027
+ rmSync(path);
18028
+ }
18029
+ }
18030
+ function getClipboardStatus(historyPath) {
18031
+ const history = readHistory(historyPath);
18032
+ const config = readConfig();
18033
+ return {
18034
+ running: false,
18035
+ port: config.port,
18036
+ historyCount: history.length
18037
+ };
18038
+ }
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 startClipboardServer(options = {}) {
18116
+ const config = options.config || readClipboardConfig();
18117
+ const port = options.port || config.port;
18118
+ const server = createServer(async (request, response) => {
18119
+ if (!authenticate(request)) {
18120
+ return jsonResponse(response, 401, { error: "unauthorized" });
18121
+ }
18122
+ const url = new URL(request.url || "/", `http://${request.headers.host || "localhost"}`);
18123
+ if (url.pathname === "/clipboard" && request.method === "POST") {
18124
+ return handleReceiveClipboard(request, response, config);
18125
+ }
18126
+ if (url.pathname === "/clipboard" && request.method === "GET") {
18127
+ return handleGetClipboard(response, config);
18128
+ }
18129
+ if (url.pathname === "/health" && request.method === "GET") {
18130
+ return jsonResponse(response, 200, { ok: true, machineId: process.env["HASNA_MACHINES_MACHINE_ID"] || "unknown" });
18131
+ }
18132
+ jsonResponse(response, 404, { error: "not found" });
18133
+ });
18134
+ server.listen(port, "0.0.0.0", () => {});
18135
+ server.on("error", (error) => {
18136
+ console.error(`clipboard server error: ${error.message}`);
18137
+ });
18138
+ return {
18139
+ server,
18140
+ port,
18141
+ close: async () => {
18142
+ await new Promise((resolve2) => server.close(() => resolve2()));
18143
+ }
18144
+ };
18145
+ }
18146
+ function handleReceiveClipboard(request, response, config) {
18147
+ let body = "";
18148
+ request.on("data", (chunk) => {
18149
+ body += chunk;
18150
+ });
18151
+ request.on("end", () => {
18152
+ try {
18153
+ const parsed = JSON.parse(body);
18154
+ const content = parsed.content || "";
18155
+ const contentType = parsed.contentType || "text";
18156
+ const sourceMachine = parsed.sourceMachine || "unknown";
18157
+ if (!content) {
18158
+ return jsonResponse(response, 400, { error: "empty content" });
18159
+ }
18160
+ const hash = computeHash(content);
18161
+ if (hash === currentContentHash) {
18162
+ return jsonResponse(response, 200, { received: false, reason: "loop detected" });
18163
+ }
18164
+ const check2 = sanitizeClipboardForRead(content, config.maxSizeBytes, config.skipPatterns);
18165
+ if (!check2.ok) {
18166
+ return jsonResponse(response, 200, { received: false, reason: check2.reason });
18167
+ }
18168
+ writeLocalClipboardSync(content);
18169
+ currentContentHash = hash;
18170
+ addClipboardEntry({
18171
+ hash,
18172
+ content,
18173
+ contentType,
18174
+ sourceMachine,
18175
+ timestamp: new Date().toISOString()
18176
+ });
18177
+ return jsonResponse(response, 200, { received: true, hash });
18178
+ } catch {
18179
+ return jsonResponse(response, 400, { error: "invalid JSON" });
18180
+ }
18181
+ });
18182
+ }
18183
+ function handleGetClipboard(response, config) {
18184
+ const content = readLocalClipboardSync();
18185
+ if (!content) {
18186
+ return jsonResponse(response, 200, { content: "", hash: null });
18187
+ }
18188
+ const hash = computeHash(content);
18189
+ return jsonResponse(response, 200, { content, hash, contentType: "text" });
18190
+ }
18191
+
18192
+ // src/commands/clipboard-daemon.ts
18193
+ var DAEMON_PID_PATH = join10(getDataDir(), "clipboard-daemon.pid");
18194
+ function readLocalClipboardSync2() {
18195
+ const platform4 = process.platform;
18196
+ if (platform4 === "darwin") {
18197
+ const result = Bun.spawnSync(["pbpaste"], { stdout: "pipe", stderr: "pipe" });
18198
+ return result.exitCode === 0 ? result.stdout.toString("utf8").trim() : "";
18199
+ }
18200
+ if (platform4 === "linux") {
18201
+ if (hasCommand3("wl-paste")) {
18202
+ const result = Bun.spawnSync(["wl-paste"], { stdout: "pipe", stderr: "pipe" });
18203
+ return result.exitCode === 0 ? result.stdout.toString("utf8").trim() : "";
18204
+ }
18205
+ if (hasCommand3("xclip")) {
18206
+ const result = Bun.spawnSync(["xclip", "-selection", "clipboard", "-o"], { stdout: "pipe", stderr: "pipe" });
18207
+ return result.exitCode === 0 ? result.stdout.toString("utf8").trim() : "";
18208
+ }
18209
+ return "";
18210
+ }
18211
+ return "";
18212
+ }
18213
+ function hasCommand3(binary) {
18214
+ const result = Bun.spawnSync(["bash", "-lc", `command -v ${binary} >/dev/null 2>&1`], { stdout: "ignore", stderr: "ignore", env: process.env });
18215
+ return result.exitCode === 0;
18216
+ }
18217
+ function computeHash2(content) {
18218
+ return createHash3("sha256").update(content).digest("hex").slice(0, 16);
18219
+ }
18220
+ function loadSharedSecret2() {
18221
+ try {
18222
+ return readFileSync9(getClipboardKeyPath(), "utf8").trim();
18223
+ } catch {
18224
+ return "";
18225
+ }
18226
+ }
18227
+ function writePid(pid) {
18228
+ writeFileSync6(DAEMON_PID_PATH, `${pid}
18229
+ `);
18230
+ }
18231
+ function readPid() {
18232
+ try {
18233
+ const pid = Number.parseInt(readFileSync9(DAEMON_PID_PATH, "utf8").trim());
18234
+ return Number.isFinite(pid) ? pid : null;
18235
+ } catch {
18236
+ return null;
18237
+ }
18238
+ }
18239
+ function isProcessRunning(pid) {
18240
+ try {
18241
+ process.kill(pid, 0);
18242
+ return true;
18243
+ } catch {
18244
+ return false;
18245
+ }
18246
+ }
18247
+ function stopClipboardDaemon() {
18248
+ const pid = readPid();
18249
+ if (pid && isProcessRunning(pid)) {
18250
+ process.kill(pid, "SIGTERM");
18251
+ return { stopped: true, pid };
18252
+ }
18253
+ return { stopped: false, pid };
18254
+ }
18255
+ function startClipboardDaemon(port) {
18256
+ const config = readClipboardConfig();
18257
+ const daemonPort = port || config.port;
18258
+ const { server, close } = startClipboardServer({ port: daemonPort });
18259
+ server.on("listening", () => {
18260
+ console.log(`clipboard daemon started on port ${daemonPort} (pid ${process.pid})`);
18261
+ writePid(process.pid);
18262
+ });
18263
+ let lastHash = "";
18264
+ const secret = loadSharedSecret2();
18265
+ const machineId = process.env["HASNA_MACHINES_MACHINE_ID"] || "unknown";
18266
+ setInterval(async () => {
18267
+ const content = readLocalClipboardSync2();
18268
+ if (!content)
18269
+ return;
18270
+ const hash = computeHash2(content);
18271
+ if (hash === lastHash)
18272
+ return;
18273
+ lastHash = hash;
18274
+ const peers = await discoverPeers();
18275
+ for (const peer of peers) {
18276
+ try {
18277
+ const res = await fetch(`http://${peer.host}:${peer.port}/clipboard`, {
18278
+ method: "POST",
18279
+ headers: {
18280
+ "content-type": "application/json",
18281
+ authorization: `Bearer ${secret}`
18282
+ },
18283
+ body: JSON.stringify({
18284
+ content,
18285
+ contentType: "text",
18286
+ sourceMachine: machineId
18287
+ }),
18288
+ signal: AbortSignal.timeout(2000)
18289
+ });
18290
+ if (res.ok) {
18291
+ const data = await res.json();
18292
+ if (data["received"] === true) {
18293
+ console.log(`clipboard sent to ${peer.host}`);
18294
+ }
18295
+ }
18296
+ } catch {}
18297
+ }
18298
+ }, 500);
18299
+ }
18300
+ async function discoverPeers() {
18301
+ const config = readClipboardConfig();
18302
+ const peers = [];
18303
+ try {
18304
+ const result = Bun.spawnSync(["tailscale", "status", "--json"], { stdout: "pipe", stderr: "pipe", env: process.env });
18305
+ if (result.exitCode === 0) {
18306
+ const status = JSON.parse(result.stdout.toString("utf8"));
18307
+ const peers_map = status["Peer"] || {};
18308
+ for (const [, peerInfo] of Object.entries(peers_map)) {
18309
+ for (const ip of peerInfo.TailscaleIPs) {
18310
+ if (ip.includes(".") && !ip.endsWith(".1")) {
18311
+ peers.push({ host: ip, port: config.port });
18312
+ }
18313
+ }
18314
+ }
18315
+ }
18316
+ } catch {}
18317
+ const knownPeers = ["100.82.44.120", "100.100.226.69", "100.71.123.34", "100.85.234.92"];
18318
+ for (const ip of knownPeers) {
18319
+ if (!peers.some((p) => p.host === ip)) {
18320
+ peers.push({ host: ip, port: config.port });
18321
+ }
18322
+ }
18323
+ return peers;
18324
+ }
18325
+
17831
18326
  // src/cli-utils.ts
17832
18327
  function parseIntegerOption(value, label, constraints = {}) {
17833
18328
  const parsed = Number.parseInt(value, 10);
@@ -17858,9 +18353,11 @@ ${items.map((item) => `- ${item}`).join(`
17858
18353
  }
17859
18354
 
17860
18355
  // src/cli/index.ts
18356
+ import { rmSync as rmSync2 } from "fs";
18357
+ import { readFileSync as readFileSync10 } from "fs";
17861
18358
  var program2 = new Command;
17862
18359
  function printJsonOrText(data, text, json = false) {
17863
- if (json) {
18360
+ if (json || program2.opts().quiet) {
17864
18361
  console.log(JSON.stringify(data, null, 2));
17865
18362
  return;
17866
18363
  }
@@ -17965,10 +18462,11 @@ function renderFleetStatus(status) {
17965
18462
  ].join(`
17966
18463
  `);
17967
18464
  }
17968
- program2.name("machines").description("Machine fleet management CLI + MCP for developers").version(getPackageVersion());
18465
+ program2.name("machines").description("Machine fleet management CLI + MCP for developers").version(getPackageVersion()).option("-q, --quiet", "Suppress non-essential output");
17969
18466
  var manifestCommand = program2.command("manifest").description("Manage the fleet manifest");
17970
18467
  var appsCommand = program2.command("apps").description("Manage installed applications per machine");
17971
18468
  var notificationsCommand = program2.command("notifications").description("Manage fleet alert delivery channels");
18469
+ var clipboardCommand = program2.command("clipboard").description("Real-time clipboard sync across fleet machines");
17972
18470
  var installClaudeCommand = program2.command("install-claude").description("Install or inspect Claude, Codex, and Gemini CLIs");
17973
18471
  manifestCommand.command("init").description("Create an empty fleet manifest").action(() => {
17974
18472
  console.log(manifestInit());
@@ -17997,7 +18495,17 @@ manifestCommand.command("get").description("Print a single machine from the mani
17997
18495
  manifestCommand.command("remove").description("Remove a machine from the manifest").argument("<id>", "Machine identifier").action((id) => {
17998
18496
  console.log(JSON.stringify(manifestRemove(id), null, 2));
17999
18497
  });
18000
- manifestCommand.command("add").description("Add or replace a machine in the fleet manifest").requiredOption("--id <id>", "Machine identifier").requiredOption("--platform <platform>", "linux | macos | windows").requiredOption("--workspace-path <path>", "Primary workspace path").option("--hostname <hostname>", "Machine hostname").option("--ssh-address <sshAddress>", "Machine SSH address").option("--tailscale-name <tailscaleName>", "Machine Tailscale DNS name").option("--connection <connection>", "local | ssh | tailscale").option("--bun-path <path>", "Bun executable directory").option("--tag <tag...>", "Machine tags").option("--package <name...>", "Desired packages").option("--app <spec...>", "Desired apps as name[:manager[:packageName]]").option("--file <spec...>", "File sync spec source:target[:copy|symlink]").action((options) => {
18498
+ manifestCommand.command("add").description("Add or replace a machine in the fleet manifest").option("--id <id>", "Machine identifier").option("--platform <platform>", "linux | macos | windows").option("--workspace-path <path>", "Primary workspace path").option("--hostname <hostname>", "Machine hostname").option("--ssh-address <sshAddress>", "Machine SSH address").option("--tailscale-name <tailscaleName>", "Machine Tailscale DNS name").option("--connection <connection>", "local | ssh | tailscale").option("--bun-path <path>", "Bun executable directory").option("--tag <tag...>", "Machine tags").option("--package <name...>", "Desired packages").option("--app <spec...>", "Desired apps as name[:manager[:packageName]]").option("--file <spec...>", "File sync spec source:target[:copy|symlink]").option("--metadata <json>", "Machine metadata as JSON").option("--from-stdin", "Read the full MachineManifest JSON from stdin").action((options) => {
18499
+ if (options["from-stdin"]) {
18500
+ if (process.stdin.isTTY) {
18501
+ console.error("error: --from-stdin requires piped input");
18502
+ process.exit(1);
18503
+ }
18504
+ const input = readFileSync10(0, "utf8");
18505
+ const machine2 = JSON.parse(input);
18506
+ console.log(JSON.stringify(manifestAdd(machine2), null, 2));
18507
+ return;
18508
+ }
18001
18509
  const packages = Array.isArray(options["package"]) ? options["package"].map((name) => ({ name: String(name) })) : undefined;
18002
18510
  const files = Array.isArray(options["file"]) ? options["file"].map((value) => {
18003
18511
  const [source, target, mode] = String(value).split(":");
@@ -18012,6 +18520,7 @@ manifestCommand.command("add").description("Add or replace a machine in the flee
18012
18520
  packageName
18013
18521
  };
18014
18522
  }) : undefined;
18523
+ const metadata = typeof options["metadata"] === "string" ? JSON.parse(options["metadata"]) : undefined;
18015
18524
  const machine = {
18016
18525
  id: String(options["id"]),
18017
18526
  hostname: options["hostname"] ? String(options["hostname"]) : undefined,
@@ -18022,6 +18531,7 @@ manifestCommand.command("add").description("Add or replace a machine in the flee
18022
18531
  workspacePath: String(options["workspacePath"]),
18023
18532
  bunPath: options["bunPath"] ? String(options["bunPath"]) : undefined,
18024
18533
  tags: Array.isArray(options["tag"]) ? options["tag"].map(String) : undefined,
18534
+ metadata,
18025
18535
  packages,
18026
18536
  apps,
18027
18537
  files
@@ -18040,13 +18550,13 @@ appsCommand.command("diff").description("Show missing and installed manifest-man
18040
18550
  const result = diffApps(options.machine);
18041
18551
  printJsonOrText(result, renderAppsDiffResult(result), options.json);
18042
18552
  });
18043
- appsCommand.command("plan").description("Preview app install steps for a machine").option("--machine <id>", "Machine identifier").option("-j, --json", "Print JSON output", false).action((options) => {
18553
+ appsCommand.command("plan").description("Preview app install steps for a machine").option("--machine <id>", "Machine identifier").action((options) => {
18044
18554
  const result = buildAppsPlan(options.machine);
18045
- console.log(options.json ? JSON.stringify(result, null, 2) : JSON.stringify(result, null, 2));
18555
+ console.log(JSON.stringify(result, null, 2));
18046
18556
  });
18047
- appsCommand.command("apply").description("Install manifest-managed apps for a machine").option("--machine <id>", "Machine identifier").option("--yes", "Confirm execution", false).option("-j, --json", "Print JSON output", false).action((options) => {
18557
+ appsCommand.command("apply").description("Install manifest-managed apps for a machine").option("--machine <id>", "Machine identifier").option("--yes", "Confirm execution", false).action((options) => {
18048
18558
  const result = runAppsInstall(options.machine, { apply: true, yes: options.yes });
18049
- console.log(options.json ? JSON.stringify(result, null, 2) : JSON.stringify(result, null, 2));
18559
+ console.log(JSON.stringify(result, null, 2));
18050
18560
  });
18051
18561
  program2.command("setup").description("Prepare a machine from the fleet manifest").option("--machine <id>", "Machine identifier").option("--apply", "Execute provisioning commands instead of previewing the plan", false).option("--yes", "Confirm execution when using --apply", false).option("-j, --json", "Print JSON output", false).action((options) => {
18052
18562
  const result = options.apply ? runSetup(options.machine, { apply: true, yes: options.yes }) : buildSetupPlan(options.machine);
@@ -18109,6 +18619,75 @@ notificationsCommand.command("remove").description("Remove a notification channe
18109
18619
  const result = removeNotificationChannel(id);
18110
18620
  printJsonOrText(result, renderNotificationConfigResult(result), options.json);
18111
18621
  });
18622
+ clipboardCommand.command("init").description("Initialize clipboard sync (generate shared secret)").option("-j, --json", "Print JSON output", false).action((options) => {
18623
+ const key = getOrCreateClipboardKey();
18624
+ const config = getDefaultClipboardConfig();
18625
+ writeClipboardConfig(config);
18626
+ const result = { keyPath: getClipboardKeyPath(), key, configPath: getConfigPath2(), config };
18627
+ printJsonOrText(result, `clipboard initialized
18628
+ key: ${key}
18629
+ port: ${config.port}`, options.json);
18630
+ });
18631
+ clipboardCommand.command("status").description("Check clipboard sync status").option("-j, --json", "Print JSON output", false).action((options) => {
18632
+ const status = getClipboardStatus();
18633
+ const config = readClipboardConfig();
18634
+ const result = { ...status, enabled: config.enabled };
18635
+ printJsonOrText(result, `clipboard sync ${config.enabled ? source_default.green("enabled") : source_default.yellow("disabled")} (port ${status.port}, ${status.historyCount} entries)`, options.json);
18636
+ });
18637
+ clipboardCommand.command("config").description("View or set clipboard sync config").option("--set <json>", "Set config values as JSON").option("-j, --json", "Print JSON output", false).action((options) => {
18638
+ if (options.set) {
18639
+ const partial = JSON.parse(options.set);
18640
+ const config2 = { ...readClipboardConfig(), ...partial };
18641
+ writeClipboardConfig(config2);
18642
+ }
18643
+ const config = readClipboardConfig();
18644
+ printJsonOrText(config, renderKeyValueTable([
18645
+ ["enabled", String(config.enabled)],
18646
+ ["port", String(config.port)],
18647
+ ["maxHistory", String(config.maxHistory)],
18648
+ ["maxSizeBytes", `${config.maxSizeBytes} bytes`],
18649
+ ["skipPatterns", config.skipPatterns.join(", ")]
18650
+ ]), options.json);
18651
+ });
18652
+ clipboardCommand.command("history").description("Show clipboard sync history").option("-j, --json", "Print JSON output", false).option("--limit <n>", "Show only the last N entries", "20").action((options) => {
18653
+ const limit = parseIntegerOption(options.limit, "limit", { min: 1, max: 100 });
18654
+ const entries = readClipboardHistory().slice(0, limit);
18655
+ if (options.json) {
18656
+ console.log(JSON.stringify(entries, null, 2));
18657
+ return;
18658
+ }
18659
+ if (entries.length === 0) {
18660
+ console.log("clipboard history: empty");
18661
+ return;
18662
+ }
18663
+ for (const entry of entries) {
18664
+ const preview = entry.content.length > 80 ? `${entry.content.slice(0, 80)}...` : entry.content;
18665
+ console.log(`${source_default.dim(entry.timestamp)} ${entry.sourceMachine.padEnd(12)} ${entry.contentType.padEnd(5)} ${preview.replace(/\n/g, " ")}`);
18666
+ }
18667
+ });
18668
+ clipboardCommand.command("clear-history").description("Clear clipboard sync history").option("--yes", "Confirm without prompt", false).action((options) => {
18669
+ if (!options.yes) {
18670
+ console.error("error: this command requires --yes");
18671
+ process.exit(1);
18672
+ }
18673
+ clearClipboardHistory();
18674
+ console.log("clipboard history cleared");
18675
+ });
18676
+ clipboardCommand.command("key").description("Show or rotate the shared secret key").option("--rotate", "Generate a new key", false).option("-j, --json", "Print JSON output", false).action((options) => {
18677
+ if (options.rotate) {
18678
+ rmSync2(getClipboardKeyPath(), { force: true });
18679
+ }
18680
+ const key = getOrCreateClipboardKey();
18681
+ printJsonOrText({ key }, key, options.json);
18682
+ });
18683
+ clipboardCommand.command("start").description("Start clipboard sync daemon").option("--port <port>", "Port to listen on").action((options) => {
18684
+ const port = options.port ? Number(options.port) : undefined;
18685
+ startClipboardDaemon(port);
18686
+ });
18687
+ clipboardCommand.command("stop").description("Stop clipboard sync daemon").action(() => {
18688
+ const result = stopClipboardDaemon();
18689
+ console.log(result.stopped ? `daemon stopped (pid ${result.pid})` : "daemon not running");
18690
+ });
18112
18691
  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) => {
18113
18692
  const result = getClaudeCliStatus(options.machine, options.tool);
18114
18693
  printJsonOrText(result, renderClaudeStatusResult(result), options.json);
@@ -18125,10 +18704,6 @@ installClaudeCommand.command("apply").description("Install or update the request
18125
18704
  const result = runClaudeInstall(options.machine, options.tool, { apply: true, yes: options.yes });
18126
18705
  console.log(JSON.stringify(result, null, 2));
18127
18706
  });
18128
- installClaudeCommand.option("--machine <id>", "Machine identifier").option("--tool <name...>", "CLI tools to install (claude, codex, gemini)").option("--apply", "Execute installation commands instead of previewing the plan", false).option("--yes", "Confirm execution when using --apply", false).option("-j, --json", "Print JSON output", false).action((options) => {
18129
- const result = options.apply ? runClaudeInstall(options.machine, options.tool, { apply: true, yes: options.yes }) : buildClaudeInstallPlan(options.machine, options.tool);
18130
- console.log(JSON.stringify(result, null, 2));
18131
- });
18132
18707
  program2.command("install-tailscale").description("Install Tailscale on a machine").option("--machine <id>", "Machine identifier").option("--apply", "Execute installation commands instead of previewing the plan", false).option("--yes", "Confirm execution when using --apply", false).option("-j, --json", "Print JSON output", false).action((options) => {
18133
18708
  const result = options.apply ? runTailscaleInstall(options.machine, { apply: true, yes: options.yes }) : buildTailscaleInstallPlan(options.machine);
18134
18709
  console.log(JSON.stringify(result, null, 2));
@@ -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":"AAwFA,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,CAqDxD"}
@@ -0,0 +1,17 @@
1
+ import { createServer } from "node:http";
2
+ import type { ClipboardConfig } from "../types.js";
3
+ export interface ClipboardServerOptions {
4
+ port?: number;
5
+ config?: ClipboardConfig;
6
+ }
7
+ export interface ClipboardServerHandle {
8
+ server: ReturnType<typeof createServer>;
9
+ port: number;
10
+ close: () => Promise<void>;
11
+ }
12
+ export declare function startClipboardServer(options?: ClipboardServerOptions): ClipboardServerHandle;
13
+ export declare function pushClipboardToPeer(host: string, port: number, token: string): Promise<{
14
+ sent: boolean;
15
+ reason?: string;
16
+ }>;
17
+ //# sourceMappingURL=clipboard-server.d.ts.map
@@ -0,0 +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"}
@@ -0,0 +1,19 @@
1
+ import type { ClipboardConfig, ClipboardEntry, ClipboardStatus } from "../types.js";
2
+ export declare function resolveConfigPath(configPath?: string): string;
3
+ export declare function resolveHistoryPath(historyPath?: string): string;
4
+ export declare function computeHash(content: string): string;
5
+ export declare function shouldSkipContent(content: string, skipPatterns: string[]): boolean;
6
+ export declare function sanitizeClipboardForRead(content: string, maxSizeBytes: number, skipPatterns: string[]): {
7
+ ok: boolean;
8
+ reason?: string;
9
+ };
10
+ export declare function getOrCreateClipboardKey(): string;
11
+ export declare function getDefaultClipboardConfig(): ClipboardConfig;
12
+ export declare function getConfigPath(configPath?: string): string;
13
+ export declare function readClipboardConfig(configPath?: string): ClipboardConfig;
14
+ export declare function writeClipboardConfig(config: ClipboardConfig, configPath?: string): void;
15
+ export declare function readClipboardHistory(historyPath?: string): ClipboardEntry[];
16
+ export declare function addClipboardEntry(entry: ClipboardEntry, historyPath?: string): void;
17
+ export declare function clearClipboardHistory(historyPath?: string): void;
18
+ export declare function getClipboardStatus(historyPath?: string): ClipboardStatus;
19
+ //# sourceMappingURL=clipboard.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"clipboard.d.ts","sourceRoot":"","sources":["../../src/commands/clipboard.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAiBpF,wBAAgB,iBAAiB,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,MAAM,CAG7D;AAED,wBAAgB,kBAAkB,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,MAAM,CAG/D;AAuCD,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAEnD;AAED,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,GAAG,OAAO,CAGlF;AAED,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,GAAG;IAAE,EAAE,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,CAQxI;AAED,wBAAgB,uBAAuB,IAAI,MAAM,CAShD;AAED,wBAAgB,yBAAyB,IAAI,eAAe,CAE3D;AAED,wBAAgB,aAAa,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,MAAM,CAEzD;AAED,wBAAgB,mBAAmB,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,eAAe,CAExE;AAED,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,eAAe,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAEvF;AAED,wBAAgB,oBAAoB,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,cAAc,EAAE,CAE3E;AAED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,cAAc,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAenF;AAED,wBAAgB,qBAAqB,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAKhE;AAED,wBAAgB,kBAAkB,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,eAAe,CAQxE"}
@@ -1 +1 @@
1
- {"version":3,"file":"serve.d.ts","sourceRoot":"","sources":["../../src/commands/serve.ts"],"names":[],"mappings":"AAQA,MAAM,WAAW,YAAY;IAC3B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AAWD,wBAAgB,YAAY,CAAC,OAAO,GAAE,YAAiB,GAAG,SAAS,CAsBlE;AAED,wBAAgB,mBAAmB,IAAI,MAAM,CA0F5C;AAcD,wBAAgB,oBAAoB,CAAC,OAAO,GAAE,YAAiB,GAAG,UAAU,CAAC,OAAO,GAAG,CAAC,KAAK,CAAC,CAkE7F"}
1
+ {"version":3,"file":"serve.d.ts","sourceRoot":"","sources":["../../src/commands/serve.ts"],"names":[],"mappings":"AAQA,MAAM,WAAW,YAAY;IAC3B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AAWD,wBAAgB,YAAY,CAAC,OAAO,GAAE,YAAiB,GAAG,SAAS,CAsBlE;AAED,wBAAgB,mBAAmB,IAAI,MAAM,CA+J5C;AAcD,wBAAgB,oBAAoB,CAAC,OAAO,GAAE,YAAiB,GAAG,UAAU,CAAC,OAAO,GAAG,CAAC,KAAK,CAAC,CAkE7F"}
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Shared cross-project types for open-knowledge, open-crm, and open-machines.
3
+ *
4
+ * Each project vendors this file. Keep it small — add fields only when all three
5
+ * projects need them.
6
+ */
7
+ export type ProjectName = "open-knowledge" | "open-crm" | "open-machines";
8
+ /**
9
+ * A reference from a record in one project to a record in another.
10
+ * Stored in `metadata._crossRefs` on the owning entity.
11
+ */
12
+ export interface CrossRef {
13
+ source_project: ProjectName;
14
+ source_id: string;
15
+ target_project: ProjectName;
16
+ target_id: string;
17
+ /** Optional human description of the relationship. */
18
+ label?: string;
19
+ /** Agent or user that created this reference. */
20
+ created_by?: string;
21
+ /** ISO timestamp of creation. */
22
+ created_at?: string;
23
+ }
24
+ /** Reserved key inside `metadata` for cross-project references. */
25
+ export declare const CROSSREFS_KEY: "_crossRefs";
26
+ //# sourceMappingURL=cross-project-types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cross-project-types.d.ts","sourceRoot":"","sources":["../src/cross-project-types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,MAAM,WAAW,GAAG,gBAAgB,GAAG,UAAU,GAAG,eAAe,CAAC;AAE1E;;;GAGG;AACH,MAAM,WAAW,QAAQ;IACvB,cAAc,EAAE,WAAW,CAAC;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,WAAW,CAAC;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,sDAAsD;IACtD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,iDAAiD;IACjD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,iCAAiC;IACjC,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,mEAAmE;AACnE,eAAO,MAAM,aAAa,EAAG,YAAqB,CAAC"}
package/dist/index.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  export * from "./types.js";
2
+ export * from "./cross-project-types.js";
2
3
  export * from "./paths.js";
3
4
  export * from "./db.js";
4
5
  export * from "./manifests.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,YAAY,CAAC;AAC3B,cAAc,SAAS,CAAC;AACxB,cAAc,gBAAgB,CAAC;AAC/B,cAAc,oBAAoB,CAAC;AACnC,cAAc,sBAAsB,CAAC;AACrC,cAAc,oBAAoB,CAAC;AACnC,cAAc,oBAAoB,CAAC;AACnC,cAAc,mBAAmB,CAAC;AAClC,cAAc,sBAAsB,CAAC;AACrC,cAAc,wBAAwB,CAAC;AACvC,cAAc,oBAAoB,CAAC;AACnC,cAAc,8BAA8B,CAAC;AAC7C,cAAc,iCAAiC,CAAC;AAChD,cAAc,6BAA6B,CAAC;AAC5C,cAAc,qBAAqB,CAAC;AACpC,cAAc,yBAAyB,CAAC;AACxC,cAAc,qBAAqB,CAAC;AACpC,cAAc,qBAAqB,CAAC;AACpC,cAAc,mBAAmB,CAAC;AAClC,cAAc,oBAAoB,CAAC;AACnC,cAAc,sBAAsB,CAAC;AACrC,cAAc,iBAAiB,CAAC;AAChC,cAAc,cAAc,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,0BAA0B,CAAC;AACzC,cAAc,YAAY,CAAC;AAC3B,cAAc,SAAS,CAAC;AACxB,cAAc,gBAAgB,CAAC;AAC/B,cAAc,oBAAoB,CAAC;AACnC,cAAc,sBAAsB,CAAC;AACrC,cAAc,oBAAoB,CAAC;AACnC,cAAc,oBAAoB,CAAC;AACnC,cAAc,mBAAmB,CAAC;AAClC,cAAc,sBAAsB,CAAC;AACrC,cAAc,wBAAwB,CAAC;AACvC,cAAc,oBAAoB,CAAC;AACnC,cAAc,8BAA8B,CAAC;AAC7C,cAAc,iCAAiC,CAAC;AAChD,cAAc,6BAA6B,CAAC;AAC5C,cAAc,qBAAqB,CAAC;AACpC,cAAc,yBAAyB,CAAC;AACxC,cAAc,qBAAqB,CAAC;AACpC,cAAc,qBAAqB,CAAC;AACpC,cAAc,mBAAmB,CAAC;AAClC,cAAc,oBAAoB,CAAC;AACnC,cAAc,sBAAsB,CAAC;AACrC,cAAc,iBAAiB,CAAC;AAChC,cAAc,cAAc,CAAC"}
package/dist/index.js CHANGED
@@ -6515,6 +6515,8 @@ var require_dist2 = __commonJS((exports, module) => {
6515
6515
  Object.defineProperty(exports, "__esModule", { value: true });
6516
6516
  exports.default = formatsPlugin;
6517
6517
  });
6518
+ // src/cross-project-types.ts
6519
+ var CROSSREFS_KEY = "_crossRefs";
6518
6520
  // src/paths.ts
6519
6521
  import { existsSync, mkdirSync } from "fs";
6520
6522
  import { dirname, join, resolve } from "path";
@@ -6533,6 +6535,12 @@ function getManifestPath() {
6533
6535
  function getNotificationsPath() {
6534
6536
  return process.env["HASNA_MACHINES_NOTIFICATIONS_PATH"] || join(getDataDir(), "notifications.json");
6535
6537
  }
6538
+ function getClipboardKeyPath() {
6539
+ return process.env["HASNA_MACHINES_CLIPBOARD_KEY_PATH"] || join(getDataDir(), "clipboard.key");
6540
+ }
6541
+ function getClipboardHistoryPath() {
6542
+ return process.env["HASNA_MACHINES_CLIPBOARD_HISTORY_PATH"] || join(getDataDir(), "clipboard-history.json");
6543
+ }
6536
6544
  function ensureParentDir(filePath) {
6537
6545
  if (filePath === ":memory:")
6538
6546
  return;
@@ -21357,11 +21365,13 @@ function renderDashboardHtml() {
21357
21365
  .unknown, .warn { background: #2f2b16; color: #ffd76a; }
21358
21366
  ul { margin: 8px 0 0; padding-left: 18px; }
21359
21367
  .muted { color: #9fb0d9; }
21368
+ .refresh { font-size: 12px; color: #6b7fa3; margin-left: auto; }
21369
+ .updated { transition: opacity 0.3s; }
21360
21370
  </style>
21361
21371
  </head>
21362
21372
  <body>
21363
21373
  <main>
21364
- <h1>Machines Dashboard</h1>
21374
+ <h1>Machines Dashboard <span class="refresh" id="last-updated"></span></h1>
21365
21375
  <div class="grid">
21366
21376
  <section class="card"><div>Manifest machines</div><div class="stat">${status.manifestMachineCount}</div></section>
21367
21377
  <section class="card"><div>Heartbeats</div><div class="stat">${status.heartbeatCount}</div></section>
@@ -21388,7 +21398,7 @@ function renderDashboardHtml() {
21388
21398
  <h2>Doctor</h2>
21389
21399
  <table>
21390
21400
  <thead><tr><th>Check</th><th>Status</th><th>Detail</th></tr></thead>
21391
- <tbody>
21401
+ <tbody id="doctor-tbody">
21392
21402
  ${doctor.checks.map((entry) => `<tr>
21393
21403
  <td>${escapeHtml(entry.summary)}</td>
21394
21404
  <td><span class="badge ${escapeHtml(entry.status)}">${escapeHtml(entry.status)}</span></td>
@@ -21398,6 +21408,16 @@ function renderDashboardHtml() {
21398
21408
  </table>
21399
21409
  </section>
21400
21410
 
21411
+ <section class="card" style="margin-top:16px">
21412
+ <h2>Apps</h2>
21413
+ <p class="muted">Use <code>/api/apps/status</code> for the full app inventory payload.</p>
21414
+ </section>
21415
+
21416
+ <section class="card" style="margin-top:16px">
21417
+ <h2>AI CLIs</h2>
21418
+ <p class="muted">Use <code>/api/install-claude/status</code> for the full CLI inventory payload.</p>
21419
+ </section>
21420
+
21401
21421
  <section class="card" style="margin-top:16px">
21402
21422
  <h2>Self Test</h2>
21403
21423
  <p class="muted">Use <code>/api/self-test</code> for the full smoke-check payload.</p>
@@ -21405,9 +21425,66 @@ function renderDashboardHtml() {
21405
21425
 
21406
21426
  <section class="card" style="margin-top:16px">
21407
21427
  <h2>Manifest</h2>
21408
- <pre>${escapeHtml(JSON.stringify(manifest, null, 2))}</pre>
21428
+ <pre id="manifest-json">${escapeHtml(JSON.stringify(manifest, null, 2))}</pre>
21409
21429
  </section>
21410
21430
  </main>
21431
+ <script>
21432
+ // Auto-refresh dashboard data every 15s
21433
+ const REFRESH_INTERVAL = 15000;
21434
+ async function refreshData() {
21435
+ try {
21436
+ const [statusRes, doctorRes] = await Promise.all([
21437
+ fetch("/api/status"),
21438
+ fetch("/api/doctor"),
21439
+ ]);
21440
+ const status = await statusRes.json();
21441
+ const doctor = await doctorRes.json();
21442
+
21443
+ // Update stat cards
21444
+ const stats = document.querySelectorAll(".stat");
21445
+ if (stats[0]) stats[0].textContent = status.manifestMachineCount;
21446
+ if (stats[1]) stats[1].textContent = status.heartbeatCount;
21447
+
21448
+ // Update machine table
21449
+ const tbody = document.querySelector("tbody");
21450
+ if (tbody && status.machines) {
21451
+ tbody.innerHTML = status.machines
21452
+ .map((m) =>
21453
+ "<tr>" +
21454
+ "<td><code>" + m.machineId + "</code></td>" +
21455
+ "<td>" + (m.platform || "unknown") + "</td>" +
21456
+ '<td><span class="badge ' + m.heartbeatStatus + '">' + m.heartbeatStatus + '</span></td>' +
21457
+ "<td>" + (m.lastHeartbeatAt || "\\u2014") + "</td>" +
21458
+ "</tr>"
21459
+ )
21460
+ .join("");
21461
+ }
21462
+
21463
+ // Update doctor table
21464
+ const doctorTbody = document.getElementById("doctor-tbody");
21465
+ if (doctorTbody && doctor.checks) {
21466
+ doctorTbody.innerHTML = doctor.checks
21467
+ .map((c) =>
21468
+ "<tr>" +
21469
+ "<td>" + c.summary + "</td>" +
21470
+ '<td><span class="badge ' + c.status + '">' + c.status + '</span></td>' +
21471
+ '<td class="muted">' + c.detail + "</td>" +
21472
+ "</tr>"
21473
+ )
21474
+ .join("");
21475
+ }
21476
+
21477
+ // Update timestamp
21478
+ document.getElementById("last-updated").textContent =
21479
+ "updated " + new Date().toLocaleTimeString();
21480
+ } catch (e) {
21481
+ // Silently ignore fetch errors during page unload
21482
+ }
21483
+ }
21484
+ document.getElementById("last-updated").textContent =
21485
+ "updated " + new Date().toLocaleTimeString();
21486
+ setInterval(refreshData, REFRESH_INTERVAL);
21487
+ </script>
21411
21488
  </body>
21412
21489
  </html>`;
21413
21490
  }
@@ -30882,6 +30959,8 @@ export {
30882
30959
  getDbPath,
30883
30960
  getDb,
30884
30961
  getDataDir,
30962
+ getClipboardKeyPath,
30963
+ getClipboardHistoryPath,
30885
30964
  getClaudeCliStatus,
30886
30965
  getAppsStatus,
30887
30966
  getAgentStatus,
@@ -30906,5 +30985,6 @@ export {
30906
30985
  buildAppsPlan,
30907
30986
  addNotificationChannel,
30908
30987
  addDomainMapping,
30909
- MACHINE_MCP_TOOL_NAMES
30988
+ MACHINE_MCP_TOOL_NAMES,
30989
+ CROSSREFS_KEY
30910
30990
  };
package/dist/mcp/index.js CHANGED
@@ -14866,11 +14866,13 @@ function renderDashboardHtml() {
14866
14866
  .unknown, .warn { background: #2f2b16; color: #ffd76a; }
14867
14867
  ul { margin: 8px 0 0; padding-left: 18px; }
14868
14868
  .muted { color: #9fb0d9; }
14869
+ .refresh { font-size: 12px; color: #6b7fa3; margin-left: auto; }
14870
+ .updated { transition: opacity 0.3s; }
14869
14871
  </style>
14870
14872
  </head>
14871
14873
  <body>
14872
14874
  <main>
14873
- <h1>Machines Dashboard</h1>
14875
+ <h1>Machines Dashboard <span class="refresh" id="last-updated"></span></h1>
14874
14876
  <div class="grid">
14875
14877
  <section class="card"><div>Manifest machines</div><div class="stat">${status.manifestMachineCount}</div></section>
14876
14878
  <section class="card"><div>Heartbeats</div><div class="stat">${status.heartbeatCount}</div></section>
@@ -14897,7 +14899,7 @@ function renderDashboardHtml() {
14897
14899
  <h2>Doctor</h2>
14898
14900
  <table>
14899
14901
  <thead><tr><th>Check</th><th>Status</th><th>Detail</th></tr></thead>
14900
- <tbody>
14902
+ <tbody id="doctor-tbody">
14901
14903
  ${doctor.checks.map((entry) => `<tr>
14902
14904
  <td>${escapeHtml(entry.summary)}</td>
14903
14905
  <td><span class="badge ${escapeHtml(entry.status)}">${escapeHtml(entry.status)}</span></td>
@@ -14907,6 +14909,16 @@ function renderDashboardHtml() {
14907
14909
  </table>
14908
14910
  </section>
14909
14911
 
14912
+ <section class="card" style="margin-top:16px">
14913
+ <h2>Apps</h2>
14914
+ <p class="muted">Use <code>/api/apps/status</code> for the full app inventory payload.</p>
14915
+ </section>
14916
+
14917
+ <section class="card" style="margin-top:16px">
14918
+ <h2>AI CLIs</h2>
14919
+ <p class="muted">Use <code>/api/install-claude/status</code> for the full CLI inventory payload.</p>
14920
+ </section>
14921
+
14910
14922
  <section class="card" style="margin-top:16px">
14911
14923
  <h2>Self Test</h2>
14912
14924
  <p class="muted">Use <code>/api/self-test</code> for the full smoke-check payload.</p>
@@ -14914,9 +14926,66 @@ function renderDashboardHtml() {
14914
14926
 
14915
14927
  <section class="card" style="margin-top:16px">
14916
14928
  <h2>Manifest</h2>
14917
- <pre>${escapeHtml(JSON.stringify(manifest, null, 2))}</pre>
14929
+ <pre id="manifest-json">${escapeHtml(JSON.stringify(manifest, null, 2))}</pre>
14918
14930
  </section>
14919
14931
  </main>
14932
+ <script>
14933
+ // Auto-refresh dashboard data every 15s
14934
+ const REFRESH_INTERVAL = 15000;
14935
+ async function refreshData() {
14936
+ try {
14937
+ const [statusRes, doctorRes] = await Promise.all([
14938
+ fetch("/api/status"),
14939
+ fetch("/api/doctor"),
14940
+ ]);
14941
+ const status = await statusRes.json();
14942
+ const doctor = await doctorRes.json();
14943
+
14944
+ // Update stat cards
14945
+ const stats = document.querySelectorAll(".stat");
14946
+ if (stats[0]) stats[0].textContent = status.manifestMachineCount;
14947
+ if (stats[1]) stats[1].textContent = status.heartbeatCount;
14948
+
14949
+ // Update machine table
14950
+ const tbody = document.querySelector("tbody");
14951
+ if (tbody && status.machines) {
14952
+ tbody.innerHTML = status.machines
14953
+ .map((m) =>
14954
+ "<tr>" +
14955
+ "<td><code>" + m.machineId + "</code></td>" +
14956
+ "<td>" + (m.platform || "unknown") + "</td>" +
14957
+ '<td><span class="badge ' + m.heartbeatStatus + '">' + m.heartbeatStatus + '</span></td>' +
14958
+ "<td>" + (m.lastHeartbeatAt || "\\u2014") + "</td>" +
14959
+ "</tr>"
14960
+ )
14961
+ .join("");
14962
+ }
14963
+
14964
+ // Update doctor table
14965
+ const doctorTbody = document.getElementById("doctor-tbody");
14966
+ if (doctorTbody && doctor.checks) {
14967
+ doctorTbody.innerHTML = doctor.checks
14968
+ .map((c) =>
14969
+ "<tr>" +
14970
+ "<td>" + c.summary + "</td>" +
14971
+ '<td><span class="badge ' + c.status + '">' + c.status + '</span></td>' +
14972
+ '<td class="muted">' + c.detail + "</td>" +
14973
+ "</tr>"
14974
+ )
14975
+ .join("");
14976
+ }
14977
+
14978
+ // Update timestamp
14979
+ document.getElementById("last-updated").textContent =
14980
+ "updated " + new Date().toLocaleTimeString();
14981
+ } catch (e) {
14982
+ // Silently ignore fetch errors during page unload
14983
+ }
14984
+ }
14985
+ document.getElementById("last-updated").textContent =
14986
+ "updated " + new Date().toLocaleTimeString();
14987
+ setInterval(refreshData, REFRESH_INTERVAL);
14988
+ </script>
14920
14989
  </body>
14921
14990
  </html>`;
14922
14991
  }
package/dist/paths.d.ts CHANGED
@@ -2,6 +2,8 @@ export declare function getDataDir(): string;
2
2
  export declare function getDbPath(): string;
3
3
  export declare function getManifestPath(): string;
4
4
  export declare function getNotificationsPath(): string;
5
+ export declare function getClipboardKeyPath(): string;
6
+ export declare function getClipboardHistoryPath(): string;
5
7
  export declare function ensureParentDir(filePath: string): void;
6
8
  export declare function ensureDataDir(): string;
7
9
  //# sourceMappingURL=paths.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"paths.d.ts","sourceRoot":"","sources":["../src/paths.ts"],"names":[],"mappings":"AAOA,wBAAgB,UAAU,IAAI,MAAM,CAEnC;AAED,wBAAgB,SAAS,IAAI,MAAM,CAElC;AAED,wBAAgB,eAAe,IAAI,MAAM,CAExC;AAED,wBAAgB,oBAAoB,IAAI,MAAM,CAE7C;AAED,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAMtD;AAED,wBAAgB,aAAa,IAAI,MAAM,CAItC"}
1
+ {"version":3,"file":"paths.d.ts","sourceRoot":"","sources":["../src/paths.ts"],"names":[],"mappings":"AAOA,wBAAgB,UAAU,IAAI,MAAM,CAEnC;AAED,wBAAgB,SAAS,IAAI,MAAM,CAElC;AAED,wBAAgB,eAAe,IAAI,MAAM,CAExC;AAED,wBAAgB,oBAAoB,IAAI,MAAM,CAE7C;AAED,wBAAgB,mBAAmB,IAAI,MAAM,CAE5C;AAED,wBAAgB,uBAAuB,IAAI,MAAM,CAEhD;AAED,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAMtD;AAED,wBAAgB,aAAa,IAAI,MAAM,CAItC"}
package/dist/types.d.ts CHANGED
@@ -25,6 +25,7 @@ export interface MachineManifest {
25
25
  workspacePath: string;
26
26
  bunPath?: string;
27
27
  tags?: string[];
28
+ metadata?: Record<string, unknown>;
28
29
  packages?: ManifestPackageSpec[];
29
30
  apps?: ManifestAppSpec[];
30
31
  files?: ManifestFileSyncSpec[];
@@ -184,4 +185,33 @@ export interface SelfTestResult {
184
185
  machineId: string;
185
186
  checks: SelfTestCheck[];
186
187
  }
188
+ export interface ClipboardEntry {
189
+ hash: string;
190
+ content: string;
191
+ contentType: "text" | "rich" | "url";
192
+ sourceMachine: string;
193
+ timestamp: string;
194
+ }
195
+ export interface ClipboardConfig {
196
+ version: 1;
197
+ enabled: boolean;
198
+ port: number;
199
+ maxHistory: number;
200
+ maxSizeBytes: number;
201
+ skipPatterns: string[];
202
+ }
203
+ export interface ClipboardStatus {
204
+ running: boolean;
205
+ pid?: number;
206
+ port: number;
207
+ lastSync?: string;
208
+ historyCount: number;
209
+ }
210
+ export interface ClipboardSyncEvent {
211
+ hash: string;
212
+ content: string;
213
+ contentType: "text" | "rich" | "url";
214
+ sourceMachine: string;
215
+ timestamp: string;
216
+ }
187
217
  //# sourceMappingURL=types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hasna/machines",
3
- "version": "0.0.3",
3
+ "version": "0.0.5",
4
4
  "description": "Machine fleet management CLI + MCP for developers",
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",