@hasna/machines 0.0.17 → 0.0.19
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 +39 -0
- package/dist/cli/index.js +310 -19
- package/dist/commands/backup.d.ts +17 -2
- package/dist/commands/backup.d.ts.map +1 -1
- package/dist/commands/ssh.d.ts.map +1 -1
- package/dist/consumer.d.ts +2 -2
- package/dist/consumer.d.ts.map +1 -1
- package/dist/consumer.js +222 -7
- package/dist/index.js +318 -16
- package/dist/manifests.d.ts +8 -0
- package/dist/manifests.d.ts.map +1 -1
- package/dist/mcp/index.js +314 -20
- package/dist/mcp/server.d.ts +1 -1
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/topology.d.ts +52 -0
- package/dist/topology.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/consumer.js
CHANGED
|
@@ -4212,6 +4212,7 @@ var machineSchema = exports_external.object({
|
|
|
4212
4212
|
workspacePath: exports_external.string(),
|
|
4213
4213
|
bunPath: exports_external.string().optional(),
|
|
4214
4214
|
tags: exports_external.array(exports_external.string()).optional(),
|
|
4215
|
+
metadata: exports_external.record(exports_external.unknown()).optional(),
|
|
4215
4216
|
packages: exports_external.array(packageSchema).optional(),
|
|
4216
4217
|
apps: exports_external.array(appSchema).optional(),
|
|
4217
4218
|
files: exports_external.array(fileSchema).optional()
|
|
@@ -4514,7 +4515,8 @@ function discoverMachineTopology(options = {}) {
|
|
|
4514
4515
|
topology: true,
|
|
4515
4516
|
compatibility: true,
|
|
4516
4517
|
route_resolution: true,
|
|
4517
|
-
cli_json_fallback: true
|
|
4518
|
+
cli_json_fallback: true,
|
|
4519
|
+
workspace_path_mapping: true
|
|
4518
4520
|
},
|
|
4519
4521
|
generated_at: now.toISOString(),
|
|
4520
4522
|
local_machine_id: localMachineId,
|
|
@@ -4639,6 +4641,215 @@ function resolveMachineRoute(machineId, options = {}) {
|
|
|
4639
4641
|
warnings
|
|
4640
4642
|
};
|
|
4641
4643
|
}
|
|
4644
|
+
function isRecord(value) {
|
|
4645
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
4646
|
+
}
|
|
4647
|
+
function metadataString(metadata, keys) {
|
|
4648
|
+
for (const key of keys) {
|
|
4649
|
+
const value = metadata[key];
|
|
4650
|
+
if (typeof value === "string" && value.trim())
|
|
4651
|
+
return value.trim();
|
|
4652
|
+
}
|
|
4653
|
+
return null;
|
|
4654
|
+
}
|
|
4655
|
+
function metadataBoolean(metadata, keys) {
|
|
4656
|
+
for (const key of keys) {
|
|
4657
|
+
const value = metadata[key];
|
|
4658
|
+
if (typeof value === "boolean")
|
|
4659
|
+
return value;
|
|
4660
|
+
}
|
|
4661
|
+
return null;
|
|
4662
|
+
}
|
|
4663
|
+
function metadataStringArray(metadata, keys) {
|
|
4664
|
+
for (const key of keys) {
|
|
4665
|
+
const value = metadata[key];
|
|
4666
|
+
if (Array.isArray(value))
|
|
4667
|
+
return value.filter((entry) => typeof entry === "string");
|
|
4668
|
+
}
|
|
4669
|
+
return [];
|
|
4670
|
+
}
|
|
4671
|
+
function readMappedPath(input) {
|
|
4672
|
+
for (const containerName of input.containers) {
|
|
4673
|
+
const container = input.metadata[containerName];
|
|
4674
|
+
if (!isRecord(container))
|
|
4675
|
+
continue;
|
|
4676
|
+
for (const key of input.keys) {
|
|
4677
|
+
const value = container[key];
|
|
4678
|
+
if (typeof value === "string" && value.trim())
|
|
4679
|
+
return value.trim();
|
|
4680
|
+
if (isRecord(value)) {
|
|
4681
|
+
const path = metadataString(value, ["path", "root", "workspacePath", "workspace_path"]);
|
|
4682
|
+
if (path)
|
|
4683
|
+
return path;
|
|
4684
|
+
}
|
|
4685
|
+
}
|
|
4686
|
+
}
|
|
4687
|
+
return null;
|
|
4688
|
+
}
|
|
4689
|
+
function trimTrailingSlash(value) {
|
|
4690
|
+
return value.replace(/\/+$/, "");
|
|
4691
|
+
}
|
|
4692
|
+
function joinPath(left, right) {
|
|
4693
|
+
return `${trimTrailingSlash(left)}/${right.replace(/^\/+/, "")}`;
|
|
4694
|
+
}
|
|
4695
|
+
function inferRepoRoot(workspaceRoot, repoName) {
|
|
4696
|
+
if (!workspaceRoot || !repoName)
|
|
4697
|
+
return null;
|
|
4698
|
+
const root = trimTrailingSlash(workspaceRoot);
|
|
4699
|
+
if (root.endsWith(`/${repoName}`) || root === repoName)
|
|
4700
|
+
return root;
|
|
4701
|
+
if (root.endsWith("/workspace") || root.endsWith("/Workspace")) {
|
|
4702
|
+
return joinPath(root, `hasna/opensource/${repoName}`);
|
|
4703
|
+
}
|
|
4704
|
+
return joinPath(root, repoName);
|
|
4705
|
+
}
|
|
4706
|
+
function projectPathFromMetadata(metadata, projectId, repoName) {
|
|
4707
|
+
const keys = [projectId, repoName].filter((value) => Boolean(value));
|
|
4708
|
+
return readMappedPath({
|
|
4709
|
+
metadata,
|
|
4710
|
+
containers: ["workspace_paths", "workspacePaths", "repo_paths", "repoPaths", "project_paths", "projectPaths", "projects"],
|
|
4711
|
+
keys
|
|
4712
|
+
});
|
|
4713
|
+
}
|
|
4714
|
+
function openFilesPathFromMetadata(metadata, projectId, repoName) {
|
|
4715
|
+
const direct = metadataString(metadata, ["open_files_root", "openFilesRoot", "open_files_path", "openFilesPath"]);
|
|
4716
|
+
if (direct)
|
|
4717
|
+
return direct;
|
|
4718
|
+
const keys = [projectId, repoName, "open-files", "open_files", "default"].filter((value) => Boolean(value));
|
|
4719
|
+
return readMappedPath({
|
|
4720
|
+
metadata,
|
|
4721
|
+
containers: ["open_files_roots", "openFilesRoots", "open_files_paths", "openFilesPaths"],
|
|
4722
|
+
keys
|
|
4723
|
+
});
|
|
4724
|
+
}
|
|
4725
|
+
function trustStatus(machine) {
|
|
4726
|
+
if (!machine)
|
|
4727
|
+
return "unknown";
|
|
4728
|
+
const explicit = metadataString(machine.metadata, ["trust_status", "trustStatus"]);
|
|
4729
|
+
if (explicit === "trusted" || explicit === "untrusted" || explicit === "unknown")
|
|
4730
|
+
return explicit;
|
|
4731
|
+
const trusted = metadataBoolean(machine.metadata, ["trusted", "syncTrusted", "sync_trusted"]);
|
|
4732
|
+
if (trusted === true)
|
|
4733
|
+
return "trusted";
|
|
4734
|
+
if (trusted === false)
|
|
4735
|
+
return "untrusted";
|
|
4736
|
+
if (machine.route_hints.some((hint) => hint.kind === "local"))
|
|
4737
|
+
return "trusted";
|
|
4738
|
+
if (machine.tags.includes("trusted"))
|
|
4739
|
+
return "trusted";
|
|
4740
|
+
return "unknown";
|
|
4741
|
+
}
|
|
4742
|
+
function authStatus(machine) {
|
|
4743
|
+
if (!machine)
|
|
4744
|
+
return "unknown";
|
|
4745
|
+
const explicit = metadataString(machine.metadata, ["auth_status", "authStatus"]);
|
|
4746
|
+
if (explicit === "authenticated" || explicit === "unauthenticated" || explicit === "unknown")
|
|
4747
|
+
return explicit;
|
|
4748
|
+
const authenticated = metadataBoolean(machine.metadata, ["authenticated", "sshAuthorized", "ssh_authorized"]);
|
|
4749
|
+
if (authenticated === true)
|
|
4750
|
+
return "authenticated";
|
|
4751
|
+
if (authenticated === false)
|
|
4752
|
+
return "unauthenticated";
|
|
4753
|
+
if (machine.route_hints.some((hint) => hint.kind === "local"))
|
|
4754
|
+
return "authenticated";
|
|
4755
|
+
return "unknown";
|
|
4756
|
+
}
|
|
4757
|
+
function primaryMachine(machine, projectId, primaryMachineId) {
|
|
4758
|
+
if (!machine)
|
|
4759
|
+
return false;
|
|
4760
|
+
if (primaryMachineId)
|
|
4761
|
+
return machine.machine_id === primaryMachineId;
|
|
4762
|
+
if (metadataBoolean(machine.metadata, ["primary", "primary_machine", "primaryMachine"]) === true)
|
|
4763
|
+
return true;
|
|
4764
|
+
const primaryProjects = metadataStringArray(machine.metadata, ["primary_projects", "primaryProjects"]);
|
|
4765
|
+
if (primaryProjects.includes(projectId))
|
|
4766
|
+
return true;
|
|
4767
|
+
return machine.tags.includes("primary");
|
|
4768
|
+
}
|
|
4769
|
+
function metadataKeysForDiagnostics(metadata) {
|
|
4770
|
+
return Object.keys(metadata).filter((key) => !/(secret|token|key|password|credential)/i.test(key)).sort();
|
|
4771
|
+
}
|
|
4772
|
+
function resolveMachineWorkspace(options) {
|
|
4773
|
+
const topology = options.topology ?? discoverMachineTopology(options);
|
|
4774
|
+
const warnings = [...topology.warnings];
|
|
4775
|
+
const { machine, matchedBy } = findRouteMachine(topology, options.machineId);
|
|
4776
|
+
const generatedAt = (options.now ?? new Date).toISOString();
|
|
4777
|
+
const repoName = options.repoName ?? options.projectId;
|
|
4778
|
+
const openFilesRepoName = options.openFilesRepoName ?? "open-files";
|
|
4779
|
+
if (!machine) {
|
|
4780
|
+
warnings.push(`machine_not_found:${options.machineId}`);
|
|
4781
|
+
return {
|
|
4782
|
+
schema_version: MACHINES_CONSUMER_CONTRACT_VERSION,
|
|
4783
|
+
package: topology.package,
|
|
4784
|
+
ok: false,
|
|
4785
|
+
requested_machine_id: options.machineId,
|
|
4786
|
+
machine_id: null,
|
|
4787
|
+
generated_at: generatedAt,
|
|
4788
|
+
project: { project_id: options.projectId, repo_name: repoName, canonical: Boolean(options.projectId) },
|
|
4789
|
+
machine: { current: false, primary: false, trust_status: "unknown", auth_status: "unknown" },
|
|
4790
|
+
paths: {
|
|
4791
|
+
workspace_root: { path: null, source: "unresolved" },
|
|
4792
|
+
project_root: { path: null, source: "unresolved" },
|
|
4793
|
+
open_files_root: { path: null, source: "unresolved" }
|
|
4794
|
+
},
|
|
4795
|
+
evidence: {
|
|
4796
|
+
topology: true,
|
|
4797
|
+
matched_by: matchedBy,
|
|
4798
|
+
manifest_declared: null,
|
|
4799
|
+
metadata_keys: []
|
|
4800
|
+
},
|
|
4801
|
+
warnings
|
|
4802
|
+
};
|
|
4803
|
+
}
|
|
4804
|
+
const metadata = machine.metadata;
|
|
4805
|
+
const workspaceRootPath = options.workspaceRoot ?? machine.workspace_path;
|
|
4806
|
+
const workspaceRootSource = options.workspaceRoot ? "argument" : machine.workspace_path ? "manifest" : "unresolved";
|
|
4807
|
+
const metadataProjectRoot = projectPathFromMetadata(metadata, options.projectId, repoName);
|
|
4808
|
+
const inferredProjectRoot = inferRepoRoot(workspaceRootPath, repoName);
|
|
4809
|
+
const projectRootPath = options.projectRoot ?? metadataProjectRoot ?? inferredProjectRoot;
|
|
4810
|
+
const projectRootSource = options.projectRoot ? "argument" : metadataProjectRoot ? "manifest_metadata" : inferredProjectRoot ? "inferred" : "unresolved";
|
|
4811
|
+
const metadataOpenFilesRoot = openFilesPathFromMetadata(metadata, options.projectId, openFilesRepoName);
|
|
4812
|
+
const inferredOpenFilesRoot = inferRepoRoot(workspaceRootPath, openFilesRepoName);
|
|
4813
|
+
const openFilesRootPath = options.openFilesRoot ?? metadataOpenFilesRoot ?? inferredOpenFilesRoot;
|
|
4814
|
+
const openFilesRootSource = options.openFilesRoot ? "argument" : metadataOpenFilesRoot ? "manifest_metadata" : inferredOpenFilesRoot ? "inferred" : "unresolved";
|
|
4815
|
+
if (projectRootSource === "inferred")
|
|
4816
|
+
warnings.push(`project_root_inferred:${options.projectId}`);
|
|
4817
|
+
if (openFilesRootSource === "inferred")
|
|
4818
|
+
warnings.push(`open_files_root_inferred:${options.projectId}`);
|
|
4819
|
+
if (!projectRootPath)
|
|
4820
|
+
warnings.push(`project_root_unresolved:${options.projectId}`);
|
|
4821
|
+
return {
|
|
4822
|
+
schema_version: MACHINES_CONSUMER_CONTRACT_VERSION,
|
|
4823
|
+
package: topology.package,
|
|
4824
|
+
ok: Boolean(projectRootPath),
|
|
4825
|
+
requested_machine_id: options.machineId,
|
|
4826
|
+
machine_id: machine.machine_id,
|
|
4827
|
+
generated_at: generatedAt,
|
|
4828
|
+
project: {
|
|
4829
|
+
project_id: options.projectId,
|
|
4830
|
+
repo_name: repoName,
|
|
4831
|
+
canonical: Boolean(options.projectId && repoName)
|
|
4832
|
+
},
|
|
4833
|
+
machine: {
|
|
4834
|
+
current: machine.machine_id === topology.local_machine_id,
|
|
4835
|
+
primary: primaryMachine(machine, options.projectId, options.primaryMachineId),
|
|
4836
|
+
trust_status: trustStatus(machine),
|
|
4837
|
+
auth_status: authStatus(machine)
|
|
4838
|
+
},
|
|
4839
|
+
paths: {
|
|
4840
|
+
workspace_root: { path: workspaceRootPath, source: workspaceRootSource },
|
|
4841
|
+
project_root: { path: projectRootPath, source: projectRootSource },
|
|
4842
|
+
open_files_root: { path: openFilesRootPath, source: openFilesRootSource }
|
|
4843
|
+
},
|
|
4844
|
+
evidence: {
|
|
4845
|
+
topology: true,
|
|
4846
|
+
matched_by: matchedBy,
|
|
4847
|
+
manifest_declared: machine.manifest_declared,
|
|
4848
|
+
metadata_keys: metadataKeysForDiagnostics(metadata)
|
|
4849
|
+
},
|
|
4850
|
+
warnings
|
|
4851
|
+
};
|
|
4852
|
+
}
|
|
4642
4853
|
function getLocalMachineTopology(options = {}) {
|
|
4643
4854
|
const topology = discoverMachineTopology(options);
|
|
4644
4855
|
return topology.machines.find((machine) => machine.machine_id === topology.local_machine_id) ?? {
|
|
@@ -4663,6 +4874,9 @@ import { spawnSync as spawnSync2 } from "child_process";
|
|
|
4663
4874
|
import { hostname as hostname4 } from "os";
|
|
4664
4875
|
|
|
4665
4876
|
// src/commands/ssh.ts
|
|
4877
|
+
function shellQuote(value) {
|
|
4878
|
+
return `'${value.replace(/'/g, "'\\''")}'`;
|
|
4879
|
+
}
|
|
4666
4880
|
function resolveSshTarget(machineId, options = {}) {
|
|
4667
4881
|
const resolved = resolveMachineRoute(machineId, options);
|
|
4668
4882
|
if (!resolved.ok || !resolved.target) {
|
|
@@ -4681,11 +4895,11 @@ function resolveSshTarget(machineId, options = {}) {
|
|
|
4681
4895
|
}
|
|
4682
4896
|
function buildSshCommand(machineId, remoteCommand, options = {}) {
|
|
4683
4897
|
const resolved = resolveSshTarget(machineId, options);
|
|
4684
|
-
return remoteCommand ? `ssh ${resolved.target} ${
|
|
4898
|
+
return remoteCommand ? `ssh ${resolved.target} ${shellQuote(remoteCommand)}` : `ssh ${resolved.target}`;
|
|
4685
4899
|
}
|
|
4686
4900
|
|
|
4687
4901
|
// src/remote.ts
|
|
4688
|
-
function
|
|
4902
|
+
function shellQuote2(value) {
|
|
4689
4903
|
return `'${value.replace(/'/g, "'\\''")}'`;
|
|
4690
4904
|
}
|
|
4691
4905
|
function machineIsLocal(machineId, localMachineId) {
|
|
@@ -4703,7 +4917,7 @@ function resolveMachineCommand(machineId, command, localMachineId = getLocalMach
|
|
|
4703
4917
|
} catch (error) {
|
|
4704
4918
|
const message = String(error.message ?? error);
|
|
4705
4919
|
if (message.includes("Machine route not found") || message.includes("Machine not found in manifest")) {
|
|
4706
|
-
return { source: "ssh", shellCommand: `ssh ${
|
|
4920
|
+
return { source: "ssh", shellCommand: `ssh ${shellQuote2(machineId)} ${shellQuote2(command)}` };
|
|
4707
4921
|
}
|
|
4708
4922
|
throw error;
|
|
4709
4923
|
}
|
|
@@ -4731,7 +4945,7 @@ var DEFAULT_COMMANDS = [
|
|
|
4731
4945
|
function defaultPackages() {
|
|
4732
4946
|
return [{ name: "@hasna/machines", command: "machines", expectedVersion: getPackageVersion(), required: true }];
|
|
4733
4947
|
}
|
|
4734
|
-
function
|
|
4948
|
+
function shellQuote3(value) {
|
|
4735
4949
|
return `'${value.replace(/'/g, "'\\''")}'`;
|
|
4736
4950
|
}
|
|
4737
4951
|
function commandId(value) {
|
|
@@ -4782,7 +4996,7 @@ function defaultRunner2(machineId, command) {
|
|
|
4782
4996
|
return runMachineCommand(machineId, command);
|
|
4783
4997
|
}
|
|
4784
4998
|
function inspectCommand(machineId, spec, runner) {
|
|
4785
|
-
const command =
|
|
4999
|
+
const command = shellQuote3(spec.command);
|
|
4786
5000
|
const versionArgs = spec.versionArgs ?? "--version";
|
|
4787
5001
|
const script = [
|
|
4788
5002
|
`cmd=${command}`,
|
|
@@ -4811,7 +5025,7 @@ function fieldCommand(field) {
|
|
|
4811
5025
|
}
|
|
4812
5026
|
function inspectWorkspace(machineId, spec, runner) {
|
|
4813
5027
|
const script = [
|
|
4814
|
-
`path=${
|
|
5028
|
+
`path=${shellQuote3(spec.path)}`,
|
|
4815
5029
|
'printf "exists=%s\\n" "$(test -d "$path" && printf yes || printf no)"',
|
|
4816
5030
|
'pkg="$path/package.json"',
|
|
4817
5031
|
'printf "package_json=%s\\n" "$(test -f "$pkg" && printf yes || printf no)"',
|
|
@@ -4971,6 +5185,7 @@ function checkMachineCompatibility(options = {}) {
|
|
|
4971
5185
|
export {
|
|
4972
5186
|
runMachineCommand,
|
|
4973
5187
|
resolveSshTarget,
|
|
5188
|
+
resolveMachineWorkspace,
|
|
4974
5189
|
resolveMachineRoute,
|
|
4975
5190
|
resolveMachineCommand,
|
|
4976
5191
|
getPackageVersion,
|