@letterstory/cli 0.141.0 → 0.143.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/README.md +5 -1
- package/lib/cli.mjs +13 -4
- package/lib/commands/admin.mjs +77 -5
- package/lib/commands/org.mjs +26 -1
- package/lib/commands/sim.mjs +14 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -481,6 +481,7 @@ letterstory admin crm onboarding <account-id> # step-by-step progress (
|
|
|
481
481
|
letterstory admin crm retry-onboarding <onboarding-id> # re-run only the failed steps
|
|
482
482
|
letterstory admin crm onboard-plan <account-id> # what the calls say: sites, Slack/app people, paid?
|
|
483
483
|
letterstory admin crm payment-link <account-id> # Stripe Payment Link at the deal amount (billing:write)
|
|
484
|
+
letterstory admin crm payment-link <account-id> --plan growth # with Lago signups on: a card-only start link, Lago bills the plan
|
|
484
485
|
```
|
|
485
486
|
|
|
486
487
|
`onboard` runs org → link → phantom sites → Slack channel → checklist → app invites →
|
|
@@ -490,7 +491,10 @@ disclosure ("safe mode") for that site, and `--disclose-note "…"` records the
|
|
|
490
491
|
as the reason; `onboard-plan` flags the sites whose calls asked for it. `onboard-plan` reads the account's recorded calls
|
|
491
492
|
(newest first, cached per set of calls; `--refresh` re-reads) and says how many sites, what each
|
|
492
493
|
is about, who to invite, and whether they've paid. `payment-link` stamps the account's org on
|
|
493
|
-
the Stripe subscription, so paying through it binds and marks the account paid by itself.
|
|
494
|
+
the Stripe subscription, so paying through it binds and marks the account paid by itself. With
|
|
495
|
+
Lago signups on (`LETTERBRACE_LAGO_SIGNUP`), it is a start link of ours instead: the client saves a
|
|
496
|
+
card, Lago charges the plan (`--plan`, default the one priced at the deal amount; `--ratio`, `--why`
|
|
497
|
+
as for `admin billing lago provision`), and no Stripe subscription is made. The account needs an org.
|
|
494
498
|
|
|
495
499
|
Every trigger — Cal.com bookings, Circleback recordings, Letterhome dinner RSVPs, RB2B
|
|
496
500
|
visits, and what an agent records here — goes through one intake (`ingestSignal`,
|
package/lib/cli.mjs
CHANGED
|
@@ -64,7 +64,7 @@ import {
|
|
|
64
64
|
} from "./commands.mjs";
|
|
65
65
|
|
|
66
66
|
// Keep in sync with cli/package.json.
|
|
67
|
-
export const VERSION = "0.
|
|
67
|
+
export const VERSION = "0.143.0";
|
|
68
68
|
|
|
69
69
|
// Flags that never take a value. Listing them explicitly means `deploy get --json <id>`
|
|
70
70
|
// can't accidentally swallow the id as --json's value. Every `type: "bool"` flag (and its
|
|
@@ -75,6 +75,8 @@ export const BOOLEAN_FLAGS = new Set([
|
|
|
75
75
|
// sim seed: make the new seed active; collections it doesn't cover start empty.
|
|
76
76
|
"activate",
|
|
77
77
|
"empty",
|
|
78
|
+
// sim create: agents can use it at once, instead of starting paused for review.
|
|
79
|
+
"live",
|
|
78
80
|
// admin models runs: only the starred ones.
|
|
79
81
|
"starred",
|
|
80
82
|
// shredder file / arena file: save the version as a candidate beside live.
|
|
@@ -295,6 +297,9 @@ export const BOOLEAN_FLAGS = new Set([
|
|
|
295
297
|
"nested",
|
|
296
298
|
// admin billing lago catalog --apply --include-drafts
|
|
297
299
|
"include-drafts",
|
|
300
|
+
// admin billing lago payments --open / payments run --dry
|
|
301
|
+
"open",
|
|
302
|
+
"dry",
|
|
298
303
|
]);
|
|
299
304
|
|
|
300
305
|
// Tiny argv parser: `--flag value`, `--flag=value`, boolean `--flag`, and positionals.
|
|
@@ -924,10 +929,14 @@ Admin (STAFF key — --staff-key / LETTERSTORY_STAFF_API_KEY; acts before an org
|
|
|
924
929
|
admin billing lago catalog [--apply] [--include-drafts]
|
|
925
930
|
| provision <org-id> --plan starter|growth|scale [--ratio 1|2|4|8 (8 needs --why "…")] [--complimentary N] [--stripe-customer cus_…] [--start ISO] [--review D]
|
|
926
931
|
| reconcile [--org <org-id>] | forward
|
|
932
|
+
| payments [--org <org-id>] [--open] | payments retry <lago-invoice-id> | payments run [--dry]
|
|
933
|
+
| signups [--status open|card_saved|provisioned|paid|abandoned]
|
|
927
934
|
Usage billing on Lago: the plan catalog, an org's
|
|
928
935
|
plan and gifted ratio, Lago's tokens against ours,
|
|
929
|
-
|
|
930
|
-
|
|
936
|
+
forwarding usage now, failed payments and the
|
|
937
|
+
retry loop, and new signups on their way onto
|
|
938
|
+
Lago. Needs spend:read (reads) / billing:write
|
|
939
|
+
(writes).
|
|
931
940
|
admin crm import <file.csv> [--source <name>] [--tag "…"] [--dry-run]
|
|
932
941
|
| capture [shot.png …] [--text "…" | --text-file f] [--context "this is for acme.com"]
|
|
933
942
|
[--account <id>] [--dry-run] [--retry] screenshots / a thread / a note → a lead
|
|
@@ -958,7 +967,7 @@ Admin (STAFF key — --staff-key / LETTERSTORY_STAFF_API_KEY; acts before an org
|
|
|
958
967
|
[--slack a@acme.com,b@acme.com] [--invite a@acme.com[:admin]]… [--no-checklist]
|
|
959
968
|
| onboarding <account-id> | retry-onboarding <onboarding-id>
|
|
960
969
|
| onboard-plan <account-id> [--refresh]
|
|
961
|
-
| payment-link <account-id> [--amount-cents N] [--description "…"]
|
|
970
|
+
| payment-link <account-id> [--amount-cents N] [--description "…"] [--plan starter] [--ratio N] [--why "…"]
|
|
962
971
|
The staff CRM's pipeline: bulk-importing a
|
|
963
972
|
CSV of leads, the inbound signal
|
|
964
973
|
feed, recording a signal with no webhook
|
package/lib/commands/admin.mjs
CHANGED
|
@@ -3719,14 +3719,24 @@ async function cmdAdminCrm(ctx) {
|
|
|
3719
3719
|
}
|
|
3720
3720
|
case "payment-link": {
|
|
3721
3721
|
// admin crm payment-link <account-id> [--amount-cents N] [--description "…"] — a Stripe
|
|
3722
|
-
// Payment Link for the monthly subscription (default: the deal amount).
|
|
3723
|
-
//
|
|
3722
|
+
// Payment Link for the monthly subscription (default: the deal amount). With Lago signups on,
|
|
3723
|
+
// a start link that saves a card and puts the org on a Lago plan instead: [--plan starter]
|
|
3724
|
+
// (default: the plan priced at the deal) [--ratio 2] [--why "…" under the price floor].
|
|
3725
|
+
// The key needs crm:write + billing:write.
|
|
3724
3726
|
if (!id)
|
|
3725
|
-
throw new CliError(
|
|
3727
|
+
throw new CliError(
|
|
3728
|
+
'Usage: admin crm payment-link <account-id> [--amount-cents N] [--description "…"] [--plan starter] [--ratio 1|2|4|8] [--why "…"]'
|
|
3729
|
+
);
|
|
3726
3730
|
const r = await staffPost(
|
|
3727
3731
|
ctx,
|
|
3728
3732
|
`${CRM}/accounts/${id}/payment-link`,
|
|
3729
|
-
compact({
|
|
3733
|
+
compact({
|
|
3734
|
+
amountCents: flagNum(flags["amount-cents"]),
|
|
3735
|
+
description: flagStr(flags.description),
|
|
3736
|
+
planCode: flagStr(flags.plan),
|
|
3737
|
+
ratio: flagNum(flags.ratio),
|
|
3738
|
+
exceptionReason: flagStr(flags.why),
|
|
3739
|
+
})
|
|
3730
3740
|
);
|
|
3731
3741
|
return out(
|
|
3732
3742
|
r,
|
|
@@ -5001,7 +5011,11 @@ const LAGO_USAGE = `Usage: admin billing lago <command>
|
|
|
5001
5011
|
catalog [--apply] [--include-drafts] what the plan catalog would change in Lago; --apply makes it so
|
|
5002
5012
|
provision <org-id> --plan starter [--ratio 4] [--why "…" above 4x] [--complimentary 20000] [--stripe-customer cus_…] [--start 2026-11-02T00:00:00Z] [--review 2027-05-01]
|
|
5003
5013
|
reconcile [--org <org-id>] Lago's tokens this period against what we rated and forwarded
|
|
5004
|
-
forward forward billable usage to Lago now (the cron's pass)
|
|
5014
|
+
forward forward billable usage to Lago now (the cron's pass)
|
|
5015
|
+
payments [--org <org-id>] [--open] failed payments and what the retry loop did about each
|
|
5016
|
+
payments retry <lago-invoice-id> charge one failed invoice again now
|
|
5017
|
+
payments run [--dry] one pass of the failed-payment loop now (--dry: report only)
|
|
5018
|
+
signups [--status open|card_saved|provisioned|paid|abandoned] new paying orgs on their way onto Lago`;
|
|
5005
5019
|
|
|
5006
5020
|
/** admin billing lago … — staff operations on Lago (docs/billing/lago-integration.md). */
|
|
5007
5021
|
async function cmdAdminBilling(ctx) {
|
|
@@ -5066,6 +5080,64 @@ async function cmdAdminBilling(ctx) {
|
|
|
5066
5080
|
printResult(io, flags, result);
|
|
5067
5081
|
return 0;
|
|
5068
5082
|
}
|
|
5083
|
+
case "payments": {
|
|
5084
|
+
const action = positionals[2];
|
|
5085
|
+
if (action === "retry") {
|
|
5086
|
+
const invoiceId = requirePositional(positionals, 3, "lago-invoice-id");
|
|
5087
|
+
const result = await staffPost(ctx, `${LAGO}/payments/retry`, { invoiceId });
|
|
5088
|
+
ok(ctx, `Charging ${invoiceId} again; the loop reads the result within the hour.`);
|
|
5089
|
+
printResult(io, flags, result);
|
|
5090
|
+
return 0;
|
|
5091
|
+
}
|
|
5092
|
+
if (action === "run") {
|
|
5093
|
+
const result = await staffPost(
|
|
5094
|
+
ctx,
|
|
5095
|
+
`${LAGO}/payments/run`,
|
|
5096
|
+
compact({ dry: flagBool(flags.dry) || undefined })
|
|
5097
|
+
);
|
|
5098
|
+
ok(
|
|
5099
|
+
ctx,
|
|
5100
|
+
result.mode === "dry"
|
|
5101
|
+
? `Would: ${result.actions.length ? result.actions.join("; ") : "nothing"}.`
|
|
5102
|
+
: `Opened ${result.opened}, resolved ${result.resolved}, emailed ${result.emails}, retried ${result.retries}, gave up ${result.gaveUp}.`
|
|
5103
|
+
);
|
|
5104
|
+
printResult(io, flags, result);
|
|
5105
|
+
return result.errors?.length ? 1 : 0;
|
|
5106
|
+
}
|
|
5107
|
+
if (action !== undefined) throw new CliError(LAGO_USAGE);
|
|
5108
|
+
const params = new URLSearchParams(
|
|
5109
|
+
compact({ orgId: flagStr(flags.org), open: flagBool(flags.open) ? "true" : undefined })
|
|
5110
|
+
);
|
|
5111
|
+
const result = await staffRequest(ctx, "GET", `${LAGO}/payments${params.size ? `?${params}` : ""}`);
|
|
5112
|
+
printResult(io, flags, result, (r) =>
|
|
5113
|
+
[
|
|
5114
|
+
`Failed-payment loop: ${r.mode}`,
|
|
5115
|
+
...(r.payments.length
|
|
5116
|
+
? r.payments.map(
|
|
5117
|
+
(p) =>
|
|
5118
|
+
` ${p.invoiceNumber ?? p.invoiceId} ${p.orgName ?? p.orgId} $${(p.amountCents / 100).toFixed(2)} ${p.status} tries ${p.attempts}, emails ${p.emailsSent}${p.nextAttemptAt ? `, next ${p.nextAttemptAt}` : ""}${p.lastError ? ` (${p.lastError})` : ""}`
|
|
5119
|
+
)
|
|
5120
|
+
: [" No failed payments."]),
|
|
5121
|
+
].join("\n")
|
|
5122
|
+
);
|
|
5123
|
+
return 0;
|
|
5124
|
+
}
|
|
5125
|
+
case "signups": {
|
|
5126
|
+
const params = new URLSearchParams(compact({ status: flagStr(flags.status) }));
|
|
5127
|
+
const result = await staffRequest(ctx, "GET", `${LAGO}/signups${params.size ? `?${params}` : ""}`);
|
|
5128
|
+
printResult(io, flags, result, (r) =>
|
|
5129
|
+
[
|
|
5130
|
+
`Lago signups: ${r.active ? "on" : "off"}${r.from ? ` (from ${r.from})` : ""}`,
|
|
5131
|
+
...(r.signups.length
|
|
5132
|
+
? r.signups.map(
|
|
5133
|
+
(x) =>
|
|
5134
|
+
` ${x.orgName ?? x.orgId} ${x.planCode}${x.ratio !== 1 ? ` ${x.ratio}x` : ""} ${x.status} ${x.source}${x.lastError ? ` (${x.lastError})` : ""}`
|
|
5135
|
+
)
|
|
5136
|
+
: [" None."]),
|
|
5137
|
+
].join("\n")
|
|
5138
|
+
);
|
|
5139
|
+
return 0;
|
|
5140
|
+
}
|
|
5069
5141
|
default:
|
|
5070
5142
|
throw new CliError(LAGO_USAGE);
|
|
5071
5143
|
}
|
package/lib/commands/org.mjs
CHANGED
|
@@ -27,6 +27,7 @@ export const ARGS = {
|
|
|
27
27
|
},
|
|
28
28
|
describe_org_config_fields: {},
|
|
29
29
|
get_org_config_source: {},
|
|
30
|
+
get_billing_payments: {},
|
|
30
31
|
list_integrations: {},
|
|
31
32
|
get_linear_connection: {},
|
|
32
33
|
list_linear_targets: { team_id: { flag: "team" } },
|
|
@@ -68,9 +69,11 @@ export async function cmdOrg(ctx) {
|
|
|
68
69
|
return orgFields(rest);
|
|
69
70
|
case "linear":
|
|
70
71
|
return orgLinear(rest);
|
|
72
|
+
case "billing":
|
|
73
|
+
return orgBilling(rest);
|
|
71
74
|
default:
|
|
72
75
|
throw new CliError(
|
|
73
|
-
`Unknown org subcommand: ${sub ?? "(none)"}. Try: export, join, diff, apply, git, integrations, linear, fields`
|
|
76
|
+
`Unknown org subcommand: ${sub ?? "(none)"}. Try: export, join, diff, apply, git, integrations, linear, fields, billing`
|
|
74
77
|
);
|
|
75
78
|
}
|
|
76
79
|
}
|
|
@@ -87,6 +90,28 @@ async function orgIntegrations(ctx) {
|
|
|
87
90
|
return 0;
|
|
88
91
|
}
|
|
89
92
|
|
|
93
|
+
/** `org billing`: the org's plan on Lago, whether it is past due, and its failed payments (get_billing_payments). */
|
|
94
|
+
async function orgBilling(ctx) {
|
|
95
|
+
const { client, flags, io } = ctx;
|
|
96
|
+
refuseUnknownFlags(ctx, {}, []);
|
|
97
|
+
const result = await client.callTool("get_billing_payments", {});
|
|
98
|
+
printResult(io, flags, result, (r) =>
|
|
99
|
+
[
|
|
100
|
+
r.plan
|
|
101
|
+
? `Plan: ${r.plan.name}${r.plan.ratio !== 1 ? ` (${r.plan.ratio}x)` : ""}`
|
|
102
|
+
: "Not billed by usage yet.",
|
|
103
|
+
...(r.pastDueAt ? [`Past due since ${r.pastDueAt}.`] : []),
|
|
104
|
+
...(r.payments.length
|
|
105
|
+
? r.payments.map(
|
|
106
|
+
(p) =>
|
|
107
|
+
` ${p.invoiceNumber ?? p.invoiceId} $${(p.amountCents / 100).toFixed(2)} ${p.status}${p.nextAttemptAt ? `, next try ${p.nextAttemptAt}` : ""}${p.payUrl ? ` pay: ${p.payUrl}` : ""}`
|
|
108
|
+
)
|
|
109
|
+
: ["No failed payments."]),
|
|
110
|
+
].join("\n")
|
|
111
|
+
);
|
|
112
|
+
return 0;
|
|
113
|
+
}
|
|
114
|
+
|
|
90
115
|
/** `org fields [section]`: what each setting in the bundle means (describe_org_config_fields). */
|
|
91
116
|
async function orgFields(ctx) {
|
|
92
117
|
const { client, flags, io, positionals } = ctx;
|
package/lib/commands/sim.mjs
CHANGED
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
// letterstory sim key <id> --agent our-ci --goal "nightly integration test"
|
|
11
11
|
// letterstory sim seed <id> --prompt "a 3-person content team with two blogs" --activate
|
|
12
12
|
// letterstory sim seed <id> --use <seed-id|none>
|
|
13
|
+
// letterstory sim limits <id> --daily-calls 5000 --daily-sessions 100
|
|
13
14
|
// letterstory sim report <id> --since 7d
|
|
14
15
|
// letterstory sim sessions <id>
|
|
15
16
|
// letterstory sim session <session-id>
|
|
@@ -57,12 +58,15 @@ export const ARGS = {
|
|
|
57
58
|
},
|
|
58
59
|
},
|
|
59
60
|
tool_route: "tool-route",
|
|
61
|
+
live: { flag: "live", type: "bool" },
|
|
60
62
|
auth_header: "auth-header",
|
|
61
63
|
},
|
|
62
64
|
list_simulations: {},
|
|
63
65
|
get_simulation: { simulation_id: simulationId },
|
|
64
66
|
update_simulation: {
|
|
65
67
|
simulation_id: simulationId,
|
|
68
|
+
daily_call_cap: { flag: "daily-calls", type: "num" },
|
|
69
|
+
daily_session_cap: { flag: "daily-sessions", type: "num" },
|
|
66
70
|
status: { flag: "status", enum: ["live", "paused"] },
|
|
67
71
|
active_seed_id: {
|
|
68
72
|
value: (flags) => {
|
|
@@ -119,6 +123,9 @@ export async function cmdSim(ctx) {
|
|
|
119
123
|
if (sub !== "rule") rest.flags = { ...rest.flags, status: sub === "pause" ? "paused" : "live" };
|
|
120
124
|
return simShow(rest, await call(rest, "update_simulation"));
|
|
121
125
|
}
|
|
126
|
+
case "limits":
|
|
127
|
+
refuseUnknownFlags(rest, ARGS.update_simulation, []);
|
|
128
|
+
return simShow(rest, await call(rest, "update_simulation"));
|
|
122
129
|
case "seed":
|
|
123
130
|
return simSeed(rest);
|
|
124
131
|
case "key":
|
|
@@ -130,7 +137,9 @@ export async function cmdSim(ctx) {
|
|
|
130
137
|
case "report":
|
|
131
138
|
return simReport(rest);
|
|
132
139
|
default:
|
|
133
|
-
throw new CliError(
|
|
140
|
+
throw new CliError(
|
|
141
|
+
"Usage: sim [list|create|show|rule|pause|resume|limits|seed|key|sessions|session|report]"
|
|
142
|
+
);
|
|
134
143
|
}
|
|
135
144
|
}
|
|
136
145
|
|
|
@@ -157,6 +166,10 @@ function simShow(ctx, result) {
|
|
|
157
166
|
` REST ${s.urls.base}`,
|
|
158
167
|
` MCP ${s.urls.mcp}`,
|
|
159
168
|
` Docs ${s.urls.llms}`,
|
|
169
|
+
` Limits ${s.daily_call_cap} calls and ${s.daily_session_cap} new sessions per 24 h; ${s.session_call_cap} calls per session`,
|
|
170
|
+
...(s.status === "paused"
|
|
171
|
+
? [" Paused: review the rules below, then `sim resume <id>` to let agents in."]
|
|
172
|
+
: []),
|
|
160
173
|
"",
|
|
161
174
|
"Resources (rule · side effect · who set it):",
|
|
162
175
|
];
|