@hasna/machines 0.0.30 → 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/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 = runMachineCommand(machine.id, buildAppProbeCommand(machine, app));
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: apps.length > 0 ? runMachineCommand(machine.id, "true").source : machine.id === detectCurrentMachineManifest().id ? "local" : runMachineCommand(machine.id, "true").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
- const result = Bun.spawnSync(["bash", "-lc", step.command], {
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 = runMachineCommand(machine.id, "true").source;
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) => parseProbe(tool, runMachineCommand(machine.id, buildProbeCommand(tool)).stdout))
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
- const result = Bun.spawnSync(["bash", "-lc", step.command], {
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
- const result = Bun.spawnSync(["bash", "-lc", step.command], {
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 = Bun.spawnSync(["bash", "-lc", step.command], {
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.toString()
6992
+ stderr: result.stderr,
6993
+ stdout: result.stdout,
6994
+ exitCode: result.exitCode,
6995
+ source: result.source
7003
6996
  });
7004
- throw new Error(`Setup step failed (${step.id}): ${result.stderr.toString().trim()}`);
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 ${quote4(packageName)}`;
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 ${quote4(packageName)}`;
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 ${quote4(packageName)} >/dev/null 2>&1`;
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 ${quote4(packageName)} >/dev/null 2>&1`;
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 = Bun.spawnSync(["bash", "-lc", packageCheckCommand(machine, pkg.name, manager)], {
7052
- stdout: "ignore",
7053
- stderr: "ignore",
7054
- env: process.env
7055
- });
7056
- const installed = check2.exitCode === 0;
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 = Bun.spawnSync(["bash", "-lc", action.command], {
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.toString()
7147
+ stderr: result.stderr,
7148
+ stdout: result.stdout,
7149
+ exitCode: result.exitCode,
7150
+ source: result.source
7155
7151
  });
7156
- throw new Error(`Sync action failed (${action.id}): ${result.stderr.toString().trim()}`);
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
@@ -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;AAaD,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"}
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.30",
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": {