@postman-cse/onboarding-repo-sync 2.1.10 → 2.1.13

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/dist/cli.cjs CHANGED
@@ -19747,7 +19747,7 @@ var require_response = __commonJS({
19747
19747
  var assert = require("node:assert");
19748
19748
  var { isomorphicEncode, serializeJavascriptValueToJSONString } = require_infra();
19749
19749
  var textEncoder = new TextEncoder("utf-8");
19750
- var Response = class _Response {
19750
+ var Response2 = class _Response {
19751
19751
  /** @type {Headers} */
19752
19752
  #headers;
19753
19753
  #state;
@@ -19917,13 +19917,13 @@ var require_response = __commonJS({
19917
19917
  response.#state = newState;
19918
19918
  }
19919
19919
  };
19920
- var { getResponseHeaders, setResponseHeaders, getResponseState, setResponseState } = Response;
19921
- Reflect.deleteProperty(Response, "getResponseHeaders");
19922
- Reflect.deleteProperty(Response, "setResponseHeaders");
19923
- Reflect.deleteProperty(Response, "getResponseState");
19924
- Reflect.deleteProperty(Response, "setResponseState");
19925
- mixinBody(Response, getResponseState);
19926
- Object.defineProperties(Response.prototype, {
19920
+ var { getResponseHeaders, setResponseHeaders, getResponseState, setResponseState } = Response2;
19921
+ Reflect.deleteProperty(Response2, "getResponseHeaders");
19922
+ Reflect.deleteProperty(Response2, "setResponseHeaders");
19923
+ Reflect.deleteProperty(Response2, "getResponseState");
19924
+ Reflect.deleteProperty(Response2, "setResponseState");
19925
+ mixinBody(Response2, getResponseState);
19926
+ Object.defineProperties(Response2.prototype, {
19927
19927
  type: kEnumerableProperty,
19928
19928
  url: kEnumerableProperty,
19929
19929
  status: kEnumerableProperty,
@@ -19939,7 +19939,7 @@ var require_response = __commonJS({
19939
19939
  configurable: true
19940
19940
  }
19941
19941
  });
19942
- Object.defineProperties(Response, {
19942
+ Object.defineProperties(Response2, {
19943
19943
  json: kEnumerableProperty,
19944
19944
  redirect: kEnumerableProperty,
19945
19945
  error: kEnumerableProperty
@@ -20072,7 +20072,7 @@ var require_response = __commonJS({
20072
20072
  }
20073
20073
  }
20074
20074
  function fromInnerResponse(innerResponse, guard) {
20075
- const response = new Response(kConstruct);
20075
+ const response = new Response2(kConstruct);
20076
20076
  setResponseState(response, innerResponse);
20077
20077
  const headers = new Headers3(kConstruct);
20078
20078
  setResponseHeaders(response, headers);
@@ -20126,14 +20126,14 @@ var require_response = __commonJS({
20126
20126
  converter: webidl.converters.HeadersInit
20127
20127
  }
20128
20128
  ]);
20129
- webidl.is.Response = webidl.util.MakeTypeAssertion(Response);
20129
+ webidl.is.Response = webidl.util.MakeTypeAssertion(Response2);
20130
20130
  module2.exports = {
20131
20131
  isNetworkError,
20132
20132
  makeNetworkError,
20133
20133
  makeResponse,
20134
20134
  makeAppropriateNetworkError,
20135
20135
  filterResponse,
20136
- Response,
20136
+ Response: Response2,
20137
20137
  cloneResponse,
20138
20138
  fromInnerResponse,
20139
20139
  getResponseState
@@ -99441,7 +99441,7 @@ var require_response2 = __commonJS({
99441
99441
  }
99442
99442
  return stream;
99443
99443
  };
99444
- var Response;
99444
+ var Response2;
99445
99445
  _.inherit(
99446
99446
  /**
99447
99447
  * Response holds data related to the request body. By default, it provides a nice wrapper for url-encoded,
@@ -99452,14 +99452,14 @@ var require_response2 = __commonJS({
99452
99452
  *
99453
99453
  * @param {Response.definition} options -
99454
99454
  */
99455
- Response = function PostmanResponse(options) {
99456
- Response.super_.apply(this, arguments);
99455
+ Response2 = function PostmanResponse(options) {
99456
+ Response2.super_.apply(this, arguments);
99457
99457
  this.update(options || {});
99458
99458
  },
99459
99459
  Property
99460
99460
  );
99461
99461
  _.assign(
99462
- Response.prototype,
99462
+ Response2.prototype,
99463
99463
  /** @lends Response.prototype */
99464
99464
  {
99465
99465
  update(options) {
@@ -99525,7 +99525,7 @@ var require_response2 = __commonJS({
99525
99525
  }
99526
99526
  );
99527
99527
  _.assign(
99528
- Response.prototype,
99528
+ Response2.prototype,
99529
99529
  /** @lends Response.prototype */
99530
99530
  {
99531
99531
  /**
@@ -99703,7 +99703,7 @@ var require_response2 = __commonJS({
99703
99703
  }
99704
99704
  );
99705
99705
  _.assign(
99706
- Response,
99706
+ Response2,
99707
99707
  /** @lends Response */
99708
99708
  {
99709
99709
  /**
@@ -99721,7 +99721,7 @@ var require_response2 = __commonJS({
99721
99721
  * @returns {Boolean}
99722
99722
  */
99723
99723
  isResponse: function(obj) {
99724
- return Boolean(obj) && (obj instanceof Response || _.inSuperChain(obj.constructor, "_postman_propertyName", Response._postman_propertyName));
99724
+ return Boolean(obj) && (obj instanceof Response2 || _.inSuperChain(obj.constructor, "_postman_propertyName", Response2._postman_propertyName));
99725
99725
  },
99726
99726
  /**
99727
99727
  * Converts the response object from the request module to the postman responseBody format
@@ -99732,7 +99732,7 @@ var require_response2 = __commonJS({
99732
99732
  * @todo Add a key: `originalRequest` to the returned object as well, referring to response.request
99733
99733
  */
99734
99734
  createFromNode: function(response, cookies) {
99735
- return new Response({
99735
+ return new Response2({
99736
99736
  cookie: cookies,
99737
99737
  body: response.body.toString(),
99738
99738
  stream: response.body,
@@ -99811,7 +99811,7 @@ var require_response2 = __commonJS({
99811
99811
  }
99812
99812
  );
99813
99813
  module2.exports = {
99814
- Response
99814
+ Response: Response2
99815
99815
  };
99816
99816
  }
99817
99817
  });
@@ -99825,7 +99825,7 @@ var require_item = __commonJS({
99825
99825
  var EventList = require_event_list().EventList;
99826
99826
  var Request = require_request13().Request;
99827
99827
  var RequestAuth = require_request_auth().RequestAuth;
99828
- var Response = require_response2().Response;
99828
+ var Response2 = require_response2().Response;
99829
99829
  var Item;
99830
99830
  var OBJECT = "object";
99831
99831
  var STRING = "string";
@@ -99892,7 +99892,7 @@ var require_item = __commonJS({
99892
99892
  *
99893
99893
  * @type {PropertyList<Response>}
99894
99894
  */
99895
- responses: new PropertyList(Response, this, definition && definition.response),
99895
+ responses: new PropertyList(Response2, this, definition && definition.response),
99896
99896
  /**
99897
99897
  * Events are a set of of {@link Script}s that are executed when certain activities are triggered on an
99898
99898
  * Item. For example, on defining an event that listens to the "test" event, would cause the associated
@@ -133661,6 +133661,54 @@ var HttpError = class _HttpError extends Error {
133661
133661
  }
133662
133662
  };
133663
133663
 
133664
+ // src/lib/postman/pmak-diagnostics.ts
133665
+ var memo = /* @__PURE__ */ new Map();
133666
+ var normalize3 = (value) => new URL(value.trim()).toString().replace(/\/+$/, "");
133667
+ function maskPmakDiagnostic(message, secrets) {
133668
+ let masked = String(message);
133669
+ for (const secret of secrets) if (secret) masked = masked.split(secret).join("***");
133670
+ return Array.from(masked, (character) => {
133671
+ const code = character.codePointAt(0) ?? 0;
133672
+ return code <= 31 || code >= 127 && code <= 159 ? " " : character;
133673
+ }).join("").replace(/\s+/g, " ").trim();
133674
+ }
133675
+ function formatRejectedMint(original, result) {
133676
+ const teamId = typeof result.payload?.user?.teamId === "number" ? ` (team ${(result.payload?.user).teamId})` : "";
133677
+ if (result.kind === "personal") return `Personal API key detected, cannot mint a service-account access token${teamId}.`;
133678
+ if (result.kind === "service-account") return `The postman-api-key authenticates (GET /me OK) but was rejected by POST /service-account-tokens${teamId} and lacks permission to mint access tokens.`;
133679
+ if (result.kind === "invalid") return "The postman-api-key is invalid, disabled, or expired.";
133680
+ return original;
133681
+ }
133682
+ async function inspectPmakIdentity(options) {
133683
+ const apiBaseUrl = normalize3(options.apiBaseUrl);
133684
+ const key = `${apiBaseUrl}\0${options.apiKey}`;
133685
+ let pending = memo.get(key);
133686
+ if (!pending) {
133687
+ pending = (async () => {
133688
+ try {
133689
+ const timeout = AbortSignal.timeout(options.timeoutMs ?? 2e3);
133690
+ const signal = options.signal ? AbortSignal.any([options.signal, timeout]) : timeout;
133691
+ const response = await (options.fetchImpl ?? fetch)(`${apiBaseUrl}/me`, { method: "GET", headers: { "X-Api-Key": options.apiKey }, signal });
133692
+ if (response.status === 401 || response.status === 403) return { kind: "invalid", status: response.status };
133693
+ if (!response.ok) return { kind: "inconclusive", status: response.status };
133694
+ const payload = await response.json().catch(() => void 0);
133695
+ const user = payload?.user;
133696
+ if (!user || typeof user !== "object" || Array.isArray(user)) return { kind: "inconclusive", payload };
133697
+ const record = user;
133698
+ if (typeof record.username === "string" && record.username || typeof record.email === "string" && record.email) return { kind: "personal", status: response.status, payload };
133699
+ if ("username" in record && "email" in record && (record.username == null || record.username === "") && (record.email == null || record.email === "")) return { kind: "service-account", status: response.status, payload };
133700
+ return { kind: "inconclusive", payload };
133701
+ } catch {
133702
+ return { kind: "inconclusive" };
133703
+ }
133704
+ })();
133705
+ memo.set(key, pending);
133706
+ }
133707
+ const result = await pending;
133708
+ if (options.mode === "preflight" && result.kind === "inconclusive") memo.delete(key);
133709
+ return result;
133710
+ }
133711
+
133664
133712
  // src/lib/postman/credential-identity.ts
133665
133713
  var sessionPath = "/api/sessions/current";
133666
133714
  var SESSION_MAX_ATTEMPTS = 3;
@@ -133754,14 +133802,8 @@ async function resolvePmakIdentity(opts) {
133754
133802
  }
133755
133803
  async function probePmakIdentity(baseUrl, apiKey, fetchImpl) {
133756
133804
  try {
133757
- const response = await fetchImpl(`${baseUrl}/me`, {
133758
- method: "GET",
133759
- headers: { "X-Api-Key": apiKey }
133760
- });
133761
- if (!response.ok) {
133762
- return void 0;
133763
- }
133764
- const payload = asRecord(await response.json());
133805
+ const diagnostic = await inspectPmakIdentity({ apiBaseUrl: baseUrl, apiKey, fetchImpl });
133806
+ const payload = asRecord(diagnostic.payload);
133765
133807
  const user = asRecord(payload?.user);
133766
133808
  if (!user) {
133767
133809
  return void 0;
@@ -134063,6 +134105,39 @@ function adviseFromHttpError(err, ctx) {
134063
134105
  return new Error(ctx.mask(advice), { cause: err });
134064
134106
  }
134065
134107
 
134108
+ // src/lib/postman/app-version.ts
134109
+ var UPDATE_URL = "https://dl.pstmn.io/update/status?currentVersion=12.0.0&platform=osx_arm64";
134110
+ var FLOOR_VERSION = "12.0.0";
134111
+ var VERSION_PATTERN = /^[0-9]+\.[0-9]+\.[0-9]+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/;
134112
+ var PostmanAppVersionProvider = class {
134113
+ fetchImpl;
134114
+ requestTimeoutMs;
134115
+ pending;
134116
+ constructor(options = {}) {
134117
+ this.fetchImpl = options.fetchImpl ?? fetch;
134118
+ this.requestTimeoutMs = options.requestTimeoutMs ?? 2e3;
134119
+ }
134120
+ async get() {
134121
+ if (process.env.POSTMAN_GATEWAY_APP_VERSION === "off") return void 0;
134122
+ this.pending ??= this.resolve();
134123
+ return this.pending;
134124
+ }
134125
+ async resolve() {
134126
+ try {
134127
+ const response = await this.fetchImpl(UPDATE_URL, {
134128
+ signal: AbortSignal.timeout(this.requestTimeoutMs)
134129
+ });
134130
+ if (!response.ok) return FLOOR_VERSION;
134131
+ const payload = await response.json();
134132
+ const version = typeof payload.version === "string" ? payload.version : "";
134133
+ return VERSION_PATTERN.test(version) ? version : FLOOR_VERSION;
134134
+ } catch {
134135
+ return FLOOR_VERSION;
134136
+ }
134137
+ }
134138
+ };
134139
+ var defaultPostmanAppVersionProvider = new PostmanAppVersionProvider();
134140
+
134066
134141
  // src/lib/postman/internal-integration-adapter.ts
134067
134142
  function extractDuplicateWorkspaceId(body) {
134068
134143
  try {
@@ -134082,6 +134157,8 @@ var BifrostInternalIntegrationAdapter = class {
134082
134157
  secretMasker;
134083
134158
  teamId;
134084
134159
  workerBaseUrl;
134160
+ gateway;
134161
+ appVersionProvider;
134085
134162
  constructor(options) {
134086
134163
  this.accessToken = String(options.accessToken || "").trim();
134087
134164
  this.getAccessToken = options.getAccessToken;
@@ -134094,6 +134171,8 @@ var BifrostInternalIntegrationAdapter = class {
134094
134171
  this.workerBaseUrl = String(
134095
134172
  options.workerBaseUrl || "https://catalog-admin.postman-account2009.workers.dev"
134096
134173
  ).replace(/\/+$/, "");
134174
+ this.gateway = options.gateway;
134175
+ this.appVersionProvider = options.appVersionProvider ?? defaultPostmanAppVersionProvider;
134097
134176
  this.secretMasker = options.secretMasker ?? createSecretMasker([this.accessToken]);
134098
134177
  }
134099
134178
  /** Live access token: re-minted value when a provider accessor is wired, else the captured one. */
@@ -134101,7 +134180,7 @@ var BifrostInternalIntegrationAdapter = class {
134101
134180
  return this.getAccessToken ? String(this.getAccessToken() || "").trim() : this.accessToken;
134102
134181
  }
134103
134182
  /** Build Bifrost proxy headers. Only includes x-entity-team-id for org-mode teams. */
134104
- bifrostHeaders() {
134183
+ async bifrostHeaders() {
134105
134184
  const headers = {
134106
134185
  "Content-Type": "application/json",
134107
134186
  "x-access-token": this.currentToken()
@@ -134109,6 +134188,8 @@ var BifrostInternalIntegrationAdapter = class {
134109
134188
  if (this.teamId && this.orgMode) {
134110
134189
  headers["x-entity-team-id"] = this.teamId;
134111
134190
  }
134191
+ const appVersion = await this.appVersionProvider.get();
134192
+ if (appVersion) headers["x-app-version"] = appVersion;
134112
134193
  return headers;
134113
134194
  }
134114
134195
  /** Reactive error-advice context, enriched with the preflight session memo when present. */
@@ -134139,6 +134220,58 @@ var BifrostInternalIntegrationAdapter = class {
134139
134220
  if (associations.length === 0) {
134140
134221
  return;
134141
134222
  }
134223
+ const mode = process.env.POSTMAN_SYSTEM_ENV_ASSOCIATION_MODE ?? "direct";
134224
+ if (mode !== "direct" && mode !== "worker") {
134225
+ throw new Error("POSTMAN_SYSTEM_ENV_ASSOCIATION_MODE must be direct or worker");
134226
+ }
134227
+ if (mode === "direct") {
134228
+ if (!this.gateway) {
134229
+ throw new Error("Direct system-environment association requires the access-token gateway");
134230
+ }
134231
+ const groups = /* @__PURE__ */ new Map();
134232
+ for (const association of associations) {
134233
+ const systemEnvId = String(association.systemEnvId || "").trim();
134234
+ const uid = String(association.envUid || "").trim();
134235
+ if (!systemEnvId || !uid) continue;
134236
+ const key = `${systemEnvId}\0${workspaceId}`;
134237
+ const uids = groups.get(key) ?? /* @__PURE__ */ new Set();
134238
+ uids.add(uid);
134239
+ groups.set(key, uids);
134240
+ }
134241
+ for (const [key, uids] of [...groups.entries()].sort(([a], [b]) => a.localeCompare(b))) {
134242
+ const [systemEnvironmentId, groupedWorkspaceId] = key.split("\0");
134243
+ const postmanEnvironmentIds = [...uids].sort();
134244
+ let response2;
134245
+ try {
134246
+ response2 = await this.gateway.requestJson({
134247
+ service: "api-catalog",
134248
+ method: "post",
134249
+ path: "/api/system-envs/associations",
134250
+ body: {
134251
+ systemEnvironmentId,
134252
+ workspaceEntries: [{ workspaceId: groupedWorkspaceId, postmanEnvironmentIds }]
134253
+ },
134254
+ fallback: "none"
134255
+ }, { retryTransient: true });
134256
+ } catch (error) {
134257
+ if (error instanceof HttpError) {
134258
+ throw adviseFromHttpError(error, this.adviceContext("system environment association")) ?? error;
134259
+ }
134260
+ throw error;
134261
+ }
134262
+ if (!response2 || response2.success !== true) {
134263
+ throw new Error("SYSTEM_ENV_ASSOCIATION_INCOMPLETE");
134264
+ }
134265
+ if (response2.data !== void 0) {
134266
+ if (!Array.isArray(response2.data)) throw new Error("SYSTEM_ENV_ASSOCIATION_INCOMPLETE");
134267
+ const seen = new Set(response2.data.filter((row) => !!row && typeof row === "object").map((row) => `${row.systemEnvironmentId}\0${row.workspaceId}\0${row.postmanEnvironmentId}`));
134268
+ if (!postmanEnvironmentIds.every((uid) => seen.has(`${systemEnvironmentId}\0${groupedWorkspaceId}\0${uid}`))) {
134269
+ throw new Error("SYSTEM_ENV_ASSOCIATION_INCOMPLETE");
134270
+ }
134271
+ }
134272
+ }
134273
+ return;
134274
+ }
134142
134275
  const token = this.currentToken();
134143
134276
  const response = await this.fetchImpl(
134144
134277
  `${this.workerBaseUrl}/api/internal/system-envs/associate`,
@@ -134196,7 +134329,7 @@ var BifrostInternalIntegrationAdapter = class {
134196
134329
  try {
134197
134330
  const response = await this.fetchImpl(url, {
134198
134331
  method: "POST",
134199
- headers: this.bifrostHeaders(),
134332
+ headers: await this.bifrostHeaders(),
134200
134333
  body: JSON.stringify(payload)
134201
134334
  });
134202
134335
  let parsed = {};
@@ -134285,7 +134418,7 @@ var BifrostInternalIntegrationAdapter = class {
134285
134418
  versionControl: true
134286
134419
  }
134287
134420
  };
134288
- const headers = this.bifrostHeaders();
134421
+ const headers = await this.bifrostHeaders();
134289
134422
  const response = await this.fetchImpl(url, {
134290
134423
  method: "POST",
134291
134424
  headers,
@@ -134340,7 +134473,7 @@ var BifrostInternalIntegrationAdapter = class {
134340
134473
  try {
134341
134474
  const response = await this.fetchImpl(`${this.bifrostBaseUrl}/ws/proxy`, {
134342
134475
  method: "POST",
134343
- headers: this.bifrostHeaders(),
134476
+ headers: await this.bifrostHeaders(),
134344
134477
  body: JSON.stringify({
134345
134478
  service: "workspaces",
134346
134479
  method: "GET",
@@ -134371,7 +134504,7 @@ var BifrostInternalIntegrationAdapter = class {
134371
134504
  }
134372
134505
  async createApiKey(name) {
134373
134506
  const url = `${this.bifrostBaseUrl}/ws/proxy`;
134374
- const headers = this.bifrostHeaders();
134507
+ const headers = await this.bifrostHeaders();
134375
134508
  const payload = {
134376
134509
  service: "identity",
134377
134510
  method: "POST",
@@ -134456,6 +134589,16 @@ async function retry(operation, options = {}) {
134456
134589
  }
134457
134590
  throw new Error("Retry exhausted without returning or throwing");
134458
134591
  }
134592
+ function fullJitterDelayMs(attempt, baseMs, capMs, random = Math.random) {
134593
+ return Math.floor(random() * Math.max(0, Math.min(capMs, baseMs * 2 ** Math.max(0, attempt))));
134594
+ }
134595
+ function parseRetryAfterMs2(value) {
134596
+ const trimmed = value?.trim();
134597
+ if (!trimmed) return void 0;
134598
+ if (/^\d+$/.test(trimmed)) return Number(trimmed) * 1e3;
134599
+ const time = Date.parse(trimmed);
134600
+ return Number.isNaN(time) ? void 0 : Math.max(0, time - Date.now());
134601
+ }
134459
134602
 
134460
134603
  // src/contracts.ts
134461
134604
  var postmanRepoSyncActionContract = {
@@ -134885,6 +135028,17 @@ var PostmanGatewayAssetsClient = class {
134885
135028
  return data.map((entry) => this.asRecord(entry)).filter((entry) => entry !== null).map((entry) => ({ uid: String(entry.id ?? entry.uid ?? "").trim(), name: String(entry.name ?? "").trim() })).filter((entry) => entry.uid && entry.name);
134886
135029
  }
134887
135030
  async getSpecContent(specId) {
135031
+ if (process.env.POSTMAN_SPEC_TREE_FAST_PATH !== "off") {
135032
+ try {
135033
+ const treeContent = await this.getSpecContentFromTree(specId);
135034
+ if (treeContent !== void 0) return treeContent;
135035
+ } catch (error) {
135036
+ if (!(error instanceof HttpError) || ![403, 404, 405, 501].includes(error.status)) throw error;
135037
+ }
135038
+ }
135039
+ return this.getSpecContentLegacy(specId);
135040
+ }
135041
+ async getSpecContentLegacy(specId) {
134888
135042
  const files = await this.gateway.requestJson({
134889
135043
  service: "specification",
134890
135044
  method: "get",
@@ -134903,6 +135057,46 @@ var PostmanGatewayAssetsClient = class {
134903
135057
  const record = this.dataOf(file);
134904
135058
  return typeof record?.content === "string" ? record.content : void 0;
134905
135059
  }
135060
+ /** Minimal `/tree` reader: validate the entire returned inventory before selecting ROOT. */
135061
+ async getSpecContentFromTree(specId) {
135062
+ const rows = [];
135063
+ const cursors = /* @__PURE__ */ new Set();
135064
+ let cursor;
135065
+ for (let page = 0; page < 100; page += 1) {
135066
+ const response = await this.gateway.requestJson({
135067
+ service: "specification",
135068
+ method: "get",
135069
+ path: `/specifications/${specId}/tree`,
135070
+ query: { fields: "id,name,type,path,parentId,fileType,content", limit: 100, ...cursor ? { cursor } : {} }
135071
+ });
135072
+ if (!Array.isArray(response?.data)) return void 0;
135073
+ for (const value of response.data) {
135074
+ const row = this.asRecord(value);
135075
+ if (!row || row.type === "FOLDER") continue;
135076
+ if (row.type !== "FILE" || typeof row.id !== "string" || !row.id.trim() || typeof row.path !== "string" || !row.path.trim() || typeof row.content !== "string" || typeof row.fileType !== "string" || !row.fileType.trim()) return void 0;
135077
+ const path5 = row.path.replace(/\\/g, "/").normalize("NFC");
135078
+ if (path5.startsWith("/") || path5.split("/").some((part) => part === ".." || !part)) throw new Error("CONTRACT_DEFINITION_PATH_INVALID");
135079
+ rows.push({ ...row, path: path5 });
135080
+ }
135081
+ const meta = this.asRecord(response?.meta);
135082
+ const cursorValue = this.asRecord(meta?.cursor)?.next;
135083
+ const next = typeof cursorValue === "string" && cursorValue.trim() ? cursorValue.trim() : void 0;
135084
+ if (!next) break;
135085
+ if (cursors.has(next)) throw new Error("SPEC_TREE_CURSOR_REPEATED");
135086
+ cursors.add(next);
135087
+ if (page === 99) throw new Error("SPEC_TREE_PAGE_LIMIT_EXCEEDED");
135088
+ cursor = next;
135089
+ }
135090
+ const paths = /* @__PURE__ */ new Set();
135091
+ for (const row of rows) {
135092
+ const path5 = String(row.path).toLocaleLowerCase();
135093
+ if (paths.has(path5)) throw new Error("CONTRACT_DEFINITION_DUPLICATE_PATH");
135094
+ paths.add(path5);
135095
+ }
135096
+ const roots = rows.filter((row) => row.fileType === "ROOT");
135097
+ if (roots.length !== 1) throw new Error("CONTRACT_DEFINITION_INVENTORY_INVALID");
135098
+ return String(roots[0]?.content);
135099
+ }
134906
135100
  async listSpecCollections(specId) {
134907
135101
  const response = await this.gateway.requestJson({
134908
135102
  service: "specification",
@@ -135462,10 +135656,12 @@ var PostmanGatewayAssetsClient = class {
135462
135656
  // src/lib/postman/token-provider.ts
135463
135657
  var MintError = class extends Error {
135464
135658
  permanent;
135465
- constructor(message, permanent) {
135659
+ status;
135660
+ constructor(message, permanent, status) {
135466
135661
  super(message);
135467
135662
  this.name = "MintError";
135468
135663
  this.permanent = permanent;
135664
+ this.status = status;
135469
135665
  }
135470
135666
  };
135471
135667
  function extractAccessToken(payload) {
@@ -135545,7 +135741,8 @@ var AccessTokenProvider = class {
135545
135741
  if (status === 401 || status === 403) {
135546
135742
  throw new MintError(
135547
135743
  `postman: re-mint failed because the postman-api-key was rejected (PMAK rejected, HTTP ${status}); confirm it is a valid, enabled service-account PMAK for the intended team.`,
135548
- true
135744
+ true,
135745
+ status
135549
135746
  );
135550
135747
  }
135551
135748
  if (status === 400 && body.toLowerCase().includes("service accounts not enabled")) {
@@ -135570,26 +135767,9 @@ var AccessTokenProvider = class {
135570
135767
  }
135571
135768
  };
135572
135769
  async function describeMintFailure(mintError, apiKey, apiBaseUrl, fetchImpl) {
135573
- const raw = mintError instanceof Error ? mintError.message : String(mintError);
135574
- const rejected = /HTTP 40[13]|PMAK rejected/.test(raw);
135575
- if (!rejected) {
135576
- return raw;
135577
- }
135578
- try {
135579
- const me = await fetchImpl(`${apiBaseUrl}/me`, { headers: { "x-api-key": apiKey } });
135580
- if (me.ok) {
135581
- const body = await me.json().catch(() => void 0);
135582
- const user = body?.user;
135583
- const looksPersonal = Boolean(user && (user.username || user.email));
135584
- if (looksPersonal) {
135585
- return "Personal API key detected, cannot mint a service-account access token. POST /service-account-tokens only accepts a SERVICE-ACCOUNT API key; this postman-api-key belongs to a user account" + (user?.teamId ? ` (team ${user.teamId})` : "") + ". Create a service account in Team Settings and use its PMAK, or mint the token elsewhere and pass postman-access-token.";
135586
- }
135587
- return "The postman-api-key authenticates (GET /me OK) but was rejected by POST /service-account-tokens" + (user?.teamId ? ` (team ${user.teamId})` : "") + ". The service account likely lacks permission to mint access tokens, or service accounts are restricted for this team. Check the service account role in Team Settings, or pass a pre-minted postman-access-token.";
135588
- }
135589
- return "The postman-api-key is invalid, disabled, or expired (rejected by both POST /service-account-tokens and GET /me). Generate a fresh service-account PMAK in Team Settings and update the secret.";
135590
- } catch {
135591
- return raw;
135592
- }
135770
+ const raw = maskPmakDiagnostic(mintError instanceof Error ? mintError.message : String(mintError), [apiKey]);
135771
+ if (!(mintError instanceof MintError) || mintError.status !== 401 && mintError.status !== 403) return raw;
135772
+ return formatRejectedMint(raw, await inspectPmakIdentity({ apiBaseUrl, apiKey, fetchImpl }));
135593
135773
  }
135594
135774
  async function mintAccessTokenIfNeeded(inputs, log, setSecret2, fetchImpl = fetch) {
135595
135775
  if (inputs.postmanAccessToken || !inputs.postmanApiKey) {
@@ -135638,6 +135818,10 @@ var AccessTokenGatewayClient = class {
135638
135818
  fallbackBaseUrl;
135639
135819
  retryBaseDelayMs;
135640
135820
  sleepImpl;
135821
+ appVersionProvider;
135822
+ requestTimeoutMs;
135823
+ retryMaxDelayMs;
135824
+ randomImpl;
135641
135825
  constructor(options) {
135642
135826
  this.tokenProvider = options.tokenProvider;
135643
135827
  this.bifrostBaseUrl = String(
@@ -135652,16 +135836,22 @@ var AccessTokenGatewayClient = class {
135652
135836
  this.fallbackBaseUrl = fallbackEnv === "off" ? void 0 : options.fallbackBaseUrl?.replace(/\/+$/, "");
135653
135837
  this.retryBaseDelayMs = options.retryBaseDelayMs ?? 400;
135654
135838
  this.sleepImpl = options.sleepImpl ?? defaultSleep;
135839
+ this.appVersionProvider = options.appVersionProvider ?? defaultPostmanAppVersionProvider;
135840
+ this.requestTimeoutMs = options.requestTimeoutMs ?? 3e4;
135841
+ this.retryMaxDelayMs = options.retryMaxDelayMs ?? 5e3;
135842
+ this.randomImpl = options.randomImpl ?? Math.random;
135655
135843
  }
135656
135844
  configureTeamContext(teamId, orgMode) {
135657
135845
  this.teamId = String(teamId || "").trim();
135658
135846
  this.orgMode = orgMode;
135659
135847
  }
135660
- buildHeaders(extra) {
135848
+ async buildHeaders(extra) {
135849
+ const appVersion = await this.appVersionProvider.get();
135661
135850
  const headers = {
135662
135851
  "Content-Type": "application/json",
135663
135852
  "x-access-token": this.tokenProvider.current(),
135664
- ...extra || {}
135853
+ ...extra || {},
135854
+ ...appVersion ? { "x-app-version": appVersion } : {}
135665
135855
  };
135666
135856
  if (this.teamId && this.orgMode) {
135667
135857
  headers["x-entity-team-id"] = this.teamId;
@@ -135670,17 +135860,24 @@ var AccessTokenGatewayClient = class {
135670
135860
  }
135671
135861
  async send(request, baseUrl) {
135672
135862
  const url = `${baseUrl ?? this.bifrostBaseUrl}/ws/proxy`;
135673
- return this.fetchImpl(url, {
135674
- method: "POST",
135675
- headers: this.buildHeaders(request.headers),
135676
- body: JSON.stringify({
135677
- service: request.service,
135678
- method: request.method,
135679
- path: request.path,
135680
- ...request.query !== void 0 ? { query: request.query } : {},
135681
- ...request.body !== void 0 ? { body: request.body } : {}
135682
- })
135683
- });
135863
+ const controller = new AbortController();
135864
+ const timer = setTimeout(() => controller.abort(), this.requestTimeoutMs);
135865
+ try {
135866
+ return await this.fetchImpl(url, {
135867
+ method: "POST",
135868
+ headers: await this.buildHeaders(request.headers),
135869
+ signal: controller.signal,
135870
+ body: JSON.stringify({
135871
+ service: request.service,
135872
+ method: request.method,
135873
+ path: request.path,
135874
+ ...request.query !== void 0 ? { query: request.query } : {},
135875
+ ...request.body !== void 0 ? { body: request.body } : {}
135876
+ })
135877
+ });
135878
+ } finally {
135879
+ clearTimeout(timer);
135880
+ }
135684
135881
  }
135685
135882
  /**
135686
135883
  * One cold, serial attempt against the fallback base URL after the primary
@@ -135707,6 +135904,7 @@ var AccessTokenGatewayClient = class {
135707
135904
  * they are the freshest authoritative answer.
135708
135905
  */
135709
135906
  fallbackEligible(request, retryTransient) {
135907
+ if (request.fallback === "none") return false;
135710
135908
  if (!this.fallbackBaseUrl) return false;
135711
135909
  return retryTransient || request.fallback === "auto";
135712
135910
  }
@@ -135736,7 +135934,7 @@ var AccessTokenGatewayClient = class {
135736
135934
  response = await this.send(request);
135737
135935
  } catch (error) {
135738
135936
  if (retryTransient && attempt < this.maxRetries) {
135739
- const delay = this.retryBaseDelayMs * 2 ** attempt;
135937
+ const delay = this.retryDelayMs(attempt);
135740
135938
  attempt += 1;
135741
135939
  await this.sleepImpl(delay);
135742
135940
  continue;
@@ -135746,7 +135944,17 @@ var AccessTokenGatewayClient = class {
135746
135944
  throw error;
135747
135945
  }
135748
135946
  if (response.ok) {
135749
- return response;
135947
+ const okBody = await response.text().catch(() => "");
135948
+ const inner = this.innerStatus(okBody);
135949
+ if (inner !== void 0) {
135950
+ if (retryTransient && this.isTransient(inner, okBody) && attempt < this.maxRetries) {
135951
+ await this.sleepImpl(this.retryDelayMs(attempt));
135952
+ attempt += 1;
135953
+ continue;
135954
+ }
135955
+ throw this.toInnerHttpError(request, inner, okBody);
135956
+ }
135957
+ return this.rebuildResponse(response, okBody);
135750
135958
  }
135751
135959
  const body = await response.text().catch(() => "");
135752
135960
  if (isExpiredAuthError(response.status, body) && this.tokenProvider.canRefresh()) {
@@ -135758,8 +135966,8 @@ var AccessTokenGatewayClient = class {
135758
135966
  const retryBody = await response.text().catch(() => "");
135759
135967
  throw this.toHttpError(request, response, retryBody);
135760
135968
  }
135761
- if (retryTransient && isRetryableSafeReadResponse(response.status) && attempt < this.maxRetries) {
135762
- const delay = this.retryBaseDelayMs * 2 ** attempt;
135969
+ if (retryTransient && this.isTransient(response.status, body) && attempt < this.maxRetries) {
135970
+ const delay = this.retryDelayMs(attempt, parseRetryAfterMs2(response.headers.get("retry-after")));
135763
135971
  attempt += 1;
135764
135972
  await this.sleepImpl(delay);
135765
135973
  continue;
@@ -135769,6 +135977,24 @@ var AccessTokenGatewayClient = class {
135769
135977
  throw this.toHttpError(request, response, body);
135770
135978
  }
135771
135979
  }
135980
+ retryDelayMs(attempt, retryAfter) {
135981
+ return retryAfter === void 0 ? fullJitterDelayMs(attempt, this.retryBaseDelayMs, this.retryMaxDelayMs, this.randomImpl) : Math.min(this.retryMaxDelayMs, retryAfter);
135982
+ }
135983
+ isTransient(status, body) {
135984
+ return status === 408 || status === 429 || status >= 500 || /ESOCKETTIMEDOUT|ETIMEDOUT|ECONNRESET|serverError|downstream/.test(body);
135985
+ }
135986
+ innerStatus(body) {
135987
+ try {
135988
+ const value = JSON.parse(body);
135989
+ const status = Number(value.status ?? value.statusCode);
135990
+ return value.error || value.success === false || status >= 400 ? status >= 400 ? status : 502 : void 0;
135991
+ } catch {
135992
+ return void 0;
135993
+ }
135994
+ }
135995
+ rebuildResponse(response, body) {
135996
+ return new Response([204, 205, 304].includes(response.status) ? null : body, { status: response.status, statusText: response.statusText, headers: response.headers });
135997
+ }
135772
135998
  /** Send a gateway request and parse the JSON body, or null when empty. */
135773
135999
  async requestJson(request, options = {}) {
135774
136000
  const response = await this.request(request, options);
@@ -135815,11 +136041,19 @@ var AccessTokenGatewayClient = class {
135815
136041
  url: `${this.bifrostBaseUrl}/ws/proxy (${request.service}: ${request.method} ${request.path})`,
135816
136042
  status: response.status,
135817
136043
  statusText: response.statusText,
135818
- requestHeaders: this.buildHeaders(request.headers),
136044
+ requestHeaders: {
136045
+ "Content-Type": "application/json",
136046
+ "x-access-token": this.tokenProvider.current(),
136047
+ ...request.headers || {},
136048
+ ...this.teamId && this.orgMode ? { "x-entity-team-id": this.teamId } : {}
136049
+ },
135819
136050
  responseBody: this.secretMasker(body),
135820
136051
  secretValues: [this.tokenProvider.current()]
135821
136052
  });
135822
136053
  }
136054
+ toInnerHttpError(request, status, body) {
136055
+ return new HttpError({ method: request.method.toUpperCase(), url: `${this.bifrostBaseUrl}/ws/proxy (${request.service}: ${request.method} ${request.path}) [inner]`, status, statusText: "Inner Error", requestHeaders: { "x-access-token": this.tokenProvider.current() }, responseBody: this.secretMasker(body), secretValues: [this.tokenProvider.current()] });
136056
+ }
135823
136057
  };
135824
136058
 
135825
136059
  // src/lib/repo/branch-decision.ts
@@ -136742,8 +136976,8 @@ function stripVolatileFields(obj) {
136742
136976
  }
136743
136977
  return obj;
136744
136978
  }
136745
- function writeJsonFile(path5, content, normalize3 = false) {
136746
- const data = normalize3 ? stripVolatileFields(content) : content;
136979
+ function writeJsonFile(path5, content, normalize4 = false) {
136980
+ const data = normalize4 ? stripVolatileFields(content) : content;
136747
136981
  (0, import_node_fs4.writeFileSync)(path5, JSON.stringify(data, null, 2));
136748
136982
  }
136749
136983
  function buildMappedSpecCloudKey(mappedSource, specSyncMode, releaseLabel) {
@@ -137766,7 +138000,8 @@ function createRepoSyncDependencies(inputs, resolved, factories, options = {}) {
137766
138000
  bifrostBaseUrl: inputs.postmanBifrostBase,
137767
138001
  orgMode: inputs.orgMode,
137768
138002
  teamId: resolved.teamId,
137769
- secretMasker: masker
138003
+ secretMasker: masker,
138004
+ gateway
137770
138005
  });
137771
138006
  return {
137772
138007
  teamId: resolved.teamId,