@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/index.js CHANGED
@@ -10982,7 +10982,103 @@ var coerce = {
10982
10982
  date: (arg) => ZodDate.create({ ...arg, coerce: true })
10983
10983
  };
10984
10984
  var NEVER = INVALID;
10985
+ // src/redaction.ts
10986
+ var REDACTED_VALUE = "[redacted]";
10987
+ var SENSITIVE_KEY_PATTERN = /(password|passwd|token|credential|private[_-]?key|privateKey|api[_-]?key|github.*key|pem|secret)/i;
10988
+ var SECRET_REFERENCE_KEY_PATTERN = /(secret(ref(erence)?|key)?|secretRef|secretKey)$/i;
10989
+ var SENSITIVE_VALUE_PATTERNS = [
10990
+ /-----BEGIN [A-Z ]*PRIVATE KEY-----/,
10991
+ /\bghp_[A-Za-z0-9_]{20,}\b/,
10992
+ /\bgithub_pat_[A-Za-z0-9_]{20,}\b/,
10993
+ /\bxox[baprs]-[A-Za-z0-9-]{20,}\b/,
10994
+ /\bAKIA[0-9A-Z]{16}\b/,
10995
+ /\bsk-[A-Za-z0-9_-]{20,}\b/
10996
+ ];
10997
+ function isSensitiveKey(key) {
10998
+ return SENSITIVE_KEY_PATTERN.test(key);
10999
+ }
11000
+ function isSecretReferenceKey(key) {
11001
+ return SECRET_REFERENCE_KEY_PATTERN.test(key);
11002
+ }
11003
+ function looksSensitiveString(value) {
11004
+ return SENSITIVE_VALUE_PATTERNS.some((pattern) => pattern.test(value));
11005
+ }
11006
+ function isRecord(value) {
11007
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
11008
+ }
11009
+ function redactPath(value) {
11010
+ return value.replace(/\/home\/[^/\s]+/g, "/home/<user>").replace(/\/Users\/[^/\s]+/g, "/Users/<user>").replace(/[A-Za-z]:\\Users\\[^\\\s]+/g, "C:\\Users\\<user>");
11011
+ }
11012
+ function redactPrivateRef(value) {
11013
+ const trimmed = value.trim();
11014
+ const scheme = trimmed.match(/^([a-z][a-z0-9+.-]*:\/\/)/i);
11015
+ if (scheme)
11016
+ return `${scheme[1]}<redacted>`;
11017
+ const colon = trimmed.match(/^([a-z][a-z0-9+.-]*):/i);
11018
+ if (colon)
11019
+ return `${colon[1]}:<redacted>`;
11020
+ return "<private-manifest-ref:redacted>";
11021
+ }
11022
+ function redactIdentifier(value) {
11023
+ return value.replace(/[^a-zA-Z0-9_.-]/g, "_").slice(0, 80) || "adapter";
11024
+ }
11025
+ function redactSensitiveValue(value, key = "") {
11026
+ if (typeof value === "string") {
11027
+ if (isSensitiveKey(key) && !(isSecretReferenceKey(key) && !looksSensitiveString(value))) {
11028
+ return REDACTED_VALUE;
11029
+ }
11030
+ if (looksSensitiveString(value))
11031
+ return REDACTED_VALUE;
11032
+ return redactPath(value);
11033
+ }
11034
+ if (Array.isArray(value)) {
11035
+ return value.map((entry) => redactSensitiveValue(entry, key));
11036
+ }
11037
+ if (isRecord(value)) {
11038
+ const redacted = {};
11039
+ for (const [entryKey, entryValue] of Object.entries(value)) {
11040
+ redacted[entryKey] = redactSensitiveValue(entryValue, entryKey);
11041
+ }
11042
+ return redacted;
11043
+ }
11044
+ return value;
11045
+ }
11046
+ function publicMetadataKeys(metadata) {
11047
+ return Object.keys(metadata ?? {}).filter((key) => !isSensitiveKey(key)).sort();
11048
+ }
11049
+ function redactMetadata(metadata) {
11050
+ return redactSensitiveValue(metadata ?? {});
11051
+ }
11052
+ function redactManifestForDiagnostics(machine) {
11053
+ const metadata = redactMetadata(machine.metadata);
11054
+ for (const key of ["user", "username", "login"]) {
11055
+ if (typeof metadata[key] === "string")
11056
+ metadata[key] = REDACTED_VALUE;
11057
+ }
11058
+ return {
11059
+ id: machine.id,
11060
+ hostname: machine.hostname ? REDACTED_VALUE : undefined,
11061
+ sshAddress: machine.sshAddress ? REDACTED_VALUE : undefined,
11062
+ tailscaleName: machine.tailscaleName ? REDACTED_VALUE : undefined,
11063
+ platform: machine.platform,
11064
+ connection: machine.connection,
11065
+ workspacePath: redactPath(machine.workspacePath),
11066
+ bunPath: machine.bunPath ? redactPath(machine.bunPath) : undefined,
11067
+ tags: machine.tags ?? [],
11068
+ metadata,
11069
+ packages: machine.packages?.map((pkg) => ({ ...pkg })),
11070
+ apps: machine.apps?.map((app) => ({ ...app })),
11071
+ files: machine.files?.map((file) => ({
11072
+ ...file,
11073
+ source: redactPath(file.source),
11074
+ target: redactPath(file.target)
11075
+ }))
11076
+ };
11077
+ }
11078
+
10985
11079
  // src/manifests.ts
11080
+ var PRIVATE_MANIFEST_REF_ENV = "HASNA_MACHINES_PRIVATE_MANIFEST_REF";
11081
+ var PRIVATE_MANIFEST_BACKEND_ENV = "HASNA_MACHINES_PRIVATE_MANIFEST_BACKEND";
10986
11082
  var packageSchema = exports_external.object({
10987
11083
  name: exports_external.string(),
10988
11084
  manager: exports_external.enum(["bun", "brew", "apt", "custom"]).optional(),
@@ -11035,6 +11131,42 @@ function normalizePlatform() {
11035
11131
  function normalizeMachines(machines) {
11036
11132
  return [...machines].sort((left, right) => left.id.localeCompare(right.id));
11037
11133
  }
11134
+ function inferPrivateBackend(rawRef, explicitBackend) {
11135
+ if (explicitBackend?.trim())
11136
+ return explicitBackend.trim();
11137
+ const scheme = rawRef.trim().match(/^([a-z][a-z0-9+.-]*)(?::\/\/|:)/i);
11138
+ return scheme?.[1] ?? null;
11139
+ }
11140
+ function fileSourceRef(path) {
11141
+ return {
11142
+ kind: "file",
11143
+ ref: redactPath(path),
11144
+ backend: "file",
11145
+ private: false,
11146
+ publicSafe: true
11147
+ };
11148
+ }
11149
+ function privateSourceRef(rawRef, backend) {
11150
+ return {
11151
+ kind: "private-ref",
11152
+ ref: redactPrivateRef(rawRef),
11153
+ backend: inferPrivateBackend(rawRef, backend),
11154
+ private: true,
11155
+ publicSafe: true
11156
+ };
11157
+ }
11158
+ function privateRefFromOptions(options) {
11159
+ const env = options.env ?? process.env;
11160
+ return options.privateRef?.trim() || env[PRIVATE_MANIFEST_REF_ENV]?.trim() || env["MACHINES_PRIVATE_MANIFEST_REF"]?.trim() || null;
11161
+ }
11162
+ function getManifestSourceRef(options = {}) {
11163
+ const rawPrivateRef = privateRefFromOptions(options);
11164
+ if (rawPrivateRef) {
11165
+ const env = options.env ?? process.env;
11166
+ return privateSourceRef(rawPrivateRef, options.privateBackend ?? env[PRIVATE_MANIFEST_BACKEND_ENV]);
11167
+ }
11168
+ return fileSourceRef(options.path ?? getManifestPath());
11169
+ }
11038
11170
  function getDefaultManifest() {
11039
11171
  return {
11040
11172
  version: 1,
@@ -11049,6 +11181,53 @@ function readManifest(path = getManifestPath()) {
11049
11181
  const raw = JSON.parse(readFileSync(path, "utf8"));
11050
11182
  return fleetSchema.parse(raw);
11051
11183
  }
11184
+ function readManifestWithSource(options = {}) {
11185
+ const path = options.path ?? getManifestPath();
11186
+ const source = getManifestSourceRef(options);
11187
+ const warnings = [];
11188
+ if (source.kind === "private-ref") {
11189
+ const rawRef = privateRefFromOptions(options);
11190
+ if (rawRef && options.adapter) {
11191
+ try {
11192
+ const manifest2 = options.adapter.readManifest({ source, rawRef });
11193
+ if (manifest2) {
11194
+ return {
11195
+ manifest: fleetSchema.parse(manifest2),
11196
+ info: {
11197
+ source,
11198
+ loadedFrom: "private-ref",
11199
+ warnings
11200
+ }
11201
+ };
11202
+ }
11203
+ warnings.push(`private_manifest_adapter_empty:${redactIdentifier(options.adapter.id)}`);
11204
+ } catch (error) {
11205
+ warnings.push(`private_manifest_adapter_failed:${redactIdentifier(options.adapter.id)}`);
11206
+ }
11207
+ } else {
11208
+ warnings.push("private_manifest_ref_without_adapter");
11209
+ }
11210
+ const fallbackSource = fileSourceRef(path);
11211
+ const manifest = readManifest(path);
11212
+ return {
11213
+ manifest,
11214
+ info: {
11215
+ source,
11216
+ loadedFrom: existsSync2(path) ? "fallback" : "default",
11217
+ fallbackSource,
11218
+ warnings
11219
+ }
11220
+ };
11221
+ }
11222
+ return {
11223
+ manifest: readManifest(path),
11224
+ info: {
11225
+ source,
11226
+ loadedFrom: existsSync2(path) ? "file" : "default",
11227
+ warnings
11228
+ }
11229
+ };
11230
+ }
11052
11231
  function validateManifest(path = getManifestPath()) {
11053
11232
  return readManifest(path);
11054
11233
  }
@@ -11287,6 +11466,19 @@ function manifestHostReachable(target) {
11287
11466
  return null;
11288
11467
  return overrides.has(target);
11289
11468
  }
11469
+ function userFromSshAddress(address) {
11470
+ if (!address)
11471
+ return null;
11472
+ const at = address.indexOf("@");
11473
+ if (at <= 0)
11474
+ return null;
11475
+ return address.slice(0, at);
11476
+ }
11477
+ function commandTargetForRoute(target, user) {
11478
+ if (target.kind === "local" || target.target.includes("@") || !user)
11479
+ return target.target;
11480
+ return `${user}@${target.target}`;
11481
+ }
11290
11482
  function routeHints(input) {
11291
11483
  const hints = [];
11292
11484
  if (input.machineId === input.localMachineId) {
@@ -11337,6 +11529,7 @@ function buildEntry(input) {
11337
11529
  });
11338
11530
  const selectedRoute = selectRouteHint(hints);
11339
11531
  const route = selectedRoute?.kind === "ssh" ? "ssh" : selectedRoute?.kind ?? "unknown";
11532
+ const routeUser = userFromSshAddress(manifest?.sshAddress) ?? (typeof manifest?.metadata?.user === "string" ? manifest.metadata.user : null);
11340
11533
  return {
11341
11534
  machine_id: input.machineId,
11342
11535
  hostname: manifest?.hostname ?? peer?.HostName ?? null,
@@ -11357,11 +11550,11 @@ function buildEntry(input) {
11357
11550
  ssh: {
11358
11551
  address: manifest?.sshAddress ?? null,
11359
11552
  route,
11360
- command_target: selectedRoute?.target ?? null
11553
+ command_target: selectedRoute ? commandTargetForRoute(selectedRoute, routeUser) : null
11361
11554
  },
11362
11555
  route_hints: hints,
11363
11556
  tags: manifest?.tags ?? [],
11364
- metadata: manifest?.metadata ?? {}
11557
+ metadata: redactMetadata(manifest?.metadata)
11365
11558
  };
11366
11559
  }
11367
11560
  function discoverMachineTopology(options = {}) {
@@ -11596,6 +11789,7 @@ function resolveMachineRoute(machineId, options = {}) {
11596
11789
  const local = route === "local" || machine.machine_id === topology.local_machine_id;
11597
11790
  const confidence = routeConfidence({ machine, hint: selectedHint, matchedBy });
11598
11791
  const ok = Boolean(selectedHint?.target);
11792
+ const commandTarget = selectedHint ? commandTargetForRoute(selectedHint, userFromSshAddress(machine.ssh.address) ?? machine.user) : null;
11599
11793
  return {
11600
11794
  schema_version: MACHINES_CONSUMER_CONTRACT_VERSION,
11601
11795
  package: topology.package,
@@ -11606,7 +11800,7 @@ function resolveMachineRoute(machineId, options = {}) {
11606
11800
  route,
11607
11801
  source: route,
11608
11802
  target: selectedHint?.target ?? null,
11609
- command_target: selectedHint?.target ?? null,
11803
+ command_target: commandTarget,
11610
11804
  confidence,
11611
11805
  local,
11612
11806
  evidence: {
@@ -11629,7 +11823,7 @@ function resolveMachineRoute(machineId, options = {}) {
11629
11823
  warnings
11630
11824
  };
11631
11825
  }
11632
- function isRecord(value) {
11826
+ function isRecord2(value) {
11633
11827
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
11634
11828
  }
11635
11829
  function metadataString(metadata, keys) {
@@ -11659,13 +11853,13 @@ function metadataStringArray(metadata, keys) {
11659
11853
  function readMappedPath(input) {
11660
11854
  for (const containerName of input.containers) {
11661
11855
  const container = input.metadata[containerName];
11662
- if (!isRecord(container))
11856
+ if (!isRecord2(container))
11663
11857
  continue;
11664
11858
  for (const key of input.keys) {
11665
11859
  const value = container[key];
11666
11860
  if (typeof value === "string" && value.trim())
11667
11861
  return value.trim();
11668
- if (isRecord(value)) {
11862
+ if (isRecord2(value)) {
11669
11863
  const path = metadataString(value, ["path", "root", "workspacePath", "workspace_path"]);
11670
11864
  if (path)
11671
11865
  return path;
@@ -11919,7 +12113,7 @@ function primaryMachine(machine, projectId, primaryMachineId) {
11919
12113
  return machine.tags.includes("primary");
11920
12114
  }
11921
12115
  function metadataKeysForDiagnostics(metadata) {
11922
- return Object.keys(metadata).filter((key) => !/(secret|token|key|password|credential)/i.test(key)).sort();
12116
+ return publicMetadataKeys(metadata);
11923
12117
  }
11924
12118
  function resolveMachineWorkspace(options) {
11925
12119
  const now = options.now ?? new Date;
@@ -12153,7 +12347,7 @@ function resolveSshTarget(machineId, options = {}) {
12153
12347
  }
12154
12348
  return {
12155
12349
  machineId: resolved.machine_id ?? machineId,
12156
- target: resolved.target,
12350
+ target: resolved.command_target ?? resolved.target,
12157
12351
  route: resolved.route,
12158
12352
  confidence: resolved.confidence,
12159
12353
  warnings: resolved.warnings
@@ -12844,12 +13038,28 @@ function renderDomainMapping(domain) {
12844
13038
  };
12845
13039
  }
12846
13040
  // src/commands/doctor.ts
12847
- function makeCheck2(id, status, summary, detail) {
12848
- return { id, status, summary, detail };
13041
+ var DOCTOR_OPTIONAL_ADAPTER_DOMAINS = ["secrets", "configs", "monitor", "repos", "mcps", "shield"];
13042
+ function makeCheck2(id, status, summary, detail, extra = {}) {
13043
+ const { data, ...rest } = extra;
13044
+ return {
13045
+ ...rest,
13046
+ id,
13047
+ status,
13048
+ summary,
13049
+ detail,
13050
+ data: data ? redactSensitiveValue(data) : undefined
13051
+ };
12849
13052
  }
12850
13053
  function parseKeyValueOutput(stdout) {
12851
- return Object.fromEntries(stdout.trim().split(`
12852
- `).map((line) => line.split("=")).filter((parts) => parts.length === 2).map(([key, value]) => [key, value]));
13054
+ const result = {};
13055
+ for (const line of stdout.trim().split(`
13056
+ `)) {
13057
+ const index = line.indexOf("=");
13058
+ if (index <= 0)
13059
+ continue;
13060
+ result[line.slice(0, index)] = line.slice(index + 1);
13061
+ }
13062
+ return result;
12853
13063
  }
12854
13064
  function buildDoctorCommand() {
12855
13065
  return [
@@ -12857,9 +13067,11 @@ function buildDoctorCommand() {
12857
13067
  'manifest_path="${HASNA_MACHINES_MANIFEST_PATH:-$data_dir/machines.json}"',
12858
13068
  'db_path="${HASNA_MACHINES_DB_PATH:-$data_dir/machines.db}"',
12859
13069
  'notifications_path="${HASNA_MACHINES_NOTIFICATIONS_PATH:-$data_dir/notifications.json}"',
13070
+ `printf 'data_dir=%s\\n' "$data_dir"`,
12860
13071
  `printf 'manifest_path=%s\\n' "$manifest_path"`,
12861
13072
  `printf 'db_path=%s\\n' "$db_path"`,
12862
13073
  `printf 'notifications_path=%s\\n' "$notifications_path"`,
13074
+ `printf 'data_dir_exists=%s\\n' "$(test -d "$data_dir" && printf yes || printf no)"`,
12863
13075
  `printf 'manifest_exists=%s\\n' "$(test -e "$manifest_path" && printf yes || printf no)"`,
12864
13076
  `printf 'db_exists=%s\\n' "$(test -e "$db_path" && printf yes || printf no)"`,
12865
13077
  `printf 'notifications_exists=%s\\n' "$(test -e "$notifications_path" && printf yes || printf no)"`,
@@ -12870,28 +13082,115 @@ function buildDoctorCommand() {
12870
13082
  `printf 'machines_mcp=%s\\n' "$(command -v machines-mcp 2>/dev/null || printf missing)"`
12871
13083
  ].join("; ");
12872
13084
  }
12873
- function runDoctor(machineId = getLocalMachineId()) {
12874
- const manifest = readManifest();
13085
+ function fallbackAdapterCheck(domain) {
13086
+ return makeCheck2(`${domain}-adapter`, "ok", `Optional ${domain} adapter`, `No ${domain} adapter configured; skipped optional private integration check.`, {
13087
+ optional: true,
13088
+ source: "open-machines",
13089
+ data: { configured: false, fallback: true }
13090
+ });
13091
+ }
13092
+ function sanitizeAdapterCheck(check, domain, adapterId) {
13093
+ const safeAdapterId = redactIdentifier(adapterId);
13094
+ return makeCheck2(check.id.startsWith(`${domain}-`) || check.id.startsWith(`${domain}:`) ? check.id : `${domain}:${check.id}`, check.status, check.summary, String(redactSensitiveValue(check.detail)), {
13095
+ ...check,
13096
+ optional: check.optional ?? true,
13097
+ source: check.source ? String(redactSensitiveValue(check.source)) : `adapter:${safeAdapterId}`,
13098
+ data: check.data ? redactSensitiveValue(check.data) : undefined
13099
+ });
13100
+ }
13101
+ function runOptionalAdapterChecks(context, adapters) {
13102
+ const checks = [];
13103
+ for (const domain of DOCTOR_OPTIONAL_ADAPTER_DOMAINS) {
13104
+ const adapter2 = adapters.find((candidate) => candidate.checks?.[domain]);
13105
+ const hook = adapter2?.checks?.[domain];
13106
+ if (!adapter2 || !hook) {
13107
+ checks.push(fallbackAdapterCheck(domain));
13108
+ continue;
13109
+ }
13110
+ try {
13111
+ const result = hook(context);
13112
+ const domainChecks = Array.isArray(result) ? result : result ? [result] : [fallbackAdapterCheck(domain)];
13113
+ checks.push(...domainChecks.map((check) => sanitizeAdapterCheck(check, domain, adapter2.id)));
13114
+ } catch {
13115
+ const safeAdapterId = redactIdentifier(adapter2.id);
13116
+ checks.push(makeCheck2(`${domain}-adapter`, "warn", `Optional ${domain} adapter failed`, "Adapter failed; details are intentionally hidden to avoid leaking private refs or credentials.", {
13117
+ optional: true,
13118
+ source: `adapter:${safeAdapterId}`,
13119
+ data: { adapter: safeAdapterId, fallback: true }
13120
+ }));
13121
+ }
13122
+ }
13123
+ return checks;
13124
+ }
13125
+ function runDoctor(machineId = getLocalMachineId(), options = {}) {
13126
+ const now = options.now ?? new Date;
13127
+ const { manifest, info: manifestSource } = readManifestWithSource({ adapter: options.manifestAdapter ?? null });
12875
13128
  const commandChecks = runMachineCommand(machineId, buildDoctorCommand());
12876
13129
  const details = parseKeyValueOutput(commandChecks.stdout);
12877
13130
  const machineInManifest = manifest.machines.find((machine) => machine.id === machineId);
13131
+ const optionalAdapterChecks = options.includeOptionalAdapters === false ? [] : runOptionalAdapterChecks({
13132
+ machineId,
13133
+ manifest,
13134
+ manifestSource,
13135
+ commandDetails: details,
13136
+ now
13137
+ }, options.adapters ?? []);
12878
13138
  const checks = [
12879
- makeCheck2("manifest-entry", machineInManifest ? "ok" : "warn", machineInManifest ? "Machine exists in manifest" : "Machine missing from manifest", machineInManifest ? JSON.stringify(machineInManifest) : `No manifest entry for ${machineId}`),
12880
- makeCheck2("manifest-path", details["manifest_exists"] === "yes" ? "ok" : "warn", "Manifest path check", `${details["manifest_path"] || "unknown"} ${details["manifest_exists"] === "yes" ? "exists" : "missing"}`),
12881
- makeCheck2("db-path", details["db_exists"] === "yes" ? "ok" : "warn", "DB path check", `${details["db_path"] || "unknown"} ${details["db_exists"] === "yes" ? "exists" : "missing"}`),
12882
- makeCheck2("notifications-path", details["notifications_exists"] === "yes" ? "ok" : "warn", "Notifications path check", `${details["notifications_path"] || "unknown"} ${details["notifications_exists"] === "yes" ? "exists" : "missing"}`),
13139
+ makeCheck2("manifest-source", manifestSource.warnings.length > 0 ? "warn" : "ok", "Manifest source boundary", `${manifestSource.source.kind}:${manifestSource.source.ref} loaded from ${manifestSource.loadedFrom}`, {
13140
+ data: {
13141
+ source: manifestSource.source,
13142
+ loadedFrom: manifestSource.loadedFrom,
13143
+ fallbackSource: manifestSource.fallbackSource,
13144
+ warnings: manifestSource.warnings
13145
+ },
13146
+ remediation: manifestSource.warnings.length > 0 ? ["Provide a private manifest adapter or unset the private manifest ref to use the local manifest only."] : undefined
13147
+ }),
13148
+ 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}`, {
13149
+ data: {
13150
+ declared: Boolean(machineInManifest),
13151
+ machine: machineInManifest ? redactManifestForDiagnostics(machineInManifest) : null
13152
+ }
13153
+ }),
13154
+ 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"}`, {
13155
+ data: {
13156
+ path: redactPath(details["data_dir"] || "unknown"),
13157
+ exists: details["data_dir_exists"] === "yes"
13158
+ }
13159
+ }),
13160
+ makeCheck2("manifest-path", details["manifest_exists"] === "yes" ? "ok" : "warn", "Manifest path check", `${redactPath(details["manifest_path"] || "unknown")} ${details["manifest_exists"] === "yes" ? "exists" : "missing"}`, {
13161
+ data: {
13162
+ path: redactPath(details["manifest_path"] || "unknown"),
13163
+ exists: details["manifest_exists"] === "yes"
13164
+ }
13165
+ }),
13166
+ makeCheck2("db-path", details["db_exists"] === "yes" ? "ok" : "warn", "DB path check", `${redactPath(details["db_path"] || "unknown")} ${details["db_exists"] === "yes" ? "exists" : "missing"}`, {
13167
+ data: {
13168
+ path: redactPath(details["db_path"] || "unknown"),
13169
+ exists: details["db_exists"] === "yes"
13170
+ }
13171
+ }),
13172
+ makeCheck2("notifications-path", details["notifications_exists"] === "yes" ? "ok" : "warn", "Notifications path check", `${redactPath(details["notifications_path"] || "unknown")} ${details["notifications_exists"] === "yes" ? "exists" : "missing"}`, {
13173
+ data: {
13174
+ path: redactPath(details["notifications_path"] || "unknown"),
13175
+ exists: details["notifications_exists"] === "yes"
13176
+ }
13177
+ }),
12883
13178
  makeCheck2("bun", details["bun"] && details["bun"] !== "missing" ? "ok" : "fail", "Bun availability", details["bun"] || "missing"),
12884
13179
  makeCheck2("machines-cli", details["machines"] && details["machines"] !== "missing" ? "ok" : "warn", "machines CLI availability", details["machines"] || "missing"),
12885
13180
  makeCheck2("machines-agent-cli", details["machines_agent"] && details["machines_agent"] !== "missing" ? "ok" : "warn", "machines-agent availability", details["machines_agent"] || "missing"),
12886
13181
  makeCheck2("machines-mcp-cli", details["machines_mcp"] && details["machines_mcp"] !== "missing" ? "ok" : "warn", "machines-mcp availability", details["machines_mcp"] || "missing"),
12887
- makeCheck2("ssh", details["ssh"] === "ok" ? "ok" : "warn", "SSH availability", details["ssh"] || "missing")
13182
+ makeCheck2("ssh", details["ssh"] === "ok" ? "ok" : "warn", "SSH availability", details["ssh"] || "missing"),
13183
+ ...optionalAdapterChecks
12888
13184
  ];
12889
13185
  return {
12890
13186
  machineId,
12891
13187
  source: commandChecks.source,
12892
- manifestPath: details["manifest_path"],
12893
- dbPath: details["db_path"],
12894
- notificationsPath: details["notifications_path"],
13188
+ schemaVersion: 1,
13189
+ generatedAt: now.toISOString(),
13190
+ manifestSource,
13191
+ manifestPath: details["manifest_path"] ? redactPath(details["manifest_path"]) : undefined,
13192
+ dbPath: details["db_path"] ? redactPath(details["db_path"]) : undefined,
13193
+ notificationsPath: details["notifications_path"] ? redactPath(details["notifications_path"]) : undefined,
12895
13194
  checks
12896
13195
  };
12897
13196
  }
@@ -14305,20 +14604,20 @@ function runSync(machineId, options = {}, runner = runMachineCommand) {
14305
14604
  return summary;
14306
14605
  }
14307
14606
  // src/commands/workspace.ts
14308
- function isRecord2(value) {
14607
+ function isRecord3(value) {
14309
14608
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
14310
14609
  }
14311
14610
  function cloneMetadata(metadata) {
14312
- return isRecord2(metadata) ? { ...metadata } : {};
14611
+ return isRecord3(metadata) ? { ...metadata } : {};
14313
14612
  }
14314
14613
  function mappedPath(metadata, field, key) {
14315
14614
  const container = metadata[field];
14316
- if (!isRecord2(container))
14615
+ if (!isRecord3(container))
14317
14616
  return null;
14318
14617
  const value = container[key];
14319
14618
  if (typeof value === "string" && value.trim())
14320
14619
  return value.trim();
14321
- if (isRecord2(value)) {
14620
+ if (isRecord3(value)) {
14322
14621
  const nested = value["path"] ?? value["root"] ?? value["workspacePath"] ?? value["workspace_path"];
14323
14622
  if (typeof nested === "string" && nested.trim())
14324
14623
  return nested.trim();
@@ -14327,7 +14626,7 @@ function mappedPath(metadata, field, key) {
14327
14626
  }
14328
14627
  function writeMappedPath(metadata, field, key, path) {
14329
14628
  const existing = metadata[field];
14330
- const container = isRecord2(existing) ? { ...existing } : {};
14629
+ const container = isRecord3(existing) ? { ...existing } : {};
14331
14630
  container[key] = path;
14332
14631
  metadata[field] = container;
14333
14632
  }
@@ -23681,10 +23980,18 @@ export {
23681
23980
  renderDomainMapping,
23682
23981
  renderDashboardHtml,
23683
23982
  removeNotificationChannel,
23983
+ redactSensitiveValue,
23984
+ redactPrivateRef,
23985
+ redactPath,
23986
+ redactMetadata,
23987
+ redactManifestForDiagnostics,
23988
+ redactIdentifier,
23684
23989
  recordSyncRun,
23685
23990
  recordSetupRun,
23686
23991
  readNotificationConfig,
23992
+ readManifestWithSource,
23687
23993
  readManifest,
23994
+ publicMetadataKeys,
23688
23995
  probeTmuxPane,
23689
23996
  parseStorageTables,
23690
23997
  parsePortOutput,
@@ -23702,6 +24009,7 @@ export {
23702
24009
  listHeartbeats,
23703
24010
  listDomainMappings,
23704
24011
  listApps,
24012
+ isSensitiveKey,
23705
24013
  getSyncMetaAll,
23706
24014
  getStorageStatus,
23707
24015
  getStoragePg,
@@ -23713,6 +24021,7 @@ export {
23713
24021
  getServeInfo,
23714
24022
  getPackageVersion,
23715
24023
  getNotificationsPath,
24024
+ getManifestSourceRef,
23716
24025
  getManifestPath,
23717
24026
  getManifestMachine,
23718
24027
  getMachinesConsumerCapabilities,
@@ -23764,6 +24073,9 @@ export {
23764
24073
  STORAGE_MODE_ENV,
23765
24074
  STORAGE_DATABASE_ENV,
23766
24075
  SCREEN_SECRET_NAMESPACE_ENV,
24076
+ REDACTED_VALUE,
24077
+ PRIVATE_MANIFEST_REF_ENV,
24078
+ PRIVATE_MANIFEST_BACKEND_ENV,
23767
24079
  MACHINE_MCP_TOOL_NAMES,
23768
24080
  MACHINES_STORAGE_TABLES,
23769
24081
  MACHINES_STORAGE_MODE_FALLBACK_ENV,
@@ -23782,6 +24094,7 @@ export {
23782
24094
  MACHINES_BACKUP_PREFIX_ENV,
23783
24095
  MACHINES_BACKUP_BUCKET_FALLBACK_ENV,
23784
24096
  MACHINES_BACKUP_BUCKET_ENV,
24097
+ DOCTOR_OPTIONAL_ADAPTER_DOMAINS,
23785
24098
  DEFAULT_SCREEN_SECRET_NAMESPACE,
23786
24099
  DEFAULT_MACHINE_RESOLVER_TTL_MS,
23787
24100
  DEFAULT_BACKUP_PREFIX,
@@ -1,5 +1,21 @@
1
1
  import { z } from "zod";
2
- import type { FleetManifest, MachineManifest } from "./types.js";
2
+ import type { FleetManifest, MachineManifest, ManifestLoadInfo, ManifestSourceRef } from "./types.js";
3
+ export declare const PRIVATE_MANIFEST_REF_ENV = "HASNA_MACHINES_PRIVATE_MANIFEST_REF";
4
+ export declare const PRIVATE_MANIFEST_BACKEND_ENV = "HASNA_MACHINES_PRIVATE_MANIFEST_BACKEND";
5
+ export interface ManifestSourceAdapter {
6
+ id: string;
7
+ readManifest(input: {
8
+ source: ManifestSourceRef;
9
+ rawRef: string;
10
+ }): FleetManifest | null | undefined;
11
+ }
12
+ export interface ReadManifestWithSourceOptions {
13
+ path?: string;
14
+ env?: NodeJS.ProcessEnv;
15
+ privateRef?: string;
16
+ privateBackend?: string;
17
+ adapter?: ManifestSourceAdapter | null;
18
+ }
3
19
  export declare const machineSchema: z.ZodObject<{
4
20
  id: z.ZodString;
5
21
  hostname: z.ZodOptional<z.ZodString>;
@@ -270,8 +286,13 @@ export declare const fleetSchema: z.ZodObject<{
270
286
  version: 1;
271
287
  generatedAt?: string | undefined;
272
288
  }>;
289
+ export declare function getManifestSourceRef(options?: ReadManifestWithSourceOptions): ManifestSourceRef;
273
290
  export declare function getDefaultManifest(): FleetManifest;
274
291
  export declare function readManifest(path?: string): FleetManifest;
292
+ export declare function readManifestWithSource(options?: ReadManifestWithSourceOptions): {
293
+ manifest: FleetManifest;
294
+ info: ManifestLoadInfo;
295
+ };
275
296
  export declare function validateManifest(path?: string): FleetManifest;
276
297
  export declare function writeManifest(manifest: FleetManifest, path?: string): string;
277
298
  export declare function getManifestMachine(machineId: string, path?: string): MachineManifest | null;
@@ -1 +1 @@
1
- {"version":3,"file":"manifests.d.ts","sourceRoot":"","sources":["../src/manifests.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,KAAK,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAoBjE,eAAO,MAAM,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAcxB,CAAC;AAEH,eAAO,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAItB,CAAC;AAoBH,wBAAgB,kBAAkB,IAAI,aAAa,CAMlD;AAED,wBAAgB,YAAY,CAAC,IAAI,SAAoB,GAAG,aAAa,CAMpE;AAED,wBAAgB,gBAAgB,CAAC,IAAI,SAAoB,GAAG,aAAa,CAExE;AAED,wBAAgB,aAAa,CAAC,QAAQ,EAAE,aAAa,EAAE,IAAI,SAAoB,GAAG,MAAM,CAWvF;AAED,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,SAAoB,GAAG,eAAe,GAAG,IAAI,CAEtG;AAED,wBAAgB,4BAA4B,IAAI,eAAe,CAiB9D"}
1
+ {"version":3,"file":"manifests.d.ts","sourceRoot":"","sources":["../src/manifests.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,OAAO,KAAK,EAAE,aAAa,EAAE,eAAe,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAEtG,eAAO,MAAM,wBAAwB,wCAAwC,CAAC;AAC9E,eAAO,MAAM,4BAA4B,4CAA4C,CAAC;AAEtF,MAAM,WAAW,qBAAqB;IACpC,EAAE,EAAE,MAAM,CAAC;IACX,YAAY,CAAC,KAAK,EAAE;QAAE,MAAM,EAAE,iBAAiB,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,GAAG,aAAa,GAAG,IAAI,GAAG,SAAS,CAAC;CACtG;AAED,MAAM,WAAW,6BAA6B;IAC5C,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACxB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,OAAO,CAAC,EAAE,qBAAqB,GAAG,IAAI,CAAC;CACxC;AAoBD,eAAO,MAAM,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAcxB,CAAC;AAEH,eAAO,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAItB,CAAC;AAsDH,wBAAgB,oBAAoB,CAAC,OAAO,GAAE,6BAAkC,GAAG,iBAAiB,CAOnG;AAED,wBAAgB,kBAAkB,IAAI,aAAa,CAMlD;AAED,wBAAgB,YAAY,CAAC,IAAI,SAAoB,GAAG,aAAa,CAMpE;AAED,wBAAgB,sBAAsB,CAAC,OAAO,GAAE,6BAAkC,GAAG;IAAE,QAAQ,EAAE,aAAa,CAAC;IAAC,IAAI,EAAE,gBAAgB,CAAA;CAAE,CAiDvI;AAED,wBAAgB,gBAAgB,CAAC,IAAI,SAAoB,GAAG,aAAa,CAExE;AAED,wBAAgB,aAAa,CAAC,QAAQ,EAAE,aAAa,EAAE,IAAI,SAAoB,GAAG,MAAM,CAWvF;AAED,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,SAAoB,GAAG,eAAe,GAAG,IAAI,CAEtG;AAED,wBAAgB,4BAA4B,IAAI,eAAe,CAiB9D"}