@hasna/machines 0.0.28 → 0.0.30
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +16 -0
- package/dist/cli/index.js +1284 -1167
- package/dist/commands/runtime.d.ts +32 -0
- package/dist/commands/runtime.d.ts.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +91 -0
- package/package.json +2 -2
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { EventsClient, type EmitResult } from "@hasna/events";
|
|
2
|
+
export interface TmuxPaneProbeResult {
|
|
3
|
+
target: string;
|
|
4
|
+
exists: boolean;
|
|
5
|
+
paneId?: string;
|
|
6
|
+
checkedAt: string;
|
|
7
|
+
exitCode?: number | null;
|
|
8
|
+
error?: string;
|
|
9
|
+
stderr?: string;
|
|
10
|
+
}
|
|
11
|
+
export interface TmuxWatchOptions {
|
|
12
|
+
target: string;
|
|
13
|
+
intervalMs?: number;
|
|
14
|
+
maxChecks?: number;
|
|
15
|
+
emitInitialMissing?: boolean;
|
|
16
|
+
deliver?: boolean;
|
|
17
|
+
tmuxCommand?: string;
|
|
18
|
+
client?: Pick<EventsClient, "emit">;
|
|
19
|
+
probe?: (target: string) => TmuxPaneProbeResult | Promise<TmuxPaneProbeResult>;
|
|
20
|
+
sleep?: (ms: number) => Promise<void>;
|
|
21
|
+
onProbe?: (probe: TmuxPaneProbeResult) => void;
|
|
22
|
+
}
|
|
23
|
+
export interface TmuxWatchResult {
|
|
24
|
+
target: string;
|
|
25
|
+
checks: number;
|
|
26
|
+
status: "present" | "missing" | "died" | "stopped";
|
|
27
|
+
lastProbe: TmuxPaneProbeResult;
|
|
28
|
+
emitted?: EmitResult;
|
|
29
|
+
}
|
|
30
|
+
export declare function probeTmuxPane(target: string, tmuxCommand?: string): TmuxPaneProbeResult;
|
|
31
|
+
export declare function watchTmuxPane(options: TmuxWatchOptions): Promise<TmuxWatchResult>;
|
|
32
|
+
//# sourceMappingURL=runtime.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../../src/commands/runtime.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,YAAY,EAAE,KAAK,UAAU,EAAmB,MAAM,eAAe,CAAC;AAE/E,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,OAAO,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,IAAI,CAAC,YAAY,EAAE,MAAM,CAAC,CAAC;IACpC,KAAK,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,mBAAmB,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAAC;IAC/E,KAAK,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACtC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,mBAAmB,KAAK,IAAI,CAAC;CAChD;AAED,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,SAAS,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAAC;IACnD,SAAS,EAAE,mBAAmB,CAAC;IAC/B,OAAO,CAAC,EAAE,UAAU,CAAC;CACtB;AAED,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,WAAW,SAAmD,GAAG,mBAAmB,CAmBjI;AAED,wBAAsB,aAAa,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,eAAe,CAAC,CA6CvF"}
|
package/dist/index.d.ts
CHANGED
|
@@ -17,6 +17,7 @@ export * from "./commands/install-claude.js";
|
|
|
17
17
|
export * from "./commands/install-tailscale.js";
|
|
18
18
|
export * from "./commands/notifications.js";
|
|
19
19
|
export * from "./commands/ports.js";
|
|
20
|
+
export * from "./commands/runtime.js";
|
|
20
21
|
export * from "./commands/self-test.js";
|
|
21
22
|
export * from "./commands/serve.js";
|
|
22
23
|
export * from "./commands/setup.js";
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
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,eAAe,CAAC;AAC9B,cAAc,oBAAoB,CAAC;AACnC,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,sBAAsB,CAAC;AACrC,cAAc,oBAAoB,CAAC;AACnC,cAAc,sBAAsB,CAAC;AACrC,cAAc,yBAAyB,CAAC;AACxC,cAAc,iBAAiB,CAAC;AAChC,OAAO,EACL,oBAAoB,EACpB,6BAA6B,EAC7B,yBAAyB,EACzB,kCAAkC,EAClC,uBAAuB,EACvB,oBAAoB,EACpB,gBAAgB,EAChB,cAAc,EACd,qBAAqB,EACrB,yBAAyB,EACzB,qBAAqB,EACrB,cAAc,EACd,YAAY,EACZ,gBAAgB,EAChB,cAAc,EACd,kBAAkB,EAClB,aAAa,EACb,oBAAoB,EACpB,WAAW,EACX,WAAW,EACX,WAAW,GACZ,MAAM,cAAc,CAAC;AACtB,YAAY,EAAE,UAAU,EAAE,WAAW,EAAE,aAAa,EAAE,QAAQ,EAAE,UAAU,IAAI,iBAAiB,EAAE,MAAM,cAAc,CAAC;AACtH,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,eAAe,CAAC;AAC9B,cAAc,oBAAoB,CAAC;AACnC,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,uBAAuB,CAAC;AACtC,cAAc,yBAAyB,CAAC;AACxC,cAAc,qBAAqB,CAAC;AACpC,cAAc,qBAAqB,CAAC;AACpC,cAAc,mBAAmB,CAAC;AAClC,cAAc,sBAAsB,CAAC;AACrC,cAAc,oBAAoB,CAAC;AACnC,cAAc,sBAAsB,CAAC;AACrC,cAAc,yBAAyB,CAAC;AACxC,cAAc,iBAAiB,CAAC;AAChC,OAAO,EACL,oBAAoB,EACpB,6BAA6B,EAC7B,yBAAyB,EACzB,kCAAkC,EAClC,uBAAuB,EACvB,oBAAoB,EACpB,gBAAgB,EAChB,cAAc,EACd,qBAAqB,EACrB,yBAAyB,EACzB,qBAAqB,EACrB,cAAc,EACd,YAAY,EACZ,gBAAgB,EAChB,cAAc,EACd,kBAAkB,EAClB,aAAa,EACb,oBAAoB,EACpB,WAAW,EACX,WAAW,EACX,WAAW,GACZ,MAAM,cAAc,CAAC;AACtB,YAAY,EAAE,UAAU,EAAE,WAAW,EAAE,aAAa,EAAE,QAAQ,EAAE,UAAU,IAAI,iBAAiB,EAAE,MAAM,cAAc,CAAC;AACtH,cAAc,cAAc,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -13427,6 +13427,10 @@ function listPorts(machineId) {
|
|
|
13427
13427
|
listeners: parsePortOutput(result.stdout, format)
|
|
13428
13428
|
};
|
|
13429
13429
|
}
|
|
13430
|
+
// src/commands/runtime.ts
|
|
13431
|
+
import { spawnSync as spawnSync4 } from "child_process";
|
|
13432
|
+
import { setTimeout as sleep } from "timers/promises";
|
|
13433
|
+
|
|
13430
13434
|
// node_modules/@hasna/events/dist/index.js
|
|
13431
13435
|
import { chmod, mkdir, readFile, rename, writeFile } from "fs/promises";
|
|
13432
13436
|
import { existsSync as existsSync7 } from "fs";
|
|
@@ -13947,6 +13951,91 @@ function normalizeRetryPolicy(policy) {
|
|
|
13947
13951
|
};
|
|
13948
13952
|
}
|
|
13949
13953
|
|
|
13954
|
+
// src/commands/runtime.ts
|
|
13955
|
+
function probeTmuxPane(target, tmuxCommand = process.env["HASNA_MACHINES_TMUX_BIN"] || "tmux") {
|
|
13956
|
+
const checkedAt = new Date().toISOString();
|
|
13957
|
+
const result = spawnSync4(tmuxCommand, ["display-message", "-p", "-t", target, "#{pane_id}"], {
|
|
13958
|
+
encoding: "utf8",
|
|
13959
|
+
timeout: 5000
|
|
13960
|
+
});
|
|
13961
|
+
const stdout = result.stdout?.trim();
|
|
13962
|
+
const stderr = result.stderr?.trim();
|
|
13963
|
+
const exists = result.status === 0 && Boolean(stdout);
|
|
13964
|
+
return {
|
|
13965
|
+
target,
|
|
13966
|
+
exists,
|
|
13967
|
+
paneId: exists ? stdout : undefined,
|
|
13968
|
+
checkedAt,
|
|
13969
|
+
exitCode: result.status,
|
|
13970
|
+
error: result.error?.message,
|
|
13971
|
+
stderr: stderr || undefined
|
|
13972
|
+
};
|
|
13973
|
+
}
|
|
13974
|
+
async function watchTmuxPane(options) {
|
|
13975
|
+
const target = options.target.trim();
|
|
13976
|
+
if (!target)
|
|
13977
|
+
throw new Error("tmux pane target is required");
|
|
13978
|
+
const intervalMs = Math.max(0, options.intervalMs ?? 5000);
|
|
13979
|
+
const maxChecks = options.maxChecks ?? Number.POSITIVE_INFINITY;
|
|
13980
|
+
const client = options.client ?? new EventsClient;
|
|
13981
|
+
const probe = options.probe ?? ((paneTarget) => probeTmuxPane(paneTarget, options.tmuxCommand));
|
|
13982
|
+
const wait = options.sleep ?? sleep;
|
|
13983
|
+
let lastPresent;
|
|
13984
|
+
let lastProbe;
|
|
13985
|
+
for (let checks = 1;checks <= maxChecks; checks += 1) {
|
|
13986
|
+
const current = await probe(target);
|
|
13987
|
+
lastProbe = current;
|
|
13988
|
+
options.onProbe?.(current);
|
|
13989
|
+
if (current.exists) {
|
|
13990
|
+
lastPresent = current;
|
|
13991
|
+
} else if (lastPresent) {
|
|
13992
|
+
const emitted = await emitTmuxEvent(client, "machines.tmux.pane_died", current, lastPresent, options.deliver !== false);
|
|
13993
|
+
return { target, checks, status: "died", lastProbe: current, emitted };
|
|
13994
|
+
} else if (options.emitInitialMissing) {
|
|
13995
|
+
const emitted = await emitTmuxEvent(client, "machines.tmux.pane_missing", current, undefined, options.deliver !== false);
|
|
13996
|
+
return { target, checks, status: "missing", lastProbe: current, emitted };
|
|
13997
|
+
}
|
|
13998
|
+
if (checks < maxChecks)
|
|
13999
|
+
await wait(intervalMs);
|
|
14000
|
+
}
|
|
14001
|
+
if (!lastProbe) {
|
|
14002
|
+
lastProbe = {
|
|
14003
|
+
target,
|
|
14004
|
+
exists: false,
|
|
14005
|
+
checkedAt: new Date().toISOString(),
|
|
14006
|
+
error: "No probe executed"
|
|
14007
|
+
};
|
|
14008
|
+
}
|
|
14009
|
+
return {
|
|
14010
|
+
target,
|
|
14011
|
+
checks: Number.isFinite(maxChecks) ? maxChecks : 0,
|
|
14012
|
+
status: lastProbe.exists ? "present" : "stopped",
|
|
14013
|
+
lastProbe
|
|
14014
|
+
};
|
|
14015
|
+
}
|
|
14016
|
+
async function emitTmuxEvent(client, type, probe, lastPresent, deliver) {
|
|
14017
|
+
const input = {
|
|
14018
|
+
source: "machines",
|
|
14019
|
+
type,
|
|
14020
|
+
subject: `tmux:${probe.target}`,
|
|
14021
|
+
severity: type === "machines.tmux.pane_died" ? "warning" : "notice",
|
|
14022
|
+
message: type === "machines.tmux.pane_died" ? `tmux pane disappeared: ${probe.target}` : `tmux pane is missing: ${probe.target}`,
|
|
14023
|
+
data: {
|
|
14024
|
+
target: probe.target,
|
|
14025
|
+
paneId: lastPresent?.paneId,
|
|
14026
|
+
lastSeenAt: lastPresent?.checkedAt,
|
|
14027
|
+
checkedAt: probe.checkedAt,
|
|
14028
|
+
exitCode: probe.exitCode,
|
|
14029
|
+
error: probe.error,
|
|
14030
|
+
stderr: probe.stderr
|
|
14031
|
+
},
|
|
14032
|
+
metadata: {
|
|
14033
|
+
monitor: "tmux-pane",
|
|
14034
|
+
runtime: "machines"
|
|
14035
|
+
}
|
|
14036
|
+
};
|
|
14037
|
+
return client.emit(input, { deliver });
|
|
14038
|
+
}
|
|
13950
14039
|
// src/commands/status.ts
|
|
13951
14040
|
function getStatus() {
|
|
13952
14041
|
const manifest = readManifest();
|
|
@@ -24057,6 +24146,7 @@ export {
|
|
|
24057
24146
|
writeNotificationConfig,
|
|
24058
24147
|
writeManifest,
|
|
24059
24148
|
writeHeartbeat,
|
|
24149
|
+
watchTmuxPane,
|
|
24060
24150
|
validateManifest,
|
|
24061
24151
|
upsertHeartbeat,
|
|
24062
24152
|
testNotificationChannel,
|
|
@@ -24090,6 +24180,7 @@ export {
|
|
|
24090
24180
|
recordSetupRun,
|
|
24091
24181
|
readNotificationConfig,
|
|
24092
24182
|
readManifest,
|
|
24183
|
+
probeTmuxPane,
|
|
24093
24184
|
parseStorageTables,
|
|
24094
24185
|
parsePortOutput,
|
|
24095
24186
|
markOffline,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hasna/machines",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.30",
|
|
4
4
|
"description": "Machine fleet management CLI + MCP for developers",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -67,7 +67,7 @@
|
|
|
67
67
|
"bun": ">=1.0.0"
|
|
68
68
|
},
|
|
69
69
|
"dependencies": {
|
|
70
|
-
"@hasna/events": "^0.1.
|
|
70
|
+
"@hasna/events": "^0.1.7",
|
|
71
71
|
"@modelcontextprotocol/sdk": "^1.26.0",
|
|
72
72
|
"chalk": "^5.6.2",
|
|
73
73
|
"commander": "^13.1.0",
|