@letterstory/cli 0.70.0 → 0.70.2

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/README.md CHANGED
@@ -398,6 +398,10 @@ export LETTERSTORY_STAFF_API_KEY=<your staff key with crm:read,crm:write>
398
398
  letterstory admin crm signals --status new # the Inbound feed (unmatched signals)
399
399
  letterstory admin crm record --source bookface --kind message \
400
400
  --title "Asked about phantoms on Bookface" --name "Jane Doe" --company Acme --domain acme.com
401
+ # Re-running that is a no-op: with no --external-id the intake derives an idempotency key from
402
+ # the signal's content, so a retried agent loop cannot fill the feed with the same observation.
403
+ # Pass --external-id when the source has a stable id (a thread id, a tweet id), or to record that
404
+ # an otherwise identical signal happened AGAIN.
401
405
  letterstory admin crm record --source twitter --kind social_engagement \
402
406
  --title "Liked our launch tweet" --name "Pat" --linkedin https://linkedin.com/in/pat
403
407
  letterstory admin crm signal <signal-id> qualify # link/create the account
package/lib/cli.mjs CHANGED
@@ -56,7 +56,7 @@ import {
56
56
  } from "./commands.mjs";
57
57
 
58
58
  // Keep in sync with cli/package.json.
59
- export const VERSION = "0.70.0";
59
+ export const VERSION = "0.70.2";
60
60
 
61
61
  // Flags that never take a value. Listing them explicitly means `deploy get --json <id>`
62
62
  // can't accidentally swallow the id as --json's value.
@@ -826,13 +826,15 @@ Sequences (multi-step cold email sent from your mailers; approving or enrolling
826
826
  sequences get <sequence-id> Flow, enrollments, who replied at each step
827
827
  sequences create --name <n> (--flow '<json>'|--flow-file <path>) [--mailer <mailer-id>]...
828
828
  [--[no-]opt-out-footer] [--[no-]postal-address-footer] [--[no-]unsubscribe-links] [--[no-]detect-reply-opt-outs]
829
- [--[no-]test-mode] [--send-window-start <HH:MM>] [--send-window-end <HH:MM>] [--send-window-timezone <IANA tz>]
829
+ [--[no-]test-mode] [--default-lede <fallback opener line>]
830
+ [--send-window-start <HH:MM>] [--send-window-end <HH:MM>] [--send-window-timezone <IANA tz>]
830
831
  New draft sequence (nothing sends yet; rails default on, opt-out line off;
831
832
  sends 9:00-17:00 in each recipient's inferred timezone, else the sender's)
832
833
  sequences update <sequence-id> [--name <n>] [--flow '<json>'|--flow-file <path>]
833
834
  [--status draft|active|paused|archived] [--mailer <mailer-id>]...
834
835
  [--[no-]opt-out-footer] [--[no-]postal-address-footer] [--[no-]unsubscribe-links] [--[no-]detect-reply-opt-outs]
835
- [--[no-]test-mode] [--send-window-start <HH:MM>] [--send-window-end <HH:MM>] [--send-window-timezone <IANA tz>]
836
+ [--[no-]test-mode] [--default-lede <fallback opener line>]
837
+ [--send-window-start <HH:MM>] [--send-window-end <HH:MM>] [--send-window-timezone <IANA tz>]
836
838
  --test-mode (internal accounts only): walks as live, sends nothing
837
839
  sequences pause|resume <sequence-id> Stop / restart sending
838
840
  sequences enroll <sequence-id> (--list <list-id> | --email <addr>... | --recipients '<json>'|--recipients-file <path>)
@@ -1795,8 +1795,15 @@ async function cmdAdminCrm(ctx) {
1795
1795
  });
1796
1796
  if (!body.title)
1797
1797
  throw new CliError(
1798
- 'Usage: admin crm record --title "Liked our launch tweet" [--source twitter] [--kind social_engagement] …'
1798
+ 'Usage: admin crm record --title "Liked our launch tweet" [--source twitter] [--kind social_engagement] [--external-id <id>] …'
1799
1799
  );
1800
+ // No local default for --external-id on purpose. The intake derives one
1801
+ // from the signal's content when none is sent (deriveExternalId in
1802
+ // src/lib/crm/signals-intake.ts), so a re-run is already a no-op and the
1803
+ // rule lives in ONE place. Computing a second fingerprint here would be a
1804
+ // copy that drifts, and it would differ from what a webhook or any other
1805
+ // agent sends for the same observation. Pass --external-id when the source
1806
+ // has a stable id of its own, or to say "this happened again".
1800
1807
  const r = await staffPost(ctx, `${CRM}/signals`, body);
1801
1808
  return out(r, (x) => `${x.duplicate ? "already recorded" : "recorded"}: ${signalLine(x.signal)}`);
1802
1809
  }
@@ -61,6 +61,8 @@ const SEQUENCE_RAIL_ARGS = {
61
61
  detect_reply_opt_outs: { flag: "detect-reply-opt-outs", type: "bool", negate: "no-detect-reply-opt-outs" },
62
62
  // Staff-only to turn on (the server refuses it for anyone else): walks as live, sends nothing.
63
63
  test_mode: { flag: "test-mode", type: "bool", negate: "no-test-mode" },
64
+ // The fallback opener line for recipients with no opener (blank clears it on update).
65
+ default_lede: "default-lede",
64
66
  };
65
67
 
66
68
  // A send-window edge from --<flag> HH:MM (or a raw minute of day) → the
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@letterstory/cli",
3
- "version": "0.70.0",
3
+ "version": "0.70.2",
4
4
  "description": "Spin up and manage Letterstory phantom blogs from your terminal.",
5
5
  "type": "module",
6
6
  "license": "UNLICENSED",