@build-astron-co/nimbus 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +628 -0
- package/bin/nimbus +38 -0
- package/package.json +80 -0
- package/src/__tests__/app.test.ts +76 -0
- package/src/__tests__/audit.test.ts +877 -0
- package/src/__tests__/circuit-breaker.test.ts +116 -0
- package/src/__tests__/cli-run.test.ts +115 -0
- package/src/__tests__/context-manager.test.ts +502 -0
- package/src/__tests__/context.test.ts +242 -0
- package/src/__tests__/enterprise.test.ts +401 -0
- package/src/__tests__/generator.test.ts +433 -0
- package/src/__tests__/hooks.test.ts +582 -0
- package/src/__tests__/init.test.ts +436 -0
- package/src/__tests__/intent-parser.test.ts +229 -0
- package/src/__tests__/llm-router.test.ts +209 -0
- package/src/__tests__/lsp.test.ts +293 -0
- package/src/__tests__/modes.test.ts +336 -0
- package/src/__tests__/permissions.test.ts +338 -0
- package/src/__tests__/serve.test.ts +275 -0
- package/src/__tests__/sessions.test.ts +227 -0
- package/src/__tests__/sharing.test.ts +288 -0
- package/src/__tests__/snapshots.test.ts +581 -0
- package/src/__tests__/state-db.test.ts +334 -0
- package/src/__tests__/stream-with-tools.test.ts +732 -0
- package/src/__tests__/subagents.test.ts +176 -0
- package/src/__tests__/system-prompt.test.ts +169 -0
- package/src/__tests__/tool-converter.test.ts +256 -0
- package/src/__tests__/tool-schemas.test.ts +397 -0
- package/src/__tests__/tools.test.ts +143 -0
- package/src/__tests__/version.test.ts +49 -0
- package/src/agent/compaction-agent.ts +227 -0
- package/src/agent/context-manager.ts +435 -0
- package/src/agent/context.ts +427 -0
- package/src/agent/deploy-preview.ts +426 -0
- package/src/agent/index.ts +68 -0
- package/src/agent/loop.ts +717 -0
- package/src/agent/modes.ts +429 -0
- package/src/agent/permissions.ts +466 -0
- package/src/agent/subagents/base.ts +116 -0
- package/src/agent/subagents/cost.ts +51 -0
- package/src/agent/subagents/explore.ts +42 -0
- package/src/agent/subagents/general.ts +54 -0
- package/src/agent/subagents/index.ts +102 -0
- package/src/agent/subagents/infra.ts +59 -0
- package/src/agent/subagents/security.ts +69 -0
- package/src/agent/system-prompt.ts +436 -0
- package/src/app.ts +122 -0
- package/src/audit/activity-log.ts +290 -0
- package/src/audit/compliance-checker.ts +540 -0
- package/src/audit/cost-tracker.ts +318 -0
- package/src/audit/index.ts +23 -0
- package/src/audit/security-scanner.ts +596 -0
- package/src/auth/guard.ts +75 -0
- package/src/auth/index.ts +56 -0
- package/src/auth/oauth.ts +455 -0
- package/src/auth/providers.ts +470 -0
- package/src/auth/sso.ts +113 -0
- package/src/auth/store.ts +505 -0
- package/src/auth/types.ts +187 -0
- package/src/build.ts +141 -0
- package/src/cli/index.ts +16 -0
- package/src/cli/init.ts +854 -0
- package/src/cli/openapi-spec.ts +356 -0
- package/src/cli/run.ts +237 -0
- package/src/cli/serve-auth.ts +80 -0
- package/src/cli/serve.ts +462 -0
- package/src/cli/web.ts +67 -0
- package/src/cli.ts +1417 -0
- package/src/clients/core-engine-client.ts +227 -0
- package/src/clients/enterprise-client.ts +334 -0
- package/src/clients/generator-client.ts +351 -0
- package/src/clients/git-client.ts +627 -0
- package/src/clients/github-client.ts +410 -0
- package/src/clients/helm-client.ts +504 -0
- package/src/clients/index.ts +80 -0
- package/src/clients/k8s-client.ts +497 -0
- package/src/clients/llm-client.ts +161 -0
- package/src/clients/rest-client.ts +130 -0
- package/src/clients/service-discovery.ts +33 -0
- package/src/clients/terraform-client.ts +482 -0
- package/src/clients/tools-client.ts +1843 -0
- package/src/clients/ws-client.ts +115 -0
- package/src/commands/analyze/index.ts +352 -0
- package/src/commands/apply/helm.ts +473 -0
- package/src/commands/apply/index.ts +213 -0
- package/src/commands/apply/k8s.ts +454 -0
- package/src/commands/apply/terraform.ts +582 -0
- package/src/commands/ask.ts +167 -0
- package/src/commands/audit/index.ts +238 -0
- package/src/commands/auth-cloud.ts +294 -0
- package/src/commands/auth-list.ts +134 -0
- package/src/commands/auth-profile.ts +121 -0
- package/src/commands/auth-status.ts +141 -0
- package/src/commands/aws/ec2.ts +501 -0
- package/src/commands/aws/iam.ts +397 -0
- package/src/commands/aws/index.ts +133 -0
- package/src/commands/aws/lambda.ts +396 -0
- package/src/commands/aws/rds.ts +439 -0
- package/src/commands/aws/s3.ts +439 -0
- package/src/commands/aws/vpc.ts +393 -0
- package/src/commands/aws-discover.ts +649 -0
- package/src/commands/aws-terraform.ts +805 -0
- package/src/commands/azure/aks.ts +376 -0
- package/src/commands/azure/functions.ts +253 -0
- package/src/commands/azure/index.ts +116 -0
- package/src/commands/azure/storage.ts +478 -0
- package/src/commands/azure/vm.ts +355 -0
- package/src/commands/billing/index.ts +256 -0
- package/src/commands/chat.ts +314 -0
- package/src/commands/config.ts +346 -0
- package/src/commands/cost/cloud-cost-estimator.ts +266 -0
- package/src/commands/cost/estimator.ts +79 -0
- package/src/commands/cost/index.ts +594 -0
- package/src/commands/cost/parsers/terraform.ts +273 -0
- package/src/commands/cost/parsers/types.ts +25 -0
- package/src/commands/cost/pricing/aws.ts +544 -0
- package/src/commands/cost/pricing/azure.ts +499 -0
- package/src/commands/cost/pricing/gcp.ts +396 -0
- package/src/commands/cost/pricing/index.ts +40 -0
- package/src/commands/demo.ts +250 -0
- package/src/commands/doctor.ts +794 -0
- package/src/commands/drift/index.ts +439 -0
- package/src/commands/explain.ts +277 -0
- package/src/commands/feedback.ts +389 -0
- package/src/commands/fix.ts +324 -0
- package/src/commands/fs/index.ts +402 -0
- package/src/commands/gcp/compute.ts +325 -0
- package/src/commands/gcp/functions.ts +271 -0
- package/src/commands/gcp/gke.ts +438 -0
- package/src/commands/gcp/iam.ts +344 -0
- package/src/commands/gcp/index.ts +129 -0
- package/src/commands/gcp/storage.ts +284 -0
- package/src/commands/generate-helm.ts +1249 -0
- package/src/commands/generate-k8s.ts +1560 -0
- package/src/commands/generate-terraform.ts +1460 -0
- package/src/commands/gh/index.ts +863 -0
- package/src/commands/git/index.ts +1343 -0
- package/src/commands/helm/index.ts +1126 -0
- package/src/commands/help.ts +539 -0
- package/src/commands/history.ts +142 -0
- package/src/commands/import.ts +868 -0
- package/src/commands/index.ts +367 -0
- package/src/commands/init.ts +1046 -0
- package/src/commands/k8s/index.ts +1137 -0
- package/src/commands/login.ts +631 -0
- package/src/commands/logout.ts +83 -0
- package/src/commands/onboarding.ts +228 -0
- package/src/commands/plan/display.ts +279 -0
- package/src/commands/plan/index.ts +599 -0
- package/src/commands/preview.ts +452 -0
- package/src/commands/questionnaire.ts +1270 -0
- package/src/commands/resume.ts +55 -0
- package/src/commands/team/index.ts +346 -0
- package/src/commands/template.ts +232 -0
- package/src/commands/tf/index.ts +1034 -0
- package/src/commands/upgrade.ts +550 -0
- package/src/commands/usage/index.ts +134 -0
- package/src/commands/version.ts +170 -0
- package/src/compat/index.ts +2 -0
- package/src/compat/runtime.ts +12 -0
- package/src/compat/sqlite.ts +107 -0
- package/src/config/index.ts +17 -0
- package/src/config/manager.ts +530 -0
- package/src/config/safety-policy.ts +358 -0
- package/src/config/schema.ts +125 -0
- package/src/config/types.ts +527 -0
- package/src/context/context-db.ts +199 -0
- package/src/demo/index.ts +349 -0
- package/src/demo/scenarios/full-journey.ts +229 -0
- package/src/demo/scenarios/getting-started.ts +127 -0
- package/src/demo/scenarios/helm-release.ts +341 -0
- package/src/demo/scenarios/k8s-deployment.ts +194 -0
- package/src/demo/scenarios/terraform-vpc.ts +170 -0
- package/src/demo/types.ts +92 -0
- package/src/engine/cost-estimator.ts +438 -0
- package/src/engine/diagram-generator.ts +256 -0
- package/src/engine/drift-detector.ts +902 -0
- package/src/engine/executor.ts +1035 -0
- package/src/engine/index.ts +76 -0
- package/src/engine/orchestrator.ts +636 -0
- package/src/engine/planner.ts +720 -0
- package/src/engine/safety.ts +743 -0
- package/src/engine/verifier.ts +770 -0
- package/src/enterprise/audit.ts +348 -0
- package/src/enterprise/auth.ts +270 -0
- package/src/enterprise/billing.ts +822 -0
- package/src/enterprise/index.ts +17 -0
- package/src/enterprise/teams.ts +443 -0
- package/src/generator/best-practices.ts +1608 -0
- package/src/generator/helm.ts +630 -0
- package/src/generator/index.ts +37 -0
- package/src/generator/intent-parser.ts +514 -0
- package/src/generator/kubernetes.ts +976 -0
- package/src/generator/terraform.ts +1867 -0
- package/src/history/index.ts +8 -0
- package/src/history/manager.ts +322 -0
- package/src/history/types.ts +34 -0
- package/src/hooks/config.ts +432 -0
- package/src/hooks/engine.ts +391 -0
- package/src/hooks/index.ts +4 -0
- package/src/llm/auth-bridge.ts +198 -0
- package/src/llm/circuit-breaker.ts +140 -0
- package/src/llm/config-loader.ts +201 -0
- package/src/llm/cost-calculator.ts +171 -0
- package/src/llm/index.ts +8 -0
- package/src/llm/model-aliases.ts +115 -0
- package/src/llm/provider-registry.ts +63 -0
- package/src/llm/providers/anthropic.ts +433 -0
- package/src/llm/providers/bedrock.ts +477 -0
- package/src/llm/providers/google.ts +405 -0
- package/src/llm/providers/ollama.ts +767 -0
- package/src/llm/providers/openai-compatible.ts +340 -0
- package/src/llm/providers/openai.ts +328 -0
- package/src/llm/providers/openrouter.ts +338 -0
- package/src/llm/router.ts +1035 -0
- package/src/llm/types.ts +232 -0
- package/src/lsp/client.ts +298 -0
- package/src/lsp/languages.ts +116 -0
- package/src/lsp/manager.ts +278 -0
- package/src/mcp/client.ts +402 -0
- package/src/mcp/index.ts +5 -0
- package/src/mcp/manager.ts +133 -0
- package/src/nimbus.ts +214 -0
- package/src/plugins/index.ts +27 -0
- package/src/plugins/loader.ts +334 -0
- package/src/plugins/manager.ts +376 -0
- package/src/plugins/types.ts +284 -0
- package/src/scanners/cicd-scanner.ts +258 -0
- package/src/scanners/cloud-scanner.ts +466 -0
- package/src/scanners/framework-scanner.ts +469 -0
- package/src/scanners/iac-scanner.ts +388 -0
- package/src/scanners/index.ts +539 -0
- package/src/scanners/language-scanner.ts +276 -0
- package/src/scanners/package-manager-scanner.ts +277 -0
- package/src/scanners/types.ts +172 -0
- package/src/sessions/manager.ts +365 -0
- package/src/sessions/types.ts +44 -0
- package/src/sharing/sync.ts +296 -0
- package/src/sharing/viewer.ts +97 -0
- package/src/snapshots/index.ts +2 -0
- package/src/snapshots/manager.ts +530 -0
- package/src/state/artifacts.ts +147 -0
- package/src/state/audit.ts +137 -0
- package/src/state/billing.ts +240 -0
- package/src/state/checkpoints.ts +117 -0
- package/src/state/config.ts +67 -0
- package/src/state/conversations.ts +14 -0
- package/src/state/credentials.ts +154 -0
- package/src/state/db.ts +58 -0
- package/src/state/index.ts +26 -0
- package/src/state/messages.ts +115 -0
- package/src/state/projects.ts +123 -0
- package/src/state/schema.ts +236 -0
- package/src/state/sessions.ts +147 -0
- package/src/state/teams.ts +200 -0
- package/src/telemetry.ts +108 -0
- package/src/tools/aws-ops.ts +952 -0
- package/src/tools/azure-ops.ts +579 -0
- package/src/tools/file-ops.ts +593 -0
- package/src/tools/gcp-ops.ts +625 -0
- package/src/tools/git-ops.ts +773 -0
- package/src/tools/github-ops.ts +799 -0
- package/src/tools/helm-ops.ts +943 -0
- package/src/tools/index.ts +17 -0
- package/src/tools/k8s-ops.ts +819 -0
- package/src/tools/schemas/converter.ts +184 -0
- package/src/tools/schemas/devops.ts +612 -0
- package/src/tools/schemas/index.ts +73 -0
- package/src/tools/schemas/standard.ts +1144 -0
- package/src/tools/schemas/types.ts +705 -0
- package/src/tools/terraform-ops.ts +862 -0
- package/src/types/ambient.d.ts +193 -0
- package/src/types/config.ts +83 -0
- package/src/types/drift.ts +116 -0
- package/src/types/enterprise.ts +335 -0
- package/src/types/index.ts +20 -0
- package/src/types/plan.ts +44 -0
- package/src/types/request.ts +65 -0
- package/src/types/response.ts +54 -0
- package/src/types/service.ts +51 -0
- package/src/ui/App.tsx +997 -0
- package/src/ui/DeployPreview.tsx +169 -0
- package/src/ui/Header.tsx +68 -0
- package/src/ui/InputBox.tsx +350 -0
- package/src/ui/MessageList.tsx +585 -0
- package/src/ui/PermissionPrompt.tsx +151 -0
- package/src/ui/StatusBar.tsx +158 -0
- package/src/ui/ToolCallDisplay.tsx +409 -0
- package/src/ui/chat-ui.ts +853 -0
- package/src/ui/index.ts +33 -0
- package/src/ui/ink/index.ts +711 -0
- package/src/ui/streaming.ts +176 -0
- package/src/ui/types.ts +57 -0
- package/src/utils/analytics.ts +72 -0
- package/src/utils/cost-warning.ts +27 -0
- package/src/utils/env.ts +46 -0
- package/src/utils/errors.ts +69 -0
- package/src/utils/event-bus.ts +38 -0
- package/src/utils/index.ts +24 -0
- package/src/utils/logger.ts +171 -0
- package/src/utils/rate-limiter.ts +121 -0
- package/src/utils/service-auth.ts +49 -0
- package/src/utils/validation.ts +53 -0
- package/src/version.ts +4 -0
- package/src/watcher/index.ts +163 -0
- package/src/wizard/approval.ts +383 -0
- package/src/wizard/index.ts +25 -0
- package/src/wizard/prompts.ts +338 -0
- package/src/wizard/types.ts +171 -0
- package/src/wizard/ui.ts +556 -0
- package/src/wizard/wizard.ts +304 -0
- package/tsconfig.json +24 -0
|
@@ -0,0 +1,344 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GCP IAM CLI Commands
|
|
3
|
+
*
|
|
4
|
+
* Operations for IAM service accounts and roles
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { execFile } from 'child_process';
|
|
8
|
+
import { promisify } from 'util';
|
|
9
|
+
import { logger } from '../../utils';
|
|
10
|
+
import { ui } from '../../wizard/ui';
|
|
11
|
+
import type { GcpCommandOptions } from './index';
|
|
12
|
+
|
|
13
|
+
const execFileAsync = promisify(execFile);
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Main IAM command router
|
|
17
|
+
*/
|
|
18
|
+
export async function iamCommand(
|
|
19
|
+
action: string,
|
|
20
|
+
args: string[],
|
|
21
|
+
options: GcpCommandOptions
|
|
22
|
+
): Promise<void> {
|
|
23
|
+
logger.info('Running GCP IAM command', { action, args, options });
|
|
24
|
+
|
|
25
|
+
switch (action) {
|
|
26
|
+
case 'service-accounts':
|
|
27
|
+
case 'sa':
|
|
28
|
+
await listServiceAccounts(options);
|
|
29
|
+
break;
|
|
30
|
+
|
|
31
|
+
case 'describe-sa':
|
|
32
|
+
if (!args[0]) {
|
|
33
|
+
ui.error('Service account email required');
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
await describeServiceAccount(args[0], options);
|
|
37
|
+
break;
|
|
38
|
+
|
|
39
|
+
case 'roles':
|
|
40
|
+
await listRoles(options);
|
|
41
|
+
break;
|
|
42
|
+
|
|
43
|
+
case 'describe-role':
|
|
44
|
+
if (!args[0]) {
|
|
45
|
+
ui.error('Role name required');
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
await describeRole(args[0], options);
|
|
49
|
+
break;
|
|
50
|
+
|
|
51
|
+
case 'bindings':
|
|
52
|
+
await listBindings(options);
|
|
53
|
+
break;
|
|
54
|
+
|
|
55
|
+
default:
|
|
56
|
+
showIamHelp();
|
|
57
|
+
break;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* List service accounts
|
|
63
|
+
*/
|
|
64
|
+
async function listServiceAccounts(options: GcpCommandOptions): Promise<void> {
|
|
65
|
+
ui.header('Service Accounts');
|
|
66
|
+
ui.newLine();
|
|
67
|
+
|
|
68
|
+
const gcloudArgs = ['iam', 'service-accounts', 'list', '--format=json'];
|
|
69
|
+
if (options.project) {
|
|
70
|
+
gcloudArgs.push(`--project=${options.project}`);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
try {
|
|
74
|
+
ui.startSpinner({ message: 'Fetching service accounts...' });
|
|
75
|
+
const { stdout } = await execFileAsync('gcloud', gcloudArgs);
|
|
76
|
+
ui.stopSpinnerSuccess('Service accounts fetched');
|
|
77
|
+
|
|
78
|
+
const accounts = JSON.parse(stdout || '[]');
|
|
79
|
+
|
|
80
|
+
if (accounts.length === 0) {
|
|
81
|
+
ui.info('No service accounts found');
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
ui.print(`Found ${accounts.length} service account(s)\n`);
|
|
86
|
+
|
|
87
|
+
// Display table
|
|
88
|
+
ui.print(ui.color(`${'Display Name'.padEnd(30) + 'Email'.padEnd(50)}Disabled`, 'cyan'));
|
|
89
|
+
ui.print('─'.repeat(90));
|
|
90
|
+
|
|
91
|
+
for (const account of accounts) {
|
|
92
|
+
const displayName = account.displayName?.substring(0, 29) || '(no name)';
|
|
93
|
+
const email = account.email?.substring(0, 49) || '';
|
|
94
|
+
const disabled = account.disabled ? 'Yes' : 'No';
|
|
95
|
+
|
|
96
|
+
ui.print(`${displayName.padEnd(30)}${email.padEnd(50)}${disabled}`);
|
|
97
|
+
}
|
|
98
|
+
} catch (error: unknown) {
|
|
99
|
+
ui.stopSpinnerFail('Failed to fetch service accounts');
|
|
100
|
+
const message = error instanceof Error ? error.message : 'Unknown error';
|
|
101
|
+
logger.error('Failed to list service accounts', { error: message });
|
|
102
|
+
ui.error(`Failed to list service accounts: ${message}`);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Describe a specific service account
|
|
108
|
+
*/
|
|
109
|
+
async function describeServiceAccount(email: string, options: GcpCommandOptions): Promise<void> {
|
|
110
|
+
ui.header(`Service Account: ${email}`);
|
|
111
|
+
ui.newLine();
|
|
112
|
+
|
|
113
|
+
const gcloudArgs = ['iam', 'service-accounts', 'describe', email, '--format=json'];
|
|
114
|
+
if (options.project) {
|
|
115
|
+
gcloudArgs.push(`--project=${options.project}`);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
try {
|
|
119
|
+
ui.startSpinner({ message: 'Fetching service account details...' });
|
|
120
|
+
const { stdout } = await execFileAsync('gcloud', gcloudArgs);
|
|
121
|
+
ui.stopSpinnerSuccess('Details fetched');
|
|
122
|
+
|
|
123
|
+
const account = JSON.parse(stdout);
|
|
124
|
+
|
|
125
|
+
ui.print(ui.bold('Basic Information:'));
|
|
126
|
+
ui.print(` Display Name: ${account.displayName || '(none)'}`);
|
|
127
|
+
ui.print(` Email: ${account.email}`);
|
|
128
|
+
ui.print(` Unique ID: ${account.uniqueId}`);
|
|
129
|
+
ui.print(` Disabled: ${account.disabled ? 'Yes' : 'No'}`);
|
|
130
|
+
ui.print(` Description: ${account.description || '(none)'}`);
|
|
131
|
+
ui.newLine();
|
|
132
|
+
|
|
133
|
+
// Get keys
|
|
134
|
+
try {
|
|
135
|
+
const keysArgs = [
|
|
136
|
+
'iam',
|
|
137
|
+
'service-accounts',
|
|
138
|
+
'keys',
|
|
139
|
+
'list',
|
|
140
|
+
'--iam-account',
|
|
141
|
+
email,
|
|
142
|
+
'--format=json',
|
|
143
|
+
];
|
|
144
|
+
if (options.project) {
|
|
145
|
+
keysArgs.push(`--project=${options.project}`);
|
|
146
|
+
}
|
|
147
|
+
const { stdout: keysOutput } = await execFileAsync('gcloud', keysArgs);
|
|
148
|
+
const keys = JSON.parse(keysOutput || '[]');
|
|
149
|
+
|
|
150
|
+
ui.print(ui.bold('Keys:'));
|
|
151
|
+
if (keys.length === 0) {
|
|
152
|
+
ui.print(' No keys found');
|
|
153
|
+
} else {
|
|
154
|
+
for (const key of keys) {
|
|
155
|
+
const keyId = key.name?.split('/').pop() || '';
|
|
156
|
+
const keyType = key.keyType || '';
|
|
157
|
+
const validAfter = key.validAfterTime || '';
|
|
158
|
+
const validBefore = key.validBeforeTime || '';
|
|
159
|
+
|
|
160
|
+
ui.print(` Key ID: ${keyId}`);
|
|
161
|
+
ui.print(` Type: ${keyType}`);
|
|
162
|
+
ui.print(` Valid After: ${validAfter}`);
|
|
163
|
+
ui.print(` Valid Before: ${validBefore}`);
|
|
164
|
+
ui.newLine();
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
} catch {
|
|
168
|
+
ui.print(ui.bold('Keys:'));
|
|
169
|
+
ui.print(' Unable to fetch keys');
|
|
170
|
+
}
|
|
171
|
+
} catch (error: unknown) {
|
|
172
|
+
ui.stopSpinnerFail('Failed to fetch details');
|
|
173
|
+
const message = error instanceof Error ? error.message : 'Unknown error';
|
|
174
|
+
logger.error('Failed to describe service account', { error: message });
|
|
175
|
+
ui.error(`Failed to describe service account: ${message}`);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* List predefined roles
|
|
181
|
+
*/
|
|
182
|
+
async function listRoles(options: GcpCommandOptions): Promise<void> {
|
|
183
|
+
ui.header('IAM Roles');
|
|
184
|
+
ui.newLine();
|
|
185
|
+
|
|
186
|
+
// List project-level custom roles
|
|
187
|
+
const gcloudArgs = ['iam', 'roles', 'list', '--format=json'];
|
|
188
|
+
if (options.project) {
|
|
189
|
+
gcloudArgs.push(`--project=${options.project}`);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
try {
|
|
193
|
+
ui.startSpinner({ message: 'Fetching roles...' });
|
|
194
|
+
const { stdout } = await execFileAsync('gcloud', gcloudArgs);
|
|
195
|
+
ui.stopSpinnerSuccess('Roles fetched');
|
|
196
|
+
|
|
197
|
+
const roles = JSON.parse(stdout || '[]');
|
|
198
|
+
|
|
199
|
+
if (roles.length === 0) {
|
|
200
|
+
ui.info(
|
|
201
|
+
'No custom roles found. Use gcloud iam roles list --filter="stage=GA" to see predefined roles.'
|
|
202
|
+
);
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
ui.print(`Found ${roles.length} custom role(s)\n`);
|
|
207
|
+
|
|
208
|
+
// Display table
|
|
209
|
+
ui.print(ui.color(`${'Name'.padEnd(40) + 'Title'.padEnd(35)}Stage`, 'cyan'));
|
|
210
|
+
ui.print('─'.repeat(85));
|
|
211
|
+
|
|
212
|
+
for (const role of roles) {
|
|
213
|
+
const name = role.name?.split('/').pop()?.substring(0, 39) || '';
|
|
214
|
+
const title = role.title?.substring(0, 34) || '';
|
|
215
|
+
const stage = role.stage || '';
|
|
216
|
+
|
|
217
|
+
ui.print(`${name.padEnd(40)}${title.padEnd(35)}${stage}`);
|
|
218
|
+
}
|
|
219
|
+
} catch (error: unknown) {
|
|
220
|
+
ui.stopSpinnerFail('Failed to fetch roles');
|
|
221
|
+
const message = error instanceof Error ? error.message : 'Unknown error';
|
|
222
|
+
logger.error('Failed to list roles', { error: message });
|
|
223
|
+
ui.error(`Failed to list roles: ${message}`);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* Describe a specific role
|
|
229
|
+
*/
|
|
230
|
+
async function describeRole(roleName: string, options: GcpCommandOptions): Promise<void> {
|
|
231
|
+
ui.header(`Role: ${roleName}`);
|
|
232
|
+
ui.newLine();
|
|
233
|
+
|
|
234
|
+
const gcloudArgs = ['iam', 'roles', 'describe', roleName, '--format=json'];
|
|
235
|
+
if (options.project && !roleName.startsWith('roles/')) {
|
|
236
|
+
gcloudArgs.push(`--project=${options.project}`);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
try {
|
|
240
|
+
ui.startSpinner({ message: 'Fetching role details...' });
|
|
241
|
+
const { stdout } = await execFileAsync('gcloud', gcloudArgs);
|
|
242
|
+
ui.stopSpinnerSuccess('Details fetched');
|
|
243
|
+
|
|
244
|
+
const role = JSON.parse(stdout);
|
|
245
|
+
|
|
246
|
+
ui.print(ui.bold('Basic Information:'));
|
|
247
|
+
ui.print(` Name: ${role.name}`);
|
|
248
|
+
ui.print(` Title: ${role.title}`);
|
|
249
|
+
ui.print(` Description: ${role.description || '(none)'}`);
|
|
250
|
+
ui.print(` Stage: ${role.stage}`);
|
|
251
|
+
ui.print(` ETag: ${role.etag}`);
|
|
252
|
+
ui.newLine();
|
|
253
|
+
|
|
254
|
+
ui.print(ui.bold('Permissions:'));
|
|
255
|
+
const permissions = role.includedPermissions || [];
|
|
256
|
+
if (permissions.length === 0) {
|
|
257
|
+
ui.print(' No permissions');
|
|
258
|
+
} else {
|
|
259
|
+
ui.print(` Total: ${permissions.length} permission(s)`);
|
|
260
|
+
// Show first 20 permissions
|
|
261
|
+
const displayPerms = permissions.slice(0, 20);
|
|
262
|
+
for (const perm of displayPerms) {
|
|
263
|
+
ui.print(` ${perm}`);
|
|
264
|
+
}
|
|
265
|
+
if (permissions.length > 20) {
|
|
266
|
+
ui.print(ui.dim(` ... and ${permissions.length - 20} more`));
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
} catch (error: unknown) {
|
|
270
|
+
ui.stopSpinnerFail('Failed to fetch details');
|
|
271
|
+
const message = error instanceof Error ? error.message : 'Unknown error';
|
|
272
|
+
logger.error('Failed to describe role', { error: message });
|
|
273
|
+
ui.error(`Failed to describe role: ${message}`);
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* List IAM bindings for the project
|
|
279
|
+
*/
|
|
280
|
+
async function listBindings(options: GcpCommandOptions): Promise<void> {
|
|
281
|
+
ui.header('IAM Policy Bindings');
|
|
282
|
+
ui.newLine();
|
|
283
|
+
|
|
284
|
+
let projectId = options.project || '';
|
|
285
|
+
|
|
286
|
+
if (!projectId) {
|
|
287
|
+
// Get current project
|
|
288
|
+
try {
|
|
289
|
+
const { stdout: projectOut } = await execFileAsync('gcloud', [
|
|
290
|
+
'config',
|
|
291
|
+
'get-value',
|
|
292
|
+
'project',
|
|
293
|
+
]);
|
|
294
|
+
projectId = projectOut.trim();
|
|
295
|
+
} catch {
|
|
296
|
+
ui.error('Project not specified and no default project configured');
|
|
297
|
+
return;
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
const gcloudArgs = ['projects', 'get-iam-policy', projectId, '--format=json'];
|
|
302
|
+
|
|
303
|
+
try {
|
|
304
|
+
ui.startSpinner({ message: 'Fetching IAM policy...' });
|
|
305
|
+
const { stdout } = await execFileAsync('gcloud', gcloudArgs);
|
|
306
|
+
ui.stopSpinnerSuccess('Policy fetched');
|
|
307
|
+
|
|
308
|
+
const policy = JSON.parse(stdout);
|
|
309
|
+
const bindings = policy.bindings || [];
|
|
310
|
+
|
|
311
|
+
if (bindings.length === 0) {
|
|
312
|
+
ui.info('No IAM bindings found');
|
|
313
|
+
return;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
ui.print(`Found ${bindings.length} binding(s)\n`);
|
|
317
|
+
|
|
318
|
+
for (const binding of bindings) {
|
|
319
|
+
ui.print(ui.bold(`Role: ${binding.role}`));
|
|
320
|
+
ui.print(' Members:');
|
|
321
|
+
for (const member of binding.members || []) {
|
|
322
|
+
ui.print(` - ${member}`);
|
|
323
|
+
}
|
|
324
|
+
ui.newLine();
|
|
325
|
+
}
|
|
326
|
+
} catch (error: unknown) {
|
|
327
|
+
ui.stopSpinnerFail('Failed to fetch policy');
|
|
328
|
+
const message = error instanceof Error ? error.message : 'Unknown error';
|
|
329
|
+
logger.error('Failed to get IAM policy', { error: message });
|
|
330
|
+
ui.error(`Failed to get IAM policy: ${message}`);
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
/**
|
|
335
|
+
* Show IAM help
|
|
336
|
+
*/
|
|
337
|
+
function showIamHelp(): void {
|
|
338
|
+
ui.print(ui.bold('IAM Commands:'));
|
|
339
|
+
ui.print(' service-accounts List all service accounts');
|
|
340
|
+
ui.print(' describe-sa <email> Show service account details');
|
|
341
|
+
ui.print(' roles List custom roles');
|
|
342
|
+
ui.print(' describe-role <name> Show role details');
|
|
343
|
+
ui.print(' bindings Show project IAM policy bindings');
|
|
344
|
+
}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GCP CLI Commands
|
|
3
|
+
*
|
|
4
|
+
* Wrapper for Google Cloud CLI operations with enhanced output and safety checks
|
|
5
|
+
*
|
|
6
|
+
* Usage:
|
|
7
|
+
* nimbus gcp compute list
|
|
8
|
+
* nimbus gcp storage ls
|
|
9
|
+
* nimbus gcp gke clusters
|
|
10
|
+
* nimbus gcp functions list
|
|
11
|
+
* nimbus gcp iam service-accounts
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { logger } from '../../utils';
|
|
15
|
+
import { ui } from '../../wizard/ui';
|
|
16
|
+
import { computeCommand } from './compute';
|
|
17
|
+
import { storageCommand } from './storage';
|
|
18
|
+
import { gkeCommand } from './gke';
|
|
19
|
+
import { functionsCommand } from './functions';
|
|
20
|
+
import { iamCommand } from './iam';
|
|
21
|
+
|
|
22
|
+
export interface GcpCommandOptions {
|
|
23
|
+
project?: string;
|
|
24
|
+
region?: string;
|
|
25
|
+
zone?: string;
|
|
26
|
+
format?: 'json' | 'table' | 'text';
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Parse common GCP options from args
|
|
31
|
+
*/
|
|
32
|
+
export function parseGcpOptions(args: string[]): GcpCommandOptions {
|
|
33
|
+
const options: GcpCommandOptions = {};
|
|
34
|
+
|
|
35
|
+
for (let i = 0; i < args.length; i++) {
|
|
36
|
+
const arg = args[i];
|
|
37
|
+
|
|
38
|
+
if ((arg === '--project' || arg === '-p') && args[i + 1]) {
|
|
39
|
+
options.project = args[++i];
|
|
40
|
+
} else if ((arg === '--region' || arg === '-r') && args[i + 1]) {
|
|
41
|
+
options.region = args[++i];
|
|
42
|
+
} else if ((arg === '--zone' || arg === '-z') && args[i + 1]) {
|
|
43
|
+
options.zone = args[++i];
|
|
44
|
+
} else if ((arg === '--format' || arg === '-f') && args[i + 1]) {
|
|
45
|
+
options.format = args[++i] as 'json' | 'table' | 'text';
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return options;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Main GCP command router
|
|
54
|
+
*/
|
|
55
|
+
export async function gcpCommand(subcommand: string, args: string[]): Promise<void> {
|
|
56
|
+
logger.info('Running GCP command', { subcommand, args });
|
|
57
|
+
|
|
58
|
+
const options = parseGcpOptions(args);
|
|
59
|
+
const positionalArgs = args.filter(arg => !arg.startsWith('-') && !arg.startsWith('--'));
|
|
60
|
+
|
|
61
|
+
switch (subcommand) {
|
|
62
|
+
case 'compute':
|
|
63
|
+
await computeCommand(positionalArgs[0], positionalArgs.slice(1), options);
|
|
64
|
+
break;
|
|
65
|
+
|
|
66
|
+
case 'storage':
|
|
67
|
+
await storageCommand(positionalArgs[0], positionalArgs.slice(1), options);
|
|
68
|
+
break;
|
|
69
|
+
|
|
70
|
+
case 'gke':
|
|
71
|
+
await gkeCommand(positionalArgs[0], positionalArgs.slice(1), options);
|
|
72
|
+
break;
|
|
73
|
+
|
|
74
|
+
case 'functions':
|
|
75
|
+
await functionsCommand(positionalArgs[0], positionalArgs.slice(1), options);
|
|
76
|
+
break;
|
|
77
|
+
|
|
78
|
+
case 'iam':
|
|
79
|
+
await iamCommand(positionalArgs[0], positionalArgs.slice(1), options);
|
|
80
|
+
break;
|
|
81
|
+
|
|
82
|
+
default:
|
|
83
|
+
showGcpHelp();
|
|
84
|
+
break;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Show GCP command help
|
|
90
|
+
*/
|
|
91
|
+
function showGcpHelp(): void {
|
|
92
|
+
ui.header('Nimbus GCP Commands');
|
|
93
|
+
ui.newLine();
|
|
94
|
+
|
|
95
|
+
ui.print('Usage: nimbus gcp <service> <action> [options]');
|
|
96
|
+
ui.newLine();
|
|
97
|
+
|
|
98
|
+
ui.print(ui.bold('Services:'));
|
|
99
|
+
ui.print(' compute Compute Engine operations');
|
|
100
|
+
ui.print(' storage Cloud Storage operations');
|
|
101
|
+
ui.print(' gke Google Kubernetes Engine operations');
|
|
102
|
+
ui.print(' functions Cloud Functions operations');
|
|
103
|
+
ui.print(' iam IAM service accounts and roles');
|
|
104
|
+
ui.newLine();
|
|
105
|
+
|
|
106
|
+
ui.print(ui.bold('Common Options:'));
|
|
107
|
+
ui.print(' --project, -p GCP project ID');
|
|
108
|
+
ui.print(' --region, -r GCP region');
|
|
109
|
+
ui.print(' --zone, -z GCP zone');
|
|
110
|
+
ui.print(' --format, -f Output format (json, table, text)');
|
|
111
|
+
ui.newLine();
|
|
112
|
+
|
|
113
|
+
ui.print(ui.bold('Examples:'));
|
|
114
|
+
ui.print(' nimbus gcp compute list List all Compute Engine instances');
|
|
115
|
+
ui.print(' nimbus gcp compute describe my-instance Describe specific instance');
|
|
116
|
+
ui.print(' nimbus gcp storage ls List all Cloud Storage buckets');
|
|
117
|
+
ui.print(' nimbus gcp storage ls gs://my-bucket List objects in bucket');
|
|
118
|
+
ui.print(' nimbus gcp gke clusters List GKE clusters');
|
|
119
|
+
ui.print(' nimbus gcp functions list List Cloud Functions');
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Re-export subcommands
|
|
123
|
+
export { computeCommand } from './compute';
|
|
124
|
+
export { storageCommand } from './storage';
|
|
125
|
+
export { gkeCommand } from './gke';
|
|
126
|
+
export { functionsCommand } from './functions';
|
|
127
|
+
export { iamCommand } from './iam';
|
|
128
|
+
|
|
129
|
+
export default gcpCommand;
|