@opensip-cli/external-tool-adapter 0.1.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.
Files changed (172) hide show
  1. package/LICENSE +202 -0
  2. package/NOTICE +8 -0
  3. package/README.md +33 -0
  4. package/dist/__tests__/acceptance-harness.test.d.ts +2 -0
  5. package/dist/__tests__/acceptance-harness.test.d.ts.map +1 -0
  6. package/dist/__tests__/acceptance-harness.test.js +106 -0
  7. package/dist/__tests__/acceptance-harness.test.js.map +1 -0
  8. package/dist/__tests__/artifact-path.test.d.ts +2 -0
  9. package/dist/__tests__/artifact-path.test.d.ts.map +1 -0
  10. package/dist/__tests__/artifact-path.test.js +19 -0
  11. package/dist/__tests__/artifact-path.test.js.map +1 -0
  12. package/dist/__tests__/binary-resolver.test.d.ts +2 -0
  13. package/dist/__tests__/binary-resolver.test.d.ts.map +1 -0
  14. package/dist/__tests__/binary-resolver.test.js +64 -0
  15. package/dist/__tests__/binary-resolver.test.js.map +1 -0
  16. package/dist/__tests__/define-external-tool-adapter.test.d.ts +2 -0
  17. package/dist/__tests__/define-external-tool-adapter.test.d.ts.map +1 -0
  18. package/dist/__tests__/define-external-tool-adapter.test.js +165 -0
  19. package/dist/__tests__/define-external-tool-adapter.test.js.map +1 -0
  20. package/dist/__tests__/doctor-command.test.d.ts +2 -0
  21. package/dist/__tests__/doctor-command.test.d.ts.map +1 -0
  22. package/dist/__tests__/doctor-command.test.js +124 -0
  23. package/dist/__tests__/doctor-command.test.js.map +1 -0
  24. package/dist/__tests__/exit-model.test.d.ts +2 -0
  25. package/dist/__tests__/exit-model.test.d.ts.map +1 -0
  26. package/dist/__tests__/exit-model.test.js +30 -0
  27. package/dist/__tests__/exit-model.test.js.map +1 -0
  28. package/dist/__tests__/fingerprint.test.d.ts +2 -0
  29. package/dist/__tests__/fingerprint.test.d.ts.map +1 -0
  30. package/dist/__tests__/fingerprint.test.js +39 -0
  31. package/dist/__tests__/fingerprint.test.js.map +1 -0
  32. package/dist/__tests__/gate-render.test.d.ts +2 -0
  33. package/dist/__tests__/gate-render.test.d.ts.map +1 -0
  34. package/dist/__tests__/gate-render.test.js +82 -0
  35. package/dist/__tests__/gate-render.test.js.map +1 -0
  36. package/dist/__tests__/ingest-json.test.d.ts +2 -0
  37. package/dist/__tests__/ingest-json.test.d.ts.map +1 -0
  38. package/dist/__tests__/ingest-json.test.js +53 -0
  39. package/dist/__tests__/ingest-json.test.js.map +1 -0
  40. package/dist/__tests__/ingest-sarif.test.d.ts +2 -0
  41. package/dist/__tests__/ingest-sarif.test.d.ts.map +1 -0
  42. package/dist/__tests__/ingest-sarif.test.js +283 -0
  43. package/dist/__tests__/ingest-sarif.test.js.map +1 -0
  44. package/dist/__tests__/manifest-commands.test.d.ts +2 -0
  45. package/dist/__tests__/manifest-commands.test.d.ts.map +1 -0
  46. package/dist/__tests__/manifest-commands.test.js +67 -0
  47. package/dist/__tests__/manifest-commands.test.js.map +1 -0
  48. package/dist/__tests__/provenance.test.d.ts +2 -0
  49. package/dist/__tests__/provenance.test.d.ts.map +1 -0
  50. package/dist/__tests__/provenance.test.js +48 -0
  51. package/dist/__tests__/provenance.test.js.map +1 -0
  52. package/dist/__tests__/redact.test.d.ts +2 -0
  53. package/dist/__tests__/redact.test.d.ts.map +1 -0
  54. package/dist/__tests__/redact.test.js +37 -0
  55. package/dist/__tests__/redact.test.js.map +1 -0
  56. package/dist/__tests__/run-loop-artifact.test.d.ts +21 -0
  57. package/dist/__tests__/run-loop-artifact.test.d.ts.map +1 -0
  58. package/dist/__tests__/run-loop-artifact.test.js +186 -0
  59. package/dist/__tests__/run-loop-artifact.test.js.map +1 -0
  60. package/dist/__tests__/run-loop-exit.test.d.ts +21 -0
  61. package/dist/__tests__/run-loop-exit.test.d.ts.map +1 -0
  62. package/dist/__tests__/run-loop-exit.test.js +123 -0
  63. package/dist/__tests__/run-loop-exit.test.js.map +1 -0
  64. package/dist/__tests__/run-loop-gate.test.d.ts +10 -0
  65. package/dist/__tests__/run-loop-gate.test.d.ts.map +1 -0
  66. package/dist/__tests__/run-loop-gate.test.js +159 -0
  67. package/dist/__tests__/run-loop-gate.test.js.map +1 -0
  68. package/dist/__tests__/session-payload.test.d.ts +12 -0
  69. package/dist/__tests__/session-payload.test.d.ts.map +1 -0
  70. package/dist/__tests__/session-payload.test.js +131 -0
  71. package/dist/__tests__/session-payload.test.js.map +1 -0
  72. package/dist/__tests__/severity-map.test.d.ts +2 -0
  73. package/dist/__tests__/severity-map.test.d.ts.map +1 -0
  74. package/dist/__tests__/severity-map.test.js +57 -0
  75. package/dist/__tests__/severity-map.test.js.map +1 -0
  76. package/dist/acceptance-harness.d.ts +48 -0
  77. package/dist/acceptance-harness.d.ts.map +1 -0
  78. package/dist/acceptance-harness.js +78 -0
  79. package/dist/acceptance-harness.js.map +1 -0
  80. package/dist/adapter-config.d.ts +58 -0
  81. package/dist/adapter-config.d.ts.map +1 -0
  82. package/dist/adapter-config.js +73 -0
  83. package/dist/adapter-config.js.map +1 -0
  84. package/dist/adapter-manifest.d.ts +57 -0
  85. package/dist/adapter-manifest.d.ts.map +1 -0
  86. package/dist/adapter-manifest.js +68 -0
  87. package/dist/adapter-manifest.js.map +1 -0
  88. package/dist/artifact-path.d.ts +26 -0
  89. package/dist/artifact-path.d.ts.map +1 -0
  90. package/dist/artifact-path.js +22 -0
  91. package/dist/artifact-path.js.map +1 -0
  92. package/dist/binary-resolver.d.ts +51 -0
  93. package/dist/binary-resolver.d.ts.map +1 -0
  94. package/dist/binary-resolver.js +66 -0
  95. package/dist/binary-resolver.js.map +1 -0
  96. package/dist/define-external-tool-adapter.d.ts +25 -0
  97. package/dist/define-external-tool-adapter.d.ts.map +1 -0
  98. package/dist/define-external-tool-adapter.js +149 -0
  99. package/dist/define-external-tool-adapter.js.map +1 -0
  100. package/dist/doctor-command.d.ts +81 -0
  101. package/dist/doctor-command.d.ts.map +1 -0
  102. package/dist/doctor-command.js +160 -0
  103. package/dist/doctor-command.js.map +1 -0
  104. package/dist/exit-model.d.ts +33 -0
  105. package/dist/exit-model.d.ts.map +1 -0
  106. package/dist/exit-model.js +35 -0
  107. package/dist/exit-model.js.map +1 -0
  108. package/dist/fingerprint.d.ts +26 -0
  109. package/dist/fingerprint.d.ts.map +1 -0
  110. package/dist/fingerprint.js +32 -0
  111. package/dist/fingerprint.js.map +1 -0
  112. package/dist/gate-render.d.ts +18 -0
  113. package/dist/gate-render.d.ts.map +1 -0
  114. package/dist/gate-render.js +25 -0
  115. package/dist/gate-render.js.map +1 -0
  116. package/dist/index.d.ts +39 -0
  117. package/dist/index.d.ts.map +1 -0
  118. package/dist/index.js +35 -0
  119. package/dist/index.js.map +1 -0
  120. package/dist/ingest-json.d.ts +32 -0
  121. package/dist/ingest-json.d.ts.map +1 -0
  122. package/dist/ingest-json.js +66 -0
  123. package/dist/ingest-json.js.map +1 -0
  124. package/dist/ingest-sarif.d.ts +113 -0
  125. package/dist/ingest-sarif.d.ts.map +1 -0
  126. package/dist/ingest-sarif.js +158 -0
  127. package/dist/ingest-sarif.js.map +1 -0
  128. package/dist/manifest-commands.d.ts +23 -0
  129. package/dist/manifest-commands.d.ts.map +1 -0
  130. package/dist/manifest-commands.js +47 -0
  131. package/dist/manifest-commands.js.map +1 -0
  132. package/dist/process-exec.d.ts +51 -0
  133. package/dist/process-exec.d.ts.map +1 -0
  134. package/dist/process-exec.js +99 -0
  135. package/dist/process-exec.js.map +1 -0
  136. package/dist/provenance.d.ts +19 -0
  137. package/dist/provenance.d.ts.map +1 -0
  138. package/dist/provenance.js +31 -0
  139. package/dist/provenance.js.map +1 -0
  140. package/dist/redact.d.ts +24 -0
  141. package/dist/redact.d.ts.map +1 -0
  142. package/dist/redact.js +38 -0
  143. package/dist/redact.js.map +1 -0
  144. package/dist/run-context.d.ts +24 -0
  145. package/dist/run-context.d.ts.map +1 -0
  146. package/dist/run-context.js +36 -0
  147. package/dist/run-context.js.map +1 -0
  148. package/dist/run-loop.d.ts +64 -0
  149. package/dist/run-loop.d.ts.map +1 -0
  150. package/dist/run-loop.js +320 -0
  151. package/dist/run-loop.js.map +1 -0
  152. package/dist/scan-emit.d.ts +81 -0
  153. package/dist/scan-emit.d.ts.map +1 -0
  154. package/dist/scan-emit.js +125 -0
  155. package/dist/scan-emit.js.map +1 -0
  156. package/dist/session-payload.d.ts +81 -0
  157. package/dist/session-payload.d.ts.map +1 -0
  158. package/dist/session-payload.js +86 -0
  159. package/dist/session-payload.js.map +1 -0
  160. package/dist/severity-map.d.ts +43 -0
  161. package/dist/severity-map.d.ts.map +1 -0
  162. package/dist/severity-map.js +84 -0
  163. package/dist/severity-map.js.map +1 -0
  164. package/dist/types.d.ts +228 -0
  165. package/dist/types.d.ts.map +1 -0
  166. package/dist/types.js +15 -0
  167. package/dist/types.js.map +1 -0
  168. package/dist/version-command.d.ts +36 -0
  169. package/dist/version-command.d.ts.map +1 -0
  170. package/dist/version-command.js +74 -0
  171. package/dist/version-command.js.map +1 -0
  172. package/package.json +52 -0
@@ -0,0 +1,64 @@
1
+ /**
2
+ * @fileoverview The scan run loop (ADR-0090 §4 / ADR-0091).
3
+ *
4
+ * resolve binary → build run context → probe version → compute artifact path →
5
+ * `execFile` (no shell, timeout + output cap) → interpret exit → read native
6
+ * output → persist via `cli.writeArtifact` (the host seam — NEVER raw fs) →
7
+ * parse (SARIF via shared `ingestSarif`; JSON via the descriptor `parse`) →
8
+ * stamp provenance → `buildSignalEnvelope` + worker-side fingerprint stamping →
9
+ * emit via `cli.emitEnvelope` (`--json`) / `cli.render` (human) → `cli.deliverSignals`
10
+ * (host derives the findings exit from the verdict) → return a `ToolRunCompletion`.
11
+ *
12
+ * For an INSTALLED adapter this body runs WORKER-SIDE; every `cli.*` call here is
13
+ * captured by the worker's recording context and replayed through the host seams
14
+ * (`writeArtifact` via host RPC; `emitEnvelope`/`render`/`deliverSignals`/
15
+ * `setExitCode` via the forwarded-result record). So the substrate never imports
16
+ * `cli` and the host stays the only privileged-effect process.
17
+ *
18
+ * This IO orchestration is excluded from unit coverage; pure helpers are covered
19
+ * directly and adapter worker E2Es exercise the whole loop.
20
+ */
21
+ import type { BinaryResolveDeps } from './binary-resolver.js';
22
+ import type { ProbeVersionInput, ProcessResult, RunProcessInput } from './process-exec.js';
23
+ import type { ScanCompletion } from './scan-emit.js';
24
+ import type { BinarySpec, ExternalCommandSpec } from './types.js';
25
+ import type { FingerprintStrategy, ToolCliContext } from '@opensip-cli/core';
26
+ /** Injectable IO seam (real impls default; tests pass stubs). */
27
+ export interface ScanLoopDeps {
28
+ readonly binaryDeps: BinaryResolveDeps;
29
+ readonly runProcess: (input: RunProcessInput) => Promise<ProcessResult>;
30
+ readonly probeVersion: (input: ProbeVersionInput) => string | undefined;
31
+ readonly readFile: (path: string) => string;
32
+ /** The artifact file's byte size — used to cap the read (OOM guard) before {@link readFile}. */
33
+ readonly fileSize: (path: string) => number;
34
+ /**
35
+ * The environment the operator binary-pin (`OPENSIP_<TOOL>_BIN`) is read from
36
+ * — injected (mirrors the doctor/version probe's `DoctorProbeDeps.env`) so the
37
+ * read flows through a seam, not a raw `process.env` reach, and stays
38
+ * unit-testable. The pin name is per-tool/dynamic, so it cannot be a static
39
+ * EnvRegistry `EnvVarSpec`.
40
+ */
41
+ readonly env: NodeJS.ProcessEnv;
42
+ readonly timeoutMs: number;
43
+ readonly maxBuffer: number;
44
+ }
45
+ export interface ScanLoopInput {
46
+ readonly cli: ToolCliContext;
47
+ readonly tool: string;
48
+ readonly adapterPackage?: string;
49
+ readonly command: ExternalCommandSpec;
50
+ readonly binary: BinarySpec;
51
+ readonly fingerprintStrategy: FingerprintStrategy;
52
+ /** The parsed command flags (`json` / `cwd` / `reportTo` / `apiKey`). */
53
+ readonly opts: Record<string, unknown>;
54
+ }
55
+ /**
56
+ * Run one scanner command end-to-end.
57
+ *
58
+ * Returns the {@link ScanCompletion} the host persists/dispatches, or `undefined`
59
+ * when the invocation is a config error (`--gate-save` + `--gate-compare` together)
60
+ * — the loop has already recorded the failure + exit via `cli.reportFailure`, so
61
+ * there is no envelope/session to return.
62
+ */
63
+ export declare function runScanLoop(input: ScanLoopInput, overrides?: Partial<ScanLoopDeps>): Promise<ScanCompletion | undefined>;
64
+ //# sourceMappingURL=run-loop.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run-loop.d.ts","sourceRoot":"","sources":["../src/run-loop.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAwBH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAE9D,OAAO,KAAK,EAAE,iBAAiB,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAC3F,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AACrD,OAAO,KAAK,EAGV,UAAU,EACV,mBAAmB,EAEpB,MAAM,YAAY,CAAC;AACpB,OAAO,KAAK,EAAE,mBAAmB,EAAU,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAUrF,iEAAiE;AACjE,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,UAAU,EAAE,iBAAiB,CAAC;IACvC,QAAQ,CAAC,UAAU,EAAE,CAAC,KAAK,EAAE,eAAe,KAAK,OAAO,CAAC,aAAa,CAAC,CAAC;IACxE,QAAQ,CAAC,YAAY,EAAE,CAAC,KAAK,EAAE,iBAAiB,KAAK,MAAM,GAAG,SAAS,CAAC;IACxE,QAAQ,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;IAC5C,gGAAgG;IAChG,QAAQ,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;IAC5C;;;;;;OAMG;IACH,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC,UAAU,CAAC;IAChC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B;AAaD,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,GAAG,EAAE,cAAc,CAAC;IAC7B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,OAAO,EAAE,mBAAmB,CAAC;IACtC,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC;IAC5B,QAAQ,CAAC,mBAAmB,EAAE,mBAAmB,CAAC;IAClD,yEAAyE;IACzE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACxC;AA2HD;;;;;;;GAOG;AACH,wBAAsB,WAAW,CAC/B,KAAK,EAAE,aAAa,EACpB,SAAS,CAAC,EAAE,OAAO,CAAC,YAAY,CAAC,GAChC,OAAO,CAAC,cAAc,GAAG,SAAS,CAAC,CAoMrC"}
@@ -0,0 +1,320 @@
1
+ /**
2
+ * @fileoverview The scan run loop (ADR-0090 §4 / ADR-0091).
3
+ *
4
+ * resolve binary → build run context → probe version → compute artifact path →
5
+ * `execFile` (no shell, timeout + output cap) → interpret exit → read native
6
+ * output → persist via `cli.writeArtifact` (the host seam — NEVER raw fs) →
7
+ * parse (SARIF via shared `ingestSarif`; JSON via the descriptor `parse`) →
8
+ * stamp provenance → `buildSignalEnvelope` + worker-side fingerprint stamping →
9
+ * emit via `cli.emitEnvelope` (`--json`) / `cli.render` (human) → `cli.deliverSignals`
10
+ * (host derives the findings exit from the verdict) → return a `ToolRunCompletion`.
11
+ *
12
+ * For an INSTALLED adapter this body runs WORKER-SIDE; every `cli.*` call here is
13
+ * captured by the worker's recording context and replayed through the host seams
14
+ * (`writeArtifact` via host RPC; `emitEnvelope`/`render`/`deliverSignals`/
15
+ * `setExitCode` via the forwarded-result record). So the substrate never imports
16
+ * `cli` and the host stays the only privileged-effect process.
17
+ *
18
+ * This IO orchestration is excluded from unit coverage; pure helpers are covered
19
+ * directly and adapter worker E2Es exercise the whole loop.
20
+ */
21
+ import { readFileSync, statSync } from 'node:fs';
22
+ import { performance } from 'node:perf_hooks';
23
+ import { buildSignalEnvelope, EXIT_CODES } from '@opensip-cli/contracts';
24
+ import { ConfigurationError, isErrorSignal, resolveProjectPaths, resolveVerdictPolicy, TimeoutError, ToolError, } from '@opensip-cli/core';
25
+ import { resolveBinary, defaultBinaryEnvVar } from './binary-resolver.js';
26
+ import { DEFAULT_EXIT_MODEL, interpretExit } from './exit-model.js';
27
+ import { asObject, getString, safeParseJson } from './ingest-json.js';
28
+ import { ingestSarif } from './ingest-sarif.js';
29
+ import { defaultBinaryDeps, probeBinaryVersion, runScannerProcess } from './process-exec.js';
30
+ import { stampProvenanceAll } from './provenance.js';
31
+ import { buildAdapterRunContext } from './run-context.js';
32
+ import { buildScanCompletion, deliverOptions, emitScanCompletion } from './scan-emit.js';
33
+ /** Logger `module` field for every event this loop emits. */
34
+ const MODULE = 'external-tool-adapter';
35
+ /** Default scanner process budget. */
36
+ const DEFAULT_TIMEOUT_MS = 300_000;
37
+ const DEFAULT_MAX_BUFFER = 64 * 1024 * 1024;
38
+ const STDERR_TAIL = 2000;
39
+ const DEFAULT_DEPS = {
40
+ binaryDeps: defaultBinaryDeps,
41
+ runProcess: runScannerProcess,
42
+ probeVersion: probeBinaryVersion,
43
+ readFile: (path) => readFileSync(path, 'utf8'),
44
+ fileSize: (path) => statSync(path).size,
45
+ env: process.env,
46
+ timeoutMs: DEFAULT_TIMEOUT_MS,
47
+ maxBuffer: DEFAULT_MAX_BUFFER,
48
+ };
49
+ const ARTIFACT_EXT = {
50
+ sarif: 'sarif',
51
+ json: 'json',
52
+ stdout: 'out',
53
+ };
54
+ function defaultArtifactName(tool, kind) {
55
+ return `${tool}.${ARTIFACT_EXT[kind]}`;
56
+ }
57
+ /** Read the namespaced operator pin `binaries.<tool>.path` from the resolved config. */
58
+ function configuredBinaryPath(config, tool) {
59
+ return getString(asObject(config.binaries)?.[tool], 'path');
60
+ }
61
+ /** Parse native output into signals (SARIF via shared ingest; JSON/stdout via the descriptor). */
62
+ function parseSignals(command, raw, ctx) {
63
+ if (command.output.kind === 'sarif') {
64
+ const parsed = safeParseJson(raw);
65
+ if (!parsed.ok)
66
+ return [];
67
+ return ingestSarif(parsed.value, { source: ctx.tool });
68
+ }
69
+ if (command.parse === undefined)
70
+ return [];
71
+ const json = command.output.kind === 'json' ? safeParseJson(raw) : undefined;
72
+ const payload = {
73
+ kind: command.output.kind,
74
+ raw,
75
+ ...(json?.ok === true ? { json: json.value } : {}),
76
+ };
77
+ return command.parse(payload, ctx);
78
+ }
79
+ /**
80
+ * Read a FILE-backed scanner report and classify its validity (A11). Size-guarded:
81
+ * an over-cap report (OOM guard) is never read into memory. Returns the reason a
82
+ * report is unusable so the caller can fault with a size-distinguished message and
83
+ * never overwrite the on-disk report with an empty/truncated buffer.
84
+ */
85
+ function readReportArtifact(path, deps) {
86
+ let size = -1;
87
+ try {
88
+ size = deps.fileSize(path);
89
+ }
90
+ catch {
91
+ return { raw: '', artifactValid: false, invalidReason: 'missing' };
92
+ }
93
+ if (size > deps.maxBuffer)
94
+ return { raw: '', artifactValid: false, invalidReason: 'oversize' };
95
+ let raw;
96
+ try {
97
+ raw = deps.readFile(path);
98
+ }
99
+ catch {
100
+ return { raw: '', artifactValid: false, invalidReason: 'missing' };
101
+ }
102
+ if (raw.length === 0)
103
+ return { raw, artifactValid: false, invalidReason: 'empty' };
104
+ if (!safeParseJson(raw).ok)
105
+ return { raw, artifactValid: false, invalidReason: 'unparseable' };
106
+ return { raw, artifactValid: true };
107
+ }
108
+ /**
109
+ * A3: append the descriptor's exclusion of opensip's `.runtime` artifact store to
110
+ * the scanner argv. The substrate supplies the path (run-context-driven) while the
111
+ * descriptor supplies the per-scanner flag shape; a config-file form (gitleaks's
112
+ * allowlist) is written through the host `writeArtifact` seam into the per-run dir,
113
+ * never a raw substrate fs write. No user-facing flag — the command manifest is
114
+ * unchanged.
115
+ */
116
+ async function applyScanExclusion(cli, command, ctx, args) {
117
+ if (command.excludeScan === undefined)
118
+ return;
119
+ const exclusion = command.excludeScan({
120
+ excludePath: resolveProjectPaths(ctx.projectRoot).runtimeDir,
121
+ configPath: (name) => ctx.artifactPath(name),
122
+ });
123
+ if (exclusion.configFile !== undefined) {
124
+ await cli.writeArtifact(exclusion.configFile.path, exclusion.configFile.contents);
125
+ }
126
+ if (exclusion.args !== undefined)
127
+ args.push(...exclusion.args);
128
+ }
129
+ /**
130
+ * A11: raise the typed `ADAPTER.ARTIFACT.INVALID` fault for an unusable file-backed
131
+ * report, distinguishing the oversize-cap case from missing/empty/unparseable.
132
+ */
133
+ function faultInvalidArtifact(tool, command, read, maxBuffer, stderr) {
134
+ const maxMiB = Math.floor(maxBuffer / (1024 * 1024));
135
+ const detail = read.invalidReason === 'oversize'
136
+ ? `report exceeded the ${String(maxMiB)} MiB cap`
137
+ : `report ${read.invalidReason ?? 'invalid'}`;
138
+ throw new ToolError(`${tool} produced no usable ${command.output.kind} report (${detail}).`, 'ADAPTER.ARTIFACT.INVALID', { stderrTail: stderr.slice(-STDERR_TAIL) });
139
+ }
140
+ /**
141
+ * Run one scanner command end-to-end.
142
+ *
143
+ * Returns the {@link ScanCompletion} the host persists/dispatches, or `undefined`
144
+ * when the invocation is a config error (`--gate-save` + `--gate-compare` together)
145
+ * — the loop has already recorded the failure + exit via `cli.reportFailure`, so
146
+ * there is no envelope/session to return.
147
+ */
148
+ export async function runScanLoop(input, overrides) {
149
+ const deps = { ...DEFAULT_DEPS, ...overrides };
150
+ const { cli, tool, command, binary } = input;
151
+ // ADR-0036 gate-ratchet: --gate-save and --gate-compare are mutually exclusive
152
+ // (mirrors fit's runGateMode). Validate BEFORE any IO so a misconfiguration
153
+ // fails fast — no wasted scanner subprocess — and return early (the host replays
154
+ // the recorded reportFailure → exit 2).
155
+ if (input.opts.gateSave === true && input.opts.gateCompare === true) {
156
+ cli.logger.warn({
157
+ evt: 'adapter.gate.config_error',
158
+ module: MODULE,
159
+ tool,
160
+ reason: 'mutually-exclusive flags',
161
+ });
162
+ await cli.reportFailure({
163
+ message: 'Error: --gate-save and --gate-compare are mutually exclusive.',
164
+ exitCode: EXIT_CODES.CONFIGURATION_ERROR,
165
+ jsonRequested: input.opts.json === true,
166
+ });
167
+ return undefined;
168
+ }
169
+ const config = (cli.scope.toolConfig?.[tool] ?? {});
170
+ const envVar = binary.envVar ?? defaultBinaryEnvVar(tool);
171
+ const resolution = resolveBinary({
172
+ command: binary.command,
173
+ configuredPath: configuredBinaryPath(config, tool),
174
+ envPath: deps.env[envVar],
175
+ }, deps.binaryDeps);
176
+ if (!resolution.found) {
177
+ throw new ConfigurationError(`${tool}: ${resolution.reason}. Run 'opensip ${tool} doctor' for setup help.`, { code: 'ADAPTER.BINARY.NOT_FOUND' });
178
+ }
179
+ const version = deps.probeVersion({
180
+ path: resolution.path,
181
+ versionArgs: binary.versionArgs,
182
+ parse: binary.versionParse,
183
+ timeoutMs: deps.timeoutMs,
184
+ });
185
+ const ctx = buildAdapterRunContext({
186
+ cli,
187
+ tool,
188
+ adapterPackage: input.adapterPackage,
189
+ binary: { path: resolution.path, layer: resolution.layer, version },
190
+ config,
191
+ });
192
+ const artifactName = command.output.path ?? defaultArtifactName(tool, command.output.kind);
193
+ const artifactFullPath = ctx.artifactPath(artifactName);
194
+ // A1/A7: create the per-run artifact dir through the host seam before the
195
+ // scanner opens its report path. The substrate never mkdirs `.runtime` itself;
196
+ // host RPC owns the privileged FS effect.
197
+ await cli.ensureArtifactDir(artifactFullPath);
198
+ // A3: keep the scanner off opensip's own persisted reports under `.runtime/`
199
+ // (see applyScanExclusion) — inherited by every adapter, no user flag.
200
+ const args = [...command.args(ctx)];
201
+ await applyScanExclusion(cli, command, ctx, args);
202
+ cli.logger.info({
203
+ evt: 'adapter.binary.resolved',
204
+ module: MODULE,
205
+ tool,
206
+ layer: resolution.layer,
207
+ path: resolution.path,
208
+ version: version ?? null,
209
+ });
210
+ const startedAt = new Date().toISOString();
211
+ const begin = performance.now();
212
+ const proc = await deps.runProcess({
213
+ command: resolution.path,
214
+ args,
215
+ cwd: ctx.projectRoot,
216
+ timeoutMs: deps.timeoutMs,
217
+ maxBuffer: deps.maxBuffer,
218
+ });
219
+ const durationMs = Math.max(0, Math.round(performance.now() - begin));
220
+ if (proc.timedOut) {
221
+ cli.logger.warn({
222
+ evt: 'adapter.scan.faulted',
223
+ module: MODULE,
224
+ tool,
225
+ reason: 'timeout',
226
+ });
227
+ throw new TimeoutError(`${tool} scan timed out after ${String(deps.timeoutMs)}ms`, {
228
+ code: 'ADAPTER.SCAN.TIMEOUT',
229
+ stderrTail: proc.stderr.slice(-STDERR_TAIL),
230
+ });
231
+ }
232
+ // Read the native output: stdout commands use the captured stdout (always valid);
233
+ // file-backed commands read + classify the report (A11) via readReportArtifact.
234
+ const read = command.output.kind === 'stdout'
235
+ ? { raw: proc.stdout, artifactValid: true }
236
+ : readReportArtifact(artifactFullPath, deps);
237
+ const verdict = interpretExit(proc.code, command.exitCodes ?? DEFAULT_EXIT_MODEL, {
238
+ artifactValid: read.artifactValid,
239
+ });
240
+ if (verdict === 'fault') {
241
+ cli.logger.warn({
242
+ evt: 'adapter.scan.faulted',
243
+ module: MODULE,
244
+ tool,
245
+ code: proc.code,
246
+ });
247
+ throw new ToolError(`${tool} scan failed (exit ${String(proc.code)})`, 'ADAPTER.SCAN.FAULT', {
248
+ stderrTail: proc.stderr.slice(-STDERR_TAIL),
249
+ });
250
+ }
251
+ // A11: a FILE-backed scanner MUST produce a readable, parseable report. An invalid
252
+ // artifact is a FAULT even under an `ok`/`findings` verdict — otherwise trivy's
253
+ // `ok:[0]` model would read a broken report as a clean PASS and the `writeArtifact`
254
+ // below would overwrite the real report with the empty buffer. We throw BEFORE
255
+ // writeArtifact, so the scanner's report on disk is never destroyed.
256
+ if (!read.artifactValid) {
257
+ cli.logger.warn({
258
+ evt: 'adapter.scan.faulted',
259
+ module: MODULE,
260
+ tool,
261
+ reason: `artifact-${read.invalidReason ?? 'invalid'}`,
262
+ });
263
+ faultInvalidArtifact(tool, command, read, deps.maxBuffer, proc.stderr);
264
+ }
265
+ const raw = read.raw;
266
+ // Persist the raw artifact through the HOST seam (0600 + retention, ADR-0080/0091).
267
+ await cli.writeArtifact(artifactFullPath, raw);
268
+ cli.logger.info({
269
+ evt: 'adapter.artifact.stored',
270
+ module: MODULE,
271
+ tool,
272
+ path: artifactFullPath,
273
+ bytes: raw.length,
274
+ });
275
+ const parsed = parseSignals(command, raw, ctx);
276
+ const provenance = {
277
+ tool,
278
+ adapterPackage: input.adapterPackage,
279
+ binaryPath: resolution.path,
280
+ binaryVersion: version,
281
+ args,
282
+ configPath: ctx.configPath,
283
+ };
284
+ const signals = stampProvenanceAll(parsed, provenance);
285
+ const unitPassed = !signals.some(isErrorSignal);
286
+ const envelope = buildSignalEnvelope({
287
+ tool,
288
+ runId: ctx.runId,
289
+ createdAt: startedAt,
290
+ units: [{ slug: command.name, passed: unitPassed, violationCount: signals.length, durationMs }],
291
+ signals,
292
+ policy: resolveVerdictPolicy(tool),
293
+ runFaulted: false,
294
+ fingerprintStrategy: input.fingerprintStrategy,
295
+ });
296
+ const deliver = deliverOptions(input.opts, ctx.projectRoot);
297
+ // The session contribution (incl. the dashboard-shaped grouped payload) is shaped
298
+ // in a sibling helper so this orchestration body stays flat (file-length budget).
299
+ const completion = buildScanCompletion({
300
+ tool,
301
+ cwd: deliver.cwd,
302
+ envelope,
303
+ signals,
304
+ binary: { path: resolution.path, layer: resolution.layer, version: version ?? null },
305
+ artifact: artifactFullPath,
306
+ durationMs,
307
+ });
308
+ // Emit + deliver + return. The gate-ratchet branch (ADR-0036) and the normal
309
+ // emit live in a sibling helper so this orchestration body stays flat.
310
+ return emitScanCompletion({
311
+ cli,
312
+ tool,
313
+ opts: input.opts,
314
+ envelope,
315
+ signalCount: signals.length,
316
+ deliver,
317
+ completion,
318
+ });
319
+ }
320
+ //# sourceMappingURL=run-loop.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run-loop.js","sourceRoot":"","sources":["../src/run-loop.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,81 @@
1
+ /**
2
+ * @fileoverview Scan-result emission + the gate-ratchet decision (ADR-0036).
3
+ *
4
+ * Split from {@link ./run-loop} (the IO orchestration: resolve → exec → parse →
5
+ * envelope) so the "what to emit + which gate branch" decision is a small,
6
+ * directly-testable unit and the loop body stays a flat pipeline. Every effect
7
+ * here is a documented `ToolCliContext` seam — for an INSTALLED adapter these run
8
+ * worker-side and replay through the host (FRR for `render`/`emitEnvelope`; host
9
+ * RPC for `saveBaseline`/`compareBaseline`/`deliverSignals`).
10
+ */
11
+ import type { BinaryResolutionLayer } from './types.js';
12
+ import type { SignalEnvelope } from '@opensip-cli/contracts';
13
+ import type { Signal, ToolCliContext } from '@opensip-cli/core';
14
+ /** What the host persists + dispatches after a scan (the run loop's return shape). */
15
+ export interface ScanCompletion {
16
+ readonly envelope: SignalEnvelope;
17
+ readonly session: {
18
+ readonly tool: string;
19
+ readonly cwd: string;
20
+ readonly score: number;
21
+ readonly passed: boolean;
22
+ readonly payload: Record<string, unknown>;
23
+ };
24
+ }
25
+ /** The `deliverSignals` options bag (cwd + optional egress targets). */
26
+ export interface DeliverOpts {
27
+ readonly cwd: string;
28
+ readonly reportTo?: string;
29
+ readonly apiKey?: string;
30
+ }
31
+ /** Build the `deliverSignals` options bag from the parsed flags (cwd + optional egress). */
32
+ export declare function deliverOptions(opts: Record<string, unknown>, fallbackCwd: string): DeliverOpts;
33
+ /** Inputs the run loop hands {@link buildScanCompletion} to shape the session row. */
34
+ export interface ScanCompletionInput {
35
+ readonly tool: string;
36
+ readonly cwd: string;
37
+ readonly envelope: SignalEnvelope;
38
+ readonly signals: readonly Signal[];
39
+ readonly binary: {
40
+ readonly path: string;
41
+ readonly layer: BinaryResolutionLayer;
42
+ readonly version: string | null;
43
+ };
44
+ readonly artifact: string;
45
+ readonly durationMs: number;
46
+ }
47
+ /**
48
+ * Shape the {@link ScanCompletion} the host persists/dispatches. The session
49
+ * payload carries the dashboard-shaped grouped detail (`__version`/`summary`/
50
+ * `checks[]` from {@link buildAdapterSessionPayload}) so the HTML report renders
51
+ * the scan's findings instead of falsely "clean" — plus the operational
52
+ * provenance the row also keeps (binary/artifact/findings/durationMs). Built from
53
+ * the already-redacted signals, so no raw secret reaches the persisted row.
54
+ */
55
+ export declare function buildScanCompletion(input: ScanCompletionInput): ScanCompletion;
56
+ export interface EmitScanCompletionInput {
57
+ readonly cli: ToolCliContext;
58
+ readonly tool: string;
59
+ readonly opts: Record<string, unknown>;
60
+ readonly envelope: SignalEnvelope;
61
+ readonly signalCount: number;
62
+ readonly deliver: DeliverOpts;
63
+ readonly completion: ScanCompletion;
64
+ }
65
+ /**
66
+ * Emit the scan result, deliver its signals, and return the completion. Branches:
67
+ *
68
+ * - `--gate-save` (ADR-0020/0035): record the baseline via the host seam, render
69
+ * `gate-done`, and deliver WITHOUT a runFailed override so the host derives the
70
+ * findings exit from the verdict.
71
+ * - `--gate-compare` (ADR-0035/0036): diff against the saved baseline, render the
72
+ * diff, and pass `degraded && failOnDegraded` as the host runFailed override
73
+ * (the findings verdict does NOT gate here — only NET-NEW findings fail).
74
+ * - normal: emit the envelope (`--json`) or a human summary, deliver without an
75
+ * override.
76
+ *
77
+ * Both gate paths still RETURN the session contribution, so a gate run persists a
78
+ * session alongside a normal scan.
79
+ */
80
+ export declare function emitScanCompletion(input: EmitScanCompletionInput): Promise<ScanCompletion>;
81
+ //# sourceMappingURL=scan-emit.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scan-emit.d.ts","sourceRoot":"","sources":["../src/scan-emit.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAOH,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AACxD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAC7D,OAAO,KAAK,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAKhE,sFAAsF;AACtF,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,QAAQ,EAAE,cAAc,CAAC;IAClC,QAAQ,CAAC,OAAO,EAAE;QAChB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QACtB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;QACrB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;QACvB,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;QACzB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;KAC3C,CAAC;CACH;AAED,wEAAwE;AACxE,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,4FAA4F;AAC5F,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,WAAW,EAAE,MAAM,GAAG,WAAW,CAM9F;AAED,sFAAsF;AACtF,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,QAAQ,EAAE,cAAc,CAAC;IAClC,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IACpC,QAAQ,CAAC,MAAM,EAAE;QACf,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QACtB,QAAQ,CAAC,KAAK,EAAE,qBAAqB,CAAC;QACtC,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;KACjC,CAAC;IACF,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC7B;AAED;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,mBAAmB,GAAG,cAAc,CAiB9E;AAiCD,MAAM,WAAW,uBAAuB;IACtC,QAAQ,CAAC,GAAG,EAAE,cAAc,CAAC;IAC7B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACvC,QAAQ,CAAC,QAAQ,EAAE,cAAc,CAAC;IAClC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAC;IAC9B,QAAQ,CAAC,UAAU,EAAE,cAAc,CAAC;CACrC;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,kBAAkB,CAAC,KAAK,EAAE,uBAAuB,GAAG,OAAO,CAAC,cAAc,CAAC,CAuChG"}
@@ -0,0 +1,125 @@
1
+ /**
2
+ * @fileoverview Scan-result emission + the gate-ratchet decision (ADR-0036).
3
+ *
4
+ * Split from {@link ./run-loop} (the IO orchestration: resolve → exec → parse →
5
+ * envelope) so the "what to emit + which gate branch" decision is a small,
6
+ * directly-testable unit and the loop body stays a flat pipeline. Every effect
7
+ * here is a documented `ToolCliContext` seam — for an INSTALLED adapter these run
8
+ * worker-side and replay through the host (FRR for `render`/`emitEnvelope`; host
9
+ * RPC for `saveBaseline`/`compareBaseline`/`deliverSignals`).
10
+ */
11
+ import { resolveFailOnDegraded } from '@opensip-cli/core';
12
+ import { renderGateCompareLines, renderGateSaveLines } from './gate-render.js';
13
+ import { buildAdapterSessionPayload } from './session-payload.js';
14
+ /** Logger `module` field for every event this module emits. */
15
+ const MODULE = 'external-tool-adapter';
16
+ /** Build the `deliverSignals` options bag from the parsed flags (cwd + optional egress). */
17
+ export function deliverOptions(opts, fallbackCwd) {
18
+ return {
19
+ cwd: typeof opts.cwd === 'string' ? opts.cwd : fallbackCwd,
20
+ ...(typeof opts.reportTo === 'string' ? { reportTo: opts.reportTo } : {}),
21
+ ...(typeof opts.apiKey === 'string' ? { apiKey: opts.apiKey } : {}),
22
+ };
23
+ }
24
+ /**
25
+ * Shape the {@link ScanCompletion} the host persists/dispatches. The session
26
+ * payload carries the dashboard-shaped grouped detail (`__version`/`summary`/
27
+ * `checks[]` from {@link buildAdapterSessionPayload}) so the HTML report renders
28
+ * the scan's findings instead of falsely "clean" — plus the operational
29
+ * provenance the row also keeps (binary/artifact/findings/durationMs). Built from
30
+ * the already-redacted signals, so no raw secret reaches the persisted row.
31
+ */
32
+ export function buildScanCompletion(input) {
33
+ return {
34
+ envelope: input.envelope,
35
+ session: {
36
+ tool: input.tool,
37
+ cwd: input.cwd,
38
+ score: input.envelope.verdict.score,
39
+ passed: input.envelope.verdict.passed,
40
+ payload: {
41
+ ...buildAdapterSessionPayload(input.signals),
42
+ binary: input.binary,
43
+ artifact: input.artifact,
44
+ findings: input.signals.length,
45
+ durationMs: input.durationMs,
46
+ },
47
+ },
48
+ };
49
+ }
50
+ /** The human (non-`--json`) one-line scan summary. */
51
+ function summaryLines(tool, signalCount, score, passed) {
52
+ return [
53
+ `${tool}: ${String(signalCount)} finding(s)`,
54
+ `verdict: ${passed ? 'PASS' : 'FAIL'} (score ${score.toFixed(2)})`,
55
+ ];
56
+ }
57
+ /** One `adapter.scan.completed` log line, with the optional gate-mode annotations. */
58
+ function logCompleted(input) {
59
+ const { cli, tool, signalCount, passed, gate, degraded } = input;
60
+ cli.logger.info({
61
+ evt: 'adapter.scan.completed',
62
+ module: MODULE,
63
+ tool,
64
+ findings: signalCount,
65
+ passed,
66
+ ...(gate === undefined ? {} : { gate }),
67
+ ...(degraded === undefined ? {} : { degraded }),
68
+ });
69
+ }
70
+ /**
71
+ * Emit the scan result, deliver its signals, and return the completion. Branches:
72
+ *
73
+ * - `--gate-save` (ADR-0020/0035): record the baseline via the host seam, render
74
+ * `gate-done`, and deliver WITHOUT a runFailed override so the host derives the
75
+ * findings exit from the verdict.
76
+ * - `--gate-compare` (ADR-0035/0036): diff against the saved baseline, render the
77
+ * diff, and pass `degraded && failOnDegraded` as the host runFailed override
78
+ * (the findings verdict does NOT gate here — only NET-NEW findings fail).
79
+ * - normal: emit the envelope (`--json`) or a human summary, deliver without an
80
+ * override.
81
+ *
82
+ * Both gate paths still RETURN the session contribution, so a gate run persists a
83
+ * session alongside a normal scan.
84
+ */
85
+ export async function emitScanCompletion(input) {
86
+ const { cli, tool, opts, envelope, signalCount, deliver, completion } = input;
87
+ if (opts.gateSave === true) {
88
+ await cli.saveBaseline(tool, envelope);
89
+ await cli.render({ type: 'gate-done', lines: renderGateSaveLines(tool, signalCount) });
90
+ await cli.deliverSignals(envelope, deliver);
91
+ logCompleted({ cli, tool, signalCount, passed: envelope.verdict.passed, gate: 'save' });
92
+ return completion;
93
+ }
94
+ if (opts.gateCompare === true) {
95
+ const result = await cli.compareBaseline(tool, envelope);
96
+ await cli.render({ type: 'gate-done', lines: renderGateCompareLines(tool, result) });
97
+ await cli.deliverSignals(envelope, {
98
+ ...deliver,
99
+ runFailed: result.degraded && resolveFailOnDegraded(tool),
100
+ });
101
+ logCompleted({
102
+ cli,
103
+ tool,
104
+ signalCount,
105
+ passed: envelope.verdict.passed,
106
+ gate: 'compare',
107
+ degraded: result.degraded,
108
+ });
109
+ return completion;
110
+ }
111
+ if (opts.json === true) {
112
+ cli.emitEnvelope(envelope);
113
+ }
114
+ else {
115
+ await cli.render({
116
+ type: 'text-lines',
117
+ title: `${tool} scan`,
118
+ lines: summaryLines(tool, signalCount, envelope.verdict.score, envelope.verdict.passed),
119
+ });
120
+ }
121
+ await cli.deliverSignals(envelope, deliver);
122
+ logCompleted({ cli, tool, signalCount, passed: envelope.verdict.passed });
123
+ return completion;
124
+ }
125
+ //# sourceMappingURL=scan-emit.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scan-emit.js","sourceRoot":"","sources":["../src/scan-emit.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,qBAAqB,EAAE,MAAM,mBAAmB,CAAC;AAE1D,OAAO,EAAE,sBAAsB,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAC/E,OAAO,EAAE,0BAA0B,EAAE,MAAM,sBAAsB,CAAC;AAMlE,+DAA+D;AAC/D,MAAM,MAAM,GAAG,uBAAuB,CAAC;AAqBvC,4FAA4F;AAC5F,MAAM,UAAU,cAAc,CAAC,IAA6B,EAAE,WAAmB;IAC/E,OAAO;QACL,GAAG,EAAE,OAAO,IAAI,CAAC,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,WAAW;QAC1D,GAAG,CAAC,OAAO,IAAI,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACzE,GAAG,CAAC,OAAO,IAAI,CAAC,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KACpE,CAAC;AACJ,CAAC;AAiBD;;;;;;;GAOG;AACH,MAAM,UAAU,mBAAmB,CAAC,KAA0B;IAC5D,OAAO;QACL,QAAQ,EAAE,KAAK,CAAC,QAAQ;QACxB,OAAO,EAAE;YACP,IAAI,EAAE,KAAK,CAAC,IAAI;YAChB,GAAG,EAAE,KAAK,CAAC,GAAG;YACd,KAAK,EAAE,KAAK,CAAC,QAAQ,CAAC,OAAO,CAAC,KAAK;YACnC,MAAM,EAAE,KAAK,CAAC,QAAQ,CAAC,OAAO,CAAC,MAAM;YACrC,OAAO,EAAE;gBACP,GAAG,0BAA0B,CAAC,KAAK,CAAC,OAAO,CAAC;gBAC5C,MAAM,EAAE,KAAK,CAAC,MAAM;gBACpB,QAAQ,EAAE,KAAK,CAAC,QAAQ;gBACxB,QAAQ,EAAE,KAAK,CAAC,OAAO,CAAC,MAAM;gBAC9B,UAAU,EAAE,KAAK,CAAC,UAAU;aAC7B;SACF;KACF,CAAC;AACJ,CAAC;AAED,sDAAsD;AACtD,SAAS,YAAY,CAAC,IAAY,EAAE,WAAmB,EAAE,KAAa,EAAE,MAAe;IACrF,OAAO;QACL,GAAG,IAAI,KAAK,MAAM,CAAC,WAAW,CAAC,aAAa;QAC5C,YAAY,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,WAAW,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG;KACnE,CAAC;AACJ,CAAC;AAWD,sFAAsF;AACtF,SAAS,YAAY,CAAC,KAAwB;IAC5C,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,GAAG,KAAK,CAAC;IACjE,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC;QACd,GAAG,EAAE,wBAAwB;QAC7B,MAAM,EAAE,MAAM;QACd,IAAI;QACJ,QAAQ,EAAE,WAAW;QACrB,MAAM;QACN,GAAG,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC;QACvC,GAAG,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC;KAChD,CAAC,CAAC;AACL,CAAC;AAYD;;;;;;;;;;;;;;GAcG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,KAA8B;IACrE,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,OAAO,EAAE,UAAU,EAAE,GAAG,KAAK,CAAC;IAC9E,IAAI,IAAI,CAAC,QAAQ,KAAK,IAAI,EAAE,CAAC;QAC3B,MAAM,GAAG,CAAC,YAAY,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;QACvC,MAAM,GAAG,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,KAAK,EAAE,mBAAmB,CAAC,IAAI,EAAE,WAAW,CAAC,EAAE,CAAC,CAAC;QACvF,MAAM,GAAG,CAAC,cAAc,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;QAC5C,YAAY,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,EAAE,QAAQ,CAAC,OAAO,CAAC,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC;QACxF,OAAO,UAAU,CAAC;IACpB,CAAC;IACD,IAAI,IAAI,CAAC,WAAW,KAAK,IAAI,EAAE,CAAC;QAC9B,MAAM,MAAM,GAAG,MAAM,GAAG,CAAC,eAAe,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;QACzD,MAAM,GAAG,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,KAAK,EAAE,sBAAsB,CAAC,IAAI,EAAE,MAAM,CAAC,EAAE,CAAC,CAAC;QACrF,MAAM,GAAG,CAAC,cAAc,CAAC,QAAQ,EAAE;YACjC,GAAG,OAAO;YACV,SAAS,EAAE,MAAM,CAAC,QAAQ,IAAI,qBAAqB,CAAC,IAAI,CAAC;SAC1D,CAAC,CAAC;QACH,YAAY,CAAC;YACX,GAAG;YACH,IAAI;YACJ,WAAW;YACX,MAAM,EAAE,QAAQ,CAAC,OAAO,CAAC,MAAM;YAC/B,IAAI,EAAE,SAAS;YACf,QAAQ,EAAE,MAAM,CAAC,QAAQ;SAC1B,CAAC,CAAC;QACH,OAAO,UAAU,CAAC;IACpB,CAAC;IAED,IAAI,IAAI,CAAC,IAAI,KAAK,IAAI,EAAE,CAAC;QACvB,GAAG,CAAC,YAAY,CAAC,QAAQ,CAAC,CAAC;IAC7B,CAAC;SAAM,CAAC;QACN,MAAM,GAAG,CAAC,MAAM,CAAC;YACf,IAAI,EAAE,YAAY;YAClB,KAAK,EAAE,GAAG,IAAI,OAAO;YACrB,KAAK,EAAE,YAAY,CAAC,IAAI,EAAE,WAAW,EAAE,QAAQ,CAAC,OAAO,CAAC,KAAK,EAAE,QAAQ,CAAC,OAAO,CAAC,MAAM,CAAC;SACxF,CAAC,CAAC;IACL,CAAC;IACD,MAAM,GAAG,CAAC,cAAc,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IAC5C,YAAY,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,EAAE,QAAQ,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IAC1E,OAAO,UAAU,CAAC;AACpB,CAAC"}