@letterstory/cli 0.176.91 → 0.176.93
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 +9 -3
- package/lib/commands/admin.mjs +10 -3
- package/lib/commands/outbound.mjs +19 -0
- package/package.json +1 -1
package/lib/cli.mjs
CHANGED
|
@@ -86,7 +86,7 @@ import {
|
|
|
86
86
|
import { fetchParkedSections, withoutParked } from "./parked.mjs";
|
|
87
87
|
|
|
88
88
|
// Matches cli/package.json; publish-cli.yml rewrites both to the published version.
|
|
89
|
-
export const VERSION = "0.176.
|
|
89
|
+
export const VERSION = "0.176.93";
|
|
90
90
|
|
|
91
91
|
/** The exit code for a gate refusal (CARD_REQUIRED, PAYMENT_FAILED, OUT_OF_TOKENS): 1 is a plain error. */
|
|
92
92
|
export const EXIT_PAYMENT_REQUIRED = 3;
|
|
@@ -354,6 +354,8 @@ export const BOOLEAN_FLAGS = new Set([
|
|
|
354
354
|
"consent",
|
|
355
355
|
"no-redesign",
|
|
356
356
|
"no-test-mode",
|
|
357
|
+
"first-touch-only",
|
|
358
|
+
"no-first-touch-only",
|
|
357
359
|
"no-unsubscribe-links",
|
|
358
360
|
"now",
|
|
359
361
|
"opt-out-footer",
|
|
@@ -1640,6 +1642,7 @@ Sequences (multi-step cold email sent from your mailers; approving or enrolling
|
|
|
1640
1642
|
[--[no-]opt-out-footer] [--[no-]postal-address-footer] [--[no-]unsubscribe-links] [--[no-]detect-reply-opt-outs]
|
|
1641
1643
|
[--[no-]test-mode] [--default-lede <fallback opener line>]
|
|
1642
1644
|
[--send-window-start <HH:MM>] [--send-window-end <HH:MM>] [--send-window-timezone <IANA tz>]
|
|
1645
|
+
[--daily-send-cap <n>|none] [--[no-]first-touch-only]
|
|
1643
1646
|
New draft sequence (nothing sends yet; rails default on, opt-out line off;
|
|
1644
1647
|
sends 9:00-17:00 in each recipient's inferred timezone, else the sender's)
|
|
1645
1648
|
sequences update <sequence-id> [--name <n>] [--flow '<json>'|--flow-file <path>]
|
|
@@ -1648,6 +1651,7 @@ Sequences (multi-step cold email sent from your mailers; approving or enrolling
|
|
|
1648
1651
|
[--[no-]test-mode] [--default-lede <fallback opener line>]
|
|
1649
1652
|
[--send-window-start <HH:MM>] [--send-window-end <HH:MM>] [--send-window-timezone <IANA tz>]
|
|
1650
1653
|
[--enroll-on-cake paid|scheduled|out_for_delivery|delivered|none] [--enroll-on-cake-list <list-id>|none]
|
|
1654
|
+
[--daily-send-cap <n>|none] [--[no-]first-touch-only]
|
|
1651
1655
|
--test-mode (internal accounts only): walks as live, sends nothing
|
|
1652
1656
|
--enroll-on-cake: start it when a recipient's Daymaker cake reaches that stage
|
|
1653
1657
|
--enroll-on-cake-list: only for prospects in that list (none = any)
|
|
@@ -1686,8 +1690,10 @@ Prospects (who you write to):
|
|
|
1686
1690
|
--find-emails: look up the business email of rows with a
|
|
1687
1691
|
LinkedIn URL and no email (paid; default 25 lookups)
|
|
1688
1692
|
prospects source --description <who you want> [--list <list-id>|--list-name <n>] [--limit <1..100>]
|
|
1689
|
-
[--one-per-company] [--enrich]
|
|
1690
|
-
|
|
1693
|
+
[--one-per-company] [--enrich] [--exclude-platform webflow]...
|
|
1694
|
+
Fill a list from paid data providers (background);
|
|
1695
|
+
--enrich starts enrichment as soon as the pull lands;
|
|
1696
|
+
--exclude-platform leaves out sites built on it
|
|
1691
1697
|
prospects enrich [--list <list-id>] [--status <s>] [--q <search>]
|
|
1692
1698
|
Funding + competitors for un-enriched rows
|
|
1693
1699
|
prospects openers <list-id> [--group <ledes-group-id>|--new-group <name>]
|
package/lib/commands/admin.mjs
CHANGED
|
@@ -5172,6 +5172,8 @@ function recipeDetail(r) {
|
|
|
5172
5172
|
`${pad}recipe: ${v.recipe || "—"}`,
|
|
5173
5173
|
`${pad}when to use: ${v.when_to_use || "—"}`,
|
|
5174
5174
|
`${pad}when not to use: ${v.when_not_to_use || "—"}`,
|
|
5175
|
+
// The facts it cannot run without (the fit holds it back in code); unset is the legacy rule until reviewed.
|
|
5176
|
+
`${pad}required inputs: ${v.requires == null ? "not reviewed (the legacy rule)" : v.requires.join(", ") || "none"}`,
|
|
5175
5177
|
];
|
|
5176
5178
|
return [
|
|
5177
5179
|
`${r.name} weight ${r.weight}/10 ${r.id}`,
|
|
@@ -5186,8 +5188,13 @@ function recipeDetail(r) {
|
|
|
5186
5188
|
` its prompt: ${r.automation.prompt}`,
|
|
5187
5189
|
]
|
|
5188
5190
|
: []),
|
|
5189
|
-
...(r.
|
|
5190
|
-
|
|
5191
|
+
...(r.tool_access
|
|
5192
|
+
? [
|
|
5193
|
+
` letter's tools: its build needs ${r.tool_access.required?.join(", ") || "none named"}; may write ${r.tool_access.writes?.join(", ") || "nothing"} (tool_access)`,
|
|
5194
|
+
]
|
|
5195
|
+
: r.primitives_access
|
|
5196
|
+
? [" letter's tools: its build may read the account through them (primitives_access)"]
|
|
5197
|
+
: []),
|
|
5191
5198
|
...(variations.length > 1
|
|
5192
5199
|
? variations.flatMap((v, i) => [` variation ${i + 1}: ${v.name}`, ...fields(v, " ")])
|
|
5193
5200
|
: fields(r, " ")),
|
|
@@ -5240,7 +5247,7 @@ function recipeFromFlags(flags) {
|
|
|
5240
5247
|
// --primitives-access true hands a start's build Letter's own tools; false takes them away.
|
|
5241
5248
|
primitives_access:
|
|
5242
5249
|
flags["primitives-access"] === undefined ? undefined : String(flags["primitives-access"]) === "true",
|
|
5243
|
-
// --immediate
|
|
5250
|
+
// --immediate is accepted for old scripts and read by nothing: a quick row is one whose runs is empty.
|
|
5244
5251
|
immediate: flags.immediate === undefined ? undefined : String(flags.immediate) === "true",
|
|
5245
5252
|
// --reads llms_txt,github declares the facts its conditions rest on; --reads "" clears them.
|
|
5246
5253
|
reads: flagStr(flags.reads)
|
|
@@ -89,6 +89,22 @@ const SEQUENCE_WINDOW_ARGS = {
|
|
|
89
89
|
send_window_end_minute: { value: windowMinute("send-window-end") },
|
|
90
90
|
send_window_timezone: "send-window-timezone",
|
|
91
91
|
};
|
|
92
|
+
// --daily-send-cap <n|none>: the most emails the sequence sends per UTC day across its mailers;
|
|
93
|
+
// --first-touch-only: only people no other sequence has written to.
|
|
94
|
+
const SEQUENCE_LIMIT_ARGS = {
|
|
95
|
+
daily_send_cap: {
|
|
96
|
+
value: (flags) => {
|
|
97
|
+
const raw = flagStr(flags["daily-send-cap"]);
|
|
98
|
+
if (raw === undefined) return undefined;
|
|
99
|
+
if (raw === "none") return null;
|
|
100
|
+
const n = Number(raw);
|
|
101
|
+
if (!Number.isInteger(n) || n < 1 || n > 500)
|
|
102
|
+
throw new CliError(`--daily-send-cap takes 1–500 or none (got ${raw})`);
|
|
103
|
+
return n;
|
|
104
|
+
},
|
|
105
|
+
},
|
|
106
|
+
first_touch_only: { flag: "first-touch-only", type: "bool", negate: "no-first-touch-only" },
|
|
107
|
+
};
|
|
92
108
|
// --enroll-on-cake <stage>: start the sequence when a recipient's Daymaker cake
|
|
93
109
|
// reaches it; `none` clears it.
|
|
94
110
|
const CAKE_TRIGGER_STAGES = ["paid", "scheduled", "out_for_delivery", "delivered"];
|
|
@@ -169,6 +185,7 @@ export const ARGS = {
|
|
|
169
185
|
mailer_ids: mailerIds,
|
|
170
186
|
...SEQUENCE_RAIL_ARGS,
|
|
171
187
|
...SEQUENCE_WINDOW_ARGS,
|
|
188
|
+
...SEQUENCE_LIMIT_ARGS,
|
|
172
189
|
enroll_on_cake: enrollOnCake,
|
|
173
190
|
enroll_on_cake_list_id: enrollOnCakeList,
|
|
174
191
|
},
|
|
@@ -180,6 +197,7 @@ export const ARGS = {
|
|
|
180
197
|
mailer_ids: mailerIds,
|
|
181
198
|
...SEQUENCE_RAIL_ARGS,
|
|
182
199
|
...SEQUENCE_WINDOW_ARGS,
|
|
200
|
+
...SEQUENCE_LIMIT_ARGS,
|
|
183
201
|
enroll_on_cake: enrollOnCake,
|
|
184
202
|
enroll_on_cake_list_id: enrollOnCakeList,
|
|
185
203
|
},
|
|
@@ -291,6 +309,7 @@ export const ARGS = {
|
|
|
291
309
|
list_name: "list-name",
|
|
292
310
|
limit: { flag: "limit", type: "num" },
|
|
293
311
|
one_per_company: { flag: "one-per-company", type: "bool" },
|
|
312
|
+
exclude_platforms: { flag: "exclude-platform", type: "list" },
|
|
294
313
|
enrich: { flag: "enrich", type: "bool" },
|
|
295
314
|
},
|
|
296
315
|
enrich_prospects: {
|