@postman-cse/onboarding-bootstrap 0.14.2 → 0.15.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.cjs CHANGED
@@ -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,313 @@ function resolvePostmanEndpointProfile(stack) {
46138
46146
  return POSTMAN_ENDPOINT_PROFILES[stack];
46139
46147
  }
46140
46148
 
46149
+ // src/lib/postman/credential-identity.ts
46150
+ var sessionPath = "/api/sessions/current";
46151
+ var pmakMemo = /* @__PURE__ */ new Map();
46152
+ var sessionMemo = /* @__PURE__ */ new Map();
46153
+ var memoizedSessionIdentity;
46154
+ function getMemoizedSessionIdentity() {
46155
+ return memoizedSessionIdentity;
46156
+ }
46157
+ function asRecord(value) {
46158
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
46159
+ return void 0;
46160
+ }
46161
+ return value;
46162
+ }
46163
+ function coerceId(raw) {
46164
+ return raw ? String(raw) : void 0;
46165
+ }
46166
+ function coerceText(raw) {
46167
+ if (typeof raw !== "string") {
46168
+ return void 0;
46169
+ }
46170
+ const trimmed = raw.trim();
46171
+ return trimmed ? trimmed : void 0;
46172
+ }
46173
+ function normalizeBaseUrl(raw) {
46174
+ return String(raw || "").replace(/\/+$/, "");
46175
+ }
46176
+ async function resolvePmakIdentity(opts) {
46177
+ const apiKey = String(opts.apiKey || "").trim();
46178
+ if (!apiKey) {
46179
+ return void 0;
46180
+ }
46181
+ const baseUrl = normalizeBaseUrl(opts.apiBaseUrl);
46182
+ const memoKey = `${baseUrl}::${apiKey}`;
46183
+ let pending = pmakMemo.get(memoKey);
46184
+ if (!pending) {
46185
+ pending = probePmakIdentity(baseUrl, apiKey, opts.fetchImpl ?? fetch);
46186
+ pmakMemo.set(memoKey, pending);
46187
+ }
46188
+ return pending;
46189
+ }
46190
+ async function probePmakIdentity(baseUrl, apiKey, fetchImpl) {
46191
+ try {
46192
+ const response = await fetchImpl(`${baseUrl}/me`, {
46193
+ method: "GET",
46194
+ headers: { "X-Api-Key": apiKey }
46195
+ });
46196
+ if (!response.ok) {
46197
+ return void 0;
46198
+ }
46199
+ const payload = asRecord(await response.json());
46200
+ const user = asRecord(payload?.user);
46201
+ if (!user) {
46202
+ return void 0;
46203
+ }
46204
+ return {
46205
+ source: "pmak/me",
46206
+ userId: coerceId(user.id),
46207
+ fullName: coerceText(user.fullName) ?? coerceText(user.username),
46208
+ teamId: coerceId(user.teamId),
46209
+ teamName: coerceText(user.teamName),
46210
+ teamDomain: coerceText(user.teamDomain)
46211
+ };
46212
+ } catch {
46213
+ return void 0;
46214
+ }
46215
+ }
46216
+ async function resolveSessionIdentity(opts) {
46217
+ const accessToken = String(opts.accessToken || "").trim();
46218
+ if (!accessToken) {
46219
+ return void 0;
46220
+ }
46221
+ const baseUrl = normalizeBaseUrl(opts.iapubBaseUrl);
46222
+ const memoKey = `${baseUrl}::${accessToken}`;
46223
+ let pending = sessionMemo.get(memoKey);
46224
+ if (!pending) {
46225
+ pending = probeSessionIdentity(baseUrl, accessToken, opts.fetchImpl ?? fetch);
46226
+ sessionMemo.set(memoKey, pending);
46227
+ }
46228
+ return pending;
46229
+ }
46230
+ async function probeSessionIdentity(baseUrl, accessToken, fetchImpl) {
46231
+ try {
46232
+ const response = await fetchImpl(`${baseUrl}${sessionPath}`, {
46233
+ method: "GET",
46234
+ headers: { "x-access-token": accessToken }
46235
+ });
46236
+ if (!response.ok) {
46237
+ return void 0;
46238
+ }
46239
+ const payload = asRecord(await response.json());
46240
+ if (!payload) {
46241
+ return void 0;
46242
+ }
46243
+ const root = asRecord(payload.session) ?? payload;
46244
+ const identity = asRecord(root.identity);
46245
+ const data = asRecord(root.data);
46246
+ const user = asRecord(data?.user);
46247
+ const roleEntries = Array.isArray(user?.roles) ? user.roles.map((entry) => coerceText(entry) ?? coerceId(entry)).filter((entry) => Boolean(entry)) : [];
46248
+ const singleRole = coerceText(user?.role);
46249
+ const roles = roleEntries.length > 0 ? roleEntries : singleRole ? [singleRole] : void 0;
46250
+ const resolved = {
46251
+ source: "iapub/sessions",
46252
+ userId: coerceId(identity?.user) ?? coerceId(user?.id),
46253
+ fullName: coerceText(user?.fullName) ?? coerceText(user?.name) ?? coerceText(user?.username),
46254
+ teamId: coerceId(identity?.team),
46255
+ teamName: coerceText(user?.teamName),
46256
+ teamDomain: coerceText(identity?.domain),
46257
+ ...roles ? { roles } : {},
46258
+ consumerType: coerceText(root.consumerType) ?? coerceText(data?.consumerType) ?? coerceText(user?.consumerType)
46259
+ };
46260
+ memoizedSessionIdentity = resolved;
46261
+ return resolved;
46262
+ } catch {
46263
+ return void 0;
46264
+ }
46265
+ }
46266
+ function describeTeam(id) {
46267
+ const label = id?.teamName ?? id?.teamDomain;
46268
+ return `team ${id?.teamId ?? "unresolved"}${label ? ` (${label})` : ""}`;
46269
+ }
46270
+ function formatIdentityLine(id, mask) {
46271
+ const teamPart = id.teamId ? describeTeam(id) : "team unresolved";
46272
+ const domainPart = id.teamDomain ? `, domain ${id.teamDomain}` : "";
46273
+ if (id.source === "pmak/me") {
46274
+ const userPart = id.userId ? `user ${id.userId}${id.fullName ? ` (${id.fullName})` : ""}, ` : "";
46275
+ return mask(`postman: PMAK identity - ${userPart}${teamPart}${domainPart}`);
46276
+ }
46277
+ return mask(
46278
+ `postman: access-token session identity - ${teamPart}${domainPart} [source: iapub/sessions]`
46279
+ );
46280
+ }
46281
+ function crossCheckIdentities(args) {
46282
+ if (args.mode === "off") {
46283
+ return { ok: true, level: "ok", message: "" };
46284
+ }
46285
+ const pmakTeamId = args.pmak?.teamId;
46286
+ const sessionTeamId = args.session?.teamId;
46287
+ if (pmakTeamId && sessionTeamId && pmakTeamId !== sessionTeamId) {
46288
+ const level = args.mode === "enforce" ? "fail" : "note";
46289
+ const lead = level === "fail" ? "credential preflight FAILED" : "credential preflight note";
46290
+ const fix = level === "fail" ? "Use one credential pair from a single parent org: re-mint the access token from the same parent org as postman-api-key (postman-resolve-service-token-action, or POST https://api.getpostman.com/service-account-tokens with that team's PMAK), or set postman-api-key to the matching parent org." : "Use one credential pair from a single parent org. Set credential-preflight: enforce to fail the run on this condition.";
46291
+ return {
46292
+ ok: false,
46293
+ level,
46294
+ message: args.mask(
46295
+ `postman: ${lead} - PMAK belongs to ${describeTeam(args.pmak)} but the access token's session belongs to a different parent org, ${describeTeam(args.session)}. Assets would be created against one team while Bifrost linking and governance act under the other, producing duplicate-link 400s and workspaces not visible to the other credential. ` + fix
46296
+ )
46297
+ };
46298
+ }
46299
+ if (pmakTeamId && sessionTeamId) {
46300
+ const scope = args.workspaceTeamId || args.explicitTeamId ? "parent org team" : "team";
46301
+ const label = args.pmak?.teamName ?? args.pmak?.teamDomain ?? args.session?.teamName ?? args.session?.teamDomain;
46302
+ return {
46303
+ ok: true,
46304
+ level: "ok",
46305
+ message: args.mask(
46306
+ `postman: credential preflight OK - PMAK and access token both resolve to ${scope} ${pmakTeamId}${label ? ` (${label})` : ""}`
46307
+ )
46308
+ };
46309
+ }
46310
+ const missing = [
46311
+ !pmakTeamId ? "PMAK identity" : void 0,
46312
+ !sessionTeamId ? "access-token session identity" : void 0
46313
+ ].filter(Boolean).join(" and ");
46314
+ return {
46315
+ ok: false,
46316
+ level: "note",
46317
+ message: args.mask(
46318
+ `postman: credential preflight note - cross-check skipped because the ${missing} did not resolve a team id; continuing with reactive error guidance only`
46319
+ )
46320
+ };
46321
+ }
46322
+ async function runCredentialPreflight(args) {
46323
+ if (args.mode === "off") {
46324
+ return;
46325
+ }
46326
+ const mask = args.mask;
46327
+ const apiKey = String(args.postmanApiKey || "").trim();
46328
+ const accessToken = String(args.postmanAccessToken || "").trim();
46329
+ let pmak;
46330
+ if (apiKey) {
46331
+ try {
46332
+ pmak = await resolvePmakIdentity({
46333
+ apiBaseUrl: args.apiBaseUrl,
46334
+ apiKey,
46335
+ fetchImpl: args.fetchImpl
46336
+ });
46337
+ } catch (error) {
46338
+ args.log.warning(
46339
+ mask(
46340
+ `postman: credential preflight could not resolve PMAK identity: ${error instanceof Error ? error.message : String(error)}`
46341
+ )
46342
+ );
46343
+ }
46344
+ if (pmak) {
46345
+ args.log.info(formatIdentityLine(pmak, mask));
46346
+ } else {
46347
+ args.log.warning(
46348
+ mask("postman: credential preflight could not resolve PMAK identity from GET /me; continuing")
46349
+ );
46350
+ }
46351
+ }
46352
+ if (!accessToken) {
46353
+ args.log.info(mask("postman: Bifrost diagnostics limited: no access token"));
46354
+ return;
46355
+ }
46356
+ let session;
46357
+ try {
46358
+ session = await resolveSessionIdentity({
46359
+ iapubBaseUrl: args.iapubBaseUrl,
46360
+ accessToken,
46361
+ fetchImpl: args.fetchImpl
46362
+ });
46363
+ } catch (error) {
46364
+ args.log.warning(
46365
+ mask(
46366
+ `postman: credential preflight could not resolve access-token session identity: ${error instanceof Error ? error.message : String(error)}`
46367
+ )
46368
+ );
46369
+ }
46370
+ if (session) {
46371
+ args.log.info(formatIdentityLine(session, mask));
46372
+ } else {
46373
+ args.log.warning(
46374
+ mask(
46375
+ "postman: credential preflight could not resolve the access-token session identity from iapub; continuing with reactive error guidance only"
46376
+ )
46377
+ );
46378
+ }
46379
+ const result = crossCheckIdentities({
46380
+ pmak,
46381
+ session,
46382
+ workspaceTeamId: args.workspaceTeamId,
46383
+ explicitTeamId: args.explicitTeamId,
46384
+ mode: args.mode,
46385
+ mask
46386
+ });
46387
+ if (!result.message) {
46388
+ return;
46389
+ }
46390
+ if (result.level === "fail") {
46391
+ throw new Error(result.message);
46392
+ }
46393
+ if (result.level === "note") {
46394
+ args.log.warning(result.message);
46395
+ return;
46396
+ }
46397
+ args.log.info(result.message);
46398
+ }
46399
+
46400
+ // src/lib/postman/error-advice.ts
46401
+ 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.";
46402
+ function workspaceTeamIdUnauthorizedAdvice(targetTeamId) {
46403
+ 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.`;
46404
+ }
46405
+ function adviseFromWorkspaceCreateError(err, targetTeamId) {
46406
+ if (err.message.includes("Only personal workspaces")) {
46407
+ return new Error(WORKSPACE_PERSONAL_ONLY_ADVICE, { cause: err });
46408
+ }
46409
+ if (targetTeamId != null && err.message.includes("You are not authorized to perform this action")) {
46410
+ return new Error(workspaceTeamIdUnauthorizedAdvice(targetTeamId), { cause: err });
46411
+ }
46412
+ return void 0;
46413
+ }
46414
+ function expiryAdvice(code) {
46415
+ 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.`;
46416
+ }
46417
+ function forbiddenAdvice(ctx) {
46418
+ 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)` : "";
46419
+ const scopedTeamId = ctx.workspaceTeamId || ctx.explicitTeamId;
46420
+ 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";
46421
+ 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.`;
46422
+ }
46423
+ function buildAdvice(status, body, ctx) {
46424
+ if (body.includes("UNAUTHENTICATED")) {
46425
+ return expiryAdvice("UNAUTHENTICATED");
46426
+ }
46427
+ if (body.includes("authenticationError")) {
46428
+ return expiryAdvice("authenticationError");
46429
+ }
46430
+ if (body.includes("Only personal workspaces")) {
46431
+ return WORKSPACE_PERSONAL_ONLY_ADVICE;
46432
+ }
46433
+ if (body.includes("projectAlreadyConnected")) {
46434
+ 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.`;
46435
+ }
46436
+ if (body.includes("invalidParamError") && body.includes("already exists")) {
46437
+ 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.`;
46438
+ }
46439
+ if (body.includes("Team feature is not available for your organization")) {
46440
+ 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.`;
46441
+ }
46442
+ if (body.includes("You are not authorized to perform this action") || status === 403 && ctx.hasAccessToken) {
46443
+ return forbiddenAdvice(ctx);
46444
+ }
46445
+ return void 0;
46446
+ }
46447
+ function adviseFromHttpError(err, ctx) {
46448
+ const body = err.responseBody || err.message || "";
46449
+ const advice = buildAdvice(err.status, body, ctx);
46450
+ if (!advice) {
46451
+ return void 0;
46452
+ }
46453
+ return new Error(ctx.mask(advice), { cause: err });
46454
+ }
46455
+
46141
46456
  // src/lib/retry.ts
46142
46457
  function sleep(delayMs) {
46143
46458
  return new Promise((resolve3) => {
@@ -46186,7 +46501,7 @@ async function retry(operation, options = {}) {
46186
46501
  }
46187
46502
 
46188
46503
  // src/lib/postman/postman-assets-client.ts
46189
- function asRecord(value) {
46504
+ function asRecord2(value) {
46190
46505
  if (!value || typeof value !== "object" || Array.isArray(value)) {
46191
46506
  return null;
46192
46507
  }
@@ -46194,8 +46509,8 @@ function asRecord(value) {
46194
46509
  }
46195
46510
  function extractWorkspacesPage(data) {
46196
46511
  const workspaces = Array.isArray(data?.workspaces) ? data.workspaces : [];
46197
- const meta = asRecord(data?.meta);
46198
- const pagination = asRecord(data?.pagination);
46512
+ const meta = asRecord2(data?.meta);
46513
+ const pagination = asRecord2(data?.pagination);
46199
46514
  const nextCursor = String(
46200
46515
  data?.nextCursor ?? data?.next_cursor ?? meta?.nextCursor ?? meta?.next_cursor ?? pagination?.nextCursor ?? pagination?.next_cursor ?? ""
46201
46516
  ).trim() || void 0;
@@ -46287,7 +46602,7 @@ var PostmanAssetsClient = class {
46287
46602
  async getTeams() {
46288
46603
  const data = await this.request("/teams");
46289
46604
  const teams = data?.data ?? [];
46290
- return Array.isArray(teams) ? teams.map((entry) => asRecord(entry)).filter((team) => Boolean(team?.id && team?.name)).map((team) => ({
46605
+ return Array.isArray(teams) ? teams.map((entry) => asRecord2(entry)).filter((team) => Boolean(team?.id && team?.name)).map((team) => ({
46291
46606
  id: Number(team.id),
46292
46607
  name: String(team.name),
46293
46608
  handle: String(team.handle || ""),
@@ -46339,34 +46654,28 @@ var PostmanAssetsClient = class {
46339
46654
  body: JSON.stringify(payload)
46340
46655
  });
46341
46656
  } 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
- );
46347
- }
46348
- if (targetTeamId != null && err instanceof Error && err.message.includes("You are not authorized to perform this action")) {
46349
- throw new Error(
46350
- `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.`,
46351
- { cause: err }
46352
- );
46657
+ if (err instanceof Error) {
46658
+ const advised = adviseFromWorkspaceCreateError(err, targetTeamId);
46659
+ if (advised) {
46660
+ throw advised;
46661
+ }
46353
46662
  }
46354
46663
  throw err;
46355
46664
  }
46356
- const createdWorkspace = asRecord(created?.workspace);
46665
+ const createdWorkspace = asRecord2(created?.workspace);
46357
46666
  const workspaceId = String(createdWorkspace?.id || "").trim();
46358
46667
  if (!workspaceId) {
46359
46668
  throw new Error("Workspace create did not return an id");
46360
46669
  }
46361
46670
  const workspace = await this.request(`/workspaces/${workspaceId}`);
46362
- let visibility = asRecord(workspace?.workspace)?.visibility;
46671
+ let visibility = asRecord2(workspace?.workspace)?.visibility;
46363
46672
  if (visibility !== "team") {
46364
46673
  await this.request(`/workspaces/${workspaceId}`, {
46365
46674
  method: "PUT",
46366
46675
  body: JSON.stringify(payload)
46367
46676
  });
46368
46677
  const reread = await this.request(`/workspaces/${workspaceId}`);
46369
- visibility = asRecord(reread?.workspace)?.visibility;
46678
+ visibility = asRecord2(reread?.workspace)?.visibility;
46370
46679
  }
46371
46680
  if (typeof visibility === "string" && visibility !== "team") {
46372
46681
  let cleanedUp = false;
@@ -46395,7 +46704,7 @@ var PostmanAssetsClient = class {
46395
46704
  async getWorkspaceVisibility(workspaceId) {
46396
46705
  try {
46397
46706
  const workspace = await this.request(`/workspaces/${workspaceId}`);
46398
- const visibility = asRecord(workspace?.workspace)?.visibility;
46707
+ const visibility = asRecord2(workspace?.workspace)?.visibility;
46399
46708
  return typeof visibility === "string" ? visibility : null;
46400
46709
  } catch {
46401
46710
  return null;
@@ -46417,7 +46726,7 @@ var PostmanAssetsClient = class {
46417
46726
  nextCursor = page.nextCursor;
46418
46727
  }
46419
46728
  } while (nextCursor);
46420
- return allWorkspaces.map((entry) => asRecord(entry)).filter((workspace) => Boolean(workspace?.id && workspace?.name)).map((workspace) => ({
46729
+ return allWorkspaces.map((entry) => asRecord2(entry)).filter((workspace) => Boolean(workspace?.id && workspace?.name)).map((workspace) => ({
46421
46730
  id: String(workspace.id),
46422
46731
  name: String(workspace.name),
46423
46732
  type: String(workspace.type ?? "team")
@@ -46465,7 +46774,7 @@ var PostmanAssetsClient = class {
46465
46774
  async inviteRequesterToWorkspace(workspaceId, email) {
46466
46775
  const users = await this.request("/users");
46467
46776
  const userList = Array.isArray(users?.data) ? users.data : [];
46468
- const user = userList.map((entry) => asRecord(entry)).find((entry) => entry?.email === email);
46777
+ const user = userList.map((entry) => asRecord2(entry)).find((entry) => entry?.email === email);
46469
46778
  if (!user?.id) {
46470
46779
  return;
46471
46780
  }
@@ -46553,12 +46862,12 @@ var PostmanAssetsClient = class {
46553
46862
  }
46554
46863
  };
46555
46864
  const extractUid = (data) => {
46556
- const root = asRecord(data);
46557
- const details = asRecord(root?.details);
46865
+ const root = asRecord2(data);
46866
+ const details = asRecord2(root?.details);
46558
46867
  const resources = Array.isArray(details?.resources) ? details.resources : [];
46559
- const firstResource = asRecord(resources[0]);
46560
- const collection = asRecord(root?.collection);
46561
- const resource = asRecord(root?.resource);
46868
+ const firstResource = asRecord2(resources[0]);
46869
+ const collection = asRecord2(root?.collection);
46870
+ const resource = asRecord2(root?.resource);
46562
46871
  return String(
46563
46872
  firstResource?.id ?? collection?.id ?? collection?.uid ?? resource?.uid ?? resource?.id ?? ""
46564
46873
  ).trim() || void 0;
@@ -46592,9 +46901,9 @@ var PostmanAssetsClient = class {
46592
46901
  if (directUid) {
46593
46902
  return directUid;
46594
46903
  }
46595
- let taskUrl = String(generationResponse?.url ?? "") || String(generationResponse?.task_url ?? "") || String(generationResponse?.taskUrl ?? "") || String(asRecord(generationResponse?.links)?.task ?? "");
46904
+ let taskUrl = String(generationResponse?.url ?? "") || String(generationResponse?.task_url ?? "") || String(generationResponse?.taskUrl ?? "") || String(asRecord2(generationResponse?.links)?.task ?? "");
46596
46905
  if (!taskUrl) {
46597
- const task = asRecord(generationResponse?.task);
46906
+ const task = asRecord2(generationResponse?.task);
46598
46907
  const taskId = generationResponse?.taskId || task?.id || generationResponse?.id;
46599
46908
  if (!taskId) {
46600
46909
  throw new Error(
@@ -46608,8 +46917,8 @@ var PostmanAssetsClient = class {
46608
46917
  setTimeout(resolve3, 2e3);
46609
46918
  });
46610
46919
  const task = await this.request(taskUrl);
46611
- const taskRecord = asRecord(task);
46612
- const taskNested = asRecord(taskRecord?.task);
46920
+ const taskRecord = asRecord2(task);
46921
+ const taskNested = asRecord2(taskRecord?.task);
46613
46922
  const status = String(taskRecord?.status || taskNested?.status || "").toLowerCase();
46614
46923
  if (status === "completed") {
46615
46924
  const taskUid = extractUid(task);
@@ -46640,7 +46949,7 @@ var PostmanAssetsClient = class {
46640
46949
  }
46641
46950
  async injectTests(collectionUid, type2) {
46642
46951
  const collectionResponse = await this.request(`/collections/${collectionUid}`);
46643
- const collection = asRecord(collectionResponse?.collection);
46952
+ const collection = asRecord2(collectionResponse?.collection);
46644
46953
  if (!collection) {
46645
46954
  throw new Error(`Failed to fetch collection ${collectionUid}`);
46646
46955
  }
@@ -46726,7 +47035,7 @@ var PostmanAssetsClient = class {
46726
47035
  });
46727
47036
  }
46728
47037
  if (Array.isArray(itemNode.item)) {
46729
- itemNode.item.map((entry) => asRecord(entry)).filter((entry) => Boolean(entry)).forEach(injectScripts);
47038
+ itemNode.item.map((entry) => asRecord2(entry)).filter((entry) => Boolean(entry)).forEach(injectScripts);
46730
47039
  }
46731
47040
  };
46732
47041
  if (Array.isArray(collection.item)) {
@@ -46754,7 +47063,7 @@ var PostmanAssetsClient = class {
46754
47063
  }
46755
47064
  })
46756
47065
  });
46757
- const environment = asRecord(response?.environment);
47066
+ const environment = asRecord2(response?.environment);
46758
47067
  const uid = String(environment?.uid || "").trim();
46759
47068
  if (!uid) {
46760
47069
  throw new Error("Environment create did not return a UID");
@@ -46787,7 +47096,7 @@ var PostmanAssetsClient = class {
46787
47096
  }
46788
47097
  })
46789
47098
  });
46790
- const monitor = asRecord(response?.monitor);
47099
+ const monitor = asRecord2(response?.monitor);
46791
47100
  const uid = String(monitor?.uid || "").trim();
46792
47101
  if (!uid) {
46793
47102
  throw new Error("Monitor create did not return a UID");
@@ -46806,8 +47115,8 @@ var PostmanAssetsClient = class {
46806
47115
  }
46807
47116
  })
46808
47117
  });
46809
- const mock = asRecord(response?.mock);
46810
- const mockConfig = asRecord(mock?.config);
47118
+ const mock = asRecord2(response?.mock);
47119
+ const mockConfig = asRecord2(mock?.config);
46811
47120
  const uid = String(mock?.uid || "").trim();
46812
47121
  if (!uid) {
46813
47122
  throw new Error("Mock create did not return a UID");
@@ -46878,6 +47187,18 @@ var BifrostInternalIntegrationAdapter = class _BifrostInternalIntegrationAdapter
46878
47187
  this.teamId = String(teamId || "").trim();
46879
47188
  this.orgMode = orgMode;
46880
47189
  }
47190
+ adviceContext(operation) {
47191
+ const session = getMemoizedSessionIdentity();
47192
+ return {
47193
+ operation,
47194
+ hasAccessToken: Boolean(this.accessToken),
47195
+ sessionTeamId: session?.teamId,
47196
+ sessionRoles: session?.roles,
47197
+ sessionConsumerType: session?.consumerType,
47198
+ explicitTeamId: this.teamId || void 0,
47199
+ mask: this.secretMasker
47200
+ };
47201
+ }
46881
47202
  async proxyRequest(service, method, requestPath2, body, options = {}) {
46882
47203
  const url = `${this.bifrostBaseUrl}/ws/proxy`;
46883
47204
  const headers = {
@@ -46944,7 +47265,7 @@ var BifrostInternalIntegrationAdapter = class _BifrostInternalIntegrationAdapter
46944
47265
  { appVersion, query: { tag: "governance" } }
46945
47266
  );
46946
47267
  if (!listResponse.ok) {
46947
- throw await HttpError.fromResponse(listResponse, {
47268
+ const httpErr = await HttpError.fromResponse(listResponse, {
46948
47269
  method: "GET",
46949
47270
  requestHeaders: {
46950
47271
  "Content-Type": "application/json",
@@ -46955,6 +47276,8 @@ var BifrostInternalIntegrationAdapter = class _BifrostInternalIntegrationAdapter
46955
47276
  secretValues: [this.accessToken],
46956
47277
  url: `${this.bifrostBaseUrl}/ws/proxy`
46957
47278
  });
47279
+ const advised = adviseFromHttpError(httpErr, this.adviceContext("governance assignment"));
47280
+ throw advised ?? httpErr;
46958
47281
  }
46959
47282
  const groups = await listResponse.json();
46960
47283
  const group = (groups.workspaceGroups ?? groups.data)?.find(
@@ -46980,7 +47303,7 @@ var BifrostInternalIntegrationAdapter = class _BifrostInternalIntegrationAdapter
46980
47303
  { appVersion }
46981
47304
  );
46982
47305
  if (!patchResponse.ok) {
46983
- throw await HttpError.fromResponse(patchResponse, {
47306
+ const httpErr = await HttpError.fromResponse(patchResponse, {
46984
47307
  method: "PATCH",
46985
47308
  requestHeaders: {
46986
47309
  "Content-Type": "application/json",
@@ -46991,6 +47314,8 @@ var BifrostInternalIntegrationAdapter = class _BifrostInternalIntegrationAdapter
46991
47314
  secretValues: [this.accessToken],
46992
47315
  url: `${this.bifrostBaseUrl}/ws/proxy`
46993
47316
  });
47317
+ const advised = adviseFromHttpError(httpErr, this.adviceContext("governance assignment"));
47318
+ throw advised ?? httpErr;
46994
47319
  }
46995
47320
  }
46996
47321
  async connectWorkspaceToRepository(workspaceId, repoUrl) {
@@ -47024,7 +47349,7 @@ var BifrostInternalIntegrationAdapter = class _BifrostInternalIntegrationAdapter
47024
47349
  );
47025
47350
  }
47026
47351
  }
47027
- throw await HttpError.fromResponse(response, {
47352
+ const httpErr = await HttpError.fromResponse(response, {
47028
47353
  method: "POST",
47029
47354
  requestHeaders: {
47030
47355
  "Content-Type": "application/json",
@@ -47034,6 +47359,8 @@ var BifrostInternalIntegrationAdapter = class _BifrostInternalIntegrationAdapter
47034
47359
  secretValues: [this.accessToken],
47035
47360
  url: `${this.bifrostBaseUrl}/ws/proxy`
47036
47361
  });
47362
+ const advised = adviseFromHttpError(httpErr, this.adviceContext("workspace repository linking"));
47363
+ throw advised ?? httpErr;
47037
47364
  }
47038
47365
  async linkCollectionsToSpecification(specificationId, collections) {
47039
47366
  if (collections.length === 0) {
@@ -47051,7 +47378,7 @@ var BifrostInternalIntegrationAdapter = class _BifrostInternalIntegrationAdapter
47051
47378
  if (response.ok) {
47052
47379
  return;
47053
47380
  }
47054
- throw await HttpError.fromResponse(response, {
47381
+ const httpErr = await HttpError.fromResponse(response, {
47055
47382
  method: "POST",
47056
47383
  requestHeaders: {
47057
47384
  "Content-Type": "application/json",
@@ -47061,6 +47388,11 @@ var BifrostInternalIntegrationAdapter = class _BifrostInternalIntegrationAdapter
47061
47388
  secretValues: [this.accessToken],
47062
47389
  url: `${this.bifrostBaseUrl}/ws/proxy`
47063
47390
  });
47391
+ const advised = adviseFromHttpError(
47392
+ httpErr,
47393
+ this.adviceContext("collection-to-specification linking")
47394
+ );
47395
+ throw advised ?? httpErr;
47064
47396
  }
47065
47397
  async syncCollection(specificationId, collectionId) {
47066
47398
  const response = await this.proxyRequest(
@@ -47071,7 +47403,7 @@ var BifrostInternalIntegrationAdapter = class _BifrostInternalIntegrationAdapter
47071
47403
  if (response.ok) {
47072
47404
  return;
47073
47405
  }
47074
- throw await HttpError.fromResponse(response, {
47406
+ const httpErr = await HttpError.fromResponse(response, {
47075
47407
  method: "POST",
47076
47408
  requestHeaders: {
47077
47409
  "Content-Type": "application/json",
@@ -47081,6 +47413,8 @@ var BifrostInternalIntegrationAdapter = class _BifrostInternalIntegrationAdapter
47081
47413
  secretValues: [this.accessToken],
47082
47414
  url: `${this.bifrostBaseUrl}/ws/proxy`
47083
47415
  });
47416
+ const advised = adviseFromHttpError(httpErr, this.adviceContext("collection sync"));
47417
+ throw advised ?? httpErr;
47084
47418
  }
47085
47419
  async getWorkspaceGitRepoUrl(workspaceId) {
47086
47420
  const response = await this.proxyRequest(
@@ -47654,7 +47988,7 @@ var STRIP_KEYS = /* @__PURE__ */ new Set([
47654
47988
  "discriminator",
47655
47989
  "format"
47656
47990
  ]);
47657
- function asRecord2(value) {
47991
+ function asRecord3(value) {
47658
47992
  if (!value || typeof value !== "object" || Array.isArray(value)) return null;
47659
47993
  return value;
47660
47994
  }
@@ -47666,7 +48000,7 @@ function decodePointerSegment(segment) {
47666
48000
  }
47667
48001
  function resolvePointer(root, ref) {
47668
48002
  if (!ref.startsWith("#/")) return void 0;
47669
- return ref.slice(2).split("/").map(decodePointerSegment).reduce((node, segment) => asRecord2(node)?.[segment], root);
48003
+ return ref.slice(2).split("/").map(decodePointerSegment).reduce((node, segment) => asRecord3(node)?.[segment], root);
47670
48004
  }
47671
48005
  function unsupported(message) {
47672
48006
  return { unsupported: message };
@@ -47676,7 +48010,7 @@ function mergeRequiredWithoutWriteOnly(required, writeOnlyProperties) {
47676
48010
  return values.length > 0 ? values : void 0;
47677
48011
  }
47678
48012
  function hasUnsupported(child) {
47679
- return asRecord2(child)?.unsupported;
48013
+ return asRecord3(child)?.unsupported;
47680
48014
  }
47681
48015
  function rootDialect(root, version, schemaRecord) {
47682
48016
  if (version === "3.0") return DRAFT_07_SCHEMA_URI;
@@ -47696,7 +48030,7 @@ function normalizeSchema(root, schema2, options) {
47696
48030
  const bad = normalized2.map(hasUnsupported).find(Boolean);
47697
48031
  return bad ? unsupported(bad) : normalized2;
47698
48032
  }
47699
- const record = asRecord2(schema2);
48033
+ const record = asRecord3(schema2);
47700
48034
  if (!record) return schema2;
47701
48035
  const ref = typeof record.$ref === "string" ? record.$ref : "";
47702
48036
  if (ref) {
@@ -47731,10 +48065,10 @@ function normalizeSchema(root, schema2, options) {
47731
48065
  const nullable = sourceSchema.nullable === true && options.version === "3.0";
47732
48066
  delete sourceSchema.nullable;
47733
48067
  const writeOnlyProperties = /* @__PURE__ */ new Set();
47734
- const rawProperties = asRecord2(sourceSchema.properties);
48068
+ const rawProperties = asRecord3(sourceSchema.properties);
47735
48069
  if (rawProperties) {
47736
48070
  for (const [propertyName, propertySchema] of Object.entries(rawProperties)) {
47737
- if (asRecord2(propertySchema)?.writeOnly === true) writeOnlyProperties.add(propertyName);
48071
+ if (asRecord3(propertySchema)?.writeOnly === true) writeOnlyProperties.add(propertyName);
47738
48072
  }
47739
48073
  }
47740
48074
  const normalized = {};
@@ -47766,7 +48100,7 @@ function normalizeSchema(root, schema2, options) {
47766
48100
  return unsupported("Tuple array items are unsupported in OpenAPI 3.0");
47767
48101
  }
47768
48102
  if (key === "properties") {
47769
- const properties = asRecord2(value);
48103
+ const properties = asRecord3(value);
47770
48104
  if (!properties) continue;
47771
48105
  const nextProperties = {};
47772
48106
  for (const [propertyName, propertySchema] of Object.entries(properties)) {
@@ -47809,7 +48143,7 @@ function normalizeSchema(root, schema2, options) {
47809
48143
  }
47810
48144
  function packSchema(root, schema2, version) {
47811
48145
  try {
47812
- const dialect = rootDialect(root, version, asRecord2(schema2) ?? void 0);
48146
+ const dialect = rootDialect(root, version, asRecord3(schema2) ?? void 0);
47813
48147
  const normalized = normalizeSchema(root, schema2, { version, rootSchema: true, dialect });
47814
48148
  const message = hasUnsupported(normalized);
47815
48149
  return message ? unsupported(message) : { schema: normalized };
@@ -47820,7 +48154,7 @@ function packSchema(root, schema2, version) {
47820
48154
 
47821
48155
  // src/lib/spec/contract-index.ts
47822
48156
  var HTTP_METHODS = /* @__PURE__ */ new Set(["get", "put", "post", "delete", "options", "head", "patch", "trace"]);
47823
- function asRecord3(value) {
48157
+ function asRecord4(value) {
47824
48158
  if (!value || typeof value !== "object" || Array.isArray(value)) return null;
47825
48159
  return value;
47826
48160
  }
@@ -47837,12 +48171,12 @@ function detectOpenApiVersion(root) {
47837
48171
  return match[1] === "1" ? "3.1" : "3.0";
47838
48172
  }
47839
48173
  function resolveInternalRef(root, value) {
47840
- const record = asRecord3(value);
48174
+ const record = asRecord4(value);
47841
48175
  if (!record) return null;
47842
48176
  const ref = typeof record.$ref === "string" ? record.$ref : "";
47843
48177
  if (!ref) return record;
47844
48178
  if (!ref.startsWith("#/")) throw new Error(`CONTRACT_UNRESOLVED_REF: External ref remained after bundling: ${ref}`);
47845
- const resolved = asRecord3(resolvePointer(root, ref));
48179
+ const resolved = asRecord4(resolvePointer(root, ref));
47846
48180
  if (!resolved) throw new Error(`CONTRACT_UNRESOLVED_REF: Unresolved OpenAPI $ref: ${ref}`);
47847
48181
  return resolved;
47848
48182
  }
@@ -47862,11 +48196,11 @@ function normalizePath(path6) {
47862
48196
  return trimmed.split("/").map((segment, index) => index === 0 ? "" : safeDecodeSegment(segment)).join("/") || "/";
47863
48197
  }
47864
48198
  function pathItemServers(pathItem) {
47865
- return asArray2(pathItem.servers).map((entry) => asRecord3(entry)).map((entry) => typeof entry?.url === "string" ? entry.url : "").filter(Boolean);
48199
+ return asArray2(pathItem.servers).map((entry) => asRecord4(entry)).map((entry) => typeof entry?.url === "string" ? entry.url : "").filter(Boolean);
47866
48200
  }
47867
48201
  function operationServers(root, pathItem, operation) {
47868
48202
  const rawServers = asArray2(operation.servers).length > 0 ? asArray2(operation.servers) : pathItemServers(pathItem).length > 0 ? asArray2(pathItem.servers) : asArray2(root.servers);
47869
- const values = rawServers.map((entry) => asRecord3(entry)).map((entry) => typeof entry?.url === "string" ? entry.url : "").filter(Boolean);
48203
+ const values = rawServers.map((entry) => asRecord4(entry)).map((entry) => typeof entry?.url === "string" ? entry.url : "").filter(Boolean);
47870
48204
  return values.length > 0 ? values : [""];
47871
48205
  }
47872
48206
  function serverPathPrefix(url) {
@@ -47887,10 +48221,10 @@ function normalizeResponseKey(status) {
47887
48221
  return /^[1-5]xx$/i.test(raw) ? raw.toUpperCase() : raw;
47888
48222
  }
47889
48223
  function collectSecurityApiKeys(root, operation) {
47890
- const securitySchemes = asRecord3(asRecord3(root.components)?.securitySchemes);
48224
+ const securitySchemes = asRecord4(asRecord4(root.components)?.securitySchemes);
47891
48225
  const requirements = operation.security === void 0 ? asArray2(root.security) : asArray2(operation.security);
47892
48226
  const names = /* @__PURE__ */ new Set();
47893
- for (const requirement of requirements.map((entry) => asRecord3(entry)).filter(Boolean)) {
48227
+ for (const requirement of requirements.map((entry) => asRecord4(entry)).filter(Boolean)) {
47894
48228
  for (const schemeName of Object.keys(requirement)) {
47895
48229
  const scheme = resolveInternalRef(root, securitySchemes?.[schemeName]);
47896
48230
  if (scheme?.type === "apiKey" && typeof scheme.name === "string" && ["query", "header"].includes(String(scheme.in))) {
@@ -47908,10 +48242,10 @@ function securitySchemeKind(scheme) {
47908
48242
  return type2;
47909
48243
  }
47910
48244
  function collectSecuritySchemeWarnings(root, operation) {
47911
- const securitySchemes = asRecord3(asRecord3(root.components)?.securitySchemes);
48245
+ const securitySchemes = asRecord4(asRecord4(root.components)?.securitySchemes);
47912
48246
  const requirements = operation.security === void 0 ? asArray2(root.security) : asArray2(operation.security);
47913
48247
  const warnings = /* @__PURE__ */ new Set();
47914
- for (const requirement of requirements.map((entry) => asRecord3(entry)).filter(Boolean)) {
48248
+ for (const requirement of requirements.map((entry) => asRecord4(entry)).filter(Boolean)) {
47915
48249
  for (const schemeName of Object.keys(requirement)) {
47916
48250
  const scheme = resolveInternalRef(root, securitySchemes?.[schemeName]);
47917
48251
  warnings.add(
@@ -47947,24 +48281,24 @@ function collectParameters(root, pathItem, operation) {
47947
48281
  function collectRequestBody(root, operation) {
47948
48282
  const body = resolveInternalRef(root, operation.requestBody);
47949
48283
  if (!body || body.required !== true) return void 0;
47950
- const content = asRecord3(body.content);
48284
+ const content = asRecord4(body.content);
47951
48285
  return {
47952
48286
  required: true,
47953
48287
  contentTypes: content ? Object.keys(content) : []
47954
48288
  };
47955
48289
  }
47956
48290
  function responseContent(root, version, response) {
47957
- const content = asRecord3(response.content);
48291
+ const content = asRecord4(response.content);
47958
48292
  if (!content) return {};
47959
48293
  const media = {};
47960
48294
  for (const [contentType, mediaObject] of Object.entries(content)) {
47961
- const schema2 = asRecord3(mediaObject)?.schema;
48295
+ const schema2 = asRecord4(mediaObject)?.schema;
47962
48296
  media[contentType] = schema2 === void 0 ? {} : packSchema(root, schema2, version);
47963
48297
  }
47964
48298
  return media;
47965
48299
  }
47966
48300
  function responseHeaders(root, version, response) {
47967
- const headers = asRecord3(response.headers);
48301
+ const headers = asRecord4(response.headers);
47968
48302
  if (!headers) return [];
47969
48303
  return Object.entries(headers).map(([name, rawHeader]) => {
47970
48304
  const header = resolveInternalRef(root, rawHeader);
@@ -47979,10 +48313,10 @@ function buildContractIndex(root) {
47979
48313
  if (root.swagger === "2.0") throw new Error("CONTRACT_UNSUPPORTED_OPENAPI_VERSION: Dynamic contract tests require OpenAPI 3.0 or 3.1 (found swagger 2.0)");
47980
48314
  if (!("openapi" in root)) throw new Error("CONTRACT_UNSUPPORTED_OPENAPI_VERSION: Dynamic contract tests require OpenAPI 3.0 or 3.1 (missing openapi)");
47981
48315
  const version = detectOpenApiVersion(root);
47982
- const paths = asRecord3(root.paths);
48316
+ const paths = asRecord4(root.paths);
47983
48317
  const operations = [];
47984
48318
  const warnings = [];
47985
- if (asRecord3(root.webhooks)) warnings.push("CONTRACT_WEBHOOKS_NOT_VALIDATED: OpenAPI webhooks are not validated by dynamic contract tests");
48319
+ if (asRecord4(root.webhooks)) warnings.push("CONTRACT_WEBHOOKS_NOT_VALIDATED: OpenAPI webhooks are not validated by dynamic contract tests");
47986
48320
  if (paths) {
47987
48321
  for (const [path6, rawPathItem] of Object.entries(paths)) {
47988
48322
  const pathItem = resolveInternalRef(root, rawPathItem);
@@ -47993,7 +48327,7 @@ function buildContractIndex(root) {
47993
48327
  const operation = resolveInternalRef(root, rawOperation);
47994
48328
  if (!operation) continue;
47995
48329
  if (operation.callbacks) warnings.push(`CONTRACT_CALLBACKS_NOT_VALIDATED: callbacks are not validated for ${lowerMethod.toUpperCase()} ${path6}`);
47996
- const responses = asRecord3(operation.responses);
48330
+ const responses = asRecord4(operation.responses);
47997
48331
  if (!responses || Object.keys(responses).length === 0) {
47998
48332
  throw new Error(`CONTRACT_OPERATION_NO_RESPONSES: ${lowerMethod.toUpperCase()} ${path6} must define at least one response`);
47999
48333
  }
@@ -48083,7 +48417,7 @@ var CONTRACT_SIZE_LIMITS = {
48083
48417
  maxTestScriptBytes: 9e5,
48084
48418
  maxCollectionUpdateBytes: 4e6
48085
48419
  };
48086
- function asRecord4(value) {
48420
+ function asRecord5(value) {
48087
48421
  if (!value || typeof value !== "object" || Array.isArray(value)) return null;
48088
48422
  return value;
48089
48423
  }
@@ -48092,7 +48426,7 @@ function asArray3(value) {
48092
48426
  }
48093
48427
  function stringifyPathSegment(segment) {
48094
48428
  if (typeof segment === "string") return segment;
48095
- const record = asRecord4(segment);
48429
+ const record = asRecord5(segment);
48096
48430
  if (!record) return String(segment ?? "");
48097
48431
  for (const key of ["value", "key", "name"]) {
48098
48432
  if (typeof record[key] === "string" && record[key]) return String(record[key]);
@@ -48110,10 +48444,10 @@ function pathFromRaw(raw) {
48110
48444
  }
48111
48445
  }
48112
48446
  function requestPath(request) {
48113
- const record = asRecord4(request);
48447
+ const record = asRecord5(request);
48114
48448
  const url = record?.url ?? request;
48115
48449
  if (typeof url === "string") return pathFromRaw(url);
48116
- const urlRecord = asRecord4(url);
48450
+ const urlRecord = asRecord5(url);
48117
48451
  if (!urlRecord) return "/";
48118
48452
  if (Array.isArray(urlRecord.path)) return normalizePath(`/${urlRecord.path.map(stringifyPathSegment).filter(Boolean).join("/")}`);
48119
48453
  if (typeof urlRecord.path === "string") return normalizePath(urlRecord.path);
@@ -48145,7 +48479,7 @@ function matchCandidate(candidate, request) {
48145
48479
  return { matched: true, staticCount, templateCount };
48146
48480
  }
48147
48481
  function matchOperation(index, request) {
48148
- const record = asRecord4(request);
48482
+ const record = asRecord5(request);
48149
48483
  const method = String(record?.method || "").toUpperCase();
48150
48484
  const path6 = requestPath(request);
48151
48485
  const candidates = index.operations.filter((operation) => operation.method === method).flatMap((operation) => operation.candidates.map((candidate) => ({ operation, score: matchCandidate(candidate, path6), serverFull: candidate !== normalizePath(operation.path) }))).filter((entry) => entry.score.matched).map((entry) => ({ operation: entry.operation, score: [entry.score.staticCount, entry.serverFull ? 2 : 1, -entry.score.templateCount] })).sort((a, b) => {
@@ -48296,17 +48630,17 @@ function createSecretsResolverItem() {
48296
48630
  }
48297
48631
  function isResolverItem(item) {
48298
48632
  if (item.name !== "00 - Resolve Secrets") return false;
48299
- const request = asRecord4(item.request);
48633
+ const request = asRecord5(item.request);
48300
48634
  if (String(request?.method || "").toUpperCase() !== "POST") return false;
48301
- const headers = asArray3(request?.header).map((entry) => asRecord4(entry));
48635
+ const headers = asArray3(request?.header).map((entry) => asRecord5(entry));
48302
48636
  const target = headers.find((entry) => entry?.key === "X-Amz-Target");
48303
48637
  return String(target?.value || "") === "secretsmanager.GetSecretValue" && !requestPath(request).includes("secretsmanager");
48304
48638
  }
48305
48639
  function requestQueryNames(request) {
48306
- const url = asRecord4(request.url);
48640
+ const url = asRecord5(request.url);
48307
48641
  const names = /* @__PURE__ */ new Set();
48308
48642
  if (Array.isArray(url?.query)) {
48309
- for (const entry of url.query.map((item) => asRecord4(item)).filter(Boolean)) {
48643
+ for (const entry of url.query.map((item) => asRecord5(item)).filter(Boolean)) {
48310
48644
  if (entry.disabled !== true && typeof entry.key === "string") names.add(entry.key.toLowerCase());
48311
48645
  }
48312
48646
  }
@@ -48321,17 +48655,17 @@ function requestQueryNames(request) {
48321
48655
  }
48322
48656
  function requestHeaderNames(request) {
48323
48657
  const names = /* @__PURE__ */ new Set();
48324
- for (const entry of asArray3(request.header).map((item) => asRecord4(item)).filter(Boolean)) {
48658
+ for (const entry of asArray3(request.header).map((item) => asRecord5(item)).filter(Boolean)) {
48325
48659
  if (entry.disabled !== true && typeof entry.key === "string") names.add(entry.key.toLowerCase());
48326
48660
  }
48327
48661
  return names;
48328
48662
  }
48329
48663
  function requestHeaderValue(request, name) {
48330
- const match = asArray3(request.header).map((item) => asRecord4(item)).filter(Boolean).find((entry) => String(entry.key || "").toLowerCase() === name.toLowerCase() && entry.disabled !== true);
48664
+ const match = asArray3(request.header).map((item) => asRecord5(item)).filter(Boolean).find((entry) => String(entry.key || "").toLowerCase() === name.toLowerCase() && entry.disabled !== true);
48331
48665
  return typeof match?.value === "string" ? match.value : void 0;
48332
48666
  }
48333
48667
  function hasRequestBody(request) {
48334
- const body = asRecord4(request.body);
48668
+ const body = asRecord5(request.body);
48335
48669
  if (!body) return false;
48336
48670
  if (typeof body.raw === "string" && body.raw.trim()) return true;
48337
48671
  return ["urlencoded", "formdata", "graphql"].some((key) => Array.isArray(body[key]) ? body[key].length > 0 : Boolean(body[key]));
@@ -48382,21 +48716,21 @@ function validateScript(script) {
48382
48716
  return void 0;
48383
48717
  }
48384
48718
  function scriptExecLines(script) {
48385
- const record = asRecord4(script);
48719
+ const record = asRecord5(script);
48386
48720
  if (!record) return [];
48387
48721
  if (Array.isArray(record.exec)) return record.exec.map((line) => String(line));
48388
48722
  if (typeof record.exec === "string") return [record.exec];
48389
48723
  return [];
48390
48724
  }
48391
48725
  function scanExecutableScripts(node, warnings) {
48392
- for (const event of asArray3(node.event).map((entry) => asRecord4(entry)).filter(Boolean)) {
48726
+ for (const event of asArray3(node.event).map((entry) => asRecord5(entry)).filter(Boolean)) {
48393
48727
  const lines = scriptExecLines(event.script);
48394
48728
  if (lines.length === 0) continue;
48395
48729
  const warning = validateScript(lines);
48396
48730
  if (warning) warnings.push(warning);
48397
48731
  }
48398
48732
  for (const child of asArray3(node.item)) {
48399
- const childRecord = asRecord4(child);
48733
+ const childRecord = asRecord5(child);
48400
48734
  if (childRecord) scanExecutableScripts(childRecord, warnings);
48401
48735
  }
48402
48736
  }
@@ -48406,7 +48740,7 @@ function instrumentContractCollection(collection, index) {
48406
48740
  const inject = (item) => {
48407
48741
  if (isResolverItem(item)) return;
48408
48742
  if (item.request) {
48409
- const request = asRecord4(item.request) ?? {};
48743
+ const request = asRecord5(item.request) ?? {};
48410
48744
  const result = matchOperation(index, request);
48411
48745
  let script;
48412
48746
  if (result.operation) {
@@ -48420,15 +48754,15 @@ function instrumentContractCollection(collection, index) {
48420
48754
  } else {
48421
48755
  script = createMappingFailureScript(`No OpenAPI operation matched request ${result.method} ${result.path}`);
48422
48756
  }
48423
- const events = asArray3(item.event).filter((entry) => asRecord4(entry)?.listen !== "test");
48757
+ const events = asArray3(item.event).filter((entry) => asRecord5(entry)?.listen !== "test");
48424
48758
  item.event = [...events, { listen: "test", script: { type: "text/javascript", exec: script } }];
48425
48759
  }
48426
48760
  for (const child of asArray3(item.item)) {
48427
- const childRecord = asRecord4(child);
48761
+ const childRecord = asRecord5(child);
48428
48762
  if (childRecord) inject(childRecord);
48429
48763
  }
48430
48764
  };
48431
- const items = asArray3(collection.item).map((entry) => asRecord4(entry)).filter((entry) => Boolean(entry)).filter((entry) => !isResolverItem(entry));
48765
+ const items = asArray3(collection.item).map((entry) => asRecord5(entry)).filter((entry) => Boolean(entry)).filter((entry) => !isResolverItem(entry));
48432
48766
  collection.item = items;
48433
48767
  for (const item of items) inject(item);
48434
48768
  const missing = index.operations.filter((operation) => !covered.has(operation.id));
@@ -60792,7 +61126,7 @@ function compileErrors(result) {
60792
61126
 
60793
61127
  // src/lib/spec/openapi-loader.ts
60794
61128
  var import_yaml2 = __toESM(require_dist(), 1);
60795
- function asRecord5(value) {
61129
+ function asRecord6(value) {
60796
61130
  if (!value || typeof value !== "object" || Array.isArray(value)) return null;
60797
61131
  return value;
60798
61132
  }
@@ -60807,7 +61141,7 @@ function parseOpenApiDocument(content) {
60807
61141
  throw new Error("CONTRACT_SPEC_PARSE_FAILED: Spec content is not valid JSON or YAML");
60808
61142
  }
60809
61143
  }
60810
- const doc = asRecord5(parsed);
61144
+ const doc = asRecord6(parsed);
60811
61145
  if (!doc) throw new Error("CONTRACT_SPEC_PARSE_FAILED: Spec content must be a JSON or YAML object");
60812
61146
  return doc;
60813
61147
  }
@@ -60835,7 +61169,7 @@ function collectExternalRefs(node, baseUrl, refs) {
60835
61169
  node.forEach((entry) => collectExternalRefs(entry, baseUrl, refs));
60836
61170
  return;
60837
61171
  }
60838
- const record = asRecord5(node);
61172
+ const record = asRecord6(node);
60839
61173
  if (!record) return;
60840
61174
  const ref = typeof record.$ref === "string" ? record.$ref : "";
60841
61175
  if (ref && !ref.startsWith("#")) {
@@ -61167,6 +61501,11 @@ function resolveInputs(env = process.env) {
61167
61501
  governanceMappingJson: parseGovernanceMappingJson(getInput("governance-mapping-json", env)),
61168
61502
  postmanApiKey: getInput("postman-api-key", env) ?? "",
61169
61503
  postmanAccessToken: getInput("postman-access-token", env),
61504
+ credentialPreflight: parseEnumInput(
61505
+ "credential-preflight",
61506
+ getInput("credential-preflight", env),
61507
+ customerPreviewActionContract.inputs["credential-preflight"].default ?? "warn"
61508
+ ),
61170
61509
  integrationBackend,
61171
61510
  folderStrategy: parseEnumInput("folder-strategy", getInput("folder-strategy", env), "Paths"),
61172
61511
  nestedFolderHierarchy: parseBooleanInput("nested-folder-hierarchy", getInput("nested-folder-hierarchy", env), false),
@@ -61176,6 +61515,7 @@ function resolveInputs(env = process.env) {
61176
61515
  postmanBifrostBase: endpointProfile.bifrostBaseUrl,
61177
61516
  postmanGatewayBase: endpointProfile.gatewayBaseUrl,
61178
61517
  postmanCliInstallUrl: endpointProfile.cliInstallUrl,
61518
+ postmanIapubBase: endpointProfile.iapubBaseUrl,
61179
61519
  githubRefName: env.GITHUB_REF_NAME,
61180
61520
  githubHeadRef: env.GITHUB_HEAD_REF,
61181
61521
  githubRef: env.GITHUB_REF,
@@ -61712,8 +62052,20 @@ For CLI usage, pass --workspace-team-id <id> or export POSTMAN_WORKSPACE_TEAM_ID
61712
62052
  governanceGroupName
61713
62053
  );
61714
62054
  } catch (error) {
62055
+ const session = getMemoizedSessionIdentity();
62056
+ const advised = error instanceof HttpError ? adviseFromHttpError(error, {
62057
+ operation: "governance assignment",
62058
+ hasAccessToken: Boolean(inputs.postmanAccessToken),
62059
+ sessionTeamId: session?.teamId,
62060
+ sessionRoles: session?.roles,
62061
+ sessionConsumerType: session?.consumerType,
62062
+ workspaceTeamId: inputs.workspaceTeamId,
62063
+ explicitTeamId: inputs.teamId || void 0,
62064
+ mask: createSecretMasker([inputs.postmanApiKey, inputs.postmanAccessToken])
62065
+ }) : void 0;
62066
+ const reported = advised ?? error;
61715
62067
  dependencies.core.warning(
61716
- `Failed to assign governance group: ${error instanceof Error ? error.message : String(error)}`
62068
+ `Failed to assign governance group: ${reported instanceof Error ? reported.message : String(reported)}`
61717
62069
  );
61718
62070
  }
61719
62071
  }
@@ -62375,6 +62727,7 @@ var cliInputNames = [
62375
62727
  "spec-path",
62376
62728
  "postman-api-key",
62377
62729
  "postman-access-token",
62730
+ "credential-preflight",
62378
62731
  "workspace-id",
62379
62732
  "spec-id",
62380
62733
  "baseline-collection-id",
@@ -62599,6 +62952,17 @@ async function runCli(argv = process.argv.slice(2), runtime = {}) {
62599
62952
  assertOutputFileAllowed2(config.resultJsonPath);
62600
62953
  assertOutputFileAllowed2(config.dotenvPath);
62601
62954
  const dependencies = createCliDependencies(inputs);
62955
+ await runCredentialPreflight({
62956
+ apiBaseUrl: inputs.postmanApiBase,
62957
+ iapubBaseUrl: inputs.postmanIapubBase,
62958
+ postmanApiKey: inputs.postmanApiKey,
62959
+ postmanAccessToken: inputs.postmanAccessToken,
62960
+ workspaceTeamId: inputs.workspaceTeamId,
62961
+ explicitTeamId: inputs.teamId || void 0,
62962
+ mode: inputs.credentialPreflight,
62963
+ mask: createSecretMasker([inputs.postmanApiKey, inputs.postmanAccessToken]),
62964
+ log: dependencies.core
62965
+ });
62602
62966
  if ((inputs.domain || inputs.governanceGroup) && !dependencies.internalIntegration) {
62603
62967
  dependencies.core.warning(
62604
62968
  "Skipping governance assignment because postman-access-token is not configured"