@pipeline-builder/pipeline-manager 0.0.1

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 (222) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +145 -0
  3. package/cdk.json +91 -0
  4. package/config.yml +94 -0
  5. package/dist/agent/ai.d.ts +48 -0
  6. package/dist/agent/ai.d.ts.map +1 -0
  7. package/dist/agent/ai.js +149 -0
  8. package/dist/agent/bootstrap.d.ts +39 -0
  9. package/dist/agent/bootstrap.d.ts.map +1 -0
  10. package/dist/agent/bootstrap.js +66 -0
  11. package/dist/agent/env-file.d.ts +17 -0
  12. package/dist/agent/env-file.d.ts.map +1 -0
  13. package/dist/agent/env-file.js +47 -0
  14. package/dist/agent/executor.d.ts +30 -0
  15. package/dist/agent/executor.d.ts.map +1 -0
  16. package/dist/agent/executor.js +60 -0
  17. package/dist/agent/health.d.ts +43 -0
  18. package/dist/agent/health.d.ts.map +1 -0
  19. package/dist/agent/health.js +109 -0
  20. package/dist/agent/ports.d.ts +26 -0
  21. package/dist/agent/ports.d.ts.map +1 -0
  22. package/dist/agent/ports.js +132 -0
  23. package/dist/agent/post-steps.d.ts +61 -0
  24. package/dist/agent/post-steps.d.ts.map +1 -0
  25. package/dist/agent/post-steps.js +131 -0
  26. package/dist/agent/prereqs.d.ts +26 -0
  27. package/dist/agent/prereqs.d.ts.map +1 -0
  28. package/dist/agent/prereqs.js +134 -0
  29. package/dist/agent/targets.d.ts +128 -0
  30. package/dist/agent/targets.d.ts.map +1 -0
  31. package/dist/agent/targets.js +265 -0
  32. package/dist/agent/tools.d.ts +13 -0
  33. package/dist/agent/tools.d.ts.map +1 -0
  34. package/dist/agent/tools.js +75 -0
  35. package/dist/agent/troubleshoot.d.ts +30 -0
  36. package/dist/agent/troubleshoot.d.ts.map +1 -0
  37. package/dist/agent/troubleshoot.js +87 -0
  38. package/dist/boilerplate.d.ts +3 -0
  39. package/dist/boilerplate.d.ts.map +1 -0
  40. package/dist/boilerplate.js +54 -0
  41. package/dist/cdk.json +91 -0
  42. package/dist/cli.d.ts +62 -0
  43. package/dist/cli.d.ts.map +1 -0
  44. package/dist/cli.js +463 -0
  45. package/dist/commands/audit-stacks.d.ts +24 -0
  46. package/dist/commands/audit-stacks.d.ts.map +1 -0
  47. package/dist/commands/audit-stacks.js +182 -0
  48. package/dist/commands/audit-tokens.d.ts +21 -0
  49. package/dist/commands/audit-tokens.d.ts.map +1 -0
  50. package/dist/commands/audit-tokens.js +153 -0
  51. package/dist/commands/bootstrap.d.ts +11 -0
  52. package/dist/commands/bootstrap.d.ts.map +1 -0
  53. package/dist/commands/bootstrap.js +159 -0
  54. package/dist/commands/create-pat.d.ts +27 -0
  55. package/dist/commands/create-pat.d.ts.map +1 -0
  56. package/dist/commands/create-pat.js +194 -0
  57. package/dist/commands/create-pipeline.d.ts +12 -0
  58. package/dist/commands/create-pipeline.d.ts.map +1 -0
  59. package/dist/commands/create-pipeline.js +315 -0
  60. package/dist/commands/deploy.d.ts +15 -0
  61. package/dist/commands/deploy.d.ts.map +1 -0
  62. package/dist/commands/deploy.js +157 -0
  63. package/dist/commands/entity-get.d.ts +51 -0
  64. package/dist/commands/entity-get.d.ts.map +1 -0
  65. package/dist/commands/entity-get.js +69 -0
  66. package/dist/commands/entity-list.d.ts +39 -0
  67. package/dist/commands/entity-list.d.ts.map +1 -0
  68. package/dist/commands/entity-list.js +88 -0
  69. package/dist/commands/get-pipeline.d.ts +13 -0
  70. package/dist/commands/get-pipeline.d.ts.map +1 -0
  71. package/dist/commands/get-pipeline.js +49 -0
  72. package/dist/commands/get-plugin.d.ts +13 -0
  73. package/dist/commands/get-plugin.d.ts.map +1 -0
  74. package/dist/commands/get-plugin.js +51 -0
  75. package/dist/commands/instantiate-template.d.ts +20 -0
  76. package/dist/commands/instantiate-template.d.ts.map +1 -0
  77. package/dist/commands/instantiate-template.js +274 -0
  78. package/dist/commands/list-pipelines.d.ts +20 -0
  79. package/dist/commands/list-pipelines.d.ts.map +1 -0
  80. package/dist/commands/list-pipelines.js +99 -0
  81. package/dist/commands/list-plugins.d.ts +20 -0
  82. package/dist/commands/list-plugins.d.ts.map +1 -0
  83. package/dist/commands/list-plugins.js +92 -0
  84. package/dist/commands/login.d.ts +21 -0
  85. package/dist/commands/login.d.ts.map +1 -0
  86. package/dist/commands/login.js +174 -0
  87. package/dist/commands/new-plugin.d.ts +12 -0
  88. package/dist/commands/new-plugin.d.ts.map +1 -0
  89. package/dist/commands/new-plugin.js +129 -0
  90. package/dist/commands/org-export.d.ts +25 -0
  91. package/dist/commands/org-export.d.ts.map +1 -0
  92. package/dist/commands/org-export.js +100 -0
  93. package/dist/commands/provision.d.ts +122 -0
  94. package/dist/commands/provision.d.ts.map +1 -0
  95. package/dist/commands/provision.js +867 -0
  96. package/dist/commands/redrive-events.d.ts +24 -0
  97. package/dist/commands/redrive-events.d.ts.map +1 -0
  98. package/dist/commands/redrive-events.js +127 -0
  99. package/dist/commands/register.d.ts +38 -0
  100. package/dist/commands/register.d.ts.map +1 -0
  101. package/dist/commands/register.js +160 -0
  102. package/dist/commands/setup-events.d.ts +15 -0
  103. package/dist/commands/setup-events.d.ts.map +1 -0
  104. package/dist/commands/setup-events.js +168 -0
  105. package/dist/commands/status.d.ts +11 -0
  106. package/dist/commands/status.d.ts.map +1 -0
  107. package/dist/commands/status.js +81 -0
  108. package/dist/commands/store-token.d.ts +26 -0
  109. package/dist/commands/store-token.d.ts.map +1 -0
  110. package/dist/commands/store-token.js +305 -0
  111. package/dist/commands/synth.d.ts +21 -0
  112. package/dist/commands/synth.d.ts.map +1 -0
  113. package/dist/commands/synth.js +158 -0
  114. package/dist/commands/upload-plugin.d.ts +21 -0
  115. package/dist/commands/upload-plugin.d.ts.map +1 -0
  116. package/dist/commands/upload-plugin.js +275 -0
  117. package/dist/commands/validate-plugin.d.ts +12 -0
  118. package/dist/commands/validate-plugin.d.ts.map +1 -0
  119. package/dist/commands/validate-plugin.js +175 -0
  120. package/dist/commands/validate-templates.d.ts +11 -0
  121. package/dist/commands/validate-templates.d.ts.map +1 -0
  122. package/dist/commands/validate-templates.js +116 -0
  123. package/dist/commands/version.d.ts +12 -0
  124. package/dist/commands/version.d.ts.map +1 -0
  125. package/dist/commands/version.js +216 -0
  126. package/dist/config/cli.constants.d.ts +116 -0
  127. package/dist/config/cli.constants.d.ts.map +1 -0
  128. package/dist/config/cli.constants.js +194 -0
  129. package/dist/config.yml +94 -0
  130. package/dist/lambda/token-renew-handler.d.ts +2 -0
  131. package/dist/lambda/token-renew-handler.d.ts.map +1 -0
  132. package/dist/lambda/token-renew-handler.js +125 -0
  133. package/dist/templates/events-stack.json +174 -0
  134. package/dist/templates/token-renew-stack.json +122 -0
  135. package/dist/types/config.d.ts +53 -0
  136. package/dist/types/config.d.ts.map +1 -0
  137. package/dist/types/config.js +4 -0
  138. package/dist/types/error.d.ts +61 -0
  139. package/dist/types/error.d.ts.map +1 -0
  140. package/dist/types/error.js +35 -0
  141. package/dist/types/index.d.ts +9 -0
  142. package/dist/types/index.d.ts.map +1 -0
  143. package/dist/types/index.js +11 -0
  144. package/dist/types/pipeline-template.d.ts +48 -0
  145. package/dist/types/pipeline-template.d.ts.map +1 -0
  146. package/dist/types/pipeline-template.js +4 -0
  147. package/dist/types/pipeline.d.ts +121 -0
  148. package/dist/types/pipeline.d.ts.map +1 -0
  149. package/dist/types/pipeline.js +4 -0
  150. package/dist/types/plugin.d.ts +135 -0
  151. package/dist/types/plugin.d.ts.map +1 -0
  152. package/dist/types/plugin.js +4 -0
  153. package/dist/utils/api-client.d.ts +26 -0
  154. package/dist/utils/api-client.d.ts.map +1 -0
  155. package/dist/utils/api-client.js +151 -0
  156. package/dist/utils/audit-log.d.ts +8 -0
  157. package/dist/utils/audit-log.d.ts.map +1 -0
  158. package/dist/utils/audit-log.js +57 -0
  159. package/dist/utils/auth-guard.d.ts +20 -0
  160. package/dist/utils/auth-guard.d.ts.map +1 -0
  161. package/dist/utils/auth-guard.js +22 -0
  162. package/dist/utils/auth-utils.d.ts +68 -0
  163. package/dist/utils/auth-utils.d.ts.map +1 -0
  164. package/dist/utils/auth-utils.js +77 -0
  165. package/dist/utils/aws-env.d.ts +21 -0
  166. package/dist/utils/aws-env.d.ts.map +1 -0
  167. package/dist/utils/aws-env.js +34 -0
  168. package/dist/utils/aws-secrets.d.ts +48 -0
  169. package/dist/utils/aws-secrets.d.ts.map +1 -0
  170. package/dist/utils/aws-secrets.js +115 -0
  171. package/dist/utils/banner.d.ts +19 -0
  172. package/dist/utils/banner.d.ts.map +1 -0
  173. package/dist/utils/banner.js +52 -0
  174. package/dist/utils/cdk-utils.d.ts +74 -0
  175. package/dist/utils/cdk-utils.d.ts.map +1 -0
  176. package/dist/utils/cdk-utils.js +152 -0
  177. package/dist/utils/command-utils.d.ts +75 -0
  178. package/dist/utils/command-utils.d.ts.map +1 -0
  179. package/dist/utils/command-utils.js +155 -0
  180. package/dist/utils/config-loader.d.ts +33 -0
  181. package/dist/utils/config-loader.d.ts.map +1 -0
  182. package/dist/utils/config-loader.js +154 -0
  183. package/dist/utils/cron.d.ts +28 -0
  184. package/dist/utils/cron.d.ts.map +1 -0
  185. package/dist/utils/cron.js +119 -0
  186. package/dist/utils/deploy-runner.d.ts +38 -0
  187. package/dist/utils/deploy-runner.d.ts.map +1 -0
  188. package/dist/utils/deploy-runner.js +112 -0
  189. package/dist/utils/error-handler.d.ts +29 -0
  190. package/dist/utils/error-handler.d.ts.map +1 -0
  191. package/dist/utils/error-handler.js +302 -0
  192. package/dist/utils/list-command-utils.d.ts +23 -0
  193. package/dist/utils/list-command-utils.d.ts.map +1 -0
  194. package/dist/utils/list-command-utils.js +57 -0
  195. package/dist/utils/output-utils.d.ts +40 -0
  196. package/dist/utils/output-utils.d.ts.map +1 -0
  197. package/dist/utils/output-utils.js +250 -0
  198. package/dist/utils/pipeline-config.d.ts +33 -0
  199. package/dist/utils/pipeline-config.d.ts.map +1 -0
  200. package/dist/utils/pipeline-config.js +55 -0
  201. package/dist/utils/platform-secret.d.ts +27 -0
  202. package/dist/utils/platform-secret.d.ts.map +1 -0
  203. package/dist/utils/platform-secret.js +92 -0
  204. package/dist/utils/plugin-resolver.d.ts +17 -0
  205. package/dist/utils/plugin-resolver.d.ts.map +1 -0
  206. package/dist/utils/plugin-resolver.js +101 -0
  207. package/dist/utils/preflight.d.ts +26 -0
  208. package/dist/utils/preflight.d.ts.map +1 -0
  209. package/dist/utils/preflight.js +59 -0
  210. package/dist/utils/rate-limiter.d.ts +15 -0
  211. package/dist/utils/rate-limiter.d.ts.map +1 -0
  212. package/dist/utils/rate-limiter.js +164 -0
  213. package/dist/utils/registry.d.ts +86 -0
  214. package/dist/utils/registry.d.ts.map +1 -0
  215. package/dist/utils/registry.js +140 -0
  216. package/dist/utils/response-utils.d.ts +21 -0
  217. package/dist/utils/response-utils.d.ts.map +1 -0
  218. package/dist/utils/response-utils.js +70 -0
  219. package/dist/utils/tls.d.ts +52 -0
  220. package/dist/utils/tls.d.ts.map +1 -0
  221. package/dist/utils/tls.js +88 -0
  222. package/package.json +181 -0
@@ -0,0 +1,101 @@
1
+ // Copyright 2026 Pipeline Builder Contributors
2
+ // SPDX-License-Identifier: Apache-2.0
3
+ import { ApiClient } from './api-client.js';
4
+ import { printWarning } from './output-utils.js';
5
+ /**
6
+ * Cache key matching `PluginLookup.normalize()`: explicit alias when set,
7
+ * otherwise `${name}-alias`. Must stay in sync with the consumers in
8
+ * pipeline-core/plugin-lookup.ts and pipeline-builder.ts that read this map.
9
+ */
10
+ function cacheKey(ref) {
11
+ return ref.alias || `${ref.name}-alias`;
12
+ }
13
+ /**
14
+ * Walk the pipeline props tree to collect every plugin reference. Plugins
15
+ * appear at `synth.plugin` and at `stages[].steps[].plugin`. Entries are
16
+ * de-duplicated by `cacheKey()` — the same key `PluginLookup.plugin()` uses,
17
+ * so the resolved-plugins map lookups match downstream.
18
+ */
19
+ function collectPluginRefs(props) {
20
+ const refs = [];
21
+ const seen = new Set();
22
+ const push = (raw) => {
23
+ if (!raw || typeof raw !== 'object')
24
+ return;
25
+ const r = raw;
26
+ if (!r.name)
27
+ return;
28
+ const key = cacheKey(r);
29
+ if (seen.has(key))
30
+ return;
31
+ seen.add(key);
32
+ refs.push(r);
33
+ };
34
+ const synth = props.synth;
35
+ push(synth?.plugin);
36
+ const stages = props.stages;
37
+ for (const stage of stages ?? []) {
38
+ for (const step of stage.steps ?? []) {
39
+ push(step.plugin);
40
+ }
41
+ }
42
+ return refs;
43
+ }
44
+ /**
45
+ * Pre-resolve plugins by calling the same `POST /api/plugins/lookup` endpoint
46
+ * the deploy-time custom resource Lambda uses. Returning the full Plugin
47
+ * record at synth time is what allows the resulting CFN template to ship
48
+ * with real CodeBuild image URIs (`<host>/<ns>/<name>:<version>`) rather than
49
+ * the `aws/codebuild/standard:7.0` fallback that the synth-time token path
50
+ * forces.
51
+ *
52
+ * Failures are non-fatal: a missing plugin or unreachable API logs a warning
53
+ * and falls through to the deploy-time custom resource path so partial
54
+ * platform outages don't block synth/deploy.
55
+ *
56
+ * Keyed by `alias || name` to match `PluginLookup.plugin()`.
57
+ */
58
+ export async function resolvePluginsForProps(client, props) {
59
+ const refs = collectPluginRefs(props);
60
+ if (refs.length === 0)
61
+ return {};
62
+ const resolved = {};
63
+ await Promise.all(refs.map(async (ref) => {
64
+ const key = cacheKey(ref);
65
+ // The plugin NAME lives on the ref (a sibling of `filter`), NOT inside the
66
+ // filter object — but the lookup matches on the filter. A filter without
67
+ // `name` (e.g. `{version, visibility, isActive, isDefault}`) matches ANY
68
+ // plugin with those attributes, and the endpoint returns an arbitrary one
69
+ // (seen: `dockerfile-multi-provider`). That made the synth and every step
70
+ // resolve to the WRONG plugin. So when the filter omits `name`, fall back to
71
+ // the ref's plugin name; an explicit filter `name` still takes precedence.
72
+ const filter = { name: ref.name, ...(ref.filter ?? { isActive: true, isDefault: true }) };
73
+ try {
74
+ const res = await client.post('/api/plugins/lookup', { filter });
75
+ // Unwrap the plugin record from whatever envelope the response middleware
76
+ // applied. The platform's standard success envelope is
77
+ // `{ success, statusCode, data: { plugin: Plugin } }` (note the DOUBLE
78
+ // nesting: data.plugin), but tolerate `{ data: Plugin }`, `{ plugin }`,
79
+ // and a bare Plugin too. Only stopping at `res.data` (which is
80
+ // `{ plugin: ... }`) made `.name` undefined → every lookup fell back to
81
+ // deploy-time resolution even though the catalog had the plugin.
82
+ const data = res.data;
83
+ const plugin = data?.plugin // { data: { plugin } }
84
+ ?? res.plugin // { plugin }
85
+ ?? data // { data: Plugin }
86
+ ?? res; // bare Plugin
87
+ if (plugin && typeof plugin === 'object' && plugin.name) {
88
+ resolved[key] = plugin;
89
+ }
90
+ else {
91
+ printWarning(`Plugin "${ref.name}" lookup returned no record — falling back to deploy-time resolution`);
92
+ }
93
+ }
94
+ catch (err) {
95
+ const msg = err instanceof Error ? err.message : String(err);
96
+ printWarning(`Plugin "${ref.name}" pre-resolution failed (${msg}) — falling back to deploy-time resolution`);
97
+ }
98
+ }));
99
+ return resolved;
100
+ }
101
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicGx1Z2luLXJlc29sdmVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL3V0aWxzL3BsdWdpbi1yZXNvbHZlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSwrQ0FBK0M7QUFDL0Msc0NBQXNDO0FBRXRDLE9BQU8sRUFBRSxTQUFTLEVBQUUsTUFBTSxpQkFBaUIsQ0FBQztBQUM1QyxPQUFPLEVBQUUsWUFBWSxFQUFFLE1BQU0sbUJBQW1CLENBQUM7QUFTakQ7Ozs7R0FJRztBQUNILFNBQVMsUUFBUSxDQUFDLEdBQWM7SUFDOUIsT0FBTyxHQUFHLENBQUMsS0FBSyxJQUFJLEdBQUcsR0FBRyxDQUFDLElBQUksUUFBUSxDQUFDO0FBQzFDLENBQUM7QUFFRDs7Ozs7R0FLRztBQUNILFNBQVMsaUJBQWlCLENBQUMsS0FBOEI7SUFDdkQsTUFBTSxJQUFJLEdBQWdCLEVBQUUsQ0FBQztJQUM3QixNQUFNLElBQUksR0FBRyxJQUFJLEdBQUcsRUFBVSxDQUFDO0lBRS9CLE1BQU0sSUFBSSxHQUFHLENBQUMsR0FBWSxFQUFRLEVBQUU7UUFDbEMsSUFBSSxDQUFDLEdBQUcsSUFBSSxPQUFPLEdBQUcsS0FBSyxRQUFRO1lBQUUsT0FBTztRQUM1QyxNQUFNLENBQUMsR0FBRyxHQUFnQixDQUFDO1FBQzNCLElBQUksQ0FBQyxDQUFDLENBQUMsSUFBSTtZQUFFLE9BQU87UUFDcEIsTUFBTSxHQUFHLEdBQUcsUUFBUSxDQUFDLENBQUMsQ0FBQyxDQUFDO1FBQ3hCLElBQUksSUFBSSxDQUFDLEdBQUcsQ0FBQyxHQUFHLENBQUM7WUFBRSxPQUFPO1FBQzFCLElBQUksQ0FBQyxHQUFHLENBQUMsR0FBRyxDQUFDLENBQUM7UUFDZCxJQUFJLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxDQUFDO0lBQ2YsQ0FBQyxDQUFDO0lBRUYsTUFBTSxLQUFLLEdBQUcsS0FBSyxDQUFDLEtBQXlDLENBQUM7SUFDOUQsSUFBSSxDQUFDLEtBQUssRUFBRSxNQUFNLENBQUMsQ0FBQztJQUVwQixNQUFNLE1BQU0sR0FBRyxLQUFLLENBQUMsTUFBb0UsQ0FBQztJQUMxRixLQUFLLE1BQU0sS0FBSyxJQUFJLE1BQU0sSUFBSSxFQUFFLEVBQUUsQ0FBQztRQUNqQyxLQUFLLE1BQU0sSUFBSSxJQUFJLEtBQUssQ0FBQyxLQUFLLElBQUksRUFBRSxFQUFFLENBQUM7WUFDckMsSUFBSSxDQUFDLElBQUksQ0FBQyxNQUFNLENBQUMsQ0FBQztRQUNwQixDQUFDO0lBQ0gsQ0FBQztJQUVELE9BQU8sSUFBSSxDQUFDO0FBQ2QsQ0FBQztBQUVEOzs7Ozs7Ozs7Ozs7O0dBYUc7QUFDSCxNQUFNLENBQUMsS0FBSyxVQUFVLHNCQUFzQixDQUMxQyxNQUFpQixFQUNqQixLQUE4QjtJQUU5QixNQUFNLElBQUksR0FBRyxpQkFBaUIsQ0FBQyxLQUFLLENBQUMsQ0FBQztJQUN0QyxJQUFJLElBQUksQ0FBQyxNQUFNLEtBQUssQ0FBQztRQUFFLE9BQU8sRUFBRSxDQUFDO0lBRWpDLE1BQU0sUUFBUSxHQUE0QixFQUFFLENBQUM7SUFFN0MsTUFBTSxPQUFPLENBQUMsR0FBRyxDQUFDLElBQUksQ0FBQyxHQUFHLENBQUMsS0FBSyxFQUFFLEdBQUcsRUFBRSxFQUFFO1FBQ3ZDLE1BQU0sR0FBRyxHQUFHLFFBQVEsQ0FBQyxHQUFHLENBQUMsQ0FBQztRQUMxQiwyRUFBMkU7UUFDM0UseUVBQXlFO1FBQ3pFLHlFQUF5RTtRQUN6RSwwRUFBMEU7UUFDMUUsMEVBQTBFO1FBQzFFLDZFQUE2RTtRQUM3RSwyRUFBMkU7UUFDM0UsTUFBTSxNQUFNLEdBQUcsRUFBRSxJQUFJLEVBQUUsR0FBRyxDQUFDLElBQUksRUFBRSxHQUFHLENBQUMsR0FBRyxDQUFDLE1BQU0sSUFBSSxFQUFFLFFBQVEsRUFBRSxJQUFJLEVBQUUsU0FBUyxFQUFFLElBQUksRUFBRSxDQUFDLEVBQUUsQ0FBQztRQUMxRixJQUFJLENBQUM7WUFDSCxNQUFNLEdBQUcsR0FBRyxNQUFNLE1BQU0sQ0FBQyxJQUFJLENBQVUscUJBQXFCLEVBQUUsRUFBRSxNQUFNLEVBQUUsQ0FBQyxDQUFDO1lBQzFFLDBFQUEwRTtZQUMxRSx1REFBdUQ7WUFDdkQsdUVBQXVFO1lBQ3ZFLHdFQUF3RTtZQUN4RSwrREFBK0Q7WUFDL0Qsd0VBQXdFO1lBQ3hFLGlFQUFpRTtZQUNqRSxNQUFNLElBQUksR0FBSSxHQUEwQixDQUFDLElBQUksQ0FBQztZQUM5QyxNQUFNLE1BQU0sR0FDVCxJQUF5QyxFQUFFLE1BQU0sQ0FBQyx1QkFBdUI7bUJBQ3RFLEdBQTRCLENBQUMsTUFBTSxDQUFDLGFBQWE7bUJBQ2xELElBQUksQ0FBQyxtQkFBbUI7bUJBQ3hCLEdBQUcsQ0FBQyxDQUFDLGNBQWM7WUFDeEIsSUFBSSxNQUFNLElBQUksT0FBTyxNQUFNLEtBQUssUUFBUSxJQUFLLE1BQTRCLENBQUMsSUFBSSxFQUFFLENBQUM7Z0JBQy9FLFFBQVEsQ0FBQyxHQUFHLENBQUMsR0FBRyxNQUFNLENBQUM7WUFDekIsQ0FBQztpQkFBTSxDQUFDO2dCQUNOLFlBQVksQ0FBQyxXQUFXLEdBQUcsQ0FBQyxJQUFJLHNFQUFzRSxDQUFDLENBQUM7WUFDMUcsQ0FBQztRQUNILENBQUM7UUFBQyxPQUFPLEdBQUcsRUFBRSxDQUFDO1lBQ2IsTUFBTSxHQUFHLEdBQUcsR0FBRyxZQUFZLEtBQUssQ0FBQyxDQUFDLENBQUMsR0FBRyxDQUFDLE9BQU8sQ0FBQyxDQUFDLENBQUMsTUFBTSxDQUFDLEdBQUcsQ0FBQyxDQUFDO1lBQzdELFlBQVksQ0FBQyxXQUFXLEdBQUcsQ0FBQyxJQUFJLDRCQUE0QixHQUFHLDRDQUE0QyxDQUFDLENBQUM7UUFDL0csQ0FBQztJQUNILENBQUMsQ0FBQyxDQUFDLENBQUM7SUFFSixPQUFPLFFBQVEsQ0FBQztBQUNsQixDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLy8gQ29weXJpZ2h0IDIwMjYgUGlwZWxpbmUgQnVpbGRlciBDb250cmlidXRvcnNcbi8vIFNQRFgtTGljZW5zZS1JZGVudGlmaWVyOiBBcGFjaGUtMi4wXG5cbmltcG9ydCB7IEFwaUNsaWVudCB9IGZyb20gJy4vYXBpLWNsaWVudC5qcyc7XG5pbXBvcnQgeyBwcmludFdhcm5pbmcgfSBmcm9tICcuL291dHB1dC11dGlscy5qcyc7XG5cbi8qKiBNaW5pbWFsIHBsdWdpbiByZWZlcmVuY2Ugc2hhcGUg4oCUIG1hdGNoZXMgUGx1Z2luT3B0aW9ucyBmcm9tIHBpcGVsaW5lLWNvcmUuICovXG5pbnRlcmZhY2UgUGx1Z2luUmVmIHtcbiAgbmFtZTogc3RyaW5nO1xuICBhbGlhcz86IHN0cmluZztcbiAgZmlsdGVyPzogUmVjb3JkPHN0cmluZywgdW5rbm93bj47XG59XG5cbi8qKlxuICogQ2FjaGUga2V5IG1hdGNoaW5nIGBQbHVnaW5Mb29rdXAubm9ybWFsaXplKClgOiBleHBsaWNpdCBhbGlhcyB3aGVuIHNldCxcbiAqIG90aGVyd2lzZSBgJHtuYW1lfS1hbGlhc2AuIE11c3Qgc3RheSBpbiBzeW5jIHdpdGggdGhlIGNvbnN1bWVycyBpblxuICogcGlwZWxpbmUtY29yZS9wbHVnaW4tbG9va3VwLnRzIGFuZCBwaXBlbGluZS1idWlsZGVyLnRzIHRoYXQgcmVhZCB0aGlzIG1hcC5cbiAqL1xuZnVuY3Rpb24gY2FjaGVLZXkocmVmOiBQbHVnaW5SZWYpOiBzdHJpbmcge1xuICByZXR1cm4gcmVmLmFsaWFzIHx8IGAke3JlZi5uYW1lfS1hbGlhc2A7XG59XG5cbi8qKlxuICogV2FsayB0aGUgcGlwZWxpbmUgcHJvcHMgdHJlZSB0byBjb2xsZWN0IGV2ZXJ5IHBsdWdpbiByZWZlcmVuY2UuIFBsdWdpbnNcbiAqIGFwcGVhciBhdCBgc3ludGgucGx1Z2luYCBhbmQgYXQgYHN0YWdlc1tdLnN0ZXBzW10ucGx1Z2luYC4gRW50cmllcyBhcmVcbiAqIGRlLWR1cGxpY2F0ZWQgYnkgYGNhY2hlS2V5KClgIOKAlCB0aGUgc2FtZSBrZXkgYFBsdWdpbkxvb2t1cC5wbHVnaW4oKWAgdXNlcyxcbiAqIHNvIHRoZSByZXNvbHZlZC1wbHVnaW5zIG1hcCBsb29rdXBzIG1hdGNoIGRvd25zdHJlYW0uXG4gKi9cbmZ1bmN0aW9uIGNvbGxlY3RQbHVnaW5SZWZzKHByb3BzOiBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPik6IFBsdWdpblJlZltdIHtcbiAgY29uc3QgcmVmczogUGx1Z2luUmVmW10gPSBbXTtcbiAgY29uc3Qgc2VlbiA9IG5ldyBTZXQ8c3RyaW5nPigpO1xuXG4gIGNvbnN0IHB1c2ggPSAocmF3OiB1bmtub3duKTogdm9pZCA9PiB7XG4gICAgaWYgKCFyYXcgfHwgdHlwZW9mIHJhdyAhPT0gJ29iamVjdCcpIHJldHVybjtcbiAgICBjb25zdCByID0gcmF3IGFzIFBsdWdpblJlZjtcbiAgICBpZiAoIXIubmFtZSkgcmV0dXJuO1xuICAgIGNvbnN0IGtleSA9IGNhY2hlS2V5KHIpO1xuICAgIGlmIChzZWVuLmhhcyhrZXkpKSByZXR1cm47XG4gICAgc2Vlbi5hZGQoa2V5KTtcbiAgICByZWZzLnB1c2gocik7XG4gIH07XG5cbiAgY29uc3Qgc3ludGggPSBwcm9wcy5zeW50aCBhcyB7IHBsdWdpbj86IHVua25vd24gfSB8IHVuZGVmaW5lZDtcbiAgcHVzaChzeW50aD8ucGx1Z2luKTtcblxuICBjb25zdCBzdGFnZXMgPSBwcm9wcy5zdGFnZXMgYXMgQXJyYXk8eyBzdGVwcz86IEFycmF5PHsgcGx1Z2luPzogdW5rbm93biB9PiB9PiB8IHVuZGVmaW5lZDtcbiAgZm9yIChjb25zdCBzdGFnZSBvZiBzdGFnZXMgPz8gW10pIHtcbiAgICBmb3IgKGNvbnN0IHN0ZXAgb2Ygc3RhZ2Uuc3RlcHMgPz8gW10pIHtcbiAgICAgIHB1c2goc3RlcC5wbHVnaW4pO1xuICAgIH1cbiAgfVxuXG4gIHJldHVybiByZWZzO1xufVxuXG4vKipcbiAqIFByZS1yZXNvbHZlIHBsdWdpbnMgYnkgY2FsbGluZyB0aGUgc2FtZSBgUE9TVCAvYXBpL3BsdWdpbnMvbG9va3VwYCBlbmRwb2ludFxuICogdGhlIGRlcGxveS10aW1lIGN1c3RvbSByZXNvdXJjZSBMYW1iZGEgdXNlcy4gUmV0dXJuaW5nIHRoZSBmdWxsIFBsdWdpblxuICogcmVjb3JkIGF0IHN5bnRoIHRpbWUgaXMgd2hhdCBhbGxvd3MgdGhlIHJlc3VsdGluZyBDRk4gdGVtcGxhdGUgdG8gc2hpcFxuICogd2l0aCByZWFsIENvZGVCdWlsZCBpbWFnZSBVUklzIChgPGhvc3Q+Lzxucz4vPG5hbWU+Ojx2ZXJzaW9uPmApIHJhdGhlciB0aGFuXG4gKiB0aGUgYGF3cy9jb2RlYnVpbGQvc3RhbmRhcmQ6Ny4wYCBmYWxsYmFjayB0aGF0IHRoZSBzeW50aC10aW1lIHRva2VuIHBhdGhcbiAqIGZvcmNlcy5cbiAqXG4gKiBGYWlsdXJlcyBhcmUgbm9uLWZhdGFsOiBhIG1pc3NpbmcgcGx1Z2luIG9yIHVucmVhY2hhYmxlIEFQSSBsb2dzIGEgd2FybmluZ1xuICogYW5kIGZhbGxzIHRocm91Z2ggdG8gdGhlIGRlcGxveS10aW1lIGN1c3RvbSByZXNvdXJjZSBwYXRoIHNvIHBhcnRpYWxcbiAqIHBsYXRmb3JtIG91dGFnZXMgZG9uJ3QgYmxvY2sgc3ludGgvZGVwbG95LlxuICpcbiAqIEtleWVkIGJ5IGBhbGlhcyB8fCBuYW1lYCB0byBtYXRjaCBgUGx1Z2luTG9va3VwLnBsdWdpbigpYC5cbiAqL1xuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIHJlc29sdmVQbHVnaW5zRm9yUHJvcHMoXG4gIGNsaWVudDogQXBpQ2xpZW50LFxuICBwcm9wczogUmVjb3JkPHN0cmluZywgdW5rbm93bj4sXG4pOiBQcm9taXNlPFJlY29yZDxzdHJpbmcsIHVua25vd24+PiB7XG4gIGNvbnN0IHJlZnMgPSBjb2xsZWN0UGx1Z2luUmVmcyhwcm9wcyk7XG4gIGlmIChyZWZzLmxlbmd0aCA9PT0gMCkgcmV0dXJuIHt9O1xuXG4gIGNvbnN0IHJlc29sdmVkOiBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPiA9IHt9O1xuXG4gIGF3YWl0IFByb21pc2UuYWxsKHJlZnMubWFwKGFzeW5jIChyZWYpID0+IHtcbiAgICBjb25zdCBrZXkgPSBjYWNoZUtleShyZWYpO1xuICAgIC8vIFRoZSBwbHVnaW4gTkFNRSBsaXZlcyBvbiB0aGUgcmVmIChhIHNpYmxpbmcgb2YgYGZpbHRlcmApLCBOT1QgaW5zaWRlIHRoZVxuICAgIC8vIGZpbHRlciBvYmplY3Qg4oCUIGJ1dCB0aGUgbG9va3VwIG1hdGNoZXMgb24gdGhlIGZpbHRlci4gQSBmaWx0ZXIgd2l0aG91dFxuICAgIC8vIGBuYW1lYCAoZS5nLiBge3ZlcnNpb24sIHZpc2liaWxpdHksIGlzQWN0aXZlLCBpc0RlZmF1bHR9YCkgbWF0Y2hlcyBBTllcbiAgICAvLyBwbHVnaW4gd2l0aCB0aG9zZSBhdHRyaWJ1dGVzLCBhbmQgdGhlIGVuZHBvaW50IHJldHVybnMgYW4gYXJiaXRyYXJ5IG9uZVxuICAgIC8vIChzZWVuOiBgZG9ja2VyZmlsZS1tdWx0aS1wcm92aWRlcmApLiBUaGF0IG1hZGUgdGhlIHN5bnRoIGFuZCBldmVyeSBzdGVwXG4gICAgLy8gcmVzb2x2ZSB0byB0aGUgV1JPTkcgcGx1Z2luLiBTbyB3aGVuIHRoZSBmaWx0ZXIgb21pdHMgYG5hbWVgLCBmYWxsIGJhY2sgdG9cbiAgICAvLyB0aGUgcmVmJ3MgcGx1Z2luIG5hbWU7IGFuIGV4cGxpY2l0IGZpbHRlciBgbmFtZWAgc3RpbGwgdGFrZXMgcHJlY2VkZW5jZS5cbiAgICBjb25zdCBmaWx0ZXIgPSB7IG5hbWU6IHJlZi5uYW1lLCAuLi4ocmVmLmZpbHRlciA/PyB7IGlzQWN0aXZlOiB0cnVlLCBpc0RlZmF1bHQ6IHRydWUgfSkgfTtcbiAgICB0cnkge1xuICAgICAgY29uc3QgcmVzID0gYXdhaXQgY2xpZW50LnBvc3Q8dW5rbm93bj4oJy9hcGkvcGx1Z2lucy9sb29rdXAnLCB7IGZpbHRlciB9KTtcbiAgICAgIC8vIFVud3JhcCB0aGUgcGx1Z2luIHJlY29yZCBmcm9tIHdoYXRldmVyIGVudmVsb3BlIHRoZSByZXNwb25zZSBtaWRkbGV3YXJlXG4gICAgICAvLyBhcHBsaWVkLiBUaGUgcGxhdGZvcm0ncyBzdGFuZGFyZCBzdWNjZXNzIGVudmVsb3BlIGlzXG4gICAgICAvLyBgeyBzdWNjZXNzLCBzdGF0dXNDb2RlLCBkYXRhOiB7IHBsdWdpbjogUGx1Z2luIH0gfWAgKG5vdGUgdGhlIERPVUJMRVxuICAgICAgLy8gbmVzdGluZzogZGF0YS5wbHVnaW4pLCBidXQgdG9sZXJhdGUgYHsgZGF0YTogUGx1Z2luIH1gLCBgeyBwbHVnaW4gfWAsXG4gICAgICAvLyBhbmQgYSBiYXJlIFBsdWdpbiB0b28uIE9ubHkgc3RvcHBpbmcgYXQgYHJlcy5kYXRhYCAod2hpY2ggaXNcbiAgICAgIC8vIGB7IHBsdWdpbjogLi4uIH1gKSBtYWRlIGAubmFtZWAgdW5kZWZpbmVkIOKGkiBldmVyeSBsb29rdXAgZmVsbCBiYWNrIHRvXG4gICAgICAvLyBkZXBsb3ktdGltZSByZXNvbHV0aW9uIGV2ZW4gdGhvdWdoIHRoZSBjYXRhbG9nIGhhZCB0aGUgcGx1Z2luLlxuICAgICAgY29uc3QgZGF0YSA9IChyZXMgYXMgeyBkYXRhPzogdW5rbm93biB9KS5kYXRhO1xuICAgICAgY29uc3QgcGx1Z2luID1cbiAgICAgICAgKGRhdGEgYXMgeyBwbHVnaW4/OiB1bmtub3duIH0gfCB1bmRlZmluZWQpPy5wbHVnaW4gLy8geyBkYXRhOiB7IHBsdWdpbiB9IH1cbiAgICAgICAgPz8gKHJlcyBhcyB7IHBsdWdpbj86IHVua25vd24gfSkucGx1Z2luIC8vIHsgcGx1Z2luIH1cbiAgICAgICAgPz8gZGF0YSAvLyB7IGRhdGE6IFBsdWdpbiB9XG4gICAgICAgID8/IHJlczsgLy8gYmFyZSBQbHVnaW5cbiAgICAgIGlmIChwbHVnaW4gJiYgdHlwZW9mIHBsdWdpbiA9PT0gJ29iamVjdCcgJiYgKHBsdWdpbiBhcyB7IG5hbWU/OiBzdHJpbmcgfSkubmFtZSkge1xuICAgICAgICByZXNvbHZlZFtrZXldID0gcGx1Z2luO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgcHJpbnRXYXJuaW5nKGBQbHVnaW4gXCIke3JlZi5uYW1lfVwiIGxvb2t1cCByZXR1cm5lZCBubyByZWNvcmQg4oCUIGZhbGxpbmcgYmFjayB0byBkZXBsb3ktdGltZSByZXNvbHV0aW9uYCk7XG4gICAgICB9XG4gICAgfSBjYXRjaCAoZXJyKSB7XG4gICAgICBjb25zdCBtc2cgPSBlcnIgaW5zdGFuY2VvZiBFcnJvciA/IGVyci5tZXNzYWdlIDogU3RyaW5nKGVycik7XG4gICAgICBwcmludFdhcm5pbmcoYFBsdWdpbiBcIiR7cmVmLm5hbWV9XCIgcHJlLXJlc29sdXRpb24gZmFpbGVkICgke21zZ30pIOKAlCBmYWxsaW5nIGJhY2sgdG8gZGVwbG95LXRpbWUgcmVzb2x1dGlvbmApO1xuICAgIH1cbiAgfSkpO1xuXG4gIHJldHVybiByZXNvbHZlZDtcbn1cbiJdfQ==
@@ -0,0 +1,26 @@
1
+ import { type Command } from 'commander';
2
+ export type LocalTool = 'cdk' | 'bundler';
3
+ /**
4
+ * Per-command local-tool requirements, checked by the CLI's `preAction` hook
5
+ * BEFORE a command's action runs — so a missing tool fails fast with an
6
+ * actionable message instead of deep inside a CDK bundle. Keyed by full command
7
+ * path (e.g. "pipeline deploy").
8
+ *
9
+ * Only `pipeline synth`/`deploy` shell out to cdk (+ esbuild/pnpm for the
10
+ * PluginLookup Lambda bundle). Every other command is API-only and requires
11
+ * nothing local, so it's absent here and never gated — as are version / help /
12
+ * completions. (`infra provision` runs its own target-specific prereq checks.)
13
+ */
14
+ export declare const COMMAND_TOOL_REQUIREMENTS: Record<string, LocalTool[]>;
15
+ /** Full space-joined command path for a Commander command, e.g. "pipeline deploy". */
16
+ export declare function commandPath(cmd: Command): string;
17
+ /** The local tools a command declares it needs (empty when it needs nothing). */
18
+ export declare function requiredToolsFor(cmd: Command): LocalTool[];
19
+ /**
20
+ * Preflight the local tools an about-to-run command declares. Reuses the tools'
21
+ * `ensureX` helpers (which print the actionable message + install hint on
22
+ * failure), then exits non-zero so the command body never runs. No-op for any
23
+ * command without requirements (API-only commands, version/help/completions).
24
+ */
25
+ export declare function preflightCommandTools(actionCommand: Command): void;
26
+ //# sourceMappingURL=preflight.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"preflight.d.ts","sourceRoot":"","sources":["../../src/utils/preflight.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,KAAK,OAAO,EAAE,MAAM,WAAW,CAAC;AAGzC,MAAM,MAAM,SAAS,GAAG,KAAK,GAAG,SAAS,CAAC;AAE1C;;;;;;;;;;GAUG;AACH,eAAO,MAAM,yBAAyB,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,EAAE,CAMjE,CAAC;AAEF,sFAAsF;AACtF,wBAAgB,WAAW,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,CAQhD;AAED,iFAAiF;AACjF,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,OAAO,GAAG,SAAS,EAAE,CAE1D;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,aAAa,EAAE,OAAO,GAAG,IAAI,CAWlE"}
@@ -0,0 +1,59 @@
1
+ // Copyright 2026 Pipeline Builder Contributors
2
+ // SPDX-License-Identifier: Apache-2.0
3
+ import {} from 'commander';
4
+ import { ensureBundlerAvailable, ensureCdkAvailable } from './cdk-utils.js';
5
+ /**
6
+ * Per-command local-tool requirements, checked by the CLI's `preAction` hook
7
+ * BEFORE a command's action runs — so a missing tool fails fast with an
8
+ * actionable message instead of deep inside a CDK bundle. Keyed by full command
9
+ * path (e.g. "pipeline deploy").
10
+ *
11
+ * Only `pipeline synth`/`deploy` shell out to cdk (+ esbuild/pnpm for the
12
+ * PluginLookup Lambda bundle). Every other command is API-only and requires
13
+ * nothing local, so it's absent here and never gated — as are version / help /
14
+ * completions. (`infra provision` runs its own target-specific prereq checks.)
15
+ */
16
+ export const COMMAND_TOOL_REQUIREMENTS = {
17
+ 'pipeline synth': ['cdk', 'bundler'],
18
+ 'pipeline deploy': ['cdk', 'bundler'],
19
+ // `infra bootstrap` runs `cdk bootstrap` (AWS env bootstrap) — cdk only; it
20
+ // synthesizes no Lambda, so no esbuild/pnpm bundler is needed.
21
+ 'infra bootstrap': ['cdk'],
22
+ };
23
+ /** Full space-joined command path for a Commander command, e.g. "pipeline deploy". */
24
+ export function commandPath(cmd) {
25
+ const parts = [];
26
+ let c = cmd;
27
+ while (c && c.parent) {
28
+ parts.unshift(c.name());
29
+ c = c.parent ?? undefined;
30
+ }
31
+ return parts.join(' ');
32
+ }
33
+ /** The local tools a command declares it needs (empty when it needs nothing). */
34
+ export function requiredToolsFor(cmd) {
35
+ return COMMAND_TOOL_REQUIREMENTS[commandPath(cmd)] ?? [];
36
+ }
37
+ /**
38
+ * Preflight the local tools an about-to-run command declares. Reuses the tools'
39
+ * `ensureX` helpers (which print the actionable message + install hint on
40
+ * failure), then exits non-zero so the command body never runs. No-op for any
41
+ * command without requirements (API-only commands, version/help/completions).
42
+ */
43
+ export function preflightCommandTools(actionCommand) {
44
+ const reqs = requiredToolsFor(actionCommand);
45
+ if (reqs.length === 0)
46
+ return;
47
+ try {
48
+ if (reqs.includes('cdk'))
49
+ ensureCdkAvailable();
50
+ if (reqs.includes('bundler'))
51
+ ensureBundlerAvailable();
52
+ }
53
+ catch {
54
+ // Message already printed by ensureX. Halt before the action runs so the
55
+ // command doesn't proceed with a broken toolchain.
56
+ process.exit(1);
57
+ }
58
+ }
59
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicHJlZmxpZ2h0LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL3V0aWxzL3ByZWZsaWdodC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSwrQ0FBK0M7QUFDL0Msc0NBQXNDO0FBRXRDLE9BQU8sRUFBZ0IsTUFBTSxXQUFXLENBQUM7QUFDekMsT0FBTyxFQUFFLHNCQUFzQixFQUFFLGtCQUFrQixFQUFFLE1BQU0sZ0JBQWdCLENBQUM7QUFJNUU7Ozs7Ozs7Ozs7R0FVRztBQUNILE1BQU0sQ0FBQyxNQUFNLHlCQUF5QixHQUFnQztJQUNwRSxnQkFBZ0IsRUFBRSxDQUFDLEtBQUssRUFBRSxTQUFTLENBQUM7SUFDcEMsaUJBQWlCLEVBQUUsQ0FBQyxLQUFLLEVBQUUsU0FBUyxDQUFDO0lBQ3JDLDRFQUE0RTtJQUM1RSwrREFBK0Q7SUFDL0QsaUJBQWlCLEVBQUUsQ0FBQyxLQUFLLENBQUM7Q0FDM0IsQ0FBQztBQUVGLHNGQUFzRjtBQUN0RixNQUFNLFVBQVUsV0FBVyxDQUFDLEdBQVk7SUFDdEMsTUFBTSxLQUFLLEdBQWEsRUFBRSxDQUFDO0lBQzNCLElBQUksQ0FBQyxHQUF3QixHQUFHLENBQUM7SUFDakMsT0FBTyxDQUFDLElBQUksQ0FBQyxDQUFDLE1BQU0sRUFBRSxDQUFDO1FBQ3JCLEtBQUssQ0FBQyxPQUFPLENBQUMsQ0FBQyxDQUFDLElBQUksRUFBRSxDQUFDLENBQUM7UUFDeEIsQ0FBQyxHQUFHLENBQUMsQ0FBQyxNQUFNLElBQUksU0FBUyxDQUFDO0lBQzVCLENBQUM7SUFDRCxPQUFPLEtBQUssQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLENBQUM7QUFDekIsQ0FBQztBQUVELGlGQUFpRjtBQUNqRixNQUFNLFVBQVUsZ0JBQWdCLENBQUMsR0FBWTtJQUMzQyxPQUFPLHlCQUF5QixDQUFDLFdBQVcsQ0FBQyxHQUFHLENBQUMsQ0FBQyxJQUFJLEVBQUUsQ0FBQztBQUMzRCxDQUFDO0FBRUQ7Ozs7O0dBS0c7QUFDSCxNQUFNLFVBQVUscUJBQXFCLENBQUMsYUFBc0I7SUFDMUQsTUFBTSxJQUFJLEdBQUcsZ0JBQWdCLENBQUMsYUFBYSxDQUFDLENBQUM7SUFDN0MsSUFBSSxJQUFJLENBQUMsTUFBTSxLQUFLLENBQUM7UUFBRSxPQUFPO0lBQzlCLElBQUksQ0FBQztRQUNILElBQUksSUFBSSxDQUFDLFFBQVEsQ0FBQyxLQUFLLENBQUM7WUFBRSxrQkFBa0IsRUFBRSxDQUFDO1FBQy9DLElBQUksSUFBSSxDQUFDLFFBQVEsQ0FBQyxTQUFTLENBQUM7WUFBRSxzQkFBc0IsRUFBRSxDQUFDO0lBQ3pELENBQUM7SUFBQyxNQUFNLENBQUM7UUFDUCx5RUFBeUU7UUFDekUsbURBQW1EO1FBQ25ELE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLENBQUM7SUFDbEIsQ0FBQztBQUNILENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvLyBDb3B5cmlnaHQgMjAyNiBQaXBlbGluZSBCdWlsZGVyIENvbnRyaWJ1dG9yc1xuLy8gU1BEWC1MaWNlbnNlLUlkZW50aWZpZXI6IEFwYWNoZS0yLjBcblxuaW1wb3J0IHsgdHlwZSBDb21tYW5kIH0gZnJvbSAnY29tbWFuZGVyJztcbmltcG9ydCB7IGVuc3VyZUJ1bmRsZXJBdmFpbGFibGUsIGVuc3VyZUNka0F2YWlsYWJsZSB9IGZyb20gJy4vY2RrLXV0aWxzLmpzJztcblxuZXhwb3J0IHR5cGUgTG9jYWxUb29sID0gJ2NkaycgfCAnYnVuZGxlcic7XG5cbi8qKlxuICogUGVyLWNvbW1hbmQgbG9jYWwtdG9vbCByZXF1aXJlbWVudHMsIGNoZWNrZWQgYnkgdGhlIENMSSdzIGBwcmVBY3Rpb25gIGhvb2tcbiAqIEJFRk9SRSBhIGNvbW1hbmQncyBhY3Rpb24gcnVucyDigJQgc28gYSBtaXNzaW5nIHRvb2wgZmFpbHMgZmFzdCB3aXRoIGFuXG4gKiBhY3Rpb25hYmxlIG1lc3NhZ2UgaW5zdGVhZCBvZiBkZWVwIGluc2lkZSBhIENESyBidW5kbGUuIEtleWVkIGJ5IGZ1bGwgY29tbWFuZFxuICogcGF0aCAoZS5nLiBcInBpcGVsaW5lIGRlcGxveVwiKS5cbiAqXG4gKiBPbmx5IGBwaXBlbGluZSBzeW50aGAvYGRlcGxveWAgc2hlbGwgb3V0IHRvIGNkayAoKyBlc2J1aWxkL3BucG0gZm9yIHRoZVxuICogUGx1Z2luTG9va3VwIExhbWJkYSBidW5kbGUpLiBFdmVyeSBvdGhlciBjb21tYW5kIGlzIEFQSS1vbmx5IGFuZCByZXF1aXJlc1xuICogbm90aGluZyBsb2NhbCwgc28gaXQncyBhYnNlbnQgaGVyZSBhbmQgbmV2ZXIgZ2F0ZWQg4oCUIGFzIGFyZSB2ZXJzaW9uIC8gaGVscCAvXG4gKiBjb21wbGV0aW9ucy4gKGBpbmZyYSBwcm92aXNpb25gIHJ1bnMgaXRzIG93biB0YXJnZXQtc3BlY2lmaWMgcHJlcmVxIGNoZWNrcy4pXG4gKi9cbmV4cG9ydCBjb25zdCBDT01NQU5EX1RPT0xfUkVRVUlSRU1FTlRTOiBSZWNvcmQ8c3RyaW5nLCBMb2NhbFRvb2xbXT4gPSB7XG4gICdwaXBlbGluZSBzeW50aCc6IFsnY2RrJywgJ2J1bmRsZXInXSxcbiAgJ3BpcGVsaW5lIGRlcGxveSc6IFsnY2RrJywgJ2J1bmRsZXInXSxcbiAgLy8gYGluZnJhIGJvb3RzdHJhcGAgcnVucyBgY2RrIGJvb3RzdHJhcGAgKEFXUyBlbnYgYm9vdHN0cmFwKSDigJQgY2RrIG9ubHk7IGl0XG4gIC8vIHN5bnRoZXNpemVzIG5vIExhbWJkYSwgc28gbm8gZXNidWlsZC9wbnBtIGJ1bmRsZXIgaXMgbmVlZGVkLlxuICAnaW5mcmEgYm9vdHN0cmFwJzogWydjZGsnXSxcbn07XG5cbi8qKiBGdWxsIHNwYWNlLWpvaW5lZCBjb21tYW5kIHBhdGggZm9yIGEgQ29tbWFuZGVyIGNvbW1hbmQsIGUuZy4gXCJwaXBlbGluZSBkZXBsb3lcIi4gKi9cbmV4cG9ydCBmdW5jdGlvbiBjb21tYW5kUGF0aChjbWQ6IENvbW1hbmQpOiBzdHJpbmcge1xuICBjb25zdCBwYXJ0czogc3RyaW5nW10gPSBbXTtcbiAgbGV0IGM6IENvbW1hbmQgfCB1bmRlZmluZWQgPSBjbWQ7XG4gIHdoaWxlIChjICYmIGMucGFyZW50KSB7XG4gICAgcGFydHMudW5zaGlmdChjLm5hbWUoKSk7XG4gICAgYyA9IGMucGFyZW50ID8/IHVuZGVmaW5lZDtcbiAgfVxuICByZXR1cm4gcGFydHMuam9pbignICcpO1xufVxuXG4vKiogVGhlIGxvY2FsIHRvb2xzIGEgY29tbWFuZCBkZWNsYXJlcyBpdCBuZWVkcyAoZW1wdHkgd2hlbiBpdCBuZWVkcyBub3RoaW5nKS4gKi9cbmV4cG9ydCBmdW5jdGlvbiByZXF1aXJlZFRvb2xzRm9yKGNtZDogQ29tbWFuZCk6IExvY2FsVG9vbFtdIHtcbiAgcmV0dXJuIENPTU1BTkRfVE9PTF9SRVFVSVJFTUVOVFNbY29tbWFuZFBhdGgoY21kKV0gPz8gW107XG59XG5cbi8qKlxuICogUHJlZmxpZ2h0IHRoZSBsb2NhbCB0b29scyBhbiBhYm91dC10by1ydW4gY29tbWFuZCBkZWNsYXJlcy4gUmV1c2VzIHRoZSB0b29scydcbiAqIGBlbnN1cmVYYCBoZWxwZXJzICh3aGljaCBwcmludCB0aGUgYWN0aW9uYWJsZSBtZXNzYWdlICsgaW5zdGFsbCBoaW50IG9uXG4gKiBmYWlsdXJlKSwgdGhlbiBleGl0cyBub24temVybyBzbyB0aGUgY29tbWFuZCBib2R5IG5ldmVyIHJ1bnMuIE5vLW9wIGZvciBhbnlcbiAqIGNvbW1hbmQgd2l0aG91dCByZXF1aXJlbWVudHMgKEFQSS1vbmx5IGNvbW1hbmRzLCB2ZXJzaW9uL2hlbHAvY29tcGxldGlvbnMpLlxuICovXG5leHBvcnQgZnVuY3Rpb24gcHJlZmxpZ2h0Q29tbWFuZFRvb2xzKGFjdGlvbkNvbW1hbmQ6IENvbW1hbmQpOiB2b2lkIHtcbiAgY29uc3QgcmVxcyA9IHJlcXVpcmVkVG9vbHNGb3IoYWN0aW9uQ29tbWFuZCk7XG4gIGlmIChyZXFzLmxlbmd0aCA9PT0gMCkgcmV0dXJuO1xuICB0cnkge1xuICAgIGlmIChyZXFzLmluY2x1ZGVzKCdjZGsnKSkgZW5zdXJlQ2RrQXZhaWxhYmxlKCk7XG4gICAgaWYgKHJlcXMuaW5jbHVkZXMoJ2J1bmRsZXInKSkgZW5zdXJlQnVuZGxlckF2YWlsYWJsZSgpO1xuICB9IGNhdGNoIHtcbiAgICAvLyBNZXNzYWdlIGFscmVhZHkgcHJpbnRlZCBieSBlbnN1cmVYLiBIYWx0IGJlZm9yZSB0aGUgYWN0aW9uIHJ1bnMgc28gdGhlXG4gICAgLy8gY29tbWFuZCBkb2Vzbid0IHByb2NlZWQgd2l0aCBhIGJyb2tlbiB0b29sY2hhaW4uXG4gICAgcHJvY2Vzcy5leGl0KDEpO1xuICB9XG59XG4iXX0=
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Check if auth attempts for this identifier are currently rate-limited.
3
+ * @returns null if allowed, or a message string if blocked.
4
+ */
5
+ export declare function checkAuthRateLimit(identifier?: string, baseUrl?: string): string | null;
6
+ /**
7
+ * Record a successful auth for this identifier — clears its failure counter.
8
+ */
9
+ export declare function recordAuthSuccess(identifier?: string, baseUrl?: string): void;
10
+ /**
11
+ * Record a failed auth attempt for this identifier. After MAX_FAILURES, locks out
12
+ * that identifier for COOLDOWN_MS.
13
+ */
14
+ export declare function recordAuthFailure(identifier?: string, baseUrl?: string): void;
15
+ //# sourceMappingURL=rate-limiter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rate-limiter.d.ts","sourceRoot":"","sources":["../../src/utils/rate-limiter.ts"],"names":[],"mappings":"AAgHA;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,UAAU,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAyBvF;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,UAAU,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAM7E;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,UAAU,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAgB7E"}
@@ -0,0 +1,164 @@
1
+ // Copyright 2026 Pipeline Builder Contributors
2
+ // SPDX-License-Identifier: Apache-2.0
3
+ import crypto from 'crypto';
4
+ import fs from 'fs';
5
+ import os from 'os';
6
+ import path from 'path';
7
+ /**
8
+ * Simple file-based rate limiter for auth operations.
9
+ * Tracks failed attempts and enforces a cooldown period after too many failures.
10
+ * State is persisted in a temp file so it survives across CLI invocations.
11
+ *
12
+ * State is keyed per (baseUrl, identifier) so a failed login for one account does
13
+ * NOT lock out another account on a shared workstation / CI host. Calls without an
14
+ * identifier fall back to a shared `_default` bucket (coarse anti-bruteforce).
15
+ */
16
+ const STATE_FILE = path.join(os.tmpdir(), '.pipeline-manager-auth-state.json');
17
+ // Directory-as-mutex guarding the read-modify-write. `mkdir` is atomic on every
18
+ // platform, so concurrent CLI invocations serialize on it instead of racing (a
19
+ // lost increment would under-count toward lockout — see withLock).
20
+ const LOCK_DIR = `${STATE_FILE}.lock`;
21
+ const MAX_FAILURES = 5;
22
+ const COOLDOWN_MS = 60_000; // 1 minute
23
+ const PRUNE_MS = 60 * 60_000; // drop buckets idle for >1h to bound file growth
24
+ const LOCK_STALE_MS = 5_000; // reclaim a lock left by a crashed process
25
+ const LOCK_WAIT_MS = 1_000; // give up waiting after this (best-effort: proceed unlocked)
26
+ const LOCK_RETRY_MS = 15; // spin interval while the lock is held
27
+ const EMPTY = { failures: 0, lastFailure: 0, lockedUntil: 0 };
28
+ /** Stable, non-reversible bucket key for a (baseUrl, identifier) pair. */
29
+ function keyFor(identifier, baseUrl) {
30
+ if (!identifier)
31
+ return '_default';
32
+ return crypto.createHash('sha256').update(`${baseUrl ?? ''}\n${identifier}`).digest('hex').slice(0, 16);
33
+ }
34
+ function readMap() {
35
+ try {
36
+ if (fs.existsSync(STATE_FILE)) {
37
+ const parsed = JSON.parse(fs.readFileSync(STATE_FILE, 'utf-8'));
38
+ if (parsed && typeof parsed === 'object')
39
+ return parsed;
40
+ }
41
+ }
42
+ catch { /* ignore corrupt state */ }
43
+ return {};
44
+ }
45
+ function writeMap(map) {
46
+ const now = Date.now();
47
+ // Prune idle buckets so the file doesn't grow unbounded across many identifiers.
48
+ for (const [k, s] of Object.entries(map)) {
49
+ if (s.lockedUntil <= now && now - s.lastFailure > PRUNE_MS)
50
+ delete map[k];
51
+ }
52
+ try {
53
+ // Write to a per-process temp file then rename: rename is atomic on the same
54
+ // filesystem, so a concurrent reader never observes a torn/half-written file.
55
+ const tmp = `${STATE_FILE}.${process.pid}.tmp`;
56
+ fs.writeFileSync(tmp, JSON.stringify(map), { mode: 0o600 });
57
+ fs.renameSync(tmp, STATE_FILE);
58
+ }
59
+ catch { /* best-effort */ }
60
+ }
61
+ /** Block the current thread for `ms` without a busy-wait (CLI-only, short waits). */
62
+ function sleep(ms) {
63
+ Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
64
+ }
65
+ /**
66
+ * Run `fn` while holding a cross-process advisory lock so the read-modify-write in
67
+ * recordAuthFailure/Success can't lose an update to a concurrent CLI invocation
68
+ * (the TOCTOU that let parallel runs under-count toward lockout). Best-effort: if
69
+ * the lock can't be acquired within LOCK_WAIT_MS we proceed anyway rather than
70
+ * block the user — server-side rate limiting remains the real enforcement.
71
+ */
72
+ function withLock(fn) {
73
+ const deadline = Date.now() + LOCK_WAIT_MS;
74
+ let held = false;
75
+ while (Date.now() < deadline) {
76
+ try {
77
+ fs.mkdirSync(LOCK_DIR);
78
+ held = true;
79
+ break;
80
+ }
81
+ catch {
82
+ // Held elsewhere — reclaim it if it's stale (owner crashed without cleanup),
83
+ // otherwise wait a beat and retry.
84
+ try {
85
+ if (Date.now() - fs.statSync(LOCK_DIR).mtimeMs > LOCK_STALE_MS) {
86
+ fs.rmdirSync(LOCK_DIR);
87
+ continue;
88
+ }
89
+ }
90
+ catch { /* lock vanished between calls — just retry */ }
91
+ sleep(LOCK_RETRY_MS);
92
+ }
93
+ }
94
+ try {
95
+ return fn();
96
+ }
97
+ finally {
98
+ if (held) {
99
+ try {
100
+ fs.rmdirSync(LOCK_DIR);
101
+ }
102
+ catch { /* already gone */ }
103
+ }
104
+ }
105
+ }
106
+ /**
107
+ * Check if auth attempts for this identifier are currently rate-limited.
108
+ * @returns null if allowed, or a message string if blocked.
109
+ */
110
+ export function checkAuthRateLimit(identifier, baseUrl) {
111
+ const key = keyFor(identifier, baseUrl);
112
+ const map = readMap();
113
+ const state = map[key] ?? EMPTY;
114
+ const now = Date.now();
115
+ if (state.lockedUntil > now) {
116
+ const waitSec = Math.ceil((state.lockedUntil - now) / 1000);
117
+ return `Too many failed login attempts. Try again in ${waitSec}s.`;
118
+ }
119
+ // Reset this bucket once the cooldown window has passed. Re-read under the lock
120
+ // so we don't clobber a concurrent recordAuthFailure for another bucket.
121
+ if (state.lastFailure !== 0 && now - state.lastFailure > COOLDOWN_MS) {
122
+ withLock(() => {
123
+ const m = readMap();
124
+ const s = m[key];
125
+ if (s && s.lastFailure !== 0 && Date.now() - s.lastFailure > COOLDOWN_MS) {
126
+ delete m[key];
127
+ writeMap(m);
128
+ }
129
+ });
130
+ }
131
+ return null;
132
+ }
133
+ /**
134
+ * Record a successful auth for this identifier — clears its failure counter.
135
+ */
136
+ export function recordAuthSuccess(identifier, baseUrl) {
137
+ withLock(() => {
138
+ const map = readMap();
139
+ delete map[keyFor(identifier, baseUrl)];
140
+ writeMap(map);
141
+ });
142
+ }
143
+ /**
144
+ * Record a failed auth attempt for this identifier. After MAX_FAILURES, locks out
145
+ * that identifier for COOLDOWN_MS.
146
+ */
147
+ export function recordAuthFailure(identifier, baseUrl) {
148
+ const key = keyFor(identifier, baseUrl);
149
+ // Read-modify-write under the lock: a concurrent CLI invocation must not read the
150
+ // same pre-increment count and overwrite this one (which would under-count).
151
+ withLock(() => {
152
+ const map = readMap();
153
+ const now = Date.now();
154
+ const state = { ...(map[key] ?? EMPTY) };
155
+ state.failures += 1;
156
+ state.lastFailure = now;
157
+ if (state.failures >= MAX_FAILURES) {
158
+ state.lockedUntil = now + COOLDOWN_MS;
159
+ }
160
+ map[key] = state;
161
+ writeMap(map);
162
+ });
163
+ }
164
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicmF0ZS1saW1pdGVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL3V0aWxzL3JhdGUtbGltaXRlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSwrQ0FBK0M7QUFDL0Msc0NBQXNDO0FBRXRDLE9BQU8sTUFBTSxNQUFNLFFBQVEsQ0FBQztBQUM1QixPQUFPLEVBQUUsTUFBTSxJQUFJLENBQUM7QUFDcEIsT0FBTyxFQUFFLE1BQU0sSUFBSSxDQUFDO0FBQ3BCLE9BQU8sSUFBSSxNQUFNLE1BQU0sQ0FBQztBQUV4Qjs7Ozs7Ozs7R0FRRztBQUVILE1BQU0sVUFBVSxHQUFHLElBQUksQ0FBQyxJQUFJLENBQUMsRUFBRSxDQUFDLE1BQU0sRUFBRSxFQUFFLG1DQUFtQyxDQUFDLENBQUM7QUFDL0UsZ0ZBQWdGO0FBQ2hGLCtFQUErRTtBQUMvRSxtRUFBbUU7QUFDbkUsTUFBTSxRQUFRLEdBQUcsR0FBRyxVQUFVLE9BQU8sQ0FBQztBQUN0QyxNQUFNLFlBQVksR0FBRyxDQUFDLENBQUM7QUFDdkIsTUFBTSxXQUFXLEdBQUcsTUFBTSxDQUFDLENBQUMsV0FBVztBQUN2QyxNQUFNLFFBQVEsR0FBRyxFQUFFLEdBQUcsTUFBTSxDQUFDLENBQUMsaURBQWlEO0FBQy9FLE1BQU0sYUFBYSxHQUFHLEtBQUssQ0FBQyxDQUFDLDJDQUEyQztBQUN4RSxNQUFNLFlBQVksR0FBRyxLQUFLLENBQUMsQ0FBQyw2REFBNkQ7QUFDekYsTUFBTSxhQUFhLEdBQUcsRUFBRSxDQUFDLENBQUMsdUNBQXVDO0FBVWpFLE1BQU0sS0FBSyxHQUFjLEVBQUUsUUFBUSxFQUFFLENBQUMsRUFBRSxXQUFXLEVBQUUsQ0FBQyxFQUFFLFdBQVcsRUFBRSxDQUFDLEVBQUUsQ0FBQztBQUV6RSwwRUFBMEU7QUFDMUUsU0FBUyxNQUFNLENBQUMsVUFBbUIsRUFBRSxPQUFnQjtJQUNuRCxJQUFJLENBQUMsVUFBVTtRQUFFLE9BQU8sVUFBVSxDQUFDO0lBQ25DLE9BQU8sTUFBTSxDQUFDLFVBQVUsQ0FBQyxRQUFRLENBQUMsQ0FBQyxNQUFNLENBQUMsR0FBRyxPQUFPLElBQUksRUFBRSxLQUFLLFVBQVUsRUFBRSxDQUFDLENBQUMsTUFBTSxDQUFDLEtBQUssQ0FBQyxDQUFDLEtBQUssQ0FBQyxDQUFDLEVBQUUsRUFBRSxDQUFDLENBQUM7QUFDMUcsQ0FBQztBQUVELFNBQVMsT0FBTztJQUNkLElBQUksQ0FBQztRQUNILElBQUksRUFBRSxDQUFDLFVBQVUsQ0FBQyxVQUFVLENBQUMsRUFBRSxDQUFDO1lBQzlCLE1BQU0sTUFBTSxHQUFHLElBQUksQ0FBQyxLQUFLLENBQUMsRUFBRSxDQUFDLFlBQVksQ0FBQyxVQUFVLEVBQUUsT0FBTyxDQUFDLENBQVksQ0FBQztZQUMzRSxJQUFJLE1BQU0sSUFBSSxPQUFPLE1BQU0sS0FBSyxRQUFRO2dCQUFFLE9BQU8sTUFBa0IsQ0FBQztRQUN0RSxDQUFDO0lBQ0gsQ0FBQztJQUFDLE1BQU0sQ0FBQyxDQUFDLDBCQUEwQixDQUFDLENBQUM7SUFDdEMsT0FBTyxFQUFFLENBQUM7QUFDWixDQUFDO0FBRUQsU0FBUyxRQUFRLENBQUMsR0FBYTtJQUM3QixNQUFNLEdBQUcsR0FBRyxJQUFJLENBQUMsR0FBRyxFQUFFLENBQUM7SUFDdkIsaUZBQWlGO0lBQ2pGLEtBQUssTUFBTSxDQUFDLENBQUMsRUFBRSxDQUFDLENBQUMsSUFBSSxNQUFNLENBQUMsT0FBTyxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUM7UUFDekMsSUFBSSxDQUFDLENBQUMsV0FBVyxJQUFJLEdBQUcsSUFBSSxHQUFHLEdBQUcsQ0FBQyxDQUFDLFdBQVcsR0FBRyxRQUFRO1lBQUUsT0FBTyxHQUFHLENBQUMsQ0FBQyxDQUFDLENBQUM7SUFDNUUsQ0FBQztJQUNELElBQUksQ0FBQztRQUNILDZFQUE2RTtRQUM3RSw4RUFBOEU7UUFDOUUsTUFBTSxHQUFHLEdBQUcsR0FBRyxVQUFVLElBQUksT0FBTyxDQUFDLEdBQUcsTUFBTSxDQUFDO1FBQy9DLEVBQUUsQ0FBQyxhQUFhLENBQUMsR0FBRyxFQUFFLElBQUksQ0FBQyxTQUFTLENBQUMsR0FBRyxDQUFDLEVBQUUsRUFBRSxJQUFJLEVBQUUsS0FBSyxFQUFFLENBQUMsQ0FBQztRQUM1RCxFQUFFLENBQUMsVUFBVSxDQUFDLEdBQUcsRUFBRSxVQUFVLENBQUMsQ0FBQztJQUNqQyxDQUFDO0lBQUMsTUFBTSxDQUFDLENBQUMsaUJBQWlCLENBQUMsQ0FBQztBQUMvQixDQUFDO0FBRUQscUZBQXFGO0FBQ3JGLFNBQVMsS0FBSyxDQUFDLEVBQVU7SUFDdkIsT0FBTyxDQUFDLElBQUksQ0FBQyxJQUFJLFVBQVUsQ0FBQyxJQUFJLGlCQUFpQixDQUFDLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxFQUFFLENBQUMsRUFBRSxFQUFFLENBQUMsQ0FBQztBQUNuRSxDQUFDO0FBRUQ7Ozs7OztHQU1HO0FBQ0gsU0FBUyxRQUFRLENBQUksRUFBVztJQUM5QixNQUFNLFFBQVEsR0FBRyxJQUFJLENBQUMsR0FBRyxFQUFFLEdBQUcsWUFBWSxDQUFDO0lBQzNDLElBQUksSUFBSSxHQUFHLEtBQUssQ0FBQztJQUNqQixPQUFPLElBQUksQ0FBQyxHQUFHLEVBQUUsR0FBRyxRQUFRLEVBQUUsQ0FBQztRQUM3QixJQUFJLENBQUM7WUFDSCxFQUFFLENBQUMsU0FBUyxDQUFDLFFBQVEsQ0FBQyxDQUFDO1lBQ3ZCLElBQUksR0FBRyxJQUFJLENBQUM7WUFDWixNQUFNO1FBQ1IsQ0FBQztRQUFDLE1BQU0sQ0FBQztZQUNQLDZFQUE2RTtZQUM3RSxtQ0FBbUM7WUFDbkMsSUFBSSxDQUFDO2dCQUNILElBQUksSUFBSSxDQUFDLEdBQUcsRUFBRSxHQUFHLEVBQUUsQ0FBQyxRQUFRLENBQUMsUUFBUSxDQUFDLENBQUMsT0FBTyxHQUFHLGFBQWEsRUFBRSxDQUFDO29CQUMvRCxFQUFFLENBQUMsU0FBUyxDQUFDLFFBQVEsQ0FBQyxDQUFDO29CQUN2QixTQUFTO2dCQUNYLENBQUM7WUFDSCxDQUFDO1lBQUMsTUFBTSxDQUFDLENBQUMsOENBQThDLENBQUMsQ0FBQztZQUMxRCxLQUFLLENBQUMsYUFBYSxDQUFDLENBQUM7UUFDdkIsQ0FBQztJQUNILENBQUM7SUFDRCxJQUFJLENBQUM7UUFDSCxPQUFPLEVBQUUsRUFBRSxDQUFDO0lBQ2QsQ0FBQztZQUFTLENBQUM7UUFDVCxJQUFJLElBQUksRUFBRSxDQUFDO1lBQ1QsSUFBSSxDQUFDO2dCQUFDLEVBQUUsQ0FBQyxTQUFTLENBQUMsUUFBUSxDQUFDLENBQUM7WUFBQyxDQUFDO1lBQUMsTUFBTSxDQUFDLENBQUMsa0JBQWtCLENBQUMsQ0FBQztRQUM5RCxDQUFDO0lBQ0gsQ0FBQztBQUNILENBQUM7QUFFRDs7O0dBR0c7QUFDSCxNQUFNLFVBQVUsa0JBQWtCLENBQUMsVUFBbUIsRUFBRSxPQUFnQjtJQUN0RSxNQUFNLEdBQUcsR0FBRyxNQUFNLENBQUMsVUFBVSxFQUFFLE9BQU8sQ0FBQyxDQUFDO0lBQ3hDLE1BQU0sR0FBRyxHQUFHLE9BQU8sRUFBRSxDQUFDO0lBQ3RCLE1BQU0sS0FBSyxHQUFHLEdBQUcsQ0FBQyxHQUFHLENBQUMsSUFBSSxLQUFLLENBQUM7SUFDaEMsTUFBTSxHQUFHLEdBQUcsSUFBSSxDQUFDLEdBQUcsRUFBRSxDQUFDO0lBRXZCLElBQUksS0FBSyxDQUFDLFdBQVcsR0FBRyxHQUFHLEVBQUUsQ0FBQztRQUM1QixNQUFNLE9BQU8sR0FBRyxJQUFJLENBQUMsSUFBSSxDQUFDLENBQUMsS0FBSyxDQUFDLFdBQVcsR0FBRyxHQUFHLENBQUMsR0FBRyxJQUFJLENBQUMsQ0FBQztRQUM1RCxPQUFPLGdEQUFnRCxPQUFPLElBQUksQ0FBQztJQUNyRSxDQUFDO0lBRUQsZ0ZBQWdGO0lBQ2hGLHlFQUF5RTtJQUN6RSxJQUFJLEtBQUssQ0FBQyxXQUFXLEtBQUssQ0FBQyxJQUFJLEdBQUcsR0FBRyxLQUFLLENBQUMsV0FBVyxHQUFHLFdBQVcsRUFBRSxDQUFDO1FBQ3JFLFFBQVEsQ0FBQyxHQUFHLEVBQUU7WUFDWixNQUFNLENBQUMsR0FBRyxPQUFPLEVBQUUsQ0FBQztZQUNwQixNQUFNLENBQUMsR0FBRyxDQUFDLENBQUMsR0FBRyxDQUFDLENBQUM7WUFDakIsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDLFdBQVcsS0FBSyxDQUFDLElBQUksSUFBSSxDQUFDLEdBQUcsRUFBRSxHQUFHLENBQUMsQ0FBQyxXQUFXLEdBQUcsV0FBVyxFQUFFLENBQUM7Z0JBQ3pFLE9BQU8sQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDO2dCQUNkLFFBQVEsQ0FBQyxDQUFDLENBQUMsQ0FBQztZQUNkLENBQUM7UUFDSCxDQUFDLENBQUMsQ0FBQztJQUNMLENBQUM7SUFFRCxPQUFPLElBQUksQ0FBQztBQUNkLENBQUM7QUFFRDs7R0FFRztBQUNILE1BQU0sVUFBVSxpQkFBaUIsQ0FBQyxVQUFtQixFQUFFLE9BQWdCO0lBQ3JFLFFBQVEsQ0FBQyxHQUFHLEVBQUU7UUFDWixNQUFNLEdBQUcsR0FBRyxPQUFPLEVBQUUsQ0FBQztRQUN0QixPQUFPLEdBQUcsQ0FBQyxNQUFNLENBQUMsVUFBVSxFQUFFLE9BQU8sQ0FBQyxDQUFDLENBQUM7UUFDeEMsUUFBUSxDQUFDLEdBQUcsQ0FBQyxDQUFDO0lBQ2hCLENBQUMsQ0FBQyxDQUFDO0FBQ0wsQ0FBQztBQUVEOzs7R0FHRztBQUNILE1BQU0sVUFBVSxpQkFBaUIsQ0FBQyxVQUFtQixFQUFFLE9BQWdCO0lBQ3JFLE1BQU0sR0FBRyxHQUFHLE1BQU0sQ0FBQyxVQUFVLEVBQUUsT0FBTyxDQUFDLENBQUM7SUFDeEMsa0ZBQWtGO0lBQ2xGLDZFQUE2RTtJQUM3RSxRQUFRLENBQUMsR0FBRyxFQUFFO1FBQ1osTUFBTSxHQUFHLEdBQUcsT0FBTyxFQUFFLENBQUM7UUFDdEIsTUFBTSxHQUFHLEdBQUcsSUFBSSxDQUFDLEdBQUcsRUFBRSxDQUFDO1FBQ3ZCLE1BQU0sS0FBSyxHQUFHLEVBQUUsR0FBRyxDQUFDLEdBQUcsQ0FBQyxHQUFHLENBQUMsSUFBSSxLQUFLLENBQUMsRUFBRSxDQUFDO1FBQ3pDLEtBQUssQ0FBQyxRQUFRLElBQUksQ0FBQyxDQUFDO1FBQ3BCLEtBQUssQ0FBQyxXQUFXLEdBQUcsR0FBRyxDQUFDO1FBQ3hCLElBQUksS0FBSyxDQUFDLFFBQVEsSUFBSSxZQUFZLEVBQUUsQ0FBQztZQUNuQyxLQUFLLENBQUMsV0FBVyxHQUFHLEdBQUcsR0FBRyxXQUFXLENBQUM7UUFDeEMsQ0FBQztRQUNELEdBQUcsQ0FBQyxHQUFHLENBQUMsR0FBRyxLQUFLLENBQUM7UUFDakIsUUFBUSxDQUFDLEdBQUcsQ0FBQyxDQUFDO0lBQ2hCLENBQUMsQ0FBQyxDQUFDO0FBQ0wsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8vIENvcHlyaWdodCAyMDI2IFBpcGVsaW5lIEJ1aWxkZXIgQ29udHJpYnV0b3JzXG4vLyBTUERYLUxpY2Vuc2UtSWRlbnRpZmllcjogQXBhY2hlLTIuMFxuXG5pbXBvcnQgY3J5cHRvIGZyb20gJ2NyeXB0byc7XG5pbXBvcnQgZnMgZnJvbSAnZnMnO1xuaW1wb3J0IG9zIGZyb20gJ29zJztcbmltcG9ydCBwYXRoIGZyb20gJ3BhdGgnO1xuXG4vKipcbiAqIFNpbXBsZSBmaWxlLWJhc2VkIHJhdGUgbGltaXRlciBmb3IgYXV0aCBvcGVyYXRpb25zLlxuICogVHJhY2tzIGZhaWxlZCBhdHRlbXB0cyBhbmQgZW5mb3JjZXMgYSBjb29sZG93biBwZXJpb2QgYWZ0ZXIgdG9vIG1hbnkgZmFpbHVyZXMuXG4gKiBTdGF0ZSBpcyBwZXJzaXN0ZWQgaW4gYSB0ZW1wIGZpbGUgc28gaXQgc3Vydml2ZXMgYWNyb3NzIENMSSBpbnZvY2F0aW9ucy5cbiAqXG4gKiBTdGF0ZSBpcyBrZXllZCBwZXIgKGJhc2VVcmwsIGlkZW50aWZpZXIpIHNvIGEgZmFpbGVkIGxvZ2luIGZvciBvbmUgYWNjb3VudCBkb2VzXG4gKiBOT1QgbG9jayBvdXQgYW5vdGhlciBhY2NvdW50IG9uIGEgc2hhcmVkIHdvcmtzdGF0aW9uIC8gQ0kgaG9zdC4gQ2FsbHMgd2l0aG91dCBhblxuICogaWRlbnRpZmllciBmYWxsIGJhY2sgdG8gYSBzaGFyZWQgYF9kZWZhdWx0YCBidWNrZXQgKGNvYXJzZSBhbnRpLWJydXRlZm9yY2UpLlxuICovXG5cbmNvbnN0IFNUQVRFX0ZJTEUgPSBwYXRoLmpvaW4ob3MudG1wZGlyKCksICcucGlwZWxpbmUtbWFuYWdlci1hdXRoLXN0YXRlLmpzb24nKTtcbi8vIERpcmVjdG9yeS1hcy1tdXRleCBndWFyZGluZyB0aGUgcmVhZC1tb2RpZnktd3JpdGUuIGBta2RpcmAgaXMgYXRvbWljIG9uIGV2ZXJ5XG4vLyBwbGF0Zm9ybSwgc28gY29uY3VycmVudCBDTEkgaW52b2NhdGlvbnMgc2VyaWFsaXplIG9uIGl0IGluc3RlYWQgb2YgcmFjaW5nIChhXG4vLyBsb3N0IGluY3JlbWVudCB3b3VsZCB1bmRlci1jb3VudCB0b3dhcmQgbG9ja291dCDigJQgc2VlIHdpdGhMb2NrKS5cbmNvbnN0IExPQ0tfRElSID0gYCR7U1RBVEVfRklMRX0ubG9ja2A7XG5jb25zdCBNQVhfRkFJTFVSRVMgPSA1O1xuY29uc3QgQ09PTERPV05fTVMgPSA2MF8wMDA7IC8vIDEgbWludXRlXG5jb25zdCBQUlVORV9NUyA9IDYwICogNjBfMDAwOyAvLyBkcm9wIGJ1Y2tldHMgaWRsZSBmb3IgPjFoIHRvIGJvdW5kIGZpbGUgZ3Jvd3RoXG5jb25zdCBMT0NLX1NUQUxFX01TID0gNV8wMDA7IC8vIHJlY2xhaW0gYSBsb2NrIGxlZnQgYnkgYSBjcmFzaGVkIHByb2Nlc3NcbmNvbnN0IExPQ0tfV0FJVF9NUyA9IDFfMDAwOyAvLyBnaXZlIHVwIHdhaXRpbmcgYWZ0ZXIgdGhpcyAoYmVzdC1lZmZvcnQ6IHByb2NlZWQgdW5sb2NrZWQpXG5jb25zdCBMT0NLX1JFVFJZX01TID0gMTU7IC8vIHNwaW4gaW50ZXJ2YWwgd2hpbGUgdGhlIGxvY2sgaXMgaGVsZFxuXG5pbnRlcmZhY2UgQXV0aFN0YXRlIHtcbiAgZmFpbHVyZXM6IG51bWJlcjtcbiAgbGFzdEZhaWx1cmU6IG51bWJlcjtcbiAgbG9ja2VkVW50aWw6IG51bWJlcjtcbn1cblxudHlwZSBTdGF0ZU1hcCA9IFJlY29yZDxzdHJpbmcsIEF1dGhTdGF0ZT47XG5cbmNvbnN0IEVNUFRZOiBBdXRoU3RhdGUgPSB7IGZhaWx1cmVzOiAwLCBsYXN0RmFpbHVyZTogMCwgbG9ja2VkVW50aWw6IDAgfTtcblxuLyoqIFN0YWJsZSwgbm9uLXJldmVyc2libGUgYnVja2V0IGtleSBmb3IgYSAoYmFzZVVybCwgaWRlbnRpZmllcikgcGFpci4gKi9cbmZ1bmN0aW9uIGtleUZvcihpZGVudGlmaWVyPzogc3RyaW5nLCBiYXNlVXJsPzogc3RyaW5nKTogc3RyaW5nIHtcbiAgaWYgKCFpZGVudGlmaWVyKSByZXR1cm4gJ19kZWZhdWx0JztcbiAgcmV0dXJuIGNyeXB0by5jcmVhdGVIYXNoKCdzaGEyNTYnKS51cGRhdGUoYCR7YmFzZVVybCA/PyAnJ31cXG4ke2lkZW50aWZpZXJ9YCkuZGlnZXN0KCdoZXgnKS5zbGljZSgwLCAxNik7XG59XG5cbmZ1bmN0aW9uIHJlYWRNYXAoKTogU3RhdGVNYXAge1xuICB0cnkge1xuICAgIGlmIChmcy5leGlzdHNTeW5jKFNUQVRFX0ZJTEUpKSB7XG4gICAgICBjb25zdCBwYXJzZWQgPSBKU09OLnBhcnNlKGZzLnJlYWRGaWxlU3luYyhTVEFURV9GSUxFLCAndXRmLTgnKSkgYXMgdW5rbm93bjtcbiAgICAgIGlmIChwYXJzZWQgJiYgdHlwZW9mIHBhcnNlZCA9PT0gJ29iamVjdCcpIHJldHVybiBwYXJzZWQgYXMgU3RhdGVNYXA7XG4gICAgfVxuICB9IGNhdGNoIHsgLyogaWdub3JlIGNvcnJ1cHQgc3RhdGUgKi8gfVxuICByZXR1cm4ge307XG59XG5cbmZ1bmN0aW9uIHdyaXRlTWFwKG1hcDogU3RhdGVNYXApOiB2b2lkIHtcbiAgY29uc3Qgbm93ID0gRGF0ZS5ub3coKTtcbiAgLy8gUHJ1bmUgaWRsZSBidWNrZXRzIHNvIHRoZSBmaWxlIGRvZXNuJ3QgZ3JvdyB1bmJvdW5kZWQgYWNyb3NzIG1hbnkgaWRlbnRpZmllcnMuXG4gIGZvciAoY29uc3QgW2ssIHNdIG9mIE9iamVjdC5lbnRyaWVzKG1hcCkpIHtcbiAgICBpZiAocy5sb2NrZWRVbnRpbCA8PSBub3cgJiYgbm93IC0gcy5sYXN0RmFpbHVyZSA+IFBSVU5FX01TKSBkZWxldGUgbWFwW2tdO1xuICB9XG4gIHRyeSB7XG4gICAgLy8gV3JpdGUgdG8gYSBwZXItcHJvY2VzcyB0ZW1wIGZpbGUgdGhlbiByZW5hbWU6IHJlbmFtZSBpcyBhdG9taWMgb24gdGhlIHNhbWVcbiAgICAvLyBmaWxlc3lzdGVtLCBzbyBhIGNvbmN1cnJlbnQgcmVhZGVyIG5ldmVyIG9ic2VydmVzIGEgdG9ybi9oYWxmLXdyaXR0ZW4gZmlsZS5cbiAgICBjb25zdCB0bXAgPSBgJHtTVEFURV9GSUxFfS4ke3Byb2Nlc3MucGlkfS50bXBgO1xuICAgIGZzLndyaXRlRmlsZVN5bmModG1wLCBKU09OLnN0cmluZ2lmeShtYXApLCB7IG1vZGU6IDBvNjAwIH0pO1xuICAgIGZzLnJlbmFtZVN5bmModG1wLCBTVEFURV9GSUxFKTtcbiAgfSBjYXRjaCB7IC8qIGJlc3QtZWZmb3J0ICovIH1cbn1cblxuLyoqIEJsb2NrIHRoZSBjdXJyZW50IHRocmVhZCBmb3IgYG1zYCB3aXRob3V0IGEgYnVzeS13YWl0IChDTEktb25seSwgc2hvcnQgd2FpdHMpLiAqL1xuZnVuY3Rpb24gc2xlZXAobXM6IG51bWJlcik6IHZvaWQge1xuICBBdG9taWNzLndhaXQobmV3IEludDMyQXJyYXkobmV3IFNoYXJlZEFycmF5QnVmZmVyKDQpKSwgMCwgMCwgbXMpO1xufVxuXG4vKipcbiAqIFJ1biBgZm5gIHdoaWxlIGhvbGRpbmcgYSBjcm9zcy1wcm9jZXNzIGFkdmlzb3J5IGxvY2sgc28gdGhlIHJlYWQtbW9kaWZ5LXdyaXRlIGluXG4gKiByZWNvcmRBdXRoRmFpbHVyZS9TdWNjZXNzIGNhbid0IGxvc2UgYW4gdXBkYXRlIHRvIGEgY29uY3VycmVudCBDTEkgaW52b2NhdGlvblxuICogKHRoZSBUT0NUT1UgdGhhdCBsZXQgcGFyYWxsZWwgcnVucyB1bmRlci1jb3VudCB0b3dhcmQgbG9ja291dCkuIEJlc3QtZWZmb3J0OiBpZlxuICogdGhlIGxvY2sgY2FuJ3QgYmUgYWNxdWlyZWQgd2l0aGluIExPQ0tfV0FJVF9NUyB3ZSBwcm9jZWVkIGFueXdheSByYXRoZXIgdGhhblxuICogYmxvY2sgdGhlIHVzZXIg4oCUIHNlcnZlci1zaWRlIHJhdGUgbGltaXRpbmcgcmVtYWlucyB0aGUgcmVhbCBlbmZvcmNlbWVudC5cbiAqL1xuZnVuY3Rpb24gd2l0aExvY2s8VD4oZm46ICgpID0+IFQpOiBUIHtcbiAgY29uc3QgZGVhZGxpbmUgPSBEYXRlLm5vdygpICsgTE9DS19XQUlUX01TO1xuICBsZXQgaGVsZCA9IGZhbHNlO1xuICB3aGlsZSAoRGF0ZS5ub3coKSA8IGRlYWRsaW5lKSB7XG4gICAgdHJ5IHtcbiAgICAgIGZzLm1rZGlyU3luYyhMT0NLX0RJUik7XG4gICAgICBoZWxkID0gdHJ1ZTtcbiAgICAgIGJyZWFrO1xuICAgIH0gY2F0Y2gge1xuICAgICAgLy8gSGVsZCBlbHNld2hlcmUg4oCUIHJlY2xhaW0gaXQgaWYgaXQncyBzdGFsZSAob3duZXIgY3Jhc2hlZCB3aXRob3V0IGNsZWFudXApLFxuICAgICAgLy8gb3RoZXJ3aXNlIHdhaXQgYSBiZWF0IGFuZCByZXRyeS5cbiAgICAgIHRyeSB7XG4gICAgICAgIGlmIChEYXRlLm5vdygpIC0gZnMuc3RhdFN5bmMoTE9DS19ESVIpLm10aW1lTXMgPiBMT0NLX1NUQUxFX01TKSB7XG4gICAgICAgICAgZnMucm1kaXJTeW5jKExPQ0tfRElSKTtcbiAgICAgICAgICBjb250aW51ZTtcbiAgICAgICAgfVxuICAgICAgfSBjYXRjaCB7IC8qIGxvY2sgdmFuaXNoZWQgYmV0d2VlbiBjYWxscyDigJQganVzdCByZXRyeSAqLyB9XG4gICAgICBzbGVlcChMT0NLX1JFVFJZX01TKTtcbiAgICB9XG4gIH1cbiAgdHJ5IHtcbiAgICByZXR1cm4gZm4oKTtcbiAgfSBmaW5hbGx5IHtcbiAgICBpZiAoaGVsZCkge1xuICAgICAgdHJ5IHsgZnMucm1kaXJTeW5jKExPQ0tfRElSKTsgfSBjYXRjaCB7IC8qIGFscmVhZHkgZ29uZSAqLyB9XG4gICAgfVxuICB9XG59XG5cbi8qKlxuICogQ2hlY2sgaWYgYXV0aCBhdHRlbXB0cyBmb3IgdGhpcyBpZGVudGlmaWVyIGFyZSBjdXJyZW50bHkgcmF0ZS1saW1pdGVkLlxuICogQHJldHVybnMgbnVsbCBpZiBhbGxvd2VkLCBvciBhIG1lc3NhZ2Ugc3RyaW5nIGlmIGJsb2NrZWQuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBjaGVja0F1dGhSYXRlTGltaXQoaWRlbnRpZmllcj86IHN0cmluZywgYmFzZVVybD86IHN0cmluZyk6IHN0cmluZyB8IG51bGwge1xuICBjb25zdCBrZXkgPSBrZXlGb3IoaWRlbnRpZmllciwgYmFzZVVybCk7XG4gIGNvbnN0IG1hcCA9IHJlYWRNYXAoKTtcbiAgY29uc3Qgc3RhdGUgPSBtYXBba2V5XSA/PyBFTVBUWTtcbiAgY29uc3Qgbm93ID0gRGF0ZS5ub3coKTtcblxuICBpZiAoc3RhdGUubG9ja2VkVW50aWwgPiBub3cpIHtcbiAgICBjb25zdCB3YWl0U2VjID0gTWF0aC5jZWlsKChzdGF0ZS5sb2NrZWRVbnRpbCAtIG5vdykgLyAxMDAwKTtcbiAgICByZXR1cm4gYFRvbyBtYW55IGZhaWxlZCBsb2dpbiBhdHRlbXB0cy4gVHJ5IGFnYWluIGluICR7d2FpdFNlY31zLmA7XG4gIH1cblxuICAvLyBSZXNldCB0aGlzIGJ1Y2tldCBvbmNlIHRoZSBjb29sZG93biB3aW5kb3cgaGFzIHBhc3NlZC4gUmUtcmVhZCB1bmRlciB0aGUgbG9ja1xuICAvLyBzbyB3ZSBkb24ndCBjbG9iYmVyIGEgY29uY3VycmVudCByZWNvcmRBdXRoRmFpbHVyZSBmb3IgYW5vdGhlciBidWNrZXQuXG4gIGlmIChzdGF0ZS5sYXN0RmFpbHVyZSAhPT0gMCAmJiBub3cgLSBzdGF0ZS5sYXN0RmFpbHVyZSA+IENPT0xET1dOX01TKSB7XG4gICAgd2l0aExvY2soKCkgPT4ge1xuICAgICAgY29uc3QgbSA9IHJlYWRNYXAoKTtcbiAgICAgIGNvbnN0IHMgPSBtW2tleV07XG4gICAgICBpZiAocyAmJiBzLmxhc3RGYWlsdXJlICE9PSAwICYmIERhdGUubm93KCkgLSBzLmxhc3RGYWlsdXJlID4gQ09PTERPV05fTVMpIHtcbiAgICAgICAgZGVsZXRlIG1ba2V5XTtcbiAgICAgICAgd3JpdGVNYXAobSk7XG4gICAgICB9XG4gICAgfSk7XG4gIH1cblxuICByZXR1cm4gbnVsbDtcbn1cblxuLyoqXG4gKiBSZWNvcmQgYSBzdWNjZXNzZnVsIGF1dGggZm9yIHRoaXMgaWRlbnRpZmllciDigJQgY2xlYXJzIGl0cyBmYWlsdXJlIGNvdW50ZXIuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiByZWNvcmRBdXRoU3VjY2VzcyhpZGVudGlmaWVyPzogc3RyaW5nLCBiYXNlVXJsPzogc3RyaW5nKTogdm9pZCB7XG4gIHdpdGhMb2NrKCgpID0+IHtcbiAgICBjb25zdCBtYXAgPSByZWFkTWFwKCk7XG4gICAgZGVsZXRlIG1hcFtrZXlGb3IoaWRlbnRpZmllciwgYmFzZVVybCldO1xuICAgIHdyaXRlTWFwKG1hcCk7XG4gIH0pO1xufVxuXG4vKipcbiAqIFJlY29yZCBhIGZhaWxlZCBhdXRoIGF0dGVtcHQgZm9yIHRoaXMgaWRlbnRpZmllci4gQWZ0ZXIgTUFYX0ZBSUxVUkVTLCBsb2NrcyBvdXRcbiAqIHRoYXQgaWRlbnRpZmllciBmb3IgQ09PTERPV05fTVMuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiByZWNvcmRBdXRoRmFpbHVyZShpZGVudGlmaWVyPzogc3RyaW5nLCBiYXNlVXJsPzogc3RyaW5nKTogdm9pZCB7XG4gIGNvbnN0IGtleSA9IGtleUZvcihpZGVudGlmaWVyLCBiYXNlVXJsKTtcbiAgLy8gUmVhZC1tb2RpZnktd3JpdGUgdW5kZXIgdGhlIGxvY2s6IGEgY29uY3VycmVudCBDTEkgaW52b2NhdGlvbiBtdXN0IG5vdCByZWFkIHRoZVxuICAvLyBzYW1lIHByZS1pbmNyZW1lbnQgY291bnQgYW5kIG92ZXJ3cml0ZSB0aGlzIG9uZSAod2hpY2ggd291bGQgdW5kZXItY291bnQpLlxuICB3aXRoTG9jaygoKSA9PiB7XG4gICAgY29uc3QgbWFwID0gcmVhZE1hcCgpO1xuICAgIGNvbnN0IG5vdyA9IERhdGUubm93KCk7XG4gICAgY29uc3Qgc3RhdGUgPSB7IC4uLihtYXBba2V5XSA/PyBFTVBUWSkgfTtcbiAgICBzdGF0ZS5mYWlsdXJlcyArPSAxO1xuICAgIHN0YXRlLmxhc3RGYWlsdXJlID0gbm93O1xuICAgIGlmIChzdGF0ZS5mYWlsdXJlcyA+PSBNQVhfRkFJTFVSRVMpIHtcbiAgICAgIHN0YXRlLmxvY2tlZFVudGlsID0gbm93ICsgQ09PTERPV05fTVM7XG4gICAgfVxuICAgIG1hcFtrZXldID0gc3RhdGU7XG4gICAgd3JpdGVNYXAobWFwKTtcbiAgfSk7XG59XG4iXX0=
@@ -0,0 +1,86 @@
1
+ /**
2
+ * Helpers for registering a deployed pipeline ARN with the platform.
3
+ *
4
+ * The registry table maps deployed CodePipeline ARNs back to pipeline records
5
+ * and orgs. It's how the dashboard's "Deployed pipelines" panel and the event
6
+ * reporting Lambda resolve incoming events to a pipeline definition.
7
+ *
8
+ * Two failure modes are handled here:
9
+ *
10
+ * 1. Platform unreachable at deploy time. The CDK stack lands in AWS but the
11
+ * registry POST fails. Without retry the deploy command would have to
12
+ * re-run cdk-deploy (slow, sometimes blocked by stack state) just to
13
+ * record the ARN. Instead, we write a pending intent to a local file;
14
+ * `pipeline-manager pipeline register` drains them.
15
+ *
16
+ * 2. User explicitly invokes `pipeline-manager pipeline register --id <pipelineId>`
17
+ * after a successful deploy that didn't register. Same path: rebuilds
18
+ * the ARN from STS, POSTs to the platform.
19
+ *
20
+ * Pending intents store ONLY the registration payload — never tokens or URLs.
21
+ * Auth is supplied by whichever command drains the intent (deploy or register),
22
+ * so a stale intent file is never an authentication risk.
23
+ */
24
+ /**
25
+ * Platform-sourced registry pull target for the CDK synth, derived from the
26
+ * platform base URL the CLI deploys against (the same host that serves the
27
+ * registry `/v2/` data path via nginx). Reuses pipeline-core's
28
+ * `parsePlatformBaseUrl` so host/port parsing stays identical on both sides.
29
+ * Returns `undefined` for an unparseable/absent URL. (Only the pull host/port
30
+ * are returned — `registry.http` describes the in-cluster push hop, which the
31
+ * synth never reads, so deriving it from the public URL would be misleading.)
32
+ */
33
+ export declare function registryOverrideFromBaseUrl(baseUrl: string | undefined): {
34
+ pullHost: string;
35
+ pullPort: number;
36
+ } | undefined;
37
+ /**
38
+ * Bake the platform-derived registry pull target into the synth props
39
+ * (`BuilderProps.registry`) so the CodeBuild image URIs use a host AWS CodeBuild
40
+ * can resolve — not the in-cluster `registry:5000` default. Shared by the
41
+ * `deploy` and `synth` commands.
42
+ *
43
+ * An explicit `IMAGE_REGISTRY_PULL_HOST` in the synth env takes precedence, so
44
+ * this is a no-op when it is set (the synth's `loadRegistryConfig` already
45
+ * honors it). Also a no-op for an unparseable/absent base URL.
46
+ */
47
+ export declare function bakePlatformRegistry(props: Record<string, unknown>, baseUrl: string | undefined): void;
48
+ /** Shape of the body POSTed to /api/pipelines/registry. */
49
+ export interface RegistryPayload {
50
+ pipelineId: string;
51
+ orgId: string;
52
+ pipelineName: string;
53
+ region: string;
54
+ project: string;
55
+ organization: string;
56
+ stackName: string;
57
+ }
58
+ /** Pipeline fields needed to construct a RegistryPayload. */
59
+ export interface PipelineForRegistry {
60
+ id: string;
61
+ orgId: string;
62
+ pipelineName?: string;
63
+ project: string;
64
+ organization: string;
65
+ }
66
+ /**
67
+ * Build a RegistryPayload from the platform's pipeline metadata. No AWS calls
68
+ * and no ARN/account: the events Lambda attributes events via the pipeline's
69
+ * `pb.pipeline-id` tag (= pipelineId), so the registry only needs the stable
70
+ * pipelineId + display metadata.
71
+ */
72
+ export declare function buildRegistryPayload(pipeline: PipelineForRegistry, regionOverride?: string): Promise<RegistryPayload>;
73
+ /**
74
+ * Persist a registration intent for later retry.
75
+ *
76
+ * Intent files are keyed by pipelineId so re-running deploy on the same
77
+ * pipeline overwrites rather than accumulates. We deliberately store the
78
+ * payload plain (not the full pipeline doc, not auth) so a stale file is
79
+ * safe to leave around indefinitely.
80
+ */
81
+ export declare function writePendingIntent(payload: RegistryPayload): Promise<string>;
82
+ /** Remove a pending intent after a successful drain. */
83
+ export declare function clearPendingIntent(pipelineId: string): Promise<void>;
84
+ /** Read all pending intents currently on disk. Empty array if dir doesn't exist. */
85
+ export declare function readPendingIntents(): Promise<RegistryPayload[]>;
86
+ //# sourceMappingURL=registry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/utils/registry.ts"],"names":[],"mappings":"AAWA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH;;;;;;;;GAQG;AACH,wBAAgB,2BAA2B,CACzC,OAAO,EAAE,MAAM,GAAG,SAAS,GAC1B;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,CAGpD;AAED;;;;;;;;;GASG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CAMtG;AAED,2DAA2D;AAC3D,MAAM,WAAW,eAAe;IAC9B,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,6DAA6D;AAC7D,MAAM,WAAW,mBAAmB;IAClC,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;CACtB;AAKD;;;;;GAKG;AACH,wBAAsB,oBAAoB,CACxC,QAAQ,EAAE,mBAAmB,EAC7B,cAAc,CAAC,EAAE,MAAM,GACtB,OAAO,CAAC,eAAe,CAAC,CAgB1B;AAED;;;;;;;GAOG;AACH,wBAAsB,kBAAkB,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,MAAM,CAAC,CAKlF;AAED,wDAAwD;AACxD,wBAAsB,kBAAkB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAQ1E;AAED,oFAAoF;AACpF,wBAAsB,kBAAkB,IAAI,OAAO,CAAC,eAAe,EAAE,CAAC,CAqBrE"}