@letterstory/cli 0.90.0 → 0.91.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/lib/cli.mjs +8 -3
- package/lib/commands/outbound.mjs +12 -13
- package/lib/commands/shared.mjs +31 -0
- package/package.json +1 -1
package/lib/cli.mjs
CHANGED
|
@@ -57,7 +57,7 @@ import {
|
|
|
57
57
|
} from "./commands.mjs";
|
|
58
58
|
|
|
59
59
|
// Keep in sync with cli/package.json.
|
|
60
|
-
export const VERSION = "0.
|
|
60
|
+
export const VERSION = "0.91.0";
|
|
61
61
|
|
|
62
62
|
// Flags that never take a value. Listing them explicitly means `deploy get --json <id>`
|
|
63
63
|
// can't accidentally swallow the id as --json's value. Every `type: "bool"` flag (and its
|
|
@@ -135,9 +135,10 @@ export const BOOLEAN_FLAGS = new Set([
|
|
|
135
135
|
"no-enrich",
|
|
136
136
|
// `offerings set-collection <id> --none` (the org itself is the subject again).
|
|
137
137
|
"none",
|
|
138
|
-
// `prospects import|source --one-per-company`, `prospects source --enrich`.
|
|
138
|
+
// `prospects import|source --one-per-company`, `prospects source --enrich`, `prospects import --find-emails`.
|
|
139
139
|
"one-per-company",
|
|
140
140
|
"enrich",
|
|
141
|
+
"find-emails",
|
|
141
142
|
// `targets update <id> --clear-framing`; `targets subqueries --retired`,
|
|
142
143
|
// `targets subquery update <id> --retire` / `--restore`.
|
|
143
144
|
"clear-framing",
|
|
@@ -992,9 +993,10 @@ Sequences (multi-step cold email sent from your mailers; approving or enrolling
|
|
|
992
993
|
[--[no-]opt-out-footer] [--[no-]postal-address-footer] [--[no-]unsubscribe-links] [--[no-]detect-reply-opt-outs]
|
|
993
994
|
[--[no-]test-mode] [--default-lede <fallback opener line>]
|
|
994
995
|
[--send-window-start <HH:MM>] [--send-window-end <HH:MM>] [--send-window-timezone <IANA tz>]
|
|
995
|
-
[--enroll-on-cake paid|scheduled|out_for_delivery|delivered|none]
|
|
996
|
+
[--enroll-on-cake paid|scheduled|out_for_delivery|delivered|none] [--enroll-on-cake-list <list-id>|none]
|
|
996
997
|
--test-mode (internal accounts only): walks as live, sends nothing
|
|
997
998
|
--enroll-on-cake: start it when a recipient's Daymaker cake reaches that stage
|
|
999
|
+
--enroll-on-cake-list: only for prospects in that list (none = any)
|
|
998
1000
|
sequences pause|resume <sequence-id> Stop / restart sending
|
|
999
1001
|
sequences enroll <sequence-id> (--list <list-id> | --email <addr>... | --recipients '<json>'|--recipients-file <path>)
|
|
1000
1002
|
sequences stop <sequence-id> <enrollment-id> Stop one recipient
|
|
@@ -1023,6 +1025,9 @@ Prospects (who you write to):
|
|
|
1023
1025
|
prospects get <prospect-id>
|
|
1024
1026
|
prospects create-list --name <n>
|
|
1025
1027
|
prospects import --csv-file <path|-> [--list <list-id>|--list-name <n>] [--one-per-company]
|
|
1028
|
+
[--find-emails [--max-email-lookups <1..100>]]
|
|
1029
|
+
--find-emails: look up the business email of rows with a
|
|
1030
|
+
LinkedIn URL and no email (paid; default 25 lookups)
|
|
1026
1031
|
prospects source --description <who you want> [--list <list-id>|--list-name <n>] [--limit <1..100>]
|
|
1027
1032
|
[--one-per-company] [--enrich] Fill a list from paid data providers (background);
|
|
1028
1033
|
--enrich starts enrichment as soon as the pull lands
|
|
@@ -93,6 +93,13 @@ const SEQUENCE_WINDOW_ARGS = {
|
|
|
93
93
|
// reaches it; `none` clears it.
|
|
94
94
|
const CAKE_TRIGGER_STAGES = ["paid", "scheduled", "out_for_delivery", "delivered"];
|
|
95
95
|
const CAKE_STAGES = ["draft", ...CAKE_TRIGGER_STAGES, "delivery_failed", "cancelled"];
|
|
96
|
+
const enrollOnCakeList = {
|
|
97
|
+
value: (flags) => {
|
|
98
|
+
const raw = flagStr(flags["enroll-on-cake-list"]);
|
|
99
|
+
if (raw === undefined) return undefined;
|
|
100
|
+
return raw === "none" ? null : raw;
|
|
101
|
+
},
|
|
102
|
+
};
|
|
96
103
|
const enrollOnCake = {
|
|
97
104
|
value: (flags) => {
|
|
98
105
|
const raw = flagStr(flags["enroll-on-cake"]);
|
|
@@ -163,6 +170,7 @@ export const ARGS = {
|
|
|
163
170
|
...SEQUENCE_RAIL_ARGS,
|
|
164
171
|
...SEQUENCE_WINDOW_ARGS,
|
|
165
172
|
enroll_on_cake: enrollOnCake,
|
|
173
|
+
enroll_on_cake_list_id: enrollOnCakeList,
|
|
166
174
|
},
|
|
167
175
|
update_sequence: {
|
|
168
176
|
sequence_id: { positional: 0, label: "sequence-id", required: true },
|
|
@@ -173,6 +181,7 @@ export const ARGS = {
|
|
|
173
181
|
...SEQUENCE_RAIL_ARGS,
|
|
174
182
|
...SEQUENCE_WINDOW_ARGS,
|
|
175
183
|
enroll_on_cake: enrollOnCake,
|
|
184
|
+
enroll_on_cake_list_id: enrollOnCakeList,
|
|
176
185
|
},
|
|
177
186
|
list_cakes: {
|
|
178
187
|
stage: { flag: "stage", enum: CAKE_STAGES },
|
|
@@ -265,6 +274,8 @@ export const ARGS = {
|
|
|
265
274
|
return csv;
|
|
266
275
|
},
|
|
267
276
|
},
|
|
277
|
+
find_emails: { flag: "find-emails", type: "bool" },
|
|
278
|
+
max_email_lookups: { flag: "max-email-lookups", type: "num" },
|
|
268
279
|
one_per_company: { flag: "one-per-company", type: "bool" },
|
|
269
280
|
list_id: "list",
|
|
270
281
|
list_name: "list-name",
|
|
@@ -378,20 +389,8 @@ export const ARGS = {
|
|
|
378
389
|
},
|
|
379
390
|
};
|
|
380
391
|
|
|
381
|
-
// Flags the computed `value` sources read (refuseUnknownFlags can't see them).
|
|
382
|
-
const VALUE_FLAGS = {
|
|
383
|
-
create_sequence: ["flow", "flow-file", "mailer", "send-window-start", "send-window-end"],
|
|
384
|
-
update_sequence: ["flow", "flow-file", "mailer", "send-window-start", "send-window-end"],
|
|
385
|
-
enroll_in_sequence: ["recipients", "recipients-file", "email"],
|
|
386
|
-
update_review_item: ["body", "file", "personalize", "personalize-mode"],
|
|
387
|
-
delete_prospects: ["id"],
|
|
388
|
-
import_prospects_csv: ["csv", "csv-file"],
|
|
389
|
-
update_prospect: ["custom"],
|
|
390
|
-
set_mailer_forwarding: ["to", "clear"],
|
|
391
|
-
};
|
|
392
|
-
|
|
393
392
|
async function run(ctx, tool, message) {
|
|
394
|
-
refuseUnknownFlags(ctx, ARGS[tool]
|
|
393
|
+
refuseUnknownFlags(ctx, ARGS[tool]);
|
|
395
394
|
const result = await ctx.client.callTool(tool, argsFromFlags(ARGS[tool], ctx.flags, ctx.positionals));
|
|
396
395
|
if (message) ok(ctx, message);
|
|
397
396
|
printResult(ctx.io, ctx.flags, result);
|
package/lib/commands/shared.mjs
CHANGED
|
@@ -83,6 +83,35 @@ export const GLOBAL_FLAGS = new Set([
|
|
|
83
83
|
"profile",
|
|
84
84
|
]);
|
|
85
85
|
|
|
86
|
+
/**
|
|
87
|
+
* The flags a computed `{ value }` source reads, found by running it against
|
|
88
|
+
* no flags and recording each key it looks up. A value source names its flags
|
|
89
|
+
* inside code the map can't see, so `sequences update --enroll-on-cake` was
|
|
90
|
+
* refused by a command whose help and parser both took it.
|
|
91
|
+
*/
|
|
92
|
+
export function valueSourceFlags(value) {
|
|
93
|
+
const read = new Set();
|
|
94
|
+
const flags = new Proxy(
|
|
95
|
+
{},
|
|
96
|
+
{
|
|
97
|
+
get: (_target, key) => {
|
|
98
|
+
if (typeof key === "string") read.add(key);
|
|
99
|
+
return undefined;
|
|
100
|
+
},
|
|
101
|
+
has: (_target, key) => {
|
|
102
|
+
if (typeof key === "string") read.add(key);
|
|
103
|
+
return false;
|
|
104
|
+
},
|
|
105
|
+
}
|
|
106
|
+
);
|
|
107
|
+
try {
|
|
108
|
+
value(flags, []);
|
|
109
|
+
} catch {
|
|
110
|
+
// A required source throws when its flag is absent; the read was recorded first.
|
|
111
|
+
}
|
|
112
|
+
return read;
|
|
113
|
+
}
|
|
114
|
+
|
|
86
115
|
/**
|
|
87
116
|
* Refuse a flag this command does not understand.
|
|
88
117
|
*
|
|
@@ -105,6 +134,8 @@ export function refuseUnknownFlags(ctx, argsMap, own = []) {
|
|
|
105
134
|
else if (source && typeof source === "object" && source.flag) {
|
|
106
135
|
allowed.add(source.flag);
|
|
107
136
|
if (source.negate) allowed.add(source.negate);
|
|
137
|
+
} else if (source && typeof source === "object" && typeof source.value === "function") {
|
|
138
|
+
valueSourceFlags(source.value).forEach((f) => allowed.add(f));
|
|
108
139
|
}
|
|
109
140
|
}
|
|
110
141
|
const unknown = Object.keys(ctx.flags).filter((f) => !allowed.has(f));
|