@topolo/cli 0.13.24 → 0.13.26

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 +608 -82
  2. package/package.json +4 -4
package/dist/index.js CHANGED
@@ -308,7 +308,7 @@ import { Command, Help } from "commander";
308
308
  import { createInterface } from "readline/promises";
309
309
  import { stdin as input } from "process";
310
310
 
311
- // node_modules/.pnpm/@topolo+sdk@0.23.26/node_modules/@topolo/sdk/dist/index.js
311
+ // node_modules/@topolo/sdk/dist/index.js
312
312
  function record(value) {
313
313
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
314
314
  }
@@ -969,39 +969,256 @@ async function createDpopProof(input2) {
969
969
  );
970
970
  return `${header}.${claims}.${encodeBase64Url(signature)}`;
971
971
  }
972
- function hostnameHasSuffix(hostname, suffix) {
973
- return hostname === suffix || hostname.endsWith(`.${suffix}`);
974
- }
975
- function workersDevAccountMatches(hostname, account) {
976
- if (!hostname.endsWith(".workers.dev")) return false;
977
- const labels = hostname.slice(0, -".workers.dev".length).split(".");
978
- return labels.length >= 2 && labels.at(-1) === account;
972
+ var TOPOLO_FIRST_PARTY_ORIGINS = Object.freeze({
973
+ production: Object.freeze([
974
+ "https://admin.topolo.app",
975
+ "https://agent.topolo.app",
976
+ "https://api.ol0.me",
977
+ "https://auth.topolo.app",
978
+ "https://backup.topolo.app",
979
+ "https://blog.topolo.app",
980
+ "https://books.topolo.app",
981
+ "https://brand.topolo.app",
982
+ "https://bugfix.topolo.app",
983
+ "https://bytes.topolo.app",
984
+ "https://calendar.topolo.app",
985
+ "https://campaigns.topolo.app",
986
+ "https://capacity.topolo.app",
987
+ "https://chat.topolo.app",
988
+ "https://commerce.topolo.app",
989
+ "https://compose.topolo.app",
990
+ "https://consent.topolo.app",
991
+ "https://contracts.topolo.app",
992
+ "https://crm.topolo.app",
993
+ "https://design.topolo.app",
994
+ "https://developers.topolo.app",
995
+ "https://director.topolo.app",
996
+ "https://docs.topolo.app",
997
+ "https://engage.topolo.app",
998
+ "https://events.topolo.app",
999
+ "https://feed-analytics-api.topolo.app",
1000
+ "https://feed-api.topolo.app",
1001
+ "https://flow.topolo.app",
1002
+ "https://forecast.topolo.app",
1003
+ "https://forms.topolo.app",
1004
+ "https://home.topolo.app",
1005
+ "https://insights.topolo.app",
1006
+ "https://inventory.topolo.app",
1007
+ "https://learn.topolo.app",
1008
+ "https://localize.topolo.app",
1009
+ "https://mail.topolo.app",
1010
+ "https://mcp.topolo.app",
1011
+ "https://mdm.topolo.app",
1012
+ "https://messages.topolo.app",
1013
+ "https://nexus.topolo.app",
1014
+ "https://notify.topolo.app",
1015
+ "https://nurture.topolo.app",
1016
+ "https://observability-watch.topolo.app",
1017
+ "https://p2p.topolo.app",
1018
+ "https://pay.topolo.app",
1019
+ "https://people.topolo.app",
1020
+ "https://quro.topolo.app",
1021
+ "https://recruit.topolo.app",
1022
+ "https://render.topolo.app",
1023
+ "https://roadmap.topolo.app",
1024
+ "https://sign.topolo.app",
1025
+ "https://socialize-api.topolo.app",
1026
+ "https://spaces.topolo.app",
1027
+ "https://speech.topolo.app",
1028
+ "https://status.topolo.app",
1029
+ "https://studio.topolo.app",
1030
+ "https://success.topolo.app",
1031
+ "https://support.topolo.app",
1032
+ "https://survey.topolo.app",
1033
+ "https://voice.topolo.app",
1034
+ "https://web.topolo.app",
1035
+ "https://www.topolo.app"
1036
+ ]),
1037
+ staging: Object.freeze([
1038
+ "https://admin.stg.topolo.us",
1039
+ "https://agent.stg.topolo.us",
1040
+ "https://api.one.stg.topolo.us",
1041
+ "https://auth.stg.topolo.us",
1042
+ "https://backup.stg.topolo.us",
1043
+ "https://blog.stg.topolo.us",
1044
+ "https://books.stg.topolo.us",
1045
+ "https://brand.stg.topolo.us",
1046
+ "https://bugfix.stg.topolo.us",
1047
+ "https://bytes.stg.topolo.us",
1048
+ "https://calendar.stg.topolo.us",
1049
+ "https://campaigns.stg.topolo.us",
1050
+ "https://capacity.stg.topolo.us",
1051
+ "https://chat.stg.topolo.us",
1052
+ "https://commerce.stg.topolo.us",
1053
+ "https://compose.stg.topolo.us",
1054
+ "https://consent.stg.topolo.us",
1055
+ "https://contracts.stg.topolo.us",
1056
+ "https://crm.stg.topolo.us",
1057
+ "https://design.stg.topolo.us",
1058
+ "https://developers.stg.topolo.us",
1059
+ "https://director.stg.topolo.us",
1060
+ "https://docs.stg.topolo.us",
1061
+ "https://engage.stg.topolo.us",
1062
+ "https://events.stg.topolo.us",
1063
+ "https://feed-analytics-api.stg.topolo.us",
1064
+ "https://feed-api.stg.topolo.us",
1065
+ "https://flow.stg.topolo.us",
1066
+ "https://forecast.stg.topolo.us",
1067
+ "https://forms.stg.topolo.us",
1068
+ "https://home.stg.topolo.us",
1069
+ "https://insights.stg.topolo.us",
1070
+ "https://inventory.stg.topolo.us",
1071
+ "https://learn.stg.topolo.us",
1072
+ "https://localize.stg.topolo.us",
1073
+ "https://mail.stg.topolo.us",
1074
+ "https://mcp.stg.topolo.us",
1075
+ "https://mdm.stg.topolo.us",
1076
+ "https://messages.stg.topolo.us",
1077
+ "https://nexus.stg.topolo.us",
1078
+ "https://notify.stg.topolo.us",
1079
+ "https://nurture.stg.topolo.us",
1080
+ "https://observability-watch.stg.topolo.us",
1081
+ "https://p2p.stg.topolo.us",
1082
+ "https://pay.stg.topolo.us",
1083
+ "https://people.stg.topolo.us",
1084
+ "https://pulse.stg.topolo.us",
1085
+ "https://recruit.stg.topolo.us",
1086
+ "https://render.stg.topolo.us",
1087
+ "https://roadmap.stg.topolo.us",
1088
+ "https://seed.stg.topolo.us",
1089
+ "https://sign.stg.topolo.us",
1090
+ "https://socialize-api.stg.topolo.us",
1091
+ "https://spaces.stg.topolo.us",
1092
+ "https://speech.stg.topolo.us",
1093
+ "https://status.stg.topolo.us",
1094
+ "https://studio-api.stg.topolo.us",
1095
+ "https://studio.stg.topolo.us",
1096
+ "https://success.stg.topolo.us",
1097
+ "https://support.stg.topolo.us",
1098
+ "https://survey.stg.topolo.us",
1099
+ "https://voice.stg.topolo.us",
1100
+ "https://web.stg.topolo.us"
1101
+ ]),
1102
+ development: Object.freeze([
1103
+ "https://admin.topolo.dev",
1104
+ "https://agent.topolo.dev",
1105
+ "https://api.one.topolo.dev",
1106
+ "https://auth.topolo.dev",
1107
+ "https://backup.topolo.dev",
1108
+ "https://blog.topolo.dev",
1109
+ "https://books.topolo.dev",
1110
+ "https://brand.topolo.dev",
1111
+ "https://bugfix.topolo.dev",
1112
+ "https://bytes.topolo.dev",
1113
+ "https://calendar.topolo.dev",
1114
+ "https://campaigns.topolo.dev",
1115
+ "https://capacity.topolo.dev",
1116
+ "https://chat.topolo.dev",
1117
+ "https://commerce.topolo.dev",
1118
+ "https://compose.topolo.dev",
1119
+ "https://consent.topolo.dev",
1120
+ "https://contracts.topolo.dev",
1121
+ "https://crm.topolo.dev",
1122
+ "https://design.topolo.dev",
1123
+ "https://developers.topolo.dev",
1124
+ "https://director.topolo.dev",
1125
+ "https://docs.topolo.dev",
1126
+ "https://engage.topolo.dev",
1127
+ "https://events.topolo.dev",
1128
+ "https://feed-analytics-api.topolo.dev",
1129
+ "https://feed-api.topolo.dev",
1130
+ "https://flow.topolo.dev",
1131
+ "https://forecast.topolo.dev",
1132
+ "https://forms.topolo.dev",
1133
+ "https://home.topolo.dev",
1134
+ "https://insights.topolo.dev",
1135
+ "https://inventory.topolo.dev",
1136
+ "https://learn.topolo.dev",
1137
+ "https://localize.topolo.dev",
1138
+ "https://mail.topolo.dev",
1139
+ "https://mcp.topolo.dev",
1140
+ "https://mdm.topolo.dev",
1141
+ "https://messages.topolo.dev",
1142
+ "https://nexus.topolo.dev",
1143
+ "https://notify.topolo.dev",
1144
+ "https://nurture.topolo.dev",
1145
+ "https://observability-watch.topolo.dev",
1146
+ "https://p2p.topolo.dev",
1147
+ "https://pay.topolo.dev",
1148
+ "https://people.topolo.dev",
1149
+ "https://pulse.topolo.dev",
1150
+ "https://recruit.topolo.dev",
1151
+ "https://render.topolo.dev",
1152
+ "https://roadmap.topolo.dev",
1153
+ "https://seed.topolo.dev",
1154
+ "https://sign.topolo.dev",
1155
+ "https://socialize-api.topolo.dev",
1156
+ "https://spaces.topolo.dev",
1157
+ "https://speech.topolo.dev",
1158
+ "https://status.topolo.dev",
1159
+ "https://studio-api.topolo.dev",
1160
+ "https://studio.topolo.dev",
1161
+ "https://success.topolo.dev",
1162
+ "https://support.topolo.dev",
1163
+ "https://survey.topolo.dev",
1164
+ "https://voice.topolo.dev",
1165
+ "https://web.topolo.dev"
1166
+ ])
1167
+ });
1168
+ var TOPOLO_REFUSED_HOST_SUFFIXES = Object.freeze(["sites.stg.topolo.us", "sites.topolo.app", "sites.topolo.dev", "topolo.site", "try.topolo.io", "workers.dev"]);
1169
+ var TOPOLO_RUNTIME_ENVIRONMENTS = ["production", "staging", "development", "local"];
1170
+ var TOPOLO_ENVIRONMENT_SERVICE_URLS = Object.freeze({
1171
+ production: Object.freeze({
1172
+ auth: "https://auth.topolo.app",
1173
+ developers: "https://developers.topolo.app"
1174
+ }),
1175
+ staging: Object.freeze({
1176
+ auth: "https://auth.stg.topolo.us",
1177
+ developers: "https://developers.stg.topolo.us"
1178
+ }),
1179
+ development: Object.freeze({
1180
+ auth: "https://auth.topolo.dev",
1181
+ developers: "https://developers.topolo.dev"
1182
+ }),
1183
+ local: Object.freeze({})
1184
+ });
1185
+ var FIRST_PARTY_ORIGIN_SETS = Object.freeze({
1186
+ production: new Set(TOPOLO_FIRST_PARTY_ORIGINS.production),
1187
+ staging: new Set(TOPOLO_FIRST_PARTY_ORIGINS.staging),
1188
+ development: new Set(TOPOLO_FIRST_PARTY_ORIGINS.development)
1189
+ });
1190
+ var LOOPBACK_HOSTNAMES = /* @__PURE__ */ new Set(["localhost", "127.0.0.1", "[::1]"]);
1191
+ function isRuntimeEnvironment(value) {
1192
+ return typeof value === "string" && TOPOLO_RUNTIME_ENVIRONMENTS.includes(value);
979
1193
  }
980
- function originMatchesEnvironment(url, environment) {
981
- const hostname = url.hostname.toLowerCase();
1194
+ function originIsFirstParty(url, environment) {
982
1195
  if (environment === "local") {
983
- return hostname === "localhost" || hostname.endsWith(".localhost") || hostname === "127.0.0.1" || hostname === "[::1]";
1196
+ return (url.protocol === "http:" || url.protocol === "https:") && LOOPBACK_HOSTNAMES.has(url.hostname);
984
1197
  }
985
1198
  if (url.protocol !== "https:") return false;
986
- if (environment === "production") {
987
- return hostnameHasSuffix(hostname, "topolo.app") || hostnameHasSuffix(hostname, "topolo.io") || hostname === "api.ol0.me" || workersDevAccountMatches(hostname, "topolo");
988
- }
989
- if (environment === "staging") {
990
- return hostnameHasSuffix(hostname, "stg.topolo.us") || workersDevAccountMatches(hostname, "topolo-staging");
1199
+ return FIRST_PARTY_ORIGIN_SETS[environment].has(url.origin);
1200
+ }
1201
+ function requireTopoloRuntimeEnvironment(environment) {
1202
+ if (!isRuntimeEnvironment(environment)) {
1203
+ throw new TopoloSdkError(
1204
+ "environment_required",
1205
+ 'Refusing to send a Topolo credential: the runtime environment is not resolved. Pass environment: "production" | "staging" | "development" | "local".'
1206
+ );
991
1207
  }
992
- return hostnameHasSuffix(hostname, "topolo.dev") || workersDevAccountMatches(hostname, "topolo-development");
1208
+ return environment;
993
1209
  }
994
1210
  function assertTopoloServiceUrlEnvironment(service, value, environment) {
1211
+ const resolved = requireTopoloRuntimeEnvironment(environment);
995
1212
  let url;
996
1213
  try {
997
1214
  url = new URL(value);
998
1215
  } catch {
999
1216
  throw new TopoloSdkError("invalid_service_url", `Invalid URL configured for Topolo service "${service}".`);
1000
1217
  }
1001
- if (url.protocol !== "https:" && url.protocol !== "http:" || url.username || url.password || !originMatchesEnvironment(url, environment)) {
1218
+ if (url.protocol !== "https:" && url.protocol !== "http:" || url.username || url.password || !originIsFirstParty(url, resolved)) {
1002
1219
  throw new TopoloSdkError(
1003
1220
  "environment_mismatch",
1004
- `Refusing Topolo service "${service}": ${url.origin} does not belong to ${environment}.`
1221
+ `Refusing Topolo service "${service}": ${url.origin} is not a first-party ${resolved} service origin.`
1005
1222
  );
1006
1223
  }
1007
1224
  return url.origin;
@@ -1014,6 +1231,44 @@ function topoloServiceOrigins(serviceUrls, environment) {
1014
1231
  ])
1015
1232
  );
1016
1233
  }
1234
+ var REDIRECT_STATUSES = /* @__PURE__ */ new Set([301, 302, 303, 307, 308]);
1235
+ async function fetchWithCredentialBoundary(fetchImpl, environment, service, target, init) {
1236
+ const url = new URL(String(target));
1237
+ const origin = assertTopoloServiceUrlEnvironment(service, url.toString(), environment);
1238
+ const response = await fetchImpl(url.toString(), { ...init, redirect: "manual" });
1239
+ let responseOrigin = origin;
1240
+ try {
1241
+ if (response.url) responseOrigin = new URL(response.url).origin;
1242
+ } catch {
1243
+ responseOrigin = "";
1244
+ }
1245
+ if (response.redirected === true || responseOrigin !== origin) {
1246
+ await response.body?.cancel().catch(() => void 0);
1247
+ throw new TopoloSdkError(
1248
+ "credential_redirect_refused",
1249
+ `Refusing a redirected response from Topolo service "${service}"; credentials are never forwarded.`
1250
+ );
1251
+ }
1252
+ if (REDIRECT_STATUSES.has(response.status)) {
1253
+ const location = response.headers.get("Location");
1254
+ let destination = null;
1255
+ if (location !== null) {
1256
+ try {
1257
+ destination = new URL(location, url).origin;
1258
+ } catch {
1259
+ destination = null;
1260
+ }
1261
+ }
1262
+ if (location !== null && destination !== origin) {
1263
+ await response.body?.cancel().catch(() => void 0);
1264
+ throw new TopoloSdkError(
1265
+ "credential_redirect_refused",
1266
+ `Refusing a redirect from Topolo service "${service}" to another origin; credentials are never forwarded.`
1267
+ );
1268
+ }
1269
+ }
1270
+ return response;
1271
+ }
1017
1272
  var APP_API_URLS = {
1018
1273
  auth: "https://auth.topolo.app",
1019
1274
  developers: "https://developers.topolo.app"
@@ -1036,9 +1291,15 @@ function resolveCatalogServiceUrl(entry, options = {}) {
1036
1291
  if (apiBaseUrl) return apiBaseUrl;
1037
1292
  return null;
1038
1293
  }
1039
- function resolveServiceUrl(service, overrides) {
1294
+ function resolveServiceUrl(service, overrides, environment) {
1040
1295
  const override = resolveServiceUrlOverride([service], overrides);
1041
1296
  if (override) return override;
1297
+ if (environment !== void 0) {
1298
+ const selected = requireTopoloRuntimeEnvironment(environment);
1299
+ const origin = TOPOLO_ENVIRONMENT_SERVICE_URLS[selected][service];
1300
+ if (!origin) throw new TopoloSdkError("service_url_required", `Configure an explicit ${service} origin for ${selected}.`);
1301
+ return origin;
1302
+ }
1042
1303
  return DEFAULT_APP_URLS[service];
1043
1304
  }
1044
1305
  function catalogServiceKeyCandidates(entry, preferred) {
@@ -1082,12 +1343,15 @@ function firstString(...values) {
1082
1343
  }
1083
1344
  return null;
1084
1345
  }
1346
+ var DATA_CLASSIFICATIONS = /* @__PURE__ */ new Set(["public", "internal", "confidential", "restricted", "public_link"]);
1347
+ var TOPOLO_REFERENCE_PATTERN = /^topolo:app_[a-z0-9]+(?:_[a-z0-9]+)+\/[A-Za-z0-9][A-Za-z0-9_-]{0,127}\/[a-z][a-z0-9_]*\.[a-z][a-z0-9_]*\/[A-Za-z0-9][A-Za-z0-9_.:-]{0,255}(?:@([1-9][0-9]*))?(?![\s\S])/;
1085
1348
  var SUPPORTED_SCHEMA_KEYWORDS = /* @__PURE__ */ new Set([
1086
1349
  "$schema",
1087
1350
  "$id",
1088
1351
  "$ref",
1089
1352
  "$defs",
1090
1353
  "definitions",
1354
+ "dataClassification",
1091
1355
  "title",
1092
1356
  "description",
1093
1357
  "default",
@@ -1138,7 +1402,10 @@ function inspectSchema(schema, path, root, depth) {
1138
1402
  if (unsupportedKeyword) {
1139
1403
  return { path, code: "unsupported_schema_keyword", message: `Action schema uses unsupported keyword ${unsupportedKeyword}.` };
1140
1404
  }
1141
- if (typeof schema.format === "string" && !["uri", "email", "date-time", "uuid"].includes(schema.format)) {
1405
+ if (Object.hasOwn(schema, "dataClassification") && !DATA_CLASSIFICATIONS.has(schema.dataClassification)) {
1406
+ return { path: `${path}.dataClassification`, code: "invalid_data_classification", message: "Schema dataClassification must be public, internal, confidential, restricted or public_link." };
1407
+ }
1408
+ if (typeof schema.format === "string" && !["uri", "email", "date-time", "uuid", "topolo-reference"].includes(schema.format)) {
1142
1409
  return { path, code: "unsupported_schema_format", message: `Action schema uses unsupported format ${schema.format}.` };
1143
1410
  }
1144
1411
  if (typeof schema.$ref === "string" && !resolveLocalReference(root, schema.$ref)) {
@@ -1300,6 +1567,10 @@ function validateString(schema, value, path, errors) {
1300
1567
  errors.push({ path, code: "invalid_schema_pattern", message: "Action schema contains an invalid pattern." });
1301
1568
  }
1302
1569
  }
1570
+ if (schema.format === "topolo-reference") {
1571
+ const match = TOPOLO_REFERENCE_PATTERN.exec(value);
1572
+ if (!match || match[1] !== void 0 && !Number.isSafeInteger(Number(match[1]))) errors.push({ path, code: "format_topolo_reference", message: "Expected a canonical Topolo resource reference." });
1573
+ }
1303
1574
  if (schema.format === "uri" && !isUrl(value)) errors.push({ path, code: "format_uri", message: "Expected an absolute URL." });
1304
1575
  if (schema.format === "email" && !/^[^@\s]+@[^@\s]+\.[^@\s]+$/.test(value)) errors.push({ path, code: "format_email", message: "Expected an email address." });
1305
1576
  if (schema.format === "date-time" && !isDateTime(value)) errors.push({ path, code: "format_date_time", message: "Expected an ISO-8601 date-time." });
@@ -1869,6 +2140,7 @@ var TopoloClient = class _TopoloClient {
1869
2140
  * use and for the generic `topolo api` / MCP passthrough tool.
1870
2141
  */
1871
2142
  async request(opts) {
2143
+ requireTopoloRuntimeEnvironment(this.environment);
1872
2144
  const method = opts.method ?? "GET";
1873
2145
  if (this.requireConfirmForWrites && WRITE_METHODS.has(method) && !opts.confirm) {
1874
2146
  throw new TopoloConfirmationRequiredError(
@@ -1950,7 +2222,7 @@ var TopoloClient = class _TopoloClient {
1950
2222
  privateJwk: requestCredential.privateJwk
1951
2223
  }));
1952
2224
  }
1953
- return await this.fetchImpl(urlStr, {
2225
+ return await this.credentialedFetch(resolvedService.serviceKey, urlStr, {
1954
2226
  method,
1955
2227
  headers: requestHeaders,
1956
2228
  body: opts.rawBody ?? (opts.body !== void 0 ? JSON.stringify(opts.body) : null),
@@ -2132,8 +2404,11 @@ var TopoloClient = class _TopoloClient {
2132
2404
  return this.refreshInFlight;
2133
2405
  }
2134
2406
  validatedServiceOrigin(service, value) {
2135
- const origin = requireHttpOrigin(value);
2136
- return this.environment ? assertTopoloServiceUrlEnvironment(service, origin, this.environment) : origin;
2407
+ return assertTopoloServiceUrlEnvironment(service, requireHttpOrigin(value), this.environment);
2408
+ }
2409
+ /** The only door to the network for anything this client sends; see fetchWithCredentialBoundary. */
2410
+ credentialedFetch(service, target, init) {
2411
+ return fetchWithCredentialBoundary((input2, requestInit) => this.fetchImpl(input2, requestInit), this.environment, service, target, init);
2137
2412
  }
2138
2413
  validatedActionEntry(entry) {
2139
2414
  if (!entry.apiBaseUrl) return entry;
@@ -2156,7 +2431,7 @@ var TopoloClient = class _TopoloClient {
2156
2431
  }
2157
2432
  return {
2158
2433
  serviceKey,
2159
- baseUrl: this.validatedServiceOrigin(serviceKey, DEFAULT_APP_URLS[serviceKey])
2434
+ baseUrl: this.validatedServiceOrigin(serviceKey, resolveServiceUrl(serviceKey, this.serviceUrls, this.environment))
2160
2435
  };
2161
2436
  }
2162
2437
  const live = await this.resolveLiveCallableService(serviceKey);
@@ -2199,7 +2474,7 @@ var TopoloClient = class _TopoloClient {
2199
2474
  if (!normalized) return null;
2200
2475
  const configuredAppId = resolveConfiguredAppId(normalized, this.appIds);
2201
2476
  if (configuredAppId) return configuredAppId;
2202
- const authBase = this.validatedServiceOrigin("developers", resolveServiceUrl("developers", this.serviceUrls));
2477
+ const authBase = this.validatedServiceOrigin("developers", resolveServiceUrl("developers", this.serviceUrls, this.environment));
2203
2478
  const cacheKey = `${authBase}|${normalized}`;
2204
2479
  const now = Date.now();
2205
2480
  const cached = _TopoloClient.appIdCache.get(cacheKey);
@@ -2212,7 +2487,7 @@ var TopoloClient = class _TopoloClient {
2212
2487
  ).toString();
2213
2488
  let res;
2214
2489
  try {
2215
- res = await this.fetchImpl(url, {
2490
+ res = await this.credentialedFetch("developers", url, {
2216
2491
  method: "GET",
2217
2492
  headers: { Accept: "application/json" }
2218
2493
  });
@@ -2496,7 +2771,7 @@ var TopoloClient = class _TopoloClient {
2496
2771
  }
2497
2772
  async putUrlSessionContent(entry, uploadUrl, method, bytes, options) {
2498
2773
  const service = entry.appSlug || entry.appId;
2499
- const resolved = entry.apiBaseUrl ? { serviceKey: normalizeLookupKey(service), baseUrl: requireHttpOrigin(entry.apiBaseUrl) } : await this.resolveRequestService(service);
2774
+ const resolved = entry.apiBaseUrl ? { serviceKey: normalizeLookupKey(service), baseUrl: this.validatedServiceOrigin(service, entry.apiBaseUrl) } : await this.resolveRequestService(service);
2500
2775
  const target = new URL(uploadUrl, resolved.baseUrl.endsWith("/") ? resolved.baseUrl : `${resolved.baseUrl}/`);
2501
2776
  const body = bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength);
2502
2777
  if (target.origin === new URL(resolved.baseUrl).origin) {
@@ -2512,7 +2787,7 @@ var TopoloClient = class _TopoloClient {
2512
2787
  });
2513
2788
  return;
2514
2789
  }
2515
- const response = await this.fetchImpl(target, {
2790
+ const response = await this.credentialedFetch(service, target, {
2516
2791
  method,
2517
2792
  headers: { "Content-Type": options.contentType.trim().toLowerCase() },
2518
2793
  body
@@ -3448,9 +3723,11 @@ var DEVICE_GRANT = "urn:ietf:params:oauth:grant-type:device_code";
3448
3723
  var TopoloOAuth = class {
3449
3724
  baseUrl;
3450
3725
  fetchImpl;
3726
+ environment;
3451
3727
  constructor(options = {}) {
3452
- this.baseUrl = options.environment ? assertTopoloServiceUrlEnvironment("auth", resolveServiceUrl("auth", options.serviceUrls), options.environment) : resolveServiceUrl("auth", options.serviceUrls);
3728
+ this.baseUrl = resolveServiceUrl("auth", options.serviceUrls, options.environment);
3453
3729
  this.fetchImpl = options.fetch ?? fetch;
3730
+ this.environment = options.environment;
3454
3731
  }
3455
3732
  /**
3456
3733
  * RFC 8628 device authorization grant — starts the flow by asking the server
@@ -3515,7 +3792,7 @@ var TopoloOAuth = class {
3515
3792
  }
3516
3793
  async #postForm(path, body) {
3517
3794
  const url = new URL(path, this.baseUrl.endsWith("/") ? this.baseUrl : `${this.baseUrl}/`);
3518
- const res = await this.fetchImpl(url.toString(), {
3795
+ const res = await fetchWithCredentialBoundary(this.fetchImpl, this.environment, "auth", url, {
3519
3796
  method: "POST",
3520
3797
  headers: {
3521
3798
  "Content-Type": "application/x-www-form-urlencoded",
@@ -4044,7 +4321,7 @@ async function pollUntilApproved(params) {
4044
4321
  // src/commands/auth-lock.ts
4045
4322
  import { createHash as createHash3 } from "crypto";
4046
4323
 
4047
- // node_modules/.pnpm/@topolo+sdk@0.23.26/node_modules/@topolo/sdk/dist/node/credential-store.js
4324
+ // node_modules/@topolo/sdk/dist/node/credential-store.js
4048
4325
  import { mkdir as mkdir2, readFile as readFile2, chmod as chmod2, lstat, readdir as readdir2 } from "fs/promises";
4049
4326
  import { existsSync as existsSync2, mkdirSync, lstatSync, readdirSync, readFileSync as readFileSync2, openSync, writeFileSync, fsyncSync, closeSync, renameSync, unlinkSync, rmdirSync, linkSync } from "fs";
4050
4327
  import { createHash as createHash2, randomUUID as randomUUID2 } from "crypto";
@@ -4386,55 +4663,256 @@ var TopoloReauthRequiredError2 = class extends TopoloAuthError2 {
4386
4663
  this.reauthorization = Object.freeze({ ...reauthorization });
4387
4664
  }
4388
4665
  };
4389
- var TOPOLO_RUNTIME_ENVIRONMENTS = ["production", "staging", "development", "local"];
4390
- var TOPOLO_ENVIRONMENT_SERVICE_URLS = {
4391
- production: {
4666
+ var TOPOLO_FIRST_PARTY_ORIGINS2 = Object.freeze({
4667
+ production: Object.freeze([
4668
+ "https://admin.topolo.app",
4669
+ "https://agent.topolo.app",
4670
+ "https://api.ol0.me",
4671
+ "https://auth.topolo.app",
4672
+ "https://backup.topolo.app",
4673
+ "https://blog.topolo.app",
4674
+ "https://books.topolo.app",
4675
+ "https://brand.topolo.app",
4676
+ "https://bugfix.topolo.app",
4677
+ "https://bytes.topolo.app",
4678
+ "https://calendar.topolo.app",
4679
+ "https://campaigns.topolo.app",
4680
+ "https://capacity.topolo.app",
4681
+ "https://chat.topolo.app",
4682
+ "https://commerce.topolo.app",
4683
+ "https://compose.topolo.app",
4684
+ "https://consent.topolo.app",
4685
+ "https://contracts.topolo.app",
4686
+ "https://crm.topolo.app",
4687
+ "https://design.topolo.app",
4688
+ "https://developers.topolo.app",
4689
+ "https://director.topolo.app",
4690
+ "https://docs.topolo.app",
4691
+ "https://engage.topolo.app",
4692
+ "https://events.topolo.app",
4693
+ "https://feed-analytics-api.topolo.app",
4694
+ "https://feed-api.topolo.app",
4695
+ "https://flow.topolo.app",
4696
+ "https://forecast.topolo.app",
4697
+ "https://forms.topolo.app",
4698
+ "https://home.topolo.app",
4699
+ "https://insights.topolo.app",
4700
+ "https://inventory.topolo.app",
4701
+ "https://learn.topolo.app",
4702
+ "https://localize.topolo.app",
4703
+ "https://mail.topolo.app",
4704
+ "https://mcp.topolo.app",
4705
+ "https://mdm.topolo.app",
4706
+ "https://messages.topolo.app",
4707
+ "https://nexus.topolo.app",
4708
+ "https://notify.topolo.app",
4709
+ "https://nurture.topolo.app",
4710
+ "https://observability-watch.topolo.app",
4711
+ "https://p2p.topolo.app",
4712
+ "https://pay.topolo.app",
4713
+ "https://people.topolo.app",
4714
+ "https://quro.topolo.app",
4715
+ "https://recruit.topolo.app",
4716
+ "https://render.topolo.app",
4717
+ "https://roadmap.topolo.app",
4718
+ "https://sign.topolo.app",
4719
+ "https://socialize-api.topolo.app",
4720
+ "https://spaces.topolo.app",
4721
+ "https://speech.topolo.app",
4722
+ "https://status.topolo.app",
4723
+ "https://studio.topolo.app",
4724
+ "https://success.topolo.app",
4725
+ "https://support.topolo.app",
4726
+ "https://survey.topolo.app",
4727
+ "https://voice.topolo.app",
4728
+ "https://web.topolo.app",
4729
+ "https://www.topolo.app"
4730
+ ]),
4731
+ staging: Object.freeze([
4732
+ "https://admin.stg.topolo.us",
4733
+ "https://agent.stg.topolo.us",
4734
+ "https://api.one.stg.topolo.us",
4735
+ "https://auth.stg.topolo.us",
4736
+ "https://backup.stg.topolo.us",
4737
+ "https://blog.stg.topolo.us",
4738
+ "https://books.stg.topolo.us",
4739
+ "https://brand.stg.topolo.us",
4740
+ "https://bugfix.stg.topolo.us",
4741
+ "https://bytes.stg.topolo.us",
4742
+ "https://calendar.stg.topolo.us",
4743
+ "https://campaigns.stg.topolo.us",
4744
+ "https://capacity.stg.topolo.us",
4745
+ "https://chat.stg.topolo.us",
4746
+ "https://commerce.stg.topolo.us",
4747
+ "https://compose.stg.topolo.us",
4748
+ "https://consent.stg.topolo.us",
4749
+ "https://contracts.stg.topolo.us",
4750
+ "https://crm.stg.topolo.us",
4751
+ "https://design.stg.topolo.us",
4752
+ "https://developers.stg.topolo.us",
4753
+ "https://director.stg.topolo.us",
4754
+ "https://docs.stg.topolo.us",
4755
+ "https://engage.stg.topolo.us",
4756
+ "https://events.stg.topolo.us",
4757
+ "https://feed-analytics-api.stg.topolo.us",
4758
+ "https://feed-api.stg.topolo.us",
4759
+ "https://flow.stg.topolo.us",
4760
+ "https://forecast.stg.topolo.us",
4761
+ "https://forms.stg.topolo.us",
4762
+ "https://home.stg.topolo.us",
4763
+ "https://insights.stg.topolo.us",
4764
+ "https://inventory.stg.topolo.us",
4765
+ "https://learn.stg.topolo.us",
4766
+ "https://localize.stg.topolo.us",
4767
+ "https://mail.stg.topolo.us",
4768
+ "https://mcp.stg.topolo.us",
4769
+ "https://mdm.stg.topolo.us",
4770
+ "https://messages.stg.topolo.us",
4771
+ "https://nexus.stg.topolo.us",
4772
+ "https://notify.stg.topolo.us",
4773
+ "https://nurture.stg.topolo.us",
4774
+ "https://observability-watch.stg.topolo.us",
4775
+ "https://p2p.stg.topolo.us",
4776
+ "https://pay.stg.topolo.us",
4777
+ "https://people.stg.topolo.us",
4778
+ "https://pulse.stg.topolo.us",
4779
+ "https://recruit.stg.topolo.us",
4780
+ "https://render.stg.topolo.us",
4781
+ "https://roadmap.stg.topolo.us",
4782
+ "https://seed.stg.topolo.us",
4783
+ "https://sign.stg.topolo.us",
4784
+ "https://socialize-api.stg.topolo.us",
4785
+ "https://spaces.stg.topolo.us",
4786
+ "https://speech.stg.topolo.us",
4787
+ "https://status.stg.topolo.us",
4788
+ "https://studio-api.stg.topolo.us",
4789
+ "https://studio.stg.topolo.us",
4790
+ "https://success.stg.topolo.us",
4791
+ "https://support.stg.topolo.us",
4792
+ "https://survey.stg.topolo.us",
4793
+ "https://voice.stg.topolo.us",
4794
+ "https://web.stg.topolo.us"
4795
+ ]),
4796
+ development: Object.freeze([
4797
+ "https://admin.topolo.dev",
4798
+ "https://agent.topolo.dev",
4799
+ "https://api.one.topolo.dev",
4800
+ "https://auth.topolo.dev",
4801
+ "https://backup.topolo.dev",
4802
+ "https://blog.topolo.dev",
4803
+ "https://books.topolo.dev",
4804
+ "https://brand.topolo.dev",
4805
+ "https://bugfix.topolo.dev",
4806
+ "https://bytes.topolo.dev",
4807
+ "https://calendar.topolo.dev",
4808
+ "https://campaigns.topolo.dev",
4809
+ "https://capacity.topolo.dev",
4810
+ "https://chat.topolo.dev",
4811
+ "https://commerce.topolo.dev",
4812
+ "https://compose.topolo.dev",
4813
+ "https://consent.topolo.dev",
4814
+ "https://contracts.topolo.dev",
4815
+ "https://crm.topolo.dev",
4816
+ "https://design.topolo.dev",
4817
+ "https://developers.topolo.dev",
4818
+ "https://director.topolo.dev",
4819
+ "https://docs.topolo.dev",
4820
+ "https://engage.topolo.dev",
4821
+ "https://events.topolo.dev",
4822
+ "https://feed-analytics-api.topolo.dev",
4823
+ "https://feed-api.topolo.dev",
4824
+ "https://flow.topolo.dev",
4825
+ "https://forecast.topolo.dev",
4826
+ "https://forms.topolo.dev",
4827
+ "https://home.topolo.dev",
4828
+ "https://insights.topolo.dev",
4829
+ "https://inventory.topolo.dev",
4830
+ "https://learn.topolo.dev",
4831
+ "https://localize.topolo.dev",
4832
+ "https://mail.topolo.dev",
4833
+ "https://mcp.topolo.dev",
4834
+ "https://mdm.topolo.dev",
4835
+ "https://messages.topolo.dev",
4836
+ "https://nexus.topolo.dev",
4837
+ "https://notify.topolo.dev",
4838
+ "https://nurture.topolo.dev",
4839
+ "https://observability-watch.topolo.dev",
4840
+ "https://p2p.topolo.dev",
4841
+ "https://pay.topolo.dev",
4842
+ "https://people.topolo.dev",
4843
+ "https://pulse.topolo.dev",
4844
+ "https://recruit.topolo.dev",
4845
+ "https://render.topolo.dev",
4846
+ "https://roadmap.topolo.dev",
4847
+ "https://seed.topolo.dev",
4848
+ "https://sign.topolo.dev",
4849
+ "https://socialize-api.topolo.dev",
4850
+ "https://spaces.topolo.dev",
4851
+ "https://speech.topolo.dev",
4852
+ "https://status.topolo.dev",
4853
+ "https://studio-api.topolo.dev",
4854
+ "https://studio.topolo.dev",
4855
+ "https://success.topolo.dev",
4856
+ "https://support.topolo.dev",
4857
+ "https://survey.topolo.dev",
4858
+ "https://voice.topolo.dev",
4859
+ "https://web.topolo.dev"
4860
+ ])
4861
+ });
4862
+ var TOPOLO_REFUSED_HOST_SUFFIXES2 = Object.freeze(["sites.stg.topolo.us", "sites.topolo.app", "sites.topolo.dev", "topolo.site", "try.topolo.io", "workers.dev"]);
4863
+ var TOPOLO_RUNTIME_ENVIRONMENTS2 = ["production", "staging", "development", "local"];
4864
+ var TOPOLO_ENVIRONMENT_SERVICE_URLS2 = Object.freeze({
4865
+ production: Object.freeze({
4392
4866
  auth: "https://auth.topolo.app",
4393
4867
  developers: "https://developers.topolo.app"
4394
- },
4395
- staging: {
4868
+ }),
4869
+ staging: Object.freeze({
4396
4870
  auth: "https://auth.stg.topolo.us",
4397
4871
  developers: "https://developers.stg.topolo.us"
4398
- },
4399
- development: {
4872
+ }),
4873
+ development: Object.freeze({
4400
4874
  auth: "https://auth.topolo.dev",
4401
4875
  developers: "https://developers.topolo.dev"
4402
- },
4403
- local: {}
4404
- };
4405
- function hostnameHasSuffix2(hostname, suffix) {
4406
- return hostname === suffix || hostname.endsWith(`.${suffix}`);
4407
- }
4408
- function workersDevAccountMatches2(hostname, account) {
4409
- if (!hostname.endsWith(".workers.dev")) return false;
4410
- const labels = hostname.slice(0, -".workers.dev".length).split(".");
4411
- return labels.length >= 2 && labels.at(-1) === account;
4876
+ }),
4877
+ local: Object.freeze({})
4878
+ });
4879
+ var FIRST_PARTY_ORIGIN_SETS2 = Object.freeze({
4880
+ production: new Set(TOPOLO_FIRST_PARTY_ORIGINS2.production),
4881
+ staging: new Set(TOPOLO_FIRST_PARTY_ORIGINS2.staging),
4882
+ development: new Set(TOPOLO_FIRST_PARTY_ORIGINS2.development)
4883
+ });
4884
+ var LOOPBACK_HOSTNAMES2 = /* @__PURE__ */ new Set(["localhost", "127.0.0.1", "[::1]"]);
4885
+ function isRuntimeEnvironment2(value) {
4886
+ return typeof value === "string" && TOPOLO_RUNTIME_ENVIRONMENTS2.includes(value);
4412
4887
  }
4413
- function originMatchesEnvironment2(url, environment) {
4414
- const hostname = url.hostname.toLowerCase();
4888
+ function originIsFirstParty2(url, environment) {
4415
4889
  if (environment === "local") {
4416
- return hostname === "localhost" || hostname.endsWith(".localhost") || hostname === "127.0.0.1" || hostname === "[::1]";
4890
+ return (url.protocol === "http:" || url.protocol === "https:") && LOOPBACK_HOSTNAMES2.has(url.hostname);
4417
4891
  }
4418
4892
  if (url.protocol !== "https:") return false;
4419
- if (environment === "production") {
4420
- return hostnameHasSuffix2(hostname, "topolo.app") || hostnameHasSuffix2(hostname, "topolo.io") || hostname === "api.ol0.me" || workersDevAccountMatches2(hostname, "topolo");
4421
- }
4422
- if (environment === "staging") {
4423
- return hostnameHasSuffix2(hostname, "stg.topolo.us") || workersDevAccountMatches2(hostname, "topolo-staging");
4893
+ return FIRST_PARTY_ORIGIN_SETS2[environment].has(url.origin);
4894
+ }
4895
+ function requireTopoloRuntimeEnvironment2(environment) {
4896
+ if (!isRuntimeEnvironment2(environment)) {
4897
+ throw new TopoloSdkError2(
4898
+ "environment_required",
4899
+ 'Refusing to send a Topolo credential: the runtime environment is not resolved. Pass environment: "production" | "staging" | "development" | "local".'
4900
+ );
4424
4901
  }
4425
- return hostnameHasSuffix2(hostname, "topolo.dev") || workersDevAccountMatches2(hostname, "topolo-development");
4902
+ return environment;
4426
4903
  }
4427
4904
  function assertTopoloServiceUrlEnvironment2(service, value, environment) {
4905
+ const resolved = requireTopoloRuntimeEnvironment2(environment);
4428
4906
  let url;
4429
4907
  try {
4430
4908
  url = new URL(value);
4431
4909
  } catch {
4432
4910
  throw new TopoloSdkError2("invalid_service_url", `Invalid URL configured for Topolo service "${service}".`);
4433
4911
  }
4434
- if (url.protocol !== "https:" && url.protocol !== "http:" || url.username || url.password || !originMatchesEnvironment2(url, environment)) {
4912
+ if (url.protocol !== "https:" && url.protocol !== "http:" || url.username || url.password || !originIsFirstParty2(url, resolved)) {
4435
4913
  throw new TopoloSdkError2(
4436
4914
  "environment_mismatch",
4437
- `Refusing Topolo service "${service}": ${url.origin} does not belong to ${environment}.`
4915
+ `Refusing Topolo service "${service}": ${url.origin} is not a first-party ${resolved} service origin.`
4438
4916
  );
4439
4917
  }
4440
4918
  return url.origin;
@@ -4447,14 +4925,58 @@ function topoloServiceOrigins2(serviceUrls, environment) {
4447
4925
  ])
4448
4926
  );
4449
4927
  }
4928
+ var REDIRECT_STATUSES2 = /* @__PURE__ */ new Set([301, 302, 303, 307, 308]);
4929
+ async function fetchWithCredentialBoundary2(fetchImpl, environment, service, target, init) {
4930
+ const url = new URL(String(target));
4931
+ const origin = assertTopoloServiceUrlEnvironment2(service, url.toString(), environment);
4932
+ const response = await fetchImpl(url.toString(), { ...init, redirect: "manual" });
4933
+ let responseOrigin = origin;
4934
+ try {
4935
+ if (response.url) responseOrigin = new URL(response.url).origin;
4936
+ } catch {
4937
+ responseOrigin = "";
4938
+ }
4939
+ if (response.redirected === true || responseOrigin !== origin) {
4940
+ await response.body?.cancel().catch(() => void 0);
4941
+ throw new TopoloSdkError2(
4942
+ "credential_redirect_refused",
4943
+ `Refusing a redirected response from Topolo service "${service}"; credentials are never forwarded.`
4944
+ );
4945
+ }
4946
+ if (REDIRECT_STATUSES2.has(response.status)) {
4947
+ const location = response.headers.get("Location");
4948
+ let destination = null;
4949
+ if (location !== null) {
4950
+ try {
4951
+ destination = new URL(location, url).origin;
4952
+ } catch {
4953
+ destination = null;
4954
+ }
4955
+ }
4956
+ if (location !== null && destination !== origin) {
4957
+ await response.body?.cancel().catch(() => void 0);
4958
+ throw new TopoloSdkError2(
4959
+ "credential_redirect_refused",
4960
+ `Refusing a redirect from Topolo service "${service}" to another origin; credentials are never forwarded.`
4961
+ );
4962
+ }
4963
+ }
4964
+ return response;
4965
+ }
4450
4966
  var APP_API_URLS2 = {
4451
4967
  auth: "https://auth.topolo.app",
4452
4968
  developers: "https://developers.topolo.app"
4453
4969
  };
4454
4970
  var DEFAULT_APP_URLS2 = APP_API_URLS2;
4455
- function resolveServiceUrl2(service, overrides) {
4971
+ function resolveServiceUrl2(service, overrides, environment) {
4456
4972
  const override = resolveServiceUrlOverride2([service], overrides);
4457
4973
  if (override) return override;
4974
+ if (environment !== void 0) {
4975
+ const selected = requireTopoloRuntimeEnvironment2(environment);
4976
+ const origin = TOPOLO_ENVIRONMENT_SERVICE_URLS2[selected][service];
4977
+ if (!origin) throw new TopoloSdkError2("service_url_required", `Configure an explicit ${service} origin for ${selected}.`);
4978
+ return origin;
4979
+ }
4458
4980
  return DEFAULT_APP_URLS2[service];
4459
4981
  }
4460
4982
  function resolveServiceUrlOverride2(keys, overrides) {
@@ -4476,9 +4998,11 @@ var DEVICE_GRANT2 = "urn:ietf:params:oauth:grant-type:device_code";
4476
4998
  var TopoloOAuth2 = class {
4477
4999
  baseUrl;
4478
5000
  fetchImpl;
5001
+ environment;
4479
5002
  constructor(options = {}) {
4480
- this.baseUrl = options.environment ? assertTopoloServiceUrlEnvironment2("auth", resolveServiceUrl2("auth", options.serviceUrls), options.environment) : resolveServiceUrl2("auth", options.serviceUrls);
5003
+ this.baseUrl = resolveServiceUrl2("auth", options.serviceUrls, options.environment);
4481
5004
  this.fetchImpl = options.fetch ?? fetch;
5005
+ this.environment = options.environment;
4482
5006
  }
4483
5007
  /**
4484
5008
  * RFC 8628 device authorization grant — starts the flow by asking the server
@@ -4543,7 +5067,7 @@ var TopoloOAuth2 = class {
4543
5067
  }
4544
5068
  async #postForm(path, body) {
4545
5069
  const url = new URL(path, this.baseUrl.endsWith("/") ? this.baseUrl : `${this.baseUrl}/`);
4546
- const res = await this.fetchImpl(url.toString(), {
5070
+ const res = await fetchWithCredentialBoundary2(this.fetchImpl, this.environment, "auth", url, {
4547
5071
  method: "POST",
4548
5072
  headers: {
4549
5073
  "Content-Type": "application/x-www-form-urlencoded",
@@ -4823,11 +5347,11 @@ function createProtectedActionSecretStore(options) {
4823
5347
  }
4824
5348
  };
4825
5349
  }
4826
- var TOPOLO_ENVIRONMENTS = TOPOLO_RUNTIME_ENVIRONMENTS;
5350
+ var TOPOLO_ENVIRONMENTS = TOPOLO_RUNTIME_ENVIRONMENTS2;
4827
5351
  function contextKey(contextType, organizationId) {
4828
5352
  return contextType === "organization" && organizationId ? `org:${organizationId}` : "personal";
4829
5353
  }
4830
- var ENVIRONMENT_DEFAULT_SERVICE_URLS = TOPOLO_ENVIRONMENT_SERVICE_URLS;
5354
+ var ENVIRONMENT_DEFAULT_SERVICE_URLS = TOPOLO_ENVIRONMENT_SERVICE_URLS2;
4831
5355
  function configPath() {
4832
5356
  const override = process.env.TOPOLO_CONFIG_PATH;
4833
5357
  if (override) return override;
@@ -5155,7 +5679,7 @@ async function signOut(environment, options = {}) {
5155
5679
  if (options.revoke === false || !grants.length) return result;
5156
5680
  let oauth;
5157
5681
  try {
5158
- oauth = boundedOAuth(authServiceUrls(profile, environment, "sign-out"));
5682
+ oauth = boundedOAuth(authServiceUrls(profile, environment, "sign-out"), environment);
5159
5683
  } catch {
5160
5684
  result.revocationFailed = grants.length;
5161
5685
  return result;
@@ -5203,14 +5727,14 @@ function configLockTimeoutMs() {
5203
5727
  const value = Number(process.env.TOPOLO_CONFIG_LOCK_TIMEOUT_MS);
5204
5728
  return Number.isFinite(value) && value >= 1e3 && value <= 12e4 ? value : CONFIG_LOCK_DEFAULT_TIMEOUT_MS;
5205
5729
  }
5206
- function boundedOAuth(serviceUrls) {
5730
+ function boundedOAuth(serviceUrls, environment) {
5207
5731
  const fetchImpl = (input2, init) => globalThis.fetch(input2, {
5208
5732
  ...init,
5209
5733
  // Never re-POST a refresh token, device code or revoked token to another origin.
5210
5734
  redirect: "error",
5211
5735
  signal: init?.signal ?? AbortSignal.timeout(AUTH_EXCHANGE_TIMEOUT_MS)
5212
5736
  });
5213
- return new TopoloOAuth2({ serviceUrls, fetch: fetchImpl });
5737
+ return new TopoloOAuth2({ serviceUrls, fetch: fetchImpl, environment });
5214
5738
  }
5215
5739
  var CONFIG_LOCK_DEAD_OWNER_MIN_AGE_MS = 2e3;
5216
5740
  var ConfigOwnershipError = class extends TopoloSdkError2 {
@@ -5827,7 +6351,7 @@ async function renewTarget(environment, target, options) {
5827
6351
  return endGrant(retired.reason === "rotated" ? "rotated_grant_lost" : retired.reason);
5828
6352
  }
5829
6353
  const serviceUrls = authServiceUrls(profile, environment, target.kind === "active" ? "credential refresh" : "context refresh");
5830
- const oauth = boundedOAuth(serviceUrls);
6354
+ const oauth = boundedOAuth(serviceUrls, environment);
5831
6355
  try {
5832
6356
  await persist((current) => {
5833
6357
  const next = clone(current);
@@ -5922,7 +6446,7 @@ async function continueReauthorization(environment, key) {
5922
6446
  const clientId = (key === DEFAULT_SLOT ? void 0 : profile.contexts?.[key]?.oauthClientId) ?? profile.oauthClientId;
5923
6447
  if (!clientId) throw reauthRequired(environment, key, entry, "unavailable");
5924
6448
  const serviceUrls = authServiceUrls(profile, environment, "sign-in renewal");
5925
- const oauth = boundedOAuth(serviceUrls);
6449
+ const oauth = boundedOAuth(serviceUrls, environment);
5926
6450
  const now = nowSeconds();
5927
6451
  let state = clone(entry);
5928
6452
  const save = async (update) => {
@@ -6601,6 +7125,7 @@ async function runDeviceFlow(options) {
6601
7125
  const config = await readConfig();
6602
7126
  const resolved = resolvedProfileConfig(config, options.environment);
6603
7127
  const oauth = new TopoloOAuth({
7128
+ environment: options.environment,
6604
7129
  serviceUrls: resolved.serviceUrls
6605
7130
  });
6606
7131
  const start = await oauth.requestDeviceCode({
@@ -10130,8 +10655,9 @@ function errorMessage(error) {
10130
10655
  }
10131
10656
 
10132
10657
  // src/commands/context.ts
10133
- function makeClient(credential, serviceUrls) {
10658
+ function makeClient(environment, credential, serviceUrls) {
10134
10659
  return createTopolo({
10660
+ environment,
10135
10661
  credential,
10136
10662
  agent: { clientName: "topolo-cli", clientVersion: CLI_VERSION },
10137
10663
  serviceUrls
@@ -10141,8 +10667,8 @@ function unwrap(res) {
10141
10667
  const r = res ?? {};
10142
10668
  return r.data ?? r;
10143
10669
  }
10144
- async function fetchMe(credential, serviceUrls) {
10145
- const topolo = makeClient(credential, serviceUrls);
10670
+ async function fetchMe(environment, credential, serviceUrls) {
10671
+ const topolo = makeClient(environment, credential, serviceUrls);
10146
10672
  const data = unwrap(await topolo.client.request({ service: "auth", method: "GET", path: "/api/auth/me" }));
10147
10673
  const user = data.user ?? {};
10148
10674
  const rawMemberships = user.organizationMemberships || data.organizations || [];
@@ -10169,8 +10695,8 @@ function credentialFromTokens(tokens) {
10169
10695
  ...expiresAt ? { expiresAt } : {}
10170
10696
  };
10171
10697
  }
10172
- async function exchangeForContext(credential, serviceUrls, target) {
10173
- const topolo = makeClient(credential, serviceUrls);
10698
+ async function exchangeForContext(environment, credential, serviceUrls, target) {
10699
+ const topolo = makeClient(environment, credential, serviceUrls);
10174
10700
  const data = unwrap(
10175
10701
  await topolo.client.request({
10176
10702
  service: "auth",
@@ -10229,7 +10755,7 @@ function registerContextCommand(program2) {
10229
10755
  let activeIdentity = null;
10230
10756
  if (cred) {
10231
10757
  try {
10232
- activeIdentity = await fetchMe(cred, serviceUrls);
10758
+ activeIdentity = await fetchMe(environment, cred, serviceUrls);
10233
10759
  } catch {
10234
10760
  activeIdentity = null;
10235
10761
  }
@@ -10238,7 +10764,7 @@ function registerContextCommand(program2) {
10238
10764
  try {
10239
10765
  const contextCredential = key === activeKey ? cred : await refreshContextCredentialIfNeeded(environment, key);
10240
10766
  if (!contextCredential) return;
10241
- const identity = key === activeKey && activeIdentity ? activeIdentity : await fetchMe(contextCredential, serviceUrls);
10767
+ const identity = key === activeKey && activeIdentity ? activeIdentity : await fetchMe(environment, contextCredential, serviceUrls);
10242
10768
  if (!identityMatchesContext(identity, ctx)) return;
10243
10769
  const row = rows.get(key) ?? {
10244
10770
  key,
@@ -10334,7 +10860,7 @@ function registerContextCommand(program2) {
10334
10860
  } else if (wantPersonal) {
10335
10861
  resolved = { contextType: "personal" };
10336
10862
  } else {
10337
- const me = await fetchMe(cred, serviceUrls);
10863
+ const me = await fetchMe(environment, cred, serviceUrls);
10338
10864
  const match = me.memberships.find(
10339
10865
  (m) => m.id === target || m.slug?.toLowerCase() === needle || m.name?.toLowerCase() === needle
10340
10866
  );
@@ -10359,7 +10885,7 @@ function registerContextCommand(program2) {
10359
10885
  const storedCredential = await refreshContextCredentialIfNeeded(environment, key);
10360
10886
  if (storedCredential) {
10361
10887
  try {
10362
- const storedIdentity = await fetchMe(storedCredential, serviceUrls);
10888
+ const storedIdentity = await fetchMe(environment, storedCredential, serviceUrls);
10363
10889
  if (identityMatchesContext(storedIdentity, resolved)) {
10364
10890
  newCredential = storedCredential;
10365
10891
  }
@@ -10369,13 +10895,13 @@ function registerContextCommand(program2) {
10369
10895
  }
10370
10896
  if (!newCredential) {
10371
10897
  if (resolved.contextType === "personal") {
10372
- const identity = await fetchMe(cred, serviceUrls);
10898
+ const identity = await fetchMe(environment, cred, serviceUrls);
10373
10899
  if (!identity.personalAvailable) {
10374
10900
  throw new Error("Personal context is not available for this account.");
10375
10901
  }
10376
10902
  }
10377
10903
  try {
10378
- newCredential = await exchangeForContext(cred, serviceUrls, {
10904
+ newCredential = await exchangeForContext(environment, cred, serviceUrls, {
10379
10905
  type: resolved.contextType,
10380
10906
  id: resolved.organizationId
10381
10907
  });
@@ -10388,7 +10914,7 @@ function registerContextCommand(program2) {
10388
10914
  );
10389
10915
  }
10390
10916
  cred = refreshed;
10391
- newCredential = await exchangeForContext(cred, serviceUrls, {
10917
+ newCredential = await exchangeForContext(environment, cred, serviceUrls, {
10392
10918
  type: resolved.contextType,
10393
10919
  id: resolved.organizationId
10394
10920
  });
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "@topolo/cli",
3
3
  "packageManager": "pnpm@10.17.1",
4
- "version": "0.13.24",
5
- "topoloPublishedVersion": "0.13.24",
4
+ "version": "0.13.26",
5
+ "topoloPublishedVersion": "0.13.26",
6
6
  "description": "Public command-line interface for Topolo customer organizations, developers, and agents.",
7
7
  "license": "UNLICENSED",
8
8
  "author": "Topolo",
@@ -33,8 +33,8 @@
33
33
  },
34
34
  "dependencies": {
35
35
  "@modelcontextprotocol/sdk": "1.31.0",
36
- "@topolo/mcp": "0.14.37",
37
- "@topolo/sdk": "0.23.26",
36
+ "@topolo/mcp": "0.14.38",
37
+ "@topolo/sdk": "0.24.0",
38
38
  "commander": "^12.1.0",
39
39
  "undici": "6.29.0"
40
40
  },