@tjamescouch/agentchat 0.1.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.
@@ -0,0 +1,811 @@
1
+ /**
2
+ * Akash Network Deployment Module
3
+ *
4
+ * Enables self-service deployment to Akash decentralized cloud.
5
+ *
6
+ * DISCLAIMER: This is infrastructure tooling, not a cryptocurrency product.
7
+ * AKT tokens are used solely to pay for compute resources.
8
+ * You are responsible for your own wallet security and funds.
9
+ */
10
+
11
+ import fs from 'fs/promises';
12
+ import path from 'path';
13
+ import os from 'os';
14
+ import yaml from 'js-yaml';
15
+
16
+ // Default paths
17
+ const AKASH_DIR = path.join(os.homedir(), '.agentchat');
18
+ const WALLET_PATH = path.join(AKASH_DIR, 'akash-wallet.json');
19
+ const DEPLOYMENTS_PATH = path.join(AKASH_DIR, 'akash-deployments.json');
20
+ const CERTIFICATE_PATH = path.join(AKASH_DIR, 'akash-cert.json');
21
+
22
+ // Network configuration
23
+ const NETWORKS = {
24
+ mainnet: {
25
+ chainId: 'akashnet-2',
26
+ rpcEndpoint: 'https://rpc.akashnet.net:443',
27
+ restEndpoint: 'https://api.akashnet.net:443',
28
+ prefix: 'akash'
29
+ },
30
+ testnet: {
31
+ chainId: 'sandbox-01',
32
+ rpcEndpoint: 'https://rpc.sandbox-01.aksh.pw:443',
33
+ restEndpoint: 'https://api.sandbox-01.aksh.pw:443',
34
+ prefix: 'akash'
35
+ }
36
+ };
37
+
38
+ // Default deposit amount (5 AKT in uakt)
39
+ const DEFAULT_DEPOSIT = '5000000';
40
+
41
+ /**
42
+ * Akash Wallet - manages keypair and signing
43
+ */
44
+ export class AkashWallet {
45
+ constructor(data) {
46
+ this.mnemonic = data.mnemonic;
47
+ this.address = data.address;
48
+ this.pubkey = data.pubkey;
49
+ this.network = data.network || 'testnet';
50
+ this.created = data.created || new Date().toISOString();
51
+ }
52
+
53
+ /**
54
+ * Generate a new wallet
55
+ */
56
+ static async generate(network = 'testnet') {
57
+ const { DirectSecp256k1HdWallet } = await import('@cosmjs/proto-signing');
58
+
59
+ const wallet = await DirectSecp256k1HdWallet.generate(24, {
60
+ prefix: NETWORKS[network].prefix
61
+ });
62
+
63
+ const [account] = await wallet.getAccounts();
64
+
65
+ return new AkashWallet({
66
+ mnemonic: wallet.mnemonic,
67
+ address: account.address,
68
+ pubkey: Buffer.from(account.pubkey).toString('base64'),
69
+ network,
70
+ created: new Date().toISOString()
71
+ });
72
+ }
73
+
74
+ /**
75
+ * Load wallet from mnemonic
76
+ */
77
+ static async fromMnemonic(mnemonic, network = 'testnet') {
78
+ const { DirectSecp256k1HdWallet } = await import('@cosmjs/proto-signing');
79
+
80
+ const wallet = await DirectSecp256k1HdWallet.fromMnemonic(mnemonic, {
81
+ prefix: NETWORKS[network].prefix
82
+ });
83
+
84
+ const [account] = await wallet.getAccounts();
85
+
86
+ return new AkashWallet({
87
+ mnemonic,
88
+ address: account.address,
89
+ pubkey: Buffer.from(account.pubkey).toString('base64'),
90
+ network
91
+ });
92
+ }
93
+
94
+ /**
95
+ * Get signing wallet instance
96
+ */
97
+ async getSigningWallet() {
98
+ const { DirectSecp256k1HdWallet } = await import('@cosmjs/proto-signing');
99
+ return DirectSecp256k1HdWallet.fromMnemonic(this.mnemonic, {
100
+ prefix: NETWORKS[this.network].prefix
101
+ });
102
+ }
103
+
104
+ /**
105
+ * Save wallet to file
106
+ */
107
+ async save(filePath = WALLET_PATH) {
108
+ await fs.mkdir(path.dirname(filePath), { recursive: true });
109
+
110
+ const data = {
111
+ version: 1,
112
+ network: this.network,
113
+ address: this.address,
114
+ pubkey: this.pubkey,
115
+ mnemonic: this.mnemonic,
116
+ created: this.created
117
+ };
118
+
119
+ await fs.writeFile(filePath, JSON.stringify(data, null, 2), { mode: 0o600 });
120
+ return filePath;
121
+ }
122
+
123
+ /**
124
+ * Load wallet from file
125
+ */
126
+ static async load(filePath = WALLET_PATH) {
127
+ const content = await fs.readFile(filePath, 'utf-8');
128
+ const data = JSON.parse(content);
129
+
130
+ if (data.version !== 1) {
131
+ throw new Error(`Unsupported wallet version: ${data.version}`);
132
+ }
133
+
134
+ return new AkashWallet(data);
135
+ }
136
+
137
+ /**
138
+ * Check if wallet file exists
139
+ */
140
+ static async exists(filePath = WALLET_PATH) {
141
+ try {
142
+ await fs.access(filePath);
143
+ return true;
144
+ } catch {
145
+ return false;
146
+ }
147
+ }
148
+
149
+ /**
150
+ * Get wallet info for display (no sensitive data)
151
+ */
152
+ getInfo() {
153
+ return {
154
+ address: this.address,
155
+ network: this.network,
156
+ created: this.created
157
+ };
158
+ }
159
+ }
160
+
161
+ /**
162
+ * Generate SDL (Stack Definition Language) for agentchat server
163
+ */
164
+ export function generateSDL(options = {}) {
165
+ const config = {
166
+ name: options.name || 'agentchat',
167
+ port: options.port || 6667,
168
+ cpu: options.cpu || 0.5,
169
+ memory: options.memory || 512,
170
+ storage: options.storage || 1,
171
+ logMessages: options.logMessages || false
172
+ };
173
+
174
+ const sdl = {
175
+ version: '2.0',
176
+ services: {
177
+ agentchat: {
178
+ image: options.image || 'ghcr.io/anthropics/agentchat:latest',
179
+ expose: [
180
+ {
181
+ port: config.port,
182
+ as: 80,
183
+ to: [{ global: true }]
184
+ }
185
+ ],
186
+ env: [
187
+ `PORT=${config.port}`,
188
+ 'HOST=0.0.0.0',
189
+ `SERVER_NAME=${config.name}`,
190
+ `LOG_MESSAGES=${config.logMessages}`
191
+ ]
192
+ }
193
+ },
194
+ profiles: {
195
+ compute: {
196
+ agentchat: {
197
+ resources: {
198
+ cpu: { units: config.cpu },
199
+ memory: { size: `${config.memory}Mi` },
200
+ storage: { size: `${config.storage}Gi` }
201
+ }
202
+ }
203
+ },
204
+ placement: {
205
+ dcloud: {
206
+ pricing: {
207
+ agentchat: {
208
+ denom: 'uakt',
209
+ amount: 1000
210
+ }
211
+ }
212
+ }
213
+ }
214
+ },
215
+ deployment: {
216
+ agentchat: {
217
+ dcloud: {
218
+ profile: 'agentchat',
219
+ count: 1
220
+ }
221
+ }
222
+ }
223
+ };
224
+
225
+ return yaml.dump(sdl, { lineWidth: -1 });
226
+ }
227
+
228
+ /**
229
+ * Akash deployment client
230
+ */
231
+ export class AkashClient {
232
+ constructor(wallet) {
233
+ this.wallet = wallet;
234
+ this.network = NETWORKS[wallet.network];
235
+ }
236
+
237
+ /**
238
+ * Get signing client for transactions
239
+ */
240
+ async getSigningClient() {
241
+ const { SigningStargateClient } = await import('@cosmjs/stargate');
242
+ const { getAkashTypeRegistry } = await import('@akashnetwork/akashjs/build/stargate/index.js');
243
+ const { Registry } = await import('@cosmjs/proto-signing');
244
+
245
+ const signingWallet = await this.wallet.getSigningWallet();
246
+ const registry = new Registry(getAkashTypeRegistry());
247
+
248
+ return SigningStargateClient.connectWithSigner(
249
+ this.network.rpcEndpoint,
250
+ signingWallet,
251
+ { registry }
252
+ );
253
+ }
254
+
255
+ /**
256
+ * Query account balance
257
+ */
258
+ async getBalance() {
259
+ const { StargateClient } = await import('@cosmjs/stargate');
260
+
261
+ let client;
262
+ try {
263
+ client = await StargateClient.connect(this.network.rpcEndpoint);
264
+ } catch (err) {
265
+ throw new Error(
266
+ `Failed to connect to ${this.wallet.network} RPC endpoint.\n` +
267
+ `Network: ${this.network.rpcEndpoint}\n` +
268
+ `The network may be temporarily unavailable. Try again later.`
269
+ );
270
+ }
271
+
272
+ const balance = await client.getBalance(this.wallet.address, 'uakt');
273
+ const akt = parseInt(balance.amount) / 1_000_000;
274
+
275
+ return {
276
+ uakt: balance.amount,
277
+ akt: akt.toFixed(6),
278
+ sufficient: parseInt(balance.amount) >= 5_000_000
279
+ };
280
+ }
281
+
282
+ /**
283
+ * Create a deployment on Akash
284
+ */
285
+ async createDeployment(sdlContent, options = {}) {
286
+ const { SDL } = await import('@akashnetwork/akashjs/build/sdl/SDL/SDL.js');
287
+ const { MsgCreateDeployment } = await import('@akashnetwork/akash-api/v1beta3');
288
+ const { Message } = await import('@akashnetwork/akashjs/build/stargate/index.js');
289
+
290
+ // Parse SDL
291
+ const sdl = SDL.fromString(sdlContent, 'beta3');
292
+
293
+ // Get signing client
294
+ const client = await this.getSigningClient();
295
+ const blockHeight = await client.getHeight();
296
+
297
+ // Create deployment ID
298
+ const dseq = options.dseq || blockHeight.toString();
299
+
300
+ // Build deployment message
301
+ const groups = sdl.groups();
302
+ const manifestVersion = await sdl.manifestVersion();
303
+
304
+ const deploymentMsg = {
305
+ id: {
306
+ owner: this.wallet.address,
307
+ dseq: dseq
308
+ },
309
+ groups: groups,
310
+ deposit: {
311
+ denom: 'uakt',
312
+ amount: options.deposit || DEFAULT_DEPOSIT
313
+ },
314
+ version: manifestVersion,
315
+ depositor: this.wallet.address
316
+ };
317
+
318
+ const msg = {
319
+ typeUrl: Message.MsgCreateDeployment,
320
+ value: MsgCreateDeployment.fromPartial(deploymentMsg)
321
+ };
322
+
323
+ // Broadcast transaction
324
+ const fee = {
325
+ amount: [{ denom: 'uakt', amount: '25000' }],
326
+ gas: '500000'
327
+ };
328
+
329
+ console.log('Broadcasting deployment transaction...');
330
+ const tx = await client.signAndBroadcast(
331
+ this.wallet.address,
332
+ [msg],
333
+ fee,
334
+ 'agentchat deployment'
335
+ );
336
+
337
+ if (tx.code !== 0) {
338
+ throw new Error(`Deployment failed: ${tx.rawLog}`);
339
+ }
340
+
341
+ console.log(`Deployment created: dseq=${dseq}, tx=${tx.transactionHash}`);
342
+
343
+ // Save deployment record
344
+ await this.saveDeployment({
345
+ dseq,
346
+ owner: this.wallet.address,
347
+ txHash: tx.transactionHash,
348
+ status: 'pending_bids',
349
+ createdAt: new Date().toISOString(),
350
+ sdl: sdlContent
351
+ });
352
+
353
+ return {
354
+ dseq,
355
+ txHash: tx.transactionHash,
356
+ status: 'pending_bids',
357
+ manifest: sdl.manifest()
358
+ };
359
+ }
360
+
361
+ /**
362
+ * Query bids for a deployment
363
+ */
364
+ async queryBids(dseq) {
365
+ const url = `${this.network.restEndpoint}/akash/market/v1beta4/bids/list?filters.owner=${this.wallet.address}&filters.dseq=${dseq}`;
366
+
367
+ const response = await fetch(url);
368
+ if (!response.ok) {
369
+ throw new Error(`Failed to query bids: ${response.statusText}`);
370
+ }
371
+
372
+ const data = await response.json();
373
+ return data.bids || [];
374
+ }
375
+
376
+ /**
377
+ * Accept a bid and create a lease
378
+ */
379
+ async createLease(dseq, provider, gseq = 1, oseq = 1) {
380
+ const { MsgCreateLease } = await import('@akashnetwork/akash-api/v1beta3');
381
+ const { Message } = await import('@akashnetwork/akashjs/build/stargate/index.js');
382
+
383
+ const client = await this.getSigningClient();
384
+
385
+ const leaseMsg = {
386
+ bidId: {
387
+ owner: this.wallet.address,
388
+ dseq: dseq,
389
+ gseq: gseq,
390
+ oseq: oseq,
391
+ provider: provider
392
+ }
393
+ };
394
+
395
+ const msg = {
396
+ typeUrl: Message.MsgCreateLease,
397
+ value: MsgCreateLease.fromPartial(leaseMsg)
398
+ };
399
+
400
+ const fee = {
401
+ amount: [{ denom: 'uakt', amount: '25000' }],
402
+ gas: '500000'
403
+ };
404
+
405
+ console.log('Creating lease...');
406
+ const tx = await client.signAndBroadcast(
407
+ this.wallet.address,
408
+ [msg],
409
+ fee,
410
+ 'create lease'
411
+ );
412
+
413
+ if (tx.code !== 0) {
414
+ throw new Error(`Lease creation failed: ${tx.rawLog}`);
415
+ }
416
+
417
+ console.log(`Lease created: provider=${provider}, tx=${tx.transactionHash}`);
418
+
419
+ // Update deployment record
420
+ await this.updateDeployment(dseq, {
421
+ status: 'active',
422
+ provider,
423
+ leaseCreatedAt: new Date().toISOString()
424
+ });
425
+
426
+ return {
427
+ dseq,
428
+ provider,
429
+ gseq,
430
+ oseq,
431
+ txHash: tx.transactionHash
432
+ };
433
+ }
434
+
435
+ /**
436
+ * Send manifest to provider
437
+ */
438
+ async sendManifest(dseq, provider, manifest) {
439
+ const { certificate } = await import('@akashnetwork/akashjs/build/certificates/index.js');
440
+
441
+ // Load or create certificate
442
+ let cert;
443
+ try {
444
+ const certData = await fs.readFile(CERTIFICATE_PATH, 'utf-8');
445
+ cert = JSON.parse(certData);
446
+ } catch {
447
+ // Generate new certificate
448
+ console.log('Generating deployment certificate...');
449
+ const generated = await certificate.create(this.wallet.address);
450
+ cert = {
451
+ cert: generated.cert,
452
+ privateKey: generated.privateKey,
453
+ publicKey: generated.publicKey
454
+ };
455
+ await fs.writeFile(CERTIFICATE_PATH, JSON.stringify(cert, null, 2), { mode: 0o600 });
456
+ }
457
+
458
+ // Query provider info to get hostUri
459
+ const providerUrl = `${this.network.restEndpoint}/akash/provider/v1beta3/providers/${provider}`;
460
+ const providerResponse = await fetch(providerUrl);
461
+ if (!providerResponse.ok) {
462
+ throw new Error(`Failed to get provider info: ${providerResponse.statusText}`);
463
+ }
464
+ const providerInfo = await providerResponse.json();
465
+ const hostUri = providerInfo.provider?.hostUri;
466
+
467
+ if (!hostUri) {
468
+ throw new Error('Provider hostUri not found');
469
+ }
470
+
471
+ // Send manifest
472
+ const manifestUrl = `${hostUri}/deployment/${dseq}/manifest`;
473
+ console.log(`Sending manifest to ${manifestUrl}...`);
474
+
475
+ const response = await fetch(manifestUrl, {
476
+ method: 'PUT',
477
+ headers: {
478
+ 'Content-Type': 'application/json'
479
+ },
480
+ body: JSON.stringify(manifest)
481
+ });
482
+
483
+ if (!response.ok) {
484
+ const text = await response.text();
485
+ throw new Error(`Failed to send manifest: ${response.statusText} - ${text}`);
486
+ }
487
+
488
+ console.log('Manifest sent successfully');
489
+
490
+ // Update deployment record
491
+ await this.updateDeployment(dseq, {
492
+ manifestSent: true,
493
+ manifestSentAt: new Date().toISOString()
494
+ });
495
+
496
+ return { success: true };
497
+ }
498
+
499
+ /**
500
+ * Get lease status from provider
501
+ */
502
+ async getLeaseStatus(dseq, provider, gseq = 1, oseq = 1) {
503
+ // Query provider info
504
+ const providerUrl = `${this.network.restEndpoint}/akash/provider/v1beta3/providers/${provider}`;
505
+ const providerResponse = await fetch(providerUrl);
506
+ if (!providerResponse.ok) {
507
+ throw new Error(`Failed to get provider info: ${providerResponse.statusText}`);
508
+ }
509
+ const providerInfo = await providerResponse.json();
510
+ const hostUri = providerInfo.provider?.hostUri;
511
+
512
+ if (!hostUri) {
513
+ throw new Error('Provider hostUri not found');
514
+ }
515
+
516
+ // Query lease status
517
+ const statusUrl = `${hostUri}/lease/${dseq}/${gseq}/${oseq}/status`;
518
+ const response = await fetch(statusUrl);
519
+
520
+ if (!response.ok) {
521
+ throw new Error(`Failed to get lease status: ${response.statusText}`);
522
+ }
523
+
524
+ return response.json();
525
+ }
526
+
527
+ /**
528
+ * Close a deployment
529
+ */
530
+ async closeDeployment(dseq) {
531
+ const { MsgCloseDeployment } = await import('@akashnetwork/akash-api/v1beta3');
532
+ const { Message } = await import('@akashnetwork/akashjs/build/stargate/index.js');
533
+
534
+ const client = await this.getSigningClient();
535
+
536
+ const closeMsg = {
537
+ id: {
538
+ owner: this.wallet.address,
539
+ dseq: dseq
540
+ }
541
+ };
542
+
543
+ const msg = {
544
+ typeUrl: Message.MsgCloseDeployment,
545
+ value: MsgCloseDeployment.fromPartial(closeMsg)
546
+ };
547
+
548
+ const fee = {
549
+ amount: [{ denom: 'uakt', amount: '25000' }],
550
+ gas: '500000'
551
+ };
552
+
553
+ console.log('Closing deployment...');
554
+ const tx = await client.signAndBroadcast(
555
+ this.wallet.address,
556
+ [msg],
557
+ fee,
558
+ 'close deployment'
559
+ );
560
+
561
+ if (tx.code !== 0) {
562
+ throw new Error(`Failed to close deployment: ${tx.rawLog}`);
563
+ }
564
+
565
+ // Update deployment record
566
+ await this.updateDeployment(dseq, {
567
+ status: 'closed',
568
+ closedAt: new Date().toISOString()
569
+ });
570
+
571
+ return { dseq, txHash: tx.transactionHash, status: 'closed' };
572
+ }
573
+
574
+ /**
575
+ * Save deployment to local records
576
+ */
577
+ async saveDeployment(deployment) {
578
+ let deployments = [];
579
+ try {
580
+ const content = await fs.readFile(DEPLOYMENTS_PATH, 'utf-8');
581
+ deployments = JSON.parse(content);
582
+ } catch {
583
+ // File doesn't exist yet
584
+ }
585
+
586
+ deployments.push(deployment);
587
+ await fs.writeFile(DEPLOYMENTS_PATH, JSON.stringify(deployments, null, 2));
588
+ }
589
+
590
+ /**
591
+ * Update deployment in local records
592
+ */
593
+ async updateDeployment(dseq, updates) {
594
+ let deployments = [];
595
+ try {
596
+ const content = await fs.readFile(DEPLOYMENTS_PATH, 'utf-8');
597
+ deployments = JSON.parse(content);
598
+ } catch {
599
+ return;
600
+ }
601
+
602
+ const index = deployments.findIndex(d => d.dseq === dseq);
603
+ if (index !== -1) {
604
+ deployments[index] = { ...deployments[index], ...updates };
605
+ await fs.writeFile(DEPLOYMENTS_PATH, JSON.stringify(deployments, null, 2));
606
+ }
607
+ }
608
+
609
+ /**
610
+ * List local deployment records
611
+ */
612
+ async listDeployments() {
613
+ try {
614
+ const content = await fs.readFile(DEPLOYMENTS_PATH, 'utf-8');
615
+ return JSON.parse(content);
616
+ } catch {
617
+ return [];
618
+ }
619
+ }
620
+ }
621
+
622
+ /**
623
+ * High-level deployment functions for CLI
624
+ */
625
+
626
+ export async function generateWallet(network = 'testnet', walletPath = WALLET_PATH) {
627
+ if (await AkashWallet.exists(walletPath)) {
628
+ throw new Error(
629
+ `Wallet already exists at ${walletPath}\n` +
630
+ 'Use --force to overwrite (WARNING: This will destroy your existing wallet!)'
631
+ );
632
+ }
633
+
634
+ const wallet = await AkashWallet.generate(network);
635
+ await wallet.save(walletPath);
636
+
637
+ return wallet;
638
+ }
639
+
640
+ export async function checkBalance(walletPath = WALLET_PATH) {
641
+ const wallet = await AkashWallet.load(walletPath);
642
+ const client = new AkashClient(wallet);
643
+
644
+ return {
645
+ wallet: wallet.getInfo(),
646
+ balance: await client.getBalance()
647
+ };
648
+ }
649
+
650
+ export async function createDeployment(options = {}) {
651
+ const walletPath = options.walletPath || WALLET_PATH;
652
+ const wallet = await AkashWallet.load(walletPath);
653
+ const client = new AkashClient(wallet);
654
+
655
+ // Check balance first
656
+ const balance = await client.getBalance();
657
+ if (!balance.sufficient) {
658
+ throw new Error(
659
+ `Insufficient balance: ${balance.akt} AKT\n` +
660
+ `Need at least 5 AKT for deployment.\n` +
661
+ `Fund your wallet: ${wallet.address}`
662
+ );
663
+ }
664
+
665
+ // Generate SDL
666
+ const sdl = generateSDL(options);
667
+
668
+ // Create deployment
669
+ const deployment = await client.createDeployment(sdl, options);
670
+
671
+ // Wait for bids
672
+ console.log('Waiting for bids (30 seconds)...');
673
+ await new Promise(resolve => setTimeout(resolve, 30000));
674
+
675
+ // Query bids
676
+ const bids = await client.queryBids(deployment.dseq);
677
+
678
+ if (bids.length === 0) {
679
+ console.log('No bids received. Deployment is pending.');
680
+ console.log(`Check status with: agentchat deploy --provider akash --status`);
681
+ return deployment;
682
+ }
683
+
684
+ // Select best bid (lowest price)
685
+ const sortedBids = bids
686
+ .filter(b => b.bid?.state === 'open')
687
+ .sort((a, b) => parseInt(a.bid?.price?.amount || 0) - parseInt(b.bid?.price?.amount || 0));
688
+
689
+ if (sortedBids.length === 0) {
690
+ console.log('No open bids available.');
691
+ return deployment;
692
+ }
693
+
694
+ const bestBid = sortedBids[0];
695
+ const provider = bestBid.bid?.bidId?.provider;
696
+
697
+ console.log(`Accepting bid from provider: ${provider}`);
698
+
699
+ // Create lease
700
+ const lease = await client.createLease(
701
+ deployment.dseq,
702
+ provider,
703
+ bestBid.bid?.bidId?.gseq || 1,
704
+ bestBid.bid?.bidId?.oseq || 1
705
+ );
706
+
707
+ // Send manifest
708
+ await client.sendManifest(deployment.dseq, provider, deployment.manifest);
709
+
710
+ // Get status
711
+ console.log('Waiting for deployment to start (15 seconds)...');
712
+ await new Promise(resolve => setTimeout(resolve, 15000));
713
+
714
+ try {
715
+ const status = await client.getLeaseStatus(deployment.dseq, provider);
716
+ const services = status.services || {};
717
+ const service = Object.values(services)[0];
718
+ const uris = service?.uris || [];
719
+
720
+ if (uris.length > 0) {
721
+ console.log(`\nDeployment ready!`);
722
+ console.log(`Endpoint: ${uris[0]}`);
723
+ return { ...deployment, ...lease, endpoint: uris[0], status: 'active' };
724
+ }
725
+ } catch (err) {
726
+ console.log('Status check failed, deployment may still be starting.');
727
+ }
728
+
729
+ return { ...deployment, ...lease, status: 'active' };
730
+ }
731
+
732
+ export async function listDeployments(walletPath = WALLET_PATH) {
733
+ const wallet = await AkashWallet.load(walletPath);
734
+ const client = new AkashClient(wallet);
735
+
736
+ return client.listDeployments();
737
+ }
738
+
739
+ export async function closeDeployment(dseq, walletPath = WALLET_PATH) {
740
+ const wallet = await AkashWallet.load(walletPath);
741
+ const client = new AkashClient(wallet);
742
+
743
+ return client.closeDeployment(dseq);
744
+ }
745
+
746
+ export async function acceptBid(dseq, provider, walletPath = WALLET_PATH) {
747
+ const wallet = await AkashWallet.load(walletPath);
748
+ const client = new AkashClient(wallet);
749
+
750
+ // Get deployment from local records
751
+ const deployments = await client.listDeployments();
752
+ const deployment = deployments.find(d => d.dseq === dseq);
753
+
754
+ if (!deployment) {
755
+ throw new Error(`Deployment ${dseq} not found in local records`);
756
+ }
757
+
758
+ // Create lease
759
+ const lease = await client.createLease(dseq, provider);
760
+
761
+ // Parse SDL and send manifest
762
+ const { SDL } = await import('@akashnetwork/akashjs/build/sdl/SDL/SDL.js');
763
+ const sdl = SDL.fromString(deployment.sdl, 'beta3');
764
+ await client.sendManifest(dseq, provider, sdl.manifest());
765
+
766
+ return lease;
767
+ }
768
+
769
+ export async function queryBids(dseq, walletPath = WALLET_PATH) {
770
+ const wallet = await AkashWallet.load(walletPath);
771
+ const client = new AkashClient(wallet);
772
+
773
+ return client.queryBids(dseq);
774
+ }
775
+
776
+ export async function getDeploymentStatus(dseq, walletPath = WALLET_PATH) {
777
+ const wallet = await AkashWallet.load(walletPath);
778
+ const client = new AkashClient(wallet);
779
+
780
+ // Get deployment from local records
781
+ const deployments = await client.listDeployments();
782
+ const deployment = deployments.find(d => d.dseq === dseq);
783
+
784
+ if (!deployment) {
785
+ throw new Error(`Deployment ${dseq} not found`);
786
+ }
787
+
788
+ if (!deployment.provider) {
789
+ // No lease yet, check for bids
790
+ const bids = await client.queryBids(dseq);
791
+ return {
792
+ ...deployment,
793
+ bids: bids.map(b => ({
794
+ provider: b.bid?.bidId?.provider,
795
+ price: b.bid?.price?.amount,
796
+ state: b.bid?.state
797
+ }))
798
+ };
799
+ }
800
+
801
+ // Has a lease, get status from provider
802
+ try {
803
+ const status = await client.getLeaseStatus(dseq, deployment.provider);
804
+ return { ...deployment, leaseStatus: status };
805
+ } catch (err) {
806
+ return { ...deployment, leaseStatusError: err.message };
807
+ }
808
+ }
809
+
810
+ // Export for CLI
811
+ export { NETWORKS, WALLET_PATH, DEPLOYMENTS_PATH, CERTIFICATE_PATH };