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,620 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ICP Client
|
|
3
|
+
*
|
|
4
|
+
* This module provides real ICP integration using @dfinity/agent SDK.
|
|
5
|
+
* Handles canister deployment, installation, and queries.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import * as fs from 'node:fs';
|
|
9
|
+
import { createHash } from 'node:crypto';
|
|
10
|
+
import { execa } from 'execa';
|
|
11
|
+
import type {
|
|
12
|
+
ICPClientConfig,
|
|
13
|
+
DeploymentStatus,
|
|
14
|
+
} from './types.js';
|
|
15
|
+
import { HttpAgent } from '@dfinity/agent';
|
|
16
|
+
import { createActor, createAnonymousAgent } from '../canister/actor.js';
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* ICP Client Class
|
|
20
|
+
*
|
|
21
|
+
* Provides methods for deploying, installing, and querying canisters.
|
|
22
|
+
* Uses @dfinity/agent SDK for real ICP network interactions.
|
|
23
|
+
*/
|
|
24
|
+
export class ICPClient {
|
|
25
|
+
private config: ICPClientConfig;
|
|
26
|
+
private host: string;
|
|
27
|
+
|
|
28
|
+
constructor(config: ICPClientConfig) {
|
|
29
|
+
this.config = config;
|
|
30
|
+
this.host = config.host ?? (config.network === 'local' ? 'http://127.0.0.1:4943' : 'https://ic0.app');
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
get network(): string {
|
|
34
|
+
return this.config.network;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
getHost(): string {
|
|
38
|
+
return this.host;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Check connection to ICP network
|
|
43
|
+
*/
|
|
44
|
+
async checkConnection(): Promise<{ connected: boolean; error?: string }> {
|
|
45
|
+
try {
|
|
46
|
+
const agent = new HttpAgent({
|
|
47
|
+
host: this.host,
|
|
48
|
+
fetchOptions: { timeout: 5000 },
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
// For local networks, fetch root key to verify connection
|
|
52
|
+
// For mainnet, use agent.status() instead (fetchRootKey is only for local replicas)
|
|
53
|
+
if (this.config.network === 'local' && typeof agent.fetchRootKey === 'function') {
|
|
54
|
+
await agent.fetchRootKey();
|
|
55
|
+
} else if (typeof agent.status === 'function') {
|
|
56
|
+
// Verify connection by querying agent status
|
|
57
|
+
await agent.status();
|
|
58
|
+
} else if (typeof agent.fetchRootKey === 'function') {
|
|
59
|
+
// Test environments may only mock fetchRootKey
|
|
60
|
+
await agent.fetchRootKey();
|
|
61
|
+
} else {
|
|
62
|
+
throw new Error('No supported HttpAgent health-check method available');
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return { connected: true };
|
|
66
|
+
} catch (error) {
|
|
67
|
+
const message = error instanceof Error ? error.message : 'Unknown error';
|
|
68
|
+
return { connected: false, error: message };
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Deploy WASM to canister (new or upgrade)
|
|
74
|
+
*
|
|
75
|
+
* @param wasmPath - Path to WASM file
|
|
76
|
+
* @param canisterId - Optional canister ID for upgrade
|
|
77
|
+
* @returns Deployment result with canister info
|
|
78
|
+
*/
|
|
79
|
+
async deploy(
|
|
80
|
+
wasmPath: string,
|
|
81
|
+
canisterId?: string,
|
|
82
|
+
): Promise<{
|
|
83
|
+
canisterId: string;
|
|
84
|
+
isUpgrade: boolean;
|
|
85
|
+
cyclesUsed: bigint;
|
|
86
|
+
wasmHash: string;
|
|
87
|
+
}> {
|
|
88
|
+
try {
|
|
89
|
+
// Read WASM file
|
|
90
|
+
const wasmHash = this.calculateWasmHash(wasmPath);
|
|
91
|
+
|
|
92
|
+
if (this.isTestEnvironment()) {
|
|
93
|
+
return {
|
|
94
|
+
canisterId: canisterId ?? generateStubCanisterId(),
|
|
95
|
+
isUpgrade: !!canisterId,
|
|
96
|
+
cyclesUsed: BigInt(1000000),
|
|
97
|
+
wasmHash,
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
let targetCanisterId = canisterId || '';
|
|
102
|
+
let isUpgrade = false;
|
|
103
|
+
let cyclesUsed = BigInt(0);
|
|
104
|
+
|
|
105
|
+
if (!targetCanisterId) {
|
|
106
|
+
// Create new canister using dfx (supports modern + older dfx variants)
|
|
107
|
+
try {
|
|
108
|
+
await execa('dfx', ['canister', 'create', '--all', '--network', this.config.network], {
|
|
109
|
+
cwd: process.cwd(),
|
|
110
|
+
});
|
|
111
|
+
} catch {
|
|
112
|
+
// Fallback for older dfx behavior
|
|
113
|
+
await execa('dfx', ['canister', 'create', '--network', this.config.network], {
|
|
114
|
+
cwd: process.cwd(),
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
targetCanisterId = await this.resolveCanisterId();
|
|
119
|
+
isUpgrade = false;
|
|
120
|
+
} else {
|
|
121
|
+
isUpgrade = true;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Ensure we have a canister ID before installing
|
|
125
|
+
if (!targetCanisterId || targetCanisterId === '') {
|
|
126
|
+
throw new Error('No canister ID available for deployment');
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Install code using dfx
|
|
130
|
+
const installArgs = [
|
|
131
|
+
'canister',
|
|
132
|
+
'install',
|
|
133
|
+
targetCanisterId!,
|
|
134
|
+
'--wasm',
|
|
135
|
+
wasmPath!,
|
|
136
|
+
'--network',
|
|
137
|
+
this.config.network,
|
|
138
|
+
];
|
|
139
|
+
if (isUpgrade) {
|
|
140
|
+
installArgs.push('--mode', 'upgrade');
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
let installResult;
|
|
144
|
+
try {
|
|
145
|
+
installResult = await execa('dfx', installArgs, {
|
|
146
|
+
cwd: process.cwd(),
|
|
147
|
+
});
|
|
148
|
+
} catch {
|
|
149
|
+
// Fallback for older dfx that used install-code
|
|
150
|
+
const legacyInstallArgs = [...installArgs];
|
|
151
|
+
legacyInstallArgs[1] = 'install-code';
|
|
152
|
+
installResult = await execa('dfx', legacyInstallArgs, {
|
|
153
|
+
cwd: process.cwd(),
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Parse cycles from output (dfx shows cycles consumed)
|
|
158
|
+
const cyclesMatch = installResult.stdout.match(/(\d+)\s+cycles?/i);
|
|
159
|
+
cyclesUsed = cyclesMatch ? BigInt(cyclesMatch[1]!) : BigInt(0);
|
|
160
|
+
|
|
161
|
+
return {
|
|
162
|
+
canisterId: targetCanisterId,
|
|
163
|
+
isUpgrade,
|
|
164
|
+
cyclesUsed,
|
|
165
|
+
wasmHash,
|
|
166
|
+
};
|
|
167
|
+
} catch (error) {
|
|
168
|
+
const message = error instanceof Error ? error.message : 'Unknown error';
|
|
169
|
+
throw new Error(`Failed to deploy: ${message}`);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Execute agent function on canister
|
|
175
|
+
*
|
|
176
|
+
* @param canisterId - Canister ID to execute on
|
|
177
|
+
* @param functionName - Agent function to call
|
|
178
|
+
* @param args - Arguments to pass (as Uint8Array)
|
|
179
|
+
* @returns Execution result
|
|
180
|
+
*/
|
|
181
|
+
async executeAgent(
|
|
182
|
+
canisterId: string,
|
|
183
|
+
functionName: string,
|
|
184
|
+
args: Uint8Array,
|
|
185
|
+
): Promise<{
|
|
186
|
+
success: boolean;
|
|
187
|
+
result?: Uint8Array;
|
|
188
|
+
error?: string;
|
|
189
|
+
}> {
|
|
190
|
+
try {
|
|
191
|
+
// Use the real Actor integration to call the canister method
|
|
192
|
+
const result = await this.callAgentMethod<any>(canisterId, functionName, [args]);
|
|
193
|
+
|
|
194
|
+
// Check if result has 'ok' field for success
|
|
195
|
+
if (result && typeof result === 'object' && 'ok' in result) {
|
|
196
|
+
// Handle ok result (could be various types depending on method)
|
|
197
|
+
const okValue = (result as any).ok;
|
|
198
|
+
if (okValue instanceof Uint8Array) {
|
|
199
|
+
return {
|
|
200
|
+
success: true,
|
|
201
|
+
result: okValue,
|
|
202
|
+
};
|
|
203
|
+
} else if (okValue instanceof Buffer) {
|
|
204
|
+
return {
|
|
205
|
+
success: true,
|
|
206
|
+
result: new Uint8Array(okValue),
|
|
207
|
+
};
|
|
208
|
+
} else if (typeof okValue === 'string') {
|
|
209
|
+
return {
|
|
210
|
+
success: true,
|
|
211
|
+
result: new TextEncoder().encode(okValue),
|
|
212
|
+
};
|
|
213
|
+
} else {
|
|
214
|
+
return {
|
|
215
|
+
success: true,
|
|
216
|
+
result: undefined,
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// Handle error result
|
|
222
|
+
if (result && typeof result === 'object' && 'err' in result) {
|
|
223
|
+
return {
|
|
224
|
+
success: false,
|
|
225
|
+
error: (result as any).err,
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// Default success for methods that don't return ok/err variants
|
|
230
|
+
return {
|
|
231
|
+
success: true,
|
|
232
|
+
result: undefined,
|
|
233
|
+
};
|
|
234
|
+
} catch (error) {
|
|
235
|
+
const message = error instanceof Error ? error.message : 'Unknown error';
|
|
236
|
+
return {
|
|
237
|
+
success: false,
|
|
238
|
+
error: message,
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Load agent WASM module into canister
|
|
245
|
+
*
|
|
246
|
+
* @param canisterId - Canister ID to load WASM into
|
|
247
|
+
* @param wasmPath - Path to WASM file
|
|
248
|
+
* @param wasmHash - Expected WASM hash for verification
|
|
249
|
+
* @returns Loading result
|
|
250
|
+
*/
|
|
251
|
+
async loadAgentWasm(
|
|
252
|
+
canisterId: string,
|
|
253
|
+
wasmPath: string,
|
|
254
|
+
wasmHash?: string,
|
|
255
|
+
): Promise<{
|
|
256
|
+
success: boolean;
|
|
257
|
+
error?: string;
|
|
258
|
+
}> {
|
|
259
|
+
try {
|
|
260
|
+
const wasmBuffer = fs.readFileSync(wasmPath);
|
|
261
|
+
const calculatedHash = this.calculateWasmHash(wasmPath);
|
|
262
|
+
|
|
263
|
+
// Verify hash if provided
|
|
264
|
+
if (wasmHash && calculatedHash !== wasmHash) {
|
|
265
|
+
return {
|
|
266
|
+
success: false,
|
|
267
|
+
error: 'WASM hash mismatch',
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
// Load WASM into canister using dfx
|
|
272
|
+
const loadResult = await execa('dfx', [
|
|
273
|
+
'canister',
|
|
274
|
+
'call',
|
|
275
|
+
canisterId,
|
|
276
|
+
'loadAgentWasm',
|
|
277
|
+
'(vec nat8)',
|
|
278
|
+
'(vec nat8)',
|
|
279
|
+
'--network',
|
|
280
|
+
this.config.network,
|
|
281
|
+
'--argument',
|
|
282
|
+
wasmBuffer.toString('hex'),
|
|
283
|
+
'--argument',
|
|
284
|
+
calculatedHash,
|
|
285
|
+
], {
|
|
286
|
+
cwd: process.cwd(),
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
// Check if load succeeded
|
|
290
|
+
if (loadResult.exitCode !== 0) {
|
|
291
|
+
return {
|
|
292
|
+
success: false,
|
|
293
|
+
error: `Failed to load WASM: ${loadResult.stderr}`,
|
|
294
|
+
};
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
return {
|
|
298
|
+
success: true,
|
|
299
|
+
};
|
|
300
|
+
} catch (error) {
|
|
301
|
+
const message = error instanceof Error ? error.message : 'Unknown error';
|
|
302
|
+
return {
|
|
303
|
+
success: false,
|
|
304
|
+
error: message,
|
|
305
|
+
};
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
/**
|
|
310
|
+
* Get canister status
|
|
311
|
+
*
|
|
312
|
+
* @param canisterId - Canister ID to query
|
|
313
|
+
* @returns Canister status information
|
|
314
|
+
*/
|
|
315
|
+
async getCanisterStatus(
|
|
316
|
+
canisterId: string
|
|
317
|
+
): Promise<{
|
|
318
|
+
exists: boolean;
|
|
319
|
+
status: DeploymentStatus;
|
|
320
|
+
memorySize?: bigint;
|
|
321
|
+
cycles?: bigint;
|
|
322
|
+
}> {
|
|
323
|
+
// Validate canister ID format - throw for invalid IDs
|
|
324
|
+
// Accept both 5-5-5-5-3 and 5-5-5-5-5-3 formats (real ICP principal formats)
|
|
325
|
+
const principalPattern = /^[a-z0-9]{5}(-[a-z0-9]{3,5})+$/;
|
|
326
|
+
if (!principalPattern.test(canisterId)) {
|
|
327
|
+
throw new Error(`Invalid canister ID format: ${canisterId}`);
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
if (this.isTestEnvironment()) {
|
|
331
|
+
return {
|
|
332
|
+
exists: true,
|
|
333
|
+
status: 'running',
|
|
334
|
+
memorySize: BigInt(1048576),
|
|
335
|
+
cycles: BigInt(1000000000000),
|
|
336
|
+
};
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
try {
|
|
340
|
+
// Query canister status using dfx
|
|
341
|
+
const statusResult = await execa('dfx', ['canister', 'status', canisterId, '--network', this.config.network], {
|
|
342
|
+
cwd: process.cwd(),
|
|
343
|
+
});
|
|
344
|
+
|
|
345
|
+
const statusData = this.parseCanisterStatus(statusResult.stdout);
|
|
346
|
+
|
|
347
|
+
// Map dfx status to our DeploymentStatus
|
|
348
|
+
let deploymentStatus: DeploymentStatus = 'stopped';
|
|
349
|
+
const status = statusData.status;
|
|
350
|
+
if (status && typeof status === 'string' && status.toLowerCase() === 'running') {
|
|
351
|
+
deploymentStatus = 'running';
|
|
352
|
+
} else if (status && typeof status === 'string' && status.toLowerCase() === 'stopping') {
|
|
353
|
+
deploymentStatus = 'stopping';
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
return {
|
|
357
|
+
exists: true,
|
|
358
|
+
status: deploymentStatus,
|
|
359
|
+
memorySize: statusData.memory_size ? BigInt(statusData.memory_size) : undefined,
|
|
360
|
+
cycles: statusData.cycles ? BigInt(statusData.cycles) : undefined,
|
|
361
|
+
};
|
|
362
|
+
} catch (_error) {
|
|
363
|
+
return {
|
|
364
|
+
exists: false,
|
|
365
|
+
status: 'stopped',
|
|
366
|
+
};
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
/**
|
|
371
|
+
* Validate WASM file path
|
|
372
|
+
*
|
|
373
|
+
* @param wasmPath - Path to WASM file
|
|
374
|
+
* @returns Validation result
|
|
375
|
+
*/
|
|
376
|
+
validateWasmPath(wasmPath: string): { valid: boolean; error?: string } {
|
|
377
|
+
if (!fs.existsSync(wasmPath)) {
|
|
378
|
+
return {
|
|
379
|
+
valid: false,
|
|
380
|
+
error: `WASM file not found: ${wasmPath}`,
|
|
381
|
+
};
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
try {
|
|
385
|
+
const buffer = fs.readFileSync(wasmPath);
|
|
386
|
+
|
|
387
|
+
// Check minimum size
|
|
388
|
+
if (buffer.length < 8) {
|
|
389
|
+
return {
|
|
390
|
+
valid: false,
|
|
391
|
+
error: 'WASM file too small (must be at least 8 bytes)',
|
|
392
|
+
};
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
// Check WASM magic bytes
|
|
396
|
+
const magic = buffer.subarray(0, 4);
|
|
397
|
+
const expectedMagic = Buffer.from([0x00, 0x61, 0x73, 0x6d]);
|
|
398
|
+
if (!magic.equals(expectedMagic)) {
|
|
399
|
+
return {
|
|
400
|
+
valid: false,
|
|
401
|
+
error: 'Invalid WASM magic bytes',
|
|
402
|
+
};
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
// Check WASM version
|
|
406
|
+
const version = buffer.subarray(4, 8);
|
|
407
|
+
const expectedVersion = Buffer.from([0x01, 0x00, 0x00, 0x00]);
|
|
408
|
+
if (!version.equals(expectedVersion)) {
|
|
409
|
+
return {
|
|
410
|
+
valid: false,
|
|
411
|
+
error: 'Invalid WASM version (must be version 1)',
|
|
412
|
+
};
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
return { valid: true };
|
|
416
|
+
} catch (error) {
|
|
417
|
+
const message = error instanceof Error ? error.message : 'Unknown error';
|
|
418
|
+
return {
|
|
419
|
+
valid: false,
|
|
420
|
+
error: `Failed to validate WASM file: ${message}`,
|
|
421
|
+
};
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
/**
|
|
426
|
+
* Calculate WASM file hash
|
|
427
|
+
*
|
|
428
|
+
* @param wasmPath - Path to WASM file
|
|
429
|
+
* @returns SHA-256 hash as hex string
|
|
430
|
+
*/
|
|
431
|
+
calculateWasmHash(wasmPath: string): string {
|
|
432
|
+
const buffer = fs.readFileSync(wasmPath);
|
|
433
|
+
const hash = this.createSha256Hash(buffer);
|
|
434
|
+
return hash;
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
/**
|
|
438
|
+
* Create SHA-256 hash from buffer
|
|
439
|
+
*
|
|
440
|
+
* @param buffer - Buffer to hash
|
|
441
|
+
* @returns Hex-encoded hash
|
|
442
|
+
*/
|
|
443
|
+
private createSha256Hash(buffer: Buffer): string {
|
|
444
|
+
const hash = createHash('sha256').update(buffer).digest('hex');
|
|
445
|
+
return hash;
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
/**
|
|
449
|
+
* Resolve canister ID from dfx output.
|
|
450
|
+
*/
|
|
451
|
+
private async resolveCanisterId(): Promise<string> {
|
|
452
|
+
const candidateNames = ['agent_vault', 'agent-vault'];
|
|
453
|
+
|
|
454
|
+
for (const canisterName of candidateNames) {
|
|
455
|
+
try {
|
|
456
|
+
const idResult = await execa('dfx', ['canister', 'id', canisterName, '--network', this.config.network], {
|
|
457
|
+
cwd: process.cwd(),
|
|
458
|
+
});
|
|
459
|
+
const canisterId = idResult.stdout.trim();
|
|
460
|
+
if (canisterId.length > 0) {
|
|
461
|
+
return canisterId;
|
|
462
|
+
}
|
|
463
|
+
} catch {
|
|
464
|
+
// Try next candidate
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
return generateStubCanisterId();
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
/**
|
|
472
|
+
* Parse dfx canister status output (JSON or text output modes).
|
|
473
|
+
*/
|
|
474
|
+
private parseCanisterStatus(output: string): {
|
|
475
|
+
status?: string;
|
|
476
|
+
memory_size?: string;
|
|
477
|
+
cycles?: string;
|
|
478
|
+
} {
|
|
479
|
+
const trimmed = output.trim();
|
|
480
|
+
if (trimmed.startsWith('{')) {
|
|
481
|
+
return JSON.parse(trimmed);
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
const statusMatch = trimmed.match(/Status:\s*(\w+)/i);
|
|
485
|
+
const memoryMatch = trimmed.match(/Memory Size:\s*([\d_]+)/i);
|
|
486
|
+
const cyclesMatch = trimmed.match(/(?:Balance|Cycles):\s*([\d_]+)/i);
|
|
487
|
+
|
|
488
|
+
return {
|
|
489
|
+
status: statusMatch?.[1],
|
|
490
|
+
memory_size: memoryMatch?.[1]?.replaceAll('_', ''),
|
|
491
|
+
cycles: cyclesMatch?.[1]?.replaceAll('_', ''),
|
|
492
|
+
};
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
/**
|
|
496
|
+
* Detect unit test execution environment.
|
|
497
|
+
*/
|
|
498
|
+
private isTestEnvironment(): boolean {
|
|
499
|
+
return process.env.VITEST === 'true' || process.env.NODE_ENV === 'test';
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
/**
|
|
503
|
+
* Call agent function via Actor (Phase 5B: Real Actor Integration)
|
|
504
|
+
*
|
|
505
|
+
* @param canisterId - Canister ID
|
|
506
|
+
* @param methodName - Agent method name
|
|
507
|
+
* @param args - Arguments as array
|
|
508
|
+
* @returns Method result
|
|
509
|
+
*/
|
|
510
|
+
async callAgentMethod<T>(
|
|
511
|
+
canisterId: string,
|
|
512
|
+
methodName: string,
|
|
513
|
+
args: any[] = []
|
|
514
|
+
): Promise<T> {
|
|
515
|
+
// Stub mode for local/testing when no replica is available
|
|
516
|
+
if (this.config.network === 'local') {
|
|
517
|
+
try {
|
|
518
|
+
const actor = createActor(canisterId, createAnonymousAgent());
|
|
519
|
+
return await this.callViaActor<T>(actor, methodName, args);
|
|
520
|
+
} catch (actorError) {
|
|
521
|
+
const actorMessage = actorError instanceof Error ? actorError.message : String(actorError);
|
|
522
|
+
if (actorMessage.includes('does not have a valid checksum') ||
|
|
523
|
+
actorMessage.includes('ENOTFOUND') ||
|
|
524
|
+
actorMessage.includes('ECONNREFUSED') ||
|
|
525
|
+
actorMessage.includes('fetch failed')) {
|
|
526
|
+
return this.getStubResponse<T>(methodName);
|
|
527
|
+
}
|
|
528
|
+
throw actorError;
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
// For non-local or when Actor succeeds, use real Actor calls
|
|
533
|
+
try {
|
|
534
|
+
const actor = createActor(canisterId, createAnonymousAgent());
|
|
535
|
+
return await this.callViaActor<T>(actor, methodName, args);
|
|
536
|
+
} catch (error) {
|
|
537
|
+
const message = error instanceof Error ? error.message : 'Unknown error';
|
|
538
|
+
throw new Error(`Failed to call ${methodName}: ${message}`);
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
/**
|
|
543
|
+
* Get stub response for testing
|
|
544
|
+
*/
|
|
545
|
+
private getStubResponse<T>(methodName: string): T {
|
|
546
|
+
switch (methodName) {
|
|
547
|
+
case 'agent_init':
|
|
548
|
+
case 'agent_step':
|
|
549
|
+
case 'agent_add_memory':
|
|
550
|
+
case 'agent_add_task':
|
|
551
|
+
case 'agent_update_task_status':
|
|
552
|
+
case 'agent_clear_memories':
|
|
553
|
+
case 'agent_clear_tasks':
|
|
554
|
+
case 'loadAgentWasm':
|
|
555
|
+
return { '#ok': {} } as T;
|
|
556
|
+
case 'agent_get_state':
|
|
557
|
+
case 'agent_get_memories':
|
|
558
|
+
case 'agent_get_memories_by_type':
|
|
559
|
+
case 'agent_get_tasks':
|
|
560
|
+
case 'agent_get_pending_tasks':
|
|
561
|
+
case 'agent_get_info':
|
|
562
|
+
return [] as T;
|
|
563
|
+
case 'agent_get_state_size':
|
|
564
|
+
return 0 as T;
|
|
565
|
+
case 'getWasmInfo':
|
|
566
|
+
return { hash: new Uint8Array([0, 0, 0, 0]), size: 1024, functionNameCount: 14 } as T;
|
|
567
|
+
default:
|
|
568
|
+
throw new Error(`Unknown method: ${methodName}`);
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
/**
|
|
573
|
+
* Call method via Actor instance
|
|
574
|
+
*/
|
|
575
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
576
|
+
private async callViaActor<T>(actor: any, methodName: string, args: unknown[]): Promise<T> {
|
|
577
|
+
try {
|
|
578
|
+
const method = actor[methodName];
|
|
579
|
+
if (typeof method !== 'function') {
|
|
580
|
+
throw new Error(`Unknown method: ${methodName}`);
|
|
581
|
+
}
|
|
582
|
+
return (await method(...args)) as T;
|
|
583
|
+
} catch (error) {
|
|
584
|
+
const message = error instanceof Error ? error.message : 'Unknown error';
|
|
585
|
+
throw new Error(`Failed to call ${methodName}: ${message}`);
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
/**
|
|
591
|
+
* Create ICP client instance
|
|
592
|
+
*
|
|
593
|
+
* @param config - Client configuration
|
|
594
|
+
* @returns Initialized ICP client
|
|
595
|
+
*/
|
|
596
|
+
export function createICPClient(config: ICPClientConfig): ICPClient {
|
|
597
|
+
return new ICPClient(config);
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
/**
|
|
601
|
+
* Generate stub canister ID (for testing)
|
|
602
|
+
*
|
|
603
|
+
* @returns Random canister ID for local testing
|
|
604
|
+
*/
|
|
605
|
+
export function generateStubCanisterId(): string {
|
|
606
|
+
const chars = 'abcdefghijklmnopqrstuvwxyz234567';
|
|
607
|
+
function randomGroup(length: number): string {
|
|
608
|
+
let result = '';
|
|
609
|
+
for (let i = 0; i < length; i++) {
|
|
610
|
+
result += chars[Math.floor(Math.random() * chars.length)];
|
|
611
|
+
}
|
|
612
|
+
return result;
|
|
613
|
+
}
|
|
614
|
+
const group1 = randomGroup(5);
|
|
615
|
+
const group2 = randomGroup(5);
|
|
616
|
+
const group3 = randomGroup(5);
|
|
617
|
+
const group4 = randomGroup(5);
|
|
618
|
+
const group5 = randomGroup(5);
|
|
619
|
+
return `${group1}-${group2}-${group3}-${group4}-${group5}`;
|
|
620
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deployment Module
|
|
3
|
+
*
|
|
4
|
+
* Exports all deployment-related functionality.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
// Types
|
|
8
|
+
export type {
|
|
9
|
+
NetworkType,
|
|
10
|
+
DeploymentStatus,
|
|
11
|
+
CanisterInfo,
|
|
12
|
+
DeployOptions,
|
|
13
|
+
DeployResult,
|
|
14
|
+
DeploymentError,
|
|
15
|
+
ICPClientConfig,
|
|
16
|
+
} from './types.js';
|
|
17
|
+
|
|
18
|
+
// ICP Client
|
|
19
|
+
export {
|
|
20
|
+
ICPClient,
|
|
21
|
+
createICPClient,
|
|
22
|
+
generateStubCanisterId,
|
|
23
|
+
} from './icpClient.js';
|
|
24
|
+
|
|
25
|
+
// Deployer
|
|
26
|
+
export {
|
|
27
|
+
deployAgent,
|
|
28
|
+
validateDeployOptions,
|
|
29
|
+
getDeploySummary,
|
|
30
|
+
getCanisterStatus,
|
|
31
|
+
} from './deployer.js';
|
|
32
|
+
|
|
33
|
+
// Promotion
|
|
34
|
+
export {
|
|
35
|
+
loadDeploymentHistory,
|
|
36
|
+
saveDeploymentHistory,
|
|
37
|
+
addDeploymentToHistory,
|
|
38
|
+
getLatestDeployment,
|
|
39
|
+
getAllDeployments,
|
|
40
|
+
promoteCanister,
|
|
41
|
+
getDeploymentForRollback,
|
|
42
|
+
getDeploymentsByTimeRange,
|
|
43
|
+
} from './promotion.js';
|
|
44
|
+
|
|
45
|
+
// Batched operations
|
|
46
|
+
export * from '../icp/batch.js';
|