agentvault 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.dfx/local/network-id +4 -0
- package/.next/trace +2 -0
- package/.vercel/README.txt +11 -0
- package/.vercel/project.json +1 -0
- package/AGENTS.md +43 -0
- package/CHANGELOG.md +196 -0
- package/LICENSE +21 -0
- package/PLAN_VAULT_INTEGRATION.md +318 -0
- package/README.md +253 -0
- package/backups/agentvault-backup-test-agent-2026-02-12T17-54-28-967Z.json +28 -0
- package/backups/agentvault-backup-test-agent-2026-02-12T17-54-29-032Z.backup +1 -0
- package/backups/agentvault-backup-test-agent-2026-02-12T17-57-42-373Z.json +28 -0
- package/backups/agentvault-backup-test-agent-2026-02-12T17-57-42-428Z.backup +1 -0
- package/backups/agentvault-backup-test-agent-2026-02-12T18-52-25-132Z.json +28 -0
- package/backups/agentvault-backup-test-agent-2026-02-12T18-52-25-247Z.backup +1 -0
- package/backups/agentvault-backup-test-agent-2026-02-12T18-54-09-216Z.json +28 -0
- package/backups/agentvault-backup-test-agent-2026-02-12T18-54-09-283Z.backup +1 -0
- package/backups/agentvault-backup-test-agent-2026-02-12T22-18-22-772Z.backup +1 -0
- package/backups/agentvault-backup-test-agent-2026-02-12T22-18-22-793Z.json +28 -0
- package/backups/test-backup.json +28 -0
- package/dist/cli/commands/approve.d.ts +4 -0
- package/dist/cli/commands/approve.js +232 -0
- package/dist/cli/commands/archive.d.ts +4 -0
- package/dist/cli/commands/archive.js +192 -0
- package/dist/cli/commands/backup.d.ts +4 -0
- package/dist/cli/commands/backup.js +164 -0
- package/dist/cli/commands/cloud-backup.d.ts +4 -0
- package/dist/cli/commands/cloud-backup.js +221 -0
- package/dist/cli/commands/cycles.d.ts +8 -0
- package/dist/cli/commands/cycles.js +83 -0
- package/dist/cli/commands/decrypt.d.ts +16 -0
- package/dist/cli/commands/decrypt.js +101 -0
- package/dist/cli/commands/deploy.d.ts +32 -0
- package/dist/cli/commands/deploy.js +208 -0
- package/dist/cli/commands/exec.d.ts +26 -0
- package/dist/cli/commands/exec.js +109 -0
- package/dist/cli/commands/fetch.d.ts +23 -0
- package/dist/cli/commands/fetch.js +164 -0
- package/dist/cli/commands/health.d.ts +8 -0
- package/dist/cli/commands/health.js +72 -0
- package/dist/cli/commands/identity.d.ts +8 -0
- package/dist/cli/commands/identity.js +140 -0
- package/dist/cli/commands/inference.d.ts +4 -0
- package/dist/cli/commands/inference.js +225 -0
- package/dist/cli/commands/info.d.ts +8 -0
- package/dist/cli/commands/info.js +59 -0
- package/dist/cli/commands/init.d.ts +19 -0
- package/dist/cli/commands/init.js +135 -0
- package/dist/cli/commands/instrument.d.ts +8 -0
- package/dist/cli/commands/instrument.js +35 -0
- package/dist/cli/commands/list.d.ts +36 -0
- package/dist/cli/commands/list.js +173 -0
- package/dist/cli/commands/logs.d.ts +8 -0
- package/dist/cli/commands/logs.js +96 -0
- package/dist/cli/commands/monitor.d.ts +8 -0
- package/dist/cli/commands/monitor.js +84 -0
- package/dist/cli/commands/network.d.ts +14 -0
- package/dist/cli/commands/network.js +258 -0
- package/dist/cli/commands/package.d.ts +36 -0
- package/dist/cli/commands/package.js +188 -0
- package/dist/cli/commands/profile.d.ts +8 -0
- package/dist/cli/commands/profile.js +76 -0
- package/dist/cli/commands/promote.d.ts +8 -0
- package/dist/cli/commands/promote.js +89 -0
- package/dist/cli/commands/rebuild.d.ts +21 -0
- package/dist/cli/commands/rebuild.js +140 -0
- package/dist/cli/commands/rollback.d.ts +8 -0
- package/dist/cli/commands/rollback.js +120 -0
- package/dist/cli/commands/show.d.ts +36 -0
- package/dist/cli/commands/show.js +200 -0
- package/dist/cli/commands/stats.d.ts +8 -0
- package/dist/cli/commands/stats.js +34 -0
- package/dist/cli/commands/status.d.ts +14 -0
- package/dist/cli/commands/status.js +83 -0
- package/dist/cli/commands/test.d.ts +8 -0
- package/dist/cli/commands/test.js +109 -0
- package/dist/cli/commands/tokens.d.ts +8 -0
- package/dist/cli/commands/tokens.js +62 -0
- package/dist/cli/commands/trace.d.ts +8 -0
- package/dist/cli/commands/trace.js +68 -0
- package/dist/cli/commands/wallet-export.d.ts +13 -0
- package/dist/cli/commands/wallet-export.js +140 -0
- package/dist/cli/commands/wallet-history.d.ts +10 -0
- package/dist/cli/commands/wallet-history.js +127 -0
- package/dist/cli/commands/wallet-import.d.ts +10 -0
- package/dist/cli/commands/wallet-import.js +209 -0
- package/dist/cli/commands/wallet-multi-send.d.ts +17 -0
- package/dist/cli/commands/wallet-multi-send.js +195 -0
- package/dist/cli/commands/wallet-process-queue.d.ts +19 -0
- package/dist/cli/commands/wallet-process-queue.js +209 -0
- package/dist/cli/commands/wallet-sign.d.ts +13 -0
- package/dist/cli/commands/wallet-sign.js +207 -0
- package/dist/cli/commands/wallet.d.ts +12 -0
- package/dist/cli/commands/wallet.js +794 -0
- package/dist/cli/index.d.ts +10 -0
- package/dist/cli/index.js +96 -0
- package/dist/vitest.config.d.ts +3 -0
- package/dist/vitest.config.js +14 -0
- package/fixup_1_0_OSS_release.md +136 -0
- package/fixup_REALEASE_PRD.md +136 -0
- package/package.json +79 -0
- package/pnpm-workspace.yaml +5 -0
- package/scripts/dev-dashboard.mjs +84 -0
- package/site/README.md +63 -0
- package/site/docusaurus.config.ts +148 -0
- package/site/package-lock.json +18383 -0
- package/site/package.json +47 -0
- package/site/sidebars.ts +86 -0
- package/site/static/.gitkeep +0 -0
- package/site/static/img/logo.svg +28 -0
- package/site/static/img/og-image.svg +35 -0
- package/src/archival/archive-manager.ts +372 -0
- package/src/archival/arweave-client.ts +289 -0
- package/src/archival/index.ts +8 -0
- package/src/backup/backup.ts +315 -0
- package/src/backup/index.ts +7 -0
- package/src/cloud-storage/cloud-sync.ts +461 -0
- package/src/cloud-storage/index.ts +11 -0
- package/src/cloud-storage/provider-detector.ts +198 -0
- package/src/cloud-storage/types.ts +104 -0
- package/src/debugging/index.ts +6 -0
- package/src/debugging/logs.ts +193 -0
- package/src/debugging/types.ts +100 -0
- package/src/deployment/deployer.ts +274 -0
- package/src/deployment/icpClient.ts +620 -0
- package/src/deployment/index.ts +46 -0
- package/src/deployment/promotion.ts +161 -0
- package/src/deployment/types.ts +111 -0
- package/src/icp/batch.ts +374 -0
- package/src/icp/cycles.ts +50 -0
- package/src/icp/environment.ts +215 -0
- package/src/icp/icpcli.ts +438 -0
- package/src/icp/icwasm.ts +222 -0
- package/src/icp/identity.ts +77 -0
- package/src/icp/index.ts +94 -0
- package/src/icp/optimization.ts +242 -0
- package/src/icp/tokens.ts +36 -0
- package/src/icp/tool-detector.ts +110 -0
- package/src/icp/types.ts +574 -0
- package/src/index.ts +25 -0
- package/src/inference/bittensor-client.ts +304 -0
- package/src/inference/index.ts +8 -0
- package/src/inference/inference-manager.ts +327 -0
- package/src/metrics/index.ts +7 -0
- package/src/metrics/metrics.ts +186 -0
- package/src/monitoring/alerting.ts +190 -0
- package/src/monitoring/health.ts +197 -0
- package/src/monitoring/index.ts +38 -0
- package/src/monitoring/info.ts +114 -0
- package/src/monitoring/types.ts +99 -0
- package/src/network/index.ts +5 -0
- package/src/network/network-config.ts +129 -0
- package/src/packaging/compiler.ts +647 -0
- package/src/packaging/config-persistence.ts +135 -0
- package/src/packaging/config-schemas.ts +156 -0
- package/src/packaging/detector.ts +220 -0
- package/src/packaging/index.ts +90 -0
- package/src/packaging/packager.ts +118 -0
- package/src/packaging/parsers/clawdbot.ts +278 -0
- package/src/packaging/parsers/cline.ts +223 -0
- package/src/packaging/parsers/generic.ts +266 -0
- package/src/packaging/parsers/goose.ts +214 -0
- package/src/packaging/parsers/index.ts +11 -0
- package/src/packaging/serializer.ts +260 -0
- package/src/packaging/types.ts +144 -0
- package/src/packaging/wasmedge-compiler.ts +406 -0
- package/src/security/index.ts +17 -0
- package/src/security/multisig.ts +415 -0
- package/src/security/types.ts +416 -0
- package/src/security/vetkeys.ts +655 -0
- package/src/testing/index.ts +6 -0
- package/src/testing/local-runner.ts +264 -0
- package/src/testing/types.ts +104 -0
- package/src/wallet/cbor-serializer.ts +323 -0
- package/src/wallet/chain-dispatcher.ts +313 -0
- package/src/wallet/cross-chain-aggregator.ts +346 -0
- package/src/wallet/index.ts +76 -0
- package/src/wallet/key-derivation.ts +425 -0
- package/src/wallet/providers/base-provider.ts +154 -0
- package/src/wallet/providers/cketh-provider.ts +434 -0
- package/src/wallet/providers/polkadot-provider.ts +503 -0
- package/src/wallet/providers/solana-provider.ts +490 -0
- package/src/wallet/transaction-queue.ts +284 -0
- package/src/wallet/types.ts +178 -0
- package/src/wallet/vetkeys-adapter.ts +431 -0
- package/src/wallet/wallet-manager.ts +597 -0
- package/src/wallet/wallet-storage.ts +380 -0
- package/vercel.json +8 -0
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Arweave Client
|
|
3
|
+
*
|
|
4
|
+
* Handles interaction with Arweave network for permanent data archival.
|
|
5
|
+
* Supports uploading data, retrieving transactions, and checking status.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
export interface ArweaveConfig {
|
|
9
|
+
host?: string;
|
|
10
|
+
port?: number;
|
|
11
|
+
protocol?: 'http' | 'https';
|
|
12
|
+
timeout?: number;
|
|
13
|
+
logging?: boolean;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface UploadOptions {
|
|
17
|
+
tags?: Record<string, string>;
|
|
18
|
+
fee?: number;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface ArchiveTransaction {
|
|
22
|
+
id: string;
|
|
23
|
+
owner: string;
|
|
24
|
+
tags: Record<string, string>;
|
|
25
|
+
size: number;
|
|
26
|
+
timestamp: Date;
|
|
27
|
+
block?: {
|
|
28
|
+
height: number;
|
|
29
|
+
indep_hash: string;
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface UploadResult {
|
|
34
|
+
success: boolean;
|
|
35
|
+
transactionId?: string;
|
|
36
|
+
error?: string;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export type JWKInterface = Record<string, any>;
|
|
40
|
+
|
|
41
|
+
export class ArweaveClient {
|
|
42
|
+
private config: Required<ArweaveConfig>;
|
|
43
|
+
private client: any = null;
|
|
44
|
+
|
|
45
|
+
constructor(config: ArweaveConfig = {}) {
|
|
46
|
+
this.config = {
|
|
47
|
+
host: config.host || 'arweave.net',
|
|
48
|
+
port: config.port || 443,
|
|
49
|
+
protocol: config.protocol || 'https',
|
|
50
|
+
timeout: config.timeout || 20000,
|
|
51
|
+
logging: config.logging || false,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Initialize Arweave client (lazy loading)
|
|
57
|
+
*/
|
|
58
|
+
private async getClient(): Promise<any> {
|
|
59
|
+
if (!this.client) {
|
|
60
|
+
try {
|
|
61
|
+
const Arweave = await this.importArweave();
|
|
62
|
+
this.client = new Arweave({
|
|
63
|
+
host: this.config.host,
|
|
64
|
+
port: this.config.port,
|
|
65
|
+
protocol: this.config.protocol,
|
|
66
|
+
timeout: this.config.timeout,
|
|
67
|
+
logging: this.config.logging,
|
|
68
|
+
});
|
|
69
|
+
} catch (_error) {
|
|
70
|
+
throw new Error(
|
|
71
|
+
'arweave is required for ArweaveClient. Install with: npm install arweave',
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return this.client;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Dynamically import arweave (optional dependency)
|
|
80
|
+
*/
|
|
81
|
+
private async importArweave(): Promise<any> {
|
|
82
|
+
try {
|
|
83
|
+
const dynamicImport = new Function('modulePath', 'return import(modulePath)') as (
|
|
84
|
+
modulePath: string
|
|
85
|
+
) => Promise<{ default: any }>;
|
|
86
|
+
const arweaveModule = await dynamicImport('arweave');
|
|
87
|
+
return arweaveModule.default;
|
|
88
|
+
} catch (_error) {
|
|
89
|
+
throw new Error(
|
|
90
|
+
'arweave is required for ArweaveClient. Install with: npm install arweave',
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Upload data to Arweave
|
|
97
|
+
*/
|
|
98
|
+
async uploadData(
|
|
99
|
+
data: string | Buffer,
|
|
100
|
+
jwk: JWKInterface,
|
|
101
|
+
options: UploadOptions = {},
|
|
102
|
+
): Promise<UploadResult> {
|
|
103
|
+
try {
|
|
104
|
+
const client = await this.getClient();
|
|
105
|
+
const transaction = await client.createTransaction(
|
|
106
|
+
{
|
|
107
|
+
data: typeof data === 'string' ? data : data.toString('base64'),
|
|
108
|
+
},
|
|
109
|
+
jwk,
|
|
110
|
+
);
|
|
111
|
+
|
|
112
|
+
if (options.tags) {
|
|
113
|
+
for (const [key, value] of Object.entries(options.tags)) {
|
|
114
|
+
transaction.addTag(key, value);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
await client.transactions.sign(transaction, jwk);
|
|
119
|
+
|
|
120
|
+
const response = await client.transactions.post(transaction);
|
|
121
|
+
|
|
122
|
+
if (response.status === 200 || response.status === 202) {
|
|
123
|
+
return {
|
|
124
|
+
success: true,
|
|
125
|
+
transactionId: transaction.id,
|
|
126
|
+
};
|
|
127
|
+
} else {
|
|
128
|
+
return {
|
|
129
|
+
success: false,
|
|
130
|
+
error: `Upload failed with status ${response.status}`,
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
} catch (error) {
|
|
134
|
+
return {
|
|
135
|
+
success: false,
|
|
136
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Upload JSON data to Arweave
|
|
143
|
+
*/
|
|
144
|
+
async uploadJSON<T>(
|
|
145
|
+
jsonData: T,
|
|
146
|
+
jwk: JWKInterface,
|
|
147
|
+
options: UploadOptions = {},
|
|
148
|
+
): Promise<UploadResult> {
|
|
149
|
+
return this.uploadData(JSON.stringify(jsonData), jwk, {
|
|
150
|
+
...options,
|
|
151
|
+
tags: {
|
|
152
|
+
'Content-Type': 'application/json',
|
|
153
|
+
...(options.tags || {}),
|
|
154
|
+
},
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Get transaction data by ID
|
|
160
|
+
*/
|
|
161
|
+
async getTransactionData(transactionId: string): Promise<string | null> {
|
|
162
|
+
try {
|
|
163
|
+
const client = await this.getClient();
|
|
164
|
+
const data = await client.transactions.getData(transactionId, {
|
|
165
|
+
decode: true,
|
|
166
|
+
});
|
|
167
|
+
return data.toString();
|
|
168
|
+
} catch (error) {
|
|
169
|
+
console.error(`Failed to fetch transaction ${transactionId}:`, error);
|
|
170
|
+
return null;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Get transaction status
|
|
176
|
+
*/
|
|
177
|
+
async getTransactionStatus(
|
|
178
|
+
transactionId: string,
|
|
179
|
+
): Promise<'pending' | 'confirmed' | 'not_found' | 'error'> {
|
|
180
|
+
try {
|
|
181
|
+
const client = await this.getClient();
|
|
182
|
+
const status = await client.transactions.getStatus(transactionId);
|
|
183
|
+
|
|
184
|
+
if (!status) {
|
|
185
|
+
return 'not_found';
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
if (status.confirmed && status.confirmed.number_of_confirmations > 0) {
|
|
189
|
+
return 'confirmed';
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
return 'pending';
|
|
193
|
+
} catch (error) {
|
|
194
|
+
console.error(`Failed to check status for ${transactionId}:`, error);
|
|
195
|
+
return 'error';
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Get transaction info
|
|
201
|
+
*/
|
|
202
|
+
async getTransaction(transactionId: string): Promise<ArchiveTransaction | null> {
|
|
203
|
+
try {
|
|
204
|
+
const client = await this.getClient();
|
|
205
|
+
const transaction = await client.transactions.get(transactionId);
|
|
206
|
+
|
|
207
|
+
const tags: Record<string, string> = {};
|
|
208
|
+
transaction.get('tags').forEach((tag: any) => {
|
|
209
|
+
const name = tag.get('name', { decode: true, string: true });
|
|
210
|
+
const value = tag.get('value', { decode: true, string: true });
|
|
211
|
+
if (name && value) {
|
|
212
|
+
tags[name] = value;
|
|
213
|
+
}
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
const block = transaction.get('block');
|
|
217
|
+
let blockInfo;
|
|
218
|
+
if (block) {
|
|
219
|
+
blockInfo = {
|
|
220
|
+
height: block.get('height'),
|
|
221
|
+
indep_hash: block.get('indep_hash'),
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
return {
|
|
226
|
+
id: transaction.id,
|
|
227
|
+
owner: transaction.owner,
|
|
228
|
+
tags,
|
|
229
|
+
size: parseInt(transaction.data_size, 10),
|
|
230
|
+
timestamp: new Date(parseInt(transaction.last_tx, 10) * 1000),
|
|
231
|
+
block: blockInfo,
|
|
232
|
+
};
|
|
233
|
+
} catch (error) {
|
|
234
|
+
console.error(`Failed to fetch transaction ${transactionId}:`, error);
|
|
235
|
+
return null;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Get wallet balance in AR
|
|
241
|
+
*/
|
|
242
|
+
async getWalletBalance(address: string): Promise<string> {
|
|
243
|
+
try {
|
|
244
|
+
const client = await this.getClient();
|
|
245
|
+
const balance = await client.wallets.getBalance(address);
|
|
246
|
+
return client.ar.winstonToAr(balance);
|
|
247
|
+
} catch (error) {
|
|
248
|
+
console.error(`Failed to fetch balance for ${address}:`, error);
|
|
249
|
+
return '0';
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* Estimate upload cost in AR
|
|
255
|
+
*/
|
|
256
|
+
async estimateUploadCost(dataSizeBytes: number): Promise<string> {
|
|
257
|
+
try {
|
|
258
|
+
const client = await this.getClient();
|
|
259
|
+
const price = await client.transactions.getPrice(dataSizeBytes);
|
|
260
|
+
return client.ar.winstonToAr(price);
|
|
261
|
+
} catch (error) {
|
|
262
|
+
console.error('Failed to estimate cost:', error);
|
|
263
|
+
return '0';
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* Generate a new Arweave wallet
|
|
269
|
+
*/
|
|
270
|
+
async generateWallet(): Promise<JWKInterface> {
|
|
271
|
+
const client = await this.getClient();
|
|
272
|
+
return client.wallets.generate();
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* Get wallet address from JWK
|
|
277
|
+
*/
|
|
278
|
+
async getAddressFromJWK(jwk: JWKInterface): Promise<string> {
|
|
279
|
+
const client = await this.getClient();
|
|
280
|
+
return client.wallets.jwkToAddress(jwk);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* Create Arweave instance
|
|
285
|
+
*/
|
|
286
|
+
async getClientInstance(): Promise<any> {
|
|
287
|
+
return this.getClient();
|
|
288
|
+
}
|
|
289
|
+
}
|
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Backup System
|
|
3
|
+
*
|
|
4
|
+
* Portable JSON format backup with embedded manifest and checksums
|
|
5
|
+
* Stores backups in ~/.agentvault/backups/
|
|
6
|
+
* CLE-101: Enhanced to include real canister state
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import fs from 'node:fs';
|
|
10
|
+
import path from 'node:path';
|
|
11
|
+
import os from 'node:os';
|
|
12
|
+
import crypto from 'node:crypto';
|
|
13
|
+
import type { AgentConfig } from '../packaging/types.js';
|
|
14
|
+
|
|
15
|
+
const AGENTVAULT_DIR = path.join(os.homedir(), '.agentvault');
|
|
16
|
+
const BACKUPS_DIR = path.join(AGENTVAULT_DIR, 'backups');
|
|
17
|
+
|
|
18
|
+
function ensureBackupsDir(): void {
|
|
19
|
+
if (!fs.existsSync(AGENTVAULT_DIR)) {
|
|
20
|
+
fs.mkdirSync(AGENTVAULT_DIR, { recursive: true });
|
|
21
|
+
}
|
|
22
|
+
if (!fs.existsSync(BACKUPS_DIR)) {
|
|
23
|
+
fs.mkdirSync(BACKUPS_DIR, { recursive: true });
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Canister state captured in backup
|
|
29
|
+
*/
|
|
30
|
+
export interface CanisterState {
|
|
31
|
+
canisterId: string;
|
|
32
|
+
status: 'running' | 'stopped' | 'stopping';
|
|
33
|
+
memorySize?: bigint;
|
|
34
|
+
cycles?: bigint;
|
|
35
|
+
moduleHash?: string;
|
|
36
|
+
fetchedAt: string;
|
|
37
|
+
tasks?: unknown[];
|
|
38
|
+
memory?: unknown;
|
|
39
|
+
context?: unknown;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export interface BackupManifest {
|
|
43
|
+
version: string;
|
|
44
|
+
agentName: string;
|
|
45
|
+
timestamp: Date;
|
|
46
|
+
created: Date;
|
|
47
|
+
agentConfig?: AgentConfig;
|
|
48
|
+
canisterId?: string;
|
|
49
|
+
canisterState?: CanisterState;
|
|
50
|
+
checksums: Record<string, string>;
|
|
51
|
+
size: number;
|
|
52
|
+
components: string[];
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export interface BackupOptions {
|
|
56
|
+
agentName: string;
|
|
57
|
+
outputPath?: string;
|
|
58
|
+
includeConfig?: boolean;
|
|
59
|
+
canisterId?: string;
|
|
60
|
+
includeCanisterState?: boolean;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export interface ImportOptions {
|
|
64
|
+
inputPath: string;
|
|
65
|
+
targetAgentName?: string;
|
|
66
|
+
overwrite?: boolean;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export interface BackupResult {
|
|
70
|
+
success: boolean;
|
|
71
|
+
path?: string;
|
|
72
|
+
error?: string;
|
|
73
|
+
sizeBytes?: number;
|
|
74
|
+
manifest?: BackupManifest;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export interface ImportResult {
|
|
78
|
+
success: boolean;
|
|
79
|
+
agentName?: string;
|
|
80
|
+
error?: string;
|
|
81
|
+
components: string[];
|
|
82
|
+
warnings: string[];
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Fetch canister state for backup
|
|
87
|
+
*/
|
|
88
|
+
async function fetchCanisterState(canisterId: string): Promise<CanisterState | null> {
|
|
89
|
+
try {
|
|
90
|
+
const { createICPClient } = await import('../deployment/icpClient.js');
|
|
91
|
+
const client = createICPClient({ network: 'local' });
|
|
92
|
+
|
|
93
|
+
const status = await client.getCanisterStatus(canisterId);
|
|
94
|
+
|
|
95
|
+
const statusMap: Record<string, 'running' | 'stopped' | 'stopping'> = {
|
|
96
|
+
running: 'running',
|
|
97
|
+
stopped: 'stopped',
|
|
98
|
+
stopping: 'stopping',
|
|
99
|
+
pending: 'stopped',
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
const state: CanisterState = {
|
|
103
|
+
canisterId,
|
|
104
|
+
status: statusMap[status.status] || 'stopped',
|
|
105
|
+
memorySize: status.memorySize,
|
|
106
|
+
cycles: status.cycles,
|
|
107
|
+
fetchedAt: new Date().toISOString(),
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
try {
|
|
111
|
+
const tasksResult = await client.callAgentMethod(canisterId, 'getTasks', []);
|
|
112
|
+
if (tasksResult) {
|
|
113
|
+
state.tasks = tasksResult as unknown[];
|
|
114
|
+
}
|
|
115
|
+
} catch {
|
|
116
|
+
// Tasks not available
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
try {
|
|
120
|
+
const memoryResult = await client.callAgentMethod(canisterId, 'getMemory', []);
|
|
121
|
+
if (memoryResult) {
|
|
122
|
+
state.memory = memoryResult;
|
|
123
|
+
}
|
|
124
|
+
} catch {
|
|
125
|
+
// Memory not available
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
try {
|
|
129
|
+
const contextResult = await client.callAgentMethod(canisterId, 'getContext', []);
|
|
130
|
+
if (contextResult) {
|
|
131
|
+
state.context = contextResult;
|
|
132
|
+
}
|
|
133
|
+
} catch {
|
|
134
|
+
// Context not available
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
return state;
|
|
138
|
+
} catch (error) {
|
|
139
|
+
console.warn('Failed to fetch canister state:', error);
|
|
140
|
+
return null;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export async function exportBackup(options: BackupOptions): Promise<BackupResult> {
|
|
145
|
+
try {
|
|
146
|
+
ensureBackupsDir();
|
|
147
|
+
|
|
148
|
+
const { agentName, outputPath, includeConfig = true, canisterId, includeCanisterState = true } = options;
|
|
149
|
+
|
|
150
|
+
const timestamp = new Date();
|
|
151
|
+
const created = new Date();
|
|
152
|
+
const filename = `${agentName}-${timestamp.toISOString().replace(/[:.]/g, '-')}.json`;
|
|
153
|
+
const filePath = outputPath || path.join(BACKUPS_DIR, filename);
|
|
154
|
+
|
|
155
|
+
const components: string[] = [];
|
|
156
|
+
if (includeConfig) {
|
|
157
|
+
components.push('config');
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const manifest: BackupManifest = {
|
|
161
|
+
version: '1.1',
|
|
162
|
+
agentName,
|
|
163
|
+
timestamp,
|
|
164
|
+
created,
|
|
165
|
+
checksums: {},
|
|
166
|
+
size: 0,
|
|
167
|
+
components,
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
if (includeConfig) {
|
|
171
|
+
manifest.canisterId = canisterId || agentName;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
if (includeCanisterState && canisterId) {
|
|
175
|
+
const canisterState = await fetchCanisterState(canisterId);
|
|
176
|
+
if (canisterState) {
|
|
177
|
+
manifest.canisterState = canisterState;
|
|
178
|
+
manifest.canisterId = canisterId;
|
|
179
|
+
components.push('canister-state');
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
const content = JSON.stringify(manifest, null, 2);
|
|
184
|
+
const checksum = crypto.createHash('sha256').update(content).digest('hex');
|
|
185
|
+
manifest.checksums[filename] = checksum;
|
|
186
|
+
|
|
187
|
+
fs.writeFileSync(filePath, JSON.stringify(manifest, null, 2), 'utf8');
|
|
188
|
+
|
|
189
|
+
const stats = fs.statSync(filePath);
|
|
190
|
+
manifest.size = stats.size;
|
|
191
|
+
|
|
192
|
+
return {
|
|
193
|
+
success: true,
|
|
194
|
+
path: filePath,
|
|
195
|
+
sizeBytes: stats.size,
|
|
196
|
+
manifest,
|
|
197
|
+
};
|
|
198
|
+
} catch (error) {
|
|
199
|
+
return {
|
|
200
|
+
success: false,
|
|
201
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
export async function previewBackup(inputPath: string): Promise<BackupManifest | null> {
|
|
207
|
+
try {
|
|
208
|
+
if (!fs.existsSync(inputPath)) {
|
|
209
|
+
return null;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
const content = fs.readFileSync(inputPath, 'utf8');
|
|
213
|
+
const manifest = JSON.parse(content) as BackupManifest;
|
|
214
|
+
|
|
215
|
+
return manifest;
|
|
216
|
+
} catch (error) {
|
|
217
|
+
console.error('Failed to preview backup:', error);
|
|
218
|
+
return null;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
export async function importBackup(options: ImportOptions): Promise<ImportResult> {
|
|
223
|
+
try {
|
|
224
|
+
const { inputPath, targetAgentName, overwrite } = options;
|
|
225
|
+
|
|
226
|
+
if (!fs.existsSync(inputPath)) {
|
|
227
|
+
return {
|
|
228
|
+
success: false,
|
|
229
|
+
agentName: undefined,
|
|
230
|
+
components: [],
|
|
231
|
+
warnings: [],
|
|
232
|
+
error: `Backup file not found: ${inputPath}`,
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
const manifest = await previewBackup(inputPath);
|
|
237
|
+
if (!manifest) {
|
|
238
|
+
return {
|
|
239
|
+
success: false,
|
|
240
|
+
agentName: undefined,
|
|
241
|
+
components: [],
|
|
242
|
+
warnings: [],
|
|
243
|
+
error: 'Invalid backup file',
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
const targetName = targetAgentName || manifest.agentName;
|
|
248
|
+
const warnings: string[] = [];
|
|
249
|
+
|
|
250
|
+
if (!overwrite) {
|
|
251
|
+
warnings.push('Using dry-run mode; no changes will be made');
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
return {
|
|
255
|
+
success: true,
|
|
256
|
+
agentName: targetName,
|
|
257
|
+
components: manifest.components,
|
|
258
|
+
warnings,
|
|
259
|
+
};
|
|
260
|
+
} catch (error) {
|
|
261
|
+
return {
|
|
262
|
+
success: false,
|
|
263
|
+
agentName: undefined,
|
|
264
|
+
components: [],
|
|
265
|
+
warnings: [],
|
|
266
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
export async function listBackups(agentName: string): Promise<BackupManifest[]> {
|
|
272
|
+
ensureBackupsDir();
|
|
273
|
+
const backups: BackupManifest[] = [];
|
|
274
|
+
|
|
275
|
+
if (!fs.existsSync(BACKUPS_DIR)) {
|
|
276
|
+
return backups;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
const files = fs.readdirSync(BACKUPS_DIR);
|
|
280
|
+
for (const file of files) {
|
|
281
|
+
if (file.startsWith(agentName) && file.endsWith('.json')) {
|
|
282
|
+
const filePath = path.join(BACKUPS_DIR, file);
|
|
283
|
+
try {
|
|
284
|
+
const manifest = await previewBackup(filePath);
|
|
285
|
+
if (manifest && manifest.agentName === agentName) {
|
|
286
|
+
backups.push(manifest);
|
|
287
|
+
}
|
|
288
|
+
} catch (error) {
|
|
289
|
+
console.error(`Failed to read backup ${file}:`, error);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
backups.sort((a, b) => b.timestamp.getTime() - a.timestamp.getTime());
|
|
295
|
+
return backups;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
export async function deleteBackup(filePath: string): Promise<boolean> {
|
|
299
|
+
try {
|
|
300
|
+
if (fs.existsSync(filePath)) {
|
|
301
|
+
fs.unlinkSync(filePath);
|
|
302
|
+
return true;
|
|
303
|
+
}
|
|
304
|
+
return false;
|
|
305
|
+
} catch (error) {
|
|
306
|
+
console.error('Failed to delete backup:', error);
|
|
307
|
+
return false;
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
export function formatBackupSize(bytes: number): string {
|
|
312
|
+
if (bytes < 1024) return `${bytes} B`;
|
|
313
|
+
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(2)} KB`;
|
|
314
|
+
return `${(bytes / (1024 * 1024)).toFixed(2)} MB`;
|
|
315
|
+
}
|