@primitivedotdev/cli 0.35.0 → 0.36.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.
@@ -620,44 +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 attachment-capable message sends. The
635
- * hand-written PrimitiveClient.send / .reply / .forward methods on
636
- * the subclass route those sends 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() and
642
- * PrimitiveClient.reply() under the hood. Exposed for the CLI's
643
- * hand-rolled send/reply commands, which call generated operations
644
- * directly; not part of the publicly-documented SDK surface.
645
- * Customer code should call .send() / .reply() on the subclass
646
- * instead.
647
- */
648
- _sendClient;
649
631
  constructor(options = {}) {
650
- 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;
651
633
  const resolvedAuth = auth ?? createDefaultAuth(apiKey);
652
634
  this.client = createClient(createConfig({
653
635
  ...config,
654
636
  auth: resolvedAuth,
655
- baseUrl: apiBaseUrl1
656
- }));
657
- this._sendClient = createClient(createConfig({
658
- ...config,
659
- auth: resolvedAuth,
660
- baseUrl: apiBaseUrl2
637
+ baseUrl: apiBaseUrl
661
638
  }));
662
639
  }
663
640
  getConfig() {
@@ -849,6 +826,11 @@ function requireString(value, key) {
849
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}`);
850
827
  return raw;
851
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
+ }
852
834
  /**
853
835
  * Sentinel returned by parseCredentials when the on-disk credentials were
854
836
  * written by an API-key-based CLI. The caller treats this as "not logged in"
@@ -880,7 +862,7 @@ function parseCredentials(raw) {
880
862
  oauth_client_id: requireString(raw, "oauth_client_id"),
881
863
  org_id: requireString(raw, "org_id"),
882
864
  org_name: orgName,
883
- api_base_url_1: requireString(raw, "api_base_url_1"),
865
+ api_base_url: readStoredApiBaseUrl(raw),
884
866
  created_at: requireString(raw, "created_at")
885
867
  };
886
868
  }
@@ -895,11 +877,28 @@ function normalize(url, fallback) {
895
877
  if (!trimmed) return fallback;
896
878
  return trimmed.replace(/\/+$/, "");
897
879
  }
898
- function normalizeApiBaseUrl1(url) {
899
- 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;
900
899
  }
901
- function normalizeApiBaseUrl2(url) {
902
- return normalize(url, DEFAULT_API_BASE_URL_2);
900
+ function normalizeApiBaseUrl(url) {
901
+ return canonicalizeKnownApiBaseUrl(normalize(url, DEFAULT_API_BASE_URL));
903
902
  }
904
903
  function cliAccessTokenExpiresAt(expiresInSeconds, now = Date.now) {
905
904
  return new Date(now() + expiresInSeconds * 1e3).toISOString();
@@ -1091,28 +1090,43 @@ function acquireCliCredentialsLock(configDir, options = {}) {
1091
1090
  });
1092
1091
  };
1093
1092
  }
1093
+ /**
1094
+ * Detect the PRIMITIVE_KEY vs PRIMITIVE_API_KEY rename trap.
1095
+ *
1096
+ * AGX feedback: users on older docs (or coming from other tools) set
1097
+ * `PRIMITIVE_KEY` and then cannot figure out why the CLI reports no
1098
+ * API key. The CLI reads `PRIMITIVE_API_KEY` only. Returns a stderr
1099
+ * hint when `PRIMITIVE_KEY` is set but `PRIMITIVE_API_KEY` is not,
1100
+ * otherwise null. Exported as a helper so both `doctor` and the
1101
+ * general auth-resolution path surface the same guidance from the
1102
+ * first command the user runs, instead of forcing them to discover
1103
+ * the rename via `doctor` after the fact.
1104
+ */
1105
+ function detectPrimitiveKeyEnvMisname(env = process.env) {
1106
+ const primitiveKey = env.PRIMITIVE_KEY;
1107
+ const primitiveApiKey = env.PRIMITIVE_API_KEY;
1108
+ if ((primitiveKey?.length ?? 0) > 0 && (primitiveApiKey?.length ?? 0) === 0) return "PRIMITIVE_KEY is set but the CLI reads PRIMITIVE_API_KEY. Rename your env var, or re-run with PRIMITIVE_API_KEY=$PRIMITIVE_KEY.";
1109
+ return null;
1110
+ }
1094
1111
  function resolveCliAuth(params) {
1095
1112
  const apiKey = params.apiKey?.trim();
1096
- const apiBaseUrl2 = normalizeApiBaseUrl2(params.apiBaseUrl2);
1113
+ const apiBaseUrl = normalizeApiBaseUrl(params.apiBaseUrl);
1097
1114
  if (apiKey) return {
1098
1115
  apiKey,
1099
- apiBaseUrl1: normalizeApiBaseUrl1(params.apiBaseUrl1),
1100
- apiBaseUrl2,
1116
+ apiBaseUrl,
1101
1117
  credentials: null,
1102
1118
  source: "flag-or-env"
1103
1119
  };
1104
1120
  const credentials = loadCliCredentials(params.configDir);
1105
1121
  if (credentials) return {
1106
1122
  apiKey: credentials.access_token,
1107
- apiBaseUrl1: credentials.api_base_url_1,
1108
- apiBaseUrl2,
1123
+ apiBaseUrl: credentials.api_base_url,
1109
1124
  credentials,
1110
1125
  source: "stored"
1111
1126
  };
1112
1127
  return {
1113
1128
  apiKey: void 0,
1114
- apiBaseUrl1: normalizeApiBaseUrl1(params.apiBaseUrl1),
1115
- apiBaseUrl2,
1129
+ apiBaseUrl,
1116
1130
  credentials: null,
1117
1131
  source: "none"
1118
1132
  };
@@ -1169,13 +1183,10 @@ function parseHeaders(raw, context) {
1169
1183
  function parseEnvironmentConfig(raw, context) {
1170
1184
  if (!isRecord(raw)) throw cliConfigError(`${context} must be a JSON object.`);
1171
1185
  const env = {};
1172
- if (raw.api_base_url_1 !== void 0) {
1173
- if (typeof raw.api_base_url_1 !== "string") throw cliConfigError(`${context}.api_base_url_1 must be a string.`);
1174
- env.api_base_url_1 = normalizeApiBaseUrl1(raw.api_base_url_1);
1175
- }
1176
- if (raw.api_base_url_2 !== void 0) {
1177
- if (typeof raw.api_base_url_2 !== "string") throw cliConfigError(`${context}.api_base_url_2 must be a string.`);
1178
- env.api_base_url_2 = normalizeApiBaseUrl2(raw.api_base_url_2);
1186
+ const rawApiBaseUrl = raw.api_base_url ?? raw.api_base_url_2 ?? raw.api_base_url_1;
1187
+ if (rawApiBaseUrl !== void 0) {
1188
+ if (typeof rawApiBaseUrl !== "string") throw cliConfigError(`${context}.api_base_url must be a string.`);
1189
+ env.api_base_url = normalizeApiBaseUrl(rawApiBaseUrl);
1179
1190
  }
1180
1191
  const headers = parseHeaders(raw.headers, context);
1181
1192
  if (Object.keys(headers).length > 0) env.headers = headers;
@@ -1259,7 +1270,7 @@ function resolveConfigEnvironment(config) {
1259
1270
  } : null;
1260
1271
  }
1261
1272
  function upsertCliEnvironment(params) {
1262
- const name = normalizeCliEnvironmentName(params.environmentName ?? "default");
1273
+ const name = normalizeCliEnvironmentName(params.environmentName ?? resolveConfigEnvironment(params.config)?.name ?? "default");
1263
1274
  const existing = params.config.environments[name] ?? {};
1264
1275
  const nextHeaders = { ...existing.headers ?? {} };
1265
1276
  for (const assignment of params.headers ?? []) {
@@ -1269,8 +1280,7 @@ function upsertCliEnvironment(params) {
1269
1280
  for (const rawName of params.unsetHeaders ?? []) delete nextHeaders[validateCliHeaderName(rawName)];
1270
1281
  const nextEnvironment = {
1271
1282
  ...existing,
1272
- ...params.apiBaseUrl1 !== void 0 ? { api_base_url_1: normalizeApiBaseUrl1(params.apiBaseUrl1) } : {},
1273
- ...params.apiBaseUrl2 !== void 0 ? { api_base_url_2: normalizeApiBaseUrl2(params.apiBaseUrl2) } : {},
1283
+ ...params.apiBaseUrl !== void 0 ? { api_base_url: normalizeApiBaseUrl(params.apiBaseUrl) } : {},
1274
1284
  ...Object.keys(nextHeaders).length > 0 ? { headers: nextHeaders } : {}
1275
1285
  };
1276
1286
  if (Object.keys(nextHeaders).length === 0) delete nextEnvironment.headers;
@@ -1301,4 +1311,4 @@ function redactCliEnvironment(environment) {
1301
1311
  };
1302
1312
  }
1303
1313
  //#endregion
1304
- 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 };
1314
+ 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, loadCliCredentials 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, normalizeApiBaseUrl as x, detectPrimitiveKeyEnvMisname as y };