@paragraph-com/cli 0.2.0 → 0.3.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 (2) hide show
  1. package/dist/index.js +235 -12
  2. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -980,7 +980,7 @@ var require_command = __commonJS({
980
980
  var EventEmitter = __require("events").EventEmitter;
981
981
  var childProcess = __require("child_process");
982
982
  var path2 = __require("path");
983
- var fs8 = __require("fs");
983
+ var fs9 = __require("fs");
984
984
  var process2 = __require("process");
985
985
  var { Argument: Argument2, humanReadableArgName } = require_argument();
986
986
  var { CommanderError: CommanderError2 } = require_error();
@@ -1913,10 +1913,10 @@ Expecting one of '${allowedValues.join("', '")}'`);
1913
1913
  const sourceExt = [".js", ".ts", ".tsx", ".mjs", ".cjs"];
1914
1914
  function findFile(baseDir, baseName) {
1915
1915
  const localBin = path2.resolve(baseDir, baseName);
1916
- if (fs8.existsSync(localBin)) return localBin;
1916
+ if (fs9.existsSync(localBin)) return localBin;
1917
1917
  if (sourceExt.includes(path2.extname(baseName))) return void 0;
1918
1918
  const foundExt = sourceExt.find(
1919
- (ext) => fs8.existsSync(`${localBin}${ext}`)
1919
+ (ext) => fs9.existsSync(`${localBin}${ext}`)
1920
1920
  );
1921
1921
  if (foundExt) return `${localBin}${foundExt}`;
1922
1922
  return void 0;
@@ -1928,7 +1928,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
1928
1928
  if (this._scriptPath) {
1929
1929
  let resolvedScriptPath;
1930
1930
  try {
1931
- resolvedScriptPath = fs8.realpathSync(this._scriptPath);
1931
+ resolvedScriptPath = fs9.realpathSync(this._scriptPath);
1932
1932
  } catch (err) {
1933
1933
  resolvedScriptPath = this._scriptPath;
1934
1934
  }
@@ -3266,7 +3266,7 @@ async function loadSellPeers() {
3266
3266
  throw err;
3267
3267
  }
3268
3268
  }
3269
- var BASE_URL, currentApiKey, setCurrentApiKey, ParagraphApiError, customAxios, getParagraphAPI, QueryResult, executeAbi, permit2Abi, AnalyticsResource, AuthResource, MISSING_COIN_PEERS_MESSAGE, CoinsResource, FeedResource, MeResource, PostsResource, PublicationsResource, SearchResource, SubscribersResource, UsersResource, ParagraphAPI;
3269
+ var BASE_URL, currentApiKey, setCurrentApiKey, ParagraphApiError, customAxios, getParagraphAPI, QueryResult, executeAbi, permit2Abi, AnalyticsResource, AuthResource, MISSING_COIN_PEERS_MESSAGE, CoinsResource, EmailsResource, FeedResource, MeResource, PostsResource, PublicationsResource, SearchResource, SubscribersResource, UsersResource, ParagraphAPI;
3270
3270
  var init_dist = __esm({
3271
3271
  "node_modules/@paragraph-com/sdk/dist/index.mjs"() {
3272
3272
  "use strict";
@@ -3695,6 +3695,16 @@ var init_dist = __esm({
3695
3695
  }
3696
3696
  );
3697
3697
  };
3698
+ const sendCustomEmail2 = (sendCustomEmailBody2) => {
3699
+ return customAxios(
3700
+ {
3701
+ url: `/v1/emails/send`,
3702
+ method: "POST",
3703
+ headers: { "Content-Type": "application/json" },
3704
+ data: sendCustomEmailBody2
3705
+ }
3706
+ );
3707
+ };
3698
3708
  const createAuthSession = (createAuthSessionBody2) => {
3699
3709
  return customAxios(
3700
3710
  {
@@ -3721,7 +3731,7 @@ var init_dist = __esm({
3721
3731
  }
3722
3732
  );
3723
3733
  };
3724
- return { getPublicationById, updatePublication: updatePublication2, getPublicationBySlug, getPublicationByDomain: getPublicationByDomain2, getSubscriberCount: getSubscriberCount2, getPostsFeed, getPostsByTag: getPostsByTag2, getPosts, getPostById, updatePost: updatePost2, deletePost: deletePost2, getPostByPublicationIdAndPostSlug, getPostByPublicationSlugAndPostSlug, createPost: createPost2, listOwnPosts, updatePostBySlug, deletePostBySlug, sendTestEmail: sendTestEmail2, getMe, getUser: getUser2, getUserByWallet, getCoin: getCoin2, getCoinByContract, getCoinHoldersById, getCoinHoldersByContract, getBuyArgsById, getBuyArgsByContract, getSellArgsById, getSellArgsByContract, getQuoteById, getQuoteByContract, getPopularCoins: getPopularCoins2, importSubscribers: importSubscribers2, addSubscriber: addSubscriber2, removeSubscriber: removeSubscriber2, listSubscribers: listSubscribers2, searchPosts: searchPosts2, searchBlogs: searchBlogs2, searchCoins: searchCoins2, analyticsQuery, analyticsSchema, createAuthSession, getAuthSession, deleteAuthSession };
3734
+ return { getPublicationById, updatePublication: updatePublication2, getPublicationBySlug, getPublicationByDomain: getPublicationByDomain2, getSubscriberCount: getSubscriberCount2, getPostsFeed, getPostsByTag: getPostsByTag2, getPosts, getPostById, updatePost: updatePost2, deletePost: deletePost2, getPostByPublicationIdAndPostSlug, getPostByPublicationSlugAndPostSlug, createPost: createPost2, listOwnPosts, updatePostBySlug, deletePostBySlug, sendTestEmail: sendTestEmail2, getMe, getUser: getUser2, getUserByWallet, getCoin: getCoin2, getCoinByContract, getCoinHoldersById, getCoinHoldersByContract, getBuyArgsById, getBuyArgsByContract, getSellArgsById, getSellArgsByContract, getQuoteById, getQuoteByContract, getPopularCoins: getPopularCoins2, importSubscribers: importSubscribers2, addSubscriber: addSubscriber2, removeSubscriber: removeSubscriber2, listSubscribers: listSubscribers2, searchPosts: searchPosts2, searchBlogs: searchBlogs2, searchCoins: searchCoins2, analyticsQuery, analyticsSchema, sendCustomEmail: sendCustomEmail2, createAuthSession, getAuthSession, deleteAuthSession };
3725
3735
  };
3726
3736
  QueryResult = class {
3727
3737
  constructor(promise) {
@@ -4230,6 +4240,68 @@ var init_dist = __esm({
4230
4240
  throw new Error("Invalid identifier provided to getQuote.");
4231
4241
  }
4232
4242
  };
4243
+ EmailsResource = class {
4244
+ constructor(api) {
4245
+ this.api = api;
4246
+ }
4247
+ /**
4248
+ * Sends a custom email from your publication to a list of recipient
4249
+ * addresses. Markdown body, rendered to HTML server-side. Each recipient
4250
+ * receives the email individually with a mandatory unsubscribe footer.
4251
+ * Requires an API key.
4252
+ *
4253
+ * **Eligibility:** Publications must be approved by Paragraph before they
4254
+ * can send custom emails. Ineligible publications receive a 403.
4255
+ *
4256
+ * **Per-recipient filtering:**
4257
+ * - Malformed addresses and known disposable domains are skipped (`invalid`).
4258
+ * - Addresses that previously unsubscribed from this publication are skipped
4259
+ * (`suppressed`).
4260
+ * - Skipped recipients are returned in the response; nothing is delivered to
4261
+ * them.
4262
+ *
4263
+ * **Caps:** Maximum of 10,000 addresses per call.
4264
+ *
4265
+ * **Async delivery:** A 200 response means recipients were accepted, not
4266
+ * delivered. Sends are queued asynchronously.
4267
+ *
4268
+ * @example
4269
+ * ```ts
4270
+ * const api = new ParagraphAPI({ apiKey: "your-api-key" });
4271
+ *
4272
+ * // Send a markdown email to a list of recipients
4273
+ * const { accepted, skipped } = await api.emails.send({
4274
+ * subject: "Hello from Paragraph",
4275
+ * body: "# Welcome\n\nThanks for reading.",
4276
+ * emails: ["reader@example.com", "another@example.com"],
4277
+ * });
4278
+ * console.log(`${accepted} queued, ${skipped.length} skipped`);
4279
+ * skipped.forEach(s => console.log(`${s.email}: ${s.reason}`));
4280
+ *
4281
+ * // Dry run — check filtering without sending
4282
+ * const preview = await api.emails.send({
4283
+ * subject: "Preview",
4284
+ * body: "Body here",
4285
+ * emails: ["reader@example.com"],
4286
+ * dryRun: true,
4287
+ * });
4288
+ * ```
4289
+ *
4290
+ * @param body - The email send request.
4291
+ * @param body.subject - Subject line (1–998 characters).
4292
+ * @param body.body - Markdown body, rendered to HTML server-side (max 100KB).
4293
+ * @param body.emails - Recipient email addresses (max 10,000).
4294
+ * @param body.dryRun - If true, run filtering and return the accepted/skipped
4295
+ * split without scheduling delivery.
4296
+ * @returns A promise resolving to `{ accepted, skipped }`. `accepted` is the
4297
+ * number of recipients queued for delivery; `skipped` lists rejected
4298
+ * recipients with their reason (`suppressed`, `invalid`, or
4299
+ * `scheduling_failed`).
4300
+ */
4301
+ send(body) {
4302
+ return this.api.sendCustomEmail(body);
4303
+ }
4304
+ };
4233
4305
  FeedResource = class {
4234
4306
  constructor(api) {
4235
4307
  this.api = api;
@@ -4899,6 +4971,7 @@ var init_dist = __esm({
4899
4971
  this.feed = new FeedResource(this.api);
4900
4972
  this.users = new UsersResource(this.api);
4901
4973
  this.coins = new CoinsResource(this.api);
4974
+ this.emails = new EmailsResource(this.api);
4902
4975
  this.search = new SearchResource(this.api);
4903
4976
  this.me = new MeResource(this.api);
4904
4977
  }
@@ -7112,7 +7185,7 @@ var init_auth2 = __esm({
7112
7185
 
7113
7186
  // node_modules/@paragraph-com/sdk/dist/zod.mjs
7114
7187
  import * as zod from "zod";
7115
- var getPublicationByIdParams, getPublicationByIdResponseSlugMax, getPublicationByIdResponseSummaryMax, getPublicationByIdResponse, updatePublicationParams, updatePublicationBodyNameMax, updatePublicationBodySummaryMax, updatePublicationBodyPinnedPostIdsItemMax, updatePublicationBodyPinnedPostIdsMax, updatePublicationBody, updatePublicationResponseSlugMax, updatePublicationResponseSummaryMax, updatePublicationResponse, getPublicationBySlugPathSlugMax, getPublicationBySlugParams, getPublicationBySlugResponseSlugMax, getPublicationBySlugResponseSummaryMax, getPublicationBySlugResponse, getPublicationByDomainParams, getPublicationByDomainResponseSlugMax, getPublicationByDomainResponseSummaryMax, getPublicationByDomainResponse, getSubscriberCountParams, getSubscriberCountResponse, getPostsFeedQueryLimitDefault, getPostsFeedQueryLimitMax, getPostsFeedQueryParams, getPostsFeedResponseItemsItemPostTitleMax, getPostsFeedResponseItemsItemPostSubtitleMax, getPostsFeedResponseItemsItemPostSlugMax, getPostsFeedResponseItemsItemPostAuthorsItemWalletAddressRegExp, getPostsFeedResponseItemsItemPostAuthorsItemBioMax, getPostsFeedResponseItemsItemPublicationSlugMax, getPostsFeedResponseItemsItemPublicationSummaryMax, getPostsFeedResponseItemsItemUserWalletAddressRegExp, getPostsFeedResponseItemsItemUserBioMax, getPostsFeedResponse, getPostsByTagParams, getPostsByTagQueryLimitDefault, getPostsByTagQueryLimitMax, getPostsByTagQueryParams, getPostsByTagResponseItemsItemTitleMax, getPostsByTagResponseItemsItemSubtitleMax, getPostsByTagResponseItemsItemSlugMax, getPostsByTagResponseItemsItemAuthorsItemWalletAddressRegExp, getPostsByTagResponseItemsItemAuthorsItemBioMax, getPostsByTagResponse, getPostsParams, getPostsQueryLimitDefault, getPostsQueryLimitMax, getPostsQueryParams, getPostsResponseItemsItemTitleMax, getPostsResponseItemsItemSubtitleMax, getPostsResponseItemsItemSlugMax, getPostsResponseItemsItemAuthorsItemWalletAddressRegExp, getPostsResponseItemsItemAuthorsItemBioMax, getPostsResponse, getPostByIdParams, getPostByIdQueryParams, getPostByIdResponseTitleMax, getPostByIdResponseSubtitleMax, getPostByIdResponseSlugMax, getPostByIdResponseAuthorsItemWalletAddressRegExp, getPostByIdResponseAuthorsItemBioMax, getPostByIdResponse, updatePostParams, updatePostBodyTitleMax, updatePostBodySubtitleMax, updatePostBodySlugMax, updatePostBodyPostPreviewMax, updatePostBodyScheduledAtMin, updatePostBodyPublishedAtMin, updatePostBody, updatePostResponse, deletePostParams, deletePostBody, deletePostResponse, getPostByPublicationIdAndPostSlugPathPostSlugMax, getPostByPublicationIdAndPostSlugParams, getPostByPublicationIdAndPostSlugQueryParams, getPostByPublicationIdAndPostSlugResponseTitleMax, getPostByPublicationIdAndPostSlugResponseSubtitleMax, getPostByPublicationIdAndPostSlugResponseSlugMax, getPostByPublicationIdAndPostSlugResponseAuthorsItemWalletAddressRegExp, getPostByPublicationIdAndPostSlugResponseAuthorsItemBioMax, getPostByPublicationIdAndPostSlugResponse, getPostByPublicationSlugAndPostSlugPathPublicationSlugMax, getPostByPublicationSlugAndPostSlugPathPostSlugMax, getPostByPublicationSlugAndPostSlugParams, getPostByPublicationSlugAndPostSlugQueryParams, getPostByPublicationSlugAndPostSlugResponseTitleMax, getPostByPublicationSlugAndPostSlugResponseSubtitleMax, getPostByPublicationSlugAndPostSlugResponseSlugMax, getPostByPublicationSlugAndPostSlugResponseAuthorsItemWalletAddressRegExp, getPostByPublicationSlugAndPostSlugResponseAuthorsItemBioMax, getPostByPublicationSlugAndPostSlugResponse, createPostBodyTitleMax, createPostBodySubtitleMax, createPostBodySlugMax, createPostBodyPostPreviewMax, createPostBodyScheduledAtMin, createPostBody, createPostResponse, listOwnPostsQueryLimitDefault, listOwnPostsQueryLimitMax, listOwnPostsQueryParams, listOwnPostsResponseItemsItemTitleMax, listOwnPostsResponseItemsItemSubtitleMax, listOwnPostsResponseItemsItemSlugMax, listOwnPostsResponseItemsItemAuthorsItemWalletAddressRegExp, listOwnPostsResponseItemsItemAuthorsItemBioMax, listOwnPostsResponse, updatePostBySlugPathSlugMax, updatePostBySlugParams, updatePostBySlugBodyTitleMax, updatePostBySlugBodySubtitleMax, updatePostBySlugBodySlugMax, updatePostBySlugBodyPostPreviewMax, updatePostBySlugBodyScheduledAtMin, updatePostBySlugBodyPublishedAtMin, updatePostBySlugBody, updatePostBySlugResponse, deletePostBySlugPathSlugMax, deletePostBySlugParams, deletePostBySlugBody, deletePostBySlugResponse, sendTestEmailParams, sendTestEmailBody, sendTestEmailResponse, getMeResponseSlugMax, getMeResponseSummaryMax, getMeResponse, getUserParams, getUserResponseWalletAddressRegExp, getUserResponseBioMax, getUserResponse, getUserByWalletPathWalletAddressRegExp, getUserByWalletParams, getUserByWalletResponseWalletAddressRegExp, getUserByWalletResponseBioMax, getUserByWalletResponse, getCoinParams, getCoinResponseContractAddressRegExp, getCoinResponse, getCoinByContractPathContractAddressRegExp, getCoinByContractParams, getCoinByContractResponseContractAddressRegExp, getCoinByContractResponse, getCoinHoldersByIdParams, getCoinHoldersByIdQueryLimitDefault, getCoinHoldersByIdQueryLimitMax, getCoinHoldersByIdQueryParams, getCoinHoldersByIdResponseItemsItemWalletAddressRegExp, getCoinHoldersByIdResponseItemsItemBalanceRegExp, getCoinHoldersByIdResponse, getCoinHoldersByContractPathContractAddressRegExp, getCoinHoldersByContractParams, getCoinHoldersByContractQueryLimitDefault, getCoinHoldersByContractQueryLimitMax, getCoinHoldersByContractQueryParams, getCoinHoldersByContractResponseItemsItemWalletAddressRegExp, getCoinHoldersByContractResponseItemsItemBalanceRegExp, getCoinHoldersByContractResponse, getBuyArgsByIdParams, getBuyArgsByIdQueryWalletAddressRegExp, getBuyArgsByIdQueryAmountRegExp, getBuyArgsByIdQueryParams, getBuyArgsByIdResponseCommandsRegExp, getBuyArgsByIdResponseInputsItemRegExp, getBuyArgsByIdResponse, getBuyArgsByContractPathContractAddressRegExp, getBuyArgsByContractParams, getBuyArgsByContractQueryWalletAddressRegExp, getBuyArgsByContractQueryAmountRegExp, getBuyArgsByContractQueryParams, getBuyArgsByContractResponseCommandsRegExp, getBuyArgsByContractResponseInputsItemRegExp, getBuyArgsByContractResponse, getSellArgsByIdParams, getSellArgsByIdQueryWalletAddressRegExp, getSellArgsByIdQueryAmountRegExp, getSellArgsByIdQueryParams, getSellArgsByIdResponseCommandsRegExp, getSellArgsByIdResponseInputsItemRegExp, getSellArgsByIdResponse, getSellArgsByContractPathContractAddressRegExp, getSellArgsByContractParams, getSellArgsByContractQueryWalletAddressRegExp, getSellArgsByContractQueryAmountRegExp, getSellArgsByContractQueryParams, getSellArgsByContractResponseCommandsRegExp, getSellArgsByContractResponseInputsItemRegExp, getSellArgsByContractResponse, getQuoteByIdParams, getQuoteByIdQueryAmountRegExp, getQuoteByIdQueryParams, getQuoteByIdResponse, getQuoteByContractParams, getQuoteByContractQueryAmountRegExp, getQuoteByContractQueryParams, getQuoteByContractResponse, getPopularCoinsResponseCoinsItemContractAddressRegExp, getPopularCoinsResponse, importSubscribersBody, importSubscribersResponse, addSubscriberBodyWalletRegExp, addSubscriberBody, addSubscriberResponse, removeSubscriberBodyWalletRegExp, removeSubscriberBody, removeSubscriberResponse, listSubscribersQueryLimitDefault, listSubscribersQueryLimitMax, listSubscribersQueryParams, listSubscribersResponseItemsItemWalletAddressRegExp, listSubscribersResponse, searchPostsQueryParams, searchPostsResponseItem, searchPostsResponse, searchBlogsQueryParams, searchBlogsResponseItem, searchBlogsResponse, searchCoinsQueryParams, searchCoinsResponseItem, searchCoinsResponse, analyticsQueryBody, analyticsQueryResponse, analyticsSchemaResponse, createAuthSessionBodyDeviceNameMax, createAuthSessionBodyCallbackUrlMax, createAuthSessionBody, getAuthSessionParams, getAuthSessionResponse, deleteAuthSessionParams, deleteAuthSessionResponse;
7188
+ var getPublicationByIdParams, getPublicationByIdResponseSlugMax, getPublicationByIdResponseSummaryMax, getPublicationByIdResponse, updatePublicationParams, updatePublicationBodyNameMax, updatePublicationBodySummaryMax, updatePublicationBodyPinnedPostIdsItemMax, updatePublicationBodyPinnedPostIdsMax, updatePublicationBody, updatePublicationResponseSlugMax, updatePublicationResponseSummaryMax, updatePublicationResponse, getPublicationBySlugPathSlugMax, getPublicationBySlugParams, getPublicationBySlugResponseSlugMax, getPublicationBySlugResponseSummaryMax, getPublicationBySlugResponse, getPublicationByDomainParams, getPublicationByDomainResponseSlugMax, getPublicationByDomainResponseSummaryMax, getPublicationByDomainResponse, getSubscriberCountParams, getSubscriberCountResponse, getPostsFeedQueryLimitDefault, getPostsFeedQueryLimitMax, getPostsFeedQueryParams, getPostsFeedResponseItemsItemPostTitleMax, getPostsFeedResponseItemsItemPostSubtitleMax, getPostsFeedResponseItemsItemPostSlugMax, getPostsFeedResponseItemsItemPostAuthorsItemWalletAddressRegExp, getPostsFeedResponseItemsItemPostAuthorsItemBioMax, getPostsFeedResponseItemsItemPublicationSlugMax, getPostsFeedResponseItemsItemPublicationSummaryMax, getPostsFeedResponseItemsItemUserWalletAddressRegExp, getPostsFeedResponseItemsItemUserBioMax, getPostsFeedResponse, getPostsByTagParams, getPostsByTagQueryLimitDefault, getPostsByTagQueryLimitMax, getPostsByTagQueryParams, getPostsByTagResponseItemsItemTitleMax, getPostsByTagResponseItemsItemSubtitleMax, getPostsByTagResponseItemsItemSlugMax, getPostsByTagResponseItemsItemAuthorsItemWalletAddressRegExp, getPostsByTagResponseItemsItemAuthorsItemBioMax, getPostsByTagResponse, getPostsParams, getPostsQueryLimitDefault, getPostsQueryLimitMax, getPostsQueryParams, getPostsResponseItemsItemTitleMax, getPostsResponseItemsItemSubtitleMax, getPostsResponseItemsItemSlugMax, getPostsResponseItemsItemAuthorsItemWalletAddressRegExp, getPostsResponseItemsItemAuthorsItemBioMax, getPostsResponse, getPostByIdParams, getPostByIdQueryParams, getPostByIdResponseTitleMax, getPostByIdResponseSubtitleMax, getPostByIdResponseSlugMax, getPostByIdResponseAuthorsItemWalletAddressRegExp, getPostByIdResponseAuthorsItemBioMax, getPostByIdResponse, updatePostParams, updatePostBodyTitleMax, updatePostBodySubtitleMax, updatePostBodySlugMax, updatePostBodyPostPreviewMax, updatePostBodyScheduledAtMin, updatePostBodyPublishedAtMin, updatePostBody, updatePostResponse, deletePostParams, deletePostBody, deletePostResponse, getPostByPublicationIdAndPostSlugPathPostSlugMax, getPostByPublicationIdAndPostSlugParams, getPostByPublicationIdAndPostSlugQueryParams, getPostByPublicationIdAndPostSlugResponseTitleMax, getPostByPublicationIdAndPostSlugResponseSubtitleMax, getPostByPublicationIdAndPostSlugResponseSlugMax, getPostByPublicationIdAndPostSlugResponseAuthorsItemWalletAddressRegExp, getPostByPublicationIdAndPostSlugResponseAuthorsItemBioMax, getPostByPublicationIdAndPostSlugResponse, getPostByPublicationSlugAndPostSlugPathPublicationSlugMax, getPostByPublicationSlugAndPostSlugPathPostSlugMax, getPostByPublicationSlugAndPostSlugParams, getPostByPublicationSlugAndPostSlugQueryParams, getPostByPublicationSlugAndPostSlugResponseTitleMax, getPostByPublicationSlugAndPostSlugResponseSubtitleMax, getPostByPublicationSlugAndPostSlugResponseSlugMax, getPostByPublicationSlugAndPostSlugResponseAuthorsItemWalletAddressRegExp, getPostByPublicationSlugAndPostSlugResponseAuthorsItemBioMax, getPostByPublicationSlugAndPostSlugResponse, createPostBodyTitleMax, createPostBodySubtitleMax, createPostBodySlugMax, createPostBodyPostPreviewMax, createPostBodyScheduledAtMin, createPostBody, createPostResponse, listOwnPostsQueryLimitDefault, listOwnPostsQueryLimitMax, listOwnPostsQueryParams, listOwnPostsResponseItemsItemTitleMax, listOwnPostsResponseItemsItemSubtitleMax, listOwnPostsResponseItemsItemSlugMax, listOwnPostsResponseItemsItemAuthorsItemWalletAddressRegExp, listOwnPostsResponseItemsItemAuthorsItemBioMax, listOwnPostsResponse, updatePostBySlugPathSlugMax, updatePostBySlugParams, updatePostBySlugBodyTitleMax, updatePostBySlugBodySubtitleMax, updatePostBySlugBodySlugMax, updatePostBySlugBodyPostPreviewMax, updatePostBySlugBodyScheduledAtMin, updatePostBySlugBodyPublishedAtMin, updatePostBySlugBody, updatePostBySlugResponse, deletePostBySlugPathSlugMax, deletePostBySlugParams, deletePostBySlugBody, deletePostBySlugResponse, sendTestEmailParams, sendTestEmailBody, sendTestEmailResponse, getMeResponseSlugMax, getMeResponseSummaryMax, getMeResponse, getUserParams, getUserResponseWalletAddressRegExp, getUserResponseBioMax, getUserResponse, getUserByWalletPathWalletAddressRegExp, getUserByWalletParams, getUserByWalletResponseWalletAddressRegExp, getUserByWalletResponseBioMax, getUserByWalletResponse, getCoinParams, getCoinResponseContractAddressRegExp, getCoinResponse, getCoinByContractPathContractAddressRegExp, getCoinByContractParams, getCoinByContractResponseContractAddressRegExp, getCoinByContractResponse, getCoinHoldersByIdParams, getCoinHoldersByIdQueryLimitDefault, getCoinHoldersByIdQueryLimitMax, getCoinHoldersByIdQueryParams, getCoinHoldersByIdResponseItemsItemWalletAddressRegExp, getCoinHoldersByIdResponseItemsItemBalanceRegExp, getCoinHoldersByIdResponse, getCoinHoldersByContractPathContractAddressRegExp, getCoinHoldersByContractParams, getCoinHoldersByContractQueryLimitDefault, getCoinHoldersByContractQueryLimitMax, getCoinHoldersByContractQueryParams, getCoinHoldersByContractResponseItemsItemWalletAddressRegExp, getCoinHoldersByContractResponseItemsItemBalanceRegExp, getCoinHoldersByContractResponse, getBuyArgsByIdParams, getBuyArgsByIdQueryWalletAddressRegExp, getBuyArgsByIdQueryAmountRegExp, getBuyArgsByIdQueryParams, getBuyArgsByIdResponseCommandsRegExp, getBuyArgsByIdResponseInputsItemRegExp, getBuyArgsByIdResponse, getBuyArgsByContractPathContractAddressRegExp, getBuyArgsByContractParams, getBuyArgsByContractQueryWalletAddressRegExp, getBuyArgsByContractQueryAmountRegExp, getBuyArgsByContractQueryParams, getBuyArgsByContractResponseCommandsRegExp, getBuyArgsByContractResponseInputsItemRegExp, getBuyArgsByContractResponse, getSellArgsByIdParams, getSellArgsByIdQueryWalletAddressRegExp, getSellArgsByIdQueryAmountRegExp, getSellArgsByIdQueryParams, getSellArgsByIdResponseCommandsRegExp, getSellArgsByIdResponseInputsItemRegExp, getSellArgsByIdResponse, getSellArgsByContractPathContractAddressRegExp, getSellArgsByContractParams, getSellArgsByContractQueryWalletAddressRegExp, getSellArgsByContractQueryAmountRegExp, getSellArgsByContractQueryParams, getSellArgsByContractResponseCommandsRegExp, getSellArgsByContractResponseInputsItemRegExp, getSellArgsByContractResponse, getQuoteByIdParams, getQuoteByIdQueryAmountRegExp, getQuoteByIdQueryParams, getQuoteByIdResponse, getQuoteByContractParams, getQuoteByContractQueryAmountRegExp, getQuoteByContractQueryParams, getQuoteByContractResponse, getPopularCoinsResponseCoinsItemContractAddressRegExp, getPopularCoinsResponse, importSubscribersBody, importSubscribersResponse, addSubscriberBodyWalletRegExp, addSubscriberBody, addSubscriberResponse, removeSubscriberBodyWalletRegExp, removeSubscriberBody, removeSubscriberResponse, listSubscribersQueryLimitDefault, listSubscribersQueryLimitMax, listSubscribersQueryParams, listSubscribersResponseItemsItemWalletAddressRegExp, listSubscribersResponse, searchPostsQueryParams, searchPostsResponseItem, searchPostsResponse, searchBlogsQueryParams, searchBlogsResponseItem, searchBlogsResponse, searchCoinsQueryParams, searchCoinsResponseItem, searchCoinsResponse, analyticsQueryBody, analyticsQueryResponse, analyticsSchemaResponse, sendCustomEmailBodySubjectMax, sendCustomEmailBodyBodyMax, sendCustomEmailBodyEmailsItemMin, sendCustomEmailBodyEmailsItemMax, sendCustomEmailBodyEmailsMax, sendCustomEmailBody, sendCustomEmailResponse, createAuthSessionBodyDeviceNameMax, createAuthSessionBodyCallbackUrlMax, createAuthSessionBody, getAuthSessionParams, getAuthSessionResponse, deleteAuthSessionParams, deleteAuthSessionResponse;
7116
7189
  var init_zod = __esm({
7117
7190
  "node_modules/@paragraph-com/sdk/dist/zod.mjs"() {
7118
7191
  "use strict";
@@ -8203,6 +8276,24 @@ var init_zod = __esm({
8203
8276
  "is_nullable": zod.string()
8204
8277
  })).describe("One row per column, grouped by table in ordinal order")
8205
8278
  });
8279
+ sendCustomEmailBodySubjectMax = 998;
8280
+ sendCustomEmailBodyBodyMax = 1e5;
8281
+ sendCustomEmailBodyEmailsItemMin = 3;
8282
+ sendCustomEmailBodyEmailsItemMax = 254;
8283
+ sendCustomEmailBodyEmailsMax = 1e4;
8284
+ sendCustomEmailBody = zod.object({
8285
+ "subject": zod.string().min(1).max(sendCustomEmailBodySubjectMax).describe("Subject line of the email"),
8286
+ "body": zod.string().min(1).max(sendCustomEmailBodyBodyMax).describe("Email body. Markdown; rendered to HTML server-side. Max 100KB."),
8287
+ "emails": zod.array(zod.string().min(sendCustomEmailBodyEmailsItemMin).max(sendCustomEmailBodyEmailsItemMax)).min(1).max(sendCustomEmailBodyEmailsMax).describe('Recipient email addresses (max 10,000). Malformed addresses are returned in `skipped` with `reason: "invalid"` rather than rejecting the whole request.'),
8288
+ "dryRun": zod.boolean().optional().describe("If true, run filtering and return the accepted/skipped split without scheduling delivery")
8289
+ });
8290
+ sendCustomEmailResponse = zod.object({
8291
+ "accepted": zod.number().describe("Number of recipients queued for delivery (or that would be queued when dryRun is true)"),
8292
+ "skipped": zod.array(zod.object({
8293
+ "email": zod.string(),
8294
+ "reason": zod.enum(["suppressed", "invalid", "scheduling_failed"])
8295
+ })).describe("Recipients that were rejected, with the reason each one was skipped. `scheduling_failed` means filtering passed but the delivery task could not be queued \u2014 retry these addresses.")
8296
+ });
8206
8297
  createAuthSessionBodyDeviceNameMax = 100;
8207
8298
  createAuthSessionBodyCallbackUrlMax = 2048;
8208
8299
  createAuthSessionBody = zod.object({
@@ -9662,10 +9753,140 @@ var init_analytics2 = __esm({
9662
9753
  }
9663
9754
  });
9664
9755
 
9756
+ // src/services/emails.ts
9757
+ async function sendCustomEmail(params) {
9758
+ const client = createClient(params.apiKey);
9759
+ return client.emails.send({
9760
+ subject: params.subject,
9761
+ body: params.body,
9762
+ emails: params.emails,
9763
+ ...params.dryRun ? { dryRun: true } : {}
9764
+ });
9765
+ }
9766
+ var init_emails = __esm({
9767
+ "src/services/emails.ts"() {
9768
+ "use strict";
9769
+ init_client();
9770
+ }
9771
+ });
9772
+
9773
+ // src/cli/commands/email.ts
9774
+ import * as fs6 from "fs";
9775
+ async function resolveBody(opts) {
9776
+ if (opts.body) return opts.body;
9777
+ if (opts.bodyFile) {
9778
+ if (!fs6.existsSync(opts.bodyFile)) {
9779
+ throw new Error(
9780
+ `File not found: "${opts.bodyFile}". Check the path, or use --body <markdown> to pass content inline.`
9781
+ );
9782
+ }
9783
+ return fs6.readFileSync(opts.bodyFile, "utf-8");
9784
+ }
9785
+ const stdin = await readStdin();
9786
+ return stdin || void 0;
9787
+ }
9788
+ function collectRecipients(value, previous) {
9789
+ const parts = value.split(",").map((s) => s.trim()).filter(Boolean);
9790
+ return previous.concat(parts);
9791
+ }
9792
+ function registerEmailCommands(program2) {
9793
+ const email = program2.command("email").description("Send custom emails from your publication");
9794
+ email.command("send").description("Send a custom markdown email to a list of recipients").requiredOption("--subject <subject>", "Email subject").option("--body <markdown>", "Email body as markdown string").option("--body-file <path>", "Read email body from a file").option(
9795
+ "--to <emails>",
9796
+ "Recipient email (repeatable, or comma-separated)",
9797
+ collectRecipients,
9798
+ []
9799
+ ).option(
9800
+ "--dry-run",
9801
+ "Run filtering and show the accepted/skipped split without sending"
9802
+ ).option("--yes", "Skip the confirmation prompt before sending").addHelpText(
9803
+ "after",
9804
+ `
9805
+ Examples:
9806
+ $ paragraph email send --subject "Hello" --body "# Hi" --to reader@example.com
9807
+ $ paragraph email send --subject "Update" --body-file ./body.md --to a@x.com --to b@x.com
9808
+ $ paragraph email send --subject "Update" --body-file ./body.md --to "a@x.com,b@x.com"
9809
+ $ cat body.md | paragraph email send --subject "Update" --to reader@example.com
9810
+ $ paragraph email send --subject "Update" --body "# Hi" --to a@x.com --dry-run`
9811
+ ).action(async function(opts) {
9812
+ try {
9813
+ const apiKey = requireApiKey();
9814
+ const body = await resolveBody(opts);
9815
+ if (!body) {
9816
+ throw new Error(
9817
+ "Provide email body via --body, --body-file, or pipe to stdin."
9818
+ );
9819
+ }
9820
+ const recipients = opts.to;
9821
+ if (recipients.length === 0) {
9822
+ throw new Error(
9823
+ "Provide at least one recipient via --to <email>. Repeat the flag or pass a comma-separated list."
9824
+ );
9825
+ }
9826
+ if (!opts.dryRun && !opts.yes) {
9827
+ if (!process.stdin.isTTY) {
9828
+ throw new Error(
9829
+ "Cannot confirm send in non-interactive mode. Use --yes to confirm, or --dry-run to preview."
9830
+ );
9831
+ }
9832
+ const ok = await confirm(
9833
+ `Send "${opts.subject}" to ${recipients.length} recipient${recipients.length === 1 ? "" : "s"}?`
9834
+ );
9835
+ if (!ok) {
9836
+ process.stderr.write("Aborted.\n");
9837
+ process.exit(1);
9838
+ }
9839
+ }
9840
+ const result = await sendCustomEmail({
9841
+ apiKey,
9842
+ subject: opts.subject,
9843
+ body,
9844
+ emails: recipients,
9845
+ dryRun: opts.dryRun
9846
+ });
9847
+ const action = opts.dryRun ? "Dry run" : "Email queued";
9848
+ writeSuccess(
9849
+ `${action}: ${result.accepted} accepted, ${result.skipped.length} skipped`
9850
+ );
9851
+ if (result.skipped.length > 0) {
9852
+ writeInfo("Skipped recipients:");
9853
+ for (const s of result.skipped) {
9854
+ process.stderr.write(` ${s.email} \u2014 ${s.reason}
9855
+ `);
9856
+ }
9857
+ }
9858
+ outputData(
9859
+ this,
9860
+ {
9861
+ Subject: opts.subject,
9862
+ Recipients: recipients.length,
9863
+ Accepted: result.accepted,
9864
+ Skipped: result.skipped.length,
9865
+ DryRun: opts.dryRun ? "yes" : "no"
9866
+ },
9867
+ { ...result, dryRun: !!opts.dryRun }
9868
+ );
9869
+ } catch (err) {
9870
+ handleError(err);
9871
+ }
9872
+ });
9873
+ }
9874
+ var init_email = __esm({
9875
+ "src/cli/commands/email.ts"() {
9876
+ "use strict";
9877
+ init_auth();
9878
+ init_emails();
9879
+ init_output();
9880
+ init_error();
9881
+ init_stdin();
9882
+ init_prompt();
9883
+ }
9884
+ });
9885
+
9665
9886
  // src/cli/program.ts
9666
9887
  function createProgram() {
9667
9888
  const program2 = new Command();
9668
- program2.name("paragraph").description("CLI for Paragraph").version("0.2.0", "-v, --version").option("--json", "Output as JSON").option("--verbose", "Show detailed output for debugging").exitOverride().configureOutput({
9889
+ program2.name("paragraph").description("CLI for Paragraph").version("0.3.0", "-v, --version").option("--json", "Output as JSON").option("--verbose", "Show detailed output for debugging").exitOverride().configureOutput({
9669
9890
  writeOut: (str) => process.stdout.write(str),
9670
9891
  writeErr: (str) => {
9671
9892
  if (process.argv.includes("--json")) {
@@ -9685,6 +9906,7 @@ function createProgram() {
9685
9906
  registerCoinCommands(program2);
9686
9907
  registerUserCommands(program2);
9687
9908
  registerAnalyticsCommands(program2);
9909
+ registerEmailCommands(program2);
9688
9910
  return program2;
9689
9911
  }
9690
9912
  var init_program = __esm({
@@ -9699,6 +9921,7 @@ var init_program = __esm({
9699
9921
  init_coin();
9700
9922
  init_user();
9701
9923
  init_analytics2();
9924
+ init_email();
9702
9925
  }
9703
9926
  });
9704
9927
 
@@ -10952,7 +11175,7 @@ import { useState as useState9 } from "react";
10952
11175
  import { Box as Box21, Text as Text14, useInput as useInput17 } from "ink";
10953
11176
  import { TextInput as TextInput5, Spinner as Spinner9 } from "@inkjs/ui";
10954
11177
  import { StatusMessage as StatusMessage9 } from "@inkjs/ui";
10955
- import * as fs6 from "fs";
11178
+ import * as fs7 from "fs";
10956
11179
  import { jsx as jsx23, jsxs as jsxs21 } from "react/jsx-runtime";
10957
11180
  function PostCreate() {
10958
11181
  const { goBack } = useNavigation();
@@ -10974,7 +11197,7 @@ function PostCreate() {
10974
11197
  try {
10975
11198
  let markdown = "";
10976
11199
  if (filePath) {
10977
- markdown = fs6.readFileSync(filePath, "utf-8");
11200
+ markdown = fs7.readFileSync(filePath, "utf-8");
10978
11201
  } else {
10979
11202
  throw new Error("A file path is required for post content.");
10980
11203
  }
@@ -11100,7 +11323,7 @@ import { useState as useState10 } from "react";
11100
11323
  import { Box as Box22, Text as Text15, useInput as useInput18 } from "ink";
11101
11324
  import { TextInput as TextInput6, Spinner as Spinner10 } from "@inkjs/ui";
11102
11325
  import { StatusMessage as StatusMessage10 } from "@inkjs/ui";
11103
- import * as fs7 from "fs";
11326
+ import * as fs8 from "fs";
11104
11327
  import { jsx as jsx24, jsxs as jsxs22 } from "react/jsx-runtime";
11105
11328
  function PostUpdate() {
11106
11329
  const { goBack, screen } = useNavigation();
@@ -11121,7 +11344,7 @@ function PostUpdate() {
11121
11344
  setStep("updating");
11122
11345
  try {
11123
11346
  let markdown;
11124
- if (filePath) markdown = fs7.readFileSync(filePath, "utf-8");
11347
+ if (filePath) markdown = fs8.readFileSync(filePath, "utf-8");
11125
11348
  await updatePost(idOrSlug, {
11126
11349
  apiKey,
11127
11350
  title: title || void 0,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@paragraph-com/cli",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "type": "module",
5
5
  "description": "CLI for Paragraph",
6
6
  "repository": {
@@ -43,7 +43,7 @@
43
43
  },
44
44
  "dependencies": {
45
45
  "@inkjs/ui": "^2.0.0",
46
- "@paragraph-com/sdk": "^2.1.0",
46
+ "@paragraph-com/sdk": "^2.2.0",
47
47
  "cli-table3": "^0.6.5",
48
48
  "commander": "^12.0.0",
49
49
  "ink": "^5.1.0",