infracensus-collector 1.6.13 → 1.6.15
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/{chunk-ICHSZE34.js → chunk-4BC3DDUT.js} +2 -2
- package/dist/{chunk-YIMZFP5U.js → chunk-ALYLNNEP.js} +3 -3
- package/dist/chunk-SLZYU75K.js +297 -0
- package/dist/chunk-SLZYU75K.js.map +7 -0
- package/dist/cli.js +2 -2
- package/dist/index.js +104 -205
- package/dist/index.js.map +4 -4
- package/dist/sensitive-data-index.js +49 -2
- package/dist/sensitive-data-index.js.map +2 -2
- package/dist/{status-XO6RJFD5.js → status-TX2NT7XU.js} +3 -3
- package/package.json +1 -1
- package/dist/chunk-A6UFQKT6.js +0 -93
- package/dist/chunk-A6UFQKT6.js.map +0 -7
- /package/dist/{chunk-ICHSZE34.js.map → chunk-4BC3DDUT.js.map} +0 -0
- /package/dist/{chunk-YIMZFP5U.js.map → chunk-ALYLNNEP.js.map} +0 -0
- /package/dist/{status-XO6RJFD5.js.map → status-TX2NT7XU.js.map} +0 -0
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
collectorVersion,
|
|
4
4
|
findModule,
|
|
5
5
|
restartNeededNotice
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-ALYLNNEP.js";
|
|
7
7
|
import {
|
|
8
8
|
redactLocal
|
|
9
9
|
} from "./chunk-DIS3S4SY.js";
|
|
@@ -312,4 +312,4 @@ export {
|
|
|
312
312
|
condenseLogTail,
|
|
313
313
|
buildUiStatus
|
|
314
314
|
};
|
|
315
|
-
//# sourceMappingURL=chunk-
|
|
315
|
+
//# sourceMappingURL=chunk-4BC3DDUT.js.map
|
|
@@ -161,8 +161,8 @@ function findModule(key) {
|
|
|
161
161
|
import { readFileSync } from "node:fs";
|
|
162
162
|
import { join } from "node:path";
|
|
163
163
|
function collectorVersion() {
|
|
164
|
-
if ("1.6.
|
|
165
|
-
return "1.6.
|
|
164
|
+
if ("1.6.15".length > 0) {
|
|
165
|
+
return "1.6.15";
|
|
166
166
|
}
|
|
167
167
|
return installedVersion();
|
|
168
168
|
}
|
|
@@ -188,4 +188,4 @@ export {
|
|
|
188
188
|
installedVersion,
|
|
189
189
|
restartNeededNotice
|
|
190
190
|
};
|
|
191
|
-
//# sourceMappingURL=chunk-
|
|
191
|
+
//# sourceMappingURL=chunk-ALYLNNEP.js.map
|
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
import {
|
|
2
|
+
planRefusalFrom
|
|
3
|
+
} from "./chunk-CGNKICWK.js";
|
|
4
|
+
import {
|
|
5
|
+
COLLECTOR_AUTH_HEADER,
|
|
6
|
+
collectorBearer,
|
|
7
|
+
noteEntitlementRefusalFromPlane
|
|
8
|
+
} from "./chunk-M5LCDMNP.js";
|
|
9
|
+
import {
|
|
10
|
+
ApiError,
|
|
11
|
+
ClaimedJobResponse,
|
|
12
|
+
ROUTES,
|
|
13
|
+
ScanJobStatusResult
|
|
14
|
+
} from "./chunk-GRMRW56F.js";
|
|
15
|
+
import {
|
|
16
|
+
PKG_ROOT
|
|
17
|
+
} from "./chunk-XSHYWQAH.js";
|
|
18
|
+
|
|
19
|
+
// src/core/sddJob.ts
|
|
20
|
+
import { spawn } from "node:child_process";
|
|
21
|
+
import { existsSync } from "node:fs";
|
|
22
|
+
import { join } from "node:path";
|
|
23
|
+
|
|
24
|
+
// src/util/cidr.ts
|
|
25
|
+
var DEFAULT_MAX_HOSTS = 1024;
|
|
26
|
+
function ipToInt(ip) {
|
|
27
|
+
const parts = ip.split(".");
|
|
28
|
+
if (parts.length !== 4) throw new Error(`invalid IPv4 address: "${ip}"`);
|
|
29
|
+
let value = 0;
|
|
30
|
+
for (const part of parts) {
|
|
31
|
+
if (!/^\d+$/.test(part)) throw new Error(`invalid IPv4 octet in "${ip}"`);
|
|
32
|
+
const octet = Number(part);
|
|
33
|
+
if (octet < 0 || octet > 255) throw new Error(`IPv4 octet out of range in "${ip}"`);
|
|
34
|
+
value = value * 256 + octet;
|
|
35
|
+
}
|
|
36
|
+
return value >>> 0;
|
|
37
|
+
}
|
|
38
|
+
function intToIp(value) {
|
|
39
|
+
return [
|
|
40
|
+
value >>> 24 & 255,
|
|
41
|
+
value >>> 16 & 255,
|
|
42
|
+
value >>> 8 & 255,
|
|
43
|
+
value & 255
|
|
44
|
+
].join(".");
|
|
45
|
+
}
|
|
46
|
+
function expandCidr(cidr, opts = {}) {
|
|
47
|
+
const maxHosts = opts.maxHosts ?? DEFAULT_MAX_HOSTS;
|
|
48
|
+
const trimmed = cidr.trim();
|
|
49
|
+
const slash = trimmed.indexOf("/");
|
|
50
|
+
if (slash < 0) throw new Error(`CIDR "${cidr}" is missing a "/prefix" (e.g. 192.168.1.0/24)`);
|
|
51
|
+
const base = trimmed.slice(0, slash);
|
|
52
|
+
const prefixStr = trimmed.slice(slash + 1);
|
|
53
|
+
if (!/^\d+$/.test(prefixStr)) throw new Error(`CIDR "${cidr}" has a non-numeric prefix`);
|
|
54
|
+
const prefix = Number(prefixStr);
|
|
55
|
+
if (prefix < 0 || prefix > 32) throw new Error(`CIDR "${cidr}" prefix must be 0..32`);
|
|
56
|
+
const baseInt = ipToInt(base);
|
|
57
|
+
const hostBits = 32 - prefix;
|
|
58
|
+
const blockSize = 2 ** hostBits;
|
|
59
|
+
const mask = hostBits === 32 ? 0 : 4294967295 << hostBits >>> 0;
|
|
60
|
+
const networkInt = (baseInt & mask) >>> 0;
|
|
61
|
+
let firstHost;
|
|
62
|
+
let count;
|
|
63
|
+
if (prefix >= 31) {
|
|
64
|
+
firstHost = networkInt;
|
|
65
|
+
count = blockSize;
|
|
66
|
+
} else {
|
|
67
|
+
firstHost = networkInt + 1;
|
|
68
|
+
count = blockSize - 2;
|
|
69
|
+
}
|
|
70
|
+
if (count > maxHosts && !opts.allowLarge) {
|
|
71
|
+
throw new Error(
|
|
72
|
+
`CIDR "${cidr}" expands to ${count} hosts, exceeding the safety cap of ${maxHosts}. Narrow the range, or set allowLarge/SCAN_ALLOW_LARGE=true to override.`
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
const hosts = [];
|
|
76
|
+
for (let i = 0; i < count; i++) {
|
|
77
|
+
hosts.push(intToIp(firstHost + i >>> 0));
|
|
78
|
+
}
|
|
79
|
+
return hosts;
|
|
80
|
+
}
|
|
81
|
+
function expandCidrs(cidrs, opts = {}) {
|
|
82
|
+
const seen = /* @__PURE__ */ new Set();
|
|
83
|
+
const out = [];
|
|
84
|
+
for (const cidr of cidrs) {
|
|
85
|
+
for (const ip of expandCidr(cidr, opts)) {
|
|
86
|
+
if (!seen.has(ip)) {
|
|
87
|
+
seen.add(ip);
|
|
88
|
+
out.push(ip);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return out;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// src/core/sddJob.ts
|
|
96
|
+
var SDD_PROGRESS_PREFIX = "@@SDD-PROGRESS ";
|
|
97
|
+
function sddHostsFromTargets(targets, allowLarge = false) {
|
|
98
|
+
const hosts = [];
|
|
99
|
+
const cidrs = [];
|
|
100
|
+
for (const t of targets) {
|
|
101
|
+
const v = t.trim();
|
|
102
|
+
if (v.length === 0) continue;
|
|
103
|
+
if (v.includes("/")) cidrs.push(v);
|
|
104
|
+
else hosts.push(v);
|
|
105
|
+
}
|
|
106
|
+
if (cidrs.length > 0) hosts.push(...expandCidrs(cidrs, { allowLarge }));
|
|
107
|
+
return [...new Set(hosts)];
|
|
108
|
+
}
|
|
109
|
+
function sddEntryCommand() {
|
|
110
|
+
const tsxCli = join(PKG_ROOT, "node_modules", "tsx", "dist", "cli.mjs");
|
|
111
|
+
const source = join(PKG_ROOT, "src", "sensitive-data-index.ts");
|
|
112
|
+
const built = join(PKG_ROOT, "dist", "sensitive-data-index.js");
|
|
113
|
+
if (existsSync(built) && !/\.ts$/.test(process.argv[1] ?? "")) {
|
|
114
|
+
return { file: process.execPath, args: [built] };
|
|
115
|
+
}
|
|
116
|
+
return { file: process.execPath, args: [tsxCli, source] };
|
|
117
|
+
}
|
|
118
|
+
function runOnlyLogins(set) {
|
|
119
|
+
if (!set) return [];
|
|
120
|
+
if (set.kind === "smb") {
|
|
121
|
+
return (set.secret.domain?.credentials ?? []).map((c) => ({ username: c.username, password: c.password }));
|
|
122
|
+
}
|
|
123
|
+
if (set.kind === "network") {
|
|
124
|
+
return (set.secret.network?.windowsCredentials ?? []).map((c) => ({
|
|
125
|
+
username: c.username,
|
|
126
|
+
password: c.password,
|
|
127
|
+
...c.domain ? { domain: c.domain } : {}
|
|
128
|
+
}));
|
|
129
|
+
}
|
|
130
|
+
return [];
|
|
131
|
+
}
|
|
132
|
+
function runSddJob(opts) {
|
|
133
|
+
const { cfg, job, log, signal } = opts;
|
|
134
|
+
const hosts = sddHostsFromTargets(job.targets, cfg.allowLargeScans);
|
|
135
|
+
if (hosts.length === 0) {
|
|
136
|
+
return Promise.resolve({
|
|
137
|
+
status: "failed",
|
|
138
|
+
message: "a sensitive-data scan needs at least one host to enumerate \u2014 a file server name, an address, or a CIDR"
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
const cmd = sddEntryCommand();
|
|
142
|
+
const runOnly = runOnlyLogins(opts.ephemeral ?? null);
|
|
143
|
+
return new Promise((resolve) => {
|
|
144
|
+
if (signal.aborted) return resolve({ status: "cancelled", message: "cancelled before it started" });
|
|
145
|
+
let lastLine = "";
|
|
146
|
+
let lastError = "";
|
|
147
|
+
let settled = false;
|
|
148
|
+
const finish = (r) => {
|
|
149
|
+
if (settled) return;
|
|
150
|
+
settled = true;
|
|
151
|
+
signal.removeEventListener("abort", onAbort);
|
|
152
|
+
resolve(r);
|
|
153
|
+
};
|
|
154
|
+
const child = (opts.spawnImpl ?? spawn)(cmd.file, cmd.args, {
|
|
155
|
+
cwd: PKG_ROOT,
|
|
156
|
+
env: {
|
|
157
|
+
...process.env,
|
|
158
|
+
TENANT_ID: cfg.tenantId,
|
|
159
|
+
SITE_ID: job.siteId,
|
|
160
|
+
SDD_HOSTS: hosts.join(","),
|
|
161
|
+
CONTROL_PLANE_URL: cfg.controlPlaneUrl,
|
|
162
|
+
COLLECTOR_API_KEY: cfg.apiKey,
|
|
163
|
+
...job.localCredentialSetId ? { SDD_CREDENTIAL_SET_ID: job.localCredentialSetId } : {},
|
|
164
|
+
// The parent reports this run's status; the child must not announce a
|
|
165
|
+
// second job for the same walk (sensitive-data-index.ts checks this).
|
|
166
|
+
SDD_JOB_ID: job.id,
|
|
167
|
+
...runOnly.length > 0 ? { SDD_RUN_CREDENTIALS_JSON: JSON.stringify(runOnly) } : {},
|
|
168
|
+
// The child is a job, not a shell session: no colour, no prompts.
|
|
169
|
+
NO_COLOR: "1"
|
|
170
|
+
},
|
|
171
|
+
windowsHide: true,
|
|
172
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
173
|
+
});
|
|
174
|
+
const onAbort = () => {
|
|
175
|
+
try {
|
|
176
|
+
child.kill();
|
|
177
|
+
} catch {
|
|
178
|
+
}
|
|
179
|
+
finish({ status: "cancelled", message: "cancelled" });
|
|
180
|
+
};
|
|
181
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
182
|
+
const relay = (level, chunk) => {
|
|
183
|
+
for (const raw of chunk.toString("utf8").split(/\r?\n/)) {
|
|
184
|
+
const line = raw.replace(/^\S+T\S+Z\s+(INFO|WARN|ERROR)\s+/, "").trim();
|
|
185
|
+
if (line.length === 0) continue;
|
|
186
|
+
if (line.startsWith(SDD_PROGRESS_PREFIX)) {
|
|
187
|
+
try {
|
|
188
|
+
const parsed = JSON.parse(line.slice(SDD_PROGRESS_PREFIX.length));
|
|
189
|
+
if (parsed && typeof parsed.phase === "string") opts.onProgress?.(parsed);
|
|
190
|
+
} catch {
|
|
191
|
+
}
|
|
192
|
+
continue;
|
|
193
|
+
}
|
|
194
|
+
if (level === "warn" || /^ERROR|error:|failed/i.test(raw)) lastError = line;
|
|
195
|
+
lastLine = line;
|
|
196
|
+
log(level, `sensitive-data: ${line}`);
|
|
197
|
+
opts.onLine?.(line);
|
|
198
|
+
}
|
|
199
|
+
};
|
|
200
|
+
child.stdout?.on("data", (d) => relay("info", d));
|
|
201
|
+
child.stderr?.on("data", (d) => relay("warn", d));
|
|
202
|
+
child.on(
|
|
203
|
+
"error",
|
|
204
|
+
(err) => finish({ status: "failed", message: `sensitive-data scan could not start: ${err.message}` })
|
|
205
|
+
);
|
|
206
|
+
child.on("close", (code) => {
|
|
207
|
+
if (signal.aborted) return finish({ status: "cancelled", message: "cancelled" });
|
|
208
|
+
if (code === 0) return finish({ status: "done", message: lastLine || "sensitive-data scan finished" });
|
|
209
|
+
finish({
|
|
210
|
+
status: "failed",
|
|
211
|
+
message: `sensitive-data scan exited ${code ?? "by signal"}: ${lastError || lastLine || "no output"}`
|
|
212
|
+
});
|
|
213
|
+
});
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// src/core/poll.ts
|
|
218
|
+
var POLL_TIMEOUT_MS = 3e4;
|
|
219
|
+
async function postJson(url, apiKey, body, signal) {
|
|
220
|
+
const timeout = AbortSignal.timeout(POLL_TIMEOUT_MS);
|
|
221
|
+
const reqSignal = signal ? AbortSignal.any([signal, timeout]) : timeout;
|
|
222
|
+
const res = await fetch(url, {
|
|
223
|
+
method: "POST",
|
|
224
|
+
headers: {
|
|
225
|
+
"content-type": "application/json",
|
|
226
|
+
[COLLECTOR_AUTH_HEADER]: collectorBearer(apiKey)
|
|
227
|
+
},
|
|
228
|
+
body: JSON.stringify(body),
|
|
229
|
+
signal: reqSignal
|
|
230
|
+
});
|
|
231
|
+
return { status: res.status, text: await res.text() };
|
|
232
|
+
}
|
|
233
|
+
function safeJson(text) {
|
|
234
|
+
try {
|
|
235
|
+
return JSON.parse(text);
|
|
236
|
+
} catch {
|
|
237
|
+
return void 0;
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
function throwIfRefused(status, text) {
|
|
241
|
+
const apiErr = ApiError.safeParse(safeJson(text));
|
|
242
|
+
if (!apiErr.success) return;
|
|
243
|
+
const refusal = planRefusalFrom(status, apiErr.data.code, apiErr.data.error);
|
|
244
|
+
if (!refusal) return;
|
|
245
|
+
if (refusal.code === "collector_not_licensed" || refusal.code === "not_paid_tier") {
|
|
246
|
+
noteEntitlementRefusalFromPlane({
|
|
247
|
+
reason: "not_paid",
|
|
248
|
+
planeMessage: refusal.planeMessage,
|
|
249
|
+
httpStatus: refusal.httpStatus
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
throw refusal;
|
|
253
|
+
}
|
|
254
|
+
function describeError(status, text) {
|
|
255
|
+
const apiErr = ApiError.safeParse(safeJson(text));
|
|
256
|
+
return apiErr.success ? `[${apiErr.data.code}] ${apiErr.data.error}` : `HTTP ${status}: ${text.slice(0, 500)}`;
|
|
257
|
+
}
|
|
258
|
+
async function claimNextJob(cfg, log, signal) {
|
|
259
|
+
const url = cfg.controlPlaneUrl.replace(/\/+$/, "") + ROUTES.collectorJobsNext;
|
|
260
|
+
log("debug", `polling for next job -> ${url}`);
|
|
261
|
+
const startedAt = new Date(Date.now() - process.uptime() * 1e3).toISOString();
|
|
262
|
+
const { status, text } = await postJson(url, cfg.apiKey, { startedAt }, signal);
|
|
263
|
+
if (status < 200 || status >= 300) {
|
|
264
|
+
throwIfRefused(status, text);
|
|
265
|
+
throw new Error(`jobs/next failed: ${describeError(status, text)}`);
|
|
266
|
+
}
|
|
267
|
+
const parsed = ClaimedJobResponse.safeParse(safeJson(text));
|
|
268
|
+
if (!parsed.success) {
|
|
269
|
+
throw new Error(`control plane returned an unrecognized jobs/next body: ${text.slice(0, 500)}`);
|
|
270
|
+
}
|
|
271
|
+
return { job: parsed.data.job, restartRequestedAt: parsed.data.restartRequestedAt };
|
|
272
|
+
}
|
|
273
|
+
async function reportStatus(cfg, body, log, signal) {
|
|
274
|
+
const url = cfg.controlPlaneUrl.replace(/\/+$/, "") + ROUTES.collectorJobStatus;
|
|
275
|
+
log("debug", `reporting job ${body.jobId} status="${body.status}" -> ${url}`);
|
|
276
|
+
const { status, text } = await postJson(url, cfg.apiKey, body, signal);
|
|
277
|
+
if (status < 200 || status >= 300) {
|
|
278
|
+
throwIfRefused(status, text);
|
|
279
|
+
throw new Error(`jobs/status failed: ${describeError(status, text)}`);
|
|
280
|
+
}
|
|
281
|
+
const parsed = ScanJobStatusResult.safeParse(safeJson(text));
|
|
282
|
+
if (!parsed.success) {
|
|
283
|
+
throw new Error(
|
|
284
|
+
`control plane returned an unrecognized jobs/status body: ${text.slice(0, 500)}`
|
|
285
|
+
);
|
|
286
|
+
}
|
|
287
|
+
return parsed.data;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
export {
|
|
291
|
+
expandCidrs,
|
|
292
|
+
SDD_PROGRESS_PREFIX,
|
|
293
|
+
runSddJob,
|
|
294
|
+
claimNextJob,
|
|
295
|
+
reportStatus
|
|
296
|
+
};
|
|
297
|
+
//# sourceMappingURL=chunk-SLZYU75K.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/core/sddJob.ts", "../src/util/cidr.ts", "../src/core/poll.ts"],
|
|
4
|
+
"sourcesContent": ["/**\r\n * Sensitive Data Discovery as a JOB \u2014 the console's Run button and the plane's\r\n * queue drive the module's own entry point as a supervised child process.\r\n *\r\n * \u2500\u2500 WHY A CHILD, NOT A DRIVER \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\n *\r\n * `runJob` runs every other domain in-process through a driver that builds one\r\n * inventory and pushes one snapshot. Sensitive-data discovery is not that: it\r\n * walks file content for hours and pushes through its own resumable\r\n * run/batch/close protocol with a frontier on disk (sensitiveData/frontierStore.ts),\r\n * all of which lives in `sensitive-data-index.ts` and is exercised from a shell\r\n * as `netdoc-collector scan sensitive-data`. Until 2026-09-12 the job runner\r\n * REFUSED the domain and told the operator to open a shell \u2014 which on a\r\n * customer's server nobody does, so the module read \"never ran\".\r\n *\r\n * Driving the entry point as a child keeps one implementation of the protocol\r\n * and gives the job what it lacked: a start from a button, a running/done/failed\r\n * status the console and the plane can show, cancellation, and a ledger line.\r\n * The child pushes its own runs to the plane exactly as the shell invocation\r\n * does; the parent only relays its progress and its verdict.\r\n *\r\n * \u2500\u2500 WHAT THE JOB SUPPLIES \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\n *\r\n * The entry reads its scope from the environment (`loadSensitiveDataConfig`):\r\n * tenancy from the daemon's config, the site and the hosts from the job. A job\r\n * target may be a hostname, an address or a CIDR; a CIDR is expanded here so\r\n * the child enumerates hosts, never a range. Logins come from the child's own\r\n * vault read (every usable `smb` set, or the one the job names via\r\n * SDD_CREDENTIAL_SET_ID), so no secret crosses this process boundary.\r\n */\r\nimport { spawn } from \"node:child_process\";\r\nimport { existsSync } from \"node:fs\";\r\nimport { join } from \"node:path\";\r\nimport type { EphemeralCredentialSet, ScanJob } from \"@netdoc/collector-contract\";\r\nimport type { AppConfig } from \"../config.js\";\r\nimport { PKG_ROOT } from \"../pkgRoot.js\";\r\nimport { expandCidrs } from \"../util/cidr.js\";\r\nimport type { Logger } from \"./runner.js\";\r\n\r\nexport interface SddJobResult {\r\n status: \"done\" | \"failed\" | \"cancelled\";\r\n message: string;\r\n}\r\n\r\n/**\r\n * The line the child prints to hand its parent a REAL progress reading \u2014 a\r\n * phase and a done/total the portal's bar can fill, which a human log line\r\n * cannot give without the parent parsing prose. Emitted only in job mode\r\n * (SDD_JOB_ID set); the shell path posts its own progress. Kept on its own line\r\n * and stripped from the human relay below so it never shows as scrollback.\r\n */\r\nexport const SDD_PROGRESS_PREFIX = \"@@SDD-PROGRESS \";\r\n\r\nexport interface SddProgress {\r\n /** \"enumerate\" | \"classify\" \u2014 the two phases with a countable denominator. */\r\n phase: string;\r\n done: number;\r\n total: number;\r\n currentTarget?: string;\r\n}\r\n\r\n/**\r\n * The hosts a job's targets name. CIDRs expand (safety-capped like the network\r\n * sweep); anything else \u2014 a hostname, an address \u2014 is passed through as is.\r\n */\r\nexport function sddHostsFromTargets(targets: readonly string[], allowLarge = false): string[] {\r\n const hosts: string[] = [];\r\n const cidrs: string[] = [];\r\n for (const t of targets) {\r\n const v = t.trim();\r\n if (v.length === 0) continue;\r\n if (v.includes(\"/\")) cidrs.push(v);\r\n else hosts.push(v);\r\n }\r\n if (cidrs.length > 0) hosts.push(...expandCidrs(cidrs, { allowLarge }));\r\n return [...new Set(hosts)];\r\n}\r\n\r\n/**\r\n * How to launch the module's entry: under tsx (dev/self-host) through the tsx\r\n * CLI against the TypeScript source, in a compiled install against dist/. The\r\n * same choice the daemon's own re-exec makes, for the same reason: plain node\r\n * cannot import .ts.\r\n */\r\nexport function sddEntryCommand(): { file: string; args: string[] } {\r\n const tsxCli = join(PKG_ROOT, \"node_modules\", \"tsx\", \"dist\", \"cli.mjs\");\r\n const source = join(PKG_ROOT, \"src\", \"sensitive-data-index.ts\");\r\n const built = join(PKG_ROOT, \"dist\", \"sensitive-data-index.js\");\r\n if (existsSync(built) && !/\\.ts$/.test(process.argv[1] ?? \"\")) {\r\n return { file: process.execPath, args: [built] };\r\n }\r\n return { file: process.execPath, args: [tsxCli, source] };\r\n}\r\n\r\n/**\r\n * The logins a run-only set carries for a share walk: an `smb` set's domain\r\n * logins, or a `network` set's Windows logins (the console offers both kinds,\r\n * and a Windows login IS an SMB login). Anything else carries nothing usable.\r\n */\r\nexport function runOnlyLogins(\r\n set: EphemeralCredentialSet | null,\r\n): Array<{ username: string; password: string; domain?: string }> {\r\n if (!set) return [];\r\n if (set.kind === \"smb\") {\r\n return (set.secret.domain?.credentials ?? []).map((c) => ({ username: c.username, password: c.password }));\r\n }\r\n if (set.kind === \"network\") {\r\n return (set.secret.network?.windowsCredentials ?? []).map((c) => ({\r\n username: c.username,\r\n password: c.password,\r\n ...(c.domain ? { domain: c.domain } : {}),\r\n }));\r\n }\r\n return [];\r\n}\r\n\r\nexport interface RunSddJobOptions {\r\n cfg: Pick<AppConfig, \"tenantId\" | \"controlPlaneUrl\" | \"apiKey\" | \"allowLargeScans\">;\r\n job: Pick<ScanJob, \"id\" | \"siteId\" | \"targets\" | \"localCredentialSetId\">;\r\n log: Logger;\r\n signal: AbortSignal;\r\n /** Called with each line the child prints, for a live progress message. */\r\n onLine?: (line: string) => void;\r\n /** Called with the child's structured progress \u2014 the portal's real % bar. */\r\n onProgress?: (p: SddProgress) => void;\r\n /**\r\n * \"Use credentials for this run only\" from the console. Forwarded to the\r\n * child in its environment as JSON \u2014 the same channel every PowerShell\r\n * credential already travels (never argv) \u2014 and tried FIRST there, ahead of\r\n * the vault: the operator typed them for this run, so they win it.\r\n */\r\n ephemeral?: EphemeralCredentialSet | null;\r\n /** Test seam: the spawn to use. */\r\n spawnImpl?: typeof spawn;\r\n}\r\n\r\n/**\r\n * Run the module for one job and report how it ended. Never throws: a child\r\n * that cannot start, exits non-zero, or is cancelled is a result, not an\r\n * exception, because the caller reports it to the plane either way.\r\n */\r\nexport function runSddJob(opts: RunSddJobOptions): Promise<SddJobResult> {\r\n const { cfg, job, log, signal } = opts;\r\n const hosts = sddHostsFromTargets(job.targets, cfg.allowLargeScans);\r\n if (hosts.length === 0) {\r\n return Promise.resolve({\r\n status: \"failed\",\r\n message: \"a sensitive-data scan needs at least one host to enumerate \u2014 a file server name, an address, or a CIDR\",\r\n });\r\n }\r\n const cmd = sddEntryCommand();\r\n const runOnly = runOnlyLogins(opts.ephemeral ?? null);\r\n return new Promise((resolve) => {\r\n if (signal.aborted) return resolve({ status: \"cancelled\", message: \"cancelled before it started\" });\r\n let lastLine = \"\";\r\n let lastError = \"\";\r\n let settled = false;\r\n const finish = (r: SddJobResult): void => {\r\n if (settled) return;\r\n settled = true;\r\n signal.removeEventListener(\"abort\", onAbort);\r\n resolve(r);\r\n };\r\n const child = (opts.spawnImpl ?? spawn)(cmd.file, cmd.args, {\r\n cwd: PKG_ROOT,\r\n env: {\r\n ...process.env,\r\n TENANT_ID: cfg.tenantId,\r\n SITE_ID: job.siteId,\r\n SDD_HOSTS: hosts.join(\",\"),\r\n CONTROL_PLANE_URL: cfg.controlPlaneUrl,\r\n COLLECTOR_API_KEY: cfg.apiKey,\r\n ...(job.localCredentialSetId ? { SDD_CREDENTIAL_SET_ID: job.localCredentialSetId } : {}),\r\n // The parent reports this run's status; the child must not announce a\r\n // second job for the same walk (sensitive-data-index.ts checks this).\r\n SDD_JOB_ID: job.id,\r\n ...(runOnly.length > 0 ? { SDD_RUN_CREDENTIALS_JSON: JSON.stringify(runOnly) } : {}),\r\n // The child is a job, not a shell session: no colour, no prompts.\r\n NO_COLOR: \"1\",\r\n },\r\n windowsHide: true,\r\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\r\n });\r\n const onAbort = (): void => {\r\n try {\r\n child.kill();\r\n } catch {\r\n /* already gone */\r\n }\r\n finish({ status: \"cancelled\", message: \"cancelled\" });\r\n };\r\n signal.addEventListener(\"abort\", onAbort, { once: true });\r\n\r\n const relay = (level: \"info\" | \"warn\", chunk: Buffer): void => {\r\n for (const raw of chunk.toString(\"utf8\").split(/\\r?\\n/)) {\r\n const line = raw.replace(/^\\S+T\\S+Z\\s+(INFO|WARN|ERROR)\\s+/, \"\").trim();\r\n if (line.length === 0) continue;\r\n // A structured progress reading, not a log line: parse it into a % bar\r\n // and DON'T relay it as scrollback or a heartbeat message.\r\n if (line.startsWith(SDD_PROGRESS_PREFIX)) {\r\n try {\r\n const parsed = JSON.parse(line.slice(SDD_PROGRESS_PREFIX.length)) as SddProgress;\r\n if (parsed && typeof parsed.phase === \"string\") opts.onProgress?.(parsed);\r\n } catch {\r\n /* a malformed progress line is not worth failing a scan over */\r\n }\r\n continue;\r\n }\r\n if (level === \"warn\" || /^ERROR|error:|failed/i.test(raw)) lastError = line;\r\n lastLine = line;\r\n log(level, `sensitive-data: ${line}`);\r\n opts.onLine?.(line);\r\n }\r\n };\r\n child.stdout?.on(\"data\", (d: Buffer) => relay(\"info\", d));\r\n child.stderr?.on(\"data\", (d: Buffer) => relay(\"warn\", d));\r\n child.on(\"error\", (err) =>\r\n finish({ status: \"failed\", message: `sensitive-data scan could not start: ${err.message}` }),\r\n );\r\n child.on(\"close\", (code) => {\r\n if (signal.aborted) return finish({ status: \"cancelled\", message: \"cancelled\" });\r\n if (code === 0) return finish({ status: \"done\", message: lastLine || \"sensitive-data scan finished\" });\r\n finish({\r\n status: \"failed\",\r\n message: `sensitive-data scan exited ${code ?? \"by signal\"}: ${lastError || lastLine || \"no output\"}`,\r\n });\r\n });\r\n });\r\n}\r\n", "/**\r\n * IPv4 CIDR expansion with a hard safety cap.\r\n *\r\n * The collector only scans networks the operator has authorized (scanTargets),\r\n * but a fat-fingered mask (e.g. \"10.0.0.0/8\" = 16M hosts) would hammer the\r\n * network for hours. We refuse anything larger than DEFAULT_MAX_HOSTS (~/22,\r\n * 1024 addresses) unless the caller explicitly opts in with `allowLarge`.\r\n */\r\n\r\nexport interface ExpandOptions {\r\n /** Maximum host count permitted without an explicit override. Default 1024 (~/22). */\r\n maxHosts?: number;\r\n /** Bypass the safety cap for deliberately large ranges. */\r\n allowLarge?: boolean;\r\n}\r\n\r\n/** ~/22. Larger ranges require allowLarge=true. */\r\nexport const DEFAULT_MAX_HOSTS = 1024;\r\n\r\nfunction ipToInt(ip: string): number {\r\n const parts = ip.split(\".\");\r\n if (parts.length !== 4) throw new Error(`invalid IPv4 address: \"${ip}\"`);\r\n let value = 0;\r\n for (const part of parts) {\r\n if (!/^\\d+$/.test(part)) throw new Error(`invalid IPv4 octet in \"${ip}\"`);\r\n const octet = Number(part);\r\n if (octet < 0 || octet > 255) throw new Error(`IPv4 octet out of range in \"${ip}\"`);\r\n value = value * 256 + octet;\r\n }\r\n // Force unsigned 32-bit.\r\n return value >>> 0;\r\n}\r\n\r\nfunction intToIp(value: number): string {\r\n return [\r\n (value >>> 24) & 0xff,\r\n (value >>> 16) & 0xff,\r\n (value >>> 8) & 0xff,\r\n value & 0xff,\r\n ].join(\".\");\r\n}\r\n\r\n/**\r\n * Expand an IPv4 CIDR (e.g. \"192.168.1.0/24\") to the list of scannable host IPs.\r\n * Network and broadcast addresses are skipped for prefixes /0../30; /31 and /32\r\n * are returned whole (RFC 3021 point-to-point / single host).\r\n */\r\nexport function expandCidr(cidr: string, opts: ExpandOptions = {}): string[] {\r\n const maxHosts = opts.maxHosts ?? DEFAULT_MAX_HOSTS;\r\n const trimmed = cidr.trim();\r\n const slash = trimmed.indexOf(\"/\");\r\n if (slash < 0) throw new Error(`CIDR \"${cidr}\" is missing a \"/prefix\" (e.g. 192.168.1.0/24)`);\r\n\r\n const base = trimmed.slice(0, slash);\r\n const prefixStr = trimmed.slice(slash + 1);\r\n if (!/^\\d+$/.test(prefixStr)) throw new Error(`CIDR \"${cidr}\" has a non-numeric prefix`);\r\n const prefix = Number(prefixStr);\r\n if (prefix < 0 || prefix > 32) throw new Error(`CIDR \"${cidr}\" prefix must be 0..32`);\r\n\r\n const baseInt = ipToInt(base);\r\n const hostBits = 32 - prefix;\r\n const blockSize = 2 ** hostBits; // exact 2^hostBits, avoids 32-bit shift overflow\r\n const mask = hostBits === 32 ? 0 : (0xffffffff << hostBits) >>> 0;\r\n const networkInt = (baseInt & mask) >>> 0;\r\n\r\n // Determine the inclusive host range.\r\n let firstHost: number;\r\n let count: number;\r\n if (prefix >= 31) {\r\n firstHost = networkInt;\r\n count = blockSize; // /31 -> 2 usable, /32 -> 1\r\n } else {\r\n firstHost = networkInt + 1; // skip network address\r\n count = blockSize - 2; // skip network + broadcast\r\n }\r\n\r\n if (count > maxHosts && !opts.allowLarge) {\r\n throw new Error(\r\n `CIDR \"${cidr}\" expands to ${count} hosts, exceeding the safety cap of ${maxHosts}. ` +\r\n `Narrow the range, or set allowLarge/SCAN_ALLOW_LARGE=true to override.`,\r\n );\r\n }\r\n\r\n const hosts: string[] = [];\r\n for (let i = 0; i < count; i++) {\r\n hosts.push(intToIp((firstHost + i) >>> 0));\r\n }\r\n return hosts;\r\n}\r\n\r\n/** Expand many CIDRs, de-duplicating overlapping addresses, preserving order. */\r\nexport function expandCidrs(cidrs: readonly string[], opts: ExpandOptions = {}): string[] {\r\n const seen = new Set<string>();\r\n const out: string[] = [];\r\n for (const cidr of cidrs) {\r\n for (const ip of expandCidr(cidr, opts)) {\r\n if (!seen.has(ip)) {\r\n seen.add(ip);\r\n out.push(ip);\r\n }\r\n }\r\n }\r\n return out;\r\n}\r\n", "/**\n * Outbound job polling for daemon mode (ADR-001 Decision 3: collector reaches OUT).\n *\n * The collector NEVER opens a listening port. Instead it POSTs to the control\n * plane to (a) claim the next pending scan job assigned to it and (b) report that\n * job's progress/terminal state. Same base URL + `Authorization: Bearer <key>`\n * approach as the snapshot push (see push.ts).\n *\n * \u2500\u2500 WHAT A CLAIMED JOB NO LONGER CARRIES (2026-09-01) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n *\n * This header used to say \"Credentials used to run the scan (the SNMP community)\n * are resolved locally and are never sent in these requests\", and it was true of\n * the REQUESTS while being quietly false of the RESPONSES: a claimed `ScanJob`\n * arrived carrying `snmpCommunities`, `sshCredentials`, `windowsCredentials` and\n * a `domainTarget.credentials[]` \u2014 the customer's own passwords, expanded onto\n * the job by the control plane out of a vault it held, and read back down here\n * over HTTPS on every claim.\n *\n * None of those fields exists any more. A job now names WHICH site, WHICH\n * domain, WHERE to point and WHICH of this collector's own credential sets to\n * use (`localCredentialSetId`), and the credentials themselves never left this\n * host to begin with. Nothing in either direction of this file carries one.\n *\n * \u2500\u2500 WHERE THE CREDENTIALS COME FROM INSTEAD, AND WHY NOT FROM HERE \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n *\n * They are resolved by `vault/resolveJobCredentials`, called through the\n * `credentials` resolver `runDaemon` is handed. This file briefly exported a\n * wrapper \u2014 resolve, and POST a failed status when the resolution missed \u2014 on\n * the argument that reporting a job's terminal state is what this module owns.\n * `core/daemon.ts` rejected it, correctly, and the reason is worth keeping here\n * rather than only there: a LOCALLY SCHEDULED run has no plane-side job id, so\n * that post would mark failed a job the control plane has never heard of, and\n * for a polled run it would double-report, because `runJob` already reports a\n * credential refusal as a failed job. Which reporter a run uses is a decision\n * per trigger (`JobReporter`), and a resolver that reports on its own behalf\n * takes that decision away.\n *\n * So the vault does not appear in this file at all. What it owes the scan path\n * is the two things it always owed: a claimed job, and a status post.\n *\n * A poll can also be REFUSED rather than fail \u2014 a lapsed subscription, or a plan\n * with no collector in it. Those come back as typed `PlanRefusal`s (see\n * core/planRefusal.ts), because the daemon's answer to them is not the answer to\n * a network blip: it keeps polling, since somebody else can lift a billing hold\n * without touching this host, but it says so once instead of once every interval.\n */\nimport {\n ROUTES,\n ClaimedJobResponse,\n ScanJobStatusResult,\n ApiError,\n collectorBearer,\n COLLECTOR_AUTH_HEADER,\n type ScanJob,\n type ScanJobStatusBody,\n} from \"@netdoc/collector-contract\";\nimport type { AppConfig } from \"../config.js\";\nimport { planRefusalFrom } from \"./planRefusal.js\";\nimport { noteEntitlementRefusalFromPlane } from \"../entitlement/index.js\";\nimport type { Logger } from \"./runner.js\";\n\n/** The subset of config a poll needs: where to reach the plane and how to auth. */\nexport type PollConfig = Pick<AppConfig, \"controlPlaneUrl\" | \"apiKey\">;\n\ninterface PostResult {\n status: number;\n text: string;\n}\n\n/** Poll/status requests are small \u2014 cap them so a sick control plane (e.g. one\n * that accepts the TCP connection but is GC-thrashing and never replies) can't\n * hang the daemon indefinitely. The caller's signal (SIGINT/cancel) still applies. */\nconst POLL_TIMEOUT_MS = 30_000;\n\nasync function postJson(\n url: string,\n apiKey: string,\n body: unknown,\n signal?: AbortSignal,\n): Promise<PostResult> {\n const timeout = AbortSignal.timeout(POLL_TIMEOUT_MS);\n const reqSignal = signal ? AbortSignal.any([signal, timeout]) : timeout;\n const res = await fetch(url, {\n method: \"POST\",\n headers: {\n \"content-type\": \"application/json\",\n [COLLECTOR_AUTH_HEADER]: collectorBearer(apiKey),\n },\n body: JSON.stringify(body),\n signal: reqSignal,\n });\n return { status: res.status, text: await res.text() };\n}\n\nfunction safeJson(text: string): unknown {\n try {\n return JSON.parse(text);\n } catch {\n return undefined;\n }\n}\n\n/**\n * Raise a typed refusal when the plane's answer is \"your plan does not permit\n * this\", so the daemon can tell that apart from a transport failure.\n *\n * No scan domain is passed, and there is nothing to pass: these two calls are\n * about the JOB QUEUE, not about one domain's inventory. A refusal here is\n * therefore tenant-wide by construction \u2014 the collector is not licensed, or the\n * subscription lapsed \u2014 which is exactly what the domain-less wording says.\n */\nfunction throwIfRefused(status: number, text: string): void {\n const apiErr = ApiError.safeParse(safeJson(text));\n if (!apiErr.success) return;\n const refusal = planRefusalFrom(status, apiErr.data.code, apiErr.data.error);\n if (!refusal) return;\n // \u2500\u2500 A REFUSAL HERE IS ALSO AN ENTITLEMENT ANSWER \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n //\n // `collector_not_licensed` and `not_paid_tier` are the plane telling us,\n // unprompted, the exact thing the entitlement gate exists to ask about \u2014 so\n // the gate is shut now rather than at the next scan. Without this the status\n // pane would go on showing a confirmed licence until something tried to scan,\n // which for a collector whose schedules are hours apart is hours of a screen\n // saying the opposite of what the plane just said.\n //\n // ONLY REFUSALS TRAVEL THIS WAY. A successful poll is not read as a\n // confirmation, even though a 200 from this route means the middleware\n // verified the licence \u2014 see `EntitlementGate.noteRefusal` for why that\n // asymmetry is deliberate and what it protects against.\n //\n // `forbidden_module` is deliberately not forwarded: it means one capability is\n // not sold to this tenant, not that the collector may not run. Shutting the\n // whole gate over it would stop every other domain from scanning.\n if (refusal.code === \"collector_not_licensed\" || refusal.code === \"not_paid_tier\") {\n noteEntitlementRefusalFromPlane({\n reason: \"not_paid\",\n planeMessage: refusal.planeMessage,\n httpStatus: refusal.httpStatus,\n });\n }\n throw refusal;\n}\n\n/** Turn a non-2xx response into a compact, human-readable error string. */\nfunction describeError(status: number, text: string): string {\n const apiErr = ApiError.safeParse(safeJson(text));\n return apiErr.success\n ? `[${apiErr.data.code}] ${apiErr.data.error}`\n : `HTTP ${status}: ${text.slice(0, 500)}`;\n}\n\n/**\n * Claim the oldest pending job assigned to this collector, or null if the queue\n * is empty. Throws on transport/auth errors so the daemon loop can log + retry.\n */\nexport interface PollResult {\n job: ScanJob | null;\n /** In-app restart signal (migration 0093) \u2014 the daemon restarts when this is\n * newer than its own start time. Null when no restart is pending. */\n restartRequestedAt: string | null;\n}\n\nexport async function claimNextJob(cfg: PollConfig, log: Logger, signal?: AbortSignal): Promise<PollResult> {\n const url = cfg.controlPlaneUrl.replace(/\\/+$/, \"\") + ROUTES.collectorJobsNext;\n log(\"debug\", `polling for next job -> ${url}`);\n // The process's own start time, so the control plane can tell whether an\n // outstanding restart request has already been satisfied. Derived from\n // `process.uptime()` rather than a value threaded down from the daemon loop:\n // it is the actual process start, and it stays correct however the collector\n // was restarted \u2014 the in-app button, the CLI, or a supervisor.\n const startedAt = new Date(Date.now() - process.uptime() * 1000).toISOString();\n const { status, text } = await postJson(url, cfg.apiKey, { startedAt }, signal);\n if (status < 200 || status >= 300) {\n throwIfRefused(status, text);\n throw new Error(`jobs/next failed: ${describeError(status, text)}`);\n }\n const parsed = ClaimedJobResponse.safeParse(safeJson(text));\n if (!parsed.success) {\n throw new Error(`control plane returned an unrecognized jobs/next body: ${text.slice(0, 500)}`);\n }\n return { job: parsed.data.job, restartRequestedAt: parsed.data.restartRequestedAt };\n}\n\n/**\n * Report a claimed job's progress/terminal state and return the plane's result.\n * `body` carries only the jobId, status, and optional message/snapshotId \u2014 never\n * any credential. Throws on transport/auth errors so the caller can decide how to\n * react. The returned `ScanJobStatusResult.cancelled` tells the daemon whether the\n * job has been cancelled and the in-flight scan should be aborted.\n */\nexport async function reportStatus(\n cfg: PollConfig,\n body: ScanJobStatusBody,\n log: Logger,\n signal?: AbortSignal,\n): Promise<ScanJobStatusResult> {\n const url = cfg.controlPlaneUrl.replace(/\\/+$/, \"\") + ROUTES.collectorJobStatus;\n log(\"debug\", `reporting job ${body.jobId} status=\"${body.status}\" -> ${url}`);\n const { status, text } = await postJson(url, cfg.apiKey, body, signal);\n if (status < 200 || status >= 300) {\n throwIfRefused(status, text);\n throw new Error(`jobs/status failed: ${describeError(status, text)}`);\n }\n const parsed = ScanJobStatusResult.safeParse(safeJson(text));\n if (!parsed.success) {\n throw new Error(\n `control plane returned an unrecognized jobs/status body: ${text.slice(0, 500)}`,\n );\n }\n return parsed.data;\n}\n\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
package/dist/cli.js
CHANGED
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
COLLECTOR_MODULES,
|
|
11
11
|
collectorVersion,
|
|
12
12
|
findModule
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-ALYLNNEP.js";
|
|
14
14
|
import "./chunk-VQAXWSBA.js";
|
|
15
15
|
import "./chunk-DIS3S4SY.js";
|
|
16
16
|
import "./chunk-A5WDSTPU.js";
|
|
@@ -129,7 +129,7 @@ async function printStatus() {
|
|
|
129
129
|
}
|
|
130
130
|
}
|
|
131
131
|
lines.push(`daemon : ${daemonLine}`);
|
|
132
|
-
const { moduleStatusesFromLedger } = await import("./status-
|
|
132
|
+
const { moduleStatusesFromLedger } = await import("./status-TX2NT7XU.js");
|
|
133
133
|
const rows = moduleStatusesFromLedger();
|
|
134
134
|
lines.push(`modules :`);
|
|
135
135
|
for (const m of rows) {
|