@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,155 @@
1
+ // Copyright 2026 Pipeline Builder Contributors
2
+ // SPDX-License-Identifier: Apache-2.0
3
+ import {} from 'commander';
4
+ import pico from 'picocolors';
5
+ import { ApiClient } from './api-client.js';
6
+ import { getSecretValue } from './aws-secrets.js';
7
+ import { getConfigWithOptions } from './config-loader.js';
8
+ import { printError, printInfo, printKeyValue, printSection, printSuccess, printWarning } from './output-utils.js';
9
+ import { formatDuration, generateExecutionId } from '../config/cli.constants.js';
10
+ const { bold, cyan, green, magenta } = pico;
11
+ /**
12
+ * Print command header with section title and execution ID.
13
+ * Returns the execution ID for use in error handlers and summaries.
14
+ *
15
+ * Pass `{ quiet: true }` to suppress the decorative output while still getting the
16
+ * id — used both by `--quiet` callers and by `--json` commands (so the banner
17
+ * doesn't land on stdout ahead of the JSON payload and break `… --json | jq`).
18
+ */
19
+ export function printCommandHeader(title, subtitle, opts) {
20
+ const executionId = generateExecutionId();
21
+ if (!opts?.quiet) {
22
+ printSection(title);
23
+ console.log(`${magenta(`[EXE-${executionId}]`)} ${cyan(bold(subtitle || title))}`);
24
+ console.log('');
25
+ }
26
+ return executionId;
27
+ }
28
+ /** Attach the standard `--verify-ssl` / `--no-verify-ssl` pair. The two together
29
+ * give Commander an explicit tri-state `options.verifySsl` (true/false/undefined).
30
+ * Returns the command so it composes: `withSslOptions(program.command('x'))…`. */
31
+ export function withSslOptions(cmd) {
32
+ return cmd
33
+ .option('--verify-ssl', 'Enable SSL certificate verification')
34
+ .option('--no-verify-ssl', 'Disable SSL certificate verification');
35
+ }
36
+ /** Attach the standard `--region <region>` option (one canonical description).
37
+ * Region resolution (flag → AWS_REGION → CDK_DEFAULT_REGION → us-east-1) lives in
38
+ * aws-env.resolveAwsRegion. */
39
+ export function withRegionOption(cmd) {
40
+ return cmd.option('--region <region>', 'AWS region (defaults to AWS_REGION / CDK_DEFAULT_REGION / us-east-1)');
41
+ }
42
+ /** Attach the standard `--profile <profile>` option (one canonical description).
43
+ * No default is pinned so the AWS default credential chain (env vars, SSO, roles)
44
+ * works out of the box; --profile only selects a shared profile when set. */
45
+ export function withProfileOption(cmd) {
46
+ return cmd.option('--profile <profile>', 'AWS CLI profile (defaults to the standard AWS credential chain)');
47
+ }
48
+ /**
49
+ * Print execution summary with ID, duration, and status.
50
+ */
51
+ export function printExecutionSummary(executionId, duration) {
52
+ console.log('');
53
+ printKeyValue({
54
+ 'Execution ID': executionId,
55
+ 'Duration': formatDuration(duration),
56
+ 'Status': green('✓ Success'),
57
+ });
58
+ }
59
+ /**
60
+ * Warn if SSL verification is disabled.
61
+ */
62
+ export function printSslWarning(verifySsl) {
63
+ if (verifySsl === false) {
64
+ printWarning('SSL certificate verification is DISABLED');
65
+ console.log('');
66
+ }
67
+ }
68
+ /**
69
+ * Resolve a platform auth token using two methods (in priority order):
70
+ *
71
+ * 1. PLATFORM_TOKEN env var
72
+ * 2. --store-tokens flag → fetch credentials from AWS Secrets Manager, then authenticate
73
+ *
74
+ * @returns JWT token string
75
+ */
76
+ export async function resolveToken(options) {
77
+ // Path 1: PLATFORM_TOKEN env var
78
+ if (process.env.PLATFORM_TOKEN) {
79
+ printInfo('Using PLATFORM_TOKEN from environment');
80
+ return process.env.PLATFORM_TOKEN;
81
+ }
82
+ // Path 2: --store-tokens → read PLATFORM_SECRET_NAME from Secrets Manager
83
+ if (options.storeTokens) {
84
+ const secretName = process.env.PLATFORM_SECRET_NAME;
85
+ if (!secretName) {
86
+ throw new Error('PLATFORM_SECRET_NAME env var is required with --store-tokens');
87
+ }
88
+ printInfo('Fetching secret from Secrets Manager', { secret: secretName });
89
+ const secretJson = await getSecretValue(secretName, { region: options.region, profile: options.profile });
90
+ const secret = JSON.parse(secretJson);
91
+ printSuccess('Secret retrieved from Secrets Manager');
92
+ // store-token writes the JWT to the `password` field (schema: { username: orgId,
93
+ // password: JWT } — satisfies CodeBuild's secretsManagerCredentials), not `accessToken`.
94
+ if (!secret.password) {
95
+ throw new Error('Secret missing password (JWT) — run "pipeline-manager infra store-token" to generate');
96
+ }
97
+ printInfo('Using stored JWT token');
98
+ return secret.password;
99
+ }
100
+ throw new Error('Authentication required. Use one of:\n' +
101
+ ' - Set PLATFORM_TOKEN environment variable\n' +
102
+ ' - Pass --store-tokens with PLATFORM_SECRET_NAME env var');
103
+ }
104
+ /**
105
+ * Initialize and return an authenticated API client.
106
+ *
107
+ * Supports two auth methods:
108
+ * 1. PLATFORM_TOKEN env var
109
+ * 2. --store-tokens + PLATFORM_SECRET_NAME env var → fetch token from Secrets Manager
110
+ */
111
+ export async function createAuthenticatedClientAsync(options) {
112
+ // Resolve token and set it in env so ApiClient/getConfig can find it
113
+ if (!process.env.PLATFORM_TOKEN) {
114
+ const token = await resolveToken(options);
115
+ process.env.PLATFORM_TOKEN = token;
116
+ }
117
+ return createAuthenticatedClient(options);
118
+ }
119
+ /**
120
+ * Initialize and return an authenticated API client (sync — requires PLATFORM_TOKEN).
121
+ * Use createAuthenticatedClientAsync for --store-tokens support.
122
+ */
123
+ export function createAuthenticatedClient(options) {
124
+ const config = getConfigWithOptions(options);
125
+ printInfo('Initializing API client', { baseUrl: config.api.baseUrl });
126
+ const client = new ApiClient(config);
127
+ if (!client.isAuthenticated()) {
128
+ printError('Not authenticated', { hint: 'Set PLATFORM_TOKEN or use --store-tokens' });
129
+ throw new Error('Authentication required');
130
+ }
131
+ printSuccess('API client initialized');
132
+ return client;
133
+ }
134
+ /**
135
+ * Validate a required entity ID (ULID or UUID format).
136
+ * Returns trimmed ID.
137
+ *
138
+ * @throws Error if ID is empty
139
+ */
140
+ export function validateEntityId(id, entityName) {
141
+ if (!id || typeof id !== 'string' || id.trim().length === 0) {
142
+ printError(`Invalid ${entityName} ID`, { provided: id });
143
+ throw new Error(`${entityName} ID must be a non-empty string`);
144
+ }
145
+ const trimmed = id.trim();
146
+ if (trimmed.length !== 26 && trimmed.length !== 36) {
147
+ printWarning(`${entityName} ID format may be invalid`, {
148
+ provided: trimmed,
149
+ expectedLength: '26 characters (ULID) or 36 characters (UUID)',
150
+ actualLength: trimmed.length,
151
+ });
152
+ }
153
+ return trimmed;
154
+ }
155
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29tbWFuZC11dGlscy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy91dGlscy9jb21tYW5kLXV0aWxzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLCtDQUErQztBQUMvQyxzQ0FBc0M7QUFFdEMsT0FBTyxFQUFnQixNQUFNLFdBQVcsQ0FBQztBQUN6QyxPQUFPLElBQUksTUFBTSxZQUFZLENBQUM7QUFDOUIsT0FBTyxFQUFFLFNBQVMsRUFBRSxNQUFNLGlCQUFpQixDQUFDO0FBQzVDLE9BQU8sRUFBRSxjQUFjLEVBQUUsTUFBTSxrQkFBa0IsQ0FBQztBQUNsRCxPQUFPLEVBQWUsb0JBQW9CLEVBQUUsTUFBTSxvQkFBb0IsQ0FBQztBQUN2RSxPQUFPLEVBQUUsVUFBVSxFQUFFLFNBQVMsRUFBRSxhQUFhLEVBQUUsWUFBWSxFQUFFLFlBQVksRUFBRSxZQUFZLEVBQUUsTUFBTSxtQkFBbUIsQ0FBQztBQUNuSCxPQUFPLEVBQUUsY0FBYyxFQUFFLG1CQUFtQixFQUFFLE1BQU0sNEJBQTRCLENBQUM7QUFFakYsTUFBTSxFQUFFLElBQUksRUFBRSxJQUFJLEVBQUUsS0FBSyxFQUFFLE9BQU8sRUFBRSxHQUFHLElBQUksQ0FBQztBQUU1Qzs7Ozs7OztHQU9HO0FBQ0gsTUFBTSxVQUFVLGtCQUFrQixDQUFDLEtBQWEsRUFBRSxRQUFpQixFQUFFLElBQTBCO0lBQzdGLE1BQU0sV0FBVyxHQUFHLG1CQUFtQixFQUFFLENBQUM7SUFDMUMsSUFBSSxDQUFDLElBQUksRUFBRSxLQUFLLEVBQUUsQ0FBQztRQUNqQixZQUFZLENBQUMsS0FBSyxDQUFDLENBQUM7UUFDcEIsT0FBTyxDQUFDLEdBQUcsQ0FBQyxHQUFHLE9BQU8sQ0FBQyxRQUFRLFdBQVcsR0FBRyxDQUFDLElBQUksSUFBSSxDQUFDLElBQUksQ0FBQyxRQUFRLElBQUksS0FBSyxDQUFDLENBQUMsRUFBRSxDQUFDLENBQUM7UUFDbkYsT0FBTyxDQUFDLEdBQUcsQ0FBQyxFQUFF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SyxFQUFFLEVBQUUsUUFBUSxFQUFFLEVBQUUsRUFBRSxDQUFDLENBQUM7UUFDekQsTUFBTSxJQUFJLEtBQUssQ0FBQyxHQUFHLFVBQVUsZ0NBQWdDLENBQUMsQ0FBQztJQUNqRSxDQUFDO0lBRUQsTUFBTSxPQUFPLEdBQUcsRUFBRSxDQUFDLElBQUksRUFBRSxDQUFDO0lBRTFCLElBQUksT0FBTyxDQUFDLE1BQU0sS0FBSyxFQUFFLElBQUksT0FBTyxDQUFDLE1BQU0sS0FBSyxFQUFFLEVBQUUsQ0FBQztRQUNuRCxZQUFZLENBQUMsR0FBRyxVQUFVLDJCQUEyQixFQUFFO1lBQ3JELFFBQVEsRUFBRSxPQUFPO1lBQ2pCLGNBQWMsRUFBRSw4Q0FBOEM7WUFDOUQsWUFBWSxFQUFFLE9BQU8sQ0FBQyxNQUFNO1NBQzdCLENBQUMsQ0FBQztJQUNMLENBQUM7SUFFRCxPQUFPLE9BQU8sQ0FBQztBQUNqQixDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLy8gQ29weXJpZ2h0IDIwMjYgUGlwZWxpbmUgQnVpbGRlciBDb250cmlidXRvcnNcbi8vIFNQRFgtTGljZW5zZS1JZGVudGlmaWVyOiBBcGFjaGUtMi4wXG5cbmltcG9ydCB7IHR5cGUgQ29tbWFuZCB9IGZyb20gJ2NvbW1hbmRlcic7XG5pbXBvcnQgcGljbyBmcm9tICdwaWNvY29sb3JzJztcbmltcG9ydCB7IEFwaUNsaWVudCB9IGZyb20gJy4vYXBpLWNsaWVudC5qcyc7XG5pbXBvcnQgeyBnZXRTZWNyZXRWYWx1ZSB9IGZyb20gJy4vYXdzLXNlY3JldHMuanMnO1xuaW1wb3J0IHsgdHlwZSBDb25maWcsIGdldENvbmZpZ1dpdGhPcHRpb25zIH0gZnJvbSAnLi9jb25maWctbG9hZGVyLmpzJztcbmltcG9ydCB7IHByaW50RXJyb3IsIHByaW50SW5mbywgcHJpbnRLZXlWYWx1ZSwgcHJpbnRTZWN0aW9uLCBwcmludFN1Y2Nlc3MsIHByaW50V2FybmluZyB9IGZyb20gJy4vb3V0cHV0LXV0aWxzLmpzJztcbmltcG9ydCB7IGZvcm1hdER1cmF0aW9uLCBnZW5lcmF0ZUV4ZWN1dGlvbklkIH0gZnJvbSAnLi4vY29uZmlnL2NsaS5jb25zdGFudHMuanMnO1xuXG5jb25zdCB7IGJvbGQsIGN5YW4sIGdyZWVuLCBtYWdlbnRhIH0gPSBwaWNvO1xuXG4vKipcbiAqIFByaW50IGNvbW1hbmQgaGVhZGVyIHdpdGggc2VjdGlvbiB0aXRsZSBhbmQgZXhlY3V0aW9uIElELlxuICogUmV0dXJucyB0aGUgZXhlY3V0aW9uIElEIGZvciB1c2UgaW4gZXJyb3IgaGFuZGxlcnMgYW5kIHN1bW1hcmllcy5cbiAqXG4gKiBQYXNzIGB7IHF1aWV0OiB0cnVlIH1gIHRvIHN1cHByZXNzIHRoZSBkZWNvcmF0aXZlIG91dHB1dCB3aGlsZSBzdGlsbCBnZXR0aW5nIHRoZVxuICogaWQg4oCUIHVzZWQgYm90aCBieSBgLS1xdWlldGAgY2FsbGVycyBhbmQgYnkgYC0tanNvbmAgY29tbWFuZHMgKHNvIHRoZSBiYW5uZXJcbiAqIGRvZXNuJ3QgbGFuZCBvbiBzdGRvdXQgYWhlYWQgb2YgdGhlIEpTT04gcGF5bG9hZCBhbmQgYnJlYWsgYOKApiAtLWpzb24gfCBqcWApLlxuICovXG5leHBvcnQgZnVuY3Rpb24gcHJpbnRDb21tYW5kSGVhZGVyKHRpdGxlOiBzdHJpbmcsIHN1YnRpdGxlPzogc3RyaW5nLCBvcHRzPzogeyBxdWlldD86IGJvb2xlYW4gfSk6IHN0cmluZyB7XG4gIGNvbnN0IGV4ZWN1dGlvbklkID0gZ2VuZXJhdGVFeGVjdXRpb25JZCgpO1xuICBpZiAoIW9wdHM/LnF1aWV0KSB7XG4gICAgcHJpbnRTZWN0aW9uKHRpdGxlKTtcbiAgICBjb25zb2xlLmxvZyhgJHttYWdlbnRhKGBbRVhFLSR7ZXhlY3V0aW9uSWR9XWApfSAke2N5YW4oYm9sZChzdWJ0aXRsZSB8fCB0aXRsZSkpfWApO1xuICAgIGNvbnNvbGUubG9nKCcnKTtcbiAgfVxuICByZXR1cm4gZXhlY3V0aW9uSWQ7XG59XG5cbi8qKiBBdHRhY2ggdGhlIHN0YW5kYXJkIGAtLXZlcmlmeS1zc2xgIC8gYC0tbm8tdmVyaWZ5LXNzbGAgcGFpci4gVGhlIHR3byB0b2dldGhlclxuICogIGdpdmUgQ29tbWFuZGVyIGFuIGV4cGxpY2l0IHRyaS1zdGF0ZSBgb3B0aW9ucy52ZXJpZnlTc2xgICh0cnVlL2ZhbHNlL3VuZGVmaW5lZCkuXG4gKiAgUmV0dXJucyB0aGUgY29tbWFuZCBzbyBpdCBjb21wb3NlczogYHdpdGhTc2xPcHRpb25zKHByb2dyYW0uY29tbWFuZCgneCcpKeKApmAuICovXG5leHBvcnQgZnVuY3Rpb24gd2l0aFNzbE9wdGlvbnM8VCBleHRlbmRzIENvbW1hbmQ+KGNtZDogVCk6IFQge1xuICByZXR1cm4gY21kXG4gICAgLm9wdGlvbignLS12ZXJpZnktc3NsJywgJ0VuYWJsZSBTU0wgY2VydGlmaWNhdGUgdmVyaWZpY2F0aW9uJylcbiAgICAub3B0aW9uKCctLW5vLXZlcmlmeS1zc2wnLCAnRGlzYWJsZSBTU0wgY2VydGlmaWNhdGUgdmVyaWZpY2F0aW9uJykgYXMgVDtcbn1cblxuLyoqIEF0dGFjaCB0aGUgc3RhbmRhcmQgYC0tcmVnaW9uIDxyZWdpb24+YCBvcHRpb24gKG9uZSBjYW5vbmljYWwgZGVzY3JpcHRpb24pLlxuICogIFJlZ2lvbiByZXNvbHV0aW9uIChmbGFnIOKGkiBBV1NfUkVHSU9OIOKGkiBDREtfREVGQVVMVF9SRUdJT04g4oaSIHVzLWVhc3QtMSkgbGl2ZXMgaW5cbiAqICBhd3MtZW52LnJlc29sdmVBd3NSZWdpb24uICovXG5leHBvcnQgZnVuY3Rpb24gd2l0aFJlZ2lvbk9wdGlvbjxUIGV4dGVuZHMgQ29tbWFuZD4oY21kOiBUKTogVCB7XG4gIHJldHVybiBjbWQub3B0aW9uKCctLXJlZ2lvbiA8cmVnaW9uPicsICdBV1MgcmVnaW9uIChkZWZhdWx0cyB0byBBV1NfUkVHSU9OIC8gQ0RLX0RFRkFVTFRfUkVHSU9OIC8gdXMtZWFzdC0xKScpIGFzIFQ7XG59XG5cbi8qKiBBdHRhY2ggdGhlIHN0YW5kYXJkIGAtLXByb2ZpbGUgPHByb2ZpbGU+YCBvcHRpb24gKG9uZSBjYW5vbmljYWwgZGVzY3JpcHRpb24pLlxuICogIE5vIGRlZmF1bHQgaXMgcGlubmVkIHNvIHRoZSBBV1MgZGVmYXVsdCBjcmVkZW50aWFsIGNoYWluIChlbnYgdmFycywgU1NPLCByb2xlcylcbiAqICB3b3JrcyBvdXQgb2YgdGhlIGJveDsgLS1wcm9maWxlIG9ubHkgc2VsZWN0cyBhIHNoYXJlZCBwcm9maWxlIHdoZW4gc2V0LiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHdpdGhQcm9maWxlT3B0aW9uPFQgZXh0ZW5kcyBDb21tYW5kPihjbWQ6IFQpOiBUIHtcbiAgcmV0dXJuIGNtZC5vcHRpb24oJy0tcHJvZmlsZSA8cHJvZmlsZT4nLCAnQVdTIENMSSBwcm9maWxlIChkZWZhdWx0cyB0byB0aGUgc3RhbmRhcmQgQVdTIGNyZWRlbnRpYWwgY2hhaW4pJykgYXMgVDtcbn1cblxuLyoqXG4gKiBQcmludCBleGVjdXRpb24gc3VtbWFyeSB3aXRoIElELCBkdXJhdGlvbiwgYW5kIHN0YXR1cy5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHByaW50RXhlY3V0aW9uU3VtbWFyeShleGVjdXRpb25JZDogc3RyaW5nLCBkdXJhdGlvbjogbnVtYmVyKTogdm9pZCB7XG4gIGNvbnNvbGUubG9nKCcnKTtcbiAgcHJpbnRLZXlWYWx1ZSh7XG4gICAgJ0V4ZWN1dGlvbiBJRCc6IGV4ZWN1dGlvbklkLFxuICAgICdEdXJhdGlvbic6IGZvcm1hdER1cmF0aW9uKGR1cmF0aW9uKSxcbiAgICAnU3RhdHVzJzogZ3JlZW4oJ+KckyBTdWNjZXNzJyksXG4gIH0pO1xufVxuXG4vKipcbiAqIFdhcm4gaWYgU1NMIHZlcmlmaWNhdGlvbiBpcyBkaXNhYmxlZC5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHByaW50U3NsV2FybmluZyh2ZXJpZnlTc2w/OiBib29sZWFuKTogdm9pZCB7XG4gIGlmICh2ZXJpZnlTc2wgPT09IGZhbHNlKSB7XG4gICAgcHJpbnRXYXJuaW5nKCdTU0wgY2VydGlmaWNhdGUgdmVyaWZpY2F0aW9uIGlzIERJU0FCTEVEJyk7XG4gICAgY29uc29sZS5sb2coJycpO1xuICB9XG59XG5cbi8qKlxuICogUmVzb2x2ZSBhIHBsYXRmb3JtIGF1dGggdG9rZW4gdXNpbmcgdHdvIG1ldGhvZHMgKGluIHByaW9yaXR5IG9yZGVyKTpcbiAqXG4gKiAxLiBQTEFURk9STV9UT0tFTiBlbnYgdmFyXG4gKiAyLiAtLXN0b3JlLXRva2VucyBmbGFnIOKGkiBmZXRjaCBjcmVkZW50aWFscyBmcm9tIEFXUyBTZWNyZXRzIE1hbmFnZXIsIHRoZW4gYXV0aGVudGljYXRlXG4gKlxuICogQHJldHVybnMgSldUIHRva2VuIHN0cmluZ1xuICovXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gcmVzb2x2ZVRva2VuKG9wdGlvbnM6IHtcbiAgc3RvcmVUb2tlbnM/OiBib29sZWFuO1xuICB2ZXJpZnlTc2w/OiBib29sZWFuO1xuICByZWdpb24/OiBzdHJpbmc7XG4gIHByb2ZpbGU/OiBzdHJpbmc7XG59KTogUHJvbWlzZTxzdHJpbmc+IHtcbiAgLy8gUGF0aCAxOiBQTEFURk9STV9UT0tFTiBlbnYgdmFyXG4gIGlmIChwcm9jZXNzLmVudi5QTEFURk9STV9UT0tFTikge1xuICAgIHByaW50SW5mbygnVXNpbmcgUExBVEZPUk1fVE9LRU4gZnJvbSBlbnZpcm9ubWVudCcpO1xuICAgIHJldHVybiBwcm9jZXNzLmVudi5QTEFURk9STV9UT0tFTjtcbiAgfVxuXG4gIC8vIFBhdGggMjogLS1zdG9yZS10b2tlbnMg4oaSIHJlYWQgUExBVEZPUk1fU0VDUkVUX05BTUUgZnJvbSBTZWNyZXRzIE1hbmFnZXJcbiAgaWYgKG9wdGlvbnMuc3RvcmVUb2tlbnMpIHtcbiAgICBjb25zdCBzZWNyZXROYW1lID0gcHJvY2Vzcy5lbnYuUExBVEZPUk1fU0VDUkVUX05BTUU7XG4gICAgaWYgKCFzZWNyZXROYW1lKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ1BMQVRGT1JNX1NFQ1JFVF9OQU1FIGVudiB2YXIgaXMgcmVxdWlyZWQgd2l0aCAtLXN0b3JlLXRva2VucycpO1xuICAgIH1cblxuICAgIHByaW50SW5mbygnRmV0Y2hpbmcgc2VjcmV0IGZyb20gU2VjcmV0cyBNYW5hZ2VyJywgeyBzZWNyZXQ6IHNlY3JldE5hbWUgfSk7XG5cbiAgICBjb25zdCBzZWNyZXRKc29uID0gYXdhaXQgZ2V0U2VjcmV0VmFsdWUoc2VjcmV0TmFtZSwgeyByZWdpb246IG9wdGlvbnMucmVnaW9uLCBwcm9maWxlOiBvcHRpb25zLnByb2ZpbGUgfSk7XG4gICAgY29uc3Qgc2VjcmV0ID0gSlNPTi5wYXJzZShzZWNyZXRKc29uKSBhcyBSZWNvcmQ8c3RyaW5nLCBzdHJpbmc+O1xuICAgIHByaW50U3VjY2VzcygnU2VjcmV0IHJldHJpZXZlZCBmcm9tIFNlY3JldHMgTWFuYWdlcicpO1xuXG4gICAgLy8gc3RvcmUtdG9rZW4gd3JpdGVzIHRoZSBKV1QgdG8gdGhlIGBwYXNzd29yZGAgZmllbGQgKHNjaGVtYTogeyB1c2VybmFtZTogb3JnSWQsXG4gICAgLy8gcGFzc3dvcmQ6IEpXVCB9IOKAlCBzYXRpc2ZpZXMgQ29kZUJ1aWxkJ3Mgc2VjcmV0c01hbmFnZXJDcmVkZW50aWFscyksIG5vdCBgYWNjZXNzVG9rZW5gLlxuICAgIGlmICghc2VjcmV0LnBhc3N3b3JkKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ1NlY3JldCBtaXNzaW5nIHBhc3N3b3JkIChKV1QpIOKAlCBydW4gXCJwaXBlbGluZS1tYW5hZ2VyIGluZnJhIHN0b3JlLXRva2VuXCIgdG8gZ2VuZXJhdGUnKTtcbiAgICB9XG5cbiAgICBwcmludEluZm8oJ1VzaW5nIHN0b3JlZCBKV1QgdG9rZW4nKTtcbiAgICByZXR1cm4gc2VjcmV0LnBhc3N3b3JkO1xuICB9XG5cbiAgdGhyb3cgbmV3IEVycm9yKFxuICAgICdBdXRoZW50aWNhdGlvbiByZXF1aXJlZC4gVXNlIG9uZSBvZjpcXG4nICtcbiAgICAnICAtIFNldCBQTEFURk9STV9UT0tFTiBlbnZpcm9ubWVudCB2YXJpYWJsZVxcbicgK1xuICAgICcgIC0gUGFzcyAtLXN0b3JlLXRva2VucyB3aXRoIFBMQVRGT1JNX1NFQ1JFVF9OQU1FIGVudiB2YXInLFxuICApO1xufVxuXG4vKipcbiAqIEluaXRpYWxpemUgYW5kIHJldHVybiBhbiBhdXRoZW50aWNhdGVkIEFQSSBjbGllbnQuXG4gKlxuICogU3VwcG9ydHMgdHdvIGF1dGggbWV0aG9kczpcbiAqIDEuIFBMQVRGT1JNX1RPS0VOIGVudiB2YXJcbiAqIDIuIC0tc3RvcmUtdG9rZW5zICsgUExBVEZPUk1fU0VDUkVUX05BTUUgZW52IHZhciDihpIgZmV0Y2ggdG9rZW4gZnJvbSBTZWNyZXRzIE1hbmFnZXJcbiAqL1xuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGNyZWF0ZUF1dGhlbnRpY2F0ZWRDbGllbnRBc3luYyhvcHRpb25zOiB7XG4gIHN0b3JlVG9rZW5zPzogYm9vbGVhbjtcbiAgdmVyaWZ5U3NsPzogYm9vbGVhbjtcbiAgcmVnaW9uPzogc3RyaW5nO1xuICBwcm9maWxlPzogc3RyaW5nO1xufSk6IFByb21pc2U8QXBpQ2xpZW50PiB7XG4gIC8vIFJlc29sdmUgdG9rZW4gYW5kIHNldCBpdCBpbiBlbnYgc28gQXBpQ2xpZW50L2dldENvbmZpZyBjYW4gZmluZCBpdFxuICBpZiAoIXByb2Nlc3MuZW52LlBMQVRGT1JNX1RPS0VOKSB7XG4gICAgY29uc3QgdG9rZW4gPSBhd2FpdCByZXNvbHZlVG9rZW4ob3B0aW9ucyk7XG4gICAgcHJvY2Vzcy5lbnYuUExBVEZPUk1fVE9LRU4gPSB0b2tlbjtcbiAgfVxuXG4gIHJldHVybiBjcmVhdGVBdXRoZW50aWNhdGVkQ2xpZW50KG9wdGlvbnMpO1xufVxuXG4vKipcbiAqIEluaXRpYWxpemUgYW5kIHJldHVybiBhbiBhdXRoZW50aWNhdGVkIEFQSSBjbGllbnQgKHN5bmMg4oCUIHJlcXVpcmVzIFBMQVRGT1JNX1RPS0VOKS5cbiAqIFVzZSBjcmVhdGVBdXRoZW50aWNhdGVkQ2xpZW50QXN5bmMgZm9yIC0tc3RvcmUtdG9rZW5zIHN1cHBvcnQuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBjcmVhdGVBdXRoZW50aWNhdGVkQ2xpZW50KG9wdGlvbnM6IHsgdmVyaWZ5U3NsPzogYm9vbGVhbiB9KTogQXBpQ2xpZW50IHtcbiAgY29uc3QgY29uZmlnOiBDb25maWcgPSBnZXRDb25maWdXaXRoT3B0aW9ucyhvcHRpb25zKTtcbiAgcHJpbnRJbmZvKCdJbml0aWFsaXppbmcgQVBJIGNsaWVudCcsIHsgYmFzZVVybDogY29uZmlnLmFwaS5iYXNlVXJsIH0pO1xuICBjb25zdCBjbGllbnQgPSBuZXcgQXBpQ2xpZW50KGNvbmZpZyk7XG5cbiAgaWYgKCFjbGllbnQuaXNBdXRoZW50aWNhdGVkKCkpIHtcbiAgICBwcmludEVycm9yKCdOb3QgYXV0aGVudGljYXRlZCcsIHsgaGludDogJ1NldCBQTEFURk9STV9UT0tFTiBvciB1c2UgLS1zdG9yZS10b2tlbnMnIH0pO1xuICAgIHRocm93IG5ldyBFcnJvcignQXV0aGVudGljYXRpb24gcmVxdWlyZWQnKTtcbiAgfVxuXG4gIHByaW50U3VjY2VzcygnQVBJIGNsaWVudCBpbml0aWFsaXplZCcpO1xuICByZXR1cm4gY2xpZW50O1xufVxuXG4vKipcbiAqIFZhbGlkYXRlIGEgcmVxdWlyZWQgZW50aXR5IElEIChVTElEIG9yIFVVSUQgZm9ybWF0KS5cbiAqIFJldHVybnMgdHJpbW1lZCBJRC5cbiAqXG4gKiBAdGhyb3dzIEVycm9yIGlmIElEIGlzIGVtcHR5XG4gKi9cbmV4cG9ydCBmdW5jdGlvbiB2YWxpZGF0ZUVudGl0eUlkKGlkOiBzdHJpbmcgfCB1bmRlZmluZWQsIGVudGl0eU5hbWU6IHN0cmluZyk6IHN0cmluZyB7XG4gIGlmICghaWQgfHwgdHlwZW9mIGlkICE9PSAnc3RyaW5nJyB8fCBpZC50cmltKCkubGVuZ3RoID09PSAwKSB7XG4gICAgcHJpbnRFcnJvcihgSW52YWxpZCAke2VudGl0eU5hbWV9IElEYCwgeyBwcm92aWRlZDogaWQgfSk7XG4gICAgdGhyb3cgbmV3IEVycm9yKGAke2VudGl0eU5hbWV9IElEIG11c3QgYmUgYSBub24tZW1wdHkgc3RyaW5nYCk7XG4gIH1cblxuICBjb25zdCB0cmltbWVkID0gaWQudHJpbSgpO1xuXG4gIGlmICh0cmltbWVkLmxlbmd0aCAhPT0gMjYgJiYgdHJpbW1lZC5sZW5ndGggIT09IDM2KSB7XG4gICAgcHJpbnRXYXJuaW5nKGAke2VudGl0eU5hbWV9IElEIGZvcm1hdCBtYXkgYmUgaW52YWxpZGAsIHtcbiAgICAgIHByb3ZpZGVkOiB0cmltbWVkLFxuICAgICAgZXhwZWN0ZWRMZW5ndGg6ICcyNiBjaGFyYWN0ZXJzIChVTElEKSBvciAzNiBjaGFyYWN0ZXJzIChVVUlEKScsXG4gICAgICBhY3R1YWxMZW5ndGg6IHRyaW1tZWQubGVuZ3RoLFxuICAgIH0pO1xuICB9XG5cbiAgcmV0dXJuIHRyaW1tZWQ7XG59XG5cbiJdfQ==
@@ -0,0 +1,33 @@
1
+ import { type Config } from '../types/index.js';
2
+ export type { Config };
3
+ /**
4
+ * Load configuration from files and environment.
5
+ *
6
+ * Priority (last wins):
7
+ * 1. Built-in defaults
8
+ * 2. User config file: ~/.pipeline-manager/config.yml
9
+ * 3. Project config file: CLI_CONFIG_PATH or ./config.yml
10
+ * 4. Environment variables
11
+ *
12
+ * Auth token MUST come from PLATFORM_TOKEN env var (never from config file).
13
+ */
14
+ /**
15
+ * Resolve the API/connection config (base URL, SSL, timeouts) from files and
16
+ * environment WITHOUT requiring `PLATFORM_TOKEN`.
17
+ *
18
+ * This is the token-optional slice of {@link getConfig}. The pre-auth login step
19
+ * (`ensurePlatformToken`) needs the resolved base URL to POST `/api/auth/login`
20
+ * *before* any token exists — calling `getConfig()` there would throw on the
21
+ * missing token and make inline-login dead-on-arrival.
22
+ */
23
+ export declare function getApiConfig(): Omit<Config, 'auth'>;
24
+ export declare function getConfig(): Config;
25
+ /**
26
+ * Load configuration, optionally disabling SSL based on command options.
27
+ * Replaces the repeated pattern:
28
+ * `options.verifySsl === false ? withSSLDisabled(getConfig()) : getConfig()`
29
+ */
30
+ export declare function getConfigWithOptions(options: {
31
+ verifySsl?: boolean;
32
+ }): Config;
33
+ //# sourceMappingURL=config-loader.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config-loader.d.ts","sourceRoot":"","sources":["../../src/utils/config-loader.ts"],"names":[],"mappings":"AAUA,OAAO,EAAE,KAAK,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAKhD,YAAY,EAAE,MAAM,EAAE,CAAC;AA6CvB;;;;;;;;;;GAUG;AACH;;;;;;;;GAQG;AACH,wBAAgB,YAAY,IAAI,IAAI,CAAC,MAAM,EAAE,MAAM,CAAC,CAoCnD;AAED,wBAAgB,SAAS,IAAI,MAAM,CAyBlC;AAaD;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE;IAAE,SAAS,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,MAAM,CAU7E"}
@@ -0,0 +1,154 @@
1
+ // Copyright 2026 Pipeline Builder Contributors
2
+ // SPDX-License-Identifier: Apache-2.0
3
+ import * as fs from 'fs';
4
+ import { fileURLToPath } from 'node:url';
5
+ import * as os from 'os';
6
+ import * as path from 'path';
7
+ import * as yaml from 'yaml';
8
+ import { printDebug, printError, printWarning } from './output-utils.js';
9
+ import { assertSslDisableAllowed, isProductionEnv } from './tls.js';
10
+ import {} from '../types/index.js';
11
+ // ESM has no __dirname; derive it from this module's URL.
12
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
13
+ /**
14
+ * Default configuration — API endpoint paths and connection settings.
15
+ */
16
+ const defaultConfig = {
17
+ api: {
18
+ baseUrl: 'https://localhost:8443',
19
+ timeout: 30_000,
20
+ pipelineUrl: '/api/pipeline',
21
+ pipelineListUrl: '/api/pipelines',
22
+ pipelineTemplateUrl: '/api/pipeline-templates',
23
+ pluginUrl: '/api/plugin',
24
+ pluginListUrl: '/api/plugins',
25
+ pluginUploadUrl: '/api/plugin/upload',
26
+ rejectUnauthorized: true,
27
+ },
28
+ };
29
+ /** User config file path: ~/.pipeline-manager/config.yml */
30
+ const USER_CONFIG_PATH = path.join(os.homedir(), '.pipeline-manager', 'config.yml');
31
+ /**
32
+ * Load a YAML config file and merge its `api` section into the config.
33
+ * Returns the merged config or the original if file doesn't exist or fails.
34
+ */
35
+ function loadConfigFile(filePath, config) {
36
+ if (!fs.existsSync(filePath))
37
+ return config;
38
+ try {
39
+ printDebug('Loading configuration', { path: filePath });
40
+ const parsed = yaml.parse(fs.readFileSync(filePath, 'utf-8'));
41
+ if (parsed?.auth) {
42
+ printWarning('Auth section in config file is ignored — use PLATFORM_TOKEN env var');
43
+ }
44
+ return { api: { ...config.api, ...parsed?.api } };
45
+ }
46
+ catch (error) {
47
+ printWarning('Failed to load config file, using defaults', {
48
+ path: filePath,
49
+ error: error instanceof Error ? error.message : String(error),
50
+ });
51
+ return config;
52
+ }
53
+ }
54
+ /**
55
+ * Load configuration from files and environment.
56
+ *
57
+ * Priority (last wins):
58
+ * 1. Built-in defaults
59
+ * 2. User config file: ~/.pipeline-manager/config.yml
60
+ * 3. Project config file: CLI_CONFIG_PATH or ./config.yml
61
+ * 4. Environment variables
62
+ *
63
+ * Auth token MUST come from PLATFORM_TOKEN env var (never from config file).
64
+ */
65
+ /**
66
+ * Resolve the API/connection config (base URL, SSL, timeouts) from files and
67
+ * environment WITHOUT requiring `PLATFORM_TOKEN`.
68
+ *
69
+ * This is the token-optional slice of {@link getConfig}. The pre-auth login step
70
+ * (`ensurePlatformToken`) needs the resolved base URL to POST `/api/auth/login`
71
+ * *before* any token exists — calling `getConfig()` there would throw on the
72
+ * missing token and make inline-login dead-on-arrival.
73
+ */
74
+ export function getApiConfig() {
75
+ const projectConfigPath = process.env.CLI_CONFIG_PATH || path.join(__dirname, '../config.yml');
76
+ // Layer 1: defaults → Layer 2: user config → Layer 3: project config
77
+ let config = loadConfigFile(USER_CONFIG_PATH, { ...defaultConfig });
78
+ config = loadConfigFile(projectConfigPath, config);
79
+ // Layer 4: environment variable overrides
80
+ if (process.env.PLATFORM_BASE_URL) {
81
+ config.api.baseUrl = process.env.PLATFORM_BASE_URL;
82
+ printDebug('Using PLATFORM_BASE_URL from environment', { baseUrl: config.api.baseUrl });
83
+ }
84
+ if (process.env.TLS_REJECT_UNAUTHORIZED !== undefined) {
85
+ const disable = process.env.TLS_REJECT_UNAUTHORIZED === '0';
86
+ if (disable && isProductionEnv()) {
87
+ printWarning('Ignoring TLS_REJECT_UNAUTHORIZED=0 in production — SSL verification remains enabled');
88
+ }
89
+ else {
90
+ config.api.rejectUnauthorized = !disable;
91
+ if (disable) {
92
+ printWarning('SSL certificate validation disabled via TLS_REJECT_UNAUTHORIZED=0');
93
+ }
94
+ }
95
+ }
96
+ if (process.env.UPLOAD_TIMEOUT) {
97
+ const parsed = parseInt(process.env.UPLOAD_TIMEOUT, 10);
98
+ if (!isNaN(parsed) && parsed > 0) {
99
+ config.api.uploadTimeout = parsed;
100
+ printDebug('Using UPLOAD_TIMEOUT from environment', { uploadTimeout: `${parsed}ms` });
101
+ }
102
+ else {
103
+ printWarning('Invalid UPLOAD_TIMEOUT value, using default', { provided: process.env.UPLOAD_TIMEOUT });
104
+ }
105
+ }
106
+ return config;
107
+ }
108
+ export function getConfig() {
109
+ const config = getApiConfig();
110
+ // Token is REQUIRED from environment
111
+ const token = process.env.PLATFORM_TOKEN;
112
+ if (!token) {
113
+ printError('PLATFORM_TOKEN environment variable is not set');
114
+ throw new Error('PLATFORM_TOKEN environment variable is required');
115
+ }
116
+ if (token.trim().length === 0) {
117
+ throw new Error('PLATFORM_TOKEN must be a non-empty string');
118
+ }
119
+ if (token.includes(' ')) {
120
+ printWarning('PLATFORM_TOKEN contains whitespace — this may cause issues');
121
+ }
122
+ printDebug('Configuration loaded successfully');
123
+ return {
124
+ ...config,
125
+ auth: { token },
126
+ };
127
+ }
128
+ /**
129
+ * Return a copy of `config` with SSL verification disabled. Module-private — the
130
+ * only caller is `getConfigWithOptions` below.
131
+ */
132
+ function withSSLDisabled(config) {
133
+ return {
134
+ ...config,
135
+ api: { ...config.api, rejectUnauthorized: false },
136
+ };
137
+ }
138
+ /**
139
+ * Load configuration, optionally disabling SSL based on command options.
140
+ * Replaces the repeated pattern:
141
+ * `options.verifySsl === false ? withSSLDisabled(getConfig()) : getConfig()`
142
+ */
143
+ export function getConfigWithOptions(options) {
144
+ const config = getConfig();
145
+ if (options.verifySsl === false) {
146
+ // Every authenticated command flows its token through here — refuse to
147
+ // disable cert verification in production (mirrors the login/renewal guards)
148
+ // so a MITM can't harvest the Bearer JWT. No-op in non-production.
149
+ assertSslDisableAllowed('authenticated API client (--no-verify-ssl)');
150
+ return withSSLDisabled(config);
151
+ }
152
+ return config;
153
+ }
154
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29uZmlnLWxvYWRlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy91dGlscy9jb25maWctbG9hZGVyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLCtDQUErQztBQUMvQyxzQ0FBc0M7QUFFdEMsT0FBTyxLQUFLLEVBQUUsTUFBTSxJQUFJLENBQUM7QUFDekIsT0FBTyxFQUFFLGFBQWEsRUFBRSxNQUFNLFVBQVUsQ0FBQztBQUN6QyxPQUFPLEtBQUssRUFBRSxNQUFNLElBQUksQ0FBQztBQUN6QixPQUFPLEtBQUssSUFBSSxNQUFNLE1BQU0sQ0FBQztBQUM3QixPQUFPLEtBQUssSUFBSSxNQUFNLE1BQU0sQ0FBQztBQUM3QixPQUFPLEVBQUUsVUFBVSxFQUFFLFVBQVUsRUFBRSxZQUFZLEVBQUUsTUFBTSxtQkFBbUIsQ0FBQztBQUN6RSxPQUFPLEVBQUUsdUJBQXVCLEVBQUUsZUFBZSxFQUFFLE1BQU0sVUFBVSxDQUFDO0FBQ3BFLE9BQU8sRUFBZSxNQUFNLG1CQUFtQixDQUFDO0FBRWhELDBEQUEwRDtBQUMxRCxNQUFNLFNBQVMsR0FBRyxJQUFJLENBQUMsT0FBTyxDQUFDLGFBQWEsQ0FBQyxPQUFPLElBQUksQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDO0FBSS9EOztHQUVHO0FBQ0gsTUFBTSxhQUFhLEdBQXlCO0lBQzFDLEdBQUcsRUFBRTtRQUNILE9BQU8sRUFBRSx3QkFBd0I7UUFDakMsT0FBTyxFQUFFLE1BQU07UUFDZixXQUFXLEVBQUUsZUFBZTtRQUM1QixlQUFlLEVBQUUsZ0JBQWdCO1FBQ2pDLG1CQUFtQixFQUFFLHlCQUF5QjtRQUM5QyxTQUFTLEVBQUUsYUFBYTtRQUN4QixhQUFhLEVBQUUsY0FBYztRQUM3QixlQUFlLEVBQUUsb0JBQW9CO1FBQ3JDLGtCQUFrQixFQUFFLElBQUk7S0FDekI7Q0FDRixDQUFDO0FBRUYsNERBQTREO0FBQzVELE1BQU0sZ0JBQWdCLEdBQUcsSUFBSSxDQUFDLElBQUksQ0FB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LEdBQUcsRUFBRSxFQUFFLEdBQUcsTUFBTSxDQUFDLEdBQUcsRUFBRSxrQkFBa0IsRUFBRSxLQUFLLEVBQUU7S0FDbEQsQ0FBQztBQUNKLENBQUM7QUFFRDs7OztHQUlHO0FBQ0gsTUFBTSxVQUFVLG9CQUFvQixDQUFDLE9BQWdDO0lBQ25FLE1BQU0sTUFBTSxHQUFHLFNBQVMsRUFBRSxDQUFDO0lBQzNCLElBQUksT0FBTyxDQUFDLFNBQVMsS0FBSyxLQUFLLEVBQUUsQ0FBQztRQUNoQyx1RUFBdUU7UUFDdkUsNkVBQTZFO1FBQzdFLG1FQUFtRTtRQUNuRSx1QkFBdUIsQ0FBQyw0Q0FBNEMsQ0FBQyxDQUFDO1FBQ3RFLE9BQU8sZUFBZSxDQUFDLE1BQU0sQ0FBQyxDQUFDO0lBQ2pDLENBQUM7SUFDRCxPQUFPLE1BQU0sQ0FBQztBQUNoQixDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLy8gQ29weXJpZ2h0IDIwMjYgUGlwZWxpbmUgQnVpbGRlciBDb250cmlidXRvcnNcbi8vIFNQRFgtTGljZW5zZS1JZGVudGlmaWVyOiBBcGFjaGUtMi4wXG5cbmltcG9ydCAqIGFzIGZzIGZyb20gJ2ZzJztcbmltcG9ydCB7IGZpbGVVUkxUb1BhdGggfSBmcm9tICdub2RlOnVybCc7XG5pbXBvcnQgKiBhcyBvcyBmcm9tICdvcyc7XG5pbXBvcnQgKiBhcyBwYXRoIGZyb20gJ3BhdGgnO1xuaW1wb3J0ICogYXMgeWFtbCBmcm9tICd5YW1sJztcbmltcG9ydCB7IHByaW50RGVidWcsIHByaW50RXJyb3IsIHByaW50V2FybmluZyB9IGZyb20gJy4vb3V0cHV0LXV0aWxzLmpzJztcbmltcG9ydCB7IGFzc2VydFNzbERpc2FibGVBbGxvd2VkLCBpc1Byb2R1Y3Rpb25FbnYgfSBmcm9tICcuL3Rscy5qcyc7XG5pbXBvcnQgeyB0eXBlIENvbmZpZyB9IGZyb20gJy4uL3R5cGVzL2luZGV4LmpzJztcblxuLy8gRVNNIGhhcyBubyBfX2Rpcm5hbWU7IGRlcml2ZSBpdCBmcm9tIHRoaXMgbW9kdWxlJ3MgVVJMLlxuY29uc3QgX19kaXJuYW1lID0gcGF0aC5kaXJuYW1lKGZpbGVVUkxUb1BhdGgoaW1wb3J0Lm1ldGEudXJsKSk7XG5cbmV4cG9ydCB0eXBlIHsgQ29uZmlnIH07XG5cbi8qKlxuICogRGVmYXVsdCBjb25maWd1cmF0aW9uIOKAlCBBUEkgZW5kcG9pbnQgcGF0aHMgYW5kIGNvbm5lY3Rpb24gc2V0dGluZ3MuXG4gKi9cbmNvbnN0IGRlZmF1bHRDb25maWc6IE9taXQ8Q29uZmlnLCAnYXV0aCc+ID0ge1xuICBhcGk6IHtcbiAgICBiYXNlVXJsOiAnaHR0cHM6Ly9sb2NhbGhvc3Q6ODQ0MycsXG4gICAgdGltZW91dDogMzBfMDAwLFxuICAgIHBpcGVsaW5lVXJsOiAnL2FwaS9waXBlbGluZScsXG4gICAgcGlwZWxpbmVMaXN0VXJsOiAnL2FwaS9waXBlbGluZXMnLFxuICAgIHBpcGVsaW5lVGVtcGxhdGVVcmw6ICcvYXBpL3BpcGVsaW5lLXRlbXBsYXRlcycsXG4gICAgcGx1Z2luVXJsOiAnL2FwaS9wbHVnaW4nLFxuICAgIHBsdWdpbkxpc3RVcmw6ICcvYXBpL3BsdWdpbnMnLFxuICAgIHBsdWdpblVwbG9hZFVybDogJy9hcGkvcGx1Z2luL3VwbG9hZCcsXG4gICAgcmVqZWN0VW5hdXRob3JpemVkOiB0cnVlLFxuICB9LFxufTtcblxuLyoqIFVzZXIgY29uZmlnIGZpbGUgcGF0aDogfi8ucGlwZWxpbmUtbWFuYWdlci9jb25maWcueW1sICovXG5jb25zdCBVU0VSX0NPTkZJR19QQVRIID0gcGF0aC5qb2luKG9zLmhvbWVkaXIoKSwgJy5waXBlbGluZS1tYW5hZ2VyJywgJ2NvbmZpZy55bWwnKTtcblxuLyoqXG4gKiBMb2FkIGEgWUFNTCBjb25maWcgZmlsZSBhbmQgbWVyZ2UgaXRzIGBhcGlgIHNlY3Rpb24gaW50byB0aGUgY29uZmlnLlxuICogUmV0dXJucyB0aGUgbWVyZ2VkIGNvbmZpZyBvciB0aGUgb3JpZ2luYWwgaWYgZmlsZSBkb2Vzbid0IGV4aXN0IG9yIGZhaWxzLlxuICovXG5mdW5jdGlvbiBsb2FkQ29uZmlnRmlsZShmaWxlUGF0aDogc3RyaW5nLCBjb25maWc6IE9taXQ8Q29uZmlnLCAnYXV0aCc+KTogT21pdDxDb25maWcsICdhdXRoJz4ge1xuICBpZiAoIWZzLmV4aXN0c1N5bmMoZmlsZVBhdGgpKSByZXR1cm4gY29uZmlnO1xuXG4gIHRyeSB7XG4gICAgcHJpbnREZWJ1ZygnTG9hZGluZyBjb25maWd1cmF0aW9uJywgeyBwYXRoOiBmaWxlUGF0aCB9KTtcbiAgICBjb25zdCBwYXJzZWQgPSB5YW1sLnBhcnNlKGZzLnJlYWRGaWxlU3luYyhmaWxlUGF0aCwgJ3V0Zi04JykpO1xuICAgIGlmIChwYXJzZWQ/LmF1dGgpIHtcbiAgICAgIHByaW50V2FybmluZygnQXV0aCBzZWN0aW9uIGluIGNvbmZpZyBmaWxlIGlzIGlnbm9yZWQg4oCUIHVzZSBQTEFURk9STV9UT0tFTiBlbnYgdmFyJyk7XG4gICAgfVxuICAgIHJldHVybiB7IGFwaTogeyAuLi5jb25maWcuYXBpLCAuLi5wYXJzZWQ/LmFwaSB9IH07XG4gIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgcHJpbnRXYXJuaW5nKCdGYWlsZWQgdG8gbG9hZCBjb25maWcgZmlsZSwgdXNpbmcgZGVmYXVsdHMnLCB7XG4gICAgICBwYXRoOiBmaWxlUGF0aCxcbiAgICAgIGVycm9yOiBlcnJvciBpbnN0YW5jZW9mIEVycm9yID8gZXJyb3IubWVzc2FnZSA6IFN0cmluZyhlcnJvciksXG4gICAgfSk7XG4gICAgcmV0dXJuIGNvbmZpZztcbiAgfVxufVxuXG4vKipcbiAqIExvYWQgY29uZmlndXJhdGlvbiBmcm9tIGZpbGVzIGFuZCBlbnZpcm9ubWVudC5cbiAqXG4gKiBQcmlvcml0eSAobGFzdCB3aW5zKTpcbiAqIDEuIEJ1aWx0LWluIGRlZmF1bHRzXG4gKiAyLiBVc2VyIGNvbmZpZyBmaWxlOiB+Ly5waXBlbGluZS1tYW5hZ2VyL2NvbmZpZy55bWxcbiAqIDMuIFByb2plY3QgY29uZmlnIGZpbGU6IENMSV9DT05GSUdfUEFUSCBvciAuL2NvbmZpZy55bWxcbiAqIDQuIEVudmlyb25tZW50IHZhcmlhYmxlc1xuICpcbiAqIEF1dGggdG9rZW4gTVVTVCBjb21lIGZyb20gUExBVEZPUk1fVE9LRU4gZW52IHZhciAobmV2ZXIgZnJvbSBjb25maWcgZmlsZSkuXG4gKi9cbi8qKlxuICogUmVzb2x2ZSB0aGUgQVBJL2Nvbm5lY3Rpb24gY29uZmlnIChiYXNlIFVSTCwgU1NMLCB0aW1lb3V0cykgZnJvbSBmaWxlcyBhbmRcbiAqIGVudmlyb25tZW50IFdJVEhPVVQgcmVxdWlyaW5nIGBQTEFURk9STV9UT0tFTmAuXG4gKlxuICogVGhpcyBpcyB0aGUgdG9rZW4tb3B0aW9uYWwgc2xpY2Ugb2Yge0BsaW5rIGdldENvbmZpZ30uIFRoZSBwcmUtYXV0aCBsb2dpbiBzdGVwXG4gKiAoYGVuc3VyZVBsYXRmb3JtVG9rZW5gKSBuZWVkcyB0aGUgcmVzb2x2ZWQgYmFzZSBVUkwgdG8gUE9TVCBgL2FwaS9hdXRoL2xvZ2luYFxuICogKmJlZm9yZSogYW55IHRva2VuIGV4aXN0cyDigJQgY2FsbGluZyBgZ2V0Q29uZmlnKClgIHRoZXJlIHdvdWxkIHRocm93IG9uIHRoZVxuICogbWlzc2luZyB0b2tlbiBhbmQgbWFrZSBpbmxpbmUtbG9naW4gZGVhZC1vbi1hcnJpdmFsLlxuICovXG5leHBvcnQgZnVuY3Rpb24gZ2V0QXBpQ29uZmlnKCk6IE9taXQ8Q29uZmlnLCAnYXV0aCc+IHtcbiAgY29uc3QgcHJvamVjdENvbmZpZ1BhdGggPSBwcm9jZXNzLmVudi5DTElfQ09ORklHX1BBVEggfHwgcGF0aC5qb2luKF9fZGlybmFtZSwgJy4uL2NvbmZpZy55bWwnKTtcblxuICAvLyBMYXllciAxOiBkZWZhdWx0cyDihpIgTGF5ZXIgMjogdXNlciBjb25maWcg4oaSIExheWVyIDM6IHByb2plY3QgY29uZmlnXG4gIGxldCBjb25maWcgPSBsb2FkQ29uZmlnRmlsZShVU0VSX0NPTkZJR19QQVRILCB7IC4uLmRlZmF1bHRDb25maWcgfSk7XG4gIGNvbmZpZyA9IGxvYWRDb25maWdGaWxlKHByb2plY3RDb25maWdQYXRoLCBjb25maWcpO1xuXG4gIC8vIExheWVyIDQ6IGVudmlyb25tZW50IHZhcmlhYmxlIG92ZXJyaWRlc1xuICBpZiAocHJvY2Vzcy5lbnYuUExBVEZPUk1fQkFTRV9VUkwpIHtcbiAgICBjb25maWcuYXBpLmJhc2VVcmwgPSBwcm9jZXNzLmVudi5QTEFURk9STV9CQVNFX1VSTDtcbiAgICBwcmludERlYnVnKCdVc2luZyBQTEFURk9STV9CQVNFX1VSTCBmcm9tIGVudmlyb25tZW50JywgeyBiYXNlVXJsOiBjb25maWcuYXBpLmJhc2VVcmwgfSk7XG4gIH1cblxuICBpZiAocHJvY2Vzcy5lbnYuVExTX1JFSkVDVF9VTkFVVEhPUklaRUQgIT09IHVuZGVmaW5lZCkge1xuICAgIGNvbnN0IGRpc2FibGUgPSBwcm9jZXNzLmVudi5UTFNfUkVKRUNUX1VOQVVUSE9SSVpFRCA9PT0gJzAnO1xuICAgIGlmIChkaXNhYmxlICYmIGlzUHJvZHVjdGlvbkVudigpKSB7XG4gICAgICBwcmludFdhcm5pbmcoJ0lnbm9yaW5nIFRMU19SRUpFQ1RfVU5BVVRIT1JJWkVEPTAgaW4gcHJvZHVjdGlvbiDigJQgU1NMIHZlcmlmaWNhdGlvbiByZW1haW5zIGVuYWJsZWQnKTtcbiAgICB9IGVsc2Uge1xuICAgICAgY29uZmlnLmFwaS5yZWplY3RVbmF1dGhvcml6ZWQgPSAhZGlzYWJsZTtcbiAgICAgIGlmIChkaXNhYmxlKSB7XG4gICAgICAgIHByaW50V2FybmluZygnU1NMIGNlcnRpZmljYXRlIHZhbGlkYXRpb24gZGlzYWJsZWQgdmlhIFRMU19SRUpFQ1RfVU5BVVRIT1JJWkVEPTAnKTtcbiAgICAgIH1cbiAgICB9XG4gIH1cblxuICBpZiAocHJvY2Vzcy5lbnYuVVBMT0FEX1RJTUVPVVQpIHtcbiAgICBjb25zdCBwYXJzZWQgPSBwYXJzZUludChwcm9jZXNzLmVudi5VUExPQURfVElNRU9VVCwgMTApO1xuICAgIGlmICghaXNOYU4ocGFyc2VkKSAmJiBwYXJzZWQgPiAwKSB7XG4gICAgICBjb25maWcuYXBpLnVwbG9hZFRpbWVvdXQgPSBwYXJzZWQ7XG4gICAgICBwcmludERlYnVnKCdVc2luZyBVUExPQURfVElNRU9VVCBmcm9tIGVudmlyb25tZW50JywgeyB1cGxvYWRUaW1lb3V0OiBgJHtwYXJzZWR9bXNgIH0pO1xuICAgIH0gZWxzZSB7XG4gICAgICBwcmludFdhcm5pbmcoJ0ludmFsaWQgVVBMT0FEX1RJTUVPVVQgdmFsdWUsIHVzaW5nIGRlZmF1bHQnLCB7IHByb3ZpZGVkOiBwcm9jZXNzLmVudi5VUExPQURfVElNRU9VVCB9KTtcbiAgICB9XG4gIH1cblxuICByZXR1cm4gY29uZmlnO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gZ2V0Q29uZmlnKCk6IENvbmZpZyB7XG4gIGNvbnN0IGNvbmZpZyA9IGdldEFwaUNvbmZpZygpO1xuXG4gIC8vIFRva2VuIGlzIFJFUVVJUkVEIGZyb20gZW52aXJvbm1lbnRcbiAgY29uc3QgdG9rZW4gPSBwcm9jZXNzLmVudi5QTEFURk9STV9UT0tFTjtcblxuICBpZiAoIXRva2VuKSB7XG4gICAgcHJpbnRFcnJvcignUExBVEZPUk1fVE9LRU4gZW52aXJvbm1lbnQgdmFyaWFibGUgaXMgbm90IHNldCcpO1xuICAgIHRocm93IG5ldyBFcnJvcignUExBVEZPUk1fVE9LRU4gZW52aXJvbm1lbnQgdmFyaWFibGUgaXMgcmVxdWlyZWQnKTtcbiAgfVxuXG4gIGlmICh0b2tlbi50cmltKCkubGVuZ3RoID09PSAwKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKCdQTEFURk9STV9UT0tFTiBtdXN0IGJlIGEgbm9uLWVtcHR5IHN0cmluZycpO1xuICB9XG5cbiAgaWYgKHRva2VuLmluY2x1ZGVzKCcgJykpIHtcbiAgICBwcmludFdhcm5pbmcoJ1BMQVRGT1JNX1RPS0VOIGNvbnRhaW5zIHdoaXRlc3BhY2Ug4oCUIHRoaXMgbWF5IGNhdXNlIGlzc3VlcycpO1xuICB9XG5cbiAgcHJpbnREZWJ1ZygnQ29uZmlndXJhdGlvbiBsb2FkZWQgc3VjY2Vzc2Z1bGx5Jyk7XG5cbiAgcmV0dXJuIHtcbiAgICAuLi5jb25maWcsXG4gICAgYXV0aDogeyB0b2tlbiB9LFxuICB9O1xufVxuXG4vKipcbiAqIFJldHVybiBhIGNvcHkgb2YgYGNvbmZpZ2Agd2l0aCBTU0wgdmVyaWZpY2F0aW9uIGRpc2FibGVkLiBNb2R1bGUtcHJpdmF0ZSDigJQgdGhlXG4gKiBvbmx5IGNhbGxlciBpcyBgZ2V0Q29uZmlnV2l0aE9wdGlvbnNgIGJlbG93LlxuICovXG5mdW5jdGlvbiB3aXRoU1NMRGlzYWJsZWQoY29uZmlnOiBDb25maWcpOiBDb25maWcge1xuICByZXR1cm4ge1xuICAgIC4uLmNvbmZpZyxcbiAgICBhcGk6IHsgLi4uY29uZmlnLmFwaSwgcmVqZWN0VW5hdXRob3JpemVkOiBmYWxzZSB9LFxuICB9O1xufVxuXG4vKipcbiAqIExvYWQgY29uZmlndXJhdGlvbiwgb3B0aW9uYWxseSBkaXNhYmxpbmcgU1NMIGJhc2VkIG9uIGNvbW1hbmQgb3B0aW9ucy5cbiAqIFJlcGxhY2VzIHRoZSByZXBlYXRlZCBwYXR0ZXJuOlxuICogICBgb3B0aW9ucy52ZXJpZnlTc2wgPT09IGZhbHNlID8gd2l0aFNTTERpc2FibGVkKGdldENvbmZpZygpKSA6IGdldENvbmZpZygpYFxuICovXG5leHBvcnQgZnVuY3Rpb24gZ2V0Q29uZmlnV2l0aE9wdGlvbnMob3B0aW9uczogeyB2ZXJpZnlTc2w/OiBib29sZWFuIH0pOiBDb25maWcge1xuICBjb25zdCBjb25maWcgPSBnZXRDb25maWcoKTtcbiAgaWYgKG9wdGlvbnMudmVyaWZ5U3NsID09PSBmYWxzZSkge1xuICAgIC8vIEV2ZXJ5IGF1dGhlbnRpY2F0ZWQgY29tbWFuZCBmbG93cyBpdHMgdG9rZW4gdGhyb3VnaCBoZXJlIOKAlCByZWZ1c2UgdG9cbiAgICAvLyBkaXNhYmxlIGNlcnQgdmVyaWZpY2F0aW9uIGluIHByb2R1Y3Rpb24gKG1pcnJvcnMgdGhlIGxvZ2luL3JlbmV3YWwgZ3VhcmRzKVxuICAgIC8vIHNvIGEgTUlUTSBjYW4ndCBoYXJ2ZXN0IHRoZSBCZWFyZXIgSldULiBOby1vcCBpbiBub24tcHJvZHVjdGlvbi5cbiAgICBhc3NlcnRTc2xEaXNhYmxlQWxsb3dlZCgnYXV0aGVudGljYXRlZCBBUEkgY2xpZW50ICgtLW5vLXZlcmlmeS1zc2wpJyk7XG4gICAgcmV0dXJuIHdpdGhTU0xEaXNhYmxlZChjb25maWcpO1xuICB9XG4gIHJldHVybiBjb25maWc7XG59XG4iXX0=
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Convert a standard 5-field cron expression into an AWS EventBridge cron
3
+ * expression, and guard against schedules that fire more often than every
4
+ * 15 minutes.
5
+ *
6
+ * Standard cron: `minute hour day-of-month month day-of-week`
7
+ * EventBridge cron: `cron(minute hour day-of-month month day-of-week year)`
8
+ *
9
+ * The non-obvious differences EventBridge imposes:
10
+ * - a 6th field (year), which we always set to `*`;
11
+ * - day-of-month and day-of-week may NOT both be `*` — exactly one must be `?`;
12
+ * - day-of-week is 1–7 with 1 = Sunday, whereas standard cron is 0–6 (or 7)
13
+ * with 0/7 = Sunday — so numeric day-of-week values must be remapped.
14
+ */
15
+ /** Minimum allowed spacing between fires, in minutes. */
16
+ export declare const MIN_SCHEDULE_INTERVAL_MIN = 15;
17
+ /**
18
+ * Throw if `fiveField` would fire more often than every {@link MIN_SCHEDULE_INTERVAL_MIN}
19
+ * minutes. Conservative: the smallest cyclic gap in the minute field is the worst-case
20
+ * spacing (it's only ever larger once the hour/day fields restrict things further).
21
+ */
22
+ export declare function assertScheduleInterval(fiveField: string): void;
23
+ /**
24
+ * Convert a validated 5-field cron expression to an AWS EventBridge `cron(...)`
25
+ * expression. Also enforces the 15-minute guard.
26
+ */
27
+ export declare function toEventBridgeCron(fiveField: string): string;
28
+ //# sourceMappingURL=cron.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cron.d.ts","sourceRoot":"","sources":["../../src/utils/cron.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;;;;GAaG;AAEH,yDAAyD;AACzD,eAAO,MAAM,yBAAyB,KAAK,CAAC;AAoC5C;;;;GAIG;AACH,wBAAgB,sBAAsB,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAuB9D;AAaD;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAmB3D"}
@@ -0,0 +1,119 @@
1
+ // Copyright 2026 Pipeline Builder Contributors
2
+ // SPDX-License-Identifier: Apache-2.0
3
+ /**
4
+ * Convert a standard 5-field cron expression into an AWS EventBridge cron
5
+ * expression, and guard against schedules that fire more often than every
6
+ * 15 minutes.
7
+ *
8
+ * Standard cron: `minute hour day-of-month month day-of-week`
9
+ * EventBridge cron: `cron(minute hour day-of-month month day-of-week year)`
10
+ *
11
+ * The non-obvious differences EventBridge imposes:
12
+ * - a 6th field (year), which we always set to `*`;
13
+ * - day-of-month and day-of-week may NOT both be `*` — exactly one must be `?`;
14
+ * - day-of-week is 1–7 with 1 = Sunday, whereas standard cron is 0–6 (or 7)
15
+ * with 0/7 = Sunday — so numeric day-of-week values must be remapped.
16
+ */
17
+ /** Minimum allowed spacing between fires, in minutes. */
18
+ export const MIN_SCHEDULE_INTERVAL_MIN = 15;
19
+ /** Expand a cron minute field (wildcard, step "/n", ranges "a-b", lists "a,b") to the sorted set of minutes it fires on. */
20
+ function expandMinuteField(field) {
21
+ const out = new Set();
22
+ for (const part of field.split(',')) {
23
+ const slash = part.split('/');
24
+ const rangePart = slash[0] ?? '';
25
+ const stepPart = slash[1];
26
+ const step = stepPart !== undefined ? parseInt(stepPart, 10) : 1;
27
+ if (stepPart !== undefined && (!Number.isInteger(step) || step < 1)) {
28
+ throw new Error(`Invalid step in minute field: "${part}"`);
29
+ }
30
+ let lo;
31
+ let hi;
32
+ if (rangePart === '*') {
33
+ lo = 0;
34
+ hi = 59;
35
+ }
36
+ else if (rangePart.includes('-')) {
37
+ const ends = rangePart.split('-');
38
+ lo = parseInt(ends[0] ?? '', 10);
39
+ hi = parseInt(ends[1] ?? '', 10);
40
+ }
41
+ else {
42
+ lo = parseInt(rangePart, 10);
43
+ // With a step (`n/step`), cron's implicit range runs to the end of the field,
44
+ // so `0/5` fires at 0,5,…,55 — expand to [n, 59] so the interval guard sees
45
+ // the true spacing. Without a step, a bare number is a single minute.
46
+ hi = stepPart !== undefined ? 59 : lo;
47
+ }
48
+ if (!Number.isInteger(lo) || !Number.isInteger(hi) || lo < 0 || hi > 59 || lo > hi) {
49
+ throw new Error(`Invalid minute field: "${part}"`);
50
+ }
51
+ for (let m = lo; m <= hi; m += step)
52
+ out.add(m);
53
+ }
54
+ return [...out].sort((a, b) => a - b);
55
+ }
56
+ /**
57
+ * Throw if `fiveField` would fire more often than every {@link MIN_SCHEDULE_INTERVAL_MIN}
58
+ * minutes. Conservative: the smallest cyclic gap in the minute field is the worst-case
59
+ * spacing (it's only ever larger once the hour/day fields restrict things further).
60
+ */
61
+ export function assertScheduleInterval(fiveField) {
62
+ const parts = fiveField.trim().split(/\s+/);
63
+ if (parts.length !== 5) {
64
+ throw new Error(`Schedule must be a 5-field cron ("minute hour day-of-month month day-of-week"), got: "${fiveField}"`);
65
+ }
66
+ const minutes = expandMinuteField(parts[0]);
67
+ if (minutes.length === 0)
68
+ throw new Error(`Minute field matches nothing: "${parts[0]}"`);
69
+ // Smallest cyclic gap across the hour boundary (e.g. {0,50} → gaps 50 and 10).
70
+ let minGap = 60;
71
+ if (minutes.length > 1) {
72
+ for (let i = 0; i < minutes.length; i++) {
73
+ const cur = minutes[i];
74
+ const next = minutes[(i + 1) % minutes.length];
75
+ const gap = (next - cur + 60) % 60 || 60;
76
+ minGap = Math.min(minGap, gap);
77
+ }
78
+ }
79
+ if (minGap < MIN_SCHEDULE_INTERVAL_MIN) {
80
+ throw new Error(`Schedule "${fiveField}" fires every ${minGap} minute(s); the minimum is ${MIN_SCHEDULE_INTERVAL_MIN}. ` +
81
+ 'Use a sparser schedule (e.g. "0 0 * * *" for daily, "*/15 * * * *" for the densest allowed).');
82
+ }
83
+ }
84
+ /** Remap a standard cron day-of-week token (0–7, 0/7=Sun) to EventBridge (1–7, 1=Sun). Passes through names and `*`/`?`. */
85
+ function remapDayOfWeek(field) {
86
+ if (field === '*' || field === '?')
87
+ return field;
88
+ // Token may contain lists/ranges/steps of numbers and/or 3-letter names.
89
+ return field.replace(/\d+/g, (n) => {
90
+ const v = parseInt(n, 10);
91
+ if (v < 0 || v > 7)
92
+ throw new Error(`Invalid day-of-week value: "${n}"`);
93
+ return String((v % 7) + 1); // 0→1 (Sun), 6→7 (Sat), 7→1 (Sun)
94
+ });
95
+ }
96
+ /**
97
+ * Convert a validated 5-field cron expression to an AWS EventBridge `cron(...)`
98
+ * expression. Also enforces the 15-minute guard.
99
+ */
100
+ export function toEventBridgeCron(fiveField) {
101
+ assertScheduleInterval(fiveField); // also validates the 5-field shape
102
+ const f = fiveField.trim().split(/\s+/);
103
+ const [minute, hour, domRaw, month, dowRaw] = [f[0], f[1], f[2], f[3], f[4]];
104
+ let dom = domRaw;
105
+ let dow = remapDayOfWeek(dowRaw);
106
+ // EventBridge: exactly one of day-of-month / day-of-week must be `?`.
107
+ if (dom === '*' && dow === '*') {
108
+ dow = '?'; // unrestricted both → pin day-of-week to ?
109
+ }
110
+ else if (dom !== '*' && dow === '*') {
111
+ dow = '?'; // specific day-of-month → day-of-week must be ?
112
+ }
113
+ else if (dom === '*' && dow !== '*') {
114
+ dom = '?'; // specific day-of-week → day-of-month must be ?
115
+ }
116
+ // (both specific is left as-is; EventBridge rejects it, surfacing a clear error)
117
+ return `cron(${minute} ${hour} ${dom} ${month} ${dow} *)`;
118
+ }
119
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY3Jvbi5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy91dGlscy9jcm9uLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLCtDQUErQztBQUMvQyxzQ0FBc0M7QUFFdEM7Ozs7Ozs7Ozs7Ozs7R0FhRztBQUVILHlEQUF5RDtBQUN6RCxNQUFNLENBQUMsTUFBTSx5QkFBeUIsR0FBRyxFQUFFLENBQUM7QUFFNUMsNEhBQTRIO0FBQzVILFNBQVMsaUJBQWlCLENBQUMsS0FBYTtJQUN0QyxNQUFNLEdBQUcsR0FBRyxJQUFJLEdBQUcsRUFBVSxDQUFDO0lBQzlCLEtBQUssTUFBTSxJQUFJLElBQUksS0FBSyxDQUFDLEtBQUssQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDO1FBQ3BDLE1BQU0sS0FBSyxHQUFHLElBQUksQ0FBQyxLQUFLLENBQUMsR0FBRyxDQUFDLENBQUM7UUFDOUIsTUFBTSxTQUFTLEdBQUcsS0FBSyxDQUFDLENBQUMsQ0FBQyxJQUFJLEVBQUUsQ0FBQztRQUNqQyxNQUFNLFFBQVEsR0FBRyxLQUFLLENBQUMsQ0FBQyxDQUFDLENBQUM7UUFDMUIsTUFBTSxJQUFJLEdBQUcsUUFBUSxLQUFLLFNBQVMsQ0FBQyxDQUFDLENBQUMsUUFBUSxDQUFDLFFBQVEsRUFBRSxFQUFFLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDO1FBQ2pFLElBQUksUUFBUSxLQUFLLFNBQVMsSUFBSSxDQUFDLENBQUMsTUFBTSxDQUFDLFNBQVMsQ0FBQyxJQUFJLENBQUMsSUFBSSxJQUFJLEdBQUcsQ0FBQyxDQUFDLEVBQUUsQ0FBQztZQUNwRSxNQUFNLElBQUksS0FBSyxDQUFDLGtDQUFrQyxJQUFJLEdBQUcsQ0FBQyxDQUFDO1FBQzdELENBQUM7UUFDRCxJQUFJLEVBQVUsQ0FBQztRQUNmLElBQUksRUFBVSxDQUFDO1FBQ2YsSUFBSSxTQUFTLEtBQUssR0FBRyxFQUFFLENBQUM7WUFDdEIsRUFBRSxHQUFHLENBQUMsQ0FBQztZQUFDLEVB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QUFFLENBQUMsQ0FBQyxDQUFDLENBQUUsRUFBRSxDQUFDLENBQUMsQ0FBQyxDQUFFLENBQUMsQ0FBQztJQUVsRixJQUFJLEdBQUcsR0FBRyxNQUFNLENBQUM7SUFDakIsSUFBSSxHQUFHLEdBQUcsY0FBYyxDQUFDLE1BQU0sQ0FBQyxDQUFDO0lBRWpDLHNFQUFzRTtJQUN0RSxJQUFJLEdBQUcsS0FBSyxHQUFHLElBQUksR0FBRyxLQUFLLEdBQUcsRUFBRSxDQUFDO1FBQy9CLEdBQUcsR0FBRyxHQUFHLENBQUMsQ0FBQywyQ0FBMkM7SUFDeEQsQ0FBQztTQUFNLElBQUksR0FBRyxLQUFLLEdBQUcsSUFBSSxHQUFHLEtBQUssR0FBRyxFQUFFLENBQUM7UUFDdEMsR0FBRyxHQUFHLEdBQUcsQ0FBQyxDQUFDLGdEQUFnRDtJQUM3RCxDQUFDO1NBQU0sSUFBSSxHQUFHLEtBQUssR0FBRyxJQUFJLEdBQUcsS0FBSyxHQUFHLEVBQUUsQ0FBQztRQUN0QyxHQUFHLEdBQUcsR0FBRyxDQUFDLENBQUMsZ0RBQWdEO0lBQzdELENBQUM7SUFDRCxpRkFBaUY7SUFFakYsT0FBTyxRQUFRLE1BQU0sSUFBSSxJQUFJLElBQUksR0FBRyxJQUFJLEtBQUssSUFBSSxHQUFHLEtBQUssQ0FBQztBQUM1RCxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLy8gQ29weXJpZ2h0IDIwMjYgUGlwZWxpbmUgQnVpbGRlciBDb250cmlidXRvcnNcbi8vIFNQRFgtTGljZW5zZS1JZGVudGlmaWVyOiBBcGFjaGUtMi4wXG5cbi8qKlxuICogQ29udmVydCBhIHN0YW5kYXJkIDUtZmllbGQgY3JvbiBleHByZXNzaW9uIGludG8gYW4gQVdTIEV2ZW50QnJpZGdlIGNyb25cbiAqIGV4cHJlc3Npb24sIGFuZCBndWFyZCBhZ2FpbnN0IHNjaGVkdWxlcyB0aGF0IGZpcmUgbW9yZSBvZnRlbiB0aGFuIGV2ZXJ5XG4gKiAxNSBtaW51dGVzLlxuICpcbiAqIFN0YW5kYXJkIGNyb246ICAgIGBtaW51dGUgaG91ciBkYXktb2YtbW9udGggbW9udGggZGF5LW9mLXdlZWtgXG4gKiBFdmVudEJyaWRnZSBjcm9uOiBgY3JvbihtaW51dGUgaG91ciBkYXktb2YtbW9udGggbW9udGggZGF5LW9mLXdlZWsgeWVhcilgXG4gKlxuICogVGhlIG5vbi1vYnZpb3VzIGRpZmZlcmVuY2VzIEV2ZW50QnJpZGdlIGltcG9zZXM6XG4gKiAgLSBhIDZ0aCBmaWVsZCAoeWVhciksIHdoaWNoIHdlIGFsd2F5cyBzZXQgdG8gYCpgO1xuICogIC0gZGF5LW9mLW1vbnRoIGFuZCBkYXktb2Ytd2VlayBtYXkgTk9UIGJvdGggYmUgYCpgIOKAlCBleGFjdGx5IG9uZSBtdXN0IGJlIGA/YDtcbiAqICAtIGRheS1vZi13ZWVrIGlzIDHigJM3IHdpdGggMSA9IFN1bmRheSwgd2hlcmVhcyBzdGFuZGFyZCBjcm9uIGlzIDDigJM2IChvciA3KVxuICogICAgd2l0aCAwLzcgPSBTdW5kYXkg4oCUIHNvIG51bWVyaWMgZGF5LW9mLXdlZWsgdmFsdWVzIG11c3QgYmUgcmVtYXBwZWQuXG4gKi9cblxuLyoqIE1pbmltdW0gYWxsb3dlZCBzcGFjaW5nIGJldHdlZW4gZmlyZXMsIGluIG1pbnV0ZXMuICovXG5leHBvcnQgY29uc3QgTUlOX1NDSEVEVUxFX0lOVEVSVkFMX01JTiA9IDE1O1xuXG4vKiogRXhwYW5kIGEgY3JvbiBtaW51dGUgZmllbGQgKHdpbGRjYXJkLCBzdGVwIFwiL25cIiwgcmFuZ2VzIFwiYS1iXCIsIGxpc3RzIFwiYSxiXCIpIHRvIHRoZSBzb3J0ZWQgc2V0IG9mIG1pbnV0ZXMgaXQgZmlyZXMgb24uICovXG5mdW5jdGlvbiBleHBhbmRNaW51dGVGaWVsZChmaWVsZDogc3RyaW5nKTogbnVtYmVyW10ge1xuICBjb25zdCBvdXQgPSBuZXcgU2V0PG51bWJlcj4oKTtcbiAgZm9yIChjb25zdCBwYXJ0IG9mIGZpZWxkLnNwbGl0KCcsJykpIHtcbiAgICBjb25zdCBzbGFzaCA9IHBhcnQuc3BsaXQoJy8nKTtcbiAgICBjb25zdCByYW5nZVBhcnQgPSBzbGFzaFswXSA/PyAnJztcbiAgICBjb25zdCBzdGVwUGFydCA9IHNsYXNoWzFdO1xuICAgIGNvbnN0IHN0ZXAgPSBzdGVwUGFydCAhPT0gdW5kZWZpbmVkID8gcGFyc2VJbnQoc3RlcFBhcnQsIDEwKSA6IDE7XG4gICAgaWYgKHN0ZXBQYXJ0ICE9PSB1bmRlZmluZWQgJiYgKCFOdW1iZXIuaXNJbnRlZ2VyKHN0ZXApIHx8IHN0ZXAgPCAxKSkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKGBJbnZhbGlkIHN0ZXAgaW4gbWludXRlIGZpZWxkOiBcIiR7cGFydH1cImApO1xuICAgIH1cbiAgICBsZXQgbG86IG51bWJlcjtcbiAgICBsZXQgaGk6IG51bWJlcjtcbiAgICBpZiAocmFuZ2VQYXJ0ID09PSAnKicpIHtcbiAgICAgIGxvID0gMDsgaGkgPSA1OTtcbiAgICB9IGVsc2UgaWYgKHJhbmdlUGFydC5pbmNsdWRlcygnLScpKSB7XG4gICAgICBjb25zdCBlbmRzID0gcmFuZ2VQYXJ0LnNwbGl0KCctJyk7XG4gICAgICBsbyA9IHBhcnNlSW50KGVuZHNbMF0gPz8gJycsIDEwKTtcbiAgICAgIGhpID0gcGFyc2VJbnQoZW5kc1sxXSA/PyAnJywgMTApO1xuICAgIH0gZWxzZSB7XG4gICAgICBsbyA9IHBhcnNlSW50KHJhbmdlUGFydCwgMTApO1xuICAgICAgLy8gV2l0aCBhIHN0ZXAgKGBuL3N0ZXBgKSwgY3JvbidzIGltcGxpY2l0IHJhbmdlIHJ1bnMgdG8gdGhlIGVuZCBvZiB0aGUgZmllbGQsXG4gICAgICAvLyBzbyBgMC81YCBmaXJlcyBhdCAwLDUs4oCmLDU1IOKAlCBleHBhbmQgdG8gW24sIDU5XSBzbyB0aGUgaW50ZXJ2YWwgZ3VhcmQgc2Vlc1xuICAgICAgLy8gdGhlIHRydWUgc3BhY2luZy4gV2l0aG91dCBhIHN0ZXAsIGEgYmFyZSBudW1iZXIgaXMgYSBzaW5nbGUgbWludXRlLlxuICAgICAgaGkgPSBzdGVwUGFydCAhPT0gdW5kZWZpbmVkID8gNTkgOiBsbztcbiAgICB9XG4gICAgaWYgKCFOdW1iZXIuaXNJbnRlZ2VyKGxvKSB8fCAhTnVtYmVyLmlzSW50ZWdlcihoaSkgfHwgbG8gPCAwIHx8IGhpID4gNTkgfHwgbG8gPiBoaSkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKGBJbnZhbGlkIG1pbnV0ZSBmaWVsZDogXCIke3BhcnR9XCJgKTtcbiAgICB9XG4gICAgZm9yIChsZXQgbSA9IGxvOyBtIDw9IGhpOyBtICs9IHN0ZXApIG91dC5hZGQobSk7XG4gIH1cbiAgcmV0dXJuIFsuLi5vdXRdLnNvcnQoKGEsIGIpID0+IGEgLSBiKTtcbn1cblxuLyoqXG4gKiBUaHJvdyBpZiBgZml2ZUZpZWxkYCB3b3VsZCBmaXJlIG1vcmUgb2Z0ZW4gdGhhbiBldmVyeSB7QGxpbmsgTUlOX1NDSEVEVUxFX0lOVEVSVkFMX01JTn1cbiAqIG1pbnV0ZXMuIENvbnNlcnZhdGl2ZTogdGhlIHNtYWxsZXN0IGN5Y2xpYyBnYXAgaW4gdGhlIG1pbnV0ZSBmaWVsZCBpcyB0aGUgd29yc3QtY2FzZVxuICogc3BhY2luZyAoaXQncyBvbmx5IGV2ZXIgbGFyZ2VyIG9uY2UgdGhlIGhvdXIvZGF5IGZpZWxkcyByZXN0cmljdCB0aGluZ3MgZnVydGhlcikuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBhc3NlcnRTY2hlZHVsZUludGVydmFsKGZpdmVGaWVsZDogc3RyaW5nKTogdm9pZCB7XG4gIGNvbnN0IHBhcnRzID0gZml2ZUZpZWxkLnRyaW0oKS5zcGxpdCgvXFxzKy8pO1xuICBpZiAocGFydHMubGVuZ3RoICE9PSA1KSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKGBTY2hlZHVsZSBtdXN0IGJlIGEgNS1maWVsZCBjcm9uIChcIm1pbnV0ZSBob3VyIGRheS1vZi1tb250aCBtb250aCBkYXktb2Ytd2Vla1wiKSwgZ290OiBcIiR7Zml2ZUZpZWxkfVwiYCk7XG4gIH1cbiAgY29uc3QgbWludXRlcyA9IGV4cGFuZE1pbnV0ZUZpZWxkKHBhcnRzWzBdISk7XG4gIGlmIChtaW51dGVzLmxlbmd0aCA9PT0gMCkgdGhyb3cgbmV3IEVycm9yKGBNaW51dGUgZmllbGQgbWF0Y2hlcyBub3RoaW5nOiBcIiR7cGFydHNbMF19XCJgKTtcbiAgLy8gU21hbGxlc3QgY3ljbGljIGdhcCBhY3Jvc3MgdGhlIGhvdXIgYm91bmRhcnkgKGUuZy4gezAsNTB9IOKGkiBnYXBzIDUwIGFuZCAxMCkuXG4gIGxldCBtaW5HYXAgPSA2MDtcbiAgaWYgKG1pbnV0ZXMubGVuZ3RoID4gMSkge1xuICAgIGZvciAobGV0IGkgPSAwOyBpIDwgbWludXRlcy5sZW5ndGg7IGkrKykge1xuICAgICAgY29uc3QgY3VyID0gbWludXRlc1tpXSE7XG4gICAgICBjb25zdCBuZXh0ID0gbWludXRlc1soaSArIDEpICUgbWludXRlcy5sZW5ndGhdITtcbiAgICAgIGNvbnN0IGdhcCA9IChuZXh0IC0gY3VyICsgNjApICUgNjAgfHwgNjA7XG4gICAgICBtaW5HYXAgPSBNYXRoLm1pbihtaW5HYXAsIGdhcCk7XG4gICAgfVxuICB9XG4gIGlmIChtaW5HYXAgPCBNSU5fU0NIRURVTEVfSU5URVJWQUxfTUlOKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKFxuICAgICAgYFNjaGVkdWxlIFwiJHtmaXZlRmllbGR9XCIgZmlyZXMgZXZlcnkgJHttaW5HYXB9IG1pbnV0ZShzKTsgdGhlIG1pbmltdW0gaXMgJHtNSU5fU0NIRURVTEVfSU5URVJWQUxfTUlOfS4gYCArXG4gICAgICAnVXNlIGEgc3BhcnNlciBzY2hlZHVsZSAoZS5nLiBcIjAgMCAqICogKlwiIGZvciBkYWlseSwgXCIqLzE1ICogKiAqICpcIiBmb3IgdGhlIGRlbnNlc3QgYWxsb3dlZCkuJyxcbiAgICApO1xuICB9XG59XG5cbi8qKiBSZW1hcCBhIHN0YW5kYXJkIGNyb24gZGF5LW9mLXdlZWsgdG9rZW4gKDDigJM3LCAwLzc9U3VuKSB0byBFdmVudEJyaWRnZSAoMeKAkzcsIDE9U3VuKS4gUGFzc2VzIHRocm91Z2ggbmFtZXMgYW5kIGAqYC9gP2AuICovXG5mdW5jdGlvbiByZW1hcERheU9mV2VlayhmaWVsZDogc3RyaW5nKTogc3RyaW5nIHtcbiAgaWYgKGZpZWxkID09PSAnKicgfHwgZmllbGQgPT09ICc/JykgcmV0dXJuIGZpZWxkO1xuICAvLyBUb2tlbiBtYXkgY29udGFpbiBsaXN0cy9yYW5nZXMvc3RlcHMgb2YgbnVtYmVycyBhbmQvb3IgMy1sZXR0ZXIgbmFtZXMuXG4gIHJldHVybiBmaWVsZC5yZXBsYWNlKC9cXGQrL2csIChuKSA9PiB7XG4gICAgY29uc3QgdiA9IHBhcnNlSW50KG4sIDEwKTtcbiAgICBpZiAodiA8IDAgfHwgdiA+IDcpIHRocm93IG5ldyBFcnJvcihgSW52YWxpZCBkYXktb2Ytd2VlayB2YWx1ZTogXCIke259XCJgKTtcbiAgICByZXR1cm4gU3RyaW5nKCh2ICUgNykgKyAxKTsgLy8gMOKGkjEgKFN1biksIDbihpI3IChTYXQpLCA34oaSMSAoU3VuKVxuICB9KTtcbn1cblxuLyoqXG4gKiBDb252ZXJ0IGEgdmFsaWRhdGVkIDUtZmllbGQgY3JvbiBleHByZXNzaW9uIHRvIGFuIEFXUyBFdmVudEJyaWRnZSBgY3JvbiguLi4pYFxuICogZXhwcmVzc2lvbi4gQWxzbyBlbmZvcmNlcyB0aGUgMTUtbWludXRlIGd1YXJkLlxuICovXG5leHBvcnQgZnVuY3Rpb24gdG9FdmVudEJyaWRnZUNyb24oZml2ZUZpZWxkOiBzdHJpbmcpOiBzdHJpbmcge1xuICBhc3NlcnRTY2hlZHVsZUludGVydmFsKGZpdmVGaWVsZCk7IC8vIGFsc28gdmFsaWRhdGVzIHRoZSA1LWZpZWxkIHNoYXBlXG4gIGNvbnN0IGYgPSBmaXZlRmllbGQudHJpbSgpLnNwbGl0KC9cXHMrLyk7XG4gIGNvbnN0IFttaW51dGUsIGhvdXIsIGRvbVJhdywgbW9udGgsIGRvd1Jhd10gPSBbZlswXSEsIGZbMV0hLCBmWzJdISwgZlszXSEsIGZbNF0hXTtcblxuICBsZXQgZG9tID0gZG9tUmF3O1xuICBsZXQgZG93ID0gcmVtYXBEYXlPZldlZWsoZG93UmF3KTtcblxuICAvLyBFdmVudEJyaWRnZTogZXhhY3RseSBvbmUgb2YgZGF5LW9mLW1vbnRoIC8gZGF5LW9mLXdlZWsgbXVzdCBiZSBgP2AuXG4gIGlmIChkb20gPT09ICcqJyAmJiBkb3cgPT09ICcqJykge1xuICAgIGRvdyA9ICc/JzsgLy8gdW5yZXN0cmljdGVkIGJvdGgg4oaSIHBpbiBkYXktb2Ytd2VlayB0byA/XG4gIH0gZWxzZSBpZiAoZG9tICE9PSAnKicgJiYgZG93ID09PSAnKicpIHtcbiAgICBkb3cgPSAnPyc7IC8vIHNwZWNpZmljIGRheS1vZi1tb250aCDihpIgZGF5LW9mLXdlZWsgbXVzdCBiZSA/XG4gIH0gZWxzZSBpZiAoZG9tID09PSAnKicgJiYgZG93ICE9PSAnKicpIHtcbiAgICBkb20gPSAnPyc7IC8vIHNwZWNpZmljIGRheS1vZi13ZWVrIOKGkiBkYXktb2YtbW9udGggbXVzdCBiZSA/XG4gIH1cbiAgLy8gKGJvdGggc3BlY2lmaWMgaXMgbGVmdCBhcy1pczsgRXZlbnRCcmlkZ2UgcmVqZWN0cyBpdCwgc3VyZmFjaW5nIGEgY2xlYXIgZXJyb3IpXG5cbiAgcmV0dXJuIGBjcm9uKCR7bWludXRlfSAke2hvdXJ9ICR7ZG9tfSAke21vbnRofSAke2Rvd30gKilgO1xufVxuIl19
@@ -0,0 +1,38 @@
1
+ import { type Pipeline } from '../types/index.js';
2
+ /** Minimal platform client shape needed to register a deployed ARN. */
3
+ interface RegistryClient {
4
+ post: (url: string, body: unknown) => Promise<unknown>;
5
+ }
6
+ /** Inputs for {@link runDeploy} — an already-resolved pipeline + props. */
7
+ export interface RunDeployInput {
8
+ /** The pipeline being deployed (id/orgId/name/project/org drive registration). */
9
+ pipeline: Pick<Pipeline, 'id' | 'orgId' | 'pipelineName' | 'project' | 'organization'>;
10
+ /** Fully-resolved props (plugins pre-resolved, registry host baked, ids included). */
11
+ propsWithIds: Record<string, unknown>;
12
+ /** AWS profile / region for the deploy + registry payload. */
13
+ profile?: string;
14
+ region?: string;
15
+ /** `cdk deploy --require-approval` level and `--output` dir. */
16
+ requireApproval: string;
17
+ output: string;
18
+ debug?: boolean;
19
+ /** For the command header / summary line. */
20
+ executionId: string;
21
+ /** Registry callback — omit (e.g. --local-spec) to skip ARN registration. */
22
+ platformClient?: RegistryClient;
23
+ platformPipelineUrl?: string;
24
+ platformBaseUrl?: string;
25
+ }
26
+ /**
27
+ * Run `cdk deploy` for an already-resolved pipeline, then register the deployed
28
+ * ARN for event reporting (best-effort). Shared by `pipeline deploy` and
29
+ * `pipeline create --deploy` so the two entry points never drift.
30
+ *
31
+ * THROWS on CDK failure (the caller maps it to a non-zero exit and, for the
32
+ * create path, keeps the created record and prints the `deploy --id` retry).
33
+ * Registry failures are NON-fatal — they're queued to a pending-intent file for
34
+ * `pipeline register` to drain later.
35
+ */
36
+ export declare function runDeploy(input: RunDeployInput): Promise<void>;
37
+ export {};
38
+ //# sourceMappingURL=deploy-runner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deploy-runner.d.ts","sourceRoot":"","sources":["../../src/utils/deploy-runner.ts"],"names":[],"mappings":"AAUA,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AASlD,uEAAuE;AACvE,UAAU,cAAc;IACtB,IAAI,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;CACxD;AAED,2EAA2E;AAC3E,MAAM,WAAW,cAAc;IAC7B,kFAAkF;IAClF,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,IAAI,GAAG,OAAO,GAAG,cAAc,GAAG,SAAS,GAAG,cAAc,CAAC,CAAC;IACvF,sFAAsF;IACtF,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACtC,8DAA8D;IAC9D,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,gEAAgE;IAChE,eAAe,EAAE,MAAM,CAAC;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,6CAA6C;IAC7C,WAAW,EAAE,MAAM,CAAC;IACpB,6EAA6E;IAC7E,cAAc,CAAC,EAAE,cAAc,CAAC;IAChC,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED;;;;;;;;;GASG;AACH,wBAAsB,SAAS,CAAC,KAAK,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CA8FpE"}