@hyperdrive.bot/cli 1.0.12 → 1.0.16
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.
- package/README.md +1495 -474
- package/dist/commands/deploy.d.ts +18 -0
- package/dist/commands/deploy.js +239 -0
- package/dist/commands/deployment/create.js +10 -2
- package/dist/commands/domain/{switch.d.ts → set-production.d.ts} +1 -1
- package/dist/commands/domain/set-production.js +27 -0
- package/dist/commands/git/list-open-prs.d.ts +12 -0
- package/dist/commands/git/list-open-prs.js +87 -0
- package/dist/commands/hook/add.d.ts +22 -0
- package/dist/commands/hook/add.js +299 -0
- package/dist/commands/hook/list.d.ts +11 -0
- package/dist/commands/hook/list.js +111 -0
- package/dist/commands/hook/logs.d.ts +13 -0
- package/dist/commands/hook/logs.js +124 -0
- package/dist/commands/hook/remove.d.ts +12 -0
- package/dist/commands/hook/remove.js +115 -0
- package/dist/commands/hook/toggle.d.ts +12 -0
- package/dist/commands/hook/toggle.js +125 -0
- package/dist/commands/init.d.ts +1 -1
- package/dist/commands/init.js +49 -9
- package/dist/commands/module/bindings.d.ts +14 -0
- package/dist/commands/module/bindings.js +125 -0
- package/dist/commands/module/create.d.ts +3 -0
- package/dist/commands/module/create.js +156 -78
- package/dist/commands/module/list.d.ts +1 -0
- package/dist/commands/module/list.js +22 -1
- package/dist/commands/module/sync.d.ts +29 -0
- package/dist/commands/module/sync.js +409 -0
- package/dist/commands/module/unlink.d.ts +11 -0
- package/dist/commands/module/unlink.js +77 -0
- package/dist/commands/module/update.d.ts +10 -0
- package/dist/commands/module/update.js +168 -5
- package/dist/commands/network/discover.d.ts +12 -0
- package/dist/commands/network/discover.js +210 -0
- package/dist/commands/network/get.d.ts +13 -0
- package/dist/commands/network/get.js +90 -0
- package/dist/commands/{auth/logout.d.ts → network/list.d.ts} +2 -9
- package/dist/commands/network/list.js +71 -0
- package/dist/commands/network/register.d.ts +16 -0
- package/dist/commands/network/register.js +144 -0
- package/dist/commands/parameter/sync.d.ts +13 -0
- package/dist/commands/parameter/sync.js +69 -1
- package/dist/commands/project/sync.d.ts +5 -11
- package/dist/commands/project/sync.js +12 -381
- package/dist/commands/seed.d.ts +93 -0
- package/dist/commands/seed.js +324 -0
- package/dist/commands/service/backup.d.ts +17 -0
- package/dist/commands/service/backup.js +156 -0
- package/dist/commands/service/backups.d.ts +14 -0
- package/dist/commands/service/backups.js +110 -0
- package/dist/commands/service/bind.d.ts +16 -0
- package/dist/commands/service/bind.js +106 -0
- package/dist/commands/service/bindings.d.ts +13 -0
- package/dist/commands/service/bindings.js +78 -0
- package/dist/commands/service/clone.d.ts +19 -0
- package/dist/commands/service/clone.js +153 -0
- package/dist/commands/service/create.d.ts +16 -0
- package/dist/commands/service/create.js +212 -0
- package/dist/commands/service/get.d.ts +13 -0
- package/dist/commands/service/get.js +97 -0
- package/dist/commands/service/list.d.ts +12 -0
- package/dist/commands/service/list.js +86 -0
- package/dist/commands/service/register.d.ts +21 -0
- package/dist/commands/service/register.js +215 -0
- package/dist/commands/service/restore.d.ts +19 -0
- package/dist/commands/service/restore.js +158 -0
- package/dist/commands/service/seed.d.ts +17 -0
- package/dist/commands/service/seed.js +173 -0
- package/dist/commands/service/templates.d.ts +10 -0
- package/dist/commands/service/templates.js +66 -0
- package/dist/commands/service/unbind.d.ts +15 -0
- package/dist/commands/service/unbind.js +74 -0
- package/dist/commands/stage/create.d.ts +23 -0
- package/dist/commands/stage/create.js +145 -6
- package/dist/commands/stage/delete.d.ts +11 -0
- package/dist/commands/stage/delete.js +85 -0
- package/dist/commands/stage/deploy.d.ts +34 -0
- package/dist/commands/stage/deploy.js +294 -0
- package/dist/commands/stage/ensure-branches.d.ts +23 -0
- package/dist/commands/stage/ensure-branches.js +101 -0
- package/dist/commands/stage/list.js +4 -0
- package/dist/commands/stage/status.d.ts +14 -0
- package/dist/commands/stage/status.js +100 -0
- package/dist/commands/{jira → tracker}/connect.js +32 -23
- package/dist/commands/tracker/hook/add.d.ts +25 -0
- package/dist/commands/tracker/hook/add.js +284 -0
- package/dist/commands/{jira → tracker}/hook/list.js +20 -11
- package/dist/commands/{jira/hook/add.d.ts → tracker/hook/logs.d.ts} +2 -3
- package/dist/commands/tracker/hook/logs.js +126 -0
- package/dist/commands/{jira → tracker}/hook/remove.js +9 -8
- package/dist/commands/{jira → tracker}/hook/toggle.js +14 -12
- package/dist/commands/tracker/project/init.d.ts +17 -0
- package/dist/commands/tracker/project/init.js +178 -0
- package/dist/commands/tracker/project/link-module.d.ts +17 -0
- package/dist/commands/tracker/project/link-module.js +287 -0
- package/dist/commands/tracker/project/list-modules.d.ts +11 -0
- package/dist/commands/tracker/project/list-modules.js +117 -0
- package/dist/commands/tracker/project/list.d.ts +10 -0
- package/dist/commands/tracker/project/list.js +90 -0
- package/dist/commands/tracker/project/status.d.ts +13 -0
- package/dist/commands/tracker/project/status.js +168 -0
- package/dist/commands/tracker/project/unlink-module.d.ts +13 -0
- package/dist/commands/tracker/project/unlink-module.js +251 -0
- package/dist/commands/{jira → tracker}/status.js +3 -3
- package/dist/lib/ensure-branches.d.ts +53 -0
- package/dist/lib/ensure-branches.js +149 -0
- package/dist/lib/git-providers/github.d.ts +16 -0
- package/dist/lib/git-providers/github.js +157 -0
- package/dist/lib/git-providers/gitlab.d.ts +16 -0
- package/dist/lib/git-providers/gitlab.js +148 -0
- package/dist/lib/git-providers/index.d.ts +67 -0
- package/dist/lib/git-providers/index.js +39 -0
- package/dist/lib/lambda-warmer.d.ts +106 -0
- package/dist/lib/lambda-warmer.js +189 -0
- package/dist/services/hyperdrive-sigv4.d.ts +360 -5
- package/dist/services/hyperdrive-sigv4.js +192 -24
- package/dist/utils/hook-flow.d.ts +60 -3
- package/dist/utils/hook-flow.js +437 -2
- package/dist/utils/hook-normalize.d.ts +6 -0
- package/dist/utils/hook-normalize.js +33 -0
- package/dist/utils/lifecycle-poller.d.ts +32 -0
- package/dist/utils/lifecycle-poller.js +72 -0
- package/dist/utils/retry.d.ts +43 -0
- package/dist/utils/retry.js +88 -0
- package/dist/utils/summary-display.js +1 -1
- package/dist/utils/tracker-project-flow.d.ts +84 -0
- package/dist/utils/tracker-project-flow.js +564 -0
- package/package.json +35 -7
- package/dist/commands/auth/login.d.ts +0 -16
- package/dist/commands/auth/login.js +0 -179
- package/dist/commands/auth/logout.js +0 -116
- package/dist/commands/auth/refresh.d.ts +0 -6
- package/dist/commands/auth/refresh.js +0 -66
- package/dist/commands/auth/status.d.ts +0 -6
- package/dist/commands/auth/status.js +0 -63
- package/dist/commands/config/get.d.ts +0 -9
- package/dist/commands/config/get.js +0 -37
- package/dist/commands/config/set.d.ts +0 -10
- package/dist/commands/config/set.js +0 -48
- package/dist/commands/config/show.d.ts +0 -6
- package/dist/commands/config/show.js +0 -10
- package/dist/commands/domain/current.d.ts +0 -6
- package/dist/commands/domain/current.js +0 -18
- package/dist/commands/domain/list.d.ts +0 -6
- package/dist/commands/domain/list.js +0 -42
- package/dist/commands/domain/switch.js +0 -40
- package/dist/commands/jira/hook/add.js +0 -147
- package/dist/services/tenant-service.d.ts +0 -127
- package/dist/services/tenant-service.js +0 -396
- package/dist/utils/auth-flow.d.ts +0 -147
- package/dist/utils/auth-flow.js +0 -479
- package/oclif.manifest.json +0 -3519
- /package/dist/commands/{jira → tracker}/connect.d.ts +0 -0
- /package/dist/commands/{jira → tracker}/hook/list.d.ts +0 -0
- /package/dist/commands/{jira → tracker}/hook/remove.d.ts +0 -0
- /package/dist/commands/{jira → tracker}/hook/toggle.d.ts +0 -0
- /package/dist/commands/{jira → tracker}/status.d.ts +0 -0
|
@@ -1,179 +0,0 @@
|
|
|
1
|
-
import { Command, Flags } from '@oclif/core';
|
|
2
|
-
import chalk from 'chalk';
|
|
3
|
-
import inquirer from 'inquirer';
|
|
4
|
-
import open from 'open';
|
|
5
|
-
import ora from 'ora';
|
|
6
|
-
import { TenantService } from '../../services/tenant-service.js';
|
|
7
|
-
import { buildAuthUrl, exchangeCodeForTokens, executeCIAuthFlow, generateCodeChallenge, generateCodeVerifier, getAWSCredentials, getCICredentials, getCredentialsPath, isCI, saveCredentials, startCallbackServer, } from '../../utils/auth-flow.js';
|
|
8
|
-
export default class Login extends Command {
|
|
9
|
-
static description = 'Authenticate with Hyperdrive using OAuth 2.0 PKCE flow (or CI credentials)';
|
|
10
|
-
static examples = [
|
|
11
|
-
'<%= config.bin %> <%= command.id %>',
|
|
12
|
-
'<%= config.bin %> <%= command.id %> --domain acme.hyperdrive.bot',
|
|
13
|
-
'<%= config.bin %> <%= command.id %> --port 9876',
|
|
14
|
-
'<%= config.bin %> <%= command.id %> --ci --domain acme.hyperdrive.bot',
|
|
15
|
-
];
|
|
16
|
-
static flags = {
|
|
17
|
-
ci: Flags.boolean({
|
|
18
|
-
default: false,
|
|
19
|
-
description: 'Use CI authentication (requires HD_CI_USERNAME and HD_CI_PASSWORD env vars)',
|
|
20
|
-
}),
|
|
21
|
-
domain: Flags.string({
|
|
22
|
-
char: 'd',
|
|
23
|
-
description: 'Tenant domain (e.g., acme.hyperdrive.bot) - allows multiple installations',
|
|
24
|
-
}),
|
|
25
|
-
port: Flags.integer({
|
|
26
|
-
char: 'p',
|
|
27
|
-
default: 8765,
|
|
28
|
-
description: 'Local callback server port',
|
|
29
|
-
}),
|
|
30
|
-
tenant: Flags.string({
|
|
31
|
-
char: 't',
|
|
32
|
-
description: '[DEPRECATED] Use --domain instead',
|
|
33
|
-
hidden: true,
|
|
34
|
-
}),
|
|
35
|
-
};
|
|
36
|
-
async run() {
|
|
37
|
-
const { flags } = await this.parse(Login);
|
|
38
|
-
// Support legacy --tenant flag (fallback to --domain)
|
|
39
|
-
const domainFlag = flags.domain || flags.tenant;
|
|
40
|
-
// Auto-detect CI environment if --ci flag not provided but CI env vars are set
|
|
41
|
-
const ciCredentials = getCICredentials();
|
|
42
|
-
const useCI = flags.ci || (isCI() && ciCredentials !== null);
|
|
43
|
-
// Handle CI authentication
|
|
44
|
-
if (useCI) {
|
|
45
|
-
await this.runCIAuth(domainFlag);
|
|
46
|
-
return;
|
|
47
|
-
}
|
|
48
|
-
const tenantService = new TenantService(domainFlag);
|
|
49
|
-
this.log(chalk.blue('🚀 Starting Hyperdrive authentication...'));
|
|
50
|
-
this.log('');
|
|
51
|
-
// Step 1: Resolve tenant configuration from bootstrap
|
|
52
|
-
let tenantConfig;
|
|
53
|
-
const spinner = ora('Fetching tenant configuration...').start();
|
|
54
|
-
try {
|
|
55
|
-
// Prompt for tenant if not provided
|
|
56
|
-
let tenantDomain = domainFlag;
|
|
57
|
-
if (!tenantDomain) {
|
|
58
|
-
spinner.stop();
|
|
59
|
-
// Get saved tenant domain from config file or env var
|
|
60
|
-
const savedTenantDomain = tenantService.getTenantDomain();
|
|
61
|
-
const answers = await inquirer.prompt([
|
|
62
|
-
{
|
|
63
|
-
default: savedTenantDomain || undefined,
|
|
64
|
-
message: 'Enter your tenant domain:',
|
|
65
|
-
name: 'tenant',
|
|
66
|
-
type: 'input',
|
|
67
|
-
validate: (input) => input.trim().length > 0 || 'Tenant domain is required',
|
|
68
|
-
},
|
|
69
|
-
]);
|
|
70
|
-
tenantDomain = answers.tenant;
|
|
71
|
-
spinner.start('Fetching tenant configuration...');
|
|
72
|
-
}
|
|
73
|
-
tenantConfig = await tenantService.fetchTenantConfig(tenantDomain);
|
|
74
|
-
spinner.succeed(chalk.green(`Tenant found: ${tenantConfig.displayName}`));
|
|
75
|
-
}
|
|
76
|
-
catch (error) {
|
|
77
|
-
spinner.fail(chalk.red('Failed to fetch tenant configuration'));
|
|
78
|
-
this.log('');
|
|
79
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
80
|
-
this.error(errorMessage + '\n\n' +
|
|
81
|
-
chalk.yellow('💡 Tips:\n') +
|
|
82
|
-
chalk.gray(' - Check your tenant domain spelling\n') +
|
|
83
|
-
chalk.gray(' - Set HYPERDRIVE_TENANT_DOMAIN environment variable\n') +
|
|
84
|
-
chalk.gray(' - Or run `hd init` to configure your environment'));
|
|
85
|
-
}
|
|
86
|
-
try {
|
|
87
|
-
// Generate PKCE parameters using extracted utilities
|
|
88
|
-
const codeVerifier = generateCodeVerifier();
|
|
89
|
-
const codeChallenge = generateCodeChallenge(codeVerifier);
|
|
90
|
-
// Start local callback server and get promise for auth code
|
|
91
|
-
const authCodePromise = startCallbackServer(flags.port, 300000); // 5 min timeout
|
|
92
|
-
// Open browser for authentication
|
|
93
|
-
const authUrl = buildAuthUrl(tenantConfig, codeChallenge, flags.port);
|
|
94
|
-
this.log(chalk.yellow('📱 Opening browser for authentication...'));
|
|
95
|
-
this.log(chalk.gray(`If browser doesn't open, visit: ${authUrl}`));
|
|
96
|
-
this.log('');
|
|
97
|
-
await open(authUrl);
|
|
98
|
-
spinner.start('Waiting for authentication...');
|
|
99
|
-
// Wait for callback with auth code
|
|
100
|
-
const code = await authCodePromise;
|
|
101
|
-
spinner.succeed(chalk.green('Authentication callback received!'));
|
|
102
|
-
// Exchange code for tokens using extracted utility
|
|
103
|
-
spinner.start('Exchanging authorization code for tokens...');
|
|
104
|
-
const tokens = await exchangeCodeForTokens(tenantConfig, code, codeVerifier, flags.port);
|
|
105
|
-
spinner.succeed(chalk.green('Tokens obtained successfully!'));
|
|
106
|
-
// Get AWS credentials from Cognito Identity Pool using extracted utility
|
|
107
|
-
spinner.start('Obtaining AWS credentials...');
|
|
108
|
-
const awsCredentials = await getAWSCredentials(tenantConfig, tokens.id_token);
|
|
109
|
-
spinner.succeed(chalk.green('AWS credentials obtained!'));
|
|
110
|
-
// Save credentials using extracted utility (domain-specific if specified)
|
|
111
|
-
spinner.start('Saving credentials...');
|
|
112
|
-
saveCredentials({
|
|
113
|
-
...tokens,
|
|
114
|
-
additionalApiUrls: tenantConfig.additionalApiUrls,
|
|
115
|
-
apiUrl: tenantConfig.apiUrl,
|
|
116
|
-
awsCredentials,
|
|
117
|
-
cognitoConfig: {
|
|
118
|
-
clientId: tenantConfig.cognitoClientId,
|
|
119
|
-
domain: tenantConfig.cognitoDomain,
|
|
120
|
-
identityPoolId: tenantConfig.cognitoIdentityPoolId,
|
|
121
|
-
userPoolId: tenantConfig.cognitoUserPoolId,
|
|
122
|
-
},
|
|
123
|
-
obtainedAt: new Date().toISOString(),
|
|
124
|
-
region: tenantConfig.region,
|
|
125
|
-
tenantDomain: tenantConfig.tenantDomain,
|
|
126
|
-
tenantId: tenantConfig.tenantId,
|
|
127
|
-
}, tenantConfig.tenantDomain);
|
|
128
|
-
spinner.succeed(chalk.green('Credentials saved!'));
|
|
129
|
-
this.log('');
|
|
130
|
-
this.log(chalk.green('✅ Successfully authenticated!'));
|
|
131
|
-
this.log(chalk.gray('You can now use all Hyperdrive CLI commands.'));
|
|
132
|
-
this.log('');
|
|
133
|
-
this.log(chalk.gray(`✓ Credentials saved to ${getCredentialsPath(tenantConfig.tenantDomain)}`));
|
|
134
|
-
this.log(chalk.gray('💡 Credentials refresh automatically when needed. You\'re all set!'));
|
|
135
|
-
}
|
|
136
|
-
catch (error) {
|
|
137
|
-
this.error(chalk.red(`Authentication failed: ${error instanceof Error ? error.message : String(error)}`));
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
/**
|
|
141
|
-
* Run CI authentication flow (non-interactive)
|
|
142
|
-
*/
|
|
143
|
-
async runCIAuth(domainFlag) {
|
|
144
|
-
this.log(chalk.blue('🤖 Starting CI authentication...'));
|
|
145
|
-
this.log('');
|
|
146
|
-
// Get CI credentials from environment
|
|
147
|
-
const ciCredentials = getCICredentials();
|
|
148
|
-
if (!ciCredentials) {
|
|
149
|
-
this.error(chalk.red('CI authentication requires HD_TOKEN environment variable.\n\n') +
|
|
150
|
-
chalk.yellow('Create a CI token with:\n') +
|
|
151
|
-
chalk.gray(' hd ci account create --name "my-ci" --scope deploy\n\n') +
|
|
152
|
-
chalk.yellow('Then add to your CI environment:\n') +
|
|
153
|
-
chalk.gray(' HD_TOKEN=hd_sk_...'));
|
|
154
|
-
}
|
|
155
|
-
// Require domain for CI auth
|
|
156
|
-
if (!domainFlag) {
|
|
157
|
-
this.error(chalk.red('Domain is required for CI authentication.\n\n') +
|
|
158
|
-
chalk.yellow('Usage:\n') +
|
|
159
|
-
chalk.gray(' hd auth login --ci --domain acme.hyperdrive.bot'));
|
|
160
|
-
}
|
|
161
|
-
const spinner = ora('Authenticating...').start();
|
|
162
|
-
const result = await executeCIAuthFlow({
|
|
163
|
-
logger: (message) => {
|
|
164
|
-
spinner.text = message;
|
|
165
|
-
},
|
|
166
|
-
password: ciCredentials.password,
|
|
167
|
-
tenantDomain: domainFlag,
|
|
168
|
-
username: ciCredentials.username,
|
|
169
|
-
});
|
|
170
|
-
if (!result.success) {
|
|
171
|
-
spinner.fail(chalk.red('Authentication failed'));
|
|
172
|
-
this.error(chalk.red(result.error || 'Unknown error'));
|
|
173
|
-
}
|
|
174
|
-
spinner.succeed(chalk.green('Authenticated successfully!'));
|
|
175
|
-
this.log('');
|
|
176
|
-
this.log(chalk.green('✅ CI authentication complete!'));
|
|
177
|
-
this.log(chalk.gray('You can now use Hyperdrive CLI commands in your CI pipeline.'));
|
|
178
|
-
}
|
|
179
|
-
}
|
|
@@ -1,116 +0,0 @@
|
|
|
1
|
-
import { Command, Flags } from '@oclif/core';
|
|
2
|
-
import chalk from 'chalk';
|
|
3
|
-
import inquirer from 'inquirer';
|
|
4
|
-
import { AuthService } from '../../services/auth-service.js';
|
|
5
|
-
import { TenantService } from '../../services/tenant-service.js';
|
|
6
|
-
export default class Logout extends Command {
|
|
7
|
-
static description = 'Remove stored credentials and logout. Use --domain to logout from a single domain. Without flags, removes all credentials and stored configuration.';
|
|
8
|
-
static examples = [
|
|
9
|
-
'<%= config.bin %> <%= command.id %>',
|
|
10
|
-
'<%= config.bin %> <%= command.id %> --domain acme.hyperdrive.bot',
|
|
11
|
-
];
|
|
12
|
-
static flags = {
|
|
13
|
-
domain: Flags.string({
|
|
14
|
-
char: 'd',
|
|
15
|
-
description: 'Logout from a specific domain only (keeps other domains and config intact)',
|
|
16
|
-
}),
|
|
17
|
-
};
|
|
18
|
-
async run() {
|
|
19
|
-
const { flags } = await this.parse(Logout);
|
|
20
|
-
const tenantService = new TenantService();
|
|
21
|
-
if (flags.domain) {
|
|
22
|
-
await this.logoutDomain(flags.domain, tenantService);
|
|
23
|
-
}
|
|
24
|
-
else {
|
|
25
|
-
await this.logoutAll(tenantService);
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
/**
|
|
29
|
-
* Remove credentials for a specific domain
|
|
30
|
-
*/
|
|
31
|
-
async logoutDomain(domain, tenantService) {
|
|
32
|
-
const authService = new AuthService(domain);
|
|
33
|
-
const credentials = authService.loadCredentials();
|
|
34
|
-
if (!credentials) {
|
|
35
|
-
this.log(chalk.yellow(`⚠️ No active session found for domain: ${domain}`));
|
|
36
|
-
const knownDomains = authService.getCredentialDomains();
|
|
37
|
-
if (knownDomains.length > 0) {
|
|
38
|
-
this.log(chalk.gray(` Logged-in domains: ${knownDomains.join(', ')}`));
|
|
39
|
-
}
|
|
40
|
-
return;
|
|
41
|
-
}
|
|
42
|
-
const { confirm } = await inquirer.prompt([
|
|
43
|
-
{
|
|
44
|
-
default: false,
|
|
45
|
-
message: `Logout from ${chalk.cyan(domain)}?`,
|
|
46
|
-
name: 'confirm',
|
|
47
|
-
type: 'confirm',
|
|
48
|
-
},
|
|
49
|
-
]);
|
|
50
|
-
if (!confirm) {
|
|
51
|
-
this.log(chalk.gray('Logout cancelled'));
|
|
52
|
-
return;
|
|
53
|
-
}
|
|
54
|
-
try {
|
|
55
|
-
authService.clearCredentials();
|
|
56
|
-
tenantService.removeDomainConfig(domain);
|
|
57
|
-
// If this was the default domain, clear the default too
|
|
58
|
-
if (tenantService.getDefaultDomain() === domain) {
|
|
59
|
-
tenantService.clearDefaultDomain();
|
|
60
|
-
}
|
|
61
|
-
this.log('');
|
|
62
|
-
this.log(chalk.green(`✅ Successfully logged out from ${domain}`));
|
|
63
|
-
this.log(chalk.gray('Run `hd auth login` to authenticate again.'));
|
|
64
|
-
}
|
|
65
|
-
catch (error) {
|
|
66
|
-
this.error(chalk.red(`Logout failed: ${error instanceof Error ? error.message : String(error)}`));
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
/**
|
|
70
|
-
* Remove default domain credentials and all stored config
|
|
71
|
-
*/
|
|
72
|
-
async logoutAll(tenantService) {
|
|
73
|
-
const authService = new AuthService();
|
|
74
|
-
const allDomains = authService.getCredentialDomains();
|
|
75
|
-
const defaultDomain = tenantService.getDefaultDomain();
|
|
76
|
-
if (allDomains.length === 0) {
|
|
77
|
-
this.log(chalk.yellow('⚠️ No active sessions found'));
|
|
78
|
-
return;
|
|
79
|
-
}
|
|
80
|
-
this.log(chalk.blue('This will remove:'));
|
|
81
|
-
for (const domain of allDomains) {
|
|
82
|
-
const isDefault = domain === defaultDomain ? chalk.gray(' (default)') : '';
|
|
83
|
-
this.log(chalk.white(` • Credentials for ${chalk.cyan(domain)}${isDefault}`));
|
|
84
|
-
}
|
|
85
|
-
this.log(chalk.white(' • Default domain setting'));
|
|
86
|
-
this.log(chalk.white(` • CLI configuration ${chalk.gray('(~/.hyperdrive/config.json)')}`));
|
|
87
|
-
this.log('');
|
|
88
|
-
const { confirm } = await inquirer.prompt([
|
|
89
|
-
{
|
|
90
|
-
default: false,
|
|
91
|
-
message: `Logout from ${allDomains.length === 1 ? allDomains[0] : `all ${allDomains.length} domains`} and remove stored settings?`,
|
|
92
|
-
name: 'confirm',
|
|
93
|
-
type: 'confirm',
|
|
94
|
-
},
|
|
95
|
-
]);
|
|
96
|
-
if (!confirm) {
|
|
97
|
-
this.log(chalk.gray('Logout cancelled'));
|
|
98
|
-
return;
|
|
99
|
-
}
|
|
100
|
-
try {
|
|
101
|
-
// Clear all credential files
|
|
102
|
-
for (const domain of allDomains) {
|
|
103
|
-
const domainAuth = new AuthService(domain);
|
|
104
|
-
domainAuth.clearCredentials();
|
|
105
|
-
}
|
|
106
|
-
// Clear all config
|
|
107
|
-
tenantService.clearAllConfig();
|
|
108
|
-
this.log('');
|
|
109
|
-
this.log(chalk.green('✅ Successfully logged out from all domains!'));
|
|
110
|
-
this.log(chalk.gray('Run `hd auth login` to authenticate again.'));
|
|
111
|
-
}
|
|
112
|
-
catch (error) {
|
|
113
|
-
this.error(chalk.red(`Logout failed: ${error instanceof Error ? error.message : String(error)}`));
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
}
|
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
import { Command } from '@oclif/core';
|
|
2
|
-
import chalk from 'chalk';
|
|
3
|
-
import ora from 'ora';
|
|
4
|
-
import { AuthService } from '../../services/auth-service.js';
|
|
5
|
-
export default class Refresh extends Command {
|
|
6
|
-
static description = 'Refresh expired AWS credentials using refresh token';
|
|
7
|
-
static examples = ['<%= config.bin %> <%= command.id %>'];
|
|
8
|
-
async run() {
|
|
9
|
-
const authService = new AuthService();
|
|
10
|
-
const spinner = ora('Checking current credentials...').start();
|
|
11
|
-
try {
|
|
12
|
-
// Load existing credentials
|
|
13
|
-
const credentials = authService.loadCredentials();
|
|
14
|
-
if (!credentials) {
|
|
15
|
-
spinner.fail(chalk.red('No credentials found'));
|
|
16
|
-
this.log('');
|
|
17
|
-
this.log(chalk.yellow('Please run `hd auth login` first'));
|
|
18
|
-
return;
|
|
19
|
-
}
|
|
20
|
-
// Check if cognitoConfig exists
|
|
21
|
-
if (!credentials.cognitoConfig) {
|
|
22
|
-
spinner.fail(chalk.red('Cognito configuration not found'));
|
|
23
|
-
this.log('');
|
|
24
|
-
this.log(chalk.yellow('Please run `hd auth login` again to update credentials'));
|
|
25
|
-
return;
|
|
26
|
-
}
|
|
27
|
-
// Check if refresh is needed
|
|
28
|
-
const needsRefresh = authService.needsRefresh(credentials);
|
|
29
|
-
if (!needsRefresh) {
|
|
30
|
-
spinner.succeed(chalk.green('Credentials are still valid!'));
|
|
31
|
-
const expiresIn = Math.floor((new Date(credentials.awsCredentials.expiration).getTime() - Date.now()) / 1000 / 60);
|
|
32
|
-
this.log(chalk.gray(`Expires in ${expiresIn} minutes`));
|
|
33
|
-
return;
|
|
34
|
-
}
|
|
35
|
-
// Refresh tokens
|
|
36
|
-
spinner.text = 'Refreshing tokens...';
|
|
37
|
-
const newTokens = await authService.refreshTokens(credentials.refresh_token, credentials.cognitoConfig);
|
|
38
|
-
spinner.succeed(chalk.green('Tokens refreshed!'));
|
|
39
|
-
// Get new AWS credentials
|
|
40
|
-
spinner.start('Obtaining new AWS credentials...');
|
|
41
|
-
const newAwsCredentials = await authService.getAWSCredentials(newTokens.id_token, credentials.region, credentials.cognitoConfig);
|
|
42
|
-
spinner.succeed(chalk.green('AWS credentials renewed!'));
|
|
43
|
-
// Save updated credentials (preserve tenant info)
|
|
44
|
-
spinner.start('Saving credentials...');
|
|
45
|
-
authService.saveCredentials({
|
|
46
|
-
...newTokens,
|
|
47
|
-
apiUrl: credentials.apiUrl,
|
|
48
|
-
awsCredentials: newAwsCredentials,
|
|
49
|
-
cognitoConfig: credentials.cognitoConfig,
|
|
50
|
-
obtainedAt: new Date().toISOString(),
|
|
51
|
-
region: credentials.region,
|
|
52
|
-
tenantDomain: credentials.tenantDomain,
|
|
53
|
-
tenantId: credentials.tenantId,
|
|
54
|
-
});
|
|
55
|
-
spinner.succeed(chalk.green('Credentials saved!'));
|
|
56
|
-
this.log('');
|
|
57
|
-
this.log(chalk.green('✅ Credentials successfully refreshed!'));
|
|
58
|
-
this.log(chalk.gray('Your session has been extended for another hour.'));
|
|
59
|
-
}
|
|
60
|
-
catch (error) {
|
|
61
|
-
spinner.fail(chalk.red('Refresh failed'));
|
|
62
|
-
this.log('');
|
|
63
|
-
this.error(chalk.red(`Failed to refresh credentials: ${error instanceof Error ? error.message : String(error)}\n\nPlease run 'hd auth login' to re-authenticate.`));
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
}
|
|
@@ -1,63 +0,0 @@
|
|
|
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
|
-
}
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import { Command } from '@oclif/core';
|
|
2
|
-
export default class ConfigGet extends Command {
|
|
3
|
-
static args: {
|
|
4
|
-
key: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
|
|
5
|
-
};
|
|
6
|
-
static description: string;
|
|
7
|
-
static examples: string[];
|
|
8
|
-
run(): Promise<void>;
|
|
9
|
-
}
|
|
@@ -1,37 +0,0 @@
|
|
|
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
|
-
}
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import { Command } from '@oclif/core';
|
|
2
|
-
export default class ConfigSet extends Command {
|
|
3
|
-
static args: {
|
|
4
|
-
key: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
|
|
5
|
-
value: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
|
|
6
|
-
};
|
|
7
|
-
static description: string;
|
|
8
|
-
static examples: string[];
|
|
9
|
-
run(): Promise<void>;
|
|
10
|
-
}
|
|
@@ -1,48 +0,0 @@
|
|
|
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
|
-
}
|
|
@@ -1,10 +0,0 @@
|
|
|
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
|
-
}
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import { Command } from '@oclif/core';
|
|
2
|
-
import chalk from 'chalk';
|
|
3
|
-
import { TenantService } from '../../services/tenant-service.js';
|
|
4
|
-
export default class DomainCurrent extends Command {
|
|
5
|
-
static description = 'Show the current default domain';
|
|
6
|
-
static examples = ['<%= config.bin %> <%= command.id %>'];
|
|
7
|
-
async run() {
|
|
8
|
-
const tenantService = new TenantService();
|
|
9
|
-
const defaultDomain = tenantService.getDefaultDomain();
|
|
10
|
-
if (defaultDomain) {
|
|
11
|
-
this.log(chalk.green(`Current default domain: ${chalk.cyan(defaultDomain)}`));
|
|
12
|
-
}
|
|
13
|
-
else {
|
|
14
|
-
this.log(chalk.yellow('No default domain set.'));
|
|
15
|
-
this.log(chalk.gray('Run `hd auth login` to set up a domain.'));
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
}
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
import { Command } from '@oclif/core';
|
|
2
|
-
import chalk from 'chalk';
|
|
3
|
-
import { AuthService } from '../../services/auth-service.js';
|
|
4
|
-
import { TenantService } from '../../services/tenant-service.js';
|
|
5
|
-
export default class DomainList extends Command {
|
|
6
|
-
static description = 'List all configured domains/installations';
|
|
7
|
-
static examples = ['<%= config.bin %> <%= command.id %>'];
|
|
8
|
-
async run() {
|
|
9
|
-
const tenantService = new TenantService();
|
|
10
|
-
const authService = new AuthService();
|
|
11
|
-
// Get configured domains and authenticated domains
|
|
12
|
-
const configuredDomains = tenantService.getConfiguredDomains();
|
|
13
|
-
const credentialDomains = authService.getCredentialDomains();
|
|
14
|
-
const defaultDomain = tenantService.getDefaultDomain();
|
|
15
|
-
// Merge both lists
|
|
16
|
-
const allDomains = new Set([...configuredDomains, ...credentialDomains]);
|
|
17
|
-
if (allDomains.size === 0) {
|
|
18
|
-
this.log(chalk.yellow('No domains configured yet.'));
|
|
19
|
-
this.log(chalk.gray('Run `hd auth login --domain <domain>` to add a domain.'));
|
|
20
|
-
return;
|
|
21
|
-
}
|
|
22
|
-
this.log(chalk.blue('📋 Configured Domains:'));
|
|
23
|
-
this.log('');
|
|
24
|
-
for (const domain of Array.from(allDomains).sort()) {
|
|
25
|
-
const isDefault = domain === defaultDomain;
|
|
26
|
-
const hasConfig = configuredDomains.includes(domain);
|
|
27
|
-
const hasCredentials = credentialDomains.includes(domain);
|
|
28
|
-
let status = '';
|
|
29
|
-
if (hasCredentials) {
|
|
30
|
-
status = chalk.green('✓ Authenticated');
|
|
31
|
-
}
|
|
32
|
-
else if (hasConfig) {
|
|
33
|
-
status = chalk.yellow('⚠ Not authenticated');
|
|
34
|
-
}
|
|
35
|
-
const defaultBadge = isDefault ? chalk.cyan(' [DEFAULT]') : '';
|
|
36
|
-
this.log(` ${chalk.white(domain)}${defaultBadge} ${status}`);
|
|
37
|
-
}
|
|
38
|
-
this.log('');
|
|
39
|
-
this.log(chalk.gray('💡 Use `hd domain switch <domain>` to change the default domain'));
|
|
40
|
-
this.log(chalk.gray('💡 Use `--domain <domain>` flag to run commands for a specific domain'));
|
|
41
|
-
}
|
|
42
|
-
}
|