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,116 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Akash Network Profile Types
|
|
3
|
+
*
|
|
4
|
+
* Type definitions for Akash deployment profiles and service configurations.
|
|
5
|
+
* These types represent the profile structure AFTER loading from agent.json
|
|
6
|
+
* but BEFORE passing to deploy-ability.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import type { ResourceConfig } from '../types.js';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Port exposure configuration for Akash SDL
|
|
13
|
+
*/
|
|
14
|
+
export interface AkashPortExpose {
|
|
15
|
+
port: number;
|
|
16
|
+
as?: number;
|
|
17
|
+
to?: string[];
|
|
18
|
+
proto?: 'tcp' | 'udp';
|
|
19
|
+
global?: boolean;
|
|
20
|
+
accept?: string[];
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Container registry credentials for private images
|
|
25
|
+
*/
|
|
26
|
+
export interface AkashRegistryCredentials {
|
|
27
|
+
host: string;
|
|
28
|
+
username: string;
|
|
29
|
+
password: string;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Pricing configuration for Akash bidding
|
|
34
|
+
*/
|
|
35
|
+
export interface AkashPricing {
|
|
36
|
+
denom: string; // e.g., "uakt"
|
|
37
|
+
amount: number | string;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Placement requirements for Akash providers
|
|
42
|
+
*/
|
|
43
|
+
export interface AkashPlacement {
|
|
44
|
+
signedBy?: {
|
|
45
|
+
allOf?: string[];
|
|
46
|
+
anyOf?: string[];
|
|
47
|
+
};
|
|
48
|
+
attributes?: Record<string, any>;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Service definition for Akash deployment
|
|
53
|
+
*
|
|
54
|
+
* Represents a single service in the deployment profile.
|
|
55
|
+
* Includes container image, runtime config, resources, and pricing.
|
|
56
|
+
*/
|
|
57
|
+
export interface AkashServiceDefinition {
|
|
58
|
+
/** Container image (e.g., "nginx:latest" or "my-app:1.0") */
|
|
59
|
+
image: string;
|
|
60
|
+
|
|
61
|
+
/** Optional command override */
|
|
62
|
+
command?: string[];
|
|
63
|
+
|
|
64
|
+
/** Environment variables (array or object format) */
|
|
65
|
+
env?: string[] | Record<string, string>;
|
|
66
|
+
|
|
67
|
+
/** Port exposures */
|
|
68
|
+
expose?: AkashPortExpose[];
|
|
69
|
+
|
|
70
|
+
/** Registry credentials for private images */
|
|
71
|
+
credentials?: AkashRegistryCredentials;
|
|
72
|
+
|
|
73
|
+
/** Resource requirements (CPU, memory, storage, GPU) */
|
|
74
|
+
resources?: ResourceConfig;
|
|
75
|
+
|
|
76
|
+
/** Pricing configuration for bidding */
|
|
77
|
+
pricing?: AkashPricing;
|
|
78
|
+
|
|
79
|
+
/** Number of instances to deploy */
|
|
80
|
+
count?: number;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Loaded Akash deployment profile
|
|
85
|
+
*
|
|
86
|
+
* Complete Akash profile structure after loading from agent.json.
|
|
87
|
+
* Contains all services and global placement/pricing settings.
|
|
88
|
+
*/
|
|
89
|
+
export interface LoadedAkashProfile {
|
|
90
|
+
/** Profile name */
|
|
91
|
+
selectedProfileName: string;
|
|
92
|
+
|
|
93
|
+
/** Deployment target (always 'akash') */
|
|
94
|
+
target: 'akash';
|
|
95
|
+
|
|
96
|
+
/** Network to deploy to */
|
|
97
|
+
network: 'mainnet' | 'testnet';
|
|
98
|
+
|
|
99
|
+
/** Container engine to use for local operations */
|
|
100
|
+
engine?: 'docker' | 'podman';
|
|
101
|
+
|
|
102
|
+
/** Service definitions */
|
|
103
|
+
services: Record<string, AkashServiceDefinition>;
|
|
104
|
+
|
|
105
|
+
/** Placement requirements (auditors, attributes) */
|
|
106
|
+
placement?: AkashPlacement;
|
|
107
|
+
|
|
108
|
+
/** WalletConnect project ID for mobile wallet connection */
|
|
109
|
+
walletConnectProjectId?: string;
|
|
110
|
+
|
|
111
|
+
/** Other profile options */
|
|
112
|
+
verbose?: boolean;
|
|
113
|
+
showCommands?: boolean;
|
|
114
|
+
yes?: boolean;
|
|
115
|
+
dryRun?: boolean;
|
|
116
|
+
}
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ambient type declarations for @kadi.build/container-registry-ability
|
|
3
|
+
*
|
|
4
|
+
* The package ships with types at src/types/index.d.ts but doesn't have a
|
|
5
|
+
* "types" field in package.json or a .d.ts for the main entry point.
|
|
6
|
+
* This declaration bridges that gap so we get proper type checking.
|
|
7
|
+
*
|
|
8
|
+
* Based on container-registry-ability v0.0.6 API surface.
|
|
9
|
+
*/
|
|
10
|
+
declare module '@kadi.build/container-registry-ability' {
|
|
11
|
+
export interface TunnelOptions {
|
|
12
|
+
subdomain?: string;
|
|
13
|
+
region?: string;
|
|
14
|
+
authtoken?: string;
|
|
15
|
+
authToken?: string;
|
|
16
|
+
host?: string;
|
|
17
|
+
port?: number;
|
|
18
|
+
protocol?: string;
|
|
19
|
+
/** Pass-through options for TunnelManager (e.g. fallbackServices) */
|
|
20
|
+
managerOptions?: Record<string, unknown>;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface RegistryUrls {
|
|
24
|
+
localUrl: string;
|
|
25
|
+
localDomain: string;
|
|
26
|
+
tunnelUrl: string | null;
|
|
27
|
+
tunnelDomain: string | null;
|
|
28
|
+
preferredUrl: string;
|
|
29
|
+
preferredDomain: string;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface Credentials {
|
|
33
|
+
accessKey: string;
|
|
34
|
+
secretKey: string;
|
|
35
|
+
expiry: Date;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface Container {
|
|
39
|
+
id: string;
|
|
40
|
+
name: string;
|
|
41
|
+
alias?: string;
|
|
42
|
+
tags: string[];
|
|
43
|
+
size: number;
|
|
44
|
+
addedAt: Date;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export interface ContainerSpec {
|
|
48
|
+
source?: string;
|
|
49
|
+
type?: 'docker' | 'podman' | 'tar' | 'mock';
|
|
50
|
+
name: string;
|
|
51
|
+
image?: string;
|
|
52
|
+
path?: string;
|
|
53
|
+
alias?: string;
|
|
54
|
+
tags?: string[];
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export interface ContainerInfo {
|
|
58
|
+
id: string;
|
|
59
|
+
name: string;
|
|
60
|
+
alias: string;
|
|
61
|
+
tags: string[];
|
|
62
|
+
size: number;
|
|
63
|
+
addedAt: Date;
|
|
64
|
+
source: string;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export interface RegistryInfo {
|
|
68
|
+
status: string;
|
|
69
|
+
serverId: string;
|
|
70
|
+
localUrl: string;
|
|
71
|
+
tunnelUrl: string | null;
|
|
72
|
+
credentials: Credentials;
|
|
73
|
+
startTime: Date;
|
|
74
|
+
containers: Container[];
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export interface TunneledContainerRegistryOptions {
|
|
78
|
+
port?: number;
|
|
79
|
+
serverName?: string;
|
|
80
|
+
tunnelService?: string;
|
|
81
|
+
tunnelOptions?: TunnelOptions;
|
|
82
|
+
credentials?: {
|
|
83
|
+
expiry?: number;
|
|
84
|
+
permissions?: string[];
|
|
85
|
+
customKey?: string;
|
|
86
|
+
customSecret?: string;
|
|
87
|
+
};
|
|
88
|
+
autoShutdown?: boolean;
|
|
89
|
+
shutdownOptions?: {
|
|
90
|
+
onCompletion?: boolean;
|
|
91
|
+
completionDelay?: number;
|
|
92
|
+
maxIdleTime?: number;
|
|
93
|
+
maxTotalTime?: number;
|
|
94
|
+
};
|
|
95
|
+
enableMonitoring?: boolean;
|
|
96
|
+
monitoringOptions?: {
|
|
97
|
+
updateInterval?: number;
|
|
98
|
+
enableDashboard?: boolean;
|
|
99
|
+
enableLogging?: boolean;
|
|
100
|
+
};
|
|
101
|
+
preferredEngine?: string;
|
|
102
|
+
containerType?: string;
|
|
103
|
+
engineOptions?: {
|
|
104
|
+
dockerSocket?: string;
|
|
105
|
+
podmanSocket?: string;
|
|
106
|
+
};
|
|
107
|
+
registryOptions?: {
|
|
108
|
+
enableCatalog?: boolean;
|
|
109
|
+
enableHealthCheck?: boolean;
|
|
110
|
+
customHeaders?: object;
|
|
111
|
+
};
|
|
112
|
+
enableLogging?: boolean;
|
|
113
|
+
logLevel?: string;
|
|
114
|
+
duration?: number;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export class TunneledContainerRegistry {
|
|
118
|
+
constructor(options?: TunneledContainerRegistryOptions);
|
|
119
|
+
|
|
120
|
+
// Core lifecycle
|
|
121
|
+
start(): Promise<RegistryInfo>;
|
|
122
|
+
stop(): Promise<void>;
|
|
123
|
+
|
|
124
|
+
// Container management
|
|
125
|
+
addContainer(containerSpec: ContainerSpec): Promise<ContainerInfo>;
|
|
126
|
+
addContainers(containerSpecs: ContainerSpec[]): Promise<ContainerInfo[]>;
|
|
127
|
+
removeContainer(nameOrId: string): Promise<void>;
|
|
128
|
+
listContainers(): Container[];
|
|
129
|
+
|
|
130
|
+
// Information
|
|
131
|
+
getRegistryInfo(): RegistryInfo;
|
|
132
|
+
getRegistryUrls(): Promise<RegistryUrls>;
|
|
133
|
+
getAccessCredentials(): Promise<Credentials>;
|
|
134
|
+
getDockerCommands(containerName?: string): Promise<object>;
|
|
135
|
+
getPodmanCommands(containerName?: string): Promise<object>;
|
|
136
|
+
getContainerCommands(containerName?: string): Promise<object>;
|
|
137
|
+
generateCommandHelp(options?: object): Promise<any>;
|
|
138
|
+
|
|
139
|
+
// Statistics
|
|
140
|
+
getStats(): object;
|
|
141
|
+
getRegistryStats(): object;
|
|
142
|
+
getContainerStats(nameOrId: string): object | null;
|
|
143
|
+
|
|
144
|
+
// Configuration
|
|
145
|
+
updateConfig(newConfig: Partial<TunneledContainerRegistryOptions>): Promise<void>;
|
|
146
|
+
refreshCredentials(options?: { expiry?: number }): Promise<Credentials>;
|
|
147
|
+
|
|
148
|
+
// Utility
|
|
149
|
+
healthCheck(): Promise<object>;
|
|
150
|
+
autoDiscoverContainers(options?: object): Promise<ContainerInfo[]>;
|
|
151
|
+
exportRegistryConfig(): Promise<object>;
|
|
152
|
+
|
|
153
|
+
// Event emitter
|
|
154
|
+
on(event: string, listener: (...args: any[]) => void): this;
|
|
155
|
+
once(event: string, listener: (...args: any[]) => void): this;
|
|
156
|
+
emit(event: string, ...args: any[]): boolean;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
// src/types/external.ts
|
|
2
|
+
|
|
3
|
+
// Commander.js types
|
|
4
|
+
export interface ICommander {
|
|
5
|
+
command(name: string): ICommand;
|
|
6
|
+
parse(argv?: string[]): void;
|
|
7
|
+
opts(): Record<string, unknown>;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface ICommand {
|
|
11
|
+
alias(alias: string): ICommand;
|
|
12
|
+
description(desc: string): ICommand;
|
|
13
|
+
// Register a subcommand on this command
|
|
14
|
+
command(name: string, opts?: { isDefault?: boolean }): ICommand;
|
|
15
|
+
// Overload for option without default value
|
|
16
|
+
option(flags: string, description: string): ICommand;
|
|
17
|
+
// Overload for option with default value but no parser function
|
|
18
|
+
option(flags: string, description: string, defaultValue: string | number | boolean): ICommand;
|
|
19
|
+
// Overload for option with parser function and optional default
|
|
20
|
+
option(
|
|
21
|
+
flags: string,
|
|
22
|
+
description: string,
|
|
23
|
+
fn: ((value: string, previous: unknown) => unknown) | RegExp,
|
|
24
|
+
defaultValue?: unknown
|
|
25
|
+
): ICommand;
|
|
26
|
+
action(fn: (...args: unknown[]) => void | Promise<void>): ICommand;
|
|
27
|
+
addHelpText(position: string, text: string): ICommand;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Logger types
|
|
31
|
+
export interface IKadiLogger {
|
|
32
|
+
log(message: string, ...args: unknown[]): void;
|
|
33
|
+
error(message: string, ...args: unknown[]): void;
|
|
34
|
+
warn(message: string, ...args: unknown[]): void;
|
|
35
|
+
info(message: string, ...args: unknown[]): void;
|
|
36
|
+
debug(message: string, ...args: unknown[]): void;
|
|
37
|
+
verbose(message: string, ...args: unknown[]): void;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Core types
|
|
41
|
+
export interface IKadiCore {
|
|
42
|
+
version: string;
|
|
43
|
+
config: Record<string, unknown>;
|
|
44
|
+
utils: {
|
|
45
|
+
readFile(path: string): Promise<string>;
|
|
46
|
+
writeFile(path: string, content: string): Promise<void>;
|
|
47
|
+
exists(path: string): Promise<boolean>;
|
|
48
|
+
};
|
|
49
|
+
}
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Core TypeScript interfaces for the KADI deploy profile system
|
|
3
|
+
*
|
|
4
|
+
* This file defines the type system for the profile-based deployment configuration
|
|
5
|
+
* that replaces the previous flag-based CLI approach. The system follows a three-tier
|
|
6
|
+
* precedence model: CLI flags > Profile settings > System defaults
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import type { ICommander, IKadiLogger, IKadiCore } from './types/external.js';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* DeployOptions represents raw CLI input before profile resolution
|
|
13
|
+
*
|
|
14
|
+
* This captures what the user provided via command-line flags. All fields are
|
|
15
|
+
* optional because users might rely on profile defaults or system defaults.
|
|
16
|
+
* The profile field indicates which profile to load from agent.json.
|
|
17
|
+
*/
|
|
18
|
+
export interface DeployOptions {
|
|
19
|
+
// Profile selection
|
|
20
|
+
profile?: string; // Name of the profile to use from agent.json
|
|
21
|
+
|
|
22
|
+
// CLI overrides - these take precedence over profile settings
|
|
23
|
+
target?: string;
|
|
24
|
+
network?: string;
|
|
25
|
+
engine?: string;
|
|
26
|
+
cert?: string;
|
|
27
|
+
useRemoteRegistry?: boolean;
|
|
28
|
+
registryDuration?: number;
|
|
29
|
+
tunnelService?: string; // Tunnel service (validated at runtime: kadi, ngrok, serveo, localtunnel)
|
|
30
|
+
interactive?: boolean;
|
|
31
|
+
dryRun?: boolean;
|
|
32
|
+
verbose?: boolean;
|
|
33
|
+
yes?: boolean;
|
|
34
|
+
showCommands?: boolean;
|
|
35
|
+
project?: string; // Path to project directory (defaults to cwd)
|
|
36
|
+
|
|
37
|
+
// Secrets injection options
|
|
38
|
+
secretTimeout?: number; // Timeout (ms) for waiting for agent to request secrets
|
|
39
|
+
autoApproveSecrets?: boolean; // Auto-approve secret requests without prompting
|
|
40
|
+
|
|
41
|
+
// Autonomous deployment options
|
|
42
|
+
autonomous?: boolean; // Enable fully autonomous deployment (no human interaction)
|
|
43
|
+
bidStrategy?: 'cheapest' | 'most-reliable' | 'balanced'; // Bid selection strategy for autonomous mode
|
|
44
|
+
bidMaxPrice?: number; // Maximum price per block in uAKT for bid filtering
|
|
45
|
+
requireAudited?: boolean; // Only accept bids from audited providers
|
|
46
|
+
secretsVault?: string; // Override vault name for secrets (default: from profile)
|
|
47
|
+
|
|
48
|
+
// Multi-instance deployment options
|
|
49
|
+
label?: string; // Human-readable label for this deployment instance (e.g. "broker-us-east")
|
|
50
|
+
instance?: string; // Target a specific deployment instance by ID
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* ResolvedDeployOptions represents the final configuration after merging
|
|
55
|
+
* CLI flags, profile settings, and system defaults
|
|
56
|
+
*
|
|
57
|
+
* This is what gets passed to deployment targets. All required fields have
|
|
58
|
+
* been resolved to concrete values, eliminating the need for target modules
|
|
59
|
+
* to handle undefined values or complex fallback logic.
|
|
60
|
+
*/
|
|
61
|
+
export interface ResolvedDeployOptions {
|
|
62
|
+
// Required fields - always have concrete values after resolution
|
|
63
|
+
target: 'local' | 'akash';
|
|
64
|
+
network: 'mainnet' | 'testnet';
|
|
65
|
+
engine: 'docker' | 'podman';
|
|
66
|
+
useRemoteRegistry: boolean;
|
|
67
|
+
registryDuration: number;
|
|
68
|
+
tunnelService: string;
|
|
69
|
+
interactive: boolean;
|
|
70
|
+
dryRun: boolean;
|
|
71
|
+
verbose: boolean;
|
|
72
|
+
yes: boolean;
|
|
73
|
+
showCommands: boolean;
|
|
74
|
+
project: string;
|
|
75
|
+
|
|
76
|
+
// Optional fields - may remain undefined if not specified anywhere
|
|
77
|
+
cert?: string; // Only defined if user provided cert path
|
|
78
|
+
profile?: string; // Profile name that was used (if any)
|
|
79
|
+
|
|
80
|
+
// Secrets injection options
|
|
81
|
+
secretTimeout?: number; // Timeout (ms) for waiting for agent to request secrets
|
|
82
|
+
autoApproveSecrets?: boolean; // Auto-approve secret requests without prompting
|
|
83
|
+
|
|
84
|
+
// Autonomous deployment options
|
|
85
|
+
autonomous?: boolean; // Enable fully autonomous deployment (no human interaction)
|
|
86
|
+
bidStrategy?: 'cheapest' | 'most-reliable' | 'balanced'; // Bid selection strategy for autonomous mode
|
|
87
|
+
bidMaxPrice?: number; // Maximum price per block in uAKT for bid filtering
|
|
88
|
+
requireAudited?: boolean; // Only accept bids from audited providers
|
|
89
|
+
secretsVault?: string; // Override vault name for secrets (default: from profile)
|
|
90
|
+
|
|
91
|
+
// Multi-instance deployment options
|
|
92
|
+
label?: string; // Human-readable label for this deployment instance
|
|
93
|
+
instance?: string; // Target a specific deployment instance by ID
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Resource configuration for Akash deployments
|
|
98
|
+
* // DO I neeD THIS?
|
|
99
|
+
*
|
|
100
|
+
* Defines compute resources needed for the deployment. These get translated
|
|
101
|
+
* into Akash SDL (Stack Definition Language) resource requirements.
|
|
102
|
+
*/
|
|
103
|
+
/**
|
|
104
|
+
* Persistent volume specification for Akash Network
|
|
105
|
+
*/
|
|
106
|
+
export interface PersistentVolumeSpec {
|
|
107
|
+
/** Volume name (must be unique within service) */
|
|
108
|
+
name: string;
|
|
109
|
+
/** Volume size (e.g., "10Gi") */
|
|
110
|
+
size: string;
|
|
111
|
+
/** Container mount path (e.g., "/data") */
|
|
112
|
+
mount: string;
|
|
113
|
+
/** Storage class: beta1 (HDD), beta2 (SSD), beta3 (NVMe), ram */
|
|
114
|
+
class?: 'beta1' | 'beta2' | 'beta3' | 'ram';
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Storage specification - supports both legacy and persistent formats
|
|
119
|
+
*
|
|
120
|
+
* **Legacy format (string):**
|
|
121
|
+
* Simple ephemeral storage that's wiped on restart
|
|
122
|
+
* Example: "2Gi"
|
|
123
|
+
*
|
|
124
|
+
* **Array format (persistent volumes):**
|
|
125
|
+
* First element: ephemeral storage size (required)
|
|
126
|
+
* Additional elements: named persistent volumes (optional)
|
|
127
|
+
* Example: ["512Mi", { name: "data", size: "10Gi", mount: "/data", class: "beta2" }]
|
|
128
|
+
*/
|
|
129
|
+
export type StorageSpec = string | [string, ...PersistentVolumeSpec[]];
|
|
130
|
+
|
|
131
|
+
export interface ResourceConfig {
|
|
132
|
+
cpu?: number; // CPU units (e.g., 0.5 for half a CPU)
|
|
133
|
+
memory?: string; // Memory with units (e.g., "512Mi", "2Gi")
|
|
134
|
+
storage?: StorageSpec; // Storage: simple string or array with persistent volumes
|
|
135
|
+
gpu?: {
|
|
136
|
+
units: number; // Number of GPU units
|
|
137
|
+
attributes: {
|
|
138
|
+
vendor?: string; // GPU vendor (e.g., "nvidia", "amd")
|
|
139
|
+
model?: string; // GPU model (e.g., "rtx3060", "a100")
|
|
140
|
+
};
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Complete agent configuration structure (agent.json)
|
|
146
|
+
*
|
|
147
|
+
* This represents the full agent.json file structure with support for both
|
|
148
|
+
* traditional service-based configuration and the new profile-based system.
|
|
149
|
+
* Variable interpolation is supported for ${name} and ${version} placeholders.
|
|
150
|
+
*/
|
|
151
|
+
export interface AgentConfig {
|
|
152
|
+
// Basic agent metadata
|
|
153
|
+
name: string; // Agent name (used for ${name} interpolation)
|
|
154
|
+
version: string; // Agent version (used for ${version} interpolation)
|
|
155
|
+
license?: string; // License identifier (e.g., "MIT")
|
|
156
|
+
description?: string; // Human-readable description
|
|
157
|
+
|
|
158
|
+
// Abilities configuration - defines what capabilities this agent has
|
|
159
|
+
abilities?: Record<string, string>; // Map of ability name to version (e.g., "data-processor": "1.2.0")
|
|
160
|
+
|
|
161
|
+
// Scripts configuration - lifecycle hooks for the agent
|
|
162
|
+
scripts?: Record<string, string>; // Map of script name to command (e.g., "setup": "npm install")
|
|
163
|
+
|
|
164
|
+
// Build configuration - compatibility with kadi-build profiles
|
|
165
|
+
build?: Record<string, any>; // Build profiles from kadi-build system
|
|
166
|
+
|
|
167
|
+
// Deploy configuration - the heart of the deploy profile system
|
|
168
|
+
// Raw JSON profiles - each target loads and validates its own profile format
|
|
169
|
+
deploy?: Record<string, any>;
|
|
170
|
+
|
|
171
|
+
// Broker configuration - WebSocket URLs for KADI broker connections
|
|
172
|
+
// Used for secrets injection to share secrets with deployed agents
|
|
173
|
+
brokers?: Record<string, string>; // Map of broker name to WebSocket URL (e.g., "default": "ws://localhost:8080/kadi")
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* KADI CLI context interface
|
|
178
|
+
*
|
|
179
|
+
* Provides access to core KADI CLI functionality like command registration,
|
|
180
|
+
* logging, and core utilities. Passed to all KADI plugins and modules.
|
|
181
|
+
*/
|
|
182
|
+
export interface IKadiContext {
|
|
183
|
+
commander: ICommander; // Commander.js instance for CLI registration
|
|
184
|
+
logger: IKadiLogger; // KADI logging utilities
|
|
185
|
+
core: IKadiCore; // Core KADI functionality and utilities
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Deployment context passed to target modules
|
|
190
|
+
*
|
|
191
|
+
* This is the complete context that deployment targets (local.js, akash.js)
|
|
192
|
+
* receive. It includes the original KADI context plus deployment-specific
|
|
193
|
+
* information like the resolved configuration and agent metadata.
|
|
194
|
+
*/
|
|
195
|
+
export interface DeploymentContext extends IKadiContext {
|
|
196
|
+
projectRoot: string; // Absolute path to the project being deployed
|
|
197
|
+
agent: AgentConfig; // Parsed agent.json configuration
|
|
198
|
+
flags: ResolvedDeployOptions; // Final deployment configuration after resolution
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Target module interface
|
|
203
|
+
*
|
|
204
|
+
* All deployment targets must implement this interface. The deploy method
|
|
205
|
+
* receives a complete DeploymentContext and is responsible for executing
|
|
206
|
+
* the deployment according to the resolved configuration.
|
|
207
|
+
*/
|
|
208
|
+
export interface DeployTarget {
|
|
209
|
+
description: string;
|
|
210
|
+
deploy(ctx: DeploymentContext): Promise<void>;
|
|
211
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AKT Price Fetcher
|
|
3
|
+
*
|
|
4
|
+
* Utility for fetching current AKT token price in USD from CoinGecko API.
|
|
5
|
+
* Used for displaying USD pricing in bid selection and deployment summaries.
|
|
6
|
+
*
|
|
7
|
+
* @module utils/akt-price
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Fetch current AKT price in USD from CoinGecko
|
|
12
|
+
*
|
|
13
|
+
* Makes a request to CoinGecko's public API to get the current market price
|
|
14
|
+
* of Akash Network (AKT) token in USD.
|
|
15
|
+
*
|
|
16
|
+
* **API Details:**
|
|
17
|
+
* - Endpoint: https://api.coingecko.com/api/v3/simple/price
|
|
18
|
+
* - No API key required for basic usage
|
|
19
|
+
* - Rate limit: ~10-50 requests/minute (free tier)
|
|
20
|
+
* - Response format: { "akash-network": { "usd": 0.45 } }
|
|
21
|
+
*
|
|
22
|
+
* **Error Handling:**
|
|
23
|
+
* Returns undefined if the fetch fails for any reason (network error, API down, etc.)
|
|
24
|
+
* This allows the caller to gracefully degrade to showing only AKT pricing.
|
|
25
|
+
*
|
|
26
|
+
* @returns Promise resolving to USD price per AKT, or undefined if unavailable
|
|
27
|
+
*
|
|
28
|
+
* @example Basic usage
|
|
29
|
+
* ```typescript
|
|
30
|
+
* const aktPrice = await fetchAktPriceUSD();
|
|
31
|
+
* if (aktPrice) {
|
|
32
|
+
* console.log(`1 AKT = $${aktPrice.toFixed(2)}`);
|
|
33
|
+
* } else {
|
|
34
|
+
* console.log('AKT price unavailable');
|
|
35
|
+
* }
|
|
36
|
+
* ```
|
|
37
|
+
*
|
|
38
|
+
* @example With bid selector
|
|
39
|
+
* ```typescript
|
|
40
|
+
* const aktPrice = await fetchAktPriceUSD();
|
|
41
|
+
* const selected = await selectBidInteractively(bids, aktPrice);
|
|
42
|
+
* ```
|
|
43
|
+
*/
|
|
44
|
+
export async function fetchAktPriceUSD(): Promise<number | undefined> {
|
|
45
|
+
try {
|
|
46
|
+
const response = await fetch(
|
|
47
|
+
'https://api.coingecko.com/api/v3/simple/price?ids=akash-network&vs_currencies=usd',
|
|
48
|
+
{
|
|
49
|
+
// 5 second timeout
|
|
50
|
+
signal: AbortSignal.timeout(5000),
|
|
51
|
+
headers: {
|
|
52
|
+
'Accept': 'application/json',
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
if (!response.ok) {
|
|
58
|
+
return undefined;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const data = await response.json();
|
|
62
|
+
const price = data?.['akash-network']?.['usd'];
|
|
63
|
+
|
|
64
|
+
// Validate price is a positive number
|
|
65
|
+
if (typeof price === 'number' && price > 0 && Number.isFinite(price)) {
|
|
66
|
+
return price;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return undefined;
|
|
70
|
+
} catch (error) {
|
|
71
|
+
// Silent failure - caller can handle missing price gracefully
|
|
72
|
+
return undefined;
|
|
73
|
+
}
|
|
74
|
+
}
|