@hasna/machines 0.0.34 → 0.0.36

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
  }
@@ -11375,7 +11554,7 @@ function buildEntry(input) {
11375
11554
  },
11376
11555
  route_hints: hints,
11377
11556
  tags: manifest?.tags ?? [],
11378
- metadata: manifest?.metadata ?? {}
11557
+ metadata: redactMetadata(manifest?.metadata)
11379
11558
  };
11380
11559
  }
11381
11560
  function discoverMachineTopology(options = {}) {
@@ -11644,7 +11823,7 @@ function resolveMachineRoute(machineId, options = {}) {
11644
11823
  warnings
11645
11824
  };
11646
11825
  }
11647
- function isRecord(value) {
11826
+ function isRecord2(value) {
11648
11827
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
11649
11828
  }
11650
11829
  function metadataString(metadata, keys) {
@@ -11674,13 +11853,13 @@ function metadataStringArray(metadata, keys) {
11674
11853
  function readMappedPath(input) {
11675
11854
  for (const containerName of input.containers) {
11676
11855
  const container = input.metadata[containerName];
11677
- if (!isRecord(container))
11856
+ if (!isRecord2(container))
11678
11857
  continue;
11679
11858
  for (const key of input.keys) {
11680
11859
  const value = container[key];
11681
11860
  if (typeof value === "string" && value.trim())
11682
11861
  return value.trim();
11683
- if (isRecord(value)) {
11862
+ if (isRecord2(value)) {
11684
11863
  const path = metadataString(value, ["path", "root", "workspacePath", "workspace_path"]);
11685
11864
  if (path)
11686
11865
  return path;
@@ -11934,7 +12113,7 @@ function primaryMachine(machine, projectId, primaryMachineId) {
11934
12113
  return machine.tags.includes("primary");
11935
12114
  }
11936
12115
  function metadataKeysForDiagnostics(metadata) {
11937
- return Object.keys(metadata).filter((key) => !/(secret|token|key|password|credential)/i.test(key)).sort();
12116
+ return publicMetadataKeys(metadata);
11938
12117
  }
11939
12118
  function resolveMachineWorkspace(options) {
11940
12119
  const now = options.now ?? new Date;
@@ -12859,12 +13038,28 @@ function renderDomainMapping(domain) {
12859
13038
  };
12860
13039
  }
12861
13040
  // src/commands/doctor.ts
12862
- function makeCheck2(id, status, summary, detail) {
12863
- 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
+ };
12864
13052
  }
12865
13053
  function parseKeyValueOutput(stdout) {
12866
- return Object.fromEntries(stdout.trim().split(`
12867
- `).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;
12868
13063
  }
12869
13064
  function buildDoctorCommand() {
12870
13065
  return [
@@ -12872,9 +13067,11 @@ function buildDoctorCommand() {
12872
13067
  'manifest_path="${HASNA_MACHINES_MANIFEST_PATH:-$data_dir/machines.json}"',
12873
13068
  'db_path="${HASNA_MACHINES_DB_PATH:-$data_dir/machines.db}"',
12874
13069
  'notifications_path="${HASNA_MACHINES_NOTIFICATIONS_PATH:-$data_dir/notifications.json}"',
13070
+ `printf 'data_dir=%s\\n' "$data_dir"`,
12875
13071
  `printf 'manifest_path=%s\\n' "$manifest_path"`,
12876
13072
  `printf 'db_path=%s\\n' "$db_path"`,
12877
13073
  `printf 'notifications_path=%s\\n' "$notifications_path"`,
13074
+ `printf 'data_dir_exists=%s\\n' "$(test -d "$data_dir" && printf yes || printf no)"`,
12878
13075
  `printf 'manifest_exists=%s\\n' "$(test -e "$manifest_path" && printf yes || printf no)"`,
12879
13076
  `printf 'db_exists=%s\\n' "$(test -e "$db_path" && printf yes || printf no)"`,
12880
13077
  `printf 'notifications_exists=%s\\n' "$(test -e "$notifications_path" && printf yes || printf no)"`,
@@ -12882,31 +13079,139 @@ function buildDoctorCommand() {
12882
13079
  `printf 'ssh=%s\\n' "$(command -v ssh >/dev/null 2>&1 && printf ok || printf missing)"`,
12883
13080
  `printf 'machines=%s\\n' "$(command -v machines 2>/dev/null || printf missing)"`,
12884
13081
  `printf 'machines_agent=%s\\n' "$(command -v machines-agent 2>/dev/null || printf missing)"`,
12885
- `printf 'machines_mcp=%s\\n' "$(command -v machines-mcp 2>/dev/null || printf missing)"`
13082
+ `printf 'machines_mcp=%s\\n' "$(command -v machines-mcp 2>/dev/null || printf missing)"`,
13083
+ `printf 'sudo_noninteractive=%s\\n' "$(sudo -n true >/dev/null 2>&1 && printf ok || printf unavailable)"`,
13084
+ `printf 'ssh_cert_support=%s\\n' "$(ssh -Q key-cert 2>/dev/null | grep -q 'ssh-ed25519-cert-v01@openssh.com' && printf ok || printf unavailable)"`,
13085
+ `printf 'gh_cli=%s\\n' "$(command -v gh 2>/dev/null || printf missing)"`,
13086
+ `printf 'gh_auth=%s\\n' "$(gh auth status >/dev/null 2>&1 && printf ok || printf unavailable)"`,
13087
+ "printf 'github_app_ref=%s\\n' \"$(test -n \\\"${HASNA_GITHUB_APP_ID:-}\\\" -a -n \\\"${HASNA_GITHUB_APP_PRIVATE_KEY_REF:-}\\\" && printf configured || printf missing)\""
12886
13088
  ].join("; ");
12887
13089
  }
12888
- function runDoctor(machineId = getLocalMachineId()) {
12889
- const manifest = readManifest();
13090
+ function fallbackAdapterCheck(domain) {
13091
+ return makeCheck2(`${domain}-adapter`, "ok", `Optional ${domain} adapter`, `No ${domain} adapter configured; skipped optional private integration check.`, {
13092
+ optional: true,
13093
+ source: "open-machines",
13094
+ data: { configured: false, fallback: true }
13095
+ });
13096
+ }
13097
+ function sanitizeAdapterCheck(check, domain, adapterId) {
13098
+ const safeAdapterId = redactIdentifier(adapterId);
13099
+ return makeCheck2(check.id.startsWith(`${domain}-`) || check.id.startsWith(`${domain}:`) ? check.id : `${domain}:${check.id}`, check.status, check.summary, String(redactSensitiveValue(check.detail)), {
13100
+ ...check,
13101
+ optional: check.optional ?? true,
13102
+ source: check.source ? String(redactSensitiveValue(check.source)) : `adapter:${safeAdapterId}`,
13103
+ data: check.data ? redactSensitiveValue(check.data) : undefined
13104
+ });
13105
+ }
13106
+ function runOptionalAdapterChecks(context, adapters) {
13107
+ const checks = [];
13108
+ for (const domain of DOCTOR_OPTIONAL_ADAPTER_DOMAINS) {
13109
+ const adapter2 = adapters.find((candidate) => candidate.checks?.[domain]);
13110
+ const hook = adapter2?.checks?.[domain];
13111
+ if (!adapter2 || !hook) {
13112
+ checks.push(fallbackAdapterCheck(domain));
13113
+ continue;
13114
+ }
13115
+ try {
13116
+ const result = hook(context);
13117
+ const domainChecks = Array.isArray(result) ? result : result ? [result] : [fallbackAdapterCheck(domain)];
13118
+ checks.push(...domainChecks.map((check) => sanitizeAdapterCheck(check, domain, adapter2.id)));
13119
+ } catch {
13120
+ const safeAdapterId = redactIdentifier(adapter2.id);
13121
+ checks.push(makeCheck2(`${domain}-adapter`, "warn", `Optional ${domain} adapter failed`, "Adapter failed; details are intentionally hidden to avoid leaking private refs or credentials.", {
13122
+ optional: true,
13123
+ source: `adapter:${safeAdapterId}`,
13124
+ data: { adapter: safeAdapterId, fallback: true }
13125
+ }));
13126
+ }
13127
+ }
13128
+ return checks;
13129
+ }
13130
+ function runDoctor(machineId = getLocalMachineId(), options = {}) {
13131
+ const now = options.now ?? new Date;
13132
+ const { manifest, info: manifestSource } = readManifestWithSource({ adapter: options.manifestAdapter ?? null });
12890
13133
  const commandChecks = runMachineCommand(machineId, buildDoctorCommand());
12891
13134
  const details = parseKeyValueOutput(commandChecks.stdout);
12892
13135
  const machineInManifest = manifest.machines.find((machine) => machine.id === machineId);
13136
+ const optionalAdapterChecks = options.includeOptionalAdapters === false ? [] : runOptionalAdapterChecks({
13137
+ machineId,
13138
+ manifest,
13139
+ manifestSource,
13140
+ commandDetails: details,
13141
+ now
13142
+ }, options.adapters ?? []);
12893
13143
  const checks = [
12894
- makeCheck2("manifest-entry", machineInManifest ? "ok" : "warn", machineInManifest ? "Machine exists in manifest" : "Machine missing from manifest", machineInManifest ? JSON.stringify(machineInManifest) : `No manifest entry for ${machineId}`),
12895
- makeCheck2("manifest-path", details["manifest_exists"] === "yes" ? "ok" : "warn", "Manifest path check", `${details["manifest_path"] || "unknown"} ${details["manifest_exists"] === "yes" ? "exists" : "missing"}`),
12896
- makeCheck2("db-path", details["db_exists"] === "yes" ? "ok" : "warn", "DB path check", `${details["db_path"] || "unknown"} ${details["db_exists"] === "yes" ? "exists" : "missing"}`),
12897
- makeCheck2("notifications-path", details["notifications_exists"] === "yes" ? "ok" : "warn", "Notifications path check", `${details["notifications_path"] || "unknown"} ${details["notifications_exists"] === "yes" ? "exists" : "missing"}`),
13144
+ makeCheck2("manifest-source", manifestSource.warnings.length > 0 ? "warn" : "ok", "Manifest source boundary", `${manifestSource.source.kind}:${manifestSource.source.ref} loaded from ${manifestSource.loadedFrom}`, {
13145
+ data: {
13146
+ source: manifestSource.source,
13147
+ loadedFrom: manifestSource.loadedFrom,
13148
+ fallbackSource: manifestSource.fallbackSource,
13149
+ warnings: manifestSource.warnings
13150
+ },
13151
+ remediation: manifestSource.warnings.length > 0 ? ["Provide a private manifest adapter or unset the private manifest ref to use the local manifest only."] : undefined
13152
+ }),
13153
+ 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}`, {
13154
+ data: {
13155
+ declared: Boolean(machineInManifest),
13156
+ machine: machineInManifest ? redactManifestForDiagnostics(machineInManifest) : null
13157
+ }
13158
+ }),
13159
+ 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"}`, {
13160
+ data: {
13161
+ path: redactPath(details["data_dir"] || "unknown"),
13162
+ exists: details["data_dir_exists"] === "yes"
13163
+ }
13164
+ }),
13165
+ makeCheck2("manifest-path", details["manifest_exists"] === "yes" ? "ok" : "warn", "Manifest path check", `${redactPath(details["manifest_path"] || "unknown")} ${details["manifest_exists"] === "yes" ? "exists" : "missing"}`, {
13166
+ data: {
13167
+ path: redactPath(details["manifest_path"] || "unknown"),
13168
+ exists: details["manifest_exists"] === "yes"
13169
+ }
13170
+ }),
13171
+ makeCheck2("db-path", details["db_exists"] === "yes" ? "ok" : "warn", "DB path check", `${redactPath(details["db_path"] || "unknown")} ${details["db_exists"] === "yes" ? "exists" : "missing"}`, {
13172
+ data: {
13173
+ path: redactPath(details["db_path"] || "unknown"),
13174
+ exists: details["db_exists"] === "yes"
13175
+ }
13176
+ }),
13177
+ makeCheck2("notifications-path", details["notifications_exists"] === "yes" ? "ok" : "warn", "Notifications path check", `${redactPath(details["notifications_path"] || "unknown")} ${details["notifications_exists"] === "yes" ? "exists" : "missing"}`, {
13178
+ data: {
13179
+ path: redactPath(details["notifications_path"] || "unknown"),
13180
+ exists: details["notifications_exists"] === "yes"
13181
+ }
13182
+ }),
12898
13183
  makeCheck2("bun", details["bun"] && details["bun"] !== "missing" ? "ok" : "fail", "Bun availability", details["bun"] || "missing"),
12899
13184
  makeCheck2("machines-cli", details["machines"] && details["machines"] !== "missing" ? "ok" : "warn", "machines CLI availability", details["machines"] || "missing"),
12900
13185
  makeCheck2("machines-agent-cli", details["machines_agent"] && details["machines_agent"] !== "missing" ? "ok" : "warn", "machines-agent availability", details["machines_agent"] || "missing"),
12901
13186
  makeCheck2("machines-mcp-cli", details["machines_mcp"] && details["machines_mcp"] !== "missing" ? "ok" : "warn", "machines-mcp availability", details["machines_mcp"] || "missing"),
12902
- makeCheck2("ssh", details["ssh"] === "ok" ? "ok" : "warn", "SSH availability", details["ssh"] || "missing")
13187
+ makeCheck2("ssh", details["ssh"] === "ok" ? "ok" : "warn", "SSH availability", details["ssh"] || "missing"),
13188
+ makeCheck2("sudo-noninteractive", details["sudo_noninteractive"] === "ok" ? "ok" : "warn", "Noninteractive sudo availability", details["sudo_noninteractive"] === "ok" ? "sudo -n is available" : "sudo -n unavailable; setup may require user-provided approval or password handling.", {
13189
+ data: { available: details["sudo_noninteractive"] === "ok" },
13190
+ remediation: details["sudo_noninteractive"] === "ok" ? undefined : ["Configure explicit sudo policy or run setup commands manually; do not store sudo passwords in public manifests."]
13191
+ }),
13192
+ makeCheck2("ssh-cert-support", details["ssh_cert_support"] === "ok" ? "ok" : "warn", "SSH certificate support", details["ssh_cert_support"] === "ok" ? "OpenSSH reports ed25519 certificate support" : "OpenSSH certificate support not detected.", {
13193
+ data: { supported: details["ssh_cert_support"] === "ok" },
13194
+ remediation: details["ssh_cert_support"] === "ok" ? undefined : ["Install or update OpenSSH before adopting SSH certificate auth for this machine."]
13195
+ }),
13196
+ makeCheck2("github-app-auth", details["github_app_ref"] === "configured" ? "ok" : "warn", "GitHub App auth references", details["github_app_ref"] === "configured" ? "GitHub App id and private-key reference are configured" : "GitHub App id/private-key reference missing; use secret references, not user tokens or raw private keys.", {
13197
+ data: {
13198
+ gh_cli: details["gh_cli"] && details["gh_cli"] !== "missing",
13199
+ gh_auth: details["gh_auth"] === "ok",
13200
+ app_ref_configured: details["github_app_ref"] === "configured"
13201
+ },
13202
+ remediation: details["github_app_ref"] === "configured" ? undefined : ["Set HASNA_GITHUB_APP_ID plus HASNA_GITHUB_APP_PRIVATE_KEY_REF or provide an equivalent open-secrets adapter."]
13203
+ }),
13204
+ ...optionalAdapterChecks
12903
13205
  ];
12904
13206
  return {
12905
13207
  machineId,
12906
13208
  source: commandChecks.source,
12907
- manifestPath: details["manifest_path"],
12908
- dbPath: details["db_path"],
12909
- notificationsPath: details["notifications_path"],
13209
+ schemaVersion: 1,
13210
+ generatedAt: now.toISOString(),
13211
+ manifestSource,
13212
+ manifestPath: details["manifest_path"] ? redactPath(details["manifest_path"]) : undefined,
13213
+ dbPath: details["db_path"] ? redactPath(details["db_path"]) : undefined,
13214
+ notificationsPath: details["notifications_path"] ? redactPath(details["notifications_path"]) : undefined,
12910
13215
  checks
12911
13216
  };
12912
13217
  }
@@ -13949,6 +14254,13 @@ function buildBaseSteps(machine) {
13949
14254
  manager: "apt",
13950
14255
  privileged: true
13951
14256
  });
14257
+ steps.push({
14258
+ id: "linux-update-downloads",
14259
+ title: "Enable Linux package list refresh and download-only upgrades",
14260
+ command: `printf '%s\\n' 'APT::Periodic::Update-Package-Lists "1";' 'APT::Periodic::Download-Upgradeable-Packages "1";' 'APT::Periodic::Unattended-Upgrade "0";' | sudo tee /etc/apt/apt.conf.d/20auto-upgrades >/dev/null`,
14261
+ manager: "apt",
14262
+ privileged: true
14263
+ });
13952
14264
  } else if (machine.platform === "macos") {
13953
14265
  steps.push({
13954
14266
  id: "brew-base",
@@ -13962,7 +14274,26 @@ function buildBaseSteps(machine) {
13962
14274
  command: "brew install git coreutils",
13963
14275
  manager: "brew"
13964
14276
  });
14277
+ steps.push({
14278
+ id: "macos-update-downloads",
14279
+ title: "Enable macOS update checks and downloads without automatic install",
14280
+ command: "sudo softwareupdate --schedule on && sudo defaults write /Library/Preferences/com.apple.SoftwareUpdate AutomaticCheckEnabled -bool true && sudo defaults write /Library/Preferences/com.apple.SoftwareUpdate AutomaticDownload -int 1 && sudo defaults write /Library/Preferences/com.apple.SoftwareUpdate AutomaticallyInstallMacOSUpdates -int 0",
14281
+ manager: "custom",
14282
+ privileged: true
14283
+ });
14284
+ steps.push({
14285
+ id: "macos-management-readiness",
14286
+ title: "Report Apple management readiness without enrolling devices",
14287
+ command: "profiles status -type enrollment 2>/dev/null || true",
14288
+ manager: "custom"
14289
+ });
13965
14290
  }
14291
+ steps.push({
14292
+ id: "github-app-auth-readiness",
14293
+ title: "Check GitHub CLI/App auth readiness without printing credentials",
14294
+ command: "command -v gh >/dev/null 2>&1 && gh auth status >/dev/null 2>&1 || true",
14295
+ manager: "custom"
14296
+ });
13966
14297
  return steps;
13967
14298
  }
13968
14299
  function buildPackageSteps(machine) {
@@ -14320,20 +14651,20 @@ function runSync(machineId, options = {}, runner = runMachineCommand) {
14320
14651
  return summary;
14321
14652
  }
14322
14653
  // src/commands/workspace.ts
14323
- function isRecord2(value) {
14654
+ function isRecord3(value) {
14324
14655
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
14325
14656
  }
14326
14657
  function cloneMetadata(metadata) {
14327
- return isRecord2(metadata) ? { ...metadata } : {};
14658
+ return isRecord3(metadata) ? { ...metadata } : {};
14328
14659
  }
14329
14660
  function mappedPath(metadata, field, key) {
14330
14661
  const container = metadata[field];
14331
- if (!isRecord2(container))
14662
+ if (!isRecord3(container))
14332
14663
  return null;
14333
14664
  const value = container[key];
14334
14665
  if (typeof value === "string" && value.trim())
14335
14666
  return value.trim();
14336
- if (isRecord2(value)) {
14667
+ if (isRecord3(value)) {
14337
14668
  const nested = value["path"] ?? value["root"] ?? value["workspacePath"] ?? value["workspace_path"];
14338
14669
  if (typeof nested === "string" && nested.trim())
14339
14670
  return nested.trim();
@@ -14342,7 +14673,7 @@ function mappedPath(metadata, field, key) {
14342
14673
  }
14343
14674
  function writeMappedPath(metadata, field, key, path) {
14344
14675
  const existing = metadata[field];
14345
- const container = isRecord2(existing) ? { ...existing } : {};
14676
+ const container = isRecord3(existing) ? { ...existing } : {};
14346
14677
  container[key] = path;
14347
14678
  metadata[field] = container;
14348
14679
  }
@@ -23696,10 +24027,18 @@ export {
23696
24027
  renderDomainMapping,
23697
24028
  renderDashboardHtml,
23698
24029
  removeNotificationChannel,
24030
+ redactSensitiveValue,
24031
+ redactPrivateRef,
24032
+ redactPath,
24033
+ redactMetadata,
24034
+ redactManifestForDiagnostics,
24035
+ redactIdentifier,
23699
24036
  recordSyncRun,
23700
24037
  recordSetupRun,
23701
24038
  readNotificationConfig,
24039
+ readManifestWithSource,
23702
24040
  readManifest,
24041
+ publicMetadataKeys,
23703
24042
  probeTmuxPane,
23704
24043
  parseStorageTables,
23705
24044
  parsePortOutput,
@@ -23717,6 +24056,7 @@ export {
23717
24056
  listHeartbeats,
23718
24057
  listDomainMappings,
23719
24058
  listApps,
24059
+ isSensitiveKey,
23720
24060
  getSyncMetaAll,
23721
24061
  getStorageStatus,
23722
24062
  getStoragePg,
@@ -23728,6 +24068,7 @@ export {
23728
24068
  getServeInfo,
23729
24069
  getPackageVersion,
23730
24070
  getNotificationsPath,
24071
+ getManifestSourceRef,
23731
24072
  getManifestPath,
23732
24073
  getManifestMachine,
23733
24074
  getMachinesConsumerCapabilities,
@@ -23779,6 +24120,9 @@ export {
23779
24120
  STORAGE_MODE_ENV,
23780
24121
  STORAGE_DATABASE_ENV,
23781
24122
  SCREEN_SECRET_NAMESPACE_ENV,
24123
+ REDACTED_VALUE,
24124
+ PRIVATE_MANIFEST_REF_ENV,
24125
+ PRIVATE_MANIFEST_BACKEND_ENV,
23782
24126
  MACHINE_MCP_TOOL_NAMES,
23783
24127
  MACHINES_STORAGE_TABLES,
23784
24128
  MACHINES_STORAGE_MODE_FALLBACK_ENV,
@@ -23797,6 +24141,7 @@ export {
23797
24141
  MACHINES_BACKUP_PREFIX_ENV,
23798
24142
  MACHINES_BACKUP_BUCKET_FALLBACK_ENV,
23799
24143
  MACHINES_BACKUP_BUCKET_ENV,
24144
+ DOCTOR_OPTIONAL_ADAPTER_DOMAINS,
23800
24145
  DEFAULT_SCREEN_SECRET_NAMESPACE,
23801
24146
  DEFAULT_MACHINE_RESOLVER_TTL_MS,
23802
24147
  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"}