@hasna/machines 0.0.29 → 0.0.31
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 +1036 -1038
- package/dist/commands/apps.d.ts +4 -3
- package/dist/commands/apps.d.ts.map +1 -1
- package/dist/commands/install-claude.d.ts +4 -3
- package/dist/commands/install-claude.d.ts.map +1 -1
- package/dist/commands/install-tailscale.d.ts +2 -1
- package/dist/commands/install-tailscale.d.ts.map +1 -1
- package/dist/commands/setup.d.ts +2 -1
- package/dist/commands/setup.d.ts.map +1 -1
- package/dist/commands/sync.d.ts +3 -2
- package/dist/commands/sync.d.ts.map +1 -1
- package/dist/consumer.js +11 -0
- package/dist/index.js +63 -67
- package/dist/mcp/index.js +63 -67
- package/dist/remote.d.ts +3 -0
- package/dist/remote.d.ts.map +1 -1
- package/package.json +3 -3
package/dist/mcp/index.js
CHANGED
|
@@ -5793,6 +5793,17 @@ function runMachineCommand(machineId, command) {
|
|
|
5793
5793
|
exitCode: result.status ?? 1
|
|
5794
5794
|
};
|
|
5795
5795
|
}
|
|
5796
|
+
function describeMachineCommandFailure(operation, result) {
|
|
5797
|
+
const detail = (result.stderr || result.stdout || "").trim();
|
|
5798
|
+
const suffix = detail ? `: ${detail}` : "";
|
|
5799
|
+
return `${operation} failed on ${result.machineId} via ${result.source} (exit ${result.exitCode})${suffix}`;
|
|
5800
|
+
}
|
|
5801
|
+
function requireMachineCommandSuccess(operation, result) {
|
|
5802
|
+
if (result.exitCode !== 0) {
|
|
5803
|
+
throw new Error(describeMachineCommandFailure(operation, result));
|
|
5804
|
+
}
|
|
5805
|
+
return result;
|
|
5806
|
+
}
|
|
5796
5807
|
|
|
5797
5808
|
// src/commands/apps.ts
|
|
5798
5809
|
function getPackageName(app) {
|
|
@@ -5886,27 +5897,28 @@ function buildAppsPlan(machineId) {
|
|
|
5886
5897
|
executed: 0
|
|
5887
5898
|
};
|
|
5888
5899
|
}
|
|
5889
|
-
function getAppsStatus(machineId) {
|
|
5900
|
+
function getAppsStatus(machineId, runner = runMachineCommand) {
|
|
5890
5901
|
const machine = resolveMachine(machineId);
|
|
5902
|
+
const readiness = requireMachineCommandSuccess("Apps status readiness check", runner(machine.id, "true"));
|
|
5891
5903
|
const apps = (machine.apps || []).map((app) => {
|
|
5892
|
-
const probe =
|
|
5904
|
+
const probe = requireMachineCommandSuccess(`App probe ${app.name}`, runner(machine.id, buildAppProbeCommand(machine, app)));
|
|
5893
5905
|
return parseProbeOutput(app, machine, probe.stdout);
|
|
5894
5906
|
});
|
|
5895
5907
|
return {
|
|
5896
5908
|
machineId: machine.id,
|
|
5897
|
-
source:
|
|
5909
|
+
source: readiness.source,
|
|
5898
5910
|
apps
|
|
5899
5911
|
};
|
|
5900
5912
|
}
|
|
5901
|
-
function diffApps(machineId) {
|
|
5902
|
-
const status = getAppsStatus(machineId);
|
|
5913
|
+
function diffApps(machineId, runner = runMachineCommand) {
|
|
5914
|
+
const status = getAppsStatus(machineId, runner);
|
|
5903
5915
|
return {
|
|
5904
5916
|
...status,
|
|
5905
5917
|
missing: status.apps.filter((app) => !app.installed).map((app) => app.name),
|
|
5906
5918
|
installed: status.apps.filter((app) => app.installed).map((app) => app.name)
|
|
5907
5919
|
};
|
|
5908
5920
|
}
|
|
5909
|
-
function runAppsInstall(machineId, options = {}) {
|
|
5921
|
+
function runAppsInstall(machineId, options = {}, runner = runMachineCommand) {
|
|
5910
5922
|
const plan = buildAppsPlan(machineId);
|
|
5911
5923
|
if (!options.apply)
|
|
5912
5924
|
return plan;
|
|
@@ -5915,14 +5927,7 @@ function runAppsInstall(machineId, options = {}) {
|
|
|
5915
5927
|
}
|
|
5916
5928
|
let executed = 0;
|
|
5917
5929
|
for (const step of plan.steps) {
|
|
5918
|
-
|
|
5919
|
-
stdout: "pipe",
|
|
5920
|
-
stderr: "pipe",
|
|
5921
|
-
env: process.env
|
|
5922
|
-
});
|
|
5923
|
-
if (result.exitCode !== 0) {
|
|
5924
|
-
throw new Error(`App install failed (${step.id}): ${result.stderr.toString().trim()}`);
|
|
5925
|
-
}
|
|
5930
|
+
requireMachineCommandSuccess(`App install ${step.id}`, runner(plan.machineId, step.command));
|
|
5926
5931
|
executed += 1;
|
|
5927
5932
|
}
|
|
5928
5933
|
return {
|
|
@@ -6209,25 +6214,28 @@ function buildClaudeInstallPlan(machineId, tools) {
|
|
|
6209
6214
|
executed: 0
|
|
6210
6215
|
};
|
|
6211
6216
|
}
|
|
6212
|
-
function getClaudeCliStatus(machineId, tools) {
|
|
6217
|
+
function getClaudeCliStatus(machineId, tools, runner = runMachineCommand) {
|
|
6213
6218
|
const machine = resolveMachine2(machineId);
|
|
6214
6219
|
const normalizedTools = normalizeTools(tools);
|
|
6215
|
-
const route =
|
|
6220
|
+
const route = requireMachineCommandSuccess("AI CLI status readiness check", runner(machine.id, "true")).source;
|
|
6216
6221
|
return {
|
|
6217
6222
|
machineId: machine.id,
|
|
6218
6223
|
source: route,
|
|
6219
|
-
tools: normalizedTools.map((tool) =>
|
|
6224
|
+
tools: normalizedTools.map((tool) => {
|
|
6225
|
+
const result = requireMachineCommandSuccess(`AI CLI probe ${tool}`, runner(machine.id, buildProbeCommand(tool)));
|
|
6226
|
+
return parseProbe(tool, result.stdout);
|
|
6227
|
+
})
|
|
6220
6228
|
};
|
|
6221
6229
|
}
|
|
6222
|
-
function diffClaudeCli(machineId, tools) {
|
|
6223
|
-
const status = getClaudeCliStatus(machineId, tools);
|
|
6230
|
+
function diffClaudeCli(machineId, tools, runner = runMachineCommand) {
|
|
6231
|
+
const status = getClaudeCliStatus(machineId, tools, runner);
|
|
6224
6232
|
return {
|
|
6225
6233
|
...status,
|
|
6226
6234
|
missing: status.tools.filter((tool) => !tool.installed).map((tool) => tool.tool),
|
|
6227
6235
|
installed: status.tools.filter((tool) => tool.installed).map((tool) => tool.tool)
|
|
6228
6236
|
};
|
|
6229
6237
|
}
|
|
6230
|
-
function runClaudeInstall(machineId, tools, options = {}) {
|
|
6238
|
+
function runClaudeInstall(machineId, tools, options = {}, runner = runMachineCommand) {
|
|
6231
6239
|
const plan = buildClaudeInstallPlan(machineId, tools);
|
|
6232
6240
|
if (!options.apply)
|
|
6233
6241
|
return plan;
|
|
@@ -6236,14 +6244,7 @@ function runClaudeInstall(machineId, tools, options = {}) {
|
|
|
6236
6244
|
}
|
|
6237
6245
|
let executed = 0;
|
|
6238
6246
|
for (const step of plan.steps) {
|
|
6239
|
-
|
|
6240
|
-
stdout: "pipe",
|
|
6241
|
-
stderr: "pipe",
|
|
6242
|
-
env: process.env
|
|
6243
|
-
});
|
|
6244
|
-
if (result.exitCode !== 0) {
|
|
6245
|
-
throw new Error(`AI CLI install failed (${step.id}): ${result.stderr.toString().trim()}`);
|
|
6246
|
-
}
|
|
6247
|
+
requireMachineCommandSuccess(`AI CLI install ${step.id}`, runner(plan.machineId, step.command));
|
|
6247
6248
|
executed += 1;
|
|
6248
6249
|
}
|
|
6249
6250
|
return {
|
|
@@ -6295,7 +6296,7 @@ function buildTailscaleInstallPlan(machineId) {
|
|
|
6295
6296
|
executed: 0
|
|
6296
6297
|
};
|
|
6297
6298
|
}
|
|
6298
|
-
function runTailscaleInstall(machineId, options = {}) {
|
|
6299
|
+
function runTailscaleInstall(machineId, options = {}, runner = runMachineCommand) {
|
|
6299
6300
|
const plan = buildTailscaleInstallPlan(machineId);
|
|
6300
6301
|
if (!options.apply)
|
|
6301
6302
|
return plan;
|
|
@@ -6304,14 +6305,7 @@ function runTailscaleInstall(machineId, options = {}) {
|
|
|
6304
6305
|
}
|
|
6305
6306
|
let executed = 0;
|
|
6306
6307
|
for (const step of plan.steps) {
|
|
6307
|
-
|
|
6308
|
-
stdout: "pipe",
|
|
6309
|
-
stderr: "pipe",
|
|
6310
|
-
env: process.env
|
|
6311
|
-
});
|
|
6312
|
-
if (result.exitCode !== 0) {
|
|
6313
|
-
throw new Error(`Tailscale install failed (${step.id}): ${result.stderr.toString().trim()}`);
|
|
6314
|
-
}
|
|
6308
|
+
requireMachineCommandSuccess(`Tailscale install ${step.id}`, runner(plan.machineId, step.command));
|
|
6315
6309
|
executed += 1;
|
|
6316
6310
|
}
|
|
6317
6311
|
return {
|
|
@@ -6980,7 +6974,7 @@ function buildSetupPlan(machineId) {
|
|
|
6980
6974
|
executed: 0
|
|
6981
6975
|
};
|
|
6982
6976
|
}
|
|
6983
|
-
function runSetup(machineId, options = {}) {
|
|
6977
|
+
function runSetup(machineId, options = {}, runner = runMachineCommand) {
|
|
6984
6978
|
const plan = buildSetupPlan(machineId);
|
|
6985
6979
|
if (!options.apply) {
|
|
6986
6980
|
return plan;
|
|
@@ -6990,18 +6984,17 @@ function runSetup(machineId, options = {}) {
|
|
|
6990
6984
|
}
|
|
6991
6985
|
let executed = 0;
|
|
6992
6986
|
for (const step of plan.steps) {
|
|
6993
|
-
const result =
|
|
6994
|
-
stdout: "pipe",
|
|
6995
|
-
stderr: "pipe",
|
|
6996
|
-
env: process.env
|
|
6997
|
-
});
|
|
6987
|
+
const result = runner(plan.machineId, step.command);
|
|
6998
6988
|
if (result.exitCode !== 0) {
|
|
6999
6989
|
recordSetupRun(plan.machineId, "failed", {
|
|
7000
6990
|
executed,
|
|
7001
6991
|
failedStep: step,
|
|
7002
|
-
stderr: result.stderr
|
|
6992
|
+
stderr: result.stderr,
|
|
6993
|
+
stdout: result.stdout,
|
|
6994
|
+
exitCode: result.exitCode,
|
|
6995
|
+
source: result.source
|
|
7003
6996
|
});
|
|
7004
|
-
throw new Error(`Setup step
|
|
6997
|
+
throw new Error(describeMachineCommandFailure(`Setup step ${step.id}`, result));
|
|
7005
6998
|
}
|
|
7006
6999
|
executed += 1;
|
|
7007
7000
|
}
|
|
@@ -7022,16 +7015,17 @@ function quote4(value) {
|
|
|
7022
7015
|
return `'${value.replace(/'/g, `'\\''`)}'`;
|
|
7023
7016
|
}
|
|
7024
7017
|
function packageCheckCommand(machine, packageName, manager = machine.platform === "macos" ? "brew" : "apt") {
|
|
7018
|
+
const quotedPackageName = quote4(packageName);
|
|
7025
7019
|
if (manager === "bun") {
|
|
7026
|
-
return `bun pm ls -g --all | grep -F ${
|
|
7020
|
+
return `if bun pm ls -g --all 2>/dev/null | grep -F ${quotedPackageName} >/dev/null 2>&1; then printf 'installed=1\\n'; else printf 'installed=0\\n'; fi`;
|
|
7027
7021
|
}
|
|
7028
7022
|
if (manager === "brew") {
|
|
7029
|
-
return `brew list --versions ${
|
|
7023
|
+
return `if brew list --versions ${quotedPackageName} >/dev/null 2>&1; then printf 'installed=1\\n'; else printf 'installed=0\\n'; fi`;
|
|
7030
7024
|
}
|
|
7031
7025
|
if (manager === "apt") {
|
|
7032
|
-
return `dpkg -s ${
|
|
7026
|
+
return `if dpkg -s ${quotedPackageName} >/dev/null 2>&1; then printf 'installed=1\\n'; else printf 'installed=0\\n'; fi`;
|
|
7033
7027
|
}
|
|
7034
|
-
return `command -v ${
|
|
7028
|
+
return `if command -v ${quotedPackageName} >/dev/null 2>&1; then printf 'installed=1\\n'; else printf 'installed=0\\n'; fi`;
|
|
7035
7029
|
}
|
|
7036
7030
|
function packageInstallCommand(machine, packageName, manager = machine.platform === "macos" ? "brew" : "apt") {
|
|
7037
7031
|
if (manager === "bun") {
|
|
@@ -7045,15 +7039,15 @@ function packageInstallCommand(machine, packageName, manager = machine.platform
|
|
|
7045
7039
|
}
|
|
7046
7040
|
return packageName;
|
|
7047
7041
|
}
|
|
7048
|
-
function detectPackageActions(machine) {
|
|
7042
|
+
function detectPackageActions(machine, runner) {
|
|
7049
7043
|
return (machine.packages || []).map((pkg, index) => {
|
|
7050
7044
|
const manager = pkg.manager || (machine.platform === "macos" ? "brew" : "apt");
|
|
7051
|
-
const check2 =
|
|
7052
|
-
|
|
7053
|
-
|
|
7054
|
-
|
|
7055
|
-
|
|
7056
|
-
|
|
7045
|
+
const check2 = runner(machine.id, packageCheckCommand(machine, pkg.name, manager));
|
|
7046
|
+
if (check2.exitCode !== 0) {
|
|
7047
|
+
throw new Error(describeMachineCommandFailure(`Sync package probe ${pkg.name}`, check2));
|
|
7048
|
+
}
|
|
7049
|
+
const installed = check2.stdout.split(`
|
|
7050
|
+
`).some((line) => line.trim() === "installed=1");
|
|
7057
7051
|
return {
|
|
7058
7052
|
id: `package-${index + 1}`,
|
|
7059
7053
|
title: `${installed ? "Package present" : "Install package"} ${pkg.name}`,
|
|
@@ -7064,6 +7058,9 @@ function detectPackageActions(machine) {
|
|
|
7064
7058
|
});
|
|
7065
7059
|
}
|
|
7066
7060
|
function detectFileActions(machine) {
|
|
7061
|
+
if ((machine.files || []).length > 0 && resolveMachineCommand(machine.id, "true").source !== "local") {
|
|
7062
|
+
throw new Error(`Remote file sync planning is not supported for ${machine.id}; refusing to inspect or apply local paths as remote state.`);
|
|
7063
|
+
}
|
|
7067
7064
|
return (machine.files || []).map((file, index) => {
|
|
7068
7065
|
const sourceExists = existsSync8(file.source);
|
|
7069
7066
|
const targetExists = existsSync8(file.target);
|
|
@@ -7087,7 +7084,7 @@ function detectFileActions(machine) {
|
|
|
7087
7084
|
};
|
|
7088
7085
|
});
|
|
7089
7086
|
}
|
|
7090
|
-
function buildSyncPlan(machineId) {
|
|
7087
|
+
function buildSyncPlan(machineId, runner = runMachineCommand) {
|
|
7091
7088
|
const manifest = readManifest();
|
|
7092
7089
|
const currentMachineId = getLocalMachineId();
|
|
7093
7090
|
const selected = machineId ? manifest.machines.find((machine) => machine.id === machineId) : manifest.machines.find((machine) => machine.id === currentMachineId);
|
|
@@ -7097,7 +7094,7 @@ function buildSyncPlan(machineId) {
|
|
|
7097
7094
|
workspacePath: `${homedir6()}/workspace`
|
|
7098
7095
|
};
|
|
7099
7096
|
const actions = [
|
|
7100
|
-
...detectPackageActions(target),
|
|
7097
|
+
...detectPackageActions(target, runner),
|
|
7101
7098
|
...detectFileActions(target)
|
|
7102
7099
|
];
|
|
7103
7100
|
return {
|
|
@@ -7127,8 +7124,8 @@ function applyFileAction(command) {
|
|
|
7127
7124
|
symlinkSync(sourcePath, targetPath);
|
|
7128
7125
|
}
|
|
7129
7126
|
}
|
|
7130
|
-
function runSync(machineId, options = {}) {
|
|
7131
|
-
const plan = buildSyncPlan(machineId);
|
|
7127
|
+
function runSync(machineId, options = {}, runner = runMachineCommand) {
|
|
7128
|
+
const plan = buildSyncPlan(machineId, runner);
|
|
7132
7129
|
if (!options.apply) {
|
|
7133
7130
|
return plan;
|
|
7134
7131
|
}
|
|
@@ -7142,18 +7139,17 @@ function runSync(machineId, options = {}) {
|
|
|
7142
7139
|
if (action.kind === "file") {
|
|
7143
7140
|
applyFileAction(action.command);
|
|
7144
7141
|
} else {
|
|
7145
|
-
const result =
|
|
7146
|
-
stdout: "pipe",
|
|
7147
|
-
stderr: "pipe",
|
|
7148
|
-
env: process.env
|
|
7149
|
-
});
|
|
7142
|
+
const result = runner(plan.machineId, action.command);
|
|
7150
7143
|
if (result.exitCode !== 0) {
|
|
7151
7144
|
recordSyncRun(plan.machineId, "failed", {
|
|
7152
7145
|
executed,
|
|
7153
7146
|
failedAction: action,
|
|
7154
|
-
stderr: result.stderr
|
|
7147
|
+
stderr: result.stderr,
|
|
7148
|
+
stdout: result.stdout,
|
|
7149
|
+
exitCode: result.exitCode,
|
|
7150
|
+
source: result.source
|
|
7155
7151
|
});
|
|
7156
|
-
throw new Error(`Sync action
|
|
7152
|
+
throw new Error(describeMachineCommandFailure(`Sync action ${action.id}`, result));
|
|
7157
7153
|
}
|
|
7158
7154
|
}
|
|
7159
7155
|
executed += 1;
|
package/dist/remote.d.ts
CHANGED
|
@@ -5,9 +5,12 @@ export interface MachineCommandResult {
|
|
|
5
5
|
stderr: string;
|
|
6
6
|
exitCode: number;
|
|
7
7
|
}
|
|
8
|
+
export type MachineCommandRunner = (machineId: string, command: string) => MachineCommandResult;
|
|
8
9
|
export declare function resolveMachineCommand(machineId: string, command: string, localMachineId?: string): {
|
|
9
10
|
source: MachineCommandResult["source"];
|
|
10
11
|
shellCommand: string;
|
|
11
12
|
};
|
|
12
13
|
export declare function runMachineCommand(machineId: string, command: string): MachineCommandResult;
|
|
14
|
+
export declare function describeMachineCommandFailure(operation: string, result: MachineCommandResult): string;
|
|
15
|
+
export declare function requireMachineCommandSuccess(operation: string, result: MachineCommandResult): MachineCommandResult;
|
|
13
16
|
//# sourceMappingURL=remote.d.ts.map
|
package/dist/remote.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"remote.d.ts","sourceRoot":"","sources":["../src/remote.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,oBAAoB;IACnC,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,OAAO,GAAG,KAAK,GAAG,WAAW,GAAG,KAAK,CAAC;IAC9C,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;CAClB;
|
|
1
|
+
{"version":3,"file":"remote.d.ts","sourceRoot":"","sources":["../src/remote.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,oBAAoB;IACnC,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,OAAO,GAAG,KAAK,GAAG,WAAW,GAAG,KAAK,CAAC;IAC9C,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,MAAM,oBAAoB,GAAG,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK,oBAAoB,CAAC;AAahG,wBAAgB,qBAAqB,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,cAAc,SAAsB,GAAG;IAAE,MAAM,EAAE,oBAAoB,CAAC,QAAQ,CAAC,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE,CAiBhL;AAED,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,oBAAoB,CAc1F;AAED,wBAAgB,6BAA6B,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,oBAAoB,GAAG,MAAM,CAIrG;AAED,wBAAgB,4BAA4B,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,oBAAoB,GAAG,oBAAoB,CAKlH"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hasna/machines",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.31",
|
|
4
4
|
"description": "Machine fleet management CLI + MCP for developers",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -57,7 +57,7 @@
|
|
|
57
57
|
},
|
|
58
58
|
"repository": {
|
|
59
59
|
"type": "git",
|
|
60
|
-
"url": "https://github.com/hasna/machines.git"
|
|
60
|
+
"url": "git+https://github.com/hasna/machines.git"
|
|
61
61
|
},
|
|
62
62
|
"homepage": "https://github.com/hasna/machines",
|
|
63
63
|
"bugs": {
|
|
@@ -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",
|