@meru454545/nexus-modscript-composer 2.0.12

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 ADDED
@@ -0,0 +1,179 @@
1
+ # @meru2802/nexus-modscript-composer
2
+
3
+ Compose multi-layer deployment scripts for NexusEPM agents. Takes individual L0 monitoring/collection scripts and produces a composed L1 collection script + L2 deployment wrapper (systemd service on Linux, Scheduled Task on Windows).
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install @meru2802/nexus-modscript-composer
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ ```typescript
14
+ import { generateFinalScript } from "@meru2802/nexus-modscript-composer";
15
+
16
+ const l0Bodies = [tomcatScript, postgresqlScript]; // raw L0 script strings with metadata headers
17
+
18
+ const result = await generateFinalScript(l0Bodies, {
19
+ platform: "windows",
20
+ templateUrls: {
21
+ l1: "https://nexus-endpoint-desktop-app.s3.ap-south-1.amazonaws.com/l1-l2-templates/l1-windows.ps1.tmpl",
22
+ l2: "https://nexus-endpoint-desktop-app.s3.ap-south-1.amazonaws.com/l1-l2-templates/l2-windows.ps1.tmpl",
23
+ },
24
+ orgId: "acme-corp",
25
+ icebergEndpoint: "https://iceberg.example.com/api/v1/collect",
26
+ agentId: "agent-001",
27
+ authToken: "bearer-token",
28
+ bufferTime: "5m",
29
+ timeout: "10m",
30
+ });
31
+
32
+ console.log(result.l1Script); // composed L1 collection script
33
+ console.log(result.l2Script); // L2 deployment wrapper
34
+ console.log(result.modules); // ["tomcat", "postgresql"]
35
+ ```
36
+
37
+ ## The L0 output contract (`OUTPUT_SCHEMA_VERSION: 2`)
38
+
39
+ An L0 collector writes a stream of **single-line JSON records** to stdout — nothing else. There are exactly two shapes:
40
+
41
+ ```
42
+ {"log":"probing for tomcat via ps"}
43
+ {"log":"found 2 catalina processes"}
44
+ {"result":{"module":"tomcat","category":"app_monitoring","status":"ok","timestamp":"...","error":null,"data":{}}}
45
+ ```
46
+
47
+ Any number of `{"log":...}` records, then exactly one `{"result":...}` as the last line.
48
+
49
+ L1 captures each module's stream, folds it into one envelope, and sends **one POST per module**. Your records travel **byte-for-byte** — L1 never unwraps or reshapes them:
50
+
51
+ ```json
52
+ {
53
+ "agent_id": "agent-001",
54
+ "org_id": "acme-corp",
55
+ "module": "tomcat",
56
+ "timestamp": "2026-07-29T10:00:00Z",
57
+ "logs": [ {"log":"probing for tomcat via ps"}, {"log":"found 2 catalina processes"} ],
58
+ "result": { "result": { "module": "tomcat", "status": "ok", "data": {} } }
59
+ }
60
+ ```
61
+
62
+ Because nothing is unwrapped, the collector payload sits at `envelope.result.result`.
63
+
64
+ If a collector errors or reaches the L1 timeout, L1 preserves every valid
65
+ `{"log":...}` record emitted before the failure, appends a synthetic error
66
+ result when needed, and still attempts the module POST before exiting.
67
+
68
+ Collectors get two helpers, defined inside the collector function:
69
+
70
+ ```sh
71
+ _nexus_log "method 3 (process): running=true score=5" # → {"log":"..."}
72
+ cat <<EOF | _nexus_result # → {"result":{...}} on one line
73
+ { "module": "tomcat", ... }
74
+ EOF
75
+ ```
76
+
77
+ Verify any collector against the contract with:
78
+
79
+ ```bash
80
+ ./scripts/verify-l0-contract.sh # all scripts in l0-scripts/
81
+ ./scripts/verify-l0-contract.sh l0-scripts/mine.sh # one script
82
+ ```
83
+
84
+ See `AIX-L0-AUTHORING-GUIDE.md` §4 for the full contract, including the PowerShell equivalents.
85
+
86
+ ## Template URLs
87
+
88
+ | Platform | Layer | URL |
89
+ |----------|-------|-----|
90
+ | Linux | L1 | `https://nexus-endpoint-desktop-app.s3.ap-south-1.amazonaws.com/l1-l2-templates/l1-linux.sh.tmpl` |
91
+ | Linux | L2 | `https://nexus-endpoint-desktop-app.s3.ap-south-1.amazonaws.com/l1-l2-templates/l2-linux.sh.tmpl` |
92
+ | Windows | L1 | `https://nexus-endpoint-desktop-app.s3.ap-south-1.amazonaws.com/l1-l2-templates/l1-windows.ps1.tmpl` |
93
+ | Windows | L2 | `https://nexus-endpoint-desktop-app.s3.ap-south-1.amazonaws.com/l1-l2-templates/l2-windows.ps1.tmpl` |
94
+
95
+ ## `generateFinalScript(l0ScriptBodies, options)`
96
+
97
+ ### Parameters
98
+
99
+ **`l0ScriptBodies`** `string[]` -- Array of raw L0 script contents. Each string must include the full metadata header (`# ==== NEXUS MODSCRIPT L0 ====` ... `# ==== END METADATA ====`) followed by the function body. All L0 scripts must target the same `OS_FAMILY` as the `platform` option.
100
+
101
+ **`options`** `GenerateOptions`:
102
+
103
+ | Field | Type | Required | Default | Description |
104
+ |-------|------|----------|---------|-------------|
105
+ | `platform` | `"linux" \| "windows"` | Yes | -- | Target OS. Must match the `OS_FAMILY` in all L0 scripts. |
106
+ | `templateUrls` | `{ l1: string, l2: string }` | Yes | -- | URLs to fetch the L1 and L2 template files. See table above. |
107
+ | `orgId` | `string` | No | `"test-client"` | Organization ID baked into L1 for Iceberg payloads. |
108
+ | `icebergEndpoint` | `string` | No | `"https://iceberg.example.com/api/v1/collect"` | URL where L1 POSTs collected data. Written to the L2 env file. |
109
+ | `agentId` | `string` | No | `"test-agent-001"` | Agent identifier included in Iceberg payloads. |
110
+ | `authToken` | `string` | No | `"test-bearer-token"` | Bearer token for Iceberg API authentication. |
111
+ | `bufferTime` | `string` | No | `"5m"` | Wait time after L1 completes before next run. Accepts `"30s"`, `"5m"`, `"1h"`. Linux: `OnUnitInactiveSec`. Windows: `Start-Sleep` in wrapper loop. |
112
+ | `timeout` | `string` | No | `"10m"` | L0 collection deadline. At the deadline L1 stops unfinished collectors and posts their partial logs; L2 retains a bounded hard-kill grace period. Same format as `bufferTime`. |
113
+ | `maxParallel` | `number` | No | `5` | Max parallel L0 modules. Linux: background jobs. Windows: runspace pool size. |
114
+ | `previousServiceId` | `string` | No | `""` | Service ID of a previous deployment to tear down before installing the new one. |
115
+ | `writeToDisk` | `boolean` | No | `false` | Write the composed scripts to timestamped files on disk. |
116
+ | `outputDir` | `string` | No | `"./final-scripts"` | Directory for `writeToDisk` output. Resolved relative to `process.cwd()`. |
117
+
118
+ ### Return Value
119
+
120
+ ```typescript
121
+ {
122
+ l1Script: string; // Composed L1 collection script (all L0 modules merged)
123
+ l2Script: string; // L2 deployment wrapper (installs L1 as systemd service or Scheduled Task)
124
+ platform: "linux" | "windows";
125
+ modules: string[]; // Names of L0 modules included, e.g. ["tomcat", "postgresql"]
126
+ l1Path?: string; // File path if writeToDisk was true
127
+ l2Path?: string; // File path if writeToDisk was true
128
+ }
129
+ ```
130
+
131
+ The function validates the composed scripts (structural checks, syntax via `bash -n`/`shellcheck`/`pwsh`, and a dry-run extraction test) before returning. It throws if validation fails.
132
+
133
+ ## Windows deletion verifier lifecycle
134
+
135
+ Use the `deletion-verifier` lifecycle only for a Windows Nexus Endpoint removal job. It requires exactly one L0: [`l0-scripts/nexus_endpoint_deletion.ps1`](l0-scripts/nexus_endpoint_deletion.ps1). The collector is read-only and reports the remaining owned artifacts; it never deletes them.
136
+
137
+ ```ts
138
+ const result = await generateFinalScript([nexusDeletionL0], {
139
+ platform: "windows",
140
+ templateUrls,
141
+ orgId: "org-1",
142
+ icebergEndpoint: "https://iceberg.example.com/v1/nexus-deletion/events",
143
+ agentId: "agent-1",
144
+ bufferTime: "30s",
145
+ timeout: "60s",
146
+ lifecycle: {
147
+ kind: "deletion-verifier",
148
+ jobId: "delete-2026-09-25-agent-1",
149
+ callbackToken: "one-job-scoped-token",
150
+ expectedWindowsUpdateAuOptions: null, // value recorded before installation; null = absent
151
+ },
152
+ });
153
+ ```
154
+
155
+ This produces the isolated task `\NexusDeletionVerifier\NexusDeletionVerifier-<jobId>` and only uses `C:\ProgramData\NexusDeletionVerifier\<jobId>`. It does not share the normal `NexusModScripts` root. L1 writes `completion.ack` only when all three conditions hold: the L0 result has `data.complete: true`, its `data.job_id` matches the configured job, and Iceberg returned a 2xx response for that exact envelope. L2 then unregisters the verifier task and deletes only its isolated root.
156
+
157
+ The normal bulk cleanup helper is [`scripts/purge-nexus-modscripts.ps1`](scripts/purge-nexus-modscripts.ps1). It removes regular `\NexusModScripts\` deployments and deliberately never removes a deletion verifier.
158
+
159
+ ### Iceberg controller contract
160
+
161
+ The controller must create a unique job ID and callback token, deploy the verifier **before** issuing the RMM uninstall command, and accept idempotent callback events. Treat completion as valid only when the authenticated callback has:
162
+
163
+ ```json
164
+ {
165
+ "module": "nexus_endpoint_deletion",
166
+ "result": {
167
+ "result": {
168
+ "data": {
169
+ "job_id": "the-controller-job-id",
170
+ "manifest_version": 1,
171
+ "complete": true,
172
+ "remaining": []
173
+ }
174
+ }
175
+ }
176
+ }
177
+ ```
178
+
179
+ Require the scoped token plus `X-Nexus-Deletion-Job` to match the job. Deduplicate `X-Nexus-Event-Id`; persist every non-complete scan as progress; and mark the job complete only after receiving this complete event. Record `AUOptions` before installation and pass that original value (or `null` when absent) as `expectedWindowsUpdateAuOptions`; restore it before accepting completion. For existing machines that lack this installation-time journal, require an explicit administrator-selected baseline and keep the job pending until one is supplied. Do not rely on the TacticalRMM DELETE HTTP response as proof of endpoint cleanup: it removes the management record before the agent-side uninstall has necessarily finished. Start the verifier after all ordinary modular scripts have been purged and before the first endpoint/RMM removal action, so it remains available to observe the final state.
@@ -0,0 +1,83 @@
1
+ import type { L0Script, ModScriptLifecycle, OsFamily } from "./types.js";
2
+ /**
3
+ * Normalize all line endings to CRLF for Windows PowerShell 5.1 compatibility.
4
+ * Strips all bare \r first (handles \r\n→\n and stray \r→nothing),
5
+ * then converts every \n to \r\n.
6
+ * Guarantees no bare \r survives before here-string terminators ("@, '@).
7
+ *
8
+ * Also replaces non-ASCII characters (box-drawing, em/en dashes) with ASCII
9
+ * equivalents. PowerShell 5.1 reads files without a BOM using the system's
10
+ * ANSI code page (usually Windows-1252). Multi-byte UTF-8 sequences like
11
+ * ─ (U+2500, box-drawing) decode as â"€, where byte 0x94 is a RIGHT DOUBLE
12
+ * QUOTATION MARK — this can corrupt the tokenizer's string-tracking state
13
+ * and cause spurious "Missing closing '}'" parse errors.
14
+ */
15
+ export declare function normalizeWindowsLineEndings(content: string): string;
16
+ export interface ComposeL1Params {
17
+ orgId: string;
18
+ l0Scripts: L0Script[];
19
+ osFamily: OsFamily;
20
+ template: string;
21
+ }
22
+ /**
23
+ * Compose an L1 script from multiple L0 scripts.
24
+ *
25
+ * For Linux: L0 functions run in parallel as background jobs (&),
26
+ * each writes to a temp file, results are pushed individually.
27
+ * For Windows: L0 functions run in parallel using PowerShell runspace pool,
28
+ * results are pushed individually.
29
+ */
30
+ export declare function composeL1(params: ComposeL1Params): string;
31
+ export interface ComposeL2Params {
32
+ l1Script: string;
33
+ serviceName?: string;
34
+ osFamily: OsFamily;
35
+ template: string;
36
+ bufferTime?: string;
37
+ bufferTimeSeconds?: number;
38
+ timeout?: string;
39
+ timeoutSeconds?: number;
40
+ icebergEndpoint?: string;
41
+ agentId?: string;
42
+ authToken?: string;
43
+ orgId?: string;
44
+ maxParallel?: number;
45
+ previousServiceId?: string;
46
+ lifecycle?: ModScriptLifecycle;
47
+ }
48
+ /**
49
+ * Wrap an L1 script inside an L2 deployment wrapper.
50
+ *
51
+ * For Linux: L2 creates systemd service+timer, writes L1 to disk,
52
+ * creates env file, enables and starts the timer.
53
+ * Parameters are baked into the script (not passed as args)
54
+ * because TacticalRMM runs the L2 once to install.
55
+ *
56
+ * For Windows: L2 creates a Scheduled Task running as SYSTEM,
57
+ * writes L1 + env + wrapper to disk, registers the task.
58
+ */
59
+ export declare function composeL2(params: ComposeL2Params): {
60
+ script: string;
61
+ args: string[];
62
+ shell: "shell" | "powershell";
63
+ requiredRuntimeArgs: string[];
64
+ };
65
+ /**
66
+ * Parse a human-readable interval string into seconds, minutes, and systemd format.
67
+ * Examples: "5m" → { seconds: 300, minutes: 5, systemd: "5min" }
68
+ */
69
+ export declare function parseInterval(interval: string): {
70
+ seconds: number;
71
+ minutes: number;
72
+ systemd: string;
73
+ };
74
+ /**
75
+ * Validate that every here-string terminator ('@ or "@) in a PowerShell script
76
+ * conforms to PowerShell 5.1 rules:
77
+ * - Must be at column 0 (no leading whitespace)
78
+ * - No bare \r anywhere in the script
79
+ *
80
+ * Returns an array of error messages (empty = valid).
81
+ */
82
+ export declare function validateHereStringTerminators(script: string): string[];
83
+ //# sourceMappingURL=composer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"composer.d.ts","sourceRoot":"","sources":["../src/composer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,kBAAkB,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAKzE;;;;;;;;;;;;GAYG;AACH,wBAAgB,2BAA2B,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CASnE;AAqBD,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,QAAQ,EAAE,CAAC;IACtB,QAAQ,EAAE,QAAQ,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;;GAOG;AACH,wBAAgB,SAAS,CAAC,MAAM,EAAE,eAAe,GAAG,MAAM,CAgFzD;AAID,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,QAAQ,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IAEjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,SAAS,CAAC,EAAE,kBAAkB,CAAC;CAChC;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,SAAS,CAAC,MAAM,EAAE,eAAe,GAAG;IAClD,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,KAAK,EAAE,OAAO,GAAG,YAAY,CAAC;IAC9B,mBAAmB,EAAE,MAAM,EAAE,CAAC;CAC/B,CAkBA;AAwMD;;;GAGG;AACH,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG;IAC/C,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;CACjB,CAuCA;AAID;;;;;;;GAOG;AACH,wBAAgB,6BAA6B,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAwBtE"}
@@ -0,0 +1,367 @@
1
+ import { createLogger } from "./logger.js";
2
+ const log = createLogger("composer");
3
+ /**
4
+ * Normalize all line endings to CRLF for Windows PowerShell 5.1 compatibility.
5
+ * Strips all bare \r first (handles \r\n→\n and stray \r→nothing),
6
+ * then converts every \n to \r\n.
7
+ * Guarantees no bare \r survives before here-string terminators ("@, '@).
8
+ *
9
+ * Also replaces non-ASCII characters (box-drawing, em/en dashes) with ASCII
10
+ * equivalents. PowerShell 5.1 reads files without a BOM using the system's
11
+ * ANSI code page (usually Windows-1252). Multi-byte UTF-8 sequences like
12
+ * ─ (U+2500, box-drawing) decode as â"€, where byte 0x94 is a RIGHT DOUBLE
13
+ * QUOTATION MARK — this can corrupt the tokenizer's string-tracking state
14
+ * and cause spurious "Missing closing '}'" parse errors.
15
+ */
16
+ export function normalizeWindowsLineEndings(content) {
17
+ return content
18
+ .replace(/\r/g, "")
19
+ .replace(/[─━┄┅┈┉╌╍═]/g, "-") // box-drawing horizontals → ASCII dash
20
+ .replace(/[│┃┆┇┊┋╎╏║]/g, "|") // box-drawing verticals → ASCII pipe
21
+ .replace(/—/g, "--") // em dash -> double dash
22
+ .replace(/–/g, "-") // en dash -> single dash
23
+ .replace(/[→←↑↓↔↕]/g, "->") // arrows -> ASCII arrow
24
+ .replace(/\n/g, "\r\n");
25
+ }
26
+ /**
27
+ * Safe template placeholder replacement that avoids special $ patterns.
28
+ * String.prototype.replace treats $', $`, $&, $1 etc. as special in the
29
+ * replacement string. Using a function callback avoids this entirely.
30
+ */
31
+ function templateReplace(template, placeholder, value) {
32
+ const pattern = new RegExp(placeholder.replace(/[{}]/g, (c) => `\\${c}`), "g");
33
+ return template.replace(pattern, () => value);
34
+ }
35
+ /**
36
+ * Compose an L1 script from multiple L0 scripts.
37
+ *
38
+ * For Linux: L0 functions run in parallel as background jobs (&),
39
+ * each writes to a temp file, results are pushed individually.
40
+ * For Windows: L0 functions run in parallel using PowerShell runspace pool,
41
+ * results are pushed individually.
42
+ */
43
+ export function composeL1(params) {
44
+ const { orgId, l0Scripts, osFamily } = params;
45
+ log.info("Composing L1 script", {
46
+ orgId,
47
+ osFamily,
48
+ moduleCount: l0Scripts.length,
49
+ modules: l0Scripts.map((l0) => l0.meta.name),
50
+ });
51
+ // Validate all L0 scripts match the target OS
52
+ for (const l0 of l0Scripts) {
53
+ if (l0.meta.osFamily !== osFamily) {
54
+ log.error("OS family mismatch in L0 script", {
55
+ scriptName: l0.meta.name,
56
+ scriptOs: l0.meta.osFamily,
57
+ targetOs: osFamily,
58
+ });
59
+ throw new Error(`L0 "${l0.meta.name}" is for ${l0.meta.osFamily}, but target OS is ${osFamily}`);
60
+ }
61
+ }
62
+ const template = params.template;
63
+ log.debug("L1 template received", { templateLength: template.length });
64
+ // Extract module names and versions
65
+ const moduleNames = l0Scripts.map((l0) => l0.meta.name);
66
+ const versionList = l0Scripts
67
+ .map((l0) => `${l0.meta.name}@${l0.meta.version}`)
68
+ .join(", ");
69
+ // Build module names array string for the target language.
70
+ // Linux (bash `arr=(...)`) and AIX (ksh `set -A arr ...`) both want
71
+ // space-separated quoted names; Windows (PowerShell `@(...)`) wants commas.
72
+ const moduleNamesArray = osFamily === "windows"
73
+ ? moduleNames.map((n) => `"${n}"`).join(", ")
74
+ : moduleNames.map((n) => `"${n}"`).join(" ");
75
+ // Concatenate all L0 function bodies
76
+ const l0Bodies = l0Scripts
77
+ .map((l0) => `# ── L0: ${l0.meta.name} v${l0.meta.version} (${l0.meta.category}) ──\n${l0.functionBody}`)
78
+ .join("\n\n");
79
+ log.trace("L0 bodies concatenated", { totalLength: l0Bodies.length });
80
+ // Replace template placeholders — use templateReplace for values that may
81
+ // contain $ characters (L0 bodies, module arrays) to avoid JS replace() $-patterns
82
+ let composed = template;
83
+ composed = templateReplace(composed, "{{ORG_ID}}", orgId);
84
+ composed = templateReplace(composed, "{{GENERATED_AT}}", new Date().toISOString());
85
+ composed = templateReplace(composed, "{{MODULE_LIST}}", moduleNames.join(", "));
86
+ composed = templateReplace(composed, "{{L0_VERSION_LIST}}", versionList);
87
+ composed = templateReplace(composed, "{{L0_FUNCTION_BODIES}}", l0Bodies);
88
+ composed = templateReplace(composed, "{{MODULE_NAMES_ARRAY}}", moduleNamesArray);
89
+ // Normalize line endings: Windows PowerShell 5.1 fails on mixed \r\n and \n,
90
+ // and bare \r before here-string terminators ("@, '@) breaks parsing.
91
+ if (osFamily === "windows") {
92
+ composed = normalizeWindowsLineEndings(composed);
93
+ const terminatorErrors = validateHereStringTerminators(composed);
94
+ if (terminatorErrors.length > 0) {
95
+ throw new Error(`L1 here-string terminator corruption after normalization: ${terminatorErrors.join("; ")}`);
96
+ }
97
+ }
98
+ log.info("L1 composition complete", {
99
+ outputLength: composed.length,
100
+ versionList,
101
+ });
102
+ return composed;
103
+ }
104
+ /**
105
+ * Wrap an L1 script inside an L2 deployment wrapper.
106
+ *
107
+ * For Linux: L2 creates systemd service+timer, writes L1 to disk,
108
+ * creates env file, enables and starts the timer.
109
+ * Parameters are baked into the script (not passed as args)
110
+ * because TacticalRMM runs the L2 once to install.
111
+ *
112
+ * For Windows: L2 creates a Scheduled Task running as SYSTEM,
113
+ * writes L1 + env + wrapper to disk, registers the task.
114
+ */
115
+ export function composeL2(params) {
116
+ const { osFamily } = params;
117
+ log.info("Composing L2 deployment wrapper", {
118
+ osFamily,
119
+ serviceName: params.serviceName,
120
+ bufferTime: params.bufferTime,
121
+ timeout: params.timeout,
122
+ orgId: params.orgId,
123
+ });
124
+ if (osFamily === "linux") {
125
+ return composeL2Linux(params);
126
+ }
127
+ else if (osFamily === "aix") {
128
+ return composeL2Aix(params);
129
+ }
130
+ else {
131
+ return composeL2Windows(params);
132
+ }
133
+ }
134
+ function composeL2Linux(params) {
135
+ const template = params.template;
136
+ log.debug("Composing L2 Linux (systemd service+timer)", {
137
+ serviceName: params.serviceName,
138
+ bufferTime: params.bufferTime,
139
+ timeout: params.timeout,
140
+ l1Length: params.l1Script.length,
141
+ });
142
+ // Replace all placeholders — must use templateReplace because L1
143
+ // contains L0 bodies with $ patterns that JS replace() misinterprets.
144
+ // Missing params get empty string (script validates at runtime).
145
+ let script = templateReplace(template, "{{L1_SCRIPT_BODY}}", params.l1Script);
146
+ script = templateReplace(script, "{{SERVICE_NAME}}", params.serviceName ?? "");
147
+ script = templateReplace(script, "{{PREVIOUS_SERVICE_ID}}", params.previousServiceId ?? "");
148
+ script = templateReplace(script, "{{BUFFER_TIME}}", params.bufferTime ?? "");
149
+ script = templateReplace(script, "{{ICEBERG_ENDPOINT}}", params.icebergEndpoint ?? "");
150
+ script = templateReplace(script, "{{AGENT_ID}}", params.agentId ?? "");
151
+ script = templateReplace(script, "{{AUTH_TOKEN}}", params.authToken ?? "");
152
+ script = templateReplace(script, "{{ORG_ID}}", params.orgId ?? "");
153
+ script = templateReplace(script, "{{MAX_PARALLEL}}", String(params.maxParallel ?? 5));
154
+ script = templateReplace(script, "{{TIMEOUT}}", params.timeout ?? "");
155
+ const args = [params.serviceName ?? "", params.previousServiceId ?? ""];
156
+ // Track which params were NOT provided at composition time
157
+ const requiredRuntimeArgs = [];
158
+ if (!params.serviceName)
159
+ requiredRuntimeArgs.push("--service-name");
160
+ if (!params.bufferTime)
161
+ requiredRuntimeArgs.push("--buffer-time");
162
+ if (!params.icebergEndpoint)
163
+ requiredRuntimeArgs.push("--iceberg-endpoint");
164
+ if (!params.agentId)
165
+ requiredRuntimeArgs.push("--agent-id");
166
+ if (!params.authToken)
167
+ requiredRuntimeArgs.push("--auth-token");
168
+ if (!params.orgId)
169
+ requiredRuntimeArgs.push("--org-id");
170
+ if (!params.timeout)
171
+ requiredRuntimeArgs.push("--timeout");
172
+ log.debug("L2 Linux composed", {
173
+ outputLength: script.length,
174
+ argCount: args.length,
175
+ requiredRuntimeArgs,
176
+ });
177
+ return { script, args, shell: "shell", requiredRuntimeArgs };
178
+ }
179
+ function composeL2Aix(params) {
180
+ const template = params.template;
181
+ log.debug("Composing L2 AIX (SRC subsystem + ksh wrapper daemon)", {
182
+ serviceName: params.serviceName,
183
+ bufferTimeSeconds: params.bufferTimeSeconds,
184
+ timeoutSeconds: params.timeoutSeconds,
185
+ l1Length: params.l1Script.length,
186
+ });
187
+ // Replace all placeholders — must use templateReplace because L1
188
+ // contains L0 bodies with $ patterns that JS replace() misinterprets.
189
+ // The SRC wrapper daemon uses integer-seconds timing (sleep N / watchdog),
190
+ // like the Windows wrapper. The human-readable forms are kept for the
191
+ // JSON service_id output only.
192
+ let script = templateReplace(template, "{{L1_SCRIPT_BODY}}", params.l1Script);
193
+ script = templateReplace(script, "{{SERVICE_NAME}}", params.serviceName ?? "");
194
+ script = templateReplace(script, "{{PREVIOUS_SERVICE_ID}}", params.previousServiceId ?? "");
195
+ script = templateReplace(script, "{{BUFFER_TIME}}", params.bufferTime ?? "");
196
+ script = templateReplace(script, "{{BUFFER_TIME_SECONDS}}", String(params.bufferTimeSeconds ?? 0));
197
+ script = templateReplace(script, "{{TIMEOUT}}", params.timeout ?? "");
198
+ script = templateReplace(script, "{{TIMEOUT_SECONDS}}", String(params.timeoutSeconds ?? 0));
199
+ script = templateReplace(script, "{{ICEBERG_ENDPOINT}}", params.icebergEndpoint ?? "");
200
+ script = templateReplace(script, "{{AGENT_ID}}", params.agentId ?? "");
201
+ script = templateReplace(script, "{{AUTH_TOKEN}}", params.authToken ?? "");
202
+ script = templateReplace(script, "{{ORG_ID}}", params.orgId ?? "");
203
+ script = templateReplace(script, "{{MAX_PARALLEL}}", String(params.maxParallel ?? 5));
204
+ const args = [params.serviceName ?? "", params.previousServiceId ?? ""];
205
+ // Track which params were NOT provided at composition time
206
+ const requiredRuntimeArgs = [];
207
+ if (!params.serviceName)
208
+ requiredRuntimeArgs.push("--service-name");
209
+ if (!params.bufferTimeSeconds)
210
+ requiredRuntimeArgs.push("--buffer-time");
211
+ if (!params.icebergEndpoint)
212
+ requiredRuntimeArgs.push("--iceberg-endpoint");
213
+ if (!params.agentId)
214
+ requiredRuntimeArgs.push("--agent-id");
215
+ if (!params.authToken)
216
+ requiredRuntimeArgs.push("--auth-token");
217
+ if (!params.orgId)
218
+ requiredRuntimeArgs.push("--org-id");
219
+ if (!params.timeoutSeconds)
220
+ requiredRuntimeArgs.push("--timeout");
221
+ log.debug("L2 AIX composed", {
222
+ outputLength: script.length,
223
+ argCount: args.length,
224
+ requiredRuntimeArgs,
225
+ });
226
+ return { script, args, shell: "shell", requiredRuntimeArgs };
227
+ }
228
+ function composeL2Windows(params) {
229
+ const template = params.template;
230
+ const lifecycle = params.lifecycle ?? { kind: "continuous" };
231
+ const effectiveAuthToken = lifecycle.kind === "deletion-verifier"
232
+ ? lifecycle.callbackToken
233
+ : (params.authToken ?? "");
234
+ log.debug("Composing L2 Windows (Scheduled Task)", {
235
+ serviceName: params.serviceName,
236
+ bufferTimeSeconds: params.bufferTimeSeconds,
237
+ timeoutSeconds: params.timeoutSeconds,
238
+ l1Length: params.l1Script.length,
239
+ });
240
+ // Replace all placeholders — must use templateReplace because L1
241
+ // contains L0 bodies with $ patterns that JS replace() misinterprets.
242
+ // Missing int params get 0 (sentinel), strings get empty.
243
+ let script = templateReplace(template, "{{L1_SCRIPT_BODY}}", params.l1Script);
244
+ script = templateReplace(script, "{{SERVICE_NAME}}", params.serviceName ?? "");
245
+ script = templateReplace(script, "{{PREVIOUS_SERVICE_ID}}", params.previousServiceId ?? "");
246
+ script = templateReplace(script, "{{BUFFER_TIME_SECONDS}}", String(params.bufferTimeSeconds ?? 0));
247
+ script = templateReplace(script, "{{TIMEOUT_SECONDS}}", String(params.timeoutSeconds ?? 0));
248
+ script = templateReplace(script, "{{ICEBERG_ENDPOINT}}", params.icebergEndpoint ?? "");
249
+ script = templateReplace(script, "{{AGENT_ID}}", params.agentId ?? "");
250
+ script = templateReplace(script, "{{AUTH_TOKEN}}", effectiveAuthToken);
251
+ script = templateReplace(script, "{{ORG_ID}}", params.orgId ?? "");
252
+ script = templateReplace(script, "{{MAX_PARALLEL}}", String(params.maxParallel ?? 5));
253
+ script = templateReplace(script, "{{LIFECYCLE_KIND}}", lifecycle.kind);
254
+ script = templateReplace(script, "{{DELETION_JOB_ID}}", lifecycle.kind === "deletion-verifier" ? lifecycle.jobId : "");
255
+ script = templateReplace(script, "{{EXPECTED_WINDOWS_UPDATE_AU_OPTIONS}}", lifecycle.kind === "deletion-verifier"
256
+ ? (lifecycle.expectedWindowsUpdateAuOptions === null
257
+ ? "absent"
258
+ : String(lifecycle.expectedWindowsUpdateAuOptions))
259
+ : "");
260
+ const moduleListMatch = params.l1Script.match(/^# MODULES:\s*(.+)$/m);
261
+ const moduleCount = moduleListMatch?.[1]
262
+ ?.split(",")
263
+ .map((name) => name.trim())
264
+ .filter(Boolean).length ?? 1;
265
+ script = templateReplace(script, "{{MODULE_COUNT}}", String(Math.max(1, moduleCount)));
266
+ // Template has LF, injected L1 has CRLF → mixed endings.
267
+ // Normalize entire L2 to CRLF for PowerShell 5.1 here-string compatibility.
268
+ script = normalizeWindowsLineEndings(script);
269
+ const terminatorErrors = validateHereStringTerminators(script);
270
+ if (terminatorErrors.length > 0) {
271
+ throw new Error(`L2 here-string terminator corruption after normalization: ${terminatorErrors.join("; ")}`);
272
+ }
273
+ const args = [`-ServiceName`, params.serviceName ?? ""];
274
+ if (params.previousServiceId) {
275
+ args.push(`-PreviousServiceId`, params.previousServiceId);
276
+ }
277
+ // Track which params were NOT provided at composition time
278
+ const requiredRuntimeArgs = [];
279
+ if (!params.serviceName)
280
+ requiredRuntimeArgs.push("-ServiceName");
281
+ if (!params.bufferTimeSeconds)
282
+ requiredRuntimeArgs.push("-BufferTimeSeconds");
283
+ if (!params.timeoutSeconds)
284
+ requiredRuntimeArgs.push("-TimeoutSeconds");
285
+ if (!params.icebergEndpoint)
286
+ requiredRuntimeArgs.push("-IcebergEndpoint");
287
+ if (!params.agentId)
288
+ requiredRuntimeArgs.push("-AgentId");
289
+ if (!effectiveAuthToken)
290
+ requiredRuntimeArgs.push("-AuthToken");
291
+ if (!params.orgId)
292
+ requiredRuntimeArgs.push("-OrgId");
293
+ if (lifecycle.kind === "deletion-verifier" && !lifecycle.jobId) {
294
+ requiredRuntimeArgs.push("deletion lifecycle jobId");
295
+ }
296
+ log.debug("L2 Windows composed", {
297
+ outputLength: script.length,
298
+ argCount: args.length,
299
+ requiredRuntimeArgs,
300
+ });
301
+ return { script, args, shell: "powershell", requiredRuntimeArgs };
302
+ }
303
+ // INTERVAL PARSING
304
+ /**
305
+ * Parse a human-readable interval string into seconds, minutes, and systemd format.
306
+ * Examples: "5m" → { seconds: 300, minutes: 5, systemd: "5min" }
307
+ */
308
+ export function parseInterval(interval) {
309
+ log.debug("Parsing interval", { interval });
310
+ const match = interval.match(/^(\d+)\s*(s|sec|seconds?|m|min|minutes?|h|hr|hours?)$/i);
311
+ if (!match) {
312
+ log.error("Invalid interval format", { interval });
313
+ throw new Error(`Invalid interval: "${interval}". Use format like "5m", "1h", "30s".`);
314
+ }
315
+ const value = parseInt(match[1], 10);
316
+ const unit = match[2].toLowerCase();
317
+ let result;
318
+ if (unit.startsWith("s")) {
319
+ result = {
320
+ seconds: value,
321
+ minutes: Math.max(1, Math.ceil(value / 60)),
322
+ systemd: `${value}s`,
323
+ };
324
+ }
325
+ else if (unit.startsWith("h")) {
326
+ result = { seconds: value * 3600, minutes: value * 60, systemd: `${value}h` };
327
+ }
328
+ else {
329
+ // minutes
330
+ result = { seconds: value * 60, minutes: value, systemd: `${value}min` };
331
+ }
332
+ log.debug("Interval parsed", {
333
+ input: interval,
334
+ seconds: result.seconds,
335
+ minutes: result.minutes,
336
+ systemd: result.systemd,
337
+ });
338
+ return result;
339
+ }
340
+ // HERE-STRING TERMINATOR VALIDATION
341
+ /**
342
+ * Validate that every here-string terminator ('@ or "@) in a PowerShell script
343
+ * conforms to PowerShell 5.1 rules:
344
+ * - Must be at column 0 (no leading whitespace)
345
+ * - No bare \r anywhere in the script
346
+ *
347
+ * Returns an array of error messages (empty = valid).
348
+ */
349
+ export function validateHereStringTerminators(script) {
350
+ const errors = [];
351
+ const lines = script.split(/\r?\n/);
352
+ for (let i = 0; i < lines.length; i++) {
353
+ const line = lines[i];
354
+ // Detect MISPLACED terminators: leading whitespace before '@ or "@
355
+ if (/^\s+['"]@\s*$/.test(line)) {
356
+ errors.push(`Line ${i + 1}: here-string terminator has leading whitespace — ` +
357
+ `PowerShell 5.1 will not recognize it`);
358
+ }
359
+ }
360
+ // Check for bare \r (not followed by \n) — corrupts here-string parsing
361
+ const bareCR = script.match(/\r(?!\n)/g);
362
+ if (bareCR) {
363
+ errors.push(`${bareCR.length} bare \\r character(s) found — will corrupt here-string parsing`);
364
+ }
365
+ return errors;
366
+ }
367
+ //# sourceMappingURL=composer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"composer.js","sourceRoot":"","sources":["../src/composer.ts"],"names":[],"mappings":"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"}