@hasna/machines 0.0.34 → 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/README.md +17 -0
- package/dist/cli/index.js +310 -24
- package/dist/commands/doctor.d.ts +23 -2
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/consumer.js +188 -9
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +322 -24
- package/dist/manifests.d.ts +22 -1
- package/dist/manifests.d.ts.map +1 -1
- package/dist/mcp/index.js +303 -19
- package/dist/redaction.d.ts +11 -0
- package/dist/redaction.d.ts.map +1 -0
- package/dist/topology.d.ts.map +1 -1
- package/dist/types.d.ts +21 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/scripts/consumer-conformance.mjs +3 -2
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
|
|
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 (!
|
|
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 (
|
|
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
|
|
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
|
-
|
|
12863
|
-
|
|
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
|
-
|
|
12867
|
-
|
|
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)"`,
|
|
@@ -12885,28 +13082,115 @@ function buildDoctorCommand() {
|
|
|
12885
13082
|
`printf 'machines_mcp=%s\\n' "$(command -v machines-mcp 2>/dev/null || printf missing)"`
|
|
12886
13083
|
].join("; ");
|
|
12887
13084
|
}
|
|
12888
|
-
function
|
|
12889
|
-
|
|
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 });
|
|
12890
13128
|
const commandChecks = runMachineCommand(machineId, buildDoctorCommand());
|
|
12891
13129
|
const details = parseKeyValueOutput(commandChecks.stdout);
|
|
12892
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 ?? []);
|
|
12893
13138
|
const checks = [
|
|
12894
|
-
makeCheck2("manifest-
|
|
12895
|
-
|
|
12896
|
-
|
|
12897
|
-
|
|
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
|
+
}),
|
|
12898
13178
|
makeCheck2("bun", details["bun"] && details["bun"] !== "missing" ? "ok" : "fail", "Bun availability", details["bun"] || "missing"),
|
|
12899
13179
|
makeCheck2("machines-cli", details["machines"] && details["machines"] !== "missing" ? "ok" : "warn", "machines CLI availability", details["machines"] || "missing"),
|
|
12900
13180
|
makeCheck2("machines-agent-cli", details["machines_agent"] && details["machines_agent"] !== "missing" ? "ok" : "warn", "machines-agent availability", details["machines_agent"] || "missing"),
|
|
12901
13181
|
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")
|
|
13182
|
+
makeCheck2("ssh", details["ssh"] === "ok" ? "ok" : "warn", "SSH availability", details["ssh"] || "missing"),
|
|
13183
|
+
...optionalAdapterChecks
|
|
12903
13184
|
];
|
|
12904
13185
|
return {
|
|
12905
13186
|
machineId,
|
|
12906
13187
|
source: commandChecks.source,
|
|
12907
|
-
|
|
12908
|
-
|
|
12909
|
-
|
|
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,
|
|
12910
13194
|
checks
|
|
12911
13195
|
};
|
|
12912
13196
|
}
|
|
@@ -14320,20 +14604,20 @@ function runSync(machineId, options = {}, runner = runMachineCommand) {
|
|
|
14320
14604
|
return summary;
|
|
14321
14605
|
}
|
|
14322
14606
|
// src/commands/workspace.ts
|
|
14323
|
-
function
|
|
14607
|
+
function isRecord3(value) {
|
|
14324
14608
|
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
14325
14609
|
}
|
|
14326
14610
|
function cloneMetadata(metadata) {
|
|
14327
|
-
return
|
|
14611
|
+
return isRecord3(metadata) ? { ...metadata } : {};
|
|
14328
14612
|
}
|
|
14329
14613
|
function mappedPath(metadata, field, key) {
|
|
14330
14614
|
const container = metadata[field];
|
|
14331
|
-
if (!
|
|
14615
|
+
if (!isRecord3(container))
|
|
14332
14616
|
return null;
|
|
14333
14617
|
const value = container[key];
|
|
14334
14618
|
if (typeof value === "string" && value.trim())
|
|
14335
14619
|
return value.trim();
|
|
14336
|
-
if (
|
|
14620
|
+
if (isRecord3(value)) {
|
|
14337
14621
|
const nested = value["path"] ?? value["root"] ?? value["workspacePath"] ?? value["workspace_path"];
|
|
14338
14622
|
if (typeof nested === "string" && nested.trim())
|
|
14339
14623
|
return nested.trim();
|
|
@@ -14342,7 +14626,7 @@ function mappedPath(metadata, field, key) {
|
|
|
14342
14626
|
}
|
|
14343
14627
|
function writeMappedPath(metadata, field, key, path) {
|
|
14344
14628
|
const existing = metadata[field];
|
|
14345
|
-
const container =
|
|
14629
|
+
const container = isRecord3(existing) ? { ...existing } : {};
|
|
14346
14630
|
container[key] = path;
|
|
14347
14631
|
metadata[field] = container;
|
|
14348
14632
|
}
|
|
@@ -23696,10 +23980,18 @@ export {
|
|
|
23696
23980
|
renderDomainMapping,
|
|
23697
23981
|
renderDashboardHtml,
|
|
23698
23982
|
removeNotificationChannel,
|
|
23983
|
+
redactSensitiveValue,
|
|
23984
|
+
redactPrivateRef,
|
|
23985
|
+
redactPath,
|
|
23986
|
+
redactMetadata,
|
|
23987
|
+
redactManifestForDiagnostics,
|
|
23988
|
+
redactIdentifier,
|
|
23699
23989
|
recordSyncRun,
|
|
23700
23990
|
recordSetupRun,
|
|
23701
23991
|
readNotificationConfig,
|
|
23992
|
+
readManifestWithSource,
|
|
23702
23993
|
readManifest,
|
|
23994
|
+
publicMetadataKeys,
|
|
23703
23995
|
probeTmuxPane,
|
|
23704
23996
|
parseStorageTables,
|
|
23705
23997
|
parsePortOutput,
|
|
@@ -23717,6 +24009,7 @@ export {
|
|
|
23717
24009
|
listHeartbeats,
|
|
23718
24010
|
listDomainMappings,
|
|
23719
24011
|
listApps,
|
|
24012
|
+
isSensitiveKey,
|
|
23720
24013
|
getSyncMetaAll,
|
|
23721
24014
|
getStorageStatus,
|
|
23722
24015
|
getStoragePg,
|
|
@@ -23728,6 +24021,7 @@ export {
|
|
|
23728
24021
|
getServeInfo,
|
|
23729
24022
|
getPackageVersion,
|
|
23730
24023
|
getNotificationsPath,
|
|
24024
|
+
getManifestSourceRef,
|
|
23731
24025
|
getManifestPath,
|
|
23732
24026
|
getManifestMachine,
|
|
23733
24027
|
getMachinesConsumerCapabilities,
|
|
@@ -23779,6 +24073,9 @@ export {
|
|
|
23779
24073
|
STORAGE_MODE_ENV,
|
|
23780
24074
|
STORAGE_DATABASE_ENV,
|
|
23781
24075
|
SCREEN_SECRET_NAMESPACE_ENV,
|
|
24076
|
+
REDACTED_VALUE,
|
|
24077
|
+
PRIVATE_MANIFEST_REF_ENV,
|
|
24078
|
+
PRIVATE_MANIFEST_BACKEND_ENV,
|
|
23782
24079
|
MACHINE_MCP_TOOL_NAMES,
|
|
23783
24080
|
MACHINES_STORAGE_TABLES,
|
|
23784
24081
|
MACHINES_STORAGE_MODE_FALLBACK_ENV,
|
|
@@ -23797,6 +24094,7 @@ export {
|
|
|
23797
24094
|
MACHINES_BACKUP_PREFIX_ENV,
|
|
23798
24095
|
MACHINES_BACKUP_BUCKET_FALLBACK_ENV,
|
|
23799
24096
|
MACHINES_BACKUP_BUCKET_ENV,
|
|
24097
|
+
DOCTOR_OPTIONAL_ADAPTER_DOMAINS,
|
|
23800
24098
|
DEFAULT_SCREEN_SECRET_NAMESPACE,
|
|
23801
24099
|
DEFAULT_MACHINE_RESOLVER_TTL_MS,
|
|
23802
24100
|
DEFAULT_BACKUP_PREFIX,
|
package/dist/manifests.d.ts
CHANGED
|
@@ -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;
|
package/dist/manifests.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"manifests.d.ts","sourceRoot":"","sources":["../src/manifests.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;
|
|
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"}
|