@cowliss/cli 0.13.0 → 0.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/blueprints/abandoned-checkout/events.json +9 -0
- package/blueprints/activity-decay/events.json +1 -0
- package/blueprints/ecommerce/events.json +28 -0
- package/blueprints/mobile-game/events.json +22 -0
- package/blueprints/saas-trial/events.json +18 -0
- package/blueprints/winback/events.json +6 -0
- package/dist/index.js +314 -25
- package/package.json +1 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"checkout.started": {
|
|
3
|
+
"cartId": "cart_10482",
|
|
4
|
+
"checkoutUrl": "https://store.example.com/checkout/10482",
|
|
5
|
+
"total": 110.0
|
|
6
|
+
},
|
|
7
|
+
"order.delivered": {
|
|
8
|
+
"orderId": "ord_10482"
|
|
9
|
+
},
|
|
10
|
+
"order.placed": {
|
|
11
|
+
"orderId": "ord_10482",
|
|
12
|
+
"total": 110.0,
|
|
13
|
+
"items": [
|
|
14
|
+
{
|
|
15
|
+
"name": "Linen shirt",
|
|
16
|
+
"price": 78.0
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
"name": "Canvas tote",
|
|
20
|
+
"price": 32.0
|
|
21
|
+
}
|
|
22
|
+
]
|
|
23
|
+
},
|
|
24
|
+
"product.viewed": {
|
|
25
|
+
"productId": "prod_linen_shirt",
|
|
26
|
+
"category": "apparel"
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"level.completed": {
|
|
3
|
+
"level": 4,
|
|
4
|
+
"score": 1250
|
|
5
|
+
},
|
|
6
|
+
"level.failed": {
|
|
7
|
+
"level": 4,
|
|
8
|
+
"attempts": 3
|
|
9
|
+
},
|
|
10
|
+
"purchase.completed": {
|
|
11
|
+
"packId": "starter_bundle",
|
|
12
|
+
"coins": 500,
|
|
13
|
+
"price": 4.99
|
|
14
|
+
},
|
|
15
|
+
"session.started": {
|
|
16
|
+
"platform": "ios",
|
|
17
|
+
"version": "1.2.0"
|
|
18
|
+
},
|
|
19
|
+
"tutorial.completed": {
|
|
20
|
+
"step": "boss_fight"
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"invite.sent": {
|
|
3
|
+
"recipientEmail": "colleague@example.com",
|
|
4
|
+
"role": "editor"
|
|
5
|
+
},
|
|
6
|
+
"project.created": {
|
|
7
|
+
"projectId": "proj_123",
|
|
8
|
+
"template": "starter"
|
|
9
|
+
},
|
|
10
|
+
"subscription.created": {
|
|
11
|
+
"plan": "pro",
|
|
12
|
+
"seats": 5
|
|
13
|
+
},
|
|
14
|
+
"trial.started": {
|
|
15
|
+
"plan": "trial",
|
|
16
|
+
"days": 14
|
|
17
|
+
}
|
|
18
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -23,6 +23,40 @@ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
|
23
23
|
//#region \0rolldown/runtime.js
|
|
24
24
|
var __commonJSMin = (cb, mod) => () => (mod || (cb((mod = { exports: {} }).exports, mod), cb = null), mod.exports);
|
|
25
25
|
|
|
26
|
+
//#endregion
|
|
27
|
+
//#region ../../packages/shared/src/agent-prompts/index.ts
|
|
28
|
+
function buildEventsPrompt(args) {
|
|
29
|
+
if (!args.sdkPackage?.trim()) throw new Error("sdkPackage is required");
|
|
30
|
+
if (!args.trackMethod?.trim()) throw new Error("trackMethod is required");
|
|
31
|
+
if (!args.referenceUrl?.trim()) throw new Error("referenceUrl is required");
|
|
32
|
+
if (!Array.isArray(args.events) || args.events.length === 0) throw new Error("events list must not be empty");
|
|
33
|
+
const lines = [
|
|
34
|
+
`Send these events from your application using ${args.sdkPackage}:`,
|
|
35
|
+
"",
|
|
36
|
+
`Reference documentation: ${args.referenceUrl}`,
|
|
37
|
+
""
|
|
38
|
+
];
|
|
39
|
+
for (const event of args.events) {
|
|
40
|
+
if (!event.name?.trim()) throw new Error("event name is required");
|
|
41
|
+
lines.push(`### Event: \`${event.name}\``);
|
|
42
|
+
if (event.description) lines.push(`${event.description}`);
|
|
43
|
+
lines.push("");
|
|
44
|
+
if (event.payload) {
|
|
45
|
+
lines.push("Example payload:");
|
|
46
|
+
lines.push("```json");
|
|
47
|
+
lines.push(JSON.stringify(event.payload, null, 2));
|
|
48
|
+
lines.push("```");
|
|
49
|
+
lines.push("");
|
|
50
|
+
}
|
|
51
|
+
lines.push("Code example:");
|
|
52
|
+
lines.push("```ts");
|
|
53
|
+
lines.push(`await ${args.trackMethod}({`, " identifiers: { /* the same identifiers you pass to identify */ },", ` event: ${JSON.stringify(event.name)},`, ` properties: ${JSON.stringify(event.payload ?? {})},`, "});");
|
|
54
|
+
lines.push("```");
|
|
55
|
+
lines.push("");
|
|
56
|
+
}
|
|
57
|
+
return lines.join("\n");
|
|
58
|
+
}
|
|
59
|
+
|
|
26
60
|
//#endregion
|
|
27
61
|
//#region ../../packages/shared/src/constants.ts
|
|
28
62
|
/** The public docs site, for the mail and the pages that point people at it. */
|
|
@@ -99,6 +133,12 @@ const IDENTIFIER_KINDS = [
|
|
|
99
133
|
"email"
|
|
100
134
|
];
|
|
101
135
|
/**
|
|
136
|
+
* The monthly free allowance, in micro-dollars. Every org gets it, no plan
|
|
137
|
+
* required: it resets at each UTC calendar month and does not roll over.
|
|
138
|
+
* Spend past it draws the prepaid wallet balance.
|
|
139
|
+
*/
|
|
140
|
+
const FREE_ALLOWANCE_MICROS = 4e6;
|
|
141
|
+
/**
|
|
102
142
|
* The fixed-amount one-time top-ups, in micro-dollars ($10 / $50 / $200,
|
|
103
143
|
* provisional). A topup is a single Stripe Checkout payment; the wallet
|
|
104
144
|
* credit carries over month to month. Provisional numbers live here so a
|
|
@@ -109,6 +149,8 @@ const TOPUP_PRESETS_MICROS = [
|
|
|
109
149
|
5e7,
|
|
110
150
|
2e8
|
|
111
151
|
];
|
|
152
|
+
/** Micro-dollars in one dollar; the only place a display conversion starts. */
|
|
153
|
+
const MICRO_DOLLARS_PER_DOLLAR = 1e6;
|
|
112
154
|
/**
|
|
113
155
|
* The marketing purpose by name, since it is the one every gate, the
|
|
114
156
|
* unsubscribe route, and the developer's own toggle all reach for.
|
|
@@ -5687,6 +5729,10 @@ const walletTopupSchema = z.object({
|
|
|
5687
5729
|
completedAt: z.iso.datetime().nullable()
|
|
5688
5730
|
});
|
|
5689
5731
|
const listWalletTopupsQuerySchema = paginationQuerySchema;
|
|
5732
|
+
/** Micro-dollars as a plain dollar string, for the whole-dollar constants. */
|
|
5733
|
+
function dollars(micros) {
|
|
5734
|
+
return `$${micros / MICRO_DOLLARS_PER_DOLLAR}`;
|
|
5735
|
+
}
|
|
5690
5736
|
|
|
5691
5737
|
//#endregion
|
|
5692
5738
|
//#region ../../packages/shared/src/governance.ts
|
|
@@ -7673,7 +7719,10 @@ const templateSchema = z.object({
|
|
|
7673
7719
|
* manifest entry, and searching inside that is a query the database layer
|
|
7674
7720
|
* would have to grow a helper for. The badges in each row still show them.
|
|
7675
7721
|
*/
|
|
7676
|
-
const listTemplatesQuerySchema = paginationQuerySchema.extend({
|
|
7722
|
+
const listTemplatesQuerySchema = paginationQuerySchema.extend({
|
|
7723
|
+
appId: appIdSchema.optional(),
|
|
7724
|
+
q: z.string().trim().max(200, "q must be at most 200 characters").optional().transform((value) => value ? value : void 0)
|
|
7725
|
+
});
|
|
7677
7726
|
/** The props a preview or a test send renders the template with. */
|
|
7678
7727
|
const renderTemplateBodySchema = z.object({ data: z.object({ props: z.record(z.string(), z.unknown()).default({}) }) });
|
|
7679
7728
|
/** A test send is a delivery like any other, so it takes the same props. */
|
|
@@ -9604,8 +9653,11 @@ const createRoute = (routeConfig) => {
|
|
|
9604
9653
|
//#region ../../packages/shared/src/contract/define.ts
|
|
9605
9654
|
/**
|
|
9606
9655
|
* `createRoute` with the contract's additions: a required `operationId`
|
|
9607
|
-
* (`<tag>.<verb>`, kept as a literal type so handler maps can key on it)
|
|
9608
|
-
* the `surfaces` block.
|
|
9656
|
+
* (`<tag>.<verb>`, kept as a literal type so handler maps can key on it),
|
|
9657
|
+
* `tags` drawn from `TAG_DOCS`, and the `surfaces` block. An optional
|
|
9658
|
+
* `description` is the text the summary cannot carry: it renders under the
|
|
9659
|
+
* operation in the docs, after the summary in the MCP tool, and after the
|
|
9660
|
+
* options in `cow <command> --help`.
|
|
9609
9661
|
*/
|
|
9610
9662
|
function defineRoute(config) {
|
|
9611
9663
|
return createRoute(config);
|
|
@@ -9746,6 +9798,7 @@ const apps = defineModule(defineRoute({
|
|
|
9746
9798
|
operationId: "apps.update",
|
|
9747
9799
|
tags: ["apps"],
|
|
9748
9800
|
summary: "Rename or archive an app",
|
|
9801
|
+
description: "Admin only. Archiving is permanent: an archived app cannot be unarchived or modified. Renaming changes the display name only; the app id never changes.",
|
|
9749
9802
|
security: SESSION_AUTH,
|
|
9750
9803
|
request: {
|
|
9751
9804
|
params: params$11,
|
|
@@ -9779,9 +9832,7 @@ const apps = defineModule(defineRoute({
|
|
|
9779
9832
|
//#region ../../packages/shared/src/contract/artifacts.ts
|
|
9780
9833
|
/**
|
|
9781
9834
|
* Raw artifact transfer, the one place in the API where bytes travel outside
|
|
9782
|
-
* an envelope
|
|
9783
|
-
* the org does not already have, so a rebuild that changes one journey
|
|
9784
|
-
* uploads one bundle.
|
|
9835
|
+
* an envelope (the story is the `artifacts` intro in `tags.ts`).
|
|
9785
9836
|
*
|
|
9786
9837
|
* Both routes are hidden from the CLI and MCP surfaces: they are plumbing
|
|
9787
9838
|
* for the composite `cow push`, not something a human or an agent invokes
|
|
@@ -9857,6 +9908,7 @@ const billing = defineModule(defineRoute({
|
|
|
9857
9908
|
operationId: "billing.topups.create",
|
|
9858
9909
|
tags: ["billing"],
|
|
9859
9910
|
summary: "Start a wallet top-up",
|
|
9911
|
+
description: "Admin only. Returns a hosted checkout URL to redirect to. `amountMicros` must be one of the fixed presets.",
|
|
9860
9912
|
security: SESSION_AUTH,
|
|
9861
9913
|
request: { body: jsonBody(createTopupBodySchema) },
|
|
9862
9914
|
responses: {
|
|
@@ -9947,6 +9999,7 @@ const catalog = defineModule(defineRoute({
|
|
|
9947
9999
|
operationId: "catalog.events.setState",
|
|
9948
10000
|
tags: ["catalog"],
|
|
9949
10001
|
summary: "Allow or deny an event name",
|
|
10002
|
+
description: "Denying an event name permanently drops any entries held for it in the review queue.",
|
|
9950
10003
|
security: SESSION_AUTH,
|
|
9951
10004
|
request: {
|
|
9952
10005
|
params: params$10,
|
|
@@ -10003,6 +10056,7 @@ const catalog = defineModule(defineRoute({
|
|
|
10003
10056
|
operationId: "catalog.traits.update",
|
|
10004
10057
|
tags: ["catalog"],
|
|
10005
10058
|
summary: "Update a catalog trait",
|
|
10059
|
+
description: "A trait type is frozen when registered and cannot be changed. Requests that specify a different `type` are rejected.",
|
|
10006
10060
|
security: SESSION_AUTH,
|
|
10007
10061
|
request: {
|
|
10008
10062
|
params: params$10,
|
|
@@ -10020,6 +10074,7 @@ const catalog = defineModule(defineRoute({
|
|
|
10020
10074
|
operationId: "catalog.traits.setState",
|
|
10021
10075
|
tags: ["catalog"],
|
|
10022
10076
|
summary: "Allow or deny a trait name",
|
|
10077
|
+
description: "Denying a trait name permanently drops any entries held for it in the review queue.",
|
|
10023
10078
|
security: SESSION_AUTH,
|
|
10024
10079
|
request: {
|
|
10025
10080
|
params: params$10,
|
|
@@ -10036,6 +10091,7 @@ const catalog = defineModule(defineRoute({
|
|
|
10036
10091
|
operationId: "catalog.quarantine.promote",
|
|
10037
10092
|
tags: ["catalog"],
|
|
10038
10093
|
summary: "Promote a quarantined name into the catalog",
|
|
10094
|
+
description: "Allow-lists the name in the catalog and backfills all held entries. Held events are recorded with their original timestamps, and held trait values apply to profiles in occurrence order.",
|
|
10039
10095
|
security: SESSION_AUTH,
|
|
10040
10096
|
request: { body: jsonBody(quarantineActionBodySchema) },
|
|
10041
10097
|
responses: {
|
|
@@ -10050,6 +10106,7 @@ const catalog = defineModule(defineRoute({
|
|
|
10050
10106
|
operationId: "catalog.quarantine.deny",
|
|
10051
10107
|
tags: ["catalog"],
|
|
10052
10108
|
summary: "Deny a quarantined name",
|
|
10109
|
+
description: "Denies the name in the catalog and permanently drops all entries held for it in the review queue.",
|
|
10053
10110
|
security: SESSION_AUTH,
|
|
10054
10111
|
request: { body: jsonBody(quarantineActionBodySchema) },
|
|
10055
10112
|
responses: {
|
|
@@ -10128,6 +10185,7 @@ const deliveries = defineModule(defineRoute({
|
|
|
10128
10185
|
operationId: "deliveries.sesFeedback",
|
|
10129
10186
|
tags: ["deliveries"],
|
|
10130
10187
|
summary: "SES delivery feedback via SNS (public)",
|
|
10188
|
+
description: "Updates delivery statuses, adds bounced and complained addresses to your suppression list, and revokes consent on complaints. Open tracking events are rejected because open tracking is disabled. Subscription confirmation requests from the platform's email provider are confirmed automatically.",
|
|
10131
10189
|
security: NO_AUTH,
|
|
10132
10190
|
surfaces: HIDDEN_FROM_TOOLS,
|
|
10133
10191
|
request: { body: { content: { "text/plain": { schema: z.string() } } } },
|
|
@@ -10141,6 +10199,7 @@ const deliveries = defineModule(defineRoute({
|
|
|
10141
10199
|
operationId: "deliveries.unsubscribe",
|
|
10142
10200
|
tags: ["deliveries"],
|
|
10143
10201
|
summary: "One-click unsubscribe (public)",
|
|
10202
|
+
description: "Revokes consent for the purpose the email was sent under on the recipient's profile. Setting `all` to `1` revokes the master `marketing` purpose instead, stopping all marketing email.",
|
|
10144
10203
|
security: NO_AUTH,
|
|
10145
10204
|
surfaces: HIDDEN_FROM_TOOLS,
|
|
10146
10205
|
request: { query: tokenQuery.extend({
|
|
@@ -10248,6 +10307,7 @@ const domains = defineModule(defineRoute({
|
|
|
10248
10307
|
operationId: "domains.update",
|
|
10249
10308
|
tags: ["domains"],
|
|
10250
10309
|
summary: "Toggle click tracking",
|
|
10310
|
+
description: "Enabling click tracking requires a verified domain, while disabling is always allowed. Tracking covers every email sent from the domain, including transactional email.",
|
|
10251
10311
|
security: SESSION_AUTH,
|
|
10252
10312
|
request: {
|
|
10253
10313
|
params: params$9,
|
|
@@ -10264,6 +10324,7 @@ const domains = defineModule(defineRoute({
|
|
|
10264
10324
|
operationId: "domains.delete",
|
|
10265
10325
|
tags: ["domains"],
|
|
10266
10326
|
summary: "Give up a sending domain",
|
|
10327
|
+
description: "Sending from this domain stops immediately, and the domain becomes available for any organization to claim.",
|
|
10267
10328
|
security: SESSION_AUTH,
|
|
10268
10329
|
request: { params: params$9 },
|
|
10269
10330
|
responses: {
|
|
@@ -10318,6 +10379,7 @@ const emails = defineModule(defineRoute({
|
|
|
10318
10379
|
operationId: "emails.verify",
|
|
10319
10380
|
tags: ["emails"],
|
|
10320
10381
|
summary: "Verify an email address (public)",
|
|
10382
|
+
description: "Marks the address verified. If the profile associated with the link still carries that address, sets `emailVerified` to `true` and records a `system.email_verified` event.",
|
|
10321
10383
|
security: NO_AUTH,
|
|
10322
10384
|
surfaces: HIDDEN_FROM_TOOLS,
|
|
10323
10385
|
request: { query: tokenQuery },
|
|
@@ -10375,6 +10437,7 @@ const events = defineModule(defineRoute({
|
|
|
10375
10437
|
operationId: "apps.events.seen",
|
|
10376
10438
|
tags: ["events"],
|
|
10377
10439
|
summary: "Whether an app has ever received these event names",
|
|
10440
|
+
description: "A name may be a pattern where `*` matches any run of characters against the whole name. A pattern reports the latest timestamp among matching events, or `null` if the app has never received one.",
|
|
10378
10441
|
security: PIPELINE_AUTH,
|
|
10379
10442
|
surfaces: { cli: false },
|
|
10380
10443
|
request: {
|
|
@@ -10436,6 +10499,7 @@ const executions = defineModule(defineRoute({
|
|
|
10436
10499
|
operationId: "executions.cancel",
|
|
10437
10500
|
tags: ["executions"],
|
|
10438
10501
|
summary: "Cancel live executions of a journey, or of one version",
|
|
10502
|
+
description: "Admin only. Each execution stops when it reaches its next step.",
|
|
10439
10503
|
security: SESSION_AUTH,
|
|
10440
10504
|
request: { body: jsonBody(cancelExecutionsBodySchema) },
|
|
10441
10505
|
responses: {
|
|
@@ -10546,6 +10610,7 @@ const ingestion = defineModule(defineRoute({
|
|
|
10546
10610
|
operationId: "ingestion.batch",
|
|
10547
10611
|
tags: ["ingestion"],
|
|
10548
10612
|
summary: "Import one source's calls in one request",
|
|
10613
|
+
description: "Batch writes skip triggers on raw events, but segments still recompute and a journey triggered by segment entry can fire. A failing item is reported in the response and does not fail the rest of the batch.",
|
|
10549
10614
|
security: API_KEY_AUTH,
|
|
10550
10615
|
surfaces: HIDDEN_FROM_TOOLS,
|
|
10551
10616
|
request: { body: jsonBody(batchBodySchema) },
|
|
@@ -10626,6 +10691,7 @@ const journeys = defineModule(defineRoute({
|
|
|
10626
10691
|
operationId: "journeys.setStatus",
|
|
10627
10692
|
tags: ["journeys"],
|
|
10628
10693
|
summary: "Put journeys on, off or on hold",
|
|
10694
|
+
description: "Admin only. Turning a journey off lets executions in flight finish, while putting it on hold pauses them before their next step. Turning a held journey on resumes them.",
|
|
10629
10695
|
security: PIPELINE_AUTH,
|
|
10630
10696
|
surfaces: { cli: false },
|
|
10631
10697
|
request: { body: jsonBody(setJourneysStatusBodySchema) },
|
|
@@ -10656,6 +10722,7 @@ const journeys = defineModule(defineRoute({
|
|
|
10656
10722
|
operationId: "journeys.delete",
|
|
10657
10723
|
tags: ["journeys"],
|
|
10658
10724
|
summary: "Delete a journey",
|
|
10725
|
+
description: "Admin only. Executions already in flight finish, but no new ones start. A push never deletes a journey, so this is the only way to remove one whose file was deleted from your code.",
|
|
10659
10726
|
security: SESSION_AUTH,
|
|
10660
10727
|
request: { params: params$7 },
|
|
10661
10728
|
responses: {
|
|
@@ -10682,6 +10749,7 @@ const journeys = defineModule(defineRoute({
|
|
|
10682
10749
|
operationId: "journeys.startEnrollment",
|
|
10683
10750
|
tags: ["journeys"],
|
|
10684
10751
|
summary: "Enroll the journey's current members",
|
|
10752
|
+
description: "Admin only. Starts an enrollment for recipients currently in the journey's segment. If an enrollment is already running, this returns the existing enrollment rather than starting another.",
|
|
10685
10753
|
security: SESSION_AUTH,
|
|
10686
10754
|
surfaces: HIDDEN_FROM_TOOLS,
|
|
10687
10755
|
request: { params: params$7 },
|
|
@@ -10709,6 +10777,7 @@ const journeys = defineModule(defineRoute({
|
|
|
10709
10777
|
operationId: "journeys.cancelEnrollment",
|
|
10710
10778
|
tags: ["journeys"],
|
|
10711
10779
|
summary: "Stop a running enrollment",
|
|
10780
|
+
description: "Admin only. Recipients already enrolled remain in the journey and their executions continue. Stopping the enrollment only prevents remaining recipients from enrolling.",
|
|
10712
10781
|
security: SESSION_AUTH,
|
|
10713
10782
|
request: { params: params$7 },
|
|
10714
10783
|
responses: {
|
|
@@ -10722,6 +10791,7 @@ const journeys = defineModule(defineRoute({
|
|
|
10722
10791
|
operationId: "journeys.dryRun",
|
|
10723
10792
|
tags: ["journeys"],
|
|
10724
10793
|
summary: "Dry-run a journey against a real user",
|
|
10794
|
+
description: "Admin only. Runs the journey's latest ready version against the specified profile with sends disabled, delivering no emails or webhooks. The execution proceeds even if the journey is turned off.",
|
|
10725
10795
|
security: SESSION_AUTH,
|
|
10726
10796
|
request: {
|
|
10727
10797
|
params: params$7,
|
|
@@ -10816,6 +10886,7 @@ const pushes = defineModule(defineRoute({
|
|
|
10816
10886
|
operationId: "pushes.create",
|
|
10817
10887
|
tags: ["pushes"],
|
|
10818
10888
|
summary: "Push a built app",
|
|
10889
|
+
description: "Every bundle and the source archive must be uploaded before creating the push. The push is refused if any referenced artifact is missing, or if a journey's `from` address uses a domain your organization has not verified.",
|
|
10819
10890
|
security: PIPELINE_AUTH,
|
|
10820
10891
|
surfaces: HIDDEN_FROM_TOOLS,
|
|
10821
10892
|
request: { body: jsonBody(createPushBodySchema) },
|
|
@@ -11056,6 +11127,7 @@ const segments = defineModule(defineRoute({
|
|
|
11056
11127
|
operationId: "segments.update",
|
|
11057
11128
|
tags: ["segments"],
|
|
11058
11129
|
summary: "Update a segment",
|
|
11130
|
+
description: "Updating a segment's `definition` recomputes its membership over existing profile history. A segment defined in code (in a journey trigger or your project's `segments/` directory) cannot be updated here: change the code and push the project again.",
|
|
11059
11131
|
security: SESSION_AUTH,
|
|
11060
11132
|
request: {
|
|
11061
11133
|
params: params$5,
|
|
@@ -11073,6 +11145,7 @@ const segments = defineModule(defineRoute({
|
|
|
11073
11145
|
operationId: "segments.delete",
|
|
11074
11146
|
tags: ["segments"],
|
|
11075
11147
|
summary: "Delete a segment",
|
|
11148
|
+
description: "Deleting a segment permanently removes all of its members. A segment defined in code (in a journey trigger or your project's `segments/` directory) cannot be deleted here: remove it from your project and push again.",
|
|
11076
11149
|
security: SESSION_AUTH,
|
|
11077
11150
|
request: { params: params$5 },
|
|
11078
11151
|
responses: {
|
|
@@ -11117,6 +11190,7 @@ const settings = defineModule(defineRoute({
|
|
|
11117
11190
|
operationId: "settings.update",
|
|
11118
11191
|
tags: ["settings"],
|
|
11119
11192
|
summary: "Update org settings",
|
|
11193
|
+
description: "Admin only. Lowering `eventRetentionDays` permanently deletes stored events older than the new window.",
|
|
11120
11194
|
security: SESSION_AUTH,
|
|
11121
11195
|
request: { body: jsonBody(updateOrgSettingsBodySchema) },
|
|
11122
11196
|
responses: {
|
|
@@ -11182,6 +11256,7 @@ const settings = defineModule(defineRoute({
|
|
|
11182
11256
|
operationId: "settings.pipelineKeys.revoke",
|
|
11183
11257
|
tags: ["settings"],
|
|
11184
11258
|
summary: "Revoke an org pipeline key",
|
|
11259
|
+
description: "Revocation is permanent. Calls authenticating with this key can still be accepted for up to a minute.",
|
|
11185
11260
|
security: SESSION_AUTH,
|
|
11186
11261
|
request: {
|
|
11187
11262
|
params: z.object({ id: z.string() }),
|
|
@@ -11198,6 +11273,7 @@ const settings = defineModule(defineRoute({
|
|
|
11198
11273
|
operationId: "settings.apiKeys.revoke",
|
|
11199
11274
|
tags: ["settings"],
|
|
11200
11275
|
summary: "Revoke an org API key",
|
|
11276
|
+
description: "Revocation is permanent. Calls authenticating with this key stop being accepted immediately.",
|
|
11201
11277
|
security: SESSION_AUTH,
|
|
11202
11278
|
request: {
|
|
11203
11279
|
params: z.object({ id: z.string() }),
|
|
@@ -11215,10 +11291,9 @@ const settings = defineModule(defineRoute({
|
|
|
11215
11291
|
const appParams = z.object({ appId: z.string() });
|
|
11216
11292
|
const params$4 = z.object({ id: z.string() });
|
|
11217
11293
|
/**
|
|
11218
|
-
*
|
|
11219
|
-
*
|
|
11220
|
-
*
|
|
11221
|
-
* its authorization.
|
|
11294
|
+
* Sources: created and listed under their parent app (so those two routes
|
|
11295
|
+
* carry the `apps` tag), addressed by their own id afterwards. The customer
|
|
11296
|
+
* story is the `sources` intro in `tags.ts`.
|
|
11222
11297
|
*/
|
|
11223
11298
|
const sources = defineModule(defineRoute({
|
|
11224
11299
|
method: "post",
|
|
@@ -11287,6 +11362,7 @@ const sources = defineModule(defineRoute({
|
|
|
11287
11362
|
operationId: "sources.archive",
|
|
11288
11363
|
tags: ["sources"],
|
|
11289
11364
|
summary: "Archive a source",
|
|
11365
|
+
description: "Admin only. Archiving is irreversible and immediately stops this source from accepting calls or webhook deliveries. For an `api` source, this halts ingestion for the parent app until you add a replacement.",
|
|
11290
11366
|
security: SESSION_AUTH,
|
|
11291
11367
|
request: { params: params$4 },
|
|
11292
11368
|
responses: {
|
|
@@ -11335,6 +11411,7 @@ const suppressions = defineModule(defineRoute({
|
|
|
11335
11411
|
operationId: "suppressions.delete",
|
|
11336
11412
|
tags: ["suppressions"],
|
|
11337
11413
|
summary: "Un-suppress an address",
|
|
11414
|
+
description: "Admin only. Clears the address at the email provider so future sends can be attempted. If the provider cannot be reached, the address remains suppressed.",
|
|
11338
11415
|
security: SESSION_AUTH,
|
|
11339
11416
|
request: { params: z.object({ id: z.string() }) },
|
|
11340
11417
|
responses: {
|
|
@@ -11345,19 +11422,160 @@ const suppressions = defineModule(defineRoute({
|
|
|
11345
11422
|
}));
|
|
11346
11423
|
|
|
11347
11424
|
//#endregion
|
|
11348
|
-
//#region ../../packages/shared/src/contract/
|
|
11425
|
+
//#region ../../packages/shared/src/contract/tags.ts
|
|
11349
11426
|
/**
|
|
11350
|
-
*
|
|
11351
|
-
*
|
|
11352
|
-
*
|
|
11353
|
-
*
|
|
11354
|
-
* deleting the key.
|
|
11427
|
+
* The prose for every contract tag: the API reference page's title,
|
|
11428
|
+
* description and intro, the OpenAPI document's `tags[]`, and the CLI's help
|
|
11429
|
+
* line for a command group named after a tag. Route `tags` are typed against
|
|
11430
|
+
* this record, so a new tag without an entry fails typecheck.
|
|
11355
11431
|
*
|
|
11356
|
-
*
|
|
11357
|
-
*
|
|
11358
|
-
|
|
11359
|
-
|
|
11360
|
-
|
|
11432
|
+
* Intro links are docs-site-relative (`/tracking/apps/`): the docs render
|
|
11433
|
+
* them as written, and `buildOpenApiDocument` prefixes them with DOCS_URL.
|
|
11434
|
+
*/
|
|
11435
|
+
const TAG_DOCS = {
|
|
11436
|
+
health: {
|
|
11437
|
+
title: "Health",
|
|
11438
|
+
description: "Liveness and readiness probes.",
|
|
11439
|
+
intro: "These routes are orchestration probes. Both sit outside the `/v1` prefix and need no credential."
|
|
11440
|
+
},
|
|
11441
|
+
ingestion: {
|
|
11442
|
+
title: "Ingestion",
|
|
11443
|
+
description: "POST /v1/identify, /v1/track, and /v1/batch: the org API-key-authenticated write path.",
|
|
11444
|
+
intro: "The write path ingests traits and events using an organization API key (`Authorization: Bearer <key>`). All calls sit behind the organization rate limiter and the billing hard stop, and dedupe retries using `Idempotency-Key` or `messageId`. Every call requires the `sourceId` of an `api` source, from which Cowliss derives and stamps the app (see [Apps tracking](/tracking/apps/)). Calls with an unknown, archived, or non-API `sourceId` are rejected. Event names starting with `system.` are rejected because that prefix is reserved for events Cowliss writes itself (see [Event patterns](/journeys/#the-system-namespace))."
|
|
11445
|
+
},
|
|
11446
|
+
users: {
|
|
11447
|
+
title: "Users",
|
|
11448
|
+
description: "Profiles: list, search, the 360 view, consent editing, merged event history, segment membership, GDPR export and erasure.",
|
|
11449
|
+
intro: "A profile holds what an app knows about a recipient: traits, identifiers, the consent map, merged event history, and segment membership. When a profile is merged into another, requests to its `usr_` id redirect to the survivor's URL. GDPR export and erasure can target a single profile by its `usr_` id, taking everything merged into it, or every profile across your apps that carries an identifier. All routes require session-token authentication, and erasure requires `org:admin`."
|
|
11450
|
+
},
|
|
11451
|
+
consent: {
|
|
11452
|
+
title: "Consent purposes",
|
|
11453
|
+
description: "The consent purposes your organization's profiles answer: `marketing` plus the ones your own code declares.",
|
|
11454
|
+
intro: "Every profile's `consent` map answers these purposes, and every switch that offers one is labelled from here. One is fixed for every organization, `marketing`, the master switch every other purpose sits under; the rest are whatever your code declares under `purposes` in [`cow.json`](/guides/repo/). `transactional` is not here: it is why a receipt or an alert goes out, which is not something a recipient chooses, so nothing stores an answer to it. Purposes are code, like journeys and templates, so this resource is read-only: a push is what adds or relabels one, and nothing ever deletes one, because profiles already hold answers against it."
|
|
11455
|
+
},
|
|
11456
|
+
events: {
|
|
11457
|
+
title: "Events",
|
|
11458
|
+
description: "The org-wide event feed, per-app volume stats, and single-event fetch.",
|
|
11459
|
+
intro: "These routes provide read views over your event history. Events are append-only and never rewritten. Ingestion writes are on the [Ingestion](/reference/api/ingestion/) page."
|
|
11460
|
+
},
|
|
11461
|
+
apps: {
|
|
11462
|
+
title: "Apps",
|
|
11463
|
+
description: "The app registry: your apps and products, and the sources that push into them.",
|
|
11464
|
+
intro: "An app is the attribution unit for one product: it carries no kind, configuration, or credential of its own, and each event and profile carries its `appId`. Inbound pipes into an app are sources created under it, and all of an app's sources land on the same profiles. This includes the first-party `api` source created with the app, which the SDK and HTTP path write through. Routes that address a source by its own id (including the public webhook receiver) are on the [Sources](/reference/api/sources/) page. See the [Apps and sources guide](/tracking/apps/)."
|
|
11465
|
+
},
|
|
11466
|
+
sources: {
|
|
11467
|
+
title: "Sources",
|
|
11468
|
+
description: "Sources addressed by id: config rotation, archiving, and the public webhook receiver.",
|
|
11469
|
+
intro: "A source is one inbound pipe into an [app](/reference/api/apps/): each app is created with the first-party `api` source, and a `kind` is singleton per app. Sources are created and listed under their parent app, and these routes address one by its own `id`. All writes are attributed to the source's parent app. The signing secret in `config` is write-only and never present in any response: `configured` reports whether one is stored. The webhook receiver is public: the caller's signature is the authorization, and the route answers with plain text rather than the envelope. See the [Apps and sources guide](/tracking/apps/)."
|
|
11470
|
+
},
|
|
11471
|
+
catalog: {
|
|
11472
|
+
title: "Catalog",
|
|
11473
|
+
description: "The tracking plan: event and trait definitions, governance states (allow/deny), and the strict-mode quarantine review queue.",
|
|
11474
|
+
intro: "The catalog is the tracking plan: event and trait definitions addressed by name, unique per organization, each in a governance state (`allowed`, `denied`, or `quarantined`, where denied always wins). Ingestion accepts by default, rejecting only denied names and canonical traits that fail format validation. With the org's `ingestionPolicy` set to `strict`, unclassified names are held in the quarantine review queue instead of applied. Property and trait types: " + propertyTypeSchema.options.map((type) => `\`${type}\``).join(", ") + ". Observation infers an array's item type when its members agree, and `array<any>` when they do not or the array is empty."
|
|
11475
|
+
},
|
|
11476
|
+
review: {
|
|
11477
|
+
title: "Review queue",
|
|
11478
|
+
description: "One list over both halves of the review queue: names held unapplied under a strict policy, and names accepted but flagged under a permissive one.",
|
|
11479
|
+
intro: "The review queue brings together everything ingestion parked for a person to decide on, replacing the two lists it unions. A held name is a write a strict policy would not apply until its name is classified; a violation is a name a permissive policy accepted and flagged. Both carry a `kind` and `name` alongside a first and a last sighting, so they list together under one cursor, ordered by last seen. This resource is a read and nothing else, as the per-item verbs stay where they are: allow or deny a held name on the [Catalog](/reference/api/catalog/) page, and resolve or dismiss a violation on the [Violations](/reference/api/violations/) page."
|
|
11480
|
+
},
|
|
11481
|
+
violations: {
|
|
11482
|
+
title: "Violations",
|
|
11483
|
+
description: "Tracking-plan violations: unknown names accepted but flagged, with one-click resolve into the catalog or dismissal.",
|
|
11484
|
+
intro: "In permissive mode, an unknown event or trait name is accepted and flagged here, so a typo never silently drops data. Resolving a violation registers the name into [the catalog](/reference/api/catalog/) pre-filled from the observed payload, while dismissing silences noise. Resolving and dismissing are admin only."
|
|
11485
|
+
},
|
|
11486
|
+
segments: {
|
|
11487
|
+
title: "Segments",
|
|
11488
|
+
description: "Computed audiences: predicate definitions, preview, member lists, and the recomputation lifecycle.",
|
|
11489
|
+
intro: "Segments are predicate definitions over traits and event history, recomputed synchronously on each write. Transitions emit `system.segment_entered` and `system.segment_exited` system events that journeys trigger on, except those found by a bulk recompute, which start nobody. See the [Segments guide](/segments/) for predicate semantics. All mutations require organization admin permissions."
|
|
11490
|
+
},
|
|
11491
|
+
journeys: {
|
|
11492
|
+
title: "Journeys",
|
|
11493
|
+
description: "One journey per key: trigger, tags, recorded step spine, the enable switch, and operational stats.",
|
|
11494
|
+
intro: "A journey is addressed by the key its author gave the file, running its latest ready version. Status decides whether the journey fires, and a push never touches it: a key is off until it is turned on. The `spine` is read from the journey's source and is display only: what a journey does during execution is whatever its code decides. A journey belongs to one app and fires on that app's events only. A trigger's `event` is a pattern in which `*` matches any sequence of characters, one or a list of them, and a pattern reaches Cowliss's own `system.` events only when its literal prefix starts with `system.`: see [Event patterns](/journeys/#event-patterns). Executions are on the [Executions](/reference/api/executions/) page."
|
|
11495
|
+
},
|
|
11496
|
+
templates: {
|
|
11497
|
+
title: "Templates",
|
|
11498
|
+
description: "The email templates a journey sends: their props, who sends them, rendering one, and sending yourself a test.",
|
|
11499
|
+
intro: "Templates are the email templates a journey sends, each addressed by the key its author gave the file. Templates arrive with a push, so there is no create and no update here. A template has versions and nothing else, no switch, because a send resolves the latest version that finished compiling at the moment it sends, so fixing copy reaches executions already in flight."
|
|
11500
|
+
},
|
|
11501
|
+
artifacts: {
|
|
11502
|
+
title: "Artifacts",
|
|
11503
|
+
description: "Content-addressed bundle and source storage: HEAD to check, PUT to upload.",
|
|
11504
|
+
intro: "Artifacts are immutable bytes named by their own `sha256:` digest and deduplicated within an organization. They serve as plumbing for `cow push` so changing one journey transfers only one bundle, and remain documented for anyone reimplementing push. This is the one place in the API where bytes travel outside an envelope. The body of a `PUT` must hash to the digest it is filed under, and repeat transfers of the same digest are a no-op. These routes carry their own size limit rather than the global request one."
|
|
11505
|
+
},
|
|
11506
|
+
pushes: {
|
|
11507
|
+
title: "Pushes",
|
|
11508
|
+
description: "One push per cow push: the whole project at one point in time, and the versions it compiles.",
|
|
11509
|
+
intro: "A push stores the app's source archive and creates a new version of every journey and template whose bundle changed; a key that did not change gets none. The push returns as soon as it is stored, and each version compiles on its own, so one file that cannot compile costs its own key and nothing else. A push turns nothing on: journey `status` is untouched."
|
|
11510
|
+
},
|
|
11511
|
+
versions: {
|
|
11512
|
+
title: "Versions",
|
|
11513
|
+
description: "One key's history: every version of a journey or a template, newest first.",
|
|
11514
|
+
intro: "A version is an immutable, content-addressed snapshot of one journey or one template. It is read-only and addressed through its key rather than on its own path. Journeys and templates share a key space, so both `key` and `kind` are required. The latest `ready` version is the code new executions and new sends use, which leaves the previous version running when a compile fails."
|
|
11515
|
+
},
|
|
11516
|
+
executions: {
|
|
11517
|
+
title: "Executions",
|
|
11518
|
+
description: "One run of one journey for one profile, pinned to the version it started on.",
|
|
11519
|
+
intro: "An execution is one journey for one profile, pinned to the version it started on. It can be filtered by journey, version, status, and profile in one place. Its detail carries what the journey logged with `api.log`. Pipeline keys can read executions, but stopping them is explicit: `cancel` is the only write and requires a dashboard session."
|
|
11520
|
+
},
|
|
11521
|
+
webhooks: {
|
|
11522
|
+
title: "Webhooks",
|
|
11523
|
+
description: "The URLs a journey POSTs a Standard-Webhooks-signed payload to, one registry the journey references by name.",
|
|
11524
|
+
intro: "Journeys reference webhooks by name, so journey code never hardcodes a URL. A webhook carries its own signing secret, shown only at creation. Payloads follow the Standard Webhooks convention with the `v: 1` version field and `webhook-id`, `webhook-timestamp`, and `webhook-signature` headers. A webhook with too many consecutive failures auto-disables until re-enabled. All mutations require `org:admin`."
|
|
11525
|
+
},
|
|
11526
|
+
domains: {
|
|
11527
|
+
title: "Sending domains",
|
|
11528
|
+
description: "Org-verified sending domains: claims, the DNS records to install, verification status, and manual re-check.",
|
|
11529
|
+
intro: "An organization cannot send without a verified domain. Claims are global and exact-string: a parent or child of a claimed domain conflicts. The `dnsRecords` array specifies what your DNS must carry (three DKIM CNAMEs, the MX return path and its SPF TXT, plus a recommended DMARC TXT), each with its own status. DNS is re-checked on a schedule and nothing pushes an event when it is, so status refreshes are pull-only."
|
|
11530
|
+
},
|
|
11531
|
+
deliveries: {
|
|
11532
|
+
title: "Deliveries",
|
|
11533
|
+
description: "The global delivery log (every email and webhook attempted, with status progression) plus the public SES feedback and one-click unsubscribe endpoints.",
|
|
11534
|
+
intro: "Each send attempt creates a delivery. A delivery is `sent` then `delivered`, `bounced` or `complained`, or it is `failed`, a typed `skipped_*`, or a dry-run `would_*`. Status precedence ensures that a late `delivered` status never overwrites `bounced`, and a complaint outranks a bounce. For webhooks, a successful response confirms delivery, making `delivered` terminal. Public routes for delivery feedback and RFC 8058 one-click unsubscribe verify signatures instead of sessions, answering per the caller's convention rather than the standard envelope. Address verification is on the [emails](/reference/api/emails/) page."
|
|
11535
|
+
},
|
|
11536
|
+
emails: {
|
|
11537
|
+
title: "Emails",
|
|
11538
|
+
description: "The public email-address verification pages: the signed link in a verification email, answered as HTML.",
|
|
11539
|
+
intro: "These routes handle direct transactional email sending and public email-address verification. The `emails.send` route is the first-party shape of the same send the [Resend facade](/resend/) serves, authenticating with an org API key and answering in the envelope with the delivery id. The public verification routes require no Cowliss credentials and answer with HTML pages rather than the envelope. Verification links authorize through a single-use `?token=` that expires after 48 hours, and an invalid, expired, or already-spent token is rejected. The `GET` request only offers the verification and the `POST` request performs it, so a link scanner that fetches the URL does not verify the address."
|
|
11540
|
+
},
|
|
11541
|
+
suppressions: {
|
|
11542
|
+
title: "Suppressions",
|
|
11543
|
+
description: "Cowliss's mirror of your organization's suppression list: the cheap send-gate check and the dashboard's why.",
|
|
11544
|
+
intro: "Cowliss mirrors the suppression list your organization accumulates from hard bounces and complaints per address so the send gate skips cheaply and the dashboard can say why. Suppression is a separate fact from [consent](/reference/api/consent/): a complaint revokes consent, while a bounce only suppresses the address. The list is your organization's own: an address one organization suppresses is untouched for every other."
|
|
11545
|
+
},
|
|
11546
|
+
settings: {
|
|
11547
|
+
title: "Settings",
|
|
11548
|
+
description: "Org settings: ingestion policy, event retention, the sending-pause state, and self-service org API keys and pipeline keys.",
|
|
11549
|
+
intro: "Organization settings configure data handling: `ingestionPolicy` decides whether unclassified event and trait names are accepted and flagged or quarantined for review, `eventRetentionDays` keeps events for at most 18 months, and `sendingPaused` is a platform pause that the organization can read but only the platform sets. This module is also the self-service home of the organization's two key families: ingestion API keys, which write events and reach nothing else, and pipeline keys, which push an app and reach nothing else. Any member can list either family, but only admins create and revoke them. Neither list shows the other family's keys, and revoking a key from the other family cannot find it. See the [Keys guide](/guides/keys/)."
|
|
11550
|
+
},
|
|
11551
|
+
billing: {
|
|
11552
|
+
title: "Billing",
|
|
11553
|
+
description: "The prepaid wallet: metered spend against the monthly free allowance, fixed-amount Stripe top-ups, and the hard stop at zero credit.",
|
|
11554
|
+
intro: "Spend is one weighted total over events, emails, webhooks and journey runs, counted from what already happened, never billed in arrears. Credit is the monthly included usage (" + dollars(FREE_ALLOWANCE_MICROS) + ", resets each UTC month, no rollover) plus the prepaid wallet (carries over). At zero credit, the platform hard-stops: ingestion is refused and sends are skipped. See the [Billing guide](/guides/billing/)."
|
|
11555
|
+
},
|
|
11556
|
+
openapi: {
|
|
11557
|
+
title: "OpenAPI",
|
|
11558
|
+
description: "The OpenAPI 3.1 document, built from this contract.",
|
|
11559
|
+
intro: "The document is built from the route contract. Requests to `GET /openapi.json` are served unauthenticated."
|
|
11560
|
+
},
|
|
11561
|
+
me: {
|
|
11562
|
+
title: "Me",
|
|
11563
|
+
description: "The signed-in developer's own settings.",
|
|
11564
|
+
intro: "These routes manage the signed-in developer's own settings. Like the rest of the dashboard API, requests use session authentication, but this resource is deliberately not org-scoped: the subject is the caller's own platform-workspace profile, taken from the session, never from the request. They are hidden from the CLI and the MCP server because both act with an organization key on that organization's data without a signed-in human. An operator changing an end user's consent uses `users.updateConsent` instead, which is the same decision made by someone else."
|
|
11565
|
+
},
|
|
11566
|
+
resend: {
|
|
11567
|
+
title: "Resend-compatible send",
|
|
11568
|
+
description: "The Resend-compatible send facade: point `RESEND_BASE_URL` at `/resend/{appOrSourceId}` and keep your code.",
|
|
11569
|
+
intro: "The Resend-compatible send facade lets developers point `RESEND_BASE_URL` at `/resend/{appOrSourceId}` and keep their code. The `appOrSourceId` path segment accepts an app id or the id of its API source, because the Resend SDK carries no other field and accepts no custom header. The bearer is the organization's ingestion key. Every send creates a delivery and requires a from-address on a domain the organization has verified. These routes answer Resend's own shapes rather than the envelope."
|
|
11570
|
+
}
|
|
11571
|
+
};
|
|
11572
|
+
|
|
11573
|
+
//#endregion
|
|
11574
|
+
//#region ../../packages/shared/src/contract/templates.ts
|
|
11575
|
+
/**
|
|
11576
|
+
* Templates, addressed by key like a journey. No create and no update: a
|
|
11577
|
+
* template has versions and nothing else (ADR 0011). What `preview` and
|
|
11578
|
+
* `testSend` do is the `templates` intro in `tags.ts`.
|
|
11361
11579
|
*/
|
|
11362
11580
|
const params$3 = z.object({ key: z.string() });
|
|
11363
11581
|
const templates = defineModule(defineRoute({
|
|
@@ -11408,6 +11626,7 @@ const templates = defineModule(defineRoute({
|
|
|
11408
11626
|
operationId: "templates.testSend",
|
|
11409
11627
|
tags: ["templates"],
|
|
11410
11628
|
summary: "Send a rendered template to your own address",
|
|
11629
|
+
description: "Sends through your organization's default sender. The test goes to you regardless of your consent settings, costs nothing, and shows as a test.",
|
|
11411
11630
|
security: SESSION_AUTH,
|
|
11412
11631
|
request: {
|
|
11413
11632
|
params: params$3,
|
|
@@ -11424,6 +11643,7 @@ const templates = defineModule(defineRoute({
|
|
|
11424
11643
|
operationId: "templates.delete",
|
|
11425
11644
|
tags: ["templates"],
|
|
11426
11645
|
summary: "Delete an email template",
|
|
11646
|
+
description: "Admin only. Deleting a key deletes every version of it and is refused while a journey still sends it, naming the journeys.",
|
|
11427
11647
|
security: SESSION_AUTH,
|
|
11428
11648
|
request: { params: params$3 },
|
|
11429
11649
|
responses: {
|
|
@@ -11504,6 +11724,7 @@ const users = defineModule(defineRoute({
|
|
|
11504
11724
|
operationId: "users.eraseByIdentifier",
|
|
11505
11725
|
tags: ["users"],
|
|
11506
11726
|
summary: "Erase every person one identifier names (GDPR)",
|
|
11727
|
+
description: "Admin only. Terminates active journey executions, then permanently deletes every profile across your apps that carries the identifier, along with their merged profiles, and their event and delivery history. This action cannot be undone.",
|
|
11507
11728
|
security: SESSION_AUTH,
|
|
11508
11729
|
request: { body: jsonBody(eraseUserByIdentifierBodySchema) },
|
|
11509
11730
|
responses: {
|
|
@@ -11531,6 +11752,7 @@ const users = defineModule(defineRoute({
|
|
|
11531
11752
|
operationId: "users.erase",
|
|
11532
11753
|
tags: ["users"],
|
|
11533
11754
|
summary: "Erase a user (GDPR)",
|
|
11755
|
+
description: "Admin only. Terminates active journey executions, then permanently deletes the profile, any profiles merged into it, and their event and delivery history. This action cannot be undone.",
|
|
11534
11756
|
security: SESSION_AUTH,
|
|
11535
11757
|
request: { params: params$2 },
|
|
11536
11758
|
responses: {
|
|
@@ -11651,6 +11873,7 @@ const violations = defineModule(defineRoute({
|
|
|
11651
11873
|
operationId: "violations.resolve",
|
|
11652
11874
|
tags: ["violations"],
|
|
11653
11875
|
summary: "Resolve a violation into the catalog",
|
|
11876
|
+
description: "Admin only. Resolving is irreversible and refuses violations that have already been handled or whose name is already in the catalog. You must supply `data.type` when resolving a trait that was only observed as `null`.",
|
|
11654
11877
|
security: SESSION_AUTH,
|
|
11655
11878
|
request: {
|
|
11656
11879
|
params: params$1,
|
|
@@ -11667,6 +11890,7 @@ const violations = defineModule(defineRoute({
|
|
|
11667
11890
|
operationId: "violations.dismiss",
|
|
11668
11891
|
tags: ["violations"],
|
|
11669
11892
|
summary: "Dismiss a violation",
|
|
11893
|
+
description: "Admin only. Dismissing is irreversible and refuses violations that have already been handled. Future sightings of the name continue to be accepted but do not flag new violations.",
|
|
11670
11894
|
security: SESSION_AUTH,
|
|
11671
11895
|
request: { params: params$1 },
|
|
11672
11896
|
responses: {
|
|
@@ -11724,6 +11948,7 @@ const webhooks = defineModule(defineRoute({
|
|
|
11724
11948
|
operationId: "webhooks.update",
|
|
11725
11949
|
tags: ["webhooks"],
|
|
11726
11950
|
summary: "Update a webhook",
|
|
11951
|
+
description: "Setting `enabled` to `true` re-enables an auto-disabled webhook and resets its consecutive failure counter to zero. Setting `enabled` to `false` disables the webhook manually.",
|
|
11727
11952
|
security: SESSION_AUTH,
|
|
11728
11953
|
request: {
|
|
11729
11954
|
params,
|
|
@@ -12136,6 +12361,7 @@ function cliCommandFor(route, moduleKey) {
|
|
|
12136
12361
|
groups: segments.slice(0, -1),
|
|
12137
12362
|
verb,
|
|
12138
12363
|
summary: route.summary ?? route.operationId,
|
|
12364
|
+
description: route.description,
|
|
12139
12365
|
positionals: pathParams(route),
|
|
12140
12366
|
queryOptions: queryOptions(route),
|
|
12141
12367
|
bodyOptions: body,
|
|
@@ -12150,7 +12376,8 @@ function shapeKeys(schema) {
|
|
|
12150
12376
|
function mcpToolFor(route, moduleKey) {
|
|
12151
12377
|
assertNoHeaders(route);
|
|
12152
12378
|
const name = route.surfaces?.mcpName ?? route.operationId.replaceAll(".", "_");
|
|
12153
|
-
const
|
|
12379
|
+
const summary = route.summary ?? route.operationId;
|
|
12380
|
+
const description = route.surfaces?.description ?? (route.description ? `${summary}\n\n${route.description}` : summary);
|
|
12154
12381
|
const data = bodyDataSchema(route);
|
|
12155
12382
|
const rawBody = data !== void 0 && !(data instanceof z.ZodObject);
|
|
12156
12383
|
const bodyKeys = data === void 0 ? [] : data instanceof z.ZodObject ? Object.keys(data.shape) : ["data"];
|
|
@@ -12304,7 +12531,7 @@ function groupFor(program, groups, segments) {
|
|
|
12304
12531
|
const segment = segments[depth - 1];
|
|
12305
12532
|
let group = groups.get(key);
|
|
12306
12533
|
if (!group) {
|
|
12307
|
-
group = parent.command(segment).description(segment);
|
|
12534
|
+
group = parent.command(segment).description(depth === 1 && Object.hasOwn(TAG_DOCS, segment) ? TAG_DOCS[segment].description : segment);
|
|
12308
12535
|
groups.set(key, group);
|
|
12309
12536
|
}
|
|
12310
12537
|
parent = group;
|
|
@@ -12318,6 +12545,7 @@ function registerContractCommands(program, run) {
|
|
|
12318
12545
|
const requestOf = (client) => client.request;
|
|
12319
12546
|
for (const spec of plan.cli) {
|
|
12320
12547
|
const command = groupFor(program, groups, spec.groups).command([spec.verb, ...spec.positionals.map((name) => `<${name}>`)].join(" ")).description(spec.summary);
|
|
12548
|
+
if (spec.description) command.addHelpText("after", `\n${spec.description}`);
|
|
12321
12549
|
for (const option of [...spec.queryOptions, ...spec.bodyOptions]) registerOption(command, option);
|
|
12322
12550
|
command.action(async (...args) => {
|
|
12323
12551
|
const positionals = args.slice(0, spec.positionals.length);
|
|
@@ -12482,6 +12710,42 @@ function registerEnable(program, clientFor, io) {
|
|
|
12482
12710
|
register(program, clientFor, io, "pause", "paused");
|
|
12483
12711
|
}
|
|
12484
12712
|
|
|
12713
|
+
//#endregion
|
|
12714
|
+
//#region src/commands/events.ts
|
|
12715
|
+
/** `cow events --help` and the `events_prompt` MCP tool, in one place. */
|
|
12716
|
+
const eventsDescription = {
|
|
12717
|
+
cli: "instructions for a coding agent to send the events this app's journeys need",
|
|
12718
|
+
mcp: "Instructions for a coding agent to send the events this app's journeys need. Reads the local project; calls no API."
|
|
12719
|
+
};
|
|
12720
|
+
async function projectEventsPrompt(dir) {
|
|
12721
|
+
const projectDir = dir ?? resolveProjectDir();
|
|
12722
|
+
const { manifest } = await buildProject(projectDir);
|
|
12723
|
+
const items = manifest.events.map((evt) => ({ name: evt.pattern }));
|
|
12724
|
+
return buildEventsPrompt({
|
|
12725
|
+
events: items,
|
|
12726
|
+
sdkPackage: "@cowliss/sdk",
|
|
12727
|
+
trackMethod: "cow.track",
|
|
12728
|
+
referenceUrl: `${DOCS_URL}/sdk/track/`
|
|
12729
|
+
});
|
|
12730
|
+
}
|
|
12731
|
+
function registerEvents(program, io) {
|
|
12732
|
+
let eventsCmd = program.commands.find((c) => c.name() === "events");
|
|
12733
|
+
if (!eventsCmd) eventsCmd = program.command("events");
|
|
12734
|
+
eventsCmd.description(eventsDescription.cli);
|
|
12735
|
+
eventsCmd.action(async (opts) => {
|
|
12736
|
+
const merged = {
|
|
12737
|
+
...program.opts(),
|
|
12738
|
+
...opts
|
|
12739
|
+
};
|
|
12740
|
+
const prompt = await projectEventsPrompt();
|
|
12741
|
+
if (merged.json === true) {
|
|
12742
|
+
emit({ data: { prompt } }, io, true);
|
|
12743
|
+
return;
|
|
12744
|
+
}
|
|
12745
|
+
io.stdout(`${prompt}\n`);
|
|
12746
|
+
});
|
|
12747
|
+
}
|
|
12748
|
+
|
|
12485
12749
|
//#endregion
|
|
12486
12750
|
//#region src/commands/init.ts
|
|
12487
12751
|
const json = (value) => `${JSON.stringify(value, null, 2)}\n`;
|
|
@@ -13085,6 +13349,11 @@ function registerPush(program, clientFor, env, io) {
|
|
|
13085
13349
|
* `unseenReport`. There is no second computation that could drift from the
|
|
13086
13350
|
* one the push itself shows.
|
|
13087
13351
|
*/
|
|
13352
|
+
/** `cow plan --help` and the `pushes_plan` MCP tool, in one place. */
|
|
13353
|
+
const planDescription = {
|
|
13354
|
+
cli: "show what a push of this repository would change, and change nothing",
|
|
13355
|
+
mcp: "What a push of the project in the current directory would change. Builds locally; uploads and stores nothing."
|
|
13356
|
+
};
|
|
13088
13357
|
/** The envelope `cow plan --json` prints, and what the MCP tool answers. */
|
|
13089
13358
|
async function projectPlan(client) {
|
|
13090
13359
|
const { plan } = await planProject({
|
|
@@ -13094,7 +13363,7 @@ async function projectPlan(client) {
|
|
|
13094
13363
|
return { data: plan };
|
|
13095
13364
|
}
|
|
13096
13365
|
function registerPlan(program, clientFor, io) {
|
|
13097
|
-
program.command("plan").description(
|
|
13366
|
+
program.command("plan").description(planDescription.cli).action(async (opts) => {
|
|
13098
13367
|
const merged = {
|
|
13099
13368
|
...program.opts(),
|
|
13100
13369
|
...opts
|
|
@@ -13195,9 +13464,28 @@ async function createMcpServer({ client }) {
|
|
|
13195
13464
|
inputSchema: tool.inputSchema
|
|
13196
13465
|
}, async (args) => toolResult(() => tool.route.request ? request(tool.route, tool.toInput(args)) : request(tool.route)));
|
|
13197
13466
|
server.registerTool("pushes_plan", {
|
|
13198
|
-
description:
|
|
13467
|
+
description: planDescription.mcp,
|
|
13199
13468
|
inputSchema: {}
|
|
13200
13469
|
}, async () => toolResult(() => projectPlan(client)));
|
|
13470
|
+
server.registerTool("events_prompt", {
|
|
13471
|
+
description: eventsDescription.mcp,
|
|
13472
|
+
inputSchema: {}
|
|
13473
|
+
}, async () => {
|
|
13474
|
+
try {
|
|
13475
|
+
return { content: [{
|
|
13476
|
+
type: "text",
|
|
13477
|
+
text: await projectEventsPrompt()
|
|
13478
|
+
}] };
|
|
13479
|
+
} catch (error) {
|
|
13480
|
+
return {
|
|
13481
|
+
content: [{
|
|
13482
|
+
type: "text",
|
|
13483
|
+
text: errorMessage$1(error)
|
|
13484
|
+
}],
|
|
13485
|
+
isError: true
|
|
13486
|
+
};
|
|
13487
|
+
}
|
|
13488
|
+
});
|
|
13201
13489
|
return server;
|
|
13202
13490
|
}
|
|
13203
13491
|
function errorMessage$1(error) {
|
|
@@ -13615,6 +13903,7 @@ function buildProgram(env, io) {
|
|
|
13615
13903
|
emit(await fn(await clientFor(merged)), io, typeof merged.json === "boolean" ? merged.json : void 0);
|
|
13616
13904
|
};
|
|
13617
13905
|
registerContractCommands(program, run);
|
|
13906
|
+
registerEvents(program, io);
|
|
13618
13907
|
registerAuth(program, env, io);
|
|
13619
13908
|
registerInit(program, clientFor, env, io);
|
|
13620
13909
|
registerAdd(program, io);
|