@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,26 @@
1
+ /**
2
+ * @fileoverview Pure scanner-artifact path composition (ADR-0091, Phase-0
3
+ * decision 1).
4
+ *
5
+ * `ProjectPaths.artifactDir(tool)` (host-owned) = `<artifacts>/<tool>`. The
6
+ * RUN segment is substrate-side: `resolveScannerArtifactPath` composes
7
+ * `<artifactDir(tool)>/<runId>/<name>`. So the immediate children of
8
+ * `artifactDir(tool)` are the per-run dirs the host's `pruneArtifactRetention`
9
+ * treats as retention units — host and substrate agree on the boundary without a
10
+ * 3-arg `ProjectPaths` method.
11
+ */
12
+ /** The minimal path scope the resolver needs (a `ProjectPaths` satisfies it). */
13
+ export interface ArtifactPathScope {
14
+ /** `<artifacts>/<tool>` (host-owned path family, ADR-0091). */
15
+ readonly artifactDir: (tool: string) => string;
16
+ /** This invocation's run id — the per-run artifact segment. */
17
+ readonly runId: string;
18
+ }
19
+ /**
20
+ * Compose the host-owned artifact path for a scanner output file:
21
+ * `<artifactDir(tool)>/<runId>/<name>`. Pure — no IO. The bytes are persisted
22
+ * through `cli.writeArtifact(path, bytes)` (the host seam, ADR-0080), never a
23
+ * raw `fs` write.
24
+ */
25
+ export declare function resolveScannerArtifactPath(scope: ArtifactPathScope, tool: string, name: string): string;
26
+ //# sourceMappingURL=artifact-path.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"artifact-path.d.ts","sourceRoot":"","sources":["../src/artifact-path.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAIH,iFAAiF;AACjF,MAAM,WAAW,iBAAiB;IAChC,+DAA+D;IAC/D,QAAQ,CAAC,WAAW,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;IAC/C,+DAA+D;IAC/D,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB;AAED;;;;;GAKG;AACH,wBAAgB,0BAA0B,CACxC,KAAK,EAAE,iBAAiB,EACxB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,GACX,MAAM,CAER"}
@@ -0,0 +1,22 @@
1
+ /**
2
+ * @fileoverview Pure scanner-artifact path composition (ADR-0091, Phase-0
3
+ * decision 1).
4
+ *
5
+ * `ProjectPaths.artifactDir(tool)` (host-owned) = `<artifacts>/<tool>`. The
6
+ * RUN segment is substrate-side: `resolveScannerArtifactPath` composes
7
+ * `<artifactDir(tool)>/<runId>/<name>`. So the immediate children of
8
+ * `artifactDir(tool)` are the per-run dirs the host's `pruneArtifactRetention`
9
+ * treats as retention units — host and substrate agree on the boundary without a
10
+ * 3-arg `ProjectPaths` method.
11
+ */
12
+ import { join } from 'node:path';
13
+ /**
14
+ * Compose the host-owned artifact path for a scanner output file:
15
+ * `<artifactDir(tool)>/<runId>/<name>`. Pure — no IO. The bytes are persisted
16
+ * through `cli.writeArtifact(path, bytes)` (the host seam, ADR-0080), never a
17
+ * raw `fs` write.
18
+ */
19
+ export function resolveScannerArtifactPath(scope, tool, name) {
20
+ return join(scope.artifactDir(tool), scope.runId, name);
21
+ }
22
+ //# sourceMappingURL=artifact-path.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"artifact-path.js","sourceRoot":"","sources":["../src/artifact-path.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAUjC;;;;;GAKG;AACH,MAAM,UAAU,0BAA0B,CACxC,KAAwB,EACxB,IAAY,EACZ,IAAY;IAEZ,OAAO,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,IAAI,CAAC,EAAE,KAAK,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;AAC1D,CAAC"}
@@ -0,0 +1,51 @@
1
+ /**
2
+ * @fileoverview Layered, deterministic binary resolution (ADR-0090 §4.3).
3
+ *
4
+ * First hit wins: an operator pin (the `OPENSIP_<TOOL>_BIN` env var, then the
5
+ * `binaries.<tool>.path` config) always beats the system `PATH`. A pin that is
6
+ * non-absolute or missing is a HARD miss — the substrate does NOT silently fall
7
+ * back to `PATH` (the operator asked for a specific binary). A `PATH` miss yields
8
+ * a not-found result (the caller raises a `ConfigurationError` pointing at
9
+ * `doctor`); resolution NEVER fetches a binary.
10
+ *
11
+ * Pure given its {@link BinaryResolveDeps} (existence check + PATH lookup) — the
12
+ * real deps live in `process-exec.ts` (the IO boundary). Returns data, never
13
+ * throws.
14
+ */
15
+ import type { BinaryResolutionLayer } from './types.js';
16
+ /** The IO deps `resolveBinary` is parameterized over (real impls in `process-exec.ts`). */
17
+ export interface BinaryResolveDeps {
18
+ readonly existsSync: (path: string) => boolean;
19
+ readonly which: (command: string, platform: NodeJS.Platform) => string | undefined;
20
+ }
21
+ /** The inputs to {@link resolveBinary}: the lookup name plus the optional operator pins. */
22
+ export interface ResolveBinaryInput {
23
+ /** The PATH lookup name (`'gitleaks'`). */
24
+ readonly command: string;
25
+ /** An operator-pinned absolute path from the namespaced config (`binaries.<tool>.path`). */
26
+ readonly configuredPath?: string;
27
+ /** An operator-pinned absolute path from `OPENSIP_<TOOL>_BIN`. */
28
+ readonly envPath?: string;
29
+ /** The host platform (defaults to `process.platform`). */
30
+ readonly platform?: NodeJS.Platform;
31
+ }
32
+ /** The outcome of {@link resolveBinary}. */
33
+ export type BinaryResolution = {
34
+ readonly found: true;
35
+ readonly path: string;
36
+ readonly layer: BinaryResolutionLayer;
37
+ } | {
38
+ readonly found: false;
39
+ readonly command: string;
40
+ readonly reason: string;
41
+ readonly searched: readonly string[];
42
+ };
43
+ /**
44
+ * Resolve the scanner binary by the layered order env-pin → config-pin → `PATH`.
45
+ * An operator pin (env or config) wins and never falls through to `PATH`; a
46
+ * broken pin is a hard miss with a reason.
47
+ */
48
+ export declare function resolveBinary(input: ResolveBinaryInput, deps: BinaryResolveDeps): BinaryResolution;
49
+ /** Derive the default env-var name that pins a tool's binary: `OPENSIP_<TOOL>_BIN`. */
50
+ export declare function defaultBinaryEnvVar(tool: string): string;
51
+ //# sourceMappingURL=binary-resolver.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"binary-resolver.d.ts","sourceRoot":"","sources":["../src/binary-resolver.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAIH,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AAExD,2FAA2F;AAC3F,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,UAAU,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC;IAC/C,QAAQ,CAAC,KAAK,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,CAAC,QAAQ,KAAK,MAAM,GAAG,SAAS,CAAC;CACpF;AAED,4FAA4F;AAC5F,MAAM,WAAW,kBAAkB;IACjC,2CAA2C;IAC3C,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,4FAA4F;IAC5F,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;IACjC,kEAAkE;IAClE,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,0DAA0D;IAC1D,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC,QAAQ,CAAC;CACrC;AAED,4CAA4C;AAC5C,MAAM,MAAM,gBAAgB,GACxB;IAAE,QAAQ,CAAC,KAAK,EAAE,IAAI,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,qBAAqB,CAAA;CAAE,GACtF;IACE,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAC;CACtC,CAAC;AA2BN;;;;GAIG;AACH,wBAAgB,aAAa,CAC3B,KAAK,EAAE,kBAAkB,EACzB,IAAI,EAAE,iBAAiB,GACtB,gBAAgB,CAsBlB;AAED,uFAAuF;AACvF,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAExD"}
@@ -0,0 +1,66 @@
1
+ /**
2
+ * @fileoverview Layered, deterministic binary resolution (ADR-0090 §4.3).
3
+ *
4
+ * First hit wins: an operator pin (the `OPENSIP_<TOOL>_BIN` env var, then the
5
+ * `binaries.<tool>.path` config) always beats the system `PATH`. A pin that is
6
+ * non-absolute or missing is a HARD miss — the substrate does NOT silently fall
7
+ * back to `PATH` (the operator asked for a specific binary). A `PATH` miss yields
8
+ * a not-found result (the caller raises a `ConfigurationError` pointing at
9
+ * `doctor`); resolution NEVER fetches a binary.
10
+ *
11
+ * Pure given its {@link BinaryResolveDeps} (existence check + PATH lookup) — the
12
+ * real deps live in `process-exec.ts` (the IO boundary). Returns data, never
13
+ * throws.
14
+ */
15
+ import { isAbsolute } from 'node:path';
16
+ /** Validate one operator-pinned path (absolute + exists). Returns the resolution or a miss reason. */
17
+ function resolvePin(path, layer, deps) {
18
+ if (!isAbsolute(path)) {
19
+ return {
20
+ found: false,
21
+ command: path,
22
+ reason: `${layer} binary path must be absolute: ${path}`,
23
+ searched: [path],
24
+ };
25
+ }
26
+ if (!deps.existsSync(path)) {
27
+ return {
28
+ found: false,
29
+ command: path,
30
+ reason: `${layer} binary path does not exist: ${path}`,
31
+ searched: [path],
32
+ };
33
+ }
34
+ return { found: true, path, layer };
35
+ }
36
+ /**
37
+ * Resolve the scanner binary by the layered order env-pin → config-pin → `PATH`.
38
+ * An operator pin (env or config) wins and never falls through to `PATH`; a
39
+ * broken pin is a hard miss with a reason.
40
+ */
41
+ export function resolveBinary(input, deps) {
42
+ const envPath = input.envPath?.trim();
43
+ if (envPath !== undefined && envPath.length > 0) {
44
+ return resolvePin(envPath, 'env', deps);
45
+ }
46
+ const configuredPath = input.configuredPath?.trim();
47
+ if (configuredPath !== undefined && configuredPath.length > 0) {
48
+ return resolvePin(configuredPath, 'config', deps);
49
+ }
50
+ const platform = input.platform ?? process.platform;
51
+ const onPath = deps.which(input.command, platform);
52
+ if (onPath !== undefined && onPath.length > 0) {
53
+ return { found: true, path: onPath, layer: 'path' };
54
+ }
55
+ return {
56
+ found: false,
57
+ command: input.command,
58
+ reason: `'${input.command}' was not found on PATH`,
59
+ searched: [`PATH:${input.command}`],
60
+ };
61
+ }
62
+ /** Derive the default env-var name that pins a tool's binary: `OPENSIP_<TOOL>_BIN`. */
63
+ export function defaultBinaryEnvVar(tool) {
64
+ return `OPENSIP_${tool.replaceAll('-', '_').toUpperCase()}_BIN`;
65
+ }
66
+ //# sourceMappingURL=binary-resolver.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"binary-resolver.js","sourceRoot":"","sources":["../src/binary-resolver.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAgCvC,sGAAsG;AACtG,SAAS,UAAU,CACjB,IAAY,EACZ,KAAuB,EACvB,IAAuB;IAEvB,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;QACtB,OAAO;YACL,KAAK,EAAE,KAAK;YACZ,OAAO,EAAE,IAAI;YACb,MAAM,EAAE,GAAG,KAAK,kCAAkC,IAAI,EAAE;YACxD,QAAQ,EAAE,CAAC,IAAI,CAAC;SACjB,CAAC;IACJ,CAAC;IACD,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;QAC3B,OAAO;YACL,KAAK,EAAE,KAAK;YACZ,OAAO,EAAE,IAAI;YACb,MAAM,EAAE,GAAG,KAAK,gCAAgC,IAAI,EAAE;YACtD,QAAQ,EAAE,CAAC,IAAI,CAAC;SACjB,CAAC;IACJ,CAAC;IACD,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;AACtC,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,aAAa,CAC3B,KAAyB,EACzB,IAAuB;IAEvB,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC;IACtC,IAAI,OAAO,KAAK,SAAS,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAChD,OAAO,UAAU,CAAC,OAAO,EAAE,KAAK,EAAE,IAAI,CAAC,CAAC;IAC1C,CAAC;IAED,MAAM,cAAc,GAAG,KAAK,CAAC,cAAc,EAAE,IAAI,EAAE,CAAC;IACpD,IAAI,cAAc,KAAK,SAAS,IAAI,cAAc,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC9D,OAAO,UAAU,CAAC,cAAc,EAAE,QAAQ,EAAE,IAAI,CAAC,CAAC;IACpD,CAAC;IAED,MAAM,QAAQ,GAAG,KAAK,CAAC,QAAQ,IAAI,OAAO,CAAC,QAAQ,CAAC;IACpD,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;IACnD,IAAI,MAAM,KAAK,SAAS,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC9C,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC;IACtD,CAAC;IACD,OAAO;QACL,KAAK,EAAE,KAAK;QACZ,OAAO,EAAE,KAAK,CAAC,OAAO;QACtB,MAAM,EAAE,IAAI,KAAK,CAAC,OAAO,yBAAyB;QAClD,QAAQ,EAAE,CAAC,QAAQ,KAAK,CAAC,OAAO,EAAE,CAAC;KACpC,CAAC;AACJ,CAAC;AAED,uFAAuF;AACvF,MAAM,UAAU,mBAAmB,CAAC,IAAY;IAC9C,OAAO,WAAW,IAAI,CAAC,UAAU,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,WAAW,EAAE,MAAM,CAAC;AAClE,CAAC"}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * @fileoverview `defineExternalToolAdapter(spec) → Tool` (ADR-0090).
3
+ *
4
+ * A thin authoring factory over `defineTool` — an adapter is an ORDINARY `Tool`
5
+ * (no new plugin kind). It owns: the primary `scan` command (and any additional
6
+ * scanner verbs), the auto-added nested `doctor`/`version` commands, the
7
+ * `message-hash` fingerprint default (stamped WORKER-SIDE in the run loop, never
8
+ * read off the synthetic Tool — `synthesizeExternalTool` drops
9
+ * `fingerprintStrategy`), and the optional namespaced config block.
10
+ *
11
+ * The scan handler runs the run loop and RETURNS a `ToolRunCompletion`
12
+ * (`{ envelope, session }`) — emission/egress happen INSIDE the loop via
13
+ * `cli.*` seams (output mode `raw-stream`/`'runtime-render-dispatch'`, like sim:
14
+ * a runtime-conditional render + egress + session flow no static mode captures).
15
+ */
16
+ import type { ExternalToolAdapterSpec } from './types.js';
17
+ import type { Tool } from '@opensip-cli/core';
18
+ /**
19
+ * Build an external-scanner adapter `Tool` from its declarative {@link
20
+ * ExternalToolAdapterSpec}. The first command is the primary verb
21
+ * (`opensip <tool>`); any additional scanner commands mount as nested verbs;
22
+ * `doctor` and `version` are always added.
23
+ */
24
+ export declare function defineExternalToolAdapter(spec: ExternalToolAdapterSpec): Tool;
25
+ //# sourceMappingURL=define-external-tool-adapter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"define-external-tool-adapter.d.ts","sourceRoot":"","sources":["../src/define-external-tool-adapter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAgBH,OAAO,KAAK,EAAuB,uBAAuB,EAAE,MAAM,YAAY,CAAC;AAC/E,OAAO,KAAK,EAEV,IAAI,EAKL,MAAM,mBAAmB,CAAC;AA8E3B;;;;;GAKG;AACH,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,uBAAuB,GAAG,IAAI,CAoE7E"}
@@ -0,0 +1,149 @@
1
+ /**
2
+ * @fileoverview `defineExternalToolAdapter(spec) → Tool` (ADR-0090).
3
+ *
4
+ * A thin authoring factory over `defineTool` — an adapter is an ORDINARY `Tool`
5
+ * (no new plugin kind). It owns: the primary `scan` command (and any additional
6
+ * scanner verbs), the auto-added nested `doctor`/`version` commands, the
7
+ * `message-hash` fingerprint default (stamped WORKER-SIDE in the run loop, never
8
+ * read off the synthetic Tool — `synthesizeExternalTool` drops
9
+ * `fingerprintStrategy`), and the optional namespaced config block.
10
+ *
11
+ * The scan handler runs the run loop and RETURNS a `ToolRunCompletion`
12
+ * (`{ envelope, session }`) — emission/egress happen INSIDE the loop via
13
+ * `cli.*` seams (output mode `raw-stream`/`'runtime-render-dispatch'`, like sim:
14
+ * a runtime-conditional render + egress + session flow no static mode captures).
15
+ */
16
+ import { defineNestedCommand, definePrimaryCommand, defineTool, ValidationError, } from '@opensip-cli/core';
17
+ import { defaultAdapterConfig, defaultAdapterConfigManifest } from './adapter-config.js';
18
+ import { buildDoctorCommand } from './doctor-command.js';
19
+ import { resolveFingerprintStrategy } from './fingerprint.js';
20
+ import { runScanLoop } from './run-loop.js';
21
+ import { buildVersionCommand } from './version-command.js';
22
+ const SCAN_COMMON_FLAGS = [
23
+ 'json',
24
+ 'cwd',
25
+ 'quiet',
26
+ 'verbose',
27
+ 'debug',
28
+ 'reportTo',
29
+ 'apiKey',
30
+ 'open',
31
+ ];
32
+ /**
33
+ * The baseline-ratchet flags every scanner verb inherits (ADR-0036). Wired ONCE
34
+ * here so all adapters get the host gate loop verbatim — `--gate-save` captures
35
+ * the current fingerprint-stamped findings as the project baseline; `--gate-compare`
36
+ * diffs against it and (per the reserved `failOnDegraded` key) hard-fails on
37
+ * net-new findings. Mirrors fit's `--gate-save` / `--gate-compare` descriptions;
38
+ * the run loop reads them as `opts.gateSave` / `opts.gateCompare`.
39
+ */
40
+ const SCAN_GATE_OPTIONS = [
41
+ {
42
+ flag: '--gate-save',
43
+ description: 'Architecture-gate: save current findings as baseline in the project SQLite store (mutually exclusive with --gate-compare)',
44
+ default: false,
45
+ },
46
+ {
47
+ flag: '--gate-compare',
48
+ description: 'Architecture-gate: compare current findings against the saved baseline; exit 1 on regression',
49
+ default: false,
50
+ },
51
+ ];
52
+ /** Run one scanner command and shape its result as a `ToolRunCompletion`. */
53
+ async function dispatchScan(cli, spec, command, rawOpts) {
54
+ const completion = await runScanLoop({
55
+ cli,
56
+ tool: spec.identity.name,
57
+ adapterPackage: spec.metadata.adapterPackage,
58
+ command,
59
+ binary: spec.binary,
60
+ fingerprintStrategy: resolveFingerprintStrategy(spec.fingerprintStrategy),
61
+ opts: rawOpts,
62
+ });
63
+ // `undefined` ⇒ a gate config error the loop already recorded via reportFailure
64
+ // (the host replays the exit). No envelope/session to persist.
65
+ if (completion === undefined)
66
+ return {};
67
+ return { envelope: completion.envelope, session: completion.session };
68
+ }
69
+ /** Validate the spec at definition time so misconfiguration fails loudly, not at runtime. */
70
+ function assertSpec(spec) {
71
+ if (spec.commands.length === 0) {
72
+ throw new ValidationError(`External adapter '${spec.identity.name}' must declare at least one command.`, {
73
+ code: 'ADAPTER.SPEC.NO_COMMANDS',
74
+ });
75
+ }
76
+ for (const command of spec.commands) {
77
+ if (command.output.kind !== 'sarif' && command.parse === undefined) {
78
+ throw new ValidationError(`External adapter '${spec.identity.name}' command '${command.name}' (${command.output.kind}) must declare a 'parse' (only SARIF commands may omit it — the shared ingestSarif handles those).`, { code: 'ADAPTER.SPEC.MISSING_PARSE' });
79
+ }
80
+ }
81
+ }
82
+ /**
83
+ * Build an external-scanner adapter `Tool` from its declarative {@link
84
+ * ExternalToolAdapterSpec}. The first command is the primary verb
85
+ * (`opensip <tool>`); any additional scanner commands mount as nested verbs;
86
+ * `doctor` and `version` are always added.
87
+ */
88
+ export function defineExternalToolAdapter(spec) {
89
+ assertSpec(spec);
90
+ const [primary, ...rest] = spec.commands;
91
+ // R6 (ADR-0090 §4.3): an adapter that declares no `config` DEFAULTS to claiming
92
+ // its namespace (`binaries.<tool>.path` operator pin + the reserved verdict-policy
93
+ // keys) so the resolver/gate keys are configurable AND an operator's `<tool>:`
94
+ // block never bricks the project. When the adapter uses the DEFAULT config the
95
+ // substrate can also emit a coarse, serializable manifest descriptor (the host
96
+ // pre-fork pass needs it — it cannot import the runtime Zod). A custom `spec.config`
97
+ // keeps the runtime Zod but emits NO descriptor (its validation defers to the
98
+ // worker deep pass), so `adapterConfigManifest` stays undefined.
99
+ const config = spec.config ?? defaultAdapterConfig();
100
+ const configManifest = spec.config === undefined ? defaultAdapterConfigManifest(spec.identity.name) : undefined;
101
+ const scanPrimary = definePrimaryCommand({
102
+ description: primary.description ?? spec.metadata.description,
103
+ commonFlags: [...SCAN_COMMON_FLAGS],
104
+ options: [...SCAN_GATE_OPTIONS],
105
+ scope: 'project',
106
+ output: 'raw-stream',
107
+ rawStreamReason: 'runtime-render-dispatch',
108
+ handler: (rawOpts, cli) => dispatchScan(cli, spec, primary, rawOpts),
109
+ });
110
+ const nestedScans = rest.map((command) => defineNestedCommand({
111
+ name: command.name,
112
+ description: command.description ?? `Run the ${spec.identity.name} ${command.name} scan`,
113
+ commonFlags: [...SCAN_COMMON_FLAGS],
114
+ options: [...SCAN_GATE_OPTIONS],
115
+ scope: 'project',
116
+ output: 'raw-stream',
117
+ rawStreamReason: 'runtime-render-dispatch',
118
+ handler: (rawOpts, cli) => dispatchScan(cli, spec, command, rawOpts),
119
+ }));
120
+ const tool = defineTool({
121
+ identity: spec.identity,
122
+ metadata: {
123
+ id: spec.metadata.id,
124
+ version: spec.metadata.version ?? '0.0.0',
125
+ description: spec.metadata.description,
126
+ },
127
+ commandSpecs: [
128
+ scanPrimary,
129
+ ...nestedScans,
130
+ buildDoctorCommand({ tool: spec.identity.name, network: spec.network, binary: spec.binary }),
131
+ buildVersionCommand({ tool: spec.identity.name, binary: spec.binary }),
132
+ ],
133
+ ...(spec.contractVersion === undefined ? {} : { contractVersion: spec.contractVersion }),
134
+ extensionPoints: {
135
+ fingerprintStrategy: resolveFingerprintStrategy(spec.fingerprintStrategy),
136
+ config,
137
+ },
138
+ });
139
+ // Stamp the adapter-substrate markers the manifest generator + parity gate read
140
+ // back (`adapter-manifest.ts`): the network posture (→ `requires`) and the coarse
141
+ // config descriptor (→ `opensipTools.config`). Kept off the core `Tool` contract
142
+ // — these are adapter concepts, not kernel ones.
143
+ const markers = {
144
+ adapterNetwork: spec.network,
145
+ ...(configManifest === undefined ? {} : { adapterConfigManifest: configManifest }),
146
+ };
147
+ return Object.assign(tool, markers);
148
+ }
149
+ //# sourceMappingURL=define-external-tool-adapter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"define-external-tool-adapter.js","sourceRoot":"","sources":["../src/define-external-tool-adapter.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,81 @@
1
+ /**
2
+ * @fileoverview The standardized `doctor` command (ADR-0090 §4.9, Phase-0
3
+ * decision 3).
4
+ *
5
+ * Every adapter gets the SAME auto-generated `doctor` (the author writes none):
6
+ * it PROBES the wrapped binary (resolve → version → minVersion → posture →
7
+ * credential-env presence) and reports a plain {@link AdapterDoctorReport} —
8
+ * NOT a `CommandResult` variant (the union is closed in `contracts`).
9
+ *
10
+ * Output mode is `raw-stream`/`'diagnostic-gate'` (not `command-result`): the
11
+ * handler must produce a STRUCTURED `cli.emitJson(report)` for `--json` AND a
12
+ * human `cli.render({ type:'text-lines' })`, then set exit 2 when not-ready. The
13
+ * `command-result` dispatch arm would JSON-stringify a single returned result
14
+ * (it cannot do both shapes) and rejects a void return — so `raw-stream`, the
15
+ * host's sanctioned runtime-conditional output escape (same as `fit export`), is
16
+ * the correct mode. The worker replays the explicit `emitJson`/`render`/
17
+ * `setExitCode` calls. (See the report's "deviation" note.)
18
+ *
19
+ * The probe runs WORKER-SIDE for an installed adapter; readiness drives exit 0
20
+ * (ready) / 2 (not-ready) so CI can gate on `opensip <tool> doctor`.
21
+ */
22
+ import type { BinaryResolveDeps } from './binary-resolver.js';
23
+ import type { ProbeVersionInput } from './process-exec.js';
24
+ import type { BinaryResolutionLayer, BinarySpec, NetworkPosture } from './types.js';
25
+ import type { ToolCliContext, ToolCommandSpecInput } from '@opensip-cli/core';
26
+ /** Whether the resolved version satisfies the declared minimum. */
27
+ export type VersionStatus = 'ok' | 'too-old' | 'unknown' | 'not-applicable';
28
+ /** The structured readiness report `doctor` emits (NOT a `CommandResult` variant). */
29
+ export interface AdapterDoctorReport {
30
+ readonly tool: string;
31
+ readonly network: NetworkPosture;
32
+ readonly binary: {
33
+ readonly found: boolean;
34
+ readonly command: string;
35
+ readonly path?: string;
36
+ readonly layer?: BinaryResolutionLayer;
37
+ };
38
+ readonly version: {
39
+ readonly detected?: string;
40
+ readonly minVersion?: string;
41
+ readonly status: VersionStatus;
42
+ };
43
+ /** Presence-only (never the value) when the posture needs a credential (ADR-0092/0071). */
44
+ readonly credentialEnv?: {
45
+ readonly name: string;
46
+ readonly present: boolean;
47
+ };
48
+ readonly installHint?: string;
49
+ readonly ready: boolean;
50
+ }
51
+ /** Deps the probe is parameterized over (real impls default; E2E injects fakes). */
52
+ export interface DoctorProbeDeps {
53
+ readonly binaryDeps: BinaryResolveDeps;
54
+ readonly probeVersion: (input: ProbeVersionInput) => string | undefined;
55
+ readonly env: NodeJS.ProcessEnv;
56
+ }
57
+ /** Compare a detected version against a minimum. */
58
+ export declare function compareVersion(detected: string | undefined, min: string | undefined): VersionStatus;
59
+ /** The inputs to {@link probeAdapter}: the tool identity, posture, binary spec, and resolved config. */
60
+ export interface ProbeAdapterInput {
61
+ readonly tool: string;
62
+ readonly network: NetworkPosture;
63
+ readonly binary: BinarySpec;
64
+ readonly config: Readonly<Record<string, unknown>>;
65
+ }
66
+ /** Probe a binary and build its {@link AdapterDoctorReport}. Pure given deps. */
67
+ export declare function probeAdapter(input: ProbeAdapterInput, deps: DoctorProbeDeps): AdapterDoctorReport;
68
+ /** Render an {@link AdapterDoctorReport} as human display lines. */
69
+ export declare function doctorReportLines(report: AdapterDoctorReport): string[];
70
+ /** The inputs to {@link buildDoctorCommand}: the tool identity, posture, and binary spec. */
71
+ export interface DoctorCommandInput {
72
+ readonly tool: string;
73
+ readonly network: NetworkPosture;
74
+ readonly binary: BinarySpec;
75
+ }
76
+ /**
77
+ * Build the nested `doctor` command. Probes the binary worker-side, emits the
78
+ * structured report (`--json`) or human lines, and sets exit 2 when not-ready.
79
+ */
80
+ export declare function buildDoctorCommand(input: DoctorCommandInput, probeDeps?: DoctorProbeDeps): ToolCommandSpecInput<unknown, ToolCliContext>;
81
+ //# sourceMappingURL=doctor-command.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"doctor-command.d.ts","sourceRoot":"","sources":["../src/doctor-command.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AASH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAC9D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAC3D,OAAO,KAAK,EAAE,qBAAqB,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AACpF,OAAO,KAAK,EAAE,cAAc,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AAI9E,mEAAmE;AACnE,MAAM,MAAM,aAAa,GAAG,IAAI,GAAG,SAAS,GAAG,SAAS,GAAG,gBAAgB,CAAC;AAE5E,sFAAsF;AACtF,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,cAAc,CAAC;IACjC,QAAQ,CAAC,MAAM,EAAE;QACf,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;QACxB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;QACzB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QACvB,QAAQ,CAAC,KAAK,CAAC,EAAE,qBAAqB,CAAC;KACxC,CAAC;IACF,QAAQ,CAAC,OAAO,EAAE;QAChB,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;QAC3B,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;QAC7B,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAC;KAChC,CAAC;IACF,2FAA2F;IAC3F,QAAQ,CAAC,aAAa,CAAC,EAAE;QAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAA;KAAE,CAAC;IAC9E,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;CACzB;AAED,oFAAoF;AACpF,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,UAAU,EAAE,iBAAiB,CAAC;IACvC,QAAQ,CAAC,YAAY,EAAE,CAAC,KAAK,EAAE,iBAAiB,KAAK,MAAM,GAAG,SAAS,CAAC;IACxE,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC,UAAU,CAAC;CACjC;AAeD,oDAAoD;AACpD,wBAAgB,cAAc,CAC5B,QAAQ,EAAE,MAAM,GAAG,SAAS,EAC5B,GAAG,EAAE,MAAM,GAAG,SAAS,GACtB,aAAa,CAaf;AAED,wGAAwG;AACxG,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,cAAc,CAAC;IACjC,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC;IAC5B,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;CACpD;AAED,iFAAiF;AACjF,wBAAgB,YAAY,CAAC,KAAK,EAAE,iBAAiB,EAAE,IAAI,EAAE,eAAe,GAAG,mBAAmB,CAqDjG;AAED,oEAAoE;AACpE,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,mBAAmB,GAAG,MAAM,EAAE,CAuBvE;AAED,6FAA6F;AAC7F,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,cAAc,CAAC;IACjC,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC;CAC7B;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,kBAAkB,EACzB,SAAS,CAAC,EAAE,eAAe,GAC1B,oBAAoB,CAAC,OAAO,EAAE,cAAc,CAAC,CAgC/C"}
@@ -0,0 +1,160 @@
1
+ /**
2
+ * @fileoverview The standardized `doctor` command (ADR-0090 §4.9, Phase-0
3
+ * decision 3).
4
+ *
5
+ * Every adapter gets the SAME auto-generated `doctor` (the author writes none):
6
+ * it PROBES the wrapped binary (resolve → version → minVersion → posture →
7
+ * credential-env presence) and reports a plain {@link AdapterDoctorReport} —
8
+ * NOT a `CommandResult` variant (the union is closed in `contracts`).
9
+ *
10
+ * Output mode is `raw-stream`/`'diagnostic-gate'` (not `command-result`): the
11
+ * handler must produce a STRUCTURED `cli.emitJson(report)` for `--json` AND a
12
+ * human `cli.render({ type:'text-lines' })`, then set exit 2 when not-ready. The
13
+ * `command-result` dispatch arm would JSON-stringify a single returned result
14
+ * (it cannot do both shapes) and rejects a void return — so `raw-stream`, the
15
+ * host's sanctioned runtime-conditional output escape (same as `fit export`), is
16
+ * the correct mode. The worker replays the explicit `emitJson`/`render`/
17
+ * `setExitCode` calls. (See the report's "deviation" note.)
18
+ *
19
+ * The probe runs WORKER-SIDE for an installed adapter; readiness drives exit 0
20
+ * (ready) / 2 (not-ready) so CI can gate on `opensip <tool> doctor`.
21
+ */
22
+ import { EXIT_CODES } from '@opensip-cli/contracts';
23
+ import { defineNestedCommand } from '@opensip-cli/core';
24
+ import { defaultBinaryEnvVar, resolveBinary } from './binary-resolver.js';
25
+ import { asObject, getString } from './ingest-json.js';
26
+ import { defaultBinaryDeps, probeBinaryVersion } from './process-exec.js';
27
+ const VERSION_PROBE_TIMEOUT_MS = 15_000;
28
+ const DEFAULT_PROBE_DEPS = {
29
+ binaryDeps: defaultBinaryDeps,
30
+ probeVersion: probeBinaryVersion,
31
+ env: process.env,
32
+ };
33
+ /** Parse a semver-ish string to numeric segments (`'8.18.0'` → `[8,18,0]`). */
34
+ function parseVersion(raw) {
35
+ const match = /(\d+)(?:\.(\d+))?(?:\.(\d+))?/.exec(raw);
36
+ if (match === null)
37
+ return undefined;
38
+ return [Number(match[1] ?? 0), Number(match[2] ?? 0), Number(match[3] ?? 0)];
39
+ }
40
+ /** Compare a detected version against a minimum. */
41
+ export function compareVersion(detected, min) {
42
+ if (min === undefined)
43
+ return 'not-applicable';
44
+ if (detected === undefined)
45
+ return 'unknown';
46
+ const a = parseVersion(detected);
47
+ const b = parseVersion(min);
48
+ if (a === undefined || b === undefined)
49
+ return 'unknown';
50
+ for (let i = 0; i < 3; i++) {
51
+ const left = a[i] ?? 0;
52
+ const right = b[i] ?? 0;
53
+ if (left > right)
54
+ return 'ok';
55
+ if (left < right)
56
+ return 'too-old';
57
+ }
58
+ return 'ok';
59
+ }
60
+ /** Probe a binary and build its {@link AdapterDoctorReport}. Pure given deps. */
61
+ export function probeAdapter(input, deps) {
62
+ const { tool, binary, network } = input;
63
+ const envVar = binary.envVar ?? defaultBinaryEnvVar(tool);
64
+ const resolution = resolveBinary({
65
+ command: binary.command,
66
+ configuredPath: getString(asObject(input.config.binaries)?.[tool], 'path'),
67
+ envPath: deps.env[envVar],
68
+ }, deps.binaryDeps);
69
+ const detected = resolution.found
70
+ ? deps.probeVersion({
71
+ path: resolution.path,
72
+ versionArgs: binary.versionArgs,
73
+ parse: binary.versionParse,
74
+ timeoutMs: VERSION_PROBE_TIMEOUT_MS,
75
+ })
76
+ : undefined;
77
+ const versionStatus = compareVersion(detected, binary.minVersion);
78
+ const credentialEnv = network === 'auth-required'
79
+ ? { name: `OPENSIP_${tool.replaceAll('-', '_').toUpperCase()}_TOKEN`, present: false }
80
+ : undefined;
81
+ const credential = credentialEnv === undefined
82
+ ? undefined
83
+ : { ...credentialEnv, present: (deps.env[credentialEnv.name] ?? '').length > 0 };
84
+ const ready = resolution.found &&
85
+ versionStatus !== 'too-old' &&
86
+ (network !== 'auth-required' || (credential?.present ?? false));
87
+ return {
88
+ tool,
89
+ network,
90
+ binary: resolution.found
91
+ ? { found: true, command: binary.command, path: resolution.path, layer: resolution.layer }
92
+ : { found: false, command: binary.command },
93
+ version: {
94
+ ...(detected === undefined ? {} : { detected }),
95
+ ...(binary.minVersion === undefined ? {} : { minVersion: binary.minVersion }),
96
+ status: versionStatus,
97
+ },
98
+ ...(credential === undefined ? {} : { credentialEnv: credential }),
99
+ ...(resolution.found || binary.installHint === undefined
100
+ ? {}
101
+ : { installHint: binary.installHint }),
102
+ ready,
103
+ };
104
+ }
105
+ /** Render an {@link AdapterDoctorReport} as human display lines. */
106
+ export function doctorReportLines(report) {
107
+ const binaryLine = report.binary.found
108
+ ? `found (${report.binary.layer ?? ''}) ${report.binary.path ?? ''}`
109
+ : `NOT FOUND (${report.binary.command})`;
110
+ const minNote = report.version.minVersion === undefined
111
+ ? ''
112
+ : ` (min ${report.version.minVersion}: ${report.version.status})`;
113
+ const lines = [
114
+ `binary: ${binaryLine}`,
115
+ `version: ${report.version.detected ?? 'unknown'}${minNote}`,
116
+ `network: ${report.network}`,
117
+ ];
118
+ if (report.credentialEnv !== undefined) {
119
+ lines.push(`credential ${report.credentialEnv.name}: ${report.credentialEnv.present ? 'set' : 'MISSING'}`);
120
+ }
121
+ if (!report.binary.found && report.installHint !== undefined) {
122
+ lines.push(`install: ${report.installHint}`);
123
+ }
124
+ lines.push(`ready: ${report.ready ? 'yes' : 'NO'}`);
125
+ return lines;
126
+ }
127
+ /**
128
+ * Build the nested `doctor` command. Probes the binary worker-side, emits the
129
+ * structured report (`--json`) or human lines, and sets exit 2 when not-ready.
130
+ */
131
+ export function buildDoctorCommand(input, probeDeps) {
132
+ const deps = probeDeps ?? DEFAULT_PROBE_DEPS;
133
+ return defineNestedCommand({
134
+ name: 'doctor',
135
+ description: `Check that the ${input.tool} binary is installed and ready`,
136
+ commonFlags: ['json', 'cwd'],
137
+ scope: 'none',
138
+ output: 'raw-stream',
139
+ rawStreamReason: 'diagnostic-gate',
140
+ handler: async (rawOpts, cli) => {
141
+ const opts = rawOpts;
142
+ const config = (cli.scope.toolConfig?.[input.tool] ?? {});
143
+ const report = probeAdapter({ tool: input.tool, network: input.network, binary: input.binary, config }, deps);
144
+ if (opts.json === true) {
145
+ cli.emitJson(report);
146
+ }
147
+ else {
148
+ await cli.render({
149
+ type: 'text-lines',
150
+ title: `${input.tool} doctor`,
151
+ lines: doctorReportLines(report),
152
+ });
153
+ }
154
+ if (!report.ready) {
155
+ cli.setExitCode(EXIT_CODES.CONFIGURATION_ERROR);
156
+ }
157
+ },
158
+ });
159
+ }
160
+ //# sourceMappingURL=doctor-command.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"doctor-command.js","sourceRoot":"","sources":["../src/doctor-command.ts"],"names":[],"mappings":"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"}