@samva/cli 0.22.2 → 0.23.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.
Files changed (3) hide show
  1. package/AGENTS.md +5 -0
  2. package/dist/bin.mjs +76 -10
  3. package/package.json +7 -7
package/AGENTS.md CHANGED
@@ -72,6 +72,11 @@ a template written in a format the compiler rejects. Templates live in `template
72
72
  `emails/`; a template is a `.tsx` file that default-exports `defineTemplate`, and the compiler reads
73
73
  it without running it.
74
74
 
75
+ `samva email send` and `samva scheduled-messages create` take `--from <address>` (a verified sender or
76
+ any address on a verified domain) and `--from-name`; omitted, the send uses the default sender, else the
77
+ only verified sender, and otherwise fails with `FromAddressRequiredError`. `--from-name` without
78
+ `--from` exits `1`.
79
+
75
80
  `samva email send --attach <path>` (repeatable) attaches local files; the filename is the basename and
76
81
  the content type comes from the extension (`application/octet-stream` when unknown). The CLI reads
77
82
  and budget-checks every file before any request (at most 10 files, 25 MiB each and in total) and
package/dist/bin.mjs CHANGED
@@ -14276,7 +14276,7 @@ var layer12 = /* @__PURE__ */ layer.pipe(/* @__PURE__ */ provideMerge(/* @__PURE
14276
14276
  // package.json
14277
14277
  var package_default = {
14278
14278
  name: "@samva/cli",
14279
- version: "0.22.2",
14279
+ version: "0.23.0",
14280
14280
  description: "Samva CLI \u2014 email and platform operations from your terminal",
14281
14281
  keywords: [
14282
14282
  "cli",
@@ -14332,16 +14332,16 @@ var package_default = {
14332
14332
  "@types/node": "catalog:",
14333
14333
  "better-auth": "catalog:",
14334
14334
  effect: "catalog:effect",
14335
- samva: "workspace:0.23.1",
14335
+ samva: "workspace:0.24.0",
14336
14336
  "tsc-rs": "catalog:",
14337
14337
  vite: "~8.3.0",
14338
14338
  vitest: "catalog:"
14339
14339
  },
14340
14340
  optionalDependencies: {
14341
- "@samva/cli-darwin-arm64": "workspace:0.22.2",
14342
- "@samva/cli-darwin-x64": "workspace:0.22.2",
14343
- "@samva/cli-linux-arm64": "workspace:0.22.2",
14344
- "@samva/cli-linux-x64": "workspace:0.22.2"
14341
+ "@samva/cli-darwin-arm64": "workspace:0.23.0",
14342
+ "@samva/cli-darwin-x64": "workspace:0.23.0",
14343
+ "@samva/cli-linux-arm64": "workspace:0.23.0",
14344
+ "@samva/cli-linux-x64": "workspace:0.23.0"
14345
14345
  },
14346
14346
  engines: {
14347
14347
  node: ">=18"
@@ -21306,6 +21306,12 @@ class SenderNotReadyError extends TaggedError3()("SenderNotReadyError", {
21306
21306
  }, { httpApiStatus: 503 }) {
21307
21307
  }
21308
21308
 
21309
+ class FromAddressRequiredError extends TaggedError3()("FromAddressRequiredError", {
21310
+ message: String4,
21311
+ verifiedDomains: ArraySchema(String4)
21312
+ }, { httpApiStatus: 422 }) {
21313
+ }
21314
+
21309
21315
  class OnboardingReviewRequiredError extends TaggedError3()("OnboardingReviewRequiredError", {
21310
21316
  message: String4,
21311
21317
  code: Literals([
@@ -21430,6 +21436,7 @@ categorize(OnboardingReviewRequiredError, {
21430
21436
  retryable: false
21431
21437
  });
21432
21438
  categorize(ValidationError, { categories: ["validation"], retryable: false });
21439
+ categorize(FromAddressRequiredError, { categories: ["validation"], retryable: false });
21433
21440
  categorize(ResourceNotFoundError, {
21434
21441
  categories: ["notFound"],
21435
21442
  retryable: false
@@ -24273,6 +24280,11 @@ var EmailDomainId2 = /* @__PURE__ */ String4.check(isPattern2(/* @__PURE__ */ ne
24273
24280
  expected: "a string matching the RegExp ^dom_[0-9a-hjkmnp-tv-z]{16,24}$",
24274
24281
  identifier: "EmailDomainId"
24275
24282
  })).pipe(brand("EmailDomainId"));
24283
+ var FromAddressRequiredError2 = class extends TaggedError3()("FromAddressRequiredError", Struct({
24284
+ message: String4,
24285
+ verifiedDomains: ArraySchema(String4)
24286
+ })) {
24287
+ };
24276
24288
  var BrandAddressRequiredError = class extends TaggedError3()("BrandAddressRequiredError", Struct({
24277
24289
  message: String4,
24278
24290
  brand: Union2([String4, Null2])
@@ -25272,6 +25284,7 @@ var apiStatuses = {
25272
25284
  ValidationError: 422,
25273
25285
  AnalyticsQueryError: 400,
25274
25286
  BrandAddressRequiredError: 422,
25287
+ FromAddressRequiredError: 422,
25275
25288
  CampaignFooterRequiredError: 422,
25276
25289
  UnauthorizedError: 401,
25277
25290
  ForbiddenError: 403,
@@ -25338,6 +25351,9 @@ var apiErrorGuidance = (error) => {
25338
25351
  case "SenderNotReadyError":
25339
25352
  next = typeof details.retryAfterSeconds === "number" ? `Wait ${details.retryAfterSeconds} seconds before retrying.` : "Wait for the sending identity to be ready before retrying.";
25340
25353
  break;
25354
+ case "FromAddressRequiredError":
25355
+ next = Array.isArray(details.verifiedDomains) && details.verifiedDomains.length > 0 ? `Pass --from with an address on ${details.verifiedDomains.join(", ")}, or add a sender there and run samva email senders set-default.` : "Verify a domain or a sender (samva email domains add, samva email senders add), then set a default sender.";
25356
+ break;
25341
25357
  case "CursorExpiredError":
25342
25358
  next = "Read current state from the API, then retry without the cursor.";
25343
25359
  break;
@@ -25564,7 +25580,7 @@ var __exportAll = (all, no_symbols) => {
25564
25580
  };
25565
25581
 
25566
25582
  // ../sdk/dist/package.js
25567
- var version = "0.23.1";
25583
+ var version = "0.24.0";
25568
25584
 
25569
25585
  // ../sdk/dist/client-attribution.js
25570
25586
  var SDK_VERSION = version;
@@ -28183,6 +28199,10 @@ var SamvaErrorMetadata = {
28183
28199
  categories: ["auth"],
28184
28200
  retryable: false
28185
28201
  },
28202
+ FromAddressRequiredError: {
28203
+ categories: ["validation"],
28204
+ retryable: false
28205
+ },
28186
28206
  GatewayTimeoutError: {
28187
28207
  categories: ["transient"],
28188
28208
  retryable: true
@@ -55090,6 +55110,7 @@ var CampaignsScheduleRun403 = /* @__PURE__ */ Union2([FlagDisabledError, Forbidd
55090
55110
  var CampaignsScheduleRun404 = ResourceNotFoundError2;
55091
55111
  var CampaignsScheduleRun409 = ConflictError2;
55092
55112
  var CampaignsScheduleRun422 = /* @__PURE__ */ Union2([
55113
+ FromAddressRequiredError2,
55093
55114
  ValidationError2,
55094
55115
  BrandAddressRequiredError,
55095
55116
  CampaignFooterRequiredError
@@ -59993,7 +60014,7 @@ var MessagesSend403 = /* @__PURE__ */ Union2([
59993
60014
  ]);
59994
60015
  var MessagesSend404 = ResourceNotFoundError2;
59995
60016
  var MessagesSend409 = ConflictError2;
59996
- var MessagesSend422 = ValidationError2;
60017
+ var MessagesSend422 = /* @__PURE__ */ Union2([ValidationError2, FromAddressRequiredError2]);
59997
60018
  var MessagesSend429 = RateLimitedError2;
59998
60019
  var MessagesSend500 = InternalError2;
59999
60020
  var MessagesSend502 = /* @__PURE__ */ Union2([BillingProviderError, ExternalServiceError2]);
@@ -60248,7 +60269,7 @@ var MessagesValidateEmail200 = ValidateEmailResponse;
60248
60269
  var MessagesValidateEmail401 = UnauthorizedError2;
60249
60270
  var MessagesValidateEmail403 = ForbiddenError2;
60250
60271
  var MessagesValidateEmail404 = ResourceNotFoundError2;
60251
- var MessagesValidateEmail422 = ValidationError2;
60272
+ var MessagesValidateEmail422 = /* @__PURE__ */ Union2([ValidationError2, FromAddressRequiredError2]);
60252
60273
  var MessagesValidateEmail500 = InternalError2;
60253
60274
  var MessagesValidateEmail503 = SenderNotReadyError2;
60254
60275
  var MessagesValidateEmail504 = GatewayTimeoutError2;
@@ -61728,6 +61749,8 @@ var templateSlugFlag2 = String9("template-slug").pipe(withDescription2("Template
61728
61749
  var templateDataFlag2 = String9("template-data").pipe(withDescription2("JSON object of variables for the template"), optional4);
61729
61750
  var localeFlag = String9("locale").pipe(withDescription2("BCP 47 language tag (for example en-US) for dates, numbers and money in a template send; defaults to the template's locale"), optional4);
61730
61751
  var timeZoneFlag = String9("time-zone").pipe(withDescription2("IANA time zone (for example America/New_York) that a template send renders dates in; defaults to UTC"), optional4);
61752
+ var fromFlag2 = String9("from").pipe(withDescription2("Sender address: a verified sender or any address on a verified domain. Omitted, the send uses the default sender, else the only verified sender"), optional4);
61753
+ var fromNameFlag = String9("from-name").pipe(withDescription2("Sender display name; applies with --from"), optional4);
61731
61754
  var replyToMessageFlag = String9("reply-to-message-id").pipe(withDescription2("Message id this email replies to (threads the conversation)"), optional4);
61732
61755
  var replyToAddressFlag = String9("reply-to").pipe(withDescription2("Reply-To header email address"), optional4);
61733
61756
  var idempotencyKeyFlag2 = String9("idempotency-key").pipe(withDescription2("Key (1-255 characters) so identical retries return the original email"), optional4);
@@ -61765,6 +61788,8 @@ var send3 = make29("send", {
61765
61788
  templateData: templateDataFlag2,
61766
61789
  locale: localeFlag,
61767
61790
  timeZone: timeZoneFlag,
61791
+ from: fromFlag2,
61792
+ fromName: fromNameFlag,
61768
61793
  replyTo: replyToMessageFlag,
61769
61794
  replyToAddress: replyToAddressFlag,
61770
61795
  idempotencyKey: idempotencyKeyFlag2,
@@ -61788,6 +61813,10 @@ var send3 = make29("send", {
61788
61813
  command: `samva email send --to you@example.com --template-slug order-receipt --template-data '{"total":42}' --locale de-DE --time-zone Europe/Berlin`,
61789
61814
  description: "Render a template's dates and money for a locale and time zone"
61790
61815
  },
61816
+ {
61817
+ command: "samva email send --to you@example.com --subject Hi --text Hello --from hello@acme.test",
61818
+ description: "Send from any address on a verified domain, or from a verified sender"
61819
+ },
61791
61820
  {
61792
61821
  command: 'echo "Hello" | samva email send --to you@example.com --subject Hi --text -',
61793
61822
  description: "Pipe plain-text body from stdin"
@@ -61818,6 +61847,8 @@ var send3 = make29("send", {
61818
61847
  templateData,
61819
61848
  locale,
61820
61849
  timeZone,
61850
+ from,
61851
+ fromName,
61821
61852
  replyTo,
61822
61853
  replyToAddress,
61823
61854
  idempotencyKey,
@@ -61839,6 +61870,11 @@ var send3 = make29("send", {
61839
61870
  publicationId: getOrUndefined(publicationId),
61840
61871
  inputContractId: getOrUndefined(inputContractId)
61841
61872
  });
61873
+ const fromValue = getOrUndefined(from);
61874
+ const fromNameValue = getOrUndefined(fromName);
61875
+ if (fromValue === undefined && fromNameValue !== undefined) {
61876
+ return yield* new SamvaCliError({ message: "--from-name applies with --from." });
61877
+ }
61842
61878
  const replyToValue = getOrUndefined(replyTo);
61843
61879
  const replyToAddressValue = getOrUndefined(replyToAddress);
61844
61880
  const idempotencyKeyValue = getOrUndefined(idempotencyKey);
@@ -61909,6 +61945,12 @@ var send3 = make29("send", {
61909
61945
  ...templateDataValue === undefined ? {} : { templateData: templateDataValue },
61910
61946
  ...localeValue === undefined ? {} : { locale: localeValue },
61911
61947
  ...timeZoneValue === undefined ? {} : { timeZone: timeZoneValue },
61948
+ ...fromValue === undefined ? {} : {
61949
+ from: {
61950
+ email: fromValue,
61951
+ ...fromNameValue === undefined ? {} : { name: fromNameValue }
61952
+ }
61953
+ },
61912
61954
  ...replyToValue === undefined ? {} : { inReplyToMessageId: replyToValue },
61913
61955
  ...replyToAddressValue === undefined ? {} : { replyTo: replyToAddressValue },
61914
61956
  ...trackOpensValue !== undefined || trackClicksValue !== undefined ? {
@@ -61959,6 +62001,7 @@ var send3 = make29("send", {
61959
62001
  yield* printFields([["Body source", source]]);
61960
62002
  yield* log("");
61961
62003
  yield* printFields([
62004
+ ...request.from ? [["From", request.from.email]] : [],
61962
62005
  ["To", request.to.join(", ")],
61963
62006
  ...ccValue ? [["Cc", ccValue]] : [],
61964
62007
  ...bccValue ? [["Bcc", bccValue]] : [],
@@ -74106,7 +74149,7 @@ var ScheduledMessagesResume401 = UnauthorizedError2;
74106
74149
  var ScheduledMessagesResume403 = ForbiddenError2;
74107
74150
  var ScheduledMessagesResume404 = ResourceNotFoundError2;
74108
74151
  var ScheduledMessagesResume409 = ConflictError2;
74109
- var ScheduledMessagesResume422 = ValidationError2;
74152
+ var ScheduledMessagesResume422 = /* @__PURE__ */ Union2([FromAddressRequiredError2, ValidationError2]);
74110
74153
  var ScheduledMessagesResume500 = InternalError2;
74111
74154
  var ScheduledMessagesResume502 = ExternalServiceError2;
74112
74155
  var ScheduledMessagesResume503 = SenderNotReadyError2;
@@ -74208,6 +74251,7 @@ var assembleScheduledEmailPayload = (input) => ({
74208
74251
  to: input.to.map((email) => ({ email })),
74209
74252
  cc: input.cc ? [{ email: input.cc }] : undefined,
74210
74253
  bcc: input.bcc ? [{ email: input.bcc }] : undefined,
74254
+ ...input.from === undefined ? {} : { from: input.from },
74211
74255
  email: {
74212
74256
  subject: input.subject,
74213
74257
  html: input.html,
@@ -74235,6 +74279,8 @@ var parseTemplateData3 = (raw) => match(raw, {
74235
74279
  var toFlag2 = String9("to").pipe(withDescription2("Recipient email address (repeatable)"), atLeast2(1));
74236
74280
  var ccFlag2 = String9("cc").pipe(withDescription2("Cc recipient email address"), optional4);
74237
74281
  var bccFlag2 = String9("bcc").pipe(withDescription2("Bcc recipient email address"), optional4);
74282
+ var fromFlag3 = String9("from").pipe(withDescription2("Sender address: a verified sender or any address on a verified domain. Omitted, the send uses the default sender, else the only verified sender"), optional4);
74283
+ var fromNameFlag2 = String9("from-name").pipe(withDescription2("Sender display name; applies with --from"), optional4);
74238
74284
  var subjectFlag3 = String9("subject").pipe(withDescription2("Email subject line (required with inline --html; omit for templates)"), optional4);
74239
74285
  var htmlFlag3 = String9("html").pipe(withDescription2("HTML body"), optional4);
74240
74286
  var textFlag3 = String9("text").pipe(withDescription2("Plain-text body"), optional4);
@@ -74256,6 +74302,8 @@ var create14 = make29("create", {
74256
74302
  to: toFlag2,
74257
74303
  cc: ccFlag2,
74258
74304
  bcc: bccFlag2,
74305
+ from: fromFlag3,
74306
+ fromName: fromNameFlag2,
74259
74307
  subject: subjectFlag3,
74260
74308
  html: htmlFlag3,
74261
74309
  text: textFlag3,
@@ -74277,6 +74325,10 @@ var create14 = make29("create", {
74277
74325
  command: 'samva scheduled-messages create --to you@example.com --subject Hi --html "<p>Hello</p>" --text Hello --at 2026-08-01T09:00:00Z',
74278
74326
  description: "Schedule an inline HTML email with a text fallback"
74279
74327
  },
74328
+ {
74329
+ command: 'samva scheduled-messages create --to you@example.com --from hello@acme.test --subject Hi --html "<p>Hello</p>" --at 2026-08-01T09:00:00Z',
74330
+ description: "Send from any address on a verified domain, or from a verified sender"
74331
+ },
74280
74332
  {
74281
74333
  command: `samva scheduled-messages create --to you@example.com --template-slug welcome-email --template-data '{"name":"Ada"}' --at 2026-08-01T09:00:00Z --timezone America/New_York`,
74282
74334
  description: "Schedule a published template; timezone is descriptive metadata"
@@ -74289,6 +74341,8 @@ var create14 = make29("create", {
74289
74341
  to,
74290
74342
  cc,
74291
74343
  bcc,
74344
+ from,
74345
+ fromName,
74292
74346
  subject,
74293
74347
  html,
74294
74348
  text,
@@ -74316,6 +74370,11 @@ var create14 = make29("create", {
74316
74370
  });
74317
74371
  const templateSlugValue = getOrUndefined(templateSlug);
74318
74372
  const templateDataValue = yield* parseTemplateData3(templateData);
74373
+ const fromValue = getOrUndefined(from);
74374
+ const fromNameValue = getOrUndefined(fromName);
74375
+ if (fromValue === undefined && fromNameValue !== undefined) {
74376
+ return yield* new SamvaCliError({ message: "--from-name applies with --from." });
74377
+ }
74319
74378
  if (templateIdValue && templateSlugValue) {
74320
74379
  return yield* new SamvaCliError({
74321
74380
  message: "Use either --template-id or --template-slug, not both."
@@ -74354,6 +74413,12 @@ var create14 = make29("create", {
74354
74413
  templateData: templateDataValue,
74355
74414
  cc: getOrUndefined(cc),
74356
74415
  bcc: getOrUndefined(bcc),
74416
+ ...fromValue === undefined ? {} : {
74417
+ from: {
74418
+ email: fromValue,
74419
+ ...fromNameValue === undefined ? {} : { name: fromNameValue }
74420
+ }
74421
+ },
74357
74422
  scheduledFor: new Date(at),
74358
74423
  timezone: getOrUndefined(timezone),
74359
74424
  idempotencyKey: getOrUndefined(idempotencyKey),
@@ -74574,6 +74639,7 @@ var SubscriptionsSubscribe404 = ResourceNotFoundError2;
74574
74639
  var SubscriptionsSubscribe409 = ConflictError2;
74575
74640
  var SubscriptionsSubscribe422 = /* @__PURE__ */ Union2([
74576
74641
  ValidationError2,
74642
+ FromAddressRequiredError2,
74577
74643
  BrandAddressRequiredError,
74578
74644
  CampaignFooterRequiredError
74579
74645
  ]);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@samva/cli",
3
- "version": "0.22.2",
3
+ "version": "0.23.0",
4
4
  "description": "Samva CLI — email and platform operations from your terminal",
5
5
  "keywords": [
6
6
  "cli",
@@ -50,22 +50,22 @@
50
50
  "@effect/vitest": "4.0.4",
51
51
  "@samva/contracts": "0.0.0",
52
52
  "@samva/core": "0.0.0",
53
- "@samva/mailbox": "0.5.5",
53
+ "@samva/mailbox": "0.5.6",
54
54
  "@samva/markup": "0.17.3",
55
55
  "@samva/vite": "0.17.3",
56
56
  "@types/node": "^26.6.2",
57
57
  "better-auth": "1.7.5",
58
58
  "effect": "4.0.4",
59
- "samva": "0.23.1",
59
+ "samva": "0.24.0",
60
60
  "tsc-rs": "0.7.0",
61
61
  "vite": "~8.3.0",
62
62
  "vitest": "^5.0.3"
63
63
  },
64
64
  "optionalDependencies": {
65
- "@samva/cli-darwin-arm64": "0.22.2",
66
- "@samva/cli-darwin-x64": "0.22.2",
67
- "@samva/cli-linux-arm64": "0.22.2",
68
- "@samva/cli-linux-x64": "0.22.2"
65
+ "@samva/cli-darwin-arm64": "0.23.0",
66
+ "@samva/cli-darwin-x64": "0.23.0",
67
+ "@samva/cli-linux-arm64": "0.23.0",
68
+ "@samva/cli-linux-x64": "0.23.0"
69
69
  },
70
70
  "engines": {
71
71
  "node": ">=18"