@rulebricks/cli 1.9.0

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 (93) hide show
  1. package/README.md +62 -0
  2. package/dist/commands/clone.d.ts +6 -0
  3. package/dist/commands/clone.js +60 -0
  4. package/dist/commands/deploy.d.ts +8 -0
  5. package/dist/commands/deploy.js +409 -0
  6. package/dist/commands/destroy.d.ts +8 -0
  7. package/dist/commands/destroy.js +298 -0
  8. package/dist/commands/init.d.ts +7 -0
  9. package/dist/commands/init.js +201 -0
  10. package/dist/commands/logs.d.ts +9 -0
  11. package/dist/commands/logs.js +222 -0
  12. package/dist/commands/open.d.ts +7 -0
  13. package/dist/commands/open.js +139 -0
  14. package/dist/commands/status.d.ts +5 -0
  15. package/dist/commands/status.js +125 -0
  16. package/dist/commands/upgrade.d.ts +7 -0
  17. package/dist/commands/upgrade.js +239 -0
  18. package/dist/components/DNSWaitScreen.d.ts +9 -0
  19. package/dist/components/DNSWaitScreen.js +73 -0
  20. package/dist/components/Wizard/WizardContext.d.ts +176 -0
  21. package/dist/components/Wizard/WizardContext.js +346 -0
  22. package/dist/components/Wizard/index.d.ts +2 -0
  23. package/dist/components/Wizard/index.js +2 -0
  24. package/dist/components/Wizard/steps/CloudProviderStep.d.ts +6 -0
  25. package/dist/components/Wizard/steps/CloudProviderStep.js +210 -0
  26. package/dist/components/Wizard/steps/CredentialsStep.d.ts +6 -0
  27. package/dist/components/Wizard/steps/CredentialsStep.js +22 -0
  28. package/dist/components/Wizard/steps/DatabaseStep.d.ts +6 -0
  29. package/dist/components/Wizard/steps/DatabaseStep.js +80 -0
  30. package/dist/components/Wizard/steps/DeploymentModeStep.d.ts +5 -0
  31. package/dist/components/Wizard/steps/DeploymentModeStep.js +26 -0
  32. package/dist/components/Wizard/steps/DomainStep.d.ts +6 -0
  33. package/dist/components/Wizard/steps/DomainStep.js +126 -0
  34. package/dist/components/Wizard/steps/FeatureConfigStep.d.ts +6 -0
  35. package/dist/components/Wizard/steps/FeatureConfigStep.js +765 -0
  36. package/dist/components/Wizard/steps/FeaturesStep.d.ts +6 -0
  37. package/dist/components/Wizard/steps/FeaturesStep.js +119 -0
  38. package/dist/components/Wizard/steps/ReviewStep.d.ts +6 -0
  39. package/dist/components/Wizard/steps/ReviewStep.js +56 -0
  40. package/dist/components/Wizard/steps/SMTPStep.d.ts +6 -0
  41. package/dist/components/Wizard/steps/SMTPStep.js +191 -0
  42. package/dist/components/Wizard/steps/SupabaseCredentialsStep.d.ts +6 -0
  43. package/dist/components/Wizard/steps/SupabaseCredentialsStep.js +76 -0
  44. package/dist/components/Wizard/steps/TierStep.d.ts +6 -0
  45. package/dist/components/Wizard/steps/TierStep.js +29 -0
  46. package/dist/components/Wizard/steps/VersionStep.d.ts +6 -0
  47. package/dist/components/Wizard/steps/VersionStep.js +113 -0
  48. package/dist/components/Wizard/steps/index.d.ts +12 -0
  49. package/dist/components/Wizard/steps/index.js +12 -0
  50. package/dist/components/common/AppShell.d.ts +31 -0
  51. package/dist/components/common/AppShell.js +31 -0
  52. package/dist/components/common/Box.d.ts +20 -0
  53. package/dist/components/common/Box.js +20 -0
  54. package/dist/components/common/Logo.d.ts +7 -0
  55. package/dist/components/common/Logo.js +22 -0
  56. package/dist/components/common/Spinner.d.ts +12 -0
  57. package/dist/components/common/Spinner.js +28 -0
  58. package/dist/components/common/index.d.ts +6 -0
  59. package/dist/components/common/index.js +5 -0
  60. package/dist/index.d.ts +2 -0
  61. package/dist/index.js +202 -0
  62. package/dist/lib/cloudCli.d.ts +156 -0
  63. package/dist/lib/cloudCli.js +691 -0
  64. package/dist/lib/config.d.ts +91 -0
  65. package/dist/lib/config.js +278 -0
  66. package/dist/lib/dns.d.ts +41 -0
  67. package/dist/lib/dns.js +235 -0
  68. package/dist/lib/dockerHub.d.ts +57 -0
  69. package/dist/lib/dockerHub.js +128 -0
  70. package/dist/lib/helm.d.ts +53 -0
  71. package/dist/lib/helm.js +209 -0
  72. package/dist/lib/helmValues.d.ts +17 -0
  73. package/dist/lib/helmValues.js +693 -0
  74. package/dist/lib/kubernetes.d.ts +161 -0
  75. package/dist/lib/kubernetes.js +755 -0
  76. package/dist/lib/terraform.d.ts +44 -0
  77. package/dist/lib/terraform.js +230 -0
  78. package/dist/lib/theme.d.ts +81 -0
  79. package/dist/lib/theme.js +115 -0
  80. package/dist/lib/validation.d.ts +47 -0
  81. package/dist/lib/validation.js +164 -0
  82. package/dist/lib/versions.d.ts +69 -0
  83. package/dist/lib/versions.js +139 -0
  84. package/dist/types/index.d.ts +718 -0
  85. package/dist/types/index.js +556 -0
  86. package/email-templates/email_change.html +325 -0
  87. package/email-templates/invite.html +383 -0
  88. package/email-templates/password_change.html +414 -0
  89. package/email-templates/verify.html +396 -0
  90. package/package.json +78 -0
  91. package/terraform/aws/main.tf +327 -0
  92. package/terraform/azure/main.tf +326 -0
  93. package/terraform/gcp/main.tf +369 -0
@@ -0,0 +1,12 @@
1
+ export { DeploymentModeStep } from './DeploymentModeStep.js';
2
+ export { CloudProviderStep } from './CloudProviderStep.js';
3
+ export { DomainStep } from './DomainStep.js';
4
+ export { SMTPStep } from './SMTPStep.js';
5
+ export { DatabaseStep } from './DatabaseStep.js';
6
+ export { SupabaseCredentialsStep } from './SupabaseCredentialsStep.js';
7
+ export { TierStep } from './TierStep.js';
8
+ export { FeaturesStep } from './FeaturesStep.js';
9
+ export { FeatureConfigStep } from './FeatureConfigStep.js';
10
+ export { VersionStep } from './VersionStep.js';
11
+ export { CredentialsStep } from './CredentialsStep.js';
12
+ export { ReviewStep } from './ReviewStep.js';
@@ -0,0 +1,12 @@
1
+ export { DeploymentModeStep } from './DeploymentModeStep.js';
2
+ export { CloudProviderStep } from './CloudProviderStep.js';
3
+ export { DomainStep } from './DomainStep.js';
4
+ export { SMTPStep } from './SMTPStep.js';
5
+ export { DatabaseStep } from './DatabaseStep.js';
6
+ export { SupabaseCredentialsStep } from './SupabaseCredentialsStep.js';
7
+ export { TierStep } from './TierStep.js';
8
+ export { FeaturesStep } from './FeaturesStep.js';
9
+ export { FeatureConfigStep } from './FeatureConfigStep.js';
10
+ export { VersionStep } from './VersionStep.js';
11
+ export { CredentialsStep } from './CredentialsStep.js';
12
+ export { ReviewStep } from './ReviewStep.js';
@@ -0,0 +1,31 @@
1
+ import { ReactNode } from "react";
2
+ interface AppShellProps {
3
+ children: ReactNode;
4
+ title?: string;
5
+ subtitle?: string;
6
+ width?: number;
7
+ }
8
+ /**
9
+ * AppShell wraps the entire application content with consistent margins,
10
+ * padding, and a border container for a focused visual experience.
11
+ */
12
+ export declare function AppShell({ children, title, subtitle, width, }: AppShellProps): import("react/jsx-runtime").JSX.Element;
13
+ interface ScreenContainerProps {
14
+ children: ReactNode;
15
+ title: string;
16
+ width?: number;
17
+ }
18
+ /**
19
+ * ScreenContainer is a simpler bordered container for individual screens/steps
20
+ */
21
+ export declare function ScreenContainer({ children, title, width, }: ScreenContainerProps): import("react/jsx-runtime").JSX.Element;
22
+ interface ProgressHeaderProps {
23
+ currentStep: number;
24
+ totalSteps: number;
25
+ stepTitle: string;
26
+ }
27
+ /**
28
+ * Progress header showing step number and progress bar
29
+ */
30
+ export declare function ProgressHeader({ currentStep, totalSteps, stepTitle, }: ProgressHeaderProps): import("react/jsx-runtime").JSX.Element;
31
+ export {};
@@ -0,0 +1,31 @@
1
+ import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
2
+ import { Box, Text } from "ink";
3
+ import { useTheme } from "../../lib/theme.js";
4
+ /**
5
+ * AppShell wraps the entire application content with consistent margins,
6
+ * padding, and a border container for a focused visual experience.
7
+ */
8
+ export function AppShell({ children, title, subtitle, width = 70, }) {
9
+ const { colors } = useTheme();
10
+ const horizontalBorder = "─".repeat(width - 2);
11
+ return (_jsxs(Box, { flexDirection: "column", paddingTop: 0, paddingLeft: 2, children: [_jsxs(Text, { color: colors.accent, children: ["\u256D", title ? `─ ${title} ` : "", "─".repeat(width - 4 - (title?.length || 0)), "\u256E"] }), subtitle && (_jsx(Box, { paddingX: 2, children: _jsx(Text, { color: colors.muted, children: subtitle }) })), _jsx(Box, { flexDirection: "column", paddingX: 2, paddingY: 1, children: children }), _jsxs(Text, { color: colors.accent, children: ["\u2570", horizontalBorder, "\u256F"] })] }));
12
+ }
13
+ /**
14
+ * ScreenContainer is a simpler bordered container for individual screens/steps
15
+ */
16
+ export function ScreenContainer({ children, title, width = 66, }) {
17
+ const { colors } = useTheme();
18
+ const innerWidth = width - 4;
19
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, { children: [_jsx(Text, { color: colors.accent, children: "\u250C\u2500 " }), _jsx(Text, { bold: true, color: "white", children: title }), _jsxs(Text, { color: colors.accent, children: [" ", "─".repeat(Math.max(0, innerWidth - title.length - 2)), "\u2510"] })] }), _jsx(Box, { flexDirection: "column", children: _jsxs(Box, { children: [_jsx(Text, { color: colors.accent, children: "\u2502" }), _jsx(Box, { flexDirection: "column", paddingX: 1, width: innerWidth, children: children }), _jsx(Text, { color: colors.accent, children: "\u2502" })] }) }), _jsxs(Text, { color: colors.accent, children: ["\u2514", "─".repeat(width - 1), "\u2518"] })] }));
20
+ }
21
+ /**
22
+ * Progress header showing step number and progress bar
23
+ */
24
+ export function ProgressHeader({ currentStep, totalSteps, stepTitle, }) {
25
+ const { colors } = useTheme();
26
+ const percentage = Math.round((currentStep / totalSteps) * 100);
27
+ const barWidth = 30;
28
+ const filled = Math.round((percentage / 100) * barWidth);
29
+ const empty = barWidth - filled;
30
+ return (_jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [_jsxs(Text, { color: colors.muted, children: ["Step ", currentStep, " of ", totalSteps, ":", " ", _jsx(Text, { color: "white", children: stepTitle })] }), _jsxs(Box, { marginTop: 1, children: [_jsx(Text, { color: colors.accent, children: "[" }), _jsx(Text, { color: colors.success, children: "█".repeat(filled) }), _jsx(Text, { color: colors.muted, children: "░".repeat(empty) }), _jsx(Text, { color: colors.accent, children: "]" }), _jsxs(Text, { color: colors.muted, children: [" ", percentage, "%"] })] })] }));
31
+ }
@@ -0,0 +1,20 @@
1
+ import React from "react";
2
+ interface BorderBoxProps {
3
+ title?: string;
4
+ children: React.ReactNode;
5
+ width?: number;
6
+ borderColor?: string;
7
+ }
8
+ export declare function BorderBox({ title, children, width, borderColor, }: BorderBoxProps): import("react/jsx-runtime").JSX.Element;
9
+ interface SectionProps {
10
+ title: string;
11
+ children: React.ReactNode;
12
+ }
13
+ export declare function Section({ title, children }: SectionProps): import("react/jsx-runtime").JSX.Element;
14
+ interface ProgressBarProps {
15
+ current: number;
16
+ total: number;
17
+ width?: number;
18
+ }
19
+ export declare function ProgressBar({ current, total, width }: ProgressBarProps): import("react/jsx-runtime").JSX.Element;
20
+ export {};
@@ -0,0 +1,20 @@
1
+ import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
2
+ import { Box as InkBox, Text } from "ink";
3
+ import { useTheme } from "../../lib/theme.js";
4
+ export function BorderBox({ title, children, width = 60, borderColor, }) {
5
+ const { colors } = useTheme();
6
+ const actualBorderColor = borderColor || colors.accent;
7
+ const horizontalBorder = "─".repeat(width - 1);
8
+ return (_jsxs(InkBox, { flexDirection: "column", children: [_jsxs(Text, { color: actualBorderColor, children: ["\u250C", title ? `─ ${title} ` : "", "─".repeat(width - 4 - (title?.length || 0)), "\u2510"] }), _jsx(InkBox, { flexDirection: "column", paddingX: 1, children: children }), _jsxs(Text, { color: actualBorderColor, children: ["\u2514", horizontalBorder, "\u2518"] })] }));
9
+ }
10
+ export function Section({ title, children }) {
11
+ const { colors } = useTheme();
12
+ return (_jsxs(InkBox, { flexDirection: "column", marginY: 1, children: [_jsx(Text, { bold: true, color: colors.accent, children: title }), _jsx(InkBox, { flexDirection: "column", marginLeft: 2, children: children })] }));
13
+ }
14
+ export function ProgressBar({ current, total, width = 30 }) {
15
+ const { colors } = useTheme();
16
+ const percentage = Math.min(100, Math.round((current / total) * 100));
17
+ const filled = Math.round((percentage / 100) * width);
18
+ const empty = width - filled;
19
+ return (_jsxs(InkBox, { children: [_jsx(Text, { color: colors.accent, children: "[" }), _jsx(Text, { color: colors.success, children: "█".repeat(filled) }), _jsx(Text, { color: colors.muted, children: "░".repeat(empty) }), _jsx(Text, { color: colors.accent, children: "]" }), _jsxs(Text, { children: [" ", percentage, "%"] })] }));
20
+ }
@@ -0,0 +1,7 @@
1
+ export declare const LOGO_LINES: string[];
2
+ /**
3
+ * Logo component that renders the ASCII art logo once using Ink's Static.
4
+ * Static ensures the logo is rendered exactly once and stays at the top
5
+ * of the output, without re-rendering when other components update.
6
+ */
7
+ export declare function Logo(): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,22 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { Box, Text, Static } from "ink";
3
+ import { useTheme } from "../../lib/theme.js";
4
+ export const LOGO_LINES = [
5
+ " ⟋ ‾‾‾‾⟋|",
6
+ " ██████ |",
7
+ " ██████ |",
8
+ " ██████ ⟋ ‾‾‾‾⟋|",
9
+ " ⟋ ⟋ ██████ |",
10
+ " ██████ ██████ |",
11
+ " ██████ ██████⟋",
12
+ " ██████⟋",
13
+ ];
14
+ /**
15
+ * Logo component that renders the ASCII art logo once using Ink's Static.
16
+ * Static ensures the logo is rendered exactly once and stays at the top
17
+ * of the output, without re-rendering when other components update.
18
+ */
19
+ export function Logo() {
20
+ const { colors } = useTheme();
21
+ return (_jsx(Static, { items: ["logo"], children: (item) => (_jsx(Box, { flexDirection: "column", marginTop: 1, marginBottom: 2, children: LOGO_LINES.map((line, i) => (_jsx(Text, { color: colors.accent, children: line }, i))) }, item)) }));
22
+ }
@@ -0,0 +1,12 @@
1
+ interface SpinnerProps {
2
+ label: string;
3
+ color?: string;
4
+ }
5
+ export declare function Spinner({ label, color }: SpinnerProps): import("react/jsx-runtime").JSX.Element;
6
+ interface StatusLineProps {
7
+ status: 'pending' | 'running' | 'success' | 'error' | 'skipped';
8
+ label: string;
9
+ detail?: string;
10
+ }
11
+ export declare function StatusLine({ status, label, detail }: StatusLineProps): import("react/jsx-runtime").JSX.Element;
12
+ export {};
@@ -0,0 +1,28 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Box, Text } from 'ink';
3
+ import InkSpinner from 'ink-spinner';
4
+ import { useTheme } from '../../lib/theme.js';
5
+ export function Spinner({ label, color }) {
6
+ const { colors } = useTheme();
7
+ const spinnerColor = color || colors.accent;
8
+ return (_jsxs(Box, { children: [_jsx(Text, { color: spinnerColor, children: _jsx(InkSpinner, { type: "dots" }) }), _jsxs(Text, { children: [" ", label] })] }));
9
+ }
10
+ export function StatusLine({ status, label, detail }) {
11
+ const { colors } = useTheme();
12
+ const getStatusConfig = () => {
13
+ switch (status) {
14
+ case 'pending':
15
+ return { icon: '○', color: colors.muted };
16
+ case 'running':
17
+ return { icon: '◐', color: colors.accent };
18
+ case 'success':
19
+ return { icon: '✓', color: colors.success };
20
+ case 'error':
21
+ return { icon: '✗', color: colors.error };
22
+ case 'skipped':
23
+ return { icon: '⊘', color: colors.warning };
24
+ }
25
+ };
26
+ const { icon, color } = getStatusConfig();
27
+ return (_jsxs(Box, { children: [_jsx(Text, { color: color, children: icon }), _jsxs(Text, { children: [" ", label] }), detail && _jsxs(Text, { color: colors.muted, children: [" - ", detail] })] }));
28
+ }
@@ -0,0 +1,6 @@
1
+ export { BorderBox, Section, ProgressBar } from "./Box.js";
2
+ export { Spinner, StatusLine } from "./Spinner.js";
3
+ export { AppShell, ScreenContainer, ProgressHeader } from "./AppShell.js";
4
+ export { Logo, LOGO_LINES } from "./Logo.js";
5
+ export { ThemeProvider, useTheme, THEMES } from "../../lib/theme.js";
6
+ export type { CommandTheme, ThemeColors } from "../../lib/theme.js";
@@ -0,0 +1,5 @@
1
+ export { BorderBox, Section, ProgressBar } from "./Box.js";
2
+ export { Spinner, StatusLine } from "./Spinner.js";
3
+ export { AppShell, ScreenContainer, ProgressHeader } from "./AppShell.js";
4
+ export { Logo, LOGO_LINES } from "./Logo.js";
5
+ export { ThemeProvider, useTheme, THEMES } from "../../lib/theme.js";
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/index.js ADDED
@@ -0,0 +1,202 @@
1
+ #!/usr/bin/env node
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ import { Command } from "commander";
4
+ import { render } from "ink";
5
+ import chalk from "chalk";
6
+ import { InitWizard } from "./commands/init.js";
7
+ import { DeployCommand } from "./commands/deploy.js";
8
+ import { UpgradeCommand } from "./commands/upgrade.js";
9
+ import { DestroyCommand } from "./commands/destroy.js";
10
+ import { StatusCommand } from "./commands/status.js";
11
+ import { LogsCommand } from "./commands/logs.js";
12
+ import { CloneCommand } from "./commands/clone.js";
13
+ import { OpenCommand } from "./commands/open.js";
14
+ import { listDeployments, deploymentExists } from "./lib/config.js";
15
+ const VERSION = "2.0.0";
16
+ const program = new Command();
17
+ program
18
+ .name("rulebricks")
19
+ .description("CLI for deploying and managing private Rulebricks instances")
20
+ .version(VERSION)
21
+ .hook("preAction", () => {
22
+ // Clear terminal for a fresh start
23
+ // Logo is now rendered via Ink's Static component in each command
24
+ console.clear();
25
+ });
26
+ // Init command - interactive configuration wizard
27
+ program
28
+ .command("init")
29
+ .description("Initialize a new Rulebricks deployment configuration")
30
+ .argument("[name]", "Deployment name")
31
+ .option("-n, --name <name>", "Deployment name (alternative to positional argument)")
32
+ .action(async (name, options) => {
33
+ const deploymentName = name || options.name;
34
+ const { waitUntilExit } = render(_jsx(InitWizard, { initialName: deploymentName }));
35
+ await waitUntilExit();
36
+ });
37
+ // Deploy command
38
+ program
39
+ .command("deploy")
40
+ .description("Deploy Rulebricks to your cluster")
41
+ .argument("[name]", "Deployment name")
42
+ .option("--skip-infra", "Skip infrastructure provisioning")
43
+ .option("--version <version>", "Specific chart version to deploy")
44
+ .action(async (name, options) => {
45
+ const deploymentName = name || (await selectDeployment());
46
+ if (!deploymentName) {
47
+ console.error(chalk.red('No deployment specified. Run "rulebricks init" first.'));
48
+ process.exit(1);
49
+ }
50
+ const { waitUntilExit } = render(_jsx(DeployCommand, { name: deploymentName, skipInfra: options.skipInfra, version: options.version }));
51
+ await waitUntilExit();
52
+ });
53
+ // Upgrade command
54
+ program
55
+ .command("upgrade")
56
+ .description("Upgrade Rulebricks to a new version")
57
+ .argument("[name]", "Deployment name")
58
+ .option("--version <version>", "Target version (defaults to latest)")
59
+ .option("--dry-run", "Preview changes without applying")
60
+ .action(async (name, options) => {
61
+ const deploymentName = name || (await selectDeployment());
62
+ if (!deploymentName) {
63
+ console.error(chalk.red("No deployment specified."));
64
+ process.exit(1);
65
+ }
66
+ const { waitUntilExit } = render(_jsx(UpgradeCommand, { name: deploymentName, targetVersion: options.version, dryRun: options.dryRun }));
67
+ await waitUntilExit();
68
+ });
69
+ // Destroy command
70
+ program
71
+ .command("destroy")
72
+ .description("Destroy a Rulebricks deployment")
73
+ .argument("[name]", "Deployment name")
74
+ .option("--cluster", "Also destroy cloud infrastructure (EKS/GKE/AKS cluster)")
75
+ .option("--config", "Also delete local configuration files")
76
+ .option("-f, --force", "Skip confirmation")
77
+ .action(async (name, options) => {
78
+ // For destroy, require explicit deployment name
79
+ if (!name) {
80
+ const deployments = await listDeployments();
81
+ if (deployments.length === 0) {
82
+ console.error(chalk.red('No deployments found. Run "rulebricks init" first.'));
83
+ }
84
+ else {
85
+ console.error(chalk.red("Please specify a deployment to destroy.\n"));
86
+ console.log("Available deployments:");
87
+ for (const d of deployments) {
88
+ console.log(` ${chalk.yellow("•")} ${d}`);
89
+ }
90
+ console.log(`\nUsage: ${chalk.cyan("rulebricks destroy <name>")}`);
91
+ }
92
+ process.exit(1);
93
+ }
94
+ const { waitUntilExit } = render(_jsx(DestroyCommand, { name: name, cluster: options.cluster, config: options.config, force: options.force }));
95
+ await waitUntilExit();
96
+ });
97
+ // Status command
98
+ program
99
+ .command("status")
100
+ .description("Show deployment status")
101
+ .argument("[name]", "Deployment name")
102
+ .action(async (name) => {
103
+ const deploymentName = name || (await selectDeployment());
104
+ if (!deploymentName) {
105
+ console.error(chalk.red("No deployment specified."));
106
+ process.exit(1);
107
+ }
108
+ const { waitUntilExit } = render(_jsx(StatusCommand, { name: deploymentName }));
109
+ await waitUntilExit();
110
+ });
111
+ // Logs command
112
+ program
113
+ .command("logs")
114
+ .description("View component logs")
115
+ .argument("[name]", "Deployment name")
116
+ .argument("[component]", "Component: app, hps, workers, kafka, supabase, traefik")
117
+ .option("-f, --follow", "Follow log output (default: true)")
118
+ .option("--no-follow", "Show logs once without following")
119
+ .option("-t, --tail <lines>", "Number of lines to show", "100")
120
+ .option("-s, --split", "Show logs in split-pane view (side-by-side columns)")
121
+ .action(async (name, component, options) => {
122
+ const deploymentName = name || (await selectDeployment());
123
+ if (!deploymentName) {
124
+ console.error(chalk.red("No deployment specified."));
125
+ process.exit(1);
126
+ }
127
+ const { waitUntilExit } = render(_jsx(LogsCommand, { name: deploymentName, component: component, follow: options.follow, tail: parseInt(options.tail, 10), split: options.split }));
128
+ await waitUntilExit();
129
+ });
130
+ // List command
131
+ program
132
+ .command("list")
133
+ .description("List all deployments")
134
+ .action(async () => {
135
+ const deployments = await listDeployments();
136
+ const listColor = chalk.green; // Use status theme color (green)
137
+ if (deployments.length === 0) {
138
+ console.log(chalk.yellow('No deployments found. Run "rulebricks init" to create one.'));
139
+ return;
140
+ }
141
+ console.log(chalk.bold("\nDeployments:\n"));
142
+ for (const name of deployments) {
143
+ console.log(` ${listColor("•")} ${name}`);
144
+ }
145
+ console.log("");
146
+ });
147
+ // Clone command
148
+ program
149
+ .command("clone")
150
+ .description("Clone an existing deployment configuration")
151
+ .argument("<source>", "Source deployment name")
152
+ .argument("<target>", "New deployment name")
153
+ .action(async (source, target) => {
154
+ const { waitUntilExit } = render(_jsx(CloneCommand, { source: source, target: target }));
155
+ await waitUntilExit();
156
+ });
157
+ // Open command
158
+ program
159
+ .command("open")
160
+ .description("Open deployment files in your editor")
161
+ .argument("<name>", "Deployment name")
162
+ .option("--config", "Open config.yaml only")
163
+ .option("--values", "Open values.yaml only")
164
+ .option("--terraform", "Open terraform directory only")
165
+ .action(async (name, options) => {
166
+ // Validate deployment exists before rendering
167
+ const exists = await deploymentExists(name);
168
+ if (!exists) {
169
+ console.error(chalk.red(`Deployment "${name}" not found.`));
170
+ const deployments = await listDeployments();
171
+ if (deployments.length > 0) {
172
+ console.log("\nAvailable deployments:");
173
+ for (const d of deployments) {
174
+ console.log(` ${chalk.yellow("•")} ${d}`);
175
+ }
176
+ }
177
+ process.exit(1);
178
+ }
179
+ const target = options.config
180
+ ? "config"
181
+ : options.values
182
+ ? "values"
183
+ : options.terraform
184
+ ? "terraform"
185
+ : "all";
186
+ const { waitUntilExit } = render(_jsx(OpenCommand, { name: name, target: target }));
187
+ await waitUntilExit();
188
+ });
189
+ // Helper to select a deployment interactively
190
+ async function selectDeployment() {
191
+ const deployments = await listDeployments();
192
+ if (deployments.length === 0) {
193
+ return null;
194
+ }
195
+ if (deployments.length === 1) {
196
+ return deployments[0];
197
+ }
198
+ // For now, return the first one. In a full implementation,
199
+ // we'd render an interactive selector
200
+ return deployments[0];
201
+ }
202
+ program.parse();
@@ -0,0 +1,156 @@
1
+ /**
2
+ * Cloud CLI detection and dynamic resource listing
3
+ *
4
+ * Detects installed cloud CLIs (AWS, GCP, Azure), checks authentication status,
5
+ * and provides functions to list regions and buckets dynamically.
6
+ */
7
+ import { CloudProvider } from "../types/index.js";
8
+ /**
9
+ * Status of a cloud provider CLI
10
+ */
11
+ export interface CloudCliStatus {
12
+ provider: CloudProvider;
13
+ installed: boolean;
14
+ authenticated: boolean;
15
+ version?: string;
16
+ identity?: string;
17
+ error?: string;
18
+ }
19
+ /**
20
+ * All cloud CLI statuses
21
+ */
22
+ export interface AllCloudCliStatus {
23
+ aws: CloudCliStatus;
24
+ gcp: CloudCliStatus;
25
+ azure: CloudCliStatus;
26
+ anyAvailable: boolean;
27
+ anyInstalled: boolean;
28
+ }
29
+ /**
30
+ * Check if AWS CLI is installed and authenticated
31
+ */
32
+ export declare function checkAwsCli(): Promise<CloudCliStatus>;
33
+ /**
34
+ * List available AWS regions
35
+ */
36
+ export declare function listAwsRegions(): Promise<string[]>;
37
+ /**
38
+ * List S3 buckets
39
+ */
40
+ export declare function listS3Buckets(): Promise<string[]>;
41
+ /**
42
+ * List EKS clusters in a specific region
43
+ */
44
+ export declare function listEksClusters(region: string): Promise<string[]>;
45
+ /**
46
+ * Check if gcloud CLI is installed and authenticated
47
+ */
48
+ export declare function checkGcloudCli(): Promise<CloudCliStatus>;
49
+ /**
50
+ * Get the active GCP project ID
51
+ */
52
+ export declare function getGcpProjectId(): Promise<string | null>;
53
+ /**
54
+ * List available GCP regions
55
+ */
56
+ export declare function listGcpRegions(): Promise<string[]>;
57
+ /**
58
+ * List GCS buckets
59
+ */
60
+ export declare function listGcsBuckets(): Promise<string[]>;
61
+ /**
62
+ * List GKE clusters in a specific region
63
+ * Note: GKE supports both regional and zonal clusters. We search for regional clusters.
64
+ */
65
+ export declare function listGkeClusters(region: string): Promise<string[]>;
66
+ /**
67
+ * Check if Azure CLI is installed and authenticated
68
+ */
69
+ export declare function checkAzureCli(): Promise<CloudCliStatus>;
70
+ /**
71
+ * Get the active Azure subscription ID
72
+ */
73
+ export declare function getAzureSubscriptionId(): Promise<string | null>;
74
+ /**
75
+ * List available Azure regions (locations)
76
+ */
77
+ export declare function listAzureRegions(): Promise<string[]>;
78
+ /**
79
+ * List Azure storage accounts (containers require a storage account)
80
+ */
81
+ export declare function listAzureStorageAccounts(): Promise<string[]>;
82
+ /**
83
+ * List Azure blob containers in a storage account
84
+ */
85
+ export declare function listAzureBlobContainers(storageAccount: string): Promise<string[]>;
86
+ /**
87
+ * List AKS clusters, optionally filtered by resource group
88
+ */
89
+ export declare function listAksClusters(resourceGroup?: string): Promise<string[]>;
90
+ /**
91
+ * Check all cloud CLIs in parallel
92
+ */
93
+ export declare function checkAllCloudClis(): Promise<AllCloudCliStatus>;
94
+ /**
95
+ * List regions for a specific provider
96
+ */
97
+ export declare function listRegions(provider: CloudProvider): Promise<string[]>;
98
+ /**
99
+ * List buckets/storage for a specific provider
100
+ */
101
+ export declare function listBuckets(provider: CloudProvider): Promise<string[]>;
102
+ /**
103
+ * List Kubernetes clusters for a specific provider
104
+ */
105
+ export declare function listClusters(provider: CloudProvider, region: string, options?: {
106
+ azureResourceGroup?: string;
107
+ }): Promise<string[]>;
108
+ /**
109
+ * Get installation URLs for cloud CLIs
110
+ */
111
+ export declare const CLI_INSTALL_URLS: Record<CloudProvider, {
112
+ name: string;
113
+ url: string;
114
+ installCmd?: string;
115
+ }>;
116
+ /**
117
+ * Get login commands for cloud CLIs
118
+ */
119
+ export declare const CLI_LOGIN_COMMANDS: Record<CloudProvider, string>;
120
+ /**
121
+ * Terraform installation status
122
+ */
123
+ export interface TerraformStatus {
124
+ installed: boolean;
125
+ version?: string;
126
+ error?: string;
127
+ }
128
+ /**
129
+ * Check if Terraform is installed
130
+ */
131
+ export declare function checkTerraform(): Promise<TerraformStatus>;
132
+ /**
133
+ * Terraform installation info
134
+ */
135
+ export declare const TERRAFORM_INSTALL_INFO: {
136
+ name: string;
137
+ url: string;
138
+ installCmd: string;
139
+ };
140
+ /**
141
+ * List S3 buckets in a specific region
142
+ * Note: S3 buckets are global, but we filter by region
143
+ */
144
+ export declare function listS3BucketsInRegion(region: string): Promise<string[]>;
145
+ /**
146
+ * List GCS buckets in a specific region
147
+ */
148
+ export declare function listGcsBucketsInRegion(region: string): Promise<string[]>;
149
+ /**
150
+ * List Azure storage accounts in a specific region
151
+ */
152
+ export declare function listAzureStorageAccountsInRegion(region: string): Promise<string[]>;
153
+ /**
154
+ * List buckets/storage for a specific provider in a specific region
155
+ */
156
+ export declare function listBucketsInRegion(provider: CloudProvider, region: string): Promise<string[]>;