@chainalert/cli 0.0.1
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/dist/alerts-STXD4TE5.js +134 -0
- package/dist/auth-6LTJFNU2.js +124 -0
- package/dist/channels-C5JZON37.js +179 -0
- package/dist/chunk-6FDEYAAT.js +27 -0
- package/dist/chunk-CE4DKHAY.js +28 -0
- package/dist/chunk-IC5RERFB.js +185 -0
- package/dist/chunk-K2BGDX7X.js +38 -0
- package/dist/chunk-WPW7UBVR.js +95 -0
- package/dist/contracts-J7JYPQXP.js +108 -0
- package/dist/detections-QSMNEXXI.js +282 -0
- package/dist/events-FM7VK5VH.js +104 -0
- package/dist/health-7R5HLRCZ.js +40 -0
- package/dist/index.js +159 -0
- package/dist/networks-CF2ARYQO.js +57 -0
- package/dist/org-contracts-DM2BTHTO.js +175 -0
- package/dist/repl-Q43IBAFT.js +1657 -0
- package/dist/rpc-configs-P2QCFCDJ.js +112 -0
- package/dist/state-changes-B6BIS4OJ.js +67 -0
- package/dist/templates-LYY2SV42.js +106 -0
- package/package.json +36 -0
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
spinner
|
|
4
|
+
} from "./chunk-CE4DKHAY.js";
|
|
5
|
+
import {
|
|
6
|
+
getAlert,
|
|
7
|
+
getAlertStats,
|
|
8
|
+
listAlerts
|
|
9
|
+
} from "./chunk-IC5RERFB.js";
|
|
10
|
+
import "./chunk-K2BGDX7X.js";
|
|
11
|
+
import {
|
|
12
|
+
dim,
|
|
13
|
+
print,
|
|
14
|
+
printError,
|
|
15
|
+
printJson,
|
|
16
|
+
printKV,
|
|
17
|
+
printTable
|
|
18
|
+
} from "./chunk-WPW7UBVR.js";
|
|
19
|
+
|
|
20
|
+
// src/commands/alerts.ts
|
|
21
|
+
async function run(args, flags) {
|
|
22
|
+
const sub = args[0] ?? "list";
|
|
23
|
+
switch (sub) {
|
|
24
|
+
case "list": {
|
|
25
|
+
try {
|
|
26
|
+
spinner.start("Fetching alerts...");
|
|
27
|
+
const result = await listAlerts({
|
|
28
|
+
severity: flags.severity,
|
|
29
|
+
detectionId: flags.detection,
|
|
30
|
+
from: flags.from,
|
|
31
|
+
to: flags.to,
|
|
32
|
+
page: flags.page ? Number(flags.page) : void 0,
|
|
33
|
+
limit: flags.limit ? Number(flags.limit) : void 0
|
|
34
|
+
});
|
|
35
|
+
spinner.stop();
|
|
36
|
+
if (flags.json) {
|
|
37
|
+
printJson(result);
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
if (!result.data.length) {
|
|
41
|
+
print("No alerts found.");
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
printTable(
|
|
45
|
+
["ID", "Detection", "Severity", "Title", "Fired At"],
|
|
46
|
+
result.data.map((a) => [
|
|
47
|
+
String(a.id),
|
|
48
|
+
a.detectionId ?? a.detectionName ?? "",
|
|
49
|
+
a.severity ?? "",
|
|
50
|
+
a.title ?? a.message ?? "",
|
|
51
|
+
a.firedAt ?? a.createdAt ?? ""
|
|
52
|
+
])
|
|
53
|
+
);
|
|
54
|
+
if (result.meta) {
|
|
55
|
+
print(dim(`
|
|
56
|
+
Page ${result.meta.page ?? 1} of ${result.meta.totalPages ?? "?"} (${result.meta.total ?? "?"} total)`));
|
|
57
|
+
}
|
|
58
|
+
} catch (err) {
|
|
59
|
+
spinner.stop();
|
|
60
|
+
printError(err.message ?? "Failed to fetch alerts.");
|
|
61
|
+
process.exitCode = 1;
|
|
62
|
+
}
|
|
63
|
+
break;
|
|
64
|
+
}
|
|
65
|
+
case "get": {
|
|
66
|
+
const id = args[1];
|
|
67
|
+
if (!id) {
|
|
68
|
+
printError("Usage: chainalert alerts get <id>");
|
|
69
|
+
process.exitCode = 1;
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
try {
|
|
73
|
+
spinner.start("Fetching alert...");
|
|
74
|
+
const result = await getAlert(id);
|
|
75
|
+
spinner.stop();
|
|
76
|
+
if (flags.json) {
|
|
77
|
+
printJson(result);
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
const a = result.data;
|
|
81
|
+
printKV([
|
|
82
|
+
["ID", String(a.id)],
|
|
83
|
+
["Detection", a.detectionId ?? a.detectionName ?? ""],
|
|
84
|
+
["Severity", a.severity ?? ""],
|
|
85
|
+
["Title", a.title ?? a.message ?? ""],
|
|
86
|
+
["Fired At", a.firedAt ?? a.createdAt ?? ""],
|
|
87
|
+
["Block", a.blockNumber ? String(a.blockNumber) : dim("(n/a)")],
|
|
88
|
+
["Tx Hash", a.txHash ?? dim("(n/a)")]
|
|
89
|
+
]);
|
|
90
|
+
if (a.details) {
|
|
91
|
+
print("");
|
|
92
|
+
print(JSON.stringify(a.details, null, 2));
|
|
93
|
+
}
|
|
94
|
+
} catch (err) {
|
|
95
|
+
spinner.stop();
|
|
96
|
+
printError(err.message ?? "Failed to fetch alert.");
|
|
97
|
+
process.exitCode = 1;
|
|
98
|
+
}
|
|
99
|
+
break;
|
|
100
|
+
}
|
|
101
|
+
case "stats": {
|
|
102
|
+
try {
|
|
103
|
+
spinner.start("Fetching alert stats...");
|
|
104
|
+
const result = await getAlertStats();
|
|
105
|
+
spinner.stop();
|
|
106
|
+
if (flags.json) {
|
|
107
|
+
printJson(result);
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
const stats = result.data ?? result;
|
|
111
|
+
const pairs = [];
|
|
112
|
+
for (const [key, value] of Object.entries(stats)) {
|
|
113
|
+
pairs.push([key, String(value)]);
|
|
114
|
+
}
|
|
115
|
+
if (pairs.length === 0) {
|
|
116
|
+
print("No stats available.");
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
printKV(pairs);
|
|
120
|
+
} catch (err) {
|
|
121
|
+
spinner.stop();
|
|
122
|
+
printError(err.message ?? "Failed to fetch alert stats.");
|
|
123
|
+
process.exitCode = 1;
|
|
124
|
+
}
|
|
125
|
+
break;
|
|
126
|
+
}
|
|
127
|
+
default:
|
|
128
|
+
printError(`Unknown subcommand: ${sub}. Use: list, get, stats`);
|
|
129
|
+
process.exitCode = 1;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
export {
|
|
133
|
+
run
|
|
134
|
+
};
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
ask
|
|
4
|
+
} from "./chunk-6FDEYAAT.js";
|
|
5
|
+
import {
|
|
6
|
+
loadConfig,
|
|
7
|
+
saveConfig
|
|
8
|
+
} from "./chunk-K2BGDX7X.js";
|
|
9
|
+
import {
|
|
10
|
+
dim,
|
|
11
|
+
print,
|
|
12
|
+
printError,
|
|
13
|
+
printJson,
|
|
14
|
+
printKV,
|
|
15
|
+
printSuccess
|
|
16
|
+
} from "./chunk-WPW7UBVR.js";
|
|
17
|
+
|
|
18
|
+
// src/commands/auth.ts
|
|
19
|
+
async function run(args, flags) {
|
|
20
|
+
const sub = args[0];
|
|
21
|
+
switch (sub) {
|
|
22
|
+
case "login": {
|
|
23
|
+
const apiKey = await ask("API key: ");
|
|
24
|
+
if (!apiKey) {
|
|
25
|
+
printError("API key cannot be empty.");
|
|
26
|
+
process.exitCode = 1;
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
const config = loadConfig();
|
|
30
|
+
config.apiKey = apiKey;
|
|
31
|
+
saveConfig(config);
|
|
32
|
+
printSuccess("API key saved.");
|
|
33
|
+
break;
|
|
34
|
+
}
|
|
35
|
+
case "logout": {
|
|
36
|
+
const config = loadConfig();
|
|
37
|
+
delete config.apiKey;
|
|
38
|
+
saveConfig(config);
|
|
39
|
+
printSuccess("API key removed.");
|
|
40
|
+
break;
|
|
41
|
+
}
|
|
42
|
+
case "whoami": {
|
|
43
|
+
const config = loadConfig();
|
|
44
|
+
if (!config.apiKey) {
|
|
45
|
+
printError("Not logged in. Run `chainalert auth login` to set an API key.");
|
|
46
|
+
process.exitCode = 1;
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
if (flags.json) {
|
|
50
|
+
printJson({
|
|
51
|
+
apiKeyPrefix: config.apiKey.slice(0, 10) + "...",
|
|
52
|
+
apiUrl: config.apiUrl ?? "(default)"
|
|
53
|
+
});
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
printKV([
|
|
57
|
+
["API Key", config.apiKey.slice(0, 10) + "..."],
|
|
58
|
+
["API URL", config.apiUrl ?? dim("(default)")]
|
|
59
|
+
]);
|
|
60
|
+
break;
|
|
61
|
+
}
|
|
62
|
+
default:
|
|
63
|
+
printError(`Unknown auth subcommand: ${sub ?? "(none)"}. Use login, logout, or whoami.`);
|
|
64
|
+
process.exitCode = 1;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
var VALID_CONFIG_KEYS = {
|
|
68
|
+
"api-url": "apiUrl",
|
|
69
|
+
"openai-key": "openaiKey"
|
|
70
|
+
};
|
|
71
|
+
async function runConfig(args, flags) {
|
|
72
|
+
const sub = args[0];
|
|
73
|
+
switch (sub) {
|
|
74
|
+
case "set": {
|
|
75
|
+
const key = args[1];
|
|
76
|
+
const value = args[2];
|
|
77
|
+
if (!key || !value) {
|
|
78
|
+
printError("Usage: chainalert config set <key> <value>");
|
|
79
|
+
process.exitCode = 1;
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
const configKey = VALID_CONFIG_KEYS[key];
|
|
83
|
+
if (!configKey) {
|
|
84
|
+
printError(`Unknown config key: ${key}. Valid keys: ${Object.keys(VALID_CONFIG_KEYS).join(", ")}`);
|
|
85
|
+
process.exitCode = 1;
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
const config = loadConfig();
|
|
89
|
+
config[configKey] = value;
|
|
90
|
+
saveConfig(config);
|
|
91
|
+
printSuccess(`${key} set.`);
|
|
92
|
+
break;
|
|
93
|
+
}
|
|
94
|
+
case "get": {
|
|
95
|
+
const key = args[1];
|
|
96
|
+
if (!key) {
|
|
97
|
+
printError("Usage: chainalert config get <key>");
|
|
98
|
+
process.exitCode = 1;
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
const configKey = VALID_CONFIG_KEYS[key];
|
|
102
|
+
if (!configKey) {
|
|
103
|
+
printError(`Unknown config key: ${key}. Valid keys: ${Object.keys(VALID_CONFIG_KEYS).join(", ")}`);
|
|
104
|
+
process.exitCode = 1;
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
const config = loadConfig();
|
|
108
|
+
const value = config[configKey];
|
|
109
|
+
if (flags.json) {
|
|
110
|
+
printJson({ [key]: value ?? null });
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
print(value ?? dim("(not set)"));
|
|
114
|
+
break;
|
|
115
|
+
}
|
|
116
|
+
default:
|
|
117
|
+
printError(`Unknown config subcommand: ${sub ?? "(none)"}. Use set or get.`);
|
|
118
|
+
process.exitCode = 1;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
export {
|
|
122
|
+
run,
|
|
123
|
+
runConfig
|
|
124
|
+
};
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
confirm
|
|
4
|
+
} from "./chunk-6FDEYAAT.js";
|
|
5
|
+
import {
|
|
6
|
+
spinner
|
|
7
|
+
} from "./chunk-CE4DKHAY.js";
|
|
8
|
+
import {
|
|
9
|
+
createChannel,
|
|
10
|
+
deleteChannel,
|
|
11
|
+
listChannels,
|
|
12
|
+
testChannel
|
|
13
|
+
} from "./chunk-IC5RERFB.js";
|
|
14
|
+
import "./chunk-K2BGDX7X.js";
|
|
15
|
+
import {
|
|
16
|
+
print,
|
|
17
|
+
printError,
|
|
18
|
+
printJson,
|
|
19
|
+
printSuccess,
|
|
20
|
+
printTable
|
|
21
|
+
} from "./chunk-WPW7UBVR.js";
|
|
22
|
+
|
|
23
|
+
// src/commands/channels.ts
|
|
24
|
+
async function run(args, flags) {
|
|
25
|
+
const sub = args[0] ?? "list";
|
|
26
|
+
switch (sub) {
|
|
27
|
+
case "list": {
|
|
28
|
+
try {
|
|
29
|
+
spinner.start("Fetching channels...");
|
|
30
|
+
const result = await listChannels({
|
|
31
|
+
type: flags.type
|
|
32
|
+
});
|
|
33
|
+
spinner.stop();
|
|
34
|
+
if (flags.json) {
|
|
35
|
+
printJson(result);
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
if (!result.data.length) {
|
|
39
|
+
print("No channels found.");
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
printTable(
|
|
43
|
+
["ID", "Name", "Type", "Enabled"],
|
|
44
|
+
result.data.map((c) => [
|
|
45
|
+
String(c.id),
|
|
46
|
+
c.name ?? "",
|
|
47
|
+
c.type ?? "",
|
|
48
|
+
c.enabled !== false ? "Yes" : "No"
|
|
49
|
+
])
|
|
50
|
+
);
|
|
51
|
+
} catch (err) {
|
|
52
|
+
spinner.stop();
|
|
53
|
+
printError(err.message ?? "Failed to fetch channels.");
|
|
54
|
+
process.exitCode = 1;
|
|
55
|
+
}
|
|
56
|
+
break;
|
|
57
|
+
}
|
|
58
|
+
case "create": {
|
|
59
|
+
if (!flags.name) {
|
|
60
|
+
printError("--name is required.");
|
|
61
|
+
process.exitCode = 1;
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
if (!flags.type) {
|
|
65
|
+
printError("--type is required.");
|
|
66
|
+
process.exitCode = 1;
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
let config;
|
|
70
|
+
switch (flags.type) {
|
|
71
|
+
case "webhook":
|
|
72
|
+
if (!flags.url) {
|
|
73
|
+
printError("--url is required for webhook channels.");
|
|
74
|
+
process.exitCode = 1;
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
config = { url: flags.url };
|
|
78
|
+
break;
|
|
79
|
+
case "email":
|
|
80
|
+
if (!flags.recipients) {
|
|
81
|
+
printError("--recipients is required for email channels.");
|
|
82
|
+
process.exitCode = 1;
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
config = { recipients: flags.recipients.split(",").map((r) => r.trim()) };
|
|
86
|
+
break;
|
|
87
|
+
case "slack":
|
|
88
|
+
if (!flags.url) {
|
|
89
|
+
printError("--url is required for slack channels.");
|
|
90
|
+
process.exitCode = 1;
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
config = { webhookUrl: flags.url };
|
|
94
|
+
break;
|
|
95
|
+
default:
|
|
96
|
+
config = {};
|
|
97
|
+
if (flags.url) config.url = flags.url;
|
|
98
|
+
if (flags.recipients) config.recipients = flags.recipients.split(",").map((r) => r.trim());
|
|
99
|
+
break;
|
|
100
|
+
}
|
|
101
|
+
try {
|
|
102
|
+
spinner.start("Creating channel...");
|
|
103
|
+
const result = await createChannel({
|
|
104
|
+
name: flags.name,
|
|
105
|
+
type: flags.type,
|
|
106
|
+
config
|
|
107
|
+
});
|
|
108
|
+
spinner.stop();
|
|
109
|
+
if (flags.json) {
|
|
110
|
+
printJson(result);
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
printSuccess(`Channel created: ${result.data.id}`);
|
|
114
|
+
} catch (err) {
|
|
115
|
+
spinner.stop();
|
|
116
|
+
printError(err.message ?? "Failed to create channel.");
|
|
117
|
+
process.exitCode = 1;
|
|
118
|
+
}
|
|
119
|
+
break;
|
|
120
|
+
}
|
|
121
|
+
case "test": {
|
|
122
|
+
const id = args[1];
|
|
123
|
+
if (!id) {
|
|
124
|
+
printError("Usage: chainalert channels test <id>");
|
|
125
|
+
process.exitCode = 1;
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
try {
|
|
129
|
+
spinner.start("Testing channel...");
|
|
130
|
+
const result = await testChannel(id);
|
|
131
|
+
spinner.stop();
|
|
132
|
+
if (flags.json) {
|
|
133
|
+
printJson(result);
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
printSuccess("Test notification sent.");
|
|
137
|
+
} catch (err) {
|
|
138
|
+
spinner.stop();
|
|
139
|
+
printError(err.message ?? "Failed to test channel.");
|
|
140
|
+
process.exitCode = 1;
|
|
141
|
+
}
|
|
142
|
+
break;
|
|
143
|
+
}
|
|
144
|
+
case "delete": {
|
|
145
|
+
const id = args[1];
|
|
146
|
+
if (!id) {
|
|
147
|
+
printError("Usage: chainalert channels delete <id>");
|
|
148
|
+
process.exitCode = 1;
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
const ok = await confirm(`Delete channel ${id}?`);
|
|
152
|
+
if (!ok) {
|
|
153
|
+
print("Cancelled.");
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
try {
|
|
157
|
+
spinner.start("Deleting channel...");
|
|
158
|
+
const result = await deleteChannel(id);
|
|
159
|
+
spinner.stop();
|
|
160
|
+
if (flags.json) {
|
|
161
|
+
printJson(result);
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
printSuccess(`Channel ${id} deleted.`);
|
|
165
|
+
} catch (err) {
|
|
166
|
+
spinner.stop();
|
|
167
|
+
printError(err.message ?? "Failed to delete channel.");
|
|
168
|
+
process.exitCode = 1;
|
|
169
|
+
}
|
|
170
|
+
break;
|
|
171
|
+
}
|
|
172
|
+
default:
|
|
173
|
+
printError(`Unknown subcommand: ${sub}. Use: list, create, test, delete`);
|
|
174
|
+
process.exitCode = 1;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
export {
|
|
178
|
+
run
|
|
179
|
+
};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/util/prompt.ts
|
|
4
|
+
import { createInterface } from "readline";
|
|
5
|
+
function confirm(question) {
|
|
6
|
+
const rl = createInterface({ input: process.stdin, output: process.stderr });
|
|
7
|
+
return new Promise((resolve) => {
|
|
8
|
+
rl.question(`${question} [y/N] `, (answer) => {
|
|
9
|
+
rl.close();
|
|
10
|
+
resolve(answer.trim().toLowerCase() === "y");
|
|
11
|
+
});
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
function ask(question) {
|
|
15
|
+
const rl = createInterface({ input: process.stdin, output: process.stderr });
|
|
16
|
+
return new Promise((resolve) => {
|
|
17
|
+
rl.question(question, (answer) => {
|
|
18
|
+
rl.close();
|
|
19
|
+
resolve(answer.trim());
|
|
20
|
+
});
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export {
|
|
25
|
+
confirm,
|
|
26
|
+
ask
|
|
27
|
+
};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/util/spinner.ts
|
|
4
|
+
var FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
5
|
+
var Spinner = class {
|
|
6
|
+
interval = null;
|
|
7
|
+
frame = 0;
|
|
8
|
+
start(msg) {
|
|
9
|
+
this.stop();
|
|
10
|
+
process.stdout.write(`${FRAMES[0]} ${msg}`);
|
|
11
|
+
this.interval = setInterval(() => {
|
|
12
|
+
this.frame = (this.frame + 1) % FRAMES.length;
|
|
13
|
+
process.stdout.write(`\r${FRAMES[this.frame]} ${msg}`);
|
|
14
|
+
}, 80);
|
|
15
|
+
}
|
|
16
|
+
stop() {
|
|
17
|
+
if (this.interval) {
|
|
18
|
+
clearInterval(this.interval);
|
|
19
|
+
this.interval = null;
|
|
20
|
+
process.stdout.write("\r\x1B[K");
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
var spinner = new Spinner();
|
|
25
|
+
|
|
26
|
+
export {
|
|
27
|
+
spinner
|
|
28
|
+
};
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
getApiKey,
|
|
4
|
+
getApiUrl
|
|
5
|
+
} from "./chunk-K2BGDX7X.js";
|
|
6
|
+
|
|
7
|
+
// src/client/http.ts
|
|
8
|
+
var ApiError = class extends Error {
|
|
9
|
+
status;
|
|
10
|
+
constructor(status, message) {
|
|
11
|
+
super(message);
|
|
12
|
+
this.name = "ApiError";
|
|
13
|
+
this.status = status;
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
async function request(method, path, opts) {
|
|
17
|
+
const baseUrl = getApiUrl();
|
|
18
|
+
if (!baseUrl) {
|
|
19
|
+
throw new Error("API URL not configured. Run `chainalert config set apiUrl <url>` first.");
|
|
20
|
+
}
|
|
21
|
+
const url = new URL(path, baseUrl);
|
|
22
|
+
if (opts?.params) {
|
|
23
|
+
for (const [key, value] of Object.entries(opts.params)) {
|
|
24
|
+
if (value !== void 0) {
|
|
25
|
+
url.searchParams.set(key, String(value));
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
const headers = {
|
|
30
|
+
"Content-Type": "application/json"
|
|
31
|
+
};
|
|
32
|
+
const apiKey = getApiKey();
|
|
33
|
+
if (apiKey) {
|
|
34
|
+
headers["X-API-Key"] = apiKey;
|
|
35
|
+
}
|
|
36
|
+
const res = await fetch(url.toString(), {
|
|
37
|
+
method,
|
|
38
|
+
headers,
|
|
39
|
+
body: opts?.body !== void 0 ? JSON.stringify(opts.body) : void 0
|
|
40
|
+
});
|
|
41
|
+
if (!res.ok) {
|
|
42
|
+
let message = `HTTP ${res.status}`;
|
|
43
|
+
try {
|
|
44
|
+
const err = await res.json();
|
|
45
|
+
if (typeof err.error === "string") message = err.error;
|
|
46
|
+
else if (typeof err.message === "string") message = err.message;
|
|
47
|
+
} catch {
|
|
48
|
+
}
|
|
49
|
+
throw new ApiError(res.status, message);
|
|
50
|
+
}
|
|
51
|
+
return await res.json();
|
|
52
|
+
}
|
|
53
|
+
function get(path, params) {
|
|
54
|
+
return request("GET", path, { params });
|
|
55
|
+
}
|
|
56
|
+
function post(path, body) {
|
|
57
|
+
return request("POST", path, { body });
|
|
58
|
+
}
|
|
59
|
+
function patch(path, body) {
|
|
60
|
+
return request("PATCH", path, { body });
|
|
61
|
+
}
|
|
62
|
+
function del(path) {
|
|
63
|
+
return request("DELETE", path);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// src/client/api.ts
|
|
67
|
+
async function getHealth() {
|
|
68
|
+
return get("/health");
|
|
69
|
+
}
|
|
70
|
+
async function listNetworks() {
|
|
71
|
+
return get("/networks");
|
|
72
|
+
}
|
|
73
|
+
async function listTemplates(params) {
|
|
74
|
+
return get("/templates", params);
|
|
75
|
+
}
|
|
76
|
+
async function getTemplate(slug) {
|
|
77
|
+
return get(`/templates/${slug}`);
|
|
78
|
+
}
|
|
79
|
+
async function listDetections(params) {
|
|
80
|
+
return get("/detections", params);
|
|
81
|
+
}
|
|
82
|
+
async function getDetection(id) {
|
|
83
|
+
return get(`/detections/${id}`);
|
|
84
|
+
}
|
|
85
|
+
async function createDetection(body) {
|
|
86
|
+
return post("/detections", body);
|
|
87
|
+
}
|
|
88
|
+
async function updateDetection(id, body) {
|
|
89
|
+
return patch(`/detections/${id}`, body);
|
|
90
|
+
}
|
|
91
|
+
async function deleteDetection(id) {
|
|
92
|
+
return del(`/detections/${id}`);
|
|
93
|
+
}
|
|
94
|
+
async function testDetection(id, body) {
|
|
95
|
+
return post(`/detections/${id}/test`, body);
|
|
96
|
+
}
|
|
97
|
+
async function listAlerts(params) {
|
|
98
|
+
return get("/alerts", params);
|
|
99
|
+
}
|
|
100
|
+
async function getAlert(id) {
|
|
101
|
+
return get(`/alerts/${id}`);
|
|
102
|
+
}
|
|
103
|
+
async function getAlertStats() {
|
|
104
|
+
return get("/alerts/stats");
|
|
105
|
+
}
|
|
106
|
+
async function listEvents(params) {
|
|
107
|
+
return get("/events", params);
|
|
108
|
+
}
|
|
109
|
+
async function getEvent(id) {
|
|
110
|
+
return get(`/events/${id}`);
|
|
111
|
+
}
|
|
112
|
+
async function listStateChanges(params) {
|
|
113
|
+
return get("/state-changes", params);
|
|
114
|
+
}
|
|
115
|
+
async function listChannels(params) {
|
|
116
|
+
return get("/channels", params);
|
|
117
|
+
}
|
|
118
|
+
async function createChannel(body) {
|
|
119
|
+
return post("/channels", body);
|
|
120
|
+
}
|
|
121
|
+
async function deleteChannel(id) {
|
|
122
|
+
return del(`/channels/${id}`);
|
|
123
|
+
}
|
|
124
|
+
async function testChannel(id) {
|
|
125
|
+
return post(`/channels/${id}/test`);
|
|
126
|
+
}
|
|
127
|
+
async function resolveContract(body) {
|
|
128
|
+
return post("/contracts/resolve", body);
|
|
129
|
+
}
|
|
130
|
+
async function getStorageSlots(id) {
|
|
131
|
+
return get(`/contracts/${id}/storage-slots`);
|
|
132
|
+
}
|
|
133
|
+
async function listOrgContracts(params) {
|
|
134
|
+
return get("/org-contracts", params);
|
|
135
|
+
}
|
|
136
|
+
async function getOrgContract(id) {
|
|
137
|
+
return get(`/org-contracts/${id}`);
|
|
138
|
+
}
|
|
139
|
+
async function registerOrgContract(body) {
|
|
140
|
+
return post("/org-contracts", body);
|
|
141
|
+
}
|
|
142
|
+
async function getOrgContractTraits(id) {
|
|
143
|
+
return get(`/org-contracts/${id}/traits`);
|
|
144
|
+
}
|
|
145
|
+
async function listRpcConfigs() {
|
|
146
|
+
return get("/rpc-configs");
|
|
147
|
+
}
|
|
148
|
+
async function addRpcConfig(body) {
|
|
149
|
+
return post("/rpc-configs", body);
|
|
150
|
+
}
|
|
151
|
+
async function testRpcConfig(id) {
|
|
152
|
+
return post(`/rpc-configs/${id}/test`);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
export {
|
|
156
|
+
getHealth,
|
|
157
|
+
listNetworks,
|
|
158
|
+
listTemplates,
|
|
159
|
+
getTemplate,
|
|
160
|
+
listDetections,
|
|
161
|
+
getDetection,
|
|
162
|
+
createDetection,
|
|
163
|
+
updateDetection,
|
|
164
|
+
deleteDetection,
|
|
165
|
+
testDetection,
|
|
166
|
+
listAlerts,
|
|
167
|
+
getAlert,
|
|
168
|
+
getAlertStats,
|
|
169
|
+
listEvents,
|
|
170
|
+
getEvent,
|
|
171
|
+
listStateChanges,
|
|
172
|
+
listChannels,
|
|
173
|
+
createChannel,
|
|
174
|
+
deleteChannel,
|
|
175
|
+
testChannel,
|
|
176
|
+
resolveContract,
|
|
177
|
+
getStorageSlots,
|
|
178
|
+
listOrgContracts,
|
|
179
|
+
getOrgContract,
|
|
180
|
+
registerOrgContract,
|
|
181
|
+
getOrgContractTraits,
|
|
182
|
+
listRpcConfigs,
|
|
183
|
+
addRpcConfig,
|
|
184
|
+
testRpcConfig
|
|
185
|
+
};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
CONFIG_DIR,
|
|
4
|
+
CONFIG_FILE,
|
|
5
|
+
DEFAULT_API_URL
|
|
6
|
+
} from "./chunk-WPW7UBVR.js";
|
|
7
|
+
|
|
8
|
+
// src/config/store.ts
|
|
9
|
+
import { readFileSync, writeFileSync, mkdirSync, existsSync } from "fs";
|
|
10
|
+
function ensureDir() {
|
|
11
|
+
if (!existsSync(CONFIG_DIR)) {
|
|
12
|
+
mkdirSync(CONFIG_DIR, { recursive: true });
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
function loadConfig() {
|
|
16
|
+
try {
|
|
17
|
+
return JSON.parse(readFileSync(CONFIG_FILE, "utf-8"));
|
|
18
|
+
} catch {
|
|
19
|
+
return {};
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
function saveConfig(config) {
|
|
23
|
+
ensureDir();
|
|
24
|
+
writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2) + "\n");
|
|
25
|
+
}
|
|
26
|
+
function getApiUrl() {
|
|
27
|
+
return loadConfig().apiUrl ?? DEFAULT_API_URL;
|
|
28
|
+
}
|
|
29
|
+
function getApiKey() {
|
|
30
|
+
return loadConfig().apiKey;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export {
|
|
34
|
+
loadConfig,
|
|
35
|
+
saveConfig,
|
|
36
|
+
getApiUrl,
|
|
37
|
+
getApiKey
|
|
38
|
+
};
|