@postman-cse/onboarding-bootstrap 0.14.1 → 0.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.cjs CHANGED
@@ -45095,6 +45095,12 @@ var customerPreviewActionContract = {
45095
45095
  description: "Postman access token used for governance and workspace mutations.",
45096
45096
  required: false
45097
45097
  },
45098
+ "credential-preflight": {
45099
+ description: "Credential identity preflight policy. warn (default) logs a note and continues when postman-api-key and postman-access-token resolve to different parent orgs; enforce fails the run on that condition before any workspace is created; off skips the identity probes entirely (the reactive error guidance still applies). Promotion of the default to enforce is planned once the live e2e legs prove both directions.",
45100
+ required: false,
45101
+ default: "warn",
45102
+ allowedValues: ["enforce", "warn", "off"]
45103
+ },
45098
45104
  "integration-backend": {
45099
45105
  description: "Integration backend for downstream workspace connectivity.",
45100
45106
  required: false,
@@ -46118,13 +46124,15 @@ var POSTMAN_ENDPOINT_PROFILES = {
46118
46124
  apiBaseUrl: "https://api.getpostman.com",
46119
46125
  bifrostBaseUrl: "https://bifrost-premium-https-v4.gw.postman.com",
46120
46126
  cliInstallUrl: "https://dl-cli.pstmn.io/install/unix.sh",
46121
- gatewayBaseUrl: "https://gateway.postman.com"
46127
+ gatewayBaseUrl: "https://gateway.postman.com",
46128
+ iapubBaseUrl: "https://iapub.postman.co"
46122
46129
  },
46123
46130
  beta: {
46124
46131
  apiBaseUrl: "https://api.getpostman-beta.com",
46125
46132
  bifrostBaseUrl: "https://bifrost-https-v4.gw.postman-beta.com",
46126
46133
  cliInstallUrl: "https://dl-cli.pstmn-beta.io/install/unix.sh",
46127
- gatewayBaseUrl: "https://gateway.postman-beta.com"
46134
+ gatewayBaseUrl: "https://gateway.postman-beta.com",
46135
+ iapubBaseUrl: "https://iapub.postman.co"
46128
46136
  }
46129
46137
  };
46130
46138
  function parsePostmanStack(value) {
@@ -46138,6 +46146,68 @@ function resolvePostmanEndpointProfile(stack) {
46138
46146
  return POSTMAN_ENDPOINT_PROFILES[stack];
46139
46147
  }
46140
46148
 
46149
+ // src/lib/postman/credential-identity.ts
46150
+ var memoizedSessionIdentity;
46151
+ function getMemoizedSessionIdentity() {
46152
+ return memoizedSessionIdentity;
46153
+ }
46154
+
46155
+ // src/lib/postman/error-advice.ts
46156
+ var WORKSPACE_PERSONAL_ONLY_ADVICE = "Workspace creation failed: This may be an Org-mode account that requires a workspace-team-id input. The Postman API does not allow creating team workspaces at the organization level. Use the workspace-team-id input to specify which sub-team should own this workspace.";
46157
+ function workspaceTeamIdUnauthorizedAdvice(targetTeamId) {
46158
+ return `The workspace-team-id input (${targetTeamId}) was rejected as unauthorized by the Postman API. In org-mode accounts it must be the numeric id of a sub-team this API key can access; GET https://api.getpostman.com/teams lists the available sub-teams. Fix the workspace-team-id value and re-run.`;
46159
+ }
46160
+ function adviseFromWorkspaceCreateError(err, targetTeamId) {
46161
+ if (err.message.includes("Only personal workspaces")) {
46162
+ return new Error(WORKSPACE_PERSONAL_ONLY_ADVICE, { cause: err });
46163
+ }
46164
+ if (targetTeamId != null && err.message.includes("You are not authorized to perform this action")) {
46165
+ return new Error(workspaceTeamIdUnauthorizedAdvice(targetTeamId), { cause: err });
46166
+ }
46167
+ return void 0;
46168
+ }
46169
+ function expiryAdvice(code) {
46170
+ return `postman: Bifrost rejected the access token (${code}). Service-account access tokens expire after about 1 to 1.5 hours; this run likely outlived its token. Re-mint a fresh token (postman-resolve-service-token-action, or POST https://api.getpostman.com/service-account-tokens) and re-run. If it was just minted, confirm postman-access-token is the token for the same parent org as postman-api-key.`;
46171
+ }
46172
+ function forbiddenAdvice(ctx) {
46173
+ const sessionDetail = ctx.sessionTeamId ? ` while the access token is valid (it resolved to team ${ctx.sessionTeamId}${ctx.sessionRoles && ctx.sessionRoles.length > 0 ? `, roles [${ctx.sessionRoles.join(", ")}]` : ""}${ctx.sessionConsumerType ? `, consumerType ${ctx.sessionConsumerType}` : ""} at preflight)` : "";
46174
+ const scopedTeamId = ctx.workspaceTeamId || ctx.explicitTeamId;
46175
+ const teamClause = scopedTeamId ? `, or workspace-team-id ${scopedTeamId} names a sub-team it cannot act in` : ", or the workspace-team-id / POSTMAN_TEAM_ID in use names a sub-team it cannot act in";
46176
+ return `postman: Bifrost refused ${ctx.operation || "this operation"} with 403${sessionDetail}. The token's identity lacks permission for this endpoint${teamClause}. Verify the token's role and that workspace-team-id / POSTMAN_TEAM_ID matches a sub-team from GET https://api.getpostman.com/teams.`;
46177
+ }
46178
+ function buildAdvice(status, body, ctx) {
46179
+ if (body.includes("UNAUTHENTICATED")) {
46180
+ return expiryAdvice("UNAUTHENTICATED");
46181
+ }
46182
+ if (body.includes("authenticationError")) {
46183
+ return expiryAdvice("authenticationError");
46184
+ }
46185
+ if (body.includes("Only personal workspaces")) {
46186
+ return WORKSPACE_PERSONAL_ONLY_ADVICE;
46187
+ }
46188
+ if (body.includes("projectAlreadyConnected")) {
46189
+ return `postman: ${ctx.operation || "this operation"} reports projectAlreadyConnected with no workspace id in the error body. The repository is already linked to a workspace this credential cannot see, usually one created by a different credential pair or sub-team. Delete the stale link or its workspace, then re-run with one credential pair from a single parent org.`;
46190
+ }
46191
+ if (body.includes("invalidParamError") && body.includes("already exists")) {
46192
+ return `postman: ${ctx.operation || "this operation"} hit a duplicate resource error (invalidParamError: already exists). A matching resource already exists, possibly under another credential pair or sub-team where this credential cannot see it. Identify which workspace holds the existing resource and re-run with one credential pair from a single parent org.`;
46193
+ }
46194
+ if (body.includes("Team feature is not available for your organization")) {
46195
+ return `postman: ${ctx.operation || "this operation"} failed because the team feature is not available for this organization. The credential belongs to an account whose plan lacks team features; use credentials from the intended team and confirm the plan supports this operation.`;
46196
+ }
46197
+ if (body.includes("You are not authorized to perform this action") || status === 403 && ctx.hasAccessToken) {
46198
+ return forbiddenAdvice(ctx);
46199
+ }
46200
+ return void 0;
46201
+ }
46202
+ function adviseFromHttpError(err, ctx) {
46203
+ const body = err.responseBody || err.message || "";
46204
+ const advice = buildAdvice(err.status, body, ctx);
46205
+ if (!advice) {
46206
+ return void 0;
46207
+ }
46208
+ return new Error(ctx.mask(advice), { cause: err });
46209
+ }
46210
+
46141
46211
  // src/lib/retry.ts
46142
46212
  function sleep(delayMs) {
46143
46213
  return new Promise((resolve3) => {
@@ -46339,11 +46409,11 @@ var PostmanAssetsClient = class {
46339
46409
  body: JSON.stringify(payload)
46340
46410
  });
46341
46411
  } catch (err) {
46342
- if (err instanceof Error && err.message.includes("Only personal workspaces")) {
46343
- throw new Error(
46344
- "Workspace creation failed: This may be an Org-mode account that requires a workspace-team-id input. The Postman API does not allow creating team workspaces at the organization level. Use the workspace-team-id input to specify which sub-team should own this workspace.",
46345
- { cause: err }
46346
- );
46412
+ if (err instanceof Error) {
46413
+ const advised = adviseFromWorkspaceCreateError(err, targetTeamId);
46414
+ if (advised) {
46415
+ throw advised;
46416
+ }
46347
46417
  }
46348
46418
  throw err;
46349
46419
  }
@@ -46872,6 +46942,18 @@ var BifrostInternalIntegrationAdapter = class _BifrostInternalIntegrationAdapter
46872
46942
  this.teamId = String(teamId || "").trim();
46873
46943
  this.orgMode = orgMode;
46874
46944
  }
46945
+ adviceContext(operation) {
46946
+ const session = getMemoizedSessionIdentity();
46947
+ return {
46948
+ operation,
46949
+ hasAccessToken: Boolean(this.accessToken),
46950
+ sessionTeamId: session?.teamId,
46951
+ sessionRoles: session?.roles,
46952
+ sessionConsumerType: session?.consumerType,
46953
+ explicitTeamId: this.teamId || void 0,
46954
+ mask: this.secretMasker
46955
+ };
46956
+ }
46875
46957
  async proxyRequest(service, method, requestPath2, body, options = {}) {
46876
46958
  const url = `${this.bifrostBaseUrl}/ws/proxy`;
46877
46959
  const headers = {
@@ -46938,7 +47020,7 @@ var BifrostInternalIntegrationAdapter = class _BifrostInternalIntegrationAdapter
46938
47020
  { appVersion, query: { tag: "governance" } }
46939
47021
  );
46940
47022
  if (!listResponse.ok) {
46941
- throw await HttpError.fromResponse(listResponse, {
47023
+ const httpErr = await HttpError.fromResponse(listResponse, {
46942
47024
  method: "GET",
46943
47025
  requestHeaders: {
46944
47026
  "Content-Type": "application/json",
@@ -46949,6 +47031,8 @@ var BifrostInternalIntegrationAdapter = class _BifrostInternalIntegrationAdapter
46949
47031
  secretValues: [this.accessToken],
46950
47032
  url: `${this.bifrostBaseUrl}/ws/proxy`
46951
47033
  });
47034
+ const advised = adviseFromHttpError(httpErr, this.adviceContext("governance assignment"));
47035
+ throw advised ?? httpErr;
46952
47036
  }
46953
47037
  const groups = await listResponse.json();
46954
47038
  const group = (groups.workspaceGroups ?? groups.data)?.find(
@@ -46974,7 +47058,7 @@ var BifrostInternalIntegrationAdapter = class _BifrostInternalIntegrationAdapter
46974
47058
  { appVersion }
46975
47059
  );
46976
47060
  if (!patchResponse.ok) {
46977
- throw await HttpError.fromResponse(patchResponse, {
47061
+ const httpErr = await HttpError.fromResponse(patchResponse, {
46978
47062
  method: "PATCH",
46979
47063
  requestHeaders: {
46980
47064
  "Content-Type": "application/json",
@@ -46985,6 +47069,8 @@ var BifrostInternalIntegrationAdapter = class _BifrostInternalIntegrationAdapter
46985
47069
  secretValues: [this.accessToken],
46986
47070
  url: `${this.bifrostBaseUrl}/ws/proxy`
46987
47071
  });
47072
+ const advised = adviseFromHttpError(httpErr, this.adviceContext("governance assignment"));
47073
+ throw advised ?? httpErr;
46988
47074
  }
46989
47075
  }
46990
47076
  async connectWorkspaceToRepository(workspaceId, repoUrl) {
@@ -47018,7 +47104,7 @@ var BifrostInternalIntegrationAdapter = class _BifrostInternalIntegrationAdapter
47018
47104
  );
47019
47105
  }
47020
47106
  }
47021
- throw await HttpError.fromResponse(response, {
47107
+ const httpErr = await HttpError.fromResponse(response, {
47022
47108
  method: "POST",
47023
47109
  requestHeaders: {
47024
47110
  "Content-Type": "application/json",
@@ -47028,6 +47114,8 @@ var BifrostInternalIntegrationAdapter = class _BifrostInternalIntegrationAdapter
47028
47114
  secretValues: [this.accessToken],
47029
47115
  url: `${this.bifrostBaseUrl}/ws/proxy`
47030
47116
  });
47117
+ const advised = adviseFromHttpError(httpErr, this.adviceContext("workspace repository linking"));
47118
+ throw advised ?? httpErr;
47031
47119
  }
47032
47120
  async linkCollectionsToSpecification(specificationId, collections) {
47033
47121
  if (collections.length === 0) {
@@ -47045,7 +47133,7 @@ var BifrostInternalIntegrationAdapter = class _BifrostInternalIntegrationAdapter
47045
47133
  if (response.ok) {
47046
47134
  return;
47047
47135
  }
47048
- throw await HttpError.fromResponse(response, {
47136
+ const httpErr = await HttpError.fromResponse(response, {
47049
47137
  method: "POST",
47050
47138
  requestHeaders: {
47051
47139
  "Content-Type": "application/json",
@@ -47055,6 +47143,11 @@ var BifrostInternalIntegrationAdapter = class _BifrostInternalIntegrationAdapter
47055
47143
  secretValues: [this.accessToken],
47056
47144
  url: `${this.bifrostBaseUrl}/ws/proxy`
47057
47145
  });
47146
+ const advised = adviseFromHttpError(
47147
+ httpErr,
47148
+ this.adviceContext("collection-to-specification linking")
47149
+ );
47150
+ throw advised ?? httpErr;
47058
47151
  }
47059
47152
  async syncCollection(specificationId, collectionId) {
47060
47153
  const response = await this.proxyRequest(
@@ -47065,7 +47158,7 @@ var BifrostInternalIntegrationAdapter = class _BifrostInternalIntegrationAdapter
47065
47158
  if (response.ok) {
47066
47159
  return;
47067
47160
  }
47068
- throw await HttpError.fromResponse(response, {
47161
+ const httpErr = await HttpError.fromResponse(response, {
47069
47162
  method: "POST",
47070
47163
  requestHeaders: {
47071
47164
  "Content-Type": "application/json",
@@ -47075,6 +47168,8 @@ var BifrostInternalIntegrationAdapter = class _BifrostInternalIntegrationAdapter
47075
47168
  secretValues: [this.accessToken],
47076
47169
  url: `${this.bifrostBaseUrl}/ws/proxy`
47077
47170
  });
47171
+ const advised = adviseFromHttpError(httpErr, this.adviceContext("collection sync"));
47172
+ throw advised ?? httpErr;
47078
47173
  }
47079
47174
  async getWorkspaceGitRepoUrl(workspaceId) {
47080
47175
  const response = await this.proxyRequest(
@@ -61161,6 +61256,11 @@ function resolveInputs(env = process.env) {
61161
61256
  governanceMappingJson: parseGovernanceMappingJson(getInput("governance-mapping-json", env)),
61162
61257
  postmanApiKey: getInput("postman-api-key", env) ?? "",
61163
61258
  postmanAccessToken: getInput("postman-access-token", env),
61259
+ credentialPreflight: parseEnumInput(
61260
+ "credential-preflight",
61261
+ getInput("credential-preflight", env),
61262
+ customerPreviewActionContract.inputs["credential-preflight"].default ?? "warn"
61263
+ ),
61164
61264
  integrationBackend,
61165
61265
  folderStrategy: parseEnumInput("folder-strategy", getInput("folder-strategy", env), "Paths"),
61166
61266
  nestedFolderHierarchy: parseBooleanInput("nested-folder-hierarchy", getInput("nested-folder-hierarchy", env), false),
@@ -61170,6 +61270,7 @@ function resolveInputs(env = process.env) {
61170
61270
  postmanBifrostBase: endpointProfile.bifrostBaseUrl,
61171
61271
  postmanGatewayBase: endpointProfile.gatewayBaseUrl,
61172
61272
  postmanCliInstallUrl: endpointProfile.cliInstallUrl,
61273
+ postmanIapubBase: endpointProfile.iapubBaseUrl,
61173
61274
  githubRefName: env.GITHUB_REF_NAME,
61174
61275
  githubHeadRef: env.GITHUB_HEAD_REF,
61175
61276
  githubRef: env.GITHUB_REF,
@@ -61706,8 +61807,20 @@ For CLI usage, pass --workspace-team-id <id> or export POSTMAN_WORKSPACE_TEAM_ID
61706
61807
  governanceGroupName
61707
61808
  );
61708
61809
  } catch (error) {
61810
+ const session = getMemoizedSessionIdentity();
61811
+ const advised = error instanceof HttpError ? adviseFromHttpError(error, {
61812
+ operation: "governance assignment",
61813
+ hasAccessToken: Boolean(inputs.postmanAccessToken),
61814
+ sessionTeamId: session?.teamId,
61815
+ sessionRoles: session?.roles,
61816
+ sessionConsumerType: session?.consumerType,
61817
+ workspaceTeamId: inputs.workspaceTeamId,
61818
+ explicitTeamId: inputs.teamId || void 0,
61819
+ mask: createSecretMasker([inputs.postmanApiKey, inputs.postmanAccessToken])
61820
+ }) : void 0;
61821
+ const reported = advised ?? error;
61709
61822
  dependencies.core.warning(
61710
- `Failed to assign governance group: ${error instanceof Error ? error.message : String(error)}`
61823
+ `Failed to assign governance group: ${reported instanceof Error ? reported.message : String(reported)}`
61711
61824
  );
61712
61825
  }
61713
61826
  }
@@ -62369,6 +62482,7 @@ var cliInputNames = [
62369
62482
  "spec-path",
62370
62483
  "postman-api-key",
62371
62484
  "postman-access-token",
62485
+ "credential-preflight",
62372
62486
  "workspace-id",
62373
62487
  "spec-id",
62374
62488
  "baseline-collection-id",