@hasna/machines 0.0.33 → 0.0.35

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 CHANGED
@@ -7092,6 +7092,104 @@ var coerce = {
7092
7092
  var NEVER = INVALID;
7093
7093
  // src/manifests.ts
7094
7094
  init_paths();
7095
+
7096
+ // src/redaction.ts
7097
+ var REDACTED_VALUE = "[redacted]";
7098
+ var SENSITIVE_KEY_PATTERN = /(password|passwd|token|credential|private[_-]?key|privateKey|api[_-]?key|github.*key|pem|secret)/i;
7099
+ var SECRET_REFERENCE_KEY_PATTERN = /(secret(ref(erence)?|key)?|secretRef|secretKey)$/i;
7100
+ var SENSITIVE_VALUE_PATTERNS = [
7101
+ /-----BEGIN [A-Z ]*PRIVATE KEY-----/,
7102
+ /\bghp_[A-Za-z0-9_]{20,}\b/,
7103
+ /\bgithub_pat_[A-Za-z0-9_]{20,}\b/,
7104
+ /\bxox[baprs]-[A-Za-z0-9-]{20,}\b/,
7105
+ /\bAKIA[0-9A-Z]{16}\b/,
7106
+ /\bsk-[A-Za-z0-9_-]{20,}\b/
7107
+ ];
7108
+ function isSensitiveKey(key) {
7109
+ return SENSITIVE_KEY_PATTERN.test(key);
7110
+ }
7111
+ function isSecretReferenceKey(key) {
7112
+ return SECRET_REFERENCE_KEY_PATTERN.test(key);
7113
+ }
7114
+ function looksSensitiveString(value) {
7115
+ return SENSITIVE_VALUE_PATTERNS.some((pattern) => pattern.test(value));
7116
+ }
7117
+ function isRecord(value) {
7118
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
7119
+ }
7120
+ function redactPath(value) {
7121
+ return value.replace(/\/home\/[^/\s]+/g, "/home/<user>").replace(/\/Users\/[^/\s]+/g, "/Users/<user>").replace(/[A-Za-z]:\\Users\\[^\\\s]+/g, "C:\\Users\\<user>");
7122
+ }
7123
+ function redactPrivateRef(value) {
7124
+ const trimmed = value.trim();
7125
+ const scheme = trimmed.match(/^([a-z][a-z0-9+.-]*:\/\/)/i);
7126
+ if (scheme)
7127
+ return `${scheme[1]}<redacted>`;
7128
+ const colon = trimmed.match(/^([a-z][a-z0-9+.-]*):/i);
7129
+ if (colon)
7130
+ return `${colon[1]}:<redacted>`;
7131
+ return "<private-manifest-ref:redacted>";
7132
+ }
7133
+ function redactIdentifier(value) {
7134
+ return value.replace(/[^a-zA-Z0-9_.-]/g, "_").slice(0, 80) || "adapter";
7135
+ }
7136
+ function redactSensitiveValue(value, key = "") {
7137
+ if (typeof value === "string") {
7138
+ if (isSensitiveKey(key) && !(isSecretReferenceKey(key) && !looksSensitiveString(value))) {
7139
+ return REDACTED_VALUE;
7140
+ }
7141
+ if (looksSensitiveString(value))
7142
+ return REDACTED_VALUE;
7143
+ return redactPath(value);
7144
+ }
7145
+ if (Array.isArray(value)) {
7146
+ return value.map((entry) => redactSensitiveValue(entry, key));
7147
+ }
7148
+ if (isRecord(value)) {
7149
+ const redacted = {};
7150
+ for (const [entryKey, entryValue] of Object.entries(value)) {
7151
+ redacted[entryKey] = redactSensitiveValue(entryValue, entryKey);
7152
+ }
7153
+ return redacted;
7154
+ }
7155
+ return value;
7156
+ }
7157
+ function publicMetadataKeys(metadata) {
7158
+ return Object.keys(metadata ?? {}).filter((key) => !isSensitiveKey(key)).sort();
7159
+ }
7160
+ function redactMetadata(metadata) {
7161
+ return redactSensitiveValue(metadata ?? {});
7162
+ }
7163
+ function redactManifestForDiagnostics(machine) {
7164
+ const metadata = redactMetadata(machine.metadata);
7165
+ for (const key of ["user", "username", "login"]) {
7166
+ if (typeof metadata[key] === "string")
7167
+ metadata[key] = REDACTED_VALUE;
7168
+ }
7169
+ return {
7170
+ id: machine.id,
7171
+ hostname: machine.hostname ? REDACTED_VALUE : undefined,
7172
+ sshAddress: machine.sshAddress ? REDACTED_VALUE : undefined,
7173
+ tailscaleName: machine.tailscaleName ? REDACTED_VALUE : undefined,
7174
+ platform: machine.platform,
7175
+ connection: machine.connection,
7176
+ workspacePath: redactPath(machine.workspacePath),
7177
+ bunPath: machine.bunPath ? redactPath(machine.bunPath) : undefined,
7178
+ tags: machine.tags ?? [],
7179
+ metadata,
7180
+ packages: machine.packages?.map((pkg) => ({ ...pkg })),
7181
+ apps: machine.apps?.map((app) => ({ ...app })),
7182
+ files: machine.files?.map((file) => ({
7183
+ ...file,
7184
+ source: redactPath(file.source),
7185
+ target: redactPath(file.target)
7186
+ }))
7187
+ };
7188
+ }
7189
+
7190
+ // src/manifests.ts
7191
+ var PRIVATE_MANIFEST_REF_ENV = "HASNA_MACHINES_PRIVATE_MANIFEST_REF";
7192
+ var PRIVATE_MANIFEST_BACKEND_ENV = "HASNA_MACHINES_PRIVATE_MANIFEST_BACKEND";
7095
7193
  var packageSchema = exports_external.object({
7096
7194
  name: exports_external.string(),
7097
7195
  manager: exports_external.enum(["bun", "brew", "apt", "custom"]).optional(),
@@ -7144,6 +7242,42 @@ function normalizePlatform() {
7144
7242
  function normalizeMachines(machines) {
7145
7243
  return [...machines].sort((left, right) => left.id.localeCompare(right.id));
7146
7244
  }
7245
+ function inferPrivateBackend(rawRef, explicitBackend) {
7246
+ if (explicitBackend?.trim())
7247
+ return explicitBackend.trim();
7248
+ const scheme = rawRef.trim().match(/^([a-z][a-z0-9+.-]*)(?::\/\/|:)/i);
7249
+ return scheme?.[1] ?? null;
7250
+ }
7251
+ function fileSourceRef(path) {
7252
+ return {
7253
+ kind: "file",
7254
+ ref: redactPath(path),
7255
+ backend: "file",
7256
+ private: false,
7257
+ publicSafe: true
7258
+ };
7259
+ }
7260
+ function privateSourceRef(rawRef, backend) {
7261
+ return {
7262
+ kind: "private-ref",
7263
+ ref: redactPrivateRef(rawRef),
7264
+ backend: inferPrivateBackend(rawRef, backend),
7265
+ private: true,
7266
+ publicSafe: true
7267
+ };
7268
+ }
7269
+ function privateRefFromOptions(options) {
7270
+ const env2 = options.env ?? process.env;
7271
+ return options.privateRef?.trim() || env2[PRIVATE_MANIFEST_REF_ENV]?.trim() || env2["MACHINES_PRIVATE_MANIFEST_REF"]?.trim() || null;
7272
+ }
7273
+ function getManifestSourceRef(options = {}) {
7274
+ const rawPrivateRef = privateRefFromOptions(options);
7275
+ if (rawPrivateRef) {
7276
+ const env2 = options.env ?? process.env;
7277
+ return privateSourceRef(rawPrivateRef, options.privateBackend ?? env2[PRIVATE_MANIFEST_BACKEND_ENV]);
7278
+ }
7279
+ return fileSourceRef(options.path ?? getManifestPath());
7280
+ }
7147
7281
  function getDefaultManifest() {
7148
7282
  return {
7149
7283
  version: 1,
@@ -7158,6 +7292,53 @@ function readManifest(path = getManifestPath()) {
7158
7292
  const raw = JSON.parse(readFileSync2(path, "utf8"));
7159
7293
  return fleetSchema.parse(raw);
7160
7294
  }
7295
+ function readManifestWithSource(options = {}) {
7296
+ const path = options.path ?? getManifestPath();
7297
+ const source = getManifestSourceRef(options);
7298
+ const warnings = [];
7299
+ if (source.kind === "private-ref") {
7300
+ const rawRef = privateRefFromOptions(options);
7301
+ if (rawRef && options.adapter) {
7302
+ try {
7303
+ const manifest2 = options.adapter.readManifest({ source, rawRef });
7304
+ if (manifest2) {
7305
+ return {
7306
+ manifest: fleetSchema.parse(manifest2),
7307
+ info: {
7308
+ source,
7309
+ loadedFrom: "private-ref",
7310
+ warnings
7311
+ }
7312
+ };
7313
+ }
7314
+ warnings.push(`private_manifest_adapter_empty:${redactIdentifier(options.adapter.id)}`);
7315
+ } catch (error) {
7316
+ warnings.push(`private_manifest_adapter_failed:${redactIdentifier(options.adapter.id)}`);
7317
+ }
7318
+ } else {
7319
+ warnings.push("private_manifest_ref_without_adapter");
7320
+ }
7321
+ const fallbackSource = fileSourceRef(path);
7322
+ const manifest = readManifest(path);
7323
+ return {
7324
+ manifest,
7325
+ info: {
7326
+ source,
7327
+ loadedFrom: existsSync3(path) ? "fallback" : "default",
7328
+ fallbackSource,
7329
+ warnings
7330
+ }
7331
+ };
7332
+ }
7333
+ return {
7334
+ manifest: readManifest(path),
7335
+ info: {
7336
+ source,
7337
+ loadedFrom: existsSync3(path) ? "file" : "default",
7338
+ warnings
7339
+ }
7340
+ };
7341
+ }
7161
7342
  function validateManifest(path = getManifestPath()) {
7162
7343
  return readManifest(path);
7163
7344
  }
@@ -7356,6 +7537,19 @@ function manifestHostReachable(target) {
7356
7537
  return null;
7357
7538
  return overrides.has(target);
7358
7539
  }
7540
+ function userFromSshAddress(address) {
7541
+ if (!address)
7542
+ return null;
7543
+ const at = address.indexOf("@");
7544
+ if (at <= 0)
7545
+ return null;
7546
+ return address.slice(0, at);
7547
+ }
7548
+ function commandTargetForRoute(target, user) {
7549
+ if (target.kind === "local" || target.target.includes("@") || !user)
7550
+ return target.target;
7551
+ return `${user}@${target.target}`;
7552
+ }
7359
7553
  function routeHints(input) {
7360
7554
  const hints = [];
7361
7555
  if (input.machineId === input.localMachineId) {
@@ -7406,6 +7600,7 @@ function buildEntry(input) {
7406
7600
  });
7407
7601
  const selectedRoute = selectRouteHint(hints);
7408
7602
  const route = selectedRoute?.kind === "ssh" ? "ssh" : selectedRoute?.kind ?? "unknown";
7603
+ const routeUser = userFromSshAddress(manifest?.sshAddress) ?? (typeof manifest?.metadata?.user === "string" ? manifest.metadata.user : null);
7409
7604
  return {
7410
7605
  machine_id: input.machineId,
7411
7606
  hostname: manifest?.hostname ?? peer?.HostName ?? null,
@@ -7426,11 +7621,11 @@ function buildEntry(input) {
7426
7621
  ssh: {
7427
7622
  address: manifest?.sshAddress ?? null,
7428
7623
  route,
7429
- command_target: selectedRoute?.target ?? null
7624
+ command_target: selectedRoute ? commandTargetForRoute(selectedRoute, routeUser) : null
7430
7625
  },
7431
7626
  route_hints: hints,
7432
7627
  tags: manifest?.tags ?? [],
7433
- metadata: manifest?.metadata ?? {}
7628
+ metadata: redactMetadata(manifest?.metadata)
7434
7629
  };
7435
7630
  }
7436
7631
  function discoverMachineTopology(options = {}) {
@@ -7634,6 +7829,7 @@ function resolveMachineRoute(machineId, options = {}) {
7634
7829
  const local = route === "local" || machine.machine_id === topology.local_machine_id;
7635
7830
  const confidence = routeConfidence({ machine, hint: selectedHint, matchedBy });
7636
7831
  const ok = Boolean(selectedHint?.target);
7832
+ const commandTarget = selectedHint ? commandTargetForRoute(selectedHint, userFromSshAddress(machine.ssh.address) ?? machine.user) : null;
7637
7833
  return {
7638
7834
  schema_version: MACHINES_CONSUMER_CONTRACT_VERSION,
7639
7835
  package: topology.package,
@@ -7644,7 +7840,7 @@ function resolveMachineRoute(machineId, options = {}) {
7644
7840
  route,
7645
7841
  source: route,
7646
7842
  target: selectedHint?.target ?? null,
7647
- command_target: selectedHint?.target ?? null,
7843
+ command_target: commandTarget,
7648
7844
  confidence,
7649
7845
  local,
7650
7846
  evidence: {
@@ -7667,7 +7863,7 @@ function resolveMachineRoute(machineId, options = {}) {
7667
7863
  warnings
7668
7864
  };
7669
7865
  }
7670
- function isRecord(value) {
7866
+ function isRecord2(value) {
7671
7867
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
7672
7868
  }
7673
7869
  function metadataString(metadata, keys) {
@@ -7697,13 +7893,13 @@ function metadataStringArray(metadata, keys) {
7697
7893
  function readMappedPath(input) {
7698
7894
  for (const containerName of input.containers) {
7699
7895
  const container = input.metadata[containerName];
7700
- if (!isRecord(container))
7896
+ if (!isRecord2(container))
7701
7897
  continue;
7702
7898
  for (const key of input.keys) {
7703
7899
  const value = container[key];
7704
7900
  if (typeof value === "string" && value.trim())
7705
7901
  return value.trim();
7706
- if (isRecord(value)) {
7902
+ if (isRecord2(value)) {
7707
7903
  const path = metadataString(value, ["path", "root", "workspacePath", "workspace_path"]);
7708
7904
  if (path)
7709
7905
  return path;
@@ -7957,7 +8153,7 @@ function primaryMachine(machine, projectId, primaryMachineId) {
7957
8153
  return machine.tags.includes("primary");
7958
8154
  }
7959
8155
  function metadataKeysForDiagnostics(metadata) {
7960
- return Object.keys(metadata).filter((key) => !/(secret|token|key|password|credential)/i.test(key)).sort();
8156
+ return publicMetadataKeys(metadata);
7961
8157
  }
7962
8158
  function resolveMachineWorkspace(options) {
7963
8159
  const now = options.now ?? new Date;
@@ -8102,7 +8298,7 @@ function resolveSshTarget(machineId, options = {}) {
8102
8298
  }
8103
8299
  return {
8104
8300
  machineId: resolved.machine_id ?? machineId,
8105
- target: resolved.target,
8301
+ target: resolved.command_target ?? resolved.target,
8106
8302
  route: resolved.route,
8107
8303
  confidence: resolved.confidence,
8108
8304
  warnings: resolved.warnings
@@ -9563,20 +9759,20 @@ function getStatus() {
9563
9759
 
9564
9760
  // src/commands/workspace.ts
9565
9761
  init_paths();
9566
- function isRecord2(value) {
9762
+ function isRecord3(value) {
9567
9763
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
9568
9764
  }
9569
9765
  function cloneMetadata(metadata) {
9570
- return isRecord2(metadata) ? { ...metadata } : {};
9766
+ return isRecord3(metadata) ? { ...metadata } : {};
9571
9767
  }
9572
9768
  function mappedPath(metadata, field, key) {
9573
9769
  const container = metadata[field];
9574
- if (!isRecord2(container))
9770
+ if (!isRecord3(container))
9575
9771
  return null;
9576
9772
  const value = container[key];
9577
9773
  if (typeof value === "string" && value.trim())
9578
9774
  return value.trim();
9579
- if (isRecord2(value)) {
9775
+ if (isRecord3(value)) {
9580
9776
  const nested = value["path"] ?? value["root"] ?? value["workspacePath"] ?? value["workspace_path"];
9581
9777
  if (typeof nested === "string" && nested.trim())
9582
9778
  return nested.trim();
@@ -9585,7 +9781,7 @@ function mappedPath(metadata, field, key) {
9585
9781
  }
9586
9782
  function writeMappedPath(metadata, field, key, path) {
9587
9783
  const existing = metadata[field];
9588
- const container = isRecord2(existing) ? { ...existing } : {};
9784
+ const container = isRecord3(existing) ? { ...existing } : {};
9589
9785
  container[key] = path;
9590
9786
  metadata[field] = container;
9591
9787
  }
@@ -9955,12 +10151,28 @@ function checkMachineCompatibility(options = {}) {
9955
10151
 
9956
10152
  // src/commands/doctor.ts
9957
10153
  init_db();
9958
- function makeCheck2(id, status, summary, detail) {
9959
- return { id, status, summary, detail };
10154
+ var DOCTOR_OPTIONAL_ADAPTER_DOMAINS = ["secrets", "configs", "monitor", "repos", "mcps", "shield"];
10155
+ function makeCheck2(id, status, summary, detail, extra = {}) {
10156
+ const { data, ...rest } = extra;
10157
+ return {
10158
+ ...rest,
10159
+ id,
10160
+ status,
10161
+ summary,
10162
+ detail,
10163
+ data: data ? redactSensitiveValue(data) : undefined
10164
+ };
9960
10165
  }
9961
10166
  function parseKeyValueOutput(stdout) {
9962
- return Object.fromEntries(stdout.trim().split(`
9963
- `).map((line) => line.split("=")).filter((parts) => parts.length === 2).map(([key, value]) => [key, value]));
10167
+ const result = {};
10168
+ for (const line of stdout.trim().split(`
10169
+ `)) {
10170
+ const index = line.indexOf("=");
10171
+ if (index <= 0)
10172
+ continue;
10173
+ result[line.slice(0, index)] = line.slice(index + 1);
10174
+ }
10175
+ return result;
9964
10176
  }
9965
10177
  function buildDoctorCommand() {
9966
10178
  return [
@@ -9968,9 +10180,11 @@ function buildDoctorCommand() {
9968
10180
  'manifest_path="${HASNA_MACHINES_MANIFEST_PATH:-$data_dir/machines.json}"',
9969
10181
  'db_path="${HASNA_MACHINES_DB_PATH:-$data_dir/machines.db}"',
9970
10182
  'notifications_path="${HASNA_MACHINES_NOTIFICATIONS_PATH:-$data_dir/notifications.json}"',
10183
+ `printf 'data_dir=%s\\n' "$data_dir"`,
9971
10184
  `printf 'manifest_path=%s\\n' "$manifest_path"`,
9972
10185
  `printf 'db_path=%s\\n' "$db_path"`,
9973
10186
  `printf 'notifications_path=%s\\n' "$notifications_path"`,
10187
+ `printf 'data_dir_exists=%s\\n' "$(test -d "$data_dir" && printf yes || printf no)"`,
9974
10188
  `printf 'manifest_exists=%s\\n' "$(test -e "$manifest_path" && printf yes || printf no)"`,
9975
10189
  `printf 'db_exists=%s\\n' "$(test -e "$db_path" && printf yes || printf no)"`,
9976
10190
  `printf 'notifications_exists=%s\\n' "$(test -e "$notifications_path" && printf yes || printf no)"`,
@@ -9981,28 +10195,115 @@ function buildDoctorCommand() {
9981
10195
  `printf 'machines_mcp=%s\\n' "$(command -v machines-mcp 2>/dev/null || printf missing)"`
9982
10196
  ].join("; ");
9983
10197
  }
9984
- function runDoctor(machineId = getLocalMachineId()) {
9985
- const manifest = readManifest();
10198
+ function fallbackAdapterCheck(domain) {
10199
+ return makeCheck2(`${domain}-adapter`, "ok", `Optional ${domain} adapter`, `No ${domain} adapter configured; skipped optional private integration check.`, {
10200
+ optional: true,
10201
+ source: "open-machines",
10202
+ data: { configured: false, fallback: true }
10203
+ });
10204
+ }
10205
+ function sanitizeAdapterCheck(check, domain, adapterId) {
10206
+ const safeAdapterId = redactIdentifier(adapterId);
10207
+ return makeCheck2(check.id.startsWith(`${domain}-`) || check.id.startsWith(`${domain}:`) ? check.id : `${domain}:${check.id}`, check.status, check.summary, String(redactSensitiveValue(check.detail)), {
10208
+ ...check,
10209
+ optional: check.optional ?? true,
10210
+ source: check.source ? String(redactSensitiveValue(check.source)) : `adapter:${safeAdapterId}`,
10211
+ data: check.data ? redactSensitiveValue(check.data) : undefined
10212
+ });
10213
+ }
10214
+ function runOptionalAdapterChecks(context, adapters) {
10215
+ const checks = [];
10216
+ for (const domain of DOCTOR_OPTIONAL_ADAPTER_DOMAINS) {
10217
+ const adapter2 = adapters.find((candidate) => candidate.checks?.[domain]);
10218
+ const hook = adapter2?.checks?.[domain];
10219
+ if (!adapter2 || !hook) {
10220
+ checks.push(fallbackAdapterCheck(domain));
10221
+ continue;
10222
+ }
10223
+ try {
10224
+ const result = hook(context);
10225
+ const domainChecks = Array.isArray(result) ? result : result ? [result] : [fallbackAdapterCheck(domain)];
10226
+ checks.push(...domainChecks.map((check) => sanitizeAdapterCheck(check, domain, adapter2.id)));
10227
+ } catch {
10228
+ const safeAdapterId = redactIdentifier(adapter2.id);
10229
+ checks.push(makeCheck2(`${domain}-adapter`, "warn", `Optional ${domain} adapter failed`, "Adapter failed; details are intentionally hidden to avoid leaking private refs or credentials.", {
10230
+ optional: true,
10231
+ source: `adapter:${safeAdapterId}`,
10232
+ data: { adapter: safeAdapterId, fallback: true }
10233
+ }));
10234
+ }
10235
+ }
10236
+ return checks;
10237
+ }
10238
+ function runDoctor(machineId = getLocalMachineId(), options = {}) {
10239
+ const now = options.now ?? new Date;
10240
+ const { manifest, info: manifestSource } = readManifestWithSource({ adapter: options.manifestAdapter ?? null });
9986
10241
  const commandChecks = runMachineCommand(machineId, buildDoctorCommand());
9987
10242
  const details = parseKeyValueOutput(commandChecks.stdout);
9988
10243
  const machineInManifest = manifest.machines.find((machine) => machine.id === machineId);
10244
+ const optionalAdapterChecks = options.includeOptionalAdapters === false ? [] : runOptionalAdapterChecks({
10245
+ machineId,
10246
+ manifest,
10247
+ manifestSource,
10248
+ commandDetails: details,
10249
+ now
10250
+ }, options.adapters ?? []);
9989
10251
  const checks = [
9990
- makeCheck2("manifest-entry", machineInManifest ? "ok" : "warn", machineInManifest ? "Machine exists in manifest" : "Machine missing from manifest", machineInManifest ? JSON.stringify(machineInManifest) : `No manifest entry for ${machineId}`),
9991
- makeCheck2("manifest-path", details["manifest_exists"] === "yes" ? "ok" : "warn", "Manifest path check", `${details["manifest_path"] || "unknown"} ${details["manifest_exists"] === "yes" ? "exists" : "missing"}`),
9992
- makeCheck2("db-path", details["db_exists"] === "yes" ? "ok" : "warn", "DB path check", `${details["db_path"] || "unknown"} ${details["db_exists"] === "yes" ? "exists" : "missing"}`),
9993
- makeCheck2("notifications-path", details["notifications_exists"] === "yes" ? "ok" : "warn", "Notifications path check", `${details["notifications_path"] || "unknown"} ${details["notifications_exists"] === "yes" ? "exists" : "missing"}`),
10252
+ makeCheck2("manifest-source", manifestSource.warnings.length > 0 ? "warn" : "ok", "Manifest source boundary", `${manifestSource.source.kind}:${manifestSource.source.ref} loaded from ${manifestSource.loadedFrom}`, {
10253
+ data: {
10254
+ source: manifestSource.source,
10255
+ loadedFrom: manifestSource.loadedFrom,
10256
+ fallbackSource: manifestSource.fallbackSource,
10257
+ warnings: manifestSource.warnings
10258
+ },
10259
+ remediation: manifestSource.warnings.length > 0 ? ["Provide a private manifest adapter or unset the private manifest ref to use the local manifest only."] : undefined
10260
+ }),
10261
+ makeCheck2("manifest-entry", machineInManifest ? "ok" : "warn", machineInManifest ? "Machine exists in manifest" : "Machine missing from manifest", machineInManifest ? JSON.stringify(redactManifestForDiagnostics(machineInManifest)) : `No manifest entry for ${machineId}`, {
10262
+ data: {
10263
+ declared: Boolean(machineInManifest),
10264
+ machine: machineInManifest ? redactManifestForDiagnostics(machineInManifest) : null
10265
+ }
10266
+ }),
10267
+ makeCheck2("data-dir", details["data_dir_exists"] === "yes" ? "ok" : "warn", "Data directory check", `${redactPath(details["data_dir"] || "unknown")} ${details["data_dir_exists"] === "yes" ? "exists" : "missing"}`, {
10268
+ data: {
10269
+ path: redactPath(details["data_dir"] || "unknown"),
10270
+ exists: details["data_dir_exists"] === "yes"
10271
+ }
10272
+ }),
10273
+ makeCheck2("manifest-path", details["manifest_exists"] === "yes" ? "ok" : "warn", "Manifest path check", `${redactPath(details["manifest_path"] || "unknown")} ${details["manifest_exists"] === "yes" ? "exists" : "missing"}`, {
10274
+ data: {
10275
+ path: redactPath(details["manifest_path"] || "unknown"),
10276
+ exists: details["manifest_exists"] === "yes"
10277
+ }
10278
+ }),
10279
+ makeCheck2("db-path", details["db_exists"] === "yes" ? "ok" : "warn", "DB path check", `${redactPath(details["db_path"] || "unknown")} ${details["db_exists"] === "yes" ? "exists" : "missing"}`, {
10280
+ data: {
10281
+ path: redactPath(details["db_path"] || "unknown"),
10282
+ exists: details["db_exists"] === "yes"
10283
+ }
10284
+ }),
10285
+ makeCheck2("notifications-path", details["notifications_exists"] === "yes" ? "ok" : "warn", "Notifications path check", `${redactPath(details["notifications_path"] || "unknown")} ${details["notifications_exists"] === "yes" ? "exists" : "missing"}`, {
10286
+ data: {
10287
+ path: redactPath(details["notifications_path"] || "unknown"),
10288
+ exists: details["notifications_exists"] === "yes"
10289
+ }
10290
+ }),
9994
10291
  makeCheck2("bun", details["bun"] && details["bun"] !== "missing" ? "ok" : "fail", "Bun availability", details["bun"] || "missing"),
9995
10292
  makeCheck2("machines-cli", details["machines"] && details["machines"] !== "missing" ? "ok" : "warn", "machines CLI availability", details["machines"] || "missing"),
9996
10293
  makeCheck2("machines-agent-cli", details["machines_agent"] && details["machines_agent"] !== "missing" ? "ok" : "warn", "machines-agent availability", details["machines_agent"] || "missing"),
9997
10294
  makeCheck2("machines-mcp-cli", details["machines_mcp"] && details["machines_mcp"] !== "missing" ? "ok" : "warn", "machines-mcp availability", details["machines_mcp"] || "missing"),
9998
- makeCheck2("ssh", details["ssh"] === "ok" ? "ok" : "warn", "SSH availability", details["ssh"] || "missing")
10295
+ makeCheck2("ssh", details["ssh"] === "ok" ? "ok" : "warn", "SSH availability", details["ssh"] || "missing"),
10296
+ ...optionalAdapterChecks
9999
10297
  ];
10000
10298
  return {
10001
10299
  machineId,
10002
10300
  source: commandChecks.source,
10003
- manifestPath: details["manifest_path"],
10004
- dbPath: details["db_path"],
10005
- notificationsPath: details["notifications_path"],
10301
+ schemaVersion: 1,
10302
+ generatedAt: now.toISOString(),
10303
+ manifestSource,
10304
+ manifestPath: details["manifest_path"] ? redactPath(details["manifest_path"]) : undefined,
10305
+ dbPath: details["db_path"] ? redactPath(details["db_path"]) : undefined,
10306
+ notificationsPath: details["notifications_path"] ? redactPath(details["notifications_path"]) : undefined,
10006
10307
  checks
10007
10308
  };
10008
10309
  }
@@ -11261,8 +11562,10 @@ function binPath() {
11261
11562
  candidates.push(which);
11262
11563
  if (process.argv[1])
11263
11564
  candidates.push(process.argv[1]);
11264
- const home = process.env["HOME"] || "/home/hasna";
11265
- candidates.push(`${home}/.bun/bin/machines`, "/home/hasna/.bun/bin/machines", "/root/.bun/bin/machines", "/usr/local/bin/machines");
11565
+ const home = process.env["HOME"];
11566
+ if (home)
11567
+ candidates.push(`${home}/.bun/bin/machines`);
11568
+ candidates.push("/root/.bun/bin/machines", "/usr/local/bin/machines");
11266
11569
  for (const c of candidates) {
11267
11570
  if (c && existsSync10(c))
11268
11571
  return c;
@@ -1,3 +1,24 @@
1
- import type { DoctorReport } from "../types.js";
2
- export declare function runDoctor(machineId?: string): DoctorReport;
1
+ import { type ManifestSourceAdapter } from "../manifests.js";
2
+ import type { DoctorCheck, DoctorReport, FleetManifest, ManifestLoadInfo } from "../types.js";
3
+ export declare const DOCTOR_OPTIONAL_ADAPTER_DOMAINS: readonly ["secrets", "configs", "monitor", "repos", "mcps", "shield"];
4
+ export type DoctorOptionalAdapterDomain = typeof DOCTOR_OPTIONAL_ADAPTER_DOMAINS[number];
5
+ export interface DoctorAdapterContext {
6
+ machineId: string;
7
+ manifest: FleetManifest;
8
+ manifestSource: ManifestLoadInfo;
9
+ commandDetails: Record<string, string>;
10
+ now: Date;
11
+ }
12
+ export type DoctorAdapterHook = (context: DoctorAdapterContext) => DoctorCheck | DoctorCheck[] | null | undefined;
13
+ export interface DoctorAdapter {
14
+ id: string;
15
+ checks?: Partial<Record<DoctorOptionalAdapterDomain, DoctorAdapterHook>>;
16
+ }
17
+ export interface DoctorOptions {
18
+ now?: Date;
19
+ manifestAdapter?: ManifestSourceAdapter | null;
20
+ adapters?: DoctorAdapter[];
21
+ includeOptionalAdapters?: boolean;
22
+ }
23
+ export declare function runDoctor(machineId?: string, options?: DoctorOptions): DoctorReport;
3
24
  //# sourceMappingURL=doctor.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../../src/commands/doctor.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAe,YAAY,EAAE,MAAM,aAAa,CAAC;AAqC7D,wBAAgB,SAAS,CAAC,SAAS,SAAsB,GAAG,YAAY,CAuEvE"}
1
+ {"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../../src/commands/doctor.ts"],"names":[],"mappings":"AACA,OAAO,EAA0B,KAAK,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AAGrF,OAAO,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAE9F,eAAO,MAAM,+BAA+B,uEAAwE,CAAC;AAErH,MAAM,MAAM,2BAA2B,GAAG,OAAO,+BAA+B,CAAC,MAAM,CAAC,CAAC;AAEzF,MAAM,WAAW,oBAAoB;IACnC,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,aAAa,CAAC;IACxB,cAAc,EAAE,gBAAgB,CAAC;IACjC,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACvC,GAAG,EAAE,IAAI,CAAC;CACX;AAED,MAAM,MAAM,iBAAiB,GAAG,CAAC,OAAO,EAAE,oBAAoB,KAAK,WAAW,GAAG,WAAW,EAAE,GAAG,IAAI,GAAG,SAAS,CAAC;AAElH,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,2BAA2B,EAAE,iBAAiB,CAAC,CAAC,CAAC;CAC1E;AAED,MAAM,WAAW,aAAa;IAC5B,GAAG,CAAC,EAAE,IAAI,CAAC;IACX,eAAe,CAAC,EAAE,qBAAqB,GAAG,IAAI,CAAC;IAC/C,QAAQ,CAAC,EAAE,aAAa,EAAE,CAAC;IAC3B,uBAAuB,CAAC,EAAE,OAAO,CAAC;CACnC;AAkHD,wBAAgB,SAAS,CAAC,SAAS,SAAsB,EAAE,OAAO,GAAE,aAAkB,GAAG,YAAY,CA0IpG"}
@@ -1 +1 @@
1
- {"version":3,"file":"heal-daemon.d.ts","sourceRoot":"","sources":["../../src/commands/heal-daemon.ts"],"names":[],"mappings":"AAUA,OAAO,EAWL,KAAK,UAAU,EAChB,MAAM,WAAW,CAAC;AAMnB,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAMD,wFAAwF;AACxF,wBAAgB,WAAW,CAAC,MAAM,EAAE,UAAU,EAAE,IAAI,GAAE;IAAE,MAAM,CAAC,EAAE,OAAO,CAAA;CAAO,GAAG,cAAc,CAsC/F;AAwBD,wBAAgB,cAAc,IAAI;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAOzE;AAED,wBAAgB,eAAe,IAAI,IAAI,CAiBtC;AAOD;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,UAAU,GAAG,MAAM,EAAE,CAyB7D;AAED,0EAA0E;AAC1E,wBAAgB,sBAAsB,IAAI,MAAM,EAAE,CAejD;AAoBD,yEAAyE;AACzE,wBAAgB,kBAAkB,IAAI,MAAM,EAAE,CA0B7C;AAED,wBAAgB,oBAAoB,IAAI,MAAM,EAAE,CAW/C;AAED,wBAAgB,iBAAiB,IAAI;IAAE,SAAS,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAM7F"}
1
+ {"version":3,"file":"heal-daemon.d.ts","sourceRoot":"","sources":["../../src/commands/heal-daemon.ts"],"names":[],"mappings":"AAUA,OAAO,EAWL,KAAK,UAAU,EAChB,MAAM,WAAW,CAAC;AAMnB,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAMD,wFAAwF;AACxF,wBAAgB,WAAW,CAAC,MAAM,EAAE,UAAU,EAAE,IAAI,GAAE;IAAE,MAAM,CAAC,EAAE,OAAO,CAAA;CAAO,GAAG,cAAc,CAsC/F;AAwBD,wBAAgB,cAAc,IAAI;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAOzE;AAED,wBAAgB,eAAe,IAAI,IAAI,CAiBtC;AAOD;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,UAAU,GAAG,MAAM,EAAE,CAyB7D;AAED,0EAA0E;AAC1E,wBAAgB,sBAAsB,IAAI,MAAM,EAAE,CAejD;AAqBD,yEAAyE;AACzE,wBAAgB,kBAAkB,IAAI,MAAM,EAAE,CA0B7C;AAED,wBAAgB,oBAAoB,IAAI,MAAM,EAAE,CAW/C;AAED,wBAAgB,iBAAiB,IAAI;IAAE,SAAS,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAM7F"}