@hasna/machines 0.0.35 → 0.0.37
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +16 -0
- package/dist/cli/index.js +61 -8
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/setup.d.ts.map +1 -1
- package/dist/index.js +61 -8
- package/dist/mcp/index.js +61 -8
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -59,10 +59,21 @@ machines self-test
|
|
|
59
59
|
`machines setup` is a dry-run plan by default. The generated playbook favors
|
|
60
60
|
idempotent operations (`mkdir -p`, command-existence guards, package-manager
|
|
61
61
|
installs) and only executes when both `--apply` and `--yes` are provided.
|
|
62
|
+
The default plan also adds update-check/download settings without enabling
|
|
63
|
+
automatic OS installation: Linux uses apt periodic download-only settings, and
|
|
64
|
+
macOS uses `softwareupdate`/`defaults` with `AutomaticallyInstallMacOSUpdates`
|
|
65
|
+
left disabled.
|
|
62
66
|
`doctor --json` includes public-safe source/ref diagnostics plus optional
|
|
63
67
|
adapter hook results for secrets, configs, monitors, repos, MCPs, and shield
|
|
64
68
|
checks. When no adapter is configured, those checks report a skipped fallback
|
|
65
69
|
instead of importing private dependencies.
|
|
70
|
+
It also reports noninteractive sudo readiness, SSH certificate support, and
|
|
71
|
+
GitHub App secret-reference readiness without printing credentials or private
|
|
72
|
+
keys.
|
|
73
|
+
|
|
74
|
+
Apple device management belongs in the private deployment layer. The public
|
|
75
|
+
setup plan can report enrollment status with `profiles status -type enrollment`,
|
|
76
|
+
but it does not enroll devices, install profiles, or publish team identifiers.
|
|
66
77
|
|
|
67
78
|
## Topology SDK
|
|
68
79
|
|
|
@@ -168,6 +179,11 @@ machines defaults to
|
|
|
168
179
|
`machines/screen-sharing/screen-<machine>-vnc-password`, or the namespace set in
|
|
169
180
|
`HASNA_MACHINES_SCREEN_SECRET_NAMESPACE`. The user comes from the manifest
|
|
170
181
|
(`metadata.user`) when present, or `--user`.
|
|
182
|
+
|
|
183
|
+
For GitHub automation, prefer GitHub App installation tokens over personal user
|
|
184
|
+
tokens. Public manifests and docs should store only opaque secret references
|
|
185
|
+
for the app id/private key material; private adapters or `open-secrets` should
|
|
186
|
+
resolve those references at runtime.
|
|
171
187
|
`screen-credentials` verifies the resolved user and secret key for a machine or
|
|
172
188
|
the full fleet without printing secret values.
|
|
173
189
|
|
package/dist/cli/index.js
CHANGED
|
@@ -8386,6 +8386,13 @@ function buildBaseSteps(machine) {
|
|
|
8386
8386
|
manager: "apt",
|
|
8387
8387
|
privileged: true
|
|
8388
8388
|
});
|
|
8389
|
+
steps.push({
|
|
8390
|
+
id: "linux-update-downloads",
|
|
8391
|
+
title: "Enable Linux package list refresh and download-only upgrades",
|
|
8392
|
+
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`,
|
|
8393
|
+
manager: "apt",
|
|
8394
|
+
privileged: true
|
|
8395
|
+
});
|
|
8389
8396
|
} else if (machine.platform === "macos") {
|
|
8390
8397
|
steps.push({
|
|
8391
8398
|
id: "brew-base",
|
|
@@ -8399,7 +8406,26 @@ function buildBaseSteps(machine) {
|
|
|
8399
8406
|
command: "brew install git coreutils",
|
|
8400
8407
|
manager: "brew"
|
|
8401
8408
|
});
|
|
8409
|
+
steps.push({
|
|
8410
|
+
id: "macos-update-downloads",
|
|
8411
|
+
title: "Enable macOS update checks and downloads without automatic install",
|
|
8412
|
+
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",
|
|
8413
|
+
manager: "custom",
|
|
8414
|
+
privileged: true
|
|
8415
|
+
});
|
|
8416
|
+
steps.push({
|
|
8417
|
+
id: "macos-management-readiness",
|
|
8418
|
+
title: "Report Apple management readiness without enrolling devices",
|
|
8419
|
+
command: "profiles status -type enrollment 2>/dev/null || true",
|
|
8420
|
+
manager: "custom"
|
|
8421
|
+
});
|
|
8402
8422
|
}
|
|
8423
|
+
steps.push({
|
|
8424
|
+
id: "github-app-auth-readiness",
|
|
8425
|
+
title: "Check GitHub CLI/App auth readiness without printing credentials",
|
|
8426
|
+
command: "command -v gh >/dev/null 2>&1 && gh auth status >/dev/null 2>&1 || true",
|
|
8427
|
+
manager: "custom"
|
|
8428
|
+
});
|
|
8403
8429
|
return steps;
|
|
8404
8430
|
}
|
|
8405
8431
|
function buildPackageSteps(machine) {
|
|
@@ -10192,7 +10218,12 @@ function buildDoctorCommand() {
|
|
|
10192
10218
|
`printf 'ssh=%s\\n' "$(command -v ssh >/dev/null 2>&1 && printf ok || printf missing)"`,
|
|
10193
10219
|
`printf 'machines=%s\\n' "$(command -v machines 2>/dev/null || printf missing)"`,
|
|
10194
10220
|
`printf 'machines_agent=%s\\n' "$(command -v machines-agent 2>/dev/null || printf missing)"`,
|
|
10195
|
-
`printf 'machines_mcp=%s\\n' "$(command -v machines-mcp 2>/dev/null || printf missing)"
|
|
10221
|
+
`printf 'machines_mcp=%s\\n' "$(command -v machines-mcp 2>/dev/null || printf missing)"`,
|
|
10222
|
+
`printf 'sudo_noninteractive=%s\\n' "$(sudo -n true >/dev/null 2>&1 && printf ok || printf unavailable)"`,
|
|
10223
|
+
`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)"`,
|
|
10224
|
+
`printf 'gh_cli=%s\\n' "$(command -v gh 2>/dev/null || printf missing)"`,
|
|
10225
|
+
`printf 'gh_auth=%s\\n' "$(gh auth status >/dev/null 2>&1 && printf ok || printf unavailable)"`,
|
|
10226
|
+
"printf 'github_app_ref=%s\\n' \"$(test -n \\\"${HASNA_GITHUB_APP_ID:-}\\\" -a -n \\\"${HASNA_GITHUB_APP_PRIVATE_KEY_REF:-}\\\" && printf configured || printf missing)\""
|
|
10196
10227
|
].join("; ");
|
|
10197
10228
|
}
|
|
10198
10229
|
function fallbackAdapterCheck(domain) {
|
|
@@ -10235,14 +10266,20 @@ function runOptionalAdapterChecks(context, adapters) {
|
|
|
10235
10266
|
}
|
|
10236
10267
|
return checks;
|
|
10237
10268
|
}
|
|
10238
|
-
function runDoctor(machineId
|
|
10269
|
+
function runDoctor(machineId, options = {}) {
|
|
10270
|
+
const implicitLocalMachine = !machineId;
|
|
10271
|
+
const requestedMachineId = machineId ?? getLocalMachineId();
|
|
10272
|
+
const reportedMachineId = implicitLocalMachine ? "local" : requestedMachineId;
|
|
10239
10273
|
const now = options.now ?? new Date;
|
|
10240
10274
|
const { manifest, info: manifestSource } = readManifestWithSource({ adapter: options.manifestAdapter ?? null });
|
|
10241
|
-
const commandChecks = runMachineCommand(
|
|
10275
|
+
const commandChecks = runMachineCommand(requestedMachineId, buildDoctorCommand());
|
|
10242
10276
|
const details = parseKeyValueOutput(commandChecks.stdout);
|
|
10243
|
-
const machineInManifest = manifest.machines.find((machine) => machine.id ===
|
|
10277
|
+
const machineInManifest = manifest.machines.find((machine) => machine.id === requestedMachineId);
|
|
10278
|
+
const diagnosticMachine = machineInManifest ? redactManifestForDiagnostics(machineInManifest) : null;
|
|
10279
|
+
if (implicitLocalMachine && diagnosticMachine)
|
|
10280
|
+
diagnosticMachine.id = reportedMachineId;
|
|
10244
10281
|
const optionalAdapterChecks = options.includeOptionalAdapters === false ? [] : runOptionalAdapterChecks({
|
|
10245
|
-
machineId,
|
|
10282
|
+
machineId: requestedMachineId,
|
|
10246
10283
|
manifest,
|
|
10247
10284
|
manifestSource,
|
|
10248
10285
|
commandDetails: details,
|
|
@@ -10258,10 +10295,10 @@ function runDoctor(machineId = getLocalMachineId(), options = {}) {
|
|
|
10258
10295
|
},
|
|
10259
10296
|
remediation: manifestSource.warnings.length > 0 ? ["Provide a private manifest adapter or unset the private manifest ref to use the local manifest only."] : undefined
|
|
10260
10297
|
}),
|
|
10261
|
-
makeCheck2("manifest-entry", machineInManifest ? "ok" : "warn", machineInManifest ? "Machine exists in manifest" : "Machine missing from manifest",
|
|
10298
|
+
makeCheck2("manifest-entry", machineInManifest ? "ok" : "warn", machineInManifest ? "Machine exists in manifest" : "Machine missing from manifest", diagnosticMachine ? JSON.stringify(diagnosticMachine) : `No manifest entry for ${reportedMachineId}`, {
|
|
10262
10299
|
data: {
|
|
10263
10300
|
declared: Boolean(machineInManifest),
|
|
10264
|
-
machine:
|
|
10301
|
+
machine: diagnosticMachine
|
|
10265
10302
|
}
|
|
10266
10303
|
}),
|
|
10267
10304
|
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"}`, {
|
|
@@ -10293,10 +10330,26 @@ function runDoctor(machineId = getLocalMachineId(), options = {}) {
|
|
|
10293
10330
|
makeCheck2("machines-agent-cli", details["machines_agent"] && details["machines_agent"] !== "missing" ? "ok" : "warn", "machines-agent availability", details["machines_agent"] || "missing"),
|
|
10294
10331
|
makeCheck2("machines-mcp-cli", details["machines_mcp"] && details["machines_mcp"] !== "missing" ? "ok" : "warn", "machines-mcp availability", details["machines_mcp"] || "missing"),
|
|
10295
10332
|
makeCheck2("ssh", details["ssh"] === "ok" ? "ok" : "warn", "SSH availability", details["ssh"] || "missing"),
|
|
10333
|
+
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.", {
|
|
10334
|
+
data: { available: details["sudo_noninteractive"] === "ok" },
|
|
10335
|
+
remediation: details["sudo_noninteractive"] === "ok" ? undefined : ["Configure explicit sudo policy or run setup commands manually; do not store sudo passwords in public manifests."]
|
|
10336
|
+
}),
|
|
10337
|
+
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.", {
|
|
10338
|
+
data: { supported: details["ssh_cert_support"] === "ok" },
|
|
10339
|
+
remediation: details["ssh_cert_support"] === "ok" ? undefined : ["Install or update OpenSSH before adopting SSH certificate auth for this machine."]
|
|
10340
|
+
}),
|
|
10341
|
+
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.", {
|
|
10342
|
+
data: {
|
|
10343
|
+
gh_cli: details["gh_cli"] && details["gh_cli"] !== "missing",
|
|
10344
|
+
gh_auth: details["gh_auth"] === "ok",
|
|
10345
|
+
app_ref_configured: details["github_app_ref"] === "configured"
|
|
10346
|
+
},
|
|
10347
|
+
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."]
|
|
10348
|
+
}),
|
|
10296
10349
|
...optionalAdapterChecks
|
|
10297
10350
|
];
|
|
10298
10351
|
return {
|
|
10299
|
-
machineId,
|
|
10352
|
+
machineId: reportedMachineId,
|
|
10300
10353
|
source: commandChecks.source,
|
|
10301
10354
|
schemaVersion: 1,
|
|
10302
10355
|
generatedAt: now.toISOString(),
|
|
@@ -1 +1 @@
|
|
|
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;
|
|
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;AAuHD,wBAAgB,SAAS,CAAC,SAAS,CAAC,EAAE,MAAM,EAAE,OAAO,GAAE,aAAkB,GAAG,YAAY,CA6LvF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"setup.d.ts","sourceRoot":"","sources":["../../src/commands/setup.ts"],"names":[],"mappings":"AAGA,OAAO,EAAoD,KAAK,oBAAoB,EAAE,MAAM,cAAc,CAAC;AAC3G,OAAO,KAAK,EAAmB,WAAW,EAAa,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"setup.d.ts","sourceRoot":"","sources":["../../src/commands/setup.ts"],"names":[],"mappings":"AAGA,OAAO,EAAoD,KAAK,oBAAoB,EAAE,MAAM,cAAc,CAAC;AAC3G,OAAO,KAAK,EAAmB,WAAW,EAAa,MAAM,aAAa,CAAC;AAiG3E,wBAAgB,cAAc,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,WAAW,CAyB9D;AAED,wBAAgB,QAAQ,CACtB,SAAS,CAAC,EAAE,MAAM,EAClB,OAAO,GAAE;IAAE,KAAK,CAAC,EAAE,OAAO,CAAC;IAAC,GAAG,CAAC,EAAE,OAAO,CAAA;CAAO,EAChD,MAAM,GAAE,oBAAwC,GAC/C,WAAW,CAmCb"}
|
package/dist/index.js
CHANGED
|
@@ -13079,7 +13079,12 @@ function buildDoctorCommand() {
|
|
|
13079
13079
|
`printf 'ssh=%s\\n' "$(command -v ssh >/dev/null 2>&1 && printf ok || printf missing)"`,
|
|
13080
13080
|
`printf 'machines=%s\\n' "$(command -v machines 2>/dev/null || printf missing)"`,
|
|
13081
13081
|
`printf 'machines_agent=%s\\n' "$(command -v machines-agent 2>/dev/null || printf missing)"`,
|
|
13082
|
-
`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)\""
|
|
13083
13088
|
].join("; ");
|
|
13084
13089
|
}
|
|
13085
13090
|
function fallbackAdapterCheck(domain) {
|
|
@@ -13122,14 +13127,20 @@ function runOptionalAdapterChecks(context, adapters) {
|
|
|
13122
13127
|
}
|
|
13123
13128
|
return checks;
|
|
13124
13129
|
}
|
|
13125
|
-
function runDoctor(machineId
|
|
13130
|
+
function runDoctor(machineId, options = {}) {
|
|
13131
|
+
const implicitLocalMachine = !machineId;
|
|
13132
|
+
const requestedMachineId = machineId ?? getLocalMachineId();
|
|
13133
|
+
const reportedMachineId = implicitLocalMachine ? "local" : requestedMachineId;
|
|
13126
13134
|
const now = options.now ?? new Date;
|
|
13127
13135
|
const { manifest, info: manifestSource } = readManifestWithSource({ adapter: options.manifestAdapter ?? null });
|
|
13128
|
-
const commandChecks = runMachineCommand(
|
|
13136
|
+
const commandChecks = runMachineCommand(requestedMachineId, buildDoctorCommand());
|
|
13129
13137
|
const details = parseKeyValueOutput(commandChecks.stdout);
|
|
13130
|
-
const machineInManifest = manifest.machines.find((machine) => machine.id ===
|
|
13138
|
+
const machineInManifest = manifest.machines.find((machine) => machine.id === requestedMachineId);
|
|
13139
|
+
const diagnosticMachine = machineInManifest ? redactManifestForDiagnostics(machineInManifest) : null;
|
|
13140
|
+
if (implicitLocalMachine && diagnosticMachine)
|
|
13141
|
+
diagnosticMachine.id = reportedMachineId;
|
|
13131
13142
|
const optionalAdapterChecks = options.includeOptionalAdapters === false ? [] : runOptionalAdapterChecks({
|
|
13132
|
-
machineId,
|
|
13143
|
+
machineId: requestedMachineId,
|
|
13133
13144
|
manifest,
|
|
13134
13145
|
manifestSource,
|
|
13135
13146
|
commandDetails: details,
|
|
@@ -13145,10 +13156,10 @@ function runDoctor(machineId = getLocalMachineId(), options = {}) {
|
|
|
13145
13156
|
},
|
|
13146
13157
|
remediation: manifestSource.warnings.length > 0 ? ["Provide a private manifest adapter or unset the private manifest ref to use the local manifest only."] : undefined
|
|
13147
13158
|
}),
|
|
13148
|
-
makeCheck2("manifest-entry", machineInManifest ? "ok" : "warn", machineInManifest ? "Machine exists in manifest" : "Machine missing from manifest",
|
|
13159
|
+
makeCheck2("manifest-entry", machineInManifest ? "ok" : "warn", machineInManifest ? "Machine exists in manifest" : "Machine missing from manifest", diagnosticMachine ? JSON.stringify(diagnosticMachine) : `No manifest entry for ${reportedMachineId}`, {
|
|
13149
13160
|
data: {
|
|
13150
13161
|
declared: Boolean(machineInManifest),
|
|
13151
|
-
machine:
|
|
13162
|
+
machine: diagnosticMachine
|
|
13152
13163
|
}
|
|
13153
13164
|
}),
|
|
13154
13165
|
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"}`, {
|
|
@@ -13180,10 +13191,26 @@ function runDoctor(machineId = getLocalMachineId(), options = {}) {
|
|
|
13180
13191
|
makeCheck2("machines-agent-cli", details["machines_agent"] && details["machines_agent"] !== "missing" ? "ok" : "warn", "machines-agent availability", details["machines_agent"] || "missing"),
|
|
13181
13192
|
makeCheck2("machines-mcp-cli", details["machines_mcp"] && details["machines_mcp"] !== "missing" ? "ok" : "warn", "machines-mcp availability", details["machines_mcp"] || "missing"),
|
|
13182
13193
|
makeCheck2("ssh", details["ssh"] === "ok" ? "ok" : "warn", "SSH availability", details["ssh"] || "missing"),
|
|
13194
|
+
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.", {
|
|
13195
|
+
data: { available: details["sudo_noninteractive"] === "ok" },
|
|
13196
|
+
remediation: details["sudo_noninteractive"] === "ok" ? undefined : ["Configure explicit sudo policy or run setup commands manually; do not store sudo passwords in public manifests."]
|
|
13197
|
+
}),
|
|
13198
|
+
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.", {
|
|
13199
|
+
data: { supported: details["ssh_cert_support"] === "ok" },
|
|
13200
|
+
remediation: details["ssh_cert_support"] === "ok" ? undefined : ["Install or update OpenSSH before adopting SSH certificate auth for this machine."]
|
|
13201
|
+
}),
|
|
13202
|
+
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.", {
|
|
13203
|
+
data: {
|
|
13204
|
+
gh_cli: details["gh_cli"] && details["gh_cli"] !== "missing",
|
|
13205
|
+
gh_auth: details["gh_auth"] === "ok",
|
|
13206
|
+
app_ref_configured: details["github_app_ref"] === "configured"
|
|
13207
|
+
},
|
|
13208
|
+
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."]
|
|
13209
|
+
}),
|
|
13183
13210
|
...optionalAdapterChecks
|
|
13184
13211
|
];
|
|
13185
13212
|
return {
|
|
13186
|
-
machineId,
|
|
13213
|
+
machineId: reportedMachineId,
|
|
13187
13214
|
source: commandChecks.source,
|
|
13188
13215
|
schemaVersion: 1,
|
|
13189
13216
|
generatedAt: now.toISOString(),
|
|
@@ -14233,6 +14260,13 @@ function buildBaseSteps(machine) {
|
|
|
14233
14260
|
manager: "apt",
|
|
14234
14261
|
privileged: true
|
|
14235
14262
|
});
|
|
14263
|
+
steps.push({
|
|
14264
|
+
id: "linux-update-downloads",
|
|
14265
|
+
title: "Enable Linux package list refresh and download-only upgrades",
|
|
14266
|
+
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`,
|
|
14267
|
+
manager: "apt",
|
|
14268
|
+
privileged: true
|
|
14269
|
+
});
|
|
14236
14270
|
} else if (machine.platform === "macos") {
|
|
14237
14271
|
steps.push({
|
|
14238
14272
|
id: "brew-base",
|
|
@@ -14246,7 +14280,26 @@ function buildBaseSteps(machine) {
|
|
|
14246
14280
|
command: "brew install git coreutils",
|
|
14247
14281
|
manager: "brew"
|
|
14248
14282
|
});
|
|
14283
|
+
steps.push({
|
|
14284
|
+
id: "macos-update-downloads",
|
|
14285
|
+
title: "Enable macOS update checks and downloads without automatic install",
|
|
14286
|
+
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",
|
|
14287
|
+
manager: "custom",
|
|
14288
|
+
privileged: true
|
|
14289
|
+
});
|
|
14290
|
+
steps.push({
|
|
14291
|
+
id: "macos-management-readiness",
|
|
14292
|
+
title: "Report Apple management readiness without enrolling devices",
|
|
14293
|
+
command: "profiles status -type enrollment 2>/dev/null || true",
|
|
14294
|
+
manager: "custom"
|
|
14295
|
+
});
|
|
14249
14296
|
}
|
|
14297
|
+
steps.push({
|
|
14298
|
+
id: "github-app-auth-readiness",
|
|
14299
|
+
title: "Check GitHub CLI/App auth readiness without printing credentials",
|
|
14300
|
+
command: "command -v gh >/dev/null 2>&1 && gh auth status >/dev/null 2>&1 || true",
|
|
14301
|
+
manager: "custom"
|
|
14302
|
+
});
|
|
14250
14303
|
return steps;
|
|
14251
14304
|
}
|
|
14252
14305
|
function buildPackageSteps(machine) {
|
package/dist/mcp/index.js
CHANGED
|
@@ -5825,7 +5825,12 @@ function buildDoctorCommand() {
|
|
|
5825
5825
|
`printf 'ssh=%s\\n' "$(command -v ssh >/dev/null 2>&1 && printf ok || printf missing)"`,
|
|
5826
5826
|
`printf 'machines=%s\\n' "$(command -v machines 2>/dev/null || printf missing)"`,
|
|
5827
5827
|
`printf 'machines_agent=%s\\n' "$(command -v machines-agent 2>/dev/null || printf missing)"`,
|
|
5828
|
-
`printf 'machines_mcp=%s\\n' "$(command -v machines-mcp 2>/dev/null || printf missing)"
|
|
5828
|
+
`printf 'machines_mcp=%s\\n' "$(command -v machines-mcp 2>/dev/null || printf missing)"`,
|
|
5829
|
+
`printf 'sudo_noninteractive=%s\\n' "$(sudo -n true >/dev/null 2>&1 && printf ok || printf unavailable)"`,
|
|
5830
|
+
`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)"`,
|
|
5831
|
+
`printf 'gh_cli=%s\\n' "$(command -v gh 2>/dev/null || printf missing)"`,
|
|
5832
|
+
`printf 'gh_auth=%s\\n' "$(gh auth status >/dev/null 2>&1 && printf ok || printf unavailable)"`,
|
|
5833
|
+
"printf 'github_app_ref=%s\\n' \"$(test -n \\\"${HASNA_GITHUB_APP_ID:-}\\\" -a -n \\\"${HASNA_GITHUB_APP_PRIVATE_KEY_REF:-}\\\" && printf configured || printf missing)\""
|
|
5829
5834
|
].join("; ");
|
|
5830
5835
|
}
|
|
5831
5836
|
function fallbackAdapterCheck(domain) {
|
|
@@ -5868,14 +5873,20 @@ function runOptionalAdapterChecks(context, adapters) {
|
|
|
5868
5873
|
}
|
|
5869
5874
|
return checks;
|
|
5870
5875
|
}
|
|
5871
|
-
function runDoctor(machineId
|
|
5876
|
+
function runDoctor(machineId, options = {}) {
|
|
5877
|
+
const implicitLocalMachine = !machineId;
|
|
5878
|
+
const requestedMachineId = machineId ?? getLocalMachineId();
|
|
5879
|
+
const reportedMachineId = implicitLocalMachine ? "local" : requestedMachineId;
|
|
5872
5880
|
const now = options.now ?? new Date;
|
|
5873
5881
|
const { manifest, info: manifestSource } = readManifestWithSource({ adapter: options.manifestAdapter ?? null });
|
|
5874
|
-
const commandChecks = runMachineCommand(
|
|
5882
|
+
const commandChecks = runMachineCommand(requestedMachineId, buildDoctorCommand());
|
|
5875
5883
|
const details = parseKeyValueOutput(commandChecks.stdout);
|
|
5876
|
-
const machineInManifest = manifest.machines.find((machine) => machine.id ===
|
|
5884
|
+
const machineInManifest = manifest.machines.find((machine) => machine.id === requestedMachineId);
|
|
5885
|
+
const diagnosticMachine = machineInManifest ? redactManifestForDiagnostics(machineInManifest) : null;
|
|
5886
|
+
if (implicitLocalMachine && diagnosticMachine)
|
|
5887
|
+
diagnosticMachine.id = reportedMachineId;
|
|
5877
5888
|
const optionalAdapterChecks = options.includeOptionalAdapters === false ? [] : runOptionalAdapterChecks({
|
|
5878
|
-
machineId,
|
|
5889
|
+
machineId: requestedMachineId,
|
|
5879
5890
|
manifest,
|
|
5880
5891
|
manifestSource,
|
|
5881
5892
|
commandDetails: details,
|
|
@@ -5891,10 +5902,10 @@ function runDoctor(machineId = getLocalMachineId(), options = {}) {
|
|
|
5891
5902
|
},
|
|
5892
5903
|
remediation: manifestSource.warnings.length > 0 ? ["Provide a private manifest adapter or unset the private manifest ref to use the local manifest only."] : undefined
|
|
5893
5904
|
}),
|
|
5894
|
-
makeCheck("manifest-entry", machineInManifest ? "ok" : "warn", machineInManifest ? "Machine exists in manifest" : "Machine missing from manifest",
|
|
5905
|
+
makeCheck("manifest-entry", machineInManifest ? "ok" : "warn", machineInManifest ? "Machine exists in manifest" : "Machine missing from manifest", diagnosticMachine ? JSON.stringify(diagnosticMachine) : `No manifest entry for ${reportedMachineId}`, {
|
|
5895
5906
|
data: {
|
|
5896
5907
|
declared: Boolean(machineInManifest),
|
|
5897
|
-
machine:
|
|
5908
|
+
machine: diagnosticMachine
|
|
5898
5909
|
}
|
|
5899
5910
|
}),
|
|
5900
5911
|
makeCheck("data-dir", details["data_dir_exists"] === "yes" ? "ok" : "warn", "Data directory check", `${redactPath(details["data_dir"] || "unknown")} ${details["data_dir_exists"] === "yes" ? "exists" : "missing"}`, {
|
|
@@ -5926,10 +5937,26 @@ function runDoctor(machineId = getLocalMachineId(), options = {}) {
|
|
|
5926
5937
|
makeCheck("machines-agent-cli", details["machines_agent"] && details["machines_agent"] !== "missing" ? "ok" : "warn", "machines-agent availability", details["machines_agent"] || "missing"),
|
|
5927
5938
|
makeCheck("machines-mcp-cli", details["machines_mcp"] && details["machines_mcp"] !== "missing" ? "ok" : "warn", "machines-mcp availability", details["machines_mcp"] || "missing"),
|
|
5928
5939
|
makeCheck("ssh", details["ssh"] === "ok" ? "ok" : "warn", "SSH availability", details["ssh"] || "missing"),
|
|
5940
|
+
makeCheck("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.", {
|
|
5941
|
+
data: { available: details["sudo_noninteractive"] === "ok" },
|
|
5942
|
+
remediation: details["sudo_noninteractive"] === "ok" ? undefined : ["Configure explicit sudo policy or run setup commands manually; do not store sudo passwords in public manifests."]
|
|
5943
|
+
}),
|
|
5944
|
+
makeCheck("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.", {
|
|
5945
|
+
data: { supported: details["ssh_cert_support"] === "ok" },
|
|
5946
|
+
remediation: details["ssh_cert_support"] === "ok" ? undefined : ["Install or update OpenSSH before adopting SSH certificate auth for this machine."]
|
|
5947
|
+
}),
|
|
5948
|
+
makeCheck("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.", {
|
|
5949
|
+
data: {
|
|
5950
|
+
gh_cli: details["gh_cli"] && details["gh_cli"] !== "missing",
|
|
5951
|
+
gh_auth: details["gh_auth"] === "ok",
|
|
5952
|
+
app_ref_configured: details["github_app_ref"] === "configured"
|
|
5953
|
+
},
|
|
5954
|
+
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."]
|
|
5955
|
+
}),
|
|
5929
5956
|
...optionalAdapterChecks
|
|
5930
5957
|
];
|
|
5931
5958
|
return {
|
|
5932
|
-
machineId,
|
|
5959
|
+
machineId: reportedMachineId,
|
|
5933
5960
|
source: commandChecks.source,
|
|
5934
5961
|
schemaVersion: 1,
|
|
5935
5962
|
generatedAt: now.toISOString(),
|
|
@@ -6720,6 +6747,13 @@ function buildBaseSteps(machine) {
|
|
|
6720
6747
|
manager: "apt",
|
|
6721
6748
|
privileged: true
|
|
6722
6749
|
});
|
|
6750
|
+
steps.push({
|
|
6751
|
+
id: "linux-update-downloads",
|
|
6752
|
+
title: "Enable Linux package list refresh and download-only upgrades",
|
|
6753
|
+
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`,
|
|
6754
|
+
manager: "apt",
|
|
6755
|
+
privileged: true
|
|
6756
|
+
});
|
|
6723
6757
|
} else if (machine.platform === "macos") {
|
|
6724
6758
|
steps.push({
|
|
6725
6759
|
id: "brew-base",
|
|
@@ -6733,7 +6767,26 @@ function buildBaseSteps(machine) {
|
|
|
6733
6767
|
command: "brew install git coreutils",
|
|
6734
6768
|
manager: "brew"
|
|
6735
6769
|
});
|
|
6770
|
+
steps.push({
|
|
6771
|
+
id: "macos-update-downloads",
|
|
6772
|
+
title: "Enable macOS update checks and downloads without automatic install",
|
|
6773
|
+
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",
|
|
6774
|
+
manager: "custom",
|
|
6775
|
+
privileged: true
|
|
6776
|
+
});
|
|
6777
|
+
steps.push({
|
|
6778
|
+
id: "macos-management-readiness",
|
|
6779
|
+
title: "Report Apple management readiness without enrolling devices",
|
|
6780
|
+
command: "profiles status -type enrollment 2>/dev/null || true",
|
|
6781
|
+
manager: "custom"
|
|
6782
|
+
});
|
|
6736
6783
|
}
|
|
6784
|
+
steps.push({
|
|
6785
|
+
id: "github-app-auth-readiness",
|
|
6786
|
+
title: "Check GitHub CLI/App auth readiness without printing credentials",
|
|
6787
|
+
command: "command -v gh >/dev/null 2>&1 && gh auth status >/dev/null 2>&1 || true",
|
|
6788
|
+
manager: "custom"
|
|
6789
|
+
});
|
|
6737
6790
|
return steps;
|
|
6738
6791
|
}
|
|
6739
6792
|
function buildPackageSteps(machine) {
|