@hyperdrive.bot/cli 1.0.2

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 (127) hide show
  1. package/README.md +1598 -0
  2. package/bin/dev.cmd +3 -0
  3. package/bin/dev.js +3 -0
  4. package/bin/run.cmd +3 -0
  5. package/bin/run.js +5 -0
  6. package/dist/commands/account/add.d.ts +16 -0
  7. package/dist/commands/account/add.js +185 -0
  8. package/dist/commands/account/list.d.ts +6 -0
  9. package/dist/commands/account/list.js +37 -0
  10. package/dist/commands/account/remove.d.ts +11 -0
  11. package/dist/commands/account/remove.js +57 -0
  12. package/dist/commands/auth/login.d.ts +16 -0
  13. package/dist/commands/auth/login.js +178 -0
  14. package/dist/commands/auth/logout.d.ts +6 -0
  15. package/dist/commands/auth/logout.js +39 -0
  16. package/dist/commands/auth/refresh.d.ts +6 -0
  17. package/dist/commands/auth/refresh.js +66 -0
  18. package/dist/commands/auth/status.d.ts +6 -0
  19. package/dist/commands/auth/status.js +63 -0
  20. package/dist/commands/ci/account/create.d.ts +16 -0
  21. package/dist/commands/ci/account/create.js +158 -0
  22. package/dist/commands/ci/account/delete.d.ts +14 -0
  23. package/dist/commands/ci/account/delete.js +88 -0
  24. package/dist/commands/ci/account/list.d.ts +10 -0
  25. package/dist/commands/ci/account/list.js +65 -0
  26. package/dist/commands/config/get.d.ts +9 -0
  27. package/dist/commands/config/get.js +37 -0
  28. package/dist/commands/config/set.d.ts +10 -0
  29. package/dist/commands/config/set.js +48 -0
  30. package/dist/commands/config/show.d.ts +6 -0
  31. package/dist/commands/config/show.js +10 -0
  32. package/dist/commands/deployment/create.d.ts +30 -0
  33. package/dist/commands/deployment/create.js +188 -0
  34. package/dist/commands/deployment/get.d.ts +13 -0
  35. package/dist/commands/deployment/get.js +101 -0
  36. package/dist/commands/deployment/launch.d.ts +15 -0
  37. package/dist/commands/deployment/launch.js +105 -0
  38. package/dist/commands/deployment/list.d.ts +11 -0
  39. package/dist/commands/deployment/list.js +91 -0
  40. package/dist/commands/domain/current.d.ts +6 -0
  41. package/dist/commands/domain/current.js +18 -0
  42. package/dist/commands/domain/list.d.ts +6 -0
  43. package/dist/commands/domain/list.js +42 -0
  44. package/dist/commands/domain/switch.d.ts +9 -0
  45. package/dist/commands/domain/switch.js +40 -0
  46. package/dist/commands/example.d.ts +13 -0
  47. package/dist/commands/example.js +24 -0
  48. package/dist/commands/git/connect.d.ts +10 -0
  49. package/dist/commands/git/connect.js +56 -0
  50. package/dist/commands/git/disconnect.d.ts +11 -0
  51. package/dist/commands/git/disconnect.js +93 -0
  52. package/dist/commands/git/list.d.ts +10 -0
  53. package/dist/commands/git/list.js +53 -0
  54. package/dist/commands/git/sync.d.ts +18 -0
  55. package/dist/commands/git/sync.js +235 -0
  56. package/dist/commands/init.d.ts +188 -0
  57. package/dist/commands/init.js +817 -0
  58. package/dist/commands/jira/connect.d.ts +9 -0
  59. package/dist/commands/jira/connect.js +141 -0
  60. package/dist/commands/jira/status.d.ts +9 -0
  61. package/dist/commands/jira/status.js +118 -0
  62. package/dist/commands/module/analyze.d.ts +29 -0
  63. package/dist/commands/module/analyze.js +201 -0
  64. package/dist/commands/module/create.d.ts +42 -0
  65. package/dist/commands/module/create.js +498 -0
  66. package/dist/commands/module/destroy.d.ts +11 -0
  67. package/dist/commands/module/destroy.js +77 -0
  68. package/dist/commands/module/get.d.ts +10 -0
  69. package/dist/commands/module/get.js +43 -0
  70. package/dist/commands/module/link.d.ts +15 -0
  71. package/dist/commands/module/link.js +175 -0
  72. package/dist/commands/module/list.d.ts +9 -0
  73. package/dist/commands/module/list.js +51 -0
  74. package/dist/commands/module/reanalyze.d.ts +30 -0
  75. package/dist/commands/module/reanalyze.js +206 -0
  76. package/dist/commands/module/update.d.ts +27 -0
  77. package/dist/commands/module/update.js +102 -0
  78. package/dist/commands/parameter/add.d.ts +15 -0
  79. package/dist/commands/parameter/add.js +99 -0
  80. package/dist/commands/parameter/backfill.d.ts +12 -0
  81. package/dist/commands/parameter/backfill.js +113 -0
  82. package/dist/commands/parameter/clear.d.ts +14 -0
  83. package/dist/commands/parameter/clear.js +95 -0
  84. package/dist/commands/parameter/list.d.ts +14 -0
  85. package/dist/commands/parameter/list.js +92 -0
  86. package/dist/commands/parameter/pull.d.ts +14 -0
  87. package/dist/commands/parameter/pull.js +124 -0
  88. package/dist/commands/parameter/remove.d.ts +15 -0
  89. package/dist/commands/parameter/remove.js +90 -0
  90. package/dist/commands/parameter/sync.d.ts +14 -0
  91. package/dist/commands/parameter/sync.js +153 -0
  92. package/dist/commands/parameter/update.d.ts +15 -0
  93. package/dist/commands/parameter/update.js +100 -0
  94. package/dist/commands/stage/create.d.ts +28 -0
  95. package/dist/commands/stage/create.js +312 -0
  96. package/dist/commands/stage/list.d.ts +9 -0
  97. package/dist/commands/stage/list.js +63 -0
  98. package/dist/commands/test-api.d.ts +9 -0
  99. package/dist/commands/test-api.js +40 -0
  100. package/dist/index.d.ts +1 -0
  101. package/dist/index.js +1 -0
  102. package/dist/services/auth-service.d.ts +84 -0
  103. package/dist/services/auth-service.js +240 -0
  104. package/dist/services/git.d.ts +46 -0
  105. package/dist/services/git.js +409 -0
  106. package/dist/services/hyperdrive-sigv4.d.ts +449 -0
  107. package/dist/services/hyperdrive-sigv4.js +375 -0
  108. package/dist/services/hyperdrive.d.ts +87 -0
  109. package/dist/services/hyperdrive.js +108 -0
  110. package/dist/services/log-tailer.d.ts +95 -0
  111. package/dist/services/log-tailer.js +242 -0
  112. package/dist/services/tenant-service.d.ts +106 -0
  113. package/dist/services/tenant-service.js +332 -0
  114. package/dist/utils/account-flow.d.ts +74 -0
  115. package/dist/utils/account-flow.js +228 -0
  116. package/dist/utils/auth-flow.d.ts +146 -0
  117. package/dist/utils/auth-flow.js +477 -0
  118. package/dist/utils/git-flow.d.ts +72 -0
  119. package/dist/utils/git-flow.js +232 -0
  120. package/dist/utils/jira-flow.d.ts +71 -0
  121. package/dist/utils/jira-flow.js +120 -0
  122. package/dist/utils/summary-display.d.ts +59 -0
  123. package/dist/utils/summary-display.js +140 -0
  124. package/dist/utils/validation.d.ts +15 -0
  125. package/dist/utils/validation.js +32 -0
  126. package/oclif.manifest.json +2819 -0
  127. package/package.json +112 -0
@@ -0,0 +1,175 @@
1
+ import { Command, Flags } from '@oclif/core';
2
+ import chalk from 'chalk';
3
+ import inquirer from 'inquirer';
4
+ import { HyperdriveSigV4Service } from '../../services/hyperdrive-sigv4.js';
5
+ export default class ModuleLink extends Command {
6
+ static description = 'Links two modules using specified environment variables as connectors';
7
+ static examples = [
8
+ '<%= config.bin %> <%= command.id %> --originSlug="vixting-integration" --targetSlug="vixting-api" --parameter="VLOW_API_ENDPOINT_PREFIX"',
9
+ ];
10
+ static flags = {
11
+ domain: Flags.string({
12
+ char: 'd',
13
+ description: 'Tenant domain (for multi-domain setups)',
14
+ }),
15
+ originSlug: Flags.string({
16
+ description: 'Slug of the origin module (the one that depends on another)',
17
+ required: true,
18
+ }),
19
+ parameter: Flags.string({
20
+ description: 'Environment variable to link (e.g., API_URL)',
21
+ multiple: true,
22
+ required: true,
23
+ }),
24
+ targetSlug: Flags.string({
25
+ description: 'Slug of the target module (the dependency)',
26
+ required: true,
27
+ }),
28
+ };
29
+ async confirmDeploymentBehavior() {
30
+ const confirmation = await inquirer.prompt([
31
+ {
32
+ default: true,
33
+ message: chalk.blue('In every deployment, we will attempt to find a backend instance in the same stage as your application is being deployed to. If no such backend instance is found, we will connect your frontend to the backend instance deployed in the default stage. Do you want to proceed?'),
34
+ name: 'confirmDeployment',
35
+ type: 'confirm',
36
+ },
37
+ ]);
38
+ return confirmation.confirmDeployment;
39
+ }
40
+ async prompt() {
41
+ await inquirer.prompt([
42
+ {
43
+ message: chalk.green('Enter the origin module slug:'),
44
+ name: 'originSlug',
45
+ validate: input => Boolean(input) || 'Origin module slug is required.',
46
+ },
47
+ ]);
48
+ await inquirer.prompt([
49
+ {
50
+ choices: ['front-end', 'back-end'],
51
+ message: chalk.green('What is the type of the module?'),
52
+ name: 'projectType',
53
+ type: 'list',
54
+ },
55
+ ]);
56
+ await this.promptForEnvironmentVariables();
57
+ await inquirer.prompt([
58
+ {
59
+ message: chalk.green('Enter the destination module slug:'),
60
+ name: 'targetSlug',
61
+ validate: input => Boolean(input) || 'Destination module slug is required.',
62
+ },
63
+ ]);
64
+ const proceed = await this.confirmDeploymentBehavior();
65
+ if (!proceed) {
66
+ this.log(chalk.red('Deployment cancelled by user.'));
67
+ }
68
+ }
69
+ async promptForEnvironmentVariables() {
70
+ const envVariables = [];
71
+ const collectVariable = async () => {
72
+ const response = await inquirer.prompt([
73
+ {
74
+ message: chalk.yellow('Enter the environment variable used to connect your front-end to your back-end:'),
75
+ name: 'envVariable',
76
+ validate: input => Boolean(input) || 'Environment variable is required.',
77
+ },
78
+ {
79
+ default: false,
80
+ message: chalk.yellow('Do you want to add another environment variable?'),
81
+ name: 'addAnother',
82
+ type: 'confirm',
83
+ },
84
+ ]);
85
+ envVariables.push(response.envVariable);
86
+ if (response.addAnother) {
87
+ return collectVariable();
88
+ }
89
+ };
90
+ await collectVariable();
91
+ return envVariables;
92
+ }
93
+ async run() {
94
+ const { flags } = await this.parse(ModuleLink);
95
+ // Validation: Ensure origin and target are different
96
+ if (flags.originSlug === flags.targetSlug) {
97
+ this.log(chalk.red('❌ Error: Cannot link a module to itself'));
98
+ this.log(chalk.gray(' originSlug and targetSlug must be different'));
99
+ this.exit(1);
100
+ }
101
+ // Validation: Ensure at least one parameter
102
+ if (!flags.parameter || flags.parameter.length === 0) {
103
+ this.log(chalk.red('❌ Error: At least one parameter is required'));
104
+ this.log(chalk.gray(' Use --parameter=PARAM_NAME to specify parameters'));
105
+ this.exit(1);
106
+ }
107
+ // Validation: Check for duplicate parameters
108
+ const uniqueParams = new Set(flags.parameter);
109
+ if (uniqueParams.size !== flags.parameter.length) {
110
+ this.log(chalk.yellow('⚠️ Warning: Duplicate parameters detected, removing duplicates'));
111
+ }
112
+ this.log(chalk.green(`🔗 Linking ${flags.originSlug} → ${flags.targetSlug}...`));
113
+ this.log(chalk.gray(` Parameters: ${[...uniqueParams].join(', ')}`));
114
+ try {
115
+ const service = new HyperdriveSigV4Service(flags.domain);
116
+ // Convert array of parameter names to object format
117
+ // Each parameter will receive the target module's deployment URL at deploy time
118
+ const parametersObject = {};
119
+ for (const param of uniqueParams) {
120
+ parametersObject[param] = true;
121
+ }
122
+ const result = await service.moduleLink({
123
+ originSlug: flags.originSlug,
124
+ parameters: parametersObject,
125
+ targetSlug: flags.targetSlug,
126
+ });
127
+ this.log('');
128
+ this.log(chalk.blue('✅ Module link successful!'));
129
+ this.log('');
130
+ this.log(chalk.bold('What happens during deployment:'));
131
+ this.log(chalk.gray(` 1. When you deploy ${chalk.cyan(flags.originSlug)}, Hyperdrive will:`));
132
+ this.log(chalk.gray(` - Look for ${chalk.cyan(flags.targetSlug)} in the same stage`));
133
+ this.log(chalk.gray(` - If not found, fall back to default stage`));
134
+ this.log(chalk.gray(` - Inject the deployment URL into these parameters:`));
135
+ for (const param of uniqueParams) {
136
+ this.log(chalk.gray(` • ${chalk.yellow(param)}`));
137
+ }
138
+ this.log('');
139
+ this.log(chalk.gray('Example: If you deploy to "prod" stage:'));
140
+ this.log(chalk.gray(` - First deploy ${chalk.cyan(flags.targetSlug)} to prod → gets URL https://api.example.com`));
141
+ this.log(chalk.gray(` - Then deploy ${chalk.cyan(flags.originSlug)} to prod → receives ${chalk.yellow(flags.parameter[0])}=https://api.example.com`));
142
+ this.log('');
143
+ this.log(chalk.gray('Link details:'));
144
+ this.log(JSON.stringify(result, null, 2));
145
+ }
146
+ catch (error) {
147
+ this.log('');
148
+ this.log(chalk.red('❌ Error linking modules'));
149
+ this.log('');
150
+ // Parse error response for better messages
151
+ if (error.response?.data?.message) {
152
+ this.log(chalk.red('Error: ' + error.response.data.message));
153
+ }
154
+ else if (error.message) {
155
+ this.log(chalk.red('Error: ' + error.message));
156
+ }
157
+ else {
158
+ this.log(chalk.red('Error: ' + error.toString()));
159
+ }
160
+ // Provide helpful hints based on common errors
161
+ if (error.response?.status === 404) {
162
+ this.log('');
163
+ this.log(chalk.yellow('💡 Tip: Make sure both modules exist'));
164
+ this.log(chalk.gray(` Run: hd module list`));
165
+ }
166
+ else if (error.response?.status === 400) {
167
+ this.log('');
168
+ this.log(chalk.yellow('💡 Tip: Check your command syntax'));
169
+ this.log(chalk.gray(` Example: hd module link --originSlug=my-app --targetSlug=my-api --parameter=API_URL`));
170
+ }
171
+ this.log('');
172
+ this.exit(1);
173
+ }
174
+ }
175
+ }
@@ -0,0 +1,9 @@
1
+ import { Command } from '@oclif/core';
2
+ export default class ModuleList extends Command {
3
+ static description: string;
4
+ static examples: string[];
5
+ static flags: {
6
+ domain: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
7
+ };
8
+ run(): Promise<void>;
9
+ }
@@ -0,0 +1,51 @@
1
+ import { Command, Flags, ux } from '@oclif/core';
2
+ import chalk from 'chalk';
3
+ import { HyperdriveSigV4Service } from '../../services/hyperdrive-sigv4.js';
4
+ export default class ModuleList extends Command {
5
+ static description = 'List all modules/projects';
6
+ static examples = [
7
+ '<%= config.bin %> <%= command.id %>',
8
+ ];
9
+ static flags = {
10
+ domain: Flags.string({
11
+ char: 'd',
12
+ description: 'Tenant domain (for multi-domain setups)',
13
+ }),
14
+ };
15
+ async run() {
16
+ const { flags } = await this.parse(ModuleList);
17
+ try {
18
+ const service = new HyperdriveSigV4Service(flags.domain);
19
+ this.log(chalk.blue('🔍 Fetching modules...'));
20
+ const modules = await service.moduleList();
21
+ if (!modules || modules.length === 0) {
22
+ this.log(chalk.yellow('⚠️ No modules found'));
23
+ return;
24
+ }
25
+ this.log(chalk.green(`✅ Found ${modules.length} module(s)\n`));
26
+ // Display modules in a table
27
+ ux.table(modules, {
28
+ createdAt: {
29
+ get: (row) => new Date(row.createdAt).toLocaleDateString(),
30
+ header: 'Created',
31
+ },
32
+ framework: {
33
+ header: 'Framework',
34
+ },
35
+ name: {
36
+ header: 'Name',
37
+ minWidth: 20,
38
+ },
39
+ slug: {
40
+ get: (row) => chalk.cyan(row.slug),
41
+ header: 'Slug',
42
+ minWidth: 15,
43
+ },
44
+ });
45
+ }
46
+ catch (error) {
47
+ console.error('Error:', error);
48
+ this.error('An error occurred while listing modules');
49
+ }
50
+ }
51
+ }
@@ -0,0 +1,30 @@
1
+ import { Command } from '@oclif/core';
2
+ export default class DockerfileReanalyze extends Command {
3
+ static description: string;
4
+ static examples: string[];
5
+ static flags: {
6
+ domain: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
7
+ prompt: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
8
+ save: import("@oclif/core/lib/interfaces/parser.js").BooleanFlag<boolean>;
9
+ slug: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
10
+ verbose: import("@oclif/core/lib/interfaces/parser.js").BooleanFlag<boolean>;
11
+ wait: import("@oclif/core/lib/interfaces/parser.js").BooleanFlag<boolean>;
12
+ };
13
+ run(): Promise<void>;
14
+ /**
15
+ * Build CloudWatch console URL for log viewing
16
+ */
17
+ private buildConsoleUrl;
18
+ /**
19
+ * Display analysis results
20
+ */
21
+ private displayResults;
22
+ /**
23
+ * Poll for Dockerfile (fallback when streaming not available)
24
+ */
25
+ private pollForDockerfile;
26
+ /**
27
+ * Stream analysis logs using CloudWatch
28
+ */
29
+ private streamAnalysisLogs;
30
+ }
@@ -0,0 +1,206 @@
1
+ import { Command, Flags } from '@oclif/core';
2
+ import chalk from 'chalk';
3
+ import { existsSync, readFileSync, writeFileSync } from 'node:fs';
4
+ import { HyperdriveSigV4Service } from '../../services/hyperdrive-sigv4.js';
5
+ import { CloudWatchLogTailer } from '../../services/log-tailer.js';
6
+ export default class DockerfileReanalyze extends Command {
7
+ static description = 'Reanalyze a module\'s Dockerfile with custom instructions';
8
+ static examples = [
9
+ '<%= config.bin %> <%= command.id %> --slug="my-module" --prompt="Use Node.js 18 instead of 20"',
10
+ '<%= config.bin %> <%= command.id %> --prompt="Fix the build - missing Python dependency"',
11
+ '<%= config.bin %> <%= command.id %> -s my-module -p "Exclude source maps from S3 upload" --verbose',
12
+ ];
13
+ static flags = {
14
+ domain: Flags.string({
15
+ char: 'd',
16
+ description: 'Tenant domain (for multi-domain setups)',
17
+ }),
18
+ prompt: Flags.string({
19
+ char: 'p',
20
+ description: 'Custom instructions for Dockerfile regeneration',
21
+ required: true,
22
+ }),
23
+ save: Flags.boolean({
24
+ default: false,
25
+ description: 'Save generated Dockerfile to current directory',
26
+ }),
27
+ slug: Flags.string({
28
+ char: 's',
29
+ default() {
30
+ if (existsSync('.hyperdrive.json')) {
31
+ const hyperdriveConfig = JSON.parse(readFileSync('.hyperdrive.json', 'utf8'));
32
+ return hyperdriveConfig.slug;
33
+ }
34
+ return '';
35
+ },
36
+ description: 'Module slug to reanalyze',
37
+ required: true,
38
+ }),
39
+ verbose: Flags.boolean({
40
+ char: 'v',
41
+ default: false,
42
+ description: 'Show detailed analysis progress and real-time logs',
43
+ }),
44
+ wait: Flags.boolean({
45
+ char: 'w',
46
+ default: true,
47
+ description: 'Wait for reanalysis to complete and show results',
48
+ }),
49
+ };
50
+ async run() {
51
+ const { flags } = await this.parse(DockerfileReanalyze);
52
+ try {
53
+ const service = new HyperdriveSigV4Service(flags.domain);
54
+ this.log(chalk.blue(`🔄 Reanalyzing module ${chalk.cyan(flags.slug)}...`));
55
+ this.log(chalk.gray(`Custom instructions: ${chalk.italic(flags.prompt)}\n`));
56
+ if (flags.verbose) {
57
+ this.log(chalk.gray('📝 Verbose mode enabled - streaming real-time logs\n'));
58
+ }
59
+ const result = await service.moduleReanalyze(flags.slug, flags.prompt);
60
+ this.log(chalk.green(`✅ Reanalysis queued successfully!`));
61
+ this.log(chalk.gray(` Job ID: ${result.jobId}`));
62
+ this.log(chalk.gray(` Status: ${result.status}`));
63
+ if (flags.verbose && result.logging) {
64
+ this.log(chalk.gray(` Project ID: ${result.projectId}`));
65
+ }
66
+ if (!flags.wait) {
67
+ this.log(chalk.yellow('\nUse --wait to poll for results, or check later with:'));
68
+ this.log(chalk.cyan(` hyperdrive module dockerfile --slug ${flags.slug}`));
69
+ return;
70
+ }
71
+ // If verbose and API returned logging config, stream logs
72
+ if (flags.verbose && result.logging) {
73
+ await this.streamAnalysisLogs(result.logging, flags, service);
74
+ }
75
+ else {
76
+ // Fallback to polling
77
+ if (flags.verbose && !result.logging) {
78
+ this.log(chalk.yellow('⚠️ Log streaming not available (API limitation), falling back to polling'));
79
+ }
80
+ this.log(chalk.blue('\n⏳ Waiting for reanalysis to complete...'));
81
+ this.log(chalk.gray(' This typically takes 2-5 minutes.\n'));
82
+ const dockerfile = await this.pollForDockerfile(service, flags.slug);
83
+ if (!dockerfile) {
84
+ this.log(chalk.yellow('\n⚠️ Reanalysis is still in progress.'));
85
+ this.log(chalk.gray(' Check back later with:'));
86
+ this.log(chalk.cyan(` hyperdrive module dockerfile --slug ${flags.slug}`));
87
+ return;
88
+ }
89
+ await this.displayResults(dockerfile, flags);
90
+ }
91
+ }
92
+ catch (error) {
93
+ console.error('Error:', error);
94
+ this.error(`Failed to reanalyze module ${flags.slug}`);
95
+ }
96
+ }
97
+ /**
98
+ * Build CloudWatch console URL for log viewing
99
+ */
100
+ buildConsoleUrl(config) {
101
+ const encodedGroup = encodeURIComponent(encodeURIComponent(config.logGroup));
102
+ const encodedStream = encodeURIComponent(encodeURIComponent(config.logStream));
103
+ return `https://${config.region}.console.aws.amazon.com/cloudwatch/home?region=${config.region}#logsV2:log-groups/log-group/${encodedGroup}/log-events/${encodedStream}`;
104
+ }
105
+ /**
106
+ * Display analysis results
107
+ */
108
+ async displayResults(dockerfile, flags) {
109
+ if (!dockerfile) {
110
+ this.log(chalk.yellow('\n⚠️ No Dockerfile found'));
111
+ return;
112
+ }
113
+ // Show results
114
+ this.log(chalk.green('\n✅ Dockerfile regenerated successfully!\n'));
115
+ if (dockerfile.analysis) {
116
+ this.log(chalk.blue('📊 Analysis Results:'));
117
+ this.log(chalk.gray(` Runtime: ${dockerfile.analysis.detectedRuntime || 'unknown'}`));
118
+ this.log(chalk.gray(` Framework: ${dockerfile.analysis.detectedFramework || 'unknown'}`));
119
+ if (dockerfile.analysis.port) {
120
+ this.log(chalk.gray(` Port: ${dockerfile.analysis.port}`));
121
+ }
122
+ if (dockerfile.analysis.healthEndpoint) {
123
+ this.log(chalk.gray(` Health: ${dockerfile.analysis.healthEndpoint}`));
124
+ }
125
+ }
126
+ this.log(chalk.blue('\n🐳 Validation Results:'));
127
+ this.log(chalk.gray(` Build: ${dockerfile.buildSuccess ? chalk.green('✓ Success') : chalk.red('✗ Failed')}`));
128
+ this.log(chalk.gray(` Run: ${dockerfile.runSuccess ? chalk.green('✓ Success') : chalk.red('✗ Failed')}`));
129
+ this.log(chalk.gray(` Port: ${dockerfile.portOpen ? chalk.green('✓ Open') : chalk.red('✗ Closed')}`));
130
+ if (dockerfile.analysis?.recommendations?.length) {
131
+ this.log(chalk.blue('\n💡 Recommendations:'));
132
+ for (const rec of dockerfile.analysis.recommendations) {
133
+ this.log(chalk.gray(` • ${rec}`));
134
+ }
135
+ }
136
+ this.log(chalk.blue('\n📄 Regenerated Dockerfile:'));
137
+ this.log(chalk.gray('─'.repeat(60)));
138
+ this.log(dockerfile.dockerfile);
139
+ this.log(chalk.gray('─'.repeat(60)));
140
+ if (flags.save) {
141
+ writeFileSync('Dockerfile', dockerfile.dockerfile);
142
+ this.log(chalk.green('\n✅ Dockerfile saved to ./Dockerfile'));
143
+ if (dockerfile.dockerignore) {
144
+ writeFileSync('.dockerignore', dockerfile.dockerignore);
145
+ this.log(chalk.green('✅ .dockerignore saved to ./.dockerignore'));
146
+ }
147
+ }
148
+ else {
149
+ this.log(chalk.gray('\nTip: Use --save to write files to current directory'));
150
+ }
151
+ }
152
+ /**
153
+ * Poll for Dockerfile (fallback when streaming not available)
154
+ */
155
+ async pollForDockerfile(service, slug, maxAttempts = 60, intervalMs = 5000) {
156
+ for (let i = 0; i < maxAttempts; i++) {
157
+ try {
158
+ const result = await service.moduleGetDockerfile(slug);
159
+ if (result) {
160
+ return result;
161
+ }
162
+ }
163
+ catch {
164
+ // Not ready yet
165
+ }
166
+ // Show progress
167
+ process.stdout.write(chalk.gray('.'));
168
+ await new Promise(resolve => setTimeout(resolve, intervalMs));
169
+ }
170
+ return null;
171
+ }
172
+ /**
173
+ * Stream analysis logs using CloudWatch
174
+ */
175
+ async streamAnalysisLogs(loggingConfig, flags, service) {
176
+ const tailer = new CloudWatchLogTailer(loggingConfig, {
177
+ pollInterval: 1000,
178
+ showDebug: false,
179
+ verbose: true,
180
+ });
181
+ this.log('');
182
+ const outcome = await tailer.tail();
183
+ this.log('');
184
+ if (outcome.success) {
185
+ this.log(chalk.green(`✅ ${outcome.message}`));
186
+ // Show CloudWatch console link
187
+ const consoleUrl = this.buildConsoleUrl(loggingConfig);
188
+ this.log(chalk.gray(` Logs: ${consoleUrl}`));
189
+ // Fetch generated Dockerfile
190
+ const dockerfile = await service.moduleGetDockerfile(flags.slug);
191
+ if (dockerfile) {
192
+ await this.displayResults(dockerfile, flags);
193
+ }
194
+ else {
195
+ this.log(chalk.yellow('\n⚠️ Dockerfile not found yet, may still be processing'));
196
+ }
197
+ }
198
+ else {
199
+ this.log(chalk.red(`❌ ${outcome.message}`));
200
+ // Show CloudWatch console link for debugging
201
+ const consoleUrl = this.buildConsoleUrl(loggingConfig);
202
+ this.log(chalk.gray(` Full logs: ${consoleUrl}`));
203
+ this.exit(1);
204
+ }
205
+ }
206
+ }
@@ -0,0 +1,27 @@
1
+ import { Command } from '@oclif/core';
2
+ export default class ModuleUpdate extends Command {
3
+ static description: string;
4
+ static examples: string[];
5
+ static flags: {
6
+ buildCommand: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
7
+ buildDirectory: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
8
+ buildFolder: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
9
+ buildRuntime: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
10
+ buildRuntimeVersion: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
11
+ ciService: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
12
+ defaultBranch: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
13
+ deploymentStrategy: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
14
+ domain: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
15
+ framework: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
16
+ installCommand: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
17
+ name: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
18
+ routeDiscovery: import("@oclif/core/lib/interfaces/parser.js").BooleanFlag<boolean>;
19
+ runCommand: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
20
+ runtime: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
21
+ runtimeVersion: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
22
+ slug: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
23
+ sourceDirectory: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
24
+ sourceLocation: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
25
+ };
26
+ run(): Promise<void>;
27
+ }
@@ -0,0 +1,102 @@
1
+ import { Command, Flags } from '@oclif/core';
2
+ import chalk from 'chalk';
3
+ import { HyperdriveSigV4Service } from '../../services/hyperdrive-sigv4.js';
4
+ export default class ModuleUpdate extends Command {
5
+ static description = 'Update a module/project configuration';
6
+ static examples = [
7
+ '<%= config.bin %> <%= command.id %> --slug="my-module" --runtimeVersion="12"',
8
+ '<%= config.bin %> <%= command.id %> --slug="my-module" --buildCommand="npm run build:prod"',
9
+ '<%= config.bin %> <%= command.id %> --slug="my-module" --name="New Name" --framework="React.js"',
10
+ ];
11
+ static flags = {
12
+ buildCommand: Flags.string({
13
+ description: 'Build command',
14
+ }),
15
+ buildDirectory: Flags.string({
16
+ description: 'Build output directory (e.g., dist, build, .next) - where compiled artifacts are output',
17
+ }),
18
+ buildFolder: Flags.string({
19
+ description: 'Build folder',
20
+ }),
21
+ buildRuntime: Flags.string({
22
+ description: 'Build runtime for Dockerfile template selection',
23
+ options: ['nodejs', 'python', 'go', 'rust', 'java', 'dotnet', 'ruby'],
24
+ }),
25
+ buildRuntimeVersion: Flags.string({
26
+ description: 'Build runtime version (e.g., 20 for Node, 3.12 for Python)',
27
+ }),
28
+ ciService: Flags.string({
29
+ description: 'CI service used',
30
+ options: ['github-actions', 'gitlab-ci', 'circle-ci', 'jenkins'],
31
+ }),
32
+ defaultBranch: Flags.string({
33
+ description: 'Default git branch to branch from (e.g., main, master)',
34
+ }),
35
+ deploymentStrategy: Flags.string({
36
+ description: 'Deployment strategy: "serverless" for Serverless Framework deployment to Lambda',
37
+ options: ['serverless'],
38
+ }),
39
+ domain: Flags.string({
40
+ char: 'd',
41
+ description: 'Tenant domain (for multi-domain setups)',
42
+ }),
43
+ framework: Flags.string({
44
+ description: 'Framework used',
45
+ options: ['express', 'fastify', 'koa', 'nextjs', 'react', 'vue', 'angular', 'svelte', 'flask', 'django', 'fastapi', 'gin', 'actix', 'spring', 'aspnet', 'rails', 'other'],
46
+ }),
47
+ installCommand: Flags.string({
48
+ description: 'Install command',
49
+ }),
50
+ name: Flags.string({
51
+ description: 'Name of the project',
52
+ }),
53
+ routeDiscovery: Flags.boolean({
54
+ allowNo: true,
55
+ description: 'Enable AI-powered route discovery for per-route Lambda functions',
56
+ }),
57
+ runCommand: Flags.string({
58
+ description: 'Run command',
59
+ }),
60
+ runtime: Flags.string({
61
+ char: 'r',
62
+ description: 'Runtime environment',
63
+ options: ['nodejs', 'python', 'go', 'rust', 'java', 'dotnet', 'ruby'],
64
+ }),
65
+ runtimeVersion: Flags.string({
66
+ char: 'v',
67
+ description: 'Runtime version (e.g., 20, 3.12, 1.21)',
68
+ }),
69
+ slug: Flags.string({
70
+ char: 's',
71
+ description: 'Module slug to update (required)',
72
+ required: true,
73
+ }),
74
+ sourceDirectory: Flags.string({
75
+ description: 'Source code directory (e.g., src, lib, . for root) - default: src for Lambda, . for static',
76
+ }),
77
+ sourceLocation: Flags.string({
78
+ description: 'Source location of the project',
79
+ }),
80
+ };
81
+ async run() {
82
+ const { flags } = await this.parse(ModuleUpdate);
83
+ const { slug, ...updateFields } = flags;
84
+ // Filter out undefined values
85
+ const updateData = Object.fromEntries(Object.entries(updateFields).filter(([_, value]) => value !== undefined));
86
+ if (Object.keys(updateData).length === 0) {
87
+ this.log(chalk.yellow('No fields to update. Please provide at least one field to update.'));
88
+ return;
89
+ }
90
+ this.log(chalk.blue(`🔄 Updating module "${slug}"...`));
91
+ const service = new HyperdriveSigV4Service(flags.domain);
92
+ try {
93
+ const result = await service.moduleUpdate({ slug, ...updateData });
94
+ this.log(chalk.green('✅ Module updated successfully!'));
95
+ this.log(JSON.stringify(result, null, 2));
96
+ }
97
+ catch (error) {
98
+ console.error('Error:', error);
99
+ this.error('An error occurred while updating the module');
100
+ }
101
+ }
102
+ }
@@ -0,0 +1,15 @@
1
+ import { Command } from '@oclif/core';
2
+ export default class ParameterAdd extends Command {
3
+ static description: string;
4
+ static examples: string[];
5
+ static flags: {
6
+ accountId: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
7
+ domain: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
8
+ key: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
9
+ moduleSlug: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
10
+ specific: import("@oclif/core/lib/interfaces/parser.js").BooleanFlag<boolean>;
11
+ stage: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
12
+ value: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
13
+ };
14
+ run(): Promise<void>;
15
+ }