arc402-cli 0.7.0 → 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/backup.d.ts +3 -0
- package/dist/commands/backup.d.ts.map +1 -0
- package/dist/commands/backup.js +106 -0
- package/dist/commands/backup.js.map +1 -0
- 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/commands/discover.d.ts.map +1 -1
- package/dist/commands/discover.js +60 -15
- package/dist/commands/discover.js.map +1 -1
- package/dist/commands/wallet.d.ts.map +1 -1
- package/dist/commands/wallet.js +136 -52
- package/dist/commands/wallet.js.map +1 -1
- package/dist/commands/watch.d.ts.map +1 -1
- package/dist/commands/watch.js +146 -9
- package/dist/commands/watch.js.map +1 -1
- package/dist/config.d.ts +8 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +25 -1
- package/dist/config.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 +66 -21
- 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/dist/endpoint-notify.d.ts +2 -1
- package/dist/endpoint-notify.d.ts.map +1 -1
- package/dist/endpoint-notify.js +12 -3
- package/dist/endpoint-notify.js.map +1 -1
- package/dist/index.js +26 -0
- package/dist/index.js.map +1 -1
- package/dist/program.d.ts.map +1 -1
- package/dist/program.js +2 -0
- package/dist/program.js.map +1 -1
- package/package.json +1 -1
- package/src/commands/backup.ts +117 -0
- package/src/commands/daemon.ts +73 -0
- package/src/commands/discover.ts +74 -21
- package/src/commands/wallet.ts +137 -51
- package/src/commands/watch.ts +207 -10
- package/src/config.ts +39 -1
- package/src/daemon/config.ts +35 -0
- package/src/daemon/index.ts +65 -26
- package/src/daemon/notify.ts +199 -59
- package/src/endpoint-notify.ts +13 -3
- package/src/index.ts +26 -0
- package/src/program.ts +2 -0
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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"}
|
|
@@ -20,6 +20,7 @@ export declare function resolveAgentEndpoint(address: string, provider: ethers.P
|
|
|
20
20
|
* POSTs JSON payload to {endpoint}{path}. Returns true on success.
|
|
21
21
|
* Never throws — logs a warning on failure.
|
|
22
22
|
* Validates endpoint URL for SSRF before connecting.
|
|
23
|
+
* If signingKey is provided, signs the payload and adds X-ARC402-Signature / X-ARC402-Signer headers.
|
|
23
24
|
*/
|
|
24
|
-
export declare function notifyAgent(endpoint: string, path: string, payload: Record<string, unknown
|
|
25
|
+
export declare function notifyAgent(endpoint: string, path: string, payload: Record<string, unknown>, signingKey?: string): Promise<boolean>;
|
|
25
26
|
//# sourceMappingURL=endpoint-notify.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"endpoint-notify.d.ts","sourceRoot":"","sources":["../src/endpoint-notify.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAIhC,eAAO,MAAM,wBAAwB,+CAA+C,CAAC;AA8BrF;;;;GAIG;AACH,wBAAsB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CA8BzE;AAED;;;GAGG;AACH,wBAAsB,oBAAoB,CACxC,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,CAAC,QAAQ,EACzB,eAAe,SAA2B,GACzC,OAAO,CAAC,MAAM,CAAC,CAIjB;AAED
|
|
1
|
+
{"version":3,"file":"endpoint-notify.d.ts","sourceRoot":"","sources":["../src/endpoint-notify.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAIhC,eAAO,MAAM,wBAAwB,+CAA+C,CAAC;AA8BrF;;;;GAIG;AACH,wBAAsB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CA8BzE;AAED;;;GAGG;AACH,wBAAsB,oBAAoB,CACxC,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,CAAC,QAAQ,EACzB,eAAe,SAA2B,GACzC,OAAO,CAAC,MAAM,CAAC,CAIjB;AAED;;;;;GAKG;AACH,wBAAsB,WAAW,CAC/B,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAChC,UAAU,CAAC,EAAE,MAAM,GAClB,OAAO,CAAC,OAAO,CAAC,CA2BlB"}
|
package/dist/endpoint-notify.js
CHANGED
|
@@ -120,8 +120,9 @@ async function resolveAgentEndpoint(address, provider, registryAddress = exports
|
|
|
120
120
|
* POSTs JSON payload to {endpoint}{path}. Returns true on success.
|
|
121
121
|
* Never throws — logs a warning on failure.
|
|
122
122
|
* Validates endpoint URL for SSRF before connecting.
|
|
123
|
+
* If signingKey is provided, signs the payload and adds X-ARC402-Signature / X-ARC402-Signer headers.
|
|
123
124
|
*/
|
|
124
|
-
async function notifyAgent(endpoint, path, payload) {
|
|
125
|
+
async function notifyAgent(endpoint, path, payload, signingKey) {
|
|
125
126
|
if (!endpoint)
|
|
126
127
|
return false;
|
|
127
128
|
try {
|
|
@@ -132,10 +133,18 @@ async function notifyAgent(endpoint, path, payload) {
|
|
|
132
133
|
return false;
|
|
133
134
|
}
|
|
134
135
|
try {
|
|
136
|
+
const body = JSON.stringify(payload);
|
|
137
|
+
const headers = { "Content-Type": "application/json" };
|
|
138
|
+
if (signingKey) {
|
|
139
|
+
const wallet = new ethers_1.ethers.Wallet(signingKey);
|
|
140
|
+
const signature = await wallet.signMessage(body);
|
|
141
|
+
headers["X-ARC402-Signature"] = signature;
|
|
142
|
+
headers["X-ARC402-Signer"] = wallet.address;
|
|
143
|
+
}
|
|
135
144
|
const res = await fetch(`${endpoint}${path}`, {
|
|
136
145
|
method: "POST",
|
|
137
|
-
headers
|
|
138
|
-
body
|
|
146
|
+
headers,
|
|
147
|
+
body,
|
|
139
148
|
});
|
|
140
149
|
return res.ok;
|
|
141
150
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"endpoint-notify.js","sourceRoot":"","sources":["../src/endpoint-notify.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4CA,kDA8BC;AAMD,oDAQC;
|
|
1
|
+
{"version":3,"file":"endpoint-notify.js","sourceRoot":"","sources":["../src/endpoint-notify.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4CA,kDA8BC;AAMD,oDAQC;AAQD,kCAgCC;AAhID;;;;GAIG;AACH,mCAAgC;AAChC,iCAA4C;AAC5C,kDAAoC;AAEvB,QAAA,wBAAwB,GAAG,4CAA4C,CAAC;AAErF,iFAAiF;AAEjF,MAAM,cAAc,GAAG;IACrB,aAAa;IACb,CAAC,CAAS,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC,KAAK,EAAE;IAChC,gBAAgB;IAChB,CAAC,CAAS,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC,KAAK,GAAG,IAAI,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC,GAAG,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC,GAAG,IAAI,CAAC,IAAI,EAAE;IAC3F,iBAAiB;IACjB,CAAC,CAAS,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC,KAAK,GAAG,IAAI,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC,GAAG,IAAI,CAAC,KAAK,GAAG;CACjE,CAAC;AAEF,SAAS,OAAO,CAAC,EAAU;IACzB,OAAO,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,GAAG,QAAQ,CAAC,KAAK,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC;AACzF,CAAC;AAED,SAAS,WAAW,CAAC,EAAU;IAC7B,6CAA6C;IAC7C,IAAI,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;QACrB,OAAO,EAAE,KAAK,KAAK,CAAC;IACtB,CAAC;IACD,MAAM,CAAC,GAAG,OAAO,CAAC,EAAE,CAAC,CAAC;IACtB,uBAAuB;IACvB,IAAI,CAAC,CAAC,KAAK,EAAE,CAAC,KAAK,GAAG;QAAE,OAAO,IAAI,CAAC;IACpC,oEAAoE;IACpE,IAAI,CAAC,CAAC,KAAK,EAAE,CAAC,KAAK,GAAG,IAAI,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC,GAAG,IAAI,CAAC,KAAK,GAAG;QAAE,OAAO,IAAI,CAAC;IACnE,OAAO,cAAc,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;AAC5C,CAAC;AAED;;;;GAIG;AACI,KAAK,UAAU,mBAAmB,CAAC,QAAgB;IACxD,IAAI,MAAW,CAAC;IAChB,IAAI,CAAC;QACH,MAAM,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC,CAAC;IAC7B,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,KAAK,CAAC,yBAAyB,QAAQ,EAAE,CAAC,CAAC;IACvD,CAAC;IAED,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,GAAG,MAAM,CAAC;IACtC,MAAM,WAAW,GAAG,QAAQ,KAAK,WAAW,IAAI,QAAQ,KAAK,WAAW,IAAI,QAAQ,KAAK,KAAK,CAAC;IAE/F,IAAI,QAAQ,KAAK,QAAQ,IAAI,CAAC,CAAC,QAAQ,KAAK,OAAO,IAAI,WAAW,CAAC,EAAE,CAAC;QACpE,MAAM,IAAI,KAAK,CAAC,yDAAyD,QAAQ,EAAE,CAAC,CAAC;IACvF,CAAC;IAED,0CAA0C;IAC1C,IAAI,CAAC,WAAW,EAAE,CAAC;QACjB,IAAI,SAAmB,CAAC;QACxB,IAAI,CAAC;YACH,SAAS,GAAG,MAAM,GAAG,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;QAC1C,CAAC;QAAC,MAAM,CAAC;YACP,0DAA0D;YAC1D,OAAO;QACT,CAAC;QACD,KAAK,MAAM,IAAI,IAAI,SAAS,EAAE,CAAC;YAC7B,IAAI,WAAW,CAAC,IAAI,CAAC,EAAE,CAAC;gBACtB,MAAM,IAAI,KAAK,CAAC,+CAA+C,IAAI,0BAA0B,CAAC,CAAC;YACjG,CAAC;QACH,CAAC;IACH,CAAC;AACH,CAAC;AAED;;;GAGG;AACI,KAAK,UAAU,oBAAoB,CACxC,OAAe,EACf,QAAyB,EACzB,eAAe,GAAG,gCAAwB;IAE1C,MAAM,QAAQ,GAAG,IAAI,eAAM,CAAC,QAAQ,CAAC,eAAe,EAAE,yBAAkB,EAAE,QAAQ,CAAC,CAAC;IACpF,MAAM,SAAS,GAAG,MAAM,QAAQ,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;IACnD,OAAQ,SAAS,CAAC,QAAmB,IAAI,EAAE,CAAC;AAC9C,CAAC;AAED;;;;;GAKG;AACI,KAAK,UAAU,WAAW,CAC/B,QAAgB,EAChB,IAAY,EACZ,OAAgC,EAChC,UAAmB;IAEnB,IAAI,CAAC,QAAQ;QAAE,OAAO,KAAK,CAAC;IAC5B,IAAI,CAAC;QACH,MAAM,mBAAmB,CAAC,QAAQ,CAAC,CAAC;IACtC,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,CAAC,IAAI,CAAC,qCAAqC,QAAQ,GAAG,IAAI,MAAM,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAC3H,OAAO,KAAK,CAAC;IACf,CAAC;IACD,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;QACrC,MAAM,OAAO,GAA2B,EAAE,cAAc,EAAE,kBAAkB,EAAE,CAAC;QAC/E,IAAI,UAAU,EAAE,CAAC;YACf,MAAM,MAAM,GAAG,IAAI,eAAM,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;YAC7C,MAAM,SAAS,GAAG,MAAM,MAAM,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;YACjD,OAAO,CAAC,oBAAoB,CAAC,GAAG,SAAS,CAAC;YAC1C,OAAO,CAAC,iBAAiB,CAAC,GAAG,MAAM,CAAC,OAAO,CAAC;QAC9C,CAAC;QACD,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,QAAQ,GAAG,IAAI,EAAE,EAAE;YAC5C,MAAM,EAAE,MAAM;YACd,OAAO;YACP,IAAI;SACL,CAAC,CAAC;QACH,OAAO,GAAG,CAAC,EAAE,CAAC;IAChB,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,CAAC,IAAI,CAAC,oCAAoC,QAAQ,GAAG,IAAI,MAAM,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAC1H,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -3,6 +3,29 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
4
|
const program_1 = require("./program");
|
|
5
5
|
const repl_1 = require("./repl");
|
|
6
|
+
const config_1 = require("./config");
|
|
7
|
+
// ── Upgrade safety check ────────────────────────────────────────────────────
|
|
8
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
9
|
+
const currentVersion = require("../package.json").version;
|
|
10
|
+
function checkUpgrade() {
|
|
11
|
+
if (!(0, config_1.configExists)())
|
|
12
|
+
return;
|
|
13
|
+
try {
|
|
14
|
+
const config = (0, config_1.loadConfig)();
|
|
15
|
+
const prev = config.lastCliVersion;
|
|
16
|
+
if (prev && prev !== currentVersion) {
|
|
17
|
+
// Compare semver loosely — just print if different
|
|
18
|
+
console.log(`◈ Upgraded from ${prev} → ${currentVersion}`);
|
|
19
|
+
}
|
|
20
|
+
if (config.lastCliVersion !== currentVersion) {
|
|
21
|
+
// Never overwrite existing fields — only update lastCliVersion
|
|
22
|
+
(0, config_1.saveConfig)({ ...config, lastCliVersion: currentVersion });
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
// Never crash on upgrade check
|
|
27
|
+
}
|
|
28
|
+
}
|
|
6
29
|
const printMode = process.argv.includes("--print");
|
|
7
30
|
if (printMode) {
|
|
8
31
|
// --print mode: skip REPL entirely, suppress ANSI/spinners, run command, exit.
|
|
@@ -11,6 +34,7 @@ if (printMode) {
|
|
|
11
34
|
process.env["NO_COLOR"] = "1";
|
|
12
35
|
process.env["FORCE_COLOR"] = "0";
|
|
13
36
|
process.env["ARC402_PRINT"] = "1";
|
|
37
|
+
checkUpgrade();
|
|
14
38
|
const program = (0, program_1.createProgram)();
|
|
15
39
|
void program.parseAsync(process.argv).then(() => process.exit(0)).catch((e) => {
|
|
16
40
|
console.error(e instanceof Error ? e.message : String(e));
|
|
@@ -19,10 +43,12 @@ if (printMode) {
|
|
|
19
43
|
}
|
|
20
44
|
else if (process.argv.length <= 2) {
|
|
21
45
|
// No subcommand — enter interactive REPL
|
|
46
|
+
checkUpgrade();
|
|
22
47
|
void (0, repl_1.startREPL)();
|
|
23
48
|
}
|
|
24
49
|
else {
|
|
25
50
|
// One-shot mode — arc402 wallet deploy still works as usual
|
|
51
|
+
checkUpgrade();
|
|
26
52
|
const program = (0, program_1.createProgram)();
|
|
27
53
|
program.parse(process.argv);
|
|
28
54
|
}
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AACA,uCAA0C;AAC1C,iCAAmC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AACA,uCAA0C;AAC1C,iCAAmC;AACnC,qCAAgE;AAEhE,+EAA+E;AAC/E,8DAA8D;AAC9D,MAAM,cAAc,GAAY,OAAO,CAAC,iBAAiB,CAAyB,CAAC,OAAO,CAAC;AAE3F,SAAS,YAAY;IACnB,IAAI,CAAC,IAAA,qBAAY,GAAE;QAAE,OAAO;IAC5B,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAA,mBAAU,GAAE,CAAC;QAC5B,MAAM,IAAI,GAAG,MAAM,CAAC,cAAc,CAAC;QACnC,IAAI,IAAI,IAAI,IAAI,KAAK,cAAc,EAAE,CAAC;YACpC,mDAAmD;YACnD,OAAO,CAAC,GAAG,CAAC,mBAAmB,IAAI,MAAM,cAAc,EAAE,CAAC,CAAC;QAC7D,CAAC;QACD,IAAI,MAAM,CAAC,cAAc,KAAK,cAAc,EAAE,CAAC;YAC7C,+DAA+D;YAC/D,IAAA,mBAAU,EAAC,EAAE,GAAG,MAAM,EAAE,cAAc,EAAE,cAAc,EAAE,CAAC,CAAC;QAC5D,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,+BAA+B;IACjC,CAAC;AACH,CAAC;AAED,MAAM,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC;AAEnD,IAAI,SAAS,EAAE,CAAC;IACd,+EAA+E;IAC/E,2DAA2D;IAC3D,OAAO,CAAC,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,SAAS,CAAC,CAAC;IAC3D,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,GAAG,GAAG,CAAC;IAC9B,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC,GAAG,GAAG,CAAC;IACjC,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,GAAG,GAAG,CAAC;IAClC,YAAY,EAAE,CAAC;IACf,MAAM,OAAO,GAAG,IAAA,uBAAa,GAAE,CAAC;IAChC,KAAK,OAAO,CAAC,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAU,EAAE,EAAE;QACrF,OAAO,CAAC,KAAK,CAAC,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;QAC1D,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC,CAAC,CAAC;AACL,CAAC;KAAM,IAAI,OAAO,CAAC,IAAI,CAAC,MAAM,IAAI,CAAC,EAAE,CAAC;IACpC,yCAAyC;IACzC,YAAY,EAAE,CAAC;IACf,KAAK,IAAA,gBAAS,GAAE,CAAC;AACnB,CAAC;KAAM,CAAC;IACN,4DAA4D;IAC5D,YAAY,EAAE,CAAC;IACf,MAAM,OAAO,GAAG,IAAA,uBAAa,GAAE,CAAC;IAChC,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;AAC9B,CAAC"}
|
package/dist/program.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"program.d.ts","sourceRoot":"","sources":["../src/program.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"program.d.ts","sourceRoot":"","sources":["../src/program.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAsCpC,wBAAgB,aAAa,IAAI,OAAO,CAgDvC"}
|
package/dist/program.js
CHANGED
|
@@ -38,6 +38,7 @@ const migrate_1 = require("./commands/migrate");
|
|
|
38
38
|
const feed_1 = require("./commands/feed");
|
|
39
39
|
const arena_1 = require("./commands/arena");
|
|
40
40
|
const watch_1 = require("./commands/watch");
|
|
41
|
+
const backup_1 = require("./commands/backup");
|
|
41
42
|
const reputation_js_1 = __importDefault(require("./commands/reputation.js"));
|
|
42
43
|
const policy_js_1 = __importDefault(require("./commands/policy.js"));
|
|
43
44
|
function createProgram() {
|
|
@@ -80,6 +81,7 @@ function createProgram() {
|
|
|
80
81
|
(0, feed_1.registerFeedCommand)(program);
|
|
81
82
|
(0, arena_1.registerArenaCommands)(program);
|
|
82
83
|
(0, watch_1.registerWatchCommand)(program);
|
|
84
|
+
(0, backup_1.registerBackupCommand)(program);
|
|
83
85
|
program.addCommand(reputation_js_1.default);
|
|
84
86
|
program.addCommand(policy_js_1.default);
|
|
85
87
|
return program;
|
package/dist/program.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"program.js","sourceRoot":"","sources":["../src/program.ts"],"names":[],"mappings":";;;;;
|
|
1
|
+
{"version":3,"file":"program.js","sourceRoot":"","sources":["../src/program.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import { execSync } from "child_process";
|
|
3
|
+
import fs from "fs";
|
|
4
|
+
import path from "path";
|
|
5
|
+
import os from "os";
|
|
6
|
+
import { c } from "../ui/colors";
|
|
7
|
+
|
|
8
|
+
const ARC402_DIR = path.join(os.homedir(), ".arc402");
|
|
9
|
+
|
|
10
|
+
// Files/patterns to exclude from backup
|
|
11
|
+
const EXCLUDE_PATTERNS = [
|
|
12
|
+
"daemon.db",
|
|
13
|
+
"daemon.log",
|
|
14
|
+
"daemon.pid",
|
|
15
|
+
"daemon.sock",
|
|
16
|
+
"repl_history",
|
|
17
|
+
];
|
|
18
|
+
|
|
19
|
+
function todayStr(): string {
|
|
20
|
+
const d = new Date();
|
|
21
|
+
const y = d.getFullYear();
|
|
22
|
+
const m = (d.getMonth() + 1).toString().padStart(2, "0");
|
|
23
|
+
const day = d.getDate().toString().padStart(2, "0");
|
|
24
|
+
return `${y}-${m}-${day}`;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function registerBackupCommand(program: Command): void {
|
|
28
|
+
// ── backup ─────────────────────────────────────────────────────────────────
|
|
29
|
+
program
|
|
30
|
+
.command("backup")
|
|
31
|
+
.description("Backup ~/.arc402/ config, keys, and wallet storage to a tar.gz archive")
|
|
32
|
+
.option("--output <path>", "Output file path (default: arc402-backup-YYYY-MM-DD.tar.gz)")
|
|
33
|
+
.action((opts: { output?: string }) => {
|
|
34
|
+
if (!fs.existsSync(ARC402_DIR)) {
|
|
35
|
+
console.error(` ${c.failure} No ~/.arc402/ directory found. Nothing to back up.`);
|
|
36
|
+
process.exit(1);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const outFile = opts.output ?? `arc402-backup-${todayStr()}.tar.gz`;
|
|
40
|
+
const absOut = path.isAbsolute(outFile) ? outFile : path.join(process.cwd(), outFile);
|
|
41
|
+
|
|
42
|
+
// Build --exclude flags
|
|
43
|
+
const excludeFlags = EXCLUDE_PATTERNS.map((p) => `--exclude='.arc402/${p}'`).join(" ");
|
|
44
|
+
|
|
45
|
+
try {
|
|
46
|
+
execSync(
|
|
47
|
+
`tar -czf "${absOut}" ${excludeFlags} -C "${os.homedir()}" .arc402`,
|
|
48
|
+
{ stdio: "pipe" }
|
|
49
|
+
);
|
|
50
|
+
} catch (err) {
|
|
51
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
52
|
+
console.error(` ${c.failure} Backup failed: ${msg}`);
|
|
53
|
+
process.exit(1);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Verify the file was created
|
|
57
|
+
if (!fs.existsSync(absOut)) {
|
|
58
|
+
console.error(` ${c.failure} Archive not found after tar completed.`);
|
|
59
|
+
process.exit(1);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const sizeBytes = fs.statSync(absOut).size;
|
|
63
|
+
const sizeStr = sizeBytes < 1024
|
|
64
|
+
? `${sizeBytes} B`
|
|
65
|
+
: sizeBytes < 1024 * 1024
|
|
66
|
+
? `${(sizeBytes / 1024).toFixed(1)} KB`
|
|
67
|
+
: `${(sizeBytes / (1024 * 1024)).toFixed(2)} MB`;
|
|
68
|
+
|
|
69
|
+
console.log(` ${c.success} Backup saved to ${c.white(path.basename(absOut))} ${c.dim(`(${sizeStr})`)}`);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
// ── restore ────────────────────────────────────────────────────────────────
|
|
73
|
+
program
|
|
74
|
+
.command("restore")
|
|
75
|
+
.description("Restore ~/.arc402/ from a backup archive")
|
|
76
|
+
.argument("<archive>", "Path to arc402-backup-*.tar.gz")
|
|
77
|
+
.action((archive: string) => {
|
|
78
|
+
const absArchive = path.isAbsolute(archive) ? archive : path.join(process.cwd(), archive);
|
|
79
|
+
|
|
80
|
+
if (!fs.existsSync(absArchive)) {
|
|
81
|
+
console.error(` ${c.failure} Archive not found: ${absArchive}`);
|
|
82
|
+
process.exit(1);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Warn if ~/.arc402/ already exists
|
|
86
|
+
if (fs.existsSync(ARC402_DIR)) {
|
|
87
|
+
const existing = fs.readdirSync(ARC402_DIR);
|
|
88
|
+
if (existing.length > 0) {
|
|
89
|
+
console.warn(` ${c.warning} Existing ~/.arc402/ has ${existing.length} file(s) — merging (existing files take precedence for conflicts).`);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
try {
|
|
94
|
+
// Extract to home dir — tar archive has .arc402/ as the root entry
|
|
95
|
+
execSync(`tar -xzf "${absArchive}" -C "${os.homedir()}"`, { stdio: "pipe" });
|
|
96
|
+
} catch (err) {
|
|
97
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
98
|
+
console.error(` ${c.failure} Restore failed: ${msg}`);
|
|
99
|
+
process.exit(1);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Verify config.json is present
|
|
103
|
+
const configPath = path.join(ARC402_DIR, "config.json");
|
|
104
|
+
if (!fs.existsSync(configPath)) {
|
|
105
|
+
console.error(` ${c.failure} config.json not found after restore — archive may be incomplete.`);
|
|
106
|
+
process.exit(1);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Fix permissions on restored directory
|
|
110
|
+
try {
|
|
111
|
+
fs.chmodSync(ARC402_DIR, 0o700);
|
|
112
|
+
fs.chmodSync(configPath, 0o600);
|
|
113
|
+
} catch { /* best-effort */ }
|
|
114
|
+
|
|
115
|
+
console.log(` ${c.success} Config restored. Run ${c.white("arc402 doctor")} to verify.`);
|
|
116
|
+
});
|
|
117
|
+
}
|