@kadi.build/deploy-ability 0.0.7 → 0.0.9
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 +72 -0
- package/README.md +240 -0
- package/dist/targets/akash/deployer.d.ts +64 -3
- package/dist/targets/akash/deployer.d.ts.map +1 -1
- package/dist/targets/akash/deployer.js +170 -4
- package/dist/targets/akash/deployer.js.map +1 -1
- package/dist/targets/akash/index.d.ts +86 -2
- package/dist/targets/akash/index.d.ts.map +1 -1
- package/dist/targets/akash/index.js +49 -2
- package/dist/targets/akash/index.js.map +1 -1
- package/dist/targets/akash/secrets-provider.d.ts +189 -0
- package/dist/targets/akash/secrets-provider.d.ts.map +1 -0
- package/dist/targets/akash/secrets-provider.js +249 -0
- package/dist/targets/akash/secrets-provider.js.map +1 -0
- package/dist/targets/akash/types.d.ts +96 -0
- package/dist/targets/akash/types.d.ts.map +1 -1
- package/dist/targets/akash/wallet-manager.d.ts +21 -3
- package/dist/targets/akash/wallet-manager.d.ts.map +1 -1
- package/dist/targets/akash/wallet-manager.js +137 -8
- package/dist/targets/akash/wallet-manager.js.map +1 -1
- package/dist/types/index.d.ts +1 -1
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js.map +1 -1
- package/dist/types/options.d.ts +74 -0
- package/dist/types/options.d.ts.map +1 -1
- package/dist/utils/registry/manager.d.ts +10 -9
- package/dist/utils/registry/manager.d.ts.map +1 -1
- package/dist/utils/registry/manager.js +28 -18
- package/dist/utils/registry/manager.js.map +1 -1
- package/dist/utils/registry/setup.d.ts +2 -2
- package/dist/utils/registry/setup.d.ts.map +1 -1
- package/dist/utils/registry/setup.js +7 -5
- package/dist/utils/registry/setup.js.map +1 -1
- package/dist/utils/registry/transformer.d.ts +1 -1
- package/dist/utils/registry/transformer.js +1 -1
- package/dist/utils/registry/types.d.ts +36 -12
- package/dist/utils/registry/types.d.ts.map +1 -1
- package/package.json +3 -2
package/.env-example
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# Local & Remote File Manager Configuration
|
|
2
|
+
# Copy this file to .env and customize the values
|
|
3
|
+
|
|
4
|
+
# =============================================================================
|
|
5
|
+
# LOCAL FILE SYSTEM CONFIGURATION
|
|
6
|
+
# =============================================================================
|
|
7
|
+
|
|
8
|
+
# Directory Paths (customize these for your setup)
|
|
9
|
+
DEFAULT_LOCAL_ROOT=/Users/yourname/Documents
|
|
10
|
+
DEFAULT_UPLOAD_DIRECTORY=./uploads
|
|
11
|
+
DEFAULT_DOWNLOAD_DIRECTORY=./downloads
|
|
12
|
+
DEFAULT_TEMP_DIRECTORY=./temp
|
|
13
|
+
|
|
14
|
+
# File Operation Limits
|
|
15
|
+
MAX_FILE_SIZE=1073741824
|
|
16
|
+
CHUNK_SIZE=8388608
|
|
17
|
+
MAX_RETRY_ATTEMPTS=3
|
|
18
|
+
TIMEOUT_MS=30000
|
|
19
|
+
|
|
20
|
+
# Performance Settings
|
|
21
|
+
MAX_CONCURRENT_OPERATIONS=5
|
|
22
|
+
ENABLE_PROGRESS_TRACKING=true
|
|
23
|
+
ENABLE_CHECKSUM_VERIFICATION=true
|
|
24
|
+
|
|
25
|
+
# Security Settings
|
|
26
|
+
ALLOW_SYMLINKS=false
|
|
27
|
+
RESTRICT_TO_BASE_PATH=true
|
|
28
|
+
MAX_PATH_LENGTH=255
|
|
29
|
+
|
|
30
|
+
# =============================================================================
|
|
31
|
+
# FEATURE CONFIGURATION (Future Buckets)
|
|
32
|
+
# =============================================================================
|
|
33
|
+
|
|
34
|
+
# File Watching (Bucket 2)
|
|
35
|
+
WATCH_ENABLED=true
|
|
36
|
+
WATCH_RECURSIVE=true
|
|
37
|
+
WATCH_IGNORE_DOTFILES=true
|
|
38
|
+
WATCH_DEBOUNCE_MS=100
|
|
39
|
+
|
|
40
|
+
# Compression (Bucket 3)
|
|
41
|
+
COMPRESSION_ENABLED=true
|
|
42
|
+
COMPRESSION_LEVEL=6
|
|
43
|
+
COMPRESSION_FORMAT=zip
|
|
44
|
+
|
|
45
|
+
# Tunneling (Bucket 4) - Multi-Service Configuration
|
|
46
|
+
TUNNEL_SERVICE=kadi
|
|
47
|
+
TUNNEL_FALLBACK_SERVICES=ngrok,serveo,localtunnel
|
|
48
|
+
TUNNEL_AUTH_TOKEN=
|
|
49
|
+
TUNNEL_SUBDOMAIN=
|
|
50
|
+
TUNNEL_REGION=us
|
|
51
|
+
TUNNEL_AUTO_FALLBACK=true
|
|
52
|
+
|
|
53
|
+
# Ngrok-specific configuration
|
|
54
|
+
NGROK_AUTH_TOKEN=your-ngrok-auth-token
|
|
55
|
+
NGROK_REGION=us
|
|
56
|
+
NGROK_PROTOCOL=http
|
|
57
|
+
|
|
58
|
+
# KĀDI Tunnel Configuration
|
|
59
|
+
KADI_TUNNEL_SERVER=broker.kadi.build
|
|
60
|
+
KADI_TUNNEL_SSH_PORT=2200
|
|
61
|
+
KADI_TUNNEL_TOKEN=your-kadi-tunnel-token
|
|
62
|
+
KADI_TUNNEL_DOMAIN=tunnel.kadi.build
|
|
63
|
+
KADI_TUNNEL_MODE=frpc
|
|
64
|
+
KADI_TUNNEL_TRANSPORT=wss
|
|
65
|
+
KADI_TUNNEL_WSS_HOST=tunnel-control.kadi.build
|
|
66
|
+
KADI_AGENT_ID=kadi
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
# Logging
|
|
70
|
+
LOG_LEVEL=info
|
|
71
|
+
ENABLE_FILE_LOGGING=false
|
|
72
|
+
LOG_FILE_PATH=./logs/application.log
|
package/README.md
CHANGED
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
**Type-Safe** - Complete TypeScript support with zero `any` types
|
|
17
17
|
**Delightful API** - Simple for common tasks, powerful for advanced use cases
|
|
18
18
|
**Flexible Wallet Support** - Human approval (WalletConnect) or autonomous signing (agents)
|
|
19
|
+
**Autonomous Mode** - Fully automated deployments with secure secrets integration
|
|
19
20
|
**Well-Documented** - Comprehensive JSDoc with IntelliSense
|
|
20
21
|
**Production-Ready** - Handles Akash Network complexity elegantly
|
|
21
22
|
|
|
@@ -167,6 +168,30 @@ if (result.success) {
|
|
|
167
168
|
}
|
|
168
169
|
```
|
|
169
170
|
|
|
171
|
+
### Autonomous Deployment (Agent-Controlled)
|
|
172
|
+
|
|
173
|
+
For fully automated deployments with secrets integration:
|
|
174
|
+
|
|
175
|
+
```typescript
|
|
176
|
+
import { createSecretsProvider, deployToAkash } from 'deploy-ability';
|
|
177
|
+
|
|
178
|
+
// In a KADI agent:
|
|
179
|
+
const secrets = createSecretsProvider(this.secrets);
|
|
180
|
+
|
|
181
|
+
const result = await deployToAkash({
|
|
182
|
+
autonomous: { secrets, bidStrategy: 'cheapest' },
|
|
183
|
+
projectRoot: process.cwd(),
|
|
184
|
+
profile: 'production',
|
|
185
|
+
network: 'mainnet',
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
if (result.success) {
|
|
189
|
+
console.log(`Deployed! DSEQ: ${result.data.dseq}`);
|
|
190
|
+
}
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
See [Autonomous Deployment with Secrets Integration](#autonomous-deployment-with-secrets-integration) for full details.
|
|
194
|
+
|
|
170
195
|
### Deploy to Local Docker
|
|
171
196
|
|
|
172
197
|
```typescript
|
|
@@ -329,6 +354,221 @@ class SelfDeployingAgent {
|
|
|
329
354
|
|
|
330
355
|
---
|
|
331
356
|
|
|
357
|
+
## Autonomous Deployment with Secrets Integration
|
|
358
|
+
|
|
359
|
+
For fully automated deployments, deploy-ability integrates with KADI's `secret-ability` to securely retrieve wallet mnemonics from encrypted vaults. This enables agents to deploy without any user interaction while keeping secrets secure.
|
|
360
|
+
|
|
361
|
+
### How It Works
|
|
362
|
+
|
|
363
|
+
```mermaid
|
|
364
|
+
sequenceDiagram
|
|
365
|
+
participant Agent as KADI Agent
|
|
366
|
+
participant Secrets as secret-ability
|
|
367
|
+
participant Keychain as OS Keychain
|
|
368
|
+
participant Deploy as deploy-ability
|
|
369
|
+
participant Akash as Akash Network
|
|
370
|
+
|
|
371
|
+
Agent->>Secrets: Get mnemonic from vault
|
|
372
|
+
Secrets->>Keychain: Get master key
|
|
373
|
+
Keychain-->>Secrets: Master key
|
|
374
|
+
Secrets-->>Agent: Decrypted mnemonic
|
|
375
|
+
Agent->>Deploy: deployToAkash({ autonomous: { secrets } })
|
|
376
|
+
Deploy->>Akash: Create deployment (auto-sign)
|
|
377
|
+
Akash-->>Deploy: Deployment result
|
|
378
|
+
Deploy-->>Agent: Success!
|
|
379
|
+
```
|
|
380
|
+
|
|
381
|
+
### Vault Configuration
|
|
382
|
+
|
|
383
|
+
Secrets are stored in a **global vault** at the machine level:
|
|
384
|
+
|
|
385
|
+
| Setting | Value |
|
|
386
|
+
|---------|-------|
|
|
387
|
+
| Config Path | `~/.kadi/secrets/config.toml` |
|
|
388
|
+
| Vault Name | `global` |
|
|
389
|
+
| Vault Type | `age` (ChaCha20-Poly1305 encryption) |
|
|
390
|
+
| Master Key | Stored in OS Keychain (macOS Keychain, GNOME Keyring, etc.) |
|
|
391
|
+
|
|
392
|
+
### Secret Keys
|
|
393
|
+
|
|
394
|
+
| Key | Description |
|
|
395
|
+
|-----|-------------|
|
|
396
|
+
| `AKASH_WALLET` | BIP39 mnemonic phrase (12 or 24 words) |
|
|
397
|
+
| `akash-tls-certificate` | Cached TLS certificate for provider communication |
|
|
398
|
+
| `akash-wallet-password` | Optional BIP39 passphrase |
|
|
399
|
+
|
|
400
|
+
### Setup: Store Your Mnemonic
|
|
401
|
+
|
|
402
|
+
Before using autonomous deployment, store your mnemonic in the global vault:
|
|
403
|
+
|
|
404
|
+
```bash
|
|
405
|
+
# Using KADI CLI
|
|
406
|
+
kadi secrets set --vault global --key AKASH_WALLET --value "your 12 or 24 word mnemonic phrase here"
|
|
407
|
+
```
|
|
408
|
+
|
|
409
|
+
Or programmatically with secret-ability:
|
|
410
|
+
|
|
411
|
+
```typescript
|
|
412
|
+
// 1. Create the global vault (one-time)
|
|
413
|
+
await secrets.invoke('config.createVault', {
|
|
414
|
+
configPath: '~/.kadi/secrets/config.toml',
|
|
415
|
+
name: 'global',
|
|
416
|
+
type: 'age'
|
|
417
|
+
});
|
|
418
|
+
|
|
419
|
+
// 2. Store the mnemonic
|
|
420
|
+
await secrets.invoke('set', {
|
|
421
|
+
configPath: '~/.kadi/secrets/config.toml',
|
|
422
|
+
vault: 'global',
|
|
423
|
+
key: 'AKASH_WALLET',
|
|
424
|
+
value: 'your 12 or 24 word mnemonic phrase here'
|
|
425
|
+
});
|
|
426
|
+
```
|
|
427
|
+
|
|
428
|
+
### Usage: Autonomous Deployment
|
|
429
|
+
|
|
430
|
+
#### Option 1: Using `createSecretsProvider` (Recommended)
|
|
431
|
+
|
|
432
|
+
Integrates directly with KADI's secret-ability:
|
|
433
|
+
|
|
434
|
+
```typescript
|
|
435
|
+
import {
|
|
436
|
+
createSecretsProvider,
|
|
437
|
+
deployToAkash
|
|
438
|
+
} from 'deploy-ability';
|
|
439
|
+
|
|
440
|
+
// In a KADI agent context:
|
|
441
|
+
class DeploymentAgent {
|
|
442
|
+
async deploy(profile: string) {
|
|
443
|
+
// Create secrets provider from secret-ability client
|
|
444
|
+
const secrets = createSecretsProvider(this.secrets);
|
|
445
|
+
|
|
446
|
+
// Deploy autonomously - no user interaction needed!
|
|
447
|
+
const result = await deployToAkash({
|
|
448
|
+
autonomous: {
|
|
449
|
+
secrets,
|
|
450
|
+
bidStrategy: 'cheapest', // or 'most-reliable'
|
|
451
|
+
},
|
|
452
|
+
projectRoot: process.cwd(),
|
|
453
|
+
profile,
|
|
454
|
+
network: 'mainnet',
|
|
455
|
+
});
|
|
456
|
+
|
|
457
|
+
if (result.success) {
|
|
458
|
+
console.log(`Deployed! DSEQ: ${result.data.dseq}`);
|
|
459
|
+
console.log(`Provider: ${result.data.providerUri}`);
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
return result;
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
```
|
|
466
|
+
|
|
467
|
+
#### Option 2: Using `createSimpleMnemonicProvider` (Testing)
|
|
468
|
+
|
|
469
|
+
For testing or when secret-ability is not available:
|
|
470
|
+
|
|
471
|
+
```typescript
|
|
472
|
+
import {
|
|
473
|
+
createSimpleMnemonicProvider,
|
|
474
|
+
deployToAkash
|
|
475
|
+
} from 'deploy-ability';
|
|
476
|
+
|
|
477
|
+
// ⚠️ For testing only - mnemonic stored in memory
|
|
478
|
+
const secrets = createSimpleMnemonicProvider(
|
|
479
|
+
'test test test test test test test test test test test junk'
|
|
480
|
+
);
|
|
481
|
+
|
|
482
|
+
const result = await deployToAkash({
|
|
483
|
+
autonomous: {
|
|
484
|
+
secrets,
|
|
485
|
+
bidStrategy: 'cheapest',
|
|
486
|
+
},
|
|
487
|
+
projectRoot: './my-app',
|
|
488
|
+
profile: 'production',
|
|
489
|
+
network: 'testnet', // Use testnet for testing!
|
|
490
|
+
});
|
|
491
|
+
```
|
|
492
|
+
|
|
493
|
+
#### Option 3: Custom SecretsProvider
|
|
494
|
+
|
|
495
|
+
Implement the `SecretsProvider` interface for custom secret backends:
|
|
496
|
+
|
|
497
|
+
```typescript
|
|
498
|
+
import { deployToAkash, SecretsProvider } from 'deploy-ability';
|
|
499
|
+
|
|
500
|
+
// Custom provider (e.g., AWS Secrets Manager, HashiCorp Vault)
|
|
501
|
+
const customSecrets: SecretsProvider = {
|
|
502
|
+
async getMnemonic() {
|
|
503
|
+
// Fetch from your secret backend
|
|
504
|
+
return await mySecretManager.getSecret('akash-wallet-mnemonic');
|
|
505
|
+
},
|
|
506
|
+
|
|
507
|
+
async getCertificate() {
|
|
508
|
+
// Optional: return cached certificate
|
|
509
|
+
const cert = await mySecretManager.getSecret('akash-certificate');
|
|
510
|
+
return cert ? JSON.parse(cert) : null;
|
|
511
|
+
},
|
|
512
|
+
|
|
513
|
+
async storeCertificate(cert) {
|
|
514
|
+
// Optional: cache the certificate
|
|
515
|
+
await mySecretManager.setSecret('akash-certificate', JSON.stringify(cert));
|
|
516
|
+
},
|
|
517
|
+
};
|
|
518
|
+
|
|
519
|
+
const result = await deployToAkash({
|
|
520
|
+
autonomous: { secrets: customSecrets, bidStrategy: 'cheapest' },
|
|
521
|
+
projectRoot: './my-app',
|
|
522
|
+
profile: 'production',
|
|
523
|
+
network: 'mainnet',
|
|
524
|
+
});
|
|
525
|
+
```
|
|
526
|
+
|
|
527
|
+
### Bid Selection Strategies
|
|
528
|
+
|
|
529
|
+
When deploying autonomously, you must specify a bid selection strategy:
|
|
530
|
+
|
|
531
|
+
| Strategy | Description |
|
|
532
|
+
|----------|-------------|
|
|
533
|
+
| `'cheapest'` | Select the lowest price bid |
|
|
534
|
+
| `'most-reliable'` | Select the bid with highest provider uptime |
|
|
535
|
+
| `'balanced'` | Balance between price and provider reliability |
|
|
536
|
+
|
|
537
|
+
You can also add filters:
|
|
538
|
+
|
|
539
|
+
```typescript
|
|
540
|
+
autonomous: {
|
|
541
|
+
secrets,
|
|
542
|
+
bidStrategy: 'cheapest',
|
|
543
|
+
bidFilter: {
|
|
544
|
+
maxPricePerMonth: { uakt: 5_000_000 }, // Max 5 AKT/month
|
|
545
|
+
requireAudited: true, // Only audited providers
|
|
546
|
+
minUptime: { value: 0.95, period: '7d' }, // 95% uptime over 7 days
|
|
547
|
+
},
|
|
548
|
+
}
|
|
549
|
+
```
|
|
550
|
+
|
|
551
|
+
### Exported Constants
|
|
552
|
+
|
|
553
|
+
For programmatic access to configuration:
|
|
554
|
+
|
|
555
|
+
```typescript
|
|
556
|
+
import {
|
|
557
|
+
AKASH_VAULT_NAME, // 'global'
|
|
558
|
+
GLOBAL_CONFIG_PATH, // '~/.kadi/secrets/config.toml'
|
|
559
|
+
AKASH_SECRET_KEYS, // { WALLET_MNEMONIC: 'AKASH_WALLET', ... }
|
|
560
|
+
} from 'deploy-ability';
|
|
561
|
+
```
|
|
562
|
+
|
|
563
|
+
### Security Model
|
|
564
|
+
|
|
565
|
+
1. **Encrypted at Rest**: Secrets are encrypted with ChaCha20-Poly1305 in `config.toml`
|
|
566
|
+
2. **Master Key in Keychain**: The encryption key is stored in the OS keychain (never in files)
|
|
567
|
+
3. **No Plaintext**: Mnemonics are never written to disk in plaintext
|
|
568
|
+
4. **Per-Machine**: Secrets are tied to the machine's keychain - can't be copied
|
|
569
|
+
|
|
570
|
+
---
|
|
571
|
+
|
|
332
572
|
## Error Handling
|
|
333
573
|
|
|
334
574
|
deploy-ability uses a **Result type pattern** for predictable error handling:
|
|
@@ -12,24 +12,29 @@
|
|
|
12
12
|
*
|
|
13
13
|
* @module targets/akash/deployer
|
|
14
14
|
*/
|
|
15
|
-
import { type AkashDeploymentOptions, type AkashDeploymentResult } from '../../types/index.js';
|
|
15
|
+
import { type AkashDeploymentOptions, type AkashDeploymentResult, type AutonomousDeploymentConfig } from '../../types/index.js';
|
|
16
16
|
import type { BidSelector } from './bids.js';
|
|
17
17
|
import type { AkashProviderTlsCertificate, WalletContext } from './types.js';
|
|
18
18
|
/**
|
|
19
19
|
* Additional parameters required to execute a deployment
|
|
20
20
|
*
|
|
21
|
+
* Supports two modes:
|
|
22
|
+
* 1. **Manual mode**: Provide `wallet`, `certificate`, and `bidSelector` directly
|
|
23
|
+
* 2. **Autonomous mode**: Provide `autonomous` config for fully automated deployment
|
|
24
|
+
*
|
|
21
25
|
* For dry-run mode, wallet and certificate are optional (only SDL generation needed).
|
|
22
|
-
* For actual deployments, wallet, certificate, and bidSelector are all required.
|
|
23
26
|
*/
|
|
24
27
|
export interface AkashDeploymentExecution extends AkashDeploymentOptions {
|
|
25
28
|
readonly wallet?: WalletContext;
|
|
26
29
|
readonly certificate?: AkashProviderTlsCertificate;
|
|
27
30
|
/**
|
|
28
|
-
* Bid selection function
|
|
31
|
+
* Bid selection function (manual mode)
|
|
29
32
|
*
|
|
30
33
|
* Called with all available bids after waiting for provider responses.
|
|
31
34
|
* Must return the selected bid or null if none are acceptable.
|
|
32
35
|
*
|
|
36
|
+
* Not required when using `autonomous` mode.
|
|
37
|
+
*
|
|
33
38
|
* @example
|
|
34
39
|
* ```typescript
|
|
35
40
|
* import { deployToAkash, selectCheapestBid} from '@kadi.build/deploy-ability/akash';
|
|
@@ -49,9 +54,65 @@ export interface AkashDeploymentExecution extends AkashDeploymentOptions {
|
|
|
49
54
|
* @default 180000 (3 minutes)
|
|
50
55
|
*/
|
|
51
56
|
readonly bidTimeout?: number;
|
|
57
|
+
/**
|
|
58
|
+
* Autonomous deployment configuration
|
|
59
|
+
*
|
|
60
|
+
* When provided, enables fully automated deployment without user interaction:
|
|
61
|
+
* - Wallet is created from mnemonic provided by SecretsProvider
|
|
62
|
+
* - Certificate is retrieved from cache or auto-generated
|
|
63
|
+
* - Bids are selected algorithmically based on strategy
|
|
64
|
+
*
|
|
65
|
+
* This is mutually exclusive with `wallet`, `certificate`, and `bidSelector`.
|
|
66
|
+
*
|
|
67
|
+
* @example
|
|
68
|
+
* ```typescript
|
|
69
|
+
* const result = await deployToAkash({
|
|
70
|
+
* projectRoot: './',
|
|
71
|
+
* profile: 'production',
|
|
72
|
+
* autonomous: {
|
|
73
|
+
* secrets: {
|
|
74
|
+
* getMnemonic: () => vault.getSecret('wallet-mnemonic'),
|
|
75
|
+
* getCertificate: () => storage.get('akash-cert'),
|
|
76
|
+
* storeCertificate: (cert) => storage.set('akash-cert', cert),
|
|
77
|
+
* },
|
|
78
|
+
* bidStrategy: 'cheapest',
|
|
79
|
+
* }
|
|
80
|
+
* });
|
|
81
|
+
* ```
|
|
82
|
+
*/
|
|
83
|
+
readonly autonomous?: AutonomousDeploymentConfig;
|
|
52
84
|
}
|
|
53
85
|
/**
|
|
54
86
|
* Executes a full Akash deployment.
|
|
87
|
+
*
|
|
88
|
+
* Supports two modes:
|
|
89
|
+
* 1. **Manual mode**: Provide `wallet`, `certificate`, and `bidSelector` directly
|
|
90
|
+
* 2. **Autonomous mode**: Provide `autonomous` config for fully automated deployment
|
|
91
|
+
*
|
|
92
|
+
* @example Manual mode
|
|
93
|
+
* ```typescript
|
|
94
|
+
* const result = await deployToAkash({
|
|
95
|
+
* wallet: myWallet,
|
|
96
|
+
* certificate: myCert,
|
|
97
|
+
* bidSelector: selectCheapestBid,
|
|
98
|
+
* projectRoot: './',
|
|
99
|
+
* profile: 'production',
|
|
100
|
+
* });
|
|
101
|
+
* ```
|
|
102
|
+
*
|
|
103
|
+
* @example Autonomous mode (agent-controlled)
|
|
104
|
+
* ```typescript
|
|
105
|
+
* const result = await deployToAkash({
|
|
106
|
+
* projectRoot: './',
|
|
107
|
+
* profile: 'production',
|
|
108
|
+
* autonomous: {
|
|
109
|
+
* secrets: {
|
|
110
|
+
* getMnemonic: () => vault.getSecret('wallet-mnemonic'),
|
|
111
|
+
* },
|
|
112
|
+
* bidStrategy: 'cheapest',
|
|
113
|
+
* }
|
|
114
|
+
* });
|
|
115
|
+
* ```
|
|
55
116
|
*/
|
|
56
117
|
export declare function deployToAkash(params: AkashDeploymentExecution): Promise<AkashDeploymentResult>;
|
|
57
118
|
//# sourceMappingURL=deployer.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"deployer.d.ts","sourceRoot":"","sources":["../../../src/targets/akash/deployer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAGH,OAAO,EAGL,KAAK,sBAAsB,EAC3B,KAAK,qBAAqB,
|
|
1
|
+
{"version":3,"file":"deployer.d.ts","sourceRoot":"","sources":["../../../src/targets/akash/deployer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAGH,OAAO,EAGL,KAAK,sBAAsB,EAC3B,KAAK,qBAAqB,EAI1B,KAAK,0BAA0B,EAGhC,MAAM,sBAAsB,CAAC;AAQ9B,OAAO,KAAK,EAAe,WAAW,EAAe,MAAM,WAAW,CAAC;AAoBvE,OAAO,KAAK,EAAE,2BAA2B,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAyF7E;;;;;;;;GAQG;AACH,MAAM,WAAW,wBAAyB,SAAQ,sBAAsB;IACtE,QAAQ,CAAC,MAAM,CAAC,EAAE,aAAa,CAAC;IAChC,QAAQ,CAAC,WAAW,CAAC,EAAE,2BAA2B,CAAC;IAEnD;;;;;;;;;;;;;;;;;;;;OAoBG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,WAAW,CAAC;IAEnC;;;OAGG;IACH,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAE7B;;;;;;;;;;;;;;;;;;;;;;;;;OAyBG;IACH,QAAQ,CAAC,UAAU,CAAC,EAAE,0BAA0B,CAAC;CAClD;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,wBAAsB,aAAa,CACjC,MAAM,EAAE,wBAAwB,GAC/B,OAAO,CAAC,qBAAqB,CAAC,CA+lBhC"}
|
|
@@ -16,12 +16,15 @@ import Long from 'long';
|
|
|
16
16
|
import { success, failure, createWalletAddress, createDeploymentSequence, } from '../../types/index.js';
|
|
17
17
|
import { defaultLogger } from '../../utils/logger.js';
|
|
18
18
|
import { loadProfile } from '../../utils/profile-loader.js';
|
|
19
|
+
import { selectCheapestBid, selectMostReliableBid, selectBalancedBid, filterBids, } from './bids.js';
|
|
19
20
|
import { sendManifestToProvider, fetchProviderLeaseStatus, } from './provider-manager.js';
|
|
20
21
|
import { AkashClient } from './client.js';
|
|
21
22
|
import { waitForContainersRunning } from './lease-monitor.js';
|
|
22
23
|
import { LeasePrice } from './pricing.js';
|
|
23
24
|
import { generateAkashSdl, createSdlObject, } from './sdl-generator.js';
|
|
24
25
|
import { DeploymentError, getErrorMessage } from '../../errors/index.js';
|
|
26
|
+
import { createWalletFromMnemonic } from './wallet-manager.js';
|
|
27
|
+
import { CertificateManager } from './certificate-manager.js';
|
|
25
28
|
/**
|
|
26
29
|
* Intelligent blacklist resolution with progressive disclosure
|
|
27
30
|
*
|
|
@@ -85,9 +88,38 @@ function resolveBlacklist(profileBlacklist, optionsBlacklist, logger) {
|
|
|
85
88
|
}
|
|
86
89
|
/**
|
|
87
90
|
* Executes a full Akash deployment.
|
|
91
|
+
*
|
|
92
|
+
* Supports two modes:
|
|
93
|
+
* 1. **Manual mode**: Provide `wallet`, `certificate`, and `bidSelector` directly
|
|
94
|
+
* 2. **Autonomous mode**: Provide `autonomous` config for fully automated deployment
|
|
95
|
+
*
|
|
96
|
+
* @example Manual mode
|
|
97
|
+
* ```typescript
|
|
98
|
+
* const result = await deployToAkash({
|
|
99
|
+
* wallet: myWallet,
|
|
100
|
+
* certificate: myCert,
|
|
101
|
+
* bidSelector: selectCheapestBid,
|
|
102
|
+
* projectRoot: './',
|
|
103
|
+
* profile: 'production',
|
|
104
|
+
* });
|
|
105
|
+
* ```
|
|
106
|
+
*
|
|
107
|
+
* @example Autonomous mode (agent-controlled)
|
|
108
|
+
* ```typescript
|
|
109
|
+
* const result = await deployToAkash({
|
|
110
|
+
* projectRoot: './',
|
|
111
|
+
* profile: 'production',
|
|
112
|
+
* autonomous: {
|
|
113
|
+
* secrets: {
|
|
114
|
+
* getMnemonic: () => vault.getSecret('wallet-mnemonic'),
|
|
115
|
+
* },
|
|
116
|
+
* bidStrategy: 'cheapest',
|
|
117
|
+
* }
|
|
118
|
+
* });
|
|
119
|
+
* ```
|
|
88
120
|
*/
|
|
89
121
|
export async function deployToAkash(params) {
|
|
90
|
-
const { wallet, certificate, bidSelector, logger: customLogger, dryRun = false, ...options } = params;
|
|
122
|
+
const { wallet: providedWallet, certificate: providedCertificate, bidSelector: providedBidSelector, autonomous, logger: customLogger, dryRun = false, ...options } = params;
|
|
91
123
|
const logger = customLogger ?? defaultLogger;
|
|
92
124
|
// ---------------------------------------------------------------------------
|
|
93
125
|
// Load and validate deployment profile
|
|
@@ -133,17 +165,106 @@ export async function deployToAkash(params) {
|
|
|
133
165
|
return success(dryRunResult);
|
|
134
166
|
}
|
|
135
167
|
// ---------------------------------------------------------------------------
|
|
168
|
+
// Resolve wallet, certificate, and bid selector
|
|
169
|
+
// Either from direct params (manual mode) or autonomous config
|
|
170
|
+
// ---------------------------------------------------------------------------
|
|
171
|
+
let wallet = providedWallet;
|
|
172
|
+
let certificate = providedCertificate;
|
|
173
|
+
let bidSelector = providedBidSelector;
|
|
174
|
+
// Track if we're in autonomous mode for cleanup
|
|
175
|
+
// Note: isAutonomous could be used for future cleanup logic
|
|
176
|
+
const _isAutonomous = !!autonomous;
|
|
177
|
+
void _isAutonomous;
|
|
178
|
+
if (autonomous) {
|
|
179
|
+
logger.log('🤖 Autonomous mode: setting up agent-controlled wallet...');
|
|
180
|
+
// Step 1: Get mnemonic from secrets provider
|
|
181
|
+
let mnemonic;
|
|
182
|
+
try {
|
|
183
|
+
mnemonic = await autonomous.secrets.getMnemonic();
|
|
184
|
+
}
|
|
185
|
+
catch (error) {
|
|
186
|
+
const errMsg = getErrorMessage(error);
|
|
187
|
+
return failure(new DeploymentError(`Failed to retrieve mnemonic from secrets provider: ${errMsg}`, 'SECRETS_ERROR', { error: errMsg }, false, 'Ensure your SecretsProvider.getMnemonic() is working correctly'));
|
|
188
|
+
}
|
|
189
|
+
// Step 2: Create wallet from mnemonic
|
|
190
|
+
const walletResult = await createWalletFromMnemonic(mnemonic, network);
|
|
191
|
+
if (!walletResult.success) {
|
|
192
|
+
return failure(new DeploymentError(`Failed to create wallet from mnemonic: ${walletResult.error.message}`, 'WALLET_CREATION_FAILED', { originalError: walletResult.error }, false, 'Check that the mnemonic is a valid BIP39 phrase (12 or 24 words)'));
|
|
193
|
+
}
|
|
194
|
+
wallet = walletResult.data;
|
|
195
|
+
logger.log(`✓ Wallet ready: ${wallet.address}`);
|
|
196
|
+
// Step 3: Get or create certificate
|
|
197
|
+
// First, try to get existing certificate from secrets provider
|
|
198
|
+
if (autonomous.secrets.getCertificate) {
|
|
199
|
+
try {
|
|
200
|
+
const existingCert = await autonomous.secrets.getCertificate();
|
|
201
|
+
if (existingCert) {
|
|
202
|
+
certificate = existingCert;
|
|
203
|
+
logger.log('✓ Using cached certificate from secrets provider');
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
catch (error) {
|
|
207
|
+
// Non-fatal: we'll generate a new certificate
|
|
208
|
+
logger.debug?.(`Could not retrieve cached certificate: ${getErrorMessage(error)}`);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
// If no cached certificate, generate and broadcast a new one
|
|
212
|
+
if (!certificate) {
|
|
213
|
+
logger.log('🔐 Generating new TLS certificate...');
|
|
214
|
+
// Create a temporary client with signing capability for certificate operations
|
|
215
|
+
const tempClient = new AkashClient({
|
|
216
|
+
network,
|
|
217
|
+
signer: wallet.signer,
|
|
218
|
+
});
|
|
219
|
+
const certManager = new CertificateManager(tempClient);
|
|
220
|
+
let certResult = await certManager.getOrCreate(wallet.address);
|
|
221
|
+
// Auto-revoke stale on-chain certificate when deploying from a new machine
|
|
222
|
+
// (cert exists on blockchain but private key is not available locally)
|
|
223
|
+
if (!certResult.success && certResult.error.code === 'CERT_NOT_FOUND') {
|
|
224
|
+
logger.log('\u26A0 Certificate exists on-chain but not available locally \u2014 revoking and creating new...');
|
|
225
|
+
const revokeResult = await certManager.revoke(wallet.address);
|
|
226
|
+
if (!revokeResult.success) {
|
|
227
|
+
return failure(new DeploymentError(`Failed to revoke stale certificate: ${revokeResult.error.message}`, 'CERTIFICATE_CREATION_FAILED', { originalError: revokeResult.error }, false, 'Could not revoke existing on-chain certificate to create a new one'));
|
|
228
|
+
}
|
|
229
|
+
logger.log(`\u2713 Revoked ${revokeResult.data.revokedCount} stale certificate(s)`);
|
|
230
|
+
// Retry - blockchain is now clear for a fresh certificate
|
|
231
|
+
certResult = await certManager.getOrCreate(wallet.address);
|
|
232
|
+
}
|
|
233
|
+
if (!certResult.success) {
|
|
234
|
+
return failure(new DeploymentError(`Failed to create certificate: ${certResult.error.message}`, 'CERTIFICATE_CREATION_FAILED', { originalError: certResult.error }, false, 'Certificate generation or blockchain broadcast failed'));
|
|
235
|
+
}
|
|
236
|
+
certificate = certResult.data;
|
|
237
|
+
logger.log('✓ Certificate generated and broadcast to blockchain');
|
|
238
|
+
// Store the new certificate if the secrets provider supports it
|
|
239
|
+
if (autonomous.secrets.storeCertificate) {
|
|
240
|
+
try {
|
|
241
|
+
await autonomous.secrets.storeCertificate(certificate);
|
|
242
|
+
logger.debug?.('Certificate stored in secrets provider for future use');
|
|
243
|
+
}
|
|
244
|
+
catch (error) {
|
|
245
|
+
// Non-fatal: certificate is already on blockchain
|
|
246
|
+
logger.warn?.(`Could not store certificate: ${getErrorMessage(error)}`);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
// Step 4: Create bid selector from strategy
|
|
251
|
+
const strategy = autonomous.bidStrategy ?? 'cheapest';
|
|
252
|
+
const filter = autonomous.bidFilter;
|
|
253
|
+
bidSelector = createBidSelectorFromStrategy(strategy, filter, logger);
|
|
254
|
+
logger.log(`✓ Bid selection strategy: ${strategy}`);
|
|
255
|
+
}
|
|
256
|
+
// ---------------------------------------------------------------------------
|
|
136
257
|
// Runtime validation: wallet and certificate required for actual deployment
|
|
137
258
|
// ---------------------------------------------------------------------------
|
|
138
259
|
if (!wallet) {
|
|
139
|
-
return failure(new DeploymentError('Wallet is required for deployment (not dry-run)', 'WALLET_REQUIRED', {}, false, 'Connect wallet using wallet-manager before deployment'));
|
|
260
|
+
return failure(new DeploymentError('Wallet is required for deployment (not dry-run)', 'WALLET_REQUIRED', {}, false, 'Connect wallet using wallet-manager before deployment, or use autonomous mode'));
|
|
140
261
|
}
|
|
141
262
|
if (!certificate) {
|
|
142
|
-
return failure(new DeploymentError('TLS certificate is required to communicate with the provider', 'CERTIFICATE_REQUIRED', {}, false, 'Generate or load a certificate using certificate-manager before deployment'));
|
|
263
|
+
return failure(new DeploymentError('TLS certificate is required to communicate with the provider', 'CERTIFICATE_REQUIRED', {}, false, 'Generate or load a certificate using certificate-manager before deployment, or use autonomous mode'));
|
|
143
264
|
}
|
|
144
265
|
if (!bidSelector) {
|
|
145
266
|
return failure(new DeploymentError('Bid selector function is required for deployment', 'BID_SELECTOR_REQUIRED', {}, false, 'Provide a bidSelector function to choose which provider bid to accept. ' +
|
|
146
|
-
'Use pre-built selectors like selectCheapestBid, selectMostReliableBid, or
|
|
267
|
+
'Use pre-built selectors like selectCheapestBid, selectMostReliableBid, or use autonomous mode.'));
|
|
147
268
|
}
|
|
148
269
|
// -----------------------------------------------------------------------
|
|
149
270
|
// Initialize AkashClient with signing capability
|
|
@@ -390,6 +511,51 @@ function determineDeposit(options, profile) {
|
|
|
390
511
|
}
|
|
391
512
|
return 5; // Default deposit in AKT
|
|
392
513
|
}
|
|
514
|
+
/**
|
|
515
|
+
* Creates a bid selector function from strategy and filter criteria
|
|
516
|
+
*
|
|
517
|
+
* Used by autonomous mode to convert configuration into a BidSelector function.
|
|
518
|
+
*/
|
|
519
|
+
function createBidSelectorFromStrategy(strategy, filter, logger) {
|
|
520
|
+
return async (bids) => {
|
|
521
|
+
let filteredBids = bids;
|
|
522
|
+
// Apply filter criteria if provided
|
|
523
|
+
if (filter) {
|
|
524
|
+
// Convert per-block price to per-month for filterBids
|
|
525
|
+
// Blocks per month: ~431,572 (6.098s per block)
|
|
526
|
+
const BLOCKS_PER_MONTH = 431572;
|
|
527
|
+
const maxPricePerMonth = filter.maxPricePerBlock
|
|
528
|
+
? { uakt: filter.maxPricePerBlock * BLOCKS_PER_MONTH }
|
|
529
|
+
: undefined;
|
|
530
|
+
filteredBids = filterBids(bids, {
|
|
531
|
+
maxPricePerMonth,
|
|
532
|
+
minUptime: filter.minUptime,
|
|
533
|
+
requireAudited: filter.requireAudited,
|
|
534
|
+
preferredRegions: filter.preferredRegions ? [...filter.preferredRegions] : undefined,
|
|
535
|
+
requireOnline: filter.requireOnline,
|
|
536
|
+
});
|
|
537
|
+
logger.debug?.(`Filtered bids: ${filteredBids.length}/${bids.length} passed criteria`);
|
|
538
|
+
if (filteredBids.length === 0) {
|
|
539
|
+
logger.warn?.('No bids passed filter criteria');
|
|
540
|
+
return null;
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
// Apply strategy to select best bid
|
|
544
|
+
switch (strategy) {
|
|
545
|
+
case 'cheapest':
|
|
546
|
+
return selectCheapestBid(filteredBids);
|
|
547
|
+
case 'most-reliable':
|
|
548
|
+
return selectMostReliableBid(filteredBids);
|
|
549
|
+
case 'balanced':
|
|
550
|
+
return selectBalancedBid(filteredBids);
|
|
551
|
+
default: {
|
|
552
|
+
// Fallback for any unknown strategy
|
|
553
|
+
logger.warn?.(`Unknown bid strategy, falling back to cheapest`);
|
|
554
|
+
return selectCheapestBid(filteredBids);
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
};
|
|
558
|
+
}
|
|
393
559
|
/** Normalises deployment data into the published AkashDeploymentData structure */
|
|
394
560
|
function createDeploymentData(params) {
|
|
395
561
|
const { deployment, lease, leaseDetails, deploymentDetails, providerUri, profileName, network, certificate, providerStatus, selectedBid, } = params;
|