@kadi.build/deploy-ability 0.0.5 → 0.0.7

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.
Files changed (55) hide show
  1. package/dist/errors/certificate-error.d.ts +2 -0
  2. package/dist/errors/certificate-error.d.ts.map +1 -1
  3. package/dist/errors/certificate-error.js +2 -0
  4. package/dist/errors/certificate-error.js.map +1 -1
  5. package/dist/errors/error-utils.d.ts +31 -0
  6. package/dist/errors/error-utils.d.ts.map +1 -0
  7. package/dist/errors/error-utils.js +44 -0
  8. package/dist/errors/error-utils.js.map +1 -0
  9. package/dist/errors/index.d.ts +1 -0
  10. package/dist/errors/index.d.ts.map +1 -1
  11. package/dist/errors/index.js +2 -0
  12. package/dist/errors/index.js.map +1 -1
  13. package/dist/targets/akash/certificate-manager.d.ts +31 -0
  14. package/dist/targets/akash/certificate-manager.d.ts.map +1 -1
  15. package/dist/targets/akash/certificate-manager.js +85 -6
  16. package/dist/targets/akash/certificate-manager.js.map +1 -1
  17. package/dist/targets/akash/client.d.ts.map +1 -1
  18. package/dist/targets/akash/client.js +18 -11
  19. package/dist/targets/akash/client.js.map +1 -1
  20. package/dist/targets/akash/deployer.d.ts.map +1 -1
  21. package/dist/targets/akash/deployer.js +11 -6
  22. package/dist/targets/akash/deployer.js.map +1 -1
  23. package/dist/targets/akash/index.d.ts +2 -2
  24. package/dist/targets/akash/index.d.ts.map +1 -1
  25. package/dist/targets/akash/index.js.map +1 -1
  26. package/dist/targets/akash/provider-manager.d.ts.map +1 -1
  27. package/dist/targets/akash/provider-manager.js +15 -9
  28. package/dist/targets/akash/provider-manager.js.map +1 -1
  29. package/dist/targets/akash/sdl-generator.js +9 -3
  30. package/dist/targets/akash/sdl-generator.js.map +1 -1
  31. package/dist/targets/akash/wallet-manager.d.ts.map +1 -1
  32. package/dist/targets/akash/wallet-manager.js +15 -10
  33. package/dist/targets/akash/wallet-manager.js.map +1 -1
  34. package/dist/targets/local/deployer.d.ts.map +1 -1
  35. package/dist/targets/local/deployer.js +23 -13
  36. package/dist/targets/local/deployer.js.map +1 -1
  37. package/dist/targets/local/network-manager.d.ts.map +1 -1
  38. package/dist/targets/local/network-manager.js +4 -3
  39. package/dist/targets/local/network-manager.js.map +1 -1
  40. package/dist/types/common.d.ts +106 -23
  41. package/dist/types/common.d.ts.map +1 -1
  42. package/dist/types/index.d.ts +1 -1
  43. package/dist/types/index.d.ts.map +1 -1
  44. package/dist/types/index.js.map +1 -1
  45. package/dist/types/options.d.ts +9 -1
  46. package/dist/types/options.d.ts.map +1 -1
  47. package/dist/types/profiles.d.ts +11 -3
  48. package/dist/types/profiles.d.ts.map +1 -1
  49. package/dist/types/profiles.js.map +1 -1
  50. package/dist/utils/registry/manager.d.ts.map +1 -1
  51. package/dist/utils/registry/manager.js +13 -6
  52. package/dist/utils/registry/manager.js.map +1 -1
  53. package/package.json +2 -2
  54. package/docs/KADI_ABILITY_CONVERSION.md +0 -1365
  55. package/docs/PIPELINE_BUILDER_DESIGN.md +0 -1149
@@ -1,1365 +0,0 @@
1
- # KADI Ability Conversion - Preserving Fluent APIs
2
-
3
- > **Document Purpose**: Guide for converting deploy-ability into a KADI ability while preserving the fluent API design
4
- >
5
- > **Author**: KADI Team
6
- >
7
- > **Date**: 2025-11-18
8
- >
9
- > **Status**: Design Proposal
10
-
11
- ---
12
-
13
- ## Executive Summary
14
-
15
- ### The Question
16
-
17
- **Can we turn deploy-ability into a KADI ability while keeping the Pipeline Builder's fluent API?**
18
-
19
- ### The Answer
20
-
21
- **Yes, absolutely!** Using the **dual export pattern** from secret-ability, you can have:
22
-
23
- - ✅ **Fluent API** for local/CLI use (best developer experience)
24
- - ✅ **Tool-based RPC** for distributed/remote use (best for agents)
25
- - ✅ **Both interfaces** maintained in the same package
26
- - ✅ **No breaking changes** to existing code
27
-
28
- ### Key Insight
29
-
30
- **Fluent APIs and KADI abilities are NOT mutually exclusive.** They serve different use cases:
31
-
32
- | Use Case | Interface | Why |
33
- |----------|-----------|-----|
34
- | Local CLI commands | Fluent API | Best DX, discoverable, type-safe |
35
- | Agent-to-agent calls | Tool-based RPC | Stateless, distributed, simple |
36
- | Scripts/automation | Fluent API | Flexible, composable, clear |
37
- | Broker/MCP integration | Tool-based RPC | Standard protocol, interoperable |
38
-
39
- ---
40
-
41
- ## The Dual Export Pattern
42
-
43
- ### How secret-ability Does It
44
-
45
- secret-ability successfully implements this pattern:
46
-
47
- **package.json:**
48
- ```json
49
- {
50
- "name": "secret-ability",
51
- "main": "./dist/kadi-ability.js",
52
- "exports": {
53
- ".": {
54
- "types": "./dist/kadi-ability.d.ts",
55
- "import": "./dist/kadi-ability.js"
56
- },
57
- "./lib": {
58
- "types": "./dist/index.d.ts",
59
- "import": "./dist/index.js"
60
- }
61
- }
62
- }
63
- ```
64
-
65
- **File structure:**
66
- ```
67
- secret-ability/
68
- ├── src/
69
- │ ├── index.ts # Library export - Fluent API (Secrets class)
70
- │ ├── kadi-ability.ts # Ability export - Tool-based RPC
71
- │ ├── core/
72
- │ │ └── secrets.ts # The Secrets class with fluent initializer
73
- │ └── ...
74
- ```
75
-
76
- ### Two Interfaces, One Codebase
77
-
78
- **Library Interface (`/lib`):**
79
- ```typescript
80
- import { Secrets } from '@kadi.build/secret-ability/lib';
81
-
82
- const secrets = new Secrets();
83
- await secrets.init()
84
- .ensure('wallet', { type: 'custom', path: './wallet.vault' })
85
- .use('wallet');
86
-
87
- await secrets.set('MNEMONIC', 'word1 word2 ...');
88
- ```
89
-
90
- **Ability Interface (default):**
91
- ```typescript
92
- const secrets = await client.load('secret-ability', 'native');
93
-
94
- await secrets.init({ vaults: [{ name: 'wallet', type: 'custom', path: './wallet.vault' }] });
95
- await secrets.useVault({ vaultName: 'wallet' });
96
- await secrets.set({ key: 'MNEMONIC', value: 'word1 word2 ...' });
97
- ```
98
-
99
- Same functionality, different interfaces for different use cases!
100
-
101
- ---
102
-
103
- ## Current State: deploy-ability
104
-
105
- ### Current Architecture
106
-
107
- deploy-ability is currently a **pure library** - not yet a KADI ability.
108
-
109
- **How it's used today:**
110
- ```typescript
111
- import { setupRegistryIfNeeded, loadProfile, deploy } from '@kadi.build/deploy-ability';
112
-
113
- // Manual setup and cleanup
114
- const registryManager = new DockerRegistryManager();
115
- await registryManager.startTemporaryRegistry();
116
-
117
- try {
118
- const profile = await loadProfile('./agent.json', 'production');
119
- await deploy(profile, wallet, certificate);
120
- } finally {
121
- await registryManager.cleanup(); // Easy to forget!
122
- }
123
- ```
124
-
125
- ### The Pipeline Builder (Proposed)
126
-
127
- The fluent API design from `PIPELINE_BUILDER_DESIGN.md`:
128
-
129
- ```typescript
130
- import { DeploymentPipeline } from '@kadi.build/deploy-ability/lib';
131
-
132
- const result = await DeploymentPipeline
133
- .create({ projectRoot: './', profile: 'production' })
134
- .withLocalImageSupport({ tunnelService: 'serveo' })
135
- .withWallet(wallet)
136
- .withCertificate(certificate)
137
- .execute();
138
- // Cleanup happens automatically! ✨
139
- ```
140
-
141
- **This is excellent design** - we want to preserve it!
142
-
143
- ---
144
-
145
- ## Proposed State: deploy-ability as KADI Ability
146
-
147
- ### Package Structure
148
-
149
- **Updated package.json:**
150
- ```json
151
- {
152
- "name": "@kadi.build/deploy-ability",
153
- "version": "0.0.5",
154
- "description": "Multi-target deployment orchestration for KADI",
155
- "type": "module",
156
- "main": "./dist/kadi-ability.js",
157
- "types": "./dist/kadi-ability.d.ts",
158
- "exports": {
159
- ".": {
160
- "types": "./dist/kadi-ability.d.ts",
161
- "import": "./dist/kadi-ability.js"
162
- },
163
- "./lib": {
164
- "types": "./dist/index.d.ts",
165
- "import": "./dist/index.js"
166
- },
167
- "./types": {
168
- "types": "./dist/types/index.d.ts",
169
- "import": "./dist/types/index.js"
170
- }
171
- },
172
- "dependencies": {
173
- "@kadi.build/core": "^0.0.1-alpha.15",
174
- "zod": "^3.22.4"
175
- }
176
- }
177
- ```
178
-
179
- ### File Structure
180
-
181
- ```
182
- deploy-ability/
183
- ├── src/
184
- │ ├── index.ts # Library export - FLUENT API ✅
185
- │ │ # Exports: DeploymentPipeline, types, utilities
186
- │ │
187
- │ ├── kadi-ability.ts # Ability export - TOOL-BASED RPC
188
- │ │ # Registers tools, wraps fluent API
189
- │ │
190
- │ ├── pipeline-builder.ts # The beautiful fluent API
191
- │ │ # DeploymentPipeline class
192
- │ │
193
- │ ├── targets/
194
- │ │ ├── local/
195
- │ │ │ └── local.ts
196
- │ │ └── akash/
197
- │ │ ├── akash.ts
198
- │ │ └── deployer.ts
199
- │ │
200
- │ ├── utils/
201
- │ │ └── registry/
202
- │ │ ├── manager.ts
203
- │ │ └── setup.ts
204
- │ │
205
- │ └── types/
206
- │ └── index.ts
207
-
208
- └── docs/
209
- ├── PIPELINE_BUILDER_DESIGN.md
210
- └── KADI_ABILITY_CONVERSION.md # This document
211
- ```
212
-
213
- ### Two Interfaces Working Together
214
-
215
- The **ability layer** internally uses the **fluent library**:
216
-
217
- ```
218
- ┌─────────────────────────────────────────────────────┐
219
- │ Consumer Code │
220
- ├─────────────────────────────────────────────────────┤
221
- │ │
222
- │ Option A: Direct Library Import │
223
- │ ┌─────────────────────────────────────────┐ │
224
- │ │ import { DeploymentPipeline } │ │
225
- │ │ from '@kadi.build/deploy-ability/lib' │ │
226
- │ │ │ │
227
- │ │ await DeploymentPipeline.create(...) │ │
228
- │ └─────────────────────────────────────────┘ │
229
- │ ↓ │
230
- │ ┌─────────────────────────────────────────┐ │
231
- │ │ Pipeline Builder (Fluent API) │ │
232
- │ └─────────────────────────────────────────┘ │
233
- │ │
234
- │ Option B: KADI Ability Loading │
235
- │ ┌─────────────────────────────────────────┐ │
236
- │ │ const deploy = await client.load(...) │ │
237
- │ │ await deploy.deploy({ ... }) │ │
238
- │ └─────────────────────────────────────────┘ │
239
- │ ↓ │
240
- │ ┌─────────────────────────────────────────┐ │
241
- │ │ Tool-based RPC Interface │ │
242
- │ │ (internally uses Pipeline Builder) │ │
243
- │ └─────────────────────────────────────────┘ │
244
- │ ↓ │
245
- │ ┌─────────────────────────────────────────┐ │
246
- │ │ Pipeline Builder (Fluent API) │ │
247
- │ └─────────────────────────────────────────┘ │
248
- │ │
249
- └─────────────────────────────────────────────────────┘
250
- ```
251
-
252
- ---
253
-
254
- ## Code Examples - Before/After
255
-
256
- ### Current Usage (Library Only)
257
-
258
- **kadi-deploy using deploy-ability today:**
259
-
260
- ```typescript
261
- // kadi-deploy/src/index.ts
262
- import { loadProfile, setupRegistryIfNeeded } from '@kadi.build/deploy-ability';
263
- import { deploy as deployToAkash } from '@kadi.build/deploy-ability/targets/akash';
264
-
265
- export async function deploy(options: DeployOptions) {
266
- // Load profile
267
- const profile = await loadProfile(
268
- options.projectRoot,
269
- options.profile,
270
- options
271
- );
272
-
273
- // Setup registry if needed (manual!)
274
- const registryCleanup = await setupRegistryIfNeeded(profile);
275
-
276
- try {
277
- // Deploy
278
- if (profile.target === 'akash') {
279
- await deployToAkash(profile, wallet, certificate);
280
- }
281
- // ... other targets
282
- } finally {
283
- // Cleanup (easy to forget!)
284
- if (registryCleanup) {
285
- await registryCleanup();
286
- }
287
- }
288
- }
289
- ```
290
-
291
- **Problems:**
292
- - Manual registry management
293
- - Easy to forget cleanup
294
- - Verbose error handling
295
- - Hard to compose operations
296
-
297
- ---
298
-
299
- ### Proposed Usage (As KADI Ability)
300
-
301
- #### Option A: Library Import (for local/CLI use)
302
-
303
- **kadi-deploy using the fluent library API:**
304
-
305
- ```typescript
306
- // kadi-deploy/src/index.ts
307
- import { DeploymentPipeline } from '@kadi.build/deploy-ability/lib';
308
-
309
- export async function deploy(options: DeployOptions) {
310
- // Beautiful fluent API! ✨
311
- const result = await DeploymentPipeline
312
- .create({
313
- projectRoot: options.projectRoot,
314
- profile: options.profile
315
- })
316
- .withLocalImageSupport({
317
- tunnelService: options.tunnelService || 'serveo'
318
- })
319
- .withWallet(wallet)
320
- .withCertificate(certificate)
321
- .execute();
322
-
323
- // Cleanup happens automatically!
324
-
325
- return result;
326
- }
327
- ```
328
-
329
- **Benefits:**
330
- - ✅ Automatic cleanup
331
- - ✅ Discoverable API
332
- - ✅ Type-safe
333
- - ✅ Easy to read and understand
334
-
335
- ---
336
-
337
- #### Option B: Ability Loading (for distributed use)
338
-
339
- **Remote agent calling deploy-ability as a KADI ability:**
340
-
341
- ```typescript
342
- // model-manager-agent/src/services/deployment-controller.ts
343
- import { KadiClient } from '@kadi.build/core';
344
-
345
- export class DeploymentController {
346
- private deployAbility: any;
347
-
348
- async initialize() {
349
- const client = new KadiClient({ name: 'model-manager' });
350
-
351
- // Load deploy-ability as a KADI ability
352
- this.deployAbility = await client.load('deploy-ability', 'native');
353
- }
354
-
355
- async deployModel(request: DeploymentRequest) {
356
- // Tool-based interface (works remotely)
357
- const result = await this.deployAbility.deploy({
358
- projectRoot: './templates',
359
- profile: 'ollama-runtime',
360
- localImageSupport: {
361
- tunnelService: 'serveo'
362
- },
363
- wallet: this.wallet,
364
- certificate: this.certificate,
365
- // Additional deployment-specific options
366
- modelName: request.modelName,
367
- gpuType: request.gpu
368
- });
369
-
370
- if (!result.success) {
371
- throw new Error(`Deployment failed: ${result.message}`);
372
- }
373
-
374
- return result;
375
- }
376
- }
377
- ```
378
-
379
- **Benefits:**
380
- - ✅ Works across process boundaries
381
- - ✅ Version-isolated through KADI
382
- - ✅ Stateless RPC
383
- - ✅ Simple parameter passing
384
-
385
- ---
386
-
387
- ## Implementation Guide
388
-
389
- ### Step 1: Create `kadi-ability.ts`
390
-
391
- This is the new file that wraps your fluent API in a tool-based interface.
392
-
393
- **File: `src/kadi-ability.ts`**
394
-
395
- ```typescript
396
- /**
397
- * KADI Ability Interface for deploy-ability
398
- *
399
- * Exposes deployment functionality through KADI's ability system.
400
- * Uses KadiClient to register tools with proper schemas and handlers.
401
- *
402
- * IMPORTANT: This is a thin wrapper around the fluent library API.
403
- * For local/CLI use, prefer importing from '/lib' for the fluent interface.
404
- *
405
- * @module kadi-ability
406
- */
407
-
408
- import { KadiClient, z } from '@kadi.build/core';
409
- import { DeploymentPipeline } from './pipeline-builder.js';
410
- import type {
411
- DeploymentResult,
412
- AkashWallet,
413
- AkashCertificate
414
- } from './types/index.js';
415
-
416
- // Create the KadiClient instance
417
- const client = new KadiClient({
418
- name: 'deploy-ability',
419
- version: '0.0.5',
420
- description: 'Multi-target deployment orchestration for KADI',
421
- role: 'ability'
422
- });
423
-
424
- // ============================================================================
425
- // Tool Registration
426
- // ============================================================================
427
-
428
- /**
429
- * deploy - Deploy to target platform
430
- *
431
- * Main deployment tool that orchestrates the entire deployment process.
432
- * Internally uses the DeploymentPipeline fluent API for clean resource management.
433
- */
434
- const deployInputSchema = z.object({
435
- projectRoot: z.string().describe('Project root directory'),
436
- profile: z.string().describe('Deployment profile name'),
437
-
438
- // Optional enhancements
439
- localImageSupport: z.object({
440
- tunnelService: z.enum(['serveo', 'ngrok', 'bore']).optional()
441
- .describe('Tunnel service for exposing local registry'),
442
- registryPort: z.number().optional()
443
- .describe('Local registry port (default: 5001)')
444
- }).optional().describe('Enable local image support with temporary registry'),
445
-
446
- wallet: z.any().optional()
447
- .describe('Akash wallet for blockchain transactions'),
448
-
449
- certificate: z.any().optional()
450
- .describe('Akash certificate for provider authentication'),
451
-
452
- // CLI overrides
453
- verbose: z.boolean().optional()
454
- .describe('Enable verbose logging'),
455
-
456
- dryRun: z.boolean().optional()
457
- .describe('Simulate deployment without executing'),
458
-
459
- yes: z.boolean().optional()
460
- .describe('Auto-confirm all prompts'),
461
- });
462
-
463
- const deployOutputSchema = z.object({
464
- success: z.boolean(),
465
- deploymentId: z.string().optional()
466
- .describe('Unique deployment identifier'),
467
- dseq: z.string().optional()
468
- .describe('Akash deployment sequence number'),
469
- endpoints: z.array(z.string()).optional()
470
- .describe('Accessible service endpoints'),
471
- leaseInfo: z.object({
472
- provider: z.string(),
473
- price: z.string(),
474
- priceUsd: z.number().optional()
475
- }).optional().describe('Lease information for Akash deployments'),
476
- message: z.string().optional()
477
- .describe('Status or error message'),
478
- });
479
-
480
- client.registerTool({
481
- name: 'deploy',
482
- description: 'Deploy agent to target platform using specified profile',
483
- input: deployInputSchema,
484
- output: deployOutputSchema
485
- }, async (params: z.infer<typeof deployInputSchema>) => {
486
- try {
487
- // Build the deployment pipeline using the fluent API
488
- let pipeline = DeploymentPipeline.create({
489
- projectRoot: params.projectRoot,
490
- profile: params.profile,
491
- verbose: params.verbose,
492
- dryRun: params.dryRun,
493
- yes: params.yes
494
- });
495
-
496
- // Add local image support if requested
497
- if (params.localImageSupport) {
498
- pipeline = pipeline.withLocalImageSupport({
499
- tunnelService: params.localImageSupport.tunnelService || 'serveo',
500
- registryPort: params.localImageSupport.registryPort
501
- });
502
- }
503
-
504
- // Add wallet if provided (Akash deployments)
505
- if (params.wallet) {
506
- pipeline = pipeline.withWallet(params.wallet as AkashWallet);
507
- }
508
-
509
- // Add certificate if provided (Akash deployments)
510
- if (params.certificate) {
511
- pipeline = pipeline.withCertificate(params.certificate as AkashCertificate);
512
- }
513
-
514
- // Execute the pipeline
515
- // The pipeline handles all resource management and cleanup automatically
516
- const result = await pipeline.execute();
517
-
518
- return {
519
- success: true,
520
- deploymentId: result.deploymentId,
521
- dseq: result.dseq,
522
- endpoints: result.endpoints,
523
- leaseInfo: result.leaseInfo,
524
- message: 'Deployment completed successfully'
525
- };
526
-
527
- } catch (error) {
528
- const errorMsg = error instanceof Error ? error.message : String(error);
529
-
530
- return {
531
- success: false,
532
- message: `Deployment failed: ${errorMsg}`
533
- };
534
- }
535
- });
536
-
537
- /**
538
- * getDeploymentStatus - Check deployment status
539
- *
540
- * Query the status of an existing deployment.
541
- */
542
- const getStatusInputSchema = z.object({
543
- deploymentId: z.string().describe('Deployment identifier to check'),
544
- target: z.enum(['local', 'akash']).describe('Deployment target')
545
- });
546
-
547
- const getStatusOutputSchema = z.object({
548
- success: z.boolean(),
549
- status: z.enum(['pending', 'active', 'failed', 'closed']).optional(),
550
- message: z.string().optional()
551
- });
552
-
553
- client.registerTool({
554
- name: 'getDeploymentStatus',
555
- description: 'Get the current status of a deployment',
556
- input: getStatusInputSchema,
557
- output: getStatusOutputSchema
558
- }, async (params: z.infer<typeof getStatusInputSchema>) => {
559
- try {
560
- // Implementation would query deployment status
561
- // This is a placeholder for the actual implementation
562
-
563
- return {
564
- success: true,
565
- status: 'active',
566
- message: 'Deployment is running'
567
- };
568
- } catch (error) {
569
- return {
570
- success: false,
571
- message: error instanceof Error ? error.message : String(error)
572
- };
573
- }
574
- });
575
-
576
- /**
577
- * closeDeployment - Close/stop a deployment
578
- *
579
- * Gracefully shut down and clean up a deployment.
580
- */
581
- const closeInputSchema = z.object({
582
- deploymentId: z.string().describe('Deployment identifier to close'),
583
- target: z.enum(['local', 'akash']).describe('Deployment target')
584
- });
585
-
586
- const closeOutputSchema = z.object({
587
- success: z.boolean(),
588
- message: z.string().optional()
589
- });
590
-
591
- client.registerTool({
592
- name: 'closeDeployment',
593
- description: 'Close and cleanup a deployment',
594
- input: closeInputSchema,
595
- output: closeOutputSchema
596
- }, async (params: z.infer<typeof closeInputSchema>) => {
597
- try {
598
- // Implementation would close the deployment
599
- // This is a placeholder for the actual implementation
600
-
601
- return {
602
- success: true,
603
- message: 'Deployment closed successfully'
604
- };
605
- } catch (error) {
606
- return {
607
- success: false,
608
- message: error instanceof Error ? error.message : String(error)
609
- };
610
- }
611
- });
612
-
613
- // Export the client as default
614
- export default client;
615
-
616
- // Allow running standalone as stdio server
617
- if (import.meta.url === `file://${process.argv[1]}`) {
618
- await client.serve('stdio');
619
- }
620
- ```
621
-
622
- ---
623
-
624
- ### Step 2: Update Library Export (`index.ts`)
625
-
626
- Make sure the library export exposes the fluent API:
627
-
628
- **File: `src/index.ts`**
629
-
630
- ```typescript
631
- /**
632
- * deploy-ability - Library Export
633
- *
634
- * Main library interface for deploy-ability.
635
- * Provides fluent API through DeploymentPipeline.
636
- *
637
- * @module deploy-ability/lib
638
- */
639
-
640
- // ============================================================================
641
- // Fluent API - Primary Interface
642
- // ============================================================================
643
-
644
- export { DeploymentPipeline } from './pipeline-builder.js';
645
-
646
- // ============================================================================
647
- // Types
648
- // ============================================================================
649
-
650
- export type {
651
- DeploymentProfile,
652
- AkashDeploymentProfile,
653
- LocalDeploymentProfile,
654
- DeploymentResult,
655
- AkashWallet,
656
- AkashCertificate,
657
- ServiceConfig,
658
- // ... all other types
659
- } from './types/index.js';
660
-
661
- // ============================================================================
662
- // Legacy Exports (for backward compatibility)
663
- // ============================================================================
664
-
665
- // Keep these for existing code that uses the old API
666
- export { loadProfile } from './utils/profileUtils.js';
667
- export { setupRegistryIfNeeded } from './utils/registry/setup.js';
668
- export { DockerRegistryManager } from './utils/registry/manager.js';
669
-
670
- // Target implementations (advanced users)
671
- export { deploy as deployToLocal } from './targets/local/local.js';
672
- export { deploy as deployToAkash } from './targets/akash/akash.js';
673
- ```
674
-
675
- ---
676
-
677
- ### Step 3: Update `package.json`
678
-
679
- Add the exports configuration:
680
-
681
- ```json
682
- {
683
- "name": "@kadi.build/deploy-ability",
684
- "version": "0.0.5",
685
- "description": "Multi-target deployment orchestration for KADI",
686
- "type": "module",
687
- "main": "./dist/kadi-ability.js",
688
- "types": "./dist/kadi-ability.d.ts",
689
- "exports": {
690
- ".": {
691
- "types": "./dist/kadi-ability.d.ts",
692
- "import": "./dist/kadi-ability.js"
693
- },
694
- "./lib": {
695
- "types": "./dist/index.d.ts",
696
- "import": "./dist/index.js"
697
- },
698
- "./types": {
699
- "types": "./dist/types/index.d.ts",
700
- "import": "./dist/types/index.js"
701
- }
702
- },
703
- "scripts": {
704
- "build": "tsc",
705
- "build:watch": "tsc --watch",
706
- "test": "vitest run"
707
- },
708
- "dependencies": {
709
- "@kadi.build/core": "^0.0.1-alpha.15",
710
- "zod": "^3.22.4"
711
- },
712
- "devDependencies": {
713
- "@types/node": "^20.0.0",
714
- "typescript": "^5.9.2",
715
- "vitest": "^1.0.0"
716
- }
717
- }
718
- ```
719
-
720
- ---
721
-
722
- ### Step 4: TypeScript Configuration
723
-
724
- Ensure TypeScript outputs both files correctly:
725
-
726
- **tsconfig.json:**
727
- ```json
728
- {
729
- "compilerOptions": {
730
- "target": "ES2022",
731
- "module": "ES2022",
732
- "moduleResolution": "bundler",
733
- "outDir": "./dist",
734
- "rootDir": "./src",
735
- "declaration": true,
736
- "declarationMap": true,
737
- "sourceMap": true,
738
- "esModuleInterop": true,
739
- "skipLibCheck": true,
740
- "strict": true
741
- },
742
- "include": ["src/**/*"],
743
- "exclude": ["node_modules", "dist"]
744
- }
745
- ```
746
-
747
- ---
748
-
749
- ## Usage Patterns
750
-
751
- ### Pattern 1: CLI Commands (kadi deploy)
752
-
753
- **Use the library's fluent API:**
754
-
755
- ```typescript
756
- // kadi-deploy/src/commands/deploy.ts
757
- import { DeploymentPipeline } from '@kadi.build/deploy-ability/lib';
758
-
759
- export async function deployCommand(options: CommandOptions) {
760
- const logger = createLogger(options.verbose);
761
-
762
- logger.info('Starting deployment...');
763
-
764
- try {
765
- const result = await DeploymentPipeline
766
- .create({
767
- projectRoot: options.projectRoot || './',
768
- profile: options.profile,
769
- verbose: options.verbose,
770
- dryRun: options.dryRun
771
- })
772
- .withLocalImageSupport({
773
- tunnelService: options.tunnelService || 'serveo'
774
- })
775
- .withWallet(await loadWallet(options.walletPath))
776
- .withCertificate(await loadCertificate(options.certPath))
777
- .execute();
778
-
779
- logger.success('Deployment completed!');
780
- logger.info(`Deployment ID: ${result.deploymentId}`);
781
-
782
- if (result.endpoints) {
783
- logger.info('Endpoints:');
784
- result.endpoints.forEach(ep => logger.info(` - ${ep}`));
785
- }
786
-
787
- } catch (error) {
788
- logger.error('Deployment failed:', error);
789
- process.exit(1);
790
- }
791
- }
792
- ```
793
-
794
- **Why fluent API here?**
795
- - Running locally, in-process
796
- - Best developer experience
797
- - Type-safe and discoverable
798
- - Automatic cleanup
799
-
800
- ---
801
-
802
- ### Pattern 2: Agent-to-Agent Calls
803
-
804
- **Use the ability's tool interface:**
805
-
806
- ```typescript
807
- // model-manager-agent/src/services/deployment-controller.ts
808
- import { KadiClient } from '@kadi.build/core';
809
-
810
- export class DeploymentController {
811
- private client: KadiClient;
812
- private deployAbility: any;
813
-
814
- constructor() {
815
- this.client = new KadiClient({
816
- name: 'model-manager-agent',
817
- projectRoot: process.cwd()
818
- });
819
- }
820
-
821
- async initialize() {
822
- // Load deploy-ability as a KADI ability
823
- this.deployAbility = await this.client.load('deploy-ability', 'native');
824
- }
825
-
826
- async deployModel(request: DeployModelRequest): Promise<DeploymentInfo> {
827
- // Call the deploy tool
828
- const result = await this.deployAbility.deploy({
829
- projectRoot: './templates',
830
- profile: this.selectProfile(request),
831
- localImageSupport: {
832
- tunnelService: 'serveo'
833
- },
834
- wallet: this.wallet,
835
- certificate: this.certificate
836
- });
837
-
838
- if (!result.success) {
839
- throw new Error(`Deployment failed: ${result.message}`);
840
- }
841
-
842
- return {
843
- deploymentId: result.deploymentId,
844
- dseq: result.dseq,
845
- endpoints: result.endpoints,
846
- status: 'active'
847
- };
848
- }
849
- }
850
- ```
851
-
852
- **Why tool interface here?**
853
- - Might be running in different process
854
- - Stateless RPC
855
- - Simple parameter passing
856
- - Version isolation through KADI
857
-
858
- ---
859
-
860
- ### Pattern 3: Automation Scripts
861
-
862
- **Use the library's fluent API:**
863
-
864
- ```typescript
865
- // scripts/deploy-all-profiles.ts
866
- import { DeploymentPipeline } from '@kadi.build/deploy-ability/lib';
867
- import { loadWallet, loadCertificate } from './utils.js';
868
-
869
- const profiles = ['staging', 'production', 'backup'];
870
-
871
- async function deployAll() {
872
- const wallet = await loadWallet();
873
- const cert = await loadCertificate();
874
-
875
- for (const profile of profiles) {
876
- console.log(`\nDeploying profile: ${profile}`);
877
-
878
- try {
879
- const result = await DeploymentPipeline
880
- .create({ projectRoot: './', profile })
881
- .withWallet(wallet)
882
- .withCertificate(cert)
883
- .execute();
884
-
885
- console.log(`✅ ${profile}: ${result.deploymentId}`);
886
- } catch (error) {
887
- console.error(`❌ ${profile} failed:`, error);
888
- }
889
- }
890
- }
891
-
892
- deployAll();
893
- ```
894
-
895
- **Why fluent API here?**
896
- - Local automation
897
- - Clear, readable code
898
- - Composable operations
899
- - Automatic resource management
900
-
901
- ---
902
-
903
- ### Pattern 4: MCP/Broker Integration
904
-
905
- **Use the ability's tool interface:**
906
-
907
- ```typescript
908
- // broker-integration/src/deploy-service.ts
909
- export class DeployService {
910
- private client: KadiClient;
911
-
912
- async handleDeployRequest(request: BrokerRequest): Promise<BrokerResponse> {
913
- // Load deploy-ability through broker's client
914
- const deployAbility = await this.client.load('deploy-ability', 'native');
915
-
916
- // Parse request parameters
917
- const params = JSON.parse(request.parameters);
918
-
919
- // Execute deployment via tool
920
- const result = await deployAbility.deploy({
921
- projectRoot: params.projectRoot,
922
- profile: params.profile,
923
- wallet: params.wallet,
924
- certificate: params.certificate
925
- });
926
-
927
- return {
928
- success: result.success,
929
- data: result,
930
- message: result.message
931
- };
932
- }
933
- }
934
- ```
935
-
936
- **Why tool interface here?**
937
- - Standard protocol (MCP/JSON-RPC)
938
- - Broker expects tool-based interface
939
- - May cross process boundaries
940
- - Needs to be serializable
941
-
942
- ---
943
-
944
- ## Comparison: Why This Works
945
-
946
- ### Fluent APIs vs Tool-based RPC
947
-
948
- | Aspect | Fluent API | Tool-based RPC |
949
- |--------|-----------|----------------|
950
- | **State** | Stateful (builder pattern) | Stateless (each call independent) |
951
- | **Location** | Local/in-process | Can be distributed |
952
- | **Composability** | High (method chaining) | Medium (parameter objects) |
953
- | **Discoverability** | Excellent (IDE autocomplete) | Good (schema-based) |
954
- | **Type Safety** | Excellent (compile-time) | Good (runtime validation) |
955
- | **Error Handling** | Synchronous exceptions | Result objects |
956
- | **Resource Management** | RAII pattern (automatic) | Manual or tool-managed |
957
- | **Network Serialization** | Not applicable | Required |
958
- | **Best For** | CLI, scripts, local dev | Agents, distributed, remote |
959
-
960
- ### Why Both Are Valid
961
-
962
- **Fluent API strengths:**
963
- - Builder pattern allows incremental configuration
964
- - Automatic resource cleanup (RAII)
965
- - Type-safe at compile time
966
- - Great IDE support
967
- - Easy to read and understand
968
-
969
- **Tool-based RPC strengths:**
970
- - Works across process boundaries
971
- - Simple serialization (JSON)
972
- - Standard protocol (compatible with MCP, JSON-RPC)
973
- - Stateless (easy to scale)
974
- - Version isolation through KADI
975
-
976
- **The solution: Offer both!**
977
-
978
- ---
979
-
980
- ## Migration Strategy
981
-
982
- ### Phase 1: Add Ability Layer (No Breaking Changes)
983
-
984
- **Week 1-2:**
985
- 1. Create `kadi-ability.ts`
986
- 2. Add exports to `package.json`
987
- 3. Write tests for tool interface
988
- 4. Update documentation
989
-
990
- **Result:**
991
- - Existing code keeps working (uses `/lib` export)
992
- - New consumers can choose ability or library
993
- - No migration required
994
-
995
- ---
996
-
997
- ### Phase 2: Update Internal Consumers
998
-
999
- **Week 3-4:**
1000
-
1001
- Update kadi-deploy and other consumers to use the fluent API:
1002
-
1003
- ```typescript
1004
- // Before (old verbose API)
1005
- const registryCleanup = await setupRegistryIfNeeded(profile);
1006
- try {
1007
- await deploy(profile, wallet, cert);
1008
- } finally {
1009
- await registryCleanup?.();
1010
- }
1011
-
1012
- // After (new fluent API)
1013
- await DeploymentPipeline
1014
- .create({ projectRoot: './', profile: 'prod' })
1015
- .withLocalImageSupport()
1016
- .withWallet(wallet)
1017
- .withCertificate(cert)
1018
- .execute();
1019
- ```
1020
-
1021
- **Result:**
1022
- - Cleaner code
1023
- - Automatic cleanup
1024
- - Better error handling
1025
- - Same functionality
1026
-
1027
- ---
1028
-
1029
- ### Phase 3: Gradual Adoption
1030
-
1031
- **Month 2:**
1032
-
1033
- As new use cases emerge:
1034
- - **Local/CLI** → Use fluent API from `/lib`
1035
- - **Distributed/Remote** → Use ability interface
1036
-
1037
- **Documentation updates:**
1038
- - Add examples for both patterns
1039
- - Explain when to use each
1040
- - Migration guide for existing code
1041
-
1042
- **Result:**
1043
- - Full ecosystem support
1044
- - Clear patterns established
1045
- - Best practices documented
1046
-
1047
- ---
1048
-
1049
- ## Benefits & Trade-offs
1050
-
1051
- ### Benefits
1052
-
1053
- ✅ **Fluent API Preserved**
1054
- - Keep the excellent Pipeline Builder design
1055
- - Best developer experience for local use
1056
- - Type-safe, discoverable, clean
1057
-
1058
- ✅ **Distributed Capability Added**
1059
- - Agents can call deployments remotely
1060
- - Standard tool-based interface
1061
- - Works with KADI ecosystem
1062
-
1063
- ✅ **Ecosystem Integration**
1064
- - Version management through KADI
1065
- - Ability loading and isolation
1066
- - Compatible with MCP/broker protocols
1067
-
1068
- ✅ **Backward Compatibility**
1069
- - Existing code keeps working
1070
- - No forced migration
1071
- - Gradual adoption path
1072
-
1073
- ✅ **Resource Management**
1074
- - Automatic cleanup via RAII (fluent API)
1075
- - No manual cleanup needed
1076
- - Fewer bugs from forgotten cleanup
1077
-
1078
- ✅ **Flexibility**
1079
- - Choose the right interface for your use case
1080
- - Not locked into one pattern
1081
- - Can mix both in same project
1082
-
1083
- ---
1084
-
1085
- ### Trade-offs
1086
-
1087
- ⚠️ **Slightly More Complex Package**
1088
- - Two entry points (`./` and `./lib`)
1089
- - Need to understand which to use when
1090
- - More documentation needed
1091
-
1092
- **Mitigation:**
1093
- - Clear documentation with examples
1094
- - Decision tree for which interface to use
1095
- - Both are well-designed and intuitive
1096
-
1097
- ⚠️ **Two Interfaces to Maintain**
1098
- - Library interface (fluent API)
1099
- - Ability interface (tools)
1100
- - Need to keep both in sync
1101
-
1102
- **Mitigation:**
1103
- - Ability wraps library (single source of truth)
1104
- - Changes to library automatically available in ability
1105
- - Thin wrapper means less maintenance
1106
-
1107
- ⚠️ **Learning Curve**
1108
- - Developers need to know both patterns exist
1109
- - When to use fluent vs tools
1110
-
1111
- **Mitigation:**
1112
- - Good defaults (CLI → fluent, agents → tools)
1113
- - Clear documentation and examples
1114
- - Each interface is simple on its own
1115
-
1116
- ---
1117
-
1118
- ### Overall Assessment
1119
-
1120
- **Score: 8/10**
1121
-
1122
- The dual export pattern is:
1123
- - ✅ **Worth implementing** - benefits outweigh costs
1124
- - ✅ **Proven pattern** - secret-ability demonstrates success
1125
- - ✅ **Best of both worlds** - no compromises on either interface
1126
- - ✅ **Future-proof** - supports both local and distributed use cases
1127
-
1128
- ---
1129
-
1130
- ## Comparison with secret-ability
1131
-
1132
- ### How They Use the Same Pattern
1133
-
1134
- Both use the dual export pattern, but for different domains:
1135
-
1136
- **secret-ability:**
1137
- ```typescript
1138
- // Library (fluent API for vault management)
1139
- import { Secrets } from '@kadi.build/secret-ability/lib';
1140
-
1141
- await secrets.init()
1142
- .ensure('wallet', { type: 'custom', path: './wallet.vault' })
1143
- .use('wallet');
1144
-
1145
- await secrets.set('KEY', 'value');
1146
- ```
1147
-
1148
- **deploy-ability (proposed):**
1149
- ```typescript
1150
- // Library (fluent API for deployment)
1151
- import { DeploymentPipeline } from '@kadi.build/deploy-ability/lib';
1152
-
1153
- await DeploymentPipeline
1154
- .create({ projectRoot: './', profile: 'production' })
1155
- .withLocalImageSupport()
1156
- .execute();
1157
- ```
1158
-
1159
- ### Lessons Learned from secret-ability
1160
-
1161
- 1. **Fluent initializer pattern works great**
1162
- - secret-ability's `SecretsInitializer` is excellent
1163
- - Chainable setup, then execute
1164
- - We're using the same pattern
1165
-
1166
- 2. **Tool interface is simple**
1167
- - Each tool maps to a library method
1168
- - Thin wrapper, minimal complexity
1169
- - Easy to maintain
1170
-
1171
- 3. **Clear separation of concerns**
1172
- - Library handles business logic
1173
- - Ability handles RPC and protocols
1174
- - Works well in practice
1175
-
1176
- 4. **Documentation is key**
1177
- - Need clear examples for both interfaces
1178
- - Explain when to use which
1179
- - Show the connection between them
1180
-
1181
- ### Differences in Use Cases
1182
-
1183
- **secret-ability:**
1184
- - Manages vault initialization and lifecycle
1185
- - Secret CRUD operations
1186
- - Vault registry management
1187
-
1188
- **deploy-ability:**
1189
- - Orchestrates deployment pipelines
1190
- - Manages temporary resources (registry, tunnel)
1191
- - Coordinates multiple services
1192
-
1193
- **Both benefit from fluent APIs** because:
1194
- - Complex multi-step processes
1195
- - Resource lifecycle management
1196
- - Clear configuration builders
1197
- - Automatic cleanup needed
1198
-
1199
- ---
1200
-
1201
- ## FAQ
1202
-
1203
- ### "Why not just use fluent API for the ability too?"
1204
-
1205
- **Short answer:** Fluent APIs don't work across process boundaries.
1206
-
1207
- **Long answer:**
1208
-
1209
- Fluent APIs require maintaining state (the builder object):
1210
-
1211
- ```typescript
1212
- const pipeline = DeploymentPipeline.create({...}); // State created
1213
- pipeline.withWallet(wallet); // State modified
1214
- pipeline.withCertificate(cert); // State modified
1215
- await pipeline.execute(); // State executed
1216
- ```
1217
-
1218
- In a distributed system:
1219
- - Each method call might go to a different instance
1220
- - You can't serialize a builder object with all its state
1221
- - The server doesn't maintain session state between calls
1222
-
1223
- Tool-based RPC is stateless:
1224
- ```typescript
1225
- await deployAbility.deploy({
1226
- // Everything in one call
1227
- profile: 'prod',
1228
- wallet: wallet,
1229
- certificate: cert
1230
- });
1231
- ```
1232
-
1233
- Each call is independent and self-contained.
1234
-
1235
- ---
1236
-
1237
- ### "Can I use the fluent API from remote services?"
1238
-
1239
- **Yes, but only if you import the library directly:**
1240
-
1241
- ```typescript
1242
- // In a remote agent
1243
- import { DeploymentPipeline } from '@kadi.build/deploy-ability/lib';
1244
-
1245
- // This works because you're using the library, not the ability
1246
- await DeploymentPipeline.create({...}).execute();
1247
- ```
1248
-
1249
- **However:**
1250
- - You lose KADI version isolation
1251
- - Direct npm dependency instead of ability loading
1252
- - Might have version conflicts
1253
-
1254
- **Recommendation:** If you're remote, use the ability interface instead.
1255
-
1256
- ---
1257
-
1258
- ### "Which interface should I use?"
1259
-
1260
- **Decision tree:**
1261
-
1262
- ```
1263
- Are you writing...
1264
- ├─ CLI command? → Use fluent API (/lib)
1265
- ├─ Local script? → Use fluent API (/lib)
1266
- ├─ Agent calling another agent? → Use ability interface (default)
1267
- ├─ Broker/MCP integration? → Use ability interface (default)
1268
- └─ Not sure? → Use fluent API if local, ability if distributed
1269
- ```
1270
-
1271
- **Rule of thumb:**
1272
- - **Same process** → Fluent API (`/lib`)
1273
- - **Different process** → Ability interface (default)
1274
-
1275
- ---
1276
-
1277
- ### "Will this break existing code?"
1278
-
1279
- **No! Here's why:**
1280
-
1281
- **For deploy-ability consumers:**
1282
- - Current code imports from default export
1283
- - We're keeping all existing exports in `/lib`
1284
- - Old API remains available
1285
-
1286
- **Example:**
1287
- ```typescript
1288
- // Old code (still works!)
1289
- import { loadProfile, setupRegistryIfNeeded } from '@kadi.build/deploy-ability';
1290
-
1291
- // New code (better!)
1292
- import { DeploymentPipeline } from '@kadi.build/deploy-ability/lib';
1293
- ```
1294
-
1295
- Both work! No forced migration.
1296
-
1297
- ---
1298
-
1299
- ### "Do I need to update my code immediately?"
1300
-
1301
- **No, it's optional:**
1302
-
1303
- The dual export pattern means:
1304
- - ✅ Existing code keeps working
1305
- - ✅ New code can use fluent API
1306
- - ✅ Gradual migration possible
1307
- - ✅ No breaking changes
1308
-
1309
- **When to update:**
1310
- - When you want the fluent API benefits
1311
- - When you need remote deployment capability
1312
- - When you're writing new code
1313
- - At your own pace
1314
-
1315
- ---
1316
-
1317
- ## Conclusion
1318
-
1319
- ### Summary
1320
-
1321
- **The dual export pattern enables deploy-ability to be both:**
1322
- 1. A KADI ability (tool-based RPC for distributed use)
1323
- 2. A fluent API library (for local/CLI use)
1324
-
1325
- **This is possible because:**
1326
- - The ability layer is a thin wrapper around the library
1327
- - Both interfaces serve different, valid use cases
1328
- - secret-ability proves this pattern works
1329
-
1330
- **The Pipeline Builder fluent API is preserved and enhanced:**
1331
- - ✅ Available via `/lib` export
1332
- - ✅ Best-in-class developer experience
1333
- - ✅ Automatic resource management
1334
- - ✅ Type-safe and discoverable
1335
-
1336
- **The ability interface adds new capabilities:**
1337
- - ✅ Distributed deployments
1338
- - ✅ Agent-to-agent communication
1339
- - ✅ KADI ecosystem integration
1340
- - ✅ Standard tool-based protocol
1341
-
1342
- ### Recommendation
1343
-
1344
- **Proceed with converting deploy-ability to a KADI ability using the dual export pattern.**
1345
-
1346
- **Implementation priority:**
1347
- 1. ✅ Create `kadi-ability.ts` with tool wrappers
1348
- 2. ✅ Update `package.json` with dual exports
1349
- 3. ✅ Update `index.ts` to export fluent API
1350
- 4. ✅ Write comprehensive documentation
1351
- 5. ✅ Add examples for both use cases
1352
- 6. ✅ Update existing consumers gradually
1353
-
1354
- **Expected outcome:**
1355
- - Best of both worlds
1356
- - No breaking changes
1357
- - Future-proof architecture
1358
- - Happy developers ✨
1359
-
1360
- ---
1361
-
1362
- **Document Version:** 1.0
1363
- **Last Updated:** 2025-11-18
1364
- **Author:** KADI Team
1365
- **Status:** Design Proposal - Ready for Implementation