@r3ply/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/LICENSE +661 -0
- package/README.md +66 -0
- package/dist/comments-markov-model.json +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.mjs +2007 -0
- package/package.json +73 -0
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,2007 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __export = (target, all) => {
|
|
4
|
+
for (var name in all)
|
|
5
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
// src/index.ts
|
|
9
|
+
import { program as program2 } from "commander";
|
|
10
|
+
|
|
11
|
+
// src/cmds/init.ts
|
|
12
|
+
import { Command } from "commander";
|
|
13
|
+
import dayjs2 from "dayjs";
|
|
14
|
+
|
|
15
|
+
// src/tty/init.ts
|
|
16
|
+
var init_exports = {};
|
|
17
|
+
__export(init_exports, {
|
|
18
|
+
print_initialized_new_project: () => print_initialized_new_project,
|
|
19
|
+
print_warn_force_is_set: () => print_warn_force_is_set
|
|
20
|
+
});
|
|
21
|
+
import chalk from "chalk";
|
|
22
|
+
import path from "path";
|
|
23
|
+
import { highlight } from "cli-highlight";
|
|
24
|
+
import TOML from "@iarna/toml";
|
|
25
|
+
function print_warn_force_is_set() {
|
|
26
|
+
console.debug(
|
|
27
|
+
`--force=true ${tty_default.txt.warn("overwrites any preexisting .r3ply dir!\n")}`
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
function print_initialized_new_project(r3ply_dir, signet_config, format) {
|
|
31
|
+
console.info(
|
|
32
|
+
`Initialized empty r3ply project at ${chalk.greenBright(path.dirname(r3ply_dir))}`,
|
|
33
|
+
`
|
|
34
|
+
|
|
35
|
+
${tty_default.txt.help("Add the following site entry to your config:")}`,
|
|
36
|
+
`
|
|
37
|
+
|
|
38
|
+
${highlight(format == "toml" ? TOML.stringify(signet_config) : JSON.stringify(signet_config, null, 2))}`,
|
|
39
|
+
`
|
|
40
|
+
${tty_default.txt.help("Help:")} You can generate a config with ${tty_default.txt.help("`re generate config`")} if you have not already.`
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// src/tty/generate.ts
|
|
45
|
+
var generate_exports = {};
|
|
46
|
+
__export(generate_exports, {
|
|
47
|
+
print_config: () => print_config,
|
|
48
|
+
print_email: () => print_email,
|
|
49
|
+
print_mail_to_link: () => print_mail_to_link,
|
|
50
|
+
print_signet: () => print_signet
|
|
51
|
+
});
|
|
52
|
+
import chalk2 from "chalk";
|
|
53
|
+
import { highlight as highlight2 } from "cli-highlight";
|
|
54
|
+
import TOML2 from "@iarna/toml";
|
|
55
|
+
function print_mail_to_link(query) {
|
|
56
|
+
if (query == "") {
|
|
57
|
+
console.debug(
|
|
58
|
+
tty_default.txt.help(
|
|
59
|
+
`# hint: use options, e.g. '--to', otherwise mailto link will be mostly empty.`
|
|
60
|
+
)
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
console.log(chalk2.blueBright(`mailto:${query}`));
|
|
64
|
+
}
|
|
65
|
+
function print_signet(signet, format) {
|
|
66
|
+
console.log(
|
|
67
|
+
highlight2(
|
|
68
|
+
format == "toml" ? TOML2.stringify({
|
|
69
|
+
site: [{ ...signet }]
|
|
70
|
+
}) : JSON.stringify(
|
|
71
|
+
{
|
|
72
|
+
site: [{ ...signet }]
|
|
73
|
+
},
|
|
74
|
+
null,
|
|
75
|
+
2
|
|
76
|
+
)
|
|
77
|
+
)
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
function print_config(site_config, format) {
|
|
81
|
+
console.log(
|
|
82
|
+
highlight2(
|
|
83
|
+
format == "toml" ? TOML2.stringify(site_config) : JSON.stringify(site_config, null, 2)
|
|
84
|
+
)
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
function print_email(email) {
|
|
88
|
+
console.log(highlight2(email, { language: "yaml" }));
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// src/tty/simulate.ts
|
|
92
|
+
var simulate_exports = {};
|
|
93
|
+
__export(simulate_exports, {
|
|
94
|
+
print_comment_via_email_initial: () => print_comment_via_email_initial,
|
|
95
|
+
print_comment_via_email_response: () => print_comment_via_email_response,
|
|
96
|
+
print_github_moderation_event: () => print_github_moderation_event,
|
|
97
|
+
print_ignored_moderation_channels: () => print_ignored_moderation_channels,
|
|
98
|
+
print_local_moderation_event: () => print_local_moderation_event
|
|
99
|
+
});
|
|
100
|
+
import { highlight as highlight3 } from "cli-highlight";
|
|
101
|
+
|
|
102
|
+
// src/util.ts
|
|
103
|
+
import path2 from "path";
|
|
104
|
+
import fs from "fs";
|
|
105
|
+
import { Result } from "oxide.ts";
|
|
106
|
+
var util;
|
|
107
|
+
((util2) => {
|
|
108
|
+
class CLIError extends Error {
|
|
109
|
+
constructor(message) {
|
|
110
|
+
super(message);
|
|
111
|
+
this.name = "CLIError";
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
util2.CLIError = CLIError;
|
|
115
|
+
async function find_up(filename, cwd = process.cwd()) {
|
|
116
|
+
let currentDir = cwd;
|
|
117
|
+
do {
|
|
118
|
+
const file_path = path2.join(currentDir, filename);
|
|
119
|
+
const result = await Result.safe(fs.promises.access(file_path));
|
|
120
|
+
if (result.isOk()) return file_path;
|
|
121
|
+
currentDir = path2.dirname(currentDir);
|
|
122
|
+
} while (currentDir !== path2.dirname(currentDir));
|
|
123
|
+
return void 0;
|
|
124
|
+
}
|
|
125
|
+
util2.find_up = find_up;
|
|
126
|
+
function unsafeUnwrap(result) {
|
|
127
|
+
return result.unwrapOrElse(() => {
|
|
128
|
+
throw new CLIError(result.unwrapErr()?.message ?? String(result.unwrapErr()));
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
util2.unsafeUnwrap = unsafeUnwrap;
|
|
132
|
+
async function read_stdin() {
|
|
133
|
+
if (process.stdin.isTTY) return "";
|
|
134
|
+
const chunks = [];
|
|
135
|
+
for await (const chunk of process.stdin) {
|
|
136
|
+
chunks.push(chunk);
|
|
137
|
+
}
|
|
138
|
+
return Buffer.concat(
|
|
139
|
+
chunks.map((c) => Buffer.isBuffer(c) ? c : Buffer.from(c))
|
|
140
|
+
).toString("utf8");
|
|
141
|
+
}
|
|
142
|
+
util2.read_stdin = read_stdin;
|
|
143
|
+
function random_int(ceiling, floor = 0) {
|
|
144
|
+
return Math.floor(Math.random() * (ceiling - floor)) + floor;
|
|
145
|
+
}
|
|
146
|
+
util2.random_int = random_int;
|
|
147
|
+
function collect_opts(value, previous = []) {
|
|
148
|
+
return previous.concat([value]);
|
|
149
|
+
}
|
|
150
|
+
util2.collect_opts = collect_opts;
|
|
151
|
+
function split_list(param) {
|
|
152
|
+
return param.split(",").map((item) => item.trim());
|
|
153
|
+
}
|
|
154
|
+
util2.split_list = split_list;
|
|
155
|
+
function print_w_quiet_and_filter_opts(params, condition) {
|
|
156
|
+
const { quiet, filter } = params;
|
|
157
|
+
if (filter) {
|
|
158
|
+
if (filter == true || filter.some((stage) => {
|
|
159
|
+
if (condition.includes("=")) {
|
|
160
|
+
return condition.includes(stage);
|
|
161
|
+
} else {
|
|
162
|
+
return stage.startsWith(condition);
|
|
163
|
+
}
|
|
164
|
+
})) {
|
|
165
|
+
return print_w_quiet(quiet, condition);
|
|
166
|
+
} else {
|
|
167
|
+
return false;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
return print_w_quiet(quiet, condition);
|
|
171
|
+
}
|
|
172
|
+
util2.print_w_quiet_and_filter_opts = print_w_quiet_and_filter_opts;
|
|
173
|
+
function print_w_quiet(quiet, condition) {
|
|
174
|
+
if (quiet) {
|
|
175
|
+
if (quiet == true) {
|
|
176
|
+
return false;
|
|
177
|
+
} else {
|
|
178
|
+
if (quiet.some((stage) => {
|
|
179
|
+
if (condition.startsWith(stage)) {
|
|
180
|
+
if (condition.includes("=")) {
|
|
181
|
+
return condition == stage;
|
|
182
|
+
} else {
|
|
183
|
+
return true;
|
|
184
|
+
}
|
|
185
|
+
} else {
|
|
186
|
+
return false;
|
|
187
|
+
}
|
|
188
|
+
})) {
|
|
189
|
+
return false;
|
|
190
|
+
} else return true;
|
|
191
|
+
}
|
|
192
|
+
} else {
|
|
193
|
+
return true;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
})(util || (util = {}));
|
|
197
|
+
|
|
198
|
+
// src/tty/simulate.ts
|
|
199
|
+
import TOML3 from "@iarna/toml";
|
|
200
|
+
function print_comment_via_email_initial(email, options) {
|
|
201
|
+
if (util.print_w_quiet_and_filter_opts(options, "email")) {
|
|
202
|
+
if (options.heading) console.log(`${tty_default.txt.info("# === Input Email ===")}`);
|
|
203
|
+
console.log(
|
|
204
|
+
highlight3(email.replace(/\r/g, ""), {
|
|
205
|
+
language: "yaml",
|
|
206
|
+
ignoreIllegals: true
|
|
207
|
+
}) + "\n\n"
|
|
208
|
+
);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
function print_comment_via_email_response(cli_system_config, {
|
|
212
|
+
site_config_path,
|
|
213
|
+
site_config
|
|
214
|
+
}, email_event_response, options, format) {
|
|
215
|
+
if (util.print_w_quiet_and_filter_opts(options, "config")) {
|
|
216
|
+
if (util.print_w_quiet_and_filter_opts(options, "config=system")) {
|
|
217
|
+
if (options.heading)
|
|
218
|
+
console.log(`${tty_default.txt.info("# === Comment: System Config ===\n")}`);
|
|
219
|
+
console.log(
|
|
220
|
+
format == "toml" ? highlight3(
|
|
221
|
+
`# Generated using site config
|
|
222
|
+
${TOML3.stringify(cli_system_config)}`,
|
|
223
|
+
{ language: "toml", ignoreIllegals: true }
|
|
224
|
+
) + "\n" : highlight3(
|
|
225
|
+
`/* Generated using site config*/
|
|
226
|
+
${JSON.stringify(cli_system_config, null, 2)}`,
|
|
227
|
+
{ language: "json", ignoreIllegals: true }
|
|
228
|
+
) + "\n"
|
|
229
|
+
);
|
|
230
|
+
}
|
|
231
|
+
if (util.print_w_quiet_and_filter_opts(options, "config=site")) {
|
|
232
|
+
if (options.heading)
|
|
233
|
+
console.log(`${tty_default.txt.info("# === Comment: Site Config ===\n")}`);
|
|
234
|
+
console.log(
|
|
235
|
+
format == "toml" ? `${highlight3(
|
|
236
|
+
`# From path ${site_config_path}
|
|
237
|
+
${TOML3.stringify(site_config)}`,
|
|
238
|
+
{ language: "toml", ignoreIllegals: true }
|
|
239
|
+
)}` : `${highlight3(
|
|
240
|
+
`/* From path ${site_config_path} */
|
|
241
|
+
${JSON.stringify(site_config, null, 2)}`,
|
|
242
|
+
{ language: "json", ignoreIllegals: true }
|
|
243
|
+
)}
|
|
244
|
+
`
|
|
245
|
+
);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
if (util.print_w_quiet_and_filter_opts(options, "prescreen")) {
|
|
249
|
+
if (options.heading)
|
|
250
|
+
console.log(
|
|
251
|
+
tty_default.txt.info("# === Comment: Prescreening Results ===") + "\n"
|
|
252
|
+
);
|
|
253
|
+
if (email_event_response.prescreening.isOk()) {
|
|
254
|
+
const result = email_event_response.prescreening.unwrap();
|
|
255
|
+
delete result.comments_configured.general_comments;
|
|
256
|
+
delete result.comments_configured.email_comments;
|
|
257
|
+
console.log(
|
|
258
|
+
format == "toml" ? highlight3(TOML3.stringify(result), {
|
|
259
|
+
language: "toml",
|
|
260
|
+
ignoreIllegals: true
|
|
261
|
+
}) : highlight3(JSON.stringify(result, null, 2), {
|
|
262
|
+
language: "json",
|
|
263
|
+
ignoreIllegals: true
|
|
264
|
+
}) + "\n"
|
|
265
|
+
);
|
|
266
|
+
} else {
|
|
267
|
+
console.log(tty_default.txt.warn("# Prescreening failed checks:\n"));
|
|
268
|
+
const prescreen_failures = email_event_response.prescreening.unwrapErr();
|
|
269
|
+
if (prescreen_failures.r3ply_is_disabled.result == "fail") {
|
|
270
|
+
console.log(
|
|
271
|
+
format == "toml" ? highlight3(
|
|
272
|
+
TOML3.stringify({
|
|
273
|
+
r3ply_is_disabled: prescreen_failures.r3ply_is_disabled.errors
|
|
274
|
+
})
|
|
275
|
+
) : highlight3(
|
|
276
|
+
JSON.stringify(
|
|
277
|
+
{
|
|
278
|
+
r3ply_is_disabled: prescreen_failures.r3ply_is_disabled.errors
|
|
279
|
+
},
|
|
280
|
+
null,
|
|
281
|
+
2
|
|
282
|
+
) + "\n"
|
|
283
|
+
)
|
|
284
|
+
);
|
|
285
|
+
}
|
|
286
|
+
if (prescreen_failures.comments_accepted.result == "fail") {
|
|
287
|
+
console.log(
|
|
288
|
+
format == "toml" ? highlight3(
|
|
289
|
+
TOML3.stringify({
|
|
290
|
+
comments_accepted: prescreen_failures.comments_accepted.errors
|
|
291
|
+
})
|
|
292
|
+
) : highlight3(
|
|
293
|
+
JSON.stringify(
|
|
294
|
+
{
|
|
295
|
+
comments_accepted: prescreen_failures.comments_accepted.errors
|
|
296
|
+
},
|
|
297
|
+
null,
|
|
298
|
+
2
|
|
299
|
+
) + "\n"
|
|
300
|
+
)
|
|
301
|
+
);
|
|
302
|
+
}
|
|
303
|
+
if (prescreen_failures.comments_configured.result == "fail") {
|
|
304
|
+
console.log(
|
|
305
|
+
format == "toml" ? highlight3(
|
|
306
|
+
TOML3.stringify({
|
|
307
|
+
comments_configured: prescreen_failures.comments_configured.errors
|
|
308
|
+
})
|
|
309
|
+
) : highlight3(
|
|
310
|
+
JSON.stringify(
|
|
311
|
+
{
|
|
312
|
+
comments_configured: prescreen_failures.comments_configured.errors
|
|
313
|
+
},
|
|
314
|
+
null,
|
|
315
|
+
2
|
|
316
|
+
)
|
|
317
|
+
)
|
|
318
|
+
);
|
|
319
|
+
}
|
|
320
|
+
if (prescreen_failures.email_size_bytes.result == "fail") {
|
|
321
|
+
console.log(
|
|
322
|
+
format == "toml" ? highlight3(
|
|
323
|
+
TOML3.stringify({
|
|
324
|
+
email_size_bytes: prescreen_failures.email_size_bytes.errors
|
|
325
|
+
})
|
|
326
|
+
) : highlight3(
|
|
327
|
+
JSON.stringify(
|
|
328
|
+
{
|
|
329
|
+
email_size_bytes: prescreen_failures.email_size_bytes.errors
|
|
330
|
+
},
|
|
331
|
+
null,
|
|
332
|
+
2
|
|
333
|
+
)
|
|
334
|
+
)
|
|
335
|
+
);
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
const receive_details = email_event_response.received;
|
|
340
|
+
if (receive_details) {
|
|
341
|
+
if (util.print_w_quiet_and_filter_opts(options, "receive")) {
|
|
342
|
+
if (options.heading) {
|
|
343
|
+
console.log(tty_default.txt.info("# === Comment: Comment Received ===") + "\n");
|
|
344
|
+
if (receive_details.isOk()) {
|
|
345
|
+
console.log(
|
|
346
|
+
format == "toml" ? highlight3(TOML3.stringify(receive_details.unwrap()), {
|
|
347
|
+
language: "toml",
|
|
348
|
+
ignoreIllegals: true
|
|
349
|
+
}) : highlight3(JSON.stringify(receive_details.unwrap()), {
|
|
350
|
+
language: "json",
|
|
351
|
+
ignoreIllegals: true
|
|
352
|
+
}) + "\n"
|
|
353
|
+
);
|
|
354
|
+
} else {
|
|
355
|
+
console.log(tty_default.txt.warn(receive_details.unwrapErr() + "\n"));
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
const deliverable_details = email_event_response.deliverable;
|
|
361
|
+
if (deliverable_details) {
|
|
362
|
+
if (util.print_w_quiet_and_filter_opts(options, "deliverable")) {
|
|
363
|
+
if (options.heading)
|
|
364
|
+
console.log(
|
|
365
|
+
`${tty_default.txt.info("# === Comment: Deliverability Details ===")}
|
|
366
|
+
`
|
|
367
|
+
);
|
|
368
|
+
if (deliverable_details.isOk()) {
|
|
369
|
+
const result = deliverable_details.unwrap();
|
|
370
|
+
delete result.email;
|
|
371
|
+
console.log(
|
|
372
|
+
format == "toml" ? `${highlight3("# Note: `From` is redacted\n" + TOML3.stringify(result), { language: "toml", ignoreIllegals: true })}` : `${highlight3("/* Note: `From` is redacted */\n" + JSON.stringify(result, null, 2), { language: "json", ignoreIllegals: true })}
|
|
373
|
+
`
|
|
374
|
+
);
|
|
375
|
+
} else {
|
|
376
|
+
console.log(`${tty_default.txt.warn(deliverable_details.unwrapErr())}
|
|
377
|
+
`);
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
const prepare_details = email_event_response.prepared;
|
|
382
|
+
if (prepare_details) {
|
|
383
|
+
if (util.print_w_quiet_and_filter_opts(options, "prepare")) {
|
|
384
|
+
if (options.heading)
|
|
385
|
+
console.log(`${tty_default.txt.info("# === Comment: Template Context ===\n")}`);
|
|
386
|
+
if (prepare_details.isOk()) {
|
|
387
|
+
console.log(
|
|
388
|
+
format == "toml" ? `${highlight3("# These are the values available to your templates\n\n" + TOML3.stringify(prepare_details.unwrap()), { language: "toml", ignoreIllegals: true })}` : `${highlight3("/* These are the values available to your templates */\n" + JSON.stringify(prepare_details.unwrap(), null, 2), { language: "json", ignoreIllegals: true })}
|
|
389
|
+
`
|
|
390
|
+
);
|
|
391
|
+
} else {
|
|
392
|
+
console.log(tty_default.txt.warn(prepare_details.unwrapErr() + "\n"));
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
const process_details = email_event_response.comment;
|
|
397
|
+
if (process_details) {
|
|
398
|
+
if (util.print_w_quiet_and_filter_opts(options, "comment")) {
|
|
399
|
+
if (options.heading)
|
|
400
|
+
console.log(`${tty_default.txt.info("# === Comment: Processed ===")}
|
|
401
|
+
`);
|
|
402
|
+
if (process_details.isOk()) {
|
|
403
|
+
console.log(highlight3(process_details.unwrap()));
|
|
404
|
+
} else {
|
|
405
|
+
console.log(tty_default.txt.warn(process_details.unwrapErr() + "\n"));
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
function print_local_moderation_event(request, ticket, count, options, format) {
|
|
411
|
+
if (util.print_w_quiet_and_filter_opts(options, "moderation")) {
|
|
412
|
+
if (util.print_w_quiet_and_filter_opts(options, `moderation=local_${count}`)) {
|
|
413
|
+
if (options.heading)
|
|
414
|
+
console.log(tty_default.txt.info(`# === Moderation: Local[${count}] ===
|
|
415
|
+
`));
|
|
416
|
+
if (request.isOk()) {
|
|
417
|
+
const partial_request = request.unwrap();
|
|
418
|
+
delete partial_request["send"];
|
|
419
|
+
partial_request.args = request.unwrap().args;
|
|
420
|
+
partial_request.args.comment = "[elided... see above (or add `comment` to --filter`)]";
|
|
421
|
+
const result_string = format == "toml" ? TOML3.stringify({
|
|
422
|
+
request: partial_request
|
|
423
|
+
}).replace(
|
|
424
|
+
"[request]",
|
|
425
|
+
"# `bypass` asks to skip moderation altogether. For local moderation it has no effect.\n[request]"
|
|
426
|
+
).replace(
|
|
427
|
+
/^(\s*)\[request\.args\]/m,
|
|
428
|
+
(_, spaces) => `${spaces}# \`relative_path\` is relative to project root.${spaces}[request.args]`
|
|
429
|
+
) : JSON.stringify(
|
|
430
|
+
{
|
|
431
|
+
request: partial_request
|
|
432
|
+
},
|
|
433
|
+
null,
|
|
434
|
+
2
|
|
435
|
+
).replace(
|
|
436
|
+
/^(\s*)"bypass":/m,
|
|
437
|
+
(_, spaces) => `${spaces}/* \`bypass\` asks to skip moderation altogether. For local moderation it has no effect. */
|
|
438
|
+
${spaces}"bypass":`
|
|
439
|
+
).replace(
|
|
440
|
+
/^(\s*)"args":/m,
|
|
441
|
+
(_, spaces) => `${spaces}/* \`relative_path\` is relative to project root. */
|
|
442
|
+
${spaces}"args":`
|
|
443
|
+
) + "\n";
|
|
444
|
+
const request_comment = format == "toml" ? `#################################
|
|
445
|
+
# Request portion of moderation #
|
|
446
|
+
#################################
|
|
447
|
+
` : `/*********************************
|
|
448
|
+
* Request portion of moderation *
|
|
449
|
+
*********************************/
|
|
450
|
+
`;
|
|
451
|
+
console.log(
|
|
452
|
+
highlight3(request_comment + "\n" + result_string, {
|
|
453
|
+
language: format
|
|
454
|
+
})
|
|
455
|
+
);
|
|
456
|
+
if (ticket) {
|
|
457
|
+
if (ticket.details.isOk()) {
|
|
458
|
+
const ticket_details = ticket.details.unwrap();
|
|
459
|
+
const ticket_string = format == "toml" ? TOML3.stringify({
|
|
460
|
+
ticket: ticket_details
|
|
461
|
+
}).replace(
|
|
462
|
+
"[ticket.local]",
|
|
463
|
+
"# `ticket.local` is the response to a request for local moderation.\n[ticket.local]"
|
|
464
|
+
) : JSON.stringify(
|
|
465
|
+
{
|
|
466
|
+
ticket: ticket_details
|
|
467
|
+
},
|
|
468
|
+
null,
|
|
469
|
+
2
|
|
470
|
+
).replace(
|
|
471
|
+
/^(\s*)"local":/m,
|
|
472
|
+
(_, spaces) => `${spaces}/* \`ticket.local\` is the response to a request for local moderation. */
|
|
473
|
+
${spaces}"local":`
|
|
474
|
+
);
|
|
475
|
+
const ticket_comment = format == "toml" ? `################################
|
|
476
|
+
# Ticket portion of moderation #
|
|
477
|
+
################################
|
|
478
|
+
` : `/********************************
|
|
479
|
+
* Ticket portion of moderation *
|
|
480
|
+
********************************/
|
|
481
|
+
`;
|
|
482
|
+
console.log(
|
|
483
|
+
highlight3(ticket_comment + "\n" + ticket_string, {
|
|
484
|
+
language: format
|
|
485
|
+
})
|
|
486
|
+
);
|
|
487
|
+
} else {
|
|
488
|
+
const error = ticket.details.unwrapErr();
|
|
489
|
+
console.log(tty_default.txt.warn(`Error: ${error.message}`));
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
} else {
|
|
493
|
+
const error = request.unwrapErr();
|
|
494
|
+
console.log(tty_default.txt.warn(`Moderation skipped: ${error.message}`));
|
|
495
|
+
}
|
|
496
|
+
console.log();
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
function print_github_moderation_event(request, ticket, count, options, format) {
|
|
501
|
+
if (util.print_w_quiet_and_filter_opts(options, "moderation")) {
|
|
502
|
+
if (util.print_w_quiet_and_filter_opts(options, `moderation=github_${count}`)) {
|
|
503
|
+
if (options.heading)
|
|
504
|
+
console.log(
|
|
505
|
+
tty_default.txt.info(`# === Moderation: GitHub[${count}] (MOCKED) ===
|
|
506
|
+
`)
|
|
507
|
+
);
|
|
508
|
+
if (request.isOk()) {
|
|
509
|
+
const partial_request = request.unwrap();
|
|
510
|
+
delete partial_request["send"];
|
|
511
|
+
partial_request.args = request.unwrap().args;
|
|
512
|
+
partial_request.args.comment_data = "[elided... see above (or add `comment` to --filter`)]";
|
|
513
|
+
const result_string = format == "toml" ? TOML3.stringify({
|
|
514
|
+
request: partial_request
|
|
515
|
+
}).replace(
|
|
516
|
+
"[request]",
|
|
517
|
+
"# `bypass` would request to skip moderation altogether.\n[request]"
|
|
518
|
+
).replace(
|
|
519
|
+
/^(\s*)\[request\.args\]/m,
|
|
520
|
+
(_, spaces) => `${spaces}# Arguments that would be sent to create a GitHub PR${spaces}[request.args]`
|
|
521
|
+
) : JSON.stringify(
|
|
522
|
+
{
|
|
523
|
+
request: partial_request
|
|
524
|
+
},
|
|
525
|
+
null,
|
|
526
|
+
2
|
|
527
|
+
).replace(
|
|
528
|
+
/^(\s*)"bypass":/m,
|
|
529
|
+
(_, spaces) => `${spaces}/* \`bypass\` would request to skip moderation altogether. */
|
|
530
|
+
${spaces}"bypass":`
|
|
531
|
+
).replace(
|
|
532
|
+
/^(\s*)"args":/m,
|
|
533
|
+
(_, spaces) => `${spaces}/* Arguments that would be sent to create a GitHub PR */
|
|
534
|
+
${spaces}"args":`
|
|
535
|
+
) + "\n";
|
|
536
|
+
const request_comment = format == "toml" ? `#################################
|
|
537
|
+
# Request portion of moderation #
|
|
538
|
+
#################################
|
|
539
|
+
` : `/*********************************
|
|
540
|
+
* Request portion of moderation *
|
|
541
|
+
*********************************/
|
|
542
|
+
`;
|
|
543
|
+
console.log(
|
|
544
|
+
highlight3(request_comment + "\n" + result_string, {
|
|
545
|
+
language: format
|
|
546
|
+
})
|
|
547
|
+
);
|
|
548
|
+
if (ticket) {
|
|
549
|
+
if (ticket.details.isOk()) {
|
|
550
|
+
const ticket_details = ticket.details.unwrap();
|
|
551
|
+
const ticket_string = format == "toml" ? TOML3.stringify({
|
|
552
|
+
ticket: ticket_details
|
|
553
|
+
}).replace(
|
|
554
|
+
"[ticket.github]",
|
|
555
|
+
"# `ticket.local` is the response to a request for local moderation.\n[ticket.local]"
|
|
556
|
+
) : JSON.stringify(
|
|
557
|
+
{
|
|
558
|
+
ticket: ticket_details
|
|
559
|
+
},
|
|
560
|
+
null,
|
|
561
|
+
2
|
|
562
|
+
).replace(
|
|
563
|
+
"[ticket.github]",
|
|
564
|
+
"# `ticket.local` is the response to a request for local moderation.\n[ticket.local]"
|
|
565
|
+
).replace(
|
|
566
|
+
/^(\s*)"github":/m,
|
|
567
|
+
(_, spaces) => `${spaces}/* \`ticket.local\` is the response to a request for local moderation. */
|
|
568
|
+
${spaces}"github":`
|
|
569
|
+
);
|
|
570
|
+
const ticket_comment = format == "toml" ? `################################
|
|
571
|
+
# Ticket portion of moderation #
|
|
572
|
+
################################
|
|
573
|
+
` : `/********************************
|
|
574
|
+
* Ticket portion of moderation *
|
|
575
|
+
********************************/
|
|
576
|
+
`;
|
|
577
|
+
console.log(
|
|
578
|
+
highlight3(ticket_comment + "\n" + ticket_string, {
|
|
579
|
+
language: format
|
|
580
|
+
})
|
|
581
|
+
);
|
|
582
|
+
} else {
|
|
583
|
+
const error = ticket.details.unwrapErr();
|
|
584
|
+
console.log(tty_default.txt.warn(`Error: ${error.message}`));
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
} else {
|
|
588
|
+
const error = request.unwrapErr();
|
|
589
|
+
console.log(tty_default.txt.warn(`Moderation skipped: ${error.message}`));
|
|
590
|
+
}
|
|
591
|
+
console.log();
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
function print_ignored_moderation_channels(ignored_moderation_types, not_implemented_moderation_results, options, format) {
|
|
596
|
+
if (util.print_w_quiet_and_filter_opts(options, "moderation")) {
|
|
597
|
+
if (util.print_w_quiet_and_filter_opts(options, `moderation=other`)) {
|
|
598
|
+
if (options.heading)
|
|
599
|
+
console.log(tty_default.txt.info(`# === Moderation: Other ===
|
|
600
|
+
`));
|
|
601
|
+
const ignored_moderation = format == "toml" ? TOML3.stringify({
|
|
602
|
+
ignored: ignored_moderation_types
|
|
603
|
+
}).replace(
|
|
604
|
+
"ignored",
|
|
605
|
+
"# moderation channels in your config that were ignored by the CLI (they're unsupported)\nignored"
|
|
606
|
+
) : JSON.stringify(
|
|
607
|
+
{
|
|
608
|
+
ignored: ignored_moderation_types
|
|
609
|
+
},
|
|
610
|
+
null,
|
|
611
|
+
2
|
|
612
|
+
).replace(
|
|
613
|
+
/^(\s*)"ignored":/m,
|
|
614
|
+
(_, spaces) => `${spaces}/* moderation channels in your config that were ignored by the CLI (they're unsupported) */
|
|
615
|
+
${spaces}"ignored":`
|
|
616
|
+
) + "\n";
|
|
617
|
+
console.log(highlight3(ignored_moderation));
|
|
618
|
+
const not_implemented_mod = format == "toml" ? TOML3.stringify({
|
|
619
|
+
not_implemented: not_implemented_moderation_results
|
|
620
|
+
}) : JSON.stringify(
|
|
621
|
+
{
|
|
622
|
+
not_implemented: not_implemented_moderation_results
|
|
623
|
+
},
|
|
624
|
+
null,
|
|
625
|
+
2
|
|
626
|
+
);
|
|
627
|
+
console.log(
|
|
628
|
+
format == "toml" ? highlight3(
|
|
629
|
+
"# unexpected moderation results that haven't been fully implemented\n" + not_implemented_mod
|
|
630
|
+
) : highlight3(
|
|
631
|
+
"/* unexpected moderation results that haven't been fully implemented */\n" + not_implemented_mod
|
|
632
|
+
)
|
|
633
|
+
);
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
// src/tty/cache.ts
|
|
639
|
+
var cache_exports = {};
|
|
640
|
+
__export(cache_exports, {
|
|
641
|
+
print_serving_cache: () => print_serving_cache
|
|
642
|
+
});
|
|
643
|
+
function print_serving_cache(cache_dir, options) {
|
|
644
|
+
if (options.reset) console.log(`Resetting cache at ${cache_dir}`);
|
|
645
|
+
console.log(
|
|
646
|
+
`Static server running at http://${options.interface}:${options.port}`
|
|
647
|
+
);
|
|
648
|
+
console.log(`Serving files from: ${cache_dir}`);
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
// src/tty/index.ts
|
|
652
|
+
import chalk3 from "chalk";
|
|
653
|
+
function txt_info(...text) {
|
|
654
|
+
return chalk3.whiteBright(text);
|
|
655
|
+
}
|
|
656
|
+
function txt_help(...text) {
|
|
657
|
+
return chalk3.yellowBright(text);
|
|
658
|
+
}
|
|
659
|
+
function txt_warn(...text) {
|
|
660
|
+
return chalk3.redBright(text);
|
|
661
|
+
}
|
|
662
|
+
var tty_default = {
|
|
663
|
+
txt: {
|
|
664
|
+
info: txt_info,
|
|
665
|
+
help: txt_help,
|
|
666
|
+
warn: txt_warn
|
|
667
|
+
},
|
|
668
|
+
cmds: {
|
|
669
|
+
init: init_exports,
|
|
670
|
+
generate: generate_exports,
|
|
671
|
+
simulate: simulate_exports,
|
|
672
|
+
cache: cache_exports
|
|
673
|
+
}
|
|
674
|
+
};
|
|
675
|
+
|
|
676
|
+
// src/lib.ts
|
|
677
|
+
import path3 from "path";
|
|
678
|
+
import fs2 from "fs";
|
|
679
|
+
import { Result as Result2, Option } from "oxide.ts";
|
|
680
|
+
import fg from "fast-glob";
|
|
681
|
+
import TOML4 from "@iarna/toml";
|
|
682
|
+
import {
|
|
683
|
+
R3plySiteConfig,
|
|
684
|
+
R3plySystemConfig
|
|
685
|
+
} from "@r3ply/schema/config";
|
|
686
|
+
import chalk4 from "chalk";
|
|
687
|
+
import { RiMarkov } from "rita";
|
|
688
|
+
import { fileURLToPath } from "url";
|
|
689
|
+
import {
|
|
690
|
+
SignetIssuer
|
|
691
|
+
} from "@r3ply/lib";
|
|
692
|
+
import dayjs from "dayjs";
|
|
693
|
+
import { build_email } from "@r3ply/wasm";
|
|
694
|
+
import crypto from "crypto";
|
|
695
|
+
import { mailbox } from "typescript-mailbox-parser";
|
|
696
|
+
var project;
|
|
697
|
+
((project2) => {
|
|
698
|
+
const R3PLY_DIR = ".r3ply";
|
|
699
|
+
const CONFIG_GLOB_PATTERNS = [
|
|
700
|
+
`**/r3ply/config.{toml,json}`,
|
|
701
|
+
`**/r3ply.config.{toml,json}`
|
|
702
|
+
];
|
|
703
|
+
const CLI_SYSTEM_CONFIG_FILENAME = "r3ply.system.config.toml";
|
|
704
|
+
const CLI_SETTINGS_FILENAME = "r3ply.cli.settings.toml";
|
|
705
|
+
const SIGNET_KEY_FILENAME = "signet.key";
|
|
706
|
+
const ENCRYPT_EMAIL_KEY_FILENAME = "encrypt_email.key";
|
|
707
|
+
project2.DEFAULT_SITE_DOMAIN = "site.local.test";
|
|
708
|
+
project2.DEFAULT_R3PLY_DOMAIN = "cli.r3ply.test";
|
|
709
|
+
project2.DEFAULT_SIGNET_KEY = "6eFnDQTHov/yKkhLp/HdZDSU/vryNJ4XeNOgX2XBCVI=";
|
|
710
|
+
project2.DEFAULT_EMAIL_KEY = "f+466zchScGV5oiKq4W5hxCct1iXBuwgRUnx8tBSuQQ=";
|
|
711
|
+
project2.DEFAULT_CLI_SIGNET_LABEL = "CLI";
|
|
712
|
+
const DEFAULT_STATIC_DIR = "static";
|
|
713
|
+
const CACHE_DIR = "cache";
|
|
714
|
+
async function find_r3ply_dir(cwd) {
|
|
715
|
+
const find_result = util.find_up(".r3ply", cwd).then((path4) => {
|
|
716
|
+
if (path4) return path4;
|
|
717
|
+
else
|
|
718
|
+
throw new util.CLIError(
|
|
719
|
+
`No ${R3PLY_DIR} directory found. ${chalk4.yellow(`You can run ${chalk4.white("`re init`")} to initialize one.`)}`
|
|
720
|
+
);
|
|
721
|
+
});
|
|
722
|
+
return Result2.safe(find_result);
|
|
723
|
+
}
|
|
724
|
+
project2.find_r3ply_dir = find_r3ply_dir;
|
|
725
|
+
async function find_project_dir(cwd) {
|
|
726
|
+
return find_r3ply_dir(cwd).then((r3ply_dir) => {
|
|
727
|
+
return r3ply_dir.map((r3ply_dir2) => path3.dirname(r3ply_dir2));
|
|
728
|
+
});
|
|
729
|
+
}
|
|
730
|
+
project2.find_project_dir = find_project_dir;
|
|
731
|
+
async function find_site_config_files(from_dir, file_glob) {
|
|
732
|
+
if (file_glob)
|
|
733
|
+
return Result2.safe(fg.async([file_glob], { dot: true, cwd: from_dir }));
|
|
734
|
+
else
|
|
735
|
+
return Result2.safe(
|
|
736
|
+
fg.async(CONFIG_GLOB_PATTERNS, { dot: true, cwd: from_dir })
|
|
737
|
+
);
|
|
738
|
+
}
|
|
739
|
+
project2.find_site_config_files = find_site_config_files;
|
|
740
|
+
async function find_static_dir(cwd) {
|
|
741
|
+
const r3ply_dir = find_r3ply_dir(cwd).then((result) => result.unwrap());
|
|
742
|
+
const static_dir = path3.join(await r3ply_dir, DEFAULT_STATIC_DIR);
|
|
743
|
+
return static_dir;
|
|
744
|
+
}
|
|
745
|
+
project2.find_static_dir = find_static_dir;
|
|
746
|
+
async function get_static_dir(cwd, reset = false) {
|
|
747
|
+
const static_dir = await find_static_dir(cwd);
|
|
748
|
+
const access = await Result2.safe(fs2.promises.access(static_dir));
|
|
749
|
+
if (access.isOk()) {
|
|
750
|
+
return static_dir;
|
|
751
|
+
} else {
|
|
752
|
+
return fs2.promises.mkdir(static_dir).then((_) => static_dir);
|
|
753
|
+
}
|
|
754
|
+
}
|
|
755
|
+
project2.get_static_dir = get_static_dir;
|
|
756
|
+
async function find_cache_dir(cwd) {
|
|
757
|
+
const static_dir = await find_static_dir(cwd);
|
|
758
|
+
const cache_dir = path3.join(await static_dir, CACHE_DIR);
|
|
759
|
+
return cache_dir;
|
|
760
|
+
}
|
|
761
|
+
project2.find_cache_dir = find_cache_dir;
|
|
762
|
+
async function get_cache_dir(cwd, reset = false) {
|
|
763
|
+
const cache_dir = await find_cache_dir(cwd);
|
|
764
|
+
const access = await Result2.safe(fs2.promises.access(cache_dir));
|
|
765
|
+
if (access.isOk()) {
|
|
766
|
+
if (reset) await clean_cache(cwd);
|
|
767
|
+
return cache_dir;
|
|
768
|
+
} else {
|
|
769
|
+
return fs2.promises.mkdir(cache_dir, { recursive: true }).then((_) => cache_dir);
|
|
770
|
+
}
|
|
771
|
+
}
|
|
772
|
+
project2.get_cache_dir = get_cache_dir;
|
|
773
|
+
async function clean_cache(cwd) {
|
|
774
|
+
const cache_dir = await find_cache_dir(cwd);
|
|
775
|
+
const access = await Result2.safe(fs2.promises.access(cache_dir));
|
|
776
|
+
if (access.isOk()) {
|
|
777
|
+
await fs2.promises.rm(cache_dir, { recursive: true, force: true }).then((_) => fs2.promises.mkdir(cache_dir));
|
|
778
|
+
} else {
|
|
779
|
+
throw new util.CLIError(`No cache found at ${cache_dir}`);
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
project2.clean_cache = clean_cache;
|
|
783
|
+
async function add_comment_to_cache(cwd, comment) {
|
|
784
|
+
const cache_dir = await get_cache_dir(cwd);
|
|
785
|
+
const comment_path = path3.join(cache_dir, comment.path);
|
|
786
|
+
const parent = path3.dirname(comment_path);
|
|
787
|
+
await fs2.promises.mkdir(parent, { recursive: true });
|
|
788
|
+
return fs2.promises.writeFile(
|
|
789
|
+
comment_path,
|
|
790
|
+
JSON.stringify(comment.content, null, 2)
|
|
791
|
+
);
|
|
792
|
+
}
|
|
793
|
+
project2.add_comment_to_cache = add_comment_to_cache;
|
|
794
|
+
async function get_comment_from_cache(cwd, comment) {
|
|
795
|
+
const cache_dir = await get_cache_dir(cwd);
|
|
796
|
+
const comment_path = path3.join(cache_dir, comment.path);
|
|
797
|
+
const access = await Result2.safe(fs2.promises.access(comment_path));
|
|
798
|
+
if (access.isOk()) {
|
|
799
|
+
return fs2.promises.readFile(comment_path).then((bytes) => JSON.parse(bytes.toString()));
|
|
800
|
+
} else {
|
|
801
|
+
return [];
|
|
802
|
+
}
|
|
803
|
+
}
|
|
804
|
+
project2.get_comment_from_cache = get_comment_from_cache;
|
|
805
|
+
async function evict_comments_from_cache(cwd, at_path, max_age_seconds) {
|
|
806
|
+
const cache_dir = await get_cache_dir(cwd);
|
|
807
|
+
const comments_path = path3.join(cache_dir, at_path);
|
|
808
|
+
return walk(comments_path);
|
|
809
|
+
async function walk(dir) {
|
|
810
|
+
const entries = await fs2.promises.readdir(dir, { withFileTypes: true });
|
|
811
|
+
for (const entry of entries) {
|
|
812
|
+
const full_path = path3.join(dir, entry.name);
|
|
813
|
+
if (entry.isDirectory()) {
|
|
814
|
+
return walk(full_path);
|
|
815
|
+
} else {
|
|
816
|
+
return fs2.promises.readFile(full_path).then(
|
|
817
|
+
(bytes) => JSON.parse(bytes.toString())
|
|
818
|
+
).then((comments) => {
|
|
819
|
+
const new_comments = [];
|
|
820
|
+
const now_unix = dayjs().unix();
|
|
821
|
+
for (const comment of comments) {
|
|
822
|
+
const comment_sent_unix = dayjs(comment.email.date).unix();
|
|
823
|
+
const age = now_unix - comment_sent_unix;
|
|
824
|
+
if (age <= max_age_seconds) new_comments.push(comment);
|
|
825
|
+
else
|
|
826
|
+
console.debug(
|
|
827
|
+
`Deleting comment with ID "${comment.comment.id}" as it is ${age} seconds old.`
|
|
828
|
+
);
|
|
829
|
+
}
|
|
830
|
+
return fs2.promises.writeFile(
|
|
831
|
+
full_path,
|
|
832
|
+
JSON.stringify(new_comments, null, 2)
|
|
833
|
+
);
|
|
834
|
+
});
|
|
835
|
+
}
|
|
836
|
+
}
|
|
837
|
+
}
|
|
838
|
+
}
|
|
839
|
+
project2.evict_comments_from_cache = evict_comments_from_cache;
|
|
840
|
+
async function get_site_config_path(cwd, config_path) {
|
|
841
|
+
const full_config_path = (async () => {
|
|
842
|
+
const project_dir = util.unsafeUnwrap(await find_project_dir(cwd));
|
|
843
|
+
if (config_path) {
|
|
844
|
+
const relative_files = util.unsafeUnwrap(
|
|
845
|
+
await find_site_config_files(cwd, config_path)
|
|
846
|
+
);
|
|
847
|
+
if (relative_files.length == 0)
|
|
848
|
+
throw new util.CLIError(
|
|
849
|
+
`No config found at ${path3.resolve(cwd, config_path)}`
|
|
850
|
+
);
|
|
851
|
+
else if (relative_files.length > 1)
|
|
852
|
+
throw new util.CLIError(
|
|
853
|
+
`Multiple matches found: ${JSON.stringify(relative_files, null, 2)}`
|
|
854
|
+
);
|
|
855
|
+
else return path3.resolve(cwd, relative_files[0]);
|
|
856
|
+
} else {
|
|
857
|
+
const relative_files = util.unsafeUnwrap(
|
|
858
|
+
await find_site_config_files(project_dir)
|
|
859
|
+
);
|
|
860
|
+
if (relative_files.length == 0)
|
|
861
|
+
throw new util.CLIError(`No r3ply config found within ${chalk4.white(project_dir)}. ${chalk4.yellow(`You can run ${chalk4.white("`re generate config`")} to generate one. (Make sure to save it to the expected file path, e.g. ${chalk4.white('"r3ply.config.toml"')}.)`)}`);
|
|
862
|
+
else if (relative_files.length > 1)
|
|
863
|
+
throw new util.CLIError(
|
|
864
|
+
`Multiple matches found: ${JSON.stringify(relative_files, null, 2)}`
|
|
865
|
+
);
|
|
866
|
+
else return path3.resolve(project_dir, relative_files[0]);
|
|
867
|
+
}
|
|
868
|
+
})();
|
|
869
|
+
return Result2.safe(full_config_path);
|
|
870
|
+
}
|
|
871
|
+
project2.get_site_config_path = get_site_config_path;
|
|
872
|
+
async function parse_site_config(cwd, config_path) {
|
|
873
|
+
const parsed_site_config = get_site_config_path(cwd, config_path).then((full_config_path) => util.unsafeUnwrap(full_config_path)).then((full_config_path) => {
|
|
874
|
+
return fs2.promises.readFile(full_config_path).then((site_config_bytes) => site_config_bytes.toString()).then(
|
|
875
|
+
(site_config_str) => full_config_path.endsWith(".toml") ? TOML4.parse(site_config_str) : JSON.parse(site_config_str)
|
|
876
|
+
).then((site_config_json) => R3plySiteConfig.parse(site_config_json));
|
|
877
|
+
});
|
|
878
|
+
return Result2.safe(parsed_site_config);
|
|
879
|
+
}
|
|
880
|
+
project2.parse_site_config = parse_site_config;
|
|
881
|
+
async function get_site_config(cwd, config_path) {
|
|
882
|
+
const site_config = parse_site_config(cwd, config_path).then((parsed_site_config) => util.unsafeUnwrap(parsed_site_config)).then(
|
|
883
|
+
(parsed_site_config) => Option(parsed_site_config.value).expect(
|
|
884
|
+
JSON.stringify(parsed_site_config.errors, null, 2)
|
|
885
|
+
)
|
|
886
|
+
);
|
|
887
|
+
return Result2.safe(site_config);
|
|
888
|
+
}
|
|
889
|
+
project2.get_site_config = get_site_config;
|
|
890
|
+
async function resolve_config(cwd, config_option) {
|
|
891
|
+
const site_config_path = await resolve_config_path(cwd, config_option);
|
|
892
|
+
return util.unsafeUnwrap(await get_site_config(cwd, site_config_path));
|
|
893
|
+
}
|
|
894
|
+
project2.resolve_config = resolve_config;
|
|
895
|
+
async function resolve_config_path(cwd, config_option) {
|
|
896
|
+
let site_config_path;
|
|
897
|
+
if (config_option) {
|
|
898
|
+
site_config_path = util.unsafeUnwrap(
|
|
899
|
+
await project2.get_site_config_path(cwd, config_option)
|
|
900
|
+
);
|
|
901
|
+
} else {
|
|
902
|
+
const project_dir = util.unsafeUnwrap(await project2.find_project_dir(cwd));
|
|
903
|
+
const cli_settings = util.unsafeUnwrap(
|
|
904
|
+
await project2.get_cli_settings(cwd)
|
|
905
|
+
);
|
|
906
|
+
site_config_path = util.unsafeUnwrap(
|
|
907
|
+
await project2.get_site_config_path(
|
|
908
|
+
project_dir,
|
|
909
|
+
cli_settings.default_config_path
|
|
910
|
+
)
|
|
911
|
+
);
|
|
912
|
+
}
|
|
913
|
+
return site_config_path;
|
|
914
|
+
}
|
|
915
|
+
project2.resolve_config_path = resolve_config_path;
|
|
916
|
+
async function get_cli_system_config(cwd) {
|
|
917
|
+
const r3ply_dir = find_r3ply_dir(cwd);
|
|
918
|
+
const result = r3ply_dir.then((r3ply_dir2) => {
|
|
919
|
+
return fs2.promises.readFile(
|
|
920
|
+
path3.resolve(
|
|
921
|
+
util.unsafeUnwrap(r3ply_dir2),
|
|
922
|
+
CLI_SYSTEM_CONFIG_FILENAME
|
|
923
|
+
)
|
|
924
|
+
);
|
|
925
|
+
}).then((text) => {
|
|
926
|
+
return R3plySystemConfig.parse(text.toString()).value;
|
|
927
|
+
});
|
|
928
|
+
return Result2.safe(result);
|
|
929
|
+
}
|
|
930
|
+
project2.get_cli_system_config = get_cli_system_config;
|
|
931
|
+
async function get_cli_settings(cwd) {
|
|
932
|
+
const r3ply_dir = util.unsafeUnwrap(await project2.find_r3ply_dir(cwd));
|
|
933
|
+
const cli_settings_path = path3.resolve(r3ply_dir, CLI_SETTINGS_FILENAME);
|
|
934
|
+
const settings = Result2.safe(
|
|
935
|
+
fs2.promises.readFile(cli_settings_path).then((bytes) => bytes.toString()).then((settings2) => TOML4.parse(settings2))
|
|
936
|
+
);
|
|
937
|
+
return settings;
|
|
938
|
+
}
|
|
939
|
+
project2.get_cli_settings = get_cli_settings;
|
|
940
|
+
async function set_cli_settings(cwd, new_settings) {
|
|
941
|
+
const r3ply_dir = util.unsafeUnwrap(await project2.find_r3ply_dir(cwd));
|
|
942
|
+
const cli_settings_path = path3.resolve(r3ply_dir, CLI_SETTINGS_FILENAME);
|
|
943
|
+
return Result2.safe(
|
|
944
|
+
fs2.promises.writeFile(cli_settings_path, TOML4.stringify(new_settings))
|
|
945
|
+
);
|
|
946
|
+
}
|
|
947
|
+
project2.set_cli_settings = set_cli_settings;
|
|
948
|
+
project2.dereference_local_file = async (base_uri, file_uri_ref) => {
|
|
949
|
+
const resolver = resolve_file_relative_to_site_config(base_uri);
|
|
950
|
+
return resolver(file_uri_ref);
|
|
951
|
+
};
|
|
952
|
+
function resolve_file_relative_to_site_config(config_path) {
|
|
953
|
+
const file_resolver = (file_uri) => {
|
|
954
|
+
if (file_uri) {
|
|
955
|
+
const r3ply_site_config_dir = path3.dirname(config_path);
|
|
956
|
+
const fully_qualified_file_path = path3.join(
|
|
957
|
+
r3ply_site_config_dir,
|
|
958
|
+
file_uri
|
|
959
|
+
);
|
|
960
|
+
return fs2.promises.readFile(fully_qualified_file_path).then((file_at_path_bytes) => file_at_path_bytes.toString());
|
|
961
|
+
} else return Promise.resolve(void 0);
|
|
962
|
+
};
|
|
963
|
+
return file_resolver;
|
|
964
|
+
}
|
|
965
|
+
project2.resolve_file_relative_to_site_config = resolve_file_relative_to_site_config;
|
|
966
|
+
async function init_r3ply_project_at(cwd, { force, rotateKeys }, dir) {
|
|
967
|
+
const new_r3ply_dir = path3.join(cwd, dir ?? "", R3PLY_DIR);
|
|
968
|
+
let parent_project_exists = find_r3ply_dir(new_r3ply_dir);
|
|
969
|
+
const signet_key = rotateKeys ? crypto.randomBytes(32).toString("base64") : project2.DEFAULT_SIGNET_KEY;
|
|
970
|
+
const email_key = rotateKeys ? crypto.randomBytes(32).toString("base64") : project2.DEFAULT_EMAIL_KEY;
|
|
971
|
+
let system_config = R3plySystemConfig({
|
|
972
|
+
domains: [project2.DEFAULT_R3PLY_DOMAIN],
|
|
973
|
+
admin: [
|
|
974
|
+
{
|
|
975
|
+
name: "Guybrush Threepwood",
|
|
976
|
+
email: "guybrush@example.com"
|
|
977
|
+
}
|
|
978
|
+
]
|
|
979
|
+
}).value;
|
|
980
|
+
const result = parent_project_exists.then((parent_project_exists2) => {
|
|
981
|
+
const file_access = Result2.safe(fs2.promises.access(new_r3ply_dir));
|
|
982
|
+
return file_access.then(async (file_access2) => {
|
|
983
|
+
if (file_access2.isErr() || force) {
|
|
984
|
+
if (file_access2.isOk() && force) {
|
|
985
|
+
await fs2.promises.rm(new_r3ply_dir, {
|
|
986
|
+
recursive: true,
|
|
987
|
+
force: true
|
|
988
|
+
});
|
|
989
|
+
parent_project_exists2 = await find_r3ply_dir(new_r3ply_dir);
|
|
990
|
+
}
|
|
991
|
+
if (parent_project_exists2.isOk()) {
|
|
992
|
+
throw new util.CLIError(
|
|
993
|
+
`Nested r3ply project. There is already a parent directory initialized at ${chalk4.blue("`" + parent_project_exists2.unwrap() + "`")}.${chalk4.yellow("(Nested projects can lead to strange effects)")}`
|
|
994
|
+
);
|
|
995
|
+
}
|
|
996
|
+
return fs2.promises.mkdir(new_r3ply_dir).then(() => {
|
|
997
|
+
return fs2.promises.writeFile(
|
|
998
|
+
path3.resolve(new_r3ply_dir, SIGNET_KEY_FILENAME),
|
|
999
|
+
signet_key
|
|
1000
|
+
).then((_) => {
|
|
1001
|
+
return fs2.promises.writeFile(
|
|
1002
|
+
path3.resolve(new_r3ply_dir, ENCRYPT_EMAIL_KEY_FILENAME),
|
|
1003
|
+
email_key
|
|
1004
|
+
);
|
|
1005
|
+
}).then((_) => {
|
|
1006
|
+
return fs2.promises.writeFile(
|
|
1007
|
+
path3.resolve(new_r3ply_dir, CLI_SYSTEM_CONFIG_FILENAME),
|
|
1008
|
+
TOML4.stringify(system_config)
|
|
1009
|
+
);
|
|
1010
|
+
}).then((_) => {
|
|
1011
|
+
return fs2.promises.writeFile(
|
|
1012
|
+
path3.resolve(new_r3ply_dir, CLI_SETTINGS_FILENAME),
|
|
1013
|
+
""
|
|
1014
|
+
);
|
|
1015
|
+
}).then((_) => {
|
|
1016
|
+
return fs2.promises.writeFile(
|
|
1017
|
+
path3.resolve(new_r3ply_dir, ".gitignore"),
|
|
1018
|
+
"static/cache"
|
|
1019
|
+
);
|
|
1020
|
+
}).then((_) => {
|
|
1021
|
+
return {
|
|
1022
|
+
r3ply_dir: new_r3ply_dir,
|
|
1023
|
+
signet_key,
|
|
1024
|
+
encrypt_email_key: email_key
|
|
1025
|
+
};
|
|
1026
|
+
});
|
|
1027
|
+
});
|
|
1028
|
+
} else {
|
|
1029
|
+
throw new util.CLIError(
|
|
1030
|
+
`Project already initialized at \`${chalk4.white(path3.dirname(new_r3ply_dir))}\`.`
|
|
1031
|
+
);
|
|
1032
|
+
}
|
|
1033
|
+
});
|
|
1034
|
+
});
|
|
1035
|
+
return Result2.safe(result);
|
|
1036
|
+
}
|
|
1037
|
+
project2.init_r3ply_project_at = init_r3ply_project_at;
|
|
1038
|
+
async function get_keys(cwd) {
|
|
1039
|
+
return find_r3ply_dir(cwd).then((r3ply_dir) => {
|
|
1040
|
+
const signet_key = fs2.promises.readFile(
|
|
1041
|
+
path3.resolve(util.unsafeUnwrap(r3ply_dir), SIGNET_KEY_FILENAME)
|
|
1042
|
+
).then((buffer) => buffer.toString());
|
|
1043
|
+
const encrypt_email_key = fs2.promises.readFile(
|
|
1044
|
+
path3.resolve(
|
|
1045
|
+
util.unsafeUnwrap(r3ply_dir),
|
|
1046
|
+
ENCRYPT_EMAIL_KEY_FILENAME
|
|
1047
|
+
)
|
|
1048
|
+
).then((buffer) => buffer.toString());
|
|
1049
|
+
return Promise.all([signet_key, encrypt_email_key]).then(
|
|
1050
|
+
([signet_key2, encrypt_email_key2]) => {
|
|
1051
|
+
return { signet_key: signet_key2, encrypt_email_key: encrypt_email_key2 };
|
|
1052
|
+
}
|
|
1053
|
+
);
|
|
1054
|
+
});
|
|
1055
|
+
}
|
|
1056
|
+
project2.get_keys = get_keys;
|
|
1057
|
+
async function set_default_cli_config_path(cwd, config_path) {
|
|
1058
|
+
const project_dir = util.unsafeUnwrap(await find_project_dir(cwd));
|
|
1059
|
+
const proposed_path = path3.resolve(project_dir, config_path);
|
|
1060
|
+
const relative = path3.relative(project_dir, proposed_path);
|
|
1061
|
+
if (relative.startsWith(".."))
|
|
1062
|
+
throw new util.CLIError(
|
|
1063
|
+
`Config path can not be outside of project directory "${project_dir}"`
|
|
1064
|
+
);
|
|
1065
|
+
const settings = util.unsafeUnwrap(await get_cli_settings(cwd));
|
|
1066
|
+
settings["default_config_path"] = config_path;
|
|
1067
|
+
return set_cli_settings(cwd, settings);
|
|
1068
|
+
}
|
|
1069
|
+
project2.set_default_cli_config_path = set_default_cli_config_path;
|
|
1070
|
+
})(project || (project = {}));
|
|
1071
|
+
var generate;
|
|
1072
|
+
((generate2) => {
|
|
1073
|
+
function date(floor = Math.floor(Date.now() / 1e3) - 31536e4, ceiling = Math.floor(Date.now() / 1e3)) {
|
|
1074
|
+
return util.random_int(ceiling, floor) * 1e3;
|
|
1075
|
+
}
|
|
1076
|
+
generate2.date = date;
|
|
1077
|
+
function email_addr() {
|
|
1078
|
+
const first = first_names[util.random_int(first_names.length)];
|
|
1079
|
+
const last = last_names[util.random_int(last_names.length)];
|
|
1080
|
+
const name = `${first} ${last}`;
|
|
1081
|
+
const birthyear = util.random_int(1990, 1899);
|
|
1082
|
+
const domain = `${domains[util.random_int(domains.length)]}.${tlds[util.random_int(tlds.length)]}`;
|
|
1083
|
+
const local = `${first}.${Math.random() > 0.5 ? birthyear : last}`;
|
|
1084
|
+
const addr = `${local}@${domain}`;
|
|
1085
|
+
const mailbox4 = `${first} ${last} <${addr}>`;
|
|
1086
|
+
return { name, domain, local, addr, mailbox: mailbox4 };
|
|
1087
|
+
}
|
|
1088
|
+
generate2.email_addr = email_addr;
|
|
1089
|
+
function message_id(domain) {
|
|
1090
|
+
return `${crypto.randomUUID()}@${domain}`;
|
|
1091
|
+
}
|
|
1092
|
+
generate2.message_id = message_id;
|
|
1093
|
+
function subject(url) {
|
|
1094
|
+
const site_path = site_paths[util.random_int(0, site_paths.length)];
|
|
1095
|
+
const site_slug = site_slugs[util.random_int(0, site_slugs.length)];
|
|
1096
|
+
return new URL(path3.join(site_path, site_slug), url).href;
|
|
1097
|
+
}
|
|
1098
|
+
generate2.subject = subject;
|
|
1099
|
+
function comment_body(seed) {
|
|
1100
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
1101
|
+
const __dirname = path3.dirname(__filename);
|
|
1102
|
+
const modelPath = path3.resolve(__dirname, "comments-markov-model.json");
|
|
1103
|
+
const model_data = fs2.promises.readFile(modelPath, "utf-8");
|
|
1104
|
+
const markov = model_data.then(
|
|
1105
|
+
(model_data2) => RiMarkov.fromJSON(model_data2)
|
|
1106
|
+
);
|
|
1107
|
+
return markov.then(
|
|
1108
|
+
(markov2) => markov2.generate({
|
|
1109
|
+
maxLength: 36,
|
|
1110
|
+
temperature: 1,
|
|
1111
|
+
allowDuplicates: true,
|
|
1112
|
+
seed
|
|
1113
|
+
})
|
|
1114
|
+
);
|
|
1115
|
+
}
|
|
1116
|
+
generate2.comment_body = comment_body;
|
|
1117
|
+
async function email(site_domain, r3ply_domain, options) {
|
|
1118
|
+
let from;
|
|
1119
|
+
if (options?.from) {
|
|
1120
|
+
from = parse_email_addr(options.from);
|
|
1121
|
+
} else {
|
|
1122
|
+
from = generate2.email_addr();
|
|
1123
|
+
}
|
|
1124
|
+
const message_id2 = options?.messageId || generate2.message_id(from.domain);
|
|
1125
|
+
const date2 = dayjs(options?.date ?? new Date(generate2.date()));
|
|
1126
|
+
const to = parse_email_addr(options?.to || `${site_domain}@${r3ply_domain}`);
|
|
1127
|
+
const subject_url = new URL(`https://${to.local}/`);
|
|
1128
|
+
const subject2 = options?.subject || generate2.subject(subject_url);
|
|
1129
|
+
const body = options?.body || await generate2.comment_body().catch(
|
|
1130
|
+
() => generate2.comment_body().catch(() => generate2.comment_body())
|
|
1131
|
+
);
|
|
1132
|
+
const email2 = Result2.safe(
|
|
1133
|
+
() => build_email(
|
|
1134
|
+
message_id2,
|
|
1135
|
+
BigInt(date2.unix()),
|
|
1136
|
+
from.name,
|
|
1137
|
+
from.addr,
|
|
1138
|
+
to.addr,
|
|
1139
|
+
subject2,
|
|
1140
|
+
body
|
|
1141
|
+
)
|
|
1142
|
+
);
|
|
1143
|
+
return email2.unwrap();
|
|
1144
|
+
}
|
|
1145
|
+
generate2.email = email;
|
|
1146
|
+
function parse_email_addr(email2) {
|
|
1147
|
+
const result = mailbox(email2);
|
|
1148
|
+
if (!result.ok)
|
|
1149
|
+
throw new util.CLIError(
|
|
1150
|
+
`Unable to parse email ${email2}, errors: ${JSON.stringify(result)}.`
|
|
1151
|
+
);
|
|
1152
|
+
result.local = result.local.replace(/^"(.*)"$/, "$1");
|
|
1153
|
+
return result;
|
|
1154
|
+
}
|
|
1155
|
+
async function signet(key, cli_system, {
|
|
1156
|
+
domain,
|
|
1157
|
+
r3ply,
|
|
1158
|
+
issued,
|
|
1159
|
+
label
|
|
1160
|
+
}) {
|
|
1161
|
+
if (r3ply == project.DEFAULT_R3PLY_DOMAIN) {
|
|
1162
|
+
return SignetIssuer(key, cli_system)(domain, r3ply, {
|
|
1163
|
+
issued_date: issued,
|
|
1164
|
+
label
|
|
1165
|
+
});
|
|
1166
|
+
} else {
|
|
1167
|
+
return fetch(
|
|
1168
|
+
new URL(
|
|
1169
|
+
`https://${r3ply}/signet/${domain}${issued ? `/${issued}` : ""}`
|
|
1170
|
+
)
|
|
1171
|
+
).then((response) => {
|
|
1172
|
+
if (response.ok)
|
|
1173
|
+
return response.json().then((json) => {
|
|
1174
|
+
return { ...json, label };
|
|
1175
|
+
});
|
|
1176
|
+
else
|
|
1177
|
+
return response.text().then((text) => {
|
|
1178
|
+
throw new util.CLIError(text);
|
|
1179
|
+
});
|
|
1180
|
+
});
|
|
1181
|
+
}
|
|
1182
|
+
}
|
|
1183
|
+
generate2.signet = signet;
|
|
1184
|
+
function config(site_config) {
|
|
1185
|
+
return TOML4.stringify(site_config);
|
|
1186
|
+
}
|
|
1187
|
+
generate2.config = config;
|
|
1188
|
+
})(generate || (generate = {}));
|
|
1189
|
+
var domains = [
|
|
1190
|
+
"ghostpirate",
|
|
1191
|
+
"lemonhead",
|
|
1192
|
+
"grog",
|
|
1193
|
+
"monkeyisland",
|
|
1194
|
+
"tryscummvm",
|
|
1195
|
+
"bananapicker",
|
|
1196
|
+
"meleeisland",
|
|
1197
|
+
"stanzboatz",
|
|
1198
|
+
"chickenpulley",
|
|
1199
|
+
"drinkgrog",
|
|
1200
|
+
"dontdrinkgrog"
|
|
1201
|
+
];
|
|
1202
|
+
var tlds = ["com", "net", "us", "biz", "org", "io"];
|
|
1203
|
+
var first_names = [
|
|
1204
|
+
"LeChuck",
|
|
1205
|
+
"Guybrush",
|
|
1206
|
+
"Elaine",
|
|
1207
|
+
"Herman",
|
|
1208
|
+
"Stan",
|
|
1209
|
+
"Otis",
|
|
1210
|
+
"Wally",
|
|
1211
|
+
"Carla",
|
|
1212
|
+
"Meathook",
|
|
1213
|
+
"Morgan",
|
|
1214
|
+
"Murray",
|
|
1215
|
+
"Bob",
|
|
1216
|
+
"Horatio",
|
|
1217
|
+
"Ignatius",
|
|
1218
|
+
"Winslow",
|
|
1219
|
+
"Charles",
|
|
1220
|
+
"Kate",
|
|
1221
|
+
"Largo",
|
|
1222
|
+
"Rum",
|
|
1223
|
+
"Guy",
|
|
1224
|
+
"Haggis",
|
|
1225
|
+
"Cutthroat",
|
|
1226
|
+
"Bobby",
|
|
1227
|
+
"Frank",
|
|
1228
|
+
"Plunder",
|
|
1229
|
+
"Crimpdigit",
|
|
1230
|
+
"Jolene",
|
|
1231
|
+
"Dinghy",
|
|
1232
|
+
"Belinda",
|
|
1233
|
+
"Betsy",
|
|
1234
|
+
"Dread",
|
|
1235
|
+
"Esteban",
|
|
1236
|
+
"Rapp",
|
|
1237
|
+
"Doro",
|
|
1238
|
+
"Santiago",
|
|
1239
|
+
"Betty",
|
|
1240
|
+
"Biff",
|
|
1241
|
+
"Clarence",
|
|
1242
|
+
"Indy",
|
|
1243
|
+
"Henry",
|
|
1244
|
+
"Sallah",
|
|
1245
|
+
"Marion",
|
|
1246
|
+
"Sophia",
|
|
1247
|
+
"Jock",
|
|
1248
|
+
"Shorty",
|
|
1249
|
+
"Kazim",
|
|
1250
|
+
"Marcus",
|
|
1251
|
+
"Vogel"
|
|
1252
|
+
];
|
|
1253
|
+
var last_names = [
|
|
1254
|
+
"'Ghost' Pirate",
|
|
1255
|
+
"Threepwood",
|
|
1256
|
+
"Marley",
|
|
1257
|
+
"Toothrot",
|
|
1258
|
+
"Sunderson",
|
|
1259
|
+
"Fettucini",
|
|
1260
|
+
"Scabb",
|
|
1261
|
+
"Rottingham",
|
|
1262
|
+
"Ozzie",
|
|
1263
|
+
"Seepgood",
|
|
1264
|
+
"Van Helgen",
|
|
1265
|
+
"de Singe",
|
|
1266
|
+
"Bloodnose",
|
|
1267
|
+
"D'Oro",
|
|
1268
|
+
"Weatherby",
|
|
1269
|
+
"Nipikin",
|
|
1270
|
+
"Pegnose",
|
|
1271
|
+
"Hook",
|
|
1272
|
+
"Flambe",
|
|
1273
|
+
"Griswold",
|
|
1274
|
+
"Booty",
|
|
1275
|
+
"Bone",
|
|
1276
|
+
"Lemonhead",
|
|
1277
|
+
"Terror",
|
|
1278
|
+
"Snugglecakes",
|
|
1279
|
+
"Hartman",
|
|
1280
|
+
"Deadeye",
|
|
1281
|
+
"Graves",
|
|
1282
|
+
"McMutton",
|
|
1283
|
+
"Tannen",
|
|
1284
|
+
"Seagull",
|
|
1285
|
+
"Plank",
|
|
1286
|
+
"Drake",
|
|
1287
|
+
"Montezuma",
|
|
1288
|
+
"Ravenwood",
|
|
1289
|
+
"Donovan",
|
|
1290
|
+
"Oxley",
|
|
1291
|
+
"Brody",
|
|
1292
|
+
"Katanga",
|
|
1293
|
+
"Molotov",
|
|
1294
|
+
"Spalko",
|
|
1295
|
+
"Reinhardt",
|
|
1296
|
+
"Belloq",
|
|
1297
|
+
"Dietrich",
|
|
1298
|
+
"McHale",
|
|
1299
|
+
"Voller",
|
|
1300
|
+
"Strasser",
|
|
1301
|
+
"Krell",
|
|
1302
|
+
"Egon"
|
|
1303
|
+
];
|
|
1304
|
+
var site_slugs = [
|
|
1305
|
+
"how-I-met-herman-toothrot",
|
|
1306
|
+
"finding-dads-diary",
|
|
1307
|
+
"lechucks-curse-explained",
|
|
1308
|
+
"secrets-of-monkey-island",
|
|
1309
|
+
"guybrushs-best-comebacks",
|
|
1310
|
+
"stan-and-his-neverending-sales",
|
|
1311
|
+
"elaine-marley-the-real-hero",
|
|
1312
|
+
"murray-the-talking-skull",
|
|
1313
|
+
"top-10-insults-from-monkey-island",
|
|
1314
|
+
"puzzle-solutions-you-forgot",
|
|
1315
|
+
"escape-from-monkey-island-review",
|
|
1316
|
+
"where-is-plunder-island",
|
|
1317
|
+
"fettucini-brothers-circus",
|
|
1318
|
+
"monkey-island-easter-eggs",
|
|
1319
|
+
"worst-ways-to-die-in-monkey-island",
|
|
1320
|
+
"indiana-jones-and-the-fate-of-atlantis-retrospective",
|
|
1321
|
+
"top-5-foes-of-indiana-jones",
|
|
1322
|
+
"finding-the-lost-dialog-of-plato",
|
|
1323
|
+
"short-rounds-missing-adventure",
|
|
1324
|
+
"best-action-scenes-in-indy-games",
|
|
1325
|
+
"henry-jones-sr-quotes",
|
|
1326
|
+
"why-marion-ravenwood-rules",
|
|
1327
|
+
"greatest-puzzles-in-fate-of-atlantis",
|
|
1328
|
+
"monkey-kombat-strategy-guide",
|
|
1329
|
+
"the-many-faces-of-lechuck",
|
|
1330
|
+
"deadly-traps-in-indiana-jones-games",
|
|
1331
|
+
"sophia-hapgood-character-analysis",
|
|
1332
|
+
"replaying-last-crusade",
|
|
1333
|
+
"jock-lindsey-indianas-best-sidekick",
|
|
1334
|
+
"marcus-brody-memorial",
|
|
1335
|
+
"did-the-nazis-win-in-fate-of-atlantis",
|
|
1336
|
+
"monkey-island-hidden-dialogue",
|
|
1337
|
+
"lost-scenes-from-indiana-jones-games",
|
|
1338
|
+
"why-we-need-more-point-and-click-adventures",
|
|
1339
|
+
"best-inventory-items-in-monkey-island",
|
|
1340
|
+
"fate-of-atlantis-secret-ending",
|
|
1341
|
+
"best-quotes-from-monkey-island",
|
|
1342
|
+
"worst-decisions-in-indy-games",
|
|
1343
|
+
"stan-never-blinks-conspiracy",
|
|
1344
|
+
"top-5-worst-ways-to-lose-in-monkey-island",
|
|
1345
|
+
"replaying-monkey-island-in-2025",
|
|
1346
|
+
"who-really-invented-grog",
|
|
1347
|
+
"cut-content-from-monkey-island",
|
|
1348
|
+
"why-monkey-island-3a-needs-to-happen",
|
|
1349
|
+
"best-easter-eggs-in-indiana-jones-games",
|
|
1350
|
+
"toughest-fights-in-monkey-island",
|
|
1351
|
+
"horrible-ways-to-die-in-indiana-jones-games",
|
|
1352
|
+
"ultimate-guide-to-monkey-island-lore",
|
|
1353
|
+
"worst-npc-in-monkey-island",
|
|
1354
|
+
"is-guybrush-a-good-pirate"
|
|
1355
|
+
];
|
|
1356
|
+
var site_paths = [
|
|
1357
|
+
"blog",
|
|
1358
|
+
"posts",
|
|
1359
|
+
"articles",
|
|
1360
|
+
"reviews",
|
|
1361
|
+
"retrospectives",
|
|
1362
|
+
"guides",
|
|
1363
|
+
"walkthroughs",
|
|
1364
|
+
"tips",
|
|
1365
|
+
"secrets",
|
|
1366
|
+
"features",
|
|
1367
|
+
"history",
|
|
1368
|
+
"interviews",
|
|
1369
|
+
"behind-the-scenes",
|
|
1370
|
+
"lore",
|
|
1371
|
+
"characters",
|
|
1372
|
+
"analysis",
|
|
1373
|
+
"easter-eggs",
|
|
1374
|
+
"strategy",
|
|
1375
|
+
"rankings",
|
|
1376
|
+
"opinion"
|
|
1377
|
+
];
|
|
1378
|
+
var moderation;
|
|
1379
|
+
((moderation2) => {
|
|
1380
|
+
async function write_comment_locally(cwd, args, dryrun = false) {
|
|
1381
|
+
const project_dir = util.unsafeUnwrap(await project.find_project_dir(cwd));
|
|
1382
|
+
const proposed_path = path3.join(project_dir, args.relative_path);
|
|
1383
|
+
const path_rel_to_proj = path3.relative(project_dir, proposed_path);
|
|
1384
|
+
if (path_rel_to_proj.startsWith(".."))
|
|
1385
|
+
throw new util.CLIError(
|
|
1386
|
+
`Can't write comment to '${proposed_path}' because path is outside r3ply project directory!`
|
|
1387
|
+
);
|
|
1388
|
+
else {
|
|
1389
|
+
if (!dryrun) {
|
|
1390
|
+
fs2.writeFileSync(proposed_path, args.comment);
|
|
1391
|
+
}
|
|
1392
|
+
return proposed_path;
|
|
1393
|
+
}
|
|
1394
|
+
}
|
|
1395
|
+
moderation2.write_comment_locally = write_comment_locally;
|
|
1396
|
+
function mock_github_api_fetcher() {
|
|
1397
|
+
const result = async (args) => {
|
|
1398
|
+
const pr_num = 123;
|
|
1399
|
+
const result2 = {
|
|
1400
|
+
repo: {
|
|
1401
|
+
owner: args.repo_owner,
|
|
1402
|
+
name: args.repo_name,
|
|
1403
|
+
url: `https://github.com/${args.repo_owner}/${args.repo_name}`
|
|
1404
|
+
},
|
|
1405
|
+
comment: {
|
|
1406
|
+
path: args.new_comment_filepath
|
|
1407
|
+
},
|
|
1408
|
+
commit: {
|
|
1409
|
+
message: args.new_comment_filepath
|
|
1410
|
+
},
|
|
1411
|
+
pr: {
|
|
1412
|
+
branch: {
|
|
1413
|
+
base: args.target_branch,
|
|
1414
|
+
head: args.source_branch
|
|
1415
|
+
},
|
|
1416
|
+
url: `https://api.github.com/repos/${args.repo_owner}/${args.repo_name}/pulls/${pr_num}`,
|
|
1417
|
+
html_url: `https://github.com/${args.repo_owner}/${args.repo_name}/pulls/${pr_num}`,
|
|
1418
|
+
diff_url: `https://github.com/${args.repo_owner}/${args.repo_name}/pulls/${pr_num}.diff`,
|
|
1419
|
+
patch_url: `https://github.com/${args.repo_owner}/${args.repo_name}/pulls/${pr_num}.patch`,
|
|
1420
|
+
issue_url: `https://github.com/${args.repo_owner}/${args.repo_name}/issues/${pr_num}`,
|
|
1421
|
+
commits_url: `https://github.com/${args.repo_owner}/${args.repo_name}/pulls/${pr_num}/commits`,
|
|
1422
|
+
comments_url: `https://github.com/${args.repo_owner}/${args.repo_name}/pulls/${pr_num}/comments`,
|
|
1423
|
+
statuses_url: `https://github.com/${args.repo_owner}/${args.repo_name}/pulls/${pr_num}/statuses/6dcb09b5b57875f334f61aebed695e2e4193db5e`,
|
|
1424
|
+
number: pr_num,
|
|
1425
|
+
state: "open",
|
|
1426
|
+
title: args.pr?.msg_title ?? "",
|
|
1427
|
+
body: args.pr?.msg_body ?? "",
|
|
1428
|
+
created_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1429
|
+
commits: 1,
|
|
1430
|
+
additions: 1,
|
|
1431
|
+
deletions: 0,
|
|
1432
|
+
changed_files: 1
|
|
1433
|
+
}
|
|
1434
|
+
};
|
|
1435
|
+
return result2;
|
|
1436
|
+
};
|
|
1437
|
+
return result;
|
|
1438
|
+
}
|
|
1439
|
+
moderation2.mock_github_api_fetcher = mock_github_api_fetcher;
|
|
1440
|
+
})(moderation || (moderation = {}));
|
|
1441
|
+
|
|
1442
|
+
// src/cmds/init.ts
|
|
1443
|
+
import { SignetIssuer as SignetIssuer2 } from "@r3ply/lib";
|
|
1444
|
+
function init_cmd(cwd) {
|
|
1445
|
+
const init_cmd2 = new Command("init").description("initialize a new r3ply project (at current directory)").option(
|
|
1446
|
+
"--date <YYYY-MM-DD>",
|
|
1447
|
+
"set date of CLI issued signet",
|
|
1448
|
+
dayjs2().format("YYYY-MM-DD")
|
|
1449
|
+
).option("-f, --force", "overwrite an existing r3ply project", false).option(
|
|
1450
|
+
"--rotate-keys",
|
|
1451
|
+
"regenerate anonymization and encryption keys",
|
|
1452
|
+
false
|
|
1453
|
+
).action(async (options) => {
|
|
1454
|
+
if (options.force) tty_default.cmds.init.print_warn_force_is_set();
|
|
1455
|
+
return project.init_r3ply_project_at(cwd, options).then(async (result) => {
|
|
1456
|
+
const system_config = util.unsafeUnwrap(
|
|
1457
|
+
await project.get_cli_system_config(cwd)
|
|
1458
|
+
);
|
|
1459
|
+
const { r3ply_dir, signet_key } = util.unsafeUnwrap(result);
|
|
1460
|
+
const signet = await SignetIssuer2(signet_key, system_config)(
|
|
1461
|
+
project.DEFAULT_SITE_DOMAIN,
|
|
1462
|
+
project.DEFAULT_R3PLY_DOMAIN,
|
|
1463
|
+
{
|
|
1464
|
+
issued_date: options.date,
|
|
1465
|
+
label: project.DEFAULT_CLI_SIGNET_LABEL
|
|
1466
|
+
}
|
|
1467
|
+
);
|
|
1468
|
+
const signet_config = {
|
|
1469
|
+
site: [signet]
|
|
1470
|
+
};
|
|
1471
|
+
tty_default.cmds.init.print_initialized_new_project(
|
|
1472
|
+
r3ply_dir,
|
|
1473
|
+
signet_config,
|
|
1474
|
+
init_cmd2.parent.opts().format
|
|
1475
|
+
);
|
|
1476
|
+
});
|
|
1477
|
+
});
|
|
1478
|
+
return init_cmd2;
|
|
1479
|
+
}
|
|
1480
|
+
var init_default = init_cmd;
|
|
1481
|
+
|
|
1482
|
+
// src/cmds/config.ts
|
|
1483
|
+
import { Command as Command2, program } from "commander";
|
|
1484
|
+
import chalk5 from "chalk";
|
|
1485
|
+
function config_cmd(cwd) {
|
|
1486
|
+
const config_cmd2 = new Command2("config").description("r3ply config commands");
|
|
1487
|
+
config_cmd2.command("validate").description("validate your r3ply configuration").action(async () => {
|
|
1488
|
+
const parent_opts = config_cmd2.parent.opts();
|
|
1489
|
+
return config_validate(cwd, { ...parent_opts });
|
|
1490
|
+
});
|
|
1491
|
+
config_cmd2.command("set-default <path>").description("the default config path r3ply will use").action(async (path4) => {
|
|
1492
|
+
return util.unsafeUnwrap(await project.set_default_cli_config_path(cwd, path4));
|
|
1493
|
+
});
|
|
1494
|
+
const generate_alias = config_cmd2.command("generate").addHelpText(
|
|
1495
|
+
"before",
|
|
1496
|
+
chalk5.yellowBright("(alias for generate config, see usage below)\n")
|
|
1497
|
+
).description("Alias for generate config").action(async () => {
|
|
1498
|
+
program.parseAsync(["generate", "config"], { from: "user" });
|
|
1499
|
+
});
|
|
1500
|
+
generate_alias.helpInformation = (ctx) => {
|
|
1501
|
+
const foo = generate_default(cwd).commands.find((c) => c.name() == "config")?.helpInformation(ctx);
|
|
1502
|
+
return foo || "See `re generate config` for more";
|
|
1503
|
+
};
|
|
1504
|
+
return config_cmd2;
|
|
1505
|
+
}
|
|
1506
|
+
async function config_validate(cwd, options) {
|
|
1507
|
+
const site_config_path = await project.resolve_config_path(
|
|
1508
|
+
cwd,
|
|
1509
|
+
options.config
|
|
1510
|
+
);
|
|
1511
|
+
const site_config = util.unsafeUnwrap(
|
|
1512
|
+
await project.parse_site_config(cwd, site_config_path)
|
|
1513
|
+
);
|
|
1514
|
+
if (!site_config.valid)
|
|
1515
|
+
throw new util.CLIError(
|
|
1516
|
+
`config failed validation:
|
|
1517
|
+
|
|
1518
|
+
${JSON.stringify(site_config.errors, null, 2)}`
|
|
1519
|
+
);
|
|
1520
|
+
}
|
|
1521
|
+
var config_default = config_cmd;
|
|
1522
|
+
|
|
1523
|
+
// src/cmds/generate.ts
|
|
1524
|
+
import { Command as Command3 } from "commander";
|
|
1525
|
+
import dayjs3 from "dayjs";
|
|
1526
|
+
import { R3plySiteConfig as R3plySiteConfig2 } from "@r3ply/schema/config";
|
|
1527
|
+
import { mailbox as mailbox2 } from "typescript-mailbox-parser";
|
|
1528
|
+
import { Result as Result3 } from "oxide.ts";
|
|
1529
|
+
function generate_cmd(cwd) {
|
|
1530
|
+
const generate_cmd2 = new Command3("generate").description(
|
|
1531
|
+
"generate useful text"
|
|
1532
|
+
);
|
|
1533
|
+
const config_cmd2 = generate_cmd2.command("config").description("generate a config").option("--site <domain>", `site domain`, project.DEFAULT_SITE_DOMAIN).option(
|
|
1534
|
+
"--r3ply <r3ply domain>",
|
|
1535
|
+
`r3ply domain`,
|
|
1536
|
+
project.DEFAULT_R3PLY_DOMAIN
|
|
1537
|
+
).option(
|
|
1538
|
+
"--date <YYYY-MM-DD>",
|
|
1539
|
+
"date signet issued",
|
|
1540
|
+
dayjs3().format("YYYY-MM-DD")
|
|
1541
|
+
).option(
|
|
1542
|
+
"--label <string>",
|
|
1543
|
+
'e.g. "prod", "test"',
|
|
1544
|
+
project.DEFAULT_CLI_SIGNET_LABEL
|
|
1545
|
+
).option("--comments <comment-source>", "options are: email", "email").option("--moderation <channel>", "See below", "local").addHelpText(
|
|
1546
|
+
"after",
|
|
1547
|
+
`
|
|
1548
|
+
Moderation <channel> options: <github | webhook | local>`
|
|
1549
|
+
).option("--verbose", "include more defaults explicitly", false).action(async (options) => {
|
|
1550
|
+
const parent_opts = generate_cmd2.parent.opts();
|
|
1551
|
+
return generate_config(cwd, { ...options, ...parent_opts });
|
|
1552
|
+
});
|
|
1553
|
+
const mailto_cmd = generate_cmd2.command("mailto [body]").description("generate a one-off `mailto:` link").option("--to <email>", "to header of email", util.collect_opts, []).option("--subject <string>", "subject header of email").option("--cc <email>", "cc header of email", util.collect_opts, []).option("--bcc <email>", "bcc header of email", util.collect_opts, []).action(async (body, options) => {
|
|
1554
|
+
return generate_mailto(body, options);
|
|
1555
|
+
});
|
|
1556
|
+
const signet_cmd = generate_cmd2.command("signet").description("get a signet issued").option("--site <domain>", `site domain`, project.DEFAULT_SITE_DOMAIN).option(
|
|
1557
|
+
"--r3ply <r3ply domain>",
|
|
1558
|
+
`r3ply domain`,
|
|
1559
|
+
project.DEFAULT_R3PLY_DOMAIN
|
|
1560
|
+
).option("--date <YYYY-MM-DD>", "date issued", dayjs3().format("YYYY-MM-DD")).option(
|
|
1561
|
+
"--label <string>",
|
|
1562
|
+
'e.g. "prod", "test"',
|
|
1563
|
+
project.DEFAULT_CLI_SIGNET_LABEL
|
|
1564
|
+
).action(async (options) => {
|
|
1565
|
+
const parent_opts = generate_cmd2.parent.opts();
|
|
1566
|
+
return generate_signet(cwd, { ...options, ...parent_opts });
|
|
1567
|
+
});
|
|
1568
|
+
const email_cmd = generate_cmd2.command("email").description("Generate a comment as an email, based on your config").argument("[input]", "Input text (can also accept pipe)").option("--message-id <id>", "Message-ID header").option("--date <date>", "Date header", "now (UTC)").option("--from <address>", "From header").option("--to <address>", "To header").option("--subject <url | path>", "Email subject").option("--body <text>", "Email body").action(
|
|
1569
|
+
async (input, options) => {
|
|
1570
|
+
const parent_opts = generate_cmd2.parent.opts();
|
|
1571
|
+
return generate_email(cwd, input, { ...options, ...parent_opts });
|
|
1572
|
+
}
|
|
1573
|
+
);
|
|
1574
|
+
return generate_cmd2;
|
|
1575
|
+
}
|
|
1576
|
+
async function generate_config(cwd, options) {
|
|
1577
|
+
const site = await project.get_keys(cwd).then(
|
|
1578
|
+
(keys) => project.get_cli_system_config(cwd).then((system_config) => {
|
|
1579
|
+
return generate.signet(
|
|
1580
|
+
keys.signet_key,
|
|
1581
|
+
util.unsafeUnwrap(system_config),
|
|
1582
|
+
{
|
|
1583
|
+
domain: options.site,
|
|
1584
|
+
r3ply: options.r3ply,
|
|
1585
|
+
issued: options.date,
|
|
1586
|
+
label: options.label
|
|
1587
|
+
}
|
|
1588
|
+
);
|
|
1589
|
+
})
|
|
1590
|
+
);
|
|
1591
|
+
const minimal_comments_config = { [options.comments]: { enabled: true } };
|
|
1592
|
+
const minimal_github_config = {
|
|
1593
|
+
owner: "<YOUR_GITHUB_USERNAME>",
|
|
1594
|
+
repo: "<YOUR_PROJECT>",
|
|
1595
|
+
"file_path_{}": "comment_{{ comment.id[:8] }}.json"
|
|
1596
|
+
};
|
|
1597
|
+
const minimal_webhook_config = {
|
|
1598
|
+
url: "https://TODO"
|
|
1599
|
+
};
|
|
1600
|
+
const minimal_local_config = {
|
|
1601
|
+
"file_path_{}": "comment_{{ comment.id[:8] }}.json"
|
|
1602
|
+
};
|
|
1603
|
+
const parsed = R3plySiteConfig2({
|
|
1604
|
+
site: [{ ...site, label: options.label }],
|
|
1605
|
+
// if --verbose is false it's removed later (set here to preserve desired key order for TOML)
|
|
1606
|
+
comments: minimal_comments_config,
|
|
1607
|
+
moderation: {
|
|
1608
|
+
[options.moderation]: [
|
|
1609
|
+
(() => {
|
|
1610
|
+
if (options.moderation == "github") {
|
|
1611
|
+
return minimal_github_config;
|
|
1612
|
+
} else if (options.moderation == "webhook") {
|
|
1613
|
+
return minimal_webhook_config;
|
|
1614
|
+
} else if (options.moderation == "local") {
|
|
1615
|
+
return minimal_local_config;
|
|
1616
|
+
} else {
|
|
1617
|
+
throw new util.CLIError(`Unknown moderation type: ${options.moderation}`);
|
|
1618
|
+
}
|
|
1619
|
+
})()
|
|
1620
|
+
]
|
|
1621
|
+
}
|
|
1622
|
+
});
|
|
1623
|
+
const config_json = parsed.value;
|
|
1624
|
+
if (options.verbose == false) {
|
|
1625
|
+
config_json.comments = minimal_comments_config;
|
|
1626
|
+
}
|
|
1627
|
+
tty_default.cmds.generate.print_config(config_json, options.format);
|
|
1628
|
+
return;
|
|
1629
|
+
}
|
|
1630
|
+
async function generate_mailto(body, { to, subject, cc, bcc }) {
|
|
1631
|
+
if (!process.stdin.isTTY) {
|
|
1632
|
+
body = await new Promise((resolve, reject) => {
|
|
1633
|
+
let data = "";
|
|
1634
|
+
process.stdin.setEncoding("utf8");
|
|
1635
|
+
process.stdin.on("data", (chunk) => data += chunk);
|
|
1636
|
+
process.stdin.on("end", () => resolve(data));
|
|
1637
|
+
process.stdin.on("error", reject);
|
|
1638
|
+
});
|
|
1639
|
+
}
|
|
1640
|
+
function parse_email_addr(str) {
|
|
1641
|
+
const mb = mailbox2(str);
|
|
1642
|
+
if (!mb.ok)
|
|
1643
|
+
throw new util.CLIError(
|
|
1644
|
+
`Invalid email '${str}', errors ${JSON.stringify(mb.errors)}`
|
|
1645
|
+
);
|
|
1646
|
+
else {
|
|
1647
|
+
if (mb.name) {
|
|
1648
|
+
return `${mb.name} <${mb.addr}>`;
|
|
1649
|
+
} else return mb.addr;
|
|
1650
|
+
}
|
|
1651
|
+
}
|
|
1652
|
+
const params = {
|
|
1653
|
+
to: to.map(parse_email_addr).join(","),
|
|
1654
|
+
subject,
|
|
1655
|
+
cc: cc.map(parse_email_addr).join(","),
|
|
1656
|
+
bcc: bcc.map(parse_email_addr).join(","),
|
|
1657
|
+
body: body ? body.replace(/\r?\n/g, "\r\n") : void 0
|
|
1658
|
+
};
|
|
1659
|
+
const query = Object.entries(params).filter(([_, v]) => v !== void 0 && v !== "").map(([k, v]) => `?${encodeURIComponent(k)}=${encodeURIComponent(v)}`).join("&");
|
|
1660
|
+
tty_default.cmds.generate.print_mail_to_link(query);
|
|
1661
|
+
}
|
|
1662
|
+
async function generate_signet(cwd, options) {
|
|
1663
|
+
const keys = await project.get_keys(cwd);
|
|
1664
|
+
const cli_system_config = util.unsafeUnwrap(
|
|
1665
|
+
await project.get_cli_system_config(cwd)
|
|
1666
|
+
);
|
|
1667
|
+
const signet = await generate.signet(keys.signet_key, cli_system_config, {
|
|
1668
|
+
domain: options.site,
|
|
1669
|
+
r3ply: options.r3ply,
|
|
1670
|
+
issued: options.date,
|
|
1671
|
+
label: options.label
|
|
1672
|
+
});
|
|
1673
|
+
tty_default.cmds.generate.print_signet(signet, options.format);
|
|
1674
|
+
}
|
|
1675
|
+
async function generate_email(cwd, input, options) {
|
|
1676
|
+
if (options.date == "now (UTC)") options.date = (/* @__PURE__ */ new Date()).toUTCString();
|
|
1677
|
+
let site_config = await project.resolve_config(
|
|
1678
|
+
cwd,
|
|
1679
|
+
options.config
|
|
1680
|
+
);
|
|
1681
|
+
const site = site_config.site[util.random_int(site_config.site.length)];
|
|
1682
|
+
if (!input && !process.stdin.isTTY) {
|
|
1683
|
+
input = await util.read_stdin();
|
|
1684
|
+
}
|
|
1685
|
+
const email = Result3.safe(
|
|
1686
|
+
// --body overrides input
|
|
1687
|
+
generate.email(site.domain, site.r3ply, { body: input, ...options })
|
|
1688
|
+
);
|
|
1689
|
+
await email.then(async (email2) => {
|
|
1690
|
+
if (email2.isOk()) {
|
|
1691
|
+
tty_default.cmds.generate.print_email(email2.unwrap());
|
|
1692
|
+
} else {
|
|
1693
|
+
throw email2.unwrapErr();
|
|
1694
|
+
}
|
|
1695
|
+
});
|
|
1696
|
+
}
|
|
1697
|
+
var generate_default = generate_cmd;
|
|
1698
|
+
|
|
1699
|
+
// src/cmds/simulate.ts
|
|
1700
|
+
import { Command as Command4 } from "commander";
|
|
1701
|
+
import { Result as Result4 } from "oxide.ts";
|
|
1702
|
+
import { R3ply, moderation as mod_todo2, util as r3ply_util2 } from "@r3ply/lib";
|
|
1703
|
+
import { mailbox as mailbox3 } from "typescript-mailbox-parser";
|
|
1704
|
+
function simulate_cmd(cwd) {
|
|
1705
|
+
const simulate_cmd2 = new Command4("simulate").description(
|
|
1706
|
+
"simulate receiving a comment using your r3ply config"
|
|
1707
|
+
);
|
|
1708
|
+
simulate_cmd2.command("email").argument("[input]", "Input text (can also accept pipe)").option("--moderate", "Moderate comment (local-only)", false).option("--dry-run", "Print output only", false).option("--message-id <id>", "Message-ID header").option("--date <date>", "Date header", "now (UTC)").option("--from <address>", "From header").option("--to <address>", "To header").option("--subject <url | path>", "Email subject").option("--body <text>", "Email body").option("--no-heading", "Hide headings for each stage of simulation", true).option(
|
|
1709
|
+
"-q, --quiet [stage...]",
|
|
1710
|
+
`silence output at \`stages\` (or all output if stages is blank).`,
|
|
1711
|
+
util.split_list
|
|
1712
|
+
).option(
|
|
1713
|
+
"-f, --filter [stage...]",
|
|
1714
|
+
`filter output at \`stages\` (or all output if stages is blank).`,
|
|
1715
|
+
util.split_list
|
|
1716
|
+
).addHelpText(
|
|
1717
|
+
"after",
|
|
1718
|
+
`
|
|
1719
|
+
Filtering/Silencing:
|
|
1720
|
+
<stage> = <email | config | prescreen | receive | deliverable | prepare | comment | moderation | notify>
|
|
1721
|
+
For substages add \`=\` after the stage name. Options are config=<site | system>, moderation=<github | webhook | local>
|
|
1722
|
+
If a substage is an array you can append an underscore + index to specify which element, e.g. moderation=local_0`
|
|
1723
|
+
).addHelpText(
|
|
1724
|
+
"after",
|
|
1725
|
+
`
|
|
1726
|
+
Examples:
|
|
1727
|
+
$ cat hello.txt | re simulate email --filter comment
|
|
1728
|
+
$ re simulate email --subject /demo/ --silence prescreen,receive,deliverable
|
|
1729
|
+
$ re simulate email --moderate --dry-run --body "testing" --filter comment,moderation=local_0`
|
|
1730
|
+
).action(
|
|
1731
|
+
async (input, options, cmd) => {
|
|
1732
|
+
const parent_opts = simulate_cmd2.parent.opts();
|
|
1733
|
+
return simulate(cwd, input, { ...options, ...parent_opts });
|
|
1734
|
+
}
|
|
1735
|
+
);
|
|
1736
|
+
return simulate_cmd2;
|
|
1737
|
+
}
|
|
1738
|
+
async function simulate(cwd, input, options) {
|
|
1739
|
+
if (options.date == "now (UTC)") options.date = (/* @__PURE__ */ new Date()).toUTCString();
|
|
1740
|
+
let site_config_path = await project.resolve_config_path(
|
|
1741
|
+
cwd,
|
|
1742
|
+
options.config
|
|
1743
|
+
);
|
|
1744
|
+
let site_config_result = await Result4.safe(
|
|
1745
|
+
project.resolve_config(cwd, options.config)
|
|
1746
|
+
);
|
|
1747
|
+
let site_config = site_config_result.expect(
|
|
1748
|
+
"Error while opening config (hint: run `re config validate` to debug)"
|
|
1749
|
+
);
|
|
1750
|
+
site_config = await r3ply_util2.config.resolve_references(
|
|
1751
|
+
site_config,
|
|
1752
|
+
site_config_path,
|
|
1753
|
+
project.dereference_local_file
|
|
1754
|
+
);
|
|
1755
|
+
const cli_system_config = util.unsafeUnwrap(
|
|
1756
|
+
await project.get_cli_system_config(cwd)
|
|
1757
|
+
);
|
|
1758
|
+
const signet = ((to) => {
|
|
1759
|
+
let site_domain = (() => {
|
|
1760
|
+
if (to) {
|
|
1761
|
+
const mb = mailbox3(to);
|
|
1762
|
+
if (!mb.ok)
|
|
1763
|
+
throw new util.CLIError(
|
|
1764
|
+
`Unable to parse --to '${to}', reasons: ${JSON.stringify(mb)}`
|
|
1765
|
+
);
|
|
1766
|
+
else {
|
|
1767
|
+
return mb.local;
|
|
1768
|
+
}
|
|
1769
|
+
} else {
|
|
1770
|
+
return project.DEFAULT_SITE_DOMAIN;
|
|
1771
|
+
}
|
|
1772
|
+
})();
|
|
1773
|
+
const site = site_config.site.find((k) => k.domain == site_domain);
|
|
1774
|
+
if (site) {
|
|
1775
|
+
return site;
|
|
1776
|
+
} else {
|
|
1777
|
+
return site_config.site[util.random_int(site_config.site.length)];
|
|
1778
|
+
}
|
|
1779
|
+
})(options.to);
|
|
1780
|
+
if (!input && !process.stdin.isTTY) input = await util.read_stdin();
|
|
1781
|
+
const email = await generate.email(signet.domain, signet.r3ply, { body: input, ...options }).then((email2) => {
|
|
1782
|
+
tty_default.cmds.simulate.print_comment_via_email_initial(email2, options);
|
|
1783
|
+
return email2;
|
|
1784
|
+
});
|
|
1785
|
+
const keys = await project.get_keys(cwd);
|
|
1786
|
+
const r3ply = R3ply(cli_system_config);
|
|
1787
|
+
const file_writer = (args) => moderation.write_comment_locally(cwd, args, options.dryRun);
|
|
1788
|
+
const local_moderation_channel = mod_todo2.LocalModeration(file_writer);
|
|
1789
|
+
const github_moderation_channel = mod_todo2.GitHubModeration(
|
|
1790
|
+
moderation.mock_github_api_fetcher()
|
|
1791
|
+
);
|
|
1792
|
+
const handle_email_comment = r3ply.comments.viaEmail(
|
|
1793
|
+
keys.signet_key,
|
|
1794
|
+
keys.encrypt_email_key,
|
|
1795
|
+
{},
|
|
1796
|
+
[local_moderation_channel, github_moderation_channel]
|
|
1797
|
+
);
|
|
1798
|
+
const email_comment_result = await Result4.safe(
|
|
1799
|
+
handle_email_comment([site_config, new TextEncoder().encode(email)])
|
|
1800
|
+
);
|
|
1801
|
+
if (email_comment_result.isErr()) {
|
|
1802
|
+
throw email_comment_result.unwrapErr();
|
|
1803
|
+
}
|
|
1804
|
+
const email_event_response = email_comment_result.unwrap();
|
|
1805
|
+
tty_default.cmds.simulate.print_comment_via_email_response(
|
|
1806
|
+
cli_system_config,
|
|
1807
|
+
{ site_config_path, site_config },
|
|
1808
|
+
email_event_response,
|
|
1809
|
+
options,
|
|
1810
|
+
options.format
|
|
1811
|
+
);
|
|
1812
|
+
if (site_config.comments?.cache && email_event_response.prepared?.unwrap()) {
|
|
1813
|
+
const ctx = email_event_response.prepared?.unwrap();
|
|
1814
|
+
const domain = ctx.r3ply.site;
|
|
1815
|
+
const path4 = ctx.comment.subject.path;
|
|
1816
|
+
const comment_path = `comments/pending/${domain}${path4}/index.html`;
|
|
1817
|
+
const pending_comments = await project.get_comment_from_cache(cwd, {
|
|
1818
|
+
path: comment_path
|
|
1819
|
+
});
|
|
1820
|
+
project.add_comment_to_cache(cwd, {
|
|
1821
|
+
path: comment_path,
|
|
1822
|
+
content: [...pending_comments, ctx]
|
|
1823
|
+
});
|
|
1824
|
+
}
|
|
1825
|
+
if (options.moderate && email_event_response.moderation) {
|
|
1826
|
+
const supported_mod_channels = ["local", "github"];
|
|
1827
|
+
for (const moderation_channel_type of supported_mod_channels) {
|
|
1828
|
+
for (const [index, { type, request }] of email_event_response.moderation.filter((m) => m.type == moderation_channel_type).entries()) {
|
|
1829
|
+
switch (type) {
|
|
1830
|
+
case "local": {
|
|
1831
|
+
const print = tty_default.cmds.simulate.print_local_moderation_event;
|
|
1832
|
+
if (request.isOk()) {
|
|
1833
|
+
const ticket = await request.unwrap().send();
|
|
1834
|
+
print(
|
|
1835
|
+
request,
|
|
1836
|
+
ticket,
|
|
1837
|
+
index,
|
|
1838
|
+
options,
|
|
1839
|
+
options.format
|
|
1840
|
+
);
|
|
1841
|
+
} else {
|
|
1842
|
+
print(
|
|
1843
|
+
request,
|
|
1844
|
+
void 0,
|
|
1845
|
+
index,
|
|
1846
|
+
options,
|
|
1847
|
+
options.format
|
|
1848
|
+
);
|
|
1849
|
+
}
|
|
1850
|
+
break;
|
|
1851
|
+
}
|
|
1852
|
+
case "github": {
|
|
1853
|
+
const print = tty_default.cmds.simulate.print_github_moderation_event;
|
|
1854
|
+
if (request.isOk()) {
|
|
1855
|
+
const ticket = await request.unwrap().send();
|
|
1856
|
+
print(
|
|
1857
|
+
request,
|
|
1858
|
+
ticket,
|
|
1859
|
+
index,
|
|
1860
|
+
options,
|
|
1861
|
+
options.format
|
|
1862
|
+
);
|
|
1863
|
+
} else {
|
|
1864
|
+
print(
|
|
1865
|
+
request,
|
|
1866
|
+
void 0,
|
|
1867
|
+
index,
|
|
1868
|
+
options,
|
|
1869
|
+
options.format
|
|
1870
|
+
);
|
|
1871
|
+
}
|
|
1872
|
+
break;
|
|
1873
|
+
}
|
|
1874
|
+
default:
|
|
1875
|
+
break;
|
|
1876
|
+
}
|
|
1877
|
+
}
|
|
1878
|
+
}
|
|
1879
|
+
if (site_config.moderation) {
|
|
1880
|
+
const ignored_moderation_results = Object.keys(
|
|
1881
|
+
site_config.moderation
|
|
1882
|
+
).filter(
|
|
1883
|
+
(moderation_key) => !supported_mod_channels.includes(moderation_key)
|
|
1884
|
+
);
|
|
1885
|
+
const other_moderation_results = email_event_response.moderation.filter(
|
|
1886
|
+
(r) => !supported_mod_channels.includes(r.type)
|
|
1887
|
+
);
|
|
1888
|
+
tty_default.cmds.simulate.print_ignored_moderation_channels(
|
|
1889
|
+
ignored_moderation_results,
|
|
1890
|
+
other_moderation_results,
|
|
1891
|
+
options,
|
|
1892
|
+
options.format
|
|
1893
|
+
);
|
|
1894
|
+
}
|
|
1895
|
+
}
|
|
1896
|
+
}
|
|
1897
|
+
var simulate_default = simulate_cmd;
|
|
1898
|
+
|
|
1899
|
+
// src/cmds/cache.ts
|
|
1900
|
+
import { Command as Command5 } from "commander";
|
|
1901
|
+
import http from "http";
|
|
1902
|
+
import handler from "serve-handler";
|
|
1903
|
+
function cache_cmd(cwd) {
|
|
1904
|
+
const cache_cmd2 = new Command5("cache").description("manage comment cache");
|
|
1905
|
+
cache_cmd2.command("serve").description("serves cached comments").option("--interface <INTERFACE>", "Interface to bind on", "127.0.0.1").option(
|
|
1906
|
+
"--port <PORT>",
|
|
1907
|
+
"Which port to use",
|
|
1908
|
+
(str) => {
|
|
1909
|
+
const num = Number.parseInt(str);
|
|
1910
|
+
if (Number.isNaN(num))
|
|
1911
|
+
throw new util.CLIError(`Port must be a number, received ${str}`);
|
|
1912
|
+
return num;
|
|
1913
|
+
},
|
|
1914
|
+
2274
|
|
1915
|
+
).option("--reset", "Clears cache on startup", false).action(async (options, cmd) => {
|
|
1916
|
+
serve(cwd, options);
|
|
1917
|
+
});
|
|
1918
|
+
cache_cmd2.command("clean").description("deletes cache").action(async () => {
|
|
1919
|
+
return project.clean_cache(cwd);
|
|
1920
|
+
});
|
|
1921
|
+
cache_cmd2.command("evict").description("evict stale pending comments").option(
|
|
1922
|
+
"--ttl <MAX_AGE_SECONDS>",
|
|
1923
|
+
"Max age allowed for comments in seconds",
|
|
1924
|
+
(str) => {
|
|
1925
|
+
const num = Number.parseInt(str);
|
|
1926
|
+
if (Number.isNaN(num))
|
|
1927
|
+
throw new util.CLIError(`ttl must be a number, received ${str}`);
|
|
1928
|
+
return num;
|
|
1929
|
+
},
|
|
1930
|
+
259200
|
|
1931
|
+
).action(async (options) => {
|
|
1932
|
+
return project.evict_comments_from_cache(
|
|
1933
|
+
cwd,
|
|
1934
|
+
"comments/pending/",
|
|
1935
|
+
options.ttl
|
|
1936
|
+
);
|
|
1937
|
+
});
|
|
1938
|
+
return cache_cmd2;
|
|
1939
|
+
}
|
|
1940
|
+
async function serve(cwd, options) {
|
|
1941
|
+
const cache_dir = await project.get_cache_dir(cwd, options.reset);
|
|
1942
|
+
const static_dir = await project.find_static_dir(cwd);
|
|
1943
|
+
const server = http.createServer(
|
|
1944
|
+
(req, res) => handler(req, res, {
|
|
1945
|
+
public: static_dir,
|
|
1946
|
+
cleanUrls: true,
|
|
1947
|
+
headers: [
|
|
1948
|
+
{
|
|
1949
|
+
source: "**",
|
|
1950
|
+
headers: [{ key: "Access-Control-Allow-Origin", value: "*" }]
|
|
1951
|
+
},
|
|
1952
|
+
{
|
|
1953
|
+
source: "/cache/comments/pending/**/index.html",
|
|
1954
|
+
headers: [
|
|
1955
|
+
{ key: "Content-Type", value: "application/json" },
|
|
1956
|
+
{
|
|
1957
|
+
key: "Cache-Control",
|
|
1958
|
+
value: "no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0"
|
|
1959
|
+
},
|
|
1960
|
+
{
|
|
1961
|
+
key: "Pragma",
|
|
1962
|
+
value: "no-cache"
|
|
1963
|
+
},
|
|
1964
|
+
{
|
|
1965
|
+
key: "Expires",
|
|
1966
|
+
value: "0"
|
|
1967
|
+
}
|
|
1968
|
+
]
|
|
1969
|
+
}
|
|
1970
|
+
]
|
|
1971
|
+
})
|
|
1972
|
+
);
|
|
1973
|
+
return new Promise((resolve) => {
|
|
1974
|
+
server.listen(options.port, options.interface, () => {
|
|
1975
|
+
tty_default.cmds.cache.print_serving_cache(cache_dir, options);
|
|
1976
|
+
resolve();
|
|
1977
|
+
});
|
|
1978
|
+
});
|
|
1979
|
+
}
|
|
1980
|
+
var cache_default = cache_cmd;
|
|
1981
|
+
|
|
1982
|
+
// src/index.ts
|
|
1983
|
+
var allowed_formats = ["toml", "json"];
|
|
1984
|
+
function validate_format(value) {
|
|
1985
|
+
if (!allowed_formats.includes(value.toLowerCase())) {
|
|
1986
|
+
throw new util.CLIError(`Format must be one of: ${allowed_formats.join(" | ")}.`);
|
|
1987
|
+
}
|
|
1988
|
+
return value.toLowerCase();
|
|
1989
|
+
}
|
|
1990
|
+
program2.name("re").version("0.0.1").description("CLI for r3ply").option("--config <path>", "specify path to config").option(
|
|
1991
|
+
"--format <toml | json>",
|
|
1992
|
+
"format to use with file output",
|
|
1993
|
+
validate_format,
|
|
1994
|
+
"toml"
|
|
1995
|
+
);
|
|
1996
|
+
program2.addCommand(init_default(process.cwd()));
|
|
1997
|
+
program2.addCommand(config_default(process.cwd()));
|
|
1998
|
+
program2.addCommand(generate_default(process.cwd()));
|
|
1999
|
+
program2.addCommand(simulate_default(process.cwd()));
|
|
2000
|
+
program2.addCommand(cache_default(process.cwd()));
|
|
2001
|
+
program2.parseAsync(process.argv).catch((error) => {
|
|
2002
|
+
if (error instanceof util.CLIError) {
|
|
2003
|
+
program2.error(tty_default.txt.warn(error.message));
|
|
2004
|
+
} else {
|
|
2005
|
+
program2.error(error?.stack ?? String(error));
|
|
2006
|
+
}
|
|
2007
|
+
});
|