@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,6 @@
1
+ import { Command } from '@oclif/core';
2
+ export default class Status extends Command {
3
+ static description: string;
4
+ static examples: string[];
5
+ run(): Promise<void>;
6
+ }
@@ -0,0 +1,63 @@
1
+ import { Command } from '@oclif/core';
2
+ import chalk from 'chalk';
3
+ import { decode } from 'jsonwebtoken';
4
+ import { AuthService } from '../../services/auth-service.js';
5
+ export default class Status extends Command {
6
+ static description = 'Show current authentication status';
7
+ static examples = ['<%= config.bin %> <%= command.id %>'];
8
+ async run() {
9
+ const authService = new AuthService();
10
+ const credentials = authService.loadCredentials();
11
+ if (!credentials) {
12
+ this.log(chalk.yellow('❌ Not authenticated'));
13
+ this.log('');
14
+ this.log(chalk.gray('Run `hd auth login` to authenticate'));
15
+ return;
16
+ }
17
+ try {
18
+ // Decode ID token to get user info (without verification - just for display)
19
+ const idTokenPayload = decode(credentials.id_token);
20
+ this.log(chalk.blue('🔐 Authentication Status'));
21
+ this.log('');
22
+ this.log(chalk.white('Status:'), chalk.green('✓ Authenticated'));
23
+ if (idTokenPayload) {
24
+ this.log(chalk.white('User:'), chalk.cyan(idTokenPayload.email || idTokenPayload.sub));
25
+ if (idTokenPayload.name) {
26
+ this.log(chalk.white('Name:'), chalk.cyan(idTokenPayload.name));
27
+ }
28
+ }
29
+ this.log('');
30
+ this.log(chalk.white('AWS Credentials:'));
31
+ const awsExpiration = new Date(credentials.awsCredentials.expiration);
32
+ const now = new Date();
33
+ const expiresInMs = awsExpiration.getTime() - now.getTime();
34
+ const expiresInMin = Math.floor(expiresInMs / 1000 / 60);
35
+ const isExpired = expiresInMs <= 0;
36
+ const isExpiringSoon = expiresInMs > 0 && expiresInMs < 5 * 60 * 1000; // Less than 5 minutes
37
+ if (isExpired) {
38
+ this.log(chalk.white(' Status:'), chalk.red('✗ Expired'));
39
+ this.log(chalk.white(' Expired:'), chalk.red(awsExpiration.toLocaleString()));
40
+ this.log('');
41
+ this.log(chalk.yellow('⚠️ Run `hd auth refresh` to renew your session'));
42
+ }
43
+ else if (isExpiringSoon) {
44
+ this.log(chalk.white(' Status:'), chalk.yellow('⚠ Expiring soon'));
45
+ this.log(chalk.white(' Expires:'), chalk.yellow(`in ${expiresInMin} minutes`));
46
+ this.log(chalk.white(' Expires at:'), chalk.gray(awsExpiration.toLocaleString()));
47
+ this.log('');
48
+ this.log(chalk.yellow('💡 Tip: Run `hd auth refresh` to extend your session'));
49
+ }
50
+ else {
51
+ this.log(chalk.white(' Status:'), chalk.green('✓ Valid'));
52
+ this.log(chalk.white(' Expires:'), chalk.green(`in ${expiresInMin} minutes`));
53
+ this.log(chalk.white(' Expires at:'), chalk.gray(awsExpiration.toLocaleString()));
54
+ }
55
+ this.log('');
56
+ this.log(chalk.gray('Access Key:'), chalk.gray(credentials.awsCredentials.accessKeyId));
57
+ this.log(chalk.gray('Session Token:'), chalk.gray(`${credentials.awsCredentials.sessionToken.substring(0, 20)}...`));
58
+ }
59
+ catch (error) {
60
+ this.error(chalk.red(`Failed to check status: ${error instanceof Error ? error.message : String(error)}`));
61
+ }
62
+ }
63
+ }
@@ -0,0 +1,16 @@
1
+ import { Command } from '@oclif/core';
2
+ export default class CIAccountCreate extends Command {
3
+ static description: string;
4
+ static examples: string[];
5
+ static flags: {
6
+ description: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string | undefined, 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
+ name: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
9
+ project: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string[] | undefined, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
10
+ scope: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string[] | undefined, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
11
+ stage: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string[] | undefined, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
12
+ };
13
+ run(): Promise<void>;
14
+ private promptForName;
15
+ private promptForScopes;
16
+ }
@@ -0,0 +1,158 @@
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
+ const VALID_SCOPES = [
6
+ 'deploy',
7
+ 'deploy:create',
8
+ 'deploy:launch',
9
+ 'module:read',
10
+ 'module:write',
11
+ 'parameter:read',
12
+ 'parameter:write',
13
+ 'stage:read',
14
+ 'stage:write',
15
+ ];
16
+ export default class CIAccountCreate extends Command {
17
+ static description = 'Create a CI token for non-interactive authentication in CI/CD pipelines';
18
+ static examples = [
19
+ '<%= config.bin %> <%= command.id %> --name="github-prod" --scope=deploy --scope=module:read',
20
+ '<%= config.bin %> <%= command.id %> --name="gitlab-staging" --scope=deploy --scope=parameter:read',
21
+ ];
22
+ static flags = {
23
+ description: Flags.string({
24
+ description: 'Optional description for the CI account',
25
+ }),
26
+ domain: Flags.string({
27
+ char: 'd',
28
+ description: 'Tenant domain',
29
+ }),
30
+ name: Flags.string({
31
+ char: 'n',
32
+ description: 'Name for the CI account (e.g., github-prod, gitlab-staging)',
33
+ }),
34
+ project: Flags.string({
35
+ description: 'Restrict to specific project slugs (can be specified multiple times)',
36
+ multiple: true,
37
+ }),
38
+ scope: Flags.string({
39
+ char: 's',
40
+ description: 'Scopes to grant (can be specified multiple times)',
41
+ multiple: true,
42
+ options: [...VALID_SCOPES],
43
+ }),
44
+ stage: Flags.string({
45
+ description: 'Restrict to specific stages (can be specified multiple times)',
46
+ multiple: true,
47
+ }),
48
+ };
49
+ async run() {
50
+ const { flags } = await this.parse(CIAccountCreate);
51
+ this.log(chalk.cyan('\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'));
52
+ this.log(chalk.cyan('🔑 Create CI Service Account'));
53
+ this.log(chalk.cyan('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'));
54
+ this.log('');
55
+ // Prompt for missing values
56
+ const name = flags.name || await this.promptForName();
57
+ const scopes = flags.scope && flags.scope.length > 0 ? flags.scope : await this.promptForScopes();
58
+ if (scopes.length === 0) {
59
+ this.error('At least one scope is required');
60
+ }
61
+ // Validate scopes
62
+ for (const scope of scopes) {
63
+ if (!VALID_SCOPES.includes(scope)) {
64
+ this.error(`Invalid scope: ${scope}. Valid scopes: ${VALID_SCOPES.join(', ')}`);
65
+ }
66
+ }
67
+ const service = new HyperdriveSigV4Service(flags.domain);
68
+ this.log(chalk.blue(`\n🔄 Creating CI account "${name}"...`));
69
+ try {
70
+ const result = await service.ciAccountCreate({
71
+ description: flags.description,
72
+ name,
73
+ projectSlugs: flags.project,
74
+ scopes,
75
+ stages: flags.stage,
76
+ });
77
+ this.log('');
78
+ this.log(chalk.green('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'));
79
+ this.log(chalk.green('✅ CI Token Created Successfully!'));
80
+ this.log(chalk.green('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'));
81
+ this.log('');
82
+ this.log(chalk.white('Account Details:'));
83
+ this.log(chalk.gray(` • ID: ${result.account.accountId}`));
84
+ this.log(chalk.gray(` • Name: ${result.account.name}`));
85
+ this.log(chalk.gray(` • Scopes: ${result.account.scopes.join(', ')}`));
86
+ this.log('');
87
+ this.log(chalk.yellow('⚠️ IMPORTANT: Save this token securely!'));
88
+ this.log(chalk.yellow(' It will NOT be shown again.'));
89
+ this.log('');
90
+ this.log(chalk.white('Token:'));
91
+ this.log('');
92
+ this.log(chalk.cyan(` ${result.token}`));
93
+ this.log('');
94
+ this.log(chalk.white('Add to your CI environment:'));
95
+ this.log('');
96
+ this.log(chalk.gray(' GitHub Actions:'));
97
+ this.log(chalk.gray(' Settings → Secrets and variables → Actions → New repository secret'));
98
+ this.log(chalk.gray(' Name: HD_TOKEN'));
99
+ this.log('');
100
+ this.log(chalk.gray(' GitLab CI:'));
101
+ this.log(chalk.gray(' Settings → CI/CD → Variables → Add variable'));
102
+ this.log(chalk.gray(' Key: HD_TOKEN (masked)'));
103
+ this.log('');
104
+ this.log(chalk.white('Usage in CI pipeline:'));
105
+ this.log('');
106
+ this.log(chalk.gray(' export HD_TOKEN=<your-token>'));
107
+ this.log(chalk.gray(' hd auth login --ci --domain <your-domain>'));
108
+ this.log(chalk.gray(' hd deployment create --project <slug> --stage <stage>'));
109
+ this.log('');
110
+ }
111
+ catch (error) {
112
+ const err = error;
113
+ const message = err.response?.data?.message || err.message;
114
+ this.error(`Failed to create CI account: ${message}`);
115
+ }
116
+ }
117
+ async promptForName() {
118
+ const { name } = await inquirer.prompt([{
119
+ message: chalk.yellow('Enter a name for this CI account (e.g., github-prod):'),
120
+ name: 'name',
121
+ validate: (input) => {
122
+ if (!input || input.trim().length === 0) {
123
+ return 'Name is required';
124
+ }
125
+ if (input.length > 50) {
126
+ return 'Name must be 50 characters or less';
127
+ }
128
+ return true;
129
+ },
130
+ }]);
131
+ return name;
132
+ }
133
+ async promptForScopes() {
134
+ const { scopes } = await inquirer.prompt([{
135
+ choices: [
136
+ { checked: true, name: 'deploy - Create and launch deployments', value: 'deploy' },
137
+ { name: 'deploy:create - Create deployments only', value: 'deploy:create' },
138
+ { name: 'deploy:launch - Launch deployments only', value: 'deploy:launch' },
139
+ { checked: true, name: 'module:read - Read module information', value: 'module:read' },
140
+ { name: 'module:write - Create/update modules', value: 'module:write' },
141
+ { name: 'parameter:read - Read parameters', value: 'parameter:read' },
142
+ { name: 'parameter:write - Create/update parameters', value: 'parameter:write' },
143
+ { name: 'stage:read - Read stage information', value: 'stage:read' },
144
+ { name: 'stage:write - Create stages', value: 'stage:write' },
145
+ ],
146
+ message: chalk.yellow('Select scopes for this CI account:'),
147
+ name: 'scopes',
148
+ type: 'checkbox',
149
+ validate: (input) => {
150
+ if (input.length === 0) {
151
+ return 'At least one scope is required';
152
+ }
153
+ return true;
154
+ },
155
+ }]);
156
+ return scopes;
157
+ }
158
+ }
@@ -0,0 +1,14 @@
1
+ import { Command } from '@oclif/core';
2
+ export default class CIAccountDelete extends Command {
3
+ static args: {
4
+ accountId: import("@oclif/core/lib/interfaces/parser.js").Arg<string | undefined, Record<string, unknown>>;
5
+ };
6
+ static description: string;
7
+ static examples: string[];
8
+ static flags: {
9
+ domain: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
10
+ force: import("@oclif/core/lib/interfaces/parser.js").BooleanFlag<boolean>;
11
+ };
12
+ run(): Promise<void>;
13
+ private selectAccount;
14
+ }
@@ -0,0 +1,88 @@
1
+ import { Args, 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 CIAccountDelete extends Command {
6
+ static args = {
7
+ accountId: Args.string({
8
+ description: 'CI token ID to delete',
9
+ required: false,
10
+ }),
11
+ };
12
+ static description = 'Delete (revoke) a CI token';
13
+ static examples = [
14
+ '<%= config.bin %> <%= command.id %> <account-id>',
15
+ '<%= config.bin %> <%= command.id %> <account-id> --force',
16
+ ];
17
+ static flags = {
18
+ domain: Flags.string({
19
+ char: 'd',
20
+ description: 'Tenant domain',
21
+ }),
22
+ force: Flags.boolean({
23
+ char: 'f',
24
+ default: false,
25
+ description: 'Skip confirmation prompt',
26
+ }),
27
+ };
28
+ async run() {
29
+ const { args, flags } = await this.parse(CIAccountDelete);
30
+ const service = new HyperdriveSigV4Service(flags.domain);
31
+ // If no account ID provided, list accounts and let user select
32
+ let accountId = args.accountId;
33
+ if (!accountId) {
34
+ accountId = await this.selectAccount(service);
35
+ }
36
+ if (!accountId) {
37
+ this.error('Account ID is required');
38
+ }
39
+ // Confirm deletion unless --force is used
40
+ if (!flags.force) {
41
+ const { confirm } = await inquirer.prompt([{
42
+ default: false,
43
+ message: chalk.yellow(`Are you sure you want to revoke CI token ${accountId}?`),
44
+ name: 'confirm',
45
+ type: 'confirm',
46
+ }]);
47
+ if (!confirm) {
48
+ this.log(chalk.gray('Revocation cancelled.'));
49
+ return;
50
+ }
51
+ }
52
+ this.log(chalk.blue(`\n🗑️ Revoking CI token...`));
53
+ try {
54
+ await service.ciAccountDelete(accountId);
55
+ this.log(chalk.green('\n✅ CI token revoked successfully.'));
56
+ this.log(chalk.gray('Any CI pipelines using this token will no longer be able to authenticate.'));
57
+ }
58
+ catch (error) {
59
+ const err = error;
60
+ const message = err.response?.data?.message || err.message;
61
+ this.error(`Failed to delete CI account: ${message}`);
62
+ }
63
+ }
64
+ async selectAccount(service) {
65
+ try {
66
+ const accounts = await service.ciAccountList();
67
+ if (accounts.length === 0) {
68
+ this.log(chalk.yellow('\nNo CI tokens found.'));
69
+ return undefined;
70
+ }
71
+ const { accountId } = await inquirer.prompt([{
72
+ choices: accounts.map(account => ({
73
+ name: `${account.name} (${account.cognitoUsername}) - ${account.status}`,
74
+ value: account.accountId,
75
+ })),
76
+ message: chalk.yellow('Select a CI token to revoke:'),
77
+ name: 'accountId',
78
+ type: 'list',
79
+ }]);
80
+ return accountId;
81
+ }
82
+ catch (error) {
83
+ const err = error;
84
+ const message = err.response?.data?.message || err.message;
85
+ this.error(`Failed to list CI accounts: ${message}`);
86
+ }
87
+ }
88
+ }
@@ -0,0 +1,10 @@
1
+ import { Command } from '@oclif/core';
2
+ export default class CIAccountList 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
+ json: import("@oclif/core/lib/interfaces/parser.js").BooleanFlag<boolean>;
8
+ };
9
+ run(): Promise<void>;
10
+ }
@@ -0,0 +1,65 @@
1
+ import { Command, Flags } from '@oclif/core';
2
+ import chalk from 'chalk';
3
+ import { HyperdriveSigV4Service } from '../../../services/hyperdrive-sigv4.js';
4
+ export default class CIAccountList extends Command {
5
+ static description = 'List all CI tokens';
6
+ static examples = [
7
+ '<%= config.bin %> <%= command.id %>',
8
+ '<%= config.bin %> <%= command.id %> --json',
9
+ ];
10
+ static flags = {
11
+ domain: Flags.string({
12
+ char: 'd',
13
+ description: 'Tenant domain',
14
+ }),
15
+ json: Flags.boolean({
16
+ description: 'Output as JSON',
17
+ }),
18
+ };
19
+ async run() {
20
+ const { flags } = await this.parse(CIAccountList);
21
+ const service = new HyperdriveSigV4Service(flags.domain);
22
+ try {
23
+ const accounts = await service.ciAccountList();
24
+ if (flags.json) {
25
+ this.log(JSON.stringify(accounts, null, 2));
26
+ return;
27
+ }
28
+ if (accounts.length === 0) {
29
+ this.log(chalk.yellow('\nNo CI tokens found.'));
30
+ this.log(chalk.gray('Create one with: hd ci account create'));
31
+ return;
32
+ }
33
+ this.log(chalk.cyan('\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'));
34
+ this.log(chalk.cyan('🔑 CI Tokens'));
35
+ this.log(chalk.cyan('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'));
36
+ this.log('');
37
+ for (const account of accounts) {
38
+ const statusColor = account.status === 'active' ? chalk.green : chalk.red;
39
+ const statusIcon = account.status === 'active' ? '●' : '○';
40
+ this.log(chalk.white(`${statusColor(statusIcon)} ${account.name}`));
41
+ this.log(chalk.gray(` ID: ${account.accountId}`));
42
+ this.log(chalk.gray(` Username: ${account.cognitoUsername}`));
43
+ this.log(chalk.gray(` Scopes: ${account.scopes.join(', ')}`));
44
+ this.log(chalk.gray(` Status: ${account.status}`));
45
+ this.log(chalk.gray(` Created: ${new Date(account.createdAt).toLocaleString()}`));
46
+ if (account.lastUsedAt) {
47
+ this.log(chalk.gray(` Last Used: ${new Date(account.lastUsedAt).toLocaleString()}`));
48
+ }
49
+ if (account.projectSlugs && account.projectSlugs.length > 0) {
50
+ this.log(chalk.gray(` Projects: ${account.projectSlugs.join(', ')}`));
51
+ }
52
+ if (account.stages && account.stages.length > 0) {
53
+ this.log(chalk.gray(` Stages: ${account.stages.join(', ')}`));
54
+ }
55
+ this.log('');
56
+ }
57
+ this.log(chalk.gray(`Total: ${accounts.length} token(s)`));
58
+ }
59
+ catch (error) {
60
+ const err = error;
61
+ const message = err.response?.data?.message || err.message;
62
+ this.error(`Failed to list CI accounts: ${message}`);
63
+ }
64
+ }
65
+ }
@@ -0,0 +1,9 @@
1
+ import { Command } from '@oclif/core';
2
+ export default class ConfigGet extends Command {
3
+ static args: {
4
+ key: import("@oclif/core/lib/interfaces/parser.js").Arg<string, Record<string, unknown>>;
5
+ };
6
+ static description: string;
7
+ static examples: string[];
8
+ run(): Promise<void>;
9
+ }
@@ -0,0 +1,37 @@
1
+ import { Args, Command } from '@oclif/core';
2
+ import chalk from 'chalk';
3
+ import { TenantService } from '../../services/tenant-service.js';
4
+ export default class ConfigGet extends Command {
5
+ static args = {
6
+ key: Args.string({
7
+ description: 'Configuration key',
8
+ options: ['tenant-domain', 'bootstrap-url', 'api-url', 'region'],
9
+ required: true,
10
+ }),
11
+ };
12
+ static description = 'Get CLI configuration value';
13
+ static examples = [
14
+ '<%= config.bin %> <%= command.id %> tenant-domain',
15
+ '<%= config.bin %> <%= command.id %> bootstrap-url',
16
+ ];
17
+ async run() {
18
+ const { args } = await this.parse(ConfigGet);
19
+ const tenantService = new TenantService();
20
+ // Convert kebab-case to camelCase
21
+ const configKey = args.key.replace(/-([a-z])/g, (g) => g[1].toUpperCase());
22
+ try {
23
+ const config = tenantService.loadConfigFile();
24
+ if (!config || !config[configKey]) {
25
+ this.log(chalk.yellow(`⚠️ ${args.key} is not set`));
26
+ this.log('');
27
+ this.log(chalk.gray(`Set it with: hd config set ${args.key} <value>`));
28
+ return;
29
+ }
30
+ this.log('');
31
+ this.log(chalk.white(`${args.key}:`), chalk.cyan(config[configKey]));
32
+ }
33
+ catch (error) {
34
+ this.error(chalk.red(`Failed to get configuration: ${error instanceof Error ? error.message : String(error)}`));
35
+ }
36
+ }
37
+ }
@@ -0,0 +1,10 @@
1
+ import { Command } from '@oclif/core';
2
+ export default class ConfigSet extends Command {
3
+ static args: {
4
+ key: import("@oclif/core/lib/interfaces/parser.js").Arg<string, Record<string, unknown>>;
5
+ value: import("@oclif/core/lib/interfaces/parser.js").Arg<string, Record<string, unknown>>;
6
+ };
7
+ static description: string;
8
+ static examples: string[];
9
+ run(): Promise<void>;
10
+ }
@@ -0,0 +1,48 @@
1
+ import { Args, Command } from '@oclif/core';
2
+ import chalk from 'chalk';
3
+ import { TenantService } from '../../services/tenant-service.js';
4
+ export default class ConfigSet extends Command {
5
+ static args = {
6
+ key: Args.string({
7
+ description: 'Configuration key',
8
+ options: ['tenant-domain', 'bootstrap-url', 'api-url', 'region'],
9
+ required: true,
10
+ }),
11
+ value: Args.string({
12
+ description: 'Configuration value',
13
+ required: true,
14
+ }),
15
+ };
16
+ static description = 'Set CLI configuration values';
17
+ static examples = [
18
+ '<%= config.bin %> <%= command.id %> tenant-domain acme.hyperdrive.bot',
19
+ '<%= config.bin %> <%= command.id %> bootstrap-url https://custom-api.example.com/tenant/bootstrap',
20
+ '<%= config.bin %> <%= command.id %> api-url https://api.us-east-1.hyperdrive.bot',
21
+ '<%= config.bin %> <%= command.id %> region us-west-2',
22
+ ];
23
+ async run() {
24
+ const { args } = await this.parse(ConfigSet);
25
+ const tenantService = new TenantService();
26
+ // Convert kebab-case to camelCase for config keys
27
+ const configKey = args.key.replace(/-([a-z])/g, (g) => g[1].toUpperCase());
28
+ try {
29
+ // Load existing config
30
+ const existingConfig = tenantService.loadConfigFile() || {};
31
+ // Update config
32
+ const newConfig = {
33
+ ...existingConfig,
34
+ [configKey]: args.value,
35
+ };
36
+ tenantService.saveConfig(newConfig);
37
+ this.log('');
38
+ this.log(chalk.green(`✅ Configuration updated successfully!`));
39
+ this.log('');
40
+ this.log(chalk.white(`${args.key}:`), chalk.cyan(args.value));
41
+ this.log('');
42
+ this.log(chalk.gray('💡 Tip: Environment variables take precedence over config file settings'));
43
+ }
44
+ catch (error) {
45
+ this.error(chalk.red(`Failed to set configuration: ${error instanceof Error ? error.message : String(error)}`));
46
+ }
47
+ }
48
+ }
@@ -0,0 +1,6 @@
1
+ import { Command } from '@oclif/core';
2
+ export default class ConfigShow extends Command {
3
+ static description: string;
4
+ static examples: string[];
5
+ run(): Promise<void>;
6
+ }
@@ -0,0 +1,10 @@
1
+ import { Command } from '@oclif/core';
2
+ import { TenantService } from '../../services/tenant-service.js';
3
+ export default class ConfigShow extends Command {
4
+ static description = 'Show all CLI configuration settings';
5
+ static examples = ['<%= config.bin %> <%= command.id %>'];
6
+ async run() {
7
+ const tenantService = new TenantService();
8
+ tenantService.showConfig();
9
+ }
10
+ }
@@ -0,0 +1,30 @@
1
+ import { Command } from '@oclif/core';
2
+ export default class DeploymentCreate extends Command {
3
+ static description: string;
4
+ static examples: string[];
5
+ static flags: {
6
+ commit: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
7
+ debug: import("@oclif/core/lib/interfaces/parser.js").BooleanFlag<boolean>;
8
+ domain: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
9
+ launch: import("@oclif/core/lib/interfaces/parser.js").BooleanFlag<boolean>;
10
+ moduleSlug: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
11
+ name: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
12
+ noStream: import("@oclif/core/lib/interfaces/parser.js").BooleanFlag<boolean>;
13
+ regions: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string[], import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
14
+ stage: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
15
+ verbose: import("@oclif/core/lib/interfaces/parser.js").BooleanFlag<boolean>;
16
+ };
17
+ run(): Promise<void>;
18
+ /**
19
+ * Build CloudWatch console URL for log viewing
20
+ */
21
+ private buildConsoleUrl;
22
+ /**
23
+ * Poll for deployment status (fallback when streaming not available)
24
+ */
25
+ private pollDeploymentStatus;
26
+ /**
27
+ * Stream deployment logs using CloudWatch
28
+ */
29
+ private streamDeploymentLogs;
30
+ }