@primitivedotdev/sdk 0.20.0 → 0.21.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.
@@ -321,7 +321,7 @@ type Options$1<TData extends TDataShape = TDataShape, ThrowOnError extends boole
321
321
  //#endregion
322
322
  //#region src/api/generated/types.gen.d.ts
323
323
  type ClientOptions = {
324
- baseUrl: 'https://www.primitive.dev/api/v1' | (string & {});
324
+ baseUrl: 'https://www.primitive.dev/api/v1' | 'https://api.primitive.dev/v1' | 'https://api.primitive.dev/v1' | 'https://www.primitive.dev/api/v1' | (string & {});
325
325
  };
326
326
  type SuccessEnvelope = {
327
327
  success: boolean;
@@ -347,7 +347,7 @@ type PaginationMeta = {
347
347
  type ErrorResponse = {
348
348
  success: boolean;
349
349
  error: {
350
- code: 'unauthorized' | 'forbidden' | 'not_found' | 'validation_error' | 'rate_limit_exceeded' | 'internal_error' | 'conflict' | 'mx_conflict' | 'outbound_disabled' | 'cannot_send_from_domain' | 'recipient_not_allowed' | 'outbound_key_missing' | 'outbound_unreachable' | 'outbound_key_invalid' | 'outbound_capacity_exhausted' | 'outbound_response_malformed' | 'outbound_relay_failed' | 'discard_not_enabled' | 'inbound_not_repliable' | 'authorization_pending' | 'slow_down' | 'access_denied' | 'expired_token' | 'invalid_device_code';
350
+ code: 'unauthorized' | 'forbidden' | 'not_found' | 'validation_error' | 'rate_limit_exceeded' | 'internal_error' | 'conflict' | 'mx_conflict' | 'outbound_disabled' | 'cannot_send_from_domain' | 'recipient_not_allowed' | 'outbound_key_missing' | 'outbound_unreachable' | 'outbound_key_invalid' | 'outbound_capacity_exhausted' | 'outbound_response_malformed' | 'outbound_relay_failed' | 'discard_not_enabled' | 'inbound_not_repliable' | 'search_timeout' | 'authorization_pending' | 'slow_down' | 'access_denied' | 'expired_token' | 'invalid_device_code';
351
351
  message: string;
352
352
  /**
353
353
  * Optional structured data that callers can inspect to recover
@@ -644,6 +644,66 @@ type EmailSummary = {
644
644
  webhook_status?: EmailWebhookStatus;
645
645
  webhook_attempt_count: number;
646
646
  };
647
+ type EmailSearchHighlights = {
648
+ /**
649
+ * Subject snippets with matching terms highlighted.
650
+ */
651
+ subject: Array<string>;
652
+ /**
653
+ * Body snippets with matching terms highlighted.
654
+ */
655
+ body: Array<string>;
656
+ };
657
+ type EmailSearchResult = EmailSummary & {
658
+ /**
659
+ * Number of parsed attachments on the email.
660
+ */
661
+ attachment_count: number;
662
+ /**
663
+ * Whether the parsed From address is known to this org from prior authenticated inbound mail.
664
+ */
665
+ from_known_address: boolean;
666
+ /**
667
+ * Relevance score. Present only when sorting by relevance.
668
+ */
669
+ score?: number;
670
+ highlights?: EmailSearchHighlights;
671
+ };
672
+ type EmailSearchMeta = {
673
+ /**
674
+ * Total number of matching records, capped when `total_capped` is true.
675
+ */
676
+ total: number;
677
+ /**
678
+ * Whether `total` was capped instead of counted exactly.
679
+ */
680
+ total_capped: boolean;
681
+ /**
682
+ * Page size used for this request.
683
+ */
684
+ limit: number;
685
+ /**
686
+ * Cursor for the next search page, or null if no more results.
687
+ */
688
+ cursor: string | null;
689
+ /**
690
+ * Sort mode used for the result page.
691
+ */
692
+ sort: 'relevance' | 'received_at_desc' | 'received_at_asc';
693
+ };
694
+ type EmailSearchFacetBucket = {
695
+ value: string | null;
696
+ count: number;
697
+ };
698
+ type EmailSearchFacets = {
699
+ by_sender: Array<EmailSearchFacetBucket>;
700
+ by_domain: Array<EmailSearchFacetBucket>;
701
+ by_status: Array<EmailSearchFacetBucket>;
702
+ has_attachment: {
703
+ true: number;
704
+ false: number;
705
+ };
706
+ };
647
707
  type EmailDetail = {
648
708
  id: string;
649
709
  message_id?: string | null;
@@ -2186,6 +2246,109 @@ type ListEmailsResponses = {
2186
2246
  };
2187
2247
  };
2188
2248
  type ListEmailsResponse = ListEmailsResponses[keyof ListEmailsResponses];
2249
+ type SearchEmailsData = {
2250
+ body?: never;
2251
+ path?: never;
2252
+ query?: {
2253
+ /**
2254
+ * Full-text search DSL query.
2255
+ */
2256
+ q?: string;
2257
+ /**
2258
+ * Filter by sender address or sender domain.
2259
+ */
2260
+ from?: string;
2261
+ /**
2262
+ * Filter by recipient address or recipient domain.
2263
+ */
2264
+ to?: string;
2265
+ /**
2266
+ * Full-text search restricted to the subject field.
2267
+ */
2268
+ subject?: string;
2269
+ /**
2270
+ * Full-text search restricted to the parsed text body.
2271
+ */
2272
+ body?: string;
2273
+ /**
2274
+ * Filter by domain ID.
2275
+ */
2276
+ domain_id?: string;
2277
+ /**
2278
+ * Filter by inbound email lifecycle status.
2279
+ */
2280
+ status?: EmailStatus;
2281
+ /**
2282
+ * Filter emails received on or after this timestamp.
2283
+ */
2284
+ date_from?: string;
2285
+ /**
2286
+ * Filter emails received on or before this timestamp.
2287
+ */
2288
+ date_to?: string;
2289
+ /**
2290
+ * Filter by whether the email has one or more attachments.
2291
+ */
2292
+ has_attachment?: 'true' | 'false';
2293
+ /**
2294
+ * Filter to emails with spam score below this value.
2295
+ */
2296
+ spam_score_lt?: number;
2297
+ /**
2298
+ * Filter to emails with spam score greater than or equal to this value.
2299
+ */
2300
+ spam_score_gte?: number;
2301
+ /**
2302
+ * Sort mode. Defaults to relevance when a text query is present,
2303
+ * otherwise `received_at_desc`.
2304
+ *
2305
+ */
2306
+ sort?: 'relevance' | 'received_at_desc' | 'received_at_asc';
2307
+ /**
2308
+ * Opaque pagination cursor from a previous search response.
2309
+ */
2310
+ cursor?: string;
2311
+ /**
2312
+ * Number of results per page
2313
+ */
2314
+ limit?: number;
2315
+ /**
2316
+ * Include subject/body highlight snippets when text search is active.
2317
+ */
2318
+ snippet?: 'true' | 'false';
2319
+ /**
2320
+ * Include facet counts for sender, domain, status, and attachment presence.
2321
+ */
2322
+ include_facets?: 'true' | 'false';
2323
+ };
2324
+ url: '/emails/search';
2325
+ };
2326
+ type SearchEmailsErrors = {
2327
+ /**
2328
+ * Invalid request parameters
2329
+ */
2330
+ 400: ErrorResponse;
2331
+ /**
2332
+ * Invalid or missing API key
2333
+ */
2334
+ 401: ErrorResponse;
2335
+ /**
2336
+ * Search query timed out
2337
+ */
2338
+ 504: ErrorResponse;
2339
+ };
2340
+ type SearchEmailsError = SearchEmailsErrors[keyof SearchEmailsErrors];
2341
+ type SearchEmailsResponses = {
2342
+ /**
2343
+ * Search results
2344
+ */
2345
+ 200: SuccessEnvelope & {
2346
+ data: Array<EmailSearchResult>;
2347
+ meta: EmailSearchMeta;
2348
+ facets?: EmailSearchFacets;
2349
+ };
2350
+ };
2351
+ type SearchEmailsResponse = SearchEmailsResponses[keyof SearchEmailsResponses];
2189
2352
  type DeleteEmailData = {
2190
2353
  body?: never;
2191
2354
  path: {
@@ -3409,7 +3572,7 @@ type SetFunctionSecretResponses = {
3409
3572
  };
3410
3573
  type SetFunctionSecretResponse = SetFunctionSecretResponses[keyof SetFunctionSecretResponses];
3411
3574
  declare namespace sdk_gen_d_exports {
3412
- export { Options, addDomain, cliLogout, createEndpoint, createFilter, createFunction, createFunctionSecret, deleteDomain, deleteEmail, deleteEndpoint, deleteFilter, deleteFunction, deleteFunctionSecret, discardEmailContent, downloadAttachments, downloadRawEmail, getAccount, getEmail, getFunction, getSendPermissions, getSentEmail, getStorageStats, getWebhookSecret, listDeliveries, listDomains, listEmails, listEndpoints, listFilters, listFunctionSecrets, listFunctions, listSentEmails, pollCliLogin, replayDelivery, replayEmailWebhooks, replyToEmail, rotateWebhookSecret, sendEmail, setFunctionSecret, startCliLogin, testEndpoint, testFunction, updateAccount, updateDomain, updateEndpoint, updateFilter, updateFunction, verifyDomain };
3575
+ export { Options, addDomain, cliLogout, createEndpoint, createFilter, createFunction, createFunctionSecret, deleteDomain, deleteEmail, deleteEndpoint, deleteFilter, deleteFunction, deleteFunctionSecret, discardEmailContent, downloadAttachments, downloadRawEmail, getAccount, getEmail, getFunction, getSendPermissions, getSentEmail, getStorageStats, getWebhookSecret, listDeliveries, listDomains, listEmails, listEndpoints, listFilters, listFunctionSecrets, listFunctions, listSentEmails, pollCliLogin, replayDelivery, replayEmailWebhooks, replyToEmail, rotateWebhookSecret, searchEmails, sendEmail, setFunctionSecret, startCliLogin, testEndpoint, testFunction, updateAccount, updateDomain, updateEndpoint, updateFilter, updateFunction, verifyDomain };
3413
3576
  }
3414
3577
  type Options<TData extends TDataShape = TDataShape, ThrowOnError extends boolean = boolean, TResponse = unknown> = Options$1<TData, ThrowOnError, TResponse> & {
3415
3578
  /**
@@ -3557,6 +3720,23 @@ declare const verifyDomain: <ThrowOnError extends boolean = false>(options: Opti
3557
3720
  *
3558
3721
  */
3559
3722
  declare const listEmails: <ThrowOnError extends boolean = false>(options?: Options<ListEmailsData, ThrowOnError>) => RequestResult<ListEmailsResponses, ListEmailsErrors, ThrowOnError, "fields">;
3723
+ /**
3724
+ * Search inbound emails
3725
+ *
3726
+ * Searches inbound emails with structured filters and optional
3727
+ * full-text matching across parsed email fields. This endpoint is
3728
+ * optimized for filtered inbox views and CLI polling workflows:
3729
+ * callers that only need new accepted mail can pass
3730
+ * `sort=received_at_asc`, `snippet=false`, `include_facets=false`,
3731
+ * and a `date_from` timestamp.
3732
+ *
3733
+ * `q`, `subject`, and `body` use the same English full-text index
3734
+ * as the web inbox search. Structured filters such as `from`, `to`,
3735
+ * `domain_id`, status, attachment presence, and spam score bounds
3736
+ * are combined with the text query.
3737
+ *
3738
+ */
3739
+ declare const searchEmails: <ThrowOnError extends boolean = false>(options?: Options<SearchEmailsData, ThrowOnError>) => RequestResult<SearchEmailsResponses, SearchEmailsErrors, ThrowOnError, "fields">;
3560
3740
  /**
3561
3741
  * Delete an email
3562
3742
  */
@@ -3796,6 +3976,15 @@ declare const getSendPermissions: <ThrowOnError extends boolean = false>(options
3796
3976
  * the request returns once the relay accepts the message for delivery.
3797
3977
  * Set `wait: true` to wait for the first downstream SMTP delivery outcome.
3798
3978
  *
3979
+ * **Host routing.** /send-mail is served by the attachments-
3980
+ * supporting host (`https://api.primitive.dev/v1`) so the
3981
+ * request body can carry inline attachments up to ~30 MiB raw.
3982
+ * The primary host (`https://www.primitive.dev/api/v1`) also
3983
+ * accepts /send-mail for attachment-free sends; sends WITH
3984
+ * attachments to the primary host return 413
3985
+ * `attachments_unsupported_on_this_endpoint`. The typed SDKs
3986
+ * route /send-mail to the attachments host automatically.
3987
+ *
3799
3988
  */
3800
3989
  declare const sendEmail: <ThrowOnError extends boolean = false>(options: Options<SendEmailData, ThrowOnError>) => RequestResult<SendEmailResponses, SendEmailErrors, ThrowOnError, "fields">;
3801
3990
  /**
@@ -3984,11 +4173,15 @@ declare const deleteFunctionSecret: <ThrowOnError extends boolean = false>(optio
3984
4173
  declare const setFunctionSecret: <ThrowOnError extends boolean = false>(options: Options<SetFunctionSecretData, ThrowOnError>) => RequestResult<SetFunctionSecretResponses, SetFunctionSecretErrors, ThrowOnError, "fields">;
3985
4174
  //#endregion
3986
4175
  //#region src/api/index.d.ts
3987
- declare const DEFAULT_BASE_URL = "https://www.primitive.dev/api/v1";
4176
+ declare const DEFAULT_API_BASE_URL_1 = "https://www.primitive.dev/api/v1";
4177
+ declare const DEFAULT_API_BASE_URL_2 = "https://api.primitive.dev/v1";
3988
4178
  interface PrimitiveApiClientOptions extends Omit<Config, "auth" | "baseUrl"> {
3989
4179
  apiKey?: string;
3990
4180
  auth?: Config["auth"];
3991
- baseUrl?: string;
4181
+ /** @internal Override for the primary API host. Production default is correct; this exists for staging/local testing only. */
4182
+ apiBaseUrl1?: string;
4183
+ /** @internal Override for the attachments-supporting send host. Production default is correct; this exists for staging/local testing only. */
4184
+ apiBaseUrl2?: string;
3992
4185
  }
3993
4186
  interface SendThreadInput {
3994
4187
  inReplyTo?: string;
@@ -4088,7 +4281,23 @@ declare class PrimitiveApiError extends Error {
4088
4281
  });
4089
4282
  }
4090
4283
  declare class PrimitiveApiClient {
4284
+ /**
4285
+ * Generated client targeting the primary API host (apiBaseUrl1). Use
4286
+ * this when passing `client: ...` to a generated operation function
4287
+ * for every endpoint EXCEPT /send-mail. The hand-written
4288
+ * PrimitiveClient.send / .reply / .forward methods on the subclass
4289
+ * route /send-mail to the host-2 client internally.
4290
+ */
4091
4291
  readonly client: Client;
4292
+ /**
4293
+ * @internal Generated client targeting the attachments-supporting
4294
+ * send host (apiBaseUrl2). Used by PrimitiveClient.send() under the
4295
+ * hood. Exposed for the CLI's hand-rolled send command, which calls
4296
+ * the generated sendEmail directly; not part of the publicly-
4297
+ * documented SDK surface. Customer code should call .send() on the
4298
+ * subclass instead.
4299
+ */
4300
+ readonly _sendClient: Client;
4092
4301
  constructor(options?: PrimitiveApiClientOptions);
4093
4302
  getConfig(): Config<ClientOptions$1>;
4094
4303
  setConfig(config: Config): Config<ClientOptions$1>;
@@ -4118,4 +4327,4 @@ declare function createPrimitiveClient(options?: PrimitiveClientOptions): Primit
4118
4327
  declare function client(options?: PrimitiveClientOptions): PrimitiveClient;
4119
4328
  declare const operations: typeof sdk_gen_d_exports;
4120
4329
  //#endregion
4121
- export { rotateWebhookSecret as $, UpdateAccountData as $a, ReplyToEmailData as $i, EmailStatus as $n, ListDomainsData as $r, CreateFunctionSecretResponses as $t, deleteFunctionSecret as A, SetFunctionSecretResponse as Aa, ListSentEmailsData as Ai, DeleteFunctionSecretResponses as An, VerifyDomainData as Ao, GetSendPermissionsResponse as Ar, CreateEndpointError as At, getWebhookSecret as B, TestEndpointData as Ba, PollCliLoginResponse as Bi, DomainVerifyResult as Bn, Options$1 as Bo, GetStorageStatsResponse as Br, CreateFilterResponses as Bt, createFunction as C, SentEmailDetail as Ca, ListFunctionSecretsResponse as Ci, DeleteFunctionErrors as Cn, UpdateFunctionData as Co, GetFunctionError as Cr, CliLogoutErrors as Ct, deleteEndpoint as D, SetFunctionSecretError as Da, ListFunctionsErrors as Di, DeleteFunctionSecretError as Dn, UpdateFunctionResponse as Do, GetSendPermissionsData as Dr, CliLogoutResult as Dt, deleteEmail as E, SetFunctionSecretData as Ea, ListFunctionsError as Ei, DeleteFunctionSecretData as En, UpdateFunctionInput as Eo, GetFunctionResponses as Er, CliLogoutResponses as Et, getEmail as F, StartCliLoginInput as Fa, PaginationMeta as Fi, DiscardEmailContentError as Fn, WebhookSecret as Fo, GetSentEmailResponse as Fr, CreateFilterData as Ft, listFilters as G, TestFunctionData as Ga, ReplayDeliveryResponse as Gi, DownloadAttachmentsResponses as Gn, GetWebhookSecretResponse as Gr, CreateFunctionResponse as Gt, listDomains as H, TestEndpointErrors as Ha, ReplayDeliveryData as Hi, DownloadAttachmentsError as Hn, RequestResult as Ho, GetWebhookSecretData as Hr, CreateFunctionError as Ht, getFunction as I, StartCliLoginResponse as Ia, PollCliLoginData as Ii, DiscardEmailContentErrors as In, Client as Io, GetSentEmailResponses as Ir, CreateFilterError as It, listSentEmails as J, TestFunctionResponse as Ja, ReplayEmailWebhooksError as Ji, DownloadRawEmailErrors as Jn, ListDeliveriesData as Jr, CreateFunctionSecretData as Jt, listFunctionSecrets as K, TestFunctionError as Ka, ReplayDeliveryResponses as Ki, DownloadRawEmailData as Kn, GetWebhookSecretResponses as Kr, CreateFunctionResponses as Kt, getSendPermissions as L, StartCliLoginResponses as La, PollCliLoginError as Li, DiscardEmailContentResponse as Ln, ClientOptions$1 as Lo, GetStorageStatsData as Lr, CreateFilterErrors as Lt, downloadAttachments as M, StartCliLoginData as Ma, ListSentEmailsErrors as Mi, DeliverySummary as Mn, VerifyDomainErrors as Mo, GetSentEmailData as Mr, CreateEndpointInput as Mt, downloadRawEmail as N, StartCliLoginError as Na, ListSentEmailsResponse as Ni, DiscardContentResult as Nn, VerifyDomainResponse as No, GetSentEmailError as Nr, CreateEndpointResponse as Nt, deleteFilter as O, SetFunctionSecretErrors as Oa, ListFunctionsResponse as Oi, DeleteFunctionSecretErrors as On, UpdateFunctionResponses as Oo, GetSendPermissionsError as Or, ClientOptions as Ot, getAccount as P, StartCliLoginErrors as Pa, ListSentEmailsResponses as Pi, DiscardEmailContentData as Pn, VerifyDomainResponses as Po, GetSentEmailErrors as Pr, CreateEndpointResponses as Pt, replyToEmail as Q, UnverifiedDomain as Qa, ReplayResult as Qi, EmailDetailReply as Qn, ListDeliveriesResponses as Qr, CreateFunctionSecretResponse as Qt, getSentEmail as R, StorageStats as Ra, PollCliLoginErrors as Ri, DiscardEmailContentResponses as Rn, Config as Ro, GetStorageStatsError as Rr, CreateFilterInput as Rt, createFilter as S, SendPermissionsMeta as Sa, ListFunctionSecretsErrors as Si, DeleteFunctionError as Sn, UpdateFilterResponses as So, GetFunctionData as Sr, CliLogoutError as St, deleteDomain as T, SentEmailSummary as Ta, ListFunctionsData as Ti, DeleteFunctionResponses as Tn, UpdateFunctionErrors as To, GetFunctionResponse as Tr, CliLogoutResponse as Tt, listEmails as U, TestEndpointResponse as Ua, ReplayDeliveryError as Ui, DownloadAttachmentsErrors as Un, ResponseStyle as Uo, GetWebhookSecretError as Ur, CreateFunctionErrors as Ut, listDeliveries as V, TestEndpointError as Va, PollCliLoginResponses as Vi, DownloadAttachmentsData as Vn, RequestOptions$1 as Vo, GetStorageStatsResponses as Vr, CreateFunctionData as Vt, listEndpoints as W, TestEndpointResponses as Wa, ReplayDeliveryErrors as Wi, DownloadAttachmentsResponse as Wn, Auth as Wo, GetWebhookSecretErrors as Wr, CreateFunctionInput as Wt, replayDelivery as X, TestInvocationResult as Xa, ReplayEmailWebhooksResponse as Xi, DownloadRawEmailResponses as Xn, ListDeliveriesErrors as Xr, CreateFunctionSecretErrors as Xt, pollCliLogin as Y, TestFunctionResponses as Ya, ReplayEmailWebhooksErrors as Yi, DownloadRawEmailResponse as Yn, ListDeliveriesError as Yr, CreateFunctionSecretError as Yt, replayEmailWebhooks as Z, TestResult as Za, ReplayEmailWebhooksResponses as Zi, EmailDetail as Zn, ListDeliveriesResponse as Zr, CreateFunctionSecretInput as Zt, operations as _, SendPermissionAddress as _a, ListFiltersErrors as _i, DeleteFilterError as _n, UpdateFilterData as _o, GetEmailData as _r, AddDomainResponse as _t, PrimitiveApiError as a, RotateWebhookSecretData as aa, ListEmailsError as ai, DeleteDomainResponses as an, UpdateDomainData as ao, FunctionDeployStatus as ar, updateAccount as at, cliLogout as b, SendPermissionRule as ba, ListFunctionSecretsData as bi, DeleteFilterResponses as bn, UpdateFilterInput as bo, GetEmailResponse as br, CliLoginStartResult as bt, PrimitiveClientOptions as c, RotateWebhookSecretResponse as ca, ListEmailsResponses as ci, DeleteEmailErrors as cn, UpdateDomainInput as co, FunctionSecretListItem as cr, updateFilter as ct, SendInput as d, SendEmailError as da, ListEndpointsErrors as di, DeleteEndpointData as dn, UpdateEndpointData as do, GateFix as dr, Account as dt, ReplyToEmailError as ea, ListDomainsError as ei, Cursor as en, UpdateAccountError as eo, EmailSummary as er, sendEmail as et, SendResult as f, SendEmailErrors as fa, ListEndpointsResponse as fi, DeleteEndpointError as fn, UpdateEndpointError as fo, GetAccountData as fr, AccountUpdated as ft, createPrimitiveClient as g, SendMailResult as ga, ListFiltersError as gi, DeleteFilterData as gn, UpdateEndpointResponses as go, GetAccountResponses as gr, AddDomainInput as gt, createPrimitiveApiClient as h, SendMailInput as ha, ListFiltersData as hi, DeleteEndpointResponses as hn, UpdateEndpointResponse as ho, GetAccountResponse as hr, AddDomainErrors as ht, PrimitiveApiClientOptions as i, ResourceId as ia, ListEmailsData as ii, DeleteDomainResponse as in, UpdateAccountResponses as io, Filter as ir, testFunction as it, discardEmailContent as j, SetFunctionSecretResponses as ja, ListSentEmailsError as ji, DeliveryStatus as jn, VerifyDomainError as jo, GetSendPermissionsResponses as jr, CreateEndpointErrors as jt, deleteFunction as k, SetFunctionSecretInput as ka, ListFunctionsResponses as ki, DeleteFunctionSecretResponse as kn, VerifiedDomain as ko, GetSendPermissionsErrors as kr, CreateEndpointData as kt, ReplyInput as l, RotateWebhookSecretResponses as la, ListEndpointsData as li, DeleteEmailResponse as ln, UpdateDomainResponse as lo, FunctionSecretWriteResult as lr, updateFunction as lt, client as m, SendEmailResponses as ma, ListEnvelope as mi, DeleteEndpointResponse as mn, UpdateEndpointInput as mo, GetAccountErrors as mr, AddDomainError as mt, ForwardInput as n, ReplyToEmailResponse as na, ListDomainsResponse as ni, DeleteDomainError as nn, UpdateAccountInput as no, Endpoint as nr, startCliLogin as nt, PrimitiveApiErrorDetails as o, RotateWebhookSecretError as oa, ListEmailsErrors as oi, DeleteEmailData as on, UpdateDomainError as oo, FunctionDetail as or, updateDomain as ot, SendThreadInput as p, SendEmailResponse as pa, ListEndpointsResponses as pi, DeleteEndpointErrors as pn, UpdateEndpointErrors as po, GetAccountError as pr, AddDomainData as pt, listFunctions as q, TestFunctionErrors as qa, ReplayEmailWebhooksData as qi, DownloadRawEmailError as qn, Limit as qr, CreateFunctionResult as qt, PrimitiveApiClient as r, ReplyToEmailResponses as ra, ListDomainsResponses as ri, DeleteDomainErrors as rn, UpdateAccountResponse as ro, ErrorResponse as rr, testEndpoint as rt, PrimitiveClient as s, RotateWebhookSecretErrors as sa, ListEmailsResponse as si, DeleteEmailError as sn, UpdateDomainErrors as so, FunctionListItem as sr, updateEndpoint as st, DEFAULT_BASE_URL as t, ReplyToEmailErrors as ta, ListDomainsErrors as ti, DeleteDomainData as tn, UpdateAccountErrors as to, EmailWebhookStatus as tr, setFunctionSecret as tt, RequestOptions as u, SendEmailData as ua, ListEndpointsError as ui, DeleteEmailResponses as un, UpdateDomainResponses as uo, GateDenial as ur, verifyDomain as ut, Options as v, SendPermissionAnyRecipient as va, ListFiltersResponse as vi, DeleteFilterErrors as vn, UpdateFilterError as vo, GetEmailError as vr, AddDomainResponses as vt, createFunctionSecret as w, SentEmailStatus as wa, ListFunctionSecretsResponses as wi, DeleteFunctionResponse as wn, UpdateFunctionError as wo, GetFunctionErrors as wr, CliLogoutInput as wt, createEndpoint as x, SendPermissionYourDomain as xa, ListFunctionSecretsError as xi, DeleteFunctionData as xn, UpdateFilterResponse as xo, GetEmailResponses as xr, CliLogoutData as xt, addDomain as y, SendPermissionManagedZone as ya, ListFiltersResponses as yi, DeleteFilterResponse as yn, UpdateFilterErrors as yo, GetEmailErrors as yr, CliLoginPollResult as yt, getStorageStats as z, SuccessEnvelope as za, PollCliLoginInput as zi, Domain as zn, CreateClientConfig as zo, GetStorageStatsErrors as zr, CreateFilterResponse as zt };
4330
+ export { replyToEmail as $, TestEndpointError as $a, ReplayDeliveryResponses as $i, EmailDetail as $n, RequestOptions$1 as $o, GetWebhookSecretResponses as $r, CreateFunctionSecretInput as $t, deleteFunction as A, SendPermissionAnyRecipient as Aa, ListFunctionSecretsResponse as Ai, DeleteFunctionSecretErrors as An, UpdateFilterError as Ao, GetFunctionError as Ar, ClientOptions as At, getStorageStats as B, SetFunctionSecretErrors as Ba, ListSentEmailsResponse as Bi, DiscardEmailContentResponses as Bn, UpdateFunctionResponses as Bo, GetSentEmailError as Br, CreateFilterInput as Bt, createFilter as C, SendEmailError as Ca, ListFiltersError as Ci, DeleteFunctionData as Cn, UpdateEndpointData as Co, GetAccountResponses as Cr, CliLogoutData as Ct, deleteEmail as D, SendMailInput as Da, ListFunctionSecretsData as Di, DeleteFunctionResponses as Dn, UpdateEndpointResponse as Do, GetEmailResponse as Dr, CliLogoutResponse as Dt, deleteDomain as E, SendEmailResponses as Ea, ListFiltersResponses as Ei, DeleteFunctionResponse as En, UpdateEndpointInput as Eo, GetEmailErrors as Er, CliLogoutInput as Et, getAccount as F, SentEmailDetail as Fa, ListFunctionsResponse as Fi, DiscardContentResult as Fn, UpdateFunctionData as Fo, GetSendPermissionsError as Fr, CreateEndpointResponse as Ft, listEndpoints as G, StartCliLoginError as Ga, PollCliLoginErrors as Gi, DownloadAttachmentsErrors as Gn, VerifyDomainResponse as Go, GetStorageStatsError as Gr, CreateFunctionErrors as Gt, listDeliveries as H, SetFunctionSecretResponse as Ha, PaginationMeta as Hi, DomainVerifyResult as Hn, VerifyDomainData as Ho, GetSentEmailResponse as Hr, CreateFilterResponses as Ht, getEmail as I, SentEmailStatus as Ia, ListFunctionsResponses as Ii, DiscardEmailContentData as In, UpdateFunctionError as Io, GetSendPermissionsErrors as Ir, CreateEndpointResponses as It, listFunctions as J, StartCliLoginResponse as Ja, PollCliLoginResponses as Ji, DownloadRawEmailData as Jn, Client as Jo, GetStorageStatsResponses as Jr, CreateFunctionResponses as Jt, listFilters as K, StartCliLoginErrors as Ka, PollCliLoginInput as Ki, DownloadAttachmentsResponse as Kn, VerifyDomainResponses as Ko, GetStorageStatsErrors as Kr, CreateFunctionInput as Kt, getFunction as L, SentEmailSummary as La, ListSentEmailsData as Li, DiscardEmailContentError as Ln, UpdateFunctionErrors as Lo, GetSendPermissionsResponse as Lr, CreateFilterData as Lt, discardEmailContent as M, SendPermissionRule as Ma, ListFunctionsData as Mi, DeleteFunctionSecretResponses as Mn, UpdateFilterInput as Mo, GetFunctionResponse as Mr, CreateEndpointError as Mt, downloadAttachments as N, SendPermissionYourDomain as Na, ListFunctionsError as Ni, DeliveryStatus as Nn, UpdateFilterResponse as No, GetFunctionResponses as Nr, CreateEndpointErrors as Nt, deleteEndpoint as O, SendMailResult as Oa, ListFunctionSecretsError as Oi, DeleteFunctionSecretData as On, UpdateEndpointResponses as Oo, GetEmailResponses as Or, CliLogoutResponses as Ot, downloadRawEmail as P, SendPermissionsMeta as Pa, ListFunctionsErrors as Pi, DeliverySummary as Pn, UpdateFilterResponses as Po, GetSendPermissionsData as Pr, CreateEndpointInput as Pt, replayEmailWebhooks as Q, TestEndpointData as Qa, ReplayDeliveryResponse as Qi, DownloadRawEmailResponses as Qn, Options$1 as Qo, GetWebhookSecretResponse as Qr, CreateFunctionSecretErrors as Qt, getSendPermissions as R, SetFunctionSecretData as Ra, ListSentEmailsError as Ri, DiscardEmailContentErrors as Rn, UpdateFunctionInput as Ro, GetSendPermissionsResponses as Rr, CreateFilterError as Rt, createEndpoint as S, SendEmailData as Sa, ListFiltersData as Si, DeleteFilterResponses as Sn, UpdateDomainResponses as So, GetAccountResponse as Sr, CliLoginStartResult as St, createFunctionSecret as T, SendEmailResponse as Ta, ListFiltersResponse as Ti, DeleteFunctionErrors as Tn, UpdateEndpointErrors as To, GetEmailError as Tr, CliLogoutErrors as Tt, listDomains as U, SetFunctionSecretResponses as Ua, PollCliLoginData as Ui, DownloadAttachmentsData as Un, VerifyDomainError as Uo, GetSentEmailResponses as Ur, CreateFunctionData as Ut, getWebhookSecret as V, SetFunctionSecretInput as Va, ListSentEmailsResponses as Vi, Domain as Vn, VerifiedDomain as Vo, GetSentEmailErrors as Vr, CreateFilterResponse as Vt, listEmails as W, StartCliLoginData as Wa, PollCliLoginError as Wi, DownloadAttachmentsError as Wn, VerifyDomainErrors as Wo, GetStorageStatsData as Wr, CreateFunctionError as Wt, pollCliLogin as X, StorageStats as Xa, ReplayDeliveryError as Xi, DownloadRawEmailErrors as Xn, Config as Xo, GetWebhookSecretError as Xr, CreateFunctionSecretData as Xt, listSentEmails as Y, StartCliLoginResponses as Ya, ReplayDeliveryData as Yi, DownloadRawEmailError as Yn, ClientOptions$1 as Yo, GetWebhookSecretData as Yr, CreateFunctionResult as Yt, replayDelivery as Z, SuccessEnvelope as Za, ReplayDeliveryErrors as Zi, DownloadRawEmailResponse as Zn, CreateClientConfig as Zo, GetWebhookSecretErrors as Zr, CreateFunctionSecretError as Zt, createPrimitiveClient as _, SearchEmailsData as _a, ListEndpointsError as _i, DeleteEndpointResponses as _n, UpdateDomainData as _o, GateDenial as _r, AddDomainErrors as _t, PrimitiveApiClientOptions as a, ReplayResult as aa, ListDeliveriesResponses as ai, DeleteDomainErrors as an, TestFunctionErrors as ao, EmailSearchResult as ar, testEndpoint as at, addDomain as b, SearchEmailsResponse as ba, ListEndpointsResponses as bi, DeleteFilterErrors as bn, UpdateDomainInput as bo, GetAccountError as br, AddDomainResponses as bt, PrimitiveClient as c, ReplyToEmailErrors as ca, ListDomainsErrors as ci, DeleteEmailData as cn, TestInvocationResult as co, EmailWebhookStatus as cr, updateDomain as ct, RequestOptions as d, ResourceId as da, ListEmailsData as di, DeleteEmailResponse as dn, UpdateAccountData as do, Filter as dr, updateFunction as dt, ReplayEmailWebhooksData as ea, Limit as ei, CreateFunctionSecretResponse as en, TestEndpointErrors as eo, EmailDetailReply as er, RequestResult as es, rotateWebhookSecret as et, SendInput as f, RotateWebhookSecretData as fa, ListEmailsError as fi, DeleteEmailResponses as fn, UpdateAccountError as fo, FunctionDeployStatus as fr, verifyDomain as ft, createPrimitiveApiClient as g, RotateWebhookSecretResponses as ga, ListEndpointsData as gi, DeleteEndpointResponse as gn, UpdateAccountResponses as go, FunctionSecretWriteResult as gr, AddDomainError as gt, client as h, RotateWebhookSecretResponse as ha, ListEmailsResponses as hi, DeleteEndpointErrors as hn, UpdateAccountResponse as ho, FunctionSecretListItem as hr, AddDomainData as ht, PrimitiveApiClient as i, ReplayEmailWebhooksResponses as ia, ListDeliveriesResponse as ii, DeleteDomainError as in, TestFunctionError as io, EmailSearchMeta as ir, startCliLogin as it, deleteFunctionSecret as j, SendPermissionManagedZone as ja, ListFunctionSecretsResponses as ji, DeleteFunctionSecretResponse as jn, UpdateFilterErrors as jo, GetFunctionErrors as jr, CreateEndpointData as jt, deleteFilter as k, SendPermissionAddress as ka, ListFunctionSecretsErrors as ki, DeleteFunctionSecretError as kn, UpdateFilterData as ko, GetFunctionData as kr, CliLogoutResult as kt, PrimitiveClientOptions as l, ReplyToEmailResponse as la, ListDomainsResponse as li, DeleteEmailError as ln, TestResult as lo, Endpoint as lr, updateEndpoint as lt, SendThreadInput as m, RotateWebhookSecretErrors as ma, ListEmailsResponse as mi, DeleteEndpointError as mn, UpdateAccountInput as mo, FunctionListItem as mr, AccountUpdated as mt, DEFAULT_API_BASE_URL_2 as n, ReplayEmailWebhooksErrors as na, ListDeliveriesError as ni, Cursor as nn, TestEndpointResponses as no, EmailSearchFacets as nr, Auth as ns, sendEmail as nt, PrimitiveApiError as o, ReplyToEmailData as oa, ListDomainsData as oi, DeleteDomainResponse as on, TestFunctionResponse as oo, EmailStatus as or, testFunction as ot, SendResult as p, RotateWebhookSecretError as pa, ListEmailsErrors as pi, DeleteEndpointData as pn, UpdateAccountErrors as po, FunctionDetail as pr, Account as pt, listFunctionSecrets as q, StartCliLoginInput as qa, PollCliLoginResponse as qi, DownloadAttachmentsResponses as qn, WebhookSecret as qo, GetStorageStatsResponse as qr, CreateFunctionResponse as qt, ForwardInput as r, ReplayEmailWebhooksResponse as ra, ListDeliveriesErrors as ri, DeleteDomainData as rn, TestFunctionData as ro, EmailSearchHighlights as rr, setFunctionSecret as rt, PrimitiveApiErrorDetails as s, ReplyToEmailError as sa, ListDomainsError as si, DeleteDomainResponses as sn, TestFunctionResponses as so, EmailSummary as sr, updateAccount as st, DEFAULT_API_BASE_URL_1 as t, ReplayEmailWebhooksError as ta, ListDeliveriesData as ti, CreateFunctionSecretResponses as tn, TestEndpointResponse as to, EmailSearchFacetBucket as tr, ResponseStyle as ts, searchEmails as tt, ReplyInput as u, ReplyToEmailResponses as ua, ListDomainsResponses as ui, DeleteEmailErrors as un, UnverifiedDomain as uo, ErrorResponse as ur, updateFilter as ut, operations as v, SearchEmailsError as va, ListEndpointsErrors as vi, DeleteFilterData as vn, UpdateDomainError as vo, GateFix as vr, AddDomainInput as vt, createFunction as w, SendEmailErrors as wa, ListFiltersErrors as wi, DeleteFunctionError as wn, UpdateEndpointError as wo, GetEmailData as wr, CliLogoutError as wt, cliLogout as x, SearchEmailsResponses as xa, ListEnvelope as xi, DeleteFilterResponse as xn, UpdateDomainResponse as xo, GetAccountErrors as xr, CliLoginPollResult as xt, Options as y, SearchEmailsErrors as ya, ListEndpointsResponse as yi, DeleteFilterError as yn, UpdateDomainErrors as yo, GetAccountData as yr, AddDomainResponse as yt, getSentEmail as z, SetFunctionSecretError as za, ListSentEmailsErrors as zi, DiscardEmailContentResponse as zn, UpdateFunctionResponse as zo, GetSentEmailData as zr, CreateFilterErrors as zt };
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { A as UnknownEvent, C as ParsedDataFailed, D as RawContentDownloadOnly, E as RawContent, M as WebhookAttachment, N as WebhookEvent, O as RawContentInline, S as ParsedDataComplete, T as ParsedStatus, _ as ForwardResultInline, a as DmarcPolicy, b as KnownWebhookEvent, c as EmailAnalysis, d as EventType, f as ForwardAnalysis, g as ForwardResultAttachmentSkipped, h as ForwardResultAttachmentAnalyzed, i as DkimSignature, j as ValidateEmailAuthResult, k as SpfResult, l as EmailAuth, m as ForwardResult, n as AuthVerdict, o as DmarcResult, p as ForwardOriginalSender, r as DkimResult, s as EmailAddress, t as AuthConfidence, u as EmailReceivedEvent, v as ForwardVerdict, w as ParsedError, x as ParsedData, y as ForwardVerification } from "./types-9vXGZjPd.js";
2
2
  import { a as buildReplySubject, c as parseHeaderAddress, i as buildForwardSubject, n as ReceivedEmailAddress, o as formatAddress, r as ReceivedEmailThread, s as normalizeReceivedEmail, t as ReceivedEmail } from "./received-email-DNjpq_Wt.js";
3
- import { a as PrimitiveApiError, c as PrimitiveClientOptions, d as SendInput, f as SendResult, g as createPrimitiveClient, l as ReplyInput, m as client, n as ForwardInput, p as SendThreadInput, s as PrimitiveClient } from "./index-C6ObsYjq.js";
3
+ import { _ as createPrimitiveClient, c as PrimitiveClient, f as SendInput, h as client, l as PrimitiveClientOptions, m as SendThreadInput, o as PrimitiveApiError, p as SendResult, r as ForwardInput, u as ReplyInput } from "./index-QTYQpSFt.js";
4
4
  import { A as VerifyOptions, B as PAYLOAD_ERRORS, C as signStandardWebhooksPayload, D as PRIMITIVE_CONFIRMED_HEADER, E as LEGACY_SIGNATURE_HEADER, F as VerifyDownloadTokenResult, G as VERIFICATION_ERRORS, H as RAW_EMAIL_ERRORS, I as generateDownloadToken, J as WebhookPayloadErrorCode, K as WebhookErrorCode, L as verifyDownloadToken, M as verifyWebhookSignature, N as GenerateDownloadTokenOptions, O as PRIMITIVE_SIGNATURE_HEADER, P as VerifyDownloadTokenOptions, Q as WebhookVerificationErrorCode, R as safeValidateEmailReceivedEvent, S as StandardWebhooksVerifyOptions, T as LEGACY_CONFIRMED_HEADER, U as RawEmailDecodeError, V as PrimitiveWebhookError, W as RawEmailDecodeErrorCode, X as WebhookValidationErrorCode, Y as WebhookValidationError, Z as WebhookVerificationError, _ as emailReceivedEventJsonSchema, a as confirmedHeaders, b as STANDARD_WEBHOOK_TIMESTAMP_HEADER, c as handleWebhook, d as isRawIncluded, f as parseWebhookEvent, g as validateEmailAuth, h as WEBHOOK_VERSION, i as WebhookHeaders, j as signWebhookPayload, k as SignResult, l as isDownloadExpired, m as verifyRawEmailDownload, n as HandleWebhookOptions, o as decodeRawEmail, p as receive, q as WebhookPayloadError, r as ReceiveRequestOptions, s as getDownloadTimeRemaining, t as DecodeRawEmailOptions, u as isEmailReceivedEvent, v as STANDARD_WEBHOOK_ID_HEADER, w as verifyStandardWebhooksSignature, x as StandardWebhooksSignResult, y as STANDARD_WEBHOOK_SIGNATURE_HEADER, z as validateEmailReceivedEvent } from "./index-CDlwyxdp.js";
5
5
 
6
6
  //#region src/index.d.ts
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { a as parseHeaderAddress, i as normalizeReceivedEmail, n as buildReplySubject, r as formatAddress, t as buildForwardSubject } from "./received-email-D6tKtWwW.js";
2
- import { a as client, i as PrimitiveClient, r as PrimitiveApiError, s as createPrimitiveClient } from "./api-DNF21MDo.js";
2
+ import { a as PrimitiveClient, c as createPrimitiveClient, i as PrimitiveApiError, o as client } from "./api-BjzvA2Fy.js";
3
3
  import { A as PRIMITIVE_CONFIRMED_HEADER, B as RAW_EMAIL_ERRORS, C as STANDARD_WEBHOOK_ID_HEADER, D as verifyStandardWebhooksSignature, E as signStandardWebhooksPayload, F as verifyDownloadToken, G as WebhookVerificationError, H as VERIFICATION_ERRORS, I as safeValidateEmailReceivedEvent, L as validateEmailReceivedEvent, M as signWebhookPayload, N as verifyWebhookSignature, O as LEGACY_CONFIRMED_HEADER, P as generateDownloadToken, R as PAYLOAD_ERRORS, S as emailReceivedEventJsonSchema, T as STANDARD_WEBHOOK_TIMESTAMP_HEADER, U as WebhookPayloadError, V as RawEmailDecodeError, W as WebhookValidationError, _ as DmarcResult, a as isDownloadExpired, b as ParsedStatus, c as parseWebhookEvent, d as WEBHOOK_VERSION, f as validateEmailAuth, g as DmarcPolicy, h as DkimResult, i as handleWebhook, j as PRIMITIVE_SIGNATURE_HEADER, k as LEGACY_SIGNATURE_HEADER, l as receive, m as AuthVerdict, n as decodeRawEmail, o as isEmailReceivedEvent, p as AuthConfidence, r as getDownloadTimeRemaining, s as isRawIncluded, t as confirmedHeaders, u as verifyRawEmailDownload, v as EventType, w as STANDARD_WEBHOOK_SIGNATURE_HEADER, x as SpfResult, y as ForwardVerdict, z as PrimitiveWebhookError } from "./webhook-rUjGV6Zu.js";
4
4
  //#region src/index.ts
5
5
  const primitive = {
@@ -333,9 +333,12 @@ export function removeStaleSavedCredentialOnUnauthorized(params) {
333
333
  }
334
334
  const baseUrlDiffersFromSaved = params.baseUrlOverridden &&
335
335
  params.auth.credentials !== null &&
336
- params.auth.baseUrl !== params.auth.credentials.base_url;
336
+ params.auth.apiBaseUrl1 !== params.auth.credentials.api_base_url_1;
337
337
  if (baseUrlDiffersFromSaved) {
338
- process.stderr.write("Saved Primitive CLI credentials were rejected by the overridden API base URL. The local credential was not removed; check --base-url / PRIMITIVE_API_URL, or run `primitive logout` to remove it.\n");
338
+ // Override env vars (PRIMITIVE_API_BASE_URL_1 / _2) are intentionally
339
+ // not advertised in --help; this hint is the only customer-visible
340
+ // mention. They're for internal staging/local testing.
341
+ process.stderr.write("Saved Primitive CLI credentials were rejected by the overridden API base URL. The local credential was not removed; unset PRIMITIVE_API_BASE_URL_1, or run `primitive logout` to remove the stored credential.\n");
339
342
  return false;
340
343
  }
341
344
  deleteCliCredentials(params.configDir);
@@ -381,10 +384,45 @@ export async function runWithTiming(enabled, fn) {
381
384
  // own static flags. Lives here so the flag's description and short
382
385
  // name stay consistent across the hand-coded and generated commands.
383
386
  export const TIME_FLAG_DESCRIPTION = "Print the wall-clock duration of this command to stderr after it completes (e.g. `[time: 1.34s]`). Useful for measuring `--wait` send latency, comparing CLI overhead, or capturing timing in scripts.";
387
+ // Shared description text for the api-base-url override flags. Keeps
388
+ // the wording identical across every command that includes them. The
389
+ // flags themselves are hidden from --help (internal staging/local-only).
390
+ export const API_BASE_URL_1_FLAG_DESCRIPTION = "Override the primary API base URL. Internal testing only; not documented to customers.";
391
+ export const API_BASE_URL_2_FLAG_DESCRIPTION = "Override the attachments-supporting send host base URL. Internal testing only; not documented to customers.";
392
+ // Helper: was either api-base-url override set by the caller? Used by
393
+ // removeStaleSavedCredentialOnUnauthorized to decide whether to
394
+ // preserve the saved credential when a 401 comes back.
395
+ export function baseUrlOverriddenFromFlags(flags) {
396
+ return (typeof flags["api-base-url-1"] === "string" ||
397
+ typeof flags["api-base-url-2"] === "string");
398
+ }
399
+ // Helper: resolve auth from a parsed-flags bag. Mirrors what every CLI
400
+ // command does inline so the api-base-url-1 / api-base-url-2 mapping
401
+ // stays in one place as we add more migration knobs.
402
+ export function resolveCliAuthFromFlags(flags, configDir) {
403
+ return resolveCliAuth({
404
+ apiKey: typeof flags["api-key"] === "string"
405
+ ? flags["api-key"]
406
+ : undefined,
407
+ apiBaseUrl1: typeof flags["api-base-url-1"] === "string"
408
+ ? flags["api-base-url-1"]
409
+ : undefined,
410
+ apiBaseUrl2: typeof flags["api-base-url-2"] === "string"
411
+ ? flags["api-base-url-2"]
412
+ : undefined,
413
+ configDir,
414
+ });
415
+ }
416
+ // Operations that route to the attachments-supporting host
417
+ // (apiBaseUrl2) instead of the primary API host. Internal to the CLI:
418
+ // as more operations migrate to host 2 over time, add their generated
419
+ // sdkName here. Today it is just /send-mail.
420
+ const HOST_2_OPERATIONS = new Set(["sendEmail"]);
384
421
  // Reserved flag names the body-field expander must never overwrite.
385
422
  // `--raw-body` and `--body-file` are the JSON escape hatches.
386
- // `--api-key`, `--base-url`, `--output` are infra. Path and query
387
- // params get added before body fields and take precedence.
423
+ // `--api-key`, `--api-base-url-1`, `--api-base-url-2`, `--output` are
424
+ // infra. Path and query params get added before body fields and take
425
+ // precedence.
388
426
  //
389
427
  // Note: `--body` is intentionally NOT reserved here. The naive
390
428
  // agent expectation (per AGX walkthrough) is that --body means
@@ -399,7 +437,8 @@ export const TIME_FLAG_DESCRIPTION = "Print the wall-clock duration of this comm
399
437
  // send` defines its own --body for the message text.
400
438
  const RESERVED_FLAG_NAMES = new Set([
401
439
  "api-key",
402
- "base-url",
440
+ "api-base-url-1",
441
+ "api-base-url-2",
403
442
  "raw-body",
404
443
  "body-file",
405
444
  "output",
@@ -438,9 +477,20 @@ function buildFlags(operation) {
438
477
  description: "Primitive API key (defaults to PRIMITIVE_API_KEY or saved `primitive login` credentials)",
439
478
  env: "PRIMITIVE_API_KEY",
440
479
  }),
441
- "base-url": Flags.string({
442
- description: "API base URL (defaults to PRIMITIVE_API_URL or production)",
443
- env: "PRIMITIVE_API_URL",
480
+ // Two override knobs for the dual-host setup. Hidden because they
481
+ // are for internal staging/local testing only. Production users
482
+ // should not override; the defaults route correctly. Env vars
483
+ // PRIMITIVE_API_BASE_URL_1 and PRIMITIVE_API_BASE_URL_2 carry the
484
+ // same semantics. Both are intentionally absent from --help output.
485
+ "api-base-url-1": Flags.string({
486
+ description: "Override the primary API base URL. Internal testing only; not documented to customers.",
487
+ env: "PRIMITIVE_API_BASE_URL_1",
488
+ hidden: true,
489
+ }),
490
+ "api-base-url-2": Flags.string({
491
+ description: "Override the attachments-supporting send host base URL. Internal testing only; not documented to customers.",
492
+ env: "PRIMITIVE_API_BASE_URL_2",
493
+ hidden: true,
444
494
  }),
445
495
  time: Flags.boolean({
446
496
  description: TIME_FLAG_DESCRIPTION,
@@ -543,19 +593,24 @@ export function createOperationCommand(operation) {
543
593
  const { flags } = await this.parse(OperationCommand);
544
594
  const parsedFlags = flags;
545
595
  await runWithTiming(parsedFlags.time === true, async () => {
546
- const baseUrlOverridden = typeof parsedFlags["base-url"] === "string";
596
+ const baseUrlOverridden = typeof parsedFlags["api-base-url-1"] === "string" ||
597
+ typeof parsedFlags["api-base-url-2"] === "string";
547
598
  const auth = resolveCliAuth({
548
599
  apiKey: typeof parsedFlags["api-key"] === "string"
549
600
  ? parsedFlags["api-key"]
550
601
  : undefined,
551
- baseUrl: typeof parsedFlags["base-url"] === "string"
552
- ? parsedFlags["base-url"]
602
+ apiBaseUrl1: typeof parsedFlags["api-base-url-1"] === "string"
603
+ ? parsedFlags["api-base-url-1"]
604
+ : undefined,
605
+ apiBaseUrl2: typeof parsedFlags["api-base-url-2"] === "string"
606
+ ? parsedFlags["api-base-url-2"]
553
607
  : undefined,
554
608
  configDir: this.config.configDir,
555
609
  });
556
610
  const apiClient = new PrimitiveApiClient({
557
611
  apiKey: auth.apiKey,
558
- baseUrl: auth.baseUrl,
612
+ apiBaseUrl1: auth.apiBaseUrl1,
613
+ apiBaseUrl2: auth.apiBaseUrl2,
559
614
  });
560
615
  // Two body sources, merged: explicit JSON via --body /
561
616
  // --body-file (the base) plus per-field flags (the
@@ -601,9 +656,15 @@ export function createOperationCommand(operation) {
601
656
  throw new Errors.CLIError(`Operation ${operation.operationId} requires a body. Pass each field as a --flag (see --help) or supply JSON via --raw-body / --body-file.`);
602
657
  }
603
658
  const operationFn = operations[operation.sdkName];
659
+ // Operations in HOST_2_OPERATIONS route to the attachments-
660
+ // supporting send host (apiBaseUrl2). Today that's only
661
+ // sendEmail; the list grows as we migrate more endpoints.
662
+ const targetClient = HOST_2_OPERATIONS.has(operation.sdkName)
663
+ ? apiClient._sendClient
664
+ : apiClient.client;
604
665
  const result = await operationFn({
605
666
  body,
606
- client: apiClient.client,
667
+ client: targetClient,
607
668
  parseAs: operation.binaryResponse ? "blob" : "auto",
608
669
  path: collectValues(operation.pathParams, parsedFlags),
609
670
  query: collectValues(operation.queryParams, parsedFlags),
@@ -1,7 +1,7 @@
1
1
  import { randomUUID } from "node:crypto";
2
2
  import { chmodSync, mkdirSync, readFileSync, renameSync, rmSync, statSync, writeFileSync, } from "node:fs";
3
3
  import { join } from "node:path";
4
- import { DEFAULT_BASE_URL } from "../api/index.js";
4
+ import { DEFAULT_API_BASE_URL_1, DEFAULT_API_BASE_URL_2, } from "../api/index.js";
5
5
  const CREDENTIALS_FILE = "credentials.json";
6
6
  const CREDENTIALS_LOCK_DIR = "credentials.lock";
7
7
  const CREDENTIALS_LOCK_STALE_MS = 30 * 60 * 1000;
@@ -16,10 +16,34 @@ function requireString(value, key) {
16
16
  }
17
17
  return raw;
18
18
  }
19
+ /**
20
+ * Sentinel returned by parseCredentials when the on-disk credentials
21
+ * were written by a pre-dual-host CLI version (i.e. they have
22
+ * `base_url` instead of `api_base_url_1`). The caller treats this as
23
+ * "no saved credentials" after auto-cleaning the stale file. Defined
24
+ * as a class-tagged error so loadCliCredentials can distinguish it
25
+ * from a genuine malformed-credentials error.
26
+ */
27
+ class StaleCredentialFormatError extends Error {
28
+ constructor() {
29
+ super("stale_credential_format");
30
+ this.name = "StaleCredentialFormatError";
31
+ }
32
+ }
19
33
  function parseCredentials(raw) {
20
34
  if (!isRecord(raw)) {
21
35
  throw new Error(`Stored Primitive CLI credentials are malformed: expected a JSON object. ${MALFORMED_CREDENTIALS_HINT}`);
22
36
  }
37
+ // Stored credentials from an older CLI version used the field name
38
+ // `base_url`; the dual-host rename moved this to `api_base_url_1`.
39
+ // Detect the old shape specifically so loadCliCredentials can wipe
40
+ // the stale file and emit a clear "you've been logged out" notice
41
+ // instead of every command hard-failing with a generic "malformed"
42
+ // error that doesn't surface the actual fix (re-login).
43
+ if (typeof raw.api_base_url_1 !== "string" &&
44
+ typeof raw.base_url === "string") {
45
+ throw new StaleCredentialFormatError();
46
+ }
23
47
  const orgName = raw.org_name;
24
48
  if (orgName !== null && typeof orgName !== "string") {
25
49
  throw new Error(`Stored Primitive CLI credentials are malformed: org_name must be a string or null. ${MALFORMED_CREDENTIALS_HINT}`);
@@ -30,19 +54,25 @@ function parseCredentials(raw) {
30
54
  key_prefix: requireString(raw, "key_prefix"),
31
55
  org_id: requireString(raw, "org_id"),
32
56
  org_name: orgName,
33
- base_url: requireString(raw, "base_url"),
57
+ api_base_url_1: requireString(raw, "api_base_url_1"),
34
58
  created_at: requireString(raw, "created_at"),
35
59
  };
36
60
  }
37
61
  export function credentialsPath(configDir) {
38
62
  return join(configDir, CREDENTIALS_FILE);
39
63
  }
40
- export function normalizeBaseUrl(baseUrl) {
41
- const trimmed = baseUrl?.trim();
64
+ function normalize(url, fallback) {
65
+ const trimmed = url?.trim();
42
66
  if (!trimmed)
43
- return DEFAULT_BASE_URL;
67
+ return fallback;
44
68
  return trimmed.replace(/\/+$/, "");
45
69
  }
70
+ export function normalizeApiBaseUrl1(url) {
71
+ return normalize(url, DEFAULT_API_BASE_URL_1);
72
+ }
73
+ export function normalizeApiBaseUrl2(url) {
74
+ return normalize(url, DEFAULT_API_BASE_URL_2);
75
+ }
46
76
  export function loadCliCredentials(configDir) {
47
77
  const path = credentialsPath(configDir);
48
78
  let contents;
@@ -62,6 +92,24 @@ export function loadCliCredentials(configDir) {
62
92
  return parseCredentials(JSON.parse(contents));
63
93
  }
64
94
  catch (error) {
95
+ if (error instanceof StaleCredentialFormatError) {
96
+ // Saved credentials were written by a pre-dual-host CLI version.
97
+ // The format is incompatible (base_url vs api_base_url_1) and
98
+ // cannot be recovered. Clear the file so the caller sees "no
99
+ // saved credentials" and emit a one-shot notice telling the
100
+ // user they need to log back in. Idempotent: once the file is
101
+ // gone, this branch never fires again.
102
+ try {
103
+ rmSync(path, { force: true });
104
+ }
105
+ catch {
106
+ // Best-effort cleanup; if the unlink fails (permissions,
107
+ // racing process), the next CLI invocation will hit this
108
+ // path again and try once more.
109
+ }
110
+ process.stderr.write("You've been logged out: your saved Primitive CLI credentials were created by an older CLI version and are no longer compatible. Run `primitive login` to re-authenticate.\n");
111
+ return null;
112
+ }
65
113
  if (error instanceof SyntaxError) {
66
114
  throw new Error("Stored Primitive CLI credentials are not valid JSON. Run `primitive logout` and then `primitive login`.");
67
115
  }
@@ -140,10 +188,15 @@ export function acquireCliCredentialsLock(configDir, options = {}) {
140
188
  }
141
189
  export function resolveCliAuth(params) {
142
190
  const apiKey = params.apiKey?.trim();
191
+ // Host 2 (api_base_url_2) is never stored; either set by env/flag or
192
+ // falls back to the production default. The login flow only deals
193
+ // with host 1.
194
+ const apiBaseUrl2 = normalizeApiBaseUrl2(params.apiBaseUrl2);
143
195
  if (apiKey) {
144
196
  return {
145
197
  apiKey,
146
- baseUrl: normalizeBaseUrl(params.baseUrl),
198
+ apiBaseUrl1: normalizeApiBaseUrl1(params.apiBaseUrl1),
199
+ apiBaseUrl2,
147
200
  credentials: null,
148
201
  source: "flag-or-env",
149
202
  };
@@ -152,16 +205,18 @@ export function resolveCliAuth(params) {
152
205
  if (credentials) {
153
206
  return {
154
207
  apiKey: credentials.api_key,
155
- baseUrl: params.baseUrl
156
- ? normalizeBaseUrl(params.baseUrl)
157
- : credentials.base_url,
208
+ apiBaseUrl1: params.apiBaseUrl1
209
+ ? normalizeApiBaseUrl1(params.apiBaseUrl1)
210
+ : credentials.api_base_url_1,
211
+ apiBaseUrl2,
158
212
  credentials,
159
213
  source: "stored",
160
214
  };
161
215
  }
162
216
  return {
163
217
  apiKey: undefined,
164
- baseUrl: normalizeBaseUrl(params.baseUrl),
218
+ apiBaseUrl1: normalizeApiBaseUrl1(params.apiBaseUrl1),
219
+ apiBaseUrl2,
165
220
  credentials: null,
166
221
  source: "none",
167
222
  };