@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,325 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GCP Compute Engine CLI Commands
|
|
3
|
+
*
|
|
4
|
+
* Operations for Compute Engine instances
|
|
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 {
|
|
12
|
+
loadSafetyPolicy,
|
|
13
|
+
evaluateSafety,
|
|
14
|
+
type SafetyContext,
|
|
15
|
+
type SafetyCheckResult,
|
|
16
|
+
} from '../../config/safety-policy';
|
|
17
|
+
import { promptForApproval, displaySafetySummary } from '../../wizard/approval';
|
|
18
|
+
import type { GcpCommandOptions } from './index';
|
|
19
|
+
|
|
20
|
+
const execFileAsync = promisify(execFile);
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Run Compute Engine safety checks
|
|
24
|
+
*/
|
|
25
|
+
async function runComputeSafetyChecks(
|
|
26
|
+
action: string,
|
|
27
|
+
instanceId: string,
|
|
28
|
+
options: GcpCommandOptions
|
|
29
|
+
): Promise<SafetyCheckResult> {
|
|
30
|
+
const safetyPolicy = loadSafetyPolicy();
|
|
31
|
+
|
|
32
|
+
const context: SafetyContext = {
|
|
33
|
+
operation: action,
|
|
34
|
+
type: 'gcp',
|
|
35
|
+
environment: options.project || 'default',
|
|
36
|
+
resources: [instanceId],
|
|
37
|
+
metadata: {
|
|
38
|
+
resourceType: 'compute-instance',
|
|
39
|
+
resourceId: instanceId,
|
|
40
|
+
},
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
return evaluateSafety(context, safetyPolicy);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Main Compute Engine command router
|
|
48
|
+
*/
|
|
49
|
+
export async function computeCommand(
|
|
50
|
+
action: string,
|
|
51
|
+
args: string[],
|
|
52
|
+
options: GcpCommandOptions
|
|
53
|
+
): Promise<void> {
|
|
54
|
+
logger.info('Running GCP Compute command', { action, args, options });
|
|
55
|
+
|
|
56
|
+
switch (action) {
|
|
57
|
+
case 'list':
|
|
58
|
+
await listInstances(options);
|
|
59
|
+
break;
|
|
60
|
+
|
|
61
|
+
case 'describe':
|
|
62
|
+
if (!args[0]) {
|
|
63
|
+
ui.error('Instance name required');
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
await describeInstance(args[0], options);
|
|
67
|
+
break;
|
|
68
|
+
|
|
69
|
+
case 'start':
|
|
70
|
+
if (!args[0]) {
|
|
71
|
+
ui.error('Instance name required');
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
await startInstance(args[0], options);
|
|
75
|
+
break;
|
|
76
|
+
|
|
77
|
+
case 'stop':
|
|
78
|
+
if (!args[0]) {
|
|
79
|
+
ui.error('Instance name required');
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
await stopInstance(args[0], options);
|
|
83
|
+
break;
|
|
84
|
+
|
|
85
|
+
case 'delete':
|
|
86
|
+
if (!args[0]) {
|
|
87
|
+
ui.error('Instance name required');
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
await deleteInstance(args[0], options);
|
|
91
|
+
break;
|
|
92
|
+
|
|
93
|
+
default:
|
|
94
|
+
showComputeHelp();
|
|
95
|
+
break;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* List Compute Engine instances
|
|
101
|
+
*/
|
|
102
|
+
async function listInstances(options: GcpCommandOptions): Promise<void> {
|
|
103
|
+
ui.header('Compute Engine Instances');
|
|
104
|
+
ui.newLine();
|
|
105
|
+
|
|
106
|
+
const gcloudArgs = ['compute', 'instances', 'list', '--format=json'];
|
|
107
|
+
if (options.project) {
|
|
108
|
+
gcloudArgs.push(`--project=${options.project}`);
|
|
109
|
+
}
|
|
110
|
+
if (options.zone) {
|
|
111
|
+
gcloudArgs.push(`--zones=${options.zone}`);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
try {
|
|
115
|
+
ui.startSpinner({ message: 'Fetching instances...' });
|
|
116
|
+
const { stdout } = await execFileAsync('gcloud', gcloudArgs);
|
|
117
|
+
ui.stopSpinnerSuccess('Instances fetched');
|
|
118
|
+
|
|
119
|
+
const instances = JSON.parse(stdout || '[]');
|
|
120
|
+
|
|
121
|
+
if (instances.length === 0) {
|
|
122
|
+
ui.info('No instances found');
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
ui.print(`Found ${instances.length} instance(s)\n`);
|
|
127
|
+
|
|
128
|
+
// Display table
|
|
129
|
+
ui.print(
|
|
130
|
+
ui.color(
|
|
131
|
+
`${
|
|
132
|
+
'Name'.padEnd(25) + 'Zone'.padEnd(25) + 'Machine Type'.padEnd(20) + 'Status'.padEnd(12)
|
|
133
|
+
}External IP`,
|
|
134
|
+
'cyan'
|
|
135
|
+
)
|
|
136
|
+
);
|
|
137
|
+
ui.print('─'.repeat(100));
|
|
138
|
+
|
|
139
|
+
for (const instance of instances) {
|
|
140
|
+
const name = instance.name?.substring(0, 24) || '';
|
|
141
|
+
const zone = instance.zone?.split('/').pop()?.substring(0, 24) || '';
|
|
142
|
+
const machineType = instance.machineType?.split('/').pop()?.substring(0, 19) || '';
|
|
143
|
+
const status = instance.status || '';
|
|
144
|
+
const externalIp = instance.networkInterfaces?.[0]?.accessConfigs?.[0]?.natIP || 'None';
|
|
145
|
+
|
|
146
|
+
const statusColor =
|
|
147
|
+
status === 'RUNNING'
|
|
148
|
+
? 'green'
|
|
149
|
+
: status === 'TERMINATED'
|
|
150
|
+
? 'red'
|
|
151
|
+
: status === 'STOPPING'
|
|
152
|
+
? 'yellow'
|
|
153
|
+
: 'white';
|
|
154
|
+
|
|
155
|
+
ui.print(
|
|
156
|
+
`${name.padEnd(25)}${zone.padEnd(25)}${machineType.padEnd(20)}${ui.color(status.padEnd(12), statusColor as 'green' | 'red' | 'yellow' | 'white')}${externalIp}`
|
|
157
|
+
);
|
|
158
|
+
}
|
|
159
|
+
} catch (error: unknown) {
|
|
160
|
+
ui.stopSpinnerFail('Failed to fetch instances');
|
|
161
|
+
const message = error instanceof Error ? error.message : 'Unknown error';
|
|
162
|
+
logger.error('Failed to list instances', { error: message });
|
|
163
|
+
ui.error(`Failed to list instances: ${message}`);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Describe a specific instance
|
|
169
|
+
*/
|
|
170
|
+
async function describeInstance(instanceName: string, options: GcpCommandOptions): Promise<void> {
|
|
171
|
+
ui.header(`Instance: ${instanceName}`);
|
|
172
|
+
ui.newLine();
|
|
173
|
+
|
|
174
|
+
const gcloudArgs = ['compute', 'instances', 'describe', instanceName, '--format=json'];
|
|
175
|
+
if (options.project) {
|
|
176
|
+
gcloudArgs.push(`--project=${options.project}`);
|
|
177
|
+
}
|
|
178
|
+
if (options.zone) {
|
|
179
|
+
gcloudArgs.push(`--zone=${options.zone}`);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
try {
|
|
183
|
+
ui.startSpinner({ message: 'Fetching instance details...' });
|
|
184
|
+
const { stdout } = await execFileAsync('gcloud', gcloudArgs);
|
|
185
|
+
ui.stopSpinnerSuccess('Details fetched');
|
|
186
|
+
|
|
187
|
+
const instance = JSON.parse(stdout);
|
|
188
|
+
|
|
189
|
+
ui.print(ui.bold('Basic Information:'));
|
|
190
|
+
ui.print(` Name: ${instance.name}`);
|
|
191
|
+
ui.print(` Zone: ${instance.zone?.split('/').pop()}`);
|
|
192
|
+
ui.print(` Machine Type: ${instance.machineType?.split('/').pop()}`);
|
|
193
|
+
ui.print(` Status: ${instance.status}`);
|
|
194
|
+
ui.print(` Created: ${instance.creationTimestamp}`);
|
|
195
|
+
ui.newLine();
|
|
196
|
+
|
|
197
|
+
ui.print(ui.bold('Network:'));
|
|
198
|
+
for (const nic of instance.networkInterfaces || []) {
|
|
199
|
+
ui.print(` Network: ${nic.network?.split('/').pop()}`);
|
|
200
|
+
ui.print(` Internal IP: ${nic.networkIP}`);
|
|
201
|
+
if (nic.accessConfigs?.[0]?.natIP) {
|
|
202
|
+
ui.print(` External IP: ${nic.accessConfigs[0].natIP}`);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
ui.newLine();
|
|
206
|
+
|
|
207
|
+
ui.print(ui.bold('Disks:'));
|
|
208
|
+
for (const disk of instance.disks || []) {
|
|
209
|
+
ui.print(` Name: ${disk.source?.split('/').pop()}`);
|
|
210
|
+
ui.print(` Boot: ${disk.boot ? 'Yes' : 'No'}`);
|
|
211
|
+
ui.print(` Size: ${disk.diskSizeGb} GB`);
|
|
212
|
+
}
|
|
213
|
+
} catch (error: unknown) {
|
|
214
|
+
ui.stopSpinnerFail('Failed to fetch details');
|
|
215
|
+
const message = error instanceof Error ? error.message : 'Unknown error';
|
|
216
|
+
logger.error('Failed to describe instance', { error: message });
|
|
217
|
+
ui.error(`Failed to describe instance: ${message}`);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Start an instance
|
|
223
|
+
*/
|
|
224
|
+
async function startInstance(instanceName: string, options: GcpCommandOptions): Promise<void> {
|
|
225
|
+
const gcloudArgs = ['compute', 'instances', 'start', instanceName];
|
|
226
|
+
if (options.project) {
|
|
227
|
+
gcloudArgs.push(`--project=${options.project}`);
|
|
228
|
+
}
|
|
229
|
+
if (options.zone) {
|
|
230
|
+
gcloudArgs.push(`--zone=${options.zone}`);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
try {
|
|
234
|
+
ui.startSpinner({ message: `Starting instance ${instanceName}...` });
|
|
235
|
+
await execFileAsync('gcloud', gcloudArgs);
|
|
236
|
+
ui.stopSpinnerSuccess(`Instance ${instanceName} started`);
|
|
237
|
+
} catch (error: unknown) {
|
|
238
|
+
ui.stopSpinnerFail(`Failed to start instance`);
|
|
239
|
+
const message = error instanceof Error ? error.message : 'Unknown error';
|
|
240
|
+
logger.error('Failed to start instance', { error: message });
|
|
241
|
+
ui.error(`Failed to start instance: ${message}`);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* Stop an instance
|
|
247
|
+
*/
|
|
248
|
+
async function stopInstance(instanceName: string, options: GcpCommandOptions): Promise<void> {
|
|
249
|
+
const gcloudArgs = ['compute', 'instances', 'stop', instanceName];
|
|
250
|
+
if (options.project) {
|
|
251
|
+
gcloudArgs.push(`--project=${options.project}`);
|
|
252
|
+
}
|
|
253
|
+
if (options.zone) {
|
|
254
|
+
gcloudArgs.push(`--zone=${options.zone}`);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
try {
|
|
258
|
+
ui.startSpinner({ message: `Stopping instance ${instanceName}...` });
|
|
259
|
+
await execFileAsync('gcloud', gcloudArgs);
|
|
260
|
+
ui.stopSpinnerSuccess(`Instance ${instanceName} stopped`);
|
|
261
|
+
} catch (error: unknown) {
|
|
262
|
+
ui.stopSpinnerFail(`Failed to stop instance`);
|
|
263
|
+
const message = error instanceof Error ? error.message : 'Unknown error';
|
|
264
|
+
logger.error('Failed to stop instance', { error: message });
|
|
265
|
+
ui.error(`Failed to stop instance: ${message}`);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* Delete an instance (requires safety approval)
|
|
271
|
+
*/
|
|
272
|
+
async function deleteInstance(instanceName: string, options: GcpCommandOptions): Promise<void> {
|
|
273
|
+
// Run safety checks
|
|
274
|
+
const safetyResult = await runComputeSafetyChecks('delete', instanceName, options);
|
|
275
|
+
|
|
276
|
+
displaySafetySummary({
|
|
277
|
+
operation: `delete ${instanceName}`,
|
|
278
|
+
risks: safetyResult.risks,
|
|
279
|
+
passed: safetyResult.passed,
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
if (safetyResult.requiresApproval) {
|
|
283
|
+
const result = await promptForApproval({
|
|
284
|
+
title: 'Delete Compute Engine Instance',
|
|
285
|
+
operation: `gcloud compute instances delete ${instanceName}`,
|
|
286
|
+
risks: safetyResult.risks,
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
if (!result.approved) {
|
|
290
|
+
ui.warning('Operation cancelled');
|
|
291
|
+
return;
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
const gcloudArgs = ['compute', 'instances', 'delete', instanceName, '--quiet'];
|
|
296
|
+
if (options.project) {
|
|
297
|
+
gcloudArgs.push(`--project=${options.project}`);
|
|
298
|
+
}
|
|
299
|
+
if (options.zone) {
|
|
300
|
+
gcloudArgs.push(`--zone=${options.zone}`);
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
try {
|
|
304
|
+
ui.startSpinner({ message: `Deleting instance ${instanceName}...` });
|
|
305
|
+
await execFileAsync('gcloud', gcloudArgs);
|
|
306
|
+
ui.stopSpinnerSuccess(`Instance ${instanceName} deleted`);
|
|
307
|
+
} catch (error: unknown) {
|
|
308
|
+
ui.stopSpinnerFail(`Failed to delete instance`);
|
|
309
|
+
const message = error instanceof Error ? error.message : 'Unknown error';
|
|
310
|
+
logger.error('Failed to delete instance', { error: message });
|
|
311
|
+
ui.error(`Failed to delete instance: ${message}`);
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
/**
|
|
316
|
+
* Show Compute help
|
|
317
|
+
*/
|
|
318
|
+
function showComputeHelp(): void {
|
|
319
|
+
ui.print(ui.bold('Compute Engine Commands:'));
|
|
320
|
+
ui.print(' list List all instances');
|
|
321
|
+
ui.print(' describe <name> Show instance details');
|
|
322
|
+
ui.print(' start <name> Start an instance');
|
|
323
|
+
ui.print(' stop <name> Stop an instance');
|
|
324
|
+
ui.print(' delete <name> Delete an instance (requires approval)');
|
|
325
|
+
}
|
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GCP Cloud Functions CLI Commands
|
|
3
|
+
*
|
|
4
|
+
* Operations for Cloud Functions
|
|
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 Cloud Functions command router
|
|
17
|
+
*/
|
|
18
|
+
export async function functionsCommand(
|
|
19
|
+
action: string,
|
|
20
|
+
args: string[],
|
|
21
|
+
options: GcpCommandOptions
|
|
22
|
+
): Promise<void> {
|
|
23
|
+
logger.info('Running GCP Functions command', { action, args, options });
|
|
24
|
+
|
|
25
|
+
switch (action) {
|
|
26
|
+
case 'list':
|
|
27
|
+
await listFunctions(options);
|
|
28
|
+
break;
|
|
29
|
+
|
|
30
|
+
case 'describe':
|
|
31
|
+
if (!args[0]) {
|
|
32
|
+
ui.error('Function name required');
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
await describeFunction(args[0], options);
|
|
36
|
+
break;
|
|
37
|
+
|
|
38
|
+
case 'call':
|
|
39
|
+
if (!args[0]) {
|
|
40
|
+
ui.error('Function name required');
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
await callFunction(args[0], args[1], options);
|
|
44
|
+
break;
|
|
45
|
+
|
|
46
|
+
case 'logs':
|
|
47
|
+
if (!args[0]) {
|
|
48
|
+
ui.error('Function name required');
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
await getFunctionLogs(args[0], options);
|
|
52
|
+
break;
|
|
53
|
+
|
|
54
|
+
default:
|
|
55
|
+
showFunctionsHelp();
|
|
56
|
+
break;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* List Cloud Functions
|
|
62
|
+
*/
|
|
63
|
+
async function listFunctions(options: GcpCommandOptions): Promise<void> {
|
|
64
|
+
ui.header('Cloud Functions');
|
|
65
|
+
ui.newLine();
|
|
66
|
+
|
|
67
|
+
const gcloudArgs = ['functions', 'list', '--format=json'];
|
|
68
|
+
if (options.project) {
|
|
69
|
+
gcloudArgs.push(`--project=${options.project}`);
|
|
70
|
+
}
|
|
71
|
+
if (options.region) {
|
|
72
|
+
gcloudArgs.push(`--regions=${options.region}`);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
try {
|
|
76
|
+
ui.startSpinner({ message: 'Fetching functions...' });
|
|
77
|
+
const { stdout } = await execFileAsync('gcloud', gcloudArgs);
|
|
78
|
+
ui.stopSpinnerSuccess('Functions fetched');
|
|
79
|
+
|
|
80
|
+
const functions = JSON.parse(stdout || '[]');
|
|
81
|
+
|
|
82
|
+
if (functions.length === 0) {
|
|
83
|
+
ui.info('No functions found');
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
ui.print(`Found ${functions.length} function(s)\n`);
|
|
88
|
+
|
|
89
|
+
// Display table
|
|
90
|
+
ui.print(
|
|
91
|
+
ui.color(
|
|
92
|
+
`${
|
|
93
|
+
'Name'.padEnd(30) + 'Region'.padEnd(15) + 'Runtime'.padEnd(15) + 'Trigger'.padEnd(15)
|
|
94
|
+
}State`,
|
|
95
|
+
'cyan'
|
|
96
|
+
)
|
|
97
|
+
);
|
|
98
|
+
ui.print('─'.repeat(90));
|
|
99
|
+
|
|
100
|
+
for (const fn of functions) {
|
|
101
|
+
const name = fn.name?.split('/').pop()?.substring(0, 29) || '';
|
|
102
|
+
const region = fn.name?.split('/')[3]?.substring(0, 14) || '';
|
|
103
|
+
const runtime = fn.runtime?.substring(0, 14) || '';
|
|
104
|
+
const trigger = fn.httpsTrigger ? 'HTTP' : fn.eventTrigger ? 'Event' : 'Unknown';
|
|
105
|
+
const state = fn.state || '';
|
|
106
|
+
|
|
107
|
+
const stateColor =
|
|
108
|
+
state === 'ACTIVE'
|
|
109
|
+
? 'green'
|
|
110
|
+
: state === 'DEPLOYING'
|
|
111
|
+
? 'yellow'
|
|
112
|
+
: state === 'FAILED'
|
|
113
|
+
? 'red'
|
|
114
|
+
: 'white';
|
|
115
|
+
|
|
116
|
+
ui.print(
|
|
117
|
+
`${name.padEnd(30)}${region.padEnd(15)}${runtime.padEnd(15)}${trigger.padEnd(15)}${ui.color(state, stateColor as 'green' | 'yellow' | 'red' | 'white')}`
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
} catch (error: unknown) {
|
|
121
|
+
ui.stopSpinnerFail('Failed to fetch functions');
|
|
122
|
+
const message = error instanceof Error ? error.message : 'Unknown error';
|
|
123
|
+
logger.error('Failed to list functions', { error: message });
|
|
124
|
+
ui.error(`Failed to list functions: ${message}`);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Describe a specific function
|
|
130
|
+
*/
|
|
131
|
+
async function describeFunction(functionName: string, options: GcpCommandOptions): Promise<void> {
|
|
132
|
+
ui.header(`Function: ${functionName}`);
|
|
133
|
+
ui.newLine();
|
|
134
|
+
|
|
135
|
+
const gcloudArgs = ['functions', 'describe', functionName, '--format=json'];
|
|
136
|
+
if (options.project) {
|
|
137
|
+
gcloudArgs.push(`--project=${options.project}`);
|
|
138
|
+
}
|
|
139
|
+
if (options.region) {
|
|
140
|
+
gcloudArgs.push(`--region=${options.region}`);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
try {
|
|
144
|
+
ui.startSpinner({ message: 'Fetching function details...' });
|
|
145
|
+
const { stdout } = await execFileAsync('gcloud', gcloudArgs);
|
|
146
|
+
ui.stopSpinnerSuccess('Details fetched');
|
|
147
|
+
|
|
148
|
+
const fn = JSON.parse(stdout);
|
|
149
|
+
|
|
150
|
+
ui.print(ui.bold('Basic Information:'));
|
|
151
|
+
ui.print(` Name: ${fn.name?.split('/').pop()}`);
|
|
152
|
+
ui.print(` Region: ${fn.name?.split('/')[3]}`);
|
|
153
|
+
ui.print(` State: ${fn.state}`);
|
|
154
|
+
ui.print(` Runtime: ${fn.runtime}`);
|
|
155
|
+
ui.print(` Entry Point: ${fn.entryPoint}`);
|
|
156
|
+
ui.newLine();
|
|
157
|
+
|
|
158
|
+
ui.print(ui.bold('Resources:'));
|
|
159
|
+
ui.print(` Memory: ${fn.availableMemoryMb} MB`);
|
|
160
|
+
ui.print(` Timeout: ${fn.timeout}`);
|
|
161
|
+
ui.print(` Max Instances: ${fn.maxInstances || 'default'}`);
|
|
162
|
+
ui.newLine();
|
|
163
|
+
|
|
164
|
+
ui.print(ui.bold('Trigger:'));
|
|
165
|
+
if (fn.httpsTrigger) {
|
|
166
|
+
ui.print(` Type: HTTP`);
|
|
167
|
+
ui.print(` URL: ${fn.httpsTrigger.url}`);
|
|
168
|
+
} else if (fn.eventTrigger) {
|
|
169
|
+
ui.print(` Type: Event`);
|
|
170
|
+
ui.print(` Event Type: ${fn.eventTrigger.eventType}`);
|
|
171
|
+
ui.print(` Resource: ${fn.eventTrigger.resource}`);
|
|
172
|
+
}
|
|
173
|
+
ui.newLine();
|
|
174
|
+
|
|
175
|
+
ui.print(ui.bold('Timestamps:'));
|
|
176
|
+
ui.print(` Created: ${fn.buildId ? `Build ID: ${fn.buildId}` : 'N/A'}`);
|
|
177
|
+
ui.print(` Updated: ${fn.updateTime}`);
|
|
178
|
+
} catch (error: unknown) {
|
|
179
|
+
ui.stopSpinnerFail('Failed to fetch details');
|
|
180
|
+
const message = error instanceof Error ? error.message : 'Unknown error';
|
|
181
|
+
logger.error('Failed to describe function', { error: message });
|
|
182
|
+
ui.error(`Failed to describe function: ${message}`);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Call a function
|
|
188
|
+
*/
|
|
189
|
+
async function callFunction(
|
|
190
|
+
functionName: string,
|
|
191
|
+
data: string | undefined,
|
|
192
|
+
options: GcpCommandOptions
|
|
193
|
+
): Promise<void> {
|
|
194
|
+
const gcloudArgs = ['functions', 'call', functionName];
|
|
195
|
+
if (data) {
|
|
196
|
+
gcloudArgs.push('--data', data);
|
|
197
|
+
}
|
|
198
|
+
if (options.project) {
|
|
199
|
+
gcloudArgs.push(`--project=${options.project}`);
|
|
200
|
+
}
|
|
201
|
+
if (options.region) {
|
|
202
|
+
gcloudArgs.push(`--region=${options.region}`);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
try {
|
|
206
|
+
ui.startSpinner({ message: `Calling function ${functionName}...` });
|
|
207
|
+
const { stdout, stderr } = await execFileAsync('gcloud', gcloudArgs);
|
|
208
|
+
ui.stopSpinnerSuccess('Function invoked');
|
|
209
|
+
ui.newLine();
|
|
210
|
+
|
|
211
|
+
if (stdout) {
|
|
212
|
+
ui.print(ui.bold('Response:'));
|
|
213
|
+
ui.print(stdout);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
if (stderr) {
|
|
217
|
+
ui.print(ui.bold('Execution ID:'));
|
|
218
|
+
ui.print(stderr);
|
|
219
|
+
}
|
|
220
|
+
} catch (error: unknown) {
|
|
221
|
+
ui.stopSpinnerFail('Failed to call function');
|
|
222
|
+
const message = error instanceof Error ? error.message : 'Unknown error';
|
|
223
|
+
logger.error('Failed to call function', { error: message });
|
|
224
|
+
ui.error(`Failed to call function: ${message}`);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Get function logs
|
|
230
|
+
*/
|
|
231
|
+
async function getFunctionLogs(functionName: string, options: GcpCommandOptions): Promise<void> {
|
|
232
|
+
ui.header(`Logs for ${functionName}`);
|
|
233
|
+
ui.newLine();
|
|
234
|
+
|
|
235
|
+
const gcloudArgs = ['functions', 'logs', 'read', functionName, '--limit=50'];
|
|
236
|
+
if (options.project) {
|
|
237
|
+
gcloudArgs.push(`--project=${options.project}`);
|
|
238
|
+
}
|
|
239
|
+
if (options.region) {
|
|
240
|
+
gcloudArgs.push(`--region=${options.region}`);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
try {
|
|
244
|
+
ui.startSpinner({ message: 'Fetching logs...' });
|
|
245
|
+
const { stdout } = await execFileAsync('gcloud', gcloudArgs);
|
|
246
|
+
ui.stopSpinnerSuccess('Logs fetched');
|
|
247
|
+
|
|
248
|
+
if (!stdout.trim()) {
|
|
249
|
+
ui.info('No logs found');
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
ui.print(stdout);
|
|
254
|
+
} catch (error: unknown) {
|
|
255
|
+
ui.stopSpinnerFail('Failed to fetch logs');
|
|
256
|
+
const message = error instanceof Error ? error.message : 'Unknown error';
|
|
257
|
+
logger.error('Failed to get logs', { error: message });
|
|
258
|
+
ui.error(`Failed to get logs: ${message}`);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* Show Functions help
|
|
264
|
+
*/
|
|
265
|
+
function showFunctionsHelp(): void {
|
|
266
|
+
ui.print(ui.bold('Cloud Functions Commands:'));
|
|
267
|
+
ui.print(' list List all functions');
|
|
268
|
+
ui.print(' describe <name> Show function details');
|
|
269
|
+
ui.print(' call <name> [data] Invoke a function');
|
|
270
|
+
ui.print(' logs <name> View function logs');
|
|
271
|
+
}
|