@primitivedotdev/cli 0.34.0 → 0.35.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -12,6 +12,8 @@ Or with npm:
12
12
  ```bash
13
13
  npm install -g @primitivedotdev/cli
14
14
  primitive whoami
15
+ # `prim` is installed as a short alias for the same CLI.
16
+ prim whoami
15
17
  ```
16
18
 
17
19
  Or with no install:
@@ -55,6 +57,8 @@ Use task-oriented commands for normal workflows:
55
57
  ```bash
56
58
  primitive send --to alice@example.com --body "Hello"
57
59
  primitive reply --id <inbound-email-id> --body "Thanks"
60
+ primitive reply --id <inbound-email-id> --body "See attached" --attachment ./report.pdf
61
+ primitive chat reply "See attached" --attachment ./report.pdf
58
62
  primitive emails list
59
63
  primitive emails get --id <inbound-email-id>
60
64
  primitive sent list
@@ -620,43 +620,21 @@ const createClient = (config = {}) => {
620
620
  * `.forward`) still lives in sdk-node because it needs the
621
621
  * `ReceivedEmail` type from the webhook parsing surface.
622
622
  */
623
- const DEFAULT_API_BASE_URL_1 = "https://www.primitive.dev/api/v1";
624
- const DEFAULT_API_BASE_URL_2 = "https://api.primitive.dev/v1";
623
+ const DEFAULT_API_BASE_URL = "https://api.primitive.dev/v1";
625
624
  function createDefaultAuth(apiKey) {
626
625
  return (security) => {
627
626
  if (security.type === "http" && security.scheme === "bearer") return apiKey;
628
627
  };
629
628
  }
630
629
  var PrimitiveApiClient = class {
631
- /**
632
- * Generated client targeting the primary API host (apiBaseUrl1). Use
633
- * this when passing `client: ...` to a generated operation function
634
- * for every endpoint EXCEPT /send-mail. The hand-written
635
- * PrimitiveClient.send / .reply / .forward methods on the subclass
636
- * route /send-mail to the host-2 client internally.
637
- */
638
630
  client;
639
- /**
640
- * @internal Generated client targeting the attachments-supporting
641
- * send host (apiBaseUrl2). Used by PrimitiveClient.send() under the
642
- * hood. Exposed for the CLI's hand-rolled send command, which calls
643
- * the generated sendEmail directly; not part of the publicly-
644
- * documented SDK surface. Customer code should call .send() on the
645
- * subclass instead.
646
- */
647
- _sendClient;
648
631
  constructor(options = {}) {
649
- const { apiKey, auth, apiBaseUrl1 = DEFAULT_API_BASE_URL_1, apiBaseUrl2 = DEFAULT_API_BASE_URL_2, ...config } = options;
632
+ const { apiKey, auth, apiBaseUrl = DEFAULT_API_BASE_URL, ...config } = options;
650
633
  const resolvedAuth = auth ?? createDefaultAuth(apiKey);
651
634
  this.client = createClient(createConfig({
652
635
  ...config,
653
636
  auth: resolvedAuth,
654
- baseUrl: apiBaseUrl1
655
- }));
656
- this._sendClient = createClient(createConfig({
657
- ...config,
658
- auth: resolvedAuth,
659
- baseUrl: apiBaseUrl2
637
+ baseUrl: apiBaseUrl
660
638
  }));
661
639
  }
662
640
  getConfig() {
@@ -848,6 +826,11 @@ function requireString(value, key) {
848
826
  if (typeof raw !== "string" || raw.trim().length === 0) throw new Error(`Stored Primitive CLI credentials are malformed: ${key} must be a non-empty string. ${MALFORMED_CREDENTIALS_HINT}`);
849
827
  return raw;
850
828
  }
829
+ function readStoredApiBaseUrl(raw) {
830
+ const value = raw.api_base_url ?? raw.api_base_url_2 ?? raw.api_base_url_1;
831
+ if (typeof value !== "string" || value.trim().length === 0) throw new Error(`Stored Primitive CLI credentials are malformed: api_base_url must be a non-empty string. ${MALFORMED_CREDENTIALS_HINT}`);
832
+ return normalizeApiBaseUrl(value);
833
+ }
851
834
  /**
852
835
  * Sentinel returned by parseCredentials when the on-disk credentials were
853
836
  * written by an API-key-based CLI. The caller treats this as "not logged in"
@@ -879,7 +862,7 @@ function parseCredentials(raw) {
879
862
  oauth_client_id: requireString(raw, "oauth_client_id"),
880
863
  org_id: requireString(raw, "org_id"),
881
864
  org_name: orgName,
882
- api_base_url_1: requireString(raw, "api_base_url_1"),
865
+ api_base_url: readStoredApiBaseUrl(raw),
883
866
  created_at: requireString(raw, "created_at")
884
867
  };
885
868
  }
@@ -894,11 +877,28 @@ function normalize(url, fallback) {
894
877
  if (!trimmed) return fallback;
895
878
  return trimmed.replace(/\/+$/, "");
896
879
  }
897
- function normalizeApiBaseUrl1(url) {
898
- return normalize(url, DEFAULT_API_BASE_URL_1);
880
+ function canonicalizeKnownApiBaseUrl(url) {
881
+ let parsed;
882
+ try {
883
+ parsed = new URL(url);
884
+ } catch {
885
+ return url;
886
+ }
887
+ if (parsed.pathname.replace(/\/+$/, "") !== "/api/v1") return url;
888
+ if (parsed.hostname === "primitive.dev" || parsed.hostname === "www.primitive.dev") {
889
+ parsed.hostname = "api.primitive.dev";
890
+ parsed.pathname = "/v1";
891
+ return parsed.toString().replace(/\/+$/, "");
892
+ }
893
+ if (parsed.hostname === "primitive-staging-1.com") {
894
+ parsed.hostname = "api.primitive-staging-1.com";
895
+ parsed.pathname = "/v1";
896
+ return parsed.toString().replace(/\/+$/, "");
897
+ }
898
+ return url;
899
899
  }
900
- function normalizeApiBaseUrl2(url) {
901
- return normalize(url, DEFAULT_API_BASE_URL_2);
900
+ function normalizeApiBaseUrl(url) {
901
+ return canonicalizeKnownApiBaseUrl(normalize(url, DEFAULT_API_BASE_URL));
902
902
  }
903
903
  function cliAccessTokenExpiresAt(expiresInSeconds, now = Date.now) {
904
904
  return new Date(now() + expiresInSeconds * 1e3).toISOString();
@@ -1092,26 +1092,23 @@ function acquireCliCredentialsLock(configDir, options = {}) {
1092
1092
  }
1093
1093
  function resolveCliAuth(params) {
1094
1094
  const apiKey = params.apiKey?.trim();
1095
- const apiBaseUrl2 = normalizeApiBaseUrl2(params.apiBaseUrl2);
1095
+ const apiBaseUrl = normalizeApiBaseUrl(params.apiBaseUrl);
1096
1096
  if (apiKey) return {
1097
1097
  apiKey,
1098
- apiBaseUrl1: normalizeApiBaseUrl1(params.apiBaseUrl1),
1099
- apiBaseUrl2,
1098
+ apiBaseUrl,
1100
1099
  credentials: null,
1101
1100
  source: "flag-or-env"
1102
1101
  };
1103
1102
  const credentials = loadCliCredentials(params.configDir);
1104
1103
  if (credentials) return {
1105
1104
  apiKey: credentials.access_token,
1106
- apiBaseUrl1: credentials.api_base_url_1,
1107
- apiBaseUrl2,
1105
+ apiBaseUrl: credentials.api_base_url,
1108
1106
  credentials,
1109
1107
  source: "stored"
1110
1108
  };
1111
1109
  return {
1112
1110
  apiKey: void 0,
1113
- apiBaseUrl1: normalizeApiBaseUrl1(params.apiBaseUrl1),
1114
- apiBaseUrl2,
1111
+ apiBaseUrl,
1115
1112
  credentials: null,
1116
1113
  source: "none"
1117
1114
  };
@@ -1168,13 +1165,10 @@ function parseHeaders(raw, context) {
1168
1165
  function parseEnvironmentConfig(raw, context) {
1169
1166
  if (!isRecord(raw)) throw cliConfigError(`${context} must be a JSON object.`);
1170
1167
  const env = {};
1171
- if (raw.api_base_url_1 !== void 0) {
1172
- if (typeof raw.api_base_url_1 !== "string") throw cliConfigError(`${context}.api_base_url_1 must be a string.`);
1173
- env.api_base_url_1 = normalizeApiBaseUrl1(raw.api_base_url_1);
1174
- }
1175
- if (raw.api_base_url_2 !== void 0) {
1176
- if (typeof raw.api_base_url_2 !== "string") throw cliConfigError(`${context}.api_base_url_2 must be a string.`);
1177
- env.api_base_url_2 = normalizeApiBaseUrl2(raw.api_base_url_2);
1168
+ const rawApiBaseUrl = raw.api_base_url ?? raw.api_base_url_2 ?? raw.api_base_url_1;
1169
+ if (rawApiBaseUrl !== void 0) {
1170
+ if (typeof rawApiBaseUrl !== "string") throw cliConfigError(`${context}.api_base_url must be a string.`);
1171
+ env.api_base_url = normalizeApiBaseUrl(rawApiBaseUrl);
1178
1172
  }
1179
1173
  const headers = parseHeaders(raw.headers, context);
1180
1174
  if (Object.keys(headers).length > 0) env.headers = headers;
@@ -1258,7 +1252,7 @@ function resolveConfigEnvironment(config) {
1258
1252
  } : null;
1259
1253
  }
1260
1254
  function upsertCliEnvironment(params) {
1261
- const name = normalizeCliEnvironmentName(params.environmentName ?? "default");
1255
+ const name = normalizeCliEnvironmentName(params.environmentName ?? resolveConfigEnvironment(params.config)?.name ?? "default");
1262
1256
  const existing = params.config.environments[name] ?? {};
1263
1257
  const nextHeaders = { ...existing.headers ?? {} };
1264
1258
  for (const assignment of params.headers ?? []) {
@@ -1268,8 +1262,7 @@ function upsertCliEnvironment(params) {
1268
1262
  for (const rawName of params.unsetHeaders ?? []) delete nextHeaders[validateCliHeaderName(rawName)];
1269
1263
  const nextEnvironment = {
1270
1264
  ...existing,
1271
- ...params.apiBaseUrl1 !== void 0 ? { api_base_url_1: normalizeApiBaseUrl1(params.apiBaseUrl1) } : {},
1272
- ...params.apiBaseUrl2 !== void 0 ? { api_base_url_2: normalizeApiBaseUrl2(params.apiBaseUrl2) } : {},
1265
+ ...params.apiBaseUrl !== void 0 ? { api_base_url: normalizeApiBaseUrl(params.apiBaseUrl) } : {},
1273
1266
  ...Object.keys(nextHeaders).length > 0 ? { headers: nextHeaders } : {}
1274
1267
  };
1275
1268
  if (Object.keys(nextHeaders).length === 0) delete nextEnvironment.headers;
@@ -1300,4 +1293,4 @@ function redactCliEnvironment(environment) {
1300
1293
  };
1301
1294
  }
1302
1295
  //#endregion
1303
- export { createClient as A, saveCliCredentials as C, loadChatConversationByLocalId as D, loadActiveChatState as E, saveActiveChatState as O, resolveCliAuth as S, deleteChatState as T, deleteCliCredentials as _, normalizeCliEnvironmentName as a, normalizeApiBaseUrl1 as b, resolveConfigEnvironment as c, validateCliHeaderName as d, validateCliHeaderValue as f, credentialsPath as g, credentialsLockPath as h, loadCliConfig as i, createConfig as j, PrimitiveApiClient as k, saveCliConfig as l, cliAccessTokenExpiresAt as m, deleteCliConfig as n, redactCliEnvironment as o, acquireCliCredentialsLock as p, emptyCliConfig as r, removeCliEnvironment as s, DEFAULT_ENVIRONMENT as t, upsertCliEnvironment as u, deleteCliCredentialsLock as v, chatStatePath as w, normalizeApiBaseUrl2 as x, loadCliCredentials as y };
1296
+ export { createConfig as A, chatStatePath as C, saveActiveChatState as D, loadChatConversationByLocalId as E, PrimitiveApiClient as O, saveCliCredentials as S, loadActiveChatState as T, deleteCliCredentials as _, normalizeCliEnvironmentName as a, normalizeApiBaseUrl as b, resolveConfigEnvironment as c, validateCliHeaderName as d, validateCliHeaderValue as f, credentialsPath as g, credentialsLockPath as h, loadCliConfig as i, createClient as k, saveCliConfig as l, cliAccessTokenExpiresAt as m, deleteCliConfig as n, redactCliEnvironment as o, acquireCliCredentialsLock as p, emptyCliConfig as r, removeCliEnvironment as s, DEFAULT_ENVIRONMENT as t, upsertCliEnvironment as u, deleteCliCredentialsLock as v, deleteChatState as w, resolveCliAuth as x, loadCliCredentials as y };