@letterapp/cli 0.4.1 → 0.5.1
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/index.js +224 -70
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -38,11 +38,18 @@ function printWarning(msg) {
|
|
|
38
38
|
}
|
|
39
39
|
function printError(err) {
|
|
40
40
|
const msg = err instanceof Error ? err.message : String(err);
|
|
41
|
+
const issues = err?.issues;
|
|
41
42
|
if (jsonMode) {
|
|
42
|
-
printJson({ error: msg });
|
|
43
|
+
printJson({ error: msg, ...issues?.length ? { issues } : {} });
|
|
43
44
|
return;
|
|
44
45
|
}
|
|
45
46
|
console.error(chalk.red("\u2717") + " " + msg);
|
|
47
|
+
if (Array.isArray(issues)) {
|
|
48
|
+
for (const i of issues) {
|
|
49
|
+
const path3 = i.path ? chalk.dim(`${i.path}: `) : "";
|
|
50
|
+
console.error(" " + path3 + (i.message ?? ""));
|
|
51
|
+
}
|
|
52
|
+
}
|
|
46
53
|
}
|
|
47
54
|
function banner() {
|
|
48
55
|
if (jsonMode) return;
|
|
@@ -205,10 +212,7 @@ var LetterClient = class {
|
|
|
205
212
|
}
|
|
206
213
|
const data = res.status === 204 ? {} : await res.json();
|
|
207
214
|
if (!res.ok) {
|
|
208
|
-
|
|
209
|
-
const err = new Error(msg);
|
|
210
|
-
err.status = res.status;
|
|
211
|
-
throw err;
|
|
215
|
+
throw apiErrorFrom(data, res.status);
|
|
212
216
|
}
|
|
213
217
|
return { status: res.status, data };
|
|
214
218
|
}
|
|
@@ -216,6 +220,16 @@ var LetterClient = class {
|
|
|
216
220
|
return this.request("GET", path3);
|
|
217
221
|
}
|
|
218
222
|
};
|
|
223
|
+
function apiErrorFrom(data, status) {
|
|
224
|
+
const errObj = data?.error;
|
|
225
|
+
const msg = errObj?.message || `HTTP ${status}`;
|
|
226
|
+
const err = new Error(msg);
|
|
227
|
+
err.status = status;
|
|
228
|
+
if (Array.isArray(errObj?.issues) && errObj.issues.length > 0) {
|
|
229
|
+
err.issues = errObj.issues;
|
|
230
|
+
}
|
|
231
|
+
return err;
|
|
232
|
+
}
|
|
219
233
|
var client = new LetterClient();
|
|
220
234
|
var ManagementClient = class {
|
|
221
235
|
maxRetries = 3;
|
|
@@ -262,10 +276,7 @@ var ManagementClient = class {
|
|
|
262
276
|
const text = await res.text();
|
|
263
277
|
const data = text ? JSON.parse(text) : {};
|
|
264
278
|
if (!res.ok) {
|
|
265
|
-
|
|
266
|
-
const err = new Error(msg);
|
|
267
|
-
err.status = res.status;
|
|
268
|
-
throw err;
|
|
279
|
+
throw apiErrorFrom(data, res.status);
|
|
269
280
|
}
|
|
270
281
|
return { status: res.status, data };
|
|
271
282
|
}
|
|
@@ -819,31 +830,36 @@ function pushText(nodes, text, marks) {
|
|
|
819
830
|
if (!text) return;
|
|
820
831
|
nodes.push(marks ? { type: "text", text, marks } : { type: "text", text });
|
|
821
832
|
}
|
|
822
|
-
function inline(text) {
|
|
833
|
+
function inline(text, carried = []) {
|
|
823
834
|
const nodes = [];
|
|
824
|
-
const token = /(`[^`]+`)|(\[[^\]]+\]\([^)]+\))|(\*\*[
|
|
835
|
+
const token = /(`[^`]+`)|(\[[^\]]+\]\([^)]+\))|(\*\*[\s\S]+?\*\*)|(__[\s\S]+?__)|(\*[\s\S]+?\*)|(_[\s\S]+?_)/;
|
|
825
836
|
let rest = text;
|
|
826
837
|
while (rest.length > 0) {
|
|
827
838
|
const m = token.exec(rest);
|
|
828
839
|
if (!m) {
|
|
829
|
-
pushText(nodes, rest);
|
|
840
|
+
pushText(nodes, rest, carried.length ? carried : void 0);
|
|
830
841
|
break;
|
|
831
842
|
}
|
|
832
|
-
if (m.index > 0)
|
|
843
|
+
if (m.index > 0) {
|
|
844
|
+
pushText(nodes, rest.slice(0, m.index), carried.length ? carried : void 0);
|
|
845
|
+
}
|
|
833
846
|
const tok = m[0];
|
|
834
847
|
if (tok.startsWith("`")) {
|
|
835
|
-
pushText(nodes, tok.slice(1, -1), [{ type: "code" }]);
|
|
848
|
+
pushText(nodes, tok.slice(1, -1), [...carried, { type: "code" }]);
|
|
836
849
|
} else if (tok.startsWith("[")) {
|
|
837
850
|
const lm = /\[([^\]]+)\]\(([^)]+)\)/.exec(tok);
|
|
838
851
|
if (lm) {
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
852
|
+
nodes.push(
|
|
853
|
+
...inline(lm[1] ?? "", [
|
|
854
|
+
...carried,
|
|
855
|
+
{ type: "link", attrs: { href: lm[2] ?? "" } }
|
|
856
|
+
])
|
|
857
|
+
);
|
|
842
858
|
}
|
|
843
859
|
} else if (tok.startsWith("**") || tok.startsWith("__")) {
|
|
844
|
-
|
|
860
|
+
nodes.push(...inline(tok.slice(2, -2), [...carried, { type: "bold" }]));
|
|
845
861
|
} else {
|
|
846
|
-
|
|
862
|
+
nodes.push(...inline(tok.slice(1, -1), [...carried, { type: "italic" }]));
|
|
847
863
|
}
|
|
848
864
|
rest = rest.slice(m.index + tok.length);
|
|
849
865
|
}
|
|
@@ -1302,41 +1318,16 @@ var SPECS = [
|
|
|
1302
1318
|
{ flag: "--status <status>", key: "status", description: "active | archived" }
|
|
1303
1319
|
],
|
|
1304
1320
|
remove: true,
|
|
1321
|
+
// `draft`, `preview`, `test-email`, `validate`, `schema`, and `scaffold`
|
|
1322
|
+
// are hand-written in `attachSequenceExtras` (they need draft-aware
|
|
1323
|
+
// behaviour the generic machinery can't express).
|
|
1305
1324
|
actions: [
|
|
1306
|
-
{
|
|
1307
|
-
name: "draft",
|
|
1308
|
-
describe: "Save the draft graph + trigger",
|
|
1309
|
-
method: "put",
|
|
1310
|
-
suffix: "/draft",
|
|
1311
|
-
fields: [
|
|
1312
|
-
{ flag: "--graph <json>", key: "graph", json: true },
|
|
1313
|
-
{ flag: "--trigger <json>", key: "trigger", json: true },
|
|
1314
|
-
{ flag: "--expected-revision <n>", key: "expected_revision", number: true }
|
|
1315
|
-
]
|
|
1316
|
-
},
|
|
1317
1325
|
{
|
|
1318
1326
|
name: "publish",
|
|
1319
1327
|
describe: "Publish the current draft",
|
|
1320
1328
|
method: "post",
|
|
1321
1329
|
suffix: "/publish"
|
|
1322
1330
|
},
|
|
1323
|
-
{
|
|
1324
|
-
name: "preview",
|
|
1325
|
-
describe: "Render an email node (--node-id <id>)",
|
|
1326
|
-
method: "post",
|
|
1327
|
-
suffix: "/preview",
|
|
1328
|
-
fields: [{ flag: "--node-id <id>", key: "nodeId" }]
|
|
1329
|
-
},
|
|
1330
|
-
{
|
|
1331
|
-
name: "test-email",
|
|
1332
|
-
describe: "Send a test email (--node-id <id> --to <email>)",
|
|
1333
|
-
method: "post",
|
|
1334
|
-
suffix: "/test-email",
|
|
1335
|
-
fields: [
|
|
1336
|
-
{ flag: "--node-id <id>", key: "nodeId" },
|
|
1337
|
-
{ flag: "--to <email>", key: "to" }
|
|
1338
|
-
]
|
|
1339
|
-
},
|
|
1340
1331
|
{
|
|
1341
1332
|
name: "activity",
|
|
1342
1333
|
describe: "Show enrollment activity",
|
|
@@ -1478,7 +1469,8 @@ var SEQUENCE_SCHEMA = {
|
|
|
1478
1469
|
type: "event",
|
|
1479
1470
|
eventName: "modification_limit_reached",
|
|
1480
1471
|
oncePerContact: true,
|
|
1481
|
-
"filter?": "FilterSpec \u2014 optional audience filter on the contact"
|
|
1472
|
+
"filter?": "FilterSpec \u2014 optional audience filter on the contact's traits/history",
|
|
1473
|
+
"where?": "Condition(s) on the firing event's own properties, e.g. { property: 'plan', op: 'eq', value: 'free' } (single object or array, AND-ed)"
|
|
1482
1474
|
},
|
|
1483
1475
|
contact_created: {
|
|
1484
1476
|
type: "contact_created",
|
|
@@ -1495,6 +1487,11 @@ var SEQUENCE_SCHEMA = {
|
|
|
1495
1487
|
"templateId?": "uuid | null (omit for project default)"
|
|
1496
1488
|
},
|
|
1497
1489
|
wait: { type: "wait", duration: { value: 1, unit: "minutes | hours | days" } },
|
|
1490
|
+
wait_event: {
|
|
1491
|
+
type: "wait_event",
|
|
1492
|
+
eventName: "completed_onboarding",
|
|
1493
|
+
timeout: { value: 7, unit: "minutes | hours | days" }
|
|
1494
|
+
},
|
|
1498
1495
|
branch: {
|
|
1499
1496
|
type: "branch",
|
|
1500
1497
|
condition: {
|
|
@@ -1508,14 +1505,17 @@ var SEQUENCE_SCHEMA = {
|
|
|
1508
1505
|
id: "e1",
|
|
1509
1506
|
source: "trigger",
|
|
1510
1507
|
target: "email1",
|
|
1511
|
-
"branch?": "yes | no \
|
|
1508
|
+
"branch?": "yes | no (branch node) \xB7 received | timeout (wait_event node) \u2014 omit for single-leg nodes"
|
|
1512
1509
|
},
|
|
1513
1510
|
notes: [
|
|
1514
1511
|
"config.type must equal the node's type.",
|
|
1515
|
-
"The email body field is `bodyDoc` (camelCase); a wait nests `duration`.",
|
|
1512
|
+
"The email body field is `bodyDoc` (camelCase); a wait nests `duration`; a wait_event nests `timeout`.",
|
|
1516
1513
|
"Draft saves reject unknown / mis-cased keys with a 400.",
|
|
1517
|
-
|
|
1518
|
-
"
|
|
1514
|
+
'Trait/filter values compare as strings: use value:"false" (not false) for a boolean trait. `letter traits list` shows keys + types.',
|
|
1515
|
+
"Event triggers can also gate on the event's own properties via `where` (e.g. only plan=free).",
|
|
1516
|
+
"wait_event pauses the contact until `eventName` fires (the `received` leg) or `timeout` elapses (the `timeout` leg). Only events that occur after the contact reaches the step count.",
|
|
1517
|
+
"Publish requires: one trigger, every node reachable, every path ends at an exit, each branch has one yes + one no edge, each wait_event has one received + one timeout edge, and each email has a subject + non-empty bodyDoc.",
|
|
1518
|
+
"`sequences validate <id>` dry-runs those publish checks; `sequences scaffold` builds a valid trigger \u2192 [wait] \u2192 email \u2192 exit graph in one command."
|
|
1519
1519
|
],
|
|
1520
1520
|
example: {
|
|
1521
1521
|
trigger: {
|
|
@@ -1580,9 +1580,118 @@ function parseDuration(raw) {
|
|
|
1580
1580
|
const unit = u.startsWith("d") ? "days" : u.startsWith("h") ? "hours" : "minutes";
|
|
1581
1581
|
return { value: Number(m[1]), unit };
|
|
1582
1582
|
}
|
|
1583
|
+
function parseAudienceTrait(raw) {
|
|
1584
|
+
const m = /^([^=!~]+?)\s*(!=|~|=)\s*(.*)$/.exec(raw);
|
|
1585
|
+
if (!m) {
|
|
1586
|
+
throw new Error(
|
|
1587
|
+
"--audience-trait must be path=value (also path!=value or path~value), e.g. plan=free"
|
|
1588
|
+
);
|
|
1589
|
+
}
|
|
1590
|
+
const op = m[2] === "!=" ? "neq" : m[2] === "~" ? "contains" : "eq";
|
|
1591
|
+
return { kind: "trait", path: (m[1] ?? "").trim(), op, value: m[3] ?? "" };
|
|
1592
|
+
}
|
|
1583
1593
|
function attachSequenceExtras(program2) {
|
|
1584
1594
|
const seq = program2.commands.find((cmd) => cmd.name() === "sequences");
|
|
1585
1595
|
if (!seq) return;
|
|
1596
|
+
seq.command("draft <id>").description(
|
|
1597
|
+
"Save the draft graph + trigger (auto-fetches the current revision unless --expected-revision is given)"
|
|
1598
|
+
).option("--graph <json>", "Graph JSON (inline or @file.json)").option("--trigger <json>", "Trigger JSON (inline or @file.json)").option(
|
|
1599
|
+
"--expected-revision <n>",
|
|
1600
|
+
"Optimistic-concurrency revision (default: current; use a number to assert)"
|
|
1601
|
+
).option("-p, --project <slug>", "Project slug (default: connected project)").action(async (id, opts) => {
|
|
1602
|
+
try {
|
|
1603
|
+
const slug = await resolveProjectSlug(opts.project);
|
|
1604
|
+
const base = `/v1/projects/${slug}/sequences`;
|
|
1605
|
+
const body = {};
|
|
1606
|
+
if (opts.graph !== void 0) {
|
|
1607
|
+
body.graph = JSON.parse(await readValue(String(opts.graph)));
|
|
1608
|
+
}
|
|
1609
|
+
if (opts.trigger !== void 0) {
|
|
1610
|
+
body.trigger = JSON.parse(await readValue(String(opts.trigger)));
|
|
1611
|
+
}
|
|
1612
|
+
const explicit = opts.expectedRevision;
|
|
1613
|
+
if (explicit !== void 0 && String(explicit) !== "auto") {
|
|
1614
|
+
body.expected_revision = Number(explicit);
|
|
1615
|
+
} else {
|
|
1616
|
+
const { data: data2 } = await mgmt.get(`${base}/${encodeURIComponent(id)}`);
|
|
1617
|
+
body.expected_revision = data2.draft_revision ?? 0;
|
|
1618
|
+
}
|
|
1619
|
+
const { data } = await mgmt.put(
|
|
1620
|
+
`${base}/${encodeURIComponent(id)}/draft`,
|
|
1621
|
+
body
|
|
1622
|
+
);
|
|
1623
|
+
emit(data);
|
|
1624
|
+
} catch (err) {
|
|
1625
|
+
fail(err);
|
|
1626
|
+
}
|
|
1627
|
+
});
|
|
1628
|
+
seq.command("preview <id>").description("Render an email node to HTML/text").requiredOption("--node-id <id>", "Email node id (e.g. email1)").option(
|
|
1629
|
+
"--config <json>",
|
|
1630
|
+
"Inline email config (inline or @file.json); omit to render the saved draft"
|
|
1631
|
+
).option("--to <email>", "Recipient address to cast in the preview").option("-p, --project <slug>", "Project slug (default: connected project)").action(async (id, opts) => {
|
|
1632
|
+
try {
|
|
1633
|
+
const slug = await resolveProjectSlug(opts.project);
|
|
1634
|
+
const base = `/v1/projects/${slug}/sequences`;
|
|
1635
|
+
const body = { node_id: String(opts.nodeId) };
|
|
1636
|
+
if (opts.config !== void 0) {
|
|
1637
|
+
body.config = JSON.parse(await readValue(String(opts.config)));
|
|
1638
|
+
}
|
|
1639
|
+
if (opts.to !== void 0) body.recipient_email = String(opts.to);
|
|
1640
|
+
const { data } = await mgmt.post(
|
|
1641
|
+
`${base}/${encodeURIComponent(id)}/preview`,
|
|
1642
|
+
body
|
|
1643
|
+
);
|
|
1644
|
+
emit(data);
|
|
1645
|
+
} catch (err) {
|
|
1646
|
+
fail(err);
|
|
1647
|
+
}
|
|
1648
|
+
});
|
|
1649
|
+
seq.command("test-email <id>").description("Send a [Test] copy of an email node").requiredOption("--node-id <id>", "Email node id (e.g. email1)").option("--to <email>", "Recipient (default: your account email)").option(
|
|
1650
|
+
"--config <json>",
|
|
1651
|
+
"Inline email config (inline or @file.json); omit to send the saved draft"
|
|
1652
|
+
).option("-p, --project <slug>", "Project slug (default: connected project)").action(async (id, opts) => {
|
|
1653
|
+
try {
|
|
1654
|
+
const slug = await resolveProjectSlug(opts.project);
|
|
1655
|
+
const base = `/v1/projects/${slug}/sequences`;
|
|
1656
|
+
const body = { node_id: String(opts.nodeId) };
|
|
1657
|
+
if (opts.to !== void 0) body.recipient_email = String(opts.to);
|
|
1658
|
+
if (opts.config !== void 0) {
|
|
1659
|
+
body.config = JSON.parse(await readValue(String(opts.config)));
|
|
1660
|
+
}
|
|
1661
|
+
const { data } = await mgmt.post(
|
|
1662
|
+
`${base}/${encodeURIComponent(id)}/test-email`,
|
|
1663
|
+
body
|
|
1664
|
+
);
|
|
1665
|
+
emit(data);
|
|
1666
|
+
} catch (err) {
|
|
1667
|
+
fail(err);
|
|
1668
|
+
}
|
|
1669
|
+
});
|
|
1670
|
+
seq.command("validate <id>").description("Dry-run the publish checks against the current draft (no changes)").option("-p, --project <slug>", "Project slug (default: connected project)").action(async (id, opts) => {
|
|
1671
|
+
try {
|
|
1672
|
+
const slug = await resolveProjectSlug(opts.project);
|
|
1673
|
+
const { data } = await mgmt.get(
|
|
1674
|
+
`/v1/projects/${slug}/sequences/${encodeURIComponent(id)}/validate`
|
|
1675
|
+
);
|
|
1676
|
+
if (isJsonMode()) {
|
|
1677
|
+
printJson(data);
|
|
1678
|
+
return;
|
|
1679
|
+
}
|
|
1680
|
+
const result = data;
|
|
1681
|
+
if (result.ok) {
|
|
1682
|
+
printSuccess("Draft is publish-ready.");
|
|
1683
|
+
return;
|
|
1684
|
+
}
|
|
1685
|
+
printError(`Not publish-ready (${result.errors.length} issue(s)):`);
|
|
1686
|
+
for (const e of result.errors) {
|
|
1687
|
+
const where = e.nodeId ? c.dim(` [${e.nodeId}]`) : "";
|
|
1688
|
+
log(` \u2022 ${e.message}${where}`);
|
|
1689
|
+
}
|
|
1690
|
+
process.exitCode = 1;
|
|
1691
|
+
} catch (err) {
|
|
1692
|
+
fail(err);
|
|
1693
|
+
}
|
|
1694
|
+
});
|
|
1586
1695
|
seq.command("schema").description("Print the canonical trigger / node / edge shapes + an example").action(() => {
|
|
1587
1696
|
if (isJsonMode()) printJson(SEQUENCE_SCHEMA);
|
|
1588
1697
|
else printSequenceSchemaHuman();
|
|
@@ -1595,7 +1704,7 @@ function attachSequenceExtras(program2) {
|
|
|
1595
1704
|
).option("--no-once-per-contact", "Allow re-enrolment on every event").option("--audience <json>", "Audience FilterSpec (inline JSON or @file.json)").option(
|
|
1596
1705
|
"--audience-trait <path=value>",
|
|
1597
1706
|
"Shorthand audience filter: trait equals value (e.g. plan=free)"
|
|
1598
|
-
).option("--wait <duration>", "Delay before the email, e.g. 30m, 1h, 2d").requiredOption("--subject <subject>", "Email subject").option("--body-md <markdown>", "Email body as Markdown (converted to rich text)").option("--body-text <text>", "Email body as plain text").option("--publish", "Publish immediately after drafting").option("-p, --project <slug>", "Project slug (default: connected project)").action(async (opts) => {
|
|
1707
|
+
).option("--wait <duration>", "Delay before the email, e.g. 30m, 1h, 2d").requiredOption("--subject <subject>", "Email subject").option("--body-md <markdown>", "Email body as Markdown (converted to rich text)").option("--body-text <text>", "Email body as plain text").option("--publish", "Publish immediately after drafting").option("--dry-run", "Print the trigger + graph it would create, without creating anything").option("--if-not-exists", "Reuse an existing sequence with the same name instead of duplicating").option("-p, --project <slug>", "Project slug (default: connected project)").action(async (opts) => {
|
|
1599
1708
|
try {
|
|
1600
1709
|
const slug = await resolveProjectSlug(opts.project);
|
|
1601
1710
|
const base = `/v1/projects/${slug}/sequences`;
|
|
@@ -1603,17 +1712,7 @@ function attachSequenceExtras(program2) {
|
|
|
1603
1712
|
if (opts.audience !== void 0) {
|
|
1604
1713
|
filter = JSON.parse(await readValue(String(opts.audience)));
|
|
1605
1714
|
} else if (opts.audienceTrait !== void 0) {
|
|
1606
|
-
|
|
1607
|
-
const idx = eq.indexOf("=");
|
|
1608
|
-
if (idx === -1) {
|
|
1609
|
-
throw new Error("--audience-trait must be path=value, e.g. plan=free");
|
|
1610
|
-
}
|
|
1611
|
-
filter = {
|
|
1612
|
-
kind: "trait",
|
|
1613
|
-
path: eq.slice(0, idx),
|
|
1614
|
-
op: "eq",
|
|
1615
|
-
value: eq.slice(idx + 1)
|
|
1616
|
-
};
|
|
1715
|
+
filter = parseAudienceTrait(String(opts.audienceTrait));
|
|
1617
1716
|
}
|
|
1618
1717
|
const trigger = opts.event ? {
|
|
1619
1718
|
type: "event",
|
|
@@ -1660,12 +1759,38 @@ function attachSequenceExtras(program2) {
|
|
|
1660
1759
|
});
|
|
1661
1760
|
edges.push({ id: "e_exit", source: prev, target: "exit1" });
|
|
1662
1761
|
const graph = { nodes, edges };
|
|
1663
|
-
|
|
1664
|
-
|
|
1762
|
+
if (opts.dryRun) {
|
|
1763
|
+
if (isJsonMode()) printJson({ slug, name: String(opts.name), trigger, graph });
|
|
1764
|
+
else {
|
|
1765
|
+
log("Would create sequence " + c.bold(String(opts.name)) + " with:\n");
|
|
1766
|
+
log("trigger:");
|
|
1767
|
+
log(JSON.stringify(trigger, null, 2));
|
|
1768
|
+
log("\ngraph:");
|
|
1769
|
+
log(JSON.stringify(graph, null, 2));
|
|
1770
|
+
}
|
|
1771
|
+
return;
|
|
1772
|
+
}
|
|
1773
|
+
let id;
|
|
1774
|
+
let reused = false;
|
|
1775
|
+
if (opts.ifNotExists) {
|
|
1776
|
+
const { data } = await mgmt.get(base);
|
|
1777
|
+
const rows = data.data ?? [];
|
|
1778
|
+
const match = rows.find((r) => r.name === String(opts.name));
|
|
1779
|
+
if (match) {
|
|
1780
|
+
id = match.id;
|
|
1781
|
+
reused = true;
|
|
1782
|
+
}
|
|
1783
|
+
}
|
|
1784
|
+
if (!id) {
|
|
1785
|
+
const created = await mgmt.post(base, { name: String(opts.name) });
|
|
1786
|
+
id = created.data.id;
|
|
1787
|
+
}
|
|
1788
|
+
const detail = await mgmt.get(`${base}/${id}`);
|
|
1789
|
+
const rev = detail.data.draft_revision ?? 0;
|
|
1665
1790
|
await mgmt.put(`${base}/${id}/draft`, {
|
|
1666
1791
|
graph,
|
|
1667
1792
|
trigger,
|
|
1668
|
-
expected_revision:
|
|
1793
|
+
expected_revision: rev
|
|
1669
1794
|
});
|
|
1670
1795
|
let published = false;
|
|
1671
1796
|
if (opts.publish) {
|
|
@@ -1673,14 +1798,16 @@ function attachSequenceExtras(program2) {
|
|
|
1673
1798
|
published = true;
|
|
1674
1799
|
}
|
|
1675
1800
|
if (isJsonMode()) {
|
|
1676
|
-
printJson({ id, slug, published, trigger, graph });
|
|
1801
|
+
printJson({ id, slug, reused, published, trigger, graph });
|
|
1677
1802
|
return;
|
|
1678
1803
|
}
|
|
1804
|
+
const verb = reused ? "Updated existing sequence" : "Created sequence";
|
|
1679
1805
|
printSuccess(
|
|
1680
|
-
|
|
1806
|
+
`${verb} ${id}${published ? " and published it" : " (draft saved)"}.`
|
|
1681
1807
|
);
|
|
1682
1808
|
if (!published) {
|
|
1683
1809
|
printInfo(`Publish when ready: ${cliCommand()} sequences publish ${id}`);
|
|
1810
|
+
printInfo(`Dry-run the publish checks: ${cliCommand()} sequences validate ${id}`);
|
|
1684
1811
|
}
|
|
1685
1812
|
printInfo(
|
|
1686
1813
|
`Tweak it on the canvas, or re-draft with ${cliCommand()} sequences draft ${id} --graph @graph.json`
|
|
@@ -1690,9 +1817,36 @@ function attachSequenceExtras(program2) {
|
|
|
1690
1817
|
}
|
|
1691
1818
|
});
|
|
1692
1819
|
}
|
|
1820
|
+
function attachTraits(program2) {
|
|
1821
|
+
const traits = program2.command("traits").description("Discover trait keys for audience filters and branch conditions");
|
|
1822
|
+
traits.command("list").description("List distinct contact/account trait keys with type + sample").option("-p, --project <slug>", "Project slug (default: connected project)").action(async (opts) => {
|
|
1823
|
+
try {
|
|
1824
|
+
const slug = await resolveProjectSlug(opts.project);
|
|
1825
|
+
const { data } = await mgmt.get(`/v1/projects/${slug}/traits`);
|
|
1826
|
+
const rows = data.data ?? [];
|
|
1827
|
+
if (isJsonMode()) {
|
|
1828
|
+
printJson(rows);
|
|
1829
|
+
return;
|
|
1830
|
+
}
|
|
1831
|
+
if (rows.length === 0) {
|
|
1832
|
+
log(c.dim("(no traits yet)"));
|
|
1833
|
+
return;
|
|
1834
|
+
}
|
|
1835
|
+
log(c.dim('Values are compared as strings (e.g. boolean false \u2192 "false").\n'));
|
|
1836
|
+
for (const r of rows) {
|
|
1837
|
+
const head = `${c.bold(r.key)} ${c.dim(`(${r.scope} ${r.type})`)}`;
|
|
1838
|
+
const sample = r.sample != null ? ` e.g. ${JSON.stringify(r.sample)}` : "";
|
|
1839
|
+
log(`${head} \xD7${r.count}${sample}`);
|
|
1840
|
+
}
|
|
1841
|
+
} catch (err) {
|
|
1842
|
+
fail(err);
|
|
1843
|
+
}
|
|
1844
|
+
});
|
|
1845
|
+
}
|
|
1693
1846
|
function registerResourceCommands(program2) {
|
|
1694
1847
|
for (const spec of SPECS) register(program2, spec);
|
|
1695
1848
|
attachSequenceExtras(program2);
|
|
1849
|
+
attachTraits(program2);
|
|
1696
1850
|
program2.command("me").description("Show the token's user, workspace, and role").action(async () => {
|
|
1697
1851
|
try {
|
|
1698
1852
|
const { data } = await mgmt.get("/v1/me");
|
|
@@ -1729,7 +1883,7 @@ function registerResourceCommands(program2) {
|
|
|
1729
1883
|
|
|
1730
1884
|
// src/index.ts
|
|
1731
1885
|
var program = new Command();
|
|
1732
|
-
program.name("letter").description("Connect your app to Letter, then manage it from the command line").version("0.
|
|
1886
|
+
program.name("letter").description("Connect your app to Letter, then manage it from the command line").version("0.5.1").option("--json", "Output raw JSON (for scripting / agents)").hook("preAction", (thisCommand) => {
|
|
1733
1887
|
if (thisCommand.opts().json) setJsonMode(true);
|
|
1734
1888
|
});
|
|
1735
1889
|
registerLoginCommand(program);
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/output.ts","../src/config.ts","../src/client.ts","../src/browser.ts","../src/env-file.ts","../src/pm.ts","../src/commands/login.ts","../src/commands/auth.ts","../src/commands/status.ts","../src/commands/config.ts","../src/commands/resources.ts","../src/markdown.ts"],"sourcesContent":["declare const PKG_VERSION: string;\nimport { Command } from \"commander\";\nimport { setJsonMode } from \"./output.js\";\nimport { registerLoginCommand } from \"./commands/login.js\";\nimport { registerAuthCommands } from \"./commands/auth.js\";\nimport { registerStatusCommand } from \"./commands/status.js\";\nimport { registerConfigCommands } from \"./commands/config.js\";\nimport { registerResourceCommands } from \"./commands/resources.js\";\n\nconst program = new Command();\n\nprogram\n .name(\"letter\")\n .description(\"Connect your app to Letter, then manage it from the command line\")\n .version(PKG_VERSION)\n .option(\"--json\", \"Output raw JSON (for scripting / agents)\")\n .hook(\"preAction\", (thisCommand) => {\n if (thisCommand.opts().json) setJsonMode(true);\n });\n\n// Two layers, one login:\n// - login/auth/status/config wire up the project and its ingestion key\n// (lt_live_*, written to the project env as LETTER_API_KEY).\n// - the resource groups drive the Management API with the workspace PAT\n// (lt_pat_*, stored in ~/.letter/credentials.json). Both credentials are\n// minted by `letter login`; each command picks the right one on its own.\nregisterLoginCommand(program); // default command (`letter` == `letter login`)\nregisterAuthCommands(program);\nregisterStatusCommand(program);\nregisterConfigCommands(program);\nregisterResourceCommands(program); // control-plane resource groups (PAT-backed)\n\nprogram.parseAsync();\n","import chalk from \"chalk\";\nimport ora, { type Ora } from \"ora\";\nimport { createInterface } from \"node:readline\";\n\nlet jsonMode = false;\n\nexport function setJsonMode(enabled: boolean) {\n jsonMode = enabled;\n}\nexport function isJsonMode() {\n return jsonMode;\n}\n\nexport function printJson(data: unknown) {\n console.log(JSON.stringify(data, null, 2));\n}\n\n/**\n * The command users should repeat to run us again, so printed hints are\n * copy-pasteable. When launched through `npx`, Node runs us from the npx cache\n * (`…/_npx/<hash>/…`) and there's no `letter` on PATH, so suggest the npx form;\n * a global install resolves the bare `letter` binary.\n */\nexport function cliCommand(): string {\n return (process.argv[1] ?? \"\").includes(\"_npx\") ? \"npx @letterapp/cli\" : \"letter\";\n}\n\nexport function log(msg = \"\") {\n if (jsonMode) return;\n console.log(msg);\n}\n\nexport function printSuccess(msg: string) {\n if (jsonMode) return;\n console.log(chalk.green(\"✓\") + \" \" + msg);\n}\n\nexport function printInfo(msg: string) {\n if (jsonMode) return;\n console.log(chalk.cyan(\"›\") + \" \" + msg);\n}\n\nexport function printWarning(msg: string) {\n if (jsonMode) return;\n console.log(chalk.yellow(\"!\") + \" \" + msg);\n}\n\nexport function printError(err: unknown) {\n const msg = err instanceof Error ? err.message : String(err);\n if (jsonMode) {\n printJson({ error: msg });\n return;\n }\n console.error(chalk.red(\"✗\") + \" \" + msg);\n}\n\n/** The Letter wordmark banner. Suppressed in JSON mode. */\nexport function banner() {\n if (jsonMode) return;\n console.log(\"\");\n console.log(\" \" + chalk.bold(\"Letter\") + chalk.red(\".\") + \" \" + chalk.dim(\"CLI\"));\n console.log(\"\");\n}\n\nexport function spinner(text: string): Ora {\n return ora({ text, color: \"cyan\", isEnabled: !jsonMode });\n}\n\n/**\n * Prompts the user and resolves with their input. Returns \"\" immediately on a\n * non-interactive stdin so automated/agent runs don't hang.\n */\nexport function prompt(question: string): Promise<string> {\n if (!process.stdin.isTTY) return Promise.resolve(\"\");\n const rl = createInterface({ input: process.stdin, output: process.stdout });\n return new Promise((resolve) => {\n rl.question(question, (answer) => {\n rl.close();\n resolve(answer.trim());\n });\n });\n}\n\n/**\n * Print a management API result. In `--json` mode, emits the raw payload so\n * agents/scripts can parse it. In human mode, renders list envelopes as a\n * compact `id label` table and single resources as pretty JSON.\n */\nexport function emit(data: unknown) {\n if (jsonMode) {\n printJson(data);\n return;\n }\n if (data && typeof data === \"object\" && Array.isArray((data as { data?: unknown }).data)) {\n const env = data as { data: Record<string, unknown>[]; next_cursor?: string | null };\n if (env.data.length === 0) {\n console.log(chalk.dim(\"(none)\"));\n } else {\n for (const row of env.data) {\n const id = String(row.id ?? row.slug ?? row.external_id ?? \"\");\n const label =\n (row.name as string) ??\n (row.email as string) ??\n (row.domain as string) ??\n (row.subject as string) ??\n \"\";\n console.log(`${chalk.bold(id)}${label ? \" \" + label : \"\"}`);\n }\n }\n if (env.next_cursor) {\n console.log(chalk.dim(`\\n› more: --cursor ${env.next_cursor}`));\n }\n return;\n }\n printJson(data);\n}\n\nexport const c = chalk;\n","import Conf from \"conf\";\nimport { homedir } from \"node:os\";\nimport path from \"node:path\";\nimport { mkdir, readFile, writeFile, rm } from \"node:fs/promises\";\nimport { cliCommand } from \"./output.js\";\n\n/** The SDK / API default. When the resolved base equals this we don't bother\n * writing LETTER_BASE_URL into the project env. */\nexport const DEFAULT_BASE_URL = \"https://api.letter.app\";\n\n/* -------------------------------------------------------------------------- */\n/* Non-secret CLI preferences (base URL). */\n/* Stored with `conf`, mirroring how the SDK/MCP resolve their endpoint. */\n/* -------------------------------------------------------------------------- */\n\nconst config = new Conf({\n projectName: \"letterapp-cli\",\n schema: {\n baseUrl: { type: \"string\", default: DEFAULT_BASE_URL },\n },\n});\n\n/** Resolve the API base: explicit flag > env > stored config > prod default. */\nexport function getBaseUrl(flag?: string): string {\n const raw =\n flag ||\n process.env.LETTER_BASE_URL ||\n (config.get(\"baseUrl\") as string) ||\n DEFAULT_BASE_URL;\n return raw.replace(/\\/$/, \"\");\n}\n\nexport function setBaseUrl(url: string): void {\n config.set(\"baseUrl\", url.replace(/\\/$/, \"\"));\n}\n\nexport function getConfigPath(): string {\n return config.path;\n}\n\nexport function resetConfig(): void {\n config.clear();\n}\n\n/* -------------------------------------------------------------------------- */\n/* Secret credential store (~/.letter/credentials.json). */\n/* Written here, read by @letterapp/mcp so the secret never lands in an MCP */\n/* config. Kept separate from `conf` and locked to owner-only permissions. */\n/* -------------------------------------------------------------------------- */\n\nexport type StoredCredential = {\n apiKey: string;\n /** Workspace Personal Access Token (lt_pat_*) for the management API/CLI. */\n pat?: string;\n baseUrl: string;\n project: { slug: string; name: string };\n workspace?: { id: string; name: string };\n savedAt: string;\n};\n\n/**\n * Resolve the project slug a management command should target: explicit\n * `--project` flag > `LETTER_PROJECT` env (for scripts/CI) > the connected\n * project from `letter login`. Throws a clear error when none is available so\n * scripts fail loudly.\n */\nexport async function resolveProjectSlug(flag?: string): Promise<string> {\n if (flag) return flag;\n if (process.env.LETTER_PROJECT) return process.env.LETTER_PROJECT;\n const cred = await readCredential();\n if (cred?.project?.slug) return cred.project.slug;\n throw new Error(\n `No project. Pass --project <slug>, set LETTER_PROJECT, or run \\`${cliCommand()} login\\`.`,\n );\n}\n\nexport function credentialsPath(): string {\n return path.join(homedir(), \".letter\", \"credentials.json\");\n}\n\nexport async function saveCredential(cred: StoredCredential): Promise<string> {\n const file = credentialsPath();\n await mkdir(path.dirname(file), { recursive: true, mode: 0o700 });\n await writeFile(file, `${JSON.stringify(cred, null, 2)}\\n`, { mode: 0o600 });\n return file;\n}\n\nexport async function readCredential(): Promise<StoredCredential | null> {\n try {\n const raw = await readFile(credentialsPath(), \"utf8\");\n return JSON.parse(raw) as StoredCredential;\n } catch {\n return null;\n }\n}\n\nexport async function clearCredential(): Promise<void> {\n await rm(credentialsPath(), { force: true });\n}\n","import { getBaseUrl, readCredential } from \"./config.js\";\nimport { cliCommand } from \"./output.js\";\n\nconst USER_AGENT = \"@letterapp/cli\";\n\n/* -------------------------------------------------------------------------- */\n/* Device authorization (RFC 8628 style). No secret ever passes through argv. */\n/* -------------------------------------------------------------------------- */\n\nexport type StartResponse = {\n device_code: string;\n user_code: string;\n verification_uri: string;\n verification_uri_complete: string;\n interval: number;\n expires_in: number;\n};\n\nexport type PollResponse =\n | { status: \"authorization_pending\" }\n | { status: \"slow_down\"; retryAfter?: number }\n | { status: \"access_denied\" }\n | { status: \"expired_token\" }\n | {\n status: \"approved\";\n api_key: string;\n /** Workspace PAT for the management API (lt_pat_*). */\n pat: string;\n base_url: string;\n project: { slug: string; name: string };\n workspace?: { id: string; name: string };\n };\n\n/** Starts a device-authorization flow against the given API base. */\nexport async function startDeviceAuth(base: string): Promise<StartResponse> {\n const res = await fetch(`${base}/v1/cli/auth/start`, {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\", \"user-agent\": USER_AGENT },\n body: \"{}\",\n });\n if (!res.ok) {\n throw new Error(\n `Could not start login (HTTP ${res.status}). Is ${base} reachable?`,\n );\n }\n return (await res.json()) as StartResponse;\n}\n\n/** Polls once for approval. */\nexport async function pollDeviceAuth(\n base: string,\n deviceCode: string,\n): Promise<PollResponse> {\n const res = await fetch(`${base}/v1/cli/auth/poll`, {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\", \"user-agent\": USER_AGENT },\n body: JSON.stringify({ device_code: deviceCode }),\n });\n\n if (res.status === 429) {\n const retryAfter = Number(res.headers.get(\"retry-after\") ?? \"5\");\n return { status: \"slow_down\", retryAfter };\n }\n if (!res.ok) {\n throw new Error(`Login poll failed (HTTP ${res.status}).`);\n }\n return (await res.json()) as PollResponse;\n}\n\n/* -------------------------------------------------------------------------- */\n/* Authenticated client. Resolves the key from the stored credential (written */\n/* by `letter login`) or LETTER_API_KEY. Used by data commands like `status`. */\n/* -------------------------------------------------------------------------- */\n\nexport interface ApiResponse<T = unknown> {\n status: number;\n data: T;\n}\n\nclass LetterClient {\n private maxRetries = 3;\n\n private async resolveAuth(): Promise<{ base: string; token: string }> {\n const cred = await readCredential();\n const token = process.env.LETTER_API_KEY || cred?.apiKey || \"\";\n if (!token) {\n throw new Error(\n `Not connected. Run \\`${cliCommand()} login\\` (or set LETTER_API_KEY) first.`,\n );\n }\n const base = getBaseUrl(process.env.LETTER_API_KEY ? undefined : cred?.baseUrl);\n return { base, token };\n }\n\n private async request<T = unknown>(\n method: string,\n path: string,\n attempt = 1,\n ): Promise<ApiResponse<T>> {\n const { base, token } = await this.resolveAuth();\n const res = await fetch(`${base}${path}`, {\n method,\n headers: {\n authorization: `Bearer ${token}`,\n accept: \"application/json\",\n \"user-agent\": USER_AGENT,\n },\n });\n\n if (res.status === 429 && attempt <= this.maxRetries) {\n const retryAfter = Number(res.headers.get(\"retry-after\") || 2);\n await new Promise((r) => setTimeout(r, retryAfter * 1000 * attempt));\n return this.request<T>(method, path, attempt + 1);\n }\n\n const data = (res.status === 204 ? {} : await res.json()) as T;\n if (!res.ok) {\n const msg =\n (data as { error?: { message?: string } })?.error?.message ||\n `HTTP ${res.status}`;\n const err = new Error(msg) as Error & { status: number };\n err.status = res.status;\n throw err;\n }\n return { status: res.status, data };\n }\n\n get<T = unknown>(path: string) {\n return this.request<T>(\"GET\", path);\n }\n}\n\nexport const client = new LetterClient();\n\n/* -------------------------------------------------------------------------- */\n/* Management client. Authenticates with the workspace PAT (lt_pat_*) and */\n/* drives the control-plane (/v1/projects, sequences, broadcasts, ...). Used by */\n/* the resource command groups. */\n/* -------------------------------------------------------------------------- */\n\nexport type RequestInitLite = {\n body?: unknown;\n query?: Record<string, string | number | undefined>;\n form?: FormData;\n};\n\nclass ManagementClient {\n private maxRetries = 3;\n\n private async resolveAuth(): Promise<{ base: string; token: string }> {\n const cred = await readCredential();\n const token = process.env.LETTER_PAT || cred?.pat || \"\";\n if (!token) {\n throw new Error(\n `Not connected for management. Run \\`${cliCommand()} login\\` (or set LETTER_PAT) first.`,\n );\n }\n const base = getBaseUrl(process.env.LETTER_PAT ? undefined : cred?.baseUrl);\n return { base, token };\n }\n\n async request<T = unknown>(\n method: string,\n path: string,\n init: RequestInitLite = {},\n attempt = 1,\n ): Promise<ApiResponse<T>> {\n const { base, token } = await this.resolveAuth();\n\n let url = `${base}${path}`;\n if (init.query) {\n const qs = new URLSearchParams();\n for (const [k, v] of Object.entries(init.query)) {\n if (v !== undefined && v !== \"\") qs.set(k, String(v));\n }\n const s = qs.toString();\n if (s) url += `?${s}`;\n }\n\n const headers: Record<string, string> = {\n authorization: `Bearer ${token}`,\n accept: \"application/json\",\n \"user-agent\": USER_AGENT,\n };\n let body: BodyInit | undefined;\n if (init.form) {\n body = init.form;\n } else if (init.body !== undefined) {\n headers[\"content-type\"] = \"application/json\";\n body = JSON.stringify(init.body);\n }\n\n const res = await fetch(url, { method, headers, body });\n\n if (res.status === 429 && attempt <= this.maxRetries) {\n const retryAfter = Number(res.headers.get(\"retry-after\") || 2);\n await new Promise((r) => setTimeout(r, retryAfter * 1000 * attempt));\n return this.request<T>(method, path, init, attempt + 1);\n }\n\n const text = await res.text();\n const data = (text ? JSON.parse(text) : {}) as T;\n if (!res.ok) {\n const msg =\n (data as { error?: { message?: string } })?.error?.message ||\n `HTTP ${res.status}`;\n const err = new Error(msg) as Error & { status: number };\n err.status = res.status;\n throw err;\n }\n return { status: res.status, data };\n }\n\n get<T = unknown>(path: string, query?: RequestInitLite[\"query\"]) {\n return this.request<T>(\"GET\", path, { query });\n }\n post<T = unknown>(path: string, body?: unknown) {\n return this.request<T>(\"POST\", path, { body });\n }\n postForm<T = unknown>(path: string, form: FormData) {\n return this.request<T>(\"POST\", path, { form });\n }\n patch<T = unknown>(path: string, body?: unknown) {\n return this.request<T>(\"PATCH\", path, { body });\n }\n put<T = unknown>(path: string, body?: unknown) {\n return this.request<T>(\"PUT\", path, { body });\n }\n delete<T = unknown>(path: string) {\n return this.request<T>(\"DELETE\", path);\n }\n}\n\nexport const mgmt = new ManagementClient();\n","import { spawn } from \"node:child_process\";\n\n/**\n * Opens `url` in the default browser. Best-effort and non-blocking: if it fails\n * (headless box, no DISPLAY) the caller has already printed the URL so the user\n * can open it manually. Returns true if a launcher was spawned.\n */\nexport function openUrl(url: string): boolean {\n const platform = process.platform;\n let command: string;\n let args: string[];\n\n if (platform === \"darwin\") {\n command = \"open\";\n args = [url];\n } else if (platform === \"win32\") {\n command = \"cmd\";\n // `start` needs an empty title arg; the comma-free form avoids quoting woes.\n args = [\"/c\", \"start\", \"\", url];\n } else {\n command = \"xdg-open\";\n args = [url];\n }\n\n try {\n const child = spawn(command, args, { stdio: \"ignore\", detached: true });\n child.on(\"error\", () => {});\n child.unref();\n return true;\n } catch {\n return false;\n }\n}\n","import path from \"node:path\";\nimport { readFile, writeFile, stat } from \"node:fs/promises\";\n\n/**\n * Upserts `key=value` in an env file, creating it if needed. Existing keys are\n * replaced in place; new keys are appended. Returns the file path. The value is\n * never logged by this module - callers print only the key name.\n */\nexport async function upsertEnv(\n cwd: string,\n file: string,\n entries: Record<string, string>,\n): Promise<string> {\n const filePath = path.join(cwd, file);\n let contents = \"\";\n try {\n contents = await readFile(filePath, \"utf8\");\n } catch {\n contents = \"\";\n }\n\n let next = contents;\n for (const [key, value] of Object.entries(entries)) {\n const line = `${key}=${value}`;\n const re = new RegExp(`^${escapeRegExp(key)}=.*$`, \"m\");\n if (re.test(next)) {\n next = next.replace(re, line);\n } else {\n if (next.length && !next.endsWith(\"\\n\")) next += \"\\n\";\n next += `${line}\\n`;\n }\n }\n\n await writeFile(filePath, next, \"utf8\");\n return filePath;\n}\n\n/** True if a file exists at `cwd/name`. */\nexport async function fileExists(cwd: string, name: string): Promise<boolean> {\n try {\n await stat(path.join(cwd, name));\n return true;\n } catch {\n return false;\n }\n}\n\nfunction escapeRegExp(s: string): string {\n return s.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n}\n","import { spawn } from \"node:child_process\";\nimport { readFile, readdir } from \"node:fs/promises\";\nimport { fileExists } from \"./env-file.js\";\n\nexport type PackageManager = \"pnpm\" | \"yarn\" | \"bun\" | \"npm\";\n\n/**\n * Detects the package manager from lockfiles (then the `npm_config_user_agent`\n * of the running process), defaulting to npm.\n */\nexport async function detectPackageManager(\n cwd: string,\n): Promise<PackageManager> {\n if (await fileExists(cwd, \"pnpm-lock.yaml\")) return \"pnpm\";\n if (await fileExists(cwd, \"yarn.lock\")) return \"yarn\";\n if (await fileExists(cwd, \"bun.lockb\")) return \"bun\";\n if (await fileExists(cwd, \"package-lock.json\")) return \"npm\";\n\n const ua = process.env.npm_config_user_agent ?? \"\";\n if (ua.startsWith(\"pnpm\")) return \"pnpm\";\n if (ua.startsWith(\"yarn\")) return \"yarn\";\n if (ua.startsWith(\"bun\")) return \"bun\";\n return \"npm\";\n}\n\n/** Detects a likely Node web framework for friendlier guidance. */\nexport async function detectFramework(cwd: string): Promise<string | null> {\n try {\n const pkg = JSON.parse(await readFile(`${cwd}/package.json`, \"utf8\")) as {\n dependencies?: Record<string, string>;\n devDependencies?: Record<string, string>;\n };\n const deps = { ...pkg.dependencies, ...pkg.devDependencies };\n if (deps.next) return \"Next.js\";\n if (deps.nuxt) return \"Nuxt\";\n if (deps[\"@remix-run/node\"] || deps[\"@remix-run/react\"]) return \"Remix\";\n if (deps.express) return \"Express\";\n if (deps.fastify) return \"Fastify\";\n if (deps.hono) return \"Hono\";\n if (deps[\"@sveltejs/kit\"]) return \"SvelteKit\";\n return null;\n } catch {\n return null;\n }\n}\n\nexport type Runtime = \"node\" | \"python\" | \"ruby\" | \"go\" | \"php\" | \"other\";\nexport type Stack = { runtime: Runtime; framework: string | null };\n\n/**\n * Detects the project's language/runtime so the CLI installs the right thing\n * (only Node has an SDK today) and prints instructions in the right language.\n * Node is checked first since it's this CLI's primary audience.\n */\nexport async function detectStack(cwd: string): Promise<Stack> {\n if (await fileExists(cwd, \"package.json\")) {\n return { runtime: \"node\", framework: await detectFramework(cwd) };\n }\n if (\n (await fileExists(cwd, \"pyproject.toml\")) ||\n (await fileExists(cwd, \"requirements.txt\")) ||\n (await fileExists(cwd, \"Pipfile\")) ||\n (await fileExists(cwd, \"setup.py\"))\n ) {\n return { runtime: \"python\", framework: await detectPythonFramework(cwd) };\n }\n if ((await fileExists(cwd, \"Gemfile\")) || (await hasGemspec(cwd))) {\n return { runtime: \"ruby\", framework: await detectRubyFramework(cwd) };\n }\n if (await fileExists(cwd, \"go.mod\")) return { runtime: \"go\", framework: null };\n if (await fileExists(cwd, \"composer.json\")) {\n return { runtime: \"php\", framework: await detectPhpFramework(cwd) };\n }\n return { runtime: \"other\", framework: null };\n}\n\n/** Human-friendly label for the detected stack (framework if known). */\nexport function stackLabel(stack: Stack): string {\n if (stack.framework) return stack.framework;\n switch (stack.runtime) {\n case \"node\":\n return \"a Node.js project\";\n case \"python\":\n return \"a Python project\";\n case \"ruby\":\n return \"a Ruby project\";\n case \"go\":\n return \"a Go project\";\n case \"php\":\n return \"a PHP project\";\n default:\n return \"your project\";\n }\n}\n\n/** The language name to tell the agent to write in. */\nexport function languageName(stack: Stack): string {\n switch (stack.runtime) {\n case \"node\":\n return \"TypeScript\";\n case \"python\":\n return \"Python\";\n case \"ruby\":\n return \"Ruby\";\n case \"go\":\n return \"Go\";\n case \"php\":\n return \"PHP\";\n default:\n return \"your language\";\n }\n}\n\nasync function readIf(cwd: string, name: string): Promise<string> {\n try {\n return await readFile(`${cwd}/${name}`, \"utf8\");\n } catch {\n return \"\";\n }\n}\n\nasync function hasGemspec(cwd: string): Promise<boolean> {\n try {\n return (await readdir(cwd)).some((f) => f.endsWith(\".gemspec\"));\n } catch {\n return false;\n }\n}\n\nasync function detectPythonFramework(cwd: string): Promise<string | null> {\n const txt = (\n (await readIf(cwd, \"requirements.txt\")) +\n (await readIf(cwd, \"pyproject.toml\")) +\n (await readIf(cwd, \"Pipfile\"))\n ).toLowerCase();\n if (txt.includes(\"django\")) return \"Django\";\n if (txt.includes(\"fastapi\")) return \"FastAPI\";\n if (txt.includes(\"flask\")) return \"Flask\";\n return null;\n}\n\nasync function detectRubyFramework(cwd: string): Promise<string | null> {\n const txt = (await readIf(cwd, \"Gemfile\")).toLowerCase();\n if (txt.includes(\"rails\")) return \"Rails\";\n if (txt.includes(\"sinatra\")) return \"Sinatra\";\n return null;\n}\n\nasync function detectPhpFramework(cwd: string): Promise<string | null> {\n const txt = (await readIf(cwd, \"composer.json\")).toLowerCase();\n if (txt.includes(\"laravel\")) return \"Laravel\";\n if (txt.includes(\"symfony\")) return \"Symfony\";\n return null;\n}\n\n/* -------------------------------------------------------------------------- */\n/* Non-Node SDK installs (letterapp on PyPI / RubyGems) */\n/* -------------------------------------------------------------------------- */\n\nconst SDK_PYPI = \"letterapp\";\nconst SDK_GEM = \"letterapp\";\n\nexport type SdkInstall = { cmd: string; args: string[]; display: string };\n\nfunction sdkInstall(cmd: string, args: string[]): SdkInstall {\n return { cmd, args, display: `${cmd} ${args.join(\" \")}` };\n}\n\n/**\n * Picks the right Python installer for the project: uv > poetry > pipenv > pip.\n * Returns the command we'd run to add `letterapp`.\n */\nexport async function pythonSdkInstall(cwd: string): Promise<SdkInstall> {\n if (await fileExists(cwd, \"uv.lock\")) return sdkInstall(\"uv\", [\"add\", SDK_PYPI]);\n const pyproject = (await readIf(cwd, \"pyproject.toml\")).toLowerCase();\n if (\n (await fileExists(cwd, \"poetry.lock\")) ||\n pyproject.includes(\"[tool.poetry]\")\n ) {\n return sdkInstall(\"poetry\", [\"add\", SDK_PYPI]);\n }\n if (await fileExists(cwd, \"Pipfile\")) {\n return sdkInstall(\"pipenv\", [\"install\", SDK_PYPI]);\n }\n return sdkInstall(\"pip\", [\"install\", SDK_PYPI]);\n}\n\n/** Bundler when there's a Gemfile, otherwise a plain `gem install`. */\nexport async function rubySdkInstall(cwd: string): Promise<SdkInstall> {\n if (await fileExists(cwd, \"Gemfile\")) {\n return sdkInstall(\"bundle\", [\"add\", SDK_GEM]);\n }\n return sdkInstall(\"gem\", [\"install\", SDK_GEM]);\n}\n\n/** Runs an SdkInstall command, streaming output. Resolves to the exit code. */\nexport function runSdkInstall(\n install: SdkInstall,\n cwd: string,\n): Promise<number> {\n return new Promise((resolve) => {\n const child = spawn(install.cmd, install.args, {\n cwd,\n stdio: \"inherit\",\n shell: process.platform === \"win32\",\n });\n child.on(\"error\", () => resolve(1));\n child.on(\"close\", (code) => resolve(code ?? 1));\n });\n}\n\nexport function installCommand(pm: PackageManager, pkg: string): string {\n switch (pm) {\n case \"pnpm\":\n return `pnpm add ${pkg}`;\n case \"yarn\":\n return `yarn add ${pkg}`;\n case \"bun\":\n return `bun add ${pkg}`;\n default:\n return `npm install ${pkg}`;\n }\n}\n\n/** Runs the install command, streaming output. Resolves to the exit code. */\nexport function runInstall(\n pm: PackageManager,\n pkg: string,\n cwd: string,\n): Promise<number> {\n const args = pm === \"npm\" ? [\"install\", pkg] : [\"add\", pkg];\n return new Promise((resolve) => {\n const child = spawn(pm, args, {\n cwd,\n stdio: \"inherit\",\n shell: process.platform === \"win32\",\n });\n child.on(\"error\", () => resolve(1));\n child.on(\"close\", (code) => resolve(code ?? 1));\n });\n}\n","import { Command } from \"commander\";\nimport {\n getBaseUrl,\n DEFAULT_BASE_URL,\n saveCredential,\n} from \"../config.js\";\nimport { startDeviceAuth, pollDeviceAuth } from \"../client.js\";\nimport { openUrl } from \"../browser.js\";\nimport { upsertEnv } from \"../env-file.js\";\nimport {\n detectPackageManager,\n detectStack,\n installCommand,\n languageName,\n pythonSdkInstall,\n rubySdkInstall,\n runInstall,\n runSdkInstall,\n stackLabel,\n type SdkInstall,\n type Stack,\n} from \"../pm.js\";\nimport {\n banner,\n c,\n cliCommand,\n log,\n printError,\n printInfo,\n printSuccess,\n printWarning,\n prompt,\n} from \"../output.js\";\n\nconst SDK_PACKAGE = \"@letterapp/node\";\nconst sleep = (ms: number) => new Promise((r) => setTimeout(r, ms));\n\n// Node projects use .env.local (Next.js convention, gitignored by default);\n// everything else uses the more universal .env.\nfunction envFileFor(stack: Stack): string {\n return stack.runtime === \"node\" ? \".env.local\" : \".env\";\n}\n\ninterface LoginOptions {\n open: boolean;\n install: boolean;\n yes?: boolean;\n baseUrl?: string;\n apiKey?: string;\n}\n\nexport function registerLoginCommand(program: Command) {\n program\n .command(\"login\", { isDefault: true })\n .alias(\"init\")\n .description(\"Connect this project to Letter (interactive device login)\")\n .option(\"--no-open\", \"Don't open the browser; print the URL to open manually\")\n .option(\"-y, --yes\", \"Non-interactive: don't wait for Enter (agents/CI)\")\n .option(\"--no-install\", \"Skip installing the SDK\")\n .option(\"--base-url <url>\", \"Target a self-hosted or local Letter instance\")\n .option(\n \"--api-key <key>\",\n \"CI only: write a key without the device flow (never use in chat)\",\n )\n .action(async (opts: LoginOptions) => {\n const code = await runLogin(opts);\n if (code !== 0) process.exitCode = code;\n });\n}\n\nasync function runLogin(opts: LoginOptions): Promise<number> {\n const base = getBaseUrl(opts.baseUrl);\n const cwd = process.cwd();\n const interactive = process.stdin.isTTY && !opts.yes;\n\n banner();\n\n // CI escape hatch: write the provided key non-interactively. Documented as\n // automation-only; interactive/agent use should rely on the device flow so\n // no secret passes through the command line or chat.\n if (opts.apiKey) {\n const envFile = envFileFor(await detectStack(cwd));\n const entries: Record<string, string> = { LETTER_API_KEY: opts.apiKey };\n if (base !== DEFAULT_BASE_URL) entries.LETTER_BASE_URL = base;\n const file = await upsertEnv(cwd, envFile, entries);\n printSuccess(`Saved LETTER_API_KEY to ${rel(cwd, file)} (--api-key).`);\n printWarning(\n `--api-key is for CI. For interactive setup, run \\`${cliCommand()} login\\`.`,\n );\n return 0;\n }\n\n // 1. Begin the flow.\n let flow;\n try {\n flow = await startDeviceAuth(base);\n } catch (err) {\n printError(err);\n return 1;\n }\n\n log(c.bold(\"Confirm this code in your browser:\"));\n log();\n log(\" \" + c.cyan(c.bold(flow.user_code)));\n log();\n\n // 2. Open the browser (interactive: wait for Enter; otherwise auto/print).\n if (interactive && opts.open) {\n await prompt(c.dim(\"Press Enter to open your browser… \"));\n }\n if (opts.open) openUrl(flow.verification_uri_complete);\n printInfo(\"If your browser didn't open, visit:\");\n log(\" \" + c.blue(flow.verification_uri_complete));\n log();\n printInfo(\"Waiting for you to approve… (Ctrl+C to cancel)\");\n\n // 3. Poll until the user approves/denies or the code expires.\n const deadline = Date.now() + flow.expires_in * 1000;\n let intervalMs = Math.max(1, flow.interval) * 1000;\n\n while (Date.now() < deadline) {\n await sleep(intervalMs);\n let res;\n try {\n res = await pollDeviceAuth(base, flow.device_code);\n } catch (err) {\n printError(err);\n return 1;\n }\n\n if (res.status === \"authorization_pending\") continue;\n if (res.status === \"slow_down\") {\n intervalMs += 1000;\n continue;\n }\n if (res.status === \"access_denied\") {\n printError(new Error(\"Request denied in the browser. Nothing was changed.\"));\n return 1;\n }\n if (res.status === \"expired_token\") {\n printError(new Error(\"This login expired. Run the command again to retry.\"));\n return 1;\n }\n\n return finish(res, cwd, opts.install);\n }\n\n printError(new Error(\"Timed out waiting for approval. Run the command again.\"));\n return 1;\n}\n\nasync function finish(\n approved: {\n api_key: string;\n pat: string;\n base_url: string;\n project: { slug: string; name: string };\n workspace?: { id: string; name: string };\n },\n cwd: string,\n doInstall: boolean,\n): Promise<number> {\n const { api_key: apiKey, pat, base_url: baseUrl, project, workspace } = approved;\n log();\n printSuccess(`Approved for project ${c.bold(project.name)}.`);\n\n const stack = await detectStack(cwd);\n const isNode = stack.runtime === \"node\";\n const envFile = envFileFor(stack);\n\n // Write the key to the project env (value never printed) + shared store.\n const entries: Record<string, string> = { LETTER_API_KEY: apiKey };\n if (baseUrl && baseUrl !== DEFAULT_BASE_URL) entries.LETTER_BASE_URL = baseUrl;\n const written = await upsertEnv(cwd, envFile, entries);\n printSuccess(`Saved LETTER_API_KEY to ${rel(cwd, written)}.`);\n\n try {\n const credFile = await saveCredential({\n apiKey,\n pat,\n baseUrl: baseUrl || DEFAULT_BASE_URL,\n project,\n workspace,\n savedAt: new Date().toISOString(),\n });\n printSuccess(`Stored credentials in ${tildify(credFile)} for tooling (MCP + CLI).`);\n } catch {\n printWarning(\"Could not write ~/.letter/credentials.json (continuing).\");\n }\n\n // Install the SDK for the detected language. Node, Python, and Ruby have\n // official SDKs; everything else integrates over the HTTP API. We capture a\n // one-line `sdkNote` per path and hand the agent a single, language-agnostic\n // prompt - the agent knows the repo and writes idiomatic calls itself.\n let sdkNote: string;\n if (isNode) {\n const pm = await detectPackageManager(cwd);\n if (doInstall) {\n printInfo(`Installing ${SDK_PACKAGE} with ${pm}…`);\n const code = await runInstall(pm, SDK_PACKAGE, cwd);\n if (code === 0) printSuccess(`Installed ${SDK_PACKAGE}.`);\n else printWarning(`Install failed. Run: ${installCommand(pm, SDK_PACKAGE)}`);\n } else {\n printInfo(`Skipped install. Run: ${installCommand(pm, SDK_PACKAGE)}`);\n }\n sdkNote = `the ${SDK_PACKAGE} SDK is installed`;\n } else if (stack.runtime === \"python\" || stack.runtime === \"ruby\") {\n const install =\n stack.runtime === \"python\"\n ? await pythonSdkInstall(cwd)\n : await rubySdkInstall(cwd);\n await installSdk(install, doInstall);\n sdkNote =\n stack.runtime === \"python\"\n ? \"the letterapp Python SDK is installed\"\n : \"the letterapp Ruby gem is installed\";\n } else {\n printInfo(\n `Detected ${stackLabel(stack)}. No ${languageName(stack)} SDK yet, so the agent should use the HTTP API.`,\n );\n sdkNote =\n `there's no official ${languageName(stack)} SDK, so call the HTTP ingestion ` +\n \"API directly (POST /v1/identify and /v1/track with \" +\n \"Authorization: Bearer LETTER_API_KEY and Content-Type: application/json)\";\n }\n\n printAgentHandoff(sdkNote, envFile, cliCommand());\n return 0;\n}\n\n/** Shared install-or-print step for the non-Node SDKs (letterapp). */\nasync function installSdk(install: SdkInstall, doInstall: boolean): Promise<void> {\n if (doInstall) {\n printInfo(`Installing letterapp (${install.display})…`);\n const code = await runSdkInstall(install, process.cwd());\n if (code === 0) printSuccess(\"Installed letterapp.\");\n else printWarning(`Install failed. Run: ${install.display}`);\n } else {\n printInfo(`Skipped install. Run: ${install.display}`);\n }\n}\n\nconst AGENT_DOCS = \"https://letter.app/docs/agent-setup\";\n\n/**\n * Language-agnostic handoff for the last mile. The CLI does the mechanical setup\n * (key in the env, SDK installed where one exists) but deliberately does NOT\n * edit source - the coding agent has the repo context to write idiomatic calls.\n * So we print one ready-to-paste prompt that works for any stack plus a link to\n * the full guide, instead of language-specific snippets.\n */\nfunction printAgentHandoff(\n sdkNote: string,\n envFile: string,\n verifyCmd: string,\n): void {\n const rule = \" \" + \"─\".repeat(68);\n log();\n log(c.bold(\"Next: finish setup with your coding agent\"));\n log();\n log(c.dim(\" Paste this to your agent (Cursor, Claude Code, Codex, OpenClaw, Hermes, …):\"));\n log(c.dim(rule));\n // Plain (uncolored) so it copies cleanly into an agent prompt.\n log(\" Integrate Letter (product email automation) into this app. The CLI\");\n log(` already did setup: LETTER_API_KEY is in ${envFile} and ${sdkNote}.`);\n log(\" Using the right idioms for this codebase:\");\n log(\" - create a shared, server-side Letter client that reads\");\n log(\" LETTER_API_KEY from the environment (only set baseUrl/base_url\");\n log(\" when LETTER_BASE_URL is present),\");\n log(\" - call identify({ userId, email }) where users sign up or log in,\");\n log(\" - call track({ userId, event }) on the 2-3 most important actions,\");\n log(\" - flush before serverless handlers or scripts exit.\");\n log(\" Never print or commit the key. Then verify with the command below.\");\n log(` Full guide: ${AGENT_DOCS}`);\n log(c.dim(rule));\n log();\n log(c.dim(` Prefer to wire it up yourself? Same guide: ${AGENT_DOCS}`));\n log();\n log(c.dim(\"Verify it landed: \") + c.bold(`${verifyCmd} status`));\n log(\n c.dim(\n `Your API key is in ${envFile} - keep it out of source control, and don't echo its value.`,\n ),\n );\n log();\n}\n\nfunction rel(cwd: string, file: string): string {\n return file.startsWith(cwd) ? file.slice(cwd.length + 1) || file : file;\n}\n\nfunction tildify(file: string): string {\n const home = process.env.HOME || process.env.USERPROFILE;\n return home && file.startsWith(home) ? `~${file.slice(home.length)}` : file;\n}\n","import { Command } from \"commander\";\nimport { readCredential, clearCredential } from \"../config.js\";\nimport { c, cliCommand, isJsonMode, printJson, printInfo, printSuccess } from \"../output.js\";\n\nfunction mask(key: string): string {\n return key.length > 12 ? key.slice(0, 8) + \"…\" + key.slice(-4) : \"set\";\n}\n\nexport function registerAuthCommands(program: Command) {\n const auth = program.command(\"auth\").description(\"Manage the stored Letter connection\");\n\n auth\n .command(\"status\")\n .description(\"Show whether this machine is connected to Letter\")\n .action(async () => {\n const cred = await readCredential();\n const envKey = process.env.LETTER_API_KEY;\n\n if (!cred && !envKey) {\n if (isJsonMode()) printJson({ connected: false });\n else printInfo(\"Not connected. Run \" + c.bold(`${cliCommand()} login`) + \" to set up.\");\n return;\n }\n\n if (isJsonMode()) {\n printJson({\n connected: true,\n source: envKey ? \"env\" : \"credentials\",\n project: cred?.project ?? null,\n base_url: cred?.baseUrl ?? null,\n key: envKey ? \"env\" : cred ? mask(cred.apiKey) : null,\n });\n return;\n }\n\n printSuccess(\"Connected\" + (cred ? ` to ${c.bold(cred.project.name)}` : \"\"));\n if (cred) {\n console.log(c.dim(\" Project: \") + cred.project.slug);\n console.log(c.dim(\" Key: \") + mask(cred.apiKey));\n console.log(c.dim(\" API: \") + cred.baseUrl);\n }\n if (envKey) console.log(c.dim(\" LETTER_API_KEY is set in the environment.\"));\n });\n\n auth\n .command(\"logout\")\n .description(\"Remove the stored credential (~/.letter/credentials.json)\")\n .action(async () => {\n await clearCredential();\n printSuccess(\"Removed stored credential.\");\n });\n}\n","import { Command } from \"commander\";\nimport { client } from \"../client.js\";\nimport { readCredential } from \"../config.js\";\nimport { c, isJsonMode, printError, printInfo, printJson, printSuccess, spinner } from \"../output.js\";\n\ntype StatusResponse = {\n contacts?: number;\n events?: number;\n [k: string]: unknown;\n};\n\nexport function registerStatusCommand(program: Command) {\n program\n .command(\"status\")\n .description(\"Check whether your project has received any contacts or events\")\n .action(async () => {\n const spin = spinner(\"Checking your Letter project…\").start();\n try {\n const { data } = await client.get<StatusResponse>(\"/v1/status\");\n spin.stop();\n\n if (isJsonMode()) {\n printJson(data);\n return;\n }\n\n const cred = await readCredential();\n const contacts = data.contacts ?? 0;\n const events = data.events ?? 0;\n\n if (cred) printInfo(`Project ${c.bold(cred.project.name)}`);\n if (contacts > 0 || events > 0) {\n printSuccess(`Connected. ${contacts} contact(s), ${events} event(s) received.`);\n } else {\n printInfo(\"Connected, but no data yet. Fire your first identify/track call.\");\n }\n } catch (err) {\n spin.stop();\n printError(err);\n process.exitCode = 1;\n }\n });\n}\n","import { Command } from \"commander\";\nimport {\n getBaseUrl,\n setBaseUrl,\n getConfigPath,\n resetConfig,\n} from \"../config.js\";\nimport { c, isJsonMode, printError, printJson, printSuccess } from \"../output.js\";\n\nexport function registerConfigCommands(program: Command) {\n const cfg = program.command(\"config\").description(\"Manage CLI configuration\");\n\n cfg\n .command(\"set\")\n .description(\"Set a config value\")\n .argument(\"<key>\", \"Config key: base-url\")\n .argument(\"<value>\", \"Value to set\")\n .action((key: string, value: string) => {\n switch (key) {\n case \"base-url\":\n setBaseUrl(value);\n printSuccess(`Base URL set to ${getBaseUrl()}`);\n break;\n default:\n printError(new Error(`Unknown config key: ${key}. Valid keys: base-url`));\n process.exitCode = 1;\n }\n });\n\n cfg\n .command(\"get\")\n .description(\"Show CLI configuration\")\n .argument(\"[key]\", \"Config key (omit to show all)\")\n .action((key?: string) => {\n const all = { base_url: getBaseUrl(), config_path: getConfigPath() };\n if (key && key !== \"base-url\" && key !== \"path\") {\n printError(new Error(`Unknown config key: ${key}`));\n process.exitCode = 1;\n return;\n }\n if (isJsonMode()) {\n printJson(key === \"base-url\" ? { base_url: all.base_url } : key === \"path\" ? { config_path: all.config_path } : all);\n return;\n }\n if (key === \"base-url\") console.log(all.base_url);\n else if (key === \"path\") console.log(all.config_path);\n else for (const [k, v] of Object.entries(all)) console.log(c.bold(k + \":\") + \" \" + v);\n });\n\n cfg\n .command(\"reset\")\n .description(\"Reset CLI configuration to defaults\")\n .action(() => {\n resetConfig();\n printSuccess(\"Configuration reset.\");\n });\n}\n","import { readFile } from \"node:fs/promises\";\nimport { Command } from \"commander\";\nimport { mgmt } from \"../client.js\";\nimport { resolveProjectSlug } from \"../config.js\";\nimport {\n cliCommand,\n emit,\n isJsonMode,\n log,\n printError,\n printInfo,\n printJson,\n printSuccess,\n} from \"../output.js\";\nimport { markdownToTipTap } from \"../markdown.js\";\n\n/**\n * Resource command groups for the management API. Each group wraps the same\n * REST endpoints the dashboard uses (`/v1/...`), so the whole app is operable\n * from the terminal. Everything honors the global `--json` flag (see\n * `output.emit`) and project-scoped groups take `--project <slug>` (defaulting\n * to the project connected by `letter login`).\n *\n * The shape is data-driven: one `ResourceSpec` per resource, expanded into\n * `list/get/create/update/delete` + custom verb subcommands by `register()`.\n * Keeping it declarative means a new endpoint is a few lines, not a new file.\n */\n\ntype Field = {\n /** commander option flag, e.g. \"--name <name>\". */\n flag: string;\n /** request-body key, e.g. \"name\". */\n key: string;\n /** parse the value as JSON (supports `@file.json` to read from disk). */\n json?: boolean;\n /** coerce to a number. */\n number?: boolean;\n /** coerce to a boolean. */\n boolean?: boolean;\n /** (multipart only) read the value as a file path and upload its bytes. */\n file?: boolean;\n description?: string;\n};\n\ntype Action = {\n name: string;\n describe: string;\n method: \"get\" | \"post\" | \"put\" | \"patch\" | \"delete\";\n /** Path suffix appended after the resource (and id, if `needsId`). */\n suffix: string;\n /** Whether the verb operates on a single item (default true). */\n needsId?: boolean;\n fields?: Field[];\n /** Query params for GET verbs. */\n query?: Field[];\n /** Send fields as multipart/form-data (for file uploads). */\n multipart?: boolean;\n};\n\ntype ResourceSpec = {\n /** Command group name (also the default path segment). */\n name: string;\n describe: string;\n /** Nested under /v1/projects/{slug} when true. */\n scoped: boolean;\n /** Path segment if different from `name`. */\n segment?: string;\n /** Hidden back-compat alias for the group name. */\n alias?: string;\n /** Positional id arg name (e.g. \"externalId\", \"email\"). Default \"id\". */\n idName?: string;\n list?: { paginated?: boolean; query?: Field[] };\n get?: boolean;\n create?: Field[];\n update?: Field[];\n remove?: boolean;\n actions?: Action[];\n};\n\nasync function readValue(raw: string): Promise<string> {\n if (raw.startsWith(\"@\")) return (await readFile(raw.slice(1), \"utf8\")).trim();\n return raw;\n}\n\nasync function buildBody(\n fields: Field[],\n opts: Record<string, unknown>,\n): Promise<Record<string, unknown>> {\n const body: Record<string, unknown> = {};\n for (const f of fields) {\n const v = opts[camel(f.key)];\n if (v === undefined) continue;\n if (f.json) {\n body[f.key] = JSON.parse(await readValue(String(v)));\n } else if (f.number) {\n body[f.key] = Number(v);\n } else if (f.boolean) {\n body[f.key] = v === true || v === \"true\";\n } else {\n body[f.key] = await readValue(String(v));\n }\n }\n return body;\n}\n\nasync function buildForm(\n fields: Field[],\n opts: Record<string, unknown>,\n): Promise<FormData> {\n const form = new FormData();\n for (const f of fields) {\n const v = opts[camel(f.key)];\n if (v === undefined) continue;\n if (f.file) {\n const path = String(v);\n const bytes = await readFile(path);\n const name = path.split(\"/\").pop() || f.key;\n form.append(f.key, new Blob([new Uint8Array(bytes)]), name);\n } else if (f.json) {\n // The server parses these fields with JSON.parse; keep them as strings.\n form.append(f.key, await readValue(String(v)));\n } else {\n form.append(f.key, String(v));\n }\n }\n return form;\n}\n\nfunction buildQuery(\n fields: Field[],\n opts: Record<string, unknown>,\n): Record<string, string | number | undefined> {\n const q: Record<string, string | number | undefined> = {};\n for (const f of fields) {\n const v = opts[camel(f.key)];\n if (v === undefined) continue;\n q[f.key] = f.number ? Number(v) : String(v);\n }\n return q;\n}\n\n/** \"external-id\" / \"external_id\" → \"externalId\" (commander camelCases flags). */\nfunction camel(key: string): string {\n return key.replace(/[-_]([a-z])/g, (_, ch: string) => ch.toUpperCase());\n}\n\nfunction applyFields(cmd: Command, fields: Field[]) {\n for (const f of fields) {\n cmd.option(f.flag, f.description ?? f.key);\n }\n}\n\nasync function basePath(spec: ResourceSpec, project?: string): Promise<string> {\n const seg = spec.segment ?? spec.name;\n if (!spec.scoped) return `/v1/${seg}`;\n const slug = await resolveProjectSlug(project);\n return `/v1/projects/${slug}/${seg}`;\n}\n\nfunction withProject(cmd: Command, scoped: boolean): Command {\n if (scoped) {\n cmd.option(\"-p, --project <slug>\", \"Project slug (default: connected project)\");\n }\n return cmd;\n}\n\nfunction fail(err: unknown) {\n printError(err);\n process.exitCode = 1;\n}\n\nfunction register(program: Command, spec: ResourceSpec) {\n const group = program.command(spec.name).description(spec.describe);\n if (spec.alias) group.alias(spec.alias);\n const idName = spec.idName ?? \"id\";\n\n if (spec.list) {\n const cmd = withProject(\n group.command(\"list\").description(`List ${spec.name}`),\n spec.scoped,\n );\n if (spec.list.paginated) {\n cmd.option(\"--limit <n>\", \"Max items (1-100)\");\n cmd.option(\"--cursor <cursor>\", \"Pagination cursor\");\n }\n if (spec.list.query) applyFields(cmd, spec.list.query);\n cmd.action(async (opts) => {\n try {\n const query: Record<string, string | number | undefined> = {};\n if (spec.list?.paginated) {\n if (opts.limit) query.limit = Number(opts.limit);\n if (opts.cursor) query.cursor = opts.cursor;\n }\n if (spec.list?.query) Object.assign(query, buildQuery(spec.list.query, opts));\n const { data } = await mgmt.get(await basePath(spec, opts.project), query);\n emit(data);\n } catch (err) {\n fail(err);\n }\n });\n }\n\n if (spec.get) {\n const cmd = withProject(\n group\n .command(\"get\")\n .description(`Get a ${spec.name} by ${idName}`)\n .argument(`<${idName}>`, idName),\n spec.scoped,\n );\n cmd.action(async (idValue: string, opts) => {\n try {\n const path = `${await basePath(spec, opts.project)}/${encodeURIComponent(idValue)}`;\n const { data } = await mgmt.get(path);\n emit(data);\n } catch (err) {\n fail(err);\n }\n });\n }\n\n if (spec.create) {\n const cmd = withProject(\n group.command(\"create\").description(`Create a ${spec.name}`),\n spec.scoped,\n );\n applyFields(cmd, spec.create);\n cmd.action(async (opts) => {\n try {\n const body = await buildBody(spec.create!, opts);\n const { data } = await mgmt.post(await basePath(spec, opts.project), body);\n emit(data);\n } catch (err) {\n fail(err);\n }\n });\n }\n\n if (spec.update) {\n const cmd = withProject(\n group\n .command(\"update\")\n .description(`Update a ${spec.name}`)\n .argument(`<${idName}>`, idName),\n spec.scoped,\n );\n applyFields(cmd, spec.update);\n cmd.action(async (idValue: string, opts) => {\n try {\n const body = await buildBody(spec.update!, opts);\n const path = `${await basePath(spec, opts.project)}/${encodeURIComponent(idValue)}`;\n const { data } = await mgmt.patch(path, body);\n emit(data);\n } catch (err) {\n fail(err);\n }\n });\n }\n\n if (spec.remove) {\n const cmd = withProject(\n group\n .command(\"delete\")\n .description(`Delete a ${spec.name}`)\n .argument(`<${idName}>`, idName),\n spec.scoped,\n );\n cmd.action(async (idValue: string, opts) => {\n try {\n const path = `${await basePath(spec, opts.project)}/${encodeURIComponent(idValue)}`;\n await mgmt.delete(path);\n printSuccess(`Deleted ${spec.name.replace(/s$/, \"\")} ${idValue}.`);\n } catch (err) {\n fail(err);\n }\n });\n }\n\n for (const action of spec.actions ?? []) {\n const needsId = action.needsId ?? true;\n const cmd = withProject(\n needsId\n ? group\n .command(action.name)\n .description(action.describe)\n .argument(`<${idName}>`, idName)\n : group.command(action.name).description(action.describe),\n spec.scoped,\n );\n if (action.fields) applyFields(cmd, action.fields);\n if (action.query) applyFields(cmd, action.query);\n\n const handler = async (...args: unknown[]) => {\n // commander passes (idValue?, opts, command). Last arg is the Command.\n const opts = args[args.length - 2] as Record<string, unknown>;\n const idValue = needsId ? (args[0] as string) : undefined;\n try {\n let path = await basePath(spec, opts.project as string | undefined);\n if (needsId) path += `/${encodeURIComponent(idValue!)}`;\n path += action.suffix;\n\n if (action.method === \"get\") {\n const query = action.query ? buildQuery(action.query, opts) : undefined;\n const { data } = await mgmt.get(path, query);\n emit(data);\n return;\n }\n if (action.multipart && action.fields) {\n const form = await buildForm(action.fields, opts);\n const { data, status } = await mgmt.request(\n action.method.toUpperCase(),\n path,\n { form },\n );\n if (status === 204) printSuccess(`${action.name} ok.`);\n else emit(data);\n return;\n }\n const body = action.fields ? await buildBody(action.fields, opts) : undefined;\n const { data, status } = await mgmt.request(\n action.method.toUpperCase(),\n path,\n { body },\n );\n if (status === 204) printSuccess(`${action.name} ok.`);\n else emit(data);\n } catch (err) {\n fail(err);\n }\n };\n cmd.action(handler);\n }\n}\n\nconst cursor = { paginated: true } as const;\n\nconst SPECS: ResourceSpec[] = [\n // -- Workspace ------------------------------------------------------------\n {\n name: \"projects\",\n describe: \"Manage projects\",\n scoped: false,\n idName: \"slug\",\n list: {},\n get: true,\n create: [\n { flag: \"--name <name>\", key: \"name\" },\n { flag: \"--timezone <tz>\", key: \"timezone\" },\n ],\n update: [\n { flag: \"--name <name>\", key: \"name\" },\n { flag: \"--timezone <tz>\", key: \"timezone\" },\n ],\n remove: true,\n },\n {\n name: \"members\",\n describe: \"Manage workspace members\",\n scoped: false,\n idName: \"userId\",\n list: {},\n create: [\n { flag: \"--email <email>\", key: \"email\" },\n { flag: \"--role <role>\", key: \"role\", description: \"member | admin\" },\n ],\n remove: true,\n },\n {\n name: \"invitations\",\n describe: \"Manage workspace invitations\",\n scoped: false,\n list: {},\n create: [\n { flag: \"--email <email>\", key: \"email\" },\n { flag: \"--role <role>\", key: \"role\", description: \"member | admin\" },\n ],\n remove: true,\n },\n {\n // Personal access tokens (lt_pat_*) authenticate the CLI / Management API.\n // The dashboard calls these \"API keys\"; `tokens` stays as a hidden alias.\n name: \"api-keys\",\n alias: \"tokens\",\n segment: \"tokens\",\n describe: \"Manage your personal access tokens (lt_pat_*, the CLI/API credential)\",\n scoped: false,\n list: {},\n create: [\n { flag: \"--name <name>\", key: \"name\" },\n {\n flag: \"--expires-at <iso>\",\n key: \"expires_at\",\n description: \"Expiry as an ISO 8601 datetime (default: never)\",\n },\n ],\n remove: true,\n },\n // -- Contacts & data ------------------------------------------------------\n {\n name: \"contacts\",\n describe: \"Manage contacts\",\n scoped: true,\n idName: \"externalId\",\n list: { ...cursor },\n get: true,\n actions: [\n {\n name: \"suppress\",\n describe: \"Suppress a contact\",\n method: \"post\",\n suffix: \"/suppress\",\n },\n {\n name: \"resubscribe\",\n describe: \"Resubscribe a contact\",\n method: \"post\",\n suffix: \"/resubscribe\",\n },\n {\n name: \"import\",\n describe: \"Upload a CSV import (--file <path> --mapping <json|@file>)\",\n method: \"post\",\n needsId: false,\n suffix: \"/imports\",\n multipart: true,\n fields: [\n { flag: \"--file <path>\", key: \"file\", file: true },\n { flag: \"--mapping <json>\", key: \"mapping\", json: true },\n { flag: \"--dedupe <strategy>\", key: \"dedupe\", description: \"update | skip\" },\n { flag: \"--row-count <n>\", key: \"rowCount\" },\n ],\n },\n ],\n },\n {\n name: \"accounts\",\n describe: \"Inspect accounts\",\n scoped: true,\n idName: \"externalId\",\n list: { ...cursor },\n get: true,\n },\n {\n name: \"events\",\n describe: \"List events\",\n scoped: true,\n list: { ...cursor, query: [{ flag: \"--name <name>\", key: \"name\" }] },\n },\n {\n name: \"suppressions\",\n describe: \"Manage the suppression list\",\n scoped: true,\n idName: \"email\",\n list: { ...cursor },\n create: [{ flag: \"--email <email>\", key: \"email\" }],\n remove: true,\n },\n {\n name: \"segments\",\n describe: \"Manage segments\",\n scoped: true,\n list: {},\n get: true,\n create: [\n { flag: \"--name <name>\", key: \"name\" },\n { flag: \"--filter <json>\", key: \"filter\", json: true },\n ],\n update: [\n { flag: \"--name <name>\", key: \"name\" },\n { flag: \"--filter <json>\", key: \"filter\", json: true },\n ],\n remove: true,\n actions: [\n {\n name: \"preview\",\n describe: \"Count contacts matching a filter (--filter <json|@file>)\",\n method: \"post\",\n needsId: false,\n suffix: \"/preview\",\n fields: [{ flag: \"--filter <json>\", key: \"filter\", json: true }],\n },\n ],\n },\n // -- Sequences ------------------------------------------------------------\n {\n name: \"sequences\",\n describe: \"Manage drip sequences\",\n scoped: true,\n list: {},\n get: true,\n create: [{ flag: \"--name <name>\", key: \"name\" }],\n update: [\n { flag: \"--name <name>\", key: \"name\" },\n { flag: \"--status <status>\", key: \"status\", description: \"active | archived\" },\n ],\n remove: true,\n actions: [\n {\n name: \"draft\",\n describe: \"Save the draft graph + trigger\",\n method: \"put\",\n suffix: \"/draft\",\n fields: [\n { flag: \"--graph <json>\", key: \"graph\", json: true },\n { flag: \"--trigger <json>\", key: \"trigger\", json: true },\n { flag: \"--expected-revision <n>\", key: \"expected_revision\", number: true },\n ],\n },\n {\n name: \"publish\",\n describe: \"Publish the current draft\",\n method: \"post\",\n suffix: \"/publish\",\n },\n {\n name: \"preview\",\n describe: \"Render an email node (--node-id <id>)\",\n method: \"post\",\n suffix: \"/preview\",\n fields: [{ flag: \"--node-id <id>\", key: \"nodeId\" }],\n },\n {\n name: \"test-email\",\n describe: \"Send a test email (--node-id <id> --to <email>)\",\n method: \"post\",\n suffix: \"/test-email\",\n fields: [\n { flag: \"--node-id <id>\", key: \"nodeId\" },\n { flag: \"--to <email>\", key: \"to\" },\n ],\n },\n {\n name: \"activity\",\n describe: \"Show enrollment activity\",\n method: \"get\",\n suffix: \"/activity\",\n },\n {\n name: \"versions\",\n describe: \"List published versions\",\n method: \"get\",\n suffix: \"/versions\",\n },\n ],\n },\n // -- Broadcasts -----------------------------------------------------------\n {\n name: \"broadcasts\",\n describe: \"Manage broadcasts\",\n scoped: true,\n list: {},\n get: true,\n create: [{ flag: \"--name <name>\", key: \"name\" }],\n update: [\n { flag: \"--name <name>\", key: \"name\" },\n { flag: \"--subject <subject>\", key: \"subject\" },\n { flag: \"--preview <preview>\", key: \"preview\" },\n { flag: \"--audience <json>\", key: \"audience\", json: true },\n { flag: \"--template-id <id>\", key: \"template_id\" },\n { flag: \"--body-doc <json>\", key: \"body_doc\", json: true },\n ],\n remove: true,\n actions: [\n {\n name: \"preflight\",\n describe: \"Validate before sending\",\n method: \"post\",\n suffix: \"/preflight\",\n },\n {\n name: \"schedule\",\n describe: \"Schedule (--scheduled-at <iso>) or send now (omit)\",\n method: \"post\",\n suffix: \"/schedule\",\n fields: [{ flag: \"--scheduled-at <iso>\", key: \"scheduled_at\" }],\n },\n {\n name: \"cancel\",\n describe: \"Cancel a scheduled or sending broadcast\",\n method: \"post\",\n suffix: \"/cancel\",\n },\n {\n name: \"live\",\n describe: \"Live status, stats, and recent activity\",\n method: \"get\",\n suffix: \"/live\",\n },\n ],\n },\n // -- Templates ------------------------------------------------------------\n {\n name: \"templates\",\n describe: \"Manage email templates\",\n scoped: true,\n list: {},\n get: true,\n create: [\n { flag: \"--name <name>\", key: \"name\" },\n { flag: \"--design <json>\", key: \"design\", json: true },\n ],\n update: [\n { flag: \"--name <name>\", key: \"name\" },\n { flag: \"--design <json>\", key: \"design\", json: true },\n ],\n remove: true,\n actions: [\n {\n name: \"default\",\n describe: \"Set as the project default\",\n method: \"post\",\n suffix: \"/default\",\n },\n {\n name: \"reset\",\n describe: \"Reset design to a preset (--preset plain|branded)\",\n method: \"post\",\n suffix: \"/reset\",\n fields: [{ flag: \"--preset <preset>\", key: \"preset\" }],\n },\n {\n name: \"logo\",\n describe: \"Upload a logo (--file <path>)\",\n method: \"post\",\n suffix: \"/logo\",\n multipart: true,\n fields: [{ flag: \"--file <path>\", key: \"file\", file: true }],\n },\n {\n name: \"remove-logo\",\n describe: \"Remove the template logo\",\n method: \"delete\",\n suffix: \"/logo\",\n },\n ],\n },\n // -- Settings -------------------------------------------------------------\n {\n name: \"domains\",\n describe: \"Manage sending domains\",\n scoped: true,\n list: {},\n create: [{ flag: \"--domain <domain>\", key: \"domain\" }],\n remove: true,\n actions: [\n {\n name: \"verify\",\n describe: \"Re-check DKIM verification\",\n method: \"post\",\n suffix: \"/verify\",\n },\n ],\n },\n {\n // Project ingestion keys (lt_live_*) authenticate the SDKs / ingestion API.\n // The dashboard calls these \"Project tokens\"; `keys` stays as a hidden alias.\n name: \"project-tokens\",\n alias: \"keys\",\n segment: \"keys\",\n describe: \"Manage project ingestion keys (lt_live_*, the SDK/ingestion credential)\",\n scoped: true,\n list: {},\n create: [{ flag: \"--name <name>\", key: \"name\" }],\n remove: true,\n },\n];\n\n/**\n * Canonical sequence shapes, printed by `sequences schema`. This is the\n * reference an agent reaches for instead of reverse-engineering the graph\n * from 400s. Mirrors the strict server validation + the /docs/sequences guide.\n */\nconst SEQUENCE_SCHEMA = {\n trigger: {\n event: {\n type: \"event\",\n eventName: \"modification_limit_reached\",\n oncePerContact: true,\n \"filter?\": \"FilterSpec — optional audience filter on the contact\",\n },\n contact_created: {\n type: \"contact_created\",\n \"filter?\": \"FilterSpec — optional audience filter on the contact\",\n },\n },\n nodeConfigs: {\n trigger: { type: \"trigger\" },\n email: {\n type: \"email\",\n subject: \"You hit your limit\",\n \"preview?\": \"Here's how to lift it\",\n \"bodyDoc?\": { type: \"doc\", content: [\"…TipTap nodes…\"] },\n \"templateId?\": \"uuid | null (omit for project default)\",\n },\n wait: { type: \"wait\", duration: { value: 1, unit: \"minutes | hours | days\" } },\n branch: {\n type: \"branch\",\n condition: {\n event: { kind: \"event\", eventName: \"Upgraded\", occurred: true, \"window?\": { value: 2, unit: \"days\" } },\n trait: { kind: \"trait\", scope: \"contact | account\", path: \"plan\", op: \"eq | neq | contains | not_contains | exists | not_exists\", \"value?\": \"pro\" },\n },\n },\n exit: { type: \"exit\", \"reason?\": \"done\" },\n },\n edge: {\n id: \"e1\",\n source: \"trigger\",\n target: \"email1\",\n \"branch?\": \"yes | no — only on edges leaving a branch node\",\n },\n notes: [\n \"config.type must equal the node's type.\",\n \"The email body field is `bodyDoc` (camelCase); a wait nests `duration`.\",\n \"Draft saves reject unknown / mis-cased keys with a 400.\",\n \"Publish requires: one trigger, every node reachable, every path ends at an exit, each branch has one yes + one no edge.\",\n \"`sequences scaffold` builds a valid trigger → [wait] → email → exit graph in one command.\",\n ],\n example: {\n trigger: {\n type: \"event\",\n eventName: \"modification_limit_reached\",\n oncePerContact: true,\n filter: { kind: \"trait\", path: \"plan\", op: \"eq\", value: \"free\" },\n },\n graph: {\n nodes: [\n { id: \"trigger\", type: \"trigger\", position: { x: 280, y: 80 }, config: { type: \"trigger\" } },\n { id: \"wait1\", type: \"wait\", position: { x: 280, y: 200 }, config: { type: \"wait\", duration: { value: 1, unit: \"hours\" } } },\n {\n id: \"email1\",\n type: \"email\",\n position: { x: 280, y: 320 },\n config: {\n type: \"email\",\n subject: \"You hit your limit\",\n bodyDoc: { type: \"doc\", content: [{ type: \"paragraph\", content: [{ type: \"text\", text: \"Upgrade to keep going.\" }] }] },\n },\n },\n { id: \"exit1\", type: \"exit\", position: { x: 280, y: 440 }, config: { type: \"exit\" } },\n ],\n edges: [\n { id: \"e1\", source: \"trigger\", target: \"wait1\" },\n { id: \"e2\", source: \"wait1\", target: \"email1\" },\n { id: \"e3\", source: \"email1\", target: \"exit1\" },\n ],\n },\n },\n} as const;\n\nfunction printSequenceSchemaHuman() {\n log(\"Sequence authoring shapes (see /docs/sequences for the full guide).\\n\");\n log(\"Trigger (how contacts enter):\");\n log(JSON.stringify(SEQUENCE_SCHEMA.trigger, null, 2));\n log(\"\\nNode configs (config.type must match the node type):\");\n log(JSON.stringify(SEQUENCE_SCHEMA.nodeConfigs, null, 2));\n log(\"\\nEdge:\");\n log(JSON.stringify(SEQUENCE_SCHEMA.edge, null, 2));\n log(\"\\nNotes:\");\n for (const n of SEQUENCE_SCHEMA.notes) log(` • ${n}`);\n log(\"\\nKnown-good example (trigger + graph):\");\n log(JSON.stringify(SEQUENCE_SCHEMA.example, null, 2));\n log(`\\nScaffold one in a single command:`);\n log(\n ` ${cliCommand()} sequences scaffold --name \"Limit reached\" \\\\\\n` +\n ` --event modification_limit_reached --audience-trait plan=free \\\\\\n` +\n ` --wait 1h --subject \"You hit your limit\" \\\\\\n` +\n ` --body-md \"Upgrade to **keep going**.\" --publish`,\n );\n}\n\n/** Parse a human duration like `30m`, `1h`, `2d` into the wait/branch shape. */\nfunction parseDuration(raw: string): {\n value: number;\n unit: \"minutes\" | \"hours\" | \"days\";\n} {\n const m = /^(\\d+)\\s*(m|min|mins|minute|minutes|h|hr|hrs|hour|hours|d|day|days)$/i.exec(\n raw.trim(),\n );\n if (!m) {\n throw new Error(`Invalid --wait \"${raw}\". Use e.g. 30m, 1h, or 2d.`);\n }\n const u = (m[2] ?? \"\").toLowerCase();\n const unit = u.startsWith(\"d\") ? \"days\" : u.startsWith(\"h\") ? \"hours\" : \"minutes\";\n return { value: Number(m[1]), unit };\n}\n\nfunction attachSequenceExtras(program: Command) {\n const seq = program.commands.find((cmd) => cmd.name() === \"sequences\");\n if (!seq) return;\n\n seq\n .command(\"schema\")\n .description(\"Print the canonical trigger / node / edge shapes + an example\")\n .action(() => {\n if (isJsonMode()) printJson(SEQUENCE_SCHEMA);\n else printSequenceSchemaHuman();\n });\n\n seq\n .command(\"scaffold\")\n .description(\n \"Create + draft (and optionally publish) a trigger → [wait] → email → exit sequence\",\n )\n .requiredOption(\"--name <name>\", \"Sequence name\")\n .option(\n \"--event <name>\",\n \"Event-trigger name (omit for a contact_created trigger)\",\n )\n .option(\"--no-once-per-contact\", \"Allow re-enrolment on every event\")\n .option(\"--audience <json>\", \"Audience FilterSpec (inline JSON or @file.json)\")\n .option(\n \"--audience-trait <path=value>\",\n \"Shorthand audience filter: trait equals value (e.g. plan=free)\",\n )\n .option(\"--wait <duration>\", \"Delay before the email, e.g. 30m, 1h, 2d\")\n .requiredOption(\"--subject <subject>\", \"Email subject\")\n .option(\"--body-md <markdown>\", \"Email body as Markdown (converted to rich text)\")\n .option(\"--body-text <text>\", \"Email body as plain text\")\n .option(\"--publish\", \"Publish immediately after drafting\")\n .option(\"-p, --project <slug>\", \"Project slug (default: connected project)\")\n .action(async (opts: Record<string, unknown>) => {\n try {\n const slug = await resolveProjectSlug(opts.project as string | undefined);\n const base = `/v1/projects/${slug}/sequences`;\n\n // Audience filter (full FilterSpec wins over the trait shorthand).\n let filter: unknown;\n if (opts.audience !== undefined) {\n filter = JSON.parse(await readValue(String(opts.audience)));\n } else if (opts.audienceTrait !== undefined) {\n const eq = String(opts.audienceTrait);\n const idx = eq.indexOf(\"=\");\n if (idx === -1) {\n throw new Error(\"--audience-trait must be path=value, e.g. plan=free\");\n }\n filter = {\n kind: \"trait\",\n path: eq.slice(0, idx),\n op: \"eq\",\n value: eq.slice(idx + 1),\n };\n }\n\n const trigger = opts.event\n ? {\n type: \"event\",\n eventName: String(opts.event),\n oncePerContact: opts.oncePerContact !== false,\n ...(filter ? { filter } : {}),\n }\n : { type: \"contact_created\", ...(filter ? { filter } : {}) };\n\n const bodyDoc =\n opts.bodyMd !== undefined\n ? markdownToTipTap(String(opts.bodyMd))\n : opts.bodyText !== undefined\n ? markdownToTipTap(String(opts.bodyText))\n : undefined;\n\n const nodes: Record<string, unknown>[] = [\n { id: \"trigger\", type: \"trigger\", position: { x: 280, y: 80 }, config: { type: \"trigger\" } },\n ];\n const edges: Record<string, unknown>[] = [];\n let prev = \"trigger\";\n let y = 200;\n if (opts.wait !== undefined) {\n nodes.push({\n id: \"wait1\",\n type: \"wait\",\n position: { x: 280, y },\n config: { type: \"wait\", duration: parseDuration(String(opts.wait)) },\n });\n edges.push({ id: \"e_wait\", source: prev, target: \"wait1\" });\n prev = \"wait1\";\n y += 120;\n }\n nodes.push({\n id: \"email1\",\n type: \"email\",\n position: { x: 280, y },\n config: {\n type: \"email\",\n subject: String(opts.subject),\n ...(bodyDoc ? { bodyDoc } : {}),\n },\n });\n edges.push({ id: \"e_email\", source: prev, target: \"email1\" });\n prev = \"email1\";\n y += 120;\n nodes.push({\n id: \"exit1\",\n type: \"exit\",\n position: { x: 280, y },\n config: { type: \"exit\" },\n });\n edges.push({ id: \"e_exit\", source: prev, target: \"exit1\" });\n const graph = { nodes, edges };\n\n const created = await mgmt.post(base, { name: String(opts.name) });\n const id = (created.data as { id: string }).id;\n await mgmt.put(`${base}/${id}/draft`, {\n graph,\n trigger,\n expected_revision: 0,\n });\n let published = false;\n if (opts.publish) {\n await mgmt.request(\"POST\", `${base}/${id}/publish`, {});\n published = true;\n }\n\n if (isJsonMode()) {\n printJson({ id, slug, published, trigger, graph });\n return;\n }\n printSuccess(\n `Created sequence ${id}${published ? \" and published it\" : \" (draft saved)\"}.`,\n );\n if (!published) {\n printInfo(`Publish when ready: ${cliCommand()} sequences publish ${id}`);\n }\n printInfo(\n `Tweak it on the canvas, or re-draft with ${cliCommand()} sequences draft ${id} --graph @graph.json`,\n );\n } catch (err) {\n fail(err);\n }\n });\n}\n\nexport function registerResourceCommands(program: Command) {\n for (const spec of SPECS) register(program, spec);\n attachSequenceExtras(program);\n\n // `letter me` — the token's identity (handy sanity check).\n program\n .command(\"me\")\n .description(\"Show the token's user, workspace, and role\")\n .action(async () => {\n try {\n const { data } = await mgmt.get(\"/v1/me\");\n emit(data);\n } catch (err) {\n fail(err);\n }\n });\n\n // Sender identity + sending mode don't fit the CRUD mold: register directly.\n program\n .command(\"sender-identity\")\n .description(\"Set From / From-name / Reply-To (--from-email, --from-name, --reply-to-email)\")\n .option(\"-p, --project <slug>\", \"Project slug (default: connected project)\")\n .option(\"--from-email <email>\", \"From address\")\n .option(\"--from-name <name>\", \"From display name\")\n .option(\"--reply-to-email <email>\", \"Reply-To address\")\n .action(async (opts) => {\n try {\n const slug = await resolveProjectSlug(opts.project);\n const body: Record<string, unknown> = {};\n if (opts.fromEmail !== undefined) body.from_email = opts.fromEmail;\n if (opts.fromName !== undefined) body.from_name = opts.fromName;\n if (opts.replyToEmail !== undefined) body.reply_to_email = opts.replyToEmail;\n const { data } = await mgmt.put(`/v1/projects/${slug}/sender-identity`, body);\n emit(data);\n } catch (err) {\n fail(err);\n }\n });\n\n program\n .command(\"sending-mode\")\n .description(\"Set the sending mode (letter_subdomain | byo_domain)\")\n .argument(\"<mode>\", \"letter_subdomain | byo_domain\")\n .option(\"-p, --project <slug>\", \"Project slug (default: connected project)\")\n .action(async (mode: string, opts) => {\n try {\n const slug = await resolveProjectSlug(opts.project);\n const { data } = await mgmt.put(`/v1/projects/${slug}/sending-mode`, {\n sending_mode: mode,\n });\n emit(data);\n } catch (err) {\n fail(err);\n }\n });\n}\n","/**\n * Minimal Markdown -> TipTap/ProseMirror converter, so email bodies can be\n * authored from the CLI without hand-writing ProseMirror JSON.\n *\n * It targets the subset the Letter email renderer (TipTap StarterKit + Link)\n * understands: headings (#..###), paragraphs, bullet/ordered lists,\n * blockquotes, horizontal rules, and inline **bold**, *italic*, `code`, and\n * [links](url) plus hard line breaks. It is intentionally small - for anything\n * richer, pass a full `bodyDoc` JSON to `sequences draft` instead.\n */\n\nexport type TipTapMark = { type: string; attrs?: Record<string, unknown> };\nexport type TipTapNode = {\n type: string;\n attrs?: Record<string, unknown>;\n marks?: TipTapMark[];\n text?: string;\n content?: TipTapNode[];\n};\nexport type TipTapDoc = { type: \"doc\"; content: TipTapNode[] };\n\nconst HR = /^\\s*([-*_])\\1{2,}\\s*$/;\nconst HEADING = /^(#{1,3})\\s+(.*)$/;\nconst QUOTE = /^\\s*>\\s?/;\nconst BULLET = /^\\s*[-*+]\\s+/;\nconst ORDERED = /^\\s*\\d+\\.\\s+/;\n\nfunction isBlockStart(line: string): boolean {\n return (\n /^#{1,3}\\s+/.test(line) ||\n BULLET.test(line) ||\n ORDERED.test(line) ||\n QUOTE.test(line) ||\n HR.test(line)\n );\n}\n\nfunction pushText(nodes: TipTapNode[], text: string, marks?: TipTapMark[]) {\n if (!text) return;\n nodes.push(marks ? { type: \"text\", text, marks } : { type: \"text\", text });\n}\n\n/** Parse inline marks (code, links, bold, italic) within a single line. */\nfunction inline(text: string): TipTapNode[] {\n const nodes: TipTapNode[] = [];\n // Order matters: code first (so `**` inside code isn't parsed as bold),\n // then links, then bold, then italic.\n const token =\n /(`[^`]+`)|(\\[[^\\]]+\\]\\([^)]+\\))|(\\*\\*[^*]+\\*\\*)|(__[^_]+__)|(\\*[^*]+\\*)|(_[^_]+_)/;\n let rest = text;\n while (rest.length > 0) {\n const m = token.exec(rest);\n if (!m) {\n pushText(nodes, rest);\n break;\n }\n if (m.index > 0) pushText(nodes, rest.slice(0, m.index));\n const tok = m[0];\n if (tok.startsWith(\"`\")) {\n pushText(nodes, tok.slice(1, -1), [{ type: \"code\" }]);\n } else if (tok.startsWith(\"[\")) {\n const lm = /\\[([^\\]]+)\\]\\(([^)]+)\\)/.exec(tok);\n if (lm) {\n pushText(nodes, lm[1] ?? \"\", [\n { type: \"link\", attrs: { href: lm[2] ?? \"\" } },\n ]);\n }\n } else if (tok.startsWith(\"**\") || tok.startsWith(\"__\")) {\n pushText(nodes, tok.slice(2, -2), [{ type: \"bold\" }]);\n } else {\n pushText(nodes, tok.slice(1, -1), [{ type: \"italic\" }]);\n }\n rest = rest.slice(m.index + tok.length);\n }\n return nodes;\n}\n\n/** Inline-parse multiple lines, joining them with hard breaks. */\nfunction inlineMultiline(lines: string[]): TipTapNode[] {\n const out: TipTapNode[] = [];\n lines.forEach((line, idx) => {\n if (idx > 0) out.push({ type: \"hardBreak\" });\n out.push(...inline(line));\n });\n return out;\n}\n\nfunction listItems(\n lines: string[],\n start: number,\n marker: RegExp,\n): { items: TipTapNode[]; next: number } {\n const items: TipTapNode[] = [];\n let i = start;\n for (let cur = lines[i]; cur !== undefined && marker.test(cur); cur = lines[i]) {\n items.push({\n type: \"listItem\",\n content: [{ type: \"paragraph\", content: inline(cur.replace(marker, \"\")) }],\n });\n i++;\n }\n return { items, next: i };\n}\n\nexport function markdownToTipTap(input: string): TipTapDoc {\n // Shells don't expand `\\n` inside double quotes, so accept literal escapes.\n const src = input.replace(/\\\\n/g, \"\\n\").replace(/\\\\t/g, \"\\t\");\n const lines = src.split(\"\\n\");\n const blocks: TipTapNode[] = [];\n let i = 0;\n while (i < lines.length) {\n const line = lines[i] ?? \"\";\n if (line.trim() === \"\") {\n i++;\n continue;\n }\n if (HR.test(line)) {\n blocks.push({ type: \"horizontalRule\" });\n i++;\n continue;\n }\n const h = HEADING.exec(line);\n if (h) {\n blocks.push({\n type: \"heading\",\n attrs: { level: (h[1] ?? \"#\").length },\n content: inline(h[2] ?? \"\"),\n });\n i++;\n continue;\n }\n if (QUOTE.test(line)) {\n const quote: string[] = [];\n for (let cur = lines[i]; cur !== undefined && QUOTE.test(cur); cur = lines[i]) {\n quote.push(cur.replace(QUOTE, \"\"));\n i++;\n }\n blocks.push({\n type: \"blockquote\",\n content: [{ type: \"paragraph\", content: inline(quote.join(\" \")) }],\n });\n continue;\n }\n if (BULLET.test(line)) {\n const { items, next } = listItems(lines, i, BULLET);\n blocks.push({ type: \"bulletList\", content: items });\n i = next;\n continue;\n }\n if (ORDERED.test(line)) {\n const { items, next } = listItems(lines, i, ORDERED);\n blocks.push({ type: \"orderedList\", content: items });\n i = next;\n continue;\n }\n // Paragraph: gather consecutive plain lines, join with hard breaks.\n const para: string[] = [];\n for (let cur = lines[i]; cur !== undefined && cur.trim() !== \"\" && !isBlockStart(cur); cur = lines[i]) {\n para.push(cur);\n i++;\n }\n blocks.push({ type: \"paragraph\", content: inlineMultiline(para) });\n }\n return { type: \"doc\", content: blocks };\n}\n"],"mappings":"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","names":["path","path","readFile","writeFile","spawn","readFile","readFile","spawn","program","program","program","program","readFile","readFile","path","program","data","status"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/output.ts","../src/config.ts","../src/client.ts","../src/browser.ts","../src/env-file.ts","../src/pm.ts","../src/commands/login.ts","../src/commands/auth.ts","../src/commands/status.ts","../src/commands/config.ts","../src/commands/resources.ts","../src/markdown.ts"],"sourcesContent":["declare const PKG_VERSION: string;\nimport { Command } from \"commander\";\nimport { setJsonMode } from \"./output.js\";\nimport { registerLoginCommand } from \"./commands/login.js\";\nimport { registerAuthCommands } from \"./commands/auth.js\";\nimport { registerStatusCommand } from \"./commands/status.js\";\nimport { registerConfigCommands } from \"./commands/config.js\";\nimport { registerResourceCommands } from \"./commands/resources.js\";\n\nconst program = new Command();\n\nprogram\n .name(\"letter\")\n .description(\"Connect your app to Letter, then manage it from the command line\")\n .version(PKG_VERSION)\n .option(\"--json\", \"Output raw JSON (for scripting / agents)\")\n .hook(\"preAction\", (thisCommand) => {\n if (thisCommand.opts().json) setJsonMode(true);\n });\n\n// Two layers, one login:\n// - login/auth/status/config wire up the project and its ingestion key\n// (lt_live_*, written to the project env as LETTER_API_KEY).\n// - the resource groups drive the Management API with the workspace PAT\n// (lt_pat_*, stored in ~/.letter/credentials.json). Both credentials are\n// minted by `letter login`; each command picks the right one on its own.\nregisterLoginCommand(program); // default command (`letter` == `letter login`)\nregisterAuthCommands(program);\nregisterStatusCommand(program);\nregisterConfigCommands(program);\nregisterResourceCommands(program); // control-plane resource groups (PAT-backed)\n\nprogram.parseAsync();\n","import chalk from \"chalk\";\nimport ora, { type Ora } from \"ora\";\nimport { createInterface } from \"node:readline\";\n\nlet jsonMode = false;\n\nexport function setJsonMode(enabled: boolean) {\n jsonMode = enabled;\n}\nexport function isJsonMode() {\n return jsonMode;\n}\n\nexport function printJson(data: unknown) {\n console.log(JSON.stringify(data, null, 2));\n}\n\n/**\n * The command users should repeat to run us again, so printed hints are\n * copy-pasteable. When launched through `npx`, Node runs us from the npx cache\n * (`…/_npx/<hash>/…`) and there's no `letter` on PATH, so suggest the npx form;\n * a global install resolves the bare `letter` binary.\n */\nexport function cliCommand(): string {\n return (process.argv[1] ?? \"\").includes(\"_npx\") ? \"npx @letterapp/cli\" : \"letter\";\n}\n\nexport function log(msg = \"\") {\n if (jsonMode) return;\n console.log(msg);\n}\n\nexport function printSuccess(msg: string) {\n if (jsonMode) return;\n console.log(chalk.green(\"✓\") + \" \" + msg);\n}\n\nexport function printInfo(msg: string) {\n if (jsonMode) return;\n console.log(chalk.cyan(\"›\") + \" \" + msg);\n}\n\nexport function printWarning(msg: string) {\n if (jsonMode) return;\n console.log(chalk.yellow(\"!\") + \" \" + msg);\n}\n\nexport type ApiIssue = { path?: string; message?: string };\n\nexport function printError(err: unknown) {\n const msg = err instanceof Error ? err.message : String(err);\n const issues = (err as { issues?: ApiIssue[] })?.issues;\n if (jsonMode) {\n printJson({ error: msg, ...(issues?.length ? { issues } : {}) });\n return;\n }\n console.error(chalk.red(\"✗\") + \" \" + msg);\n // Validation 400s carry field-level issues; print them so callers don't\n // have to re-probe the API to learn which key/shape was wrong.\n if (Array.isArray(issues)) {\n for (const i of issues) {\n const path = i.path ? chalk.dim(`${i.path}: `) : \"\";\n console.error(\" \" + path + (i.message ?? \"\"));\n }\n }\n}\n\n/** The Letter wordmark banner. Suppressed in JSON mode. */\nexport function banner() {\n if (jsonMode) return;\n console.log(\"\");\n console.log(\" \" + chalk.bold(\"Letter\") + chalk.red(\".\") + \" \" + chalk.dim(\"CLI\"));\n console.log(\"\");\n}\n\nexport function spinner(text: string): Ora {\n return ora({ text, color: \"cyan\", isEnabled: !jsonMode });\n}\n\n/**\n * Prompts the user and resolves with their input. Returns \"\" immediately on a\n * non-interactive stdin so automated/agent runs don't hang.\n */\nexport function prompt(question: string): Promise<string> {\n if (!process.stdin.isTTY) return Promise.resolve(\"\");\n const rl = createInterface({ input: process.stdin, output: process.stdout });\n return new Promise((resolve) => {\n rl.question(question, (answer) => {\n rl.close();\n resolve(answer.trim());\n });\n });\n}\n\n/**\n * Print a management API result. In `--json` mode, emits the raw payload so\n * agents/scripts can parse it. In human mode, renders list envelopes as a\n * compact `id label` table and single resources as pretty JSON.\n */\nexport function emit(data: unknown) {\n if (jsonMode) {\n printJson(data);\n return;\n }\n if (data && typeof data === \"object\" && Array.isArray((data as { data?: unknown }).data)) {\n const env = data as { data: Record<string, unknown>[]; next_cursor?: string | null };\n if (env.data.length === 0) {\n console.log(chalk.dim(\"(none)\"));\n } else {\n for (const row of env.data) {\n const id = String(row.id ?? row.slug ?? row.external_id ?? \"\");\n const label =\n (row.name as string) ??\n (row.email as string) ??\n (row.domain as string) ??\n (row.subject as string) ??\n \"\";\n console.log(`${chalk.bold(id)}${label ? \" \" + label : \"\"}`);\n }\n }\n if (env.next_cursor) {\n console.log(chalk.dim(`\\n› more: --cursor ${env.next_cursor}`));\n }\n return;\n }\n printJson(data);\n}\n\nexport const c = chalk;\n","import Conf from \"conf\";\nimport { homedir } from \"node:os\";\nimport path from \"node:path\";\nimport { mkdir, readFile, writeFile, rm } from \"node:fs/promises\";\nimport { cliCommand } from \"./output.js\";\n\n/** The SDK / API default. When the resolved base equals this we don't bother\n * writing LETTER_BASE_URL into the project env. */\nexport const DEFAULT_BASE_URL = \"https://api.letter.app\";\n\n/* -------------------------------------------------------------------------- */\n/* Non-secret CLI preferences (base URL). */\n/* Stored with `conf`, mirroring how the SDK/MCP resolve their endpoint. */\n/* -------------------------------------------------------------------------- */\n\nconst config = new Conf({\n projectName: \"letterapp-cli\",\n schema: {\n baseUrl: { type: \"string\", default: DEFAULT_BASE_URL },\n },\n});\n\n/** Resolve the API base: explicit flag > env > stored config > prod default. */\nexport function getBaseUrl(flag?: string): string {\n const raw =\n flag ||\n process.env.LETTER_BASE_URL ||\n (config.get(\"baseUrl\") as string) ||\n DEFAULT_BASE_URL;\n return raw.replace(/\\/$/, \"\");\n}\n\nexport function setBaseUrl(url: string): void {\n config.set(\"baseUrl\", url.replace(/\\/$/, \"\"));\n}\n\nexport function getConfigPath(): string {\n return config.path;\n}\n\nexport function resetConfig(): void {\n config.clear();\n}\n\n/* -------------------------------------------------------------------------- */\n/* Secret credential store (~/.letter/credentials.json). */\n/* Written here, read by @letterapp/mcp so the secret never lands in an MCP */\n/* config. Kept separate from `conf` and locked to owner-only permissions. */\n/* -------------------------------------------------------------------------- */\n\nexport type StoredCredential = {\n apiKey: string;\n /** Workspace Personal Access Token (lt_pat_*) for the management API/CLI. */\n pat?: string;\n baseUrl: string;\n project: { slug: string; name: string };\n workspace?: { id: string; name: string };\n savedAt: string;\n};\n\n/**\n * Resolve the project slug a management command should target: explicit\n * `--project` flag > `LETTER_PROJECT` env (for scripts/CI) > the connected\n * project from `letter login`. Throws a clear error when none is available so\n * scripts fail loudly.\n */\nexport async function resolveProjectSlug(flag?: string): Promise<string> {\n if (flag) return flag;\n if (process.env.LETTER_PROJECT) return process.env.LETTER_PROJECT;\n const cred = await readCredential();\n if (cred?.project?.slug) return cred.project.slug;\n throw new Error(\n `No project. Pass --project <slug>, set LETTER_PROJECT, or run \\`${cliCommand()} login\\`.`,\n );\n}\n\nexport function credentialsPath(): string {\n return path.join(homedir(), \".letter\", \"credentials.json\");\n}\n\nexport async function saveCredential(cred: StoredCredential): Promise<string> {\n const file = credentialsPath();\n await mkdir(path.dirname(file), { recursive: true, mode: 0o700 });\n await writeFile(file, `${JSON.stringify(cred, null, 2)}\\n`, { mode: 0o600 });\n return file;\n}\n\nexport async function readCredential(): Promise<StoredCredential | null> {\n try {\n const raw = await readFile(credentialsPath(), \"utf8\");\n return JSON.parse(raw) as StoredCredential;\n } catch {\n return null;\n }\n}\n\nexport async function clearCredential(): Promise<void> {\n await rm(credentialsPath(), { force: true });\n}\n","import { getBaseUrl, readCredential } from \"./config.js\";\nimport { cliCommand } from \"./output.js\";\n\nconst USER_AGENT = \"@letterapp/cli\";\n\n/* -------------------------------------------------------------------------- */\n/* Device authorization (RFC 8628 style). No secret ever passes through argv. */\n/* -------------------------------------------------------------------------- */\n\nexport type StartResponse = {\n device_code: string;\n user_code: string;\n verification_uri: string;\n verification_uri_complete: string;\n interval: number;\n expires_in: number;\n};\n\nexport type PollResponse =\n | { status: \"authorization_pending\" }\n | { status: \"slow_down\"; retryAfter?: number }\n | { status: \"access_denied\" }\n | { status: \"expired_token\" }\n | {\n status: \"approved\";\n api_key: string;\n /** Workspace PAT for the management API (lt_pat_*). */\n pat: string;\n base_url: string;\n project: { slug: string; name: string };\n workspace?: { id: string; name: string };\n };\n\n/** Starts a device-authorization flow against the given API base. */\nexport async function startDeviceAuth(base: string): Promise<StartResponse> {\n const res = await fetch(`${base}/v1/cli/auth/start`, {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\", \"user-agent\": USER_AGENT },\n body: \"{}\",\n });\n if (!res.ok) {\n throw new Error(\n `Could not start login (HTTP ${res.status}). Is ${base} reachable?`,\n );\n }\n return (await res.json()) as StartResponse;\n}\n\n/** Polls once for approval. */\nexport async function pollDeviceAuth(\n base: string,\n deviceCode: string,\n): Promise<PollResponse> {\n const res = await fetch(`${base}/v1/cli/auth/poll`, {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\", \"user-agent\": USER_AGENT },\n body: JSON.stringify({ device_code: deviceCode }),\n });\n\n if (res.status === 429) {\n const retryAfter = Number(res.headers.get(\"retry-after\") ?? \"5\");\n return { status: \"slow_down\", retryAfter };\n }\n if (!res.ok) {\n throw new Error(`Login poll failed (HTTP ${res.status}).`);\n }\n return (await res.json()) as PollResponse;\n}\n\n/* -------------------------------------------------------------------------- */\n/* Authenticated client. Resolves the key from the stored credential (written */\n/* by `letter login`) or LETTER_API_KEY. Used by data commands like `status`. */\n/* -------------------------------------------------------------------------- */\n\nexport interface ApiResponse<T = unknown> {\n status: number;\n data: T;\n}\n\nclass LetterClient {\n private maxRetries = 3;\n\n private async resolveAuth(): Promise<{ base: string; token: string }> {\n const cred = await readCredential();\n const token = process.env.LETTER_API_KEY || cred?.apiKey || \"\";\n if (!token) {\n throw new Error(\n `Not connected. Run \\`${cliCommand()} login\\` (or set LETTER_API_KEY) first.`,\n );\n }\n const base = getBaseUrl(process.env.LETTER_API_KEY ? undefined : cred?.baseUrl);\n return { base, token };\n }\n\n private async request<T = unknown>(\n method: string,\n path: string,\n attempt = 1,\n ): Promise<ApiResponse<T>> {\n const { base, token } = await this.resolveAuth();\n const res = await fetch(`${base}${path}`, {\n method,\n headers: {\n authorization: `Bearer ${token}`,\n accept: \"application/json\",\n \"user-agent\": USER_AGENT,\n },\n });\n\n if (res.status === 429 && attempt <= this.maxRetries) {\n const retryAfter = Number(res.headers.get(\"retry-after\") || 2);\n await new Promise((r) => setTimeout(r, retryAfter * 1000 * attempt));\n return this.request<T>(method, path, attempt + 1);\n }\n\n const data = (res.status === 204 ? {} : await res.json()) as T;\n if (!res.ok) {\n throw apiErrorFrom(data, res.status);\n }\n return { status: res.status, data };\n }\n\n get<T = unknown>(path: string) {\n return this.request<T>(\"GET\", path);\n }\n}\n\nexport type ApiIssue = { path?: string; message?: string };\n\n/** Build an Error carrying the API's `error.message` + field-level `issues`. */\nfunction apiErrorFrom(data: unknown, status: number): Error {\n const errObj = (data as {\n error?: { message?: string; issues?: ApiIssue[] };\n })?.error;\n const msg = errObj?.message || `HTTP ${status}`;\n const err = new Error(msg) as Error & {\n status: number;\n issues?: ApiIssue[];\n };\n err.status = status;\n if (Array.isArray(errObj?.issues) && errObj.issues.length > 0) {\n err.issues = errObj.issues;\n }\n return err;\n}\n\nexport const client = new LetterClient();\n\n/* -------------------------------------------------------------------------- */\n/* Management client. Authenticates with the workspace PAT (lt_pat_*) and */\n/* drives the control-plane (/v1/projects, sequences, broadcasts, ...). Used by */\n/* the resource command groups. */\n/* -------------------------------------------------------------------------- */\n\nexport type RequestInitLite = {\n body?: unknown;\n query?: Record<string, string | number | undefined>;\n form?: FormData;\n};\n\nclass ManagementClient {\n private maxRetries = 3;\n\n private async resolveAuth(): Promise<{ base: string; token: string }> {\n const cred = await readCredential();\n const token = process.env.LETTER_PAT || cred?.pat || \"\";\n if (!token) {\n throw new Error(\n `Not connected for management. Run \\`${cliCommand()} login\\` (or set LETTER_PAT) first.`,\n );\n }\n const base = getBaseUrl(process.env.LETTER_PAT ? undefined : cred?.baseUrl);\n return { base, token };\n }\n\n async request<T = unknown>(\n method: string,\n path: string,\n init: RequestInitLite = {},\n attempt = 1,\n ): Promise<ApiResponse<T>> {\n const { base, token } = await this.resolveAuth();\n\n let url = `${base}${path}`;\n if (init.query) {\n const qs = new URLSearchParams();\n for (const [k, v] of Object.entries(init.query)) {\n if (v !== undefined && v !== \"\") qs.set(k, String(v));\n }\n const s = qs.toString();\n if (s) url += `?${s}`;\n }\n\n const headers: Record<string, string> = {\n authorization: `Bearer ${token}`,\n accept: \"application/json\",\n \"user-agent\": USER_AGENT,\n };\n let body: BodyInit | undefined;\n if (init.form) {\n body = init.form;\n } else if (init.body !== undefined) {\n headers[\"content-type\"] = \"application/json\";\n body = JSON.stringify(init.body);\n }\n\n const res = await fetch(url, { method, headers, body });\n\n if (res.status === 429 && attempt <= this.maxRetries) {\n const retryAfter = Number(res.headers.get(\"retry-after\") || 2);\n await new Promise((r) => setTimeout(r, retryAfter * 1000 * attempt));\n return this.request<T>(method, path, init, attempt + 1);\n }\n\n const text = await res.text();\n const data = (text ? JSON.parse(text) : {}) as T;\n if (!res.ok) {\n throw apiErrorFrom(data, res.status);\n }\n return { status: res.status, data };\n }\n\n get<T = unknown>(path: string, query?: RequestInitLite[\"query\"]) {\n return this.request<T>(\"GET\", path, { query });\n }\n post<T = unknown>(path: string, body?: unknown) {\n return this.request<T>(\"POST\", path, { body });\n }\n postForm<T = unknown>(path: string, form: FormData) {\n return this.request<T>(\"POST\", path, { form });\n }\n patch<T = unknown>(path: string, body?: unknown) {\n return this.request<T>(\"PATCH\", path, { body });\n }\n put<T = unknown>(path: string, body?: unknown) {\n return this.request<T>(\"PUT\", path, { body });\n }\n delete<T = unknown>(path: string) {\n return this.request<T>(\"DELETE\", path);\n }\n}\n\nexport const mgmt = new ManagementClient();\n","import { spawn } from \"node:child_process\";\n\n/**\n * Opens `url` in the default browser. Best-effort and non-blocking: if it fails\n * (headless box, no DISPLAY) the caller has already printed the URL so the user\n * can open it manually. Returns true if a launcher was spawned.\n */\nexport function openUrl(url: string): boolean {\n const platform = process.platform;\n let command: string;\n let args: string[];\n\n if (platform === \"darwin\") {\n command = \"open\";\n args = [url];\n } else if (platform === \"win32\") {\n command = \"cmd\";\n // `start` needs an empty title arg; the comma-free form avoids quoting woes.\n args = [\"/c\", \"start\", \"\", url];\n } else {\n command = \"xdg-open\";\n args = [url];\n }\n\n try {\n const child = spawn(command, args, { stdio: \"ignore\", detached: true });\n child.on(\"error\", () => {});\n child.unref();\n return true;\n } catch {\n return false;\n }\n}\n","import path from \"node:path\";\nimport { readFile, writeFile, stat } from \"node:fs/promises\";\n\n/**\n * Upserts `key=value` in an env file, creating it if needed. Existing keys are\n * replaced in place; new keys are appended. Returns the file path. The value is\n * never logged by this module - callers print only the key name.\n */\nexport async function upsertEnv(\n cwd: string,\n file: string,\n entries: Record<string, string>,\n): Promise<string> {\n const filePath = path.join(cwd, file);\n let contents = \"\";\n try {\n contents = await readFile(filePath, \"utf8\");\n } catch {\n contents = \"\";\n }\n\n let next = contents;\n for (const [key, value] of Object.entries(entries)) {\n const line = `${key}=${value}`;\n const re = new RegExp(`^${escapeRegExp(key)}=.*$`, \"m\");\n if (re.test(next)) {\n next = next.replace(re, line);\n } else {\n if (next.length && !next.endsWith(\"\\n\")) next += \"\\n\";\n next += `${line}\\n`;\n }\n }\n\n await writeFile(filePath, next, \"utf8\");\n return filePath;\n}\n\n/** True if a file exists at `cwd/name`. */\nexport async function fileExists(cwd: string, name: string): Promise<boolean> {\n try {\n await stat(path.join(cwd, name));\n return true;\n } catch {\n return false;\n }\n}\n\nfunction escapeRegExp(s: string): string {\n return s.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n}\n","import { spawn } from \"node:child_process\";\nimport { readFile, readdir } from \"node:fs/promises\";\nimport { fileExists } from \"./env-file.js\";\n\nexport type PackageManager = \"pnpm\" | \"yarn\" | \"bun\" | \"npm\";\n\n/**\n * Detects the package manager from lockfiles (then the `npm_config_user_agent`\n * of the running process), defaulting to npm.\n */\nexport async function detectPackageManager(\n cwd: string,\n): Promise<PackageManager> {\n if (await fileExists(cwd, \"pnpm-lock.yaml\")) return \"pnpm\";\n if (await fileExists(cwd, \"yarn.lock\")) return \"yarn\";\n if (await fileExists(cwd, \"bun.lockb\")) return \"bun\";\n if (await fileExists(cwd, \"package-lock.json\")) return \"npm\";\n\n const ua = process.env.npm_config_user_agent ?? \"\";\n if (ua.startsWith(\"pnpm\")) return \"pnpm\";\n if (ua.startsWith(\"yarn\")) return \"yarn\";\n if (ua.startsWith(\"bun\")) return \"bun\";\n return \"npm\";\n}\n\n/** Detects a likely Node web framework for friendlier guidance. */\nexport async function detectFramework(cwd: string): Promise<string | null> {\n try {\n const pkg = JSON.parse(await readFile(`${cwd}/package.json`, \"utf8\")) as {\n dependencies?: Record<string, string>;\n devDependencies?: Record<string, string>;\n };\n const deps = { ...pkg.dependencies, ...pkg.devDependencies };\n if (deps.next) return \"Next.js\";\n if (deps.nuxt) return \"Nuxt\";\n if (deps[\"@remix-run/node\"] || deps[\"@remix-run/react\"]) return \"Remix\";\n if (deps.express) return \"Express\";\n if (deps.fastify) return \"Fastify\";\n if (deps.hono) return \"Hono\";\n if (deps[\"@sveltejs/kit\"]) return \"SvelteKit\";\n return null;\n } catch {\n return null;\n }\n}\n\nexport type Runtime = \"node\" | \"python\" | \"ruby\" | \"go\" | \"php\" | \"other\";\nexport type Stack = { runtime: Runtime; framework: string | null };\n\n/**\n * Detects the project's language/runtime so the CLI installs the right thing\n * (only Node has an SDK today) and prints instructions in the right language.\n * Node is checked first since it's this CLI's primary audience.\n */\nexport async function detectStack(cwd: string): Promise<Stack> {\n if (await fileExists(cwd, \"package.json\")) {\n return { runtime: \"node\", framework: await detectFramework(cwd) };\n }\n if (\n (await fileExists(cwd, \"pyproject.toml\")) ||\n (await fileExists(cwd, \"requirements.txt\")) ||\n (await fileExists(cwd, \"Pipfile\")) ||\n (await fileExists(cwd, \"setup.py\"))\n ) {\n return { runtime: \"python\", framework: await detectPythonFramework(cwd) };\n }\n if ((await fileExists(cwd, \"Gemfile\")) || (await hasGemspec(cwd))) {\n return { runtime: \"ruby\", framework: await detectRubyFramework(cwd) };\n }\n if (await fileExists(cwd, \"go.mod\")) return { runtime: \"go\", framework: null };\n if (await fileExists(cwd, \"composer.json\")) {\n return { runtime: \"php\", framework: await detectPhpFramework(cwd) };\n }\n return { runtime: \"other\", framework: null };\n}\n\n/** Human-friendly label for the detected stack (framework if known). */\nexport function stackLabel(stack: Stack): string {\n if (stack.framework) return stack.framework;\n switch (stack.runtime) {\n case \"node\":\n return \"a Node.js project\";\n case \"python\":\n return \"a Python project\";\n case \"ruby\":\n return \"a Ruby project\";\n case \"go\":\n return \"a Go project\";\n case \"php\":\n return \"a PHP project\";\n default:\n return \"your project\";\n }\n}\n\n/** The language name to tell the agent to write in. */\nexport function languageName(stack: Stack): string {\n switch (stack.runtime) {\n case \"node\":\n return \"TypeScript\";\n case \"python\":\n return \"Python\";\n case \"ruby\":\n return \"Ruby\";\n case \"go\":\n return \"Go\";\n case \"php\":\n return \"PHP\";\n default:\n return \"your language\";\n }\n}\n\nasync function readIf(cwd: string, name: string): Promise<string> {\n try {\n return await readFile(`${cwd}/${name}`, \"utf8\");\n } catch {\n return \"\";\n }\n}\n\nasync function hasGemspec(cwd: string): Promise<boolean> {\n try {\n return (await readdir(cwd)).some((f) => f.endsWith(\".gemspec\"));\n } catch {\n return false;\n }\n}\n\nasync function detectPythonFramework(cwd: string): Promise<string | null> {\n const txt = (\n (await readIf(cwd, \"requirements.txt\")) +\n (await readIf(cwd, \"pyproject.toml\")) +\n (await readIf(cwd, \"Pipfile\"))\n ).toLowerCase();\n if (txt.includes(\"django\")) return \"Django\";\n if (txt.includes(\"fastapi\")) return \"FastAPI\";\n if (txt.includes(\"flask\")) return \"Flask\";\n return null;\n}\n\nasync function detectRubyFramework(cwd: string): Promise<string | null> {\n const txt = (await readIf(cwd, \"Gemfile\")).toLowerCase();\n if (txt.includes(\"rails\")) return \"Rails\";\n if (txt.includes(\"sinatra\")) return \"Sinatra\";\n return null;\n}\n\nasync function detectPhpFramework(cwd: string): Promise<string | null> {\n const txt = (await readIf(cwd, \"composer.json\")).toLowerCase();\n if (txt.includes(\"laravel\")) return \"Laravel\";\n if (txt.includes(\"symfony\")) return \"Symfony\";\n return null;\n}\n\n/* -------------------------------------------------------------------------- */\n/* Non-Node SDK installs (letterapp on PyPI / RubyGems) */\n/* -------------------------------------------------------------------------- */\n\nconst SDK_PYPI = \"letterapp\";\nconst SDK_GEM = \"letterapp\";\n\nexport type SdkInstall = { cmd: string; args: string[]; display: string };\n\nfunction sdkInstall(cmd: string, args: string[]): SdkInstall {\n return { cmd, args, display: `${cmd} ${args.join(\" \")}` };\n}\n\n/**\n * Picks the right Python installer for the project: uv > poetry > pipenv > pip.\n * Returns the command we'd run to add `letterapp`.\n */\nexport async function pythonSdkInstall(cwd: string): Promise<SdkInstall> {\n if (await fileExists(cwd, \"uv.lock\")) return sdkInstall(\"uv\", [\"add\", SDK_PYPI]);\n const pyproject = (await readIf(cwd, \"pyproject.toml\")).toLowerCase();\n if (\n (await fileExists(cwd, \"poetry.lock\")) ||\n pyproject.includes(\"[tool.poetry]\")\n ) {\n return sdkInstall(\"poetry\", [\"add\", SDK_PYPI]);\n }\n if (await fileExists(cwd, \"Pipfile\")) {\n return sdkInstall(\"pipenv\", [\"install\", SDK_PYPI]);\n }\n return sdkInstall(\"pip\", [\"install\", SDK_PYPI]);\n}\n\n/** Bundler when there's a Gemfile, otherwise a plain `gem install`. */\nexport async function rubySdkInstall(cwd: string): Promise<SdkInstall> {\n if (await fileExists(cwd, \"Gemfile\")) {\n return sdkInstall(\"bundle\", [\"add\", SDK_GEM]);\n }\n return sdkInstall(\"gem\", [\"install\", SDK_GEM]);\n}\n\n/** Runs an SdkInstall command, streaming output. Resolves to the exit code. */\nexport function runSdkInstall(\n install: SdkInstall,\n cwd: string,\n): Promise<number> {\n return new Promise((resolve) => {\n const child = spawn(install.cmd, install.args, {\n cwd,\n stdio: \"inherit\",\n shell: process.platform === \"win32\",\n });\n child.on(\"error\", () => resolve(1));\n child.on(\"close\", (code) => resolve(code ?? 1));\n });\n}\n\nexport function installCommand(pm: PackageManager, pkg: string): string {\n switch (pm) {\n case \"pnpm\":\n return `pnpm add ${pkg}`;\n case \"yarn\":\n return `yarn add ${pkg}`;\n case \"bun\":\n return `bun add ${pkg}`;\n default:\n return `npm install ${pkg}`;\n }\n}\n\n/** Runs the install command, streaming output. Resolves to the exit code. */\nexport function runInstall(\n pm: PackageManager,\n pkg: string,\n cwd: string,\n): Promise<number> {\n const args = pm === \"npm\" ? [\"install\", pkg] : [\"add\", pkg];\n return new Promise((resolve) => {\n const child = spawn(pm, args, {\n cwd,\n stdio: \"inherit\",\n shell: process.platform === \"win32\",\n });\n child.on(\"error\", () => resolve(1));\n child.on(\"close\", (code) => resolve(code ?? 1));\n });\n}\n","import { Command } from \"commander\";\nimport {\n getBaseUrl,\n DEFAULT_BASE_URL,\n saveCredential,\n} from \"../config.js\";\nimport { startDeviceAuth, pollDeviceAuth } from \"../client.js\";\nimport { openUrl } from \"../browser.js\";\nimport { upsertEnv } from \"../env-file.js\";\nimport {\n detectPackageManager,\n detectStack,\n installCommand,\n languageName,\n pythonSdkInstall,\n rubySdkInstall,\n runInstall,\n runSdkInstall,\n stackLabel,\n type SdkInstall,\n type Stack,\n} from \"../pm.js\";\nimport {\n banner,\n c,\n cliCommand,\n log,\n printError,\n printInfo,\n printSuccess,\n printWarning,\n prompt,\n} from \"../output.js\";\n\nconst SDK_PACKAGE = \"@letterapp/node\";\nconst sleep = (ms: number) => new Promise((r) => setTimeout(r, ms));\n\n// Node projects use .env.local (Next.js convention, gitignored by default);\n// everything else uses the more universal .env.\nfunction envFileFor(stack: Stack): string {\n return stack.runtime === \"node\" ? \".env.local\" : \".env\";\n}\n\ninterface LoginOptions {\n open: boolean;\n install: boolean;\n yes?: boolean;\n baseUrl?: string;\n apiKey?: string;\n}\n\nexport function registerLoginCommand(program: Command) {\n program\n .command(\"login\", { isDefault: true })\n .alias(\"init\")\n .description(\"Connect this project to Letter (interactive device login)\")\n .option(\"--no-open\", \"Don't open the browser; print the URL to open manually\")\n .option(\"-y, --yes\", \"Non-interactive: don't wait for Enter (agents/CI)\")\n .option(\"--no-install\", \"Skip installing the SDK\")\n .option(\"--base-url <url>\", \"Target a self-hosted or local Letter instance\")\n .option(\n \"--api-key <key>\",\n \"CI only: write a key without the device flow (never use in chat)\",\n )\n .action(async (opts: LoginOptions) => {\n const code = await runLogin(opts);\n if (code !== 0) process.exitCode = code;\n });\n}\n\nasync function runLogin(opts: LoginOptions): Promise<number> {\n const base = getBaseUrl(opts.baseUrl);\n const cwd = process.cwd();\n const interactive = process.stdin.isTTY && !opts.yes;\n\n banner();\n\n // CI escape hatch: write the provided key non-interactively. Documented as\n // automation-only; interactive/agent use should rely on the device flow so\n // no secret passes through the command line or chat.\n if (opts.apiKey) {\n const envFile = envFileFor(await detectStack(cwd));\n const entries: Record<string, string> = { LETTER_API_KEY: opts.apiKey };\n if (base !== DEFAULT_BASE_URL) entries.LETTER_BASE_URL = base;\n const file = await upsertEnv(cwd, envFile, entries);\n printSuccess(`Saved LETTER_API_KEY to ${rel(cwd, file)} (--api-key).`);\n printWarning(\n `--api-key is for CI. For interactive setup, run \\`${cliCommand()} login\\`.`,\n );\n return 0;\n }\n\n // 1. Begin the flow.\n let flow;\n try {\n flow = await startDeviceAuth(base);\n } catch (err) {\n printError(err);\n return 1;\n }\n\n log(c.bold(\"Confirm this code in your browser:\"));\n log();\n log(\" \" + c.cyan(c.bold(flow.user_code)));\n log();\n\n // 2. Open the browser (interactive: wait for Enter; otherwise auto/print).\n if (interactive && opts.open) {\n await prompt(c.dim(\"Press Enter to open your browser… \"));\n }\n if (opts.open) openUrl(flow.verification_uri_complete);\n printInfo(\"If your browser didn't open, visit:\");\n log(\" \" + c.blue(flow.verification_uri_complete));\n log();\n printInfo(\"Waiting for you to approve… (Ctrl+C to cancel)\");\n\n // 3. Poll until the user approves/denies or the code expires.\n const deadline = Date.now() + flow.expires_in * 1000;\n let intervalMs = Math.max(1, flow.interval) * 1000;\n\n while (Date.now() < deadline) {\n await sleep(intervalMs);\n let res;\n try {\n res = await pollDeviceAuth(base, flow.device_code);\n } catch (err) {\n printError(err);\n return 1;\n }\n\n if (res.status === \"authorization_pending\") continue;\n if (res.status === \"slow_down\") {\n intervalMs += 1000;\n continue;\n }\n if (res.status === \"access_denied\") {\n printError(new Error(\"Request denied in the browser. Nothing was changed.\"));\n return 1;\n }\n if (res.status === \"expired_token\") {\n printError(new Error(\"This login expired. Run the command again to retry.\"));\n return 1;\n }\n\n return finish(res, cwd, opts.install);\n }\n\n printError(new Error(\"Timed out waiting for approval. Run the command again.\"));\n return 1;\n}\n\nasync function finish(\n approved: {\n api_key: string;\n pat: string;\n base_url: string;\n project: { slug: string; name: string };\n workspace?: { id: string; name: string };\n },\n cwd: string,\n doInstall: boolean,\n): Promise<number> {\n const { api_key: apiKey, pat, base_url: baseUrl, project, workspace } = approved;\n log();\n printSuccess(`Approved for project ${c.bold(project.name)}.`);\n\n const stack = await detectStack(cwd);\n const isNode = stack.runtime === \"node\";\n const envFile = envFileFor(stack);\n\n // Write the key to the project env (value never printed) + shared store.\n const entries: Record<string, string> = { LETTER_API_KEY: apiKey };\n if (baseUrl && baseUrl !== DEFAULT_BASE_URL) entries.LETTER_BASE_URL = baseUrl;\n const written = await upsertEnv(cwd, envFile, entries);\n printSuccess(`Saved LETTER_API_KEY to ${rel(cwd, written)}.`);\n\n try {\n const credFile = await saveCredential({\n apiKey,\n pat,\n baseUrl: baseUrl || DEFAULT_BASE_URL,\n project,\n workspace,\n savedAt: new Date().toISOString(),\n });\n printSuccess(`Stored credentials in ${tildify(credFile)} for tooling (MCP + CLI).`);\n } catch {\n printWarning(\"Could not write ~/.letter/credentials.json (continuing).\");\n }\n\n // Install the SDK for the detected language. Node, Python, and Ruby have\n // official SDKs; everything else integrates over the HTTP API. We capture a\n // one-line `sdkNote` per path and hand the agent a single, language-agnostic\n // prompt - the agent knows the repo and writes idiomatic calls itself.\n let sdkNote: string;\n if (isNode) {\n const pm = await detectPackageManager(cwd);\n if (doInstall) {\n printInfo(`Installing ${SDK_PACKAGE} with ${pm}…`);\n const code = await runInstall(pm, SDK_PACKAGE, cwd);\n if (code === 0) printSuccess(`Installed ${SDK_PACKAGE}.`);\n else printWarning(`Install failed. Run: ${installCommand(pm, SDK_PACKAGE)}`);\n } else {\n printInfo(`Skipped install. Run: ${installCommand(pm, SDK_PACKAGE)}`);\n }\n sdkNote = `the ${SDK_PACKAGE} SDK is installed`;\n } else if (stack.runtime === \"python\" || stack.runtime === \"ruby\") {\n const install =\n stack.runtime === \"python\"\n ? await pythonSdkInstall(cwd)\n : await rubySdkInstall(cwd);\n await installSdk(install, doInstall);\n sdkNote =\n stack.runtime === \"python\"\n ? \"the letterapp Python SDK is installed\"\n : \"the letterapp Ruby gem is installed\";\n } else {\n printInfo(\n `Detected ${stackLabel(stack)}. No ${languageName(stack)} SDK yet, so the agent should use the HTTP API.`,\n );\n sdkNote =\n `there's no official ${languageName(stack)} SDK, so call the HTTP ingestion ` +\n \"API directly (POST /v1/identify and /v1/track with \" +\n \"Authorization: Bearer LETTER_API_KEY and Content-Type: application/json)\";\n }\n\n printAgentHandoff(sdkNote, envFile, cliCommand());\n return 0;\n}\n\n/** Shared install-or-print step for the non-Node SDKs (letterapp). */\nasync function installSdk(install: SdkInstall, doInstall: boolean): Promise<void> {\n if (doInstall) {\n printInfo(`Installing letterapp (${install.display})…`);\n const code = await runSdkInstall(install, process.cwd());\n if (code === 0) printSuccess(\"Installed letterapp.\");\n else printWarning(`Install failed. Run: ${install.display}`);\n } else {\n printInfo(`Skipped install. Run: ${install.display}`);\n }\n}\n\nconst AGENT_DOCS = \"https://letter.app/docs/agent-setup\";\n\n/**\n * Language-agnostic handoff for the last mile. The CLI does the mechanical setup\n * (key in the env, SDK installed where one exists) but deliberately does NOT\n * edit source - the coding agent has the repo context to write idiomatic calls.\n * So we print one ready-to-paste prompt that works for any stack plus a link to\n * the full guide, instead of language-specific snippets.\n */\nfunction printAgentHandoff(\n sdkNote: string,\n envFile: string,\n verifyCmd: string,\n): void {\n const rule = \" \" + \"─\".repeat(68);\n log();\n log(c.bold(\"Next: finish setup with your coding agent\"));\n log();\n log(c.dim(\" Paste this to your agent (Cursor, Claude Code, Codex, OpenClaw, Hermes, …):\"));\n log(c.dim(rule));\n // Plain (uncolored) so it copies cleanly into an agent prompt.\n log(\" Integrate Letter (product email automation) into this app. The CLI\");\n log(` already did setup: LETTER_API_KEY is in ${envFile} and ${sdkNote}.`);\n log(\" Using the right idioms for this codebase:\");\n log(\" - create a shared, server-side Letter client that reads\");\n log(\" LETTER_API_KEY from the environment (only set baseUrl/base_url\");\n log(\" when LETTER_BASE_URL is present),\");\n log(\" - call identify({ userId, email }) where users sign up or log in,\");\n log(\" - call track({ userId, event }) on the 2-3 most important actions,\");\n log(\" - flush before serverless handlers or scripts exit.\");\n log(\" Never print or commit the key. Then verify with the command below.\");\n log(` Full guide: ${AGENT_DOCS}`);\n log(c.dim(rule));\n log();\n log(c.dim(` Prefer to wire it up yourself? Same guide: ${AGENT_DOCS}`));\n log();\n log(c.dim(\"Verify it landed: \") + c.bold(`${verifyCmd} status`));\n log(\n c.dim(\n `Your API key is in ${envFile} - keep it out of source control, and don't echo its value.`,\n ),\n );\n log();\n}\n\nfunction rel(cwd: string, file: string): string {\n return file.startsWith(cwd) ? file.slice(cwd.length + 1) || file : file;\n}\n\nfunction tildify(file: string): string {\n const home = process.env.HOME || process.env.USERPROFILE;\n return home && file.startsWith(home) ? `~${file.slice(home.length)}` : file;\n}\n","import { Command } from \"commander\";\nimport { readCredential, clearCredential } from \"../config.js\";\nimport { c, cliCommand, isJsonMode, printJson, printInfo, printSuccess } from \"../output.js\";\n\nfunction mask(key: string): string {\n return key.length > 12 ? key.slice(0, 8) + \"…\" + key.slice(-4) : \"set\";\n}\n\nexport function registerAuthCommands(program: Command) {\n const auth = program.command(\"auth\").description(\"Manage the stored Letter connection\");\n\n auth\n .command(\"status\")\n .description(\"Show whether this machine is connected to Letter\")\n .action(async () => {\n const cred = await readCredential();\n const envKey = process.env.LETTER_API_KEY;\n\n if (!cred && !envKey) {\n if (isJsonMode()) printJson({ connected: false });\n else printInfo(\"Not connected. Run \" + c.bold(`${cliCommand()} login`) + \" to set up.\");\n return;\n }\n\n if (isJsonMode()) {\n printJson({\n connected: true,\n source: envKey ? \"env\" : \"credentials\",\n project: cred?.project ?? null,\n base_url: cred?.baseUrl ?? null,\n key: envKey ? \"env\" : cred ? mask(cred.apiKey) : null,\n });\n return;\n }\n\n printSuccess(\"Connected\" + (cred ? ` to ${c.bold(cred.project.name)}` : \"\"));\n if (cred) {\n console.log(c.dim(\" Project: \") + cred.project.slug);\n console.log(c.dim(\" Key: \") + mask(cred.apiKey));\n console.log(c.dim(\" API: \") + cred.baseUrl);\n }\n if (envKey) console.log(c.dim(\" LETTER_API_KEY is set in the environment.\"));\n });\n\n auth\n .command(\"logout\")\n .description(\"Remove the stored credential (~/.letter/credentials.json)\")\n .action(async () => {\n await clearCredential();\n printSuccess(\"Removed stored credential.\");\n });\n}\n","import { Command } from \"commander\";\nimport { client } from \"../client.js\";\nimport { readCredential } from \"../config.js\";\nimport { c, isJsonMode, printError, printInfo, printJson, printSuccess, spinner } from \"../output.js\";\n\ntype StatusResponse = {\n contacts?: number;\n events?: number;\n [k: string]: unknown;\n};\n\nexport function registerStatusCommand(program: Command) {\n program\n .command(\"status\")\n .description(\"Check whether your project has received any contacts or events\")\n .action(async () => {\n const spin = spinner(\"Checking your Letter project…\").start();\n try {\n const { data } = await client.get<StatusResponse>(\"/v1/status\");\n spin.stop();\n\n if (isJsonMode()) {\n printJson(data);\n return;\n }\n\n const cred = await readCredential();\n const contacts = data.contacts ?? 0;\n const events = data.events ?? 0;\n\n if (cred) printInfo(`Project ${c.bold(cred.project.name)}`);\n if (contacts > 0 || events > 0) {\n printSuccess(`Connected. ${contacts} contact(s), ${events} event(s) received.`);\n } else {\n printInfo(\"Connected, but no data yet. Fire your first identify/track call.\");\n }\n } catch (err) {\n spin.stop();\n printError(err);\n process.exitCode = 1;\n }\n });\n}\n","import { Command } from \"commander\";\nimport {\n getBaseUrl,\n setBaseUrl,\n getConfigPath,\n resetConfig,\n} from \"../config.js\";\nimport { c, isJsonMode, printError, printJson, printSuccess } from \"../output.js\";\n\nexport function registerConfigCommands(program: Command) {\n const cfg = program.command(\"config\").description(\"Manage CLI configuration\");\n\n cfg\n .command(\"set\")\n .description(\"Set a config value\")\n .argument(\"<key>\", \"Config key: base-url\")\n .argument(\"<value>\", \"Value to set\")\n .action((key: string, value: string) => {\n switch (key) {\n case \"base-url\":\n setBaseUrl(value);\n printSuccess(`Base URL set to ${getBaseUrl()}`);\n break;\n default:\n printError(new Error(`Unknown config key: ${key}. Valid keys: base-url`));\n process.exitCode = 1;\n }\n });\n\n cfg\n .command(\"get\")\n .description(\"Show CLI configuration\")\n .argument(\"[key]\", \"Config key (omit to show all)\")\n .action((key?: string) => {\n const all = { base_url: getBaseUrl(), config_path: getConfigPath() };\n if (key && key !== \"base-url\" && key !== \"path\") {\n printError(new Error(`Unknown config key: ${key}`));\n process.exitCode = 1;\n return;\n }\n if (isJsonMode()) {\n printJson(key === \"base-url\" ? { base_url: all.base_url } : key === \"path\" ? { config_path: all.config_path } : all);\n return;\n }\n if (key === \"base-url\") console.log(all.base_url);\n else if (key === \"path\") console.log(all.config_path);\n else for (const [k, v] of Object.entries(all)) console.log(c.bold(k + \":\") + \" \" + v);\n });\n\n cfg\n .command(\"reset\")\n .description(\"Reset CLI configuration to defaults\")\n .action(() => {\n resetConfig();\n printSuccess(\"Configuration reset.\");\n });\n}\n","import { readFile } from \"node:fs/promises\";\nimport { Command } from \"commander\";\nimport { mgmt } from \"../client.js\";\nimport { resolveProjectSlug } from \"../config.js\";\nimport {\n c,\n cliCommand,\n emit,\n isJsonMode,\n log,\n printError,\n printInfo,\n printJson,\n printSuccess,\n} from \"../output.js\";\nimport { markdownToTipTap } from \"../markdown.js\";\n\n/**\n * Resource command groups for the management API. Each group wraps the same\n * REST endpoints the dashboard uses (`/v1/...`), so the whole app is operable\n * from the terminal. Everything honors the global `--json` flag (see\n * `output.emit`) and project-scoped groups take `--project <slug>` (defaulting\n * to the project connected by `letter login`).\n *\n * The shape is data-driven: one `ResourceSpec` per resource, expanded into\n * `list/get/create/update/delete` + custom verb subcommands by `register()`.\n * Keeping it declarative means a new endpoint is a few lines, not a new file.\n */\n\ntype Field = {\n /** commander option flag, e.g. \"--name <name>\". */\n flag: string;\n /** request-body key, e.g. \"name\". */\n key: string;\n /** parse the value as JSON (supports `@file.json` to read from disk). */\n json?: boolean;\n /** coerce to a number. */\n number?: boolean;\n /** coerce to a boolean. */\n boolean?: boolean;\n /** (multipart only) read the value as a file path and upload its bytes. */\n file?: boolean;\n description?: string;\n};\n\ntype Action = {\n name: string;\n describe: string;\n method: \"get\" | \"post\" | \"put\" | \"patch\" | \"delete\";\n /** Path suffix appended after the resource (and id, if `needsId`). */\n suffix: string;\n /** Whether the verb operates on a single item (default true). */\n needsId?: boolean;\n fields?: Field[];\n /** Query params for GET verbs. */\n query?: Field[];\n /** Send fields as multipart/form-data (for file uploads). */\n multipart?: boolean;\n};\n\ntype ResourceSpec = {\n /** Command group name (also the default path segment). */\n name: string;\n describe: string;\n /** Nested under /v1/projects/{slug} when true. */\n scoped: boolean;\n /** Path segment if different from `name`. */\n segment?: string;\n /** Hidden back-compat alias for the group name. */\n alias?: string;\n /** Positional id arg name (e.g. \"externalId\", \"email\"). Default \"id\". */\n idName?: string;\n list?: { paginated?: boolean; query?: Field[] };\n get?: boolean;\n create?: Field[];\n update?: Field[];\n remove?: boolean;\n actions?: Action[];\n};\n\nasync function readValue(raw: string): Promise<string> {\n if (raw.startsWith(\"@\")) return (await readFile(raw.slice(1), \"utf8\")).trim();\n return raw;\n}\n\nasync function buildBody(\n fields: Field[],\n opts: Record<string, unknown>,\n): Promise<Record<string, unknown>> {\n const body: Record<string, unknown> = {};\n for (const f of fields) {\n const v = opts[camel(f.key)];\n if (v === undefined) continue;\n if (f.json) {\n body[f.key] = JSON.parse(await readValue(String(v)));\n } else if (f.number) {\n body[f.key] = Number(v);\n } else if (f.boolean) {\n body[f.key] = v === true || v === \"true\";\n } else {\n body[f.key] = await readValue(String(v));\n }\n }\n return body;\n}\n\nasync function buildForm(\n fields: Field[],\n opts: Record<string, unknown>,\n): Promise<FormData> {\n const form = new FormData();\n for (const f of fields) {\n const v = opts[camel(f.key)];\n if (v === undefined) continue;\n if (f.file) {\n const path = String(v);\n const bytes = await readFile(path);\n const name = path.split(\"/\").pop() || f.key;\n form.append(f.key, new Blob([new Uint8Array(bytes)]), name);\n } else if (f.json) {\n // The server parses these fields with JSON.parse; keep them as strings.\n form.append(f.key, await readValue(String(v)));\n } else {\n form.append(f.key, String(v));\n }\n }\n return form;\n}\n\nfunction buildQuery(\n fields: Field[],\n opts: Record<string, unknown>,\n): Record<string, string | number | undefined> {\n const q: Record<string, string | number | undefined> = {};\n for (const f of fields) {\n const v = opts[camel(f.key)];\n if (v === undefined) continue;\n q[f.key] = f.number ? Number(v) : String(v);\n }\n return q;\n}\n\n/** \"external-id\" / \"external_id\" → \"externalId\" (commander camelCases flags). */\nfunction camel(key: string): string {\n return key.replace(/[-_]([a-z])/g, (_, ch: string) => ch.toUpperCase());\n}\n\nfunction applyFields(cmd: Command, fields: Field[]) {\n for (const f of fields) {\n cmd.option(f.flag, f.description ?? f.key);\n }\n}\n\nasync function basePath(spec: ResourceSpec, project?: string): Promise<string> {\n const seg = spec.segment ?? spec.name;\n if (!spec.scoped) return `/v1/${seg}`;\n const slug = await resolveProjectSlug(project);\n return `/v1/projects/${slug}/${seg}`;\n}\n\nfunction withProject(cmd: Command, scoped: boolean): Command {\n if (scoped) {\n cmd.option(\"-p, --project <slug>\", \"Project slug (default: connected project)\");\n }\n return cmd;\n}\n\nfunction fail(err: unknown) {\n printError(err);\n process.exitCode = 1;\n}\n\nfunction register(program: Command, spec: ResourceSpec) {\n const group = program.command(spec.name).description(spec.describe);\n if (spec.alias) group.alias(spec.alias);\n const idName = spec.idName ?? \"id\";\n\n if (spec.list) {\n const cmd = withProject(\n group.command(\"list\").description(`List ${spec.name}`),\n spec.scoped,\n );\n if (spec.list.paginated) {\n cmd.option(\"--limit <n>\", \"Max items (1-100)\");\n cmd.option(\"--cursor <cursor>\", \"Pagination cursor\");\n }\n if (spec.list.query) applyFields(cmd, spec.list.query);\n cmd.action(async (opts) => {\n try {\n const query: Record<string, string | number | undefined> = {};\n if (spec.list?.paginated) {\n if (opts.limit) query.limit = Number(opts.limit);\n if (opts.cursor) query.cursor = opts.cursor;\n }\n if (spec.list?.query) Object.assign(query, buildQuery(spec.list.query, opts));\n const { data } = await mgmt.get(await basePath(spec, opts.project), query);\n emit(data);\n } catch (err) {\n fail(err);\n }\n });\n }\n\n if (spec.get) {\n const cmd = withProject(\n group\n .command(\"get\")\n .description(`Get a ${spec.name} by ${idName}`)\n .argument(`<${idName}>`, idName),\n spec.scoped,\n );\n cmd.action(async (idValue: string, opts) => {\n try {\n const path = `${await basePath(spec, opts.project)}/${encodeURIComponent(idValue)}`;\n const { data } = await mgmt.get(path);\n emit(data);\n } catch (err) {\n fail(err);\n }\n });\n }\n\n if (spec.create) {\n const cmd = withProject(\n group.command(\"create\").description(`Create a ${spec.name}`),\n spec.scoped,\n );\n applyFields(cmd, spec.create);\n cmd.action(async (opts) => {\n try {\n const body = await buildBody(spec.create!, opts);\n const { data } = await mgmt.post(await basePath(spec, opts.project), body);\n emit(data);\n } catch (err) {\n fail(err);\n }\n });\n }\n\n if (spec.update) {\n const cmd = withProject(\n group\n .command(\"update\")\n .description(`Update a ${spec.name}`)\n .argument(`<${idName}>`, idName),\n spec.scoped,\n );\n applyFields(cmd, spec.update);\n cmd.action(async (idValue: string, opts) => {\n try {\n const body = await buildBody(spec.update!, opts);\n const path = `${await basePath(spec, opts.project)}/${encodeURIComponent(idValue)}`;\n const { data } = await mgmt.patch(path, body);\n emit(data);\n } catch (err) {\n fail(err);\n }\n });\n }\n\n if (spec.remove) {\n const cmd = withProject(\n group\n .command(\"delete\")\n .description(`Delete a ${spec.name}`)\n .argument(`<${idName}>`, idName),\n spec.scoped,\n );\n cmd.action(async (idValue: string, opts) => {\n try {\n const path = `${await basePath(spec, opts.project)}/${encodeURIComponent(idValue)}`;\n await mgmt.delete(path);\n printSuccess(`Deleted ${spec.name.replace(/s$/, \"\")} ${idValue}.`);\n } catch (err) {\n fail(err);\n }\n });\n }\n\n for (const action of spec.actions ?? []) {\n const needsId = action.needsId ?? true;\n const cmd = withProject(\n needsId\n ? group\n .command(action.name)\n .description(action.describe)\n .argument(`<${idName}>`, idName)\n : group.command(action.name).description(action.describe),\n spec.scoped,\n );\n if (action.fields) applyFields(cmd, action.fields);\n if (action.query) applyFields(cmd, action.query);\n\n const handler = async (...args: unknown[]) => {\n // commander passes (idValue?, opts, command). Last arg is the Command.\n const opts = args[args.length - 2] as Record<string, unknown>;\n const idValue = needsId ? (args[0] as string) : undefined;\n try {\n let path = await basePath(spec, opts.project as string | undefined);\n if (needsId) path += `/${encodeURIComponent(idValue!)}`;\n path += action.suffix;\n\n if (action.method === \"get\") {\n const query = action.query ? buildQuery(action.query, opts) : undefined;\n const { data } = await mgmt.get(path, query);\n emit(data);\n return;\n }\n if (action.multipart && action.fields) {\n const form = await buildForm(action.fields, opts);\n const { data, status } = await mgmt.request(\n action.method.toUpperCase(),\n path,\n { form },\n );\n if (status === 204) printSuccess(`${action.name} ok.`);\n else emit(data);\n return;\n }\n const body = action.fields ? await buildBody(action.fields, opts) : undefined;\n const { data, status } = await mgmt.request(\n action.method.toUpperCase(),\n path,\n { body },\n );\n if (status === 204) printSuccess(`${action.name} ok.`);\n else emit(data);\n } catch (err) {\n fail(err);\n }\n };\n cmd.action(handler);\n }\n}\n\nconst cursor = { paginated: true } as const;\n\nconst SPECS: ResourceSpec[] = [\n // -- Workspace ------------------------------------------------------------\n {\n name: \"projects\",\n describe: \"Manage projects\",\n scoped: false,\n idName: \"slug\",\n list: {},\n get: true,\n create: [\n { flag: \"--name <name>\", key: \"name\" },\n { flag: \"--timezone <tz>\", key: \"timezone\" },\n ],\n update: [\n { flag: \"--name <name>\", key: \"name\" },\n { flag: \"--timezone <tz>\", key: \"timezone\" },\n ],\n remove: true,\n },\n {\n name: \"members\",\n describe: \"Manage workspace members\",\n scoped: false,\n idName: \"userId\",\n list: {},\n create: [\n { flag: \"--email <email>\", key: \"email\" },\n { flag: \"--role <role>\", key: \"role\", description: \"member | admin\" },\n ],\n remove: true,\n },\n {\n name: \"invitations\",\n describe: \"Manage workspace invitations\",\n scoped: false,\n list: {},\n create: [\n { flag: \"--email <email>\", key: \"email\" },\n { flag: \"--role <role>\", key: \"role\", description: \"member | admin\" },\n ],\n remove: true,\n },\n {\n // Personal access tokens (lt_pat_*) authenticate the CLI / Management API.\n // The dashboard calls these \"API keys\"; `tokens` stays as a hidden alias.\n name: \"api-keys\",\n alias: \"tokens\",\n segment: \"tokens\",\n describe: \"Manage your personal access tokens (lt_pat_*, the CLI/API credential)\",\n scoped: false,\n list: {},\n create: [\n { flag: \"--name <name>\", key: \"name\" },\n {\n flag: \"--expires-at <iso>\",\n key: \"expires_at\",\n description: \"Expiry as an ISO 8601 datetime (default: never)\",\n },\n ],\n remove: true,\n },\n // -- Contacts & data ------------------------------------------------------\n {\n name: \"contacts\",\n describe: \"Manage contacts\",\n scoped: true,\n idName: \"externalId\",\n list: { ...cursor },\n get: true,\n actions: [\n {\n name: \"suppress\",\n describe: \"Suppress a contact\",\n method: \"post\",\n suffix: \"/suppress\",\n },\n {\n name: \"resubscribe\",\n describe: \"Resubscribe a contact\",\n method: \"post\",\n suffix: \"/resubscribe\",\n },\n {\n name: \"import\",\n describe: \"Upload a CSV import (--file <path> --mapping <json|@file>)\",\n method: \"post\",\n needsId: false,\n suffix: \"/imports\",\n multipart: true,\n fields: [\n { flag: \"--file <path>\", key: \"file\", file: true },\n { flag: \"--mapping <json>\", key: \"mapping\", json: true },\n { flag: \"--dedupe <strategy>\", key: \"dedupe\", description: \"update | skip\" },\n { flag: \"--row-count <n>\", key: \"rowCount\" },\n ],\n },\n ],\n },\n {\n name: \"accounts\",\n describe: \"Inspect accounts\",\n scoped: true,\n idName: \"externalId\",\n list: { ...cursor },\n get: true,\n },\n {\n name: \"events\",\n describe: \"List events\",\n scoped: true,\n list: { ...cursor, query: [{ flag: \"--name <name>\", key: \"name\" }] },\n },\n {\n name: \"suppressions\",\n describe: \"Manage the suppression list\",\n scoped: true,\n idName: \"email\",\n list: { ...cursor },\n create: [{ flag: \"--email <email>\", key: \"email\" }],\n remove: true,\n },\n {\n name: \"segments\",\n describe: \"Manage segments\",\n scoped: true,\n list: {},\n get: true,\n create: [\n { flag: \"--name <name>\", key: \"name\" },\n { flag: \"--filter <json>\", key: \"filter\", json: true },\n ],\n update: [\n { flag: \"--name <name>\", key: \"name\" },\n { flag: \"--filter <json>\", key: \"filter\", json: true },\n ],\n remove: true,\n actions: [\n {\n name: \"preview\",\n describe: \"Count contacts matching a filter (--filter <json|@file>)\",\n method: \"post\",\n needsId: false,\n suffix: \"/preview\",\n fields: [{ flag: \"--filter <json>\", key: \"filter\", json: true }],\n },\n ],\n },\n // -- Sequences ------------------------------------------------------------\n {\n name: \"sequences\",\n describe: \"Manage drip sequences\",\n scoped: true,\n list: {},\n get: true,\n create: [{ flag: \"--name <name>\", key: \"name\" }],\n update: [\n { flag: \"--name <name>\", key: \"name\" },\n { flag: \"--status <status>\", key: \"status\", description: \"active | archived\" },\n ],\n remove: true,\n // `draft`, `preview`, `test-email`, `validate`, `schema`, and `scaffold`\n // are hand-written in `attachSequenceExtras` (they need draft-aware\n // behaviour the generic machinery can't express).\n actions: [\n {\n name: \"publish\",\n describe: \"Publish the current draft\",\n method: \"post\",\n suffix: \"/publish\",\n },\n {\n name: \"activity\",\n describe: \"Show enrollment activity\",\n method: \"get\",\n suffix: \"/activity\",\n },\n {\n name: \"versions\",\n describe: \"List published versions\",\n method: \"get\",\n suffix: \"/versions\",\n },\n ],\n },\n // -- Broadcasts -----------------------------------------------------------\n {\n name: \"broadcasts\",\n describe: \"Manage broadcasts\",\n scoped: true,\n list: {},\n get: true,\n create: [{ flag: \"--name <name>\", key: \"name\" }],\n update: [\n { flag: \"--name <name>\", key: \"name\" },\n { flag: \"--subject <subject>\", key: \"subject\" },\n { flag: \"--preview <preview>\", key: \"preview\" },\n { flag: \"--audience <json>\", key: \"audience\", json: true },\n { flag: \"--template-id <id>\", key: \"template_id\" },\n { flag: \"--body-doc <json>\", key: \"body_doc\", json: true },\n ],\n remove: true,\n actions: [\n {\n name: \"preflight\",\n describe: \"Validate before sending\",\n method: \"post\",\n suffix: \"/preflight\",\n },\n {\n name: \"schedule\",\n describe: \"Schedule (--scheduled-at <iso>) or send now (omit)\",\n method: \"post\",\n suffix: \"/schedule\",\n fields: [{ flag: \"--scheduled-at <iso>\", key: \"scheduled_at\" }],\n },\n {\n name: \"cancel\",\n describe: \"Cancel a scheduled or sending broadcast\",\n method: \"post\",\n suffix: \"/cancel\",\n },\n {\n name: \"live\",\n describe: \"Live status, stats, and recent activity\",\n method: \"get\",\n suffix: \"/live\",\n },\n ],\n },\n // -- Templates ------------------------------------------------------------\n {\n name: \"templates\",\n describe: \"Manage email templates\",\n scoped: true,\n list: {},\n get: true,\n create: [\n { flag: \"--name <name>\", key: \"name\" },\n { flag: \"--design <json>\", key: \"design\", json: true },\n ],\n update: [\n { flag: \"--name <name>\", key: \"name\" },\n { flag: \"--design <json>\", key: \"design\", json: true },\n ],\n remove: true,\n actions: [\n {\n name: \"default\",\n describe: \"Set as the project default\",\n method: \"post\",\n suffix: \"/default\",\n },\n {\n name: \"reset\",\n describe: \"Reset design to a preset (--preset plain|branded)\",\n method: \"post\",\n suffix: \"/reset\",\n fields: [{ flag: \"--preset <preset>\", key: \"preset\" }],\n },\n {\n name: \"logo\",\n describe: \"Upload a logo (--file <path>)\",\n method: \"post\",\n suffix: \"/logo\",\n multipart: true,\n fields: [{ flag: \"--file <path>\", key: \"file\", file: true }],\n },\n {\n name: \"remove-logo\",\n describe: \"Remove the template logo\",\n method: \"delete\",\n suffix: \"/logo\",\n },\n ],\n },\n // -- Settings -------------------------------------------------------------\n {\n name: \"domains\",\n describe: \"Manage sending domains\",\n scoped: true,\n list: {},\n create: [{ flag: \"--domain <domain>\", key: \"domain\" }],\n remove: true,\n actions: [\n {\n name: \"verify\",\n describe: \"Re-check DKIM verification\",\n method: \"post\",\n suffix: \"/verify\",\n },\n ],\n },\n {\n // Project ingestion keys (lt_live_*) authenticate the SDKs / ingestion API.\n // The dashboard calls these \"Project tokens\"; `keys` stays as a hidden alias.\n name: \"project-tokens\",\n alias: \"keys\",\n segment: \"keys\",\n describe: \"Manage project ingestion keys (lt_live_*, the SDK/ingestion credential)\",\n scoped: true,\n list: {},\n create: [{ flag: \"--name <name>\", key: \"name\" }],\n remove: true,\n },\n];\n\n/**\n * Canonical sequence shapes, printed by `sequences schema`. This is the\n * reference an agent reaches for instead of reverse-engineering the graph\n * from 400s. Mirrors the strict server validation + the /docs/sequences guide.\n */\nconst SEQUENCE_SCHEMA = {\n trigger: {\n event: {\n type: \"event\",\n eventName: \"modification_limit_reached\",\n oncePerContact: true,\n \"filter?\": \"FilterSpec — optional audience filter on the contact's traits/history\",\n \"where?\":\n \"Condition(s) on the firing event's own properties, e.g. { property: 'plan', op: 'eq', value: 'free' } (single object or array, AND-ed)\",\n },\n contact_created: {\n type: \"contact_created\",\n \"filter?\": \"FilterSpec — optional audience filter on the contact\",\n },\n },\n nodeConfigs: {\n trigger: { type: \"trigger\" },\n email: {\n type: \"email\",\n subject: \"You hit your limit\",\n \"preview?\": \"Here's how to lift it\",\n \"bodyDoc?\": { type: \"doc\", content: [\"…TipTap nodes…\"] },\n \"templateId?\": \"uuid | null (omit for project default)\",\n },\n wait: { type: \"wait\", duration: { value: 1, unit: \"minutes | hours | days\" } },\n wait_event: {\n type: \"wait_event\",\n eventName: \"completed_onboarding\",\n timeout: { value: 7, unit: \"minutes | hours | days\" },\n },\n branch: {\n type: \"branch\",\n condition: {\n event: { kind: \"event\", eventName: \"Upgraded\", occurred: true, \"window?\": { value: 2, unit: \"days\" } },\n trait: { kind: \"trait\", scope: \"contact | account\", path: \"plan\", op: \"eq | neq | contains | not_contains | exists | not_exists\", \"value?\": \"pro\" },\n },\n },\n exit: { type: \"exit\", \"reason?\": \"done\" },\n },\n edge: {\n id: \"e1\",\n source: \"trigger\",\n target: \"email1\",\n \"branch?\":\n \"yes | no (branch node) · received | timeout (wait_event node) — omit for single-leg nodes\",\n },\n notes: [\n \"config.type must equal the node's type.\",\n \"The email body field is `bodyDoc` (camelCase); a wait nests `duration`; a wait_event nests `timeout`.\",\n \"Draft saves reject unknown / mis-cased keys with a 400.\",\n \"Trait/filter values compare as strings: use value:\\\"false\\\" (not false) for a boolean trait. `letter traits list` shows keys + types.\",\n \"Event triggers can also gate on the event's own properties via `where` (e.g. only plan=free).\",\n \"wait_event pauses the contact until `eventName` fires (the `received` leg) or `timeout` elapses (the `timeout` leg). Only events that occur after the contact reaches the step count.\",\n \"Publish requires: one trigger, every node reachable, every path ends at an exit, each branch has one yes + one no edge, each wait_event has one received + one timeout edge, and each email has a subject + non-empty bodyDoc.\",\n \"`sequences validate <id>` dry-runs those publish checks; `sequences scaffold` builds a valid trigger → [wait] → email → exit graph in one command.\",\n ],\n example: {\n trigger: {\n type: \"event\",\n eventName: \"modification_limit_reached\",\n oncePerContact: true,\n filter: { kind: \"trait\", path: \"plan\", op: \"eq\", value: \"free\" },\n },\n graph: {\n nodes: [\n { id: \"trigger\", type: \"trigger\", position: { x: 280, y: 80 }, config: { type: \"trigger\" } },\n { id: \"wait1\", type: \"wait\", position: { x: 280, y: 200 }, config: { type: \"wait\", duration: { value: 1, unit: \"hours\" } } },\n {\n id: \"email1\",\n type: \"email\",\n position: { x: 280, y: 320 },\n config: {\n type: \"email\",\n subject: \"You hit your limit\",\n bodyDoc: { type: \"doc\", content: [{ type: \"paragraph\", content: [{ type: \"text\", text: \"Upgrade to keep going.\" }] }] },\n },\n },\n { id: \"exit1\", type: \"exit\", position: { x: 280, y: 440 }, config: { type: \"exit\" } },\n ],\n edges: [\n { id: \"e1\", source: \"trigger\", target: \"wait1\" },\n { id: \"e2\", source: \"wait1\", target: \"email1\" },\n { id: \"e3\", source: \"email1\", target: \"exit1\" },\n ],\n },\n },\n} as const;\n\nfunction printSequenceSchemaHuman() {\n log(\"Sequence authoring shapes (see /docs/sequences for the full guide).\\n\");\n log(\"Trigger (how contacts enter):\");\n log(JSON.stringify(SEQUENCE_SCHEMA.trigger, null, 2));\n log(\"\\nNode configs (config.type must match the node type):\");\n log(JSON.stringify(SEQUENCE_SCHEMA.nodeConfigs, null, 2));\n log(\"\\nEdge:\");\n log(JSON.stringify(SEQUENCE_SCHEMA.edge, null, 2));\n log(\"\\nNotes:\");\n for (const n of SEQUENCE_SCHEMA.notes) log(` • ${n}`);\n log(\"\\nKnown-good example (trigger + graph):\");\n log(JSON.stringify(SEQUENCE_SCHEMA.example, null, 2));\n log(`\\nScaffold one in a single command:`);\n log(\n ` ${cliCommand()} sequences scaffold --name \"Limit reached\" \\\\\\n` +\n ` --event modification_limit_reached --audience-trait plan=free \\\\\\n` +\n ` --wait 1h --subject \"You hit your limit\" \\\\\\n` +\n ` --body-md \"Upgrade to **keep going**.\" --publish`,\n );\n}\n\n/** Parse a human duration like `30m`, `1h`, `2d` into the wait/branch shape. */\nfunction parseDuration(raw: string): {\n value: number;\n unit: \"minutes\" | \"hours\" | \"days\";\n} {\n const m = /^(\\d+)\\s*(m|min|mins|minute|minutes|h|hr|hrs|hour|hours|d|day|days)$/i.exec(\n raw.trim(),\n );\n if (!m) {\n throw new Error(`Invalid --wait \"${raw}\". Use e.g. 30m, 1h, or 2d.`);\n }\n const u = (m[2] ?? \"\").toLowerCase();\n const unit = u.startsWith(\"d\") ? \"days\" : u.startsWith(\"h\") ? \"hours\" : \"minutes\";\n return { value: Number(m[1]), unit };\n}\n\n/** Trait shorthand `path=value` (also `!=` and `~` for contains). Values are\n * always strings — Letter compares trait values as strings, so `isPro=false`\n * matches the boolean trait `false`. Use `--audience @file.json` for the full\n * FilterSpec (exists / groups / event history). */\nfunction parseAudienceTrait(raw: string): Record<string, unknown> {\n const m = /^([^=!~]+?)\\s*(!=|~|=)\\s*(.*)$/.exec(raw);\n if (!m) {\n throw new Error(\n \"--audience-trait must be path=value (also path!=value or path~value), e.g. plan=free\",\n );\n }\n const op = m[2] === \"!=\" ? \"neq\" : m[2] === \"~\" ? \"contains\" : \"eq\";\n return { kind: \"trait\", path: (m[1] ?? \"\").trim(), op, value: m[3] ?? \"\" };\n}\n\nfunction attachSequenceExtras(program: Command) {\n const seq = program.commands.find((cmd) => cmd.name() === \"sequences\");\n if (!seq) return;\n\n seq\n .command(\"draft <id>\")\n .description(\n \"Save the draft graph + trigger (auto-fetches the current revision unless --expected-revision is given)\",\n )\n .option(\"--graph <json>\", \"Graph JSON (inline or @file.json)\")\n .option(\"--trigger <json>\", \"Trigger JSON (inline or @file.json)\")\n .option(\n \"--expected-revision <n>\",\n \"Optimistic-concurrency revision (default: current; use a number to assert)\",\n )\n .option(\"-p, --project <slug>\", \"Project slug (default: connected project)\")\n .action(async (id: string, opts: Record<string, unknown>) => {\n try {\n const slug = await resolveProjectSlug(opts.project as string | undefined);\n const base = `/v1/projects/${slug}/sequences`;\n const body: Record<string, unknown> = {};\n if (opts.graph !== undefined) {\n body.graph = JSON.parse(await readValue(String(opts.graph)));\n }\n if (opts.trigger !== undefined) {\n body.trigger = JSON.parse(await readValue(String(opts.trigger)));\n }\n const explicit = opts.expectedRevision;\n if (explicit !== undefined && String(explicit) !== \"auto\") {\n body.expected_revision = Number(explicit);\n } else {\n const { data } = await mgmt.get(`${base}/${encodeURIComponent(id)}`);\n body.expected_revision =\n (data as { draft_revision?: number }).draft_revision ?? 0;\n }\n const { data } = await mgmt.put(\n `${base}/${encodeURIComponent(id)}/draft`,\n body,\n );\n emit(data);\n } catch (err) {\n fail(err);\n }\n });\n\n seq\n .command(\"preview <id>\")\n .description(\"Render an email node to HTML/text\")\n .requiredOption(\"--node-id <id>\", \"Email node id (e.g. email1)\")\n .option(\n \"--config <json>\",\n \"Inline email config (inline or @file.json); omit to render the saved draft\",\n )\n .option(\"--to <email>\", \"Recipient address to cast in the preview\")\n .option(\"-p, --project <slug>\", \"Project slug (default: connected project)\")\n .action(async (id: string, opts: Record<string, unknown>) => {\n try {\n const slug = await resolveProjectSlug(opts.project as string | undefined);\n const base = `/v1/projects/${slug}/sequences`;\n const body: Record<string, unknown> = { node_id: String(opts.nodeId) };\n if (opts.config !== undefined) {\n body.config = JSON.parse(await readValue(String(opts.config)));\n }\n if (opts.to !== undefined) body.recipient_email = String(opts.to);\n const { data } = await mgmt.post(\n `${base}/${encodeURIComponent(id)}/preview`,\n body,\n );\n emit(data);\n } catch (err) {\n fail(err);\n }\n });\n\n seq\n .command(\"test-email <id>\")\n .description(\"Send a [Test] copy of an email node\")\n .requiredOption(\"--node-id <id>\", \"Email node id (e.g. email1)\")\n .option(\"--to <email>\", \"Recipient (default: your account email)\")\n .option(\n \"--config <json>\",\n \"Inline email config (inline or @file.json); omit to send the saved draft\",\n )\n .option(\"-p, --project <slug>\", \"Project slug (default: connected project)\")\n .action(async (id: string, opts: Record<string, unknown>) => {\n try {\n const slug = await resolveProjectSlug(opts.project as string | undefined);\n const base = `/v1/projects/${slug}/sequences`;\n const body: Record<string, unknown> = { node_id: String(opts.nodeId) };\n if (opts.to !== undefined) body.recipient_email = String(opts.to);\n if (opts.config !== undefined) {\n body.config = JSON.parse(await readValue(String(opts.config)));\n }\n const { data } = await mgmt.post(\n `${base}/${encodeURIComponent(id)}/test-email`,\n body,\n );\n emit(data);\n } catch (err) {\n fail(err);\n }\n });\n\n seq\n .command(\"validate <id>\")\n .description(\"Dry-run the publish checks against the current draft (no changes)\")\n .option(\"-p, --project <slug>\", \"Project slug (default: connected project)\")\n .action(async (id: string, opts: Record<string, unknown>) => {\n try {\n const slug = await resolveProjectSlug(opts.project as string | undefined);\n const { data } = await mgmt.get(\n `/v1/projects/${slug}/sequences/${encodeURIComponent(id)}/validate`,\n );\n if (isJsonMode()) {\n printJson(data);\n return;\n }\n const result = data as {\n ok: boolean;\n errors: { code: string; message: string; nodeId?: string }[];\n };\n if (result.ok) {\n printSuccess(\"Draft is publish-ready.\");\n return;\n }\n printError(`Not publish-ready (${result.errors.length} issue(s)):`);\n for (const e of result.errors) {\n const where = e.nodeId ? c.dim(` [${e.nodeId}]`) : \"\";\n log(` • ${e.message}${where}`);\n }\n process.exitCode = 1;\n } catch (err) {\n fail(err);\n }\n });\n\n seq\n .command(\"schema\")\n .description(\"Print the canonical trigger / node / edge shapes + an example\")\n .action(() => {\n if (isJsonMode()) printJson(SEQUENCE_SCHEMA);\n else printSequenceSchemaHuman();\n });\n\n seq\n .command(\"scaffold\")\n .description(\n \"Create + draft (and optionally publish) a trigger → [wait] → email → exit sequence\",\n )\n .requiredOption(\"--name <name>\", \"Sequence name\")\n .option(\n \"--event <name>\",\n \"Event-trigger name (omit for a contact_created trigger)\",\n )\n .option(\"--no-once-per-contact\", \"Allow re-enrolment on every event\")\n .option(\"--audience <json>\", \"Audience FilterSpec (inline JSON or @file.json)\")\n .option(\n \"--audience-trait <path=value>\",\n \"Shorthand audience filter: trait equals value (e.g. plan=free)\",\n )\n .option(\"--wait <duration>\", \"Delay before the email, e.g. 30m, 1h, 2d\")\n .requiredOption(\"--subject <subject>\", \"Email subject\")\n .option(\"--body-md <markdown>\", \"Email body as Markdown (converted to rich text)\")\n .option(\"--body-text <text>\", \"Email body as plain text\")\n .option(\"--publish\", \"Publish immediately after drafting\")\n .option(\"--dry-run\", \"Print the trigger + graph it would create, without creating anything\")\n .option(\"--if-not-exists\", \"Reuse an existing sequence with the same name instead of duplicating\")\n .option(\"-p, --project <slug>\", \"Project slug (default: connected project)\")\n .action(async (opts: Record<string, unknown>) => {\n try {\n const slug = await resolveProjectSlug(opts.project as string | undefined);\n const base = `/v1/projects/${slug}/sequences`;\n\n // Audience filter (full FilterSpec wins over the trait shorthand).\n let filter: unknown;\n if (opts.audience !== undefined) {\n filter = JSON.parse(await readValue(String(opts.audience)));\n } else if (opts.audienceTrait !== undefined) {\n filter = parseAudienceTrait(String(opts.audienceTrait));\n }\n\n const trigger = opts.event\n ? {\n type: \"event\",\n eventName: String(opts.event),\n oncePerContact: opts.oncePerContact !== false,\n ...(filter ? { filter } : {}),\n }\n : { type: \"contact_created\", ...(filter ? { filter } : {}) };\n\n const bodyDoc =\n opts.bodyMd !== undefined\n ? markdownToTipTap(String(opts.bodyMd))\n : opts.bodyText !== undefined\n ? markdownToTipTap(String(opts.bodyText))\n : undefined;\n\n const nodes: Record<string, unknown>[] = [\n { id: \"trigger\", type: \"trigger\", position: { x: 280, y: 80 }, config: { type: \"trigger\" } },\n ];\n const edges: Record<string, unknown>[] = [];\n let prev = \"trigger\";\n let y = 200;\n if (opts.wait !== undefined) {\n nodes.push({\n id: \"wait1\",\n type: \"wait\",\n position: { x: 280, y },\n config: { type: \"wait\", duration: parseDuration(String(opts.wait)) },\n });\n edges.push({ id: \"e_wait\", source: prev, target: \"wait1\" });\n prev = \"wait1\";\n y += 120;\n }\n nodes.push({\n id: \"email1\",\n type: \"email\",\n position: { x: 280, y },\n config: {\n type: \"email\",\n subject: String(opts.subject),\n ...(bodyDoc ? { bodyDoc } : {}),\n },\n });\n edges.push({ id: \"e_email\", source: prev, target: \"email1\" });\n prev = \"email1\";\n y += 120;\n nodes.push({\n id: \"exit1\",\n type: \"exit\",\n position: { x: 280, y },\n config: { type: \"exit\" },\n });\n edges.push({ id: \"e_exit\", source: prev, target: \"exit1\" });\n const graph = { nodes, edges };\n\n // --dry-run: show exactly what would be sent, mutate nothing.\n if (opts.dryRun) {\n if (isJsonMode()) printJson({ slug, name: String(opts.name), trigger, graph });\n else {\n log(\"Would create sequence \" + c.bold(String(opts.name)) + \" with:\\n\");\n log(\"trigger:\");\n log(JSON.stringify(trigger, null, 2));\n log(\"\\ngraph:\");\n log(JSON.stringify(graph, null, 2));\n }\n return;\n }\n\n // --if-not-exists: reuse a same-named sequence instead of duplicating.\n let id: string | undefined;\n let reused = false;\n if (opts.ifNotExists) {\n const { data } = await mgmt.get(base);\n const rows = (data as { data?: { id: string; name: string }[] }).data ?? [];\n const match = rows.find((r) => r.name === String(opts.name));\n if (match) {\n id = match.id;\n reused = true;\n }\n }\n if (!id) {\n const created = await mgmt.post(base, { name: String(opts.name) });\n id = (created.data as { id: string }).id;\n }\n // Save the draft at the sequence's current revision (0 for brand-new).\n const detail = await mgmt.get(`${base}/${id}`);\n const rev = (detail.data as { draft_revision?: number }).draft_revision ?? 0;\n await mgmt.put(`${base}/${id}/draft`, {\n graph,\n trigger,\n expected_revision: rev,\n });\n let published = false;\n if (opts.publish) {\n await mgmt.request(\"POST\", `${base}/${id}/publish`, {});\n published = true;\n }\n\n if (isJsonMode()) {\n printJson({ id, slug, reused, published, trigger, graph });\n return;\n }\n const verb = reused ? \"Updated existing sequence\" : \"Created sequence\";\n printSuccess(\n `${verb} ${id}${published ? \" and published it\" : \" (draft saved)\"}.`,\n );\n if (!published) {\n printInfo(`Publish when ready: ${cliCommand()} sequences publish ${id}`);\n printInfo(`Dry-run the publish checks: ${cliCommand()} sequences validate ${id}`);\n }\n printInfo(\n `Tweak it on the canvas, or re-draft with ${cliCommand()} sequences draft ${id} --graph @graph.json`,\n );\n } catch (err) {\n fail(err);\n }\n });\n}\n\nfunction attachTraits(program: Command) {\n const traits = program\n .command(\"traits\")\n .description(\"Discover trait keys for audience filters and branch conditions\");\n traits\n .command(\"list\")\n .description(\"List distinct contact/account trait keys with type + sample\")\n .option(\"-p, --project <slug>\", \"Project slug (default: connected project)\")\n .action(async (opts: Record<string, unknown>) => {\n try {\n const slug = await resolveProjectSlug(opts.project as string | undefined);\n const { data } = await mgmt.get(`/v1/projects/${slug}/traits`);\n const rows =\n (data as {\n data?: {\n key: string;\n scope: string;\n type: string;\n count: number;\n sample: string | null;\n }[];\n }).data ?? [];\n if (isJsonMode()) {\n printJson(rows);\n return;\n }\n if (rows.length === 0) {\n log(c.dim(\"(no traits yet)\"));\n return;\n }\n log(c.dim(\"Values are compared as strings (e.g. boolean false → \\\"false\\\").\\n\"));\n for (const r of rows) {\n const head = `${c.bold(r.key)} ${c.dim(`(${r.scope} ${r.type})`)}`;\n const sample = r.sample != null ? ` e.g. ${JSON.stringify(r.sample)}` : \"\";\n log(`${head} ×${r.count}${sample}`);\n }\n } catch (err) {\n fail(err);\n }\n });\n}\n\nexport function registerResourceCommands(program: Command) {\n for (const spec of SPECS) register(program, spec);\n attachSequenceExtras(program);\n attachTraits(program);\n\n // `letter me` — the token's identity (handy sanity check).\n program\n .command(\"me\")\n .description(\"Show the token's user, workspace, and role\")\n .action(async () => {\n try {\n const { data } = await mgmt.get(\"/v1/me\");\n emit(data);\n } catch (err) {\n fail(err);\n }\n });\n\n // Sender identity + sending mode don't fit the CRUD mold: register directly.\n program\n .command(\"sender-identity\")\n .description(\"Set From / From-name / Reply-To (--from-email, --from-name, --reply-to-email)\")\n .option(\"-p, --project <slug>\", \"Project slug (default: connected project)\")\n .option(\"--from-email <email>\", \"From address\")\n .option(\"--from-name <name>\", \"From display name\")\n .option(\"--reply-to-email <email>\", \"Reply-To address\")\n .action(async (opts) => {\n try {\n const slug = await resolveProjectSlug(opts.project);\n const body: Record<string, unknown> = {};\n if (opts.fromEmail !== undefined) body.from_email = opts.fromEmail;\n if (opts.fromName !== undefined) body.from_name = opts.fromName;\n if (opts.replyToEmail !== undefined) body.reply_to_email = opts.replyToEmail;\n const { data } = await mgmt.put(`/v1/projects/${slug}/sender-identity`, body);\n emit(data);\n } catch (err) {\n fail(err);\n }\n });\n\n program\n .command(\"sending-mode\")\n .description(\"Set the sending mode (letter_subdomain | byo_domain)\")\n .argument(\"<mode>\", \"letter_subdomain | byo_domain\")\n .option(\"-p, --project <slug>\", \"Project slug (default: connected project)\")\n .action(async (mode: string, opts) => {\n try {\n const slug = await resolveProjectSlug(opts.project);\n const { data } = await mgmt.put(`/v1/projects/${slug}/sending-mode`, {\n sending_mode: mode,\n });\n emit(data);\n } catch (err) {\n fail(err);\n }\n });\n}\n","/**\n * Minimal Markdown -> TipTap/ProseMirror converter, so email bodies can be\n * authored from the CLI without hand-writing ProseMirror JSON.\n *\n * It targets the subset the Letter email renderer (TipTap StarterKit + Link)\n * understands: headings (#..###), paragraphs, bullet/ordered lists,\n * blockquotes, horizontal rules, and inline **bold**, *italic*, `code`, and\n * [links](url) plus hard line breaks. It is intentionally small - for anything\n * richer, pass a full `bodyDoc` JSON to `sequences draft` instead.\n */\n\nexport type TipTapMark = { type: string; attrs?: Record<string, unknown> };\nexport type TipTapNode = {\n type: string;\n attrs?: Record<string, unknown>;\n marks?: TipTapMark[];\n text?: string;\n content?: TipTapNode[];\n};\nexport type TipTapDoc = { type: \"doc\"; content: TipTapNode[] };\n\nconst HR = /^\\s*([-*_])\\1{2,}\\s*$/;\nconst HEADING = /^(#{1,3})\\s+(.*)$/;\nconst QUOTE = /^\\s*>\\s?/;\nconst BULLET = /^\\s*[-*+]\\s+/;\nconst ORDERED = /^\\s*\\d+\\.\\s+/;\n\nfunction isBlockStart(line: string): boolean {\n return (\n /^#{1,3}\\s+/.test(line) ||\n BULLET.test(line) ||\n ORDERED.test(line) ||\n QUOTE.test(line) ||\n HR.test(line)\n );\n}\n\nfunction pushText(nodes: TipTapNode[], text: string, marks?: TipTapMark[]) {\n if (!text) return;\n nodes.push(marks ? { type: \"text\", text, marks } : { type: \"text\", text });\n}\n\n/**\n * Parse inline marks within a single line. Recursive so marks nest correctly:\n * `**[text](url)**` becomes one text node carrying both `bold` and `link`,\n * rather than a literal `[text](url)` string inside a bold run. `carried` is\n * the set of marks inherited from the enclosing span.\n */\nfunction inline(text: string, carried: TipTapMark[] = []): TipTapNode[] {\n const nodes: TipTapNode[] = [];\n // Non-greedy bodies so `**a** **b**` splits into two runs and an outer\n // mark can contain a nested token (e.g. a link inside bold). Code first so\n // `**` inside backticks isn't treated as bold; then links, bold, italic.\n const token =\n /(`[^`]+`)|(\\[[^\\]]+\\]\\([^)]+\\))|(\\*\\*[\\s\\S]+?\\*\\*)|(__[\\s\\S]+?__)|(\\*[\\s\\S]+?\\*)|(_[\\s\\S]+?_)/;\n let rest = text;\n while (rest.length > 0) {\n const m = token.exec(rest);\n if (!m) {\n pushText(nodes, rest, carried.length ? carried : undefined);\n break;\n }\n if (m.index > 0) {\n pushText(nodes, rest.slice(0, m.index), carried.length ? carried : undefined);\n }\n const tok = m[0];\n if (tok.startsWith(\"`\")) {\n pushText(nodes, tok.slice(1, -1), [...carried, { type: \"code\" }]);\n } else if (tok.startsWith(\"[\")) {\n const lm = /\\[([^\\]]+)\\]\\(([^)]+)\\)/.exec(tok);\n if (lm) {\n nodes.push(\n ...inline(lm[1] ?? \"\", [\n ...carried,\n { type: \"link\", attrs: { href: lm[2] ?? \"\" } },\n ]),\n );\n }\n } else if (tok.startsWith(\"**\") || tok.startsWith(\"__\")) {\n nodes.push(...inline(tok.slice(2, -2), [...carried, { type: \"bold\" }]));\n } else {\n nodes.push(...inline(tok.slice(1, -1), [...carried, { type: \"italic\" }]));\n }\n rest = rest.slice(m.index + tok.length);\n }\n return nodes;\n}\n\n/** Inline-parse multiple lines, joining them with hard breaks. */\nfunction inlineMultiline(lines: string[]): TipTapNode[] {\n const out: TipTapNode[] = [];\n lines.forEach((line, idx) => {\n if (idx > 0) out.push({ type: \"hardBreak\" });\n out.push(...inline(line));\n });\n return out;\n}\n\nfunction listItems(\n lines: string[],\n start: number,\n marker: RegExp,\n): { items: TipTapNode[]; next: number } {\n const items: TipTapNode[] = [];\n let i = start;\n for (let cur = lines[i]; cur !== undefined && marker.test(cur); cur = lines[i]) {\n items.push({\n type: \"listItem\",\n content: [{ type: \"paragraph\", content: inline(cur.replace(marker, \"\")) }],\n });\n i++;\n }\n return { items, next: i };\n}\n\nexport function markdownToTipTap(input: string): TipTapDoc {\n // Shells don't expand `\\n` inside double quotes, so accept literal escapes.\n const src = input.replace(/\\\\n/g, \"\\n\").replace(/\\\\t/g, \"\\t\");\n const lines = src.split(\"\\n\");\n const blocks: TipTapNode[] = [];\n let i = 0;\n while (i < lines.length) {\n const line = lines[i] ?? \"\";\n if (line.trim() === \"\") {\n i++;\n continue;\n }\n if (HR.test(line)) {\n blocks.push({ type: \"horizontalRule\" });\n i++;\n continue;\n }\n const h = HEADING.exec(line);\n if (h) {\n blocks.push({\n type: \"heading\",\n attrs: { level: (h[1] ?? \"#\").length },\n content: inline(h[2] ?? \"\"),\n });\n i++;\n continue;\n }\n if (QUOTE.test(line)) {\n const quote: string[] = [];\n for (let cur = lines[i]; cur !== undefined && QUOTE.test(cur); cur = lines[i]) {\n quote.push(cur.replace(QUOTE, \"\"));\n i++;\n }\n blocks.push({\n type: \"blockquote\",\n content: [{ type: \"paragraph\", content: inline(quote.join(\" \")) }],\n });\n continue;\n }\n if (BULLET.test(line)) {\n const { items, next } = listItems(lines, i, BULLET);\n blocks.push({ type: \"bulletList\", content: items });\n i = next;\n continue;\n }\n if (ORDERED.test(line)) {\n const { items, next } = listItems(lines, i, ORDERED);\n blocks.push({ type: \"orderedList\", content: items });\n i = next;\n continue;\n }\n // Paragraph: gather consecutive plain lines, join with hard breaks.\n const para: string[] = [];\n for (let cur = lines[i]; cur !== undefined && cur.trim() !== \"\" && !isBlockStart(cur); cur = lines[i]) {\n para.push(cur);\n i++;\n }\n blocks.push({ type: \"paragraph\", content: inlineMultiline(para) });\n }\n return { type: \"doc\", content: blocks };\n}\n"],"mappings":";;;AACA,SAAS,eAAe;;;ACDxB,OAAO,WAAW;AAClB,OAAO,SAAuB;AAC9B,SAAS,uBAAuB;AAEhC,IAAI,WAAW;AAER,SAAS,YAAY,SAAkB;AAC5C,aAAW;AACb;AACO,SAAS,aAAa;AAC3B,SAAO;AACT;AAEO,SAAS,UAAU,MAAe;AACvC,UAAQ,IAAI,KAAK,UAAU,MAAM,MAAM,CAAC,CAAC;AAC3C;AAQO,SAAS,aAAqB;AACnC,UAAQ,QAAQ,KAAK,CAAC,KAAK,IAAI,SAAS,MAAM,IAAI,uBAAuB;AAC3E;AAEO,SAAS,IAAI,MAAM,IAAI;AAC5B,MAAI,SAAU;AACd,UAAQ,IAAI,GAAG;AACjB;AAEO,SAAS,aAAa,KAAa;AACxC,MAAI,SAAU;AACd,UAAQ,IAAI,MAAM,MAAM,QAAG,IAAI,MAAM,GAAG;AAC1C;AAEO,SAAS,UAAU,KAAa;AACrC,MAAI,SAAU;AACd,UAAQ,IAAI,MAAM,KAAK,QAAG,IAAI,MAAM,GAAG;AACzC;AAEO,SAAS,aAAa,KAAa;AACxC,MAAI,SAAU;AACd,UAAQ,IAAI,MAAM,OAAO,GAAG,IAAI,MAAM,GAAG;AAC3C;AAIO,SAAS,WAAW,KAAc;AACvC,QAAM,MAAM,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC3D,QAAM,SAAU,KAAiC;AACjD,MAAI,UAAU;AACZ,cAAU,EAAE,OAAO,KAAK,GAAI,QAAQ,SAAS,EAAE,OAAO,IAAI,CAAC,EAAG,CAAC;AAC/D;AAAA,EACF;AACA,UAAQ,MAAM,MAAM,IAAI,QAAG,IAAI,MAAM,GAAG;AAGxC,MAAI,MAAM,QAAQ,MAAM,GAAG;AACzB,eAAW,KAAK,QAAQ;AACtB,YAAMA,QAAO,EAAE,OAAO,MAAM,IAAI,GAAG,EAAE,IAAI,IAAI,IAAI;AACjD,cAAQ,MAAM,OAAOA,SAAQ,EAAE,WAAW,GAAG;AAAA,IAC/C;AAAA,EACF;AACF;AAGO,SAAS,SAAS;AACvB,MAAI,SAAU;AACd,UAAQ,IAAI,EAAE;AACd,UAAQ,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,IAAI,GAAG,IAAI,MAAM,MAAM,IAAI,KAAK,CAAC;AACjF,UAAQ,IAAI,EAAE;AAChB;AAEO,SAAS,QAAQ,MAAmB;AACzC,SAAO,IAAI,EAAE,MAAM,OAAO,QAAQ,WAAW,CAAC,SAAS,CAAC;AAC1D;AAMO,SAAS,OAAO,UAAmC;AACxD,MAAI,CAAC,QAAQ,MAAM,MAAO,QAAO,QAAQ,QAAQ,EAAE;AACnD,QAAM,KAAK,gBAAgB,EAAE,OAAO,QAAQ,OAAO,QAAQ,QAAQ,OAAO,CAAC;AAC3E,SAAO,IAAI,QAAQ,CAAC,YAAY;AAC9B,OAAG,SAAS,UAAU,CAAC,WAAW;AAChC,SAAG,MAAM;AACT,cAAQ,OAAO,KAAK,CAAC;AAAA,IACvB,CAAC;AAAA,EACH,CAAC;AACH;AAOO,SAAS,KAAK,MAAe;AAClC,MAAI,UAAU;AACZ,cAAU,IAAI;AACd;AAAA,EACF;AACA,MAAI,QAAQ,OAAO,SAAS,YAAY,MAAM,QAAS,KAA4B,IAAI,GAAG;AACxF,UAAM,MAAM;AACZ,QAAI,IAAI,KAAK,WAAW,GAAG;AACzB,cAAQ,IAAI,MAAM,IAAI,QAAQ,CAAC;AAAA,IACjC,OAAO;AACL,iBAAW,OAAO,IAAI,MAAM;AAC1B,cAAM,KAAK,OAAO,IAAI,MAAM,IAAI,QAAQ,IAAI,eAAe,EAAE;AAC7D,cAAM,QACH,IAAI,QACJ,IAAI,SACJ,IAAI,UACJ,IAAI,WACL;AACF,gBAAQ,IAAI,GAAG,MAAM,KAAK,EAAE,CAAC,GAAG,QAAQ,OAAO,QAAQ,EAAE,EAAE;AAAA,MAC7D;AAAA,IACF;AACA,QAAI,IAAI,aAAa;AACnB,cAAQ,IAAI,MAAM,IAAI;AAAA,wBAAsB,IAAI,WAAW,EAAE,CAAC;AAAA,IAChE;AACA;AAAA,EACF;AACA,YAAU,IAAI;AAChB;AAEO,IAAM,IAAI;;;AChIjB,OAAO,UAAU;AACjB,SAAS,eAAe;AACxB,OAAO,UAAU;AACjB,SAAS,OAAO,UAAU,WAAW,UAAU;AAKxC,IAAM,mBAAmB;AAOhC,IAAM,SAAS,IAAI,KAAK;AAAA,EACtB,aAAa;AAAA,EACb,QAAQ;AAAA,IACN,SAAS,EAAE,MAAM,UAAU,SAAS,iBAAiB;AAAA,EACvD;AACF,CAAC;AAGM,SAAS,WAAW,MAAuB;AAChD,QAAM,MACJ,QACA,QAAQ,IAAI,mBACX,OAAO,IAAI,SAAS,KACrB;AACF,SAAO,IAAI,QAAQ,OAAO,EAAE;AAC9B;AAEO,SAAS,WAAW,KAAmB;AAC5C,SAAO,IAAI,WAAW,IAAI,QAAQ,OAAO,EAAE,CAAC;AAC9C;AAEO,SAAS,gBAAwB;AACtC,SAAO,OAAO;AAChB;AAEO,SAAS,cAAoB;AAClC,SAAO,MAAM;AACf;AAwBA,eAAsB,mBAAmB,MAAgC;AACvE,MAAI,KAAM,QAAO;AACjB,MAAI,QAAQ,IAAI,eAAgB,QAAO,QAAQ,IAAI;AACnD,QAAM,OAAO,MAAM,eAAe;AAClC,MAAI,MAAM,SAAS,KAAM,QAAO,KAAK,QAAQ;AAC7C,QAAM,IAAI;AAAA,IACR,mEAAmE,WAAW,CAAC;AAAA,EACjF;AACF;AAEO,SAAS,kBAA0B;AACxC,SAAO,KAAK,KAAK,QAAQ,GAAG,WAAW,kBAAkB;AAC3D;AAEA,eAAsB,eAAe,MAAyC;AAC5E,QAAM,OAAO,gBAAgB;AAC7B,QAAM,MAAM,KAAK,QAAQ,IAAI,GAAG,EAAE,WAAW,MAAM,MAAM,IAAM,CAAC;AAChE,QAAM,UAAU,MAAM,GAAG,KAAK,UAAU,MAAM,MAAM,CAAC,CAAC;AAAA,GAAM,EAAE,MAAM,IAAM,CAAC;AAC3E,SAAO;AACT;AAEA,eAAsB,iBAAmD;AACvE,MAAI;AACF,UAAM,MAAM,MAAM,SAAS,gBAAgB,GAAG,MAAM;AACpD,WAAO,KAAK,MAAM,GAAG;AAAA,EACvB,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,eAAsB,kBAAiC;AACrD,QAAM,GAAG,gBAAgB,GAAG,EAAE,OAAO,KAAK,CAAC;AAC7C;;;AC/FA,IAAM,aAAa;AA+BnB,eAAsB,gBAAgB,MAAsC;AAC1E,QAAM,MAAM,MAAM,MAAM,GAAG,IAAI,sBAAsB;AAAA,IACnD,QAAQ;AAAA,IACR,SAAS,EAAE,gBAAgB,oBAAoB,cAAc,WAAW;AAAA,IACxE,MAAM;AAAA,EACR,CAAC;AACD,MAAI,CAAC,IAAI,IAAI;AACX,UAAM,IAAI;AAAA,MACR,+BAA+B,IAAI,MAAM,SAAS,IAAI;AAAA,IACxD;AAAA,EACF;AACA,SAAQ,MAAM,IAAI,KAAK;AACzB;AAGA,eAAsB,eACpB,MACA,YACuB;AACvB,QAAM,MAAM,MAAM,MAAM,GAAG,IAAI,qBAAqB;AAAA,IAClD,QAAQ;AAAA,IACR,SAAS,EAAE,gBAAgB,oBAAoB,cAAc,WAAW;AAAA,IACxE,MAAM,KAAK,UAAU,EAAE,aAAa,WAAW,CAAC;AAAA,EAClD,CAAC;AAED,MAAI,IAAI,WAAW,KAAK;AACtB,UAAM,aAAa,OAAO,IAAI,QAAQ,IAAI,aAAa,KAAK,GAAG;AAC/D,WAAO,EAAE,QAAQ,aAAa,WAAW;AAAA,EAC3C;AACA,MAAI,CAAC,IAAI,IAAI;AACX,UAAM,IAAI,MAAM,2BAA2B,IAAI,MAAM,IAAI;AAAA,EAC3D;AACA,SAAQ,MAAM,IAAI,KAAK;AACzB;AAYA,IAAM,eAAN,MAAmB;AAAA,EACT,aAAa;AAAA,EAErB,MAAc,cAAwD;AACpE,UAAM,OAAO,MAAM,eAAe;AAClC,UAAM,QAAQ,QAAQ,IAAI,kBAAkB,MAAM,UAAU;AAC5D,QAAI,CAAC,OAAO;AACV,YAAM,IAAI;AAAA,QACR,wBAAwB,WAAW,CAAC;AAAA,MACtC;AAAA,IACF;AACA,UAAM,OAAO,WAAW,QAAQ,IAAI,iBAAiB,SAAY,MAAM,OAAO;AAC9E,WAAO,EAAE,MAAM,MAAM;AAAA,EACvB;AAAA,EAEA,MAAc,QACZ,QACAC,OACA,UAAU,GACe;AACzB,UAAM,EAAE,MAAM,MAAM,IAAI,MAAM,KAAK,YAAY;AAC/C,UAAM,MAAM,MAAM,MAAM,GAAG,IAAI,GAAGA,KAAI,IAAI;AAAA,MACxC;AAAA,MACA,SAAS;AAAA,QACP,eAAe,UAAU,KAAK;AAAA,QAC9B,QAAQ;AAAA,QACR,cAAc;AAAA,MAChB;AAAA,IACF,CAAC;AAED,QAAI,IAAI,WAAW,OAAO,WAAW,KAAK,YAAY;AACpD,YAAM,aAAa,OAAO,IAAI,QAAQ,IAAI,aAAa,KAAK,CAAC;AAC7D,YAAM,IAAI,QAAQ,CAAC,MAAM,WAAW,GAAG,aAAa,MAAO,OAAO,CAAC;AACnE,aAAO,KAAK,QAAW,QAAQA,OAAM,UAAU,CAAC;AAAA,IAClD;AAEA,UAAM,OAAQ,IAAI,WAAW,MAAM,CAAC,IAAI,MAAM,IAAI,KAAK;AACvD,QAAI,CAAC,IAAI,IAAI;AACX,YAAM,aAAa,MAAM,IAAI,MAAM;AAAA,IACrC;AACA,WAAO,EAAE,QAAQ,IAAI,QAAQ,KAAK;AAAA,EACpC;AAAA,EAEA,IAAiBA,OAAc;AAC7B,WAAO,KAAK,QAAW,OAAOA,KAAI;AAAA,EACpC;AACF;AAKA,SAAS,aAAa,MAAe,QAAuB;AAC1D,QAAM,SAAU,MAEZ;AACJ,QAAM,MAAM,QAAQ,WAAW,QAAQ,MAAM;AAC7C,QAAM,MAAM,IAAI,MAAM,GAAG;AAIzB,MAAI,SAAS;AACb,MAAI,MAAM,QAAQ,QAAQ,MAAM,KAAK,OAAO,OAAO,SAAS,GAAG;AAC7D,QAAI,SAAS,OAAO;AAAA,EACtB;AACA,SAAO;AACT;AAEO,IAAM,SAAS,IAAI,aAAa;AAcvC,IAAM,mBAAN,MAAuB;AAAA,EACb,aAAa;AAAA,EAErB,MAAc,cAAwD;AACpE,UAAM,OAAO,MAAM,eAAe;AAClC,UAAM,QAAQ,QAAQ,IAAI,cAAc,MAAM,OAAO;AACrD,QAAI,CAAC,OAAO;AACV,YAAM,IAAI;AAAA,QACR,uCAAuC,WAAW,CAAC;AAAA,MACrD;AAAA,IACF;AACA,UAAM,OAAO,WAAW,QAAQ,IAAI,aAAa,SAAY,MAAM,OAAO;AAC1E,WAAO,EAAE,MAAM,MAAM;AAAA,EACvB;AAAA,EAEA,MAAM,QACJ,QACAA,OACA,OAAwB,CAAC,GACzB,UAAU,GACe;AACzB,UAAM,EAAE,MAAM,MAAM,IAAI,MAAM,KAAK,YAAY;AAE/C,QAAI,MAAM,GAAG,IAAI,GAAGA,KAAI;AACxB,QAAI,KAAK,OAAO;AACd,YAAM,KAAK,IAAI,gBAAgB;AAC/B,iBAAW,CAAC,GAAG,CAAC,KAAK,OAAO,QAAQ,KAAK,KAAK,GAAG;AAC/C,YAAI,MAAM,UAAa,MAAM,GAAI,IAAG,IAAI,GAAG,OAAO,CAAC,CAAC;AAAA,MACtD;AACA,YAAM,IAAI,GAAG,SAAS;AACtB,UAAI,EAAG,QAAO,IAAI,CAAC;AAAA,IACrB;AAEA,UAAM,UAAkC;AAAA,MACtC,eAAe,UAAU,KAAK;AAAA,MAC9B,QAAQ;AAAA,MACR,cAAc;AAAA,IAChB;AACA,QAAI;AACJ,QAAI,KAAK,MAAM;AACb,aAAO,KAAK;AAAA,IACd,WAAW,KAAK,SAAS,QAAW;AAClC,cAAQ,cAAc,IAAI;AAC1B,aAAO,KAAK,UAAU,KAAK,IAAI;AAAA,IACjC;AAEA,UAAM,MAAM,MAAM,MAAM,KAAK,EAAE,QAAQ,SAAS,KAAK,CAAC;AAEtD,QAAI,IAAI,WAAW,OAAO,WAAW,KAAK,YAAY;AACpD,YAAM,aAAa,OAAO,IAAI,QAAQ,IAAI,aAAa,KAAK,CAAC;AAC7D,YAAM,IAAI,QAAQ,CAAC,MAAM,WAAW,GAAG,aAAa,MAAO,OAAO,CAAC;AACnE,aAAO,KAAK,QAAW,QAAQA,OAAM,MAAM,UAAU,CAAC;AAAA,IACxD;AAEA,UAAM,OAAO,MAAM,IAAI,KAAK;AAC5B,UAAM,OAAQ,OAAO,KAAK,MAAM,IAAI,IAAI,CAAC;AACzC,QAAI,CAAC,IAAI,IAAI;AACX,YAAM,aAAa,MAAM,IAAI,MAAM;AAAA,IACrC;AACA,WAAO,EAAE,QAAQ,IAAI,QAAQ,KAAK;AAAA,EACpC;AAAA,EAEA,IAAiBA,OAAc,OAAkC;AAC/D,WAAO,KAAK,QAAW,OAAOA,OAAM,EAAE,MAAM,CAAC;AAAA,EAC/C;AAAA,EACA,KAAkBA,OAAc,MAAgB;AAC9C,WAAO,KAAK,QAAW,QAAQA,OAAM,EAAE,KAAK,CAAC;AAAA,EAC/C;AAAA,EACA,SAAsBA,OAAc,MAAgB;AAClD,WAAO,KAAK,QAAW,QAAQA,OAAM,EAAE,KAAK,CAAC;AAAA,EAC/C;AAAA,EACA,MAAmBA,OAAc,MAAgB;AAC/C,WAAO,KAAK,QAAW,SAASA,OAAM,EAAE,KAAK,CAAC;AAAA,EAChD;AAAA,EACA,IAAiBA,OAAc,MAAgB;AAC7C,WAAO,KAAK,QAAW,OAAOA,OAAM,EAAE,KAAK,CAAC;AAAA,EAC9C;AAAA,EACA,OAAoBA,OAAc;AAChC,WAAO,KAAK,QAAW,UAAUA,KAAI;AAAA,EACvC;AACF;AAEO,IAAM,OAAO,IAAI,iBAAiB;;;AClPzC,SAAS,aAAa;AAOf,SAAS,QAAQ,KAAsB;AAC5C,QAAM,WAAW,QAAQ;AACzB,MAAI;AACJ,MAAI;AAEJ,MAAI,aAAa,UAAU;AACzB,cAAU;AACV,WAAO,CAAC,GAAG;AAAA,EACb,WAAW,aAAa,SAAS;AAC/B,cAAU;AAEV,WAAO,CAAC,MAAM,SAAS,IAAI,GAAG;AAAA,EAChC,OAAO;AACL,cAAU;AACV,WAAO,CAAC,GAAG;AAAA,EACb;AAEA,MAAI;AACF,UAAM,QAAQ,MAAM,SAAS,MAAM,EAAE,OAAO,UAAU,UAAU,KAAK,CAAC;AACtE,UAAM,GAAG,SAAS,MAAM;AAAA,IAAC,CAAC;AAC1B,UAAM,MAAM;AACZ,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;;;AChCA,OAAOC,WAAU;AACjB,SAAS,YAAAC,WAAU,aAAAC,YAAW,YAAY;AAO1C,eAAsB,UACpB,KACA,MACA,SACiB;AACjB,QAAM,WAAWF,MAAK,KAAK,KAAK,IAAI;AACpC,MAAI,WAAW;AACf,MAAI;AACF,eAAW,MAAMC,UAAS,UAAU,MAAM;AAAA,EAC5C,QAAQ;AACN,eAAW;AAAA,EACb;AAEA,MAAI,OAAO;AACX,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,OAAO,GAAG;AAClD,UAAM,OAAO,GAAG,GAAG,IAAI,KAAK;AAC5B,UAAM,KAAK,IAAI,OAAO,IAAI,aAAa,GAAG,CAAC,QAAQ,GAAG;AACtD,QAAI,GAAG,KAAK,IAAI,GAAG;AACjB,aAAO,KAAK,QAAQ,IAAI,IAAI;AAAA,IAC9B,OAAO;AACL,UAAI,KAAK,UAAU,CAAC,KAAK,SAAS,IAAI,EAAG,SAAQ;AACjD,cAAQ,GAAG,IAAI;AAAA;AAAA,IACjB;AAAA,EACF;AAEA,QAAMC,WAAU,UAAU,MAAM,MAAM;AACtC,SAAO;AACT;AAGA,eAAsB,WAAW,KAAa,MAAgC;AAC5E,MAAI;AACF,UAAM,KAAKF,MAAK,KAAK,KAAK,IAAI,CAAC;AAC/B,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAAS,aAAa,GAAmB;AACvC,SAAO,EAAE,QAAQ,uBAAuB,MAAM;AAChD;;;ACjDA,SAAS,SAAAG,cAAa;AACtB,SAAS,YAAAC,WAAU,eAAe;AASlC,eAAsB,qBACpB,KACyB;AACzB,MAAI,MAAM,WAAW,KAAK,gBAAgB,EAAG,QAAO;AACpD,MAAI,MAAM,WAAW,KAAK,WAAW,EAAG,QAAO;AAC/C,MAAI,MAAM,WAAW,KAAK,WAAW,EAAG,QAAO;AAC/C,MAAI,MAAM,WAAW,KAAK,mBAAmB,EAAG,QAAO;AAEvD,QAAM,KAAK,QAAQ,IAAI,yBAAyB;AAChD,MAAI,GAAG,WAAW,MAAM,EAAG,QAAO;AAClC,MAAI,GAAG,WAAW,MAAM,EAAG,QAAO;AAClC,MAAI,GAAG,WAAW,KAAK,EAAG,QAAO;AACjC,SAAO;AACT;AAGA,eAAsB,gBAAgB,KAAqC;AACzE,MAAI;AACF,UAAM,MAAM,KAAK,MAAM,MAAMC,UAAS,GAAG,GAAG,iBAAiB,MAAM,CAAC;AAIpE,UAAM,OAAO,EAAE,GAAG,IAAI,cAAc,GAAG,IAAI,gBAAgB;AAC3D,QAAI,KAAK,KAAM,QAAO;AACtB,QAAI,KAAK,KAAM,QAAO;AACtB,QAAI,KAAK,iBAAiB,KAAK,KAAK,kBAAkB,EAAG,QAAO;AAChE,QAAI,KAAK,QAAS,QAAO;AACzB,QAAI,KAAK,QAAS,QAAO;AACzB,QAAI,KAAK,KAAM,QAAO;AACtB,QAAI,KAAK,eAAe,EAAG,QAAO;AAClC,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAUA,eAAsB,YAAY,KAA6B;AAC7D,MAAI,MAAM,WAAW,KAAK,cAAc,GAAG;AACzC,WAAO,EAAE,SAAS,QAAQ,WAAW,MAAM,gBAAgB,GAAG,EAAE;AAAA,EAClE;AACA,MACG,MAAM,WAAW,KAAK,gBAAgB,KACtC,MAAM,WAAW,KAAK,kBAAkB,KACxC,MAAM,WAAW,KAAK,SAAS,KAC/B,MAAM,WAAW,KAAK,UAAU,GACjC;AACA,WAAO,EAAE,SAAS,UAAU,WAAW,MAAM,sBAAsB,GAAG,EAAE;AAAA,EAC1E;AACA,MAAK,MAAM,WAAW,KAAK,SAAS,KAAO,MAAM,WAAW,GAAG,GAAI;AACjE,WAAO,EAAE,SAAS,QAAQ,WAAW,MAAM,oBAAoB,GAAG,EAAE;AAAA,EACtE;AACA,MAAI,MAAM,WAAW,KAAK,QAAQ,EAAG,QAAO,EAAE,SAAS,MAAM,WAAW,KAAK;AAC7E,MAAI,MAAM,WAAW,KAAK,eAAe,GAAG;AAC1C,WAAO,EAAE,SAAS,OAAO,WAAW,MAAM,mBAAmB,GAAG,EAAE;AAAA,EACpE;AACA,SAAO,EAAE,SAAS,SAAS,WAAW,KAAK;AAC7C;AAGO,SAAS,WAAW,OAAsB;AAC/C,MAAI,MAAM,UAAW,QAAO,MAAM;AAClC,UAAQ,MAAM,SAAS;AAAA,IACrB,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT;AACE,aAAO;AAAA,EACX;AACF;AAGO,SAAS,aAAa,OAAsB;AACjD,UAAQ,MAAM,SAAS;AAAA,IACrB,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT;AACE,aAAO;AAAA,EACX;AACF;AAEA,eAAe,OAAO,KAAa,MAA+B;AAChE,MAAI;AACF,WAAO,MAAMA,UAAS,GAAG,GAAG,IAAI,IAAI,IAAI,MAAM;AAAA,EAChD,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,eAAe,WAAW,KAA+B;AACvD,MAAI;AACF,YAAQ,MAAM,QAAQ,GAAG,GAAG,KAAK,CAAC,MAAM,EAAE,SAAS,UAAU,CAAC;AAAA,EAChE,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,eAAe,sBAAsB,KAAqC;AACxE,QAAM,OACH,MAAM,OAAO,KAAK,kBAAkB,IACpC,MAAM,OAAO,KAAK,gBAAgB,IAClC,MAAM,OAAO,KAAK,SAAS,GAC5B,YAAY;AACd,MAAI,IAAI,SAAS,QAAQ,EAAG,QAAO;AACnC,MAAI,IAAI,SAAS,SAAS,EAAG,QAAO;AACpC,MAAI,IAAI,SAAS,OAAO,EAAG,QAAO;AAClC,SAAO;AACT;AAEA,eAAe,oBAAoB,KAAqC;AACtE,QAAM,OAAO,MAAM,OAAO,KAAK,SAAS,GAAG,YAAY;AACvD,MAAI,IAAI,SAAS,OAAO,EAAG,QAAO;AAClC,MAAI,IAAI,SAAS,SAAS,EAAG,QAAO;AACpC,SAAO;AACT;AAEA,eAAe,mBAAmB,KAAqC;AACrE,QAAM,OAAO,MAAM,OAAO,KAAK,eAAe,GAAG,YAAY;AAC7D,MAAI,IAAI,SAAS,SAAS,EAAG,QAAO;AACpC,MAAI,IAAI,SAAS,SAAS,EAAG,QAAO;AACpC,SAAO;AACT;AAMA,IAAM,WAAW;AACjB,IAAM,UAAU;AAIhB,SAAS,WAAW,KAAa,MAA4B;AAC3D,SAAO,EAAE,KAAK,MAAM,SAAS,GAAG,GAAG,IAAI,KAAK,KAAK,GAAG,CAAC,GAAG;AAC1D;AAMA,eAAsB,iBAAiB,KAAkC;AACvE,MAAI,MAAM,WAAW,KAAK,SAAS,EAAG,QAAO,WAAW,MAAM,CAAC,OAAO,QAAQ,CAAC;AAC/E,QAAM,aAAa,MAAM,OAAO,KAAK,gBAAgB,GAAG,YAAY;AACpE,MACG,MAAM,WAAW,KAAK,aAAa,KACpC,UAAU,SAAS,eAAe,GAClC;AACA,WAAO,WAAW,UAAU,CAAC,OAAO,QAAQ,CAAC;AAAA,EAC/C;AACA,MAAI,MAAM,WAAW,KAAK,SAAS,GAAG;AACpC,WAAO,WAAW,UAAU,CAAC,WAAW,QAAQ,CAAC;AAAA,EACnD;AACA,SAAO,WAAW,OAAO,CAAC,WAAW,QAAQ,CAAC;AAChD;AAGA,eAAsB,eAAe,KAAkC;AACrE,MAAI,MAAM,WAAW,KAAK,SAAS,GAAG;AACpC,WAAO,WAAW,UAAU,CAAC,OAAO,OAAO,CAAC;AAAA,EAC9C;AACA,SAAO,WAAW,OAAO,CAAC,WAAW,OAAO,CAAC;AAC/C;AAGO,SAAS,cACd,SACA,KACiB;AACjB,SAAO,IAAI,QAAQ,CAAC,YAAY;AAC9B,UAAM,QAAQC,OAAM,QAAQ,KAAK,QAAQ,MAAM;AAAA,MAC7C;AAAA,MACA,OAAO;AAAA,MACP,OAAO,QAAQ,aAAa;AAAA,IAC9B,CAAC;AACD,UAAM,GAAG,SAAS,MAAM,QAAQ,CAAC,CAAC;AAClC,UAAM,GAAG,SAAS,CAAC,SAAS,QAAQ,QAAQ,CAAC,CAAC;AAAA,EAChD,CAAC;AACH;AAEO,SAAS,eAAe,IAAoB,KAAqB;AACtE,UAAQ,IAAI;AAAA,IACV,KAAK;AACH,aAAO,YAAY,GAAG;AAAA,IACxB,KAAK;AACH,aAAO,YAAY,GAAG;AAAA,IACxB,KAAK;AACH,aAAO,WAAW,GAAG;AAAA,IACvB;AACE,aAAO,eAAe,GAAG;AAAA,EAC7B;AACF;AAGO,SAAS,WACd,IACA,KACA,KACiB;AACjB,QAAM,OAAO,OAAO,QAAQ,CAAC,WAAW,GAAG,IAAI,CAAC,OAAO,GAAG;AAC1D,SAAO,IAAI,QAAQ,CAAC,YAAY;AAC9B,UAAM,QAAQA,OAAM,IAAI,MAAM;AAAA,MAC5B;AAAA,MACA,OAAO;AAAA,MACP,OAAO,QAAQ,aAAa;AAAA,IAC9B,CAAC;AACD,UAAM,GAAG,SAAS,MAAM,QAAQ,CAAC,CAAC;AAClC,UAAM,GAAG,SAAS,CAAC,SAAS,QAAQ,QAAQ,CAAC,CAAC;AAAA,EAChD,CAAC;AACH;;;AC9MA,IAAM,cAAc;AACpB,IAAM,QAAQ,CAAC,OAAe,IAAI,QAAQ,CAAC,MAAM,WAAW,GAAG,EAAE,CAAC;AAIlE,SAAS,WAAW,OAAsB;AACxC,SAAO,MAAM,YAAY,SAAS,eAAe;AACnD;AAUO,SAAS,qBAAqBC,UAAkB;AACrD,EAAAA,SACG,QAAQ,SAAS,EAAE,WAAW,KAAK,CAAC,EACpC,MAAM,MAAM,EACZ,YAAY,2DAA2D,EACvE,OAAO,aAAa,wDAAwD,EAC5E,OAAO,aAAa,mDAAmD,EACvE,OAAO,gBAAgB,yBAAyB,EAChD,OAAO,oBAAoB,+CAA+C,EAC1E;AAAA,IACC;AAAA,IACA;AAAA,EACF,EACC,OAAO,OAAO,SAAuB;AACpC,UAAM,OAAO,MAAM,SAAS,IAAI;AAChC,QAAI,SAAS,EAAG,SAAQ,WAAW;AAAA,EACrC,CAAC;AACL;AAEA,eAAe,SAAS,MAAqC;AAC3D,QAAM,OAAO,WAAW,KAAK,OAAO;AACpC,QAAM,MAAM,QAAQ,IAAI;AACxB,QAAM,cAAc,QAAQ,MAAM,SAAS,CAAC,KAAK;AAEjD,SAAO;AAKP,MAAI,KAAK,QAAQ;AACf,UAAM,UAAU,WAAW,MAAM,YAAY,GAAG,CAAC;AACjD,UAAM,UAAkC,EAAE,gBAAgB,KAAK,OAAO;AACtE,QAAI,SAAS,iBAAkB,SAAQ,kBAAkB;AACzD,UAAM,OAAO,MAAM,UAAU,KAAK,SAAS,OAAO;AAClD,iBAAa,2BAA2B,IAAI,KAAK,IAAI,CAAC,eAAe;AACrE;AAAA,MACE,qDAAqD,WAAW,CAAC;AAAA,IACnE;AACA,WAAO;AAAA,EACT;AAGA,MAAI;AACJ,MAAI;AACF,WAAO,MAAM,gBAAgB,IAAI;AAAA,EACnC,SAAS,KAAK;AACZ,eAAW,GAAG;AACd,WAAO;AAAA,EACT;AAEA,MAAI,EAAE,KAAK,oCAAoC,CAAC;AAChD,MAAI;AACJ,MAAI,SAAS,EAAE,KAAK,EAAE,KAAK,KAAK,SAAS,CAAC,CAAC;AAC3C,MAAI;AAGJ,MAAI,eAAe,KAAK,MAAM;AAC5B,UAAM,OAAO,EAAE,IAAI,yCAAoC,CAAC;AAAA,EAC1D;AACA,MAAI,KAAK,KAAM,SAAQ,KAAK,yBAAyB;AACrD,YAAU,qCAAqC;AAC/C,MAAI,SAAS,EAAE,KAAK,KAAK,yBAAyB,CAAC;AACnD,MAAI;AACJ,YAAU,qDAAgD;AAG1D,QAAM,WAAW,KAAK,IAAI,IAAI,KAAK,aAAa;AAChD,MAAI,aAAa,KAAK,IAAI,GAAG,KAAK,QAAQ,IAAI;AAE9C,SAAO,KAAK,IAAI,IAAI,UAAU;AAC5B,UAAM,MAAM,UAAU;AACtB,QAAI;AACJ,QAAI;AACF,YAAM,MAAM,eAAe,MAAM,KAAK,WAAW;AAAA,IACnD,SAAS,KAAK;AACZ,iBAAW,GAAG;AACd,aAAO;AAAA,IACT;AAEA,QAAI,IAAI,WAAW,wBAAyB;AAC5C,QAAI,IAAI,WAAW,aAAa;AAC9B,oBAAc;AACd;AAAA,IACF;AACA,QAAI,IAAI,WAAW,iBAAiB;AAClC,iBAAW,IAAI,MAAM,qDAAqD,CAAC;AAC3E,aAAO;AAAA,IACT;AACA,QAAI,IAAI,WAAW,iBAAiB;AAClC,iBAAW,IAAI,MAAM,qDAAqD,CAAC;AAC3E,aAAO;AAAA,IACT;AAEA,WAAO,OAAO,KAAK,KAAK,KAAK,OAAO;AAAA,EACtC;AAEA,aAAW,IAAI,MAAM,wDAAwD,CAAC;AAC9E,SAAO;AACT;AAEA,eAAe,OACb,UAOA,KACA,WACiB;AACjB,QAAM,EAAE,SAAS,QAAQ,KAAK,UAAU,SAAS,SAAS,UAAU,IAAI;AACxE,MAAI;AACJ,eAAa,wBAAwB,EAAE,KAAK,QAAQ,IAAI,CAAC,GAAG;AAE5D,QAAM,QAAQ,MAAM,YAAY,GAAG;AACnC,QAAM,SAAS,MAAM,YAAY;AACjC,QAAM,UAAU,WAAW,KAAK;AAGhC,QAAM,UAAkC,EAAE,gBAAgB,OAAO;AACjE,MAAI,WAAW,YAAY,iBAAkB,SAAQ,kBAAkB;AACvE,QAAM,UAAU,MAAM,UAAU,KAAK,SAAS,OAAO;AACrD,eAAa,2BAA2B,IAAI,KAAK,OAAO,CAAC,GAAG;AAE5D,MAAI;AACF,UAAM,WAAW,MAAM,eAAe;AAAA,MACpC;AAAA,MACA;AAAA,MACA,SAAS,WAAW;AAAA,MACpB;AAAA,MACA;AAAA,MACA,UAAS,oBAAI,KAAK,GAAE,YAAY;AAAA,IAClC,CAAC;AACD,iBAAa,yBAAyB,QAAQ,QAAQ,CAAC,2BAA2B;AAAA,EACpF,QAAQ;AACN,iBAAa,0DAA0D;AAAA,EACzE;AAMA,MAAI;AACJ,MAAI,QAAQ;AACV,UAAM,KAAK,MAAM,qBAAqB,GAAG;AACzC,QAAI,WAAW;AACb,gBAAU,cAAc,WAAW,SAAS,EAAE,QAAG;AACjD,YAAM,OAAO,MAAM,WAAW,IAAI,aAAa,GAAG;AAClD,UAAI,SAAS,EAAG,cAAa,aAAa,WAAW,GAAG;AAAA,UACnD,cAAa,wBAAwB,eAAe,IAAI,WAAW,CAAC,EAAE;AAAA,IAC7E,OAAO;AACL,gBAAU,yBAAyB,eAAe,IAAI,WAAW,CAAC,EAAE;AAAA,IACtE;AACA,cAAU,OAAO,WAAW;AAAA,EAC9B,WAAW,MAAM,YAAY,YAAY,MAAM,YAAY,QAAQ;AACjE,UAAM,UACJ,MAAM,YAAY,WACd,MAAM,iBAAiB,GAAG,IAC1B,MAAM,eAAe,GAAG;AAC9B,UAAM,WAAW,SAAS,SAAS;AACnC,cACE,MAAM,YAAY,WACd,0CACA;AAAA,EACR,OAAO;AACL;AAAA,MACE,YAAY,WAAW,KAAK,CAAC,QAAQ,aAAa,KAAK,CAAC;AAAA,IAC1D;AACA,cACE,uBAAuB,aAAa,KAAK,CAAC;AAAA,EAG9C;AAEA,oBAAkB,SAAS,SAAS,WAAW,CAAC;AAChD,SAAO;AACT;AAGA,eAAe,WAAW,SAAqB,WAAmC;AAChF,MAAI,WAAW;AACb,cAAU,yBAAyB,QAAQ,OAAO,SAAI;AACtD,UAAM,OAAO,MAAM,cAAc,SAAS,QAAQ,IAAI,CAAC;AACvD,QAAI,SAAS,EAAG,cAAa,sBAAsB;AAAA,QAC9C,cAAa,wBAAwB,QAAQ,OAAO,EAAE;AAAA,EAC7D,OAAO;AACL,cAAU,yBAAyB,QAAQ,OAAO,EAAE;AAAA,EACtD;AACF;AAEA,IAAM,aAAa;AASnB,SAAS,kBACP,SACA,SACA,WACM;AACN,QAAM,OAAO,OAAO,SAAI,OAAO,EAAE;AACjC,MAAI;AACJ,MAAI,EAAE,KAAK,2CAA2C,CAAC;AACvD,MAAI;AACJ,MAAI,EAAE,IAAI,oFAA+E,CAAC;AAC1F,MAAI,EAAE,IAAI,IAAI,CAAC;AAEf,MAAI,sEAAsE;AAC1E,MAAI,6CAA6C,OAAO,QAAQ,OAAO,GAAG;AAC1E,MAAI,6CAA6C;AACjD,MAAI,6DAA6D;AACjE,MAAI,sEAAsE;AAC1E,MAAI,yCAAyC;AAC7C,MAAI,uEAAuE;AAC3E,MAAI,wEAAwE;AAC5E,MAAI,yDAAyD;AAC7D,MAAI,sEAAsE;AAC1E,MAAI,iBAAiB,UAAU,EAAE;AACjC,MAAI,EAAE,IAAI,IAAI,CAAC;AACf,MAAI;AACJ,MAAI,EAAE,IAAI,gDAAgD,UAAU,EAAE,CAAC;AACvE,MAAI;AACJ,MAAI,EAAE,IAAI,oBAAoB,IAAI,EAAE,KAAK,GAAG,SAAS,SAAS,CAAC;AAC/D;AAAA,IACE,EAAE;AAAA,MACA,sBAAsB,OAAO;AAAA,IAC/B;AAAA,EACF;AACA,MAAI;AACN;AAEA,SAAS,IAAI,KAAa,MAAsB;AAC9C,SAAO,KAAK,WAAW,GAAG,IAAI,KAAK,MAAM,IAAI,SAAS,CAAC,KAAK,OAAO;AACrE;AAEA,SAAS,QAAQ,MAAsB;AACrC,QAAM,OAAO,QAAQ,IAAI,QAAQ,QAAQ,IAAI;AAC7C,SAAO,QAAQ,KAAK,WAAW,IAAI,IAAI,IAAI,KAAK,MAAM,KAAK,MAAM,CAAC,KAAK;AACzE;;;AClSA,SAAS,KAAK,KAAqB;AACjC,SAAO,IAAI,SAAS,KAAK,IAAI,MAAM,GAAG,CAAC,IAAI,WAAM,IAAI,MAAM,EAAE,IAAI;AACnE;AAEO,SAAS,qBAAqBC,UAAkB;AACrD,QAAM,OAAOA,SAAQ,QAAQ,MAAM,EAAE,YAAY,qCAAqC;AAEtF,OACG,QAAQ,QAAQ,EAChB,YAAY,kDAAkD,EAC9D,OAAO,YAAY;AAClB,UAAM,OAAO,MAAM,eAAe;AAClC,UAAM,SAAS,QAAQ,IAAI;AAE3B,QAAI,CAAC,QAAQ,CAAC,QAAQ;AACpB,UAAI,WAAW,EAAG,WAAU,EAAE,WAAW,MAAM,CAAC;AAAA,UAC3C,WAAU,wBAAwB,EAAE,KAAK,GAAG,WAAW,CAAC,QAAQ,IAAI,aAAa;AACtF;AAAA,IACF;AAEA,QAAI,WAAW,GAAG;AAChB,gBAAU;AAAA,QACR,WAAW;AAAA,QACX,QAAQ,SAAS,QAAQ;AAAA,QACzB,SAAS,MAAM,WAAW;AAAA,QAC1B,UAAU,MAAM,WAAW;AAAA,QAC3B,KAAK,SAAS,QAAQ,OAAO,KAAK,KAAK,MAAM,IAAI;AAAA,MACnD,CAAC;AACD;AAAA,IACF;AAEA,iBAAa,eAAe,OAAO,OAAO,EAAE,KAAK,KAAK,QAAQ,IAAI,CAAC,KAAK,GAAG;AAC3E,QAAI,MAAM;AACR,cAAQ,IAAI,EAAE,IAAI,aAAa,IAAI,KAAK,QAAQ,IAAI;AACpD,cAAQ,IAAI,EAAE,IAAI,aAAa,IAAI,KAAK,KAAK,MAAM,CAAC;AACpD,cAAQ,IAAI,EAAE,IAAI,aAAa,IAAI,KAAK,OAAO;AAAA,IACjD;AACA,QAAI,OAAQ,SAAQ,IAAI,EAAE,IAAI,6CAA6C,CAAC;AAAA,EAC9E,CAAC;AAEH,OACG,QAAQ,QAAQ,EAChB,YAAY,2DAA2D,EACvE,OAAO,YAAY;AAClB,UAAM,gBAAgB;AACtB,iBAAa,4BAA4B;AAAA,EAC3C,CAAC;AACL;;;ACxCO,SAAS,sBAAsBC,UAAkB;AACtD,EAAAA,SACG,QAAQ,QAAQ,EAChB,YAAY,gEAAgE,EAC5E,OAAO,YAAY;AAClB,UAAM,OAAO,QAAQ,oCAA+B,EAAE,MAAM;AAC5D,QAAI;AACF,YAAM,EAAE,KAAK,IAAI,MAAM,OAAO,IAAoB,YAAY;AAC9D,WAAK,KAAK;AAEV,UAAI,WAAW,GAAG;AAChB,kBAAU,IAAI;AACd;AAAA,MACF;AAEA,YAAM,OAAO,MAAM,eAAe;AAClC,YAAM,WAAW,KAAK,YAAY;AAClC,YAAM,SAAS,KAAK,UAAU;AAE9B,UAAI,KAAM,WAAU,WAAW,EAAE,KAAK,KAAK,QAAQ,IAAI,CAAC,EAAE;AAC1D,UAAI,WAAW,KAAK,SAAS,GAAG;AAC9B,qBAAa,cAAc,QAAQ,gBAAgB,MAAM,qBAAqB;AAAA,MAChF,OAAO;AACL,kBAAU,kEAAkE;AAAA,MAC9E;AAAA,IACF,SAAS,KAAK;AACZ,WAAK,KAAK;AACV,iBAAW,GAAG;AACd,cAAQ,WAAW;AAAA,IACrB;AAAA,EACF,CAAC;AACL;;;ACjCO,SAAS,uBAAuBC,UAAkB;AACvD,QAAM,MAAMA,SAAQ,QAAQ,QAAQ,EAAE,YAAY,0BAA0B;AAE5E,MACG,QAAQ,KAAK,EACb,YAAY,oBAAoB,EAChC,SAAS,SAAS,sBAAsB,EACxC,SAAS,WAAW,cAAc,EAClC,OAAO,CAAC,KAAa,UAAkB;AACtC,YAAQ,KAAK;AAAA,MACX,KAAK;AACH,mBAAW,KAAK;AAChB,qBAAa,mBAAmB,WAAW,CAAC,EAAE;AAC9C;AAAA,MACF;AACE,mBAAW,IAAI,MAAM,uBAAuB,GAAG,wBAAwB,CAAC;AACxE,gBAAQ,WAAW;AAAA,IACvB;AAAA,EACF,CAAC;AAEH,MACG,QAAQ,KAAK,EACb,YAAY,wBAAwB,EACpC,SAAS,SAAS,+BAA+B,EACjD,OAAO,CAAC,QAAiB;AACxB,UAAM,MAAM,EAAE,UAAU,WAAW,GAAG,aAAa,cAAc,EAAE;AACnE,QAAI,OAAO,QAAQ,cAAc,QAAQ,QAAQ;AAC/C,iBAAW,IAAI,MAAM,uBAAuB,GAAG,EAAE,CAAC;AAClD,cAAQ,WAAW;AACnB;AAAA,IACF;AACA,QAAI,WAAW,GAAG;AAChB,gBAAU,QAAQ,aAAa,EAAE,UAAU,IAAI,SAAS,IAAI,QAAQ,SAAS,EAAE,aAAa,IAAI,YAAY,IAAI,GAAG;AACnH;AAAA,IACF;AACA,QAAI,QAAQ,WAAY,SAAQ,IAAI,IAAI,QAAQ;AAAA,aACvC,QAAQ,OAAQ,SAAQ,IAAI,IAAI,WAAW;AAAA,QAC/C,YAAW,CAAC,GAAG,CAAC,KAAK,OAAO,QAAQ,GAAG,EAAG,SAAQ,IAAI,EAAE,KAAK,IAAI,GAAG,IAAI,MAAM,CAAC;AAAA,EACtF,CAAC;AAEH,MACG,QAAQ,OAAO,EACf,YAAY,qCAAqC,EACjD,OAAO,MAAM;AACZ,gBAAY;AACZ,iBAAa,sBAAsB;AAAA,EACrC,CAAC;AACL;;;ACxDA,SAAS,YAAAC,iBAAgB;;;ACqBzB,IAAM,KAAK;AACX,IAAM,UAAU;AAChB,IAAM,QAAQ;AACd,IAAM,SAAS;AACf,IAAM,UAAU;AAEhB,SAAS,aAAa,MAAuB;AAC3C,SACE,aAAa,KAAK,IAAI,KACtB,OAAO,KAAK,IAAI,KAChB,QAAQ,KAAK,IAAI,KACjB,MAAM,KAAK,IAAI,KACf,GAAG,KAAK,IAAI;AAEhB;AAEA,SAAS,SAAS,OAAqB,MAAc,OAAsB;AACzE,MAAI,CAAC,KAAM;AACX,QAAM,KAAK,QAAQ,EAAE,MAAM,QAAQ,MAAM,MAAM,IAAI,EAAE,MAAM,QAAQ,KAAK,CAAC;AAC3E;AAQA,SAAS,OAAO,MAAc,UAAwB,CAAC,GAAiB;AACtE,QAAM,QAAsB,CAAC;AAI7B,QAAM,QACJ;AACF,MAAI,OAAO;AACX,SAAO,KAAK,SAAS,GAAG;AACtB,UAAM,IAAI,MAAM,KAAK,IAAI;AACzB,QAAI,CAAC,GAAG;AACN,eAAS,OAAO,MAAM,QAAQ,SAAS,UAAU,MAAS;AAC1D;AAAA,IACF;AACA,QAAI,EAAE,QAAQ,GAAG;AACf,eAAS,OAAO,KAAK,MAAM,GAAG,EAAE,KAAK,GAAG,QAAQ,SAAS,UAAU,MAAS;AAAA,IAC9E;AACA,UAAM,MAAM,EAAE,CAAC;AACf,QAAI,IAAI,WAAW,GAAG,GAAG;AACvB,eAAS,OAAO,IAAI,MAAM,GAAG,EAAE,GAAG,CAAC,GAAG,SAAS,EAAE,MAAM,OAAO,CAAC,CAAC;AAAA,IAClE,WAAW,IAAI,WAAW,GAAG,GAAG;AAC9B,YAAM,KAAK,0BAA0B,KAAK,GAAG;AAC7C,UAAI,IAAI;AACN,cAAM;AAAA,UACJ,GAAG,OAAO,GAAG,CAAC,KAAK,IAAI;AAAA,YACrB,GAAG;AAAA,YACH,EAAE,MAAM,QAAQ,OAAO,EAAE,MAAM,GAAG,CAAC,KAAK,GAAG,EAAE;AAAA,UAC/C,CAAC;AAAA,QACH;AAAA,MACF;AAAA,IACF,WAAW,IAAI,WAAW,IAAI,KAAK,IAAI,WAAW,IAAI,GAAG;AACvD,YAAM,KAAK,GAAG,OAAO,IAAI,MAAM,GAAG,EAAE,GAAG,CAAC,GAAG,SAAS,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC;AAAA,IACxE,OAAO;AACL,YAAM,KAAK,GAAG,OAAO,IAAI,MAAM,GAAG,EAAE,GAAG,CAAC,GAAG,SAAS,EAAE,MAAM,SAAS,CAAC,CAAC,CAAC;AAAA,IAC1E;AACA,WAAO,KAAK,MAAM,EAAE,QAAQ,IAAI,MAAM;AAAA,EACxC;AACA,SAAO;AACT;AAGA,SAAS,gBAAgB,OAA+B;AACtD,QAAM,MAAoB,CAAC;AAC3B,QAAM,QAAQ,CAAC,MAAM,QAAQ;AAC3B,QAAI,MAAM,EAAG,KAAI,KAAK,EAAE,MAAM,YAAY,CAAC;AAC3C,QAAI,KAAK,GAAG,OAAO,IAAI,CAAC;AAAA,EAC1B,CAAC;AACD,SAAO;AACT;AAEA,SAAS,UACP,OACA,OACA,QACuC;AACvC,QAAM,QAAsB,CAAC;AAC7B,MAAI,IAAI;AACR,WAAS,MAAM,MAAM,CAAC,GAAG,QAAQ,UAAa,OAAO,KAAK,GAAG,GAAG,MAAM,MAAM,CAAC,GAAG;AAC9E,UAAM,KAAK;AAAA,MACT,MAAM;AAAA,MACN,SAAS,CAAC,EAAE,MAAM,aAAa,SAAS,OAAO,IAAI,QAAQ,QAAQ,EAAE,CAAC,EAAE,CAAC;AAAA,IAC3E,CAAC;AACD;AAAA,EACF;AACA,SAAO,EAAE,OAAO,MAAM,EAAE;AAC1B;AAEO,SAAS,iBAAiB,OAA0B;AAEzD,QAAM,MAAM,MAAM,QAAQ,QAAQ,IAAI,EAAE,QAAQ,QAAQ,GAAI;AAC5D,QAAM,QAAQ,IAAI,MAAM,IAAI;AAC5B,QAAM,SAAuB,CAAC;AAC9B,MAAI,IAAI;AACR,SAAO,IAAI,MAAM,QAAQ;AACvB,UAAM,OAAO,MAAM,CAAC,KAAK;AACzB,QAAI,KAAK,KAAK,MAAM,IAAI;AACtB;AACA;AAAA,IACF;AACA,QAAI,GAAG,KAAK,IAAI,GAAG;AACjB,aAAO,KAAK,EAAE,MAAM,iBAAiB,CAAC;AACtC;AACA;AAAA,IACF;AACA,UAAM,IAAI,QAAQ,KAAK,IAAI;AAC3B,QAAI,GAAG;AACL,aAAO,KAAK;AAAA,QACV,MAAM;AAAA,QACN,OAAO,EAAE,QAAQ,EAAE,CAAC,KAAK,KAAK,OAAO;AAAA,QACrC,SAAS,OAAO,EAAE,CAAC,KAAK,EAAE;AAAA,MAC5B,CAAC;AACD;AACA;AAAA,IACF;AACA,QAAI,MAAM,KAAK,IAAI,GAAG;AACpB,YAAM,QAAkB,CAAC;AACzB,eAAS,MAAM,MAAM,CAAC,GAAG,QAAQ,UAAa,MAAM,KAAK,GAAG,GAAG,MAAM,MAAM,CAAC,GAAG;AAC7E,cAAM,KAAK,IAAI,QAAQ,OAAO,EAAE,CAAC;AACjC;AAAA,MACF;AACA,aAAO,KAAK;AAAA,QACV,MAAM;AAAA,QACN,SAAS,CAAC,EAAE,MAAM,aAAa,SAAS,OAAO,MAAM,KAAK,GAAG,CAAC,EAAE,CAAC;AAAA,MACnE,CAAC;AACD;AAAA,IACF;AACA,QAAI,OAAO,KAAK,IAAI,GAAG;AACrB,YAAM,EAAE,OAAO,KAAK,IAAI,UAAU,OAAO,GAAG,MAAM;AAClD,aAAO,KAAK,EAAE,MAAM,cAAc,SAAS,MAAM,CAAC;AAClD,UAAI;AACJ;AAAA,IACF;AACA,QAAI,QAAQ,KAAK,IAAI,GAAG;AACtB,YAAM,EAAE,OAAO,KAAK,IAAI,UAAU,OAAO,GAAG,OAAO;AACnD,aAAO,KAAK,EAAE,MAAM,eAAe,SAAS,MAAM,CAAC;AACnD,UAAI;AACJ;AAAA,IACF;AAEA,UAAM,OAAiB,CAAC;AACxB,aAAS,MAAM,MAAM,CAAC,GAAG,QAAQ,UAAa,IAAI,KAAK,MAAM,MAAM,CAAC,aAAa,GAAG,GAAG,MAAM,MAAM,CAAC,GAAG;AACrG,WAAK,KAAK,GAAG;AACb;AAAA,IACF;AACA,WAAO,KAAK,EAAE,MAAM,aAAa,SAAS,gBAAgB,IAAI,EAAE,CAAC;AAAA,EACnE;AACA,SAAO,EAAE,MAAM,OAAO,SAAS,OAAO;AACxC;;;AD/FA,eAAe,UAAU,KAA8B;AACrD,MAAI,IAAI,WAAW,GAAG,EAAG,SAAQ,MAAMC,UAAS,IAAI,MAAM,CAAC,GAAG,MAAM,GAAG,KAAK;AAC5E,SAAO;AACT;AAEA,eAAe,UACb,QACA,MACkC;AAClC,QAAM,OAAgC,CAAC;AACvC,aAAW,KAAK,QAAQ;AACtB,UAAM,IAAI,KAAK,MAAM,EAAE,GAAG,CAAC;AAC3B,QAAI,MAAM,OAAW;AACrB,QAAI,EAAE,MAAM;AACV,WAAK,EAAE,GAAG,IAAI,KAAK,MAAM,MAAM,UAAU,OAAO,CAAC,CAAC,CAAC;AAAA,IACrD,WAAW,EAAE,QAAQ;AACnB,WAAK,EAAE,GAAG,IAAI,OAAO,CAAC;AAAA,IACxB,WAAW,EAAE,SAAS;AACpB,WAAK,EAAE,GAAG,IAAI,MAAM,QAAQ,MAAM;AAAA,IACpC,OAAO;AACL,WAAK,EAAE,GAAG,IAAI,MAAM,UAAU,OAAO,CAAC,CAAC;AAAA,IACzC;AAAA,EACF;AACA,SAAO;AACT;AAEA,eAAe,UACb,QACA,MACmB;AACnB,QAAM,OAAO,IAAI,SAAS;AAC1B,aAAW,KAAK,QAAQ;AACtB,UAAM,IAAI,KAAK,MAAM,EAAE,GAAG,CAAC;AAC3B,QAAI,MAAM,OAAW;AACrB,QAAI,EAAE,MAAM;AACV,YAAMC,QAAO,OAAO,CAAC;AACrB,YAAM,QAAQ,MAAMD,UAASC,KAAI;AACjC,YAAM,OAAOA,MAAK,MAAM,GAAG,EAAE,IAAI,KAAK,EAAE;AACxC,WAAK,OAAO,EAAE,KAAK,IAAI,KAAK,CAAC,IAAI,WAAW,KAAK,CAAC,CAAC,GAAG,IAAI;AAAA,IAC5D,WAAW,EAAE,MAAM;AAEjB,WAAK,OAAO,EAAE,KAAK,MAAM,UAAU,OAAO,CAAC,CAAC,CAAC;AAAA,IAC/C,OAAO;AACL,WAAK,OAAO,EAAE,KAAK,OAAO,CAAC,CAAC;AAAA,IAC9B;AAAA,EACF;AACA,SAAO;AACT;AAEA,SAAS,WACP,QACA,MAC6C;AAC7C,QAAM,IAAiD,CAAC;AACxD,aAAW,KAAK,QAAQ;AACtB,UAAM,IAAI,KAAK,MAAM,EAAE,GAAG,CAAC;AAC3B,QAAI,MAAM,OAAW;AACrB,MAAE,EAAE,GAAG,IAAI,EAAE,SAAS,OAAO,CAAC,IAAI,OAAO,CAAC;AAAA,EAC5C;AACA,SAAO;AACT;AAGA,SAAS,MAAM,KAAqB;AAClC,SAAO,IAAI,QAAQ,gBAAgB,CAAC,GAAG,OAAe,GAAG,YAAY,CAAC;AACxE;AAEA,SAAS,YAAY,KAAc,QAAiB;AAClD,aAAW,KAAK,QAAQ;AACtB,QAAI,OAAO,EAAE,MAAM,EAAE,eAAe,EAAE,GAAG;AAAA,EAC3C;AACF;AAEA,eAAe,SAAS,MAAoB,SAAmC;AAC7E,QAAM,MAAM,KAAK,WAAW,KAAK;AACjC,MAAI,CAAC,KAAK,OAAQ,QAAO,OAAO,GAAG;AACnC,QAAM,OAAO,MAAM,mBAAmB,OAAO;AAC7C,SAAO,gBAAgB,IAAI,IAAI,GAAG;AACpC;AAEA,SAAS,YAAY,KAAc,QAA0B;AAC3D,MAAI,QAAQ;AACV,QAAI,OAAO,wBAAwB,2CAA2C;AAAA,EAChF;AACA,SAAO;AACT;AAEA,SAAS,KAAK,KAAc;AAC1B,aAAW,GAAG;AACd,UAAQ,WAAW;AACrB;AAEA,SAAS,SAASC,UAAkB,MAAoB;AACtD,QAAM,QAAQA,SAAQ,QAAQ,KAAK,IAAI,EAAE,YAAY,KAAK,QAAQ;AAClE,MAAI,KAAK,MAAO,OAAM,MAAM,KAAK,KAAK;AACtC,QAAM,SAAS,KAAK,UAAU;AAE9B,MAAI,KAAK,MAAM;AACb,UAAM,MAAM;AAAA,MACV,MAAM,QAAQ,MAAM,EAAE,YAAY,QAAQ,KAAK,IAAI,EAAE;AAAA,MACrD,KAAK;AAAA,IACP;AACA,QAAI,KAAK,KAAK,WAAW;AACvB,UAAI,OAAO,eAAe,mBAAmB;AAC7C,UAAI,OAAO,qBAAqB,mBAAmB;AAAA,IACrD;AACA,QAAI,KAAK,KAAK,MAAO,aAAY,KAAK,KAAK,KAAK,KAAK;AACrD,QAAI,OAAO,OAAO,SAAS;AACzB,UAAI;AACF,cAAM,QAAqD,CAAC;AAC5D,YAAI,KAAK,MAAM,WAAW;AACxB,cAAI,KAAK,MAAO,OAAM,QAAQ,OAAO,KAAK,KAAK;AAC/C,cAAI,KAAK,OAAQ,OAAM,SAAS,KAAK;AAAA,QACvC;AACA,YAAI,KAAK,MAAM,MAAO,QAAO,OAAO,OAAO,WAAW,KAAK,KAAK,OAAO,IAAI,CAAC;AAC5E,cAAM,EAAE,KAAK,IAAI,MAAM,KAAK,IAAI,MAAM,SAAS,MAAM,KAAK,OAAO,GAAG,KAAK;AACzE,aAAK,IAAI;AAAA,MACX,SAAS,KAAK;AACZ,aAAK,GAAG;AAAA,MACV;AAAA,IACF,CAAC;AAAA,EACH;AAEA,MAAI,KAAK,KAAK;AACZ,UAAM,MAAM;AAAA,MACV,MACG,QAAQ,KAAK,EACb,YAAY,SAAS,KAAK,IAAI,OAAO,MAAM,EAAE,EAC7C,SAAS,IAAI,MAAM,KAAK,MAAM;AAAA,MACjC,KAAK;AAAA,IACP;AACA,QAAI,OAAO,OAAO,SAAiB,SAAS;AAC1C,UAAI;AACF,cAAMD,QAAO,GAAG,MAAM,SAAS,MAAM,KAAK,OAAO,CAAC,IAAI,mBAAmB,OAAO,CAAC;AACjF,cAAM,EAAE,KAAK,IAAI,MAAM,KAAK,IAAIA,KAAI;AACpC,aAAK,IAAI;AAAA,MACX,SAAS,KAAK;AACZ,aAAK,GAAG;AAAA,MACV;AAAA,IACF,CAAC;AAAA,EACH;AAEA,MAAI,KAAK,QAAQ;AACf,UAAM,MAAM;AAAA,MACV,MAAM,QAAQ,QAAQ,EAAE,YAAY,YAAY,KAAK,IAAI,EAAE;AAAA,MAC3D,KAAK;AAAA,IACP;AACA,gBAAY,KAAK,KAAK,MAAM;AAC5B,QAAI,OAAO,OAAO,SAAS;AACzB,UAAI;AACF,cAAM,OAAO,MAAM,UAAU,KAAK,QAAS,IAAI;AAC/C,cAAM,EAAE,KAAK,IAAI,MAAM,KAAK,KAAK,MAAM,SAAS,MAAM,KAAK,OAAO,GAAG,IAAI;AACzE,aAAK,IAAI;AAAA,MACX,SAAS,KAAK;AACZ,aAAK,GAAG;AAAA,MACV;AAAA,IACF,CAAC;AAAA,EACH;AAEA,MAAI,KAAK,QAAQ;AACf,UAAM,MAAM;AAAA,MACV,MACG,QAAQ,QAAQ,EAChB,YAAY,YAAY,KAAK,IAAI,EAAE,EACnC,SAAS,IAAI,MAAM,KAAK,MAAM;AAAA,MACjC,KAAK;AAAA,IACP;AACA,gBAAY,KAAK,KAAK,MAAM;AAC5B,QAAI,OAAO,OAAO,SAAiB,SAAS;AAC1C,UAAI;AACF,cAAM,OAAO,MAAM,UAAU,KAAK,QAAS,IAAI;AAC/C,cAAMA,QAAO,GAAG,MAAM,SAAS,MAAM,KAAK,OAAO,CAAC,IAAI,mBAAmB,OAAO,CAAC;AACjF,cAAM,EAAE,KAAK,IAAI,MAAM,KAAK,MAAMA,OAAM,IAAI;AAC5C,aAAK,IAAI;AAAA,MACX,SAAS,KAAK;AACZ,aAAK,GAAG;AAAA,MACV;AAAA,IACF,CAAC;AAAA,EACH;AAEA,MAAI,KAAK,QAAQ;AACf,UAAM,MAAM;AAAA,MACV,MACG,QAAQ,QAAQ,EAChB,YAAY,YAAY,KAAK,IAAI,EAAE,EACnC,SAAS,IAAI,MAAM,KAAK,MAAM;AAAA,MACjC,KAAK;AAAA,IACP;AACA,QAAI,OAAO,OAAO,SAAiB,SAAS;AAC1C,UAAI;AACF,cAAMA,QAAO,GAAG,MAAM,SAAS,MAAM,KAAK,OAAO,CAAC,IAAI,mBAAmB,OAAO,CAAC;AACjF,cAAM,KAAK,OAAOA,KAAI;AACtB,qBAAa,WAAW,KAAK,KAAK,QAAQ,MAAM,EAAE,CAAC,IAAI,OAAO,GAAG;AAAA,MACnE,SAAS,KAAK;AACZ,aAAK,GAAG;AAAA,MACV;AAAA,IACF,CAAC;AAAA,EACH;AAEA,aAAW,UAAU,KAAK,WAAW,CAAC,GAAG;AACvC,UAAM,UAAU,OAAO,WAAW;AAClC,UAAM,MAAM;AAAA,MACV,UACI,MACG,QAAQ,OAAO,IAAI,EACnB,YAAY,OAAO,QAAQ,EAC3B,SAAS,IAAI,MAAM,KAAK,MAAM,IACjC,MAAM,QAAQ,OAAO,IAAI,EAAE,YAAY,OAAO,QAAQ;AAAA,MAC1D,KAAK;AAAA,IACP;AACA,QAAI,OAAO,OAAQ,aAAY,KAAK,OAAO,MAAM;AACjD,QAAI,OAAO,MAAO,aAAY,KAAK,OAAO,KAAK;AAE/C,UAAM,UAAU,UAAU,SAAoB;AAE5C,YAAM,OAAO,KAAK,KAAK,SAAS,CAAC;AACjC,YAAM,UAAU,UAAW,KAAK,CAAC,IAAe;AAChD,UAAI;AACF,YAAIA,QAAO,MAAM,SAAS,MAAM,KAAK,OAA6B;AAClE,YAAI,QAAS,CAAAA,SAAQ,IAAI,mBAAmB,OAAQ,CAAC;AACrD,QAAAA,SAAQ,OAAO;AAEf,YAAI,OAAO,WAAW,OAAO;AAC3B,gBAAM,QAAQ,OAAO,QAAQ,WAAW,OAAO,OAAO,IAAI,IAAI;AAC9D,gBAAM,EAAE,MAAAE,MAAK,IAAI,MAAM,KAAK,IAAIF,OAAM,KAAK;AAC3C,eAAKE,KAAI;AACT;AAAA,QACF;AACA,YAAI,OAAO,aAAa,OAAO,QAAQ;AACrC,gBAAM,OAAO,MAAM,UAAU,OAAO,QAAQ,IAAI;AAChD,gBAAM,EAAE,MAAAA,OAAM,QAAAC,QAAO,IAAI,MAAM,KAAK;AAAA,YAClC,OAAO,OAAO,YAAY;AAAA,YAC1BH;AAAA,YACA,EAAE,KAAK;AAAA,UACT;AACA,cAAIG,YAAW,IAAK,cAAa,GAAG,OAAO,IAAI,MAAM;AAAA,cAChD,MAAKD,KAAI;AACd;AAAA,QACF;AACA,cAAM,OAAO,OAAO,SAAS,MAAM,UAAU,OAAO,QAAQ,IAAI,IAAI;AACpE,cAAM,EAAE,MAAM,OAAO,IAAI,MAAM,KAAK;AAAA,UAClC,OAAO,OAAO,YAAY;AAAA,UAC1BF;AAAA,UACA,EAAE,KAAK;AAAA,QACT;AACA,YAAI,WAAW,IAAK,cAAa,GAAG,OAAO,IAAI,MAAM;AAAA,YAChD,MAAK,IAAI;AAAA,MAChB,SAAS,KAAK;AACZ,aAAK,GAAG;AAAA,MACV;AAAA,IACF;AACA,QAAI,OAAO,OAAO;AAAA,EACpB;AACF;AAEA,IAAM,SAAS,EAAE,WAAW,KAAK;AAEjC,IAAM,QAAwB;AAAA;AAAA,EAE5B;AAAA,IACE,MAAM;AAAA,IACN,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,MAAM,CAAC;AAAA,IACP,KAAK;AAAA,IACL,QAAQ;AAAA,MACN,EAAE,MAAM,iBAAiB,KAAK,OAAO;AAAA,MACrC,EAAE,MAAM,mBAAmB,KAAK,WAAW;AAAA,IAC7C;AAAA,IACA,QAAQ;AAAA,MACN,EAAE,MAAM,iBAAiB,KAAK,OAAO;AAAA,MACrC,EAAE,MAAM,mBAAmB,KAAK,WAAW;AAAA,IAC7C;AAAA,IACA,QAAQ;AAAA,EACV;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,MAAM,CAAC;AAAA,IACP,QAAQ;AAAA,MACN,EAAE,MAAM,mBAAmB,KAAK,QAAQ;AAAA,MACxC,EAAE,MAAM,iBAAiB,KAAK,QAAQ,aAAa,iBAAiB;AAAA,IACtE;AAAA,IACA,QAAQ;AAAA,EACV;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,MAAM,CAAC;AAAA,IACP,QAAQ;AAAA,MACN,EAAE,MAAM,mBAAmB,KAAK,QAAQ;AAAA,MACxC,EAAE,MAAM,iBAAiB,KAAK,QAAQ,aAAa,iBAAiB;AAAA,IACtE;AAAA,IACA,QAAQ;AAAA,EACV;AAAA,EACA;AAAA;AAAA;AAAA,IAGE,MAAM;AAAA,IACN,OAAO;AAAA,IACP,SAAS;AAAA,IACT,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,MAAM,CAAC;AAAA,IACP,QAAQ;AAAA,MACN,EAAE,MAAM,iBAAiB,KAAK,OAAO;AAAA,MACrC;AAAA,QACE,MAAM;AAAA,QACN,KAAK;AAAA,QACL,aAAa;AAAA,MACf;AAAA,IACF;AAAA,IACA,QAAQ;AAAA,EACV;AAAA;AAAA,EAEA;AAAA,IACE,MAAM;AAAA,IACN,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,MAAM,EAAE,GAAG,OAAO;AAAA,IAClB,KAAK;AAAA,IACL,SAAS;AAAA,MACP;AAAA,QACE,MAAM;AAAA,QACN,UAAU;AAAA,QACV,QAAQ;AAAA,QACR,QAAQ;AAAA,MACV;AAAA,MACA;AAAA,QACE,MAAM;AAAA,QACN,UAAU;AAAA,QACV,QAAQ;AAAA,QACR,QAAQ;AAAA,MACV;AAAA,MACA;AAAA,QACE,MAAM;AAAA,QACN,UAAU;AAAA,QACV,QAAQ;AAAA,QACR,SAAS;AAAA,QACT,QAAQ;AAAA,QACR,WAAW;AAAA,QACX,QAAQ;AAAA,UACN,EAAE,MAAM,iBAAiB,KAAK,QAAQ,MAAM,KAAK;AAAA,UACjD,EAAE,MAAM,oBAAoB,KAAK,WAAW,MAAM,KAAK;AAAA,UACvD,EAAE,MAAM,uBAAuB,KAAK,UAAU,aAAa,gBAAgB;AAAA,UAC3E,EAAE,MAAM,mBAAmB,KAAK,WAAW;AAAA,QAC7C;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,MAAM,EAAE,GAAG,OAAO;AAAA,IAClB,KAAK;AAAA,EACP;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,MAAM,EAAE,GAAG,QAAQ,OAAO,CAAC,EAAE,MAAM,iBAAiB,KAAK,OAAO,CAAC,EAAE;AAAA,EACrE;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,MAAM,EAAE,GAAG,OAAO;AAAA,IAClB,QAAQ,CAAC,EAAE,MAAM,mBAAmB,KAAK,QAAQ,CAAC;AAAA,IAClD,QAAQ;AAAA,EACV;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,MAAM,CAAC;AAAA,IACP,KAAK;AAAA,IACL,QAAQ;AAAA,MACN,EAAE,MAAM,iBAAiB,KAAK,OAAO;AAAA,MACrC,EAAE,MAAM,mBAAmB,KAAK,UAAU,MAAM,KAAK;AAAA,IACvD;AAAA,IACA,QAAQ;AAAA,MACN,EAAE,MAAM,iBAAiB,KAAK,OAAO;AAAA,MACrC,EAAE,MAAM,mBAAmB,KAAK,UAAU,MAAM,KAAK;AAAA,IACvD;AAAA,IACA,QAAQ;AAAA,IACR,SAAS;AAAA,MACP;AAAA,QACE,MAAM;AAAA,QACN,UAAU;AAAA,QACV,QAAQ;AAAA,QACR,SAAS;AAAA,QACT,QAAQ;AAAA,QACR,QAAQ,CAAC,EAAE,MAAM,mBAAmB,KAAK,UAAU,MAAM,KAAK,CAAC;AAAA,MACjE;AAAA,IACF;AAAA,EACF;AAAA;AAAA,EAEA;AAAA,IACE,MAAM;AAAA,IACN,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,MAAM,CAAC;AAAA,IACP,KAAK;AAAA,IACL,QAAQ,CAAC,EAAE,MAAM,iBAAiB,KAAK,OAAO,CAAC;AAAA,IAC/C,QAAQ;AAAA,MACN,EAAE,MAAM,iBAAiB,KAAK,OAAO;AAAA,MACrC,EAAE,MAAM,qBAAqB,KAAK,UAAU,aAAa,oBAAoB;AAAA,IAC/E;AAAA,IACA,QAAQ;AAAA;AAAA;AAAA;AAAA,IAIR,SAAS;AAAA,MACP;AAAA,QACE,MAAM;AAAA,QACN,UAAU;AAAA,QACV,QAAQ;AAAA,QACR,QAAQ;AAAA,MACV;AAAA,MACA;AAAA,QACE,MAAM;AAAA,QACN,UAAU;AAAA,QACV,QAAQ;AAAA,QACR,QAAQ;AAAA,MACV;AAAA,MACA;AAAA,QACE,MAAM;AAAA,QACN,UAAU;AAAA,QACV,QAAQ;AAAA,QACR,QAAQ;AAAA,MACV;AAAA,IACF;AAAA,EACF;AAAA;AAAA,EAEA;AAAA,IACE,MAAM;AAAA,IACN,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,MAAM,CAAC;AAAA,IACP,KAAK;AAAA,IACL,QAAQ,CAAC,EAAE,MAAM,iBAAiB,KAAK,OAAO,CAAC;AAAA,IAC/C,QAAQ;AAAA,MACN,EAAE,MAAM,iBAAiB,KAAK,OAAO;AAAA,MACrC,EAAE,MAAM,uBAAuB,KAAK,UAAU;AAAA,MAC9C,EAAE,MAAM,uBAAuB,KAAK,UAAU;AAAA,MAC9C,EAAE,MAAM,qBAAqB,KAAK,YAAY,MAAM,KAAK;AAAA,MACzD,EAAE,MAAM,sBAAsB,KAAK,cAAc;AAAA,MACjD,EAAE,MAAM,qBAAqB,KAAK,YAAY,MAAM,KAAK;AAAA,IAC3D;AAAA,IACA,QAAQ;AAAA,IACR,SAAS;AAAA,MACP;AAAA,QACE,MAAM;AAAA,QACN,UAAU;AAAA,QACV,QAAQ;AAAA,QACR,QAAQ;AAAA,MACV;AAAA,MACA;AAAA,QACE,MAAM;AAAA,QACN,UAAU;AAAA,QACV,QAAQ;AAAA,QACR,QAAQ;AAAA,QACR,QAAQ,CAAC,EAAE,MAAM,wBAAwB,KAAK,eAAe,CAAC;AAAA,MAChE;AAAA,MACA;AAAA,QACE,MAAM;AAAA,QACN,UAAU;AAAA,QACV,QAAQ;AAAA,QACR,QAAQ;AAAA,MACV;AAAA,MACA;AAAA,QACE,MAAM;AAAA,QACN,UAAU;AAAA,QACV,QAAQ;AAAA,QACR,QAAQ;AAAA,MACV;AAAA,IACF;AAAA,EACF;AAAA;AAAA,EAEA;AAAA,IACE,MAAM;AAAA,IACN,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,MAAM,CAAC;AAAA,IACP,KAAK;AAAA,IACL,QAAQ;AAAA,MACN,EAAE,MAAM,iBAAiB,KAAK,OAAO;AAAA,MACrC,EAAE,MAAM,mBAAmB,KAAK,UAAU,MAAM,KAAK;AAAA,IACvD;AAAA,IACA,QAAQ;AAAA,MACN,EAAE,MAAM,iBAAiB,KAAK,OAAO;AAAA,MACrC,EAAE,MAAM,mBAAmB,KAAK,UAAU,MAAM,KAAK;AAAA,IACvD;AAAA,IACA,QAAQ;AAAA,IACR,SAAS;AAAA,MACP;AAAA,QACE,MAAM;AAAA,QACN,UAAU;AAAA,QACV,QAAQ;AAAA,QACR,QAAQ;AAAA,MACV;AAAA,MACA;AAAA,QACE,MAAM;AAAA,QACN,UAAU;AAAA,QACV,QAAQ;AAAA,QACR,QAAQ;AAAA,QACR,QAAQ,CAAC,EAAE,MAAM,qBAAqB,KAAK,SAAS,CAAC;AAAA,MACvD;AAAA,MACA;AAAA,QACE,MAAM;AAAA,QACN,UAAU;AAAA,QACV,QAAQ;AAAA,QACR,QAAQ;AAAA,QACR,WAAW;AAAA,QACX,QAAQ,CAAC,EAAE,MAAM,iBAAiB,KAAK,QAAQ,MAAM,KAAK,CAAC;AAAA,MAC7D;AAAA,MACA;AAAA,QACE,MAAM;AAAA,QACN,UAAU;AAAA,QACV,QAAQ;AAAA,QACR,QAAQ;AAAA,MACV;AAAA,IACF;AAAA,EACF;AAAA;AAAA,EAEA;AAAA,IACE,MAAM;AAAA,IACN,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,MAAM,CAAC;AAAA,IACP,QAAQ,CAAC,EAAE,MAAM,qBAAqB,KAAK,SAAS,CAAC;AAAA,IACrD,QAAQ;AAAA,IACR,SAAS;AAAA,MACP;AAAA,QACE,MAAM;AAAA,QACN,UAAU;AAAA,QACV,QAAQ;AAAA,QACR,QAAQ;AAAA,MACV;AAAA,IACF;AAAA,EACF;AAAA,EACA;AAAA;AAAA;AAAA,IAGE,MAAM;AAAA,IACN,OAAO;AAAA,IACP,SAAS;AAAA,IACT,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,MAAM,CAAC;AAAA,IACP,QAAQ,CAAC,EAAE,MAAM,iBAAiB,KAAK,OAAO,CAAC;AAAA,IAC/C,QAAQ;AAAA,EACV;AACF;AAOA,IAAM,kBAAkB;AAAA,EACtB,SAAS;AAAA,IACP,OAAO;AAAA,MACL,MAAM;AAAA,MACN,WAAW;AAAA,MACX,gBAAgB;AAAA,MAChB,WAAW;AAAA,MACX,UACE;AAAA,IACJ;AAAA,IACA,iBAAiB;AAAA,MACf,MAAM;AAAA,MACN,WAAW;AAAA,IACb;AAAA,EACF;AAAA,EACA,aAAa;AAAA,IACX,SAAS,EAAE,MAAM,UAAU;AAAA,IAC3B,OAAO;AAAA,MACL,MAAM;AAAA,MACN,SAAS;AAAA,MACT,YAAY;AAAA,MACZ,YAAY,EAAE,MAAM,OAAO,SAAS,CAAC,0BAAgB,EAAE;AAAA,MACvD,eAAe;AAAA,IACjB;AAAA,IACA,MAAM,EAAE,MAAM,QAAQ,UAAU,EAAE,OAAO,GAAG,MAAM,yBAAyB,EAAE;AAAA,IAC7E,YAAY;AAAA,MACV,MAAM;AAAA,MACN,WAAW;AAAA,MACX,SAAS,EAAE,OAAO,GAAG,MAAM,yBAAyB;AAAA,IACtD;AAAA,IACA,QAAQ;AAAA,MACN,MAAM;AAAA,MACN,WAAW;AAAA,QACT,OAAO,EAAE,MAAM,SAAS,WAAW,YAAY,UAAU,MAAM,WAAW,EAAE,OAAO,GAAG,MAAM,OAAO,EAAE;AAAA,QACrG,OAAO,EAAE,MAAM,SAAS,OAAO,qBAAqB,MAAM,QAAQ,IAAI,4DAA4D,UAAU,MAAM;AAAA,MACpJ;AAAA,IACF;AAAA,IACA,MAAM,EAAE,MAAM,QAAQ,WAAW,OAAO;AAAA,EAC1C;AAAA,EACA,MAAM;AAAA,IACJ,IAAI;AAAA,IACJ,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,WACE;AAAA,EACJ;AAAA,EACA,OAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA,SAAS;AAAA,IACP,SAAS;AAAA,MACP,MAAM;AAAA,MACN,WAAW;AAAA,MACX,gBAAgB;AAAA,MAChB,QAAQ,EAAE,MAAM,SAAS,MAAM,QAAQ,IAAI,MAAM,OAAO,OAAO;AAAA,IACjE;AAAA,IACA,OAAO;AAAA,MACL,OAAO;AAAA,QACL,EAAE,IAAI,WAAW,MAAM,WAAW,UAAU,EAAE,GAAG,KAAK,GAAG,GAAG,GAAG,QAAQ,EAAE,MAAM,UAAU,EAAE;AAAA,QAC3F,EAAE,IAAI,SAAS,MAAM,QAAQ,UAAU,EAAE,GAAG,KAAK,GAAG,IAAI,GAAG,QAAQ,EAAE,MAAM,QAAQ,UAAU,EAAE,OAAO,GAAG,MAAM,QAAQ,EAAE,EAAE;AAAA,QAC3H;AAAA,UACE,IAAI;AAAA,UACJ,MAAM;AAAA,UACN,UAAU,EAAE,GAAG,KAAK,GAAG,IAAI;AAAA,UAC3B,QAAQ;AAAA,YACN,MAAM;AAAA,YACN,SAAS;AAAA,YACT,SAAS,EAAE,MAAM,OAAO,SAAS,CAAC,EAAE,MAAM,aAAa,SAAS,CAAC,EAAE,MAAM,QAAQ,MAAM,yBAAyB,CAAC,EAAE,CAAC,EAAE;AAAA,UACxH;AAAA,QACF;AAAA,QACA,EAAE,IAAI,SAAS,MAAM,QAAQ,UAAU,EAAE,GAAG,KAAK,GAAG,IAAI,GAAG,QAAQ,EAAE,MAAM,OAAO,EAAE;AAAA,MACtF;AAAA,MACA,OAAO;AAAA,QACL,EAAE,IAAI,MAAM,QAAQ,WAAW,QAAQ,QAAQ;AAAA,QAC/C,EAAE,IAAI,MAAM,QAAQ,SAAS,QAAQ,SAAS;AAAA,QAC9C,EAAE,IAAI,MAAM,QAAQ,UAAU,QAAQ,QAAQ;AAAA,MAChD;AAAA,IACF;AAAA,EACF;AACF;AAEA,SAAS,2BAA2B;AAClC,MAAI,uEAAuE;AAC3E,MAAI,+BAA+B;AACnC,MAAI,KAAK,UAAU,gBAAgB,SAAS,MAAM,CAAC,CAAC;AACpD,MAAI,wDAAwD;AAC5D,MAAI,KAAK,UAAU,gBAAgB,aAAa,MAAM,CAAC,CAAC;AACxD,MAAI,SAAS;AACb,MAAI,KAAK,UAAU,gBAAgB,MAAM,MAAM,CAAC,CAAC;AACjD,MAAI,UAAU;AACd,aAAW,KAAK,gBAAgB,MAAO,KAAI,YAAO,CAAC,EAAE;AACrD,MAAI,yCAAyC;AAC7C,MAAI,KAAK,UAAU,gBAAgB,SAAS,MAAM,CAAC,CAAC;AACpD,MAAI;AAAA,kCAAqC;AACzC;AAAA,IACE,KAAK,WAAW,CAAC;AAAA;AAAA;AAAA;AAAA,EAInB;AACF;AAGA,SAAS,cAAc,KAGrB;AACA,QAAM,IAAI,wEAAwE;AAAA,IAChF,IAAI,KAAK;AAAA,EACX;AACA,MAAI,CAAC,GAAG;AACN,UAAM,IAAI,MAAM,mBAAmB,GAAG,6BAA6B;AAAA,EACrE;AACA,QAAM,KAAK,EAAE,CAAC,KAAK,IAAI,YAAY;AACnC,QAAM,OAAO,EAAE,WAAW,GAAG,IAAI,SAAS,EAAE,WAAW,GAAG,IAAI,UAAU;AACxE,SAAO,EAAE,OAAO,OAAO,EAAE,CAAC,CAAC,GAAG,KAAK;AACrC;AAMA,SAAS,mBAAmB,KAAsC;AAChE,QAAM,IAAI,iCAAiC,KAAK,GAAG;AACnD,MAAI,CAAC,GAAG;AACN,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AACA,QAAM,KAAK,EAAE,CAAC,MAAM,OAAO,QAAQ,EAAE,CAAC,MAAM,MAAM,aAAa;AAC/D,SAAO,EAAE,MAAM,SAAS,OAAO,EAAE,CAAC,KAAK,IAAI,KAAK,GAAG,IAAI,OAAO,EAAE,CAAC,KAAK,GAAG;AAC3E;AAEA,SAAS,qBAAqBC,UAAkB;AAC9C,QAAM,MAAMA,SAAQ,SAAS,KAAK,CAAC,QAAQ,IAAI,KAAK,MAAM,WAAW;AACrE,MAAI,CAAC,IAAK;AAEV,MACG,QAAQ,YAAY,EACpB;AAAA,IACC;AAAA,EACF,EACC,OAAO,kBAAkB,mCAAmC,EAC5D,OAAO,oBAAoB,qCAAqC,EAChE;AAAA,IACC;AAAA,IACA;AAAA,EACF,EACC,OAAO,wBAAwB,2CAA2C,EAC1E,OAAO,OAAO,IAAY,SAAkC;AAC3D,QAAI;AACF,YAAM,OAAO,MAAM,mBAAmB,KAAK,OAA6B;AACxE,YAAM,OAAO,gBAAgB,IAAI;AACjC,YAAM,OAAgC,CAAC;AACvC,UAAI,KAAK,UAAU,QAAW;AAC5B,aAAK,QAAQ,KAAK,MAAM,MAAM,UAAU,OAAO,KAAK,KAAK,CAAC,CAAC;AAAA,MAC7D;AACA,UAAI,KAAK,YAAY,QAAW;AAC9B,aAAK,UAAU,KAAK,MAAM,MAAM,UAAU,OAAO,KAAK,OAAO,CAAC,CAAC;AAAA,MACjE;AACA,YAAM,WAAW,KAAK;AACtB,UAAI,aAAa,UAAa,OAAO,QAAQ,MAAM,QAAQ;AACzD,aAAK,oBAAoB,OAAO,QAAQ;AAAA,MAC1C,OAAO;AACL,cAAM,EAAE,MAAAC,MAAK,IAAI,MAAM,KAAK,IAAI,GAAG,IAAI,IAAI,mBAAmB,EAAE,CAAC,EAAE;AACnE,aAAK,oBACFA,MAAqC,kBAAkB;AAAA,MAC5D;AACA,YAAM,EAAE,KAAK,IAAI,MAAM,KAAK;AAAA,QAC1B,GAAG,IAAI,IAAI,mBAAmB,EAAE,CAAC;AAAA,QACjC;AAAA,MACF;AACA,WAAK,IAAI;AAAA,IACX,SAAS,KAAK;AACZ,WAAK,GAAG;AAAA,IACV;AAAA,EACF,CAAC;AAEH,MACG,QAAQ,cAAc,EACtB,YAAY,mCAAmC,EAC/C,eAAe,kBAAkB,6BAA6B,EAC9D;AAAA,IACC;AAAA,IACA;AAAA,EACF,EACC,OAAO,gBAAgB,0CAA0C,EACjE,OAAO,wBAAwB,2CAA2C,EAC1E,OAAO,OAAO,IAAY,SAAkC;AAC3D,QAAI;AACF,YAAM,OAAO,MAAM,mBAAmB,KAAK,OAA6B;AACxE,YAAM,OAAO,gBAAgB,IAAI;AACjC,YAAM,OAAgC,EAAE,SAAS,OAAO,KAAK,MAAM,EAAE;AACrE,UAAI,KAAK,WAAW,QAAW;AAC7B,aAAK,SAAS,KAAK,MAAM,MAAM,UAAU,OAAO,KAAK,MAAM,CAAC,CAAC;AAAA,MAC/D;AACA,UAAI,KAAK,OAAO,OAAW,MAAK,kBAAkB,OAAO,KAAK,EAAE;AAChE,YAAM,EAAE,KAAK,IAAI,MAAM,KAAK;AAAA,QAC1B,GAAG,IAAI,IAAI,mBAAmB,EAAE,CAAC;AAAA,QACjC;AAAA,MACF;AACA,WAAK,IAAI;AAAA,IACX,SAAS,KAAK;AACZ,WAAK,GAAG;AAAA,IACV;AAAA,EACF,CAAC;AAEH,MACG,QAAQ,iBAAiB,EACzB,YAAY,qCAAqC,EACjD,eAAe,kBAAkB,6BAA6B,EAC9D,OAAO,gBAAgB,yCAAyC,EAChE;AAAA,IACC;AAAA,IACA;AAAA,EACF,EACC,OAAO,wBAAwB,2CAA2C,EAC1E,OAAO,OAAO,IAAY,SAAkC;AAC3D,QAAI;AACF,YAAM,OAAO,MAAM,mBAAmB,KAAK,OAA6B;AACxE,YAAM,OAAO,gBAAgB,IAAI;AACjC,YAAM,OAAgC,EAAE,SAAS,OAAO,KAAK,MAAM,EAAE;AACrE,UAAI,KAAK,OAAO,OAAW,MAAK,kBAAkB,OAAO,KAAK,EAAE;AAChE,UAAI,KAAK,WAAW,QAAW;AAC7B,aAAK,SAAS,KAAK,MAAM,MAAM,UAAU,OAAO,KAAK,MAAM,CAAC,CAAC;AAAA,MAC/D;AACA,YAAM,EAAE,KAAK,IAAI,MAAM,KAAK;AAAA,QAC1B,GAAG,IAAI,IAAI,mBAAmB,EAAE,CAAC;AAAA,QACjC;AAAA,MACF;AACA,WAAK,IAAI;AAAA,IACX,SAAS,KAAK;AACZ,WAAK,GAAG;AAAA,IACV;AAAA,EACF,CAAC;AAEH,MACG,QAAQ,eAAe,EACvB,YAAY,mEAAmE,EAC/E,OAAO,wBAAwB,2CAA2C,EAC1E,OAAO,OAAO,IAAY,SAAkC;AAC3D,QAAI;AACF,YAAM,OAAO,MAAM,mBAAmB,KAAK,OAA6B;AACxE,YAAM,EAAE,KAAK,IAAI,MAAM,KAAK;AAAA,QAC1B,gBAAgB,IAAI,cAAc,mBAAmB,EAAE,CAAC;AAAA,MAC1D;AACA,UAAI,WAAW,GAAG;AAChB,kBAAU,IAAI;AACd;AAAA,MACF;AACA,YAAM,SAAS;AAIf,UAAI,OAAO,IAAI;AACb,qBAAa,yBAAyB;AACtC;AAAA,MACF;AACA,iBAAW,sBAAsB,OAAO,OAAO,MAAM,aAAa;AAClE,iBAAW,KAAK,OAAO,QAAQ;AAC7B,cAAM,QAAQ,EAAE,SAAS,EAAE,IAAI,KAAK,EAAE,MAAM,GAAG,IAAI;AACnD,YAAI,YAAO,EAAE,OAAO,GAAG,KAAK,EAAE;AAAA,MAChC;AACA,cAAQ,WAAW;AAAA,IACrB,SAAS,KAAK;AACZ,WAAK,GAAG;AAAA,IACV;AAAA,EACF,CAAC;AAEH,MACG,QAAQ,QAAQ,EAChB,YAAY,+DAA+D,EAC3E,OAAO,MAAM;AACZ,QAAI,WAAW,EAAG,WAAU,eAAe;AAAA,QACtC,0BAAyB;AAAA,EAChC,CAAC;AAEH,MACG,QAAQ,UAAU,EAClB;AAAA,IACC;AAAA,EACF,EACC,eAAe,iBAAiB,eAAe,EAC/C;AAAA,IACC;AAAA,IACA;AAAA,EACF,EACC,OAAO,yBAAyB,mCAAmC,EACnE,OAAO,qBAAqB,iDAAiD,EAC7E;AAAA,IACC;AAAA,IACA;AAAA,EACF,EACC,OAAO,qBAAqB,0CAA0C,EACtE,eAAe,uBAAuB,eAAe,EACrD,OAAO,wBAAwB,iDAAiD,EAChF,OAAO,sBAAsB,0BAA0B,EACvD,OAAO,aAAa,oCAAoC,EACxD,OAAO,aAAa,sEAAsE,EAC1F,OAAO,mBAAmB,sEAAsE,EAChG,OAAO,wBAAwB,2CAA2C,EAC1E,OAAO,OAAO,SAAkC;AAC/C,QAAI;AACF,YAAM,OAAO,MAAM,mBAAmB,KAAK,OAA6B;AACxE,YAAM,OAAO,gBAAgB,IAAI;AAGjC,UAAI;AACJ,UAAI,KAAK,aAAa,QAAW;AAC/B,iBAAS,KAAK,MAAM,MAAM,UAAU,OAAO,KAAK,QAAQ,CAAC,CAAC;AAAA,MAC5D,WAAW,KAAK,kBAAkB,QAAW;AAC3C,iBAAS,mBAAmB,OAAO,KAAK,aAAa,CAAC;AAAA,MACxD;AAEA,YAAM,UAAU,KAAK,QACjB;AAAA,QACE,MAAM;AAAA,QACN,WAAW,OAAO,KAAK,KAAK;AAAA,QAC5B,gBAAgB,KAAK,mBAAmB;AAAA,QACxC,GAAI,SAAS,EAAE,OAAO,IAAI,CAAC;AAAA,MAC7B,IACA,EAAE,MAAM,mBAAmB,GAAI,SAAS,EAAE,OAAO,IAAI,CAAC,EAAG;AAE7D,YAAM,UACJ,KAAK,WAAW,SACZ,iBAAiB,OAAO,KAAK,MAAM,CAAC,IACpC,KAAK,aAAa,SAChB,iBAAiB,OAAO,KAAK,QAAQ,CAAC,IACtC;AAER,YAAM,QAAmC;AAAA,QACvC,EAAE,IAAI,WAAW,MAAM,WAAW,UAAU,EAAE,GAAG,KAAK,GAAG,GAAG,GAAG,QAAQ,EAAE,MAAM,UAAU,EAAE;AAAA,MAC7F;AACA,YAAM,QAAmC,CAAC;AAC1C,UAAI,OAAO;AACX,UAAI,IAAI;AACR,UAAI,KAAK,SAAS,QAAW;AAC3B,cAAM,KAAK;AAAA,UACT,IAAI;AAAA,UACJ,MAAM;AAAA,UACN,UAAU,EAAE,GAAG,KAAK,EAAE;AAAA,UACtB,QAAQ,EAAE,MAAM,QAAQ,UAAU,cAAc,OAAO,KAAK,IAAI,CAAC,EAAE;AAAA,QACrE,CAAC;AACD,cAAM,KAAK,EAAE,IAAI,UAAU,QAAQ,MAAM,QAAQ,QAAQ,CAAC;AAC1D,eAAO;AACP,aAAK;AAAA,MACP;AACA,YAAM,KAAK;AAAA,QACT,IAAI;AAAA,QACJ,MAAM;AAAA,QACN,UAAU,EAAE,GAAG,KAAK,EAAE;AAAA,QACtB,QAAQ;AAAA,UACN,MAAM;AAAA,UACN,SAAS,OAAO,KAAK,OAAO;AAAA,UAC5B,GAAI,UAAU,EAAE,QAAQ,IAAI,CAAC;AAAA,QAC/B;AAAA,MACF,CAAC;AACD,YAAM,KAAK,EAAE,IAAI,WAAW,QAAQ,MAAM,QAAQ,SAAS,CAAC;AAC5D,aAAO;AACP,WAAK;AACL,YAAM,KAAK;AAAA,QACT,IAAI;AAAA,QACJ,MAAM;AAAA,QACN,UAAU,EAAE,GAAG,KAAK,EAAE;AAAA,QACtB,QAAQ,EAAE,MAAM,OAAO;AAAA,MACzB,CAAC;AACD,YAAM,KAAK,EAAE,IAAI,UAAU,QAAQ,MAAM,QAAQ,QAAQ,CAAC;AAC1D,YAAM,QAAQ,EAAE,OAAO,MAAM;AAG7B,UAAI,KAAK,QAAQ;AACf,YAAI,WAAW,EAAG,WAAU,EAAE,MAAM,MAAM,OAAO,KAAK,IAAI,GAAG,SAAS,MAAM,CAAC;AAAA,aACxE;AACH,cAAI,2BAA2B,EAAE,KAAK,OAAO,KAAK,IAAI,CAAC,IAAI,UAAU;AACrE,cAAI,UAAU;AACd,cAAI,KAAK,UAAU,SAAS,MAAM,CAAC,CAAC;AACpC,cAAI,UAAU;AACd,cAAI,KAAK,UAAU,OAAO,MAAM,CAAC,CAAC;AAAA,QACpC;AACA;AAAA,MACF;AAGA,UAAI;AACJ,UAAI,SAAS;AACb,UAAI,KAAK,aAAa;AACpB,cAAM,EAAE,KAAK,IAAI,MAAM,KAAK,IAAI,IAAI;AACpC,cAAM,OAAQ,KAAmD,QAAQ,CAAC;AAC1E,cAAM,QAAQ,KAAK,KAAK,CAAC,MAAM,EAAE,SAAS,OAAO,KAAK,IAAI,CAAC;AAC3D,YAAI,OAAO;AACT,eAAK,MAAM;AACX,mBAAS;AAAA,QACX;AAAA,MACF;AACA,UAAI,CAAC,IAAI;AACP,cAAM,UAAU,MAAM,KAAK,KAAK,MAAM,EAAE,MAAM,OAAO,KAAK,IAAI,EAAE,CAAC;AACjE,aAAM,QAAQ,KAAwB;AAAA,MACxC;AAEA,YAAM,SAAS,MAAM,KAAK,IAAI,GAAG,IAAI,IAAI,EAAE,EAAE;AAC7C,YAAM,MAAO,OAAO,KAAqC,kBAAkB;AAC3E,YAAM,KAAK,IAAI,GAAG,IAAI,IAAI,EAAE,UAAU;AAAA,QACpC;AAAA,QACA;AAAA,QACA,mBAAmB;AAAA,MACrB,CAAC;AACD,UAAI,YAAY;AAChB,UAAI,KAAK,SAAS;AAChB,cAAM,KAAK,QAAQ,QAAQ,GAAG,IAAI,IAAI,EAAE,YAAY,CAAC,CAAC;AACtD,oBAAY;AAAA,MACd;AAEA,UAAI,WAAW,GAAG;AAChB,kBAAU,EAAE,IAAI,MAAM,QAAQ,WAAW,SAAS,MAAM,CAAC;AACzD;AAAA,MACF;AACA,YAAM,OAAO,SAAS,8BAA8B;AACpD;AAAA,QACE,GAAG,IAAI,IAAI,EAAE,GAAG,YAAY,sBAAsB,gBAAgB;AAAA,MACpE;AACA,UAAI,CAAC,WAAW;AACd,kBAAU,uBAAuB,WAAW,CAAC,sBAAsB,EAAE,EAAE;AACvE,kBAAU,+BAA+B,WAAW,CAAC,uBAAuB,EAAE,EAAE;AAAA,MAClF;AACA;AAAA,QACE,4CAA4C,WAAW,CAAC,oBAAoB,EAAE;AAAA,MAChF;AAAA,IACF,SAAS,KAAK;AACZ,WAAK,GAAG;AAAA,IACV;AAAA,EACF,CAAC;AACL;AAEA,SAAS,aAAaD,UAAkB;AACtC,QAAM,SAASA,SACZ,QAAQ,QAAQ,EAChB,YAAY,gEAAgE;AAC/E,SACG,QAAQ,MAAM,EACd,YAAY,6DAA6D,EACzE,OAAO,wBAAwB,2CAA2C,EAC1E,OAAO,OAAO,SAAkC;AAC/C,QAAI;AACF,YAAM,OAAO,MAAM,mBAAmB,KAAK,OAA6B;AACxE,YAAM,EAAE,KAAK,IAAI,MAAM,KAAK,IAAI,gBAAgB,IAAI,SAAS;AAC7D,YAAM,OACH,KAQE,QAAQ,CAAC;AACd,UAAI,WAAW,GAAG;AAChB,kBAAU,IAAI;AACd;AAAA,MACF;AACA,UAAI,KAAK,WAAW,GAAG;AACrB,YAAI,EAAE,IAAI,iBAAiB,CAAC;AAC5B;AAAA,MACF;AACA,UAAI,EAAE,IAAI,uEAAoE,CAAC;AAC/E,iBAAW,KAAK,MAAM;AACpB,cAAM,OAAO,GAAG,EAAE,KAAK,EAAE,GAAG,CAAC,IAAI,EAAE,IAAI,IAAI,EAAE,KAAK,IAAI,EAAE,IAAI,GAAG,CAAC;AAChE,cAAM,SAAS,EAAE,UAAU,OAAO,UAAU,KAAK,UAAU,EAAE,MAAM,CAAC,KAAK;AACzE,YAAI,GAAG,IAAI,SAAM,EAAE,KAAK,GAAG,MAAM,EAAE;AAAA,MACrC;AAAA,IACF,SAAS,KAAK;AACZ,WAAK,GAAG;AAAA,IACV;AAAA,EACF,CAAC;AACL;AAEO,SAAS,yBAAyBA,UAAkB;AACzD,aAAW,QAAQ,MAAO,UAASA,UAAS,IAAI;AAChD,uBAAqBA,QAAO;AAC5B,eAAaA,QAAO;AAGpB,EAAAA,SACG,QAAQ,IAAI,EACZ,YAAY,4CAA4C,EACxD,OAAO,YAAY;AAClB,QAAI;AACF,YAAM,EAAE,KAAK,IAAI,MAAM,KAAK,IAAI,QAAQ;AACxC,WAAK,IAAI;AAAA,IACX,SAAS,KAAK;AACZ,WAAK,GAAG;AAAA,IACV;AAAA,EACF,CAAC;AAGH,EAAAA,SACG,QAAQ,iBAAiB,EACzB,YAAY,+EAA+E,EAC3F,OAAO,wBAAwB,2CAA2C,EAC1E,OAAO,wBAAwB,cAAc,EAC7C,OAAO,sBAAsB,mBAAmB,EAChD,OAAO,4BAA4B,kBAAkB,EACrD,OAAO,OAAO,SAAS;AACtB,QAAI;AACF,YAAM,OAAO,MAAM,mBAAmB,KAAK,OAAO;AAClD,YAAM,OAAgC,CAAC;AACvC,UAAI,KAAK,cAAc,OAAW,MAAK,aAAa,KAAK;AACzD,UAAI,KAAK,aAAa,OAAW,MAAK,YAAY,KAAK;AACvD,UAAI,KAAK,iBAAiB,OAAW,MAAK,iBAAiB,KAAK;AAChE,YAAM,EAAE,KAAK,IAAI,MAAM,KAAK,IAAI,gBAAgB,IAAI,oBAAoB,IAAI;AAC5E,WAAK,IAAI;AAAA,IACX,SAAS,KAAK;AACZ,WAAK,GAAG;AAAA,IACV;AAAA,EACF,CAAC;AAEH,EAAAA,SACG,QAAQ,cAAc,EACtB,YAAY,sDAAsD,EAClE,SAAS,UAAU,+BAA+B,EAClD,OAAO,wBAAwB,2CAA2C,EAC1E,OAAO,OAAO,MAAc,SAAS;AACpC,QAAI;AACF,YAAM,OAAO,MAAM,mBAAmB,KAAK,OAAO;AAClD,YAAM,EAAE,KAAK,IAAI,MAAM,KAAK,IAAI,gBAAgB,IAAI,iBAAiB;AAAA,QACnE,cAAc;AAAA,MAChB,CAAC;AACD,WAAK,IAAI;AAAA,IACX,SAAS,KAAK;AACZ,WAAK,GAAG;AAAA,IACV;AAAA,EACF,CAAC;AACL;;;AXzpCA,IAAM,UAAU,IAAI,QAAQ;AAE5B,QACG,KAAK,QAAQ,EACb,YAAY,kEAAkE,EAC9E,QAAQ,OAAW,EACnB,OAAO,UAAU,0CAA0C,EAC3D,KAAK,aAAa,CAAC,gBAAgB;AAClC,MAAI,YAAY,KAAK,EAAE,KAAM,aAAY,IAAI;AAC/C,CAAC;AAQH,qBAAqB,OAAO;AAC5B,qBAAqB,OAAO;AAC5B,sBAAsB,OAAO;AAC7B,uBAAuB,OAAO;AAC9B,yBAAyB,OAAO;AAEhC,QAAQ,WAAW;","names":["path","path","path","readFile","writeFile","spawn","readFile","readFile","spawn","program","program","program","program","readFile","readFile","path","program","data","status"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@letterapp/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.1",
|
|
4
4
|
"description": "Letter CLI - connect your app to Letter in one command. Interactive, secure device login: no API key ever touches your shell or chat.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"homepage": "https://letter.app",
|