@postman-cse/onboarding-repo-sync 2.1.7 → 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;
@@ -126915,7 +127068,106 @@ var BifrostInternalIntegrationAdapter = class {
126915
127068
  throw advised ?? httpErr;
126916
127069
  }
126917
127070
  }
126918
- async connectWorkspaceToRepository(workspaceId, repoUrl) {
127071
+ /**
127072
+ * Probe who (if anyone) already owns the global `(repo, path)` filesystem
127073
+ * link. Non-fatal: unexpected statuses / network errors yield `unknown` so
127074
+ * callers can proceed and rely on reactive POST conflict handling.
127075
+ *
127076
+ * Bifrost states (FileSystemController.fetchWorkspaceForFileSystem):
127077
+ * - 200 + data null -> free
127078
+ * - 200 + data -> linked-visible (workspace payload)
127079
+ * - 403 + error.meta.workspaceId -> linked-invisible
127080
+ * - 200 + error.meta.workspaceId -> linked-invisible (proxy envelope)
127081
+ */
127082
+ async findWorkspaceForRepo(repoUrl, path9 = "/") {
127083
+ const fsPath = path9 && path9.trim() ? path9.trim() : "/";
127084
+ const url = `${this.bifrostBaseUrl}/ws/proxy`;
127085
+ const payload = {
127086
+ service: "workspaces",
127087
+ method: "GET",
127088
+ path: `/workspaces/filesystem?repo=${encodeURIComponent(repoUrl)}&path=${encodeURIComponent(fsPath)}`
127089
+ };
127090
+ try {
127091
+ const response = await this.fetchImpl(url, {
127092
+ method: "POST",
127093
+ headers: this.bifrostHeaders(),
127094
+ body: JSON.stringify(payload)
127095
+ });
127096
+ let parsed = {};
127097
+ try {
127098
+ parsed = await response.json();
127099
+ } catch {
127100
+ return {
127101
+ state: "unknown",
127102
+ reason: this.unknownFilesystemLookupReason(
127103
+ repoUrl,
127104
+ fsPath,
127105
+ `returned non-JSON body (HTTP ${response.status})`
127106
+ )
127107
+ };
127108
+ }
127109
+ const errorMetaWorkspaceId = typeof parsed?.error?.meta?.workspaceId === "string" && parsed.error.meta.workspaceId.trim() ? parsed.error.meta.workspaceId.trim() : void 0;
127110
+ if (errorMetaWorkspaceId) {
127111
+ return { state: "linked-invisible", workspaceId: errorMetaWorkspaceId };
127112
+ }
127113
+ if (response.ok) {
127114
+ if (parsed?.data == null) {
127115
+ return { state: "free" };
127116
+ }
127117
+ const id = typeof parsed.data.id === "string" ? parsed.data.id.trim() : "";
127118
+ if (!id) {
127119
+ return {
127120
+ state: "unknown",
127121
+ reason: this.unknownFilesystemLookupReason(
127122
+ repoUrl,
127123
+ fsPath,
127124
+ "returned 200 without a workspace id"
127125
+ )
127126
+ };
127127
+ }
127128
+ const name = typeof parsed.data.name === "string" && parsed.data.name.trim() ? parsed.data.name.trim() : void 0;
127129
+ return {
127130
+ state: "linked-visible",
127131
+ workspace: name ? { id, name } : { id }
127132
+ };
127133
+ }
127134
+ if (response.status === 403) {
127135
+ const workspaceId = extractDuplicateWorkspaceId(
127136
+ JSON.stringify(parsed)
127137
+ );
127138
+ if (workspaceId) {
127139
+ return { state: "linked-invisible", workspaceId };
127140
+ }
127141
+ return {
127142
+ state: "unknown",
127143
+ reason: this.unknownFilesystemLookupReason(
127144
+ repoUrl,
127145
+ fsPath,
127146
+ "returned 403 without error.meta.workspaceId"
127147
+ )
127148
+ };
127149
+ }
127150
+ return {
127151
+ state: "unknown",
127152
+ reason: this.unknownFilesystemLookupReason(
127153
+ repoUrl,
127154
+ fsPath,
127155
+ `returned HTTP ${response.status}`
127156
+ )
127157
+ };
127158
+ } catch (error2) {
127159
+ const cause = error2 instanceof Error ? error2.message : String(error2);
127160
+ return {
127161
+ state: "unknown",
127162
+ reason: this.unknownFilesystemLookupReason(
127163
+ repoUrl,
127164
+ fsPath,
127165
+ `failed: ${cause}`
127166
+ )
127167
+ };
127168
+ }
127169
+ }
127170
+ async connectWorkspaceToRepository(workspaceId, repoUrl, options) {
126919
127171
  const url = `${this.bifrostBaseUrl}/ws/proxy`;
126920
127172
  const payload = {
126921
127173
  service: "workspaces",
@@ -126942,6 +127194,11 @@ var BifrostInternalIntegrationAdapter = class {
126942
127194
  if (isDuplicate) {
126943
127195
  const existingWorkspaceId = extractDuplicateWorkspaceId(body);
126944
127196
  if (existingWorkspaceId && existingWorkspaceId !== workspaceId) {
127197
+ if (options?.preflightWasFree) {
127198
+ throw new Error(
127199
+ `REPOSITORY_LINK_CONFLICT_UNRESOLVED: Preflight found no active owner, but link creation reported workspace ${existingWorkspaceId}. Stop and contact Postman support; do not alter the repository URL.`
127200
+ );
127201
+ }
126945
127202
  throw new Error(
126946
127203
  await this.describeWorkspaceLinkConflict(existingWorkspaceId, workspaceId, repoUrl)
126947
127204
  );
@@ -127120,6 +127377,12 @@ var postmanRepoSyncActionContract = {
127120
127377
  required: false,
127121
127378
  default: ".github/workflows/ci.yml"
127122
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
+ },
127123
127386
  "project-name": {
127124
127387
  description: "Service project name used for environment, mock, and monitor naming.",
127125
127388
  required: true
@@ -128831,6 +129094,25 @@ function parseAssetMarker(description) {
128831
129094
  }
128832
129095
 
128833
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
+ }
128834
129116
  function parseBooleanInput(value, defaultValue) {
128835
129117
  const normalized = String(value || "").trim().toLowerCase();
128836
129118
  if (!normalized) return defaultValue;
@@ -128930,6 +129212,17 @@ function parseBranchStrategy(value) {
128930
129212
  `Unsupported branch-strategy "${normalized}". Supported values: ${allowed.join(", ")}`
128931
129213
  );
128932
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
+ }
128933
129226
  function normalizeReleaseLabel(value) {
128934
129227
  const cleaned = normalizeInputValue(value).replace(/^refs\/heads\//, "").replace(/^refs\/tags\//, "").replace(/\s+/g, "-").replace(/[^A-Za-z0-9._-]/g, "-").replace(/-+/g, "-").replace(/^[._-]+|[._-]+$/g, "");
128935
129228
  return cleaned;
@@ -129007,6 +129300,7 @@ function resolveInputs(env = process.env) {
129007
129300
  provider: repoContext.provider,
129008
129301
  ciWorkflowBase64: getInput2("ci-workflow-base64", env),
129009
129302
  generateCiWorkflow: parseBooleanInput(getInput2("generate-ci-workflow", env), true),
129303
+ ciRunnerOs: parseCiRunnerOs(getInput2("ci-runner-os", env)),
129010
129304
  monitorType: getInput2("monitor-type", env) || "cloud",
129011
129305
  ciWorkflowPath: getInput2("ci-workflow-path", env) || (repoContext.provider === "azure-devops" ? "azure-pipelines.yml" : ".github/workflows/ci.yml"),
129012
129306
  orgMode: parseBooleanInput(getInput2("org-mode", env), false),
@@ -129025,6 +129319,7 @@ function resolveInputs(env = process.env) {
129025
129319
  postmanBifrostBase: endpointProfile.bifrostBaseUrl,
129026
129320
  postmanFallbackBase: endpointProfile.fallbackBaseUrl,
129027
129321
  postmanCliInstallUrl: endpointProfile.cliInstallUrl,
129322
+ postmanCliWindowsInstallUrl: endpointProfile.cliWindowsInstallUrl,
129028
129323
  postmanIapubBase: endpointProfile.iapubBaseUrl
129029
129324
  };
129030
129325
  }
@@ -129294,6 +129589,7 @@ function readActionInputs(actionCore) {
129294
129589
  INPUT_GH_FALLBACK_TOKEN: ghFallbackToken,
129295
129590
  INPUT_CI_WORKFLOW_BASE64: readInput(actionCore, "ci-workflow-base64"),
129296
129591
  INPUT_GENERATE_CI_WORKFLOW: readInput(actionCore, "generate-ci-workflow"),
129592
+ INPUT_CI_RUNNER_OS: readInput(actionCore, "ci-runner-os"),
129297
129593
  INPUT_MONITOR_TYPE: readInput(actionCore, "monitor-type") || "cloud",
129298
129594
  INPUT_CI_WORKFLOW_PATH: readInput(actionCore, "ci-workflow-path"),
129299
129595
  INPUT_ORG_MODE: readInput(actionCore, "org-mode"),
@@ -129411,19 +129707,34 @@ async function upsertEnvironments(inputs, dependencies, resourcesState, assetMar
129411
129707
  if (!inputs.workspaceId) {
129412
129708
  return envUids;
129413
129709
  }
129710
+ const mask = resolveRepoSyncMasker(dependencies);
129711
+ const envRemediation = "verify access-token/team/workspace permissions then rerun";
129414
129712
  for (const envName of inputs.environments) {
129415
129713
  const runtimeUrl = String(inputs.envRuntimeUrls[envName] || "").trim();
129416
129714
  const displayName = `${inputs.projectName} - ${envName}`;
129417
129715
  let existingUid = String(envUids[envName] || "").trim();
129418
129716
  if (!existingUid) {
129419
- const discovered = await dependencies.postman.findEnvironmentByName(
129420
- inputs.workspaceId,
129421
- displayName
129422
- );
129423
- if (discovered?.uid) {
129424
- existingUid = discovered.uid;
129425
- dependencies.core.info(
129426
- `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 }
129427
129738
  );
129428
129739
  }
129429
129740
  }
@@ -129443,17 +129754,43 @@ async function upsertEnvironments(inputs, dependencies, resourcesState, assetMar
129443
129754
  }
129444
129755
  const values2 = buildEnvironmentValues(envName, runtimeUrl);
129445
129756
  if (marker) values2.push({ key: "x-pm-onboarding", value: JSON.stringify(marker), type: "default" });
129446
- 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
+ }
129447
129771
  envUids[envName] = existingUid;
129448
129772
  continue;
129449
129773
  }
129450
129774
  const values = buildEnvironmentValues(envName, runtimeUrl);
129451
129775
  if (assetMarker) values.push({ key: "x-pm-onboarding", value: JSON.stringify(assetMarker), type: "default" });
129452
- envUids[envName] = await dependencies.postman.createEnvironment(
129453
- inputs.workspaceId,
129454
- displayName,
129455
- values
129456
- );
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
+ }
129457
129794
  }
129458
129795
  return envUids;
129459
129796
  }
@@ -129710,11 +130047,15 @@ function renderCiWorkflow(inputs) {
129710
130047
  if (inputs.provider === "azure-devops") {
129711
130048
  return getCiWorkflowTemplate(inputs.provider, {
129712
130049
  postmanCliInstallUrl: inputs.postmanCliInstallUrl,
130050
+ postmanCliWindowsInstallUrl: inputs.postmanCliWindowsInstallUrl,
130051
+ runnerOs: inputs.ciRunnerOs,
129713
130052
  postmanRegion: inputs.postmanRegion
129714
130053
  });
129715
130054
  }
129716
130055
  return renderCiWorkflowTemplate({
129717
130056
  postmanCliInstallUrl: inputs.postmanCliInstallUrl,
130057
+ postmanCliWindowsInstallUrl: inputs.postmanCliWindowsInstallUrl,
130058
+ runnerOs: inputs.ciRunnerOs,
129718
130059
  postmanRegion: inputs.postmanRegion
129719
130060
  });
129720
130061
  }
@@ -129809,6 +130150,7 @@ async function runRepoSync(inputs, dependencies) {
129809
130150
  }
129810
130151
  }
129811
130152
  async function runRepoSyncInner(inputs, dependencies) {
130153
+ const mask = resolveRepoSyncMasker(dependencies);
129812
130154
  const branchDecision = decideBranchTier(inputs);
129813
130155
  assertBranchAssetIds(inputs, branchDecision);
129814
130156
  const isCanonicalWriter = branchDecision.tier === "legacy" || branchDecision.tier === "canonical";
@@ -129868,6 +130210,39 @@ async function runRepoSyncInner(inputs, dependencies) {
129868
130210
  }
129869
130211
  }
129870
130212
  }
130213
+ let skipRepositoryLinkPost = false;
130214
+ let repositoryLinkPreflightWasFree = false;
130215
+ if (inputs.workspaceLinkEnabled && inputs.workspaceId && inputs.repoUrl && dependencies.internalIntegration?.findWorkspaceForRepo) {
130216
+ const probe = await dependencies.internalIntegration.findWorkspaceForRepo(
130217
+ inputs.repoUrl,
130218
+ "/"
130219
+ );
130220
+ if (probe.state === "linked-visible") {
130221
+ if (probe.workspace.id === inputs.workspaceId) {
130222
+ skipRepositoryLinkPost = true;
130223
+ dependencies.core.info(
130224
+ `REPOSITORY_LINK_ALREADY_TARGET: Repository ${inputs.repoUrl} at path / is already linked to target workspace ${inputs.workspaceId}; continuing.`
130225
+ );
130226
+ } else {
130227
+ const ownerName = probe.workspace.name?.trim() || "unknown";
130228
+ throw new Error(
130229
+ `REPOSITORY_LINK_CONFLICT_VISIBLE: Repository ${inputs.repoUrl} at path / is already linked to workspace ${probe.workspace.id} ("${ownerName}"). No Postman assets were changed. Reuse that workspace or disconnect it in Workspace Settings, then rerun.`
130230
+ );
130231
+ }
130232
+ } else if (probe.state === "linked-invisible") {
130233
+ let message = `REPOSITORY_LINK_CONFLICT_INVISIBLE: Repository ${inputs.repoUrl} at path / is linked to workspace ${probe.workspaceId}, but these credentials cannot view it. No Postman assets were changed. Ask its owner or a team admin to disconnect it.`;
130234
+ if (inputs.orgMode) {
130235
+ message += " Verify workspace-team-id; if the owner is in another sub-team, ask that sub-team's admin to disconnect it.";
130236
+ }
130237
+ throw new Error(message);
130238
+ } else if (probe.state === "free") {
130239
+ repositoryLinkPreflightWasFree = true;
130240
+ } else {
130241
+ dependencies.core.warning(
130242
+ `REPOSITORY_LINK_PREFLIGHT_UNKNOWN: Unable to determine the existing repository link (${probe.reason}); continuing and relying on link creation conflict handling.`
130243
+ );
130244
+ }
130245
+ }
129871
130246
  const branchAssetMarker = buildBranchAssetMarker(branchDecision, inputs);
129872
130247
  const envUids = await upsertEnvironments(inputs, dependencies, resourcesState, branchAssetMarker);
129873
130248
  outputs["environment-uids-json"] = JSON.stringify(envUids);
@@ -129885,8 +130260,15 @@ async function runRepoSyncInner(inputs, dependencies) {
129885
130260
  outputs["environment-sync-status"] = "success";
129886
130261
  } catch (error2) {
129887
130262
  outputs["environment-sync-status"] = "failed";
130263
+ const associationSummary = associations.map((entry) => `${entry.envUid}->${entry.systemEnvId}`).join(", ");
129888
130264
  dependencies.core.warning(
129889
- `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
+ })
129890
130272
  );
129891
130273
  }
129892
130274
  } else if (Object.keys(envUids).length > 0) {
@@ -129914,37 +130296,72 @@ async function runRepoSyncInner(inputs, dependencies) {
129914
130296
  dependencies.core.info(`Reusing mock from explicit input: ${resolvedMockUrl}`);
129915
130297
  }
129916
130298
  if (!resolvedMockUrl && inputs.baselineCollectionId) {
129917
- const discovered = await dependencies.postman.findMockByCollection(
129918
- inputs.baselineCollectionId,
129919
- mockEnvUid,
129920
- mockName
129921
- );
129922
- if (discovered) {
129923
- resolvedMockUrl = discovered.mockUrl;
129924
- 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
+ );
129925
130320
  }
129926
130321
  }
129927
130322
  if (!resolvedMockUrl) {
129928
- const mock = await retry(
129929
- () => dependencies.postman.createMock(
129930
- inputs.workspaceId,
129931
- mockName,
129932
- inputs.baselineCollectionId,
129933
- mockEnvUid
129934
- ),
129935
- {
129936
- maxAttempts: 3,
129937
- delayMs: 2e3,
129938
- backoffMultiplier: 2,
129939
- onRetry: ({ attempt, error: error2 }) => {
129940
- dependencies.core.warning(
129941
- `Mock create attempt ${attempt} failed (${error2 instanceof Error ? error2.message : String(error2)}); retrying`
129942
- );
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
+ }
129943
130349
  }
129944
- }
129945
- );
129946
- resolvedMockUrl = mock.url;
129947
- 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
+ }
129948
130365
  }
129949
130366
  outputs["mock-url"] = resolvedMockUrl;
129950
130367
  }
@@ -129955,35 +130372,71 @@ async function runRepoSyncInner(inputs, dependencies) {
129955
130372
  if (monitorEnvUid && inputs.monitorType !== "cli") {
129956
130373
  let resolvedMonitorId = "";
129957
130374
  const monitorName = `${assetProjectName} - Smoke Monitor`;
130375
+ const monitorRemediation = "verify monitor IDs/access or set monitor-cron then rerun";
129958
130376
  if (inputs.monitorId) {
129959
130377
  const valid = await dependencies.postman.monitorExists(inputs.monitorId);
129960
130378
  if (valid) {
129961
130379
  resolvedMonitorId = inputs.monitorId;
129962
130380
  dependencies.core.info(`Reusing monitor from explicit input: ${resolvedMonitorId}`);
129963
130381
  } else {
129964
- 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
+ );
129965
130392
  }
129966
130393
  }
129967
130394
  if (!resolvedMonitorId && inputs.smokeCollectionId) {
129968
- const discovered = await dependencies.postman.findMonitorByCollection(
129969
- inputs.smokeCollectionId,
129970
- monitorEnvUid,
129971
- monitorName
129972
- );
129973
- if (discovered) {
129974
- resolvedMonitorId = discovered.uid;
129975
- 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
+ );
129976
130416
  }
129977
130417
  }
129978
130418
  if (!resolvedMonitorId) {
129979
- resolvedMonitorId = await dependencies.postman.createMonitor(
129980
- inputs.workspaceId,
129981
- monitorName,
129982
- inputs.smokeCollectionId,
129983
- monitorEnvUid,
129984
- effectiveCron || void 0
129985
- );
129986
- 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
+ }
129987
130440
  }
129988
130441
  outputs["monitor-id"] = resolvedMonitorId;
129989
130442
  if (!effectiveCron && resolvedMonitorId) {
@@ -129992,7 +130445,13 @@ async function runRepoSyncInner(inputs, dependencies) {
129992
130445
  dependencies.core.info(`Triggered one-time run for monitor: ${resolvedMonitorId}`);
129993
130446
  } catch (error2) {
129994
130447
  dependencies.core.warning(
129995
- `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
+ })
129996
130455
  );
129997
130456
  }
129998
130457
  }
@@ -130000,18 +130459,33 @@ async function runRepoSyncInner(inputs, dependencies) {
130000
130459
  }
130001
130460
  if (inputs.workspaceLinkEnabled && inputs.workspaceId && inputs.repoUrl && dependencies.internalIntegration) {
130002
130461
  try {
130003
- await dependencies.internalIntegration.connectWorkspaceToRepository(
130004
- inputs.workspaceId,
130005
- inputs.repoUrl
130006
- );
130007
- outputs["workspace-link-status"] = "success";
130008
- dependencies.core.info(
130009
- `workspace-link-status=success workspace-id=${inputs.workspaceId} repo=${inputs.repoUrl}`
130010
- );
130462
+ if (skipRepositoryLinkPost) {
130463
+ outputs["workspace-link-status"] = "success";
130464
+ dependencies.core.info(
130465
+ `workspace-link-status=success workspace-id=${inputs.workspaceId} repo=${inputs.repoUrl} (preflight already-target; link POST skipped)`
130466
+ );
130467
+ } else {
130468
+ await dependencies.internalIntegration.connectWorkspaceToRepository(
130469
+ inputs.workspaceId,
130470
+ inputs.repoUrl,
130471
+ repositoryLinkPreflightWasFree ? { preflightWasFree: true } : void 0
130472
+ );
130473
+ outputs["workspace-link-status"] = "success";
130474
+ dependencies.core.info(
130475
+ `workspace-link-status=success workspace-id=${inputs.workspaceId} repo=${inputs.repoUrl}`
130476
+ );
130477
+ }
130011
130478
  } catch (error2) {
130012
130479
  outputs["workspace-link-status"] = "failed";
130013
- const message = `Workspace link failed: ${error2 instanceof Error ? error2.message : String(error2)}`;
130480
+ const rawMessage = error2 instanceof Error ? error2.message : String(error2);
130481
+ const isUnresolvedContract = rawMessage.startsWith(
130482
+ "REPOSITORY_LINK_CONFLICT_UNRESOLVED:"
130483
+ );
130484
+ const message = isUnresolvedContract ? rawMessage : `Workspace link failed: ${rawMessage}`;
130014
130485
  if (branchDecision.tier === "canonical") {
130486
+ if (isUnresolvedContract && error2 instanceof Error) {
130487
+ throw error2;
130488
+ }
130015
130489
  throw new Error(message, { cause: error2 });
130016
130490
  }
130017
130491
  dependencies.core.warning(message);
@@ -130042,18 +130516,57 @@ async function runRepoSyncInner(inputs, dependencies) {
130042
130516
  dependencies.core.info(`Tagged spec version at finalize: ${tag.name || tagName}`);
130043
130517
  } catch (error2) {
130044
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";
130045
130521
  if (status === 409) {
130046
- 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
+ }
130047
130531
  const latest = tags[0];
130048
- 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))) {
130049
130533
  outputs["spec-version-tag"] = latest.name;
130050
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)}`;
130051
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
+ );
130052
130547
  } else {
130053
- 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
+ );
130054
130558
  }
130055
130559
  } else {
130056
- 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
+ );
130057
130570
  }
130058
130571
  }
130059
130572
  }
@@ -130084,7 +130597,16 @@ async function resolvePostmanApiKeyAndTeamId(inputs, actionCore, actionExec, mas
130084
130597
  }
130085
130598
  } catch (error2) {
130086
130599
  if (typeof error2 === "object" && error2 !== null && "status" in error2 && (error2.status === 401 || error2.status === 403)) {
130087
- 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
+ );
130088
130610
  } else {
130089
130611
  throw error2;
130090
130612
  }
@@ -130106,6 +130628,7 @@ async function resolvePostmanApiKeyAndTeamId(inputs, actionCore, actionExec, mas
130106
130628
  const keyName = `repo-sync-action-${Date.now()}`;
130107
130629
  apiKey = await internalIntegration.createApiKey(keyName);
130108
130630
  actionCore.setSecret(apiKey);
130631
+ const persistSecretRemediation = "grant Actions secrets write permission or set POSTMAN_API_KEY manually then rerun";
130109
130632
  if (inputs.provider === "azure-devops") {
130110
130633
  if (options.persistGeneratedApiKeySecret ?? true) {
130111
130634
  actionCore.warning(
@@ -130132,16 +130655,48 @@ async function resolvePostmanApiKeyAndTeamId(inputs, actionCore, actionExec, mas
130132
130655
  ignoreReturnCode: true
130133
130656
  });
130134
130657
  if (ghCommand.exitCode !== 0) {
130135
- 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
+ );
130136
130667
  }
130137
130668
  } catch (error2) {
130138
130669
  actionCore.warning(
130139
- `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
+ })
130140
130677
  );
130141
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
+ );
130142
130689
  }
130143
130690
  } else if (options.persistGeneratedApiKeySecret ?? true) {
130144
- 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
+ );
130145
130700
  } else {
130146
130701
  actionCore.info("Skipping generated POSTMAN_API_KEY GitHub secret persistence for this run.");
130147
130702
  }
@@ -130180,7 +130735,13 @@ async function resolvePostmanApiKeyAndTeamId(inputs, actionCore, actionExec, mas
130180
130735
  const isNonOrgSquads = error2 instanceof HttpError && error2.status === 400 && /squad feature is not available/i.test(error2.responseBody);
130181
130736
  if (!isNonOrgSquads) {
130182
130737
  actionCore.warning(
130183
- `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
+ })
130184
130745
  );
130185
130746
  }
130186
130747
  }
@@ -130300,7 +130861,8 @@ function createRepoSyncDependencies(inputs, resolved, factories, options = {}) {
130300
130861
  core: factories.core,
130301
130862
  postman,
130302
130863
  internalIntegration,
130303
- repoMutation
130864
+ repoMutation,
130865
+ secretMasker: masker
130304
130866
  };
130305
130867
  }
130306
130868
  function decideBranchTier(inputs, env = process.env) {