@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/action.cjs CHANGED
@@ -125116,6 +125116,7 @@ var POSTMAN_ENDPOINT_PROFILES = {
125116
125116
  bifrostBaseUrl: "https://bifrost-premium-https-v4.gw.postman.com",
125117
125117
  fallbackBaseUrl: "https://go.postman.co/_api",
125118
125118
  cliInstallUrl: "https://dl-cli.pstmn.io/install/unix.sh",
125119
+ cliWindowsInstallUrl: "https://dl-cli.pstmn.io/install/win64.ps1",
125119
125120
  iapubBaseUrl: "https://iapub.postman.co"
125120
125121
  },
125121
125122
  beta: {
@@ -125123,6 +125124,7 @@ var POSTMAN_ENDPOINT_PROFILES = {
125123
125124
  bifrostBaseUrl: "https://bifrost-https-v4.gw.postman-beta.com",
125124
125125
  fallbackBaseUrl: "https://go.postman-beta.co/_api",
125125
125126
  cliInstallUrl: "https://dl-cli.pstmn-beta.io/install/unix.sh",
125127
+ cliWindowsInstallUrl: "https://dl-cli.pstmn-beta.io/install/win64.ps1",
125126
125128
  iapubBaseUrl: "https://iapub.postman.co"
125127
125129
  }
125128
125130
  };
@@ -125156,6 +125158,7 @@ function resolvePostmanEndpointProfile(stack, region = "us") {
125156
125158
 
125157
125159
  // src/lib/ci-workflow-template.ts
125158
125160
  var DEFAULT_POSTMAN_CLI_INSTALL_URL = POSTMAN_ENDPOINT_PROFILES.prod.cliInstallUrl;
125161
+ var DEFAULT_POSTMAN_CLI_WINDOWS_INSTALL_URL = POSTMAN_ENDPOINT_PROFILES.prod.cliWindowsInstallUrl;
125159
125162
  function validateHttpsInstallUrl(url) {
125160
125163
  const safeUrlPattern = /^https:\/\/[A-Za-z0-9.-]+\/[A-Za-z0-9._~/?=&%-]+$/;
125161
125164
  if (!safeUrlPattern.test(url)) {
@@ -125172,7 +125175,18 @@ function resolvePostmanRegion(postmanRegionOption) {
125172
125175
  }
125173
125176
  return postmanRegion;
125174
125177
  }
125178
+ function resolveCiRunnerOs(runnerOsOption) {
125179
+ const runnerOs = String(runnerOsOption || "").trim() || "linux";
125180
+ if (runnerOs === "linux" || runnerOs === "windows") {
125181
+ return runnerOs;
125182
+ }
125183
+ throw new Error("ci-runner-os must be one of: linux, windows; got: " + runnerOs);
125184
+ }
125175
125185
  function renderCiWorkflowTemplate(options = {}) {
125186
+ const runnerOs = resolveCiRunnerOs(options.runnerOs);
125187
+ if (runnerOs === "windows") {
125188
+ throw new Error("ci-runner-os=windows is currently supported for azure-devops workflows only");
125189
+ }
125176
125190
  const rawUrl = String(options.postmanCliInstallUrl || "").trim() || DEFAULT_POSTMAN_CLI_INSTALL_URL;
125177
125191
  const installUrl = validateHttpsInstallUrl(rawUrl);
125178
125192
  const postmanRegion = resolvePostmanRegion(options.postmanRegion);
@@ -125279,6 +125293,132 @@ function buildCiWorkflowLines(installUrl, postmanRegion) {
125279
125293
  ];
125280
125294
  }
125281
125295
  var CI_WORKFLOW_TEMPLATE = renderCiWorkflowTemplate();
125296
+ function buildAdoWindowsCollectionRunLines(displayName, collectionEnvironmentName) {
125297
+ return [
125298
+ " - pwsh: |",
125299
+ " $ErrorActionPreference = 'Stop'",
125300
+ " function Resolve-AdoOptional([string]$Value) {",
125301
+ " if ($Value -match '^\\$\\([^)]+\\)$') { return '' }",
125302
+ " return $Value",
125303
+ " }",
125304
+ ` $collectionUid = $env:${collectionEnvironmentName}`,
125305
+ " $ciEnvironment = Resolve-AdoOptional $env:CI_ENVIRONMENT",
125306
+ " if ([string]::IsNullOrWhiteSpace($ciEnvironment)) { $ciEnvironment = 'Production' }",
125307
+ ` $arguments = @('collection', 'run', $collectionUid, '-e', $env:POSTMAN_ENVIRONMENT_UID, '--report-events', '--env-var', "CI_ENVIRONMENT=$ciEnvironment")`,
125308
+ " $sslRoot = Join-Path $env:AGENT_TEMPDIRECTORY 'postman-ssl'",
125309
+ " $clientCert = Join-Path $sslRoot 'client.crt'",
125310
+ " $clientKey = Join-Path $sslRoot 'client.key'",
125311
+ " $caCert = Join-Path $sslRoot 'ca.crt'",
125312
+ " if (Test-Path -LiteralPath $clientCert) {",
125313
+ " $arguments += @('--ssl-client-cert', $clientCert, '--ssl-client-key', $clientKey)",
125314
+ " $passphrase = Resolve-AdoOptional $env:POSTMAN_SSL_CLIENT_PASSPHRASE",
125315
+ " if (-not [string]::IsNullOrWhiteSpace($passphrase)) {",
125316
+ " $arguments += @('--ssl-client-passphrase', $passphrase)",
125317
+ " }",
125318
+ " if (Test-Path -LiteralPath $caCert) {",
125319
+ " $arguments += @('--ssl-extra-ca-certs', $caCert)",
125320
+ " }",
125321
+ " }",
125322
+ " & postman @arguments",
125323
+ ` if ($LASTEXITCODE -ne 0) { throw '${displayName} failed with exit code ' + $LASTEXITCODE }`,
125324
+ ` displayName: ${displayName}`,
125325
+ " env:",
125326
+ ` ${collectionEnvironmentName}: $(${collectionEnvironmentName})`,
125327
+ " POSTMAN_ENVIRONMENT_UID: $(POSTMAN_ENVIRONMENT_UID)",
125328
+ " CI_ENVIRONMENT: $(CI_ENVIRONMENT)",
125329
+ " POSTMAN_SSL_CLIENT_PASSPHRASE: $(POSTMAN_SSL_CLIENT_PASSPHRASE)"
125330
+ ];
125331
+ }
125332
+ function buildAdoWindowsCiWorkflowLines(installUrl, postmanRegion) {
125333
+ return [
125334
+ "trigger:",
125335
+ " branches:",
125336
+ " include:",
125337
+ " - main",
125338
+ "schedules:",
125339
+ ' - cron: "0 */6 * * *"',
125340
+ " displayName: Scheduled run",
125341
+ " branches:",
125342
+ " include:",
125343
+ " - main",
125344
+ " always: true",
125345
+ "pool:",
125346
+ " vmImage: windows-latest",
125347
+ "steps:",
125348
+ " - checkout: self",
125349
+ " persistCredentials: true",
125350
+ " - pwsh: |",
125351
+ " $ErrorActionPreference = 'Stop'",
125352
+ " if (-not (Get-Command postman -ErrorAction SilentlyContinue)) {",
125353
+ " [System.Net.ServicePointManager]::SecurityProtocol = 3072",
125354
+ " Invoke-Expression ((New-Object System.Net.WebClient).DownloadString($env:POSTMAN_CLI_INSTALL_URL))",
125355
+ " }",
125356
+ " & postman --version",
125357
+ " if ($LASTEXITCODE -ne 0) { throw 'Postman CLI installation failed' }",
125358
+ " displayName: Install Postman CLI",
125359
+ " env:",
125360
+ ` POSTMAN_CLI_INSTALL_URL: ${installUrl}`,
125361
+ " - pwsh: |",
125362
+ " $ErrorActionPreference = 'Stop'",
125363
+ " $arguments = @('login', '--with-api-key', $env:POSTMAN_API_KEY)",
125364
+ ...postmanRegion === "eu" ? [" $arguments += @('--region', 'eu')"] : [],
125365
+ " & postman @arguments",
125366
+ " if ($LASTEXITCODE -ne 0) { throw 'Postman CLI login failed' }",
125367
+ " displayName: Login to Postman CLI",
125368
+ " env:",
125369
+ " POSTMAN_API_KEY: $(POSTMAN_API_KEY)",
125370
+ " - pwsh: |",
125371
+ " $ErrorActionPreference = 'Stop'",
125372
+ " $section = ''",
125373
+ " $smoke = ''",
125374
+ " $contract = ''",
125375
+ " $environment = ''",
125376
+ " $fallbackEnvironment = ''",
125377
+ " foreach ($line in Get-Content -LiteralPath '.postman/resources.yaml') {",
125378
+ " if ($line -match '^ (collections|environments):\\s*$') { $section = $Matches[1]; continue }",
125379
+ " if ($line -notmatch '^ (.+?):\\s+(.+?)\\s*$') { continue }",
125380
+ ` $key = $Matches[1].Trim().Trim("'").Trim('"')`,
125381
+ ` $value = $Matches[2].Trim().Trim("'").Trim('"')`,
125382
+ " if ($section -eq 'collections' -and $key -match '\\[Smoke\\]') { $smoke = $value }",
125383
+ " if ($section -eq 'collections' -and $key -match '\\[Contract\\]') { $contract = $value }",
125384
+ " if ($section -eq 'environments') {",
125385
+ " if ([string]::IsNullOrWhiteSpace($fallbackEnvironment)) { $fallbackEnvironment = $value }",
125386
+ " if ($key -match 'prod\\.postman_environment\\.json$') { $environment = $value }",
125387
+ " }",
125388
+ " }",
125389
+ " if ([string]::IsNullOrWhiteSpace($environment)) { $environment = $fallbackEnvironment }",
125390
+ " if ([string]::IsNullOrWhiteSpace($smoke)) { throw 'Missing smoke collection UID in .postman/resources.yaml' }",
125391
+ " if ([string]::IsNullOrWhiteSpace($contract)) { throw 'Missing contract collection UID in .postman/resources.yaml' }",
125392
+ " if ([string]::IsNullOrWhiteSpace($environment)) { throw 'Missing environment UID in .postman/resources.yaml' }",
125393
+ ' Write-Host "##vso[task.setvariable variable=POSTMAN_SMOKE_COLLECTION_UID]$smoke"',
125394
+ ' Write-Host "##vso[task.setvariable variable=POSTMAN_CONTRACT_COLLECTION_UID]$contract"',
125395
+ ' Write-Host "##vso[task.setvariable variable=POSTMAN_ENVIRONMENT_UID]$environment"',
125396
+ " displayName: Resolve Postman Resource IDs",
125397
+ " - pwsh: |",
125398
+ " $ErrorActionPreference = 'Stop'",
125399
+ " function Resolve-AdoOptional([string]$Value) {",
125400
+ " if ($Value -match '^\\$\\([^)]+\\)$') { return '' }",
125401
+ " return $Value",
125402
+ " }",
125403
+ " $sslRoot = Join-Path $env:AGENT_TEMPDIRECTORY 'postman-ssl'",
125404
+ " New-Item -ItemType Directory -Path $sslRoot -Force | Out-Null",
125405
+ " [IO.File]::WriteAllBytes((Join-Path $sslRoot 'client.crt'), [Convert]::FromBase64String($env:POSTMAN_SSL_CLIENT_CERT_B64))",
125406
+ " [IO.File]::WriteAllBytes((Join-Path $sslRoot 'client.key'), [Convert]::FromBase64String($env:POSTMAN_SSL_CLIENT_KEY_B64))",
125407
+ " $extraCa = Resolve-AdoOptional $env:POSTMAN_SSL_EXTRA_CA_CERTS_B64",
125408
+ " if (-not [string]::IsNullOrWhiteSpace($extraCa)) {",
125409
+ " [IO.File]::WriteAllBytes((Join-Path $sslRoot 'ca.crt'), [Convert]::FromBase64String($extraCa))",
125410
+ " }",
125411
+ " condition: ne(variables['POSTMAN_SSL_CLIENT_CERT_B64'], '')",
125412
+ " displayName: Decode SSL certificates",
125413
+ " env:",
125414
+ " POSTMAN_SSL_CLIENT_CERT_B64: $(POSTMAN_SSL_CLIENT_CERT_B64)",
125415
+ " POSTMAN_SSL_CLIENT_KEY_B64: $(POSTMAN_SSL_CLIENT_KEY_B64)",
125416
+ " POSTMAN_SSL_EXTRA_CA_CERTS_B64: $(POSTMAN_SSL_EXTRA_CA_CERTS_B64)",
125417
+ ...buildAdoWindowsCollectionRunLines("Run Smoke Tests", "POSTMAN_SMOKE_COLLECTION_UID"),
125418
+ ...buildAdoWindowsCollectionRunLines("Run Contract Tests", "POSTMAN_CONTRACT_COLLECTION_UID"),
125419
+ ""
125420
+ ];
125421
+ }
125282
125422
  function buildAdoCiWorkflowLines(installUrl, postmanRegion) {
125283
125423
  return [
125284
125424
  "trigger:",
@@ -125458,10 +125598,12 @@ function renderGcWorkflowTemplate() {
125458
125598
  }
125459
125599
  var GC_WORKFLOW_TEMPLATE = renderGcWorkflowTemplate();
125460
125600
  function getCiWorkflowTemplate(provider, options = {}) {
125601
+ const runnerOs = resolveCiRunnerOs(options.runnerOs);
125461
125602
  if (provider === "azure-devops") {
125462
- const rawUrl = String(options.postmanCliInstallUrl || "").trim() || DEFAULT_POSTMAN_CLI_INSTALL_URL;
125603
+ const rawUrl = runnerOs === "windows" ? String(options.postmanCliWindowsInstallUrl || "").trim() || DEFAULT_POSTMAN_CLI_WINDOWS_INSTALL_URL : String(options.postmanCliInstallUrl || "").trim() || DEFAULT_POSTMAN_CLI_INSTALL_URL;
125463
125604
  const postmanRegion = resolvePostmanRegion(options.postmanRegion);
125464
- return buildAdoCiWorkflowLines(validateHttpsInstallUrl(rawUrl), postmanRegion).join("\n");
125605
+ const installUrl = validateHttpsInstallUrl(rawUrl);
125606
+ return (runnerOs === "windows" ? buildAdoWindowsCiWorkflowLines(installUrl, postmanRegion) : buildAdoCiWorkflowLines(installUrl, postmanRegion)).join("\n");
125465
125607
  }
125466
125608
  return renderCiWorkflowTemplate(options);
125467
125609
  }
@@ -126856,6 +126998,17 @@ var BifrostInternalIntegrationAdapter = class {
126856
126998
  mask: this.secretMasker
126857
126999
  };
126858
127000
  }
127001
+ /**
127002
+ * One-line non-fatal preflight reason: operation + entity (repo/path) + cause +
127003
+ * operator action. Secrets are redacted via secretMasker, then CR/LF and other
127004
+ * line-breaking whitespace are collapsed to spaces so CI logs stay one line.
127005
+ * Display-only: does not alter request repoUrl/path or probe classification.
127006
+ */
127007
+ unknownFilesystemLookupReason(repoUrl, fsPath, cause) {
127008
+ return this.secretMasker(
127009
+ `filesystem lookup for repository ${repoUrl} path ${fsPath} ${cause}; verify Bifrost connectivity/credentials then rerun`
127010
+ ).replace(/[\r\n\v\f\u2028\u2029]+/g, " ");
127011
+ }
126859
127012
  async associateSystemEnvironments(workspaceId, associations) {
126860
127013
  if (associations.length === 0) {
126861
127014
  return;
@@ -126895,7 +127048,106 @@ var BifrostInternalIntegrationAdapter = class {
126895
127048
  throw advised ?? httpErr;
126896
127049
  }
126897
127050
  }
126898
- async connectWorkspaceToRepository(workspaceId, repoUrl) {
127051
+ /**
127052
+ * Probe who (if anyone) already owns the global `(repo, path)` filesystem
127053
+ * link. Non-fatal: unexpected statuses / network errors yield `unknown` so
127054
+ * callers can proceed and rely on reactive POST conflict handling.
127055
+ *
127056
+ * Bifrost states (FileSystemController.fetchWorkspaceForFileSystem):
127057
+ * - 200 + data null -> free
127058
+ * - 200 + data -> linked-visible (workspace payload)
127059
+ * - 403 + error.meta.workspaceId -> linked-invisible
127060
+ * - 200 + error.meta.workspaceId -> linked-invisible (proxy envelope)
127061
+ */
127062
+ async findWorkspaceForRepo(repoUrl, path9 = "/") {
127063
+ const fsPath = path9 && path9.trim() ? path9.trim() : "/";
127064
+ const url = `${this.bifrostBaseUrl}/ws/proxy`;
127065
+ const payload = {
127066
+ service: "workspaces",
127067
+ method: "GET",
127068
+ path: `/workspaces/filesystem?repo=${encodeURIComponent(repoUrl)}&path=${encodeURIComponent(fsPath)}`
127069
+ };
127070
+ try {
127071
+ const response = await this.fetchImpl(url, {
127072
+ method: "POST",
127073
+ headers: this.bifrostHeaders(),
127074
+ body: JSON.stringify(payload)
127075
+ });
127076
+ let parsed = {};
127077
+ try {
127078
+ parsed = await response.json();
127079
+ } catch {
127080
+ return {
127081
+ state: "unknown",
127082
+ reason: this.unknownFilesystemLookupReason(
127083
+ repoUrl,
127084
+ fsPath,
127085
+ `returned non-JSON body (HTTP ${response.status})`
127086
+ )
127087
+ };
127088
+ }
127089
+ const errorMetaWorkspaceId = typeof parsed?.error?.meta?.workspaceId === "string" && parsed.error.meta.workspaceId.trim() ? parsed.error.meta.workspaceId.trim() : void 0;
127090
+ if (errorMetaWorkspaceId) {
127091
+ return { state: "linked-invisible", workspaceId: errorMetaWorkspaceId };
127092
+ }
127093
+ if (response.ok) {
127094
+ if (parsed?.data == null) {
127095
+ return { state: "free" };
127096
+ }
127097
+ const id = typeof parsed.data.id === "string" ? parsed.data.id.trim() : "";
127098
+ if (!id) {
127099
+ return {
127100
+ state: "unknown",
127101
+ reason: this.unknownFilesystemLookupReason(
127102
+ repoUrl,
127103
+ fsPath,
127104
+ "returned 200 without a workspace id"
127105
+ )
127106
+ };
127107
+ }
127108
+ const name = typeof parsed.data.name === "string" && parsed.data.name.trim() ? parsed.data.name.trim() : void 0;
127109
+ return {
127110
+ state: "linked-visible",
127111
+ workspace: name ? { id, name } : { id }
127112
+ };
127113
+ }
127114
+ if (response.status === 403) {
127115
+ const workspaceId = extractDuplicateWorkspaceId(
127116
+ JSON.stringify(parsed)
127117
+ );
127118
+ if (workspaceId) {
127119
+ return { state: "linked-invisible", workspaceId };
127120
+ }
127121
+ return {
127122
+ state: "unknown",
127123
+ reason: this.unknownFilesystemLookupReason(
127124
+ repoUrl,
127125
+ fsPath,
127126
+ "returned 403 without error.meta.workspaceId"
127127
+ )
127128
+ };
127129
+ }
127130
+ return {
127131
+ state: "unknown",
127132
+ reason: this.unknownFilesystemLookupReason(
127133
+ repoUrl,
127134
+ fsPath,
127135
+ `returned HTTP ${response.status}`
127136
+ )
127137
+ };
127138
+ } catch (error2) {
127139
+ const cause = error2 instanceof Error ? error2.message : String(error2);
127140
+ return {
127141
+ state: "unknown",
127142
+ reason: this.unknownFilesystemLookupReason(
127143
+ repoUrl,
127144
+ fsPath,
127145
+ `failed: ${cause}`
127146
+ )
127147
+ };
127148
+ }
127149
+ }
127150
+ async connectWorkspaceToRepository(workspaceId, repoUrl, options) {
126899
127151
  const url = `${this.bifrostBaseUrl}/ws/proxy`;
126900
127152
  const payload = {
126901
127153
  service: "workspaces",
@@ -126922,6 +127174,11 @@ var BifrostInternalIntegrationAdapter = class {
126922
127174
  if (isDuplicate) {
126923
127175
  const existingWorkspaceId = extractDuplicateWorkspaceId(body);
126924
127176
  if (existingWorkspaceId && existingWorkspaceId !== workspaceId) {
127177
+ if (options?.preflightWasFree) {
127178
+ throw new Error(
127179
+ `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.`
127180
+ );
127181
+ }
126925
127182
  throw new Error(
126926
127183
  await this.describeWorkspaceLinkConflict(existingWorkspaceId, workspaceId, repoUrl)
126927
127184
  );
@@ -127100,6 +127357,12 @@ var postmanRepoSyncActionContract = {
127100
127357
  required: false,
127101
127358
  default: ".github/workflows/ci.yml"
127102
127359
  },
127360
+ "ci-runner-os": {
127361
+ description: "Runner operating system for the generated CI workflow.",
127362
+ required: false,
127363
+ default: "linux",
127364
+ allowedValues: ["linux", "windows"]
127365
+ },
127103
127366
  "project-name": {
127104
127367
  description: "Service project name used for environment, mock, and monitor naming.",
127105
127368
  required: true
@@ -128811,6 +129074,25 @@ function parseAssetMarker(description) {
128811
129074
  }
128812
129075
 
128813
129076
  // src/index.ts
129077
+ var identitySecretMasker = (input) => input;
129078
+ function resolveRepoSyncMasker(dependencies) {
129079
+ return dependencies.secretMasker ?? identitySecretMasker;
129080
+ }
129081
+ function causeText(cause) {
129082
+ return cause instanceof Error ? cause.message : String(cause);
129083
+ }
129084
+ function toOneLineDisplay(value) {
129085
+ return String(value ?? "").replace(/[\r\n\u0085\u2028\u2029\v\f]+/g, " ").replace(/[ \t]{2,}/g, " ").trim();
129086
+ }
129087
+ function formatOrchestrationIssue(params) {
129088
+ const operation = toOneLineDisplay(params.mask(params.operation));
129089
+ const entity = toOneLineDisplay(params.mask(params.entity));
129090
+ const maskedCause = toOneLineDisplay(params.mask(causeText(params.cause)));
129091
+ const detail = toOneLineDisplay(params.mask(params.detail ?? ""));
129092
+ const remediation = toOneLineDisplay(params.mask(params.remediation));
129093
+ const body = `${operation} failed for ${entity}: ${maskedCause}${detail}`.replace(/[.]+$/u, "");
129094
+ return toOneLineDisplay(`${body}. ${remediation}`);
129095
+ }
128814
129096
  function parseBooleanInput(value, defaultValue) {
128815
129097
  const normalized = String(value || "").trim().toLowerCase();
128816
129098
  if (!normalized) return defaultValue;
@@ -128910,6 +129192,17 @@ function parseBranchStrategy(value) {
128910
129192
  `Unsupported branch-strategy "${normalized}". Supported values: ${allowed.join(", ")}`
128911
129193
  );
128912
129194
  }
129195
+ function parseCiRunnerOs(value) {
129196
+ const definition = postmanRepoSyncActionContract.inputs["ci-runner-os"];
129197
+ const allowed = definition.allowedValues ?? [];
129198
+ const normalized = String(value || "").trim() || (definition.default ?? "linux");
129199
+ if (allowed.includes(normalized)) {
129200
+ return normalized;
129201
+ }
129202
+ throw new Error(
129203
+ `Unsupported ci-runner-os "${normalized}". Supported values: ${allowed.join(", ")}`
129204
+ );
129205
+ }
128913
129206
  function normalizeReleaseLabel(value) {
128914
129207
  const cleaned = normalizeInputValue(value).replace(/^refs\/heads\//, "").replace(/^refs\/tags\//, "").replace(/\s+/g, "-").replace(/[^A-Za-z0-9._-]/g, "-").replace(/-+/g, "-").replace(/^[._-]+|[._-]+$/g, "");
128915
129208
  return cleaned;
@@ -128987,6 +129280,7 @@ function resolveInputs(env = process.env) {
128987
129280
  provider: repoContext.provider,
128988
129281
  ciWorkflowBase64: getInput2("ci-workflow-base64", env),
128989
129282
  generateCiWorkflow: parseBooleanInput(getInput2("generate-ci-workflow", env), true),
129283
+ ciRunnerOs: parseCiRunnerOs(getInput2("ci-runner-os", env)),
128990
129284
  monitorType: getInput2("monitor-type", env) || "cloud",
128991
129285
  ciWorkflowPath: getInput2("ci-workflow-path", env) || (repoContext.provider === "azure-devops" ? "azure-pipelines.yml" : ".github/workflows/ci.yml"),
128992
129286
  orgMode: parseBooleanInput(getInput2("org-mode", env), false),
@@ -129005,6 +129299,7 @@ function resolveInputs(env = process.env) {
129005
129299
  postmanBifrostBase: endpointProfile.bifrostBaseUrl,
129006
129300
  postmanFallbackBase: endpointProfile.fallbackBaseUrl,
129007
129301
  postmanCliInstallUrl: endpointProfile.cliInstallUrl,
129302
+ postmanCliWindowsInstallUrl: endpointProfile.cliWindowsInstallUrl,
129008
129303
  postmanIapubBase: endpointProfile.iapubBaseUrl
129009
129304
  };
129010
129305
  }
@@ -129274,6 +129569,7 @@ function readActionInputs(actionCore) {
129274
129569
  INPUT_GH_FALLBACK_TOKEN: ghFallbackToken,
129275
129570
  INPUT_CI_WORKFLOW_BASE64: readInput(actionCore, "ci-workflow-base64"),
129276
129571
  INPUT_GENERATE_CI_WORKFLOW: readInput(actionCore, "generate-ci-workflow"),
129572
+ INPUT_CI_RUNNER_OS: readInput(actionCore, "ci-runner-os"),
129277
129573
  INPUT_MONITOR_TYPE: readInput(actionCore, "monitor-type") || "cloud",
129278
129574
  INPUT_CI_WORKFLOW_PATH: readInput(actionCore, "ci-workflow-path"),
129279
129575
  INPUT_ORG_MODE: readInput(actionCore, "org-mode"),
@@ -129391,19 +129687,34 @@ async function upsertEnvironments(inputs, dependencies, resourcesState, assetMar
129391
129687
  if (!inputs.workspaceId) {
129392
129688
  return envUids;
129393
129689
  }
129690
+ const mask = resolveRepoSyncMasker(dependencies);
129691
+ const envRemediation = "verify access-token/team/workspace permissions then rerun";
129394
129692
  for (const envName of inputs.environments) {
129395
129693
  const runtimeUrl = String(inputs.envRuntimeUrls[envName] || "").trim();
129396
129694
  const displayName = `${inputs.projectName} - ${envName}`;
129397
129695
  let existingUid = String(envUids[envName] || "").trim();
129398
129696
  if (!existingUid) {
129399
- const discovered = await dependencies.postman.findEnvironmentByName(
129400
- inputs.workspaceId,
129401
- displayName
129402
- );
129403
- if (discovered?.uid) {
129404
- existingUid = discovered.uid;
129405
- dependencies.core.info(
129406
- `Discovered existing environment for ${displayName}: ${existingUid}`
129697
+ try {
129698
+ const discovered = await dependencies.postman.findEnvironmentByName(
129699
+ inputs.workspaceId,
129700
+ displayName
129701
+ );
129702
+ if (discovered?.uid) {
129703
+ existingUid = discovered.uid;
129704
+ dependencies.core.info(
129705
+ `Discovered existing environment for ${displayName}: ${existingUid}`
129706
+ );
129707
+ }
129708
+ } catch (error2) {
129709
+ throw new Error(
129710
+ formatOrchestrationIssue({
129711
+ operation: "Environment discovery",
129712
+ entity: `workspace ${inputs.workspaceId} environment "${displayName}"`,
129713
+ cause: error2,
129714
+ remediation: envRemediation,
129715
+ mask
129716
+ }),
129717
+ { cause: error2 }
129407
129718
  );
129408
129719
  }
129409
129720
  }
@@ -129423,17 +129734,43 @@ async function upsertEnvironments(inputs, dependencies, resourcesState, assetMar
129423
129734
  }
129424
129735
  const values2 = buildEnvironmentValues(envName, runtimeUrl);
129425
129736
  if (marker) values2.push({ key: "x-pm-onboarding", value: JSON.stringify(marker), type: "default" });
129426
- await dependencies.postman.updateEnvironment(existingUid, displayName, values2);
129737
+ try {
129738
+ await dependencies.postman.updateEnvironment(existingUid, displayName, values2);
129739
+ } catch (error2) {
129740
+ throw new Error(
129741
+ formatOrchestrationIssue({
129742
+ operation: "Environment update",
129743
+ entity: `workspace ${inputs.workspaceId} environment "${displayName}" (uid ${existingUid})`,
129744
+ cause: error2,
129745
+ remediation: envRemediation,
129746
+ mask
129747
+ }),
129748
+ { cause: error2 }
129749
+ );
129750
+ }
129427
129751
  envUids[envName] = existingUid;
129428
129752
  continue;
129429
129753
  }
129430
129754
  const values = buildEnvironmentValues(envName, runtimeUrl);
129431
129755
  if (assetMarker) values.push({ key: "x-pm-onboarding", value: JSON.stringify(assetMarker), type: "default" });
129432
- envUids[envName] = await dependencies.postman.createEnvironment(
129433
- inputs.workspaceId,
129434
- displayName,
129435
- values
129436
- );
129756
+ try {
129757
+ envUids[envName] = await dependencies.postman.createEnvironment(
129758
+ inputs.workspaceId,
129759
+ displayName,
129760
+ values
129761
+ );
129762
+ } catch (error2) {
129763
+ throw new Error(
129764
+ formatOrchestrationIssue({
129765
+ operation: "Environment create",
129766
+ entity: `workspace ${inputs.workspaceId} environment "${displayName}"`,
129767
+ cause: error2,
129768
+ remediation: envRemediation,
129769
+ mask
129770
+ }),
129771
+ { cause: error2 }
129772
+ );
129773
+ }
129437
129774
  }
129438
129775
  return envUids;
129439
129776
  }
@@ -129690,11 +130027,15 @@ function renderCiWorkflow(inputs) {
129690
130027
  if (inputs.provider === "azure-devops") {
129691
130028
  return getCiWorkflowTemplate(inputs.provider, {
129692
130029
  postmanCliInstallUrl: inputs.postmanCliInstallUrl,
130030
+ postmanCliWindowsInstallUrl: inputs.postmanCliWindowsInstallUrl,
130031
+ runnerOs: inputs.ciRunnerOs,
129693
130032
  postmanRegion: inputs.postmanRegion
129694
130033
  });
129695
130034
  }
129696
130035
  return renderCiWorkflowTemplate({
129697
130036
  postmanCliInstallUrl: inputs.postmanCliInstallUrl,
130037
+ postmanCliWindowsInstallUrl: inputs.postmanCliWindowsInstallUrl,
130038
+ runnerOs: inputs.ciRunnerOs,
129698
130039
  postmanRegion: inputs.postmanRegion
129699
130040
  });
129700
130041
  }
@@ -129789,6 +130130,7 @@ async function runRepoSync(inputs, dependencies) {
129789
130130
  }
129790
130131
  }
129791
130132
  async function runRepoSyncInner(inputs, dependencies) {
130133
+ const mask = resolveRepoSyncMasker(dependencies);
129792
130134
  const branchDecision = decideBranchTier(inputs);
129793
130135
  assertBranchAssetIds(inputs, branchDecision);
129794
130136
  const isCanonicalWriter = branchDecision.tier === "legacy" || branchDecision.tier === "canonical";
@@ -129848,6 +130190,39 @@ async function runRepoSyncInner(inputs, dependencies) {
129848
130190
  }
129849
130191
  }
129850
130192
  }
130193
+ let skipRepositoryLinkPost = false;
130194
+ let repositoryLinkPreflightWasFree = false;
130195
+ if (inputs.workspaceLinkEnabled && inputs.workspaceId && inputs.repoUrl && dependencies.internalIntegration?.findWorkspaceForRepo) {
130196
+ const probe = await dependencies.internalIntegration.findWorkspaceForRepo(
130197
+ inputs.repoUrl,
130198
+ "/"
130199
+ );
130200
+ if (probe.state === "linked-visible") {
130201
+ if (probe.workspace.id === inputs.workspaceId) {
130202
+ skipRepositoryLinkPost = true;
130203
+ dependencies.core.info(
130204
+ `REPOSITORY_LINK_ALREADY_TARGET: Repository ${inputs.repoUrl} at path / is already linked to target workspace ${inputs.workspaceId}; continuing.`
130205
+ );
130206
+ } else {
130207
+ const ownerName = probe.workspace.name?.trim() || "unknown";
130208
+ throw new Error(
130209
+ `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.`
130210
+ );
130211
+ }
130212
+ } else if (probe.state === "linked-invisible") {
130213
+ 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.`;
130214
+ if (inputs.orgMode) {
130215
+ message += " Verify workspace-team-id; if the owner is in another sub-team, ask that sub-team's admin to disconnect it.";
130216
+ }
130217
+ throw new Error(message);
130218
+ } else if (probe.state === "free") {
130219
+ repositoryLinkPreflightWasFree = true;
130220
+ } else {
130221
+ dependencies.core.warning(
130222
+ `REPOSITORY_LINK_PREFLIGHT_UNKNOWN: Unable to determine the existing repository link (${probe.reason}); continuing and relying on link creation conflict handling.`
130223
+ );
130224
+ }
130225
+ }
129851
130226
  const branchAssetMarker = buildBranchAssetMarker(branchDecision, inputs);
129852
130227
  const envUids = await upsertEnvironments(inputs, dependencies, resourcesState, branchAssetMarker);
129853
130228
  outputs["environment-uids-json"] = JSON.stringify(envUids);
@@ -129865,8 +130240,15 @@ async function runRepoSyncInner(inputs, dependencies) {
129865
130240
  outputs["environment-sync-status"] = "success";
129866
130241
  } catch (error2) {
129867
130242
  outputs["environment-sync-status"] = "failed";
130243
+ const associationSummary = associations.map((entry) => `${entry.envUid}->${entry.systemEnvId}`).join(", ");
129868
130244
  dependencies.core.warning(
129869
- `System environment association failed: ${error2 instanceof Error ? error2.message : String(error2)}`
130245
+ formatOrchestrationIssue({
130246
+ operation: "System environment association",
130247
+ entity: `workspace ${inputs.workspaceId} (${associationSummary})`,
130248
+ cause: error2,
130249
+ remediation: "verify access-token/team/system-env mapping then rerun",
130250
+ mask
130251
+ })
129870
130252
  );
129871
130253
  }
129872
130254
  } else if (Object.keys(envUids).length > 0) {
@@ -129894,37 +130276,72 @@ async function runRepoSyncInner(inputs, dependencies) {
129894
130276
  dependencies.core.info(`Reusing mock from explicit input: ${resolvedMockUrl}`);
129895
130277
  }
129896
130278
  if (!resolvedMockUrl && inputs.baselineCollectionId) {
129897
- const discovered = await dependencies.postman.findMockByCollection(
129898
- inputs.baselineCollectionId,
129899
- mockEnvUid,
129900
- mockName
129901
- );
129902
- if (discovered) {
129903
- resolvedMockUrl = discovered.mockUrl;
129904
- dependencies.core.info(`Discovered existing mock for collection ${inputs.baselineCollectionId}: ${resolvedMockUrl}`);
130279
+ try {
130280
+ const discovered = await dependencies.postman.findMockByCollection(
130281
+ inputs.baselineCollectionId,
130282
+ mockEnvUid,
130283
+ mockName
130284
+ );
130285
+ if (discovered) {
130286
+ resolvedMockUrl = discovered.mockUrl;
130287
+ dependencies.core.info(`Discovered existing mock for collection ${inputs.baselineCollectionId}: ${resolvedMockUrl}`);
130288
+ }
130289
+ } catch (error2) {
130290
+ throw new Error(
130291
+ formatOrchestrationIssue({
130292
+ operation: "Mock discovery",
130293
+ entity: `mock "${mockName}" workspace ${inputs.workspaceId} collection ${inputs.baselineCollectionId} environment ${mockEnvUid}`,
130294
+ cause: error2,
130295
+ remediation: "verify collection/environment access then rerun",
130296
+ mask
130297
+ }),
130298
+ { cause: error2 }
130299
+ );
129905
130300
  }
129906
130301
  }
129907
130302
  if (!resolvedMockUrl) {
129908
- const mock = await retry(
129909
- () => dependencies.postman.createMock(
129910
- inputs.workspaceId,
129911
- mockName,
129912
- inputs.baselineCollectionId,
129913
- mockEnvUid
129914
- ),
129915
- {
129916
- maxAttempts: 3,
129917
- delayMs: 2e3,
129918
- backoffMultiplier: 2,
129919
- onRetry: ({ attempt, error: error2 }) => {
129920
- dependencies.core.warning(
129921
- `Mock create attempt ${attempt} failed (${error2 instanceof Error ? error2.message : String(error2)}); retrying`
129922
- );
130303
+ const mockEntity = `mock "${mockName}" workspace ${inputs.workspaceId} collection ${inputs.baselineCollectionId} environment ${mockEnvUid}`;
130304
+ const mockRemediation = "verify collection/environment access then rerun";
130305
+ try {
130306
+ const mock = await retry(
130307
+ () => dependencies.postman.createMock(
130308
+ inputs.workspaceId,
130309
+ mockName,
130310
+ inputs.baselineCollectionId,
130311
+ mockEnvUid
130312
+ ),
130313
+ {
130314
+ maxAttempts: 3,
130315
+ delayMs: 2e3,
130316
+ backoffMultiplier: 2,
130317
+ onRetry: ({ attempt, maxAttempts, error: error2 }) => {
130318
+ dependencies.core.warning(
130319
+ formatOrchestrationIssue({
130320
+ operation: `Mock create attempt ${attempt}/${maxAttempts}`,
130321
+ entity: mockEntity,
130322
+ cause: error2,
130323
+ remediation: mockRemediation,
130324
+ mask,
130325
+ detail: "; retrying"
130326
+ })
130327
+ );
130328
+ }
129923
130329
  }
129924
- }
129925
- );
129926
- resolvedMockUrl = mock.url;
129927
- dependencies.core.info(`Created new mock: ${resolvedMockUrl}`);
130330
+ );
130331
+ resolvedMockUrl = mock.url;
130332
+ dependencies.core.info(`Created new mock: ${resolvedMockUrl}`);
130333
+ } catch (error2) {
130334
+ throw new Error(
130335
+ formatOrchestrationIssue({
130336
+ operation: "Mock create",
130337
+ entity: mockEntity,
130338
+ cause: error2,
130339
+ remediation: mockRemediation,
130340
+ mask
130341
+ }),
130342
+ { cause: error2 }
130343
+ );
130344
+ }
129928
130345
  }
129929
130346
  outputs["mock-url"] = resolvedMockUrl;
129930
130347
  }
@@ -129935,35 +130352,71 @@ async function runRepoSyncInner(inputs, dependencies) {
129935
130352
  if (monitorEnvUid && inputs.monitorType !== "cli") {
129936
130353
  let resolvedMonitorId = "";
129937
130354
  const monitorName = `${assetProjectName} - Smoke Monitor`;
130355
+ const monitorRemediation = "verify monitor IDs/access or set monitor-cron then rerun";
129938
130356
  if (inputs.monitorId) {
129939
130357
  const valid = await dependencies.postman.monitorExists(inputs.monitorId);
129940
130358
  if (valid) {
129941
130359
  resolvedMonitorId = inputs.monitorId;
129942
130360
  dependencies.core.info(`Reusing monitor from explicit input: ${resolvedMonitorId}`);
129943
130361
  } else {
129944
- dependencies.core.warning(`Explicit monitor-id ${inputs.monitorId} not found in Postman, falling through to discovery.`);
130362
+ dependencies.core.warning(
130363
+ formatOrchestrationIssue({
130364
+ operation: "Explicit monitor-id lookup",
130365
+ entity: `monitor-id ${inputs.monitorId} workspace ${inputs.workspaceId} collection ${inputs.smokeCollectionId} environment ${monitorEnvUid}`,
130366
+ cause: "not found in Postman",
130367
+ remediation: monitorRemediation,
130368
+ mask,
130369
+ detail: "; falling through to discovery"
130370
+ })
130371
+ );
129945
130372
  }
129946
130373
  }
129947
130374
  if (!resolvedMonitorId && inputs.smokeCollectionId) {
129948
- const discovered = await dependencies.postman.findMonitorByCollection(
129949
- inputs.smokeCollectionId,
129950
- monitorEnvUid,
129951
- monitorName
129952
- );
129953
- if (discovered) {
129954
- resolvedMonitorId = discovered.uid;
129955
- dependencies.core.info(`Discovered existing monitor for collection ${inputs.smokeCollectionId}: ${resolvedMonitorId}`);
130375
+ try {
130376
+ const discovered = await dependencies.postman.findMonitorByCollection(
130377
+ inputs.smokeCollectionId,
130378
+ monitorEnvUid,
130379
+ monitorName
130380
+ );
130381
+ if (discovered) {
130382
+ resolvedMonitorId = discovered.uid;
130383
+ dependencies.core.info(`Discovered existing monitor for collection ${inputs.smokeCollectionId}: ${resolvedMonitorId}`);
130384
+ }
130385
+ } catch (error2) {
130386
+ throw new Error(
130387
+ formatOrchestrationIssue({
130388
+ operation: "Monitor discovery",
130389
+ entity: `monitor "${monitorName}" workspace ${inputs.workspaceId} collection ${inputs.smokeCollectionId} environment ${monitorEnvUid}`,
130390
+ cause: error2,
130391
+ remediation: monitorRemediation,
130392
+ mask
130393
+ }),
130394
+ { cause: error2 }
130395
+ );
129956
130396
  }
129957
130397
  }
129958
130398
  if (!resolvedMonitorId) {
129959
- resolvedMonitorId = await dependencies.postman.createMonitor(
129960
- inputs.workspaceId,
129961
- monitorName,
129962
- inputs.smokeCollectionId,
129963
- monitorEnvUid,
129964
- effectiveCron || void 0
129965
- );
129966
- dependencies.core.info(`Created new monitor: ${resolvedMonitorId}${effectiveCron ? "" : " (disabled \u2014 no cron configured; will trigger a one-time run)"}`);
130399
+ try {
130400
+ resolvedMonitorId = await dependencies.postman.createMonitor(
130401
+ inputs.workspaceId,
130402
+ monitorName,
130403
+ inputs.smokeCollectionId,
130404
+ monitorEnvUid,
130405
+ effectiveCron || void 0
130406
+ );
130407
+ dependencies.core.info(`Created new monitor: ${resolvedMonitorId}${effectiveCron ? "" : " (disabled \u2014 no cron configured; will trigger a one-time run)"}`);
130408
+ } catch (error2) {
130409
+ throw new Error(
130410
+ formatOrchestrationIssue({
130411
+ operation: "Monitor create",
130412
+ entity: `monitor "${monitorName}" workspace ${inputs.workspaceId} collection ${inputs.smokeCollectionId} environment ${monitorEnvUid}`,
130413
+ cause: error2,
130414
+ remediation: monitorRemediation,
130415
+ mask
130416
+ }),
130417
+ { cause: error2 }
130418
+ );
130419
+ }
129967
130420
  }
129968
130421
  outputs["monitor-id"] = resolvedMonitorId;
129969
130422
  if (!effectiveCron && resolvedMonitorId) {
@@ -129972,7 +130425,13 @@ async function runRepoSyncInner(inputs, dependencies) {
129972
130425
  dependencies.core.info(`Triggered one-time run for monitor: ${resolvedMonitorId}`);
129973
130426
  } catch (error2) {
129974
130427
  dependencies.core.warning(
129975
- `Failed to trigger one-time run for monitor ${resolvedMonitorId}: ${error2 instanceof Error ? error2.message : String(error2)}`
130428
+ formatOrchestrationIssue({
130429
+ operation: "Monitor one-time run",
130430
+ entity: `monitor ${resolvedMonitorId} name "${monitorName}" workspace ${inputs.workspaceId} collection ${inputs.smokeCollectionId} environment ${monitorEnvUid}`,
130431
+ cause: error2,
130432
+ remediation: monitorRemediation,
130433
+ mask
130434
+ })
129976
130435
  );
129977
130436
  }
129978
130437
  }
@@ -129980,18 +130439,33 @@ async function runRepoSyncInner(inputs, dependencies) {
129980
130439
  }
129981
130440
  if (inputs.workspaceLinkEnabled && inputs.workspaceId && inputs.repoUrl && dependencies.internalIntegration) {
129982
130441
  try {
129983
- await dependencies.internalIntegration.connectWorkspaceToRepository(
129984
- inputs.workspaceId,
129985
- inputs.repoUrl
129986
- );
129987
- outputs["workspace-link-status"] = "success";
129988
- dependencies.core.info(
129989
- `workspace-link-status=success workspace-id=${inputs.workspaceId} repo=${inputs.repoUrl}`
129990
- );
130442
+ if (skipRepositoryLinkPost) {
130443
+ outputs["workspace-link-status"] = "success";
130444
+ dependencies.core.info(
130445
+ `workspace-link-status=success workspace-id=${inputs.workspaceId} repo=${inputs.repoUrl} (preflight already-target; link POST skipped)`
130446
+ );
130447
+ } else {
130448
+ await dependencies.internalIntegration.connectWorkspaceToRepository(
130449
+ inputs.workspaceId,
130450
+ inputs.repoUrl,
130451
+ repositoryLinkPreflightWasFree ? { preflightWasFree: true } : void 0
130452
+ );
130453
+ outputs["workspace-link-status"] = "success";
130454
+ dependencies.core.info(
130455
+ `workspace-link-status=success workspace-id=${inputs.workspaceId} repo=${inputs.repoUrl}`
130456
+ );
130457
+ }
129991
130458
  } catch (error2) {
129992
130459
  outputs["workspace-link-status"] = "failed";
129993
- const message = `Workspace link failed: ${error2 instanceof Error ? error2.message : String(error2)}`;
130460
+ const rawMessage = error2 instanceof Error ? error2.message : String(error2);
130461
+ const isUnresolvedContract = rawMessage.startsWith(
130462
+ "REPOSITORY_LINK_CONFLICT_UNRESOLVED:"
130463
+ );
130464
+ const message = isUnresolvedContract ? rawMessage : `Workspace link failed: ${rawMessage}`;
129994
130465
  if (branchDecision.tier === "canonical") {
130466
+ if (isUnresolvedContract && error2 instanceof Error) {
130467
+ throw error2;
130468
+ }
129995
130469
  throw new Error(message, { cause: error2 });
129996
130470
  }
129997
130471
  dependencies.core.warning(message);
@@ -130022,18 +130496,57 @@ async function runRepoSyncInner(inputs, dependencies) {
130022
130496
  dependencies.core.info(`Tagged spec version at finalize: ${tag.name || tagName}`);
130023
130497
  } catch (error2) {
130024
130498
  const status = error2.status;
130499
+ const specEntity = `spec ${inputs.specId} tag "${tagName}" workspace ${inputs.workspaceId}`;
130500
+ const specRemediation = "inspect existing tags/access and rerun";
130025
130501
  if (status === 409) {
130026
- const tags = await dependencies.postman.listSpecVersionTags?.(inputs.specId).catch(() => []) ?? [];
130502
+ let tags = [];
130503
+ let listError;
130504
+ if (dependencies.postman.listSpecVersionTags) {
130505
+ try {
130506
+ tags = await dependencies.postman.listSpecVersionTags(inputs.specId);
130507
+ } catch (lookupError) {
130508
+ listError = lookupError;
130509
+ }
130510
+ }
130027
130511
  const latest = tags[0];
130028
- if (latest && (latest.name === tagName || /\([0-9a-f]{7}\)$/.test(latest.name) || /^[0-9a-f]{7}$/.test(latest.name))) {
130512
+ if (!listError && latest && (latest.name === tagName || /\([0-9a-f]{7}\)$/.test(latest.name) || /^[0-9a-f]{7}$/.test(latest.name))) {
130029
130513
  outputs["spec-version-tag"] = latest.name;
130030
130514
  outputs["spec-version-url"] = `https://web.postman.co/workspace/${encodeURIComponent(inputs.workspaceId)}/specification/${encodeURIComponent(inputs.specId)}?tagId=${encodeURIComponent(latest.id)}&versionLabel=${encodeURIComponent(latest.name)}`;
130031
130515
  dependencies.core.info(`Latest changelog group already tagged as "${latest.name}"; adopting.`);
130516
+ } else if (listError) {
130517
+ dependencies.core.warning(
130518
+ formatOrchestrationIssue({
130519
+ operation: "Spec Hub tagging conflict",
130520
+ entity: specEntity,
130521
+ cause: `create=${causeText(error2)}; listSpecVersionTags=${causeText(listError)}`,
130522
+ remediation: specRemediation,
130523
+ mask,
130524
+ detail: "; could not list existing tags to confirm adoption"
130525
+ })
130526
+ );
130032
130527
  } else {
130033
- dependencies.core.warning("Latest changelog group already carries a hand-applied tag; leaving it in place.");
130528
+ dependencies.core.warning(
130529
+ formatOrchestrationIssue({
130530
+ operation: "Spec Hub tagging conflict",
130531
+ entity: specEntity,
130532
+ cause: error2,
130533
+ remediation: specRemediation,
130534
+ mask,
130535
+ detail: "; latest changelog group already carries a hand-applied or nonmatching tag; leaving it in place"
130536
+ })
130537
+ );
130034
130538
  }
130035
130539
  } else {
130036
- dependencies.core.warning(`Spec version tagging failed (non-fatal): ${error2 instanceof Error ? error2.message : String(error2)}`);
130540
+ dependencies.core.warning(
130541
+ formatOrchestrationIssue({
130542
+ operation: "Spec version tagging",
130543
+ entity: specEntity,
130544
+ cause: error2,
130545
+ remediation: specRemediation,
130546
+ mask,
130547
+ detail: " (non-fatal)"
130548
+ })
130549
+ );
130037
130550
  }
130038
130551
  }
130039
130552
  }
@@ -130064,7 +130577,16 @@ async function resolvePostmanApiKeyAndTeamId(inputs, actionCore, actionExec, mas
130064
130577
  }
130065
130578
  } catch (error2) {
130066
130579
  if (typeof error2 === "object" && error2 !== null && "status" in error2 && (error2.status === 401 || error2.status === 403)) {
130067
- actionCore.warning("Provided postman-api-key is invalid or expired.");
130580
+ const status = error2.status;
130581
+ actionCore.warning(
130582
+ formatOrchestrationIssue({
130583
+ operation: "PMAK GET /me validation",
130584
+ entity: `status ${status}`,
130585
+ cause: error2,
130586
+ remediation: "replace postman-api-key or provide a valid postman-access-token then rerun",
130587
+ mask: masker
130588
+ })
130589
+ );
130068
130590
  } else {
130069
130591
  throw error2;
130070
130592
  }
@@ -130086,6 +130608,7 @@ async function resolvePostmanApiKeyAndTeamId(inputs, actionCore, actionExec, mas
130086
130608
  const keyName = `repo-sync-action-${Date.now()}`;
130087
130609
  apiKey = await internalIntegration.createApiKey(keyName);
130088
130610
  actionCore.setSecret(apiKey);
130611
+ const persistSecretRemediation = "grant Actions secrets write permission or set POSTMAN_API_KEY manually then rerun";
130089
130612
  if (inputs.provider === "azure-devops") {
130090
130613
  if (options.persistGeneratedApiKeySecret ?? true) {
130091
130614
  actionCore.warning(
@@ -130112,16 +130635,48 @@ async function resolvePostmanApiKeyAndTeamId(inputs, actionCore, actionExec, mas
130112
130635
  ignoreReturnCode: true
130113
130636
  });
130114
130637
  if (ghCommand.exitCode !== 0) {
130115
- actionCore.warning(`Failed to save POSTMAN_API_KEY secret: ${ghCommand.stderr}`);
130638
+ actionCore.warning(
130639
+ formatOrchestrationIssue({
130640
+ operation: "gh secret set POSTMAN_API_KEY",
130641
+ entity: `repository ${repo}`,
130642
+ cause: ghCommand.stderr || `exit code ${ghCommand.exitCode}`,
130643
+ remediation: persistSecretRemediation,
130644
+ mask: masker
130645
+ })
130646
+ );
130116
130647
  }
130117
130648
  } catch (error2) {
130118
130649
  actionCore.warning(
130119
- `Error saving POSTMAN_API_KEY secret: ${error2 instanceof Error ? error2.message : String(error2)}`
130650
+ formatOrchestrationIssue({
130651
+ operation: "gh secret set POSTMAN_API_KEY",
130652
+ entity: `repository ${repo}`,
130653
+ cause: error2,
130654
+ remediation: persistSecretRemediation,
130655
+ mask: masker
130656
+ })
130120
130657
  );
130121
130658
  }
130659
+ } else {
130660
+ actionCore.warning(
130661
+ formatOrchestrationIssue({
130662
+ operation: "gh secret set POSTMAN_API_KEY",
130663
+ entity: "repository (missing)",
130664
+ cause: "repository context is empty",
130665
+ remediation: "set repository context or persist POSTMAN_API_KEY manually then rerun",
130666
+ mask: masker
130667
+ })
130668
+ );
130122
130669
  }
130123
130670
  } else if (options.persistGeneratedApiKeySecret ?? true) {
130124
- actionCore.warning("No GitHub token provided; cannot save generated POSTMAN_API_KEY secret.");
130671
+ actionCore.warning(
130672
+ formatOrchestrationIssue({
130673
+ operation: "gh secret set POSTMAN_API_KEY",
130674
+ entity: inputs.repository ? `repository ${inputs.repository}` : "repository (unknown)",
130675
+ cause: "no GitHub token provided",
130676
+ remediation: "provide github-token/gh-fallback-token or set POSTMAN_API_KEY manually then rerun",
130677
+ mask: masker
130678
+ })
130679
+ );
130125
130680
  } else {
130126
130681
  actionCore.info("Skipping generated POSTMAN_API_KEY GitHub secret persistence for this run.");
130127
130682
  }
@@ -130160,7 +130715,13 @@ async function resolvePostmanApiKeyAndTeamId(inputs, actionCore, actionExec, mas
130160
130715
  const isNonOrgSquads = error2 instanceof HttpError && error2.status === 400 && /squad feature is not available/i.test(error2.responseBody);
130161
130716
  if (!isNonOrgSquads) {
130162
130717
  actionCore.warning(
130163
- `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)}`
130718
+ formatOrchestrationIssue({
130719
+ operation: "Org-mode auto-detection via ums squads",
130720
+ entity: `team ${teamId}`,
130721
+ cause: error2,
130722
+ remediation: "set org-mode and team-id explicitly then rerun",
130723
+ mask: masker
130724
+ })
130164
130725
  );
130165
130726
  }
130166
130727
  }
@@ -130280,7 +130841,8 @@ function createRepoSyncDependencies(inputs, resolved, factories, options = {}) {
130280
130841
  core: factories.core,
130281
130842
  postman,
130282
130843
  internalIntegration,
130283
- repoMutation
130844
+ repoMutation,
130845
+ secretMasker: masker
130284
130846
  };
130285
130847
  }
130286
130848
  function decideBranchTier(inputs, env = process.env) {