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,186 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Metrics Module
|
|
3
|
+
*
|
|
4
|
+
* Stores and retrieves canister metrics (cycles, memory, messages, etc.)
|
|
5
|
+
* in JSONL format: ~/.agentvault/metrics/<canister-id>/YYYY-MM-DD.jsonl
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import fs from 'node:fs';
|
|
9
|
+
import path from 'node:path';
|
|
10
|
+
import os from 'node:os';
|
|
11
|
+
|
|
12
|
+
export type MetricName = 'cycles_balance' | 'memory_heap' | 'message_count' | 'request_count' | 'error_count';
|
|
13
|
+
|
|
14
|
+
export interface MetricDataPoint {
|
|
15
|
+
timestamp: Date;
|
|
16
|
+
value: number;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface MetricTimeSeries {
|
|
20
|
+
metric: MetricName;
|
|
21
|
+
dataPoints: MetricDataPoint[];
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface MetricsSummary {
|
|
25
|
+
canisterId: string;
|
|
26
|
+
metrics: Record<MetricName, {
|
|
27
|
+
current: number;
|
|
28
|
+
min: number;
|
|
29
|
+
max: number;
|
|
30
|
+
avg: number;
|
|
31
|
+
delta?: number;
|
|
32
|
+
}>;
|
|
33
|
+
period: { start: Date; end: Date };
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export interface CollectorConfig {
|
|
37
|
+
interval?: number;
|
|
38
|
+
retentionDays?: number;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const AGENTVAULT_DIR = path.join(os.homedir(), '.agentvault');
|
|
42
|
+
const METRICS_DIR = path.join(AGENTVAULT_DIR, 'metrics');
|
|
43
|
+
|
|
44
|
+
function getMetricsDir(canisterId: string): string {
|
|
45
|
+
const dir = path.join(METRICS_DIR, canisterId);
|
|
46
|
+
if (!fs.existsSync(dir)) {
|
|
47
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
48
|
+
}
|
|
49
|
+
return dir;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function getMetricsFile(canisterId: string, date: Date): string {
|
|
53
|
+
const metricsDir = getMetricsDir(canisterId);
|
|
54
|
+
const dateStr = date.toISOString().split('T')[0];
|
|
55
|
+
return path.join(metricsDir, `${dateStr}.jsonl`);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export function storeMetrics(canisterId: string, metrics: Record<MetricName, number>): void {
|
|
59
|
+
const now = new Date();
|
|
60
|
+
const metricsFile = getMetricsFile(canisterId, now);
|
|
61
|
+
|
|
62
|
+
for (const [metric, value] of Object.entries(metrics) as [MetricName, number][]) {
|
|
63
|
+
const line = JSON.stringify({
|
|
64
|
+
timestamp: now.toISOString(),
|
|
65
|
+
metric,
|
|
66
|
+
value,
|
|
67
|
+
});
|
|
68
|
+
fs.appendFileSync(metricsFile, `${line}\n`);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export function getTimeSeries(
|
|
73
|
+
canisterId: string,
|
|
74
|
+
metric: MetricName,
|
|
75
|
+
from: Date,
|
|
76
|
+
to: Date,
|
|
77
|
+
): MetricTimeSeries {
|
|
78
|
+
const dataPoints: MetricDataPoint[] = [];
|
|
79
|
+
const currentDate = new Date(from);
|
|
80
|
+
|
|
81
|
+
while (currentDate <= to) {
|
|
82
|
+
const metricsFile = getMetricsFile(canisterId, currentDate);
|
|
83
|
+
if (fs.existsSync(metricsFile)) {
|
|
84
|
+
const lines = fs.readFileSync(metricsFile, 'utf8').trim().split('\n');
|
|
85
|
+
for (const line of lines) {
|
|
86
|
+
if (line) {
|
|
87
|
+
const parsed = JSON.parse(line);
|
|
88
|
+
if (parsed.metric === metric) {
|
|
89
|
+
const timestamp = new Date(parsed.timestamp);
|
|
90
|
+
if (timestamp >= from && timestamp <= to) {
|
|
91
|
+
dataPoints.push({ timestamp, value: parsed.value });
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
currentDate.setDate(currentDate.getDate() + 1);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
dataPoints.sort((a, b) => a.timestamp.getTime() - b.timestamp.getTime());
|
|
101
|
+
|
|
102
|
+
return { metric, dataPoints };
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export function getSummary(canisterId: string, days: number = 7): MetricsSummary {
|
|
106
|
+
const now = new Date();
|
|
107
|
+
const from = new Date(now);
|
|
108
|
+
from.setDate(now.getDate() - days);
|
|
109
|
+
|
|
110
|
+
const metricValues: Partial<Record<MetricName, number[]>> = {};
|
|
111
|
+
const metricCurrent: Partial<Record<MetricName, number>> = {};
|
|
112
|
+
|
|
113
|
+
for (const metric of ['cycles_balance', 'memory_heap', 'message_count', 'request_count', 'error_count'] as MetricName[]) {
|
|
114
|
+
const series = getTimeSeries(canisterId, metric, from, now);
|
|
115
|
+
if (series.dataPoints.length > 0) {
|
|
116
|
+
metricValues[metric] = series.dataPoints.map(dp => dp.value);
|
|
117
|
+
const lastPoint = series.dataPoints[series.dataPoints.length - 1];
|
|
118
|
+
if (lastPoint) {
|
|
119
|
+
metricCurrent[metric] = lastPoint.value;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const summary: MetricsSummary = {
|
|
125
|
+
canisterId,
|
|
126
|
+
metrics: {} as Record<MetricName, { current: number; min: number; max: number; avg: number; delta?: number }>,
|
|
127
|
+
period: { start: from, end: now },
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
for (const [metric, values] of Object.entries(metricValues)) {
|
|
131
|
+
if (values) {
|
|
132
|
+
const currentValue = metricCurrent[metric as MetricName];
|
|
133
|
+
if (currentValue !== undefined) {
|
|
134
|
+
const min = Math.min(...values);
|
|
135
|
+
const max = Math.max(...values);
|
|
136
|
+
const avg = values.reduce((a, b) => a + b, 0) / values.length;
|
|
137
|
+
|
|
138
|
+
summary.metrics[metric as MetricName] = {
|
|
139
|
+
current: currentValue,
|
|
140
|
+
min,
|
|
141
|
+
max,
|
|
142
|
+
avg,
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
return summary;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
export function pruneOldData(canisterId: string, retentionDays: number): void {
|
|
152
|
+
const metricsDir = getMetricsDir(canisterId);
|
|
153
|
+
if (!fs.existsSync(metricsDir)) return;
|
|
154
|
+
|
|
155
|
+
const cutoff = new Date();
|
|
156
|
+
cutoff.setDate(cutoff.getDate() - retentionDays);
|
|
157
|
+
|
|
158
|
+
const files = fs.readdirSync(metricsDir);
|
|
159
|
+
for (const file of files) {
|
|
160
|
+
if (file.endsWith('.jsonl')) {
|
|
161
|
+
const dateStr = file.replace('.jsonl', '');
|
|
162
|
+
const fileDate = new Date(dateStr);
|
|
163
|
+
if (fileDate < cutoff) {
|
|
164
|
+
fs.unlinkSync(path.join(metricsDir, file));
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
export function startCollector(
|
|
171
|
+
canisterId: string,
|
|
172
|
+
config: CollectorConfig = {},
|
|
173
|
+
): NodeJS.Timeout {
|
|
174
|
+
const { interval = 60000, retentionDays = 7 } = config;
|
|
175
|
+
|
|
176
|
+
const collect = async () => {
|
|
177
|
+
try {
|
|
178
|
+
pruneOldData(canisterId, retentionDays);
|
|
179
|
+
} catch (error) {
|
|
180
|
+
console.error(`Failed to collect metrics for ${canisterId}:`, error);
|
|
181
|
+
}
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
collect();
|
|
185
|
+
return setInterval(collect, interval);
|
|
186
|
+
}
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Alerting
|
|
3
|
+
*
|
|
4
|
+
* Generates, stores, and retrieves monitoring alerts for canisters.
|
|
5
|
+
* Tracks cycle depletion, memory issues, and performance degradation.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import * as fs from 'node:fs';
|
|
9
|
+
import * as path from 'node:path';
|
|
10
|
+
import type {
|
|
11
|
+
MonitoringAlert,
|
|
12
|
+
CanisterStatusInfo,
|
|
13
|
+
HealthThresholds,
|
|
14
|
+
} from './types.js';
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Default alert storage path
|
|
18
|
+
*/
|
|
19
|
+
const ALERTS_DIR = path.join(process.env.HOME || process.cwd(), '.agentvault', 'alerts');
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Alert storage file path
|
|
23
|
+
*/
|
|
24
|
+
const getAlertFilePath = (canisterId: string) =>
|
|
25
|
+
path.join(ALERTS_DIR, `${canisterId}.json`);
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Load alerts from storage.
|
|
29
|
+
*
|
|
30
|
+
* @param canisterId - Canister ID
|
|
31
|
+
* @returns Stored alerts array
|
|
32
|
+
*/
|
|
33
|
+
export function loadAlerts(canisterId: string): MonitoringAlert[] {
|
|
34
|
+
const alertFile = getAlertFilePath(canisterId);
|
|
35
|
+
if (!fs.existsSync(alertFile)) {
|
|
36
|
+
return [];
|
|
37
|
+
}
|
|
38
|
+
try {
|
|
39
|
+
const content = fs.readFileSync(alertFile, 'utf-8');
|
|
40
|
+
return JSON.parse(content);
|
|
41
|
+
} catch {
|
|
42
|
+
return [];
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Save alerts to storage.
|
|
48
|
+
*
|
|
49
|
+
* @param canisterId - Canister ID
|
|
50
|
+
* @param alerts - Alerts to store
|
|
51
|
+
*/
|
|
52
|
+
export function saveAlerts(canisterId: string, alerts: MonitoringAlert[]): void {
|
|
53
|
+
const alertFile = getAlertFilePath(canisterId);
|
|
54
|
+
const content = JSON.stringify(alerts, null, 2);
|
|
55
|
+
fs.mkdirSync(path.dirname(alertFile), { recursive: true });
|
|
56
|
+
fs.writeFileSync(alertFile, content, 'utf-8');
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Append a new alert to storage.
|
|
61
|
+
*
|
|
62
|
+
* @param canisterId - Canister ID
|
|
63
|
+
* @param alert - Alert to add
|
|
64
|
+
*/
|
|
65
|
+
export function appendAlert(canisterId: string, alert: MonitoringAlert): void {
|
|
66
|
+
const alerts = loadAlerts(canisterId);
|
|
67
|
+
alerts.push(alert);
|
|
68
|
+
saveAlerts(canisterId, alerts);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Clear alerts for a canister.
|
|
73
|
+
*
|
|
74
|
+
* @param canisterId - Canister ID
|
|
75
|
+
*/
|
|
76
|
+
export function clearAlerts(canisterId: string): void {
|
|
77
|
+
const alertFile = getAlertFilePath(canisterId);
|
|
78
|
+
if (fs.existsSync(alertFile)) {
|
|
79
|
+
fs.unlinkSync(alertFile);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Generate health alerts for a canister based on its current status.
|
|
85
|
+
*
|
|
86
|
+
* @param statusInfo - Canister status information
|
|
87
|
+
* @param thresholds - Health check thresholds
|
|
88
|
+
* @returns Array of alerts
|
|
89
|
+
*/
|
|
90
|
+
export function generateHealthAlerts(
|
|
91
|
+
statusInfo: CanisterStatusInfo,
|
|
92
|
+
thresholds: HealthThresholds = {},
|
|
93
|
+
): MonitoringAlert[] {
|
|
94
|
+
const alerts: MonitoringAlert[] = [];
|
|
95
|
+
|
|
96
|
+
const cycles = statusInfo.cycles;
|
|
97
|
+
if (cycles !== undefined) {
|
|
98
|
+
if (thresholds.cyclesCritical !== undefined && cycles < thresholds.cyclesCritical) {
|
|
99
|
+
alerts.push({
|
|
100
|
+
severity: 'critical',
|
|
101
|
+
message: `Cycle balance critically low: ${cycles.toString()}`,
|
|
102
|
+
canisterId: statusInfo.canisterId,
|
|
103
|
+
metric: 'cycles',
|
|
104
|
+
value: cycles.toString(),
|
|
105
|
+
threshold: thresholds.cyclesCritical.toString(),
|
|
106
|
+
timestamp: new Date(),
|
|
107
|
+
});
|
|
108
|
+
} else if (thresholds.cyclesWarning !== undefined && cycles < thresholds.cyclesWarning) {
|
|
109
|
+
alerts.push({
|
|
110
|
+
severity: 'warning',
|
|
111
|
+
message: `Cycle balance low: ${cycles.toString()}`,
|
|
112
|
+
canisterId: statusInfo.canisterId,
|
|
113
|
+
metric: 'cycles',
|
|
114
|
+
value: cycles.toString(),
|
|
115
|
+
threshold: thresholds.cyclesWarning.toString(),
|
|
116
|
+
timestamp: new Date(),
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const memoryBytes = statusInfo.memorySize;
|
|
122
|
+
if (memoryBytes !== undefined) {
|
|
123
|
+
const memoryMB = Number(memoryBytes) / (1024 * 1024);
|
|
124
|
+
// IC canisters have ~4GB max memory (4096 MB)
|
|
125
|
+
const maxMemoryMB = 4096;
|
|
126
|
+
const criticalThresholdMB = maxMemoryMB * ((thresholds.memoryCriticalPercent ?? 0) / 100);
|
|
127
|
+
const warningThresholdMB = maxMemoryMB * ((thresholds.memoryWarningPercent ?? 0) / 100);
|
|
128
|
+
|
|
129
|
+
if (thresholds.memoryCriticalPercent !== undefined && memoryMB > criticalThresholdMB) {
|
|
130
|
+
alerts.push({
|
|
131
|
+
severity: 'critical',
|
|
132
|
+
message: `Memory usage critically high: ${memoryMB.toFixed(2)} MB`,
|
|
133
|
+
canisterId: statusInfo.canisterId,
|
|
134
|
+
metric: 'memory',
|
|
135
|
+
value: `${memoryMB.toFixed(2)} MB`,
|
|
136
|
+
threshold: `${thresholds.memoryCriticalPercent}%`,
|
|
137
|
+
timestamp: new Date(),
|
|
138
|
+
});
|
|
139
|
+
} else if (thresholds.memoryWarningPercent !== undefined && memoryMB > warningThresholdMB) {
|
|
140
|
+
alerts.push({
|
|
141
|
+
severity: 'warning',
|
|
142
|
+
message: `Memory usage high: ${memoryMB.toFixed(2)} MB`,
|
|
143
|
+
canisterId: statusInfo.canisterId,
|
|
144
|
+
metric: 'memory',
|
|
145
|
+
value: `${memoryMB.toFixed(2)} MB`,
|
|
146
|
+
threshold: `${thresholds.memoryWarningPercent}%`,
|
|
147
|
+
timestamp: new Date(),
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
if (statusInfo.status === 'stopped') {
|
|
153
|
+
alerts.push({
|
|
154
|
+
severity: 'critical',
|
|
155
|
+
message: 'Canister is stopped',
|
|
156
|
+
canisterId: statusInfo.canisterId,
|
|
157
|
+
metric: 'status',
|
|
158
|
+
value: statusInfo.status,
|
|
159
|
+
threshold: 'healthy',
|
|
160
|
+
timestamp: new Date(),
|
|
161
|
+
});
|
|
162
|
+
} else if (statusInfo.status === 'stopping') {
|
|
163
|
+
alerts.push({
|
|
164
|
+
severity: 'warning',
|
|
165
|
+
message: 'Canister is stopping',
|
|
166
|
+
canisterId: statusInfo.canisterId,
|
|
167
|
+
metric: 'status',
|
|
168
|
+
value: statusInfo.status,
|
|
169
|
+
threshold: 'healthy',
|
|
170
|
+
timestamp: new Date(),
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
return alerts;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Get recent alerts for a canister.
|
|
179
|
+
*
|
|
180
|
+
* @param canisterId - Canister ID
|
|
181
|
+
* @param limit - Maximum number of alerts to return (default: 10)
|
|
182
|
+
* @returns Recent alerts (newest first)
|
|
183
|
+
*/
|
|
184
|
+
export function getRecentAlerts(
|
|
185
|
+
canisterId: string,
|
|
186
|
+
limit: number = 10,
|
|
187
|
+
): MonitoringAlert[] {
|
|
188
|
+
const alerts = loadAlerts(canisterId);
|
|
189
|
+
return alerts.slice(-limit);
|
|
190
|
+
}
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Health Monitoring
|
|
3
|
+
*
|
|
4
|
+
* Performs health checks on canisters and determines overall health status
|
|
5
|
+
* based on resource usage thresholds.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { getCanisterInfo } from './info.js';
|
|
9
|
+
import type {
|
|
10
|
+
CanisterHealthStatus,
|
|
11
|
+
CanisterStatusInfo,
|
|
12
|
+
HealthThresholds,
|
|
13
|
+
MonitoringOptions,
|
|
14
|
+
MonitoringAlert,
|
|
15
|
+
} from './types.js';
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Determine health status based on resource metrics.
|
|
19
|
+
*
|
|
20
|
+
* @param statusInfo - Canister status information
|
|
21
|
+
* @param thresholds - Health check thresholds
|
|
22
|
+
* @returns Health status
|
|
23
|
+
*/
|
|
24
|
+
export function determineHealthStatus(
|
|
25
|
+
statusInfo: CanisterStatusInfo,
|
|
26
|
+
thresholds: HealthThresholds = {},
|
|
27
|
+
): CanisterHealthStatus {
|
|
28
|
+
// Check cycles
|
|
29
|
+
const cycles = statusInfo.cycles;
|
|
30
|
+
if (cycles !== undefined) {
|
|
31
|
+
if (thresholds.cyclesCritical && cycles < thresholds.cyclesCritical) {
|
|
32
|
+
return 'critical';
|
|
33
|
+
} else if (thresholds.cyclesWarning && cycles < thresholds.cyclesWarning) {
|
|
34
|
+
return 'warning';
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Check memory
|
|
39
|
+
const memoryBytes = statusInfo.memorySize;
|
|
40
|
+
if (memoryBytes !== undefined) {
|
|
41
|
+
const memoryMB = Number(memoryBytes) / (1024 * 1024);
|
|
42
|
+
// IC canisters have ~4GB max memory (4096 MB)
|
|
43
|
+
const maxMemoryMB = 4096;
|
|
44
|
+
const criticalThresholdMB = maxMemoryMB * ((thresholds.memoryCriticalPercent ?? 0) / 100);
|
|
45
|
+
const warningThresholdMB = maxMemoryMB * ((thresholds.memoryWarningPercent ?? 0) / 100);
|
|
46
|
+
|
|
47
|
+
if (thresholds.memoryCriticalPercent && memoryMB > criticalThresholdMB) {
|
|
48
|
+
return 'critical';
|
|
49
|
+
} else if (thresholds.memoryWarningPercent && memoryMB > warningThresholdMB) {
|
|
50
|
+
return 'warning';
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Check canister status
|
|
55
|
+
const status = statusInfo.status?.toLowerCase() ?? '';
|
|
56
|
+
if (status === 'stopped') {
|
|
57
|
+
return 'critical';
|
|
58
|
+
} else if (status === 'stopping') {
|
|
59
|
+
return 'warning';
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return 'healthy';
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Generate alerts based on health status.
|
|
67
|
+
*
|
|
68
|
+
* @param statusInfo - Canister status information
|
|
69
|
+
* @param thresholds - Health check thresholds
|
|
70
|
+
* @param currentHealth - Current health status
|
|
71
|
+
* @returns Generated alerts
|
|
72
|
+
*/
|
|
73
|
+
export function generateHealthAlerts(
|
|
74
|
+
statusInfo: CanisterStatusInfo,
|
|
75
|
+
thresholds: HealthThresholds = {},
|
|
76
|
+
currentHealth: CanisterHealthStatus = 'healthy',
|
|
77
|
+
): MonitoringAlert[] {
|
|
78
|
+
const alerts: MonitoringAlert[] = [];
|
|
79
|
+
|
|
80
|
+
const cycles = statusInfo.cycles;
|
|
81
|
+
if (cycles !== undefined && thresholds.cyclesCritical !== undefined) {
|
|
82
|
+
if (cycles < thresholds.cyclesCritical) {
|
|
83
|
+
alerts.push({
|
|
84
|
+
severity: 'critical',
|
|
85
|
+
message: `Cycle balance critically low: ${formatCycles(cycles)}`,
|
|
86
|
+
canisterId: statusInfo.canisterId,
|
|
87
|
+
metric: 'cycles',
|
|
88
|
+
value: formatCycles(cycles),
|
|
89
|
+
threshold: formatCycles(thresholds.cyclesCritical),
|
|
90
|
+
timestamp: new Date(),
|
|
91
|
+
});
|
|
92
|
+
} else if (thresholds.cyclesWarning !== undefined && cycles < thresholds.cyclesWarning) {
|
|
93
|
+
alerts.push({
|
|
94
|
+
severity: 'warning',
|
|
95
|
+
message: `Cycle balance low: ${formatCycles(cycles)}`,
|
|
96
|
+
canisterId: statusInfo.canisterId,
|
|
97
|
+
metric: 'cycles',
|
|
98
|
+
value: formatCycles(cycles),
|
|
99
|
+
threshold: formatCycles(thresholds.cyclesWarning),
|
|
100
|
+
timestamp: new Date(),
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const memoryBytes = statusInfo.memorySize;
|
|
106
|
+
if (memoryBytes !== undefined) {
|
|
107
|
+
const memoryMB = Number(memoryBytes) / (1024 * 1024);
|
|
108
|
+
if (thresholds.memoryCriticalPercent !== undefined && memoryMB > 1024 * thresholds.memoryCriticalPercent) {
|
|
109
|
+
alerts.push({
|
|
110
|
+
severity: 'critical',
|
|
111
|
+
message: `Memory usage critically high: ${memoryMB.toFixed(2)} MB`,
|
|
112
|
+
canisterId: statusInfo.canisterId,
|
|
113
|
+
metric: 'memory',
|
|
114
|
+
value: `${memoryMB.toFixed(2)} MB`,
|
|
115
|
+
threshold: `${thresholds.memoryCriticalPercent}%`,
|
|
116
|
+
timestamp: new Date(),
|
|
117
|
+
});
|
|
118
|
+
} else if (thresholds.memoryWarningPercent !== undefined && memoryMB > 1024 * thresholds.memoryWarningPercent) {
|
|
119
|
+
alerts.push({
|
|
120
|
+
severity: 'warning',
|
|
121
|
+
message: `Memory usage high: ${memoryMB.toFixed(2)} MB`,
|
|
122
|
+
canisterId: statusInfo.canisterId,
|
|
123
|
+
metric: 'memory',
|
|
124
|
+
value: `${memoryMB.toFixed(2)} MB`,
|
|
125
|
+
threshold: `${thresholds.memoryWarningPercent}%`,
|
|
126
|
+
timestamp: new Date(),
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
if (currentHealth !== 'healthy') {
|
|
132
|
+
alerts.push({
|
|
133
|
+
severity: 'warning',
|
|
134
|
+
message: `Canister status: ${statusInfo.status}`,
|
|
135
|
+
canisterId: statusInfo.canisterId,
|
|
136
|
+
metric: 'status',
|
|
137
|
+
value: statusInfo.status,
|
|
138
|
+
threshold: 'healthy',
|
|
139
|
+
timestamp: new Date(),
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
return alerts;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Format cycles bigint to human-readable string.
|
|
148
|
+
*/
|
|
149
|
+
function formatCycles(cycles: bigint): string {
|
|
150
|
+
if (cycles >= BigInt(1_000_000_000_000)) {
|
|
151
|
+
const value = Number(cycles) / 1_000_000_000_000;
|
|
152
|
+
return `${value.toFixed(2)} T`;
|
|
153
|
+
}
|
|
154
|
+
return cycles.toString();
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Perform a one-time health check.
|
|
159
|
+
*
|
|
160
|
+
* @param options - Monitoring options
|
|
161
|
+
* @returns Canister status with health status
|
|
162
|
+
*/
|
|
163
|
+
export async function checkHealth(
|
|
164
|
+
canisterId: string,
|
|
165
|
+
options?: Partial<MonitoringOptions>,
|
|
166
|
+
): Promise<CanisterStatusInfo> {
|
|
167
|
+
const monitoringOptions: MonitoringOptions = { canister: canisterId, ...options };
|
|
168
|
+
const statusInfo = await getCanisterInfo(canisterId, monitoringOptions);
|
|
169
|
+
|
|
170
|
+
const health = determineHealthStatus(statusInfo, options?.thresholds ?? {});
|
|
171
|
+
|
|
172
|
+
return {
|
|
173
|
+
...statusInfo,
|
|
174
|
+
health,
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Check multiple canisters' health status.
|
|
180
|
+
*
|
|
181
|
+
* @param canisterIds - Array of canister IDs to check
|
|
182
|
+
* @param options - Monitoring options applied to all
|
|
183
|
+
* @returns Array of canister status info
|
|
184
|
+
*/
|
|
185
|
+
export async function checkMultipleHealth(
|
|
186
|
+
canisterIds: string[],
|
|
187
|
+
options?: Partial<MonitoringOptions>,
|
|
188
|
+
): Promise<CanisterStatusInfo[]> {
|
|
189
|
+
const results = await Promise.all(
|
|
190
|
+
canisterIds.map((id) => getCanisterInfo(id, { canister: id, ...options }))
|
|
191
|
+
);
|
|
192
|
+
|
|
193
|
+
return results.map((statusInfo) => ({
|
|
194
|
+
...statusInfo,
|
|
195
|
+
health: determineHealthStatus(statusInfo, options?.thresholds ?? {}),
|
|
196
|
+
}));
|
|
197
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Monitoring Module
|
|
3
|
+
*
|
|
4
|
+
* Provides canister health monitoring and alerting capabilities.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
// Health
|
|
8
|
+
export {
|
|
9
|
+
determineHealthStatus,
|
|
10
|
+
generateHealthAlerts,
|
|
11
|
+
checkHealth,
|
|
12
|
+
checkMultipleHealth,
|
|
13
|
+
} from './health.js';
|
|
14
|
+
|
|
15
|
+
// Info
|
|
16
|
+
export {
|
|
17
|
+
getCanisterInfo,
|
|
18
|
+
} from './info.js';
|
|
19
|
+
|
|
20
|
+
// Alerting
|
|
21
|
+
export {
|
|
22
|
+
loadAlerts,
|
|
23
|
+
saveAlerts,
|
|
24
|
+
appendAlert,
|
|
25
|
+
clearAlerts,
|
|
26
|
+
getRecentAlerts,
|
|
27
|
+
} from './alerting.js';
|
|
28
|
+
|
|
29
|
+
// Types
|
|
30
|
+
export type {
|
|
31
|
+
CanisterHealthStatus,
|
|
32
|
+
CanisterStatusInfo,
|
|
33
|
+
HealthThresholds,
|
|
34
|
+
MonitoringOptions,
|
|
35
|
+
MonitoringAlert,
|
|
36
|
+
AlertSeverity,
|
|
37
|
+
ResourceUsageSnapshot,
|
|
38
|
+
} from './types.js';
|