@hasna/machines 0.0.20 → 0.0.22

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/consumer.js CHANGED
@@ -4317,7 +4317,8 @@ var MACHINES_CONSUMER_CAPABILITIES = {
4317
4317
  compatibility: true,
4318
4318
  route_resolution: true,
4319
4319
  cli_json_fallback: true,
4320
- workspace_path_mapping: true
4320
+ workspace_path_mapping: true,
4321
+ workspace_diagnostics: true
4321
4322
  };
4322
4323
  var MACHINES_CONSUMER_CONTRACT = {
4323
4324
  schema_version: MACHINES_CONSUMER_CONTRACT_VERSION,
@@ -4731,6 +4732,170 @@ function inferRepoRoot(workspaceRoot, repoName) {
4731
4732
  }
4732
4733
  return joinPath(root, repoName);
4733
4734
  }
4735
+ function shellQuote(value) {
4736
+ return `'${value.replace(/'/g, "'\\''")}'`;
4737
+ }
4738
+ function shellCommand(command) {
4739
+ return command.map(shellQuote).join(" ");
4740
+ }
4741
+ function canCheckPathForMachine(machine, localMachineId) {
4742
+ if (!machine)
4743
+ return false;
4744
+ if (machine.machine_id === localMachineId)
4745
+ return true;
4746
+ return machine.route_hints.some((hint) => hint.kind === "local");
4747
+ }
4748
+ function checkedPathExists(path, check) {
4749
+ if (!path || !check)
4750
+ return null;
4751
+ return existsSync4(path);
4752
+ }
4753
+ function repairHint(input) {
4754
+ const command = [
4755
+ "machines",
4756
+ "workspace",
4757
+ "repair",
4758
+ "--machine",
4759
+ input.machineId,
4760
+ "--project",
4761
+ input.projectId
4762
+ ];
4763
+ if (input.repoName)
4764
+ command.push("--repo", input.repoName);
4765
+ if (input.openFilesRepoName)
4766
+ command.push("--open-files-repo", input.openFilesRepoName);
4767
+ command.push("--json");
4768
+ const applyCommand = [...command.slice(0, -1), "--apply", "--json"];
4769
+ return {
4770
+ id: `repair:${input.machineId}:${input.projectId}`,
4771
+ reason: input.reason,
4772
+ command,
4773
+ shell_command: shellCommand(command),
4774
+ apply_command: applyCommand,
4775
+ apply_shell_command: shellCommand(applyCommand)
4776
+ };
4777
+ }
4778
+ function pathDiagnostic(input) {
4779
+ if (!input.path.path) {
4780
+ return {
4781
+ id: input.id,
4782
+ status: "missing",
4783
+ severity: input.required ? "fail" : "warn",
4784
+ message: `${input.label} is unresolved.`,
4785
+ path: null,
4786
+ source: input.path.source,
4787
+ path_exists: null
4788
+ };
4789
+ }
4790
+ if (input.pathExists === false) {
4791
+ return {
4792
+ id: input.id,
4793
+ status: "stale",
4794
+ severity: "fail",
4795
+ message: `${input.label} points to a path that does not exist on this machine.`,
4796
+ path: input.path.path,
4797
+ source: input.path.source,
4798
+ path_exists: false
4799
+ };
4800
+ }
4801
+ if (input.path.source === "inferred") {
4802
+ return {
4803
+ id: input.id,
4804
+ status: "inferred",
4805
+ severity: "warn",
4806
+ message: `${input.label} was inferred from the workspace root; write an explicit manifest mapping for repeatable downstream sync.`,
4807
+ path: input.path.path,
4808
+ source: input.path.source,
4809
+ path_exists: input.pathExists
4810
+ };
4811
+ }
4812
+ return {
4813
+ id: input.id,
4814
+ status: "ok",
4815
+ severity: "ok",
4816
+ message: `${input.label} is explicit enough for downstream sync.`,
4817
+ path: input.path.path,
4818
+ source: input.path.source,
4819
+ path_exists: input.pathExists
4820
+ };
4821
+ }
4822
+ function workspaceDiagnostics(input) {
4823
+ const checkPaths = canCheckPathForMachine(input.machine, input.localMachineId);
4824
+ const diagnostics = [];
4825
+ if (!input.machine) {
4826
+ diagnostics.push({
4827
+ id: "manifest",
4828
+ status: "missing_manifest",
4829
+ severity: "fail",
4830
+ message: "Machine is not present in topology or manifest.",
4831
+ path: null,
4832
+ source: "manifest",
4833
+ path_exists: null
4834
+ });
4835
+ } else if (!input.resolution.evidence.manifest_declared) {
4836
+ diagnostics.push({
4837
+ id: "manifest",
4838
+ status: "missing_manifest",
4839
+ severity: "warn",
4840
+ message: "Machine came from live topology but is not declared in the manifest.",
4841
+ path: null,
4842
+ source: "manifest",
4843
+ path_exists: null
4844
+ });
4845
+ }
4846
+ diagnostics.push(pathDiagnostic({
4847
+ id: "workspace_root",
4848
+ label: "Workspace root",
4849
+ path: input.resolution.paths.workspace_root,
4850
+ pathExists: checkedPathExists(input.resolution.paths.workspace_root.path, checkPaths),
4851
+ required: false
4852
+ }));
4853
+ diagnostics.push(pathDiagnostic({
4854
+ id: "project_root",
4855
+ label: "Project root",
4856
+ path: input.resolution.paths.project_root,
4857
+ pathExists: checkedPathExists(input.resolution.paths.project_root.path, checkPaths),
4858
+ required: true
4859
+ }));
4860
+ diagnostics.push(pathDiagnostic({
4861
+ id: "open_files_root",
4862
+ label: "Open-files root",
4863
+ path: input.resolution.paths.open_files_root,
4864
+ pathExists: checkedPathExists(input.resolution.paths.open_files_root.path, checkPaths),
4865
+ required: false
4866
+ }));
4867
+ if (input.resolution.machine.trust_status !== "trusted") {
4868
+ diagnostics.push({
4869
+ id: "trust",
4870
+ status: "untrusted",
4871
+ severity: "warn",
4872
+ message: `Machine trust status is ${input.resolution.machine.trust_status}; manifest repair apply requires trust or --allow-untrusted.`,
4873
+ path: null,
4874
+ source: "trust",
4875
+ path_exists: null
4876
+ });
4877
+ }
4878
+ if (input.resolution.machine.auth_status !== "authenticated") {
4879
+ diagnostics.push({
4880
+ id: "auth",
4881
+ status: "unknown_auth",
4882
+ severity: "warn",
4883
+ message: `Machine auth status is ${input.resolution.machine.auth_status}; remote sync may still fail if SSH is unavailable.`,
4884
+ path: null,
4885
+ source: "auth",
4886
+ path_exists: null
4887
+ });
4888
+ }
4889
+ const needsRepair = diagnostics.some((entry) => (entry.id === "project_root" || entry.id === "open_files_root") && (entry.status === "missing" || entry.status === "inferred" || entry.status === "stale"));
4890
+ const repairHints = needsRepair ? [repairHint({
4891
+ machineId: input.resolution.machine_id ?? input.resolution.requested_machine_id,
4892
+ projectId: input.resolution.project.project_id,
4893
+ repoName: input.resolution.project.repo_name,
4894
+ openFilesRepoName: input.openFilesRepoName,
4895
+ reason: "Write explicit workspace_paths and open_files_roots manifest metadata."
4896
+ })] : [];
4897
+ return { diagnostics, repairHints };
4898
+ }
4734
4899
  function projectPathFromMetadata(metadata, projectId, repoName) {
4735
4900
  const keys = [projectId, repoName].filter((value) => Boolean(value));
4736
4901
  return readMappedPath({
@@ -4806,7 +4971,7 @@ function resolveMachineWorkspace(options) {
4806
4971
  const openFilesRepoName = options.openFilesRepoName ?? "open-files";
4807
4972
  if (!machine) {
4808
4973
  warnings.push(`machine_not_found:${options.machineId}`);
4809
- return {
4974
+ const resolution2 = {
4810
4975
  schema_version: MACHINES_CONSUMER_CONTRACT_VERSION,
4811
4976
  package: topology.package,
4812
4977
  ok: false,
@@ -4820,6 +4985,8 @@ function resolveMachineWorkspace(options) {
4820
4985
  project_root: { path: null, source: "unresolved" },
4821
4986
  open_files_root: { path: null, source: "unresolved" }
4822
4987
  },
4988
+ diagnostics: [],
4989
+ repair_hints: [],
4823
4990
  evidence: {
4824
4991
  topology: true,
4825
4992
  matched_by: matchedBy,
@@ -4828,6 +4995,17 @@ function resolveMachineWorkspace(options) {
4828
4995
  },
4829
4996
  warnings
4830
4997
  };
4998
+ const diagnostics2 = workspaceDiagnostics({
4999
+ machine,
5000
+ localMachineId: topology.local_machine_id,
5001
+ resolution: resolution2,
5002
+ openFilesRepoName
5003
+ });
5004
+ return {
5005
+ ...resolution2,
5006
+ diagnostics: diagnostics2.diagnostics,
5007
+ repair_hints: diagnostics2.repairHints
5008
+ };
4831
5009
  }
4832
5010
  const metadata = machine.metadata;
4833
5011
  const workspaceRootPath = options.workspaceRoot ?? machine.workspace_path;
@@ -4846,7 +5024,7 @@ function resolveMachineWorkspace(options) {
4846
5024
  warnings.push(`open_files_root_inferred:${options.projectId}`);
4847
5025
  if (!projectRootPath)
4848
5026
  warnings.push(`project_root_unresolved:${options.projectId}`);
4849
- return {
5027
+ const resolution = {
4850
5028
  schema_version: MACHINES_CONSUMER_CONTRACT_VERSION,
4851
5029
  package: topology.package,
4852
5030
  ok: Boolean(projectRootPath),
@@ -4869,6 +5047,8 @@ function resolveMachineWorkspace(options) {
4869
5047
  project_root: { path: projectRootPath, source: projectRootSource },
4870
5048
  open_files_root: { path: openFilesRootPath, source: openFilesRootSource }
4871
5049
  },
5050
+ diagnostics: [],
5051
+ repair_hints: [],
4872
5052
  evidence: {
4873
5053
  topology: true,
4874
5054
  matched_by: matchedBy,
@@ -4877,6 +5057,17 @@ function resolveMachineWorkspace(options) {
4877
5057
  },
4878
5058
  warnings
4879
5059
  };
5060
+ const diagnostics = workspaceDiagnostics({
5061
+ machine,
5062
+ localMachineId: topology.local_machine_id,
5063
+ resolution,
5064
+ openFilesRepoName
5065
+ });
5066
+ return {
5067
+ ...resolution,
5068
+ diagnostics: diagnostics.diagnostics,
5069
+ repair_hints: diagnostics.repairHints
5070
+ };
4880
5071
  }
4881
5072
  function getLocalMachineTopology(options = {}) {
4882
5073
  const topology = discoverMachineTopology(options);
@@ -4902,7 +5093,7 @@ import { spawnSync as spawnSync2 } from "child_process";
4902
5093
  import { hostname as hostname4 } from "os";
4903
5094
 
4904
5095
  // src/commands/ssh.ts
4905
- function shellQuote(value) {
5096
+ function shellQuote2(value) {
4906
5097
  return `'${value.replace(/'/g, "'\\''")}'`;
4907
5098
  }
4908
5099
  function resolveSshTarget(machineId, options = {}) {
@@ -4923,11 +5114,11 @@ function resolveSshTarget(machineId, options = {}) {
4923
5114
  }
4924
5115
  function buildSshCommand(machineId, remoteCommand, options = {}) {
4925
5116
  const resolved = resolveSshTarget(machineId, options);
4926
- return remoteCommand ? `ssh ${resolved.target} ${shellQuote(remoteCommand)}` : `ssh ${resolved.target}`;
5117
+ return remoteCommand ? `ssh ${resolved.target} ${shellQuote2(remoteCommand)}` : `ssh ${resolved.target}`;
4927
5118
  }
4928
5119
 
4929
5120
  // src/remote.ts
4930
- function shellQuote2(value) {
5121
+ function shellQuote3(value) {
4931
5122
  return `'${value.replace(/'/g, "'\\''")}'`;
4932
5123
  }
4933
5124
  function machineIsLocal(machineId, localMachineId) {
@@ -4945,7 +5136,7 @@ function resolveMachineCommand(machineId, command, localMachineId = getLocalMach
4945
5136
  } catch (error) {
4946
5137
  const message = String(error.message ?? error);
4947
5138
  if (message.includes("Machine route not found") || message.includes("Machine not found in manifest")) {
4948
- return { source: "ssh", shellCommand: `ssh ${shellQuote2(machineId)} ${shellQuote2(command)}` };
5139
+ return { source: "ssh", shellCommand: `ssh ${shellQuote3(machineId)} ${shellQuote3(command)}` };
4949
5140
  }
4950
5141
  throw error;
4951
5142
  }
@@ -4973,7 +5164,7 @@ var DEFAULT_COMMANDS = [
4973
5164
  function defaultPackages() {
4974
5165
  return [{ name: "@hasna/machines", command: "machines", expectedVersion: getPackageVersion(), required: true }];
4975
5166
  }
4976
- function shellQuote3(value) {
5167
+ function shellQuote4(value) {
4977
5168
  return `'${value.replace(/'/g, "'\\''")}'`;
4978
5169
  }
4979
5170
  function commandId(value) {
@@ -5024,7 +5215,7 @@ function defaultRunner2(machineId, command) {
5024
5215
  return runMachineCommand(machineId, command);
5025
5216
  }
5026
5217
  function inspectCommand(machineId, spec, runner) {
5027
- const command = shellQuote3(spec.command);
5218
+ const command = shellQuote4(spec.command);
5028
5219
  const versionArgs = spec.versionArgs ?? "--version";
5029
5220
  const script = [
5030
5221
  `cmd=${command}`,
@@ -5053,7 +5244,7 @@ function fieldCommand(field) {
5053
5244
  }
5054
5245
  function inspectWorkspace(machineId, spec, runner) {
5055
5246
  const script = [
5056
- `path=${shellQuote3(spec.path)}`,
5247
+ `path=${shellQuote4(spec.path)}`,
5057
5248
  'printf "exists=%s\\n" "$(test -d "$path" && printf yes || printf no)"',
5058
5249
  'pkg="$path/package.json"',
5059
5250
  'printf "package_json=%s\\n" "$(test -f "$pkg" && printf yes || printf no)"',
package/dist/index.d.ts CHANGED
@@ -23,6 +23,7 @@ export * from "./commands/setup.js";
23
23
  export * from "./commands/ssh.js";
24
24
  export * from "./commands/sync.js";
25
25
  export * from "./commands/status.js";
26
+ export * from "./commands/workspace.js";
26
27
  export * from "./mcp/server.js";
27
28
  export { MACHINES_STORAGE_ENV, MACHINES_STORAGE_FALLBACK_ENV, MACHINES_STORAGE_MODE_ENV, MACHINES_STORAGE_MODE_FALLBACK_ENV, MACHINES_STORAGE_TABLES, STORAGE_DATABASE_ENV, STORAGE_MODE_ENV, STORAGE_TABLES, getStorageDatabaseEnv, getStorageDatabaseEnvName, getStorageDatabaseUrl, getStorageMode, getStoragePg, getStorageStatus, getSyncMetaAll, parseStorageTables, resolveTables, runStorageMigrations, storagePull, storagePush, storageSync, } from "./storage.js";
28
29
  export type { StorageEnv, StorageMode, StorageStatus, SyncMeta, SyncResult as StorageSyncResult } from "./storage.js";
@@ -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,oBAAoB,CAAC;AACnC,cAAc,sBAAsB,CAAC;AACrC,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,yBAAyB,CAAC;AACxC,cAAc,qBAAqB,CAAC;AACpC,cAAc,qBAAqB,CAAC;AACpC,cAAc,mBAAmB,CAAC;AAClC,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"}