@postman-cse/onboarding-repo-sync 2.1.0 → 2.1.2

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
@@ -1,3 +1,4 @@
1
+ #!/usr/bin/env node
1
2
  "use strict";
2
3
  var __create = Object.create;
3
4
  var __defProp = Object.defineProperty;
@@ -119797,7 +119798,7 @@ __export(cli_exports, {
119797
119798
  });
119798
119799
  module.exports = __toCommonJS(cli_exports);
119799
119800
  var import_node_child_process = require("node:child_process");
119800
- var import_node_fs3 = require("node:fs");
119801
+ var import_node_fs4 = require("node:fs");
119801
119802
  var import_promises = require("node:fs/promises");
119802
119803
  var import_node_path3 = __toESM(require("node:path"), 1);
119803
119804
  var import_node_util = require("node:util");
@@ -120165,7 +120166,7 @@ var ExitCode;
120165
120166
  })(ExitCode || (ExitCode = {}));
120166
120167
 
120167
120168
  // src/index.ts
120168
- var import_node_fs2 = require("node:fs");
120169
+ var import_node_fs3 = require("node:fs");
120169
120170
  var path3 = __toESM(require("node:path"), 1);
120170
120171
 
120171
120172
  // node_modules/js-yaml/dist/js-yaml.mjs
@@ -123468,6 +123469,7 @@ function getCiWorkflowTemplate(provider, options = {}) {
123468
123469
  }
123469
123470
 
123470
123471
  // src/lib/github/repo-mutation.ts
123472
+ var import_node_fs = require("node:fs");
123471
123473
  var import_node_path = __toESM(require("node:path"), 1);
123472
123474
 
123473
123475
  // src/lib/secrets.ts
@@ -123709,17 +123711,21 @@ function normalizeStagePaths(stagePaths) {
123709
123711
  if (hasControlCharacter(rawPath) || import_node_path.default.isAbsolute(stagePath) || import_node_path.default.win32.isAbsolute(stagePath) || segments.includes("..") || stagePath.startsWith(":") || hasControlCharacter(stagePath)) {
123710
123712
  throw new Error(`Unsafe git stage path: ${stagePath}`);
123711
123713
  }
123712
- normalized.push(stagePath);
123714
+ if (!normalized.includes(stagePath)) {
123715
+ normalized.push(stagePath);
123716
+ }
123713
123717
  }
123714
123718
  return normalized;
123715
123719
  }
123716
123720
  var RepoMutationService = class {
123721
+ cwd;
123717
123722
  execute;
123718
123723
  provider;
123719
123724
  repository;
123720
123725
  repoUrl;
123721
123726
  secretMasker;
123722
123727
  constructor(options) {
123728
+ this.cwd = options.cwd ?? process.cwd();
123723
123729
  this.execute = options.execute;
123724
123730
  this.provider = options.provider ?? "github";
123725
123731
  this.repository = options.repository;
@@ -123728,7 +123734,8 @@ var RepoMutationService = class {
123728
123734
  }
123729
123735
  async commitAndPush(options) {
123730
123736
  const resolvedCurrentRef = resolveCurrentRef(options);
123731
- const stagePaths = normalizeStagePaths(options.stagePaths);
123737
+ const removePaths = normalizeStagePaths(options.removePaths ?? []);
123738
+ const stagePaths = normalizeStagePaths([...options.stagePaths, ...removePaths]);
123732
123739
  const tokens = this.provider === "azure-devops" ? buildPushTokenOrder({ adoToken: options.adoToken }) : buildPushTokenOrder({
123733
123740
  fallbackToken: options.fallbackToken,
123734
123741
  githubToken: options.githubToken
@@ -123741,8 +123748,43 @@ var RepoMutationService = class {
123741
123748
  resolvedCurrentRef
123742
123749
  };
123743
123750
  }
123751
+ const changed = await this.execute("git", [
123752
+ "status",
123753
+ "--porcelain=v1",
123754
+ "--untracked-files=all",
123755
+ "--",
123756
+ ...stagePaths
123757
+ ]);
123758
+ if (changed.exitCode !== 0) {
123759
+ throw new Error(this.secretMasker(changed.stderr || changed.stdout || "Failed to inspect generated changes"));
123760
+ }
123761
+ const hasPlannedRemoval = removePaths.some(
123762
+ (removePath) => (0, import_node_fs.existsSync)(import_node_path.default.resolve(this.cwd, removePath))
123763
+ );
123764
+ if (!changed.stdout.trim() && !hasPlannedRemoval) {
123765
+ return {
123766
+ commitSha: "",
123767
+ pushed: false,
123768
+ resolvedCurrentRef
123769
+ };
123770
+ }
123771
+ const usePersistedCredentials = tokens.length === 0 && this.provider === "azure-devops";
123772
+ if (options.repoWriteMode === "commit-and-push") {
123773
+ if (!supportsTokenRemote(this.provider)) {
123774
+ throw new Error(`repo-write-mode=commit-and-push is not supported for git provider "${this.provider}"`);
123775
+ }
123776
+ if (!resolvedCurrentRef) {
123777
+ throw new Error("No current ref could be resolved for repo-write-mode=commit-and-push");
123778
+ }
123779
+ if (tokens.length === 0 && !usePersistedCredentials) {
123780
+ throw new Error("No push token configured for repo-write-mode=commit-and-push");
123781
+ }
123782
+ }
123744
123783
  await this.execute("git", ["config", "user.name", options.committerName]);
123745
123784
  await this.execute("git", ["config", "user.email", options.committerEmail]);
123785
+ for (const removePath of removePaths) {
123786
+ (0, import_node_fs.rmSync)(import_node_path.default.resolve(this.cwd, removePath), { force: true });
123787
+ }
123746
123788
  await this.execute("git", ["add", "-A", "--", ...stagePaths]);
123747
123789
  const staged = await this.execute("git", ["diff", "--cached", "--quiet"]);
123748
123790
  if (staged.exitCode === 0) {
@@ -123765,16 +123807,6 @@ var RepoMutationService = class {
123765
123807
  resolvedCurrentRef
123766
123808
  };
123767
123809
  }
123768
- if (!resolvedCurrentRef) {
123769
- throw new Error("No current ref could be resolved for repo-write-mode=commit-and-push");
123770
- }
123771
- const usePersistedCredentials = tokens.length === 0 && this.provider === "azure-devops";
123772
- if (tokens.length === 0 && !usePersistedCredentials) {
123773
- throw new Error("No push token configured for repo-write-mode=commit-and-push");
123774
- }
123775
- if (tokens.length > 0 && !supportsTokenRemote(this.provider)) {
123776
- throw new Error(`repo-write-mode=commit-and-push is not supported for git provider "${this.provider}"`);
123777
- }
123778
123810
  const originalRemote = (await this.execute("git", ["remote", "get-url", "origin"])).stdout.trim();
123779
123811
  let pushed = false;
123780
123812
  let lastError = "";
@@ -124294,11 +124326,11 @@ function createTelemetryContext(options) {
124294
124326
  }
124295
124327
 
124296
124328
  // src/action-version.ts
124297
- var import_node_fs = require("node:fs");
124329
+ var import_node_fs2 = require("node:fs");
124298
124330
  var import_node_path2 = require("node:path");
124299
124331
  function resolveActionVersion2() {
124300
124332
  try {
124301
- const raw = (0, import_node_fs.readFileSync)((0, import_node_path2.join)(__dirname, "..", "package.json"), "utf8");
124333
+ const raw = (0, import_node_fs2.readFileSync)((0, import_node_path2.join)(__dirname, "..", "package.json"), "utf8");
124302
124334
  return JSON.parse(raw).version ?? "unknown";
124303
124335
  } catch {
124304
124336
  return "unknown";
@@ -125362,12 +125394,20 @@ async function retry(operation, options = {}) {
125362
125394
  }
125363
125395
 
125364
125396
  // src/lib/postman/postman-gateway-assets-client.ts
125397
+ var MAX_CREATE_FLIGHTS = 256;
125398
+ var createFlights = /* @__PURE__ */ new Map();
125365
125399
  var PostmanGatewayAssetsClient = class {
125366
125400
  gateway;
125367
125401
  workspaceId;
125402
+ sleep;
125403
+ reconcileAttempts;
125404
+ reconcileDelayMs;
125368
125405
  constructor(options) {
125369
125406
  this.gateway = options.gateway;
125370
125407
  this.workspaceId = String(options.workspaceId || "").trim();
125408
+ this.sleep = options.sleep ?? sleep;
125409
+ this.reconcileAttempts = Math.max(1, options.reconcileAttempts ?? 3);
125410
+ this.reconcileDelayMs = Math.max(0, options.reconcileDelayMs ?? 500);
125371
125411
  }
125372
125412
  asRecord(value) {
125373
125413
  if (!value || typeof value !== "object" || Array.isArray(value)) return null;
@@ -125397,8 +125437,64 @@ var PostmanGatewayAssetsClient = class {
125397
125437
  const parts = trimmed.split("-");
125398
125438
  return parts.length >= 6 ? parts.slice(1).join("-") : trimmed;
125399
125439
  }
125400
- isTransient(error) {
125401
- return error instanceof HttpError && (error.status === 429 || error.status >= 500);
125440
+ isAmbiguousCreateOutcome(error) {
125441
+ if (error instanceof HttpError) {
125442
+ return error.status === 408 || error.status === 429 || error.status >= 500;
125443
+ }
125444
+ return error instanceof Error;
125445
+ }
125446
+ isRetryableIdempotentWriteOutcome(error) {
125447
+ if (error instanceof HttpError) {
125448
+ return error.status === 408 || error.status === 429 || error.status >= 500;
125449
+ }
125450
+ return error instanceof Error;
125451
+ }
125452
+ selectExactMatch(kind, identity, matches) {
125453
+ if (matches.length > 1) {
125454
+ const ids = matches.map((match) => match.uid).join(", ");
125455
+ throw new Error(
125456
+ `Multiple ${kind}s match ${identity}: ${ids}. Refusing to choose one; remove duplicates or pass an explicit asset ID.`
125457
+ );
125458
+ }
125459
+ return matches[0] ?? null;
125460
+ }
125461
+ async singleFlight(key, fingerprint, kind, operation) {
125462
+ const existing = createFlights.get(key);
125463
+ if (existing) {
125464
+ if (existing.fingerprint !== fingerprint) {
125465
+ throw new Error(`Incompatible concurrent ${kind} create for ${key}`);
125466
+ }
125467
+ return existing.promise;
125468
+ }
125469
+ if (createFlights.size >= MAX_CREATE_FLIGHTS) {
125470
+ throw new Error(`Too many concurrent Postman asset creates (limit ${MAX_CREATE_FLIGHTS})`);
125471
+ }
125472
+ const pending = operation().finally(() => {
125473
+ if (createFlights.get(key)?.promise === pending) {
125474
+ createFlights.delete(key);
125475
+ }
125476
+ });
125477
+ createFlights.set(key, { fingerprint, promise: pending });
125478
+ return pending;
125479
+ }
125480
+ async discoverAfterAmbiguousCreate(discover, error) {
125481
+ if (!this.isAmbiguousCreateOutcome(error)) {
125482
+ return null;
125483
+ }
125484
+ for (let attempt = 0; attempt < this.reconcileAttempts; attempt += 1) {
125485
+ if (attempt > 0) {
125486
+ await this.sleep(this.reconcileDelayMs);
125487
+ }
125488
+ const found = await discover();
125489
+ if (found) {
125490
+ return found;
125491
+ }
125492
+ }
125493
+ return null;
125494
+ }
125495
+ publicEnvironmentUid(data, bareId) {
125496
+ const owner = String(data?.owner ?? "").trim();
125497
+ return owner && !bareId.startsWith(`${owner}-`) ? `${owner}-${bareId}` : bareId;
125402
125498
  }
125403
125499
  // --- environments (service: sync) ---
125404
125500
  //
@@ -125409,9 +125505,10 @@ var PostmanGatewayAssetsClient = class {
125409
125505
  /**
125410
125506
  * Create/upsert an environment through the sync service.
125411
125507
  * POST /environment/import?workspace=:ws { id:<uuid>, name, values } ->
125412
- * { data:{ id:<bare uuid>, owner } }. The id is generated once and reused
125413
- * across retries so the import is idempotent (a retry upserts the same
125414
- * environment instead of duplicating it).
125508
+ * { data:{ id:<bare uuid>, owner } }. The id is generated once for the
125509
+ * operation. Unsafe creates submit once (no blind retry); on an ambiguous
125510
+ * response the client discovers by exact workspace-scoped name before
125511
+ * failing.
125415
125512
  *
125416
125513
  * Returns the PUBLIC uid (`<owner>-<uuid>`), not the bare model id the sync
125417
125514
  * import echoes: mock/monitor create reference the environment by its public
@@ -125421,33 +125518,54 @@ var PostmanGatewayAssetsClient = class {
125421
125518
  */
125422
125519
  async createEnvironment(workspaceId, name, values) {
125423
125520
  const ws = workspaceId || this.workspaceId;
125424
- const id = crypto.randomUUID();
125425
- const body = {
125426
- id,
125427
- name,
125428
- values: values.map((v) => ({
125429
- key: v.key,
125430
- value: v.value,
125431
- type: v.type ?? "default",
125432
- enabled: v.enabled ?? true
125433
- }))
125434
- };
125435
- const response = await retry(
125436
- () => this.gateway.requestJson({
125437
- service: "sync",
125438
- method: "post",
125439
- path: `/environment/import?workspace=${ws}`,
125440
- body
125441
- }),
125442
- { maxAttempts: 5, delayMs: 2e3, backoffMultiplier: 2, maxDelayMs: 15e3, shouldRetry: (e) => this.isTransient(e) }
125443
- );
125444
- const data = this.dataOf(response);
125445
- const bareId = this.idOf(data);
125446
- if (!bareId) {
125447
- throw new Error("Environment import did not return a UID");
125448
- }
125449
- const owner = String(data?.owner ?? "").trim();
125450
- return owner && !bareId.startsWith(`${owner}-`) ? `${owner}-${bareId}` : bareId;
125521
+ const envName = String(name ?? "").trim();
125522
+ const flightKey = `environment:${ws}:${envName}`;
125523
+ const normalizedValues = values.map((v) => ({
125524
+ key: v.key,
125525
+ value: v.value,
125526
+ type: v.type ?? "default",
125527
+ enabled: v.enabled ?? true
125528
+ }));
125529
+ return this.singleFlight(flightKey, JSON.stringify(normalizedValues), "environment", async () => {
125530
+ const existing = await this.findEnvironmentByName(ws, envName);
125531
+ if (existing?.uid) {
125532
+ await this.updateEnvironment(existing.uid, envName, values);
125533
+ return existing.uid;
125534
+ }
125535
+ const id = crypto.randomUUID();
125536
+ const body = {
125537
+ id,
125538
+ name: envName,
125539
+ values: normalizedValues
125540
+ };
125541
+ try {
125542
+ const response = await this.gateway.requestJson(
125543
+ {
125544
+ service: "sync",
125545
+ method: "post",
125546
+ path: `/environment/import?workspace=${ws}`,
125547
+ body
125548
+ },
125549
+ { retryTransient: false }
125550
+ );
125551
+ const data = this.dataOf(response);
125552
+ const bareId = this.idOf(data);
125553
+ if (!bareId) {
125554
+ throw new Error("Environment import did not return a UID");
125555
+ }
125556
+ return this.publicEnvironmentUid(data, bareId);
125557
+ } catch (error) {
125558
+ const adopted = await this.discoverAfterAmbiguousCreate(async () => {
125559
+ const match = await this.findEnvironmentByName(ws, envName);
125560
+ return match?.uid ?? null;
125561
+ }, error);
125562
+ if (adopted) {
125563
+ await this.updateEnvironment(adopted, envName, values);
125564
+ return adopted;
125565
+ }
125566
+ throw error;
125567
+ }
125568
+ });
125451
125569
  }
125452
125570
  /**
125453
125571
  * Update an existing environment through the sync service.
@@ -125477,7 +125595,7 @@ var PostmanGatewayAssetsClient = class {
125477
125595
  path: `/environment/${id}`,
125478
125596
  body
125479
125597
  }),
125480
- { maxAttempts: 5, delayMs: 2e3, backoffMultiplier: 2, maxDelayMs: 15e3, shouldRetry: (e) => this.isTransient(e) }
125598
+ { maxAttempts: 5, delayMs: 2e3, backoffMultiplier: 2, maxDelayMs: 15e3, shouldRetry: (e) => this.isRetryableIdempotentWriteOutcome(e) }
125481
125599
  );
125482
125600
  }
125483
125601
  /**
@@ -125501,13 +125619,37 @@ var PostmanGatewayAssetsClient = class {
125501
125619
  */
125502
125620
  async listEnvironments(workspaceId) {
125503
125621
  const ws = workspaceId || this.workspaceId;
125504
- const response = await this.gateway.requestJson({
125505
- service: "sync",
125506
- method: "post",
125507
- path: `/list/environment?workspace=${ws}`
125508
- });
125622
+ const response = await this.gateway.requestJson(
125623
+ {
125624
+ service: "sync",
125625
+ method: "post",
125626
+ path: `/list/environment?workspace=${ws}`
125627
+ },
125628
+ { retryTransient: true }
125629
+ );
125509
125630
  const items = Array.isArray(response?.data) ? response.data : [];
125510
- return items.map((raw) => this.asRecord(raw)).filter((e) => e !== null).map((e) => ({ name: String(e.name ?? ""), uid: this.idOf(e) }));
125631
+ return items.map((raw) => this.asRecord(raw)).filter((e) => e !== null).map((e) => {
125632
+ const bareOrPublic = this.idOf(e);
125633
+ return {
125634
+ name: String(e.name ?? ""),
125635
+ uid: this.publicEnvironmentUid(e, bareOrPublic)
125636
+ };
125637
+ });
125638
+ }
125639
+ /** Exact name match within a workspace. Prefer tracked UIDs before calling. */
125640
+ async findEnvironmentByName(workspaceId, name) {
125641
+ const want = String(name ?? "").trim();
125642
+ if (!want) {
125643
+ return null;
125644
+ }
125645
+ const environments = await this.listEnvironments(workspaceId);
125646
+ const matches = environments.filter((entry) => entry.name === want);
125647
+ const match = this.selectExactMatch(
125648
+ "environment",
125649
+ `workspace ${workspaceId} and name "${want}"`,
125650
+ matches
125651
+ );
125652
+ return match?.uid ? { uid: match.uid, name: match.name } : null;
125511
125653
  }
125512
125654
  // --- collection read (service: collection, v3 export) ---
125513
125655
  //
@@ -125537,32 +125679,51 @@ var PostmanGatewayAssetsClient = class {
125537
125679
  // --- mocks (service: mock) ---
125538
125680
  async createMock(workspaceId, name, collectionUid, environmentUid) {
125539
125681
  const ws = workspaceId || this.workspaceId;
125682
+ const mockName = String(name ?? "").trim();
125540
125683
  const collection = String(collectionUid ?? "").trim();
125541
125684
  const environment = String(environmentUid ?? "").trim();
125542
- const body = {
125543
- name,
125544
- collection,
125545
- private: false,
125546
- ...environment ? { environment } : {}
125547
- };
125548
- const response = await retry(
125549
- () => this.gateway.requestJson({
125550
- service: "mock",
125551
- method: "post",
125552
- path: `/mocks?workspace=${ws}`,
125553
- body
125554
- }),
125555
- { maxAttempts: 5, delayMs: 2e3, backoffMultiplier: 2, maxDelayMs: 15e3, shouldRetry: (e) => this.isTransient(e) }
125556
- );
125557
- const record = this.dataOf(response);
125558
- const uid = this.idOf(record);
125559
- if (!uid) {
125560
- throw new Error("Mock create did not return a UID");
125561
- }
125562
- return {
125563
- uid,
125564
- url: String(record?.url ?? record?.mockUrl ?? "").trim()
125565
- };
125685
+ const flightKey = `mock:${ws}:${collection}:${environment}:${mockName}`;
125686
+ return this.singleFlight(flightKey, flightKey, "mock", async () => {
125687
+ const existing = await this.findMockByCollection(collection, environment, mockName);
125688
+ if (existing) {
125689
+ return { uid: existing.uid, url: existing.mockUrl };
125690
+ }
125691
+ const body = {
125692
+ name: mockName,
125693
+ collection,
125694
+ private: false,
125695
+ ...environment ? { environment } : {}
125696
+ };
125697
+ try {
125698
+ const response = await this.gateway.requestJson(
125699
+ {
125700
+ service: "mock",
125701
+ method: "post",
125702
+ path: `/mocks?workspace=${ws}`,
125703
+ body
125704
+ },
125705
+ { retryTransient: false }
125706
+ );
125707
+ const record = this.dataOf(response);
125708
+ const uid = this.idOf(record);
125709
+ if (!uid) {
125710
+ throw new Error("Mock create did not return a UID");
125711
+ }
125712
+ return {
125713
+ uid,
125714
+ url: String(record?.url ?? record?.mockUrl ?? "").trim()
125715
+ };
125716
+ } catch (error) {
125717
+ const adopted = await this.discoverAfterAmbiguousCreate(
125718
+ () => this.findMockByCollection(collection, environment, mockName),
125719
+ error
125720
+ );
125721
+ if (adopted) {
125722
+ return { uid: adopted.uid, url: adopted.mockUrl };
125723
+ }
125724
+ throw error;
125725
+ }
125726
+ });
125566
125727
  }
125567
125728
  async listMocks() {
125568
125729
  const response = await this.gateway.requestJson({
@@ -125591,10 +125752,19 @@ var PostmanGatewayAssetsClient = class {
125591
125752
  return false;
125592
125753
  }
125593
125754
  }
125594
- async findMockByCollection(collectionUid) {
125755
+ async findMockByCollection(collectionUid, environmentUid, name) {
125595
125756
  const mocks = await this.listMocks();
125596
125757
  const want = String(collectionUid ?? "").trim();
125597
- const match = mocks.find((m) => m.collection === want);
125758
+ const environment = String(environmentUid ?? "").trim();
125759
+ const mockName = String(name ?? "").trim();
125760
+ const matches = mocks.filter(
125761
+ (mock) => mock.collection === want && mock.environment === environment && mock.name === mockName
125762
+ );
125763
+ const match = this.selectExactMatch(
125764
+ "mock",
125765
+ `workspace ${this.workspaceId}, name "${mockName}", collection ${want}, and environment ${environment || "(none)"}`,
125766
+ matches
125767
+ );
125598
125768
  return match ? { uid: match.uid, mockUrl: match.mockUrl } : null;
125599
125769
  }
125600
125770
  // --- monitors (service: monitors; collection-based = jobTemplates) ---
@@ -125611,32 +125781,51 @@ var PostmanGatewayAssetsClient = class {
125611
125781
  async createMonitor(workspaceId, name, collectionUid, environmentUid, cronSchedule) {
125612
125782
  const ws = workspaceId || this.workspaceId;
125613
125783
  const effectiveCron = cronSchedule && cronSchedule.trim() ? cronSchedule.trim() : "0 0 * * 0";
125784
+ const monitorName = String(name ?? "").trim();
125614
125785
  const collection = String(collectionUid ?? "").trim();
125615
125786
  const environment = String(environmentUid ?? "").trim();
125616
- const body = {
125617
- name,
125618
- collection,
125619
- options: { strictSSL: false, followRedirects: true, requestTimeout: null, requestDelay: 0 },
125620
- notifications: { onFailure: [], onError: [] },
125621
- retry: {},
125622
- schedule: { cronPattern: effectiveCron, timeZone: "UTC" },
125623
- distribution: null,
125624
- ...environment ? { environment } : {}
125625
- };
125626
- const response = await retry(
125627
- () => this.gateway.requestJson({
125628
- service: "monitors",
125629
- method: "post",
125630
- path: `/jobTemplates?workspace=${ws}`,
125631
- body
125632
- }),
125633
- { maxAttempts: 5, delayMs: 2e3, backoffMultiplier: 2, maxDelayMs: 15e3, shouldRetry: (e) => this.isTransient(e) }
125634
- );
125635
- const uid = this.idOf(this.dataOf(response));
125636
- if (!uid) {
125637
- throw new Error("Monitor create did not return a UID");
125638
- }
125639
- return uid;
125787
+ const flightKey = `monitor:${ws}:${collection}:${environment}:${monitorName}`;
125788
+ return this.singleFlight(flightKey, effectiveCron, "monitor", async () => {
125789
+ const existing = await this.findMonitorByCollection(collection, environment, monitorName);
125790
+ if (existing?.uid) {
125791
+ return existing.uid;
125792
+ }
125793
+ const body = {
125794
+ name: monitorName,
125795
+ collection,
125796
+ options: { strictSSL: false, followRedirects: true, requestTimeout: null, requestDelay: 0 },
125797
+ notifications: { onFailure: [], onError: [] },
125798
+ retry: {},
125799
+ schedule: { cronPattern: effectiveCron, timeZone: "UTC" },
125800
+ distribution: null,
125801
+ ...environment ? { environment } : {}
125802
+ };
125803
+ try {
125804
+ const response = await this.gateway.requestJson(
125805
+ {
125806
+ service: "monitors",
125807
+ method: "post",
125808
+ path: `/jobTemplates?workspace=${ws}`,
125809
+ body
125810
+ },
125811
+ { retryTransient: false }
125812
+ );
125813
+ const uid = this.idOf(this.dataOf(response));
125814
+ if (!uid) {
125815
+ throw new Error("Monitor create did not return a UID");
125816
+ }
125817
+ return uid;
125818
+ } catch (error) {
125819
+ const adopted = await this.discoverAfterAmbiguousCreate(
125820
+ () => this.findMonitorByCollection(collection, environment, monitorName),
125821
+ error
125822
+ );
125823
+ if (adopted?.uid) {
125824
+ return adopted.uid;
125825
+ }
125826
+ throw error;
125827
+ }
125828
+ });
125640
125829
  }
125641
125830
  async listMonitors() {
125642
125831
  const response = await this.gateway.requestJson({
@@ -125659,24 +125848,30 @@ var PostmanGatewayAssetsClient = class {
125659
125848
  return false;
125660
125849
  }
125661
125850
  }
125662
- async findMonitorByCollection(collectionUid) {
125851
+ async findMonitorByCollection(collectionUid, environmentUid, name) {
125663
125852
  const want = String(collectionUid ?? "").trim();
125664
- const response = await this.gateway.requestJson({
125665
- service: "monitors",
125666
- method: "get",
125667
- path: `/collections/${want}/jobTemplates?_etc=true`
125668
- });
125669
- const data = response?.data;
125670
- const monitors = this.mapJobTemplates(Array.isArray(data) ? data : []);
125671
- const match = monitors.find((m) => m.collectionUid === want) ?? monitors[0];
125853
+ const environment = String(environmentUid ?? "").trim();
125854
+ const monitorName = String(name ?? "").trim();
125855
+ const monitors = await this.listMonitors();
125856
+ const matches = monitors.filter(
125857
+ (monitor) => monitor.collectionUid === want && monitor.environmentUid === environment && monitor.name === monitorName
125858
+ );
125859
+ const match = this.selectExactMatch(
125860
+ "monitor",
125861
+ `workspace ${this.workspaceId}, name "${monitorName}", collection ${want}, and environment ${environment || "(none)"}`,
125862
+ matches
125863
+ );
125672
125864
  return match?.uid ? { uid: match.uid, name: match.name } : null;
125673
125865
  }
125674
125866
  async runMonitor(uid) {
125675
- await this.gateway.requestJson({
125676
- service: "monitors",
125677
- method: "post",
125678
- path: `/jobTemplates/${uid}/jobs`
125679
- });
125867
+ await this.gateway.requestJson(
125868
+ {
125869
+ service: "monitors",
125870
+ method: "post",
125871
+ path: `/jobTemplates/${uid}/jobs`
125872
+ },
125873
+ { retryTransient: false }
125874
+ );
125680
125875
  }
125681
125876
  };
125682
125877
 
@@ -125842,12 +126037,8 @@ async function mintAccessTokenIfNeeded(inputs, log, setSecret2, fetchImpl = fetc
125842
126037
  function isExpiredAuthError(status, body) {
125843
126038
  return status === 401 || body.includes("UNAUTHENTICATED") || body.includes("authenticationError");
125844
126039
  }
125845
- function isTransientGatewayError(status, body) {
125846
- if (status === 502 || status === 503 || status === 504) return true;
125847
- if (status >= 500 && (body.includes("ESOCKETTIMEDOUT") || body.includes("ETIMEDOUT") || body.includes("ECONNRESET") || body.includes("serverError") || body.includes("downstream"))) {
125848
- return true;
125849
- }
125850
- return false;
126040
+ function isRetryableSafeReadResponse(status) {
126041
+ return status === 408 || status === 429 || status >= 500;
125851
126042
  }
125852
126043
  function defaultSleep(ms) {
125853
126044
  return new Promise((resolve2) => setTimeout(resolve2, ms));
@@ -125906,14 +126097,28 @@ var AccessTokenGatewayClient = class {
125906
126097
  }
125907
126098
  /**
125908
126099
  * Send a gateway request, refreshing the token once on an auth failure and
125909
- * retrying transient downstream failures (5xx / Bifrost read timeouts) with
125910
- * exponential backoff. The auth-refresh-once path is independent of the
125911
- * transient-retry budget.
126100
+ * optionally retrying transient downstream failures (5xx / Bifrost read
126101
+ * timeouts) with exponential backoff. Safe reads keep transient retries;
126102
+ * unsafe creates pass `{ retryTransient: false }` and reconcile after an
126103
+ * ambiguous response instead of re-POSTing. Auth refresh remains independent
126104
+ * of the transient-retry budget.
125912
126105
  */
125913
- async request(request) {
126106
+ async request(request, options = {}) {
126107
+ const retryTransient = options.retryTransient ?? request.method === "get";
125914
126108
  let attempt = 0;
125915
126109
  for (; ; ) {
125916
- let response = await this.send(request);
126110
+ let response;
126111
+ try {
126112
+ response = await this.send(request);
126113
+ } catch (error) {
126114
+ if (retryTransient && attempt < this.maxRetries) {
126115
+ const delay = this.retryBaseDelayMs * 2 ** attempt;
126116
+ attempt += 1;
126117
+ await this.sleepImpl(delay);
126118
+ continue;
126119
+ }
126120
+ throw error;
126121
+ }
125917
126122
  if (response.ok) {
125918
126123
  return response;
125919
126124
  }
@@ -125927,7 +126132,7 @@ var AccessTokenGatewayClient = class {
125927
126132
  const retryBody = await response.text().catch(() => "");
125928
126133
  throw this.toHttpError(request, response, retryBody);
125929
126134
  }
125930
- if (isTransientGatewayError(response.status, body) && attempt < this.maxRetries) {
126135
+ if (retryTransient && isRetryableSafeReadResponse(response.status) && attempt < this.maxRetries) {
125931
126136
  const delay = this.retryBaseDelayMs * 2 ** attempt;
125932
126137
  attempt += 1;
125933
126138
  await this.sleepImpl(delay);
@@ -125937,8 +126142,8 @@ var AccessTokenGatewayClient = class {
125937
126142
  }
125938
126143
  }
125939
126144
  /** Send a gateway request and parse the JSON body, or null when empty. */
125940
- async requestJson(request) {
125941
- const response = await this.request(request);
126145
+ async requestJson(request, options = {}) {
126146
+ const response = await this.request(request, options);
125942
126147
  const text = await response.text().catch(() => "");
125943
126148
  if (!text.trim()) {
125944
126149
  return null;
@@ -126001,8 +126206,27 @@ function normalizeInputValue(value) {
126001
126206
  return String(value ?? "").trim();
126002
126207
  }
126003
126208
  function getInput(name, env = process.env) {
126004
- const envName = `INPUT_${name.replace(/-/g, "_").toUpperCase()}`;
126005
- return normalizeInputValue(env[envName]);
126209
+ const normalizedName = `INPUT_${name.replace(/-/g, "_").toUpperCase()}`;
126210
+ const runnerName = `INPUT_${name.replace(/ /g, "_").toUpperCase()}`;
126211
+ const normalizedRaw = env[normalizedName];
126212
+ const runnerRaw = runnerName === normalizedName ? void 0 : env[runnerName];
126213
+ const hasNormalized = normalizedRaw !== void 0;
126214
+ const hasRunner = runnerRaw !== void 0;
126215
+ if (hasNormalized && hasRunner) {
126216
+ const normalizedValue = normalizeInputValue(normalizedRaw);
126217
+ const runnerValue = normalizeInputValue(runnerRaw);
126218
+ if (normalizedValue !== runnerValue) {
126219
+ throw new Error(
126220
+ `Conflicting values for ${name}: ${normalizedName}=${JSON.stringify(normalizedValue)} vs ${runnerName}=${JSON.stringify(runnerValue)}`
126221
+ );
126222
+ }
126223
+ }
126224
+ return normalizeInputValue(hasNormalized ? normalizedRaw : runnerRaw);
126225
+ }
126226
+ function hasInput(name, env = process.env) {
126227
+ const normalizedName = `INPUT_${name.replace(/-/g, "_").toUpperCase()}`;
126228
+ const runnerName = `INPUT_${name.replace(/ /g, "_").toUpperCase()}`;
126229
+ return env[normalizedName] !== void 0 || runnerName !== normalizedName && env[runnerName] !== void 0;
126006
126230
  }
126007
126231
  function parseJsonMap(raw) {
126008
126232
  if (!raw.trim()) return {};
@@ -126029,7 +126253,9 @@ function normalizeRepoWriteMode(value) {
126029
126253
  if (value === "none" || value === "commit-only" || value === "commit-and-push") {
126030
126254
  return value;
126031
126255
  }
126032
- return "commit-and-push";
126256
+ throw new Error(
126257
+ `Unsupported repo-write-mode "${value}". Allowed values: none, commit-only, commit-and-push`
126258
+ );
126033
126259
  }
126034
126260
  function normalizeCollectionSyncMode(value) {
126035
126261
  if (value === "refresh" || value === "version") {
@@ -126111,7 +126337,7 @@ function resolveInputs(env = process.env) {
126111
126337
  environmentUids,
126112
126338
  envRuntimeUrls,
126113
126339
  artifactDir: getInput("artifact-dir", env) || "postman",
126114
- repoWriteMode: normalizeRepoWriteMode(getInput("repo-write-mode", env) || "commit-and-push"),
126340
+ repoWriteMode: hasInput("repo-write-mode", env) ? normalizeRepoWriteMode(getInput("repo-write-mode", env)) : "commit-and-push",
126115
126341
  currentRef: getInput("current-ref", env) || normalizeInputValue(env.GITHUB_REF) || normalizeInputValue(env.BUILD_SOURCEBRANCH),
126116
126342
  githubHeadRef: getInput("github-head-ref", env) || normalizeInputValue(env.GITHUB_HEAD_REF) || normalizeInputValue(env.SYSTEM_PULLREQUEST_SOURCEBRANCH),
126117
126343
  githubRefName: getInput("github-ref-name", env) || normalizeInputValue(env.GITHUB_REF_NAME) || normalizeInputValue(repoContext.ref),
@@ -126160,7 +126386,7 @@ function buildEnvironmentValues(envName, baseUrl) {
126160
126386
  var LEGACY_BASELINE_COLLECTION_PREFIX = "[Baseline]";
126161
126387
  function readResourcesState() {
126162
126388
  try {
126163
- return load((0, import_node_fs2.readFileSync)(".postman/resources.yaml", "utf8"));
126389
+ return load((0, import_node_fs3.readFileSync)(".postman/resources.yaml", "utf8"));
126164
126390
  } catch {
126165
126391
  return null;
126166
126392
  }
@@ -126198,7 +126424,7 @@ function isOpenApiSpecFile(filePath) {
126198
126424
  return false;
126199
126425
  }
126200
126426
  try {
126201
- const raw = (0, import_node_fs2.readFileSync)(filePath, "utf8");
126427
+ const raw = (0, import_node_fs3.readFileSync)(filePath, "utf8");
126202
126428
  const parsed = filePath.endsWith(".json") ? JSON.parse(raw) : load(raw);
126203
126429
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
126204
126430
  return false;
@@ -126227,7 +126453,7 @@ function scanLocalSpecReferences(baseDir = ".") {
126227
126453
  const found = /* @__PURE__ */ new Set();
126228
126454
  const refs = [];
126229
126455
  const visit2 = (currentDir) => {
126230
- for (const entry of (0, import_node_fs2.readdirSync)(currentDir, { withFileTypes: true })) {
126456
+ for (const entry of (0, import_node_fs3.readdirSync)(currentDir, { withFileTypes: true })) {
126231
126457
  if (ignoredDirs.has(entry.name)) {
126232
126458
  continue;
126233
126459
  }
@@ -126257,7 +126483,7 @@ function resolveMappedSpecReference(explicitSpecPath, discoveredSpecs) {
126257
126483
  const normalizedExplicitPath = normalizeToPosix(explicitSpecPath.trim());
126258
126484
  if (normalizedExplicitPath) {
126259
126485
  const explicitFullPath = path3.resolve(normalizedExplicitPath);
126260
- if ((0, import_node_fs2.existsSync)(explicitFullPath) && (0, import_node_fs2.statSync)(explicitFullPath).isFile()) {
126486
+ if ((0, import_node_fs3.existsSync)(explicitFullPath) && (0, import_node_fs3.statSync)(explicitFullPath).isFile()) {
126261
126487
  return {
126262
126488
  repoRelativePath: normalizedExplicitPath,
126263
126489
  configRelativePath: normalizeToPosix(path3.join("..", normalizedExplicitPath))
@@ -126320,25 +126546,35 @@ async function upsertEnvironments(inputs, dependencies, resourcesState) {
126320
126546
  for (const envName of inputs.environments) {
126321
126547
  const runtimeUrl = String(inputs.envRuntimeUrls[envName] || "").trim();
126322
126548
  const values = buildEnvironmentValues(envName, runtimeUrl);
126323
- const existingUid = envUids[envName];
126324
- if (existingUid) {
126325
- await dependencies.postman.updateEnvironment(
126326
- existingUid,
126327
- `${inputs.projectName} - ${envName}`,
126328
- values
126549
+ const displayName = `${inputs.projectName} - ${envName}`;
126550
+ let existingUid = String(envUids[envName] || "").trim();
126551
+ if (!existingUid) {
126552
+ const discovered = await dependencies.postman.findEnvironmentByName(
126553
+ inputs.workspaceId,
126554
+ displayName
126329
126555
  );
126556
+ if (discovered?.uid) {
126557
+ existingUid = discovered.uid;
126558
+ dependencies.core.info(
126559
+ `Discovered existing environment for ${displayName}: ${existingUid}`
126560
+ );
126561
+ }
126562
+ }
126563
+ if (existingUid) {
126564
+ await dependencies.postman.updateEnvironment(existingUid, displayName, values);
126565
+ envUids[envName] = existingUid;
126330
126566
  continue;
126331
126567
  }
126332
126568
  envUids[envName] = await dependencies.postman.createEnvironment(
126333
126569
  inputs.workspaceId,
126334
- `${inputs.projectName} - ${envName}`,
126570
+ displayName,
126335
126571
  values
126336
126572
  );
126337
126573
  }
126338
126574
  return envUids;
126339
126575
  }
126340
126576
  function ensureDir(path5) {
126341
- (0, import_node_fs2.mkdirSync)(path5, { recursive: true });
126577
+ (0, import_node_fs3.mkdirSync)(path5, { recursive: true });
126342
126578
  }
126343
126579
  function getCollectionDirectoryName(kind, projectName) {
126344
126580
  if (kind === "Baseline") {
@@ -126375,7 +126611,7 @@ function stripVolatileFields(obj) {
126375
126611
  }
126376
126612
  function writeJsonFile(path5, content, normalize3 = false) {
126377
126613
  const data = normalize3 ? stripVolatileFields(content) : content;
126378
- (0, import_node_fs2.writeFileSync)(path5, JSON.stringify(data, null, 2));
126614
+ (0, import_node_fs3.writeFileSync)(path5, JSON.stringify(data, null, 2));
126379
126615
  }
126380
126616
  function buildResourcesManifest(workspaceId, collectionMap, envMap, artifactDir, localSpecRefs, mappedSpecRef, specId) {
126381
126617
  const manifest = {
@@ -126449,21 +126685,21 @@ function assertPathWithinCwd(targetPath, fieldName) {
126449
126685
  if (!rawPath || hasControlCharacter2(originalPath) || path3.isAbsolute(rawPath) || path3.win32.isAbsolute(rawPath) || segments.includes("..") || rawPath.startsWith(":") || hasControlCharacter2(rawPath)) {
126450
126686
  throw new Error(`${fieldName} must stay within the repository root; received ${targetPath}`);
126451
126687
  }
126452
- const base = (0, import_node_fs2.realpathSync)(process.cwd());
126688
+ const base = (0, import_node_fs3.realpathSync)(process.cwd());
126453
126689
  const resolved = path3.resolve(base, rawPath);
126454
126690
  const relative2 = path3.relative(base, resolved);
126455
126691
  if (relative2.startsWith("..") || path3.isAbsolute(relative2)) {
126456
126692
  throw new Error(`${fieldName} must stay within the repository root; received ${targetPath}`);
126457
126693
  }
126458
126694
  let existingPath = resolved;
126459
- while (!(0, import_node_fs2.existsSync)(existingPath)) {
126695
+ while (!(0, import_node_fs3.existsSync)(existingPath)) {
126460
126696
  const parent = path3.dirname(existingPath);
126461
126697
  if (parent === existingPath) {
126462
126698
  break;
126463
126699
  }
126464
126700
  existingPath = parent;
126465
126701
  }
126466
- const realExistingPath = (0, import_node_fs2.realpathSync)(existingPath);
126702
+ const realExistingPath = (0, import_node_fs3.realpathSync)(existingPath);
126467
126703
  const realRelative = path3.relative(base, realExistingPath);
126468
126704
  if (realRelative.startsWith("..") || path3.isAbsolute(realRelative)) {
126469
126705
  throw new Error(`${fieldName} must stay within the repository root; received ${targetPath}`);
@@ -126491,8 +126727,8 @@ async function exportArtifacts(inputs, dependencies, envUids, assetProjectName)
126491
126727
  ensureDir(specsDir);
126492
126728
  ensureDir(".postman");
126493
126729
  const globalsFilePath = `${globalsDir}/workspace.globals.yaml`;
126494
- if (!(0, import_node_fs2.existsSync)(globalsFilePath)) {
126495
- (0, import_node_fs2.writeFileSync)(globalsFilePath, "name: Globals\nvalues: []\n");
126730
+ if (!(0, import_node_fs3.existsSync)(globalsFilePath)) {
126731
+ (0, import_node_fs3.writeFileSync)(globalsFilePath, "name: Globals\nvalues: []\n");
126496
126732
  }
126497
126733
  if (inputs.generateCiWorkflow) {
126498
126734
  const ciDir = inputs.ciWorkflowPath.split("/").slice(0, -1).join("/");
@@ -126528,7 +126764,7 @@ async function exportArtifacts(inputs, dependencies, envUids, assetProjectName)
126528
126764
  true
126529
126765
  );
126530
126766
  }
126531
- (0, import_node_fs2.writeFileSync)(".postman/resources.yaml", buildResourcesManifest(
126767
+ (0, import_node_fs3.writeFileSync)(".postman/resources.yaml", buildResourcesManifest(
126532
126768
  inputs.workspaceId,
126533
126769
  manifestCollections,
126534
126770
  envUids,
@@ -126538,7 +126774,7 @@ async function exportArtifacts(inputs, dependencies, envUids, assetProjectName)
126538
126774
  inputs.specId || void 0
126539
126775
  ));
126540
126776
  if (mappedSpec && Object.keys(manifestCollections).length > 0) {
126541
- (0, import_node_fs2.writeFileSync)(
126777
+ (0, import_node_fs3.writeFileSync)(
126542
126778
  ".postman/workflows.yaml",
126543
126779
  buildSpecCollectionWorkflowManifest(
126544
126780
  mappedSpec.configRelativePath,
@@ -126575,29 +126811,27 @@ function createRepoSummary(outputs, envUids, pushed) {
126575
126811
  });
126576
126812
  }
126577
126813
  async function commitAndPushGeneratedFiles(inputs, dependencies) {
126578
- if (!dependencies.repoMutation || inputs.repoWriteMode === "none") {
126579
- return { commitSha: "", resolvedCurrentRef: "", pushed: false };
126580
- }
126581
126814
  if (inputs.generateCiWorkflow) {
126815
+ assertPathWithinCwd(inputs.ciWorkflowPath, "ci-workflow-path");
126582
126816
  const ciWorkflow = renderCiWorkflow(inputs);
126583
126817
  const parts = inputs.ciWorkflowPath.split("/");
126584
126818
  if (parts.length > 1) {
126585
126819
  const dir = parts.slice(0, -1).join("/");
126586
126820
  ensureDir(dir);
126587
126821
  }
126588
- (0, import_node_fs2.writeFileSync)(inputs.ciWorkflowPath, ciWorkflow);
126822
+ (0, import_node_fs3.writeFileSync)(inputs.ciWorkflowPath, ciWorkflow);
126589
126823
  }
126590
- const provisionPath = ".github/workflows/provision.yml";
126591
- const provisionExists = inputs.provider === "github" && (0, import_node_fs2.existsSync)(provisionPath);
126592
- if (provisionExists) {
126593
- (0, import_node_fs2.rmSync)(provisionPath);
126824
+ if (!dependencies.repoMutation || inputs.repoWriteMode === "none") {
126825
+ return { commitSha: "", resolvedCurrentRef: "", pushed: false };
126594
126826
  }
126827
+ const provisionPath = ".github/workflows/provision.yml";
126828
+ const provisionExists = inputs.provider === "github" && (0, import_node_fs3.existsSync)(provisionPath);
126595
126829
  const stagePaths = [
126596
126830
  inputs.artifactDir,
126597
126831
  ".postman",
126598
126832
  inputs.generateCiWorkflow ? inputs.ciWorkflowPath : null,
126599
126833
  provisionExists ? provisionPath : null
126600
- ].filter((entry) => typeof entry === "string" && ((0, import_node_fs2.existsSync)(entry) || entry === provisionPath));
126834
+ ].filter((entry) => typeof entry === "string" && ((0, import_node_fs3.existsSync)(entry) || entry === provisionPath));
126601
126835
  if (stagePaths.length === 0) {
126602
126836
  dependencies.core.info("No generated repository paths were found; skipping repo mutation.");
126603
126837
  return {
@@ -126621,6 +126855,7 @@ async function commitAndPushGeneratedFiles(inputs, dependencies) {
126621
126855
  adoToken: inputs.provider === "azure-devops" ? inputs.adoToken : void 0,
126622
126856
  githubToken: inputs.provider === "azure-devops" ? void 0 : inputs.githubToken,
126623
126857
  fallbackToken: inputs.provider === "azure-devops" ? void 0 : inputs.ghFallbackToken,
126858
+ removePaths: provisionExists ? [provisionPath] : [],
126624
126859
  stagePaths
126625
126860
  });
126626
126861
  return {
@@ -126703,12 +126938,17 @@ async function runRepoSyncInner(inputs, dependencies) {
126703
126938
  const mockEnvUid = envUids.dev || envUids.prod || Object.values(envUids)[0];
126704
126939
  if (mockEnvUid) {
126705
126940
  let resolvedMockUrl = "";
126941
+ const mockName = `${assetProjectName} Mock`;
126706
126942
  if (inputs.mockUrl) {
126707
126943
  resolvedMockUrl = inputs.mockUrl;
126708
126944
  dependencies.core.info(`Reusing mock from explicit input: ${resolvedMockUrl}`);
126709
126945
  }
126710
126946
  if (!resolvedMockUrl && inputs.baselineCollectionId) {
126711
- const discovered = await dependencies.postman.findMockByCollection(inputs.baselineCollectionId);
126947
+ const discovered = await dependencies.postman.findMockByCollection(
126948
+ inputs.baselineCollectionId,
126949
+ mockEnvUid,
126950
+ mockName
126951
+ );
126712
126952
  if (discovered) {
126713
126953
  resolvedMockUrl = discovered.mockUrl;
126714
126954
  dependencies.core.info(`Discovered existing mock for collection ${inputs.baselineCollectionId}: ${resolvedMockUrl}`);
@@ -126717,7 +126957,7 @@ async function runRepoSyncInner(inputs, dependencies) {
126717
126957
  if (!resolvedMockUrl) {
126718
126958
  const mock = await dependencies.postman.createMock(
126719
126959
  inputs.workspaceId,
126720
- `${assetProjectName} Mock`,
126960
+ mockName,
126721
126961
  inputs.baselineCollectionId,
126722
126962
  mockEnvUid
126723
126963
  );
@@ -126732,6 +126972,7 @@ async function runRepoSyncInner(inputs, dependencies) {
126732
126972
  const effectiveCron = inputs.monitorCron && inputs.monitorCron.trim() ? inputs.monitorCron.trim() : "";
126733
126973
  if (monitorEnvUid && inputs.monitorType !== "cli") {
126734
126974
  let resolvedMonitorId = "";
126975
+ const monitorName = `${assetProjectName} - Smoke Monitor`;
126735
126976
  if (inputs.monitorId) {
126736
126977
  const valid = await dependencies.postman.monitorExists(inputs.monitorId);
126737
126978
  if (valid) {
@@ -126742,7 +126983,11 @@ async function runRepoSyncInner(inputs, dependencies) {
126742
126983
  }
126743
126984
  }
126744
126985
  if (!resolvedMonitorId && inputs.smokeCollectionId) {
126745
- const discovered = await dependencies.postman.findMonitorByCollection(inputs.smokeCollectionId);
126986
+ const discovered = await dependencies.postman.findMonitorByCollection(
126987
+ inputs.smokeCollectionId,
126988
+ monitorEnvUid,
126989
+ monitorName
126990
+ );
126746
126991
  if (discovered) {
126747
126992
  resolvedMonitorId = discovered.uid;
126748
126993
  dependencies.core.info(`Discovered existing monitor for collection ${inputs.smokeCollectionId}: ${resolvedMonitorId}`);
@@ -126751,7 +126996,7 @@ async function runRepoSyncInner(inputs, dependencies) {
126751
126996
  if (!resolvedMonitorId) {
126752
126997
  resolvedMonitorId = await dependencies.postman.createMonitor(
126753
126998
  inputs.workspaceId,
126754
- `${assetProjectName} - Smoke Monitor`,
126999
+ monitorName,
126755
127000
  inputs.smokeCollectionId,
126756
127001
  monitorEnvUid,
126757
127002
  effectiveCron || void 0
@@ -126964,6 +127209,7 @@ function createRepoSyncDependencies(inputs, resolved, factories, options = {}) {
126964
127209
  createEnvironment: gatewayAssets.createEnvironment.bind(gatewayAssets),
126965
127210
  getEnvironment: gatewayAssets.getEnvironment.bind(gatewayAssets),
126966
127211
  updateEnvironment: gatewayAssets.updateEnvironment.bind(gatewayAssets),
127212
+ findEnvironmentByName: gatewayAssets.findEnvironmentByName.bind(gatewayAssets),
126967
127213
  // Collection read via the v3 export endpoint — returns canonical v3 IR,
126968
127214
  // written to disk by `convertAndSplitAnyCollection`. PMAK is never used for
126969
127215
  // collection reads.
@@ -127020,6 +127266,71 @@ function createRepoSyncDependencies(inputs, resolved, factories, options = {}) {
127020
127266
 
127021
127267
  // src/cli.ts
127022
127268
  var execFileAsync = (0, import_node_util.promisify)(import_node_child_process.execFile);
127269
+ var CLI_INPUT_NAMES = [
127270
+ "project-name",
127271
+ "workspace-id",
127272
+ "baseline-collection-id",
127273
+ "smoke-collection-id",
127274
+ "contract-collection-id",
127275
+ "collection-sync-mode",
127276
+ "spec-sync-mode",
127277
+ "release-label",
127278
+ "environments-json",
127279
+ "git-provider",
127280
+ "ado-token",
127281
+ "repo-url",
127282
+ "integration-backend",
127283
+ "workspace-link-enabled",
127284
+ "environment-sync-enabled",
127285
+ "system-env-map-json",
127286
+ "environment-uids-json",
127287
+ "env-runtime-urls-json",
127288
+ "artifact-dir",
127289
+ "repo-write-mode",
127290
+ "repository",
127291
+ "current-ref",
127292
+ "github-head-ref",
127293
+ "github-ref-name",
127294
+ "committer-name",
127295
+ "committer-email",
127296
+ "postman-api-key",
127297
+ "postman-access-token",
127298
+ "credential-preflight",
127299
+ "github-token",
127300
+ "gh-fallback-token",
127301
+ "ci-workflow-base64",
127302
+ "generate-ci-workflow",
127303
+ "monitor-type",
127304
+ "ci-workflow-path",
127305
+ "org-mode",
127306
+ "monitor-id",
127307
+ "mock-url",
127308
+ "monitor-cron",
127309
+ "ssl-client-cert",
127310
+ "ssl-client-key",
127311
+ "ssl-client-passphrase",
127312
+ "ssl-extra-ca-certs",
127313
+ "spec-id",
127314
+ "spec-path",
127315
+ "team-id",
127316
+ "postman-region",
127317
+ "postman-stack"
127318
+ ];
127319
+ var HELP_TEXT = `Usage: postman-repo-sync [options]
127320
+
127321
+ Sync Postman artifacts into a git repository.
127322
+
127323
+ Options:
127324
+ --help Show this help and exit
127325
+ --version Show version and exit
127326
+ --result-json <path> Write JSON result (default: postman-repo-sync-result.json)
127327
+ --dotenv-path <path> Optional dotenv output path
127328
+ --<input-name> <value> Action input as kebab-case flag (same names as action.yml)
127329
+
127330
+ Examples:
127331
+ postman-repo-sync --help
127332
+ postman-repo-sync --repo-write-mode none --workspace-id <id> ...
127333
+ `;
127023
127334
  var ConsoleReporter = class {
127024
127335
  info(message) {
127025
127336
  console.error(message);
@@ -127032,19 +127343,6 @@ var ConsoleReporter = class {
127032
127343
  setSecret() {
127033
127344
  }
127034
127345
  };
127035
- function readFlag(argv, name) {
127036
- const prefix = `--${name}=`;
127037
- for (let index = 0; index < argv.length; index += 1) {
127038
- const arg = argv[index];
127039
- if (arg === `--${name}`) {
127040
- return argv[index + 1];
127041
- }
127042
- if (arg?.startsWith(prefix)) {
127043
- return arg.slice(prefix.length);
127044
- }
127045
- }
127046
- return void 0;
127047
- }
127048
127346
  function normalizeCliFlag(name) {
127049
127347
  return `INPUT_${name.replace(/-/g, "_").toUpperCase()}`;
127050
127348
  }
@@ -127107,68 +127405,117 @@ function createCliExec(secretMasker) {
127107
127405
  }
127108
127406
  };
127109
127407
  }
127408
+ function resolvePackageVersion() {
127409
+ const candidates = [];
127410
+ if (typeof __filename === "string" && __filename) {
127411
+ candidates.push(import_node_path3.default.join(import_node_path3.default.dirname(__filename), "..", "package.json"));
127412
+ }
127413
+ candidates.push(import_node_path3.default.join(process.cwd(), "package.json"));
127414
+ for (const packageJsonPath of candidates) {
127415
+ try {
127416
+ const packageJson = JSON.parse((0, import_node_fs4.readFileSync)(packageJsonPath, "utf8"));
127417
+ if (packageJson.name === "@postman-cse/onboarding-repo-sync" && packageJson.version) {
127418
+ return String(packageJson.version).trim();
127419
+ }
127420
+ } catch {
127421
+ }
127422
+ }
127423
+ return "0.0.0";
127424
+ }
127425
+ function wantsHelp(argv) {
127426
+ return argv.includes("--help") || argv.includes("-h");
127427
+ }
127428
+ function wantsVersion(argv) {
127429
+ return argv.includes("--version") || argv.includes("-V");
127430
+ }
127431
+ function runnerFormEnvName(name) {
127432
+ return `INPUT_${name.replace(/ /g, "_").toUpperCase()}`;
127433
+ }
127110
127434
  function parseCliArgs(argv, env = process.env) {
127111
- const inputNames = [
127112
- "project-name",
127113
- "workspace-id",
127114
- "baseline-collection-id",
127115
- "smoke-collection-id",
127116
- "contract-collection-id",
127117
- "collection-sync-mode",
127118
- "spec-sync-mode",
127119
- "release-label",
127120
- "environments-json",
127121
- "git-provider",
127122
- "ado-token",
127123
- "repo-url",
127124
- "integration-backend",
127125
- "workspace-link-enabled",
127126
- "environment-sync-enabled",
127127
- "system-env-map-json",
127128
- "environment-uids-json",
127129
- "env-runtime-urls-json",
127130
- "artifact-dir",
127131
- "repo-write-mode",
127132
- "repository",
127133
- "current-ref",
127134
- "github-head-ref",
127135
- "github-ref-name",
127136
- "committer-name",
127137
- "committer-email",
127138
- "postman-api-key",
127139
- "postman-access-token",
127140
- "credential-preflight",
127141
- "github-token",
127142
- "gh-fallback-token",
127143
- "ci-workflow-base64",
127144
- "generate-ci-workflow",
127145
- "monitor-type",
127146
- "ci-workflow-path",
127147
- "org-mode",
127148
- "monitor-id",
127149
- "mock-url",
127150
- "monitor-cron",
127151
- "ssl-client-cert",
127152
- "ssl-client-key",
127153
- "ssl-client-passphrase",
127154
- "ssl-extra-ca-certs",
127155
- "spec-id",
127156
- "spec-path",
127157
- "team-id",
127158
- "postman-region",
127159
- "postman-stack"
127160
- ];
127435
+ const inputNames = new Set(CLI_INPUT_NAMES);
127161
127436
  const inputEnv = { ...env };
127162
- for (const name of inputNames) {
127163
- const value = readFlag(argv, name);
127164
- if (value !== void 0) {
127165
- inputEnv[normalizeCliFlag(name)] = value;
127437
+ let resultJsonPath = "postman-repo-sync-result.json";
127438
+ let dotenvPath;
127439
+ const seenFlags = /* @__PURE__ */ new Map();
127440
+ let sawHelp = false;
127441
+ let sawVersion = false;
127442
+ for (let index = 0; index < argv.length; index += 1) {
127443
+ const arg = argv[index];
127444
+ if (!arg) {
127445
+ continue;
127446
+ }
127447
+ if (arg === "--help" || arg === "-h") {
127448
+ sawHelp = true;
127449
+ continue;
127450
+ }
127451
+ if (arg === "--version" || arg === "-V") {
127452
+ sawVersion = true;
127453
+ continue;
127454
+ }
127455
+ if (!arg.startsWith("--")) {
127456
+ throw new Error(`Unexpected positional argument: ${arg}`);
127457
+ }
127458
+ let name;
127459
+ let value;
127460
+ const separator = arg.indexOf("=");
127461
+ if (separator !== -1) {
127462
+ name = arg.slice(2, separator);
127463
+ value = arg.slice(separator + 1);
127464
+ if (value === "") {
127465
+ throw new Error(`Missing value for --${name}`);
127466
+ }
127467
+ } else {
127468
+ name = arg.slice(2);
127469
+ const next = argv[index + 1];
127470
+ if (next === void 0 || next.startsWith("--")) {
127471
+ throw new Error(`Missing value for --${name}`);
127472
+ }
127473
+ value = next;
127474
+ index += 1;
127475
+ }
127476
+ if (!name) {
127477
+ throw new Error(`Unknown option ${arg}`);
127478
+ }
127479
+ if (name === "result-json") {
127480
+ const previous2 = seenFlags.get(name);
127481
+ if (previous2 !== void 0 && previous2 !== value) {
127482
+ throw new Error(`Conflicting values for --${name}`);
127483
+ }
127484
+ seenFlags.set(name, value);
127485
+ resultJsonPath = value;
127486
+ continue;
127487
+ }
127488
+ if (name === "dotenv-path") {
127489
+ const previous2 = seenFlags.get(name);
127490
+ if (previous2 !== void 0 && previous2 !== value) {
127491
+ throw new Error(`Conflicting values for --${name}`);
127492
+ }
127493
+ seenFlags.set(name, value);
127494
+ dotenvPath = value;
127495
+ continue;
127496
+ }
127497
+ if (!inputNames.has(name)) {
127498
+ throw new Error(`Unknown option --${name}`);
127499
+ }
127500
+ const previous = seenFlags.get(name);
127501
+ if (previous !== void 0 && previous !== value) {
127502
+ throw new Error(`Conflicting values for --${name}`);
127503
+ }
127504
+ seenFlags.set(name, value);
127505
+ const normalized = normalizeCliFlag(name);
127506
+ const runnerForm = runnerFormEnvName(name);
127507
+ inputEnv[normalized] = value;
127508
+ if (runnerForm !== normalized) {
127509
+ delete inputEnv[runnerForm];
127166
127510
  }
127167
127511
  }
127512
+ if (sawHelp && sawVersion) {
127513
+ throw new Error("Cannot use --help and --version together");
127514
+ }
127168
127515
  return {
127169
127516
  inputEnv,
127170
- resultJsonPath: readFlag(argv, "result-json") ?? "postman-repo-sync-result.json",
127171
- dotenvPath: readFlag(argv, "dotenv-path")
127517
+ resultJsonPath,
127518
+ dotenvPath
127172
127519
  };
127173
127520
  }
127174
127521
  function toDotenv(outputs) {
@@ -127216,6 +127563,20 @@ function createCliDependencies(inputs, resolved) {
127216
127563
  );
127217
127564
  }
127218
127565
  async function runCli(argv = process.argv.slice(2), runtime = {}) {
127566
+ const writeStdout = runtime.writeStdout ?? ((chunk) => process.stdout.write(chunk));
127567
+ if (wantsHelp(argv) && wantsVersion(argv)) {
127568
+ throw new Error("Cannot use --help and --version together");
127569
+ }
127570
+ if (wantsHelp(argv)) {
127571
+ writeStdout(HELP_TEXT.endsWith("\n") ? HELP_TEXT : `${HELP_TEXT}
127572
+ `);
127573
+ return;
127574
+ }
127575
+ if (wantsVersion(argv)) {
127576
+ writeStdout(`${resolvePackageVersion()}
127577
+ `);
127578
+ return;
127579
+ }
127219
127580
  const env = runtime.env ?? process.env;
127220
127581
  const config = parseCliArgs(argv, env);
127221
127582
  const inputs = resolveInputs(config.inputEnv);
@@ -127267,7 +127628,6 @@ async function runCli(argv = process.argv.slice(2), runtime = {}) {
127267
127628
  const result = await (runtime.executeRepoSync ?? runRepoSync)(inputs, dependencies);
127268
127629
  await writeOptionalFile(config.resultJsonPath, JSON.stringify(result, null, 2));
127269
127630
  await writeOptionalFile(config.dotenvPath, toDotenv(result));
127270
- const writeStdout = runtime.writeStdout ?? ((chunk) => process.stdout.write(chunk));
127271
127631
  writeStdout(`${JSON.stringify(result, null, 2)}
127272
127632
  `);
127273
127633
  }
@@ -127278,7 +127638,7 @@ function isEntrypoint(currentPath, entrypointPath) {
127278
127638
  return false;
127279
127639
  }
127280
127640
  try {
127281
- return (0, import_node_fs3.realpathSync)(currentPath) === (0, import_node_fs3.realpathSync)(entrypointPath);
127641
+ return (0, import_node_fs4.realpathSync)(currentPath) === (0, import_node_fs4.realpathSync)(entrypointPath);
127282
127642
  } catch {
127283
127643
  return import_node_path3.default.resolve(currentPath) === import_node_path3.default.resolve(entrypointPath);
127284
127644
  }