kadi-deploy 0.19.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/.env.example +6 -0
- package/.prettierrc +6 -0
- package/README.md +589 -0
- package/agent.json +23 -0
- package/index.js +11 -0
- package/package.json +42 -0
- package/quick-command.txt +92 -0
- package/scripts/preflight.js +458 -0
- package/scripts/preflight.sh +300 -0
- package/src/cli/bid-selector.ts +222 -0
- package/src/cli/colors.ts +216 -0
- package/src/cli/index.ts +11 -0
- package/src/cli/prompts.ts +190 -0
- package/src/cli/spinners.ts +165 -0
- package/src/commands/deploy-local.ts +475 -0
- package/src/commands/deploy.ts +1342 -0
- package/src/commands/down.ts +679 -0
- package/src/commands/index.ts +10 -0
- package/src/commands/lock.ts +571 -0
- package/src/config/agent-loader.ts +177 -0
- package/src/config/index.ts +9 -0
- package/src/display/deployment-info.ts +220 -0
- package/src/display/pricing.ts +137 -0
- package/src/display/resources.ts +234 -0
- package/src/enhanced-registry-manager.ts +892 -0
- package/src/index.ts +307 -0
- package/src/infrastructure/registry.ts +269 -0
- package/src/schemas/profiles.ts +529 -0
- package/src/secrets/broker-urls.ts +109 -0
- package/src/secrets/handshake.ts +407 -0
- package/src/secrets/index.ts +69 -0
- package/src/secrets/inject-env.ts +171 -0
- package/src/secrets/nonce.ts +31 -0
- package/src/secrets/normalize.ts +204 -0
- package/src/secrets/prepare.ts +152 -0
- package/src/secrets/validate.ts +243 -0
- package/src/secrets/vault.ts +80 -0
- package/src/types/akash.ts +116 -0
- package/src/types/container-registry-ability.d.ts +158 -0
- package/src/types/external.ts +49 -0
- package/src/types.ts +211 -0
- package/src/utils/akt-price.ts +74 -0
- package/tests/agent-loader.test.ts +239 -0
- package/tests/autonomous.test.ts +244 -0
- package/tests/down.test.ts +1143 -0
- package/tests/lock.test.ts +1148 -0
- package/tests/nonce.test.ts +34 -0
- package/tests/normalize.test.ts +270 -0
- package/tests/secrets-schema.test.ts +301 -0
- package/tests/types.test.ts +198 -0
- package/tsconfig.json +18 -0
- package/vitest.config.ts +9 -0
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent Configuration Loader
|
|
3
|
+
*
|
|
4
|
+
* Handles loading and parsing agent.json files from the project directory.
|
|
5
|
+
* Provides validation and helpful error messages when agent.json is missing or invalid.
|
|
6
|
+
*
|
|
7
|
+
* @module config/agent-loader
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import path from 'node:path';
|
|
11
|
+
import fsPromises from 'node:fs/promises';
|
|
12
|
+
import type { AgentConfig } from '../types.js';
|
|
13
|
+
import { validateProfile, type Profile } from '../schemas/profiles.js';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Error thrown when agent.json cannot be loaded or parsed
|
|
17
|
+
*/
|
|
18
|
+
export class AgentConfigError extends Error {
|
|
19
|
+
constructor(
|
|
20
|
+
message: string,
|
|
21
|
+
public readonly cause?: Error
|
|
22
|
+
) {
|
|
23
|
+
super(message);
|
|
24
|
+
this.name = 'AgentConfigError';
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Loads and parses agent.json from the specified project directory
|
|
30
|
+
*
|
|
31
|
+
* @param projectRoot - Absolute path to project directory containing agent.json
|
|
32
|
+
* @returns Parsed agent configuration
|
|
33
|
+
* @throws {AgentConfigError} If file cannot be read or parsed
|
|
34
|
+
*
|
|
35
|
+
* @example
|
|
36
|
+
* ```typescript
|
|
37
|
+
* try {
|
|
38
|
+
* const config = await loadAgentConfig('/path/to/project');
|
|
39
|
+
* console.log(`Loaded agent: ${config.name} v${config.version}`);
|
|
40
|
+
* } catch (error) {
|
|
41
|
+
* if (error instanceof AgentConfigError) {
|
|
42
|
+
* console.error('Failed to load agent.json:', error.message);
|
|
43
|
+
* }
|
|
44
|
+
* }
|
|
45
|
+
* ```
|
|
46
|
+
*/
|
|
47
|
+
export async function loadAgentConfig(projectRoot: string): Promise<AgentConfig> {
|
|
48
|
+
const agentJsonPath = path.join(projectRoot, 'agent.json');
|
|
49
|
+
|
|
50
|
+
try {
|
|
51
|
+
const raw = await fsPromises.readFile(agentJsonPath, 'utf8');
|
|
52
|
+
const config = JSON.parse(raw);
|
|
53
|
+
|
|
54
|
+
// Basic validation
|
|
55
|
+
if (!config.name) {
|
|
56
|
+
throw new AgentConfigError(
|
|
57
|
+
'agent.json is missing required field: "name"'
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return config;
|
|
62
|
+
} catch (error) {
|
|
63
|
+
if (error instanceof AgentConfigError) {
|
|
64
|
+
throw error;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if ((error as NodeJS.ErrnoException).code === 'ENOENT') {
|
|
68
|
+
throw new AgentConfigError(
|
|
69
|
+
`agent.json not found in ${projectRoot}\n` +
|
|
70
|
+
'Make sure you are in a KADI agent project directory.'
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (error instanceof SyntaxError) {
|
|
75
|
+
throw new AgentConfigError(
|
|
76
|
+
`agent.json contains invalid JSON: ${error.message}`,
|
|
77
|
+
error
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
throw new AgentConfigError(
|
|
82
|
+
`Failed to load agent.json: ${(error as Error).message}`,
|
|
83
|
+
error as Error
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Gets list of available deploy profiles from agent configuration
|
|
90
|
+
*
|
|
91
|
+
* Filters out special keys like 'services' and 'networks' that are not profiles.
|
|
92
|
+
*
|
|
93
|
+
* @param config - Loaded agent configuration
|
|
94
|
+
* @returns Array of profile names
|
|
95
|
+
*
|
|
96
|
+
* @example
|
|
97
|
+
* ```typescript
|
|
98
|
+
* const config = await loadAgentConfig('./');
|
|
99
|
+
* const profiles = getDeployProfiles(config);
|
|
100
|
+
* console.log('Available profiles:', profiles.join(', '));
|
|
101
|
+
* ```
|
|
102
|
+
*/
|
|
103
|
+
export function getDeployProfiles(config: AgentConfig): string[] {
|
|
104
|
+
if (!config.deploy) return [];
|
|
105
|
+
|
|
106
|
+
return Object.keys(config.deploy).filter(
|
|
107
|
+
(key) => key !== 'services' && key !== 'networks'
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Validates that a profile exists in the agent configuration
|
|
113
|
+
*
|
|
114
|
+
* @param config - Loaded agent configuration
|
|
115
|
+
* @param profileName - Profile name to validate
|
|
116
|
+
* @returns True if profile exists
|
|
117
|
+
*
|
|
118
|
+
* @example
|
|
119
|
+
* ```typescript
|
|
120
|
+
* if (!hasProfile(config, 'production')) {
|
|
121
|
+
* console.error('Production profile not found');
|
|
122
|
+
* }
|
|
123
|
+
* ```
|
|
124
|
+
*/
|
|
125
|
+
export function hasProfile(config: AgentConfig, profileName: string): boolean {
|
|
126
|
+
return getDeployProfiles(config).includes(profileName);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Gets and validates a specific deploy profile from agent configuration
|
|
131
|
+
*
|
|
132
|
+
* Validates the profile structure using Zod schemas to ensure all required
|
|
133
|
+
* fields are present and correctly typed. Throws descriptive errors if
|
|
134
|
+
* validation fails.
|
|
135
|
+
*
|
|
136
|
+
* @param config - Loaded agent configuration
|
|
137
|
+
* @param profileName - Profile name to retrieve
|
|
138
|
+
* @returns Validated profile configuration or undefined if not found
|
|
139
|
+
* @throws Error if profile exists but fails validation
|
|
140
|
+
*
|
|
141
|
+
* @example
|
|
142
|
+
* ```typescript
|
|
143
|
+
* const prodProfile = getProfile(config, 'production');
|
|
144
|
+
* if (prodProfile) {
|
|
145
|
+
* // prodProfile is fully typed and validated
|
|
146
|
+
* console.log('Target:', prodProfile.target);
|
|
147
|
+
* }
|
|
148
|
+
* ```
|
|
149
|
+
*/
|
|
150
|
+
export function getProfile(config: AgentConfig, profileName: string): Profile | undefined {
|
|
151
|
+
const rawProfile = config.deploy?.[profileName];
|
|
152
|
+
if (!rawProfile) {
|
|
153
|
+
return undefined;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// Validate profile structure with Zod
|
|
157
|
+
return validateProfile(rawProfile, profileName);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Gets the first available profile from agent configuration
|
|
162
|
+
*
|
|
163
|
+
* Useful when user doesn't specify a profile and we want to use a sensible default.
|
|
164
|
+
*
|
|
165
|
+
* @param config - Loaded agent configuration
|
|
166
|
+
* @returns First profile name or undefined if no profiles exist
|
|
167
|
+
*
|
|
168
|
+
* @example
|
|
169
|
+
* ```typescript
|
|
170
|
+
* const defaultProfile = getFirstProfile(config);
|
|
171
|
+
* console.log('Using profile:', defaultProfile || 'none');
|
|
172
|
+
* ```
|
|
173
|
+
*/
|
|
174
|
+
export function getFirstProfile(config: AgentConfig): string | undefined {
|
|
175
|
+
const profiles = getDeployProfiles(config);
|
|
176
|
+
return profiles.length > 0 ? profiles[0] : undefined;
|
|
177
|
+
}
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deployment Information Display
|
|
3
|
+
*
|
|
4
|
+
* Formats and displays comprehensive deployment information from deploy-ability's
|
|
5
|
+
* AkashDeploymentData type. Provides a beautiful, informative summary of deployments.
|
|
6
|
+
*
|
|
7
|
+
* @module display/deployment-info
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import type { AkashDeploymentData } from '@kadi.build/deploy-ability/types';
|
|
11
|
+
import {
|
|
12
|
+
bold,
|
|
13
|
+
dim,
|
|
14
|
+
success,
|
|
15
|
+
info,
|
|
16
|
+
highlight,
|
|
17
|
+
formatKeyValue,
|
|
18
|
+
formatUrl,
|
|
19
|
+
formatHeader
|
|
20
|
+
} from '../cli/colors.js';
|
|
21
|
+
import { displayLeasePrice, displayLeasePriceWithUsd } from './pricing.js';
|
|
22
|
+
import { displayServiceResources, displayServicesTable } from './resources.js';
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Displays comprehensive deployment information
|
|
26
|
+
*
|
|
27
|
+
* Shows all relevant deployment details including:
|
|
28
|
+
* - Deployment ID and status
|
|
29
|
+
* - Provider information
|
|
30
|
+
* - Lease pricing
|
|
31
|
+
* - Service resources
|
|
32
|
+
* - Endpoints (if available)
|
|
33
|
+
* - Quick access links
|
|
34
|
+
*
|
|
35
|
+
* @param data - Deployment data from deploy-ability
|
|
36
|
+
* @param aktPriceUsd - Optional AKT market price for USD conversion
|
|
37
|
+
*
|
|
38
|
+
* @example
|
|
39
|
+
* ```typescript
|
|
40
|
+
* const result = await deployToAkash(options);
|
|
41
|
+
* if (result.success) {
|
|
42
|
+
* displayDeploymentInfo(result.data);
|
|
43
|
+
* }
|
|
44
|
+
* ```
|
|
45
|
+
*/
|
|
46
|
+
export function displayDeploymentInfo(
|
|
47
|
+
data: AkashDeploymentData,
|
|
48
|
+
aktPriceUsd?: number
|
|
49
|
+
): void {
|
|
50
|
+
const separator = '─'.repeat(70);
|
|
51
|
+
|
|
52
|
+
console.log('');
|
|
53
|
+
console.log(success('✓ Deployment Successful!'));
|
|
54
|
+
console.log('');
|
|
55
|
+
console.log(separator);
|
|
56
|
+
console.log(formatHeader('Deployment Information'));
|
|
57
|
+
console.log(separator);
|
|
58
|
+
console.log('');
|
|
59
|
+
|
|
60
|
+
// Core deployment details
|
|
61
|
+
console.log(formatKeyValue('Deployment ID (DSEQ)', bold(data.dseq.toString())));
|
|
62
|
+
console.log(formatKeyValue('Owner', data.owner));
|
|
63
|
+
console.log(formatKeyValue('Provider', data.provider));
|
|
64
|
+
console.log(formatKeyValue('Network', highlight(data.network)));
|
|
65
|
+
console.log(formatKeyValue('Status', data.status === 'active' ? success('Active') : dim('Closed')));
|
|
66
|
+
console.log(formatKeyValue('Deployed At', data.deployedAt.toLocaleString()));
|
|
67
|
+
console.log('');
|
|
68
|
+
|
|
69
|
+
// Provider information
|
|
70
|
+
console.log(formatHeader('Provider Details'));
|
|
71
|
+
console.log(separator);
|
|
72
|
+
console.log('');
|
|
73
|
+
console.log(formatKeyValue('Provider URI', data.providerUri));
|
|
74
|
+
console.log(formatKeyValue('Group Sequence', data.gseq.toString()));
|
|
75
|
+
console.log(formatKeyValue('Order Sequence', data.oseq.toString()));
|
|
76
|
+
console.log('');
|
|
77
|
+
|
|
78
|
+
// Lease pricing
|
|
79
|
+
console.log(formatHeader('Lease Pricing'));
|
|
80
|
+
console.log(separator);
|
|
81
|
+
console.log('');
|
|
82
|
+
console.log(formatKeyValue('Created At', data.leaseCreatedAt));
|
|
83
|
+
|
|
84
|
+
if (aktPriceUsd) {
|
|
85
|
+
displayLeasePriceWithUsd(data.leasePrice, aktPriceUsd);
|
|
86
|
+
} else {
|
|
87
|
+
displayLeasePrice(data.leasePrice);
|
|
88
|
+
}
|
|
89
|
+
console.log('');
|
|
90
|
+
|
|
91
|
+
// Service resources
|
|
92
|
+
if (data.services && data.services.length > 0) {
|
|
93
|
+
console.log(formatHeader('Services'));
|
|
94
|
+
console.log(separator);
|
|
95
|
+
console.log('');
|
|
96
|
+
displayServiceResources(data.services);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Endpoints (if available)
|
|
100
|
+
if (data.endpoints && Object.keys(data.endpoints).length > 0) {
|
|
101
|
+
console.log(formatHeader('Service Endpoints'));
|
|
102
|
+
console.log(separator);
|
|
103
|
+
console.log('');
|
|
104
|
+
for (const [serviceName, endpoint] of Object.entries(data.endpoints)) {
|
|
105
|
+
console.log(formatKeyValue(serviceName, formatUrl(endpoint)));
|
|
106
|
+
}
|
|
107
|
+
console.log('');
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Quick access links
|
|
111
|
+
console.log(formatHeader('Quick Access'));
|
|
112
|
+
console.log(separator);
|
|
113
|
+
console.log('');
|
|
114
|
+
|
|
115
|
+
const consoleUrl = data.network === 'mainnet'
|
|
116
|
+
? `https://console.akash.network/deployments/${data.dseq}`
|
|
117
|
+
: `https://console-sandbox.akash.network/deployments/${data.dseq}`;
|
|
118
|
+
|
|
119
|
+
console.log(formatKeyValue('Akash Console', formatUrl(consoleUrl)));
|
|
120
|
+
console.log(formatKeyValue('Provider Dashboard', formatUrl(data.providerUri)));
|
|
121
|
+
console.log('');
|
|
122
|
+
|
|
123
|
+
console.log(dim('💡 Your deployment is now live on the Akash Network!'));
|
|
124
|
+
console.log('');
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Displays a compact deployment summary
|
|
129
|
+
*
|
|
130
|
+
* Shows only the most important information in a concise format.
|
|
131
|
+
* Useful for quick confirmations or when displaying multiple deployments.
|
|
132
|
+
*
|
|
133
|
+
* @param data - Deployment data from deploy-ability
|
|
134
|
+
* @param aktPriceUsd - Optional AKT market price for USD conversion
|
|
135
|
+
*
|
|
136
|
+
* @example
|
|
137
|
+
* ```typescript
|
|
138
|
+
* displayDeploymentSummary(result.data, 0.52);
|
|
139
|
+
* // Output:
|
|
140
|
+
* // ✓ Deployed DSEQ 12345 to testnet
|
|
141
|
+
* // Provider: akash1...
|
|
142
|
+
* // Cost: 320.55 AKT/month ($166.69/month)
|
|
143
|
+
* ```
|
|
144
|
+
*/
|
|
145
|
+
export function displayDeploymentSummary(data: AkashDeploymentData, aktPriceUsd?: number): void {
|
|
146
|
+
console.log('');
|
|
147
|
+
console.log(success(`✓ Deployed DSEQ ${bold(data.dseq.toString())} to ${highlight(data.network)}`));
|
|
148
|
+
console.log(` Provider: ${dim(data.provider)}`);
|
|
149
|
+
|
|
150
|
+
// Display cost with USD if price available
|
|
151
|
+
const aktCost = data.leasePrice.akt.perMonth.toFixed(2);
|
|
152
|
+
if (aktPriceUsd) {
|
|
153
|
+
const usdCost = data.leasePrice.toUSD(aktPriceUsd).perMonth.toFixed(2);
|
|
154
|
+
console.log(` Cost: ${info(`${aktCost} AKT/month`)} ${dim(`($${usdCost}/month)`)}`);
|
|
155
|
+
} else {
|
|
156
|
+
console.log(` Cost: ${info(`${aktCost} AKT/month`)}`);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
if (data.services && data.services.length > 0) {
|
|
160
|
+
const serviceCount = data.services.length;
|
|
161
|
+
const replicaCount = data.services.reduce((sum, s) => sum + s.replicaCount, 0);
|
|
162
|
+
console.log(` Services: ${info(serviceCount.toString())} (${replicaCount} total replicas)`);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
console.log('');
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Displays deployment profile information
|
|
170
|
+
*
|
|
171
|
+
* Shows which profile was used for deployment and its key settings.
|
|
172
|
+
*
|
|
173
|
+
* @param profileName - Profile name that was used
|
|
174
|
+
* @param data - Deployment data
|
|
175
|
+
*
|
|
176
|
+
* @example
|
|
177
|
+
* ```typescript
|
|
178
|
+
* displayProfileInfo('production', result.data);
|
|
179
|
+
* ```
|
|
180
|
+
*/
|
|
181
|
+
export function displayProfileInfo(profileName: string, data: AkashDeploymentData): void {
|
|
182
|
+
console.log(formatHeader('Deployment Profile'));
|
|
183
|
+
console.log(` ${formatKeyValue('Profile Name', bold(profileName))}`);
|
|
184
|
+
console.log(` ${formatKeyValue('Network', data.network)}`);
|
|
185
|
+
console.log(` ${formatKeyValue('Target', 'akash')}`);
|
|
186
|
+
console.log('');
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Displays dry-run information
|
|
191
|
+
*
|
|
192
|
+
* Shows what would be deployed without actually executing the deployment.
|
|
193
|
+
*
|
|
194
|
+
* @param profileName - Profile name
|
|
195
|
+
* @param sdl - Generated SDL content
|
|
196
|
+
*
|
|
197
|
+
* @example
|
|
198
|
+
* ```typescript
|
|
199
|
+
* displayDryRunInfo('production', sdlContent);
|
|
200
|
+
* ```
|
|
201
|
+
*/
|
|
202
|
+
export function displayDryRunInfo(profileName: string, sdl: string): void {
|
|
203
|
+
console.log('');
|
|
204
|
+
console.log(formatHeader('Dry Run - Deployment Preview'));
|
|
205
|
+
console.log(` ${formatKeyValue('Profile', bold(profileName))}`);
|
|
206
|
+
console.log('');
|
|
207
|
+
console.log(dim('Generated SDL:'));
|
|
208
|
+
console.log('─'.repeat(70));
|
|
209
|
+
console.log(sdl);
|
|
210
|
+
console.log('─'.repeat(70));
|
|
211
|
+
console.log('');
|
|
212
|
+
console.log(dim('ℹ️ This is a dry run - no deployment was created'));
|
|
213
|
+
console.log('');
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Barrel export
|
|
218
|
+
*/
|
|
219
|
+
export * from './pricing.js';
|
|
220
|
+
export * from './resources.js';
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lease Pricing Display Utilities
|
|
3
|
+
*
|
|
4
|
+
* Formats and displays lease pricing information from deploy-ability's LeasePrice type.
|
|
5
|
+
* Provides multiple views: per-block, per-hour, per-month in both uAKT and AKT.
|
|
6
|
+
*
|
|
7
|
+
* @module display/pricing
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import type { LeasePrice } from '@kadi.build/deploy-ability/akash';
|
|
11
|
+
import { formatKeyValue, success, dim } from '../cli/colors.js';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Displays lease pricing in a formatted, human-readable manner
|
|
15
|
+
*
|
|
16
|
+
* Shows pricing in multiple time units (block, hour, month) and denominations (uAKT, AKT).
|
|
17
|
+
*
|
|
18
|
+
* @param leasePrice - Lease price object from deploy-ability
|
|
19
|
+
* @param indent - Indentation level (default: 0)
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* ```typescript
|
|
23
|
+
* displayLeasePrice(deploymentData.leasePrice);
|
|
24
|
+
* // Output:
|
|
25
|
+
* // Price per block: 450 uAKT (0.000450 AKT)
|
|
26
|
+
* // Price per hour: 443,705 uAKT (0.443705 AKT)
|
|
27
|
+
* // Price per month: 320,548,800 uAKT (320.548800 AKT)
|
|
28
|
+
* ```
|
|
29
|
+
*/
|
|
30
|
+
export function displayLeasePrice(leasePrice: LeasePrice, indent: number = 0): void {
|
|
31
|
+
const prefix = ' '.repeat(indent);
|
|
32
|
+
|
|
33
|
+
console.log(`${prefix}${formatKeyValue('Price per block', `${leasePrice.uakt.perBlock} uAKT (${leasePrice.akt.perBlock} AKT)`)}`);
|
|
34
|
+
console.log(`${prefix}${formatKeyValue('Price per hour', `${leasePrice.uakt.perHour.toLocaleString()} uAKT (${leasePrice.akt.perHour} AKT)`)}`);
|
|
35
|
+
console.log(`${prefix}${formatKeyValue('Price per month', `${leasePrice.uakt.perMonth.toLocaleString()} uAKT (${success(leasePrice.akt.perMonth + ' AKT')})`)}`)
|
|
36
|
+
;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Displays lease pricing with USD conversion
|
|
41
|
+
*
|
|
42
|
+
* Shows pricing in uAKT, AKT, and USD when AKT market price is provided.
|
|
43
|
+
*
|
|
44
|
+
* @param leasePrice - Lease price object from deploy-ability
|
|
45
|
+
* @param aktPriceUsd - Current AKT market price in USD (optional)
|
|
46
|
+
* @param indent - Indentation level (default: 0)
|
|
47
|
+
*
|
|
48
|
+
* @example
|
|
49
|
+
* ```typescript
|
|
50
|
+
* // With USD conversion
|
|
51
|
+
* displayLeasePriceWithUsd(deploymentData.leasePrice, 0.50);
|
|
52
|
+
* // Output includes USD amounts:
|
|
53
|
+
* // Price per month: 320.55 AKT ($160.27 USD)
|
|
54
|
+
* ```
|
|
55
|
+
*/
|
|
56
|
+
export function displayLeasePriceWithUsd(
|
|
57
|
+
leasePrice: LeasePrice,
|
|
58
|
+
aktPriceUsd?: number,
|
|
59
|
+
indent: number = 0
|
|
60
|
+
): void {
|
|
61
|
+
const prefix = ' '.repeat(indent);
|
|
62
|
+
|
|
63
|
+
if (aktPriceUsd) {
|
|
64
|
+
const usdPrices = leasePrice.toUSD(aktPriceUsd);
|
|
65
|
+
|
|
66
|
+
console.log(`${prefix}${formatKeyValue('Price per block', `${leasePrice.uakt.perBlock} uAKT ($${usdPrices.perBlock.toFixed(6)} USD)`)}`);
|
|
67
|
+
console.log(`${prefix}${formatKeyValue('Price per hour', `${leasePrice.akt.perHour} AKT ($${usdPrices.perHour.toFixed(4)} USD)`)}`);
|
|
68
|
+
console.log(`${prefix}${formatKeyValue('Price per month', `${success(leasePrice.akt.perMonth + ' AKT')} (${success('$' + usdPrices.perMonth.toFixed(2) + ' USD')})`)}`)
|
|
69
|
+
;
|
|
70
|
+
} else {
|
|
71
|
+
// No USD price available, use standard display
|
|
72
|
+
displayLeasePrice(leasePrice, indent);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Gets a compact one-line pricing summary
|
|
78
|
+
*
|
|
79
|
+
* Useful for displaying in summaries or lists.
|
|
80
|
+
*
|
|
81
|
+
* @param leasePrice - Lease price object from deploy-ability
|
|
82
|
+
* @returns Formatted price string
|
|
83
|
+
*
|
|
84
|
+
* @example
|
|
85
|
+
* ```typescript
|
|
86
|
+
* const summary = getPricingSummary(leasePrice);
|
|
87
|
+
* console.log(`Monthly cost: ${summary}`);
|
|
88
|
+
* // Output: "Monthly cost: 320.55 AKT/month"
|
|
89
|
+
* ```
|
|
90
|
+
*/
|
|
91
|
+
export function getPricingSummary(leasePrice: LeasePrice): string {
|
|
92
|
+
return `${leasePrice.akt.perMonth} AKT/month`;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Gets a compact one-line pricing summary with USD
|
|
97
|
+
*
|
|
98
|
+
* @param leasePrice - Lease price object from deploy-ability
|
|
99
|
+
* @param aktPriceUsd - Current AKT market price in USD
|
|
100
|
+
* @returns Formatted price string with USD
|
|
101
|
+
*
|
|
102
|
+
* @example
|
|
103
|
+
* ```typescript
|
|
104
|
+
* const summary = getPricingSummaryWithUsd(leasePrice, 0.50);
|
|
105
|
+
* console.log(`Monthly cost: ${summary}`);
|
|
106
|
+
* // Output: "Monthly cost: 320.55 AKT ($160.27 USD)/month"
|
|
107
|
+
* ```
|
|
108
|
+
*/
|
|
109
|
+
export function getPricingSummaryWithUsd(leasePrice: LeasePrice, aktPriceUsd: number): string {
|
|
110
|
+
const usdPrices = leasePrice.toUSD(aktPriceUsd);
|
|
111
|
+
return `${leasePrice.akt.perMonth} AKT ($${usdPrices.perMonth.toFixed(2)} USD)/month`;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Displays pricing calculation methodology
|
|
116
|
+
*
|
|
117
|
+
* Shows the formulas and constants used for price calculations.
|
|
118
|
+
* Useful for transparency and debugging.
|
|
119
|
+
*
|
|
120
|
+
* @example
|
|
121
|
+
* ```typescript
|
|
122
|
+
* displayPricingMethodology();
|
|
123
|
+
* // Output:
|
|
124
|
+
* // Pricing Calculation:
|
|
125
|
+
* // - Average block time: 6.098 seconds
|
|
126
|
+
* // - Blocks per hour: 590.3...
|
|
127
|
+
* // - Average month: 30.437 days
|
|
128
|
+
* ```
|
|
129
|
+
*/
|
|
130
|
+
export function displayPricingMethodology(): void {
|
|
131
|
+
console.log(dim('\nPricing Calculation Methodology:'));
|
|
132
|
+
console.log(dim(' • Average block time: 6.098 seconds'));
|
|
133
|
+
console.log(dim(' • Blocks per hour: 590.36 (3600 / 6.098)'));
|
|
134
|
+
console.log(dim(' • Hours per month: 730.48 (30.437 days)'));
|
|
135
|
+
console.log(dim(' • Per month = per block × blocks/hour × hours/month'));
|
|
136
|
+
console.log(dim(' • Source: Akash Console pricing logic\n'));
|
|
137
|
+
}
|