@postman-cse/onboarding-bootstrap 0.12.0 → 0.13.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/README.md CHANGED
@@ -222,7 +222,7 @@ Install globally:
222
222
  npm install -g @postman-cse/onboarding-bootstrap
223
223
  ```
224
224
 
225
- The CLI package supports Node.js 20+. The examples below use Node.js 24 to match the GitHub Action runtime.
225
+ The CLI package supports Node.js 24+ to match the GitHub Action runtime.
226
226
 
227
227
  Basic usage:
228
228
 
package/action.yml CHANGED
@@ -86,6 +86,10 @@ inputs:
86
86
  description: Determines how requests are named in generated collections (Fallback or URL)
87
87
  required: false
88
88
  default: Fallback
89
+ postman-stack:
90
+ description: Postman stack profile
91
+ required: false
92
+ default: prod
89
93
  outputs:
90
94
  workspace-id:
91
95
  description: Postman workspace ID
package/dist/cli.cjs CHANGED
@@ -46951,6 +46951,12 @@ var openAlphaActionContract = {
46951
46951
  required: false,
46952
46952
  default: "Fallback",
46953
46953
  allowedValues: ["Fallback", "URL"]
46954
+ },
46955
+ "postman-stack": {
46956
+ description: "Postman stack profile.",
46957
+ required: false,
46958
+ default: "prod",
46959
+ allowedValues: ["prod", "beta"]
46954
46960
  }
46955
46961
  },
46956
46962
  outputs: {
@@ -47141,6 +47147,32 @@ var HttpError = class _HttpError extends Error {
47141
47147
  }
47142
47148
  };
47143
47149
 
47150
+ // src/lib/postman/base-urls.ts
47151
+ var POSTMAN_ENDPOINT_PROFILES = {
47152
+ prod: {
47153
+ apiBaseUrl: "https://api.getpostman.com",
47154
+ bifrostBaseUrl: "https://bifrost-premium-https-v4.gw.postman.com",
47155
+ cliInstallUrl: "https://dl-cli.pstmn.io/install/unix.sh",
47156
+ gatewayBaseUrl: "https://gateway.postman.com"
47157
+ },
47158
+ beta: {
47159
+ apiBaseUrl: "https://api.getpostman-beta.com",
47160
+ bifrostBaseUrl: "https://bifrost-https-v4.gw.postman-beta.com",
47161
+ cliInstallUrl: "https://dl-cli.pstmn-beta.io/install/unix.sh",
47162
+ gatewayBaseUrl: "https://gateway.postman-beta.com"
47163
+ }
47164
+ };
47165
+ function parsePostmanStack(value) {
47166
+ const normalized = String(value || "prod").trim().toLowerCase();
47167
+ if (normalized === "prod" || normalized === "beta") {
47168
+ return normalized;
47169
+ }
47170
+ throw new Error(`Unsupported postman-stack "${value}". Supported values: prod, beta`);
47171
+ }
47172
+ function resolvePostmanEndpointProfile(stack) {
47173
+ return POSTMAN_ENDPOINT_PROFILES[stack];
47174
+ }
47175
+
47144
47176
  // src/lib/retry.ts
47145
47177
  function sleep(delayMs) {
47146
47178
  return new Promise((resolve3) => {
@@ -47195,6 +47227,15 @@ function asRecord(value) {
47195
47227
  }
47196
47228
  return value;
47197
47229
  }
47230
+ function extractWorkspacesPage(data) {
47231
+ const workspaces = Array.isArray(data?.workspaces) ? data.workspaces : [];
47232
+ const meta = asRecord(data?.meta);
47233
+ const pagination = asRecord(data?.pagination);
47234
+ const nextCursor = String(
47235
+ data?.nextCursor ?? data?.next_cursor ?? meta?.nextCursor ?? meta?.next_cursor ?? pagination?.nextCursor ?? pagination?.next_cursor ?? ""
47236
+ ).trim() || void 0;
47237
+ return { nextCursor, workspaces };
47238
+ }
47198
47239
  function normalizeGitRepoUrl(url) {
47199
47240
  const raw = String(url || "").trim();
47200
47241
  if (!raw) return "";
@@ -47243,20 +47284,27 @@ function extractGitRepoUrl(value) {
47243
47284
  var PostmanAssetsClient = class {
47244
47285
  apiKey;
47245
47286
  baseUrl;
47287
+ bifrostBaseUrl;
47246
47288
  fetchImpl;
47247
47289
  secretMasker;
47248
47290
  constructor(options) {
47249
47291
  this.apiKey = String(options.apiKey || "").trim();
47250
- this.baseUrl = String(options.baseUrl || "https://api.getpostman.com").replace(
47292
+ this.baseUrl = String(options.baseUrl || POSTMAN_ENDPOINT_PROFILES.prod.apiBaseUrl).replace(
47251
47293
  /\/+$/,
47252
47294
  ""
47253
47295
  );
47296
+ this.bifrostBaseUrl = String(
47297
+ options.bifrostBaseUrl || POSTMAN_ENDPOINT_PROFILES.prod.bifrostBaseUrl
47298
+ ).replace(/\/+$/, "");
47254
47299
  this.fetchImpl = options.fetchImpl ?? fetch;
47255
47300
  this.secretMasker = options.secretMasker ?? createSecretMasker([this.apiKey]);
47256
47301
  }
47257
47302
  getBaseUrl() {
47258
47303
  return this.baseUrl;
47259
47304
  }
47305
+ getBifrostBaseUrl() {
47306
+ return this.bifrostBaseUrl;
47307
+ }
47260
47308
  async getMe() {
47261
47309
  return this.request("/me", { method: "GET" });
47262
47310
  }
@@ -47357,20 +47405,33 @@ var PostmanAssetsClient = class {
47357
47405
  });
47358
47406
  }
47359
47407
  async listWorkspaces() {
47360
- const data = await this.request("/workspaces");
47361
- const workspaces = data?.workspaces ?? [];
47362
- return Array.isArray(workspaces) ? workspaces.map((entry) => asRecord(entry)).filter((workspace) => Boolean(workspace?.id && workspace?.name)).map((workspace) => ({
47408
+ const allWorkspaces = [];
47409
+ const seenCursors = /* @__PURE__ */ new Set();
47410
+ let nextCursor;
47411
+ do {
47412
+ const query = nextCursor ? `?cursor=${encodeURIComponent(nextCursor)}` : "";
47413
+ const data = await this.request(`/workspaces${query}`);
47414
+ const page = extractWorkspacesPage(data);
47415
+ allWorkspaces.push(...page.workspaces);
47416
+ if (!page.nextCursor || seenCursors.has(page.nextCursor)) {
47417
+ nextCursor = void 0;
47418
+ } else {
47419
+ seenCursors.add(page.nextCursor);
47420
+ nextCursor = page.nextCursor;
47421
+ }
47422
+ } while (nextCursor);
47423
+ return allWorkspaces.map((entry) => asRecord(entry)).filter((workspace) => Boolean(workspace?.id && workspace?.name)).map((workspace) => ({
47363
47424
  id: String(workspace.id),
47364
47425
  name: String(workspace.name),
47365
47426
  type: String(workspace.type ?? "team")
47366
- })) : [];
47427
+ }));
47367
47428
  }
47368
47429
  async findWorkspacesByName(name) {
47369
47430
  const workspaces = await this.listWorkspaces();
47370
47431
  return workspaces.filter((w) => w.name === name).sort((a, b) => a.id.localeCompare(b.id)).map((w) => ({ id: w.id, name: w.name }));
47371
47432
  }
47372
47433
  async getWorkspaceGitRepoUrl(workspaceId, teamId, accessToken) {
47373
- const url = "https://bifrost-premium-https-v4.gw.postman.com/ws/proxy";
47434
+ const url = `${this.bifrostBaseUrl}/ws/proxy`;
47374
47435
  const headers = {
47375
47436
  "x-access-token": accessToken,
47376
47437
  "Content-Type": "application/json"
@@ -47389,8 +47450,12 @@ var PostmanAssetsClient = class {
47389
47450
  });
47390
47451
  if (response.status === 404) return null;
47391
47452
  if (!response.ok) {
47392
- const body2 = await response.text();
47393
- throw new Error(`Bifrost workspace lookup failed: ${response.status} - ${body2}`);
47453
+ throw await HttpError.fromResponse(response, {
47454
+ method: "POST",
47455
+ requestHeaders: headers,
47456
+ secretValues: [this.apiKey, accessToken],
47457
+ url
47458
+ });
47394
47459
  }
47395
47460
  const body = await response.text();
47396
47461
  if (!body.trim()) return null;
@@ -47790,26 +47855,32 @@ var PostmanAssetsClient = class {
47790
47855
  // src/lib/postman/internal-integration-adapter.ts
47791
47856
  var BifrostInternalIntegrationAdapter = class {
47792
47857
  accessToken;
47858
+ bifrostBaseUrl;
47793
47859
  fetchImpl;
47860
+ gatewayBaseUrl;
47861
+ orgMode;
47794
47862
  secretMasker;
47795
47863
  teamId;
47796
- workerBaseUrl;
47797
47864
  constructor(options) {
47798
47865
  this.accessToken = String(options.accessToken || "").trim();
47866
+ this.bifrostBaseUrl = String(
47867
+ options.bifrostBaseUrl || POSTMAN_ENDPOINT_PROFILES.prod.bifrostBaseUrl
47868
+ ).replace(/\/+$/, "");
47799
47869
  this.fetchImpl = options.fetchImpl ?? fetch;
47870
+ this.gatewayBaseUrl = String(
47871
+ options.gatewayBaseUrl || POSTMAN_ENDPOINT_PROFILES.prod.gatewayBaseUrl
47872
+ ).replace(/\/+$/, "");
47873
+ this.orgMode = options.orgMode ?? false;
47800
47874
  this.secretMasker = options.secretMasker ?? createSecretMasker([this.accessToken]);
47801
47875
  this.teamId = String(options.teamId || "").trim();
47802
- this.workerBaseUrl = String(
47803
- options.workerBaseUrl || "https://catalog-admin.postman-account2009.workers.dev"
47804
- ).replace(/\/+$/, "");
47805
47876
  }
47806
47877
  async proxyRequest(service, method, requestPath2, body) {
47807
- const url = "https://bifrost-premium-https-v4.gw.postman.com/ws/proxy";
47878
+ const url = `${this.bifrostBaseUrl}/ws/proxy`;
47808
47879
  const headers = {
47809
47880
  "Content-Type": "application/json",
47810
47881
  "x-access-token": this.accessToken
47811
47882
  };
47812
- if (this.teamId) {
47883
+ if (this.teamId && this.orgMode) {
47813
47884
  headers["x-entity-team-id"] = this.teamId;
47814
47885
  }
47815
47886
  return this.fetchImpl(url, {
@@ -47834,14 +47905,12 @@ var BifrostInternalIntegrationAdapter = class {
47834
47905
  if (!groupName) {
47835
47906
  return;
47836
47907
  }
47837
- const listResponse = await this.fetchImpl(
47838
- "https://gateway.postman.com/configure/workspace-groups",
47839
- {
47840
- headers: {
47841
- "x-access-token": this.accessToken
47842
- }
47908
+ const listUrl = `${this.gatewayBaseUrl}/configure/workspace-groups`;
47909
+ const listResponse = await this.fetchImpl(listUrl, {
47910
+ headers: {
47911
+ "x-access-token": this.accessToken
47843
47912
  }
47844
- );
47913
+ });
47845
47914
  if (!listResponse.ok) {
47846
47915
  throw await HttpError.fromResponse(listResponse, {
47847
47916
  method: "GET",
@@ -47849,7 +47918,7 @@ var BifrostInternalIntegrationAdapter = class {
47849
47918
  "x-access-token": this.accessToken
47850
47919
  },
47851
47920
  secretValues: [this.accessToken],
47852
- url: "https://gateway.postman.com/configure/workspace-groups"
47921
+ url: listUrl
47853
47922
  });
47854
47923
  }
47855
47924
  const groups = await listResponse.json();
@@ -47857,19 +47926,17 @@ var BifrostInternalIntegrationAdapter = class {
47857
47926
  if (!group?.id) {
47858
47927
  return;
47859
47928
  }
47860
- const patchResponse = await this.fetchImpl(
47861
- `https://gateway.postman.com/configure/workspace-groups/${group.id}`,
47862
- {
47863
- method: "PATCH",
47864
- headers: {
47865
- "Content-Type": "application/json",
47866
- "x-access-token": this.accessToken
47867
- },
47868
- body: JSON.stringify({
47869
- workspaces: [workspaceId]
47870
- })
47871
- }
47872
- );
47929
+ const patchUrl = `${this.gatewayBaseUrl}/configure/workspace-groups/${group.id}`;
47930
+ const patchResponse = await this.fetchImpl(patchUrl, {
47931
+ method: "PATCH",
47932
+ headers: {
47933
+ "Content-Type": "application/json",
47934
+ "x-access-token": this.accessToken
47935
+ },
47936
+ body: JSON.stringify({
47937
+ workspaces: [workspaceId]
47938
+ })
47939
+ });
47873
47940
  if (!patchResponse.ok) {
47874
47941
  throw await HttpError.fromResponse(patchResponse, {
47875
47942
  method: "PATCH",
@@ -47878,40 +47945,7 @@ var BifrostInternalIntegrationAdapter = class {
47878
47945
  "x-access-token": this.accessToken
47879
47946
  },
47880
47947
  secretValues: [this.accessToken],
47881
- url: `https://gateway.postman.com/configure/workspace-groups/${group.id}`
47882
- });
47883
- }
47884
- }
47885
- async associateSystemEnvironments(workspaceId, associations) {
47886
- if (associations.length === 0) {
47887
- return;
47888
- }
47889
- const response = await this.fetchImpl(
47890
- `${this.workerBaseUrl}/api/internal/system-envs/associate`,
47891
- {
47892
- method: "POST",
47893
- headers: {
47894
- Authorization: `Bearer ${this.accessToken}`,
47895
- "Content-Type": "application/json"
47896
- },
47897
- body: JSON.stringify({
47898
- workspace_id: workspaceId,
47899
- associations: associations.map((entry) => ({
47900
- env_uid: entry.envUid,
47901
- system_env_id: entry.systemEnvId
47902
- }))
47903
- })
47904
- }
47905
- );
47906
- if (!response.ok) {
47907
- throw await HttpError.fromResponse(response, {
47908
- method: "POST",
47909
- requestHeaders: {
47910
- Authorization: `Bearer ${this.accessToken}`,
47911
- "Content-Type": "application/json"
47912
- },
47913
- secretValues: [this.accessToken],
47914
- url: `${this.workerBaseUrl}/api/internal/system-envs/associate`
47948
+ url: patchUrl
47915
47949
  });
47916
47950
  }
47917
47951
  }
@@ -47951,10 +47985,10 @@ var BifrostInternalIntegrationAdapter = class {
47951
47985
  requestHeaders: {
47952
47986
  "Content-Type": "application/json",
47953
47987
  "x-access-token": this.accessToken,
47954
- ...this.teamId ? { "x-entity-team-id": this.teamId } : {}
47988
+ ...this.teamId && this.orgMode ? { "x-entity-team-id": this.teamId } : {}
47955
47989
  },
47956
47990
  secretValues: [this.accessToken],
47957
- url: "https://bifrost-premium-https-v4.gw.postman.com/ws/proxy"
47991
+ url: `${this.bifrostBaseUrl}/ws/proxy`
47958
47992
  });
47959
47993
  }
47960
47994
  async linkCollectionsToSpecification(specificationId, collections) {
@@ -47978,10 +48012,10 @@ var BifrostInternalIntegrationAdapter = class {
47978
48012
  requestHeaders: {
47979
48013
  "Content-Type": "application/json",
47980
48014
  "x-access-token": this.accessToken,
47981
- ...this.teamId ? { "x-entity-team-id": this.teamId } : {}
48015
+ ...this.teamId && this.orgMode ? { "x-entity-team-id": this.teamId } : {}
47982
48016
  },
47983
48017
  secretValues: [this.accessToken],
47984
- url: "https://bifrost-premium-https-v4.gw.postman.com/ws/proxy"
48018
+ url: `${this.bifrostBaseUrl}/ws/proxy`
47985
48019
  });
47986
48020
  }
47987
48021
  async syncCollection(specificationId, collectionId) {
@@ -47998,10 +48032,10 @@ var BifrostInternalIntegrationAdapter = class {
47998
48032
  requestHeaders: {
47999
48033
  "Content-Type": "application/json",
48000
48034
  "x-access-token": this.accessToken,
48001
- ...this.teamId ? { "x-entity-team-id": this.teamId } : {}
48035
+ ...this.teamId && this.orgMode ? { "x-entity-team-id": this.teamId } : {}
48002
48036
  },
48003
48037
  secretValues: [this.accessToken],
48004
- url: "https://bifrost-premium-https-v4.gw.postman.com/ws/proxy"
48038
+ url: `${this.bifrostBaseUrl}/ws/proxy`
48005
48039
  });
48006
48040
  }
48007
48041
  async getWorkspaceGitRepoUrl(workspaceId) {
@@ -48422,10 +48456,18 @@ async function resolveCanonicalWorkspaceSelection(args) {
48422
48456
  args.warn?.(`Workspace duplicate check failed; falling back to repo workspace ${args.repoWorkspaceId}: ${error}`);
48423
48457
  }
48424
48458
  if (matchingWorkspaces.length > 0) {
48425
- matchingWorkspaces = await Promise.all(matchingWorkspaces.map(async (workspace) => ({
48459
+ const lookupResults = await Promise.allSettled(matchingWorkspaces.map(async (workspace) => ({
48426
48460
  ...workspace,
48427
48461
  linkedRepoUrl: await args.postman.getWorkspaceGitRepoUrl(workspace.id, args.teamId, args.accessToken)
48428
48462
  })));
48463
+ matchingWorkspaces = lookupResults.map((result, index) => {
48464
+ if (result.status === "fulfilled") {
48465
+ return result.value;
48466
+ }
48467
+ const workspace = matchingWorkspaces[index];
48468
+ args.warn?.(`Workspace Git repo lookup failed for ${workspace?.id ?? "unknown workspace"}; ignoring linked-repo metadata for that candidate: ${result.reason}`);
48469
+ return workspace ? { ...workspace, linkedRepoUrl: null } : void 0;
48470
+ }).filter((workspace) => Boolean(workspace));
48429
48471
  }
48430
48472
  return chooseCanonicalWorkspace({
48431
48473
  repoWorkspaceId: args.repoWorkspaceId,
@@ -61754,6 +61796,8 @@ function resolveInputs(env = process.env) {
61754
61796
  throw new Error(`spec-url must be a valid HTTPS URL, got: ${sanitizeUrlForLog(specUrl)}`, { cause: error });
61755
61797
  }
61756
61798
  }
61799
+ const postmanStack = parsePostmanStack(getInput("postman-stack", env));
61800
+ const endpointProfile = resolvePostmanEndpointProfile(postmanStack);
61757
61801
  return {
61758
61802
  projectName: getInput("project-name", env) ?? env.GITHUB_REPOSITORY?.split("/").pop() ?? env.CI_PROJECT_NAME ?? "",
61759
61803
  workspaceId: getInput("workspace-id", env),
@@ -61781,6 +61825,11 @@ function resolveInputs(env = process.env) {
61781
61825
  folderStrategy: parseEnumInput("folder-strategy", getInput("folder-strategy", env), "Paths"),
61782
61826
  nestedFolderHierarchy: parseBooleanInput("nested-folder-hierarchy", getInput("nested-folder-hierarchy", env), false),
61783
61827
  requestNameSource: parseEnumInput("request-name-source", getInput("request-name-source", env), "Fallback"),
61828
+ postmanStack,
61829
+ postmanApiBase: endpointProfile.apiBaseUrl,
61830
+ postmanBifrostBase: endpointProfile.bifrostBaseUrl,
61831
+ postmanGatewayBase: endpointProfile.gatewayBaseUrl,
61832
+ postmanCliInstallUrl: endpointProfile.cliInstallUrl,
61784
61833
  githubRefName: env.GITHUB_REF_NAME,
61785
61834
  githubHeadRef: env.GITHUB_HEAD_REF,
61786
61835
  githubRef: env.GITHUB_REF,
@@ -61847,7 +61896,8 @@ function readActionInputs(actionCore) {
61847
61896
  INPUT_FOLDER_STRATEGY: optionalInput(actionCore, "folder-strategy") ?? openAlphaActionContract.inputs["folder-strategy"].default,
61848
61897
  INPUT_NESTED_FOLDER_HIERARCHY: optionalInput(actionCore, "nested-folder-hierarchy") ?? openAlphaActionContract.inputs["nested-folder-hierarchy"].default,
61849
61898
  INPUT_REQUEST_NAME_SOURCE: optionalInput(actionCore, "request-name-source") ?? openAlphaActionContract.inputs["request-name-source"].default,
61850
- INPUT_OPENAPI_VERSION: optionalInput(actionCore, "openapi-version") ?? ""
61899
+ INPUT_OPENAPI_VERSION: optionalInput(actionCore, "openapi-version") ?? "",
61900
+ INPUT_POSTMAN_STACK: optionalInput(actionCore, "postman-stack") ?? openAlphaActionContract.inputs["postman-stack"].default
61851
61901
  });
61852
61902
  return inputs;
61853
61903
  }
@@ -61857,13 +61907,29 @@ function createWorkspaceName(inputs) {
61857
61907
  async function runGroup(actionCore, name, fn) {
61858
61908
  return actionCore.group(name, fn);
61859
61909
  }
61860
- async function ensurePostmanCli(dependencies, postmanApiKey) {
61910
+ function validateHttpsInstallUrl(url) {
61911
+ const safeUrlPattern = /^https:\/\/[A-Za-z0-9.-]+\/[A-Za-z0-9._~/?=&%-]+$/;
61912
+ if (!safeUrlPattern.test(url)) {
61913
+ throw new Error(
61914
+ `postman-cli-install-url must be an https URL with safe characters; got: ${url}`
61915
+ );
61916
+ }
61917
+ return url;
61918
+ }
61919
+ async function ensurePostmanCli(dependencies, postmanApiKey, installUrl = "https://dl-cli.pstmn.io/install/unix.sh") {
61920
+ const validatedUrl = validateHttpsInstallUrl(installUrl);
61861
61921
  const existing = await dependencies.io.which("postman", false).catch(() => "");
61862
61922
  if (!existing) {
61863
- await dependencies.exec.exec("sh", [
61864
- "-c",
61865
- 'curl -o- "https://dl-cli.pstmn.io/install/unix.sh" | sh'
61866
- ]);
61923
+ await dependencies.exec.exec(
61924
+ "sh",
61925
+ ["-c", 'curl -fsSL "$POSTMAN_CLI_INSTALL_URL" | sh'],
61926
+ {
61927
+ env: {
61928
+ ...process.env,
61929
+ POSTMAN_CLI_INSTALL_URL: validatedUrl
61930
+ }
61931
+ }
61932
+ );
61867
61933
  }
61868
61934
  await dependencies.exec.exec("postman", ["login", "--with-api-key", postmanApiKey]);
61869
61935
  }
@@ -62081,7 +62147,7 @@ async function runBootstrap(inputs, dependencies) {
62081
62147
  const workspaceName = createWorkspaceName(inputs);
62082
62148
  const aboutText = `Auto-provisioned by Postman CS open-alpha for ${inputs.projectName}`;
62083
62149
  await runGroup(dependencies.core, "Install Postman CLI", async () => {
62084
- await ensurePostmanCli(dependencies, inputs.postmanApiKey);
62150
+ await ensurePostmanCli(dependencies, inputs.postmanApiKey, inputs.postmanCliInstallUrl);
62085
62151
  });
62086
62152
  const resourcesState = readResourcesState();
62087
62153
  let specId = inputs.specId;
@@ -62197,13 +62263,17 @@ async function runBootstrap(inputs, dependencies) {
62197
62263
  "GET /teams returned multiple teams but none include organizationId. Org-mode detection may be degraded due to an upstream API change. If workspace creation fails, set workspace-team-id explicitly."
62198
62264
  );
62199
62265
  }
62200
- const orgIds = new Set(teams.filter((t) => t.organizationId != null).map((t) => t.organizationId));
62201
- const meTeamId = parseInt(teamId, 10);
62202
- const isOrgMode = teams.length > 1 && orgIds.size === 1 && orgIds.has(meTeamId);
62266
+ const isOrgMode = teams.some((t) => t.organizationId != null);
62203
62267
  if (isOrgMode) {
62204
- const teamList = teams.map((t) => ` ${t.id} ${t.name}`).join("\n");
62205
- throw new Error(
62206
- `Org-mode account detected. Workspace creation requires a specific sub-team ID.
62268
+ if (teams.length === 1) {
62269
+ workspaceTeamId = teams[0].id;
62270
+ dependencies.core.info(
62271
+ `Org-mode account detected. Using sub-team ${teams[0].id} (${teams[0].name}) for workspace creation.`
62272
+ );
62273
+ } else {
62274
+ const teamList = teams.map((t) => ` ${t.id} ${t.name}`).join("\n");
62275
+ throw new Error(
62276
+ `Org-mode account detected. Workspace creation requires a specific sub-team ID.
62207
62277
 
62208
62278
  Available sub-teams:
62209
62279
  ${teamList}
@@ -62215,7 +62285,8 @@ Or for reuse across runs, create a repository variable and reference it:
62215
62285
  workspace-team-id: \${{ vars.POSTMAN_WORKSPACE_TEAM_ID }}
62216
62286
 
62217
62287
  For CLI usage, pass --workspace-team-id <id> or export POSTMAN_WORKSPACE_TEAM_ID=<id>.`
62218
- );
62288
+ );
62289
+ }
62219
62290
  } else if (teams.length > 1) {
62220
62291
  dependencies.core.warning(
62221
62292
  `API key has access to ${teams.length} teams but org-mode could not be confirmed. Proceeding without teamId. If workspace creation fails, set workspace-team-id explicitly.`
@@ -62840,12 +62911,26 @@ For CLI usage, pass --workspace-team-id <id> or export POSTMAN_WORKSPACE_TEAM_ID
62840
62911
  }
62841
62912
  async function runAction(actionCore = core2, actionExec = exec, actionIo = io) {
62842
62913
  const inputs = readActionInputs(actionCore);
62914
+ let orgMode = false;
62915
+ if (inputs.postmanAccessToken && inputs.teamId) {
62916
+ try {
62917
+ const tempPostman = new PostmanAssetsClient({
62918
+ apiKey: inputs.postmanApiKey,
62919
+ baseUrl: inputs.postmanApiBase,
62920
+ bifrostBaseUrl: inputs.postmanBifrostBase,
62921
+ secretMasker: createSecretMasker([inputs.postmanApiKey, inputs.postmanAccessToken])
62922
+ });
62923
+ const teams = await tempPostman.getTeams();
62924
+ orgMode = teams.some((t) => t.organizationId != null);
62925
+ } catch {
62926
+ }
62927
+ }
62843
62928
  const dependencies = createBootstrapDependencies(inputs, {
62844
62929
  core: actionCore,
62845
62930
  exec: actionExec,
62846
62931
  io: actionIo,
62847
62932
  specFetcher: fetch
62848
- });
62933
+ }, orgMode);
62849
62934
  if (inputs.domain && !dependencies.internalIntegration) {
62850
62935
  actionCore.warning(
62851
62936
  "Skipping governance assignment because postman-access-token is not configured"
@@ -62853,18 +62938,23 @@ async function runAction(actionCore = core2, actionExec = exec, actionIo = io) {
62853
62938
  }
62854
62939
  return runBootstrap(inputs, dependencies);
62855
62940
  }
62856
- function createBootstrapDependencies(inputs, factories) {
62941
+ function createBootstrapDependencies(inputs, factories, orgMode = false) {
62857
62942
  const secretMasker = createSecretMasker([
62858
62943
  inputs.postmanApiKey,
62859
62944
  inputs.postmanAccessToken
62860
62945
  ]);
62861
62946
  const postman = new PostmanAssetsClient({
62862
62947
  apiKey: inputs.postmanApiKey,
62948
+ baseUrl: inputs.postmanApiBase,
62949
+ bifrostBaseUrl: inputs.postmanBifrostBase,
62863
62950
  secretMasker
62864
62951
  });
62865
62952
  const internalIntegration = inputs.postmanAccessToken ? createInternalIntegrationAdapter({
62866
62953
  accessToken: inputs.postmanAccessToken,
62867
62954
  backend: inputs.integrationBackend,
62955
+ bifrostBaseUrl: inputs.postmanBifrostBase,
62956
+ gatewayBaseUrl: inputs.postmanGatewayBase,
62957
+ orgMode,
62868
62958
  secretMasker,
62869
62959
  teamId: inputs.teamId || ""
62870
62960
  }) : void 0;
@@ -62948,7 +63038,8 @@ var cliInputNames = [
62948
63038
  "request-name-source",
62949
63039
  "workspace-team-id",
62950
63040
  "repo-url",
62951
- "openapi-version"
63041
+ "openapi-version",
63042
+ "postman-stack"
62952
63043
  ];
62953
63044
  var execFileAsync = (0, import_node_util.promisify)(import_node_child_process.execFile);
62954
63045
  function toCommandLabel(commandLine, args, secretMasker) {
package/dist/index.cjs CHANGED
@@ -46945,6 +46945,12 @@ var openAlphaActionContract = {
46945
46945
  required: false,
46946
46946
  default: "Fallback",
46947
46947
  allowedValues: ["Fallback", "URL"]
46948
+ },
46949
+ "postman-stack": {
46950
+ description: "Postman stack profile.",
46951
+ required: false,
46952
+ default: "prod",
46953
+ allowedValues: ["prod", "beta"]
46948
46954
  }
46949
46955
  },
46950
46956
  outputs: {
@@ -47135,6 +47141,32 @@ var HttpError = class _HttpError extends Error {
47135
47141
  }
47136
47142
  };
47137
47143
 
47144
+ // src/lib/postman/base-urls.ts
47145
+ var POSTMAN_ENDPOINT_PROFILES = {
47146
+ prod: {
47147
+ apiBaseUrl: "https://api.getpostman.com",
47148
+ bifrostBaseUrl: "https://bifrost-premium-https-v4.gw.postman.com",
47149
+ cliInstallUrl: "https://dl-cli.pstmn.io/install/unix.sh",
47150
+ gatewayBaseUrl: "https://gateway.postman.com"
47151
+ },
47152
+ beta: {
47153
+ apiBaseUrl: "https://api.getpostman-beta.com",
47154
+ bifrostBaseUrl: "https://bifrost-https-v4.gw.postman-beta.com",
47155
+ cliInstallUrl: "https://dl-cli.pstmn-beta.io/install/unix.sh",
47156
+ gatewayBaseUrl: "https://gateway.postman-beta.com"
47157
+ }
47158
+ };
47159
+ function parsePostmanStack(value) {
47160
+ const normalized = String(value || "prod").trim().toLowerCase();
47161
+ if (normalized === "prod" || normalized === "beta") {
47162
+ return normalized;
47163
+ }
47164
+ throw new Error(`Unsupported postman-stack "${value}". Supported values: prod, beta`);
47165
+ }
47166
+ function resolvePostmanEndpointProfile(stack) {
47167
+ return POSTMAN_ENDPOINT_PROFILES[stack];
47168
+ }
47169
+
47138
47170
  // src/lib/retry.ts
47139
47171
  function sleep(delayMs) {
47140
47172
  return new Promise((resolve3) => {
@@ -47189,6 +47221,15 @@ function asRecord(value) {
47189
47221
  }
47190
47222
  return value;
47191
47223
  }
47224
+ function extractWorkspacesPage(data) {
47225
+ const workspaces = Array.isArray(data?.workspaces) ? data.workspaces : [];
47226
+ const meta = asRecord(data?.meta);
47227
+ const pagination = asRecord(data?.pagination);
47228
+ const nextCursor = String(
47229
+ data?.nextCursor ?? data?.next_cursor ?? meta?.nextCursor ?? meta?.next_cursor ?? pagination?.nextCursor ?? pagination?.next_cursor ?? ""
47230
+ ).trim() || void 0;
47231
+ return { nextCursor, workspaces };
47232
+ }
47192
47233
  function normalizeGitRepoUrl(url) {
47193
47234
  const raw = String(url || "").trim();
47194
47235
  if (!raw) return "";
@@ -47237,20 +47278,27 @@ function extractGitRepoUrl(value) {
47237
47278
  var PostmanAssetsClient = class {
47238
47279
  apiKey;
47239
47280
  baseUrl;
47281
+ bifrostBaseUrl;
47240
47282
  fetchImpl;
47241
47283
  secretMasker;
47242
47284
  constructor(options) {
47243
47285
  this.apiKey = String(options.apiKey || "").trim();
47244
- this.baseUrl = String(options.baseUrl || "https://api.getpostman.com").replace(
47286
+ this.baseUrl = String(options.baseUrl || POSTMAN_ENDPOINT_PROFILES.prod.apiBaseUrl).replace(
47245
47287
  /\/+$/,
47246
47288
  ""
47247
47289
  );
47290
+ this.bifrostBaseUrl = String(
47291
+ options.bifrostBaseUrl || POSTMAN_ENDPOINT_PROFILES.prod.bifrostBaseUrl
47292
+ ).replace(/\/+$/, "");
47248
47293
  this.fetchImpl = options.fetchImpl ?? fetch;
47249
47294
  this.secretMasker = options.secretMasker ?? createSecretMasker([this.apiKey]);
47250
47295
  }
47251
47296
  getBaseUrl() {
47252
47297
  return this.baseUrl;
47253
47298
  }
47299
+ getBifrostBaseUrl() {
47300
+ return this.bifrostBaseUrl;
47301
+ }
47254
47302
  async getMe() {
47255
47303
  return this.request("/me", { method: "GET" });
47256
47304
  }
@@ -47351,20 +47399,33 @@ var PostmanAssetsClient = class {
47351
47399
  });
47352
47400
  }
47353
47401
  async listWorkspaces() {
47354
- const data = await this.request("/workspaces");
47355
- const workspaces = data?.workspaces ?? [];
47356
- return Array.isArray(workspaces) ? workspaces.map((entry) => asRecord(entry)).filter((workspace) => Boolean(workspace?.id && workspace?.name)).map((workspace) => ({
47402
+ const allWorkspaces = [];
47403
+ const seenCursors = /* @__PURE__ */ new Set();
47404
+ let nextCursor;
47405
+ do {
47406
+ const query = nextCursor ? `?cursor=${encodeURIComponent(nextCursor)}` : "";
47407
+ const data = await this.request(`/workspaces${query}`);
47408
+ const page = extractWorkspacesPage(data);
47409
+ allWorkspaces.push(...page.workspaces);
47410
+ if (!page.nextCursor || seenCursors.has(page.nextCursor)) {
47411
+ nextCursor = void 0;
47412
+ } else {
47413
+ seenCursors.add(page.nextCursor);
47414
+ nextCursor = page.nextCursor;
47415
+ }
47416
+ } while (nextCursor);
47417
+ return allWorkspaces.map((entry) => asRecord(entry)).filter((workspace) => Boolean(workspace?.id && workspace?.name)).map((workspace) => ({
47357
47418
  id: String(workspace.id),
47358
47419
  name: String(workspace.name),
47359
47420
  type: String(workspace.type ?? "team")
47360
- })) : [];
47421
+ }));
47361
47422
  }
47362
47423
  async findWorkspacesByName(name) {
47363
47424
  const workspaces = await this.listWorkspaces();
47364
47425
  return workspaces.filter((w) => w.name === name).sort((a, b) => a.id.localeCompare(b.id)).map((w) => ({ id: w.id, name: w.name }));
47365
47426
  }
47366
47427
  async getWorkspaceGitRepoUrl(workspaceId, teamId, accessToken) {
47367
- const url = "https://bifrost-premium-https-v4.gw.postman.com/ws/proxy";
47428
+ const url = `${this.bifrostBaseUrl}/ws/proxy`;
47368
47429
  const headers = {
47369
47430
  "x-access-token": accessToken,
47370
47431
  "Content-Type": "application/json"
@@ -47383,8 +47444,12 @@ var PostmanAssetsClient = class {
47383
47444
  });
47384
47445
  if (response.status === 404) return null;
47385
47446
  if (!response.ok) {
47386
- const body2 = await response.text();
47387
- throw new Error(`Bifrost workspace lookup failed: ${response.status} - ${body2}`);
47447
+ throw await HttpError.fromResponse(response, {
47448
+ method: "POST",
47449
+ requestHeaders: headers,
47450
+ secretValues: [this.apiKey, accessToken],
47451
+ url
47452
+ });
47388
47453
  }
47389
47454
  const body = await response.text();
47390
47455
  if (!body.trim()) return null;
@@ -47784,26 +47849,32 @@ var PostmanAssetsClient = class {
47784
47849
  // src/lib/postman/internal-integration-adapter.ts
47785
47850
  var BifrostInternalIntegrationAdapter = class {
47786
47851
  accessToken;
47852
+ bifrostBaseUrl;
47787
47853
  fetchImpl;
47854
+ gatewayBaseUrl;
47855
+ orgMode;
47788
47856
  secretMasker;
47789
47857
  teamId;
47790
- workerBaseUrl;
47791
47858
  constructor(options) {
47792
47859
  this.accessToken = String(options.accessToken || "").trim();
47860
+ this.bifrostBaseUrl = String(
47861
+ options.bifrostBaseUrl || POSTMAN_ENDPOINT_PROFILES.prod.bifrostBaseUrl
47862
+ ).replace(/\/+$/, "");
47793
47863
  this.fetchImpl = options.fetchImpl ?? fetch;
47864
+ this.gatewayBaseUrl = String(
47865
+ options.gatewayBaseUrl || POSTMAN_ENDPOINT_PROFILES.prod.gatewayBaseUrl
47866
+ ).replace(/\/+$/, "");
47867
+ this.orgMode = options.orgMode ?? false;
47794
47868
  this.secretMasker = options.secretMasker ?? createSecretMasker([this.accessToken]);
47795
47869
  this.teamId = String(options.teamId || "").trim();
47796
- this.workerBaseUrl = String(
47797
- options.workerBaseUrl || "https://catalog-admin.postman-account2009.workers.dev"
47798
- ).replace(/\/+$/, "");
47799
47870
  }
47800
47871
  async proxyRequest(service, method, requestPath2, body) {
47801
- const url = "https://bifrost-premium-https-v4.gw.postman.com/ws/proxy";
47872
+ const url = `${this.bifrostBaseUrl}/ws/proxy`;
47802
47873
  const headers = {
47803
47874
  "Content-Type": "application/json",
47804
47875
  "x-access-token": this.accessToken
47805
47876
  };
47806
- if (this.teamId) {
47877
+ if (this.teamId && this.orgMode) {
47807
47878
  headers["x-entity-team-id"] = this.teamId;
47808
47879
  }
47809
47880
  return this.fetchImpl(url, {
@@ -47828,14 +47899,12 @@ var BifrostInternalIntegrationAdapter = class {
47828
47899
  if (!groupName) {
47829
47900
  return;
47830
47901
  }
47831
- const listResponse = await this.fetchImpl(
47832
- "https://gateway.postman.com/configure/workspace-groups",
47833
- {
47834
- headers: {
47835
- "x-access-token": this.accessToken
47836
- }
47902
+ const listUrl = `${this.gatewayBaseUrl}/configure/workspace-groups`;
47903
+ const listResponse = await this.fetchImpl(listUrl, {
47904
+ headers: {
47905
+ "x-access-token": this.accessToken
47837
47906
  }
47838
- );
47907
+ });
47839
47908
  if (!listResponse.ok) {
47840
47909
  throw await HttpError.fromResponse(listResponse, {
47841
47910
  method: "GET",
@@ -47843,7 +47912,7 @@ var BifrostInternalIntegrationAdapter = class {
47843
47912
  "x-access-token": this.accessToken
47844
47913
  },
47845
47914
  secretValues: [this.accessToken],
47846
- url: "https://gateway.postman.com/configure/workspace-groups"
47915
+ url: listUrl
47847
47916
  });
47848
47917
  }
47849
47918
  const groups = await listResponse.json();
@@ -47851,19 +47920,17 @@ var BifrostInternalIntegrationAdapter = class {
47851
47920
  if (!group?.id) {
47852
47921
  return;
47853
47922
  }
47854
- const patchResponse = await this.fetchImpl(
47855
- `https://gateway.postman.com/configure/workspace-groups/${group.id}`,
47856
- {
47857
- method: "PATCH",
47858
- headers: {
47859
- "Content-Type": "application/json",
47860
- "x-access-token": this.accessToken
47861
- },
47862
- body: JSON.stringify({
47863
- workspaces: [workspaceId]
47864
- })
47865
- }
47866
- );
47923
+ const patchUrl = `${this.gatewayBaseUrl}/configure/workspace-groups/${group.id}`;
47924
+ const patchResponse = await this.fetchImpl(patchUrl, {
47925
+ method: "PATCH",
47926
+ headers: {
47927
+ "Content-Type": "application/json",
47928
+ "x-access-token": this.accessToken
47929
+ },
47930
+ body: JSON.stringify({
47931
+ workspaces: [workspaceId]
47932
+ })
47933
+ });
47867
47934
  if (!patchResponse.ok) {
47868
47935
  throw await HttpError.fromResponse(patchResponse, {
47869
47936
  method: "PATCH",
@@ -47872,40 +47939,7 @@ var BifrostInternalIntegrationAdapter = class {
47872
47939
  "x-access-token": this.accessToken
47873
47940
  },
47874
47941
  secretValues: [this.accessToken],
47875
- url: `https://gateway.postman.com/configure/workspace-groups/${group.id}`
47876
- });
47877
- }
47878
- }
47879
- async associateSystemEnvironments(workspaceId, associations) {
47880
- if (associations.length === 0) {
47881
- return;
47882
- }
47883
- const response = await this.fetchImpl(
47884
- `${this.workerBaseUrl}/api/internal/system-envs/associate`,
47885
- {
47886
- method: "POST",
47887
- headers: {
47888
- Authorization: `Bearer ${this.accessToken}`,
47889
- "Content-Type": "application/json"
47890
- },
47891
- body: JSON.stringify({
47892
- workspace_id: workspaceId,
47893
- associations: associations.map((entry) => ({
47894
- env_uid: entry.envUid,
47895
- system_env_id: entry.systemEnvId
47896
- }))
47897
- })
47898
- }
47899
- );
47900
- if (!response.ok) {
47901
- throw await HttpError.fromResponse(response, {
47902
- method: "POST",
47903
- requestHeaders: {
47904
- Authorization: `Bearer ${this.accessToken}`,
47905
- "Content-Type": "application/json"
47906
- },
47907
- secretValues: [this.accessToken],
47908
- url: `${this.workerBaseUrl}/api/internal/system-envs/associate`
47942
+ url: patchUrl
47909
47943
  });
47910
47944
  }
47911
47945
  }
@@ -47945,10 +47979,10 @@ var BifrostInternalIntegrationAdapter = class {
47945
47979
  requestHeaders: {
47946
47980
  "Content-Type": "application/json",
47947
47981
  "x-access-token": this.accessToken,
47948
- ...this.teamId ? { "x-entity-team-id": this.teamId } : {}
47982
+ ...this.teamId && this.orgMode ? { "x-entity-team-id": this.teamId } : {}
47949
47983
  },
47950
47984
  secretValues: [this.accessToken],
47951
- url: "https://bifrost-premium-https-v4.gw.postman.com/ws/proxy"
47985
+ url: `${this.bifrostBaseUrl}/ws/proxy`
47952
47986
  });
47953
47987
  }
47954
47988
  async linkCollectionsToSpecification(specificationId, collections) {
@@ -47972,10 +48006,10 @@ var BifrostInternalIntegrationAdapter = class {
47972
48006
  requestHeaders: {
47973
48007
  "Content-Type": "application/json",
47974
48008
  "x-access-token": this.accessToken,
47975
- ...this.teamId ? { "x-entity-team-id": this.teamId } : {}
48009
+ ...this.teamId && this.orgMode ? { "x-entity-team-id": this.teamId } : {}
47976
48010
  },
47977
48011
  secretValues: [this.accessToken],
47978
- url: "https://bifrost-premium-https-v4.gw.postman.com/ws/proxy"
48012
+ url: `${this.bifrostBaseUrl}/ws/proxy`
47979
48013
  });
47980
48014
  }
47981
48015
  async syncCollection(specificationId, collectionId) {
@@ -47992,10 +48026,10 @@ var BifrostInternalIntegrationAdapter = class {
47992
48026
  requestHeaders: {
47993
48027
  "Content-Type": "application/json",
47994
48028
  "x-access-token": this.accessToken,
47995
- ...this.teamId ? { "x-entity-team-id": this.teamId } : {}
48029
+ ...this.teamId && this.orgMode ? { "x-entity-team-id": this.teamId } : {}
47996
48030
  },
47997
48031
  secretValues: [this.accessToken],
47998
- url: "https://bifrost-premium-https-v4.gw.postman.com/ws/proxy"
48032
+ url: `${this.bifrostBaseUrl}/ws/proxy`
47999
48033
  });
48000
48034
  }
48001
48035
  async getWorkspaceGitRepoUrl(workspaceId) {
@@ -48416,10 +48450,18 @@ async function resolveCanonicalWorkspaceSelection(args) {
48416
48450
  args.warn?.(`Workspace duplicate check failed; falling back to repo workspace ${args.repoWorkspaceId}: ${error}`);
48417
48451
  }
48418
48452
  if (matchingWorkspaces.length > 0) {
48419
- matchingWorkspaces = await Promise.all(matchingWorkspaces.map(async (workspace) => ({
48453
+ const lookupResults = await Promise.allSettled(matchingWorkspaces.map(async (workspace) => ({
48420
48454
  ...workspace,
48421
48455
  linkedRepoUrl: await args.postman.getWorkspaceGitRepoUrl(workspace.id, args.teamId, args.accessToken)
48422
48456
  })));
48457
+ matchingWorkspaces = lookupResults.map((result, index) => {
48458
+ if (result.status === "fulfilled") {
48459
+ return result.value;
48460
+ }
48461
+ const workspace = matchingWorkspaces[index];
48462
+ args.warn?.(`Workspace Git repo lookup failed for ${workspace?.id ?? "unknown workspace"}; ignoring linked-repo metadata for that candidate: ${result.reason}`);
48463
+ return workspace ? { ...workspace, linkedRepoUrl: null } : void 0;
48464
+ }).filter((workspace) => Boolean(workspace));
48423
48465
  }
48424
48466
  return chooseCanonicalWorkspace({
48425
48467
  repoWorkspaceId: args.repoWorkspaceId,
@@ -61748,6 +61790,8 @@ function resolveInputs(env = process.env) {
61748
61790
  throw new Error(`spec-url must be a valid HTTPS URL, got: ${sanitizeUrlForLog(specUrl)}`, { cause: error });
61749
61791
  }
61750
61792
  }
61793
+ const postmanStack = parsePostmanStack(getInput("postman-stack", env));
61794
+ const endpointProfile = resolvePostmanEndpointProfile(postmanStack);
61751
61795
  return {
61752
61796
  projectName: getInput("project-name", env) ?? env.GITHUB_REPOSITORY?.split("/").pop() ?? env.CI_PROJECT_NAME ?? "",
61753
61797
  workspaceId: getInput("workspace-id", env),
@@ -61775,6 +61819,11 @@ function resolveInputs(env = process.env) {
61775
61819
  folderStrategy: parseEnumInput("folder-strategy", getInput("folder-strategy", env), "Paths"),
61776
61820
  nestedFolderHierarchy: parseBooleanInput("nested-folder-hierarchy", getInput("nested-folder-hierarchy", env), false),
61777
61821
  requestNameSource: parseEnumInput("request-name-source", getInput("request-name-source", env), "Fallback"),
61822
+ postmanStack,
61823
+ postmanApiBase: endpointProfile.apiBaseUrl,
61824
+ postmanBifrostBase: endpointProfile.bifrostBaseUrl,
61825
+ postmanGatewayBase: endpointProfile.gatewayBaseUrl,
61826
+ postmanCliInstallUrl: endpointProfile.cliInstallUrl,
61778
61827
  githubRefName: env.GITHUB_REF_NAME,
61779
61828
  githubHeadRef: env.GITHUB_HEAD_REF,
61780
61829
  githubRef: env.GITHUB_REF,
@@ -61841,7 +61890,8 @@ function readActionInputs(actionCore) {
61841
61890
  INPUT_FOLDER_STRATEGY: optionalInput(actionCore, "folder-strategy") ?? openAlphaActionContract.inputs["folder-strategy"].default,
61842
61891
  INPUT_NESTED_FOLDER_HIERARCHY: optionalInput(actionCore, "nested-folder-hierarchy") ?? openAlphaActionContract.inputs["nested-folder-hierarchy"].default,
61843
61892
  INPUT_REQUEST_NAME_SOURCE: optionalInput(actionCore, "request-name-source") ?? openAlphaActionContract.inputs["request-name-source"].default,
61844
- INPUT_OPENAPI_VERSION: optionalInput(actionCore, "openapi-version") ?? ""
61893
+ INPUT_OPENAPI_VERSION: optionalInput(actionCore, "openapi-version") ?? "",
61894
+ INPUT_POSTMAN_STACK: optionalInput(actionCore, "postman-stack") ?? openAlphaActionContract.inputs["postman-stack"].default
61845
61895
  });
61846
61896
  return inputs;
61847
61897
  }
@@ -61851,13 +61901,29 @@ function createWorkspaceName(inputs) {
61851
61901
  async function runGroup(actionCore, name, fn) {
61852
61902
  return actionCore.group(name, fn);
61853
61903
  }
61854
- async function ensurePostmanCli(dependencies, postmanApiKey) {
61904
+ function validateHttpsInstallUrl(url) {
61905
+ const safeUrlPattern = /^https:\/\/[A-Za-z0-9.-]+\/[A-Za-z0-9._~/?=&%-]+$/;
61906
+ if (!safeUrlPattern.test(url)) {
61907
+ throw new Error(
61908
+ `postman-cli-install-url must be an https URL with safe characters; got: ${url}`
61909
+ );
61910
+ }
61911
+ return url;
61912
+ }
61913
+ async function ensurePostmanCli(dependencies, postmanApiKey, installUrl = "https://dl-cli.pstmn.io/install/unix.sh") {
61914
+ const validatedUrl = validateHttpsInstallUrl(installUrl);
61855
61915
  const existing = await dependencies.io.which("postman", false).catch(() => "");
61856
61916
  if (!existing) {
61857
- await dependencies.exec.exec("sh", [
61858
- "-c",
61859
- 'curl -o- "https://dl-cli.pstmn.io/install/unix.sh" | sh'
61860
- ]);
61917
+ await dependencies.exec.exec(
61918
+ "sh",
61919
+ ["-c", 'curl -fsSL "$POSTMAN_CLI_INSTALL_URL" | sh'],
61920
+ {
61921
+ env: {
61922
+ ...process.env,
61923
+ POSTMAN_CLI_INSTALL_URL: validatedUrl
61924
+ }
61925
+ }
61926
+ );
61861
61927
  }
61862
61928
  await dependencies.exec.exec("postman", ["login", "--with-api-key", postmanApiKey]);
61863
61929
  }
@@ -62075,7 +62141,7 @@ async function runBootstrap(inputs, dependencies) {
62075
62141
  const workspaceName = createWorkspaceName(inputs);
62076
62142
  const aboutText = `Auto-provisioned by Postman CS open-alpha for ${inputs.projectName}`;
62077
62143
  await runGroup(dependencies.core, "Install Postman CLI", async () => {
62078
- await ensurePostmanCli(dependencies, inputs.postmanApiKey);
62144
+ await ensurePostmanCli(dependencies, inputs.postmanApiKey, inputs.postmanCliInstallUrl);
62079
62145
  });
62080
62146
  const resourcesState = readResourcesState();
62081
62147
  let specId = inputs.specId;
@@ -62191,13 +62257,17 @@ async function runBootstrap(inputs, dependencies) {
62191
62257
  "GET /teams returned multiple teams but none include organizationId. Org-mode detection may be degraded due to an upstream API change. If workspace creation fails, set workspace-team-id explicitly."
62192
62258
  );
62193
62259
  }
62194
- const orgIds = new Set(teams.filter((t) => t.organizationId != null).map((t) => t.organizationId));
62195
- const meTeamId = parseInt(teamId, 10);
62196
- const isOrgMode = teams.length > 1 && orgIds.size === 1 && orgIds.has(meTeamId);
62260
+ const isOrgMode = teams.some((t) => t.organizationId != null);
62197
62261
  if (isOrgMode) {
62198
- const teamList = teams.map((t) => ` ${t.id} ${t.name}`).join("\n");
62199
- throw new Error(
62200
- `Org-mode account detected. Workspace creation requires a specific sub-team ID.
62262
+ if (teams.length === 1) {
62263
+ workspaceTeamId = teams[0].id;
62264
+ dependencies.core.info(
62265
+ `Org-mode account detected. Using sub-team ${teams[0].id} (${teams[0].name}) for workspace creation.`
62266
+ );
62267
+ } else {
62268
+ const teamList = teams.map((t) => ` ${t.id} ${t.name}`).join("\n");
62269
+ throw new Error(
62270
+ `Org-mode account detected. Workspace creation requires a specific sub-team ID.
62201
62271
 
62202
62272
  Available sub-teams:
62203
62273
  ${teamList}
@@ -62209,7 +62279,8 @@ Or for reuse across runs, create a repository variable and reference it:
62209
62279
  workspace-team-id: \${{ vars.POSTMAN_WORKSPACE_TEAM_ID }}
62210
62280
 
62211
62281
  For CLI usage, pass --workspace-team-id <id> or export POSTMAN_WORKSPACE_TEAM_ID=<id>.`
62212
- );
62282
+ );
62283
+ }
62213
62284
  } else if (teams.length > 1) {
62214
62285
  dependencies.core.warning(
62215
62286
  `API key has access to ${teams.length} teams but org-mode could not be confirmed. Proceeding without teamId. If workspace creation fails, set workspace-team-id explicitly.`
@@ -62834,12 +62905,26 @@ For CLI usage, pass --workspace-team-id <id> or export POSTMAN_WORKSPACE_TEAM_ID
62834
62905
  }
62835
62906
  async function runAction(actionCore = core2, actionExec = exec, actionIo = io) {
62836
62907
  const inputs = readActionInputs(actionCore);
62908
+ let orgMode = false;
62909
+ if (inputs.postmanAccessToken && inputs.teamId) {
62910
+ try {
62911
+ const tempPostman = new PostmanAssetsClient({
62912
+ apiKey: inputs.postmanApiKey,
62913
+ baseUrl: inputs.postmanApiBase,
62914
+ bifrostBaseUrl: inputs.postmanBifrostBase,
62915
+ secretMasker: createSecretMasker([inputs.postmanApiKey, inputs.postmanAccessToken])
62916
+ });
62917
+ const teams = await tempPostman.getTeams();
62918
+ orgMode = teams.some((t) => t.organizationId != null);
62919
+ } catch {
62920
+ }
62921
+ }
62837
62922
  const dependencies = createBootstrapDependencies(inputs, {
62838
62923
  core: actionCore,
62839
62924
  exec: actionExec,
62840
62925
  io: actionIo,
62841
62926
  specFetcher: fetch
62842
- });
62927
+ }, orgMode);
62843
62928
  if (inputs.domain && !dependencies.internalIntegration) {
62844
62929
  actionCore.warning(
62845
62930
  "Skipping governance assignment because postman-access-token is not configured"
@@ -62847,18 +62932,23 @@ async function runAction(actionCore = core2, actionExec = exec, actionIo = io) {
62847
62932
  }
62848
62933
  return runBootstrap(inputs, dependencies);
62849
62934
  }
62850
- function createBootstrapDependencies(inputs, factories) {
62935
+ function createBootstrapDependencies(inputs, factories, orgMode = false) {
62851
62936
  const secretMasker = createSecretMasker([
62852
62937
  inputs.postmanApiKey,
62853
62938
  inputs.postmanAccessToken
62854
62939
  ]);
62855
62940
  const postman = new PostmanAssetsClient({
62856
62941
  apiKey: inputs.postmanApiKey,
62942
+ baseUrl: inputs.postmanApiBase,
62943
+ bifrostBaseUrl: inputs.postmanBifrostBase,
62857
62944
  secretMasker
62858
62945
  });
62859
62946
  const internalIntegration = inputs.postmanAccessToken ? createInternalIntegrationAdapter({
62860
62947
  accessToken: inputs.postmanAccessToken,
62861
62948
  backend: inputs.integrationBackend,
62949
+ bifrostBaseUrl: inputs.postmanBifrostBase,
62950
+ gatewayBaseUrl: inputs.postmanGatewayBase,
62951
+ orgMode,
62862
62952
  secretMasker,
62863
62953
  teamId: inputs.teamId || ""
62864
62954
  }) : void 0;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@postman-cse/onboarding-bootstrap",
3
- "version": "0.12.0",
3
+ "version": "0.13.0",
4
4
  "description": "Public open-alpha Postman bootstrap GitHub Action.",
5
5
  "type": "module",
6
6
  "main": "dist/index.cjs",
@@ -14,10 +14,10 @@
14
14
  "LICENSE"
15
15
  ],
16
16
  "engines": {
17
- "node": ">=20"
17
+ "node": ">=24"
18
18
  },
19
19
  "scripts": {
20
- "build": "npm run typecheck && rm -rf dist && esbuild src/index.ts --bundle --platform=node --target=node20 --format=cjs --outfile=dist/index.cjs && esbuild src/cli.ts --bundle --platform=node --target=node20 --format=cjs --outfile=dist/cli.cjs",
20
+ "build": "npm run typecheck && rm -rf dist && esbuild src/index.ts --bundle --platform=node --target=node24 --format=cjs --outfile=dist/index.cjs && esbuild src/cli.ts --bundle --platform=node --target=node24 --format=cjs --outfile=dist/cli.cjs",
21
21
  "check:dist": "npm run build && git diff --exit-code -- dist",
22
22
  "lint": "eslint .",
23
23
  "lint:fix": "eslint . --fix",