fjall 0.95.0 → 0.99.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 (228) hide show
  1. package/bin/.bundled +3 -3
  2. package/bin/.metafile.json +7569 -4226
  3. package/bin/assets/generators/application/generator.js +1 -1
  4. package/bin/assets/generators/compute/generator.js +1 -1
  5. package/bin/assets/generators/compute/service/generator.js +1 -1
  6. package/bin/assets/generators/database/generator.js +1 -1
  7. package/bin/assets/generators/domain/generator.js +2 -2
  8. package/bin/assets/generators/organisation/files/organisation/infrastructure.ts +8 -2
  9. package/bin/assets/generators/shared/files/cdk.json +1 -1
  10. package/bin/assets/generators/shared/files/package.json +8 -7
  11. package/bin/assets/generators/shared/files/tsconfig.json +5 -4
  12. package/bin/assets/generators/utils/integrationTestUtils.d.ts +9 -0
  13. package/bin/assets/generators/utils/integrationTestUtils.js +4 -2
  14. package/bin/assets/generators/utils/planning/generatorHelpers.js +2 -2
  15. package/bin/assets/src/util/__tests__/fjallApiClientTestHelpers.d.ts +9 -0
  16. package/bin/assets/src/util/__tests__/fjallApiClientTestHelpers.js +1 -0
  17. package/bin/assets/src/util/__tests__/outputTestHelpers.d.ts +9 -0
  18. package/bin/assets/src/util/__tests__/outputTestHelpers.js +1 -0
  19. package/bin/assets/src/util/agent/__tests__/toonTestHelpers.d.ts +91 -0
  20. package/bin/assets/src/util/agent/__tests__/toonTestHelpers.js +1 -0
  21. package/bin/assets/src/util/agent/actionRequired.d.ts +60 -0
  22. package/bin/assets/src/util/agent/actionRequired.js +1 -0
  23. package/bin/assets/src/util/agent/agentCallbacks.d.ts +21 -0
  24. package/bin/assets/src/util/agent/agentCallbacks.js +1 -0
  25. package/bin/assets/src/util/agent/agentInit.d.ts +17 -0
  26. package/bin/assets/src/util/agent/agentInit.js +288 -0
  27. package/bin/assets/src/util/agent/agentOutput.d.ts +61 -0
  28. package/bin/assets/src/util/agent/agentOutput.js +8 -0
  29. package/bin/assets/src/util/agent/budget.d.ts +19 -0
  30. package/bin/assets/src/util/agent/budget.js +4 -0
  31. package/bin/assets/src/util/agent/detectAgent.d.ts +51 -0
  32. package/bin/assets/src/util/agent/detectAgent.js +1 -0
  33. package/bin/assets/src/util/agent/errorCodeMap.d.ts +16 -0
  34. package/bin/assets/src/util/agent/errorCodeMap.js +1 -0
  35. package/bin/assets/src/util/agent/errorCodes.d.ts +48 -0
  36. package/bin/assets/src/util/agent/errorCodes.js +1 -0
  37. package/bin/assets/src/util/agent/fieldSelection.d.ts +22 -0
  38. package/bin/assets/src/util/agent/fieldSelection.js +1 -0
  39. package/bin/assets/src/util/agent/getSurface.d.ts +27 -0
  40. package/bin/assets/src/util/agent/getSurface.js +1 -0
  41. package/bin/assets/src/util/agent/index.d.ts +27 -0
  42. package/bin/assets/src/util/agent/index.js +1 -0
  43. package/bin/assets/src/util/agent/mcpProtocolEmit.d.ts +31 -0
  44. package/bin/assets/src/util/agent/mcpProtocolEmit.js +2 -0
  45. package/bin/assets/src/util/agent/schemas/appsSchemas.d.ts +18 -0
  46. package/bin/assets/src/util/agent/schemas/appsSchemas.js +1 -0
  47. package/bin/assets/src/util/agent/schemas/assetSchemas.d.ts +13 -0
  48. package/bin/assets/src/util/agent/schemas/assetSchemas.js +1 -0
  49. package/bin/assets/src/util/agent/schemas/awsSchemas.d.ts +5 -0
  50. package/bin/assets/src/util/agent/schemas/awsSchemas.js +1 -0
  51. package/bin/assets/src/util/agent/schemas/deploySchemas.d.ts +8 -0
  52. package/bin/assets/src/util/agent/schemas/deploySchemas.js +1 -0
  53. package/bin/assets/src/util/agent/schemas/index.d.ts +10 -0
  54. package/bin/assets/src/util/agent/schemas/index.js +1 -0
  55. package/bin/assets/src/util/agent/schemas/infraSchemas.d.ts +45 -0
  56. package/bin/assets/src/util/agent/schemas/infraSchemas.js +1 -0
  57. package/bin/assets/src/util/agent/schemas/secretsSchemas.d.ts +13 -0
  58. package/bin/assets/src/util/agent/schemas/secretsSchemas.js +1 -0
  59. package/bin/assets/src/util/agent/schemas/types.d.ts +98 -0
  60. package/bin/assets/src/util/agent/schemas/types.js +0 -0
  61. package/bin/assets/src/util/agent/schemas/userSchemas.d.ts +21 -0
  62. package/bin/assets/src/util/agent/schemas/userSchemas.js +1 -0
  63. package/bin/assets/src/util/agent/sessionHooks.d.ts +47 -0
  64. package/bin/assets/src/util/agent/sessionHooks.js +6 -0
  65. package/bin/assets/src/util/agent/streaming.d.ts +51 -0
  66. package/bin/assets/src/util/agent/streaming.js +1 -0
  67. package/bin/assets/src/util/agent/suggestionEntries/coreEntries.d.ts +2 -0
  68. package/bin/assets/src/util/agent/suggestionEntries/coreEntries.js +1 -0
  69. package/bin/assets/src/util/agent/suggestionEntries/identityEntries.d.ts +2 -0
  70. package/bin/assets/src/util/agent/suggestionEntries/identityEntries.js +1 -0
  71. package/bin/assets/src/util/agent/suggestionEntries/index.d.ts +3 -0
  72. package/bin/assets/src/util/agent/suggestionEntries/index.js +1 -0
  73. package/bin/assets/src/util/agent/suggestionEntries/infraEntries.d.ts +2 -0
  74. package/bin/assets/src/util/agent/suggestionEntries/infraEntries.js +1 -0
  75. package/bin/assets/src/util/agent/suggestionEntries/observabilityEntries.d.ts +2 -0
  76. package/bin/assets/src/util/agent/suggestionEntries/observabilityEntries.js +1 -0
  77. package/bin/assets/src/util/agent/suggestionEntries/secretsEntries.d.ts +2 -0
  78. package/bin/assets/src/util/agent/suggestionEntries/secretsEntries.js +1 -0
  79. package/bin/assets/src/util/agent/suggestionEntries/types.d.ts +17 -0
  80. package/bin/assets/src/util/agent/suggestionEntries/types.js +1 -0
  81. package/bin/assets/src/util/agent/suggestions.d.ts +30 -0
  82. package/bin/assets/src/util/agent/suggestions.js +1 -0
  83. package/bin/assets/src/util/agent/tokenScopes.d.ts +24 -0
  84. package/bin/assets/src/util/agent/tokenScopes.js +1 -0
  85. package/bin/assets/src/util/agent/toonFormatter.d.ts +55 -0
  86. package/bin/assets/src/util/agent/toonFormatter.js +14 -0
  87. package/bin/assets/src/util/api/Credentials.d.ts +13 -0
  88. package/bin/assets/src/util/api/Credentials.js +1 -0
  89. package/bin/assets/src/util/api/FjallApiClient.d.ts +33 -0
  90. package/bin/assets/src/util/api/FjallApiClient.js +1 -0
  91. package/bin/assets/src/util/api/FjallApiClient.types.d.ts +375 -0
  92. package/bin/assets/src/util/api/FjallApiClient.types.js +1 -0
  93. package/bin/assets/src/util/api/FjallApiClientBase.d.ts +13 -0
  94. package/bin/assets/src/util/api/FjallApiClientBase.js +1 -0
  95. package/bin/assets/src/util/api/FjallApiClientDeviceCode.d.ts +13 -0
  96. package/bin/assets/src/util/api/FjallApiClientDeviceCode.js +1 -0
  97. package/bin/assets/src/util/api/FjallApiClientErrors.d.ts +5 -0
  98. package/bin/assets/src/util/api/FjallApiClientErrors.js +1 -0
  99. package/bin/assets/src/util/api/FjallApiClientResources.d.ts +45 -0
  100. package/bin/assets/src/util/api/FjallApiClientResources.js +1 -0
  101. package/bin/assets/src/util/api/index.d.ts +7 -0
  102. package/bin/assets/src/util/api/index.js +1 -0
  103. package/bin/assets/src/util/api/resolveApiKey.d.ts +1 -0
  104. package/bin/assets/src/util/api/resolveApiKey.js +1 -0
  105. package/bin/assets/src/util/api/scaffoldNotification.d.ts +2 -0
  106. package/bin/assets/src/util/api/scaffoldNotification.js +1 -0
  107. package/bin/assets/src/util/awsCleanup.d.ts +9 -0
  108. package/bin/assets/src/util/awsCleanup.js +1 -0
  109. package/bin/assets/src/util/awsTags.d.ts +19 -0
  110. package/bin/assets/src/util/awsTags.js +1 -0
  111. package/bin/assets/src/util/buildxEventAdapter.d.ts +20 -0
  112. package/bin/assets/src/util/buildxEventAdapter.js +1 -0
  113. package/bin/assets/src/util/caseConversion.d.ts +1 -0
  114. package/bin/assets/src/util/caseConversion.js +1 -0
  115. package/bin/assets/src/util/codemod/emitCliTelemetry.d.ts +32 -0
  116. package/bin/assets/src/util/codemod/emitCliTelemetry.js +1 -0
  117. package/bin/assets/src/util/codemod/exitCodes.d.ts +11 -0
  118. package/bin/assets/src/util/codemod/exitCodes.js +1 -0
  119. package/bin/assets/src/util/codemod/index.d.ts +3 -0
  120. package/bin/assets/src/util/codemod/index.js +1 -0
  121. package/bin/assets/src/util/codemod/renderCodemod.d.ts +5 -0
  122. package/bin/assets/src/util/codemod/renderCodemod.js +1 -0
  123. package/bin/assets/src/util/codemod/stepLabels.d.ts +11 -0
  124. package/bin/assets/src/util/codemod/stepLabels.js +1 -0
  125. package/bin/assets/src/util/colourUtils.d.ts +21 -0
  126. package/bin/assets/src/util/colourUtils.js +1 -0
  127. package/bin/assets/src/util/commandErrorHandler.d.ts +16 -0
  128. package/bin/assets/src/util/commandErrorHandler.js +1 -0
  129. package/bin/assets/src/util/commandResult.d.ts +63 -0
  130. package/bin/assets/src/util/commandResult.js +1 -0
  131. package/bin/assets/src/util/concurrency.d.ts +35 -0
  132. package/bin/assets/src/util/concurrency.js +1 -0
  133. package/bin/assets/src/util/deploymentEvents.d.ts +155 -0
  134. package/bin/assets/src/util/deploymentEvents.js +1 -0
  135. package/bin/assets/src/util/errorDisplay.d.ts +4 -0
  136. package/bin/assets/src/util/errorDisplay.js +2 -0
  137. package/bin/assets/src/util/errorUtils.d.ts +1 -0
  138. package/bin/assets/src/util/errorUtils.js +1 -0
  139. package/bin/assets/src/util/executionMode.d.ts +18 -0
  140. package/bin/assets/src/util/executionMode.js +1 -0
  141. package/bin/assets/src/util/formatDeltaValue.d.ts +1 -0
  142. package/bin/assets/src/util/formatDeltaValue.js +1 -0
  143. package/bin/assets/src/util/formatDuration.d.ts +1 -0
  144. package/bin/assets/src/util/formatDuration.js +1 -0
  145. package/bin/assets/src/util/formatRelativeTime.d.ts +1 -0
  146. package/bin/assets/src/util/formatRelativeTime.js +1 -0
  147. package/bin/assets/src/util/fuzzyMatch.d.ts +38 -0
  148. package/bin/assets/src/util/fuzzyMatch.js +1 -0
  149. package/bin/assets/src/util/gitDetection.d.ts +8 -0
  150. package/bin/assets/src/util/gitDetection.js +1 -0
  151. package/bin/assets/src/util/index.d.ts +50 -0
  152. package/bin/assets/src/util/index.js +1 -0
  153. package/bin/assets/src/util/log.d.ts +29 -0
  154. package/bin/assets/src/util/log.js +4 -0
  155. package/bin/assets/src/util/logger/CorrelatedLogger.d.ts +15 -0
  156. package/bin/assets/src/util/logger/CorrelatedLogger.js +1 -0
  157. package/bin/assets/src/util/logger/DeploymentLogger.d.ts +33 -0
  158. package/bin/assets/src/util/logger/DeploymentLogger.js +2 -0
  159. package/bin/assets/src/util/logger/FileRotator.d.ts +17 -0
  160. package/bin/assets/src/util/logger/FileRotator.js +1 -0
  161. package/bin/assets/src/util/logger/LogFileWriter.d.ts +54 -0
  162. package/bin/assets/src/util/logger/LogFileWriter.js +4 -0
  163. package/bin/assets/src/util/logger/Logger.d.ts +43 -0
  164. package/bin/assets/src/util/logger/Logger.js +1 -0
  165. package/bin/assets/src/util/logger/index.d.ts +15 -0
  166. package/bin/assets/src/util/logger/index.js +2 -0
  167. package/bin/assets/src/util/logger/logDir.d.ts +5 -0
  168. package/bin/assets/src/util/logger/logDir.js +1 -0
  169. package/bin/assets/src/util/logger/types.d.ts +48 -0
  170. package/bin/assets/src/util/logger/types.js +1 -0
  171. package/bin/assets/src/util/nonInteractive/index.d.ts +3 -0
  172. package/bin/assets/src/util/nonInteractive/index.js +1 -0
  173. package/bin/assets/src/util/nonInteractive/nonInteractiveCallbacks.d.ts +18 -0
  174. package/bin/assets/src/util/nonInteractive/nonInteractiveCallbacks.js +1 -0
  175. package/bin/assets/src/util/nonInteractive/nonInteractiveCascadeOutput.d.ts +51 -0
  176. package/bin/assets/src/util/nonInteractive/nonInteractiveCascadeOutput.js +1 -0
  177. package/bin/assets/src/util/nonInteractive/nonInteractiveLabels.d.ts +23 -0
  178. package/bin/assets/src/util/nonInteractive/nonInteractiveLabels.js +1 -0
  179. package/bin/assets/src/util/nonInteractive/nonInteractiveOutput.d.ts +128 -0
  180. package/bin/assets/src/util/nonInteractive/nonInteractiveOutput.js +4 -0
  181. package/bin/assets/src/util/nonInteractive/nonInteractiveSummaryOutput.d.ts +29 -0
  182. package/bin/assets/src/util/nonInteractive/nonInteractiveSummaryOutput.js +3 -0
  183. package/bin/assets/src/util/organisationStructure.d.ts +9 -0
  184. package/bin/assets/src/util/organisationStructure.js +1 -0
  185. package/bin/assets/src/util/parseTakeOption.d.ts +1 -0
  186. package/bin/assets/src/util/parseTakeOption.js +1 -0
  187. package/bin/assets/src/util/passwordValidation.d.ts +22 -0
  188. package/bin/assets/src/util/passwordValidation.js +1 -0
  189. package/bin/assets/src/util/pathHelpers.d.ts +19 -0
  190. package/bin/assets/src/util/pathHelpers.js +1 -0
  191. package/bin/assets/src/util/patternDetection.d.ts +7 -0
  192. package/bin/assets/src/util/patternDetection.js +1 -0
  193. package/bin/assets/src/util/promptYesNo.d.ts +5 -0
  194. package/bin/assets/src/util/promptYesNo.js +1 -0
  195. package/bin/assets/src/util/readStdin.d.ts +9 -0
  196. package/bin/assets/src/util/readStdin.js +1 -0
  197. package/bin/assets/src/util/secretsUtils.d.ts +155 -0
  198. package/bin/assets/src/util/secretsUtils.js +3 -0
  199. package/bin/assets/src/util/signalCleanup.d.ts +13 -0
  200. package/bin/assets/src/util/signalCleanup.js +4 -0
  201. package/bin/assets/src/util/stripAnsi.d.ts +2 -0
  202. package/bin/assets/src/util/stripAnsi.js +1 -0
  203. package/bin/assets/src/util/synchronizedOutput.d.ts +26 -0
  204. package/bin/assets/src/util/synchronizedOutput.js +1 -0
  205. package/bin/assets/src/util/targetDetection.d.ts +27 -0
  206. package/bin/assets/src/util/targetDetection.js +1 -0
  207. package/bin/assets/src/util/targetHelpers.d.ts +20 -0
  208. package/bin/assets/src/util/targetHelpers.js +1 -0
  209. package/bin/assets/src/util/terminalCapabilities.d.ts +21 -0
  210. package/bin/assets/src/util/terminalCapabilities.js +1 -0
  211. package/bin/assets/src/util/terminalEscapes.d.ts +29 -0
  212. package/bin/assets/src/util/terminalEscapes.js +1 -0
  213. package/bin/assets/src/util/terminalFocus.d.ts +33 -0
  214. package/bin/assets/src/util/terminalFocus.js +1 -0
  215. package/bin/assets/src/util/theme.d.ts +80 -0
  216. package/bin/assets/src/util/theme.js +1 -0
  217. package/bin/assets/src/util/truncateMiddle.d.ts +9 -0
  218. package/bin/assets/src/util/truncateMiddle.js +1 -0
  219. package/bin/assets/src/util/typeGuards.d.ts +5 -0
  220. package/bin/assets/src/util/typeGuards.js +1 -0
  221. package/bin/assets/src/util/uiRouter.d.ts +13 -0
  222. package/bin/assets/src/util/uiRouter.js +1 -0
  223. package/bin/assets/src/util/urlHelpers.d.ts +4 -0
  224. package/bin/assets/src/util/urlHelpers.js +1 -0
  225. package/bin/assets/src/util/versionDisplay.d.ts +5 -0
  226. package/bin/assets/src/util/versionDisplay.js +1 -0
  227. package/bin/fjall.bundle.js +761 -550
  228. package/package.json +38 -35
@@ -0,0 +1,128 @@
1
+ /**
2
+ * Non-interactive output for CI/CD and piped environments.
3
+ *
4
+ * Rendering implementations split across:
5
+ * - ./nonInteractiveCascadeOutput.ts (cascade, parallel, deployment progress)
6
+ * - ./nonInteractiveSummaryOutput.ts (summary, error detail, secrets)
7
+ */
8
+ import type { FailureAnalysis } from "@fjall/util/aws";
9
+ import type { OrgSetupEventType, PreDeploymentEventType } from "../../types/callbacks.js";
10
+ import type { CascadeDeploymentResult } from "../../services/deployment/CascadeUtils.js";
11
+ import { type SummaryOptions } from "./nonInteractiveLabels.js";
12
+ export { ORG_SETUP_LABELS, PREPARE_LABELS, PRE_DEPLOY_LABELS, type SummaryOptions } from "./nonInteractiveLabels.js";
13
+ export declare const STEP_STATUS: {
14
+ readonly COMPLETED: "completed";
15
+ readonly ERROR: "error";
16
+ readonly SKIPPED: "skipped";
17
+ };
18
+ export declare class OutputWriter {
19
+ private stepTimers;
20
+ private cascadeTimers;
21
+ private parallelStackTimers;
22
+ private eventHistory;
23
+ private lastEcsMessage;
24
+ private lastDockerMessage;
25
+ private verbose;
26
+ private silent;
27
+ private cascadeResult;
28
+ private versionUpdate;
29
+ constructor(options?: {
30
+ verbose?: boolean;
31
+ versionUpdate?: {
32
+ current: string;
33
+ latest: string;
34
+ installSource?: "npm" | "homebrew";
35
+ } | null;
36
+ /**
37
+ * When true, suppress stdout writes from `line()` and `indent()`. Used
38
+ * by agent-mode callers that share an OutputWriter with code paths
39
+ * which would otherwise interleave human cascade output with the
40
+ * structured TOON wire format.
41
+ */
42
+ silent?: boolean;
43
+ });
44
+ line(text: string): void;
45
+ indent(text: string, level?: number): void;
46
+ log(message: string, level?: "info" | "debug" | "warn"): void;
47
+ formatDuration(ms: number): string;
48
+ private formatCounter;
49
+ stepStart(stepId: string, stepName: string, stepIndex?: number, stepTotal?: number): void;
50
+ stepComplete(stepId: string, stepName: string, status: "completed" | "skipped" | "error", stepIndex?: number, stepTotal?: number): void;
51
+ ssoUrl(url: string | null): void;
52
+ authComplete(): void;
53
+ accountDetected(isManagedAccount: boolean): void;
54
+ configurationDetected(config: {
55
+ pattern?: string | null;
56
+ hasDockerfile?: boolean;
57
+ hasDifferences?: boolean;
58
+ }): void;
59
+ orgSetupProgress(event: {
60
+ type: OrgSetupEventType;
61
+ phase: string;
62
+ message?: string;
63
+ detail?: string;
64
+ }): void;
65
+ changeDetectionComplete(changes: {
66
+ hasOrgChanges: boolean;
67
+ hasPlatformChanges: boolean;
68
+ hasAccountChanges: boolean;
69
+ hasMemberAccounts: boolean;
70
+ hasPlatformAccount: boolean;
71
+ hasDomainChanges: boolean;
72
+ hasDomainConfiguration: boolean;
73
+ }): void;
74
+ preDeploymentProgress(event: {
75
+ type: PreDeploymentEventType;
76
+ phase: string;
77
+ message?: string;
78
+ }): void;
79
+ dockerProgress(message: string, percentage?: number): void;
80
+ ecsUpdate(status: string): void;
81
+ ecsProgress(message: string, percentage?: number): void;
82
+ openNextProgress(message: string, type: string, status: string): void;
83
+ resourceProgress(event: {
84
+ logicalId?: string;
85
+ resourceType?: string;
86
+ status?: string;
87
+ statusReason?: string;
88
+ timestamp?: Date;
89
+ }): void;
90
+ parallelDeployStart(stacks: readonly string[]): void;
91
+ parallelStackStart(stack: string): void;
92
+ parallelStackComplete(stack: string, status: string, error?: string): void;
93
+ parallelDeployComplete(allSuccessful: boolean): void;
94
+ cascadePhaseStart(_phase: "platform" | "domains" | "accounts"): void;
95
+ cascadeAccountStart(operationKey: string, _accountId: string, _region: string): void;
96
+ cascadeAccountPhaseChange(operationKey: string, phase: "bootstrap" | "synth" | "deploy" | "destroy", _region?: string): void;
97
+ cascadeAccountResourceProgress(operationKey: string, event: {
98
+ logicalId?: string;
99
+ resourceType?: string;
100
+ status?: string;
101
+ statusReason?: string;
102
+ }, _region?: string): void;
103
+ cascadeAccountComplete(operationKey: string, success: boolean, error?: string): void;
104
+ cascadeComplete(result: CascadeDeploymentResult): void;
105
+ getCascadeResult(): CascadeDeploymentResult | null;
106
+ getFailureAnalysis(): FailureAnalysis | null;
107
+ errorDetail(analysis: FailureAnalysis): void;
108
+ summary(options: SummaryOptions): void;
109
+ cdkOutput(_chunk: string): void;
110
+ secretsWarning(validationResult: {
111
+ missing: Array<{
112
+ name: string;
113
+ expectedPath: string;
114
+ namespace: {
115
+ app: string;
116
+ cluster?: string;
117
+ service?: string;
118
+ };
119
+ }>;
120
+ totalRequired: number;
121
+ }): void;
122
+ }
123
+ export declare class NoopOutputWriter extends OutputWriter {
124
+ stepStart(): void;
125
+ stepComplete(): void;
126
+ log(): void;
127
+ indent(): void;
128
+ }
@@ -0,0 +1,4 @@
1
+ import i from"chalk";import{theme as p}from"../theme.js";import{logger as u}from"../logger/index.js";import{formatDuration as m}from"../formatDuration.js";import{renderParallelDeployStart as g,renderParallelStackStart as f,renderParallelStackComplete as $,renderParallelDeployComplete as P,renderCascadeAccountStart as S,renderCascadeAccountPhaseChange as C,renderCascadeAccountResourceProgress as D,renderCascadeAccountComplete as b,renderOrgSetupProgress as x,renderChangeDetectionComplete as y,renderPreDeploymentProgress as k,renderResourceProgress as R}from"./nonInteractiveCascadeOutput.js";import{computeFailureAnalysis as A,renderErrorDetail as E,renderSummary as T,renderSecretsWarning as v}from"./nonInteractiveSummaryOutput.js";import{ORG_SETUP_LABELS as V,PREPARE_LABELS as j,PRE_DEPLOY_LABELS as q}from"./nonInteractiveLabels.js";const W={COMPLETED:"completed",ERROR:"error",SKIPPED:"skipped"},o=p.colours;class w{stepTimers=new Map;cascadeTimers=new Map;parallelStackTimers=new Map;eventHistory=new Map;lastEcsMessage="";lastDockerMessage="";verbose;silent;cascadeResult=null;versionUpdate;constructor(e={}){this.verbose=e.verbose??!1,this.silent=e.silent??!1,this.versionUpdate=e.versionUpdate??null,u.debug("OutputWriter","Constructor",{inputVerbose:e.verbose,resolvedVerbose:this.verbose,silent:this.silent})}line(e){this.silent||process.stdout.write(`${e}
2
+ `)}indent(e,t=1){this.silent||process.stdout.write(`${" ".repeat(t)}${e}
3
+ `)}log(e,t="info"){const s=t==="warn"?i.hex(o.warning)("[warn]"):i.hex(o.dim)("[log]");this.indent(`${s} ${e}`)}formatDuration(e){return m(e)}formatCounter(e,t){return e!==void 0&&t!==void 0?`[${e}/${t}] `:""}stepStart(e,t,s,r){this.stepTimers.set(e,Date.now());const a=this.formatCounter(s,r);this.line(""),this.line(i.hex(o.summitBlush).bold(`==> ${a}${t}`))}stepComplete(e,t,s,r,a){const l=this.stepTimers.get(e),h=l?this.formatDuration(Date.now()-l):"",n=h?` ${i.hex(o.dim)(`(${h})`)}`:"",c=this.formatCounter(r,a);s==="completed"?this.line(i.hex(o.completed)(`<== ${c}${t}`)+n):s==="error"?this.line(i.hex(o.error)(`x=> ${c}${t}`)+n):this.line(i.hex(o.dim)(`-=> ${c}${t} skipped`)+n)}ssoUrl(e){e&&(this.line(`
4
+ SSO authentication required. Open this URL in your browser:`),this.indent(i.hex(o.primary)(e)),this.line(""))}authComplete(){this.indent(i.hex(o.completed)("\u2713")+" Authentication successful")}accountDetected(e){this.verbose&&this.indent(`Account type: ${e?"managed":"external"}`)}configurationDetected(e){this.verbose&&(this.indent(`Pattern: ${e.pattern??"standard"}`),this.indent(`Docker: ${e.hasDockerfile?"yes":"no"}`),this.indent(`Infrastructure changes: ${e.hasDifferences?"yes":"no"}`))}orgSetupProgress(e){x(this,e,this.verbose)}changeDetectionComplete(e){y(this,e)}preDeploymentProgress(e){k(this,e,this.verbose)}dockerProgress(e,t){if(!e)return;const s=t!==void 0?` (${t}%)`:"",r=`${e}${s}`;r!==this.lastDockerMessage&&(this.lastDockerMessage=r,this.indent(`[docker] ${r}`))}ecsUpdate(e){this.indent(`[ecs] ${e}`)}ecsProgress(e,t){const s=t!==void 0?` (${t}%)`:"",r=`${e}${s}`;r!==this.lastEcsMessage&&(this.lastEcsMessage=r,this.indent(`[ecs] ${r}`))}openNextProgress(e,t,s){const r=s==="complete"?i.hex(o.completed)("\u2713"):s==="error"?i.hex(o.error)("\u2717"):i.hex(o.fjallPink)("\u2026");this.indent(`[opennext:${t}] ${r} ${e}`)}resourceProgress(e){if(e.logicalId&&e.status&&e.resourceType){const t=this.eventHistory.get(e.logicalId)??[];t.push({logicalId:e.logicalId,resourceType:e.resourceType,status:e.status,statusReason:e.statusReason,timestamp:e.timestamp??new Date}),this.eventHistory.set(e.logicalId,t)}R(this,e,this.verbose)}parallelDeployStart(e){g(this,e)}parallelStackStart(e){f(this,this.parallelStackTimers,e)}parallelStackComplete(e,t,s){$(this,this.parallelStackTimers,e,t,s)}parallelDeployComplete(e){P(this,e)}cascadePhaseStart(e){}cascadeAccountStart(e,t,s){S(this,this.cascadeTimers,e,this.verbose)}cascadeAccountPhaseChange(e,t,s){C(this,e,t,this.verbose)}cascadeAccountResourceProgress(e,t,s){D(this,e,t,this.verbose)}cascadeAccountComplete(e,t,s){b(this,this.cascadeTimers,e,t,this.verbose,s)}cascadeComplete(e){u.debug("OutputWriter","cascadeComplete called",{accountCount:e.accountResults.length,hasPlatformResult:!!e.platformResult,verbose:this.verbose}),this.cascadeResult=e}getCascadeResult(){return this.cascadeResult}getFailureAnalysis(){return A(this.eventHistory)}errorDetail(e){E(this,e)}summary(e){T(this,e,this.versionUpdate)}cdkOutput(e){}secretsWarning(e){v(this,e)}}class B extends w{stepStart(){}stepComplete(){}log(){}indent(){}}export{B as NoopOutputWriter,V as ORG_SETUP_LABELS,w as OutputWriter,j as PREPARE_LABELS,q as PRE_DEPLOY_LABELS,W as STEP_STATUS};
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Summary, error detail, failure analysis, and secrets rendering
3
+ * for non-interactive output.
4
+ *
5
+ * Extracted from nonInteractiveOutput.ts for file-size compliance.
6
+ */
7
+ import { type FailureAnalysis } from "@fjall/util/aws";
8
+ import type { ResourceEvent } from "../../aws/utils/cloudformationEvents.js";
9
+ import type { SummaryOptions } from "./nonInteractiveLabels.js";
10
+ import type { OutputPrimitives } from "./nonInteractiveCascadeOutput.js";
11
+ export declare function computeFailureAnalysis(eventHistory: Map<string, ResourceEvent[]>): FailureAnalysis | null;
12
+ export declare function renderErrorDetail(out: OutputPrimitives, analysis: FailureAnalysis): void;
13
+ export declare function renderSummary(out: OutputPrimitives, options: SummaryOptions, versionUpdate: {
14
+ current: string;
15
+ latest: string;
16
+ installSource?: "npm" | "homebrew";
17
+ } | null): void;
18
+ export declare function renderSecretsWarning(out: OutputPrimitives, validationResult: {
19
+ missing: Array<{
20
+ name: string;
21
+ expectedPath: string;
22
+ namespace: {
23
+ app: string;
24
+ cluster?: string;
25
+ service?: string;
26
+ };
27
+ }>;
28
+ totalRequired: number;
29
+ }): void;
@@ -0,0 +1,3 @@
1
+ import r from"chalk";import{theme as v}from"../theme.js";import{CloudFormationFailureAnalyser as b}from"@fjall/util/aws";import{upgradeHint as S}from"../versionDisplay.js";const i=v.colours,u=50;function $(e){const n=`\u2500\u2500 ${e} `;return r.hex(i.dim)(n+"\u2500".repeat(Math.max(0,u-n.length)))}function E(e){return e.size===0?null:new b().analyseFailure(e)}function F(e,n){const c=$("Error Detail"),l=r.hex(i.dim)("\u2500".repeat(u));if(e.line(""),e.line(c),e.line(` ${r.hex(i.error)(n.summary)}`),e.line(` Category: ${n.rootCause.category}`),n.dependencyChain.length>1){e.line(""),e.line(" Dependency chain:");for(const t of n.dependencyChain)e.line(` ${t}`)}if(n.remediation.length>0){e.line(""),e.line(" Remediation:");for(const t of n.remediation)e.line(` ${r.hex(i.dim)("\u2022")} ${t}`)}e.line(l)}function I(e,n,c){const{operation:l,target:t,success:s,duration:h}=n,g=e.formatDuration(h),x={deploy:"deployed",destroy:"destroyed",build:"built",create:"created"},d=n.verb??x[l]??`${l} completed`,y=$("Summary"),D=r.hex(i.dim)("\u2500".repeat(u));if(e.line(""),e.line(y),s?e.line(` Result: ${r.hex(i.completed)("\u2713")} ${t} ${d} successfully`):e.line(` Result: ${r.hex(i.error)("\u2717")} ${t} ${l} failed`),e.line(` Duration: ${g}`),n.url&&e.line(` URL: ${r.hex(i.primary)(n.url)}`),n.details)for(const[m,f]of Object.entries(n.details))e.line(` ${m.padEnd(9)} ${f}`);const a=n.cascadeResult;if(a){const m=a.accountResults.length,f=a.accountResults.filter(o=>o.success&&!o.skipped).length,R=a.accountResults.filter(o=>o.skipped).length,p=m-R;if(p>0&&e.line(` Accounts: ${f}/${p} ${d}`),a.platformResult){const o=a.platformResult.success?`1/1 ${d}`:"0/1 failed";e.line(` Platform: ${o}`)}}e.line(D),c&&(e.line(r.hex(i.deployYellow)(` Update available: ${c.current} \u2192 ${c.latest}`)),e.line(r.dim(` ${S(c.installSource)}`)))}function M(e,n){const{missing:c,totalRequired:l}=n;e.line(""),e.line(r.hex(i.warning)(` Warning: ${c.length} of ${l} required SSM secret(s) are missing:`));for(const t of c)e.indent(`- ${t.name} (expected at ${t.expectedPath})`,2);e.line(""),e.indent("To create missing secrets, run:");for(const t of c){const s=t.namespace;e.indent(r.hex(i.warning)(`fjall secret set ${t.name}=<value> --app ${s.app}${s.cluster?` --cluster ${s.cluster}`:""}${s.service?` --service ${s.service}`:""}`),2)}e.line(r.hex(i.warning)(`
2
+ Deployment will continue, but ECS tasks may fail to start without these secrets.
3
+ `))}export{E as computeFailureAnalysis,F as renderErrorDetail,M as renderSecretsWarning,I as renderSummary};
@@ -0,0 +1,9 @@
1
+ export type OrganisationDir = {
2
+ path: string;
3
+ name: string;
4
+ };
5
+ export declare const ORGANISATION_DIRS: OrganisationDir[];
6
+ declare const ORGANISATION_TARGET_VALUES: readonly ["organisation", "platform", "account"];
7
+ export type OrganisationTarget = (typeof ORGANISATION_TARGET_VALUES)[number];
8
+ export declare const ORGANISATION_TARGETS: ReadonlySet<string>;
9
+ export {};
@@ -0,0 +1 @@
1
+ const a=[{path:"organisation",name:"Organisation"},{path:"platform",name:"Platform"},{path:"account",name:"Account"}],t=["organisation","platform","account"],n=new Set(t);export{a as ORGANISATION_DIRS,n as ORGANISATION_TARGETS};
@@ -0,0 +1 @@
1
+ export declare function parseTakeOption(raw?: string): number | undefined;
@@ -0,0 +1 @@
1
+ function i(e){if(e===void 0||e==="")return;const n=parseInt(e,10);if(!Number.isFinite(n)||n<=0)throw new Error(`Invalid --take value: "${e}" (expected a positive integer)`);return n}export{i as parseTakeOption};
@@ -0,0 +1,22 @@
1
+ /**
2
+ * TODO: Remove this file when import functionality is refactored.
3
+ * See SecretManagerService.ts TODO for details.
4
+ */
5
+ import type { ValidationResult } from "../types/validation.js";
6
+ export type { ValidationResult } from "../types/validation.js";
7
+ /**
8
+ * Validate RDS password requirements (8-128 chars, uppercase, lowercase, number, no /@" or spaces)
9
+ */
10
+ export declare function validateRdsPassword(password: string): ValidationResult;
11
+ /**
12
+ * Validate that password and confirmation match
13
+ */
14
+ export declare function validatePasswordConfirmation(password: string, confirmation: string): ValidationResult;
15
+ /**
16
+ * Validate all passwords for a set of databases
17
+ */
18
+ export declare function validateAllPasswords(passwords: Map<string, string>, confirmations: Map<string, string>, resourceNames: string[]): Map<string, ValidationResult>;
19
+ /**
20
+ * Check if all password validations passed
21
+ */
22
+ export declare function allPasswordsValid(validations: Map<string, ValidationResult>): boolean;
@@ -0,0 +1 @@
1
+ function u(s){const t=[];return s.length<8&&t.push("Password must be at least 8 characters"),s.length>128&&t.push("Password must be less than 128 characters"),/[a-z]/.test(s)||t.push("Password must contain at least one lowercase letter"),/[A-Z]/.test(s)||t.push("Password must contain at least one uppercase letter"),/[0-9]/.test(s)||t.push("Password must contain at least one number"),/[/@" ]/.test(s)&&t.push('Password cannot contain /, @, ", or spaces'),{valid:t.length===0,errors:t}}function c(s,t){return s!==t?{valid:!1,errors:["Passwords do not match"]}:{valid:!0,errors:[]}}function d(s,t,a){const r=new Map;for(const e of a){const n=s.get(e)||"",l=t.get(e)||"";if(!n){r.set(e,{valid:!1,errors:["Password is required"]});continue}const o=u(n);if(!o.valid){r.set(e,o);continue}const i=c(n,l);if(!i.valid){r.set(e,i);continue}r.set(e,{valid:!0,errors:[]})}return r}function f(s){for(const[t,a]of s)if(!a.valid)return!1;return!0}export{f as allPasswordsValid,d as validateAllPasswords,c as validatePasswordConfirmation,u as validateRdsPassword};
@@ -0,0 +1,19 @@
1
+ export declare function getFjallPath(): string;
2
+ export declare function getProjectRoot(appPath: string): string;
3
+ export declare function getAppNameFromInfrastructure(infrastructurePath: string): string;
4
+ export declare function getAppName(appFolderPath: string): string;
5
+ export declare function getInfrastructurePath(appName: string): string;
6
+ export declare function getAppPath(appName: string): string;
7
+ export declare function getOrganisationComponentPath(componentName: string): string;
8
+ export declare function getDeploymentLogsPath(deploymentType: string): string;
9
+ export declare function getCloudFormationLogsPath(deploymentType: string): string;
10
+ export declare function getDomainsDir(): string;
11
+ export declare function getDomainComponentPath(domainName: string): string;
12
+ export declare function getDomainInfrastructurePath(domainName: string): string;
13
+ export declare function getDomainZoneFilePath(domainName: string): string;
14
+ export declare function getDomainDeployStatePath(domainName: string): string;
15
+ export declare const INFRASTRUCTURE_FILENAME = "infrastructure.ts";
16
+ export declare const ZONE_BIND_FILENAME = "zone.bind";
17
+ export declare const DEPLOY_STATE_FILENAME = ".deploy-state.json";
18
+ export declare const CDK_OUT_DIR = "cdk.out";
19
+ export declare function getCdkOutPath(appPath: string): string;
@@ -0,0 +1 @@
1
+ import{join as o,dirname as c,normalize as p}from"node:path";import{readFileSync as s,existsSync as f}from"node:fs";const r="fjall";function a(t,e){const n=p(t);if(n.includes("..")||n.startsWith("/"))throw new Error(`Invalid ${e}: path traversal is not allowed`)}function E(){return r}function I(t){const e=c(t);return c(e)||"."}function m(t){if(!f(t))throw new Error(`Infrastructure file not found: ${t}`);const n=s(t,"utf-8").match(/const\s+appName\s*=\s*["']([^"']+)["']/);if(!n||!n[1])throw new Error(`Could not find appName in infrastructure file: ${t}. Expected format: const appName = "yourAppName";`);return n[1]}function P(t){const e=o(t,u);return m(e)}function A(t){return a(t,"app name"),o(r,t,u)}function F(t){return a(t,"app name"),o(r,t)}function N(t){return a(t,"component name"),o(r,t)}function j(t){return a(t,"deployment type"),o(r,t,"logs")}function L(t){return a(t,"deployment type"),o(r,t,"logs","cloudformation")}function l(){return o(r,"domains")}function i(t){return a(t,"domain name"),o(l(),t)}function R(t){return o(i(t),u)}function _(t){return o(i(t),x)}function w(t){return o(i(t),g)}const u="infrastructure.ts",x="zone.bind",g=".deploy-state.json",d="cdk.out";function C(t){return o(t,d)}export{d as CDK_OUT_DIR,g as DEPLOY_STATE_FILENAME,u as INFRASTRUCTURE_FILENAME,x as ZONE_BIND_FILENAME,P as getAppName,m as getAppNameFromInfrastructure,F as getAppPath,C as getCdkOutPath,L as getCloudFormationLogsPath,j as getDeploymentLogsPath,i as getDomainComponentPath,w as getDomainDeployStatePath,R as getDomainInfrastructurePath,_ as getDomainZoneFilePath,l as getDomainsDir,E as getFjallPath,A as getInfrastructurePath,N as getOrganisationComponentPath,I as getProjectRoot};
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Re-exports pattern detection from @fjall/deploy-core.
3
+ *
4
+ * The authoritative implementation lives in deploy-core. CLI consumers
5
+ * import from this module so their import paths stay unchanged.
6
+ */
7
+ export { type OpenNextPattern, OPENNEXT_PATTERNS, isOpenNextPattern, type AppResourceFlags, deriveResourcesFromManifestStacks, detectPattern, detectPayloadPattern as detectPayload, detectDatabase } from "@fjall/deploy-core";
@@ -0,0 +1 @@
1
+ import{OPENNEXT_PATTERNS as a,isOpenNextPattern as r,deriveResourcesFromManifestStacks as d,detectPattern as s,detectPayloadPattern as P,detectDatabase as c}from"@fjall/deploy-core";export{a as OPENNEXT_PATTERNS,d as deriveResourcesFromManifestStacks,c as detectDatabase,s as detectPattern,P as detectPayload,r as isOpenNextPattern};
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Prompt the user via readline for a yes/no answer.
3
+ * Returns false if stdin is not a TTY (CI/CD).
4
+ */
5
+ export declare function promptYesNo(question: string): Promise<boolean>;
@@ -0,0 +1 @@
1
+ import*as t from"readline";function i(o){if(!process.stdin.isTTY)return Promise.resolve(!1);const e=t.createInterface({input:process.stdin,output:process.stderr});return new Promise(r=>{e.once("close",()=>r(!1)),e.question(`${o} (y/N): `,s=>{e.close(),r(s.trim().toLowerCase()==="y"||s.trim().toLowerCase()==="yes")})})}export{i as promptYesNo};
@@ -0,0 +1,9 @@
1
+ import { type Result } from "../types/Result.js";
2
+ /**
3
+ * Read all of stdin into a UTF-8 string, enforcing a timeout. Strips a single
4
+ * trailing newline so piped values (`echo foo | ...`) match user intent.
5
+ *
6
+ * Returns a `Result` rather than throwing — callers at the command boundary
7
+ * can surface errors via their OutputWriter without a try/catch.
8
+ */
9
+ export declare function readStdinWithTimeout(timeoutMs: number): Promise<Result<string, Error>>;
@@ -0,0 +1 @@
1
+ import{failure as a,success as u}from"../types/Result.js";async function d(e){const n=[],o=(async()=>{for await(const r of process.stdin)n.push(Buffer.from(r))})();o.catch(()=>{});let t;const s=new Promise((r,c)=>{t=setTimeout(()=>c(new Error(`stdin read timed out after ${e/1e3}s`)),e)});try{await Promise.race([o,s])}catch(r){return process.stdin.destroy(),a(r instanceof Error?r:new Error(String(r)))}finally{t!==void 0&&clearTimeout(t)}const i=Buffer.concat(n).toString("utf-8").replace(/\n$/,"");return u(i)}export{d as readStdinWithTimeout};
@@ -0,0 +1,155 @@
1
+ /**
2
+ * Shared utilities for Fjall Secrets Management
3
+ *
4
+ * Provides common functions for formatting, validation, and error handling
5
+ * across the secrets implementation (commands, services, and UI screens).
6
+ */
7
+ import type { SecretNamespace, SecretEntry } from "../types/secrets.js";
8
+ import { type Result } from "../types/Result.js";
9
+ export declare const APP_NAME_PATTERN: RegExp;
10
+ export declare const SECRET_NAME_PATTERN: RegExp;
11
+ export declare const APP_NAME_ERROR: "Must start with a letter, contain only letters, numbers, and hyphens";
12
+ export declare const SECRET_NAME_ERROR: "Secret name must start with a letter or underscore and contain only letters, numbers, underscores, hyphens, or periods";
13
+ /**
14
+ * Format a namespace for display.
15
+ *
16
+ * Builds a hierarchical path string from the namespace components.
17
+ * If the namespace is undefined, returns "(not set)".
18
+ *
19
+ * @param ns - The secret namespace to format, or undefined
20
+ * @returns A formatted path string (e.g., "/myapp/prod/api") or "(not set)"
21
+ *
22
+ * @example
23
+ * formatNamespace({ app: "myapp" }) // returns "/myapp"
24
+ * formatNamespace({ app: "myapp", cluster: "prod" }) // returns "/myapp/prod"
25
+ * formatNamespace({ app: "myapp", cluster: "prod", service: "api" }) // returns "/myapp/prod/api"
26
+ * formatNamespace(undefined) // returns "(not set)"
27
+ */
28
+ export declare function formatNamespace(ns: SecretNamespace | undefined): string;
29
+ /**
30
+ * Build the SSM parameter path from a namespace and optional secret name.
31
+ *
32
+ * Constructs the full path used in AWS SSM Parameter Store following
33
+ * the Fjall hierarchy:
34
+ * - ECS: /<app>/<cluster>/<service>/<name>
35
+ * - Lambda: /<app>/lambda/<functionName>/<name>
36
+ *
37
+ * @param namespace - The secret namespace containing app, cluster, service, or lambda
38
+ * @param name - Optional secret name to append to the path
39
+ * @returns The full SSM parameter path
40
+ *
41
+ * @example
42
+ * buildParameterPath({ app: "myapp" }) // returns "/myapp"
43
+ * buildParameterPath({ app: "myapp", cluster: "prod" }, "API_KEY") // returns "/myapp/prod/API_KEY"
44
+ * buildParameterPath({ app: "myapp", cluster: "prod", service: "api" }, "DB_URL") // returns "/myapp/prod/api/DB_URL"
45
+ * buildParameterPath({ app: "myapp", lambda: "handler" }, "API_KEY") // returns "/myapp/lambda/handler/API_KEY"
46
+ */
47
+ export declare function buildParameterPath(namespace: SecretNamespace, name?: string): string;
48
+ /**
49
+ * Parse an SSM secrets path into a SecretNamespace.
50
+ *
51
+ * This is the inverse of buildParameterPath. It extracts the namespace
52
+ * components from an SSM path string.
53
+ *
54
+ * @param ssmSecretsPath - The SSM path (e.g., "/myapp/cluster/service")
55
+ * @param fallbackApp - Fallback app name if path is malformed
56
+ * @param lambdaName - If provided, creates a Lambda namespace instead of ECS
57
+ * @returns The parsed SecretNamespace
58
+ *
59
+ * @example
60
+ * parseNamespaceFromPath("/myapp/cluster/service", "default")
61
+ * // returns { app: "myapp", cluster: "cluster", service: "service" }
62
+ *
63
+ * parseNamespaceFromPath("/myapp/lambda/handler", "default", "handler")
64
+ * // returns { app: "myapp", lambda: "handler" }
65
+ */
66
+ export declare function parseNamespaceFromPath(ssmSecretsPath: string, fallbackApp: string, lambdaName?: string): SecretNamespace;
67
+ /**
68
+ * Validate an application name.
69
+ *
70
+ * Application names must start with a letter and contain only
71
+ * letters, numbers, and hyphens.
72
+ *
73
+ * @param name - The application name to validate
74
+ * @returns true if the name is valid, false otherwise
75
+ *
76
+ * @example
77
+ * isValidAppName("myapp") // returns true
78
+ * isValidAppName("my-app-123") // returns true
79
+ * isValidAppName("MyApp") // returns true
80
+ * isValidAppName("123app") // returns false (starts with number)
81
+ */
82
+ export declare function isValidAppName(name: string): boolean;
83
+ /**
84
+ * Validate a secret namespace.
85
+ *
86
+ * A namespace must have a non-empty app name, and if a service is specified,
87
+ * a cluster must also be specified.
88
+ *
89
+ * @param namespace - The namespace to validate
90
+ * @returns true if the namespace is valid, false otherwise
91
+ *
92
+ * @example
93
+ * isValidNamespace({ app: "myapp" }) // returns true
94
+ * isValidNamespace({ app: "myapp", cluster: "prod" }) // returns true
95
+ * isValidNamespace({ app: "myapp", cluster: "prod", service: "api" }) // returns true
96
+ * isValidNamespace({ app: "myapp", service: "api" }) // returns false (service without cluster)
97
+ * isValidNamespace({ app: "" }) // returns false (empty app)
98
+ */
99
+ export declare function isValidNamespace(namespace: SecretNamespace): boolean;
100
+ /**
101
+ * Validate a secret name.
102
+ *
103
+ * Secret names must start with a letter or underscore and contain only
104
+ * letters, numbers, underscores, hyphens, or periods. This matches
105
+ * the characters allowed by AWS SSM Parameter Store.
106
+ *
107
+ * @param name - The secret name to validate
108
+ * @returns true if the name is valid, false otherwise
109
+ *
110
+ * @example
111
+ * isValidSecretName("API_KEY") // returns true
112
+ * isValidSecretName("db.password") // returns true
113
+ * isValidSecretName("my-secret") // returns true
114
+ * isValidSecretName("123_KEY") // returns false (starts with number)
115
+ */
116
+ export declare function isValidSecretName(name: string): boolean;
117
+ /**
118
+ * Parse .env file contents into key-value pairs.
119
+ *
120
+ * Parses a dotenv-formatted string, extracting valid secret names and values.
121
+ * Handles both single and double-quoted values, comments, and empty lines.
122
+ *
123
+ * **Limitation**: Multiline values are not supported. Values containing literal
124
+ * newlines (e.g., private keys, certificates) will be truncated at the first
125
+ * newline. For such values, use base64 encoding or store them in AWS Secrets
126
+ * Manager instead of SSM Parameter Store.
127
+ *
128
+ * @param content - The raw .env file content
129
+ * @returns A record of secret names to values
130
+ *
131
+ * @example
132
+ * parseDotEnv('API_KEY="abc123"\nDB_URL=postgres://...')
133
+ * // returns { API_KEY: "abc123", DB_URL: "postgres://..." }
134
+ */
135
+ export declare function parseDotEnv(content: string): Record<string, string>;
136
+ /**
137
+ * Parse a key=value pair, validating the key is a valid secret name.
138
+ */
139
+ export declare function parseKeyValue(input: string): {
140
+ key: string;
141
+ value: string;
142
+ } | null;
143
+ /**
144
+ * Read a secret value from a file, trimming the trailing newline.
145
+ */
146
+ export declare function readSecretValueFromFile(filePath: string): Result<string, Error>;
147
+ /**
148
+ * Read and parse a .env file into key-value pairs.
149
+ * Returns failure if the file doesn't exist, can't be read, or contains no valid secrets.
150
+ */
151
+ export declare function readDotEnvFile(filePath: string): Result<Record<string, string>, Error>;
152
+ /**
153
+ * Format a list of secrets as an aligned table string.
154
+ */
155
+ export declare function formatSecretsTable(secrets: readonly SecretEntry[]): string;
@@ -0,0 +1,3 @@
1
+ import{existsSync as u,readFileSync as c}from"node:fs";import{success as l,failure as a}from"../types/Result.js";import{VALIDATION_PATTERNS as f,VALIDATION_MESSAGES as p}from"../validation/patterns.js";import{getErrorMessage as d}from"./errorUtils.js";const h=f.IDENTIFIER,E=f.SECRET_NAME,M=p.IDENTIFIER,P=p.SECRET_NAME;function m(e){const t=[e.app];return e.lambda?t.push("lambda",e.lambda):(e.cluster&&t.push(e.cluster),e.service&&e.cluster&&t.push(e.service)),t}function _(e){return e?"/"+m(e).join("/"):"(not set)"}function $(e,t){const n=m(e);return t&&n.push(t),"/"+n.join("/")}function g(e,t,n){const r=e.split("/").filter(Boolean);return n?{app:r[0]??t,lambda:n}:{app:r[0]??t,cluster:r.length>1?r[1]:void 0,service:r.length>2?r[2]:void 0}}function v(e){return h.test(e)}function F(e){return!(!e.app||e.service&&!e.cluster)}function N(e){return E.test(e)}function A(e){const t={};for(const n of e.split(`
2
+ `)){const r=n.trim();if(!r||r.startsWith("#"))continue;const o=r.indexOf("=");if(o===-1)continue;const i=r.slice(0,o).trim();if(!E.test(i))continue;const s=r.slice(o+1),x=s.length>=2&&(s.startsWith('"')&&s.endsWith('"')||s.startsWith("'")&&s.endsWith("'"))?s.slice(1,-1):s;t[i]=x}return t}function b(e){const t=e.indexOf("=");if(t===-1)return null;const n=e.substring(0,t),r=e.substring(t+1);return N(n)?{key:n,value:r}:null}function y(e){if(!u(e))return a(new Error(`File not found: ${e}`));try{return l(c(e,"utf-8").replace(/\n$/,""))}catch(t){return a(new Error(d(t)))}}function O(e){if(!u(e))return a(new Error(`File not found: ${e}`));let t;try{t=c(e,"utf-8")}catch(r){return a(new Error(`Could not read file: ${d(r)}`))}const n=A(t);return Object.keys(n).length===0?a(new Error("No valid secrets found in file")):l(n)}function V(e){const t=e.reduce((o,i)=>Math.max(o,i.name.length),4),n=e.reduce((o,i)=>Math.max(o,i.fullPath.length),4),r=[` ${"Name".padEnd(t)} ${"Path".padEnd(n)} Last Modified`,` ${"-".repeat(t)} ${"-".repeat(n)} ${"-".repeat(20)}`];for(const o of e){const i=o.lastModified?o.lastModified.toISOString().replace("T"," ").slice(0,19):"N/A";r.push(` ${o.name.padEnd(t)} ${o.fullPath.padEnd(n)} ${i}`)}return r.join(`
3
+ `)}export{M as APP_NAME_ERROR,h as APP_NAME_PATTERN,P as SECRET_NAME_ERROR,E as SECRET_NAME_PATTERN,$ as buildParameterPath,_ as formatNamespace,V as formatSecretsTable,v as isValidAppName,F as isValidNamespace,N as isValidSecretName,A as parseDotEnv,b as parseKeyValue,g as parseNamespaceFromPath,O as readDotEnvFile,y as readSecretValueFromFile};
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Registers a cleanup function with ProcessManager for graceful cancellation
3
+ * in CLI commands. The cleanup is automatically deregistered when the wrapped
4
+ * function completes.
5
+ *
6
+ * Always prints the cancellation message on shutdown, even when no cleanup
7
+ * callback is provided.
8
+ *
9
+ * @param message - User-facing cancellation message (e.g. "Deployment cancelled by user")
10
+ * @param fn - The async operation to wrap
11
+ * @param cleanup - Optional async cleanup before exit (e.g. AwsContext.destroyInstance)
12
+ */
13
+ export declare function withSignalCleanup<T>(message: string, fn: () => Promise<T>, cleanup?: () => Promise<void>): Promise<T>;
@@ -0,0 +1,4 @@
1
+ import{processManager as a}from"../ui/utils/ProcessManager.js";async function s(n,t,r){const i=a.registerCleanup(`withSignalCleanup: ${n}`,async()=>{process.stderr.write(`
2
+
3
+ ${n}
4
+ `),r&&await r()});try{return await t()}finally{i()}}export{s as withSignalCleanup};
@@ -0,0 +1,2 @@
1
+ /** Strip ANSI escape codes from a string (SGR, CSI, and OSC sequences) */
2
+ export declare function stripAnsi(str: string): string;
@@ -0,0 +1 @@
1
+ function r(x){return x.replace(/\x1b\[[0-9;]*[A-Za-z]|\x1b\][^\x07]*\x07|\x1b\(B/g,"")}export{r as stripAnsi};
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Synchronized terminal output using DEC private mode 2026.
3
+ *
4
+ * Wraps frame writes in BSU/ESU (Begin/End Synchronized Update) markers
5
+ * so the terminal renders each frame atomically, preventing flicker during
6
+ * high-frequency updates (e.g. CloudFormation events, spinner + progress).
7
+ *
8
+ * Safe no-op on terminals that don't support it.
9
+ */
10
+ /** Begin Synchronized Update — tells the terminal to buffer output */
11
+ declare const BSU = "\u001B[?2026h";
12
+ /** End Synchronized Update — tells the terminal to flush the buffer */
13
+ declare const ESU = "\u001B[?2026l";
14
+ /** Whether the current terminal supports synchronized output */
15
+ export declare const SYNC_OUTPUT_SUPPORTED: boolean;
16
+ /**
17
+ * Wrap a stdout write function so each call is sent as an atomic frame.
18
+ * On unsupported terminals, returns the original function unchanged.
19
+ */
20
+ export declare function wrapSynchronizedOutput(write: (data: string) => boolean): (data: string) => boolean;
21
+ /**
22
+ * Wrap raw data in BSU/ESU markers for synchronized output.
23
+ * Returns the data unchanged on unsupported terminals.
24
+ */
25
+ export declare function synchronize(data: string): string;
26
+ export { BSU, ESU };
@@ -0,0 +1 @@
1
+ const n="\x1B[?2026h",o="\x1B[?2026l";function c(){if(process.env.TMUX)return!1;const r=process.env.TERM_PROGRAM??"",t=process.env.TERM??"";if(r==="iTerm.app"||r==="ghostty"||r==="WezTerm"||t.includes("kitty")||t==="foot"||t==="foot-extra"||r==="Alacritty"||process.env.WT_SESSION)return!0;const e=process.env.VTE_VERSION;if(e){const s=parseInt(e.slice(0,2),10),i=parseInt(e.slice(2,4),10);if(s>0||i>=68)return!0}return!1}const u=c();function f(r){return u?t=>r(n+t+o):r}function p(r){return u?n+r+o:r}export{n as BSU,o as ESU,u as SYNC_OUTPUT_SUPPORTED,p as synchronize,f as wrapSynchronizedOutput};
@@ -0,0 +1,27 @@
1
+ import { type Result } from "../types/Result.js";
2
+ export type TargetType = "organisation" | "application";
3
+ export interface Target {
4
+ name: string;
5
+ type: TargetType;
6
+ path: string;
7
+ description: string;
8
+ appName?: string;
9
+ }
10
+ export declare function getAvailableTargets(baseDir?: string): Target[];
11
+ export declare function getOrganisationTargets(baseDir?: string): Target[];
12
+ export declare function getApplicationTargets(baseDir?: string): Target[];
13
+ /** Local app targets intersected with apps registered on the Fjall API for the authenticated org. */
14
+ export declare function getRegisteredApplicationTargets(baseDir?: string): Promise<Result<Target[], Error>>;
15
+ /** Check if a target name is an organisation target */
16
+ export declare function isOrganisationTarget(targetName: string): boolean;
17
+ export interface TargetValidationResult {
18
+ error?: string;
19
+ path: string;
20
+ }
21
+ interface TargetValidationMessages {
22
+ folderNotFound: string;
23
+ noInfrastructure: string;
24
+ }
25
+ /** Validate that a deploy/destroy target exists and has required files */
26
+ export declare function validateTarget(target: string, messages: TargetValidationMessages): TargetValidationResult;
27
+ export {};
@@ -0,0 +1 @@
1
+ import{existsSync as a,readdirSync as y}from"fs";import{join as c}from"path";import{ORGANISATION_DIRS as h,ORGANISATION_TARGETS as C}from"./organisationStructure.js";import{Config as I}from"@fjall/util/config";import{logger as N}from"./logger/index.js";import{getAppNameFromInfrastructure as w,getAppPath as R,getOrganisationComponentPath as S,INFRASTRUCTURE_FILENAME as d}from"./pathHelpers.js";import{FjallApiClient as D}from"./api/FjallApiClient.js";import{success as F,failure as A}from"../types/Result.js";function T(e){const t=[],o=e||(()=>{const r=I.getConfigDirectory();return r&&a(r)?r:process.cwd()})();if(!o||!a(o))return t;const n=o;if(!a(n))return t;const i=y(n,{withFileTypes:!0});for(const r of i){if(!r.isDirectory())continue;const s=c(o,r.name),g=h.find(p=>p.path===r.name);if(g)t.push({name:r.name,type:"organisation",path:s,description:g.name||r.name});else{const p=a(c(s,"cdk.json")),f=c(s,d),l=a(f);if(p||l){let u;if(l)try{u=w(f)}catch(m){N.debug("TargetDetection","Failed to parse infrastructure, falling back to directory name",{path:f,error:m instanceof Error?m.message:String(m)}),u=r.name}t.push({name:r.name,type:"application",path:s,description:"Application",appName:u})}}}return t}function _(e){return T(e).filter(t=>t.type==="organisation")}function O(e){return T(e).filter(t=>t.type==="application")}async function v(e){const t=O(e),o=D.fromCredentials();if(!o)return A(new Error("Not authenticated \u2014 run `fjall login` and try again."));const n=await o.listApps();if(!n.success)return A(new Error(`Could not reach Fjall API: ${n.error.message}`));const i=new Set(n.data.map(r=>r.name.toLowerCase()));return F(t.filter(r=>i.has(r.name.toLowerCase())))}function U(e){return h.some(t=>t.path===e)}function J(e,t){const o=C.has(e.toLowerCase()),n=o?S(e):R(e);if(!a(n))return{error:t.folderNotFound,path:n};if(!o){const i=c(n,d);if(!a(i))return{error:t.noInfrastructure,path:n}}return{path:n}}export{O as getApplicationTargets,T as getAvailableTargets,_ as getOrganisationTargets,v as getRegisteredApplicationTargets,U as isOrganisationTarget,J as validateTarget};
@@ -0,0 +1,20 @@
1
+ import { type ProviderAccount } from "@fjall/util/config";
2
+ export declare const TARGET_MESSAGES: {
3
+ readonly FETCH_ORG_CONFIG_FAILED: "Failed to fetch organisation config. Are you logged in?";
4
+ readonly FETCH_ORG_CONFIG_ERROR: "Failed to fetch organisation config";
5
+ readonly NO_TARGETS_AVAILABLE: "No deployment targets available. Connect an AWS account first.";
6
+ readonly NO_ACTIVE_TARGET: "No active target set. Use 'fjall target set' to set one.";
7
+ readonly NO_ACTIVE_TARGET_CLI: "No active target set. Use 'fjall target set <name>' to set one.";
8
+ readonly TARGET_STALE: "Target no longer exists in organisation config";
9
+ };
10
+ /** Filter out the root management account — used when listing deployable targets. */
11
+ export declare function filterNonRootAccounts(accounts: ProviderAccount[]): ProviderAccount[];
12
+ /**
13
+ * Filter out every structural environment (root, platform, and any future
14
+ * additions) — used by `accounts list` and the MCP `plan_app_scaffold`
15
+ * auto-target resolver. New entries to `STRUCTURAL_ENVIRONMENTS` are
16
+ * automatically excluded.
17
+ */
18
+ export declare function filterStructuralAccounts(accounts: ProviderAccount[]): ProviderAccount[];
19
+ export declare function loadActiveTarget(): string | undefined;
20
+ export declare function saveActiveTarget(name: string): void;
@@ -0,0 +1 @@
1
+ import{Config as o}from"@fjall/util/config";import{STRUCTURAL_ENVIRONMENTS as n}from"@fjall/util";const c={FETCH_ORG_CONFIG_FAILED:"Failed to fetch organisation config. Are you logged in?",FETCH_ORG_CONFIG_ERROR:"Failed to fetch organisation config",NO_TARGETS_AVAILABLE:"No deployment targets available. Connect an AWS account first.",NO_ACTIVE_TARGET:"No active target set. Use 'fjall target set' to set one.",NO_ACTIVE_TARGET_CLI:"No active target set. Use 'fjall target set <name>' to set one.",TARGET_STALE:"Target no longer exists in organisation config"},i=new Set(Object.values(n));function g(t){return t.filter(e=>e.environment!==n.ROOT)}function T(t){return t.filter(e=>!i.has(e.environment))}function s(){return o.loadConfig().getActiveTarget()}function f(t){const e=o.loadConfig();e.setActiveTarget(t),e.saveConfig()}export{c as TARGET_MESSAGES,g as filterNonRootAccounts,T as filterStructuralAccounts,s as loadActiveTarget,f as saveActiveTarget};
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Initialise terminal capability detection.
3
+ * Adjusts chalk colour level based on detected terminal environment.
4
+ *
5
+ * Order matters:
6
+ * 1. Boost xterm.js first (VS Code reports level 2 but supports truecolor)
7
+ * 2. Clamp for tmux (default tmux config doesn't pass truecolor to outer terminal)
8
+ */
9
+ export declare function initTerminalCapabilities(): void;
10
+ /** Whether the process is running inside tmux */
11
+ export declare function isTmux(): boolean;
12
+ /** Whether chalk level was clamped for tmux compatibility */
13
+ export declare function isTmuxClamped(): boolean;
14
+ /** Whether chalk level was boosted for VS Code xterm.js */
15
+ export declare function isXtermJsBoosted(): boolean;
16
+ /** Whether the terminal supports truecolor (24-bit RGB) */
17
+ export declare function isFullColor(): boolean;
18
+ /** Whether the terminal supports 256 colours */
19
+ export declare function is256Color(): boolean;
20
+ /** Reset state for test isolation */
21
+ export declare function _resetTerminalCapabilities(): void;
@@ -0,0 +1 @@
1
+ import e from"chalk";import{logger as n}from"./logger/index.js";let o=!1,r=!1,t=!1,i=!1,l;function f(){if(o)return;o=!0,l=e.level,r=!!process.env.TMUX,(process.env.TERM_PROGRAM??"")==="vscode"&&e.level===2&&(e.level=3,i=!0,n.debug("Terminal","Boosted chalk level to 3 (truecolor) for VS Code xterm.js")),r&&!process.env.FJALL_TMUX_TRUECOLOR&&e.level>2&&(e.level=2,t=!0,n.debug("Terminal","Clamped chalk level to 2 (256-color) for tmux compatibility",{originalLevel:l}))}function m(){return r}function c(){return t}function p(){return i}function v(){return e.level>=3}function d(){return e.level>=2}function x(){l!==void 0&&(e.level=l),o=!1,r=!1,t=!1,i=!1,l=void 0}export{x as _resetTerminalCapabilities,f as initTerminalCapabilities,d as is256Color,v as isFullColor,m as isTmux,c as isTmuxClamped,p as isXtermJsBoosted};