frontpage-host 0.1.18 → 0.1.19
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/AGENTS.md +4 -1
- package/dist/bin.js +6 -0
- package/dist/commands/mail.d.ts +1 -0
- package/dist/commands/mail.js +482 -0
- package/dist/commands/people.js +115 -13
- package/dist/commands/shop-ids.d.ts +3 -0
- package/dist/commands/shop-ids.js +3 -0
- package/dist/commands/shop.d.ts +1 -0
- package/dist/commands/shop.js +595 -0
- package/dist/modules/commands.js +4 -4
- package/dist/registry.js +62 -5
- package/llms.txt +2 -1
- package/package.json +1 -1
package/dist/commands/people.js
CHANGED
|
@@ -1,18 +1,32 @@
|
|
|
1
|
+
import { writeFile } from "node:fs/promises";
|
|
1
2
|
import { resolveSlug } from "../site.js";
|
|
2
|
-
import { CliHttpError, EXIT_OK, EXIT_USAGE, exitCodeForError, v1Delete, v1Get, v1Patch, v1Post, } from "../http/client.js";
|
|
3
|
+
import { CliHttpError, EXIT_OK, EXIT_USAGE, apiBase, exitCodeForError, getBearerAuthHeader, httpErrorFromV1, v1Delete, v1Get, v1Patch, v1Post, } from "../http/client.js";
|
|
3
4
|
import { assertNoUnknownFlags, hasFlag, takeFlag } from "../argv.js";
|
|
4
5
|
import { emitErr, emitOk, isJson } from "../runtime.js";
|
|
5
|
-
const USAGE = "Usage: frontpage people list|get|add|set|note|rm-note|follow-up|hide|unhide [--site <slug>] [--json]";
|
|
6
|
+
const USAGE = "Usage: frontpage people list|get|add|set|note|rm-note|follow-up|hide|unhide|tag|export [--site <slug>] [--json]";
|
|
7
|
+
const PERSON_ID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
6
8
|
function requireId(work) {
|
|
7
9
|
const fromFlag = takeFlag(work, "--id");
|
|
8
|
-
if (fromFlag)
|
|
9
|
-
return fromFlag;
|
|
10
10
|
const positional = work.find((a) => !a.startsWith("-"));
|
|
11
|
-
|
|
12
|
-
work.splice(work.indexOf(positional), 1)
|
|
13
|
-
|
|
11
|
+
const raw = fromFlag ?? (positional
|
|
12
|
+
? work.splice(work.indexOf(positional), 1)[0]
|
|
13
|
+
: undefined);
|
|
14
|
+
if (!raw) {
|
|
15
|
+
throw new CliHttpError(`Person id is required. Run: frontpage help people`, EXIT_USAGE, 0, "bad_request");
|
|
14
16
|
}
|
|
15
|
-
|
|
17
|
+
if (!PERSON_ID_RE.test(raw)) {
|
|
18
|
+
throw new CliHttpError("Person not found.", EXIT_USAGE, 404, "not_found");
|
|
19
|
+
}
|
|
20
|
+
return raw;
|
|
21
|
+
}
|
|
22
|
+
function requireEventId(raw) {
|
|
23
|
+
if (!raw) {
|
|
24
|
+
throw new CliHttpError(`Usage: frontpage people rm-note <id> --event <eventId>`, EXIT_USAGE, 0, "bad_request");
|
|
25
|
+
}
|
|
26
|
+
if (!PERSON_ID_RE.test(raw)) {
|
|
27
|
+
throw new CliHttpError("Event not found.", EXIT_USAGE, 404, "not_found");
|
|
28
|
+
}
|
|
29
|
+
return raw;
|
|
16
30
|
}
|
|
17
31
|
function displayName(row) {
|
|
18
32
|
return row.name || row.email || row.phone || row.id;
|
|
@@ -27,10 +41,29 @@ function printPerson(row) {
|
|
|
27
41
|
bits.push(`follow-up ${row.followUpAt}`);
|
|
28
42
|
if (row.hiddenAt)
|
|
29
43
|
bits.push("hidden");
|
|
44
|
+
if (row.tags?.length)
|
|
45
|
+
bits.push(row.tags.join(","));
|
|
30
46
|
if (row.lastEvent?.summary)
|
|
31
47
|
bits.push(row.lastEvent.summary);
|
|
32
48
|
console.log(`${row.id} ${bits.join(" · ")}`);
|
|
33
49
|
}
|
|
50
|
+
async function fetchPeopleCsv(path) {
|
|
51
|
+
const headers = await getBearerAuthHeader();
|
|
52
|
+
const res = await fetch(`${apiBase()}${path}`, {
|
|
53
|
+
headers: { ...headers, Accept: "text/csv" },
|
|
54
|
+
});
|
|
55
|
+
const text = await res.text();
|
|
56
|
+
if (res.ok)
|
|
57
|
+
return text;
|
|
58
|
+
let parsed = {};
|
|
59
|
+
try {
|
|
60
|
+
parsed = text ? JSON.parse(text) : {};
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
parsed = {};
|
|
64
|
+
}
|
|
65
|
+
throw httpErrorFromV1(res.status, parsed, text || `Request failed (${res.status})`);
|
|
66
|
+
}
|
|
34
67
|
export async function runPeople(args) {
|
|
35
68
|
try {
|
|
36
69
|
const { slug, rest } = await resolveSlug(args);
|
|
@@ -48,6 +81,7 @@ export async function runPeople(args) {
|
|
|
48
81
|
const limit = takeFlag(work, "--limit");
|
|
49
82
|
const cursor = takeFlag(work, "--cursor");
|
|
50
83
|
const hidden = hasFlag(work, "--hidden");
|
|
84
|
+
const tag = takeFlag(work, "--tag");
|
|
51
85
|
assertNoUnknownFlags(work, "people");
|
|
52
86
|
const params = new URLSearchParams();
|
|
53
87
|
if (q)
|
|
@@ -70,6 +104,8 @@ export async function runPeople(args) {
|
|
|
70
104
|
params.set("cursor", cursor);
|
|
71
105
|
if (hidden)
|
|
72
106
|
params.set("hidden", "1");
|
|
107
|
+
if (tag)
|
|
108
|
+
params.set("tag", tag);
|
|
73
109
|
const qs = params.toString();
|
|
74
110
|
const data = await v1Get(qs ? `${base}?${qs}` : base);
|
|
75
111
|
if (isJson()) {
|
|
@@ -171,12 +207,8 @@ export async function runPeople(args) {
|
|
|
171
207
|
if (sub === "rm-note") {
|
|
172
208
|
const work = rest.slice(1);
|
|
173
209
|
const id = requireId(work);
|
|
174
|
-
const eventId = takeFlag(work, "--event") ?? takeFlag(work, "--event-id");
|
|
210
|
+
const eventId = requireEventId(takeFlag(work, "--event") ?? takeFlag(work, "--event-id"));
|
|
175
211
|
assertNoUnknownFlags(work, "people");
|
|
176
|
-
if (!eventId) {
|
|
177
|
-
console.error("Usage: frontpage people rm-note <id> --event <eventId>");
|
|
178
|
-
return EXIT_USAGE;
|
|
179
|
-
}
|
|
180
212
|
const data = await v1Delete(`${base}/${encodeURIComponent(id)}/events/${encodeURIComponent(eventId)}`);
|
|
181
213
|
if (isJson()) {
|
|
182
214
|
emitOk("people", data);
|
|
@@ -227,6 +259,76 @@ export async function runPeople(args) {
|
|
|
227
259
|
console.log(`Restored ${displayName(data.person)}`);
|
|
228
260
|
return EXIT_OK;
|
|
229
261
|
}
|
|
262
|
+
if (sub === "tag") {
|
|
263
|
+
const work = rest.slice(1);
|
|
264
|
+
const id = requireId(work);
|
|
265
|
+
const add = takeFlag(work, "--add");
|
|
266
|
+
const remove = takeFlag(work, "--remove") ?? takeFlag(work, "--rm");
|
|
267
|
+
assertNoUnknownFlags(work, "people");
|
|
268
|
+
if ((!add && !remove) || (add && remove)) {
|
|
269
|
+
console.error("Usage: frontpage people tag <id> --add <tag> | --remove <tag>");
|
|
270
|
+
return EXIT_USAGE;
|
|
271
|
+
}
|
|
272
|
+
const data = add
|
|
273
|
+
? await v1Post(`${base}/${encodeURIComponent(id)}/tags`, {
|
|
274
|
+
tag: add,
|
|
275
|
+
})
|
|
276
|
+
: await v1Delete(`${base}/${encodeURIComponent(id)}/tags/${encodeURIComponent(remove)}`);
|
|
277
|
+
if (isJson()) {
|
|
278
|
+
emitOk("people", data);
|
|
279
|
+
return EXIT_OK;
|
|
280
|
+
}
|
|
281
|
+
printPerson(data.person);
|
|
282
|
+
return EXIT_OK;
|
|
283
|
+
}
|
|
284
|
+
if (sub === "export") {
|
|
285
|
+
const work = rest.slice(1);
|
|
286
|
+
const q = takeFlag(work, "--q") ?? takeFlag(work, "--query");
|
|
287
|
+
const status = takeFlag(work, "--status");
|
|
288
|
+
const source = takeFlag(work, "--source");
|
|
289
|
+
const formSlug = takeFlag(work, "--form-slug");
|
|
290
|
+
const followUp = takeFlag(work, "--follow-up");
|
|
291
|
+
const sort = takeFlag(work, "--sort");
|
|
292
|
+
const lastSeenDays = takeFlag(work, "--last-seen-days");
|
|
293
|
+
const tag = takeFlag(work, "--tag");
|
|
294
|
+
const out = takeFlag(work, "--out");
|
|
295
|
+
const hidden = hasFlag(work, "--hidden");
|
|
296
|
+
assertNoUnknownFlags(work, "people");
|
|
297
|
+
const params = new URLSearchParams();
|
|
298
|
+
if (q)
|
|
299
|
+
params.set("q", q);
|
|
300
|
+
if (status)
|
|
301
|
+
params.set("status", status);
|
|
302
|
+
if (source)
|
|
303
|
+
params.set("source", source);
|
|
304
|
+
if (formSlug)
|
|
305
|
+
params.set("formSlug", formSlug);
|
|
306
|
+
if (followUp)
|
|
307
|
+
params.set("followUp", followUp);
|
|
308
|
+
if (sort)
|
|
309
|
+
params.set("sort", sort);
|
|
310
|
+
if (lastSeenDays)
|
|
311
|
+
params.set("lastSeenDays", lastSeenDays);
|
|
312
|
+
if (hidden)
|
|
313
|
+
params.set("hidden", "1");
|
|
314
|
+
if (tag)
|
|
315
|
+
params.set("tag", tag);
|
|
316
|
+
const qs = params.toString();
|
|
317
|
+
const csv = await fetchPeopleCsv(qs ? `${base}/export?${qs}` : `${base}/export`);
|
|
318
|
+
if (out) {
|
|
319
|
+
await writeFile(out, csv, "utf8");
|
|
320
|
+
if (isJson()) {
|
|
321
|
+
emitOk("people", { path: out, bytes: Buffer.byteLength(csv, "utf8") });
|
|
322
|
+
return EXIT_OK;
|
|
323
|
+
}
|
|
324
|
+
console.log(out);
|
|
325
|
+
return EXIT_OK;
|
|
326
|
+
}
|
|
327
|
+
process.stdout.write(csv);
|
|
328
|
+
if (!csv.endsWith("\n"))
|
|
329
|
+
process.stdout.write("\n");
|
|
330
|
+
return EXIT_OK;
|
|
331
|
+
}
|
|
230
332
|
console.error(USAGE);
|
|
231
333
|
return EXIT_USAGE;
|
|
232
334
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function runShop(args: string[]): Promise<number>;
|