@simplepush/cli 0.4.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/main.mjs +111 -13
- package/dist/main.mjs.map +1 -1
- package/package.json +2 -2
package/dist/main.mjs
CHANGED
|
@@ -13,6 +13,9 @@ import { Client, DownloadError, OrgClient, TypeFilter, decryptSubmission, decryp
|
|
|
13
13
|
import { mkdir, stat, writeFile } from "node:fs/promises";
|
|
14
14
|
import { connect, createConnection } from "node:net";
|
|
15
15
|
import path, { basename, extname, join } from "node:path";
|
|
16
|
+
//#region package.json
|
|
17
|
+
var version = "0.5.0";
|
|
18
|
+
//#endregion
|
|
16
19
|
//#region src/errors.ts
|
|
17
20
|
/** No CLI session saved — `sp auth login` hasn't been run (or was logged out). */
|
|
18
21
|
var NotLoggedIn = class extends Data.TaggedError("NotLoggedIn") {};
|
|
@@ -1887,7 +1890,7 @@ const formatOption$4 = Options.choice("format", ["json", "pretty"]).pipe(Options
|
|
|
1887
1890
|
const sinceOption$1 = mappedText("since", resolveSince).pipe(Options.optional, Options.withDescription("Only items created at or after this instant: ISO-8601, or a relative window like 7d, 12h, 30m."));
|
|
1888
1891
|
const untilOption$1 = mappedText("until", (raw) => resolveUntil(raw).toISOString()).pipe(Options.optional, Options.withDescription("Only items created at or before this instant (same forms as --since)."));
|
|
1889
1892
|
const memberOption$3 = Options.text("member").pipe(Options.optional, Options.withDescription("Only items involving this person: a usr_ id, or a member name on an org."));
|
|
1890
|
-
const limitOption$
|
|
1893
|
+
const limitOption$2 = Options.integer("limit").pipe(Options.optional, Options.withDescription("Page size (server default 50). With --all this is the page size, not a cap."));
|
|
1891
1894
|
const cursorOption = Options.text("cursor").pipe(Options.optional, Options.withDescription("Continue a previous page: pass the cursor it reported."));
|
|
1892
1895
|
const allOption = Options.boolean("all").pipe(Options.withDescription("Follow cursors until the last page instead of printing one page and a cursor."));
|
|
1893
1896
|
const STATUSES = [
|
|
@@ -2119,7 +2122,7 @@ function extractRaw(v) {
|
|
|
2119
2122
|
const eventTypeOption = Options.text("type").pipe(Options.withDescription("Filter by event type. Repeatable."), Options.repeated);
|
|
2120
2123
|
const sinceOption = mappedText("since", resolveSince).pipe(Options.withDescription("Replay from this point. Accepts `24h`, `7d`, or an ISO 8601 timestamp."), Options.optional);
|
|
2121
2124
|
const untilOption = mappedText("until", resolveUntil).pipe(Options.withDescription("Stop at this timestamp. Forces a finite range, so `--follow` is ignored."), Options.optional);
|
|
2122
|
-
const limitOption = Options.integer("limit").pipe(Options.withDescription("Maximum number of events to print, then exit."), Options.optional);
|
|
2125
|
+
const limitOption$1 = Options.integer("limit").pipe(Options.withDescription("Maximum number of events to print, then exit."), Options.optional);
|
|
2123
2126
|
const followOption = Options.boolean("follow").pipe(Options.withAlias("f"), Options.withDescription("After history is drained, keep streaming live instead of exiting. Only meaningful with `--since`."));
|
|
2124
2127
|
const memberOption$2 = Options.text("member").pipe(Options.optional, Options.withDescription("Only events by this person: a usr_ id, or the actor's name."));
|
|
2125
2128
|
const formatOption$3 = Options.choice("format", [
|
|
@@ -2134,7 +2137,7 @@ const eventsCommand = Command.make("events", {
|
|
|
2134
2137
|
member: memberOption$2,
|
|
2135
2138
|
since: sinceOption,
|
|
2136
2139
|
until: untilOption,
|
|
2137
|
-
limit: limitOption,
|
|
2140
|
+
limit: limitOption$1,
|
|
2138
2141
|
follow: followOption,
|
|
2139
2142
|
format: formatOption$3,
|
|
2140
2143
|
direct: directOption,
|
|
@@ -2417,7 +2420,7 @@ const getCommand = Command.make("get", {
|
|
|
2417
2420
|
topic: topicOption,
|
|
2418
2421
|
member: memberOption$3,
|
|
2419
2422
|
group: groupOption,
|
|
2420
|
-
limit: limitOption$
|
|
2423
|
+
limit: limitOption$2,
|
|
2421
2424
|
cursor: cursorOption,
|
|
2422
2425
|
all: allOption,
|
|
2423
2426
|
format: formatOption$4,
|
|
@@ -2432,6 +2435,100 @@ const getCommand = Command.make("get", {
|
|
|
2432
2435
|
return Effect.fail(new UserError({ message: `cannot read '${args.what}': ${hint}` }));
|
|
2433
2436
|
})).pipe(Command.withDescription("Read what came back: 'tasks' or 'submissions' for filtered listings, a tsk_ id for one task with its subtasks, a sub_ id for one subtask, a grptsk_ id for a group's per-recipient roster."));
|
|
2434
2437
|
//#endregion
|
|
2438
|
+
//#region src/commands/search.ts
|
|
2439
|
+
const KINDS = [
|
|
2440
|
+
"task",
|
|
2441
|
+
"subtask",
|
|
2442
|
+
"answer",
|
|
2443
|
+
"reply",
|
|
2444
|
+
"notification",
|
|
2445
|
+
"notification_answer",
|
|
2446
|
+
"submission"
|
|
2447
|
+
];
|
|
2448
|
+
const kindOption = Options.text("kind").pipe(Options.repeated, Options.mapTryCatch((values) => {
|
|
2449
|
+
const flat = values.flatMap((v) => v.split(",")).map((s) => s.trim()).filter((s) => s.length > 0);
|
|
2450
|
+
const bad = flat.filter((s) => !KINDS.includes(s));
|
|
2451
|
+
if (bad.length > 0) throw new Error(`unknown kind ${bad.join(", ")} — use ${KINDS.join(", ")}`);
|
|
2452
|
+
return flat;
|
|
2453
|
+
}, (e) => HelpDoc.p(e instanceof Error ? e.message : String(e))), Options.withDescription("Only these hit kinds (repeatable or comma-separated)."));
|
|
2454
|
+
function parsePoint(raw) {
|
|
2455
|
+
const parts = raw.split(",");
|
|
2456
|
+
if (parts.length === 2) {
|
|
2457
|
+
const latitude = Number(parts[0].trim());
|
|
2458
|
+
const longitude = Number(parts[1].trim());
|
|
2459
|
+
if (Number.isFinite(latitude) && Number.isFinite(longitude) && Math.abs(latitude) <= 90 && Math.abs(longitude) <= 180) return {
|
|
2460
|
+
latitude,
|
|
2461
|
+
longitude
|
|
2462
|
+
};
|
|
2463
|
+
}
|
|
2464
|
+
throw new Error(`expected \`lat,lng\` with lat in [-90,90] and lng in [-180,180], got '${raw}'`);
|
|
2465
|
+
}
|
|
2466
|
+
const nearOption = Options.text("near").pipe(Options.mapTryCatch(parsePoint, (e) => HelpDoc.p(e instanceof Error ? e.message : String(e))), Options.optional, Options.withDescription("Center of a radius filter as `lat,lng`; requires --radius. Hits come back with their point and its distance, nearest first when no words are given."));
|
|
2467
|
+
const radiusOption = Options.integer("radius").pipe(Options.optional, Options.withDescription("Radius around --near, in meters (1..1000000)."));
|
|
2468
|
+
const withinOption = Options.text("within").pipe(Options.mapTryCatch((raw) => {
|
|
2469
|
+
const points = raw.split(";").map((s) => s.trim()).filter((s) => s.length > 0).map(parsePoint);
|
|
2470
|
+
if (points.length < 3) throw new Error("--within needs at least 3 `lat,lng` points separated by `;`");
|
|
2471
|
+
return points;
|
|
2472
|
+
}, (e) => HelpDoc.p(e instanceof Error ? e.message : String(e))), Options.optional, Options.withDescription("Polygon to search inside: `lat,lng` corners joined with `;` (at least 3). Not combinable with --near/--radius; hits carry no distance and come newest first without words."));
|
|
2473
|
+
const limitOption = Options.integer("limit").pipe(Options.optional, Options.withDescription("Best hits to return; server default 20, at most 100."));
|
|
2474
|
+
/** One block per hit: id, kind, when, then whatever the hit carries. */
|
|
2475
|
+
function formatHit(h) {
|
|
2476
|
+
return [
|
|
2477
|
+
`${h.ref} [${h.kind}] ${h.createdAt}`,
|
|
2478
|
+
h.title !== void 0 ? ` ${h.title}` : void 0,
|
|
2479
|
+
h.actor !== void 0 ? ` by: ${h.actor}` : void 0,
|
|
2480
|
+
h.snippet !== void 0 ? ` ${h.snippet}` : void 0,
|
|
2481
|
+
h.location !== void 0 ? ` at: ${h.location.latitude},${h.location.longitude}${h.location.distanceMeters !== void 0 ? ` (${Math.round(h.location.distanceMeters)}m away)` : ""}` : void 0
|
|
2482
|
+
].filter((l) => l !== void 0).join("\n");
|
|
2483
|
+
}
|
|
2484
|
+
const searchCommand = Command.make("search", {
|
|
2485
|
+
words: Args.text({ name: "words" }).pipe(Args.withDescription("The words to look for; all must appear. Quote a phrase (nested quotes) for adjacency. Optional when --near/--radius or --within is given."), Args.repeated),
|
|
2486
|
+
kind: kindOption,
|
|
2487
|
+
since: sinceOption$1,
|
|
2488
|
+
until: untilOption$1,
|
|
2489
|
+
member: memberOption$3,
|
|
2490
|
+
near: nearOption,
|
|
2491
|
+
radius: radiusOption,
|
|
2492
|
+
within: withinOption,
|
|
2493
|
+
limit: limitOption,
|
|
2494
|
+
format: formatOption$4,
|
|
2495
|
+
...credentialOptions
|
|
2496
|
+
}, (args) => withQueryClient(args, (ctx) => Effect.gen(function* () {
|
|
2497
|
+
const query = args.words.join(" ").trim();
|
|
2498
|
+
const near = Option.getOrUndefined(args.near);
|
|
2499
|
+
const radius = Option.getOrUndefined(args.radius);
|
|
2500
|
+
const within = Option.getOrUndefined(args.within);
|
|
2501
|
+
const problem = near !== void 0 && radius === void 0 ? "--near requires --radius (meters)" : radius !== void 0 && near === void 0 ? "--radius requires --near" : within !== void 0 && near !== void 0 ? "--within cannot be combined with --near/--radius" : query === "" && near === void 0 && within === void 0 ? "give words to search for, or an area (--near with --radius, or --within)" : void 0;
|
|
2502
|
+
if (problem !== void 0) return yield* Effect.fail(new UserError({ message: problem }));
|
|
2503
|
+
const opts = {
|
|
2504
|
+
...args.kind.length > 0 ? { kind: args.kind } : {},
|
|
2505
|
+
...Option.match(args.since, {
|
|
2506
|
+
onNone: () => ({}),
|
|
2507
|
+
onSome: (since) => ({ since })
|
|
2508
|
+
}),
|
|
2509
|
+
...Option.match(args.until, {
|
|
2510
|
+
onNone: () => ({}),
|
|
2511
|
+
onSome: (until) => ({ until })
|
|
2512
|
+
}),
|
|
2513
|
+
...Option.match(args.member, {
|
|
2514
|
+
onNone: () => ({}),
|
|
2515
|
+
onSome: (member) => ({ member })
|
|
2516
|
+
}),
|
|
2517
|
+
...Option.match(args.limit, {
|
|
2518
|
+
onNone: () => ({}),
|
|
2519
|
+
onSome: (limit) => ({ limit })
|
|
2520
|
+
}),
|
|
2521
|
+
...near !== void 0 && radius !== void 0 ? {
|
|
2522
|
+
near,
|
|
2523
|
+
radiusMeters: radius
|
|
2524
|
+
} : {},
|
|
2525
|
+
...within !== void 0 ? { within } : {}
|
|
2526
|
+
};
|
|
2527
|
+
const res = yield* sdkCall("search", () => ctx.client.search(query === "" ? void 0 : query, opts));
|
|
2528
|
+
for (const h of res.hits) yield* ctx.out.print(args.format === "json" ? JSON.stringify(h) : formatHit(h));
|
|
2529
|
+
if (res.hits.length === 0 && args.format === "pretty") yield* ctx.out.info("no hits");
|
|
2530
|
+
}))).pipe(Command.withDescription("Ranked full-text and location search over everything this credential reads, across all time. Words match literally plus stemmed in the org's configured languages; --near/--radius or --within search by place instead of or on top of words."));
|
|
2531
|
+
//#endregion
|
|
2435
2532
|
//#region src/input-spec.ts
|
|
2436
2533
|
const SETTING_RE = /^[A-Za-z_][A-Za-z0-9_]*=/;
|
|
2437
2534
|
function looksLikeSetting(seg) {
|
|
@@ -3715,7 +3812,7 @@ const fileInput$1 = repeatedText("file-input", void 0, "Add a file upload input.
|
|
|
3715
3812
|
const locationInput$1 = repeatedText("location-input", void 0, "Add a location input (the recipient shares their device GPS position from the app). Format: `description[;key=value...]`. Settings: `required=true|false`. Repeatable.");
|
|
3716
3813
|
const linkOption$1 = repeatedText("link", "l", "Attach a remote URL (a link attachment). Repeatable. For local files use --file.");
|
|
3717
3814
|
const fileOption$1 = repeatedText("file", "f", "Attach a local file, uploaded as a file attachment (encrypted under the send's key — topic password or org master key — when the send is encrypted). Repeatable.");
|
|
3718
|
-
const
|
|
3815
|
+
const autoCommitOption$1 = Options.boolean("auto-commit").pipe(Options.withDescription("Complete the task as inputs are filled, without an explicit Submit. Default is form mode: the recipient submits everything at once."));
|
|
3719
3816
|
const waitOption$1 = Options.boolean("wait").pipe(Options.withDescription("Block until the task is completed; print the result to stdout. Requires exactly one input on the request."));
|
|
3720
3817
|
const replyOption$1 = Options.choice("reply", [
|
|
3721
3818
|
"one-shot",
|
|
@@ -3744,7 +3841,7 @@ const taskCommand = Command.make("task", {
|
|
|
3744
3841
|
"location-input": locationInput$1,
|
|
3745
3842
|
link: linkOption$1,
|
|
3746
3843
|
file: fileOption$1,
|
|
3747
|
-
|
|
3844
|
+
"auto-commit": autoCommitOption$1,
|
|
3748
3845
|
wait: waitOption$1,
|
|
3749
3846
|
reply: replyOption$1,
|
|
3750
3847
|
member: memberOption,
|
|
@@ -3795,7 +3892,7 @@ const taskCommand = Command.make("task", {
|
|
|
3795
3892
|
inputs,
|
|
3796
3893
|
links: [...args.link],
|
|
3797
3894
|
files: [...args.file],
|
|
3798
|
-
autoCommit:
|
|
3895
|
+
autoCommit: args["auto-commit"],
|
|
3799
3896
|
reply: Option.getOrUndefined(args.reply),
|
|
3800
3897
|
markdown: args.markdown,
|
|
3801
3898
|
noEncrypt: args["no-encrypt"],
|
|
@@ -3822,7 +3919,7 @@ const taskCommand = Command.make("task", {
|
|
|
3822
3919
|
inputs,
|
|
3823
3920
|
links: [...args.link],
|
|
3824
3921
|
...files.length > 0 ? { files } : {},
|
|
3825
|
-
autoCommit:
|
|
3922
|
+
autoCommit: args["auto-commit"],
|
|
3826
3923
|
...Option.isSome(args.reply) ? { reply: args.reply.value } : {},
|
|
3827
3924
|
...args.markdown ? { contentFormat: "markdown" } : {},
|
|
3828
3925
|
...expiresAt !== void 0 ? { expiresAt } : {}
|
|
@@ -4029,7 +4126,7 @@ const photoInput = Options.text("photo-input").pipe(Options.withDescription("Add
|
|
|
4029
4126
|
const voiceRecordingInput = Options.text("voice-recording-input").pipe(Options.withDescription("Add a voice recording input. Format: `description[;key=value...]`. Settings: `required=true|false`. Repeatable."), Options.repeated);
|
|
4030
4127
|
const fileInput = Options.text("file-input").pipe(Options.withDescription("Add a file upload input. Format: `description[;key=value...]`. Settings: `required=true|false`. Repeatable."), Options.repeated);
|
|
4031
4128
|
const locationInput = Options.text("location-input").pipe(Options.withDescription("Add a location input (the recipient shares their device GPS position from the app). Format: `description[;key=value...]`. Settings: `required=true|false`. Repeatable."), Options.repeated);
|
|
4032
|
-
const
|
|
4129
|
+
const autoCommitOption = Options.boolean("auto-commit").pipe(Options.withDescription("Complete the subtask as inputs are filled, without an explicit Submit. Default is form mode: the recipient submits everything at once."));
|
|
4033
4130
|
const waitOption = Options.boolean("wait").pipe(Options.withDescription("Block until the subtask is completed; print the result to stdout. Requires at least one input on the append. On a group append, the first member's completion wins."));
|
|
4034
4131
|
const formatOption = Options.choice("format", ["text", "json"]).pipe(Options.withDescription("stdout format for an append: `text` (the bare sub_ id(s), default) or `json` (a `sent` line piped to `sp collect`)."), Options.withDefault("text"));
|
|
4035
4132
|
const replyOption = Options.choice("reply", [
|
|
@@ -4054,7 +4151,7 @@ const subtaskCommand = Command.make("subtask", {
|
|
|
4054
4151
|
"location-input": locationInput,
|
|
4055
4152
|
link: linkOption,
|
|
4056
4153
|
file: fileOption,
|
|
4057
|
-
|
|
4154
|
+
"auto-commit": autoCommitOption,
|
|
4058
4155
|
wait: waitOption,
|
|
4059
4156
|
format: formatOption,
|
|
4060
4157
|
reply: replyOption,
|
|
@@ -4084,7 +4181,7 @@ const subtaskCommand = Command.make("subtask", {
|
|
|
4084
4181
|
...content !== void 0 ? { content } : {},
|
|
4085
4182
|
...inputs.length > 0 ? { inputs } : {},
|
|
4086
4183
|
links: [...args.link],
|
|
4087
|
-
autoCommit:
|
|
4184
|
+
autoCommit: args["auto-commit"],
|
|
4088
4185
|
...Option.isSome(args.reply) ? { reply: args.reply.value } : {},
|
|
4089
4186
|
...args.markdown ? { contentFormat: "markdown" } : {}
|
|
4090
4187
|
};
|
|
@@ -4243,11 +4340,12 @@ const root = Command.make("simplepush").pipe(Command.withSubcommands([
|
|
|
4243
4340
|
taskCommand,
|
|
4244
4341
|
subtaskCommand,
|
|
4245
4342
|
cancelCommand,
|
|
4246
|
-
getCommand
|
|
4343
|
+
getCommand,
|
|
4344
|
+
searchCommand
|
|
4247
4345
|
]));
|
|
4248
4346
|
const cli = Command.run(root, {
|
|
4249
4347
|
name: "Simplepush CLI",
|
|
4250
|
-
version
|
|
4348
|
+
version
|
|
4251
4349
|
});
|
|
4252
4350
|
const MainLive = Layer.mergeAll(CliOutput.Default, Sodium.Default, AuthStore.Default, VaultStore.Default, InviteStore.Default, Api.Default, VaultAccess.Default).pipe(Layer.provideMerge(FetchHttpClient.layer), Layer.provideMerge(NodeContext.layer));
|
|
4253
4351
|
/** Render any failure through the typed-error table, then re-fail so the
|