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

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
@@ -26571,7 +26571,7 @@ var require_util7 = __commonJS({
26571
26571
  exports2.esc = esc;
26572
26572
  exports2.slugify = slugify;
26573
26573
  exports2.isObject = isObject;
26574
- exports2.isPlainObject = isPlainObject2;
26574
+ exports2.isPlainObject = isPlainObject3;
26575
26575
  exports2.shallowClone = shallowClone;
26576
26576
  exports2.numKeys = numKeys;
26577
26577
  exports2.escapeRegex = escapeRegex;
@@ -26754,7 +26754,7 @@ var require_util7 = __commonJS({
26754
26754
  return false;
26755
26755
  }
26756
26756
  });
26757
- function isPlainObject2(o) {
26757
+ function isPlainObject3(o) {
26758
26758
  if (isObject(o) === false)
26759
26759
  return false;
26760
26760
  const ctor = o.constructor;
@@ -26771,7 +26771,7 @@ var require_util7 = __commonJS({
26771
26771
  return true;
26772
26772
  }
26773
26773
  function shallowClone(o) {
26774
- if (isPlainObject2(o))
26774
+ if (isPlainObject3(o))
26775
26775
  return { ...o };
26776
26776
  if (Array.isArray(o))
26777
26777
  return [...o];
@@ -26976,7 +26976,7 @@ var require_util7 = __commonJS({
26976
26976
  return clone(schema, def);
26977
26977
  }
26978
26978
  function extend(schema, shape) {
26979
- if (!isPlainObject2(shape)) {
26979
+ if (!isPlainObject3(shape)) {
26980
26980
  throw new Error("Invalid input to extend: expected a plain object");
26981
26981
  }
26982
26982
  const checks = schema._zod.def.checks;
@@ -26999,7 +26999,7 @@ var require_util7 = __commonJS({
26999
26999
  return clone(schema, def);
27000
27000
  }
27001
27001
  function safeExtend(schema, shape) {
27002
- if (!isPlainObject2(shape)) {
27002
+ if (!isPlainObject3(shape)) {
27003
27003
  throw new Error("Invalid input to safeExtend: expected a plain object");
27004
27004
  }
27005
27005
  const def = mergeDefs(schema._zod.def, {
@@ -54818,7 +54818,7 @@ var require_lodash = __commonJS({
54818
54818
  } else {
54819
54819
  newValue = [];
54820
54820
  }
54821
- } else if (isPlainObject2(srcValue) || isArguments(srcValue)) {
54821
+ } else if (isPlainObject3(srcValue) || isArguments(srcValue)) {
54822
54822
  newValue = objValue;
54823
54823
  if (isArguments(objValue)) {
54824
54824
  newValue = toPlainObject(objValue);
@@ -55782,7 +55782,7 @@ var require_lodash = __commonJS({
55782
55782
  return objValue;
55783
55783
  }
55784
55784
  function customOmitClone(value) {
55785
- return isPlainObject2(value) ? undefined2 : value;
55785
+ return isPlainObject3(value) ? undefined2 : value;
55786
55786
  }
55787
55787
  function equalArrays(array, other, bitmask, customizer, equalFunc, stack) {
55788
55788
  var isPartial = bitmask & COMPARE_PARTIAL_FLAG, arrLength = array.length, othLength = other.length;
@@ -57283,7 +57283,7 @@ var require_lodash = __commonJS({
57283
57283
  var isBuffer = nativeIsBuffer || stubFalse;
57284
57284
  var isDate = nodeIsDate ? baseUnary(nodeIsDate) : baseIsDate;
57285
57285
  function isElement(value) {
57286
- return isObjectLike(value) && value.nodeType === 1 && !isPlainObject2(value);
57286
+ return isObjectLike(value) && value.nodeType === 1 && !isPlainObject3(value);
57287
57287
  }
57288
57288
  function isEmpty(value) {
57289
57289
  if (value == null) {
@@ -57319,7 +57319,7 @@ var require_lodash = __commonJS({
57319
57319
  return false;
57320
57320
  }
57321
57321
  var tag = baseGetTag(value);
57322
- return tag == errorTag || tag == domExcTag || typeof value.message == "string" && typeof value.name == "string" && !isPlainObject2(value);
57322
+ return tag == errorTag || tag == domExcTag || typeof value.message == "string" && typeof value.name == "string" && !isPlainObject3(value);
57323
57323
  }
57324
57324
  function isFinite2(value) {
57325
57325
  return typeof value == "number" && nativeIsFinite(value);
@@ -57370,7 +57370,7 @@ var require_lodash = __commonJS({
57370
57370
  function isNumber(value) {
57371
57371
  return typeof value == "number" || isObjectLike(value) && baseGetTag(value) == numberTag;
57372
57372
  }
57373
- function isPlainObject2(value) {
57373
+ function isPlainObject3(value) {
57374
57374
  if (!isObjectLike(value) || baseGetTag(value) != objectTag) {
57375
57375
  return false;
57376
57376
  }
@@ -58441,7 +58441,7 @@ var require_lodash = __commonJS({
58441
58441
  lodash.isNumber = isNumber;
58442
58442
  lodash.isObject = isObject;
58443
58443
  lodash.isObjectLike = isObjectLike;
58444
- lodash.isPlainObject = isPlainObject2;
58444
+ lodash.isPlainObject = isPlainObject3;
58445
58445
  lodash.isRegExp = isRegExp;
58446
58446
  lodash.isSafeInteger = isSafeInteger;
58447
58447
  lodash.isSet = isSet;
@@ -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
@@ -128814,11 +128814,12 @@ __export(cli_exports, {
128814
128814
  normalizeCliFlag: () => normalizeCliFlag,
128815
128815
  parseCliArgs: () => parseCliArgs,
128816
128816
  runCli: () => runCli,
128817
- toDotenv: () => toDotenv
128817
+ toDotenv: () => toDotenv,
128818
+ writeOptionalFileAtomic: () => writeOptionalFileAtomic
128818
128819
  });
128819
128820
  module.exports = __toCommonJS(cli_exports);
128820
128821
  var import_node_child_process = require("node:child_process");
128821
- var import_node_fs5 = require("node:fs");
128822
+ var import_node_fs6 = require("node:fs");
128822
128823
  var import_promises = require("node:fs/promises");
128823
128824
  var import_node_path3 = __toESM(require("node:path"), 1);
128824
128825
  var import_node_util = require("node:util");
@@ -129186,7 +129187,8 @@ var ExitCode;
129186
129187
  })(ExitCode || (ExitCode = {}));
129187
129188
 
129188
129189
  // src/index.ts
129189
- var import_node_fs4 = require("node:fs");
129190
+ var import_node_crypto3 = require("node:crypto");
129191
+ var import_node_fs5 = require("node:fs");
129190
129192
  var path3 = __toESM(require("node:path"), 1);
129191
129193
 
129192
129194
  // node_modules/js-yaml/dist/js-yaml.mjs
@@ -132102,6 +132104,7 @@ function dump(input, options = {}) {
132102
132104
  }
132103
132105
 
132104
132106
  // src/postman-v3/converter.ts
132107
+ var import_node_fs = require("node:fs");
132105
132108
  var fs2 = __toESM(require("node:fs/promises"), 1);
132106
132109
  var path = __toESM(require("node:path"), 1);
132107
132110
  var V2 = __toESM(require_v2(), 1);
@@ -132172,6 +132175,72 @@ function structuredCloneSafe(value) {
132172
132175
  if (typeof structuredClone === "function") return structuredClone(value);
132173
132176
  return JSON.parse(JSON.stringify(value));
132174
132177
  }
132178
+ var ArtifactDigestStreamError = class extends Error {
132179
+ code = "ARTIFACT_DIGEST_STREAM";
132180
+ constructor(message, options) {
132181
+ super(message, options);
132182
+ this.name = "ArtifactDigestStreamError";
132183
+ }
132184
+ };
132185
+ function sameFileIdentity(left, right) {
132186
+ return left.dev === right.dev && left.ino === right.ino && left.size === right.size;
132187
+ }
132188
+ async function appendArtifactDigestFileStreaming(hash, relative3, absolute, expected) {
132189
+ const flags = typeof import_node_fs.constants.O_NOFOLLOW === "number" ? import_node_fs.constants.O_RDONLY | import_node_fs.constants.O_NOFOLLOW : import_node_fs.constants.O_RDONLY;
132190
+ let handle;
132191
+ try {
132192
+ handle = await fs2.open(absolute, flags);
132193
+ } catch (error) {
132194
+ const code = error?.code;
132195
+ if (code === "ELOOP" || code === "EMLINK" || code === "EINVAL") {
132196
+ throw new ArtifactDigestStreamError(
132197
+ `prebuilt collection tree file became a symlink or unsupported node at ${relative3}`,
132198
+ { cause: error }
132199
+ );
132200
+ }
132201
+ throw new ArtifactDigestStreamError(
132202
+ `prebuilt collection tree file changed or became unsupported at ${relative3}`,
132203
+ { cause: error }
132204
+ );
132205
+ }
132206
+ try {
132207
+ const before = await handle.stat();
132208
+ if (!before.isFile() || !sameFileIdentity(before, expected)) {
132209
+ throw new ArtifactDigestStreamError(
132210
+ `prebuilt collection tree file changed or became unsupported at ${relative3}`
132211
+ );
132212
+ }
132213
+ hash.update(relative3);
132214
+ hash.update("\0");
132215
+ const content = Buffer.allocUnsafe(expected.size);
132216
+ let total = 0;
132217
+ const buffer = Buffer.alloc(64 * 1024);
132218
+ for (; ; ) {
132219
+ const { bytesRead } = await handle.read(buffer, 0, buffer.length, null);
132220
+ if (bytesRead === 0) {
132221
+ break;
132222
+ }
132223
+ if (total + bytesRead > expected.size) {
132224
+ throw new ArtifactDigestStreamError(
132225
+ `prebuilt collection tree file changed while reading at ${relative3}`
132226
+ );
132227
+ }
132228
+ buffer.copy(content, total, 0, bytesRead);
132229
+ hash.update(buffer.subarray(0, bytesRead));
132230
+ total += bytesRead;
132231
+ }
132232
+ hash.update("\0");
132233
+ const after = await handle.stat();
132234
+ if (!after.isFile() || !sameFileIdentity(after, expected) || total !== expected.size) {
132235
+ throw new ArtifactDigestStreamError(
132236
+ `prebuilt collection tree file changed while reading at ${relative3}`
132237
+ );
132238
+ }
132239
+ return total === expected.size ? content : content.subarray(0, total);
132240
+ } finally {
132241
+ await handle.close();
132242
+ }
132243
+ }
132175
132244
  async function listFilesRelative(dir, base) {
132176
132245
  let entries;
132177
132246
  try {
@@ -132732,7 +132801,7 @@ function getCiWorkflowTemplate(provider, options = {}) {
132732
132801
  }
132733
132802
 
132734
132803
  // src/lib/github/repo-mutation.ts
132735
- var import_node_fs = require("node:fs");
132804
+ var import_node_fs2 = require("node:fs");
132736
132805
  var import_node_path = __toESM(require("node:path"), 1);
132737
132806
 
132738
132807
  // src/lib/secrets.ts
@@ -133022,7 +133091,7 @@ var RepoMutationService = class {
133022
133091
  throw new Error(this.secretMasker(changed.stderr || changed.stdout || "Failed to inspect generated changes"));
133023
133092
  }
133024
133093
  const hasPlannedRemoval = removePaths.some(
133025
- (removePath) => (0, import_node_fs.existsSync)(import_node_path.default.resolve(this.cwd, removePath))
133094
+ (removePath) => (0, import_node_fs2.existsSync)(import_node_path.default.resolve(this.cwd, removePath))
133026
133095
  );
133027
133096
  if (!changed.stdout.trim() && !hasPlannedRemoval) {
133028
133097
  return {
@@ -133046,7 +133115,7 @@ var RepoMutationService = class {
133046
133115
  await this.execute("git", ["config", "user.name", options.committerName]);
133047
133116
  await this.execute("git", ["config", "user.email", options.committerEmail]);
133048
133117
  for (const removePath of removePaths) {
133049
- (0, import_node_fs.rmSync)(import_node_path.default.resolve(this.cwd, removePath), { force: true });
133118
+ (0, import_node_fs2.rmSync)(import_node_path.default.resolve(this.cwd, removePath), { force: true });
133050
133119
  }
133051
133120
  await this.execute("git", ["add", "-A", "--", ...stagePaths]);
133052
133121
  const staged = await this.execute("git", ["diff", "--cached", "--quiet"]);
@@ -133589,14 +133658,14 @@ function createTelemetryContext(options) {
133589
133658
  }
133590
133659
 
133591
133660
  // src/action-version.ts
133592
- var import_node_fs2 = require("node:fs");
133661
+ var import_node_fs3 = require("node:fs");
133593
133662
  var import_node_path2 = require("node:path");
133594
133663
  function resolveActionVersion2() {
133595
133664
  if (false) {
133596
133665
  return void 0;
133597
133666
  }
133598
133667
  try {
133599
- const raw = (0, import_node_fs2.readFileSync)((0, import_node_path2.join)(__dirname, "..", "package.json"), "utf8");
133668
+ const raw = (0, import_node_fs3.readFileSync)((0, import_node_path2.join)(__dirname, "..", "package.json"), "utf8");
133600
133669
  return JSON.parse(raw).version ?? "unknown";
133601
133670
  } catch {
133602
133671
  return "unknown";
@@ -133661,6 +133730,54 @@ var HttpError = class _HttpError extends Error {
133661
133730
  }
133662
133731
  };
133663
133732
 
133733
+ // src/lib/postman/pmak-diagnostics.ts
133734
+ var memo = /* @__PURE__ */ new Map();
133735
+ var normalize3 = (value) => new URL(value.trim()).toString().replace(/\/+$/, "");
133736
+ function maskPmakDiagnostic(message, secrets) {
133737
+ let masked = String(message);
133738
+ for (const secret of secrets) if (secret) masked = masked.split(secret).join("***");
133739
+ return Array.from(masked, (character) => {
133740
+ const code = character.codePointAt(0) ?? 0;
133741
+ return code <= 31 || code >= 127 && code <= 159 ? " " : character;
133742
+ }).join("").replace(/\s+/g, " ").trim();
133743
+ }
133744
+ function formatRejectedMint(original, result) {
133745
+ const teamId = typeof result.payload?.user?.teamId === "number" ? ` (team ${(result.payload?.user).teamId})` : "";
133746
+ if (result.kind === "personal") return `Personal API key detected, cannot mint a service-account access token${teamId}.`;
133747
+ 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.`;
133748
+ if (result.kind === "invalid") return "The postman-api-key is invalid, disabled, or expired.";
133749
+ return original;
133750
+ }
133751
+ async function inspectPmakIdentity(options) {
133752
+ const apiBaseUrl = normalize3(options.apiBaseUrl);
133753
+ const key = `${apiBaseUrl}\0${options.apiKey}`;
133754
+ let pending = memo.get(key);
133755
+ if (!pending) {
133756
+ pending = (async () => {
133757
+ try {
133758
+ const timeout = AbortSignal.timeout(options.timeoutMs ?? 2e3);
133759
+ const signal = options.signal ? AbortSignal.any([options.signal, timeout]) : timeout;
133760
+ const response = await (options.fetchImpl ?? fetch)(`${apiBaseUrl}/me`, { method: "GET", headers: { "X-Api-Key": options.apiKey }, signal });
133761
+ if (response.status === 401 || response.status === 403) return { kind: "invalid", status: response.status };
133762
+ if (!response.ok) return { kind: "inconclusive", status: response.status };
133763
+ const payload = await response.json().catch(() => void 0);
133764
+ const user = payload?.user;
133765
+ if (!user || typeof user !== "object" || Array.isArray(user)) return { kind: "inconclusive", payload };
133766
+ const record = user;
133767
+ if (typeof record.username === "string" && record.username || typeof record.email === "string" && record.email) return { kind: "personal", status: response.status, payload };
133768
+ 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 };
133769
+ return { kind: "inconclusive", payload };
133770
+ } catch {
133771
+ return { kind: "inconclusive" };
133772
+ }
133773
+ })();
133774
+ memo.set(key, pending);
133775
+ }
133776
+ const result = await pending;
133777
+ if (options.mode === "preflight" && result.kind === "inconclusive") memo.delete(key);
133778
+ return result;
133779
+ }
133780
+
133664
133781
  // src/lib/postman/credential-identity.ts
133665
133782
  var sessionPath = "/api/sessions/current";
133666
133783
  var SESSION_MAX_ATTEMPTS = 3;
@@ -133754,14 +133871,8 @@ async function resolvePmakIdentity(opts) {
133754
133871
  }
133755
133872
  async function probePmakIdentity(baseUrl, apiKey, fetchImpl) {
133756
133873
  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());
133874
+ const diagnostic = await inspectPmakIdentity({ apiBaseUrl: baseUrl, apiKey, fetchImpl });
133875
+ const payload = asRecord(diagnostic.payload);
133765
133876
  const user = asRecord(payload?.user);
133766
133877
  if (!user) {
133767
133878
  return void 0;
@@ -133793,7 +133904,8 @@ async function resolveSessionIdentity(opts) {
133793
133904
  opts.fetchImpl ?? fetch,
133794
133905
  Math.max(1, opts.maxAttempts ?? SESSION_MAX_ATTEMPTS),
133795
133906
  opts.sleepImpl ?? defaultSessionSleep,
133796
- opts.randomImpl ?? defaultRandom
133907
+ opts.randomImpl ?? defaultRandom,
133908
+ opts.onRetryEvent
133797
133909
  );
133798
133910
  sessionMemo.set(memoKey, pending);
133799
133911
  }
@@ -133827,7 +133939,7 @@ async function parseSessionResponse(response) {
133827
133939
  consumerType: coerceText(root.consumerType) ?? coerceText(data?.consumerType) ?? coerceText(user?.consumerType)
133828
133940
  };
133829
133941
  }
133830
- async function probeSessionIdentity(baseUrl, accessToken, fetchImpl, maxAttempts, sleepImpl, random) {
133942
+ async function probeSessionIdentity(baseUrl, accessToken, fetchImpl, maxAttempts, sleepImpl, random, onRetryEvent) {
133831
133943
  let failure = "unavailable";
133832
133944
  for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
133833
133945
  let response;
@@ -133839,7 +133951,9 @@ async function probeSessionIdentity(baseUrl, accessToken, fetchImpl, maxAttempts
133839
133951
  } catch {
133840
133952
  failure = "unavailable";
133841
133953
  if (attempt < maxAttempts) {
133842
- await sleepImpl(computeSessionRetryDelayMs(void 0, attempt, random));
133954
+ const delay = computeSessionRetryDelayMs(void 0, attempt, random);
133955
+ onRetryEvent?.({ class: "transport", attempt, delay });
133956
+ await sleepImpl(delay);
133843
133957
  continue;
133844
133958
  }
133845
133959
  break;
@@ -133861,7 +133975,9 @@ async function probeSessionIdentity(baseUrl, accessToken, fetchImpl, maxAttempts
133861
133975
  if (response.status === 429 || response.status >= 500) {
133862
133976
  failure = "unavailable";
133863
133977
  if (attempt < maxAttempts) {
133864
- await sleepImpl(computeSessionRetryDelayMs(response, attempt, random));
133978
+ const delay = computeSessionRetryDelayMs(response, attempt, random);
133979
+ onRetryEvent?.({ class: "http", status: response.status, attempt, delay });
133980
+ await sleepImpl(delay);
133865
133981
  continue;
133866
133982
  }
133867
133983
  break;
@@ -133963,7 +134079,8 @@ async function runCredentialPreflight(args) {
133963
134079
  accessToken,
133964
134080
  fetchImpl: args.fetchImpl,
133965
134081
  ...args.sleepImpl ? { sleepImpl: args.sleepImpl } : {},
133966
- ...args.randomImpl ? { randomImpl: args.randomImpl } : {}
134082
+ ...args.randomImpl ? { randomImpl: args.randomImpl } : {},
134083
+ onRetryEvent: args.retryEvent ?? args.log.retryEvent
133967
134084
  });
133968
134085
  } catch (error) {
133969
134086
  args.log.warning(
@@ -134063,6 +134180,39 @@ function adviseFromHttpError(err, ctx) {
134063
134180
  return new Error(ctx.mask(advice), { cause: err });
134064
134181
  }
134065
134182
 
134183
+ // src/lib/postman/app-version.ts
134184
+ var UPDATE_URL = "https://dl.pstmn.io/update/status?currentVersion=12.0.0&platform=osx_arm64";
134185
+ var FLOOR_VERSION = "12.0.0";
134186
+ var VERSION_PATTERN = /^[0-9]+\.[0-9]+\.[0-9]+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/;
134187
+ var PostmanAppVersionProvider = class {
134188
+ fetchImpl;
134189
+ requestTimeoutMs;
134190
+ pending;
134191
+ constructor(options = {}) {
134192
+ this.fetchImpl = options.fetchImpl ?? fetch;
134193
+ this.requestTimeoutMs = options.requestTimeoutMs ?? 2e3;
134194
+ }
134195
+ async get() {
134196
+ if (process.env.POSTMAN_GATEWAY_APP_VERSION === "off") return void 0;
134197
+ this.pending ??= this.resolve();
134198
+ return this.pending;
134199
+ }
134200
+ async resolve() {
134201
+ try {
134202
+ const response = await this.fetchImpl(UPDATE_URL, {
134203
+ signal: AbortSignal.timeout(this.requestTimeoutMs)
134204
+ });
134205
+ if (!response.ok) return FLOOR_VERSION;
134206
+ const payload = await response.json();
134207
+ const version = typeof payload.version === "string" ? payload.version : "";
134208
+ return VERSION_PATTERN.test(version) ? version : FLOOR_VERSION;
134209
+ } catch {
134210
+ return FLOOR_VERSION;
134211
+ }
134212
+ }
134213
+ };
134214
+ var defaultPostmanAppVersionProvider = new PostmanAppVersionProvider();
134215
+
134066
134216
  // src/lib/postman/internal-integration-adapter.ts
134067
134217
  function extractDuplicateWorkspaceId(body) {
134068
134218
  try {
@@ -134082,6 +134232,8 @@ var BifrostInternalIntegrationAdapter = class {
134082
134232
  secretMasker;
134083
134233
  teamId;
134084
134234
  workerBaseUrl;
134235
+ gateway;
134236
+ appVersionProvider;
134085
134237
  constructor(options) {
134086
134238
  this.accessToken = String(options.accessToken || "").trim();
134087
134239
  this.getAccessToken = options.getAccessToken;
@@ -134094,6 +134246,8 @@ var BifrostInternalIntegrationAdapter = class {
134094
134246
  this.workerBaseUrl = String(
134095
134247
  options.workerBaseUrl || "https://catalog-admin.postman-account2009.workers.dev"
134096
134248
  ).replace(/\/+$/, "");
134249
+ this.gateway = options.gateway;
134250
+ this.appVersionProvider = options.appVersionProvider ?? defaultPostmanAppVersionProvider;
134097
134251
  this.secretMasker = options.secretMasker ?? createSecretMasker([this.accessToken]);
134098
134252
  }
134099
134253
  /** Live access token: re-minted value when a provider accessor is wired, else the captured one. */
@@ -134101,7 +134255,7 @@ var BifrostInternalIntegrationAdapter = class {
134101
134255
  return this.getAccessToken ? String(this.getAccessToken() || "").trim() : this.accessToken;
134102
134256
  }
134103
134257
  /** Build Bifrost proxy headers. Only includes x-entity-team-id for org-mode teams. */
134104
- bifrostHeaders() {
134258
+ async bifrostHeaders() {
134105
134259
  const headers = {
134106
134260
  "Content-Type": "application/json",
134107
134261
  "x-access-token": this.currentToken()
@@ -134109,6 +134263,8 @@ var BifrostInternalIntegrationAdapter = class {
134109
134263
  if (this.teamId && this.orgMode) {
134110
134264
  headers["x-entity-team-id"] = this.teamId;
134111
134265
  }
134266
+ const appVersion = await this.appVersionProvider.get();
134267
+ if (appVersion) headers["x-app-version"] = appVersion;
134112
134268
  return headers;
134113
134269
  }
134114
134270
  /** Reactive error-advice context, enriched with the preflight session memo when present. */
@@ -134139,6 +134295,58 @@ var BifrostInternalIntegrationAdapter = class {
134139
134295
  if (associations.length === 0) {
134140
134296
  return;
134141
134297
  }
134298
+ const mode = process.env.POSTMAN_SYSTEM_ENV_ASSOCIATION_MODE ?? "direct";
134299
+ if (mode !== "direct" && mode !== "worker") {
134300
+ throw new Error("POSTMAN_SYSTEM_ENV_ASSOCIATION_MODE must be direct or worker");
134301
+ }
134302
+ if (mode === "direct") {
134303
+ if (!this.gateway) {
134304
+ throw new Error("Direct system-environment association requires the access-token gateway");
134305
+ }
134306
+ const groups = /* @__PURE__ */ new Map();
134307
+ for (const association of associations) {
134308
+ const systemEnvId = String(association.systemEnvId || "").trim();
134309
+ const uid = String(association.envUid || "").trim();
134310
+ if (!systemEnvId || !uid) continue;
134311
+ const key = `${systemEnvId}\0${workspaceId}`;
134312
+ const uids = groups.get(key) ?? /* @__PURE__ */ new Set();
134313
+ uids.add(uid);
134314
+ groups.set(key, uids);
134315
+ }
134316
+ for (const [key, uids] of [...groups.entries()].sort(([a], [b]) => a.localeCompare(b))) {
134317
+ const [systemEnvironmentId, groupedWorkspaceId] = key.split("\0");
134318
+ const postmanEnvironmentIds = [...uids].sort();
134319
+ let response2;
134320
+ try {
134321
+ response2 = await this.gateway.requestJson({
134322
+ service: "api-catalog",
134323
+ method: "post",
134324
+ path: "/api/system-envs/associations",
134325
+ body: {
134326
+ systemEnvironmentId,
134327
+ workspaceEntries: [{ workspaceId: groupedWorkspaceId, postmanEnvironmentIds }]
134328
+ },
134329
+ fallback: "none"
134330
+ }, { retryTransient: true });
134331
+ } catch (error) {
134332
+ if (error instanceof HttpError) {
134333
+ throw adviseFromHttpError(error, this.adviceContext("system environment association")) ?? error;
134334
+ }
134335
+ throw error;
134336
+ }
134337
+ if (!response2 || response2.success !== true) {
134338
+ throw new Error("SYSTEM_ENV_ASSOCIATION_INCOMPLETE");
134339
+ }
134340
+ if (response2.data !== void 0) {
134341
+ if (!Array.isArray(response2.data)) throw new Error("SYSTEM_ENV_ASSOCIATION_INCOMPLETE");
134342
+ const seen = new Set(response2.data.filter((row) => !!row && typeof row === "object").map((row) => `${row.systemEnvironmentId}\0${row.workspaceId}\0${row.postmanEnvironmentId}`));
134343
+ if (!postmanEnvironmentIds.every((uid) => seen.has(`${systemEnvironmentId}\0${groupedWorkspaceId}\0${uid}`))) {
134344
+ throw new Error("SYSTEM_ENV_ASSOCIATION_INCOMPLETE");
134345
+ }
134346
+ }
134347
+ }
134348
+ return;
134349
+ }
134142
134350
  const token = this.currentToken();
134143
134351
  const response = await this.fetchImpl(
134144
134352
  `${this.workerBaseUrl}/api/internal/system-envs/associate`,
@@ -134196,7 +134404,7 @@ var BifrostInternalIntegrationAdapter = class {
134196
134404
  try {
134197
134405
  const response = await this.fetchImpl(url, {
134198
134406
  method: "POST",
134199
- headers: this.bifrostHeaders(),
134407
+ headers: await this.bifrostHeaders(),
134200
134408
  body: JSON.stringify(payload)
134201
134409
  });
134202
134410
  let parsed = {};
@@ -134285,7 +134493,7 @@ var BifrostInternalIntegrationAdapter = class {
134285
134493
  versionControl: true
134286
134494
  }
134287
134495
  };
134288
- const headers = this.bifrostHeaders();
134496
+ const headers = await this.bifrostHeaders();
134289
134497
  const response = await this.fetchImpl(url, {
134290
134498
  method: "POST",
134291
134499
  headers,
@@ -134340,7 +134548,7 @@ var BifrostInternalIntegrationAdapter = class {
134340
134548
  try {
134341
134549
  const response = await this.fetchImpl(`${this.bifrostBaseUrl}/ws/proxy`, {
134342
134550
  method: "POST",
134343
- headers: this.bifrostHeaders(),
134551
+ headers: await this.bifrostHeaders(),
134344
134552
  body: JSON.stringify({
134345
134553
  service: "workspaces",
134346
134554
  method: "GET",
@@ -134371,7 +134579,7 @@ var BifrostInternalIntegrationAdapter = class {
134371
134579
  }
134372
134580
  async createApiKey(name) {
134373
134581
  const url = `${this.bifrostBaseUrl}/ws/proxy`;
134374
- const headers = this.bifrostHeaders();
134582
+ const headers = await this.bifrostHeaders();
134375
134583
  const payload = {
134376
134584
  service: "identity",
134377
134585
  method: "POST",
@@ -134456,6 +134664,16 @@ async function retry(operation, options = {}) {
134456
134664
  }
134457
134665
  throw new Error("Retry exhausted without returning or throwing");
134458
134666
  }
134667
+ function fullJitterDelayMs(attempt, baseMs, capMs, random = Math.random) {
134668
+ return Math.floor(random() * Math.max(0, Math.min(capMs, baseMs * 2 ** Math.max(0, attempt))));
134669
+ }
134670
+ function parseRetryAfterMs2(value) {
134671
+ const trimmed = value?.trim();
134672
+ if (!trimmed) return void 0;
134673
+ if (/^\d+$/.test(trimmed)) return Number(trimmed) * 1e3;
134674
+ const time = Date.parse(trimmed);
134675
+ return Number.isNaN(time) ? void 0 : Math.max(0, time - Date.now());
134676
+ }
134459
134677
 
134460
134678
  // src/contracts.ts
134461
134679
  var postmanRepoSyncActionContract = {
@@ -134514,6 +134732,11 @@ var postmanRepoSyncActionContract = {
134514
134732
  description: "Contract collection ID used for exported artifacts.",
134515
134733
  required: false
134516
134734
  },
134735
+ "prebuilt-collections-json": {
134736
+ description: "Optional digest-bound JSON manifest of unique baseline, smoke, or contract roles with confined repo-relative path, SHA-256 artifact digest of the on-disk v3 collection tree (sorted relative-path + NUL + bytes + NUL), and canonical cloud ID. The optional payloadDigest field is the semantic v2 payload digest carried for provenance (format-validated only, not the reuse gate). Exact role, path, cloudId, and artifactDigest matches reuse the on-disk tree without cloud export.",
134737
+ required: false,
134738
+ default: ""
134739
+ },
134517
134740
  "collection-sync-mode": {
134518
134741
  description: "Collection sync lifecycle mode (refresh or version).",
134519
134742
  required: false,
@@ -134756,7 +134979,7 @@ var postmanRepoSyncActionContract = {
134756
134979
  "Create or update Postman environments from runtime URLs.",
134757
134980
  "Associate Postman environments to system environments through Postman integration APIs.",
134758
134981
  "Create mock servers and smoke monitors from generated collections.",
134759
- "Export Postman collections in the Collection v3 multi-file YAML directory structure under `postman/collections/` (e.g., `<name>/collection.yaml`, nested folder and request YAML files), and export environments plus `.postman/resources.yaml` into the repository.",
134982
+ "Export Postman collections in the Collection v3 multi-file YAML directory structure under `postman/collections/` (canonical `.resources/definition.yaml` plus request/resource YAML files), and export environments plus `.postman/resources.yaml` into the repository.",
134760
134983
  "Link the Postman workspace to the repository (GitHub or GitLab) through Postman integration APIs.",
134761
134984
  "Commit synced artifacts and push them back to the current checked out ref."
134762
134985
  ],
@@ -134885,6 +135108,17 @@ var PostmanGatewayAssetsClient = class {
134885
135108
  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
135109
  }
134887
135110
  async getSpecContent(specId) {
135111
+ if (process.env.POSTMAN_SPEC_TREE_FAST_PATH !== "off") {
135112
+ try {
135113
+ const treeContent = await this.getSpecContentFromTree(specId);
135114
+ if (treeContent !== void 0) return treeContent;
135115
+ } catch (error) {
135116
+ if (!(error instanceof HttpError) || ![403, 404, 405, 501].includes(error.status)) throw error;
135117
+ }
135118
+ }
135119
+ return this.getSpecContentLegacy(specId);
135120
+ }
135121
+ async getSpecContentLegacy(specId) {
134888
135122
  const files = await this.gateway.requestJson({
134889
135123
  service: "specification",
134890
135124
  method: "get",
@@ -134903,6 +135137,46 @@ var PostmanGatewayAssetsClient = class {
134903
135137
  const record = this.dataOf(file);
134904
135138
  return typeof record?.content === "string" ? record.content : void 0;
134905
135139
  }
135140
+ /** Minimal `/tree` reader: validate the entire returned inventory before selecting ROOT. */
135141
+ async getSpecContentFromTree(specId) {
135142
+ const rows = [];
135143
+ const cursors = /* @__PURE__ */ new Set();
135144
+ let cursor;
135145
+ for (let page = 0; page < 100; page += 1) {
135146
+ const response = await this.gateway.requestJson({
135147
+ service: "specification",
135148
+ method: "get",
135149
+ path: `/specifications/${specId}/tree`,
135150
+ query: { fields: "id,name,type,path,parentId,fileType,content", limit: 100, ...cursor ? { cursor } : {} }
135151
+ });
135152
+ if (!Array.isArray(response?.data)) return void 0;
135153
+ for (const value of response.data) {
135154
+ const row = this.asRecord(value);
135155
+ if (!row || row.type === "FOLDER") continue;
135156
+ 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;
135157
+ const path5 = row.path.replace(/\\/g, "/").normalize("NFC");
135158
+ if (path5.startsWith("/") || path5.split("/").some((part) => part === ".." || !part)) throw new Error("CONTRACT_DEFINITION_PATH_INVALID");
135159
+ rows.push({ ...row, path: path5 });
135160
+ }
135161
+ const meta = this.asRecord(response?.meta);
135162
+ const cursorValue = this.asRecord(meta?.cursor)?.next;
135163
+ const next = typeof cursorValue === "string" && cursorValue.trim() ? cursorValue.trim() : void 0;
135164
+ if (!next) break;
135165
+ if (cursors.has(next)) throw new Error("SPEC_TREE_CURSOR_REPEATED");
135166
+ cursors.add(next);
135167
+ if (page === 99) throw new Error("SPEC_TREE_PAGE_LIMIT_EXCEEDED");
135168
+ cursor = next;
135169
+ }
135170
+ const paths = /* @__PURE__ */ new Set();
135171
+ for (const row of rows) {
135172
+ const path5 = String(row.path).toLocaleLowerCase();
135173
+ if (paths.has(path5)) throw new Error("CONTRACT_DEFINITION_DUPLICATE_PATH");
135174
+ paths.add(path5);
135175
+ }
135176
+ const roots = rows.filter((row) => row.fileType === "ROOT");
135177
+ if (roots.length !== 1) throw new Error("CONTRACT_DEFINITION_INVENTORY_INVALID");
135178
+ return String(roots[0]?.content);
135179
+ }
134906
135180
  async listSpecCollections(specId) {
134907
135181
  const response = await this.gateway.requestJson({
134908
135182
  service: "specification",
@@ -135462,10 +135736,12 @@ var PostmanGatewayAssetsClient = class {
135462
135736
  // src/lib/postman/token-provider.ts
135463
135737
  var MintError = class extends Error {
135464
135738
  permanent;
135465
- constructor(message, permanent) {
135739
+ status;
135740
+ constructor(message, permanent, status) {
135466
135741
  super(message);
135467
135742
  this.name = "MintError";
135468
135743
  this.permanent = permanent;
135744
+ this.status = status;
135469
135745
  }
135470
135746
  };
135471
135747
  function extractAccessToken(payload) {
@@ -135488,6 +135764,7 @@ var AccessTokenProvider = class {
135488
135764
  maxAttempts;
135489
135765
  onToken;
135490
135766
  sleep;
135767
+ onRetryEvent;
135491
135768
  inflight;
135492
135769
  constructor(options) {
135493
135770
  this.token = String(options.accessToken || "").trim();
@@ -135499,6 +135776,7 @@ var AccessTokenProvider = class {
135499
135776
  this.maxAttempts = Math.max(1, options.maxAttempts ?? 2);
135500
135777
  this.onToken = options.onToken;
135501
135778
  this.sleep = options.sleep;
135779
+ this.onRetryEvent = options.onRetryEvent;
135502
135780
  }
135503
135781
  current() {
135504
135782
  return this.token;
@@ -135524,7 +135802,12 @@ var AccessTokenProvider = class {
135524
135802
  delayMs: 1e3,
135525
135803
  backoffMultiplier: 2,
135526
135804
  ...this.sleep ? { sleep: this.sleep } : {},
135527
- shouldRetry: (error) => !(error instanceof MintError && error.permanent)
135805
+ shouldRetry: (error) => !(error instanceof MintError && error.permanent),
135806
+ onRetry: ({ attempt, delayMs, error }) => {
135807
+ const status = error instanceof MintError ? error.status : void 0;
135808
+ const retryClass = status === void 0 ? "transport" : "http";
135809
+ this.onRetryEvent?.({ class: retryClass, ...status === void 0 ? {} : { status }, attempt, delay: Math.max(0, delayMs) });
135810
+ }
135528
135811
  });
135529
135812
  this.token = token;
135530
135813
  this.onToken?.(token);
@@ -135545,7 +135828,8 @@ var AccessTokenProvider = class {
135545
135828
  if (status === 401 || status === 403) {
135546
135829
  throw new MintError(
135547
135830
  `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
135831
+ true,
135832
+ status
135549
135833
  );
135550
135834
  }
135551
135835
  if (status === 400 && body.toLowerCase().includes("service accounts not enabled")) {
@@ -135554,7 +135838,7 @@ var AccessTokenProvider = class {
135554
135838
  true
135555
135839
  );
135556
135840
  }
135557
- throw new MintError(`postman: re-mint failed (service-account-tokens HTTP ${status}).`, false);
135841
+ throw new MintError(`postman: re-mint failed (service-account-tokens HTTP ${status}).`, false, status);
135558
135842
  }
135559
135843
  let parsed;
135560
135844
  try {
@@ -135570,26 +135854,9 @@ var AccessTokenProvider = class {
135570
135854
  }
135571
135855
  };
135572
135856
  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
- }
135857
+ const raw = maskPmakDiagnostic(mintError instanceof Error ? mintError.message : String(mintError), [apiKey]);
135858
+ if (!(mintError instanceof MintError) || mintError.status !== 401 && mintError.status !== 403) return raw;
135859
+ return formatRejectedMint(raw, await inspectPmakIdentity({ apiBaseUrl, apiKey, fetchImpl }));
135593
135860
  }
135594
135861
  async function mintAccessTokenIfNeeded(inputs, log, setSecret2, fetchImpl = fetch) {
135595
135862
  if (inputs.postmanAccessToken || !inputs.postmanApiKey) {
@@ -135602,7 +135869,8 @@ async function mintAccessTokenIfNeeded(inputs, log, setSecret2, fetchImpl = fetc
135602
135869
  apiKey: inputs.postmanApiKey,
135603
135870
  apiBaseUrl,
135604
135871
  fetchImpl,
135605
- onToken: (token) => setSecret2?.(token)
135872
+ onToken: (token) => setSecret2?.(token),
135873
+ onRetryEvent: (event) => log.retryEvent?.(event)
135606
135874
  });
135607
135875
  try {
135608
135876
  inputs.postmanAccessToken = await provider.refresh();
@@ -135638,6 +135906,11 @@ var AccessTokenGatewayClient = class {
135638
135906
  fallbackBaseUrl;
135639
135907
  retryBaseDelayMs;
135640
135908
  sleepImpl;
135909
+ appVersionProvider;
135910
+ requestTimeoutMs;
135911
+ retryMaxDelayMs;
135912
+ randomImpl;
135913
+ onRetryEvent;
135641
135914
  constructor(options) {
135642
135915
  this.tokenProvider = options.tokenProvider;
135643
135916
  this.bifrostBaseUrl = String(
@@ -135652,16 +135925,23 @@ var AccessTokenGatewayClient = class {
135652
135925
  this.fallbackBaseUrl = fallbackEnv === "off" ? void 0 : options.fallbackBaseUrl?.replace(/\/+$/, "");
135653
135926
  this.retryBaseDelayMs = options.retryBaseDelayMs ?? 400;
135654
135927
  this.sleepImpl = options.sleepImpl ?? defaultSleep;
135928
+ this.appVersionProvider = options.appVersionProvider ?? defaultPostmanAppVersionProvider;
135929
+ this.requestTimeoutMs = options.requestTimeoutMs ?? 3e4;
135930
+ this.retryMaxDelayMs = options.retryMaxDelayMs ?? 5e3;
135931
+ this.randomImpl = options.randomImpl ?? Math.random;
135932
+ this.onRetryEvent = options.onRetryEvent;
135655
135933
  }
135656
135934
  configureTeamContext(teamId, orgMode) {
135657
135935
  this.teamId = String(teamId || "").trim();
135658
135936
  this.orgMode = orgMode;
135659
135937
  }
135660
- buildHeaders(extra) {
135938
+ async buildHeaders(extra) {
135939
+ const appVersion = await this.appVersionProvider.get();
135661
135940
  const headers = {
135662
135941
  "Content-Type": "application/json",
135663
135942
  "x-access-token": this.tokenProvider.current(),
135664
- ...extra || {}
135943
+ ...extra || {},
135944
+ ...appVersion ? { "x-app-version": appVersion } : {}
135665
135945
  };
135666
135946
  if (this.teamId && this.orgMode) {
135667
135947
  headers["x-entity-team-id"] = this.teamId;
@@ -135670,17 +135950,24 @@ var AccessTokenGatewayClient = class {
135670
135950
  }
135671
135951
  async send(request, baseUrl) {
135672
135952
  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
- });
135953
+ const controller = new AbortController();
135954
+ const timer = setTimeout(() => controller.abort(), this.requestTimeoutMs);
135955
+ try {
135956
+ return await this.fetchImpl(url, {
135957
+ method: "POST",
135958
+ headers: await this.buildHeaders(request.headers),
135959
+ signal: controller.signal,
135960
+ body: JSON.stringify({
135961
+ service: request.service,
135962
+ method: request.method,
135963
+ path: request.path,
135964
+ ...request.query !== void 0 ? { query: request.query } : {},
135965
+ ...request.body !== void 0 ? { body: request.body } : {}
135966
+ })
135967
+ });
135968
+ } finally {
135969
+ clearTimeout(timer);
135970
+ }
135684
135971
  }
135685
135972
  /**
135686
135973
  * One cold, serial attempt against the fallback base URL after the primary
@@ -135707,11 +135994,13 @@ var AccessTokenGatewayClient = class {
135707
135994
  * they are the freshest authoritative answer.
135708
135995
  */
135709
135996
  fallbackEligible(request, retryTransient) {
135997
+ if (request.fallback === "none") return false;
135710
135998
  if (!this.fallbackBaseUrl) return false;
135711
135999
  return retryTransient || request.fallback === "auto";
135712
136000
  }
135713
- async attemptFallback(request, retryTransient) {
136001
+ async attemptFallback(request, retryTransient, status) {
135714
136002
  if (!this.fallbackEligible(request, retryTransient)) return null;
136003
+ this.emitRetryEvent("fallback", status, 1, 0);
135715
136004
  const response = await this.tryFallback(request);
135716
136005
  if (!response) return null;
135717
136006
  if (response.ok) return response;
@@ -135736,7 +136025,8 @@ var AccessTokenGatewayClient = class {
135736
136025
  response = await this.send(request);
135737
136026
  } catch (error) {
135738
136027
  if (retryTransient && attempt < this.maxRetries) {
135739
- const delay = this.retryBaseDelayMs * 2 ** attempt;
136028
+ const delay = this.retryDelayMs(attempt);
136029
+ this.emitRetryEvent("transport", void 0, attempt + 1, delay);
135740
136030
  attempt += 1;
135741
136031
  await this.sleepImpl(delay);
135742
136032
  continue;
@@ -135746,10 +136036,23 @@ var AccessTokenGatewayClient = class {
135746
136036
  throw error;
135747
136037
  }
135748
136038
  if (response.ok) {
135749
- return response;
136039
+ const okBody = await response.text().catch(() => "");
136040
+ const inner = this.innerStatus(okBody);
136041
+ if (inner !== void 0) {
136042
+ if (retryTransient && this.isTransient(inner, okBody) && attempt < this.maxRetries) {
136043
+ const delay = this.retryDelayMs(attempt);
136044
+ this.emitRetryEvent("inner", inner, attempt + 1, delay);
136045
+ await this.sleepImpl(delay);
136046
+ attempt += 1;
136047
+ continue;
136048
+ }
136049
+ throw this.toInnerHttpError(request, inner, okBody);
136050
+ }
136051
+ return this.rebuildResponse(response, okBody);
135750
136052
  }
135751
136053
  const body = await response.text().catch(() => "");
135752
136054
  if (isExpiredAuthError(response.status, body) && this.tokenProvider.canRefresh()) {
136055
+ this.emitRetryEvent("auth", response.status, 1, 0);
135753
136056
  await this.tokenProvider.refresh();
135754
136057
  response = await this.send(request);
135755
136058
  if (response.ok) {
@@ -135758,17 +136061,44 @@ var AccessTokenGatewayClient = class {
135758
136061
  const retryBody = await response.text().catch(() => "");
135759
136062
  throw this.toHttpError(request, response, retryBody);
135760
136063
  }
135761
- if (retryTransient && isRetryableSafeReadResponse(response.status) && attempt < this.maxRetries) {
135762
- const delay = this.retryBaseDelayMs * 2 ** attempt;
136064
+ if (retryTransient && this.isTransient(response.status, body) && attempt < this.maxRetries) {
136065
+ const delay = this.retryDelayMs(attempt, parseRetryAfterMs2(response.headers.get("retry-after")));
136066
+ this.emitRetryEvent("http", response.status, attempt + 1, delay);
135763
136067
  attempt += 1;
135764
136068
  await this.sleepImpl(delay);
135765
136069
  continue;
135766
136070
  }
135767
- const fallbackResponse = await this.attemptFallback(request, retryTransient);
136071
+ const fallbackResponse = await this.attemptFallback(request, retryTransient, response.status);
135768
136072
  if (fallbackResponse) return fallbackResponse;
135769
136073
  throw this.toHttpError(request, response, body);
135770
136074
  }
135771
136075
  }
136076
+ retryDelayMs(attempt, retryAfter) {
136077
+ return retryAfter === void 0 ? fullJitterDelayMs(attempt, this.retryBaseDelayMs, this.retryMaxDelayMs, this.randomImpl) : Math.min(this.retryMaxDelayMs, retryAfter);
136078
+ }
136079
+ emitRetryEvent(retryClass, status, attempt, delay) {
136080
+ this.onRetryEvent?.({
136081
+ class: retryClass,
136082
+ ...status === void 0 ? {} : { status },
136083
+ attempt: Math.max(1, attempt),
136084
+ delay: Math.max(0, delay)
136085
+ });
136086
+ }
136087
+ isTransient(status, body) {
136088
+ return status === 408 || status === 429 || status >= 500 || /ESOCKETTIMEDOUT|ETIMEDOUT|ECONNRESET|serverError|downstream/.test(body);
136089
+ }
136090
+ innerStatus(body) {
136091
+ try {
136092
+ const value = JSON.parse(body);
136093
+ const status = Number(value.status ?? value.statusCode);
136094
+ return value.error || value.success === false || status >= 400 ? status >= 400 ? status : 502 : void 0;
136095
+ } catch {
136096
+ return void 0;
136097
+ }
136098
+ }
136099
+ rebuildResponse(response, body) {
136100
+ return new Response([204, 205, 304].includes(response.status) ? null : body, { status: response.status, statusText: response.statusText, headers: response.headers });
136101
+ }
135772
136102
  /** Send a gateway request and parse the JSON body, or null when empty. */
135773
136103
  async requestJson(request, options = {}) {
135774
136104
  const response = await this.request(request, options);
@@ -135815,15 +136145,23 @@ var AccessTokenGatewayClient = class {
135815
136145
  url: `${this.bifrostBaseUrl}/ws/proxy (${request.service}: ${request.method} ${request.path})`,
135816
136146
  status: response.status,
135817
136147
  statusText: response.statusText,
135818
- requestHeaders: this.buildHeaders(request.headers),
136148
+ requestHeaders: {
136149
+ "Content-Type": "application/json",
136150
+ "x-access-token": this.tokenProvider.current(),
136151
+ ...request.headers || {},
136152
+ ...this.teamId && this.orgMode ? { "x-entity-team-id": this.teamId } : {}
136153
+ },
135819
136154
  responseBody: this.secretMasker(body),
135820
136155
  secretValues: [this.tokenProvider.current()]
135821
136156
  });
135822
136157
  }
136158
+ toInnerHttpError(request, status, body) {
136159
+ 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()] });
136160
+ }
135823
136161
  };
135824
136162
 
135825
136163
  // src/lib/repo/branch-decision.ts
135826
- var import_node_fs3 = require("node:fs");
136164
+ var import_node_fs4 = require("node:fs");
135827
136165
  var import_node_crypto2 = require("node:crypto");
135828
136166
  var ContractError = class extends Error {
135829
136167
  code;
@@ -135864,7 +136202,7 @@ function readGithubEvent(env) {
135864
136202
  const path5 = clean(env.GITHUB_EVENT_PATH);
135865
136203
  if (!path5) return void 0;
135866
136204
  try {
135867
- return JSON.parse((0, import_node_fs3.readFileSync)(path5, "utf8"));
136205
+ return JSON.parse((0, import_node_fs4.readFileSync)(path5, "utf8"));
135868
136206
  } catch {
135869
136207
  return void 0;
135870
136208
  }
@@ -136317,6 +136655,7 @@ function resolveInputs(env = process.env) {
136317
136655
  baselineCollectionId: getInput("baseline-collection-id", env),
136318
136656
  smokeCollectionId: getInput("smoke-collection-id", env),
136319
136657
  contractCollectionId: getInput("contract-collection-id", env),
136658
+ prebuiltCollectionsJson: getInput("prebuilt-collections-json", env),
136320
136659
  specId: getInput("spec-id", env),
136321
136660
  specContentChanged: parseBooleanInput(getInput("spec-content-changed", env), true),
136322
136661
  specPath: getInput("spec-path", env),
@@ -136432,7 +136771,7 @@ var StateUnreadableError = class extends Error {
136432
136771
  function readResourcesState() {
136433
136772
  let raw;
136434
136773
  try {
136435
- raw = (0, import_node_fs4.readFileSync)(".postman/resources.yaml", "utf8");
136774
+ raw = (0, import_node_fs5.readFileSync)(".postman/resources.yaml", "utf8");
136436
136775
  } catch {
136437
136776
  return null;
136438
136777
  }
@@ -136491,12 +136830,15 @@ function getEnvironmentUidsFromResources(resourcesState) {
136491
136830
  function normalizeToPosix(filePath) {
136492
136831
  return filePath.split(path3.sep).join("/").replace(/\\/g, "/");
136493
136832
  }
136833
+ function canonicalizeRelativePath(value) {
136834
+ return normalizeToPosix(String(value ?? "").trim()).replace(/^\.\/+/, "").replace(/\/{2,}/g, "/").replace(/\/+$/g, "");
136835
+ }
136494
136836
  function isOpenApiSpecFile(filePath) {
136495
136837
  if (!(filePath.endsWith(".json") || filePath.endsWith(".yaml") || filePath.endsWith(".yml"))) {
136496
136838
  return false;
136497
136839
  }
136498
136840
  try {
136499
- const raw = (0, import_node_fs4.readFileSync)(filePath, "utf8");
136841
+ const raw = (0, import_node_fs5.readFileSync)(filePath, "utf8");
136500
136842
  const parsed = filePath.endsWith(".json") ? JSON.parse(raw) : load(raw);
136501
136843
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
136502
136844
  return false;
@@ -136525,7 +136867,7 @@ function scanLocalSpecReferences(baseDir = ".") {
136525
136867
  const found = /* @__PURE__ */ new Set();
136526
136868
  const refs = [];
136527
136869
  const visit2 = (currentDir) => {
136528
- for (const entry of (0, import_node_fs4.readdirSync)(currentDir, { withFileTypes: true })) {
136870
+ for (const entry of (0, import_node_fs5.readdirSync)(currentDir, { withFileTypes: true })) {
136529
136871
  if (ignoredDirs.has(entry.name)) {
136530
136872
  continue;
136531
136873
  }
@@ -136555,7 +136897,7 @@ function resolveMappedSpecReference(explicitSpecPath, discoveredSpecs) {
136555
136897
  const normalizedExplicitPath = normalizeToPosix(explicitSpecPath.trim());
136556
136898
  if (normalizedExplicitPath) {
136557
136899
  const explicitFullPath = path3.resolve(normalizedExplicitPath);
136558
- if ((0, import_node_fs4.existsSync)(explicitFullPath) && (0, import_node_fs4.statSync)(explicitFullPath).isFile()) {
136900
+ if ((0, import_node_fs5.existsSync)(explicitFullPath) && (0, import_node_fs5.statSync)(explicitFullPath).isFile()) {
136559
136901
  return {
136560
136902
  repoRelativePath: normalizedExplicitPath,
136561
136903
  configRelativePath: normalizeToPosix(path3.join("..", normalizedExplicitPath))
@@ -136681,6 +137023,7 @@ async function upsertEnvironments(inputs, dependencies, resourcesState, assetMar
136681
137023
  );
136682
137024
  }
136683
137025
  envUids[envName] = existingUid;
137026
+ dependencies.core.setOutput("environment-uids-json", JSON.stringify(envUids));
136684
137027
  continue;
136685
137028
  }
136686
137029
  const values = buildEnvironmentValues(envName, runtimeUrl);
@@ -136691,6 +137034,7 @@ async function upsertEnvironments(inputs, dependencies, resourcesState, assetMar
136691
137034
  displayName,
136692
137035
  values
136693
137036
  );
137037
+ dependencies.core.setOutput("environment-uids-json", JSON.stringify(envUids));
136694
137038
  } catch (error) {
136695
137039
  throw new Error(
136696
137040
  formatOrchestrationIssue({
@@ -136707,7 +137051,7 @@ async function upsertEnvironments(inputs, dependencies, resourcesState, assetMar
136707
137051
  return envUids;
136708
137052
  }
136709
137053
  function ensureDir(path5) {
136710
- (0, import_node_fs4.mkdirSync)(path5, { recursive: true });
137054
+ (0, import_node_fs5.mkdirSync)(path5, { recursive: true });
136711
137055
  }
136712
137056
  function getCollectionDirectoryName(kind, projectName) {
136713
137057
  if (kind === "Baseline") {
@@ -136742,9 +137086,9 @@ function stripVolatileFields(obj) {
136742
137086
  }
136743
137087
  return obj;
136744
137088
  }
136745
- function writeJsonFile(path5, content, normalize3 = false) {
136746
- const data = normalize3 ? stripVolatileFields(content) : content;
136747
- (0, import_node_fs4.writeFileSync)(path5, JSON.stringify(data, null, 2));
137089
+ function writeJsonFile(path5, content, normalize4 = false) {
137090
+ const data = normalize4 ? stripVolatileFields(content) : content;
137091
+ (0, import_node_fs5.writeFileSync)(path5, JSON.stringify(data, null, 2));
136748
137092
  }
136749
137093
  function buildMappedSpecCloudKey(mappedSource, specSyncMode, releaseLabel) {
136750
137094
  if (specSyncMode !== "version") {
@@ -136808,22 +137152,422 @@ function buildResourcesManifest(workspaceId, collectionMap, envMap, artifactDir,
136808
137152
  sortKeys: false
136809
137153
  });
136810
137154
  }
136811
- function buildSpecCollectionWorkflowManifest(specRef, collectionRefs) {
136812
- return dump(
136813
- {
136814
- workflows: {
136815
- syncSpecToCollection: collectionRefs.map((collectionRef) => ({
136816
- spec: specRef,
136817
- collection: collectionRef
136818
- }))
137155
+ function isPlainObject2(value) {
137156
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
137157
+ }
137158
+ function buildSpecCollectionWorkflowManifest(specRef, collectionRefs, existingRaw) {
137159
+ const pairs = collectionRefs.map((collectionRef) => ({
137160
+ spec: specRef,
137161
+ collection: collectionRef
137162
+ }));
137163
+ let root = {};
137164
+ if (typeof existingRaw === "string" && existingRaw.trim()) {
137165
+ let parsed;
137166
+ try {
137167
+ parsed = load(existingRaw);
137168
+ } catch (error) {
137169
+ throw new Error(
137170
+ `CONTRACT_WORKFLOWS_UNREADABLE: .postman/workflows.yaml exists but is not parseable YAML (${error instanceof Error ? error.message : String(error)}). Fix or delete the file; refusing to overwrite unrelated workflow data.`,
137171
+ { cause: error }
137172
+ );
137173
+ }
137174
+ if (parsed === null || parsed === void 0) {
137175
+ root = {};
137176
+ } else if (!isPlainObject2(parsed)) {
137177
+ throw new Error(
137178
+ "CONTRACT_WORKFLOWS_UNREADABLE: .postman/workflows.yaml exists but does not contain a YAML mapping. Fix or delete the file; refusing to overwrite unrelated workflow data."
137179
+ );
137180
+ } else {
137181
+ root = { ...parsed };
137182
+ }
137183
+ }
137184
+ const workflows = isPlainObject2(root.workflows) ? { ...root.workflows } : {};
137185
+ const desiredCollections = new Set(collectionRefs);
137186
+ const currentPairs = (Array.isArray(workflows.syncSpecToCollection) ? workflows.syncSpecToCollection : []).map((entry) => isPlainObject2(entry) ? { ...entry } : null).filter((entry) => Boolean(entry)).filter(
137187
+ (entry) => String(entry.spec ?? "") !== specRef || desiredCollections.has(String(entry.collection ?? ""))
137188
+ );
137189
+ for (const pair of pairs) {
137190
+ const index = currentPairs.findIndex(
137191
+ (entry) => String(entry.spec ?? "") === pair.spec && String(entry.collection ?? "") === pair.collection
137192
+ );
137193
+ if (index >= 0) {
137194
+ const previous = currentPairs[index];
137195
+ currentPairs[index] = { ...previous, spec: pair.spec, collection: pair.collection };
137196
+ } else {
137197
+ currentPairs.push({ spec: pair.spec, collection: pair.collection });
137198
+ }
137199
+ }
137200
+ workflows.syncSpecToCollection = currentPairs;
137201
+ root.workflows = workflows;
137202
+ return dump(root, {
137203
+ lineWidth: -1,
137204
+ noRefs: true,
137205
+ sortKeys: false
137206
+ });
137207
+ }
137208
+ var PREBUILT_COLLECTION_ROLES = /* @__PURE__ */ new Set([
137209
+ "baseline",
137210
+ "smoke",
137211
+ "contract"
137212
+ ]);
137213
+ var SHA256_HEX = /^[a-f0-9]{64}$/;
137214
+ var PREBUILT_CLOUD_ID = /^[A-Za-z0-9][A-Za-z0-9._:-]*$/;
137215
+ function failPrebuiltCollections(detail) {
137216
+ throw new Error(`CONTRACT_PREBUILT_COLLECTIONS_INVALID: ${detail}`);
137217
+ }
137218
+ function parsePrebuiltCollectionsJson(raw) {
137219
+ const trimmed = String(raw ?? "").trim();
137220
+ if (!trimmed) {
137221
+ return [];
137222
+ }
137223
+ let parsed;
137224
+ try {
137225
+ parsed = JSON.parse(trimmed);
137226
+ } catch (error) {
137227
+ throw new Error(
137228
+ `CONTRACT_PREBUILT_COLLECTIONS_INVALID: malformed JSON (${error instanceof Error ? error.message : String(error)})`,
137229
+ { cause: error }
137230
+ );
137231
+ }
137232
+ let entriesRaw;
137233
+ if (Array.isArray(parsed)) {
137234
+ entriesRaw = parsed;
137235
+ } else if (isPlainObject2(parsed)) {
137236
+ if (parsed.schemaVersion !== 1) {
137237
+ failPrebuiltCollections("schemaVersion must be 1");
137238
+ }
137239
+ if (!Array.isArray(parsed.collections)) {
137240
+ failPrebuiltCollections("collections must be an array when schemaVersion is set");
137241
+ }
137242
+ entriesRaw = parsed.collections;
137243
+ } else {
137244
+ failPrebuiltCollections("expected a JSON array or {schemaVersion:1,collections:[]}");
137245
+ }
137246
+ const seenRoles = /* @__PURE__ */ new Set();
137247
+ const entries = [];
137248
+ for (const [index, value] of entriesRaw.entries()) {
137249
+ if (!isPlainObject2(value)) {
137250
+ failPrebuiltCollections(`collections[${index}] must be an object`);
137251
+ }
137252
+ const roleRaw = String(value.role ?? "").trim();
137253
+ if (!PREBUILT_COLLECTION_ROLES.has(roleRaw)) {
137254
+ failPrebuiltCollections(
137255
+ `collections[${index}].role must be one of baseline|smoke|contract`
137256
+ );
137257
+ }
137258
+ const role = roleRaw;
137259
+ if (seenRoles.has(role)) {
137260
+ failPrebuiltCollections(`duplicate role ${role}`);
137261
+ }
137262
+ seenRoles.add(role);
137263
+ const collectionPath = String(
137264
+ value.collectionPath ?? value.path ?? ""
137265
+ ).trim();
137266
+ if (!collectionPath || hasControlCharacter2(collectionPath)) {
137267
+ failPrebuiltCollections(
137268
+ `collections[${index}].collectionPath must be a non-empty confined relative path`
137269
+ );
137270
+ }
137271
+ const cloudId = String(value.cloudId ?? "").trim();
137272
+ if (!cloudId || !PREBUILT_CLOUD_ID.test(cloudId)) {
137273
+ failPrebuiltCollections(
137274
+ `collections[${index}].cloudId must be a non-empty Postman collection id`
137275
+ );
137276
+ }
137277
+ let payloadDigest;
137278
+ if (Object.prototype.hasOwnProperty.call(value, "payloadDigest")) {
137279
+ const payloadDigestRaw = value.payloadDigest;
137280
+ if (payloadDigestRaw !== void 0 && payloadDigestRaw !== null) {
137281
+ payloadDigest = String(payloadDigestRaw).trim();
137282
+ if (!SHA256_HEX.test(payloadDigest)) {
137283
+ failPrebuiltCollections(
137284
+ `collections[${index}].payloadDigest must be lowercase 64-hex`
137285
+ );
137286
+ }
136819
137287
  }
136820
- },
136821
- {
136822
- lineWidth: -1,
136823
- noRefs: true,
136824
- sortKeys: false
136825
137288
  }
137289
+ const artifactDigest = String(value.artifactDigest ?? "").trim();
137290
+ if (!SHA256_HEX.test(artifactDigest)) {
137291
+ failPrebuiltCollections(
137292
+ `collections[${index}].artifactDigest must be lowercase 64-hex`
137293
+ );
137294
+ }
137295
+ entries.push({
137296
+ role,
137297
+ collectionPath,
137298
+ cloudId,
137299
+ ...payloadDigest !== void 0 ? { payloadDigest } : {},
137300
+ artifactDigest
137301
+ });
137302
+ }
137303
+ return entries;
137304
+ }
137305
+ function assertPathWithinArtifactRoot(targetPath, artifactDir, fieldName) {
137306
+ assertPathWithinCwd(targetPath, fieldName);
137307
+ assertPathWithinCwd(artifactDir, "artifact-dir");
137308
+ const cwd = (0, import_node_fs5.realpathSync)(process.cwd());
137309
+ const artifactRoot = path3.resolve(cwd, artifactDir.trim());
137310
+ const resolved = path3.resolve(cwd, targetPath.trim());
137311
+ const relativeToArtifact = path3.relative(artifactRoot, resolved);
137312
+ if (!relativeToArtifact || relativeToArtifact.startsWith("..") || path3.isAbsolute(relativeToArtifact)) {
137313
+ failPrebuiltCollections(
137314
+ `${fieldName} must stay under artifact-dir (${artifactDir}); received ${targetPath}`
137315
+ );
137316
+ }
137317
+ let existingPath = resolved;
137318
+ while (!(0, import_node_fs5.existsSync)(existingPath)) {
137319
+ const parent = path3.dirname(existingPath);
137320
+ if (parent === existingPath) {
137321
+ break;
137322
+ }
137323
+ existingPath = parent;
137324
+ }
137325
+ const realExisting = (0, import_node_fs5.realpathSync)(existingPath);
137326
+ const realRelative = path3.relative(artifactRoot, realExisting);
137327
+ if (realRelative.startsWith("..") || path3.isAbsolute(realRelative)) {
137328
+ failPrebuiltCollections(
137329
+ `${fieldName} resolves outside artifact-dir via symlink; received ${targetPath}`
137330
+ );
137331
+ }
137332
+ return normalizeToPosix(path3.relative(cwd, resolved));
137333
+ }
137334
+ function prebuiltDirectoryTraversalIdentity(absolutePath, stats, options = {}) {
137335
+ const platform2 = options.platform ?? process.platform;
137336
+ const inodeIsZero = stats.ino === 0 || stats.ino === 0n;
137337
+ if (platform2 !== "win32" && !inodeIsZero) {
137338
+ return `${stats.dev}:${stats.ino}`;
137339
+ }
137340
+ const resolveCanonicalPath = options.resolveCanonicalPath ?? ((candidate) => import_node_fs5.realpathSync.native(candidate));
137341
+ const canonical = resolveCanonicalPath(absolutePath);
137342
+ return platform2 === "win32" ? canonical.toLowerCase() : canonical;
137343
+ }
137344
+ function listPrebuiltCollectionTreeFiles(collectionPath, artifactDir) {
137345
+ const confined = assertPathWithinArtifactRoot(
137346
+ collectionPath,
137347
+ artifactDir,
137348
+ "prebuilt collection path"
136826
137349
  );
137350
+ const absRoot = path3.resolve(process.cwd(), confined);
137351
+ if (!(0, import_node_fs5.existsSync)(absRoot)) {
137352
+ return [];
137353
+ }
137354
+ let rootStat;
137355
+ try {
137356
+ rootStat = (0, import_node_fs5.lstatSync)(absRoot);
137357
+ } catch {
137358
+ return [];
137359
+ }
137360
+ if (rootStat.isSymbolicLink()) {
137361
+ failPrebuiltCollections(
137362
+ `prebuilt collection path must not be a symlink; received ${collectionPath}`
137363
+ );
137364
+ }
137365
+ if (!rootStat.isDirectory()) {
137366
+ return [];
137367
+ }
137368
+ const files = [];
137369
+ const pendingDirectories = [absRoot];
137370
+ const seenDirectories = /* @__PURE__ */ new Set();
137371
+ while (pendingDirectories.length > 0) {
137372
+ const currentAbsolute = pendingDirectories.pop();
137373
+ if (!currentAbsolute) {
137374
+ break;
137375
+ }
137376
+ const currentStat = (0, import_node_fs5.lstatSync)(currentAbsolute);
137377
+ if (currentStat.isSymbolicLink() || !currentStat.isDirectory()) {
137378
+ failPrebuiltCollections(
137379
+ `prebuilt collection tree directory changed or became unsupported at ${normalizeToPosix(path3.relative(absRoot, currentAbsolute)) || "."}`
137380
+ );
137381
+ }
137382
+ const currentKey = prebuiltDirectoryTraversalIdentity(currentAbsolute, currentStat);
137383
+ if (seenDirectories.has(currentKey)) {
137384
+ failPrebuiltCollections(
137385
+ `prebuilt collection tree directory cycle detected at ${normalizeToPosix(path3.relative(absRoot, currentAbsolute)) || "."}`
137386
+ );
137387
+ }
137388
+ seenDirectories.add(currentKey);
137389
+ const entries = (0, import_node_fs5.readdirSync)(currentAbsolute, { withFileTypes: true });
137390
+ for (const entry of entries) {
137391
+ const abs = path3.join(currentAbsolute, entry.name);
137392
+ if (entry.isSymbolicLink()) {
137393
+ failPrebuiltCollections(
137394
+ `prebuilt collection tree must not contain symlinks; received ${normalizeToPosix(path3.relative(absRoot, abs))}`
137395
+ );
137396
+ }
137397
+ if (entry.isDirectory()) {
137398
+ pendingDirectories.push(abs);
137399
+ continue;
137400
+ }
137401
+ if (!entry.isFile()) {
137402
+ failPrebuiltCollections(
137403
+ `prebuilt collection tree contains unsupported entry type at ${normalizeToPosix(path3.relative(absRoot, abs))}`
137404
+ );
137405
+ }
137406
+ const fileLstat = (0, import_node_fs5.lstatSync)(abs);
137407
+ if (fileLstat.isSymbolicLink() || !fileLstat.isFile()) {
137408
+ failPrebuiltCollections(
137409
+ `prebuilt collection tree file changed or became unsupported at ${normalizeToPosix(path3.relative(absRoot, abs))}`
137410
+ );
137411
+ }
137412
+ files.push({
137413
+ absolute: abs,
137414
+ relative: normalizeToPosix(path3.relative(absRoot, abs)),
137415
+ dev: fileLstat.dev,
137416
+ ino: fileLstat.ino,
137417
+ size: fileLstat.size
137418
+ });
137419
+ }
137420
+ }
137421
+ files.sort((a, b) => a.relative.localeCompare(b.relative));
137422
+ return files;
137423
+ }
137424
+ function validateCanonicalV3CollectionFile(relative3, bytes) {
137425
+ let expectedKind;
137426
+ if (/(^|\/)\.resources\/definition\.yaml$/.test(relative3)) {
137427
+ expectedKind = "collection";
137428
+ } else if (relative3.endsWith(".request.yaml")) {
137429
+ expectedKind = "request";
137430
+ } else if (relative3.endsWith(".example.yaml")) {
137431
+ expectedKind = "example";
137432
+ } else if (relative3.endsWith(".message.yaml")) {
137433
+ expectedKind = "message";
137434
+ } else {
137435
+ failPrebuiltCollections(`prebuilt collection tree contains unexpected file ${relative3}`);
137436
+ }
137437
+ let parsed;
137438
+ try {
137439
+ parsed = load(bytes.toString("utf8"));
137440
+ } catch (error) {
137441
+ failPrebuiltCollections(
137442
+ `prebuilt collection tree contains malformed YAML at ${relative3} (${error instanceof Error ? error.message : String(error)})`
137443
+ );
137444
+ }
137445
+ if (!isPlainObject2(parsed)) {
137446
+ failPrebuiltCollections(`prebuilt collection tree file ${relative3} must contain a YAML mapping`);
137447
+ }
137448
+ const kind = typeof parsed.$kind === "string" ? parsed.$kind.trim() : "";
137449
+ if (!kind) {
137450
+ failPrebuiltCollections(`prebuilt collection tree file ${relative3} must contain a nonempty $kind`);
137451
+ }
137452
+ const agrees = expectedKind === "collection" ? kind === "collection" : kind.endsWith(`-${expectedKind}`);
137453
+ if (!agrees) {
137454
+ failPrebuiltCollections(
137455
+ `prebuilt collection tree file ${relative3} has $kind ${kind} inconsistent with its filename`
137456
+ );
137457
+ }
137458
+ }
137459
+ async function digestAndValidatePrebuiltCollectionTree(files) {
137460
+ if (!files.some((file) => file.relative === ".resources/definition.yaml")) {
137461
+ failPrebuiltCollections("prebuilt collection tree is missing .resources/definition.yaml");
137462
+ }
137463
+ const hash = (0, import_node_crypto3.createHash)("sha256");
137464
+ for (const file of files) {
137465
+ let bytes;
137466
+ try {
137467
+ bytes = await appendArtifactDigestFileStreaming(hash, file.relative, file.absolute, {
137468
+ dev: file.dev,
137469
+ ino: file.ino,
137470
+ size: file.size
137471
+ });
137472
+ } catch (error) {
137473
+ if (error instanceof ArtifactDigestStreamError) {
137474
+ failPrebuiltCollections(error.message);
137475
+ }
137476
+ const code = error?.code;
137477
+ if (code === "ELOOP" || code === "EPERM") {
137478
+ failPrebuiltCollections(
137479
+ `prebuilt collection tree file became a symlink or unsupported node at ${file.relative}`
137480
+ );
137481
+ }
137482
+ failPrebuiltCollections(
137483
+ `prebuilt collection tree file changed or became unsupported at ${file.relative}`
137484
+ );
137485
+ }
137486
+ validateCanonicalV3CollectionFile(file.relative, bytes);
137487
+ }
137488
+ return hash.digest("hex");
137489
+ }
137490
+ async function preparePrebuiltCollections(inputs) {
137491
+ const prepared = /* @__PURE__ */ new Map();
137492
+ for (const entry of parsePrebuiltCollectionsJson(inputs.prebuiltCollectionsJson ?? "")) {
137493
+ const confinedPath = assertPathWithinArtifactRoot(
137494
+ entry.collectionPath,
137495
+ inputs.artifactDir,
137496
+ `prebuilt ${entry.role} collection path`
137497
+ );
137498
+ const absolute = path3.resolve(process.cwd(), confinedPath);
137499
+ if (!(0, import_node_fs5.existsSync)(absolute)) {
137500
+ prepared.set(entry.role, { entry, confinedPath });
137501
+ continue;
137502
+ }
137503
+ const files = listPrebuiltCollectionTreeFiles(confinedPath, inputs.artifactDir);
137504
+ const artifactDigest = await digestAndValidatePrebuiltCollectionTree(files);
137505
+ prepared.set(entry.role, {
137506
+ entry,
137507
+ confinedPath,
137508
+ artifactDigest
137509
+ });
137510
+ }
137511
+ return prepared;
137512
+ }
137513
+ function tryReusePrebuiltCollection(options) {
137514
+ const { prepared, expectedPath, expectedCloudId } = options;
137515
+ const { entry } = prepared;
137516
+ if (!expectedCloudId || entry.cloudId !== expectedCloudId) {
137517
+ return false;
137518
+ }
137519
+ const expectedNormalized = canonicalizeRelativePath(expectedPath);
137520
+ const entryNormalized = canonicalizeRelativePath(entry.collectionPath);
137521
+ if (entryNormalized !== expectedNormalized) {
137522
+ return false;
137523
+ }
137524
+ const confined = canonicalizeRelativePath(prepared.confinedPath);
137525
+ if (confined !== expectedNormalized) {
137526
+ return false;
137527
+ }
137528
+ const expectedName = path3.posix.basename(expectedNormalized);
137529
+ if (path3.posix.basename(confined) !== expectedName) {
137530
+ return false;
137531
+ }
137532
+ return prepared.artifactDigest === entry.artifactDigest;
137533
+ }
137534
+ async function exportCollectionArtifact(options) {
137535
+ const {
137536
+ role,
137537
+ collectionId,
137538
+ dirName,
137539
+ collectionsDir,
137540
+ prebuiltByRole,
137541
+ postman,
137542
+ core
137543
+ } = options;
137544
+ if (!collectionId) {
137545
+ return void 0;
137546
+ }
137547
+ const expectedPath = `${collectionsDir}/${dirName}`;
137548
+ const entry = prebuiltByRole.get(role);
137549
+ if (entry) {
137550
+ const reused = tryReusePrebuiltCollection({
137551
+ prepared: entry,
137552
+ expectedPath,
137553
+ expectedCloudId: collectionId
137554
+ });
137555
+ if (reused) {
137556
+ core.info(
137557
+ `Reusing prebuilt ${role} collection tree at ${expectedPath} (artifactDigest match)`
137558
+ );
137559
+ return `../${expectedPath}`;
137560
+ }
137561
+ core.info(
137562
+ `Prebuilt ${role} collection entry present but did not exactly match; exporting from cloud`
137563
+ );
137564
+ }
137565
+ const col = await postman.getCollection(collectionId);
137566
+ await convertAndSplitAnyCollection(
137567
+ col,
137568
+ expectedPath
137569
+ );
137570
+ return `../${expectedPath}`;
136827
137571
  }
136828
137572
  function hasControlCharacter2(value) {
136829
137573
  for (let index = 0; index < value.length; index += 1) {
@@ -136841,21 +137585,21 @@ function assertPathWithinCwd(targetPath, fieldName) {
136841
137585
  if (!rawPath || hasControlCharacter2(originalPath) || path3.isAbsolute(rawPath) || path3.win32.isAbsolute(rawPath) || segments.includes("..") || rawPath.startsWith(":") || hasControlCharacter2(rawPath)) {
136842
137586
  throw new Error(`${fieldName} must stay within the repository root; received ${targetPath}`);
136843
137587
  }
136844
- const base = (0, import_node_fs4.realpathSync)(process.cwd());
137588
+ const base = (0, import_node_fs5.realpathSync)(process.cwd());
136845
137589
  const resolved = path3.resolve(base, rawPath);
136846
137590
  const relative3 = path3.relative(base, resolved);
136847
137591
  if (relative3.startsWith("..") || path3.isAbsolute(relative3)) {
136848
137592
  throw new Error(`${fieldName} must stay within the repository root; received ${targetPath}`);
136849
137593
  }
136850
137594
  let existingPath = resolved;
136851
- while (!(0, import_node_fs4.existsSync)(existingPath)) {
137595
+ while (!(0, import_node_fs5.existsSync)(existingPath)) {
136852
137596
  const parent = path3.dirname(existingPath);
136853
137597
  if (parent === existingPath) {
136854
137598
  break;
136855
137599
  }
136856
137600
  existingPath = parent;
136857
137601
  }
136858
- const realExistingPath = (0, import_node_fs4.realpathSync)(existingPath);
137602
+ const realExistingPath = (0, import_node_fs5.realpathSync)(existingPath);
136859
137603
  const realRelative = path3.relative(base, realExistingPath);
136860
137604
  if (realRelative.startsWith("..") || path3.isAbsolute(realRelative)) {
136861
137605
  throw new Error(`${fieldName} must stay within the repository root; received ${targetPath}`);
@@ -136883,8 +137627,8 @@ async function exportArtifacts(inputs, dependencies, envUids, assetProjectName,
136883
137627
  ensureDir(specsDir);
136884
137628
  ensureDir(".postman");
136885
137629
  const globalsFilePath = `${globalsDir}/workspace.globals.yaml`;
136886
- if (!(0, import_node_fs4.existsSync)(globalsFilePath)) {
136887
- (0, import_node_fs4.writeFileSync)(globalsFilePath, "name: Globals\nvalues: []\n");
137630
+ if (!(0, import_node_fs5.existsSync)(globalsFilePath)) {
137631
+ (0, import_node_fs5.writeFileSync)(globalsFilePath, "name: Globals\nvalues: []\n");
136888
137632
  }
136889
137633
  if (inputs.generateCiWorkflow) {
136890
137634
  const ciDir = inputs.ciWorkflowPath.split("/").slice(0, -1).join("/");
@@ -136900,23 +137644,42 @@ async function exportArtifacts(inputs, dependencies, envUids, assetProjectName,
136900
137644
  inputs.specSyncMode,
136901
137645
  options.releaseLabel
136902
137646
  ) : void 0;
136903
- if (inputs.baselineCollectionId) {
136904
- const col = await dependencies.postman.getCollection(inputs.baselineCollectionId);
136905
- const dirName = getCollectionDirectoryName("Baseline", assetProjectName);
136906
- await convertAndSplitAnyCollection(col, `${collectionsDir}/${dirName}`);
136907
- manifestCollections[`../${collectionsDir}/${dirName}`] = inputs.baselineCollectionId;
136908
- }
136909
- if (inputs.smokeCollectionId) {
136910
- const col = await dependencies.postman.getCollection(inputs.smokeCollectionId);
136911
- const dirName = getCollectionDirectoryName("Smoke", assetProjectName);
136912
- await convertAndSplitAnyCollection(col, `${collectionsDir}/${dirName}`);
136913
- manifestCollections[`../${collectionsDir}/${dirName}`] = inputs.smokeCollectionId;
136914
- }
136915
- if (inputs.contractCollectionId) {
136916
- const col = await dependencies.postman.getCollection(inputs.contractCollectionId);
136917
- const dirName = getCollectionDirectoryName("Contract", assetProjectName);
136918
- await convertAndSplitAnyCollection(col, `${collectionsDir}/${dirName}`);
136919
- manifestCollections[`../${collectionsDir}/${dirName}`] = inputs.contractCollectionId;
137647
+ const prebuiltByRole = options.preparedPrebuiltCollections;
137648
+ const baselineRef = await exportCollectionArtifact({
137649
+ role: "baseline",
137650
+ collectionId: inputs.baselineCollectionId,
137651
+ dirName: getCollectionDirectoryName("Baseline", assetProjectName),
137652
+ collectionsDir,
137653
+ prebuiltByRole,
137654
+ postman: dependencies.postman,
137655
+ core: dependencies.core
137656
+ });
137657
+ if (baselineRef) {
137658
+ manifestCollections[baselineRef] = inputs.baselineCollectionId;
137659
+ }
137660
+ const smokeRef = await exportCollectionArtifact({
137661
+ role: "smoke",
137662
+ collectionId: inputs.smokeCollectionId,
137663
+ dirName: getCollectionDirectoryName("Smoke", assetProjectName),
137664
+ collectionsDir,
137665
+ prebuiltByRole,
137666
+ postman: dependencies.postman,
137667
+ core: dependencies.core
137668
+ });
137669
+ if (smokeRef) {
137670
+ manifestCollections[smokeRef] = inputs.smokeCollectionId;
137671
+ }
137672
+ const contractRef = await exportCollectionArtifact({
137673
+ role: "contract",
137674
+ collectionId: inputs.contractCollectionId,
137675
+ dirName: getCollectionDirectoryName("Contract", assetProjectName),
137676
+ collectionsDir,
137677
+ prebuiltByRole,
137678
+ postman: dependencies.postman,
137679
+ core: dependencies.core
137680
+ });
137681
+ if (contractRef) {
137682
+ manifestCollections[contractRef] = inputs.contractCollectionId;
136920
137683
  }
136921
137684
  for (const [envName, envUid] of Object.entries(envUids)) {
136922
137685
  writeJsonFile(
@@ -136931,7 +137694,7 @@ async function exportArtifacts(inputs, dependencies, envUids, assetProjectName,
136931
137694
  workspaceLinkEnabled: inputs.workspaceLinkEnabled,
136932
137695
  workspaceLinkStatus: options.workspaceLinkStatus
136933
137696
  });
136934
- (0, import_node_fs4.writeFileSync)(".postman/resources.yaml", buildResourcesManifest(
137697
+ (0, import_node_fs5.writeFileSync)(".postman/resources.yaml", buildResourcesManifest(
136935
137698
  durableWorkspaceId,
136936
137699
  manifestCollections,
136937
137700
  envUids,
@@ -136943,11 +137706,18 @@ async function exportArtifacts(inputs, dependencies, envUids, assetProjectName,
136943
137706
  options.priorState
136944
137707
  ));
136945
137708
  if (mappedSpec && Object.keys(manifestCollections).length > 0) {
136946
- (0, import_node_fs4.writeFileSync)(
137709
+ let existingWorkflows;
137710
+ try {
137711
+ existingWorkflows = (0, import_node_fs5.readFileSync)(".postman/workflows.yaml", "utf8");
137712
+ } catch {
137713
+ existingWorkflows = void 0;
137714
+ }
137715
+ (0, import_node_fs5.writeFileSync)(
136947
137716
  ".postman/workflows.yaml",
136948
137717
  buildSpecCollectionWorkflowManifest(
136949
137718
  mappedSpec.configRelativePath,
136950
- Object.keys(manifestCollections)
137719
+ Object.keys(manifestCollections),
137720
+ existingWorkflows
136951
137721
  )
136952
137722
  );
136953
137723
  }
@@ -136992,12 +137762,12 @@ async function commitAndPushGeneratedFiles(inputs, dependencies) {
136992
137762
  const dir = parts.slice(0, -1).join("/");
136993
137763
  ensureDir(dir);
136994
137764
  }
136995
- (0, import_node_fs4.writeFileSync)(inputs.ciWorkflowPath, ciWorkflow);
137765
+ (0, import_node_fs5.writeFileSync)(inputs.ciWorkflowPath, ciWorkflow);
136996
137766
  if (inputs.provider === "github" || inputs.provider === "unknown") {
136997
137767
  const gcPath = ".github/workflows/postman-preview-gc.yml";
136998
137768
  if (inputs.ciWorkflowPath.endsWith(".github/workflows/ci.yml") || inputs.ciWorkflowPath === ".github/workflows/ci.yml") {
136999
137769
  ensureDir(".github/workflows");
137000
- (0, import_node_fs4.writeFileSync)(gcPath, renderGcWorkflowTemplate());
137770
+ (0, import_node_fs5.writeFileSync)(gcPath, renderGcWorkflowTemplate());
137001
137771
  }
137002
137772
  }
137003
137773
  }
@@ -137005,16 +137775,16 @@ async function commitAndPushGeneratedFiles(inputs, dependencies) {
137005
137775
  return { commitSha: "", resolvedCurrentRef: "", pushed: false };
137006
137776
  }
137007
137777
  const provisionPath = ".github/workflows/provision.yml";
137008
- const provisionExists = inputs.provider === "github" && (0, import_node_fs4.existsSync)(provisionPath);
137778
+ const provisionExists = inputs.provider === "github" && (0, import_node_fs5.existsSync)(provisionPath);
137009
137779
  const gcWorkflowPath = ".github/workflows/postman-preview-gc.yml";
137010
- const gcExists = inputs.generateCiWorkflow && (0, import_node_fs4.existsSync)(gcWorkflowPath);
137780
+ const gcExists = inputs.generateCiWorkflow && (0, import_node_fs5.existsSync)(gcWorkflowPath);
137011
137781
  const stagePaths = [
137012
137782
  inputs.artifactDir,
137013
137783
  ".postman",
137014
137784
  inputs.generateCiWorkflow ? inputs.ciWorkflowPath : null,
137015
137785
  gcExists ? gcWorkflowPath : null,
137016
137786
  provisionExists ? provisionPath : null
137017
- ].filter((entry) => typeof entry === "string" && ((0, import_node_fs4.existsSync)(entry) || entry === provisionPath));
137787
+ ].filter((entry) => typeof entry === "string" && ((0, import_node_fs5.existsSync)(entry) || entry === provisionPath));
137018
137788
  if (stagePaths.length === 0) {
137019
137789
  dependencies.core.info("No generated repository paths were found; skipping repo mutation.");
137020
137790
  return {
@@ -137122,6 +137892,7 @@ async function runRepoSyncInner(inputs, dependencies) {
137122
137892
  }
137123
137893
  }
137124
137894
  }
137895
+ const preparedPrebuiltCollections = await preparePrebuiltCollections(inputs);
137125
137896
  let skipRepositoryLinkPost = false;
137126
137897
  let repositoryLinkPreflightWasFree = false;
137127
137898
  if (inputs.workspaceLinkEnabled && inputs.workspaceId && inputs.repoUrl && dependencies.internalIntegration?.findWorkspaceForRepo) {
@@ -137158,6 +137929,7 @@ async function runRepoSyncInner(inputs, dependencies) {
137158
137929
  const branchAssetMarker = buildBranchAssetMarker(branchDecision, inputs);
137159
137930
  const envUids = await upsertEnvironments(inputs, dependencies, resourcesState, branchAssetMarker);
137160
137931
  outputs["environment-uids-json"] = JSON.stringify(envUids);
137932
+ dependencies.core.setOutput("environment-uids-json", outputs["environment-uids-json"]);
137161
137933
  if (inputs.environmentSyncEnabled && inputs.workspaceId && dependencies.internalIntegration) {
137162
137934
  const associations = Object.entries(envUids).map(([envName, envUid]) => ({
137163
137935
  envUid,
@@ -137276,6 +138048,7 @@ async function runRepoSyncInner(inputs, dependencies) {
137276
138048
  }
137277
138049
  }
137278
138050
  outputs["mock-url"] = resolvedMockUrl;
138051
+ dependencies.core.setOutput("mock-url", resolvedMockUrl);
137279
138052
  }
137280
138053
  }
137281
138054
  if (inputs.workspaceId && inputs.smokeCollectionId && Object.keys(envUids).length > 0) {
@@ -137351,6 +138124,7 @@ async function runRepoSyncInner(inputs, dependencies) {
137351
138124
  }
137352
138125
  }
137353
138126
  outputs["monitor-id"] = resolvedMonitorId;
138127
+ dependencies.core.setOutput("monitor-id", resolvedMonitorId);
137354
138128
  if (!effectiveCron && resolvedMonitorId) {
137355
138129
  try {
137356
138130
  await dependencies.postman.runMonitor(resolvedMonitorId);
@@ -137408,7 +138182,8 @@ async function runRepoSyncInner(inputs, dependencies) {
137408
138182
  priorWorkspaceId: resourcesState?.workspace?.id,
137409
138183
  existingSpecs: resourcesState?.cloudResources?.specs,
137410
138184
  releaseLabel,
137411
- priorState: resourcesState
138185
+ priorState: resourcesState,
138186
+ preparedPrebuiltCollections
137412
138187
  });
137413
138188
  const commit = await commitAndPushGeneratedFiles(inputs, dependencies);
137414
138189
  outputs["commit-sha"] = commit.commitSha;
@@ -137634,7 +138409,12 @@ async function resolvePostmanApiKeyAndTeamId(inputs, actionCore, actionExec, mas
137634
138409
  bifrostBaseUrl: inputs.postmanBifrostBase,
137635
138410
  // No fallback on the org-mode probe: the expected non-org 400 must
137636
138411
  // surface verbatim, not be re-fired against the /_api alias.
137637
- secretMasker: masker
138412
+ secretMasker: masker,
138413
+ onRetryEvent: (event) => {
138414
+ actionCore.info(
138415
+ `[postman retry] action=repo-sync class=${event.class} status=${event.status ?? "none"} attempt=${event.attempt} delayMs=${event.delay}`
138416
+ );
138417
+ }
137638
138418
  });
137639
138419
  const squads = await gateway.getSquads(teamId);
137640
138420
  if (squads.length > 0) {
@@ -137698,7 +138478,12 @@ function createRepoSyncDependencies(inputs, resolved, factories, options = {}) {
137698
138478
  fallbackBaseUrl: inputs.postmanFallbackBase,
137699
138479
  teamId: resolved.teamId,
137700
138480
  orgMode: inputs.orgMode,
137701
- secretMasker: masker
138481
+ secretMasker: masker,
138482
+ onRetryEvent: (event) => {
138483
+ factories.core.info(
138484
+ `[postman retry] action=repo-sync class=${event.class} status=${event.status ?? "none"} attempt=${event.attempt} delayMs=${event.delay}`
138485
+ );
138486
+ }
137702
138487
  });
137703
138488
  const gatewayAssets = new PostmanGatewayAssetsClient({
137704
138489
  gateway,
@@ -137766,7 +138551,8 @@ function createRepoSyncDependencies(inputs, resolved, factories, options = {}) {
137766
138551
  bifrostBaseUrl: inputs.postmanBifrostBase,
137767
138552
  orgMode: inputs.orgMode,
137768
138553
  teamId: resolved.teamId,
137769
- secretMasker: masker
138554
+ secretMasker: masker,
138555
+ gateway
137770
138556
  });
137771
138557
  return {
137772
138558
  teamId: resolved.teamId,
@@ -138180,6 +138966,7 @@ var CLI_INPUT_NAMES = [
138180
138966
  "baseline-collection-id",
138181
138967
  "smoke-collection-id",
138182
138968
  "contract-collection-id",
138969
+ "prebuilt-collections-json",
138183
138970
  "collection-sync-mode",
138184
138971
  "spec-sync-mode",
138185
138972
  "release-label",
@@ -138251,17 +139038,38 @@ Examples:
138251
139038
  postman-repo-sync --repo-write-mode none --workspace-id <id> ...
138252
139039
  `;
138253
139040
  var ConsoleReporter = class {
139041
+ constructor(onOutput) {
139042
+ this.onOutput = onOutput;
139043
+ }
139044
+ onOutput;
138254
139045
  info(message) {
138255
139046
  console.error(message);
138256
139047
  }
138257
139048
  warning(message) {
138258
139049
  console.error(`warning: ${message}`);
138259
139050
  }
138260
- setOutput() {
139051
+ retryEvent(event) {
139052
+ this.info(
139053
+ `[postman retry] action=repo-sync class=${event.class} status=${event.status ?? "none"} attempt=${event.attempt} delayMs=${event.delay}`
139054
+ );
139055
+ }
139056
+ setOutput(name, value) {
139057
+ this.onOutput?.(name, value);
138261
139058
  }
138262
139059
  setSecret() {
138263
139060
  }
138264
139061
  };
139062
+ async function timedStage(reporter, stage, operation) {
139063
+ const started = performance.now();
139064
+ try {
139065
+ const result = await operation();
139066
+ reporter.info(`[repo-sync timing] ${JSON.stringify({ stage, ms: Math.max(0, performance.now() - started), status: "success" })}`);
139067
+ return result;
139068
+ } catch (error) {
139069
+ reporter.info(`[repo-sync timing] ${JSON.stringify({ stage, ms: Math.max(0, performance.now() - started), status: "error" })}`);
139070
+ throw error;
139071
+ }
139072
+ }
138265
139073
  function normalizeCliFlag(name) {
138266
139074
  return `INPUT_${name.replace(/-/g, "_").toUpperCase()}`;
138267
139075
  }
@@ -138335,7 +139143,7 @@ function resolvePackageVersion() {
138335
139143
  candidates.push(import_node_path3.default.join(process.cwd(), "package.json"));
138336
139144
  for (const packageJsonPath of candidates) {
138337
139145
  try {
138338
- const packageJson = JSON.parse((0, import_node_fs5.readFileSync)(packageJsonPath, "utf8"));
139146
+ const packageJson = JSON.parse((0, import_node_fs6.readFileSync)(packageJsonPath, "utf8"));
138339
139147
  if (packageJson.name === "@postman-cse/onboarding-repo-sync" && packageJson.version) {
138340
139148
  return String(packageJson.version).trim();
138341
139149
  }
@@ -138446,20 +139254,107 @@ function toDotenv(outputs) {
138446
139254
  String(value ?? "")
138447
139255
  ]).map(([key, value]) => `${key}=${JSON.stringify(value)}`).join("\n");
138448
139256
  }
138449
- async function writeOptionalFile(filePath, content) {
138450
- if (!filePath) {
138451
- return;
138452
- }
138453
- const workspaceRoot = import_node_path3.default.resolve(process.cwd());
139257
+ function assertOutputFileAllowed(filePath) {
139258
+ const workspaceRoot = (0, import_node_fs6.realpathSync)(import_node_path3.default.resolve(process.cwd()));
138454
139259
  const resolved = import_node_path3.default.resolve(workspaceRoot, filePath);
138455
139260
  const relative3 = import_node_path3.default.relative(workspaceRoot, resolved);
138456
139261
  if (relative3.startsWith("..") || import_node_path3.default.isAbsolute(relative3)) {
138457
139262
  throw new Error(`Output path must stay within workspace: ${filePath}`);
138458
139263
  }
138459
- await (0, import_promises.mkdir)(import_node_path3.default.dirname(resolved), { recursive: true });
139264
+ let existingParent = import_node_path3.default.dirname(resolved);
139265
+ while (!(0, import_node_fs6.existsSync)(existingParent)) {
139266
+ const parent = import_node_path3.default.dirname(existingParent);
139267
+ if (parent === existingParent) break;
139268
+ existingParent = parent;
139269
+ }
139270
+ const parentReal = (0, import_node_fs6.realpathSync)(existingParent);
139271
+ const parentRelative = import_node_path3.default.relative(workspaceRoot, parentReal);
139272
+ if (parentRelative.startsWith("..") || import_node_path3.default.isAbsolute(parentRelative)) {
139273
+ throw new Error(`Output path must stay within workspace: ${filePath}`);
139274
+ }
139275
+ if ((0, import_node_fs6.existsSync)(resolved) || (() => {
139276
+ try {
139277
+ (0, import_node_fs6.lstatSync)(resolved);
139278
+ return true;
139279
+ } catch {
139280
+ return false;
139281
+ }
139282
+ })()) {
139283
+ const targetReal = (0, import_node_fs6.realpathSync)(resolved);
139284
+ const targetRelative = import_node_path3.default.relative(workspaceRoot, targetReal);
139285
+ if (targetRelative.startsWith("..") || import_node_path3.default.isAbsolute(targetRelative)) {
139286
+ throw new Error(`Output path must stay within workspace: ${filePath}`);
139287
+ }
139288
+ }
139289
+ return { resolved, directory: import_node_path3.default.dirname(resolved) };
139290
+ }
139291
+ function resolveSymlinkAwarePath(filePath) {
139292
+ const workspaceRoot = (0, import_node_fs6.realpathSync)(import_node_path3.default.resolve(process.cwd()));
139293
+ const resolved = import_node_path3.default.resolve(workspaceRoot, filePath);
139294
+ let existing = resolved;
139295
+ while (!(0, import_node_fs6.existsSync)(existing)) {
139296
+ const parent = import_node_path3.default.dirname(existing);
139297
+ if (parent === existing) break;
139298
+ existing = parent;
139299
+ }
139300
+ return import_node_path3.default.resolve((0, import_node_fs6.realpathSync)(existing), import_node_path3.default.relative(existing, resolved));
139301
+ }
139302
+ function assertResultPathOutsideGeneratedPaths(resultJsonPath, inputs) {
139303
+ const result = assertOutputFileAllowed(resultJsonPath).resolved;
139304
+ const resultReal = resolveSymlinkAwarePath(result);
139305
+ for (const root of [inputs.artifactDir, ".postman"].map(resolveSymlinkAwarePath)) {
139306
+ const relative3 = import_node_path3.default.relative(root, resultReal);
139307
+ if (!relative3 || !relative3.startsWith("..") && !import_node_path3.default.isAbsolute(relative3)) {
139308
+ throw new Error(
139309
+ `--result-json must not overlap a generated or staged repository path: ${resultJsonPath}`
139310
+ );
139311
+ }
139312
+ }
139313
+ const generatedFiles = [];
139314
+ if (inputs.generateCiWorkflow) {
139315
+ generatedFiles.push(inputs.ciWorkflowPath);
139316
+ if ((inputs.provider === "github" || inputs.provider === "unknown") && (inputs.ciWorkflowPath.endsWith(".github/workflows/ci.yml") || inputs.ciWorkflowPath === ".github/workflows/ci.yml")) {
139317
+ generatedFiles.push(".github/workflows/postman-preview-gc.yml");
139318
+ }
139319
+ }
139320
+ if (inputs.provider === "github" && (0, import_node_fs6.existsSync)(".github/workflows/provision.yml")) {
139321
+ generatedFiles.push(".github/workflows/provision.yml");
139322
+ }
139323
+ for (const generatedFile of generatedFiles) {
139324
+ if (resultReal === resolveSymlinkAwarePath(generatedFile)) {
139325
+ throw new Error(
139326
+ `--result-json must not overlap a generated or staged repository path: ${resultJsonPath}`
139327
+ );
139328
+ }
139329
+ }
139330
+ }
139331
+ async function writeOptionalFile(filePath, content) {
139332
+ if (!filePath) {
139333
+ return;
139334
+ }
139335
+ const { resolved, directory } = assertOutputFileAllowed(filePath);
139336
+ await (0, import_promises.mkdir)(directory, { recursive: true });
139337
+ const validated = assertOutputFileAllowed(filePath);
139338
+ if (validated.resolved !== resolved) throw new Error(`Output path changed during validation: ${filePath}`);
138460
139339
  await (0, import_promises.writeFile)(resolved, content, "utf8");
138461
139340
  }
138462
- function createCliDependencies(inputs, resolved) {
139341
+ function writeOptionalFileAtomic(filePath, content) {
139342
+ if (!filePath) return;
139343
+ const { resolved, directory } = assertOutputFileAllowed(filePath);
139344
+ (0, import_node_fs6.mkdirSync)(directory, { recursive: true });
139345
+ const validated = assertOutputFileAllowed(filePath);
139346
+ const temporaryDirectory = (0, import_node_fs6.mkdtempSync)(
139347
+ import_node_path3.default.join((0, import_node_fs6.realpathSync)(validated.directory), `.${import_node_path3.default.basename(resolved)}.`)
139348
+ );
139349
+ const temporary = import_node_path3.default.join(temporaryDirectory, import_node_path3.default.basename(resolved));
139350
+ try {
139351
+ (0, import_node_fs6.writeFileSync)(temporary, content, "utf8");
139352
+ (0, import_node_fs6.renameSync)(temporary, resolved);
139353
+ } finally {
139354
+ (0, import_node_fs6.rmSync)(temporaryDirectory, { recursive: true, force: true });
139355
+ }
139356
+ }
139357
+ function createCliDependencies(inputs, resolved, reporter = new ConsoleReporter()) {
138463
139358
  const secretMasker = createSecretMasker([
138464
139359
  resolved.apiKey,
138465
139360
  inputs.postmanAccessToken,
@@ -138476,7 +139371,7 @@ function createCliDependencies(inputs, resolved) {
138476
139371
  inputs,
138477
139372
  resolved,
138478
139373
  {
138479
- core: new ConsoleReporter(),
139374
+ core: reporter,
138480
139375
  exec: cliExec
138481
139376
  },
138482
139377
  {
@@ -138506,17 +139401,24 @@ async function runCli(argv = process.argv.slice(2), runtime = {}) {
138506
139401
  }
138507
139402
  const config = parseCliArgs(argv, env);
138508
139403
  const inputs = resolveInputs(config.inputEnv);
138509
- const reporter = new ConsoleReporter();
138510
139404
  const branchDecision = decideBranchTier(inputs, config.inputEnv);
139405
+ if (branchDecision.tier !== "gated") {
139406
+ assertResultPathOutsideGeneratedPaths(config.resultJsonPath, inputs);
139407
+ }
139408
+ const partialOutputs = {};
139409
+ const reporter = new ConsoleReporter((name, value) => {
139410
+ partialOutputs[name] = String(value ?? "");
139411
+ writeOptionalFileAtomic(config.resultJsonPath, JSON.stringify(partialOutputs, null, 2));
139412
+ });
138511
139413
  if (branchDecision.tier === "gated") {
138512
139414
  const result2 = runGatedSkip(inputs, branchDecision, reporter);
138513
- await writeOptionalFile(config.resultJsonPath, JSON.stringify(result2, null, 2));
139415
+ writeOptionalFileAtomic(config.resultJsonPath, JSON.stringify(result2, null, 2));
138514
139416
  await writeOptionalFile(config.dotenvPath, toDotenv(result2));
138515
139417
  writeStdout(`${JSON.stringify(result2, null, 2)}
138516
139418
  `);
138517
139419
  return;
138518
139420
  }
138519
- await mintAccessTokenIfNeeded(inputs, reporter);
139421
+ await timedStage(reporter, "access-token mint", () => mintAccessTokenIfNeeded(inputs, reporter));
138520
139422
  const initialMasker = createSecretMasker([
138521
139423
  inputs.postmanApiKey,
138522
139424
  inputs.postmanAccessToken,
@@ -138528,7 +139430,7 @@ async function runCli(argv = process.argv.slice(2), runtime = {}) {
138528
139430
  inputs.sslClientPassphrase,
138529
139431
  inputs.sslExtraCaCerts
138530
139432
  ]);
138531
- await runCredentialPreflight({
139433
+ await timedStage(reporter, "credential preflight", () => runCredentialPreflight({
138532
139434
  apiBaseUrl: inputs.postmanApiBase,
138533
139435
  iapubBaseUrl: inputs.postmanIapubBase,
138534
139436
  postmanApiKey: inputs.postmanApiKey,
@@ -138537,9 +139439,9 @@ async function runCli(argv = process.argv.slice(2), runtime = {}) {
138537
139439
  mode: inputs.credentialPreflight,
138538
139440
  mask: initialMasker,
138539
139441
  log: reporter
138540
- });
139442
+ }));
138541
139443
  const resolvingExec = createCliExec(initialMasker);
138542
- const resolved = await resolvePostmanApiKeyAndTeamId(
139444
+ const resolved = await timedStage(reporter, "API-key/team resolution", () => resolvePostmanApiKeyAndTeamId(
138543
139445
  inputs,
138544
139446
  reporter,
138545
139447
  resolvingExec,
@@ -138548,8 +139450,8 @@ async function runCli(argv = process.argv.slice(2), runtime = {}) {
138548
139450
  persistGeneratedApiKeySecret: false,
138549
139451
  env
138550
139452
  }
138551
- );
138552
- const dependencies = createCliDependencies(inputs, resolved);
139453
+ ));
139454
+ const dependencies = createCliDependencies(inputs, resolved, reporter);
138553
139455
  if (inputs.environmentSyncEnabled && !dependencies.internalIntegration) {
138554
139456
  dependencies.core.warning(
138555
139457
  "Skipping system environment association because postman-access-token is not configured"
@@ -138560,8 +139462,12 @@ async function runCli(argv = process.argv.slice(2), runtime = {}) {
138560
139462
  "Skipping workspace linking because postman-access-token is not configured"
138561
139463
  );
138562
139464
  }
138563
- const result = await (runtime.executeRepoSync ?? runRepoSync)(inputs, dependencies);
138564
- await writeOptionalFile(config.resultJsonPath, JSON.stringify(result, null, 2));
139465
+ const result = await timedStage(
139466
+ reporter,
139467
+ "runRepoSync finalize",
139468
+ () => (runtime.executeRepoSync ?? runRepoSync)(inputs, dependencies)
139469
+ );
139470
+ writeOptionalFileAtomic(config.resultJsonPath, JSON.stringify(result, null, 2));
138565
139471
  await writeOptionalFile(config.dotenvPath, toDotenv(result));
138566
139472
  writeStdout(`${JSON.stringify(result, null, 2)}
138567
139473
  `);
@@ -138647,7 +139553,7 @@ function isEntrypoint(currentPath, entrypointPath) {
138647
139553
  return false;
138648
139554
  }
138649
139555
  try {
138650
- return (0, import_node_fs5.realpathSync)(currentPath) === (0, import_node_fs5.realpathSync)(entrypointPath);
139556
+ return (0, import_node_fs6.realpathSync)(currentPath) === (0, import_node_fs6.realpathSync)(entrypointPath);
138651
139557
  } catch {
138652
139558
  return import_node_path3.default.resolve(currentPath) === import_node_path3.default.resolve(entrypointPath);
138653
139559
  }
@@ -138668,7 +139574,8 @@ if (isEntrypoint(currentModulePath, entrypoint)) {
138668
139574
  normalizeCliFlag,
138669
139575
  parseCliArgs,
138670
139576
  runCli,
138671
- toDotenv
139577
+ toDotenv,
139578
+ writeOptionalFileAtomic
138672
139579
  });
138673
139580
  /*! Bundled license information:
138674
139581