@postman-cse/onboarding-repo-sync 2.1.8 → 2.1.9

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/index.cjs CHANGED
@@ -125136,6 +125136,7 @@ var POSTMAN_ENDPOINT_PROFILES = {
125136
125136
  bifrostBaseUrl: "https://bifrost-premium-https-v4.gw.postman.com",
125137
125137
  fallbackBaseUrl: "https://go.postman.co/_api",
125138
125138
  cliInstallUrl: "https://dl-cli.pstmn.io/install/unix.sh",
125139
+ cliWindowsInstallUrl: "https://dl-cli.pstmn.io/install/win64.ps1",
125139
125140
  iapubBaseUrl: "https://iapub.postman.co"
125140
125141
  },
125141
125142
  beta: {
@@ -125143,6 +125144,7 @@ var POSTMAN_ENDPOINT_PROFILES = {
125143
125144
  bifrostBaseUrl: "https://bifrost-https-v4.gw.postman-beta.com",
125144
125145
  fallbackBaseUrl: "https://go.postman-beta.co/_api",
125145
125146
  cliInstallUrl: "https://dl-cli.pstmn-beta.io/install/unix.sh",
125147
+ cliWindowsInstallUrl: "https://dl-cli.pstmn-beta.io/install/win64.ps1",
125146
125148
  iapubBaseUrl: "https://iapub.postman.co"
125147
125149
  }
125148
125150
  };
@@ -125176,6 +125178,7 @@ function resolvePostmanEndpointProfile(stack, region = "us") {
125176
125178
 
125177
125179
  // src/lib/ci-workflow-template.ts
125178
125180
  var DEFAULT_POSTMAN_CLI_INSTALL_URL = POSTMAN_ENDPOINT_PROFILES.prod.cliInstallUrl;
125181
+ var DEFAULT_POSTMAN_CLI_WINDOWS_INSTALL_URL = POSTMAN_ENDPOINT_PROFILES.prod.cliWindowsInstallUrl;
125179
125182
  function validateHttpsInstallUrl(url) {
125180
125183
  const safeUrlPattern = /^https:\/\/[A-Za-z0-9.-]+\/[A-Za-z0-9._~/?=&%-]+$/;
125181
125184
  if (!safeUrlPattern.test(url)) {
@@ -125192,7 +125195,18 @@ function resolvePostmanRegion(postmanRegionOption) {
125192
125195
  }
125193
125196
  return postmanRegion;
125194
125197
  }
125198
+ function resolveCiRunnerOs(runnerOsOption) {
125199
+ const runnerOs = String(runnerOsOption || "").trim() || "linux";
125200
+ if (runnerOs === "linux" || runnerOs === "windows") {
125201
+ return runnerOs;
125202
+ }
125203
+ throw new Error("ci-runner-os must be one of: linux, windows; got: " + runnerOs);
125204
+ }
125195
125205
  function renderCiWorkflowTemplate(options = {}) {
125206
+ const runnerOs = resolveCiRunnerOs(options.runnerOs);
125207
+ if (runnerOs === "windows") {
125208
+ throw new Error("ci-runner-os=windows is currently supported for azure-devops workflows only");
125209
+ }
125196
125210
  const rawUrl = String(options.postmanCliInstallUrl || "").trim() || DEFAULT_POSTMAN_CLI_INSTALL_URL;
125197
125211
  const installUrl = validateHttpsInstallUrl(rawUrl);
125198
125212
  const postmanRegion = resolvePostmanRegion(options.postmanRegion);
@@ -125299,6 +125313,132 @@ function buildCiWorkflowLines(installUrl, postmanRegion) {
125299
125313
  ];
125300
125314
  }
125301
125315
  var CI_WORKFLOW_TEMPLATE = renderCiWorkflowTemplate();
125316
+ function buildAdoWindowsCollectionRunLines(displayName, collectionEnvironmentName) {
125317
+ return [
125318
+ " - pwsh: |",
125319
+ " $ErrorActionPreference = 'Stop'",
125320
+ " function Resolve-AdoOptional([string]$Value) {",
125321
+ " if ($Value -match '^\\$\\([^)]+\\)$') { return '' }",
125322
+ " return $Value",
125323
+ " }",
125324
+ ` $collectionUid = $env:${collectionEnvironmentName}`,
125325
+ " $ciEnvironment = Resolve-AdoOptional $env:CI_ENVIRONMENT",
125326
+ " if ([string]::IsNullOrWhiteSpace($ciEnvironment)) { $ciEnvironment = 'Production' }",
125327
+ ` $arguments = @('collection', 'run', $collectionUid, '-e', $env:POSTMAN_ENVIRONMENT_UID, '--report-events', '--env-var', "CI_ENVIRONMENT=$ciEnvironment")`,
125328
+ " $sslRoot = Join-Path $env:AGENT_TEMPDIRECTORY 'postman-ssl'",
125329
+ " $clientCert = Join-Path $sslRoot 'client.crt'",
125330
+ " $clientKey = Join-Path $sslRoot 'client.key'",
125331
+ " $caCert = Join-Path $sslRoot 'ca.crt'",
125332
+ " if (Test-Path -LiteralPath $clientCert) {",
125333
+ " $arguments += @('--ssl-client-cert', $clientCert, '--ssl-client-key', $clientKey)",
125334
+ " $passphrase = Resolve-AdoOptional $env:POSTMAN_SSL_CLIENT_PASSPHRASE",
125335
+ " if (-not [string]::IsNullOrWhiteSpace($passphrase)) {",
125336
+ " $arguments += @('--ssl-client-passphrase', $passphrase)",
125337
+ " }",
125338
+ " if (Test-Path -LiteralPath $caCert) {",
125339
+ " $arguments += @('--ssl-extra-ca-certs', $caCert)",
125340
+ " }",
125341
+ " }",
125342
+ " & postman @arguments",
125343
+ ` if ($LASTEXITCODE -ne 0) { throw '${displayName} failed with exit code ' + $LASTEXITCODE }`,
125344
+ ` displayName: ${displayName}`,
125345
+ " env:",
125346
+ ` ${collectionEnvironmentName}: $(${collectionEnvironmentName})`,
125347
+ " POSTMAN_ENVIRONMENT_UID: $(POSTMAN_ENVIRONMENT_UID)",
125348
+ " CI_ENVIRONMENT: $(CI_ENVIRONMENT)",
125349
+ " POSTMAN_SSL_CLIENT_PASSPHRASE: $(POSTMAN_SSL_CLIENT_PASSPHRASE)"
125350
+ ];
125351
+ }
125352
+ function buildAdoWindowsCiWorkflowLines(installUrl, postmanRegion) {
125353
+ return [
125354
+ "trigger:",
125355
+ " branches:",
125356
+ " include:",
125357
+ " - main",
125358
+ "schedules:",
125359
+ ' - cron: "0 */6 * * *"',
125360
+ " displayName: Scheduled run",
125361
+ " branches:",
125362
+ " include:",
125363
+ " - main",
125364
+ " always: true",
125365
+ "pool:",
125366
+ " vmImage: windows-latest",
125367
+ "steps:",
125368
+ " - checkout: self",
125369
+ " persistCredentials: true",
125370
+ " - pwsh: |",
125371
+ " $ErrorActionPreference = 'Stop'",
125372
+ " if (-not (Get-Command postman -ErrorAction SilentlyContinue)) {",
125373
+ " [System.Net.ServicePointManager]::SecurityProtocol = 3072",
125374
+ " Invoke-Expression ((New-Object System.Net.WebClient).DownloadString($env:POSTMAN_CLI_INSTALL_URL))",
125375
+ " }",
125376
+ " & postman --version",
125377
+ " if ($LASTEXITCODE -ne 0) { throw 'Postman CLI installation failed' }",
125378
+ " displayName: Install Postman CLI",
125379
+ " env:",
125380
+ ` POSTMAN_CLI_INSTALL_URL: ${installUrl}`,
125381
+ " - pwsh: |",
125382
+ " $ErrorActionPreference = 'Stop'",
125383
+ " $arguments = @('login', '--with-api-key', $env:POSTMAN_API_KEY)",
125384
+ ...postmanRegion === "eu" ? [" $arguments += @('--region', 'eu')"] : [],
125385
+ " & postman @arguments",
125386
+ " if ($LASTEXITCODE -ne 0) { throw 'Postman CLI login failed' }",
125387
+ " displayName: Login to Postman CLI",
125388
+ " env:",
125389
+ " POSTMAN_API_KEY: $(POSTMAN_API_KEY)",
125390
+ " - pwsh: |",
125391
+ " $ErrorActionPreference = 'Stop'",
125392
+ " $section = ''",
125393
+ " $smoke = ''",
125394
+ " $contract = ''",
125395
+ " $environment = ''",
125396
+ " $fallbackEnvironment = ''",
125397
+ " foreach ($line in Get-Content -LiteralPath '.postman/resources.yaml') {",
125398
+ " if ($line -match '^ (collections|environments):\\s*$') { $section = $Matches[1]; continue }",
125399
+ " if ($line -notmatch '^ (.+?):\\s+(.+?)\\s*$') { continue }",
125400
+ ` $key = $Matches[1].Trim().Trim("'").Trim('"')`,
125401
+ ` $value = $Matches[2].Trim().Trim("'").Trim('"')`,
125402
+ " if ($section -eq 'collections' -and $key -match '\\[Smoke\\]') { $smoke = $value }",
125403
+ " if ($section -eq 'collections' -and $key -match '\\[Contract\\]') { $contract = $value }",
125404
+ " if ($section -eq 'environments') {",
125405
+ " if ([string]::IsNullOrWhiteSpace($fallbackEnvironment)) { $fallbackEnvironment = $value }",
125406
+ " if ($key -match 'prod\\.postman_environment\\.json$') { $environment = $value }",
125407
+ " }",
125408
+ " }",
125409
+ " if ([string]::IsNullOrWhiteSpace($environment)) { $environment = $fallbackEnvironment }",
125410
+ " if ([string]::IsNullOrWhiteSpace($smoke)) { throw 'Missing smoke collection UID in .postman/resources.yaml' }",
125411
+ " if ([string]::IsNullOrWhiteSpace($contract)) { throw 'Missing contract collection UID in .postman/resources.yaml' }",
125412
+ " if ([string]::IsNullOrWhiteSpace($environment)) { throw 'Missing environment UID in .postman/resources.yaml' }",
125413
+ ' Write-Host "##vso[task.setvariable variable=POSTMAN_SMOKE_COLLECTION_UID]$smoke"',
125414
+ ' Write-Host "##vso[task.setvariable variable=POSTMAN_CONTRACT_COLLECTION_UID]$contract"',
125415
+ ' Write-Host "##vso[task.setvariable variable=POSTMAN_ENVIRONMENT_UID]$environment"',
125416
+ " displayName: Resolve Postman Resource IDs",
125417
+ " - pwsh: |",
125418
+ " $ErrorActionPreference = 'Stop'",
125419
+ " function Resolve-AdoOptional([string]$Value) {",
125420
+ " if ($Value -match '^\\$\\([^)]+\\)$') { return '' }",
125421
+ " return $Value",
125422
+ " }",
125423
+ " $sslRoot = Join-Path $env:AGENT_TEMPDIRECTORY 'postman-ssl'",
125424
+ " New-Item -ItemType Directory -Path $sslRoot -Force | Out-Null",
125425
+ " [IO.File]::WriteAllBytes((Join-Path $sslRoot 'client.crt'), [Convert]::FromBase64String($env:POSTMAN_SSL_CLIENT_CERT_B64))",
125426
+ " [IO.File]::WriteAllBytes((Join-Path $sslRoot 'client.key'), [Convert]::FromBase64String($env:POSTMAN_SSL_CLIENT_KEY_B64))",
125427
+ " $extraCa = Resolve-AdoOptional $env:POSTMAN_SSL_EXTRA_CA_CERTS_B64",
125428
+ " if (-not [string]::IsNullOrWhiteSpace($extraCa)) {",
125429
+ " [IO.File]::WriteAllBytes((Join-Path $sslRoot 'ca.crt'), [Convert]::FromBase64String($extraCa))",
125430
+ " }",
125431
+ " condition: ne(variables['POSTMAN_SSL_CLIENT_CERT_B64'], '')",
125432
+ " displayName: Decode SSL certificates",
125433
+ " env:",
125434
+ " POSTMAN_SSL_CLIENT_CERT_B64: $(POSTMAN_SSL_CLIENT_CERT_B64)",
125435
+ " POSTMAN_SSL_CLIENT_KEY_B64: $(POSTMAN_SSL_CLIENT_KEY_B64)",
125436
+ " POSTMAN_SSL_EXTRA_CA_CERTS_B64: $(POSTMAN_SSL_EXTRA_CA_CERTS_B64)",
125437
+ ...buildAdoWindowsCollectionRunLines("Run Smoke Tests", "POSTMAN_SMOKE_COLLECTION_UID"),
125438
+ ...buildAdoWindowsCollectionRunLines("Run Contract Tests", "POSTMAN_CONTRACT_COLLECTION_UID"),
125439
+ ""
125440
+ ];
125441
+ }
125302
125442
  function buildAdoCiWorkflowLines(installUrl, postmanRegion) {
125303
125443
  return [
125304
125444
  "trigger:",
@@ -125478,10 +125618,12 @@ function renderGcWorkflowTemplate() {
125478
125618
  }
125479
125619
  var GC_WORKFLOW_TEMPLATE = renderGcWorkflowTemplate();
125480
125620
  function getCiWorkflowTemplate(provider, options = {}) {
125621
+ const runnerOs = resolveCiRunnerOs(options.runnerOs);
125481
125622
  if (provider === "azure-devops") {
125482
- const rawUrl = String(options.postmanCliInstallUrl || "").trim() || DEFAULT_POSTMAN_CLI_INSTALL_URL;
125623
+ const rawUrl = runnerOs === "windows" ? String(options.postmanCliWindowsInstallUrl || "").trim() || DEFAULT_POSTMAN_CLI_WINDOWS_INSTALL_URL : String(options.postmanCliInstallUrl || "").trim() || DEFAULT_POSTMAN_CLI_INSTALL_URL;
125483
125624
  const postmanRegion = resolvePostmanRegion(options.postmanRegion);
125484
- return buildAdoCiWorkflowLines(validateHttpsInstallUrl(rawUrl), postmanRegion).join("\n");
125625
+ const installUrl = validateHttpsInstallUrl(rawUrl);
125626
+ return (runnerOs === "windows" ? buildAdoWindowsCiWorkflowLines(installUrl, postmanRegion) : buildAdoCiWorkflowLines(installUrl, postmanRegion)).join("\n");
125485
125627
  }
125486
125628
  return renderCiWorkflowTemplate(options);
125487
125629
  }
@@ -126876,6 +127018,17 @@ var BifrostInternalIntegrationAdapter = class {
126876
127018
  mask: this.secretMasker
126877
127019
  };
126878
127020
  }
127021
+ /**
127022
+ * One-line non-fatal preflight reason: operation + entity (repo/path) + cause +
127023
+ * operator action. Secrets are redacted via secretMasker, then CR/LF and other
127024
+ * line-breaking whitespace are collapsed to spaces so CI logs stay one line.
127025
+ * Display-only: does not alter request repoUrl/path or probe classification.
127026
+ */
127027
+ unknownFilesystemLookupReason(repoUrl, fsPath, cause) {
127028
+ return this.secretMasker(
127029
+ `filesystem lookup for repository ${repoUrl} path ${fsPath} ${cause}; verify Bifrost connectivity/credentials then rerun`
127030
+ ).replace(/[\r\n\v\f\u2028\u2029]+/g, " ");
127031
+ }
126879
127032
  async associateSystemEnvironments(workspaceId, associations) {
126880
127033
  if (associations.length === 0) {
126881
127034
  return;
@@ -126946,7 +127099,11 @@ var BifrostInternalIntegrationAdapter = class {
126946
127099
  } catch {
126947
127100
  return {
126948
127101
  state: "unknown",
126949
- reason: `filesystem lookup returned non-JSON body (HTTP ${response.status})`
127102
+ reason: this.unknownFilesystemLookupReason(
127103
+ repoUrl,
127104
+ fsPath,
127105
+ `returned non-JSON body (HTTP ${response.status})`
127106
+ )
126950
127107
  };
126951
127108
  }
126952
127109
  const errorMetaWorkspaceId = typeof parsed?.error?.meta?.workspaceId === "string" && parsed.error.meta.workspaceId.trim() ? parsed.error.meta.workspaceId.trim() : void 0;
@@ -126961,7 +127118,11 @@ var BifrostInternalIntegrationAdapter = class {
126961
127118
  if (!id) {
126962
127119
  return {
126963
127120
  state: "unknown",
126964
- reason: "filesystem lookup returned 200 without a workspace id"
127121
+ reason: this.unknownFilesystemLookupReason(
127122
+ repoUrl,
127123
+ fsPath,
127124
+ "returned 200 without a workspace id"
127125
+ )
126965
127126
  };
126966
127127
  }
126967
127128
  const name = typeof parsed.data.name === "string" && parsed.data.name.trim() ? parsed.data.name.trim() : void 0;
@@ -126979,17 +127140,30 @@ var BifrostInternalIntegrationAdapter = class {
126979
127140
  }
126980
127141
  return {
126981
127142
  state: "unknown",
126982
- reason: "filesystem lookup returned 403 without error.meta.workspaceId"
127143
+ reason: this.unknownFilesystemLookupReason(
127144
+ repoUrl,
127145
+ fsPath,
127146
+ "returned 403 without error.meta.workspaceId"
127147
+ )
126983
127148
  };
126984
127149
  }
126985
127150
  return {
126986
127151
  state: "unknown",
126987
- reason: `filesystem lookup returned HTTP ${response.status}`
127152
+ reason: this.unknownFilesystemLookupReason(
127153
+ repoUrl,
127154
+ fsPath,
127155
+ `returned HTTP ${response.status}`
127156
+ )
126988
127157
  };
126989
127158
  } catch (error2) {
127159
+ const cause = error2 instanceof Error ? error2.message : String(error2);
126990
127160
  return {
126991
127161
  state: "unknown",
126992
- reason: error2 instanceof Error ? error2.message : String(error2)
127162
+ reason: this.unknownFilesystemLookupReason(
127163
+ repoUrl,
127164
+ fsPath,
127165
+ `failed: ${cause}`
127166
+ )
126993
127167
  };
126994
127168
  }
126995
127169
  }
@@ -127203,6 +127377,12 @@ var postmanRepoSyncActionContract = {
127203
127377
  required: false,
127204
127378
  default: ".github/workflows/ci.yml"
127205
127379
  },
127380
+ "ci-runner-os": {
127381
+ description: "Runner operating system for the generated CI workflow.",
127382
+ required: false,
127383
+ default: "linux",
127384
+ allowedValues: ["linux", "windows"]
127385
+ },
127206
127386
  "project-name": {
127207
127387
  description: "Service project name used for environment, mock, and monitor naming.",
127208
127388
  required: true
@@ -128914,6 +129094,25 @@ function parseAssetMarker(description) {
128914
129094
  }
128915
129095
 
128916
129096
  // src/index.ts
129097
+ var identitySecretMasker = (input) => input;
129098
+ function resolveRepoSyncMasker(dependencies) {
129099
+ return dependencies.secretMasker ?? identitySecretMasker;
129100
+ }
129101
+ function causeText(cause) {
129102
+ return cause instanceof Error ? cause.message : String(cause);
129103
+ }
129104
+ function toOneLineDisplay(value) {
129105
+ return String(value ?? "").replace(/[\r\n\u0085\u2028\u2029\v\f]+/g, " ").replace(/[ \t]{2,}/g, " ").trim();
129106
+ }
129107
+ function formatOrchestrationIssue(params) {
129108
+ const operation = toOneLineDisplay(params.mask(params.operation));
129109
+ const entity = toOneLineDisplay(params.mask(params.entity));
129110
+ const maskedCause = toOneLineDisplay(params.mask(causeText(params.cause)));
129111
+ const detail = toOneLineDisplay(params.mask(params.detail ?? ""));
129112
+ const remediation = toOneLineDisplay(params.mask(params.remediation));
129113
+ const body = `${operation} failed for ${entity}: ${maskedCause}${detail}`.replace(/[.]+$/u, "");
129114
+ return toOneLineDisplay(`${body}. ${remediation}`);
129115
+ }
128917
129116
  function parseBooleanInput(value, defaultValue) {
128918
129117
  const normalized = String(value || "").trim().toLowerCase();
128919
129118
  if (!normalized) return defaultValue;
@@ -129013,6 +129212,17 @@ function parseBranchStrategy(value) {
129013
129212
  `Unsupported branch-strategy "${normalized}". Supported values: ${allowed.join(", ")}`
129014
129213
  );
129015
129214
  }
129215
+ function parseCiRunnerOs(value) {
129216
+ const definition = postmanRepoSyncActionContract.inputs["ci-runner-os"];
129217
+ const allowed = definition.allowedValues ?? [];
129218
+ const normalized = String(value || "").trim() || (definition.default ?? "linux");
129219
+ if (allowed.includes(normalized)) {
129220
+ return normalized;
129221
+ }
129222
+ throw new Error(
129223
+ `Unsupported ci-runner-os "${normalized}". Supported values: ${allowed.join(", ")}`
129224
+ );
129225
+ }
129016
129226
  function normalizeReleaseLabel(value) {
129017
129227
  const cleaned = normalizeInputValue(value).replace(/^refs\/heads\//, "").replace(/^refs\/tags\//, "").replace(/\s+/g, "-").replace(/[^A-Za-z0-9._-]/g, "-").replace(/-+/g, "-").replace(/^[._-]+|[._-]+$/g, "");
129018
129228
  return cleaned;
@@ -129090,6 +129300,7 @@ function resolveInputs(env = process.env) {
129090
129300
  provider: repoContext.provider,
129091
129301
  ciWorkflowBase64: getInput2("ci-workflow-base64", env),
129092
129302
  generateCiWorkflow: parseBooleanInput(getInput2("generate-ci-workflow", env), true),
129303
+ ciRunnerOs: parseCiRunnerOs(getInput2("ci-runner-os", env)),
129093
129304
  monitorType: getInput2("monitor-type", env) || "cloud",
129094
129305
  ciWorkflowPath: getInput2("ci-workflow-path", env) || (repoContext.provider === "azure-devops" ? "azure-pipelines.yml" : ".github/workflows/ci.yml"),
129095
129306
  orgMode: parseBooleanInput(getInput2("org-mode", env), false),
@@ -129108,6 +129319,7 @@ function resolveInputs(env = process.env) {
129108
129319
  postmanBifrostBase: endpointProfile.bifrostBaseUrl,
129109
129320
  postmanFallbackBase: endpointProfile.fallbackBaseUrl,
129110
129321
  postmanCliInstallUrl: endpointProfile.cliInstallUrl,
129322
+ postmanCliWindowsInstallUrl: endpointProfile.cliWindowsInstallUrl,
129111
129323
  postmanIapubBase: endpointProfile.iapubBaseUrl
129112
129324
  };
129113
129325
  }
@@ -129377,6 +129589,7 @@ function readActionInputs(actionCore) {
129377
129589
  INPUT_GH_FALLBACK_TOKEN: ghFallbackToken,
129378
129590
  INPUT_CI_WORKFLOW_BASE64: readInput(actionCore, "ci-workflow-base64"),
129379
129591
  INPUT_GENERATE_CI_WORKFLOW: readInput(actionCore, "generate-ci-workflow"),
129592
+ INPUT_CI_RUNNER_OS: readInput(actionCore, "ci-runner-os"),
129380
129593
  INPUT_MONITOR_TYPE: readInput(actionCore, "monitor-type") || "cloud",
129381
129594
  INPUT_CI_WORKFLOW_PATH: readInput(actionCore, "ci-workflow-path"),
129382
129595
  INPUT_ORG_MODE: readInput(actionCore, "org-mode"),
@@ -129494,19 +129707,34 @@ async function upsertEnvironments(inputs, dependencies, resourcesState, assetMar
129494
129707
  if (!inputs.workspaceId) {
129495
129708
  return envUids;
129496
129709
  }
129710
+ const mask = resolveRepoSyncMasker(dependencies);
129711
+ const envRemediation = "verify access-token/team/workspace permissions then rerun";
129497
129712
  for (const envName of inputs.environments) {
129498
129713
  const runtimeUrl = String(inputs.envRuntimeUrls[envName] || "").trim();
129499
129714
  const displayName = `${inputs.projectName} - ${envName}`;
129500
129715
  let existingUid = String(envUids[envName] || "").trim();
129501
129716
  if (!existingUid) {
129502
- const discovered = await dependencies.postman.findEnvironmentByName(
129503
- inputs.workspaceId,
129504
- displayName
129505
- );
129506
- if (discovered?.uid) {
129507
- existingUid = discovered.uid;
129508
- dependencies.core.info(
129509
- `Discovered existing environment for ${displayName}: ${existingUid}`
129717
+ try {
129718
+ const discovered = await dependencies.postman.findEnvironmentByName(
129719
+ inputs.workspaceId,
129720
+ displayName
129721
+ );
129722
+ if (discovered?.uid) {
129723
+ existingUid = discovered.uid;
129724
+ dependencies.core.info(
129725
+ `Discovered existing environment for ${displayName}: ${existingUid}`
129726
+ );
129727
+ }
129728
+ } catch (error2) {
129729
+ throw new Error(
129730
+ formatOrchestrationIssue({
129731
+ operation: "Environment discovery",
129732
+ entity: `workspace ${inputs.workspaceId} environment "${displayName}"`,
129733
+ cause: error2,
129734
+ remediation: envRemediation,
129735
+ mask
129736
+ }),
129737
+ { cause: error2 }
129510
129738
  );
129511
129739
  }
129512
129740
  }
@@ -129526,17 +129754,43 @@ async function upsertEnvironments(inputs, dependencies, resourcesState, assetMar
129526
129754
  }
129527
129755
  const values2 = buildEnvironmentValues(envName, runtimeUrl);
129528
129756
  if (marker) values2.push({ key: "x-pm-onboarding", value: JSON.stringify(marker), type: "default" });
129529
- await dependencies.postman.updateEnvironment(existingUid, displayName, values2);
129757
+ try {
129758
+ await dependencies.postman.updateEnvironment(existingUid, displayName, values2);
129759
+ } catch (error2) {
129760
+ throw new Error(
129761
+ formatOrchestrationIssue({
129762
+ operation: "Environment update",
129763
+ entity: `workspace ${inputs.workspaceId} environment "${displayName}" (uid ${existingUid})`,
129764
+ cause: error2,
129765
+ remediation: envRemediation,
129766
+ mask
129767
+ }),
129768
+ { cause: error2 }
129769
+ );
129770
+ }
129530
129771
  envUids[envName] = existingUid;
129531
129772
  continue;
129532
129773
  }
129533
129774
  const values = buildEnvironmentValues(envName, runtimeUrl);
129534
129775
  if (assetMarker) values.push({ key: "x-pm-onboarding", value: JSON.stringify(assetMarker), type: "default" });
129535
- envUids[envName] = await dependencies.postman.createEnvironment(
129536
- inputs.workspaceId,
129537
- displayName,
129538
- values
129539
- );
129776
+ try {
129777
+ envUids[envName] = await dependencies.postman.createEnvironment(
129778
+ inputs.workspaceId,
129779
+ displayName,
129780
+ values
129781
+ );
129782
+ } catch (error2) {
129783
+ throw new Error(
129784
+ formatOrchestrationIssue({
129785
+ operation: "Environment create",
129786
+ entity: `workspace ${inputs.workspaceId} environment "${displayName}"`,
129787
+ cause: error2,
129788
+ remediation: envRemediation,
129789
+ mask
129790
+ }),
129791
+ { cause: error2 }
129792
+ );
129793
+ }
129540
129794
  }
129541
129795
  return envUids;
129542
129796
  }
@@ -129793,11 +130047,15 @@ function renderCiWorkflow(inputs) {
129793
130047
  if (inputs.provider === "azure-devops") {
129794
130048
  return getCiWorkflowTemplate(inputs.provider, {
129795
130049
  postmanCliInstallUrl: inputs.postmanCliInstallUrl,
130050
+ postmanCliWindowsInstallUrl: inputs.postmanCliWindowsInstallUrl,
130051
+ runnerOs: inputs.ciRunnerOs,
129796
130052
  postmanRegion: inputs.postmanRegion
129797
130053
  });
129798
130054
  }
129799
130055
  return renderCiWorkflowTemplate({
129800
130056
  postmanCliInstallUrl: inputs.postmanCliInstallUrl,
130057
+ postmanCliWindowsInstallUrl: inputs.postmanCliWindowsInstallUrl,
130058
+ runnerOs: inputs.ciRunnerOs,
129801
130059
  postmanRegion: inputs.postmanRegion
129802
130060
  });
129803
130061
  }
@@ -129892,6 +130150,7 @@ async function runRepoSync(inputs, dependencies) {
129892
130150
  }
129893
130151
  }
129894
130152
  async function runRepoSyncInner(inputs, dependencies) {
130153
+ const mask = resolveRepoSyncMasker(dependencies);
129895
130154
  const branchDecision = decideBranchTier(inputs);
129896
130155
  assertBranchAssetIds(inputs, branchDecision);
129897
130156
  const isCanonicalWriter = branchDecision.tier === "legacy" || branchDecision.tier === "canonical";
@@ -130001,8 +130260,15 @@ async function runRepoSyncInner(inputs, dependencies) {
130001
130260
  outputs["environment-sync-status"] = "success";
130002
130261
  } catch (error2) {
130003
130262
  outputs["environment-sync-status"] = "failed";
130263
+ const associationSummary = associations.map((entry) => `${entry.envUid}->${entry.systemEnvId}`).join(", ");
130004
130264
  dependencies.core.warning(
130005
- `System environment association failed: ${error2 instanceof Error ? error2.message : String(error2)}`
130265
+ formatOrchestrationIssue({
130266
+ operation: "System environment association",
130267
+ entity: `workspace ${inputs.workspaceId} (${associationSummary})`,
130268
+ cause: error2,
130269
+ remediation: "verify access-token/team/system-env mapping then rerun",
130270
+ mask
130271
+ })
130006
130272
  );
130007
130273
  }
130008
130274
  } else if (Object.keys(envUids).length > 0) {
@@ -130030,37 +130296,72 @@ async function runRepoSyncInner(inputs, dependencies) {
130030
130296
  dependencies.core.info(`Reusing mock from explicit input: ${resolvedMockUrl}`);
130031
130297
  }
130032
130298
  if (!resolvedMockUrl && inputs.baselineCollectionId) {
130033
- const discovered = await dependencies.postman.findMockByCollection(
130034
- inputs.baselineCollectionId,
130035
- mockEnvUid,
130036
- mockName
130037
- );
130038
- if (discovered) {
130039
- resolvedMockUrl = discovered.mockUrl;
130040
- dependencies.core.info(`Discovered existing mock for collection ${inputs.baselineCollectionId}: ${resolvedMockUrl}`);
130299
+ try {
130300
+ const discovered = await dependencies.postman.findMockByCollection(
130301
+ inputs.baselineCollectionId,
130302
+ mockEnvUid,
130303
+ mockName
130304
+ );
130305
+ if (discovered) {
130306
+ resolvedMockUrl = discovered.mockUrl;
130307
+ dependencies.core.info(`Discovered existing mock for collection ${inputs.baselineCollectionId}: ${resolvedMockUrl}`);
130308
+ }
130309
+ } catch (error2) {
130310
+ throw new Error(
130311
+ formatOrchestrationIssue({
130312
+ operation: "Mock discovery",
130313
+ entity: `mock "${mockName}" workspace ${inputs.workspaceId} collection ${inputs.baselineCollectionId} environment ${mockEnvUid}`,
130314
+ cause: error2,
130315
+ remediation: "verify collection/environment access then rerun",
130316
+ mask
130317
+ }),
130318
+ { cause: error2 }
130319
+ );
130041
130320
  }
130042
130321
  }
130043
130322
  if (!resolvedMockUrl) {
130044
- const mock = await retry(
130045
- () => dependencies.postman.createMock(
130046
- inputs.workspaceId,
130047
- mockName,
130048
- inputs.baselineCollectionId,
130049
- mockEnvUid
130050
- ),
130051
- {
130052
- maxAttempts: 3,
130053
- delayMs: 2e3,
130054
- backoffMultiplier: 2,
130055
- onRetry: ({ attempt, error: error2 }) => {
130056
- dependencies.core.warning(
130057
- `Mock create attempt ${attempt} failed (${error2 instanceof Error ? error2.message : String(error2)}); retrying`
130058
- );
130323
+ const mockEntity = `mock "${mockName}" workspace ${inputs.workspaceId} collection ${inputs.baselineCollectionId} environment ${mockEnvUid}`;
130324
+ const mockRemediation = "verify collection/environment access then rerun";
130325
+ try {
130326
+ const mock = await retry(
130327
+ () => dependencies.postman.createMock(
130328
+ inputs.workspaceId,
130329
+ mockName,
130330
+ inputs.baselineCollectionId,
130331
+ mockEnvUid
130332
+ ),
130333
+ {
130334
+ maxAttempts: 3,
130335
+ delayMs: 2e3,
130336
+ backoffMultiplier: 2,
130337
+ onRetry: ({ attempt, maxAttempts, error: error2 }) => {
130338
+ dependencies.core.warning(
130339
+ formatOrchestrationIssue({
130340
+ operation: `Mock create attempt ${attempt}/${maxAttempts}`,
130341
+ entity: mockEntity,
130342
+ cause: error2,
130343
+ remediation: mockRemediation,
130344
+ mask,
130345
+ detail: "; retrying"
130346
+ })
130347
+ );
130348
+ }
130059
130349
  }
130060
- }
130061
- );
130062
- resolvedMockUrl = mock.url;
130063
- dependencies.core.info(`Created new mock: ${resolvedMockUrl}`);
130350
+ );
130351
+ resolvedMockUrl = mock.url;
130352
+ dependencies.core.info(`Created new mock: ${resolvedMockUrl}`);
130353
+ } catch (error2) {
130354
+ throw new Error(
130355
+ formatOrchestrationIssue({
130356
+ operation: "Mock create",
130357
+ entity: mockEntity,
130358
+ cause: error2,
130359
+ remediation: mockRemediation,
130360
+ mask
130361
+ }),
130362
+ { cause: error2 }
130363
+ );
130364
+ }
130064
130365
  }
130065
130366
  outputs["mock-url"] = resolvedMockUrl;
130066
130367
  }
@@ -130071,35 +130372,71 @@ async function runRepoSyncInner(inputs, dependencies) {
130071
130372
  if (monitorEnvUid && inputs.monitorType !== "cli") {
130072
130373
  let resolvedMonitorId = "";
130073
130374
  const monitorName = `${assetProjectName} - Smoke Monitor`;
130375
+ const monitorRemediation = "verify monitor IDs/access or set monitor-cron then rerun";
130074
130376
  if (inputs.monitorId) {
130075
130377
  const valid = await dependencies.postman.monitorExists(inputs.monitorId);
130076
130378
  if (valid) {
130077
130379
  resolvedMonitorId = inputs.monitorId;
130078
130380
  dependencies.core.info(`Reusing monitor from explicit input: ${resolvedMonitorId}`);
130079
130381
  } else {
130080
- dependencies.core.warning(`Explicit monitor-id ${inputs.monitorId} not found in Postman, falling through to discovery.`);
130382
+ dependencies.core.warning(
130383
+ formatOrchestrationIssue({
130384
+ operation: "Explicit monitor-id lookup",
130385
+ entity: `monitor-id ${inputs.monitorId} workspace ${inputs.workspaceId} collection ${inputs.smokeCollectionId} environment ${monitorEnvUid}`,
130386
+ cause: "not found in Postman",
130387
+ remediation: monitorRemediation,
130388
+ mask,
130389
+ detail: "; falling through to discovery"
130390
+ })
130391
+ );
130081
130392
  }
130082
130393
  }
130083
130394
  if (!resolvedMonitorId && inputs.smokeCollectionId) {
130084
- const discovered = await dependencies.postman.findMonitorByCollection(
130085
- inputs.smokeCollectionId,
130086
- monitorEnvUid,
130087
- monitorName
130088
- );
130089
- if (discovered) {
130090
- resolvedMonitorId = discovered.uid;
130091
- dependencies.core.info(`Discovered existing monitor for collection ${inputs.smokeCollectionId}: ${resolvedMonitorId}`);
130395
+ try {
130396
+ const discovered = await dependencies.postman.findMonitorByCollection(
130397
+ inputs.smokeCollectionId,
130398
+ monitorEnvUid,
130399
+ monitorName
130400
+ );
130401
+ if (discovered) {
130402
+ resolvedMonitorId = discovered.uid;
130403
+ dependencies.core.info(`Discovered existing monitor for collection ${inputs.smokeCollectionId}: ${resolvedMonitorId}`);
130404
+ }
130405
+ } catch (error2) {
130406
+ throw new Error(
130407
+ formatOrchestrationIssue({
130408
+ operation: "Monitor discovery",
130409
+ entity: `monitor "${monitorName}" workspace ${inputs.workspaceId} collection ${inputs.smokeCollectionId} environment ${monitorEnvUid}`,
130410
+ cause: error2,
130411
+ remediation: monitorRemediation,
130412
+ mask
130413
+ }),
130414
+ { cause: error2 }
130415
+ );
130092
130416
  }
130093
130417
  }
130094
130418
  if (!resolvedMonitorId) {
130095
- resolvedMonitorId = await dependencies.postman.createMonitor(
130096
- inputs.workspaceId,
130097
- monitorName,
130098
- inputs.smokeCollectionId,
130099
- monitorEnvUid,
130100
- effectiveCron || void 0
130101
- );
130102
- dependencies.core.info(`Created new monitor: ${resolvedMonitorId}${effectiveCron ? "" : " (disabled \u2014 no cron configured; will trigger a one-time run)"}`);
130419
+ try {
130420
+ resolvedMonitorId = await dependencies.postman.createMonitor(
130421
+ inputs.workspaceId,
130422
+ monitorName,
130423
+ inputs.smokeCollectionId,
130424
+ monitorEnvUid,
130425
+ effectiveCron || void 0
130426
+ );
130427
+ dependencies.core.info(`Created new monitor: ${resolvedMonitorId}${effectiveCron ? "" : " (disabled \u2014 no cron configured; will trigger a one-time run)"}`);
130428
+ } catch (error2) {
130429
+ throw new Error(
130430
+ formatOrchestrationIssue({
130431
+ operation: "Monitor create",
130432
+ entity: `monitor "${monitorName}" workspace ${inputs.workspaceId} collection ${inputs.smokeCollectionId} environment ${monitorEnvUid}`,
130433
+ cause: error2,
130434
+ remediation: monitorRemediation,
130435
+ mask
130436
+ }),
130437
+ { cause: error2 }
130438
+ );
130439
+ }
130103
130440
  }
130104
130441
  outputs["monitor-id"] = resolvedMonitorId;
130105
130442
  if (!effectiveCron && resolvedMonitorId) {
@@ -130108,7 +130445,13 @@ async function runRepoSyncInner(inputs, dependencies) {
130108
130445
  dependencies.core.info(`Triggered one-time run for monitor: ${resolvedMonitorId}`);
130109
130446
  } catch (error2) {
130110
130447
  dependencies.core.warning(
130111
- `Failed to trigger one-time run for monitor ${resolvedMonitorId}: ${error2 instanceof Error ? error2.message : String(error2)}`
130448
+ formatOrchestrationIssue({
130449
+ operation: "Monitor one-time run",
130450
+ entity: `monitor ${resolvedMonitorId} name "${monitorName}" workspace ${inputs.workspaceId} collection ${inputs.smokeCollectionId} environment ${monitorEnvUid}`,
130451
+ cause: error2,
130452
+ remediation: monitorRemediation,
130453
+ mask
130454
+ })
130112
130455
  );
130113
130456
  }
130114
130457
  }
@@ -130173,18 +130516,57 @@ async function runRepoSyncInner(inputs, dependencies) {
130173
130516
  dependencies.core.info(`Tagged spec version at finalize: ${tag.name || tagName}`);
130174
130517
  } catch (error2) {
130175
130518
  const status = error2.status;
130519
+ const specEntity = `spec ${inputs.specId} tag "${tagName}" workspace ${inputs.workspaceId}`;
130520
+ const specRemediation = "inspect existing tags/access and rerun";
130176
130521
  if (status === 409) {
130177
- const tags = await dependencies.postman.listSpecVersionTags?.(inputs.specId).catch(() => []) ?? [];
130522
+ let tags = [];
130523
+ let listError;
130524
+ if (dependencies.postman.listSpecVersionTags) {
130525
+ try {
130526
+ tags = await dependencies.postman.listSpecVersionTags(inputs.specId);
130527
+ } catch (lookupError) {
130528
+ listError = lookupError;
130529
+ }
130530
+ }
130178
130531
  const latest = tags[0];
130179
- if (latest && (latest.name === tagName || /\([0-9a-f]{7}\)$/.test(latest.name) || /^[0-9a-f]{7}$/.test(latest.name))) {
130532
+ if (!listError && latest && (latest.name === tagName || /\([0-9a-f]{7}\)$/.test(latest.name) || /^[0-9a-f]{7}$/.test(latest.name))) {
130180
130533
  outputs["spec-version-tag"] = latest.name;
130181
130534
  outputs["spec-version-url"] = `https://web.postman.co/workspace/${encodeURIComponent(inputs.workspaceId)}/specification/${encodeURIComponent(inputs.specId)}?tagId=${encodeURIComponent(latest.id)}&versionLabel=${encodeURIComponent(latest.name)}`;
130182
130535
  dependencies.core.info(`Latest changelog group already tagged as "${latest.name}"; adopting.`);
130536
+ } else if (listError) {
130537
+ dependencies.core.warning(
130538
+ formatOrchestrationIssue({
130539
+ operation: "Spec Hub tagging conflict",
130540
+ entity: specEntity,
130541
+ cause: `create=${causeText(error2)}; listSpecVersionTags=${causeText(listError)}`,
130542
+ remediation: specRemediation,
130543
+ mask,
130544
+ detail: "; could not list existing tags to confirm adoption"
130545
+ })
130546
+ );
130183
130547
  } else {
130184
- dependencies.core.warning("Latest changelog group already carries a hand-applied tag; leaving it in place.");
130548
+ dependencies.core.warning(
130549
+ formatOrchestrationIssue({
130550
+ operation: "Spec Hub tagging conflict",
130551
+ entity: specEntity,
130552
+ cause: error2,
130553
+ remediation: specRemediation,
130554
+ mask,
130555
+ detail: "; latest changelog group already carries a hand-applied or nonmatching tag; leaving it in place"
130556
+ })
130557
+ );
130185
130558
  }
130186
130559
  } else {
130187
- dependencies.core.warning(`Spec version tagging failed (non-fatal): ${error2 instanceof Error ? error2.message : String(error2)}`);
130560
+ dependencies.core.warning(
130561
+ formatOrchestrationIssue({
130562
+ operation: "Spec version tagging",
130563
+ entity: specEntity,
130564
+ cause: error2,
130565
+ remediation: specRemediation,
130566
+ mask,
130567
+ detail: " (non-fatal)"
130568
+ })
130569
+ );
130188
130570
  }
130189
130571
  }
130190
130572
  }
@@ -130215,7 +130597,16 @@ async function resolvePostmanApiKeyAndTeamId(inputs, actionCore, actionExec, mas
130215
130597
  }
130216
130598
  } catch (error2) {
130217
130599
  if (typeof error2 === "object" && error2 !== null && "status" in error2 && (error2.status === 401 || error2.status === 403)) {
130218
- actionCore.warning("Provided postman-api-key is invalid or expired.");
130600
+ const status = error2.status;
130601
+ actionCore.warning(
130602
+ formatOrchestrationIssue({
130603
+ operation: "PMAK GET /me validation",
130604
+ entity: `status ${status}`,
130605
+ cause: error2,
130606
+ remediation: "replace postman-api-key or provide a valid postman-access-token then rerun",
130607
+ mask: masker
130608
+ })
130609
+ );
130219
130610
  } else {
130220
130611
  throw error2;
130221
130612
  }
@@ -130237,6 +130628,7 @@ async function resolvePostmanApiKeyAndTeamId(inputs, actionCore, actionExec, mas
130237
130628
  const keyName = `repo-sync-action-${Date.now()}`;
130238
130629
  apiKey = await internalIntegration.createApiKey(keyName);
130239
130630
  actionCore.setSecret(apiKey);
130631
+ const persistSecretRemediation = "grant Actions secrets write permission or set POSTMAN_API_KEY manually then rerun";
130240
130632
  if (inputs.provider === "azure-devops") {
130241
130633
  if (options.persistGeneratedApiKeySecret ?? true) {
130242
130634
  actionCore.warning(
@@ -130263,16 +130655,48 @@ async function resolvePostmanApiKeyAndTeamId(inputs, actionCore, actionExec, mas
130263
130655
  ignoreReturnCode: true
130264
130656
  });
130265
130657
  if (ghCommand.exitCode !== 0) {
130266
- actionCore.warning(`Failed to save POSTMAN_API_KEY secret: ${ghCommand.stderr}`);
130658
+ actionCore.warning(
130659
+ formatOrchestrationIssue({
130660
+ operation: "gh secret set POSTMAN_API_KEY",
130661
+ entity: `repository ${repo}`,
130662
+ cause: ghCommand.stderr || `exit code ${ghCommand.exitCode}`,
130663
+ remediation: persistSecretRemediation,
130664
+ mask: masker
130665
+ })
130666
+ );
130267
130667
  }
130268
130668
  } catch (error2) {
130269
130669
  actionCore.warning(
130270
- `Error saving POSTMAN_API_KEY secret: ${error2 instanceof Error ? error2.message : String(error2)}`
130670
+ formatOrchestrationIssue({
130671
+ operation: "gh secret set POSTMAN_API_KEY",
130672
+ entity: `repository ${repo}`,
130673
+ cause: error2,
130674
+ remediation: persistSecretRemediation,
130675
+ mask: masker
130676
+ })
130271
130677
  );
130272
130678
  }
130679
+ } else {
130680
+ actionCore.warning(
130681
+ formatOrchestrationIssue({
130682
+ operation: "gh secret set POSTMAN_API_KEY",
130683
+ entity: "repository (missing)",
130684
+ cause: "repository context is empty",
130685
+ remediation: "set repository context or persist POSTMAN_API_KEY manually then rerun",
130686
+ mask: masker
130687
+ })
130688
+ );
130273
130689
  }
130274
130690
  } else if (options.persistGeneratedApiKeySecret ?? true) {
130275
- actionCore.warning("No GitHub token provided; cannot save generated POSTMAN_API_KEY secret.");
130691
+ actionCore.warning(
130692
+ formatOrchestrationIssue({
130693
+ operation: "gh secret set POSTMAN_API_KEY",
130694
+ entity: inputs.repository ? `repository ${inputs.repository}` : "repository (unknown)",
130695
+ cause: "no GitHub token provided",
130696
+ remediation: "provide github-token/gh-fallback-token or set POSTMAN_API_KEY manually then rerun",
130697
+ mask: masker
130698
+ })
130699
+ );
130276
130700
  } else {
130277
130701
  actionCore.info("Skipping generated POSTMAN_API_KEY GitHub secret persistence for this run.");
130278
130702
  }
@@ -130311,7 +130735,13 @@ async function resolvePostmanApiKeyAndTeamId(inputs, actionCore, actionExec, mas
130311
130735
  const isNonOrgSquads = error2 instanceof HttpError && error2.status === 400 && /squad feature is not available/i.test(error2.responseBody);
130312
130736
  if (!isNonOrgSquads) {
130313
130737
  actionCore.warning(
130314
- `Org-mode auto-detection via ums squads failed (non-fatal; set org-mode explicitly if Bifrost calls fail): ${error2 instanceof Error ? error2.message : String(error2)}`
130738
+ formatOrchestrationIssue({
130739
+ operation: "Org-mode auto-detection via ums squads",
130740
+ entity: `team ${teamId}`,
130741
+ cause: error2,
130742
+ remediation: "set org-mode and team-id explicitly then rerun",
130743
+ mask: masker
130744
+ })
130315
130745
  );
130316
130746
  }
130317
130747
  }
@@ -130431,7 +130861,8 @@ function createRepoSyncDependencies(inputs, resolved, factories, options = {}) {
130431
130861
  core: factories.core,
130432
130862
  postman,
130433
130863
  internalIntegration,
130434
- repoMutation
130864
+ repoMutation,
130865
+ secretMasker: masker
130435
130866
  };
130436
130867
  }
130437
130868
  function decideBranchTier(inputs, env = process.env) {