@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,158 @@
1
+ /**
2
+ * @fileoverview The single, substrate-LOCAL SARIF ingest (ADR-0091).
3
+ *
4
+ * This is the ONLY SARIF read/parse path in the workspace (enforced by the
5
+ * `single-sarif-ingest` dependency-cruiser rule). The OpenSIP SARIF *writer*'s
6
+ * shape types are file-private to `@opensip-cli/output` (a layer-3 peer the
7
+ * substrate may not import) and deliberately minimal — so a foreign-scanner
8
+ * ingest defines its OWN defensive INPUT types here: it must read
9
+ * `result.fingerprints`/`partialFingerprints`/`guid`, `properties`, multiple
10
+ * `runs`/`locations`, `ruleIndex`→`rules` joins, and
11
+ * `properties["security-severity"]`.
12
+ *
13
+ * Severity recovery is the core job: the writer collapses BOTH `critical` and
14
+ * `high` → SARIF `error`, so a level-only inverse is ambiguous. `ingestSarif`
15
+ * reads the CVSS `security-severity` number (off the rule descriptor, falling
16
+ * back to the result) and applies the FIRST/NVD v3 bands BEFORE falling back to
17
+ * `level`. The native level/severity are preserved on `Signal.metadata`.
18
+ *
19
+ * Message stability is the second job: real scanners (Trivy) put a VERBOSE,
20
+ * version-volatile block in `result.message.text` (e.g. a `Package:/Installed
21
+ * Version:/Severity:/Fixed Version:/Link:` listing). Surfacing that as
22
+ * `Signal.message` would also feed it into the `message-hash` fingerprint, so the
23
+ * baseline would churn on every dependency bump — defeating the whole point of a
24
+ * line-shift-tolerant hash. So `ingestSarif` prefers the STABLE rule title
25
+ * (`driver.rules[ruleIndex].shortDescription.text`) for `Signal.message` when
26
+ * present and stashes the verbose `result.message.text` on `metadata.detail`. It
27
+ * falls back to `result.message.text` only when no rule/shortDescription exists.
28
+ */
29
+ import { createSignal } from '@opensip-cli/core';
30
+ import { cvssToSeverity, parseCvss, sarifLevelToSeverity } from './severity-map.js';
31
+ const SECURITY_SEVERITY_KEY = 'security-severity';
32
+ /** Find the rule descriptor for a result by `ruleIndex` first, then by id. */
33
+ function resolveRule(result, rules) {
34
+ if (typeof result.ruleIndex === 'number' &&
35
+ result.ruleIndex >= 0 &&
36
+ result.ruleIndex < rules.length) {
37
+ return rules[result.ruleIndex];
38
+ }
39
+ if (typeof result.ruleId === 'string') {
40
+ return rules.find((rule) => rule.id === result.ruleId);
41
+ }
42
+ return undefined;
43
+ }
44
+ /** Read `properties["security-severity"]` off the rule, then the result. */
45
+ function readSecuritySeverity(result, rule) {
46
+ for (const props of [rule?.properties, result.properties]) {
47
+ const raw = props?.[SECURITY_SEVERITY_KEY];
48
+ const cvss = parseCvss(raw);
49
+ if (cvss !== undefined)
50
+ return { raw, cvss };
51
+ }
52
+ return undefined;
53
+ }
54
+ /**
55
+ * Recover the four-bucket OpenSIP severity for one SARIF result: prefer the CVSS
56
+ * `security-severity` number (bands), else fall back to `level` (then the rule's
57
+ * `defaultConfiguration.level`). The native `securitySeverity`/`level` are
58
+ * returned so the caller can preserve them on metadata.
59
+ */
60
+ function recoverSeverity(result, rule) {
61
+ const security = readSecuritySeverity(result, rule);
62
+ if (security !== undefined) {
63
+ return {
64
+ severity: cvssToSeverity(security.cvss),
65
+ securitySeverity: security.raw,
66
+ nativeLevel: result.level,
67
+ };
68
+ }
69
+ const level = result.level ?? rule?.defaultConfiguration?.level;
70
+ return { severity: sarifLevelToSeverity(level), nativeLevel: level };
71
+ }
72
+ /** Collapse a result's locations to the primary site + a count of extras. */
73
+ function primaryLocation(result) {
74
+ const locations = result.locations ?? [];
75
+ const first = locations[0]?.physicalLocation;
76
+ return {
77
+ file: first?.artifactLocation?.uri,
78
+ line: first?.region?.startLine,
79
+ column: first?.region?.startColumn,
80
+ extra: locations.length > 1 ? locations.length - 1 : 0,
81
+ };
82
+ }
83
+ /**
84
+ * Resolve `Signal.message` (the fingerprint basis) for one result, preferring the
85
+ * STABLE rule title over the version-volatile `result.message.text`. When a title
86
+ * is used and a distinct verbose result message exists, the verbose block is
87
+ * returned as `detail` to stash on metadata (so nothing is lost).
88
+ */
89
+ function resolveMessage(result, rule, ruleId) {
90
+ const title = rule?.shortDescription?.text;
91
+ const verbose = result.message?.text;
92
+ if (title !== undefined && title.length > 0) {
93
+ return {
94
+ message: title,
95
+ ...(verbose !== undefined && verbose !== title ? { detail: verbose } : {}),
96
+ };
97
+ }
98
+ return { message: verbose ?? rule?.fullDescription?.text ?? ruleId };
99
+ }
100
+ /** Build the opaque metadata bag for one result (native severity/level/fingerprint/help/detail). */
101
+ function buildMetadata(result, rule, severity, extras) {
102
+ const nativeFingerprint = result.guid ?? result.fingerprints ?? result.partialFingerprints;
103
+ const helpUri = rule?.helpUri ?? result.helpUri;
104
+ return {
105
+ nativeLevel: severity.nativeLevel ?? null,
106
+ nativeSeverity: severity.securitySeverity ?? severity.nativeLevel ?? null,
107
+ ...(severity.securitySeverity === undefined
108
+ ? {}
109
+ : { securitySeverity: severity.securitySeverity }),
110
+ ...(nativeFingerprint === undefined ? {} : { nativeFingerprint }),
111
+ ...(extras.extraLocations > 0 ? { additionalLocations: extras.extraLocations } : {}),
112
+ ...(extras.detail === undefined ? {} : { detail: extras.detail }),
113
+ ...(helpUri === undefined ? {} : { helpUri }),
114
+ };
115
+ }
116
+ /** Normalize one SARIF result to a Signal, joined against its run's rules. */
117
+ function resultToSignal(result, rules, source, category) {
118
+ const rule = resolveRule(result, rules);
119
+ const ruleId = result.ruleId ?? rule?.id ?? 'unknown';
120
+ const severity = recoverSeverity(result, rule);
121
+ const { message, detail } = resolveMessage(result, rule, ruleId);
122
+ const location = primaryLocation(result);
123
+ return createSignal({
124
+ source,
125
+ severity: severity.severity,
126
+ category,
127
+ ruleId,
128
+ message,
129
+ code: {
130
+ ...(location.file === undefined ? {} : { file: location.file }),
131
+ ...(location.line === undefined ? {} : { line: location.line }),
132
+ ...(location.column === undefined ? {} : { column: location.column }),
133
+ },
134
+ metadata: buildMetadata(result, rule, severity, { extraLocations: location.extra, detail }),
135
+ });
136
+ }
137
+ /**
138
+ * Ingest a SARIF 2.1.0 log into normalized {@link Signal}s. Defensive over
139
+ * foreign output: tolerates multiple runs, missing rules/locations, and
140
+ * `ruleIndex` joins. Severity is recovered from CVSS `security-severity` before
141
+ * `level` (ADR-0091); native fingerprints/level/severity are preserved on
142
+ * `metadata`. `Signal.fingerprint` is left unstamped — the host ratchet's
143
+ * `message-hash` strategy stamps it worker-side at envelope construction.
144
+ */
145
+ export function ingestSarif(sarif, options) {
146
+ const category = options?.category ?? 'security';
147
+ const signals = [];
148
+ for (const run of sarif.runs ?? []) {
149
+ const driver = run.tool?.driver;
150
+ const rules = driver?.rules ?? [];
151
+ const source = options?.source ?? driver?.name?.toLowerCase() ?? 'sarif';
152
+ for (const result of run.results ?? []) {
153
+ signals.push(resultToSignal(result, rules, source, category));
154
+ }
155
+ }
156
+ return signals;
157
+ }
158
+ //# sourceMappingURL=ingest-sarif.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ingest-sarif.js","sourceRoot":"","sources":["../src/ingest-sarif.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,23 @@
1
+ /**
2
+ * @fileoverview Manifest command-shell derivation (ADR-0090, Phase-0 decision 7).
3
+ *
4
+ * An installed adapter mounts from its STATIC `package.json#opensipTools.commands`,
5
+ * not its runtime — and `assertCommandNamesMatch` THROWS if the static shells
6
+ * drift from the runtime `commandSpecs` (at install + worker import). So the
7
+ * shells must be GENERATED from the runtime, never hand-authored.
8
+ *
9
+ * `deriveAdapterManifestCommands(tool)` produces exactly that serializable data
10
+ * (the scan + doctor + version shells). A Phase-3 generator extension
11
+ * (`build-tool-command-manifests.mjs`) writes it into each adapter package.json
12
+ * and `--check`s parity; this helper is the single source the generator consumes.
13
+ */
14
+ import type { ManifestCommandShell } from './types.js';
15
+ import type { Tool } from '@opensip-cli/core';
16
+ /**
17
+ * Derive the serializable `opensipTools.commands` shells from a built adapter
18
+ * {@link Tool}'s `commandSpecs` — name, description, aliases, common flags,
19
+ * options (e.g. the gate flags, minus `parse`), scope, output mode, the nesting
20
+ * `parent`, and (for `raw-stream`) the `rawStreamReason`. Pure.
21
+ */
22
+ export declare function deriveAdapterManifestCommands(tool: Tool): readonly ManifestCommandShell[];
23
+ //# sourceMappingURL=manifest-commands.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"manifest-commands.d.ts","sourceRoot":"","sources":["../src/manifest-commands.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAIH,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AACvD,OAAO,KAAK,EAAwC,IAAI,EAAE,MAAM,mBAAmB,CAAC;AAkBpF;;;;;GAKG;AACH,wBAAgB,6BAA6B,CAAC,IAAI,EAAE,IAAI,GAAG,SAAS,oBAAoB,EAAE,CAYzF"}
@@ -0,0 +1,47 @@
1
+ /**
2
+ * @fileoverview Manifest command-shell derivation (ADR-0090, Phase-0 decision 7).
3
+ *
4
+ * An installed adapter mounts from its STATIC `package.json#opensipTools.commands`,
5
+ * not its runtime — and `assertCommandNamesMatch` THROWS if the static shells
6
+ * drift from the runtime `commandSpecs` (at install + worker import). So the
7
+ * shells must be GENERATED from the runtime, never hand-authored.
8
+ *
9
+ * `deriveAdapterManifestCommands(tool)` produces exactly that serializable data
10
+ * (the scan + doctor + version shells). A Phase-3 generator extension
11
+ * (`build-tool-command-manifests.mjs`) writes it into each adapter package.json
12
+ * and `--check`s parity; this helper is the single source the generator consumes.
13
+ */
14
+ import { isAbsolute } from 'node:path';
15
+ /**
16
+ * `OptionSpec` → `ManifestOptionDescriptor`: every field except the
17
+ * non-serializable `parse` closure, and minus a machine-specific absolute-path
18
+ * string `default` (resolved lazily at runtime, so baking it would make the
19
+ * manifest non-deterministic). Byte-identical to the shared generator's
20
+ * `deriveOptionDescriptor` (`scripts/build-tool-command-manifests.mjs`), so the
21
+ * two derivation paths agree (the `tool.test.ts` parity guard pins this).
22
+ */
23
+ function toManifestOption(option) {
24
+ const dropDefault = typeof option.default === 'string' && isAbsolute(option.default);
25
+ const entries = Object.entries(option).filter(([key]) => key !== 'parse' && !(dropDefault && key === 'default'));
26
+ return Object.fromEntries(entries);
27
+ }
28
+ /**
29
+ * Derive the serializable `opensipTools.commands` shells from a built adapter
30
+ * {@link Tool}'s `commandSpecs` — name, description, aliases, common flags,
31
+ * options (e.g. the gate flags, minus `parse`), scope, output mode, the nesting
32
+ * `parent`, and (for `raw-stream`) the `rawStreamReason`. Pure.
33
+ */
34
+ export function deriveAdapterManifestCommands(tool) {
35
+ return (tool.commandSpecs ?? []).map((spec) => ({
36
+ name: spec.name,
37
+ description: spec.description,
38
+ aliases: [...(spec.aliases ?? [])],
39
+ commonFlags: [...spec.commonFlags],
40
+ ...(spec.options === undefined ? {} : { options: spec.options.map(toManifestOption) }),
41
+ scope: spec.scope,
42
+ output: spec.output,
43
+ ...(spec.parent === undefined ? {} : { parent: spec.parent }),
44
+ ...(spec.rawStreamReason === undefined ? {} : { rawStreamReason: spec.rawStreamReason }),
45
+ }));
46
+ }
47
+ //# sourceMappingURL=manifest-commands.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"manifest-commands.js","sourceRoot":"","sources":["../src/manifest-commands.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAKvC;;;;;;;GAOG;AACH,SAAS,gBAAgB,CAAC,MAAkB;IAC1C,MAAM,WAAW,GAAG,OAAO,MAAM,CAAC,OAAO,KAAK,QAAQ,IAAI,UAAU,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IACrF,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,MAAM,CAC3C,CAAC,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC,GAAG,KAAK,OAAO,IAAI,CAAC,CAAC,WAAW,IAAI,GAAG,KAAK,SAAS,CAAC,CAClE,CAAC;IACF,OAAO,MAAM,CAAC,WAAW,CAAC,OAAO,CAA6B,CAAC;AACjE,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,6BAA6B,CAAC,IAAU;IACtD,OAAO,CAAC,IAAI,CAAC,YAAY,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;QAC9C,IAAI,EAAE,IAAI,CAAC,IAAI;QACf,WAAW,EAAE,IAAI,CAAC,WAAW;QAC7B,OAAO,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC;QAClC,WAAW,EAAE,CAAC,GAAG,IAAI,CAAC,WAAW,CAAC;QAClC,GAAG,CAAC,IAAI,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,EAAE,CAAC;QACtF,KAAK,EAAE,IAAI,CAAC,KAAK;QACjB,MAAM,EAAE,IAAI,CAAC,MAAM;QACnB,GAAG,CAAC,IAAI,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC;QAC7D,GAAG,CAAC,IAAI,CAAC,eAAe,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,eAAe,EAAE,IAAI,CAAC,eAAe,EAAE,CAAC;KACzF,CAAC,CAAC,CAAC;AACN,CAAC"}
@@ -0,0 +1,51 @@
1
+ /**
2
+ * @fileoverview The substrate's SINGLE subprocess/IO boundary (ADR-0090 §11).
3
+ *
4
+ * `execFile` / `execFileSync` ONLY — NEVER `exec` with a shell string. Args are
5
+ * descriptor-controlled arrays, so a scanner argument can never be interpreted as
6
+ * a shell metacharacter. This module wraps the three IO primitives the substrate
7
+ * needs — PATH lookup (`which`/`where`), the scanner process runner (timeout +
8
+ * output cap), and the version probe — plus the real {@link BinaryResolveDeps}.
9
+ *
10
+ * It is the one file excluded from unit coverage: it runs a real external binary
11
+ * and is exercised end-to-end by each adapter's worker E2E (ADR-0090 D6 Tier 2).
12
+ * Every decision it feeds (resolution, exit modeling, ingest) is pure and covered
13
+ * directly.
14
+ */
15
+ import type { BinaryResolveDeps } from './binary-resolver.js';
16
+ /** The captured outcome of a scanner process run. */
17
+ export interface ProcessResult {
18
+ /** The exit code (`-1` when the process was killed by the timeout). */
19
+ readonly code: number;
20
+ readonly stdout: string;
21
+ readonly stderr: string;
22
+ readonly timedOut: boolean;
23
+ }
24
+ export interface RunProcessInput {
25
+ readonly command: string;
26
+ readonly args: readonly string[];
27
+ readonly cwd: string;
28
+ readonly timeoutMs: number;
29
+ /** Max captured stdout+stderr bytes before the run is aborted. */
30
+ readonly maxBuffer: number;
31
+ }
32
+ /**
33
+ * Run a scanner binary with `execFile` (no shell), capturing stdout/stderr and
34
+ * the exit code EVEN on a nonzero exit (a scanner signalling findings via exit
35
+ * code is not an error here — {@link interpretExit} classifies it). Rejects only
36
+ * on a spawn failure (e.g. `ENOENT`); a timeout resolves with `timedOut: true`.
37
+ */
38
+ export declare function runScannerProcess(input: RunProcessInput): Promise<ProcessResult>;
39
+ export interface ProbeVersionInput {
40
+ readonly path: string;
41
+ readonly versionArgs: readonly string[];
42
+ readonly parse?: (stdout: string) => string;
43
+ readonly timeoutMs: number;
44
+ }
45
+ /** Probe a binary's version (`execFileSync`, no shell). Returns `undefined` on any failure. */
46
+ export declare function probeBinaryVersion(input: ProbeVersionInput): string | undefined;
47
+ /** PATH lookup via `which` (POSIX) / `where` (Windows), no shell. `undefined` if absent. */
48
+ export declare function whichBinary(command: string, platform: NodeJS.Platform): string | undefined;
49
+ /** The real binary-resolution IO deps (existence check + PATH lookup). */
50
+ export declare const defaultBinaryDeps: BinaryResolveDeps;
51
+ //# sourceMappingURL=process-exec.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"process-exec.d.ts","sourceRoot":"","sources":["../src/process-exec.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAKH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAE9D,qDAAqD;AACrD,MAAM,WAAW,aAAa;IAC5B,uEAAuE;IACvE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;CAC5B;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,CAAC;IACjC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,kEAAkE;IAClE,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,eAAe,GAAG,OAAO,CAAC,aAAa,CAAC,CA8ChF;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,WAAW,EAAE,SAAS,MAAM,EAAE,CAAC;IACxC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,MAAM,CAAC;IAC5C,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B;AAED,+FAA+F;AAC/F,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,iBAAiB,GAAG,MAAM,GAAG,SAAS,CAe/E;AAED,4FAA4F;AAC5F,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,CAAC,QAAQ,GAAG,MAAM,GAAG,SAAS,CAc1F;AAED,0EAA0E;AAC1E,eAAO,MAAM,iBAAiB,EAAE,iBAG/B,CAAC"}
@@ -0,0 +1,99 @@
1
+ /**
2
+ * @fileoverview The substrate's SINGLE subprocess/IO boundary (ADR-0090 §11).
3
+ *
4
+ * `execFile` / `execFileSync` ONLY — NEVER `exec` with a shell string. Args are
5
+ * descriptor-controlled arrays, so a scanner argument can never be interpreted as
6
+ * a shell metacharacter. This module wraps the three IO primitives the substrate
7
+ * needs — PATH lookup (`which`/`where`), the scanner process runner (timeout +
8
+ * output cap), and the version probe — plus the real {@link BinaryResolveDeps}.
9
+ *
10
+ * It is the one file excluded from unit coverage: it runs a real external binary
11
+ * and is exercised end-to-end by each adapter's worker E2E (ADR-0090 D6 Tier 2).
12
+ * Every decision it feeds (resolution, exit modeling, ingest) is pure and covered
13
+ * directly.
14
+ */
15
+ import { execFile, execFileSync } from 'node:child_process';
16
+ import { existsSync } from 'node:fs';
17
+ /**
18
+ * Run a scanner binary with `execFile` (no shell), capturing stdout/stderr and
19
+ * the exit code EVEN on a nonzero exit (a scanner signalling findings via exit
20
+ * code is not an error here — {@link interpretExit} classifies it). Rejects only
21
+ * on a spawn failure (e.g. `ENOENT`); a timeout resolves with `timedOut: true`.
22
+ */
23
+ export function runScannerProcess(input) {
24
+ return new Promise((resolve, reject) => {
25
+ execFile(input.command, [...input.args], {
26
+ cwd: input.cwd,
27
+ timeout: input.timeoutMs,
28
+ maxBuffer: input.maxBuffer,
29
+ encoding: 'utf8',
30
+ windowsHide: true,
31
+ }, (error, stdout, stderr) => {
32
+ const out = typeof stdout === 'string' ? stdout : String(stdout ?? '');
33
+ const err = typeof stderr === 'string' ? stderr : String(stderr ?? '');
34
+ if (error === null) {
35
+ resolve({ code: 0, stdout: out, stderr: err, timedOut: false });
36
+ return;
37
+ }
38
+ const failure = error;
39
+ if (failure.killed === true || failure.signal === 'SIGTERM') {
40
+ resolve({ code: -1, stdout: out, stderr: err, timedOut: true });
41
+ return;
42
+ }
43
+ // A string `code` (`ENOENT`/`EACCES`) is a spawn failure, not an exit code.
44
+ if (typeof failure.code === 'string') {
45
+ reject(error instanceof Error
46
+ ? error
47
+ : new Error(`scanner '${input.command}' failed to spawn (${failure.code})`));
48
+ return;
49
+ }
50
+ resolve({
51
+ code: typeof failure.code === 'number' ? failure.code : 1,
52
+ stdout: out,
53
+ stderr: err,
54
+ timedOut: false,
55
+ });
56
+ });
57
+ });
58
+ }
59
+ /** Probe a binary's version (`execFileSync`, no shell). Returns `undefined` on any failure. */
60
+ export function probeBinaryVersion(input) {
61
+ try {
62
+ const stdout = execFileSync(input.path, [...input.versionArgs], {
63
+ encoding: 'utf8',
64
+ timeout: input.timeoutMs,
65
+ windowsHide: true,
66
+ });
67
+ const text = typeof stdout === 'string' ? stdout : String(stdout);
68
+ return (input.parse ?? ((s) => s.trim()))(text);
69
+ }
70
+ catch {
71
+ // intentionally silent: a version probe failure (binary missing, slow, or
72
+ // a non-version output) is a normal "version unknown" — surfaced as
73
+ // undefined, which doctor renders. No logger is available in this pure seam.
74
+ return undefined;
75
+ }
76
+ }
77
+ /** PATH lookup via `which` (POSIX) / `where` (Windows), no shell. `undefined` if absent. */
78
+ export function whichBinary(command, platform) {
79
+ const finder = platform === 'win32' ? 'where' : 'which';
80
+ try {
81
+ const out = execFileSync(finder, [command], { encoding: 'utf8', windowsHide: true });
82
+ return String(out)
83
+ .split(/\r?\n/)
84
+ .map((line) => line.trim())
85
+ .find((line) => line.length > 0);
86
+ }
87
+ catch {
88
+ // intentionally silent: a `which`/`where` miss means "not on PATH" — a normal
89
+ // resolution outcome surfaced as undefined (the resolver then reports a
90
+ // not-found with an install hint). No logger in this pure IO seam.
91
+ return undefined;
92
+ }
93
+ }
94
+ /** The real binary-resolution IO deps (existence check + PATH lookup). */
95
+ export const defaultBinaryDeps = {
96
+ existsSync: (path) => existsSync(path),
97
+ which: (command, platform) => whichBinary(command, platform),
98
+ };
99
+ //# sourceMappingURL=process-exec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"process-exec.js","sourceRoot":"","sources":["../src/process-exec.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * @fileoverview Provenance stamping (ADR-0090 §8, ADR-0092 review bar item 4).
3
+ *
4
+ * Every normalized signal carries, on `metadata.provenance`, the facts a
5
+ * reviewer/operator needs to trust the finding: the tool, the adapter package,
6
+ * the resolved binary version + path, the exact args, and the config file path.
7
+ * Pure — returns a new `Signal`; the matched secret is NEVER part of provenance.
8
+ */
9
+ import type { AdapterProvenance } from './types.js';
10
+ import type { Signal } from '@opensip-cli/core';
11
+ /**
12
+ * Return a copy of `signal` with {@link AdapterProvenance} merged under
13
+ * `metadata.provenance`. Undefined provenance fields are dropped so the bag stays
14
+ * compact and deterministic.
15
+ */
16
+ export declare function stampProvenance(signal: Signal, provenance: AdapterProvenance): Signal;
17
+ /** Stamp provenance across a batch of signals. */
18
+ export declare function stampProvenanceAll(signals: readonly Signal[], provenance: AdapterProvenance): readonly Signal[];
19
+ //# sourceMappingURL=provenance.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provenance.d.ts","sourceRoot":"","sources":["../src/provenance.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AACpD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAEhD;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,iBAAiB,GAAG,MAAM,CAYrF;AAED,kDAAkD;AAClD,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,SAAS,MAAM,EAAE,EAC1B,UAAU,EAAE,iBAAiB,GAC5B,SAAS,MAAM,EAAE,CAEnB"}
@@ -0,0 +1,31 @@
1
+ /**
2
+ * @fileoverview Provenance stamping (ADR-0090 §8, ADR-0092 review bar item 4).
3
+ *
4
+ * Every normalized signal carries, on `metadata.provenance`, the facts a
5
+ * reviewer/operator needs to trust the finding: the tool, the adapter package,
6
+ * the resolved binary version + path, the exact args, and the config file path.
7
+ * Pure — returns a new `Signal`; the matched secret is NEVER part of provenance.
8
+ */
9
+ /**
10
+ * Return a copy of `signal` with {@link AdapterProvenance} merged under
11
+ * `metadata.provenance`. Undefined provenance fields are dropped so the bag stays
12
+ * compact and deterministic.
13
+ */
14
+ export function stampProvenance(signal, provenance) {
15
+ const bag = {
16
+ tool: provenance.tool,
17
+ binaryPath: provenance.binaryPath,
18
+ args: [...provenance.args],
19
+ ...(provenance.adapterPackage === undefined
20
+ ? {}
21
+ : { adapterPackage: provenance.adapterPackage }),
22
+ ...(provenance.binaryVersion === undefined ? {} : { binaryVersion: provenance.binaryVersion }),
23
+ ...(provenance.configPath === undefined ? {} : { configPath: provenance.configPath }),
24
+ };
25
+ return { ...signal, metadata: { ...signal.metadata, provenance: bag } };
26
+ }
27
+ /** Stamp provenance across a batch of signals. */
28
+ export function stampProvenanceAll(signals, provenance) {
29
+ return signals.map((signal) => stampProvenance(signal, provenance));
30
+ }
31
+ //# sourceMappingURL=provenance.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provenance.js","sourceRoot":"","sources":["../src/provenance.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAKH;;;;GAIG;AACH,MAAM,UAAU,eAAe,CAAC,MAAc,EAAE,UAA6B;IAC3E,MAAM,GAAG,GAA4B;QACnC,IAAI,EAAE,UAAU,CAAC,IAAI;QACrB,UAAU,EAAE,UAAU,CAAC,UAAU;QACjC,IAAI,EAAE,CAAC,GAAG,UAAU,CAAC,IAAI,CAAC;QAC1B,GAAG,CAAC,UAAU,CAAC,cAAc,KAAK,SAAS;YACzC,CAAC,CAAC,EAAE;YACJ,CAAC,CAAC,EAAE,cAAc,EAAE,UAAU,CAAC,cAAc,EAAE,CAAC;QAClD,GAAG,CAAC,UAAU,CAAC,aAAa,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,aAAa,EAAE,UAAU,CAAC,aAAa,EAAE,CAAC;QAC9F,GAAG,CAAC,UAAU,CAAC,UAAU,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,UAAU,CAAC,UAAU,EAAE,CAAC;KACtF,CAAC;IACF,OAAO,EAAE,GAAG,MAAM,EAAE,QAAQ,EAAE,EAAE,GAAG,MAAM,CAAC,QAAQ,EAAE,UAAU,EAAE,GAAG,EAAE,EAAE,CAAC;AAC1E,CAAC;AAED,kDAAkD;AAClD,MAAM,UAAU,kBAAkB,CAChC,OAA0B,EAC1B,UAA6B;IAE7B,OAAO,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,eAAe,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC,CAAC;AACtE,CAAC"}
@@ -0,0 +1,24 @@
1
+ /**
2
+ * @fileoverview Secret redaction for secret-scanner parsers (ADR-0091 / ADR-0092).
3
+ *
4
+ * A secret scanner (gitleaks) captures the live credential (`Secret`) and the
5
+ * surrounding match region (`Match`). Neither may EVER reach `Signal.message`,
6
+ * `Signal.metadata` in raw form, or any egress payload. A parser stores only a
7
+ * {@link redactSecret} PREVIEW (or {@link secretHash}) so a finding stays
8
+ * identifiable without leaking the value.
9
+ *
10
+ * Pure functions; `redactSecret` provably never returns the raw string.
11
+ */
12
+ /**
13
+ * Mask a secret to a short, non-reversible PREVIEW: the first 4 characters plus
14
+ * an ellipsis (`'AKIA…'`). A value of length `<= 4` collapses to just `'…'`, so
15
+ * the full raw secret is NEVER returned. Empty/undefined → `''`.
16
+ */
17
+ export declare function redactSecret(raw: string | undefined | null): string;
18
+ /**
19
+ * A stable, non-reversible identity for a secret: the first 12 hex chars of its
20
+ * SHA-256. Useful when two findings must be told apart without storing any part
21
+ * of the value. Empty/undefined → `''`.
22
+ */
23
+ export declare function secretHash(raw: string | undefined | null): string;
24
+ //# sourceMappingURL=redact.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"redact.d.ts","sourceRoot":"","sources":["../src/redact.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAIH;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,GAAG,MAAM,CAMnE;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,GAAG,MAAM,CAGjE"}
package/dist/redact.js ADDED
@@ -0,0 +1,38 @@
1
+ /**
2
+ * @fileoverview Secret redaction for secret-scanner parsers (ADR-0091 / ADR-0092).
3
+ *
4
+ * A secret scanner (gitleaks) captures the live credential (`Secret`) and the
5
+ * surrounding match region (`Match`). Neither may EVER reach `Signal.message`,
6
+ * `Signal.metadata` in raw form, or any egress payload. A parser stores only a
7
+ * {@link redactSecret} PREVIEW (or {@link secretHash}) so a finding stays
8
+ * identifiable without leaking the value.
9
+ *
10
+ * Pure functions; `redactSecret` provably never returns the raw string.
11
+ */
12
+ import { createHash } from 'node:crypto';
13
+ /**
14
+ * Mask a secret to a short, non-reversible PREVIEW: the first 4 characters plus
15
+ * an ellipsis (`'AKIA…'`). A value of length `<= 4` collapses to just `'…'`, so
16
+ * the full raw secret is NEVER returned. Empty/undefined → `''`.
17
+ */
18
+ export function redactSecret(raw) {
19
+ if (raw === undefined || raw === null)
20
+ return '';
21
+ const s = String(raw);
22
+ if (s.length === 0)
23
+ return '';
24
+ if (s.length <= 4)
25
+ return '…';
26
+ return `${s.slice(0, 4)}…`;
27
+ }
28
+ /**
29
+ * A stable, non-reversible identity for a secret: the first 12 hex chars of its
30
+ * SHA-256. Useful when two findings must be told apart without storing any part
31
+ * of the value. Empty/undefined → `''`.
32
+ */
33
+ export function secretHash(raw) {
34
+ if (raw === undefined || raw === null || raw.length === 0)
35
+ return '';
36
+ return createHash('sha256').update(String(raw)).digest('hex').slice(0, 12);
37
+ }
38
+ //# sourceMappingURL=redact.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"redact.js","sourceRoot":"","sources":["../src/redact.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC;;;;GAIG;AACH,MAAM,UAAU,YAAY,CAAC,GAA8B;IACzD,IAAI,GAAG,KAAK,SAAS,IAAI,GAAG,KAAK,IAAI;QAAE,OAAO,EAAE,CAAC;IACjD,MAAM,CAAC,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;IACtB,IAAI,CAAC,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IAC9B,IAAI,CAAC,CAAC,MAAM,IAAI,CAAC;QAAE,OAAO,GAAG,CAAC;IAC9B,OAAO,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,CAAC;AAC7B,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,UAAU,CAAC,GAA8B;IACvD,IAAI,GAAG,KAAK,SAAS,IAAI,GAAG,KAAK,IAAI,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IACrE,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;AAC7E,CAAC"}
@@ -0,0 +1,24 @@
1
+ /**
2
+ * @fileoverview Build the {@link AdapterRunContext} from a `ToolCliContext`
3
+ * (ADR-0090, Phase-0 decision 8).
4
+ *
5
+ * Layer-legal: paths come from core `resolveProjectPaths`, NOT a `cli` import.
6
+ * `scan` REQUIRES a project — a config-less/project-agnostic run has no targeting
7
+ * root to scan, so a missing `projectContext` is a `ConfigurationError` (exit 2),
8
+ * mirroring MCP's datastore-unavailable handling (Phase-0 decision 2).
9
+ */
10
+ import type { AdapterRunContext, ResolvedBinary } from './types.js';
11
+ import type { ToolCliContext } from '@opensip-cli/core';
12
+ export interface BuildRunContextInput {
13
+ readonly cli: ToolCliContext;
14
+ readonly tool: string;
15
+ readonly adapterPackage?: string;
16
+ readonly binary: ResolvedBinary;
17
+ readonly config: Readonly<Record<string, unknown>>;
18
+ }
19
+ /**
20
+ * Construct the per-run {@link AdapterRunContext}. Throws `ConfigurationError`
21
+ * when no project scope is present (scan needs a targeting root).
22
+ */
23
+ export declare function buildAdapterRunContext(input: BuildRunContextInput): AdapterRunContext;
24
+ //# sourceMappingURL=run-context.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run-context.d.ts","sourceRoot":"","sources":["../src/run-context.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAMH,OAAO,KAAK,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AACpE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAExD,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,GAAG,EAAE,cAAc,CAAC;IAC7B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,MAAM,EAAE,cAAc,CAAC;IAChC,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;CACpD;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,oBAAoB,GAAG,iBAAiB,CA2BrF"}
@@ -0,0 +1,36 @@
1
+ /**
2
+ * @fileoverview Build the {@link AdapterRunContext} from a `ToolCliContext`
3
+ * (ADR-0090, Phase-0 decision 8).
4
+ *
5
+ * Layer-legal: paths come from core `resolveProjectPaths`, NOT a `cli` import.
6
+ * `scan` REQUIRES a project — a config-less/project-agnostic run has no targeting
7
+ * root to scan, so a missing `projectContext` is a `ConfigurationError` (exit 2),
8
+ * mirroring MCP's datastore-unavailable handling (Phase-0 decision 2).
9
+ */
10
+ import { ConfigurationError, resolveProjectPaths } from '@opensip-cli/core';
11
+ import { resolveScannerArtifactPath } from './artifact-path.js';
12
+ /**
13
+ * Construct the per-run {@link AdapterRunContext}. Throws `ConfigurationError`
14
+ * when no project scope is present (scan needs a targeting root).
15
+ */
16
+ export function buildAdapterRunContext(input) {
17
+ const project = input.cli.scope.projectContext;
18
+ if (project === undefined) {
19
+ throw new ConfigurationError(`Cannot run '${input.tool}': no opensip-cli project found here. ` +
20
+ `Run 'opensip ${input.tool}' from inside an opensip-cli project (a directory with opensip-cli.config.yml).`, { code: 'ADAPTER.SCAN.NO_PROJECT' });
21
+ }
22
+ const projectPaths = resolveProjectPaths(project.projectRoot);
23
+ const runId = input.cli.scope.runId;
24
+ return {
25
+ tool: input.tool,
26
+ adapterPackage: input.adapterPackage,
27
+ projectRoot: project.projectRoot,
28
+ runId,
29
+ logger: input.cli.logger,
30
+ config: input.config,
31
+ binary: input.binary,
32
+ configPath: project.configPath,
33
+ artifactPath: (name) => resolveScannerArtifactPath({ artifactDir: projectPaths.artifactDir, runId }, input.tool, name),
34
+ };
35
+ }
36
+ //# sourceMappingURL=run-context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run-context.js","sourceRoot":"","sources":["../src/run-context.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAE5E,OAAO,EAAE,0BAA0B,EAAE,MAAM,oBAAoB,CAAC;AAahE;;;GAGG;AACH,MAAM,UAAU,sBAAsB,CAAC,KAA2B;IAChE,MAAM,OAAO,GAAG,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,cAAc,CAAC;IAC/C,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;QAC1B,MAAM,IAAI,kBAAkB,CAC1B,eAAe,KAAK,CAAC,IAAI,wCAAwC;YAC/D,gBAAgB,KAAK,CAAC,IAAI,iFAAiF,EAC7G,EAAE,IAAI,EAAE,yBAAyB,EAAE,CACpC,CAAC;IACJ,CAAC;IACD,MAAM,YAAY,GAAG,mBAAmB,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC;IAC9D,MAAM,KAAK,GAAG,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC;IACpC,OAAO;QACL,IAAI,EAAE,KAAK,CAAC,IAAI;QAChB,cAAc,EAAE,KAAK,CAAC,cAAc;QACpC,WAAW,EAAE,OAAO,CAAC,WAAW;QAChC,KAAK;QACL,MAAM,EAAE,KAAK,CAAC,GAAG,CAAC,MAAM;QACxB,MAAM,EAAE,KAAK,CAAC,MAAM;QACpB,MAAM,EAAE,KAAK,CAAC,MAAM;QACpB,UAAU,EAAE,OAAO,CAAC,UAAU;QAC9B,YAAY,EAAE,CAAC,IAAI,EAAE,EAAE,CACrB,0BAA0B,CACxB,EAAE,WAAW,EAAE,YAAY,CAAC,WAAW,EAAE,KAAK,EAAE,EAChD,KAAK,CAAC,IAAI,EACV,IAAI,CACL;KACJ,CAAC;AACJ,CAAC"}