arc402-cli 0.7.1 → 0.7.2
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/commands/daemon.d.ts.map +1 -1
- package/dist/commands/daemon.js +67 -0
- package/dist/commands/daemon.js.map +1 -1
- package/dist/daemon/config.d.ts +15 -1
- package/dist/daemon/config.d.ts.map +1 -1
- package/dist/daemon/config.js +32 -0
- package/dist/daemon/config.js.map +1 -1
- package/dist/daemon/index.d.ts.map +1 -1
- package/dist/daemon/index.js +1 -9
- package/dist/daemon/index.js.map +1 -1
- package/dist/daemon/notify.d.ts +35 -6
- package/dist/daemon/notify.d.ts.map +1 -1
- package/dist/daemon/notify.js +176 -48
- package/dist/daemon/notify.js.map +1 -1
- package/package.json +1 -1
- package/src/commands/daemon.ts +73 -0
- package/src/daemon/config.ts +35 -0
- package/src/daemon/index.ts +2 -14
- package/src/daemon/notify.ts +199 -59
package/dist/daemon/notify.d.ts
CHANGED
|
@@ -1,14 +1,42 @@
|
|
|
1
|
+
import type { DaemonConfig } from "./config";
|
|
1
2
|
export type NotifyEvent = "hire_request" | "hire_accepted" | "hire_rejected" | "delivery" | "dispute" | "channel_challenge" | "low_balance" | "daemon_started" | "daemon_stopped";
|
|
2
|
-
export interface
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
export interface NotificationChannel {
|
|
4
|
+
send(title: string, body: string): Promise<void>;
|
|
5
|
+
}
|
|
6
|
+
export declare class TelegramChannel implements NotificationChannel {
|
|
7
|
+
private botToken;
|
|
8
|
+
private chatId;
|
|
9
|
+
constructor(botToken: string, chatId: string);
|
|
10
|
+
send(title: string, body: string): Promise<void>;
|
|
11
|
+
}
|
|
12
|
+
export declare class DiscordChannel implements NotificationChannel {
|
|
13
|
+
private webhookUrl;
|
|
14
|
+
constructor(webhookUrl: string);
|
|
15
|
+
send(title: string, body: string): Promise<void>;
|
|
16
|
+
}
|
|
17
|
+
export declare class WebhookChannel implements NotificationChannel {
|
|
18
|
+
private url;
|
|
19
|
+
private headers;
|
|
20
|
+
constructor(url: string, headers?: Record<string, string>);
|
|
21
|
+
send(title: string, body: string): Promise<void>;
|
|
22
|
+
}
|
|
23
|
+
export declare class EmailChannel implements NotificationChannel {
|
|
24
|
+
private cfg;
|
|
25
|
+
constructor(cfg: {
|
|
26
|
+
smtpHost: string;
|
|
27
|
+
smtpPort: number;
|
|
28
|
+
smtpUser: string;
|
|
29
|
+
smtpPass: string;
|
|
30
|
+
to: string;
|
|
31
|
+
});
|
|
32
|
+
send(title: string, body: string): Promise<void>;
|
|
5
33
|
}
|
|
6
34
|
export declare class Notifier {
|
|
7
|
-
private
|
|
35
|
+
private channels;
|
|
8
36
|
private notifyFlags;
|
|
9
|
-
constructor(
|
|
37
|
+
constructor(channels: NotificationChannel[], flags?: Partial<Record<NotifyEvent, boolean>>);
|
|
10
38
|
isEnabled(): boolean;
|
|
11
|
-
send(event: NotifyEvent,
|
|
39
|
+
send(event: NotifyEvent, title: string, body: string): Promise<void>;
|
|
12
40
|
notifyHireRequest(hireId: string, hirerAddress: string, priceEth: string, capability: string): Promise<void>;
|
|
13
41
|
notifyHireAccepted(hireId: string, agreementId: string): Promise<void>;
|
|
14
42
|
notifyHireRejected(hireId: string, reason: string): Promise<void>;
|
|
@@ -19,4 +47,5 @@ export declare class Notifier {
|
|
|
19
47
|
notifyStarted(walletAddress: string, subsystems: string[]): Promise<void>;
|
|
20
48
|
notifyStopped(): Promise<void>;
|
|
21
49
|
}
|
|
50
|
+
export declare function buildNotifier(config: DaemonConfig): Notifier;
|
|
22
51
|
//# sourceMappingURL=notify.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"notify.d.ts","sourceRoot":"","sources":["../../src/daemon/notify.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"notify.d.ts","sourceRoot":"","sources":["../../src/daemon/notify.ts"],"names":[],"mappings":"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"}
|
package/dist/daemon/notify.js
CHANGED
|
@@ -33,35 +33,38 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
33
33
|
};
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
-
exports.Notifier = void 0;
|
|
36
|
+
exports.Notifier = exports.EmailChannel = exports.WebhookChannel = exports.DiscordChannel = exports.TelegramChannel = void 0;
|
|
37
|
+
exports.buildNotifier = buildNotifier;
|
|
37
38
|
/**
|
|
38
|
-
*
|
|
39
|
-
*
|
|
39
|
+
* Multi-channel notification module for daemon events.
|
|
40
|
+
* Supports Telegram, Discord webhooks, generic webhooks, and email (via nodemailer).
|
|
40
41
|
*/
|
|
41
42
|
const https = __importStar(require("https"));
|
|
42
|
-
|
|
43
|
+
const http = __importStar(require("http"));
|
|
44
|
+
// ─── HTTP helper ──────────────────────────────────────────────────────────────
|
|
45
|
+
function httpPost(url, payload, extraHeaders) {
|
|
43
46
|
return new Promise((resolve, reject) => {
|
|
44
|
-
const
|
|
47
|
+
const parsed = new URL(url);
|
|
48
|
+
const mod = parsed.protocol === "https:" ? https : http;
|
|
45
49
|
const options = {
|
|
46
|
-
hostname:
|
|
47
|
-
port: 443,
|
|
48
|
-
path:
|
|
50
|
+
hostname: parsed.hostname,
|
|
51
|
+
port: parsed.port || (parsed.protocol === "https:" ? 443 : 80),
|
|
52
|
+
path: parsed.pathname + parsed.search,
|
|
49
53
|
method: "POST",
|
|
50
54
|
headers: {
|
|
51
55
|
"Content-Type": "application/json",
|
|
52
56
|
"Content-Length": Buffer.byteLength(payload),
|
|
57
|
+
...extraHeaders,
|
|
53
58
|
},
|
|
54
59
|
};
|
|
55
|
-
const req =
|
|
56
|
-
|
|
57
|
-
res.on("data", (c) => { data += c.toString(); });
|
|
60
|
+
const req = mod.request(options, (res) => {
|
|
61
|
+
res.resume();
|
|
58
62
|
res.on("end", () => {
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
reject(new Error(`Telegram API error: ${parsed.description}`));
|
|
63
|
+
if (res.statusCode && res.statusCode >= 200 && res.statusCode < 300) {
|
|
64
|
+
resolve();
|
|
62
65
|
}
|
|
63
66
|
else {
|
|
64
|
-
|
|
67
|
+
reject(new Error(`HTTP ${res.statusCode}`));
|
|
65
68
|
}
|
|
66
69
|
});
|
|
67
70
|
});
|
|
@@ -70,9 +73,105 @@ function telegramPost(botToken, method, body) {
|
|
|
70
73
|
req.end();
|
|
71
74
|
});
|
|
72
75
|
}
|
|
76
|
+
// ─── Telegram channel ─────────────────────────────────────────────────────────
|
|
77
|
+
class TelegramChannel {
|
|
78
|
+
constructor(botToken, chatId) {
|
|
79
|
+
this.botToken = botToken;
|
|
80
|
+
this.chatId = chatId;
|
|
81
|
+
}
|
|
82
|
+
async send(title, body) {
|
|
83
|
+
const text = body ? `<b>${title}</b>\n${body}` : `<b>${title}</b>`;
|
|
84
|
+
const payload = JSON.stringify({ chat_id: this.chatId, text, parse_mode: "HTML" });
|
|
85
|
+
const options = {
|
|
86
|
+
hostname: "api.telegram.org",
|
|
87
|
+
port: 443,
|
|
88
|
+
path: `/bot${this.botToken}/sendMessage`,
|
|
89
|
+
method: "POST",
|
|
90
|
+
headers: {
|
|
91
|
+
"Content-Type": "application/json",
|
|
92
|
+
"Content-Length": Buffer.byteLength(payload),
|
|
93
|
+
},
|
|
94
|
+
};
|
|
95
|
+
await new Promise((resolve, reject) => {
|
|
96
|
+
const req = https.request(options, (res) => {
|
|
97
|
+
let data = "";
|
|
98
|
+
res.on("data", (c) => { data += c.toString(); });
|
|
99
|
+
res.on("end", () => {
|
|
100
|
+
const parsed = JSON.parse(data);
|
|
101
|
+
if (!parsed.ok) {
|
|
102
|
+
reject(new Error(`Telegram API error: ${parsed.description}`));
|
|
103
|
+
}
|
|
104
|
+
else {
|
|
105
|
+
resolve();
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
});
|
|
109
|
+
req.on("error", reject);
|
|
110
|
+
req.write(payload);
|
|
111
|
+
req.end();
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
exports.TelegramChannel = TelegramChannel;
|
|
116
|
+
// ─── Discord channel ──────────────────────────────────────────────────────────
|
|
117
|
+
class DiscordChannel {
|
|
118
|
+
constructor(webhookUrl) {
|
|
119
|
+
this.webhookUrl = webhookUrl;
|
|
120
|
+
}
|
|
121
|
+
async send(title, body) {
|
|
122
|
+
const content = body ? `**${title}**\n${body}` : `**${title}**`;
|
|
123
|
+
await httpPost(this.webhookUrl, JSON.stringify({ content }), {});
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
exports.DiscordChannel = DiscordChannel;
|
|
127
|
+
// ─── Generic webhook channel ──────────────────────────────────────────────────
|
|
128
|
+
class WebhookChannel {
|
|
129
|
+
constructor(url, headers = {}) {
|
|
130
|
+
this.url = url;
|
|
131
|
+
this.headers = headers;
|
|
132
|
+
}
|
|
133
|
+
async send(title, body) {
|
|
134
|
+
await httpPost(this.url, JSON.stringify({ title, body, timestamp: new Date().toISOString() }), this.headers);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
exports.WebhookChannel = WebhookChannel;
|
|
138
|
+
// ─── Email channel (optional — requires nodemailer) ───────────────────────────
|
|
139
|
+
class EmailChannel {
|
|
140
|
+
constructor(cfg) {
|
|
141
|
+
this.cfg = cfg;
|
|
142
|
+
}
|
|
143
|
+
async send(title, body) {
|
|
144
|
+
// nodemailer is an optional runtime dependency — load via require to skip
|
|
145
|
+
// compile-time module resolution. Throws a clear message if not installed.
|
|
146
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports, @typescript-eslint/no-explicit-any
|
|
147
|
+
let nodemailer;
|
|
148
|
+
try {
|
|
149
|
+
// Using Function constructor avoids TypeScript static import analysis.
|
|
150
|
+
nodemailer = (new Function("require", "return require('nodemailer')"))(require);
|
|
151
|
+
}
|
|
152
|
+
catch {
|
|
153
|
+
throw new Error("nodemailer is not installed. Run: npm install nodemailer");
|
|
154
|
+
}
|
|
155
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access
|
|
156
|
+
const transport = nodemailer.createTransport({
|
|
157
|
+
host: this.cfg.smtpHost,
|
|
158
|
+
port: this.cfg.smtpPort,
|
|
159
|
+
auth: { user: this.cfg.smtpUser, pass: this.cfg.smtpPass },
|
|
160
|
+
});
|
|
161
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access
|
|
162
|
+
await transport.sendMail({
|
|
163
|
+
from: this.cfg.smtpUser,
|
|
164
|
+
to: this.cfg.to,
|
|
165
|
+
subject: title,
|
|
166
|
+
text: body,
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
exports.EmailChannel = EmailChannel;
|
|
171
|
+
// ─── Notifier ─────────────────────────────────────────────────────────────────
|
|
73
172
|
class Notifier {
|
|
74
|
-
constructor(
|
|
75
|
-
this.
|
|
173
|
+
constructor(channels, flags = {}) {
|
|
174
|
+
this.channels = channels;
|
|
76
175
|
this.notifyFlags = {
|
|
77
176
|
hire_request: flags.hire_request ?? true,
|
|
78
177
|
hire_accepted: flags.hire_accepted ?? true,
|
|
@@ -86,63 +185,92 @@ class Notifier {
|
|
|
86
185
|
};
|
|
87
186
|
}
|
|
88
187
|
isEnabled() {
|
|
89
|
-
return this.
|
|
188
|
+
return this.channels.length > 0;
|
|
90
189
|
}
|
|
91
|
-
async send(event,
|
|
92
|
-
if (!this.config)
|
|
93
|
-
return;
|
|
190
|
+
async send(event, title, body) {
|
|
94
191
|
if (!this.notifyFlags[event])
|
|
95
192
|
return;
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
// Non-fatal — log and continue
|
|
105
|
-
process.stderr.write(`[notify] Telegram send failed: ${err}\n`);
|
|
106
|
-
}
|
|
193
|
+
await Promise.all(this.channels.map(async (ch) => {
|
|
194
|
+
try {
|
|
195
|
+
await ch.send(title, body);
|
|
196
|
+
}
|
|
197
|
+
catch (err) {
|
|
198
|
+
process.stderr.write(`[notify] Channel send failed: ${err}\n`);
|
|
199
|
+
}
|
|
200
|
+
}));
|
|
107
201
|
}
|
|
108
202
|
async notifyHireRequest(hireId, hirerAddress, priceEth, capability) {
|
|
109
203
|
const short = hirerAddress.slice(0, 10);
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
`
|
|
113
|
-
`From: <code>${short}...</code>`,
|
|
204
|
+
await this.send("hire_request", "Hire Request", [
|
|
205
|
+
`ID: ${hireId}`,
|
|
206
|
+
`From: ${short}...`,
|
|
114
207
|
`Capability: ${capability || "unspecified"}`,
|
|
115
208
|
`Price: ${priceEth} ETH`,
|
|
116
209
|
``,
|
|
117
|
-
`Approve:
|
|
118
|
-
`Reject:
|
|
119
|
-
].join("\n");
|
|
120
|
-
await this.send("hire_request", msg);
|
|
210
|
+
`Approve: arc402 daemon approve ${hireId}`,
|
|
211
|
+
`Reject: arc402 daemon reject ${hireId}`,
|
|
212
|
+
].join("\n"));
|
|
121
213
|
}
|
|
122
214
|
async notifyHireAccepted(hireId, agreementId) {
|
|
123
|
-
await this.send("hire_accepted",
|
|
215
|
+
await this.send("hire_accepted", "Hire Accepted", `ID: ${hireId}\nAgreement: ${agreementId}`);
|
|
124
216
|
}
|
|
125
217
|
async notifyHireRejected(hireId, reason) {
|
|
126
|
-
await this.send("hire_rejected",
|
|
218
|
+
await this.send("hire_rejected", "Hire Rejected", `ID: ${hireId}\nReason: ${reason}`);
|
|
127
219
|
}
|
|
128
220
|
async notifyDelivery(agreementId, deliveryHash, userOpHash) {
|
|
129
|
-
await this.send("delivery",
|
|
221
|
+
await this.send("delivery", "Delivery Submitted", [
|
|
222
|
+
`Agreement: ${agreementId}`,
|
|
223
|
+
`Delivery hash: ${deliveryHash.slice(0, 16)}...`,
|
|
224
|
+
`UserOp: ${userOpHash.slice(0, 16)}...`,
|
|
225
|
+
].join("\n"));
|
|
130
226
|
}
|
|
131
227
|
async notifyDispute(agreementId, raisedBy) {
|
|
132
|
-
await this.send("dispute",
|
|
228
|
+
await this.send("dispute", "Dispute Raised", `Agreement: ${agreementId}\nBy: ${raisedBy}`);
|
|
133
229
|
}
|
|
134
230
|
async notifyChannelChallenge(channelId, txHash) {
|
|
135
|
-
await this.send("channel_challenge",
|
|
231
|
+
await this.send("channel_challenge", "Channel Challenged", `Channel: ${channelId.slice(0, 16)}...\nTx: ${txHash.slice(0, 16)}...`);
|
|
136
232
|
}
|
|
137
233
|
async notifyLowBalance(balanceEth, thresholdEth) {
|
|
138
|
-
await this.send("low_balance",
|
|
234
|
+
await this.send("low_balance", "Low Balance Alert", `Current: ${balanceEth} ETH\nThreshold: ${thresholdEth} ETH`);
|
|
139
235
|
}
|
|
140
236
|
async notifyStarted(walletAddress, subsystems) {
|
|
141
|
-
await this.send("daemon_started",
|
|
237
|
+
await this.send("daemon_started", "ARC-402 Daemon Started", `Wallet: ${walletAddress}\nSubsystems: ${subsystems.join(", ")}`);
|
|
142
238
|
}
|
|
143
239
|
async notifyStopped() {
|
|
144
|
-
await this.send("daemon_stopped",
|
|
240
|
+
await this.send("daemon_stopped", "ARC-402 Daemon Stopped", "");
|
|
145
241
|
}
|
|
146
242
|
}
|
|
147
243
|
exports.Notifier = Notifier;
|
|
244
|
+
// ─── Factory ──────────────────────────────────────────────────────────────────
|
|
245
|
+
function buildNotifier(config) {
|
|
246
|
+
const notif = config.notifications;
|
|
247
|
+
const channels = [];
|
|
248
|
+
if (notif.telegram_bot_token && notif.telegram_chat_id) {
|
|
249
|
+
channels.push(new TelegramChannel(notif.telegram_bot_token, notif.telegram_chat_id));
|
|
250
|
+
}
|
|
251
|
+
if (notif.discord?.webhook_url) {
|
|
252
|
+
channels.push(new DiscordChannel(notif.discord.webhook_url));
|
|
253
|
+
}
|
|
254
|
+
if (notif.webhook?.url) {
|
|
255
|
+
channels.push(new WebhookChannel(notif.webhook.url, notif.webhook.headers ?? {}));
|
|
256
|
+
}
|
|
257
|
+
if (notif.email?.smtp_host && notif.email?.smtp_user && notif.email?.to) {
|
|
258
|
+
channels.push(new EmailChannel({
|
|
259
|
+
smtpHost: notif.email.smtp_host,
|
|
260
|
+
smtpPort: notif.email.smtp_port,
|
|
261
|
+
smtpUser: notif.email.smtp_user,
|
|
262
|
+
smtpPass: notif.email.smtp_pass,
|
|
263
|
+
to: notif.email.to,
|
|
264
|
+
}));
|
|
265
|
+
}
|
|
266
|
+
return new Notifier(channels, {
|
|
267
|
+
hire_request: notif.notify_on_hire_request,
|
|
268
|
+
hire_accepted: notif.notify_on_hire_accepted,
|
|
269
|
+
hire_rejected: notif.notify_on_hire_rejected,
|
|
270
|
+
delivery: notif.notify_on_delivery,
|
|
271
|
+
dispute: notif.notify_on_dispute,
|
|
272
|
+
channel_challenge: notif.notify_on_channel_challenge,
|
|
273
|
+
low_balance: notif.notify_on_low_balance,
|
|
274
|
+
});
|
|
275
|
+
}
|
|
148
276
|
//# sourceMappingURL=notify.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"notify.js","sourceRoot":"","sources":["../../src/daemon/notify.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"notify.js","sourceRoot":"","sources":["../../src/daemon/notify.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
package/src/commands/daemon.ts
CHANGED
|
@@ -19,7 +19,9 @@ import {
|
|
|
19
19
|
DAEMON_SOCK,
|
|
20
20
|
DAEMON_TOML,
|
|
21
21
|
TEMPLATE_DAEMON_TOML,
|
|
22
|
+
loadDaemonConfig,
|
|
22
23
|
} from "../daemon/config";
|
|
24
|
+
import { buildNotifier } from "../daemon/notify";
|
|
23
25
|
import {
|
|
24
26
|
buildOpenShellSecretExports,
|
|
25
27
|
buildOpenShellSshConfig,
|
|
@@ -943,6 +945,77 @@ export function registerDaemonCommands(program: Command): void {
|
|
|
943
945
|
console.log(" 5. Start the OpenShell-owned ARC-402 runtime: arc402 daemon start");
|
|
944
946
|
});
|
|
945
947
|
|
|
948
|
+
// ── daemon notifications ──────────────────────────────────────────────────────
|
|
949
|
+
const notifications = daemon
|
|
950
|
+
.command("notifications")
|
|
951
|
+
.description("Show or test configured notification channels");
|
|
952
|
+
|
|
953
|
+
notifications
|
|
954
|
+
.command("show")
|
|
955
|
+
.description("Show all configured notification channels")
|
|
956
|
+
.action(() => {
|
|
957
|
+
if (!fs.existsSync(DAEMON_TOML)) {
|
|
958
|
+
console.error("daemon.toml not found. Run `arc402 daemon init` first.");
|
|
959
|
+
process.exit(1);
|
|
960
|
+
}
|
|
961
|
+
const cfg = loadDaemonConfig();
|
|
962
|
+
const notif = cfg.notifications;
|
|
963
|
+
const channels: string[] = [];
|
|
964
|
+
|
|
965
|
+
if (notif.telegram_bot_token && notif.telegram_chat_id) {
|
|
966
|
+
channels.push(`telegram chat_id=${notif.telegram_chat_id}`);
|
|
967
|
+
}
|
|
968
|
+
if (notif.discord?.webhook_url) {
|
|
969
|
+
const u = new URL(notif.discord.webhook_url);
|
|
970
|
+
channels.push(`discord ${u.hostname}${u.pathname.slice(0, 30)}...`);
|
|
971
|
+
}
|
|
972
|
+
if (notif.webhook?.url) {
|
|
973
|
+
channels.push(`webhook ${notif.webhook.url}`);
|
|
974
|
+
}
|
|
975
|
+
if (notif.email?.smtp_host && notif.email?.to) {
|
|
976
|
+
channels.push(`email ${notif.email.smtp_host}:${notif.email.smtp_port} → ${notif.email.to}`);
|
|
977
|
+
}
|
|
978
|
+
|
|
979
|
+
if (channels.length === 0) {
|
|
980
|
+
console.log("No notification channels configured.");
|
|
981
|
+
console.log("Edit ~/.arc402/daemon.toml to add Telegram, Discord, webhook, or email.");
|
|
982
|
+
} else {
|
|
983
|
+
console.log(`Configured channels (${channels.length}):`);
|
|
984
|
+
for (const ch of channels) console.log(` ${ch}`);
|
|
985
|
+
}
|
|
986
|
+
});
|
|
987
|
+
|
|
988
|
+
notifications
|
|
989
|
+
.command("test")
|
|
990
|
+
.description("Send a test message to all configured channels")
|
|
991
|
+
.action(async () => {
|
|
992
|
+
if (!fs.existsSync(DAEMON_TOML)) {
|
|
993
|
+
console.error("daemon.toml not found. Run `arc402 daemon init` first.");
|
|
994
|
+
process.exit(1);
|
|
995
|
+
}
|
|
996
|
+
const cfg = loadDaemonConfig();
|
|
997
|
+
const notifier = buildNotifier(cfg);
|
|
998
|
+
if (!notifier.isEnabled()) {
|
|
999
|
+
console.log("No notification channels configured. Nothing to test.");
|
|
1000
|
+
process.exit(0);
|
|
1001
|
+
}
|
|
1002
|
+
console.log("Sending test notification to all channels...");
|
|
1003
|
+
try {
|
|
1004
|
+
await notifier.send("daemon_started", "ARC-402 Test Notification",
|
|
1005
|
+
"This is a test message from arc402 daemon notifications test."
|
|
1006
|
+
);
|
|
1007
|
+
console.log("Test notification sent successfully.");
|
|
1008
|
+
} catch (err) {
|
|
1009
|
+
console.error(`Test notification failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
1010
|
+
process.exit(1);
|
|
1011
|
+
}
|
|
1012
|
+
});
|
|
1013
|
+
|
|
1014
|
+
// Default action: show (arc402 daemon notifications → arc402 daemon notifications show)
|
|
1015
|
+
notifications.action(() => {
|
|
1016
|
+
notifications.help();
|
|
1017
|
+
});
|
|
1018
|
+
|
|
946
1019
|
// ── daemon channel-watch ─────────────────────────────────────────────────────
|
|
947
1020
|
daemon
|
|
948
1021
|
.command("channel-watch")
|
package/src/daemon/config.ts
CHANGED
|
@@ -68,6 +68,9 @@ export interface DaemonConfig {
|
|
|
68
68
|
notify_on_channel_challenge: boolean;
|
|
69
69
|
notify_on_low_balance: boolean;
|
|
70
70
|
low_balance_threshold_eth: string;
|
|
71
|
+
discord: { webhook_url: string };
|
|
72
|
+
webhook: { url: string; headers: Record<string, string> };
|
|
73
|
+
email: { smtp_host: string; smtp_port: number; smtp_user: string; smtp_pass: string; to: string };
|
|
71
74
|
};
|
|
72
75
|
work: {
|
|
73
76
|
handler: "exec" | "http" | "noop";
|
|
@@ -114,6 +117,9 @@ function withDefaults(raw: Record<string, unknown>): DaemonConfig {
|
|
|
114
117
|
const wt = (raw.watchtower as Record<string, unknown>) ?? {};
|
|
115
118
|
const p = (raw.policy as Record<string, unknown>) ?? {};
|
|
116
119
|
const notif = (raw.notifications as Record<string, unknown>) ?? {};
|
|
120
|
+
const notifDiscord = (notif.discord as Record<string, unknown>) ?? {};
|
|
121
|
+
const notifWebhook = (notif.webhook as Record<string, unknown>) ?? {};
|
|
122
|
+
const notifEmail = (notif.email as Record<string, unknown>) ?? {};
|
|
117
123
|
const work = (raw.work as Record<string, unknown>) ?? {};
|
|
118
124
|
|
|
119
125
|
return {
|
|
@@ -169,6 +175,18 @@ function withDefaults(raw: Record<string, unknown>): DaemonConfig {
|
|
|
169
175
|
notify_on_channel_challenge: bool(notif.notify_on_channel_challenge, true),
|
|
170
176
|
notify_on_low_balance: bool(notif.notify_on_low_balance, true),
|
|
171
177
|
low_balance_threshold_eth: str(notif.low_balance_threshold_eth, "0.005"),
|
|
178
|
+
discord: { webhook_url: str(notifDiscord.webhook_url) },
|
|
179
|
+
webhook: {
|
|
180
|
+
url: str(notifWebhook.url),
|
|
181
|
+
headers: (notifWebhook.headers as Record<string, string>) ?? {},
|
|
182
|
+
},
|
|
183
|
+
email: {
|
|
184
|
+
smtp_host: str(notifEmail.smtp_host),
|
|
185
|
+
smtp_port: num(notifEmail.smtp_port, 587),
|
|
186
|
+
smtp_user: str(notifEmail.smtp_user),
|
|
187
|
+
smtp_pass: str(notifEmail.smtp_pass, "env:SMTP_PASS"),
|
|
188
|
+
to: str(notifEmail.to),
|
|
189
|
+
},
|
|
172
190
|
},
|
|
173
191
|
work: {
|
|
174
192
|
handler: (str(work.handler, "noop")) as "exec" | "http" | "noop",
|
|
@@ -213,6 +231,9 @@ export function loadDaemonConfig(configPath = DAEMON_TOML): DaemonConfig {
|
|
|
213
231
|
// Resolve optional env: values silently (missing = disabled feature)
|
|
214
232
|
config.notifications.telegram_bot_token = tryResolveEnvValue(config.notifications.telegram_bot_token);
|
|
215
233
|
config.notifications.telegram_chat_id = tryResolveEnvValue(config.notifications.telegram_chat_id);
|
|
234
|
+
config.notifications.discord.webhook_url = tryResolveEnvValue(config.notifications.discord.webhook_url);
|
|
235
|
+
config.notifications.webhook.url = tryResolveEnvValue(config.notifications.webhook.url);
|
|
236
|
+
config.notifications.email.smtp_pass = tryResolveEnvValue(config.notifications.email.smtp_pass);
|
|
216
237
|
config.work.http_auth_token = tryResolveEnvValue(config.work.http_auth_token);
|
|
217
238
|
|
|
218
239
|
return config;
|
|
@@ -300,6 +321,20 @@ notify_on_channel_challenge = true # Notify when watchtower submits a channel c
|
|
|
300
321
|
notify_on_low_balance = false # Disabled by default — enable if you want balance alerts
|
|
301
322
|
low_balance_threshold_eth = "0.005" # Balance alert threshold
|
|
302
323
|
|
|
324
|
+
[notifications.discord]
|
|
325
|
+
webhook_url = "" # Discord channel webhook URL (leave empty to disable)
|
|
326
|
+
|
|
327
|
+
[notifications.webhook]
|
|
328
|
+
url = "" # POST JSON {title, body, timestamp} to this URL (leave empty to disable)
|
|
329
|
+
# headers = { Authorization = "Bearer ..." } # Optional headers
|
|
330
|
+
|
|
331
|
+
[notifications.email]
|
|
332
|
+
smtp_host = "" # SMTP server hostname (leave empty to disable)
|
|
333
|
+
smtp_port = 587
|
|
334
|
+
smtp_user = "" # SMTP login / from address
|
|
335
|
+
smtp_pass = "env:SMTP_PASS" # Load from env, not hardcoded
|
|
336
|
+
to = "" # Recipient address
|
|
337
|
+
|
|
303
338
|
[work]
|
|
304
339
|
handler = "noop" # exec | http | noop
|
|
305
340
|
exec_command = "" # called with agreementId and spec as args (exec mode)
|
package/src/daemon/index.ts
CHANGED
|
@@ -27,7 +27,7 @@ import {
|
|
|
27
27
|
type DaemonConfig,
|
|
28
28
|
} from "./config";
|
|
29
29
|
import { verifyWallet, getWalletBalance } from "./wallet-monitor";
|
|
30
|
-
import {
|
|
30
|
+
import { buildNotifier } from "./notify";
|
|
31
31
|
import { HireListener } from "./hire-listener";
|
|
32
32
|
import { UserOpsManager, buildAcceptCalldata } from "./userops";
|
|
33
33
|
import { generateReceipt, extractLearnings, createJobDirectory, cleanJobDirectory } from "./job-lifecycle";
|
|
@@ -519,19 +519,7 @@ export async function runDaemon(foreground = false): Promise<void> {
|
|
|
519
519
|
}
|
|
520
520
|
|
|
521
521
|
// ── Setup notifier ───────────────────────────────────────────────────────
|
|
522
|
-
const notifier =
|
|
523
|
-
config.notifications.telegram_bot_token,
|
|
524
|
-
config.notifications.telegram_chat_id,
|
|
525
|
-
{
|
|
526
|
-
hire_request: config.notifications.notify_on_hire_request,
|
|
527
|
-
hire_accepted: config.notifications.notify_on_hire_accepted,
|
|
528
|
-
hire_rejected: config.notifications.notify_on_hire_rejected,
|
|
529
|
-
delivery: config.notifications.notify_on_delivery,
|
|
530
|
-
dispute: config.notifications.notify_on_dispute,
|
|
531
|
-
channel_challenge: config.notifications.notify_on_channel_challenge,
|
|
532
|
-
low_balance: config.notifications.notify_on_low_balance,
|
|
533
|
-
}
|
|
534
|
-
);
|
|
522
|
+
const notifier = buildNotifier(config);
|
|
535
523
|
|
|
536
524
|
// ── Step 10: Start relay listener ───────────────────────────────────────
|
|
537
525
|
const hireListener = new HireListener(config, db, notifier, config.wallet.contract_address);
|