@postman-cse/onboarding-insights 2.0.0 → 2.1.1
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 +21 -76
- package/action.yml +2 -2
- package/dist/action.cjs +245 -41
- package/dist/cli.cjs +421 -90
- package/dist/index.cjs +247 -41
- package/package.json +6 -5
package/dist/cli.cjs
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
1
2
|
"use strict";
|
|
2
3
|
var __create = Object.create;
|
|
3
4
|
var __defProp = Object.defineProperty;
|
|
@@ -18685,9 +18686,10 @@ __export(cli_exports, {
|
|
|
18685
18686
|
toDotenv: () => toDotenv
|
|
18686
18687
|
});
|
|
18687
18688
|
module.exports = __toCommonJS(cli_exports);
|
|
18688
|
-
var
|
|
18689
|
+
var import_node_crypto2 = require("node:crypto");
|
|
18690
|
+
var import_node_fs2 = require("node:fs");
|
|
18689
18691
|
var import_promises = require("node:fs/promises");
|
|
18690
|
-
var
|
|
18692
|
+
var import_node_path2 = __toESM(require("node:path"), 1);
|
|
18691
18693
|
|
|
18692
18694
|
// src/lib/retry.ts
|
|
18693
18695
|
function sleep(delayMs) {
|
|
@@ -18891,6 +18893,53 @@ var AccessTokenProvider = class {
|
|
|
18891
18893
|
return token;
|
|
18892
18894
|
}
|
|
18893
18895
|
};
|
|
18896
|
+
async function describeMintFailure(mintError, apiKey, apiBaseUrl, fetchImpl) {
|
|
18897
|
+
const raw = mintError instanceof Error ? mintError.message : String(mintError);
|
|
18898
|
+
const rejected = /HTTP 40[13]|PMAK rejected/.test(raw);
|
|
18899
|
+
if (!rejected) {
|
|
18900
|
+
return raw;
|
|
18901
|
+
}
|
|
18902
|
+
try {
|
|
18903
|
+
const me = await fetchImpl(`${apiBaseUrl}/me`, { headers: { "x-api-key": apiKey } });
|
|
18904
|
+
if (me.ok) {
|
|
18905
|
+
const body = await me.json().catch(() => void 0);
|
|
18906
|
+
const user = body?.user;
|
|
18907
|
+
const looksPersonal = Boolean(user && (user.username || user.email));
|
|
18908
|
+
if (looksPersonal) {
|
|
18909
|
+
return "Personal API key detected, cannot mint a service-account access token. POST /service-account-tokens only accepts a SERVICE-ACCOUNT API key; this postman-api-key belongs to a user account" + (user?.teamId ? ` (team ${user.teamId})` : "") + ". Create a service account in Team Settings and use its PMAK, or mint the token elsewhere and pass postman-access-token.";
|
|
18910
|
+
}
|
|
18911
|
+
return "The postman-api-key authenticates (GET /me OK) but was rejected by POST /service-account-tokens" + (user?.teamId ? ` (team ${user.teamId})` : "") + ". The service account likely lacks permission to mint access tokens, or service accounts are restricted for this team. Check the service account role in Team Settings, or pass a pre-minted postman-access-token.";
|
|
18912
|
+
}
|
|
18913
|
+
return "The postman-api-key is invalid, disabled, or expired (rejected by both POST /service-account-tokens and GET /me). Generate a fresh service-account PMAK in Team Settings and update the secret.";
|
|
18914
|
+
} catch {
|
|
18915
|
+
return raw;
|
|
18916
|
+
}
|
|
18917
|
+
}
|
|
18918
|
+
async function mintAccessTokenIfNeeded(inputs, log, setSecret2, fetchImpl = fetch) {
|
|
18919
|
+
if (inputs.postmanAccessToken || !inputs.postmanApiKey) {
|
|
18920
|
+
return;
|
|
18921
|
+
}
|
|
18922
|
+
const apiBaseUrl = String(
|
|
18923
|
+
inputs.postmanApiBase || POSTMAN_ENDPOINT_PROFILES.prod.apiBaseUrl
|
|
18924
|
+
).replace(/\/+$/, "");
|
|
18925
|
+
const provider = new AccessTokenProvider({
|
|
18926
|
+
apiKey: inputs.postmanApiKey,
|
|
18927
|
+
apiBaseUrl,
|
|
18928
|
+
fetchImpl,
|
|
18929
|
+
onToken: (token) => setSecret2?.(token)
|
|
18930
|
+
});
|
|
18931
|
+
try {
|
|
18932
|
+
inputs.postmanAccessToken = await provider.refresh();
|
|
18933
|
+
log.info(
|
|
18934
|
+
"postman: no postman-access-token configured - minted a short-lived service-account access token from the postman-api-key."
|
|
18935
|
+
);
|
|
18936
|
+
} catch (error2) {
|
|
18937
|
+
const diagnosis = await describeMintFailure(error2, inputs.postmanApiKey, apiBaseUrl, fetchImpl);
|
|
18938
|
+
log.warning(
|
|
18939
|
+
"postman: could not mint an access token from the postman-api-key. " + diagnosis + " Continuing without an access token - access-token-only functionality will be unavailable unless postman-access-token is provided."
|
|
18940
|
+
);
|
|
18941
|
+
}
|
|
18942
|
+
}
|
|
18894
18943
|
|
|
18895
18944
|
// node_modules/@actions/core/lib/core.js
|
|
18896
18945
|
var core_exports = {};
|
|
@@ -21167,12 +21216,62 @@ function getIDToken(aud) {
|
|
|
21167
21216
|
|
|
21168
21217
|
// src/lib/credential-identity.ts
|
|
21169
21218
|
var sessionPath = "/api/sessions/current";
|
|
21219
|
+
var SESSION_MAX_ATTEMPTS = 3;
|
|
21220
|
+
var SESSION_RETRY_BASE_DELAY_MS = 500;
|
|
21221
|
+
var SESSION_RETRY_MAX_DELAY_MS = 8e3;
|
|
21170
21222
|
var pmakMemo = /* @__PURE__ */ new Map();
|
|
21171
21223
|
var sessionMemo = /* @__PURE__ */ new Map();
|
|
21172
21224
|
var memoizedSessionIdentity;
|
|
21225
|
+
var memoizedSessionFailure;
|
|
21226
|
+
function defaultSessionSleep(ms) {
|
|
21227
|
+
return new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
21228
|
+
}
|
|
21229
|
+
function defaultRandom() {
|
|
21230
|
+
return Math.random();
|
|
21231
|
+
}
|
|
21232
|
+
function parseRetryAfterMs(value) {
|
|
21233
|
+
const trimmed = value?.trim();
|
|
21234
|
+
if (!trimmed) {
|
|
21235
|
+
return void 0;
|
|
21236
|
+
}
|
|
21237
|
+
if (/^\d+$/.test(trimmed)) {
|
|
21238
|
+
return Number(trimmed) * 1e3;
|
|
21239
|
+
}
|
|
21240
|
+
const dateMs = Date.parse(trimmed);
|
|
21241
|
+
if (!Number.isNaN(dateMs)) {
|
|
21242
|
+
return Math.max(0, dateMs - Date.now());
|
|
21243
|
+
}
|
|
21244
|
+
return void 0;
|
|
21245
|
+
}
|
|
21246
|
+
function parseRateLimitResetMs(value) {
|
|
21247
|
+
const trimmed = value?.trim();
|
|
21248
|
+
if (!trimmed || !/^\d+$/.test(trimmed)) {
|
|
21249
|
+
return void 0;
|
|
21250
|
+
}
|
|
21251
|
+
const seconds = Number(trimmed);
|
|
21252
|
+
const nowSeconds = Date.now() / 1e3;
|
|
21253
|
+
if (seconds > nowSeconds) {
|
|
21254
|
+
return Math.max(0, (seconds - nowSeconds) * 1e3);
|
|
21255
|
+
}
|
|
21256
|
+
return seconds * 1e3;
|
|
21257
|
+
}
|
|
21258
|
+
function computeSessionRetryDelayMs(response, attempt, random) {
|
|
21259
|
+
const headers = response?.headers;
|
|
21260
|
+
const signal = parseRetryAfterMs(headers?.get("retry-after") ?? null) ?? parseRateLimitResetMs(
|
|
21261
|
+
headers?.get("ratelimit-reset") ?? headers?.get("x-ratelimit-reset") ?? null
|
|
21262
|
+
);
|
|
21263
|
+
if (signal !== void 0) {
|
|
21264
|
+
return Math.min(Math.max(0, signal), SESSION_RETRY_MAX_DELAY_MS);
|
|
21265
|
+
}
|
|
21266
|
+
const ceiling = Math.min(SESSION_RETRY_MAX_DELAY_MS, SESSION_RETRY_BASE_DELAY_MS * 2 ** (attempt - 1));
|
|
21267
|
+
return Math.round(random() * ceiling);
|
|
21268
|
+
}
|
|
21173
21269
|
function getMemoizedSessionIdentity() {
|
|
21174
21270
|
return memoizedSessionIdentity;
|
|
21175
21271
|
}
|
|
21272
|
+
function getSessionResolutionFailure() {
|
|
21273
|
+
return memoizedSessionFailure;
|
|
21274
|
+
}
|
|
21176
21275
|
function asRecord(value) {
|
|
21177
21276
|
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
21178
21277
|
return void 0;
|
|
@@ -21241,46 +21340,90 @@ async function resolveSessionIdentity(opts) {
|
|
|
21241
21340
|
const memoKey = `${baseUrl}::${accessToken}`;
|
|
21242
21341
|
let pending = sessionMemo.get(memoKey);
|
|
21243
21342
|
if (!pending) {
|
|
21244
|
-
pending = probeSessionIdentity(
|
|
21343
|
+
pending = probeSessionIdentity(
|
|
21344
|
+
baseUrl,
|
|
21345
|
+
accessToken,
|
|
21346
|
+
opts.fetchImpl ?? fetch,
|
|
21347
|
+
Math.max(1, opts.maxAttempts ?? SESSION_MAX_ATTEMPTS),
|
|
21348
|
+
opts.sleepImpl ?? defaultSessionSleep,
|
|
21349
|
+
opts.randomImpl ?? defaultRandom
|
|
21350
|
+
);
|
|
21245
21351
|
sessionMemo.set(memoKey, pending);
|
|
21246
21352
|
}
|
|
21247
21353
|
return pending;
|
|
21248
21354
|
}
|
|
21249
|
-
async function
|
|
21355
|
+
async function parseSessionResponse(response) {
|
|
21356
|
+
let payload;
|
|
21250
21357
|
try {
|
|
21251
|
-
|
|
21252
|
-
method: "GET",
|
|
21253
|
-
headers: { "x-access-token": accessToken }
|
|
21254
|
-
});
|
|
21255
|
-
if (!response.ok) {
|
|
21256
|
-
return void 0;
|
|
21257
|
-
}
|
|
21258
|
-
const payload = asRecord(await response.json());
|
|
21259
|
-
if (!payload) {
|
|
21260
|
-
return void 0;
|
|
21261
|
-
}
|
|
21262
|
-
const root = asRecord(payload.session) ?? payload;
|
|
21263
|
-
const identity = asRecord(root.identity);
|
|
21264
|
-
const data = asRecord(root.data);
|
|
21265
|
-
const user = asRecord(data?.user);
|
|
21266
|
-
const roleEntries = Array.isArray(user?.roles) ? user.roles.map((entry) => coerceText(entry) ?? coerceId(entry)).filter((entry) => Boolean(entry)) : [];
|
|
21267
|
-
const singleRole = coerceText(user?.role);
|
|
21268
|
-
const roles = roleEntries.length > 0 ? roleEntries : singleRole ? [singleRole] : void 0;
|
|
21269
|
-
const resolved = {
|
|
21270
|
-
source: "iapub/sessions",
|
|
21271
|
-
userId: coerceId(identity?.user) ?? coerceId(user?.id),
|
|
21272
|
-
fullName: coerceText(user?.fullName) ?? coerceText(user?.name) ?? coerceText(user?.username),
|
|
21273
|
-
teamId: coerceId(identity?.team),
|
|
21274
|
-
teamName: coerceText(user?.teamName),
|
|
21275
|
-
teamDomain: coerceText(identity?.domain),
|
|
21276
|
-
...roles ? { roles } : {},
|
|
21277
|
-
consumerType: coerceText(root.consumerType) ?? coerceText(data?.consumerType) ?? coerceText(user?.consumerType)
|
|
21278
|
-
};
|
|
21279
|
-
memoizedSessionIdentity = resolved;
|
|
21280
|
-
return resolved;
|
|
21358
|
+
payload = asRecord(await response.json());
|
|
21281
21359
|
} catch {
|
|
21282
21360
|
return void 0;
|
|
21283
21361
|
}
|
|
21362
|
+
if (!payload) {
|
|
21363
|
+
return void 0;
|
|
21364
|
+
}
|
|
21365
|
+
const root = asRecord(payload.session) ?? payload;
|
|
21366
|
+
const identity = asRecord(root.identity);
|
|
21367
|
+
const data = asRecord(root.data);
|
|
21368
|
+
const user = asRecord(data?.user);
|
|
21369
|
+
const roleEntries = Array.isArray(user?.roles) ? user.roles.map((entry) => coerceText(entry) ?? coerceId(entry)).filter((entry) => Boolean(entry)) : [];
|
|
21370
|
+
const singleRole = coerceText(user?.role);
|
|
21371
|
+
const roles = roleEntries.length > 0 ? roleEntries : singleRole ? [singleRole] : void 0;
|
|
21372
|
+
return {
|
|
21373
|
+
source: "iapub/sessions",
|
|
21374
|
+
userId: coerceId(identity?.user) ?? coerceId(user?.id),
|
|
21375
|
+
fullName: coerceText(user?.fullName) ?? coerceText(user?.name) ?? coerceText(user?.username),
|
|
21376
|
+
teamId: coerceId(identity?.team),
|
|
21377
|
+
teamName: coerceText(user?.teamName),
|
|
21378
|
+
teamDomain: coerceText(identity?.domain),
|
|
21379
|
+
...roles ? { roles } : {},
|
|
21380
|
+
consumerType: coerceText(root.consumerType) ?? coerceText(data?.consumerType) ?? coerceText(user?.consumerType)
|
|
21381
|
+
};
|
|
21382
|
+
}
|
|
21383
|
+
async function probeSessionIdentity(baseUrl, accessToken, fetchImpl, maxAttempts, sleepImpl, random) {
|
|
21384
|
+
let failure = "unavailable";
|
|
21385
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
|
|
21386
|
+
let response;
|
|
21387
|
+
try {
|
|
21388
|
+
response = await fetchImpl(`${baseUrl}${sessionPath}`, {
|
|
21389
|
+
method: "GET",
|
|
21390
|
+
headers: { "x-access-token": accessToken }
|
|
21391
|
+
});
|
|
21392
|
+
} catch {
|
|
21393
|
+
failure = "unavailable";
|
|
21394
|
+
if (attempt < maxAttempts) {
|
|
21395
|
+
await sleepImpl(computeSessionRetryDelayMs(void 0, attempt, random));
|
|
21396
|
+
continue;
|
|
21397
|
+
}
|
|
21398
|
+
break;
|
|
21399
|
+
}
|
|
21400
|
+
if (response.ok) {
|
|
21401
|
+
const resolved = await parseSessionResponse(response);
|
|
21402
|
+
if (resolved) {
|
|
21403
|
+
memoizedSessionIdentity = resolved;
|
|
21404
|
+
memoizedSessionFailure = void 0;
|
|
21405
|
+
return resolved;
|
|
21406
|
+
}
|
|
21407
|
+
failure = "unavailable";
|
|
21408
|
+
break;
|
|
21409
|
+
}
|
|
21410
|
+
if (response.status === 401 || response.status === 403) {
|
|
21411
|
+
failure = "auth";
|
|
21412
|
+
break;
|
|
21413
|
+
}
|
|
21414
|
+
if (response.status === 429 || response.status >= 500) {
|
|
21415
|
+
failure = "unavailable";
|
|
21416
|
+
if (attempt < maxAttempts) {
|
|
21417
|
+
await sleepImpl(computeSessionRetryDelayMs(response, attempt, random));
|
|
21418
|
+
continue;
|
|
21419
|
+
}
|
|
21420
|
+
break;
|
|
21421
|
+
}
|
|
21422
|
+
failure = "unavailable";
|
|
21423
|
+
break;
|
|
21424
|
+
}
|
|
21425
|
+
memoizedSessionFailure = failure;
|
|
21426
|
+
return void 0;
|
|
21284
21427
|
}
|
|
21285
21428
|
function describeTeam(id) {
|
|
21286
21429
|
const label = id?.teamName ?? id?.teamDomain;
|
|
@@ -21373,7 +21516,9 @@ async function runCredentialPreflight(args) {
|
|
|
21373
21516
|
session = await resolveSessionIdentity({
|
|
21374
21517
|
iapubBaseUrl: args.iapubBaseUrl,
|
|
21375
21518
|
accessToken,
|
|
21376
|
-
fetchImpl: args.fetchImpl
|
|
21519
|
+
fetchImpl: args.fetchImpl,
|
|
21520
|
+
...args.sleepImpl ? { sleepImpl: args.sleepImpl } : {},
|
|
21521
|
+
...args.randomImpl ? { randomImpl: args.randomImpl } : {}
|
|
21377
21522
|
});
|
|
21378
21523
|
} catch (error2) {
|
|
21379
21524
|
args.log.warning(
|
|
@@ -21393,11 +21538,20 @@ async function runCredentialPreflight(args) {
|
|
|
21393
21538
|
);
|
|
21394
21539
|
}
|
|
21395
21540
|
} else {
|
|
21541
|
+
const failure = getSessionResolutionFailure();
|
|
21542
|
+
const detail = failure === "auth" ? "the access token was rejected by iapub (401/403), so it is invalid or expired. Re-mint it with postman-resolve-service-token-action (or POST https://api.getpostman.com/service-account-tokens) and re-run." : "iapub was unreachable after retries (network or 5xx). This is usually transient; re-run the job.";
|
|
21543
|
+
const base = "postman: credential preflight could not resolve the access-token session identity from iapub: " + detail;
|
|
21544
|
+
if (args.mode === "enforce") {
|
|
21545
|
+
throw new Error(
|
|
21546
|
+
mask(
|
|
21547
|
+
`${base} (credential-preflight: enforce requires a resolvable session identity; use credential-preflight: warn to continue with reactive error guidance only.)`
|
|
21548
|
+
)
|
|
21549
|
+
);
|
|
21550
|
+
}
|
|
21396
21551
|
args.log.warning(
|
|
21397
|
-
mask(
|
|
21398
|
-
"postman: credential preflight could not resolve the access-token session identity from iapub; continuing with reactive error guidance only"
|
|
21399
|
-
)
|
|
21552
|
+
mask(`${base} Continuing with reactive error guidance only (credential-preflight: warn).`)
|
|
21400
21553
|
);
|
|
21554
|
+
return;
|
|
21401
21555
|
}
|
|
21402
21556
|
const result = crossCheckIdentities({
|
|
21403
21557
|
pmak,
|
|
@@ -22010,6 +22164,35 @@ function getFinalServiceSegment(serviceName) {
|
|
|
22010
22164
|
return lastSlash === -1 ? serviceName : serviceName.slice(lastSlash + 1);
|
|
22011
22165
|
}
|
|
22012
22166
|
|
|
22167
|
+
// src/lib/input.ts
|
|
22168
|
+
function normalizeInputValue(value) {
|
|
22169
|
+
return String(value ?? "").trim();
|
|
22170
|
+
}
|
|
22171
|
+
function runnerInputEnvName(name) {
|
|
22172
|
+
return `INPUT_${name.replace(/ /g, "_").toUpperCase()}`;
|
|
22173
|
+
}
|
|
22174
|
+
function normalizedInputEnvName(name) {
|
|
22175
|
+
return `INPUT_${name.replace(/-/g, "_").toUpperCase()}`;
|
|
22176
|
+
}
|
|
22177
|
+
function getInput2(name, env = process.env) {
|
|
22178
|
+
const normalizedName = normalizedInputEnvName(name);
|
|
22179
|
+
const runnerName = runnerInputEnvName(name);
|
|
22180
|
+
const normalizedRaw = env[normalizedName];
|
|
22181
|
+
const runnerRaw = runnerName === normalizedName ? void 0 : env[runnerName];
|
|
22182
|
+
const hasNormalized = normalizedRaw !== void 0;
|
|
22183
|
+
const hasRunner = runnerRaw !== void 0;
|
|
22184
|
+
if (hasNormalized && hasRunner) {
|
|
22185
|
+
const normalizedValue = normalizeInputValue(normalizedRaw);
|
|
22186
|
+
const runnerValue = normalizeInputValue(runnerRaw);
|
|
22187
|
+
if (normalizedValue !== runnerValue) {
|
|
22188
|
+
throw new Error(
|
|
22189
|
+
`Conflicting values for ${name}: ${normalizedName}=${JSON.stringify(normalizedValue)} vs ${runnerName}=${JSON.stringify(runnerValue)}`
|
|
22190
|
+
);
|
|
22191
|
+
}
|
|
22192
|
+
}
|
|
22193
|
+
return normalizeInputValue(hasNormalized ? normalizedRaw : runnerRaw);
|
|
22194
|
+
}
|
|
22195
|
+
|
|
22013
22196
|
// node_modules/@postman-cse/automation-telemetry-core/dist/ci-context.js
|
|
22014
22197
|
function norm(value) {
|
|
22015
22198
|
const trimmed = (value ?? "").trim();
|
|
@@ -22264,7 +22447,7 @@ function resolveActionVersion(explicit) {
|
|
|
22264
22447
|
if (explicit) {
|
|
22265
22448
|
return explicit;
|
|
22266
22449
|
}
|
|
22267
|
-
return "
|
|
22450
|
+
return typeof __ACTION_VERSION__ !== "undefined" && __ACTION_VERSION__ ? __ACTION_VERSION__ : "unknown";
|
|
22268
22451
|
}
|
|
22269
22452
|
function telemetryDisabled(env) {
|
|
22270
22453
|
const flag = String(env.POSTMAN_ACTIONS_TELEMETRY ?? "").trim().toLowerCase();
|
|
@@ -22386,6 +22569,18 @@ function createTelemetryContext(options) {
|
|
|
22386
22569
|
};
|
|
22387
22570
|
}
|
|
22388
22571
|
|
|
22572
|
+
// src/action-version.ts
|
|
22573
|
+
var import_node_fs = require("node:fs");
|
|
22574
|
+
var import_node_path = require("node:path");
|
|
22575
|
+
function resolveActionVersion2() {
|
|
22576
|
+
try {
|
|
22577
|
+
const raw = (0, import_node_fs.readFileSync)((0, import_node_path.join)(__dirname, "..", "package.json"), "utf8");
|
|
22578
|
+
return JSON.parse(raw).version ?? "unknown";
|
|
22579
|
+
} catch {
|
|
22580
|
+
return "unknown";
|
|
22581
|
+
}
|
|
22582
|
+
}
|
|
22583
|
+
|
|
22389
22584
|
// src/index.ts
|
|
22390
22585
|
var POLL_TIMEOUT_MIN = 10;
|
|
22391
22586
|
var POLL_TIMEOUT_MAX = 600;
|
|
@@ -22447,12 +22642,16 @@ function clamp(value, min, max, fallback) {
|
|
|
22447
22642
|
return Math.min(max, Math.max(min, parsed));
|
|
22448
22643
|
}
|
|
22449
22644
|
function resolveInputs(env = process.env) {
|
|
22450
|
-
const get = (name, fallback = "") =>
|
|
22645
|
+
const get = (name, fallback = "") => getInput2(name, env) || fallback;
|
|
22451
22646
|
const projectName = get("project-name");
|
|
22452
22647
|
if (!projectName) throw new Error("project-name is required");
|
|
22453
22648
|
const postmanAccessToken = get("postman-access-token");
|
|
22454
|
-
if (!postmanAccessToken) throw new Error("postman-access-token is required");
|
|
22455
22649
|
const postmanApiKey = get("postman-api-key");
|
|
22650
|
+
if (!postmanAccessToken && !postmanApiKey) {
|
|
22651
|
+
throw new Error(
|
|
22652
|
+
"postman-access-token is required (or provide a service-account postman-api-key so the action can mint one)."
|
|
22653
|
+
);
|
|
22654
|
+
}
|
|
22456
22655
|
const postmanTeamId = get("postman-team-id") || env.POSTMAN_TEAM_ID?.trim() || "";
|
|
22457
22656
|
const workspaceId = get("workspace-id") || env.POSTMAN_WORKSPACE_ID?.trim() || "";
|
|
22458
22657
|
if (!workspaceId) {
|
|
@@ -22678,6 +22877,24 @@ function createInsightsBifrostClient(inputs, tokenProvider, teamId, apiKey) {
|
|
|
22678
22877
|
}
|
|
22679
22878
|
|
|
22680
22879
|
// src/cli.ts
|
|
22880
|
+
var INPUT_NAMES = [
|
|
22881
|
+
"project-name",
|
|
22882
|
+
"workspace-id",
|
|
22883
|
+
"environment-id",
|
|
22884
|
+
"system-environment-id",
|
|
22885
|
+
"cluster-name",
|
|
22886
|
+
"repo-url",
|
|
22887
|
+
"postman-access-token",
|
|
22888
|
+
"postman-api-key",
|
|
22889
|
+
"credential-preflight",
|
|
22890
|
+
"postman-team-id",
|
|
22891
|
+
"github-token",
|
|
22892
|
+
"poll-timeout-seconds",
|
|
22893
|
+
"poll-interval-seconds",
|
|
22894
|
+
"postman-region",
|
|
22895
|
+
"postman-stack"
|
|
22896
|
+
];
|
|
22897
|
+
var OUTPUT_OPTION_NAMES = ["result-json", "dotenv-path"];
|
|
22681
22898
|
var ConsoleReporter = class {
|
|
22682
22899
|
secretValues = [];
|
|
22683
22900
|
info(message) {
|
|
@@ -22699,51 +22916,101 @@ var ConsoleReporter = class {
|
|
|
22699
22916
|
return masked;
|
|
22700
22917
|
}
|
|
22701
22918
|
};
|
|
22702
|
-
function
|
|
22703
|
-
|
|
22704
|
-
|
|
22705
|
-
|
|
22706
|
-
|
|
22707
|
-
|
|
22708
|
-
|
|
22709
|
-
|
|
22710
|
-
|
|
22919
|
+
function normalizeCliFlag(name) {
|
|
22920
|
+
return normalizedInputEnvName(name);
|
|
22921
|
+
}
|
|
22922
|
+
function resolvePackageVersion() {
|
|
22923
|
+
const candidates = [];
|
|
22924
|
+
if (typeof __filename === "string") {
|
|
22925
|
+
candidates.push(import_node_path2.default.join(import_node_path2.default.dirname(__filename), "..", "package.json"));
|
|
22926
|
+
}
|
|
22927
|
+
candidates.push(import_node_path2.default.join(process.cwd(), "package.json"));
|
|
22928
|
+
for (const candidate of candidates) {
|
|
22929
|
+
try {
|
|
22930
|
+
const packageJson = JSON.parse((0, import_node_fs2.readFileSync)(candidate, "utf8"));
|
|
22931
|
+
if (packageJson.name === "@postman-cse/onboarding-insights" && packageJson.version) {
|
|
22932
|
+
return String(packageJson.version).trim();
|
|
22933
|
+
}
|
|
22934
|
+
} catch {
|
|
22711
22935
|
}
|
|
22712
22936
|
}
|
|
22713
|
-
return
|
|
22937
|
+
return resolveActionVersion2();
|
|
22714
22938
|
}
|
|
22715
|
-
function
|
|
22716
|
-
|
|
22939
|
+
function renderHelp() {
|
|
22940
|
+
const inputFlags = INPUT_NAMES.map((name) => ` --${name} <value>`).join("\n");
|
|
22941
|
+
return [
|
|
22942
|
+
"Usage: postman-insights-onboard [options]",
|
|
22943
|
+
"",
|
|
22944
|
+
"Options:",
|
|
22945
|
+
inputFlags,
|
|
22946
|
+
" --result-json <path> Optional JSON output file (opt-in)",
|
|
22947
|
+
" --dotenv-path <path> Optional dotenv output file",
|
|
22948
|
+
" --help Show this help and exit",
|
|
22949
|
+
" --version Print version and exit",
|
|
22950
|
+
""
|
|
22951
|
+
].join("\n");
|
|
22717
22952
|
}
|
|
22718
22953
|
function parseCliArgs(argv, env = process.env) {
|
|
22719
|
-
|
|
22720
|
-
"
|
|
22721
|
-
|
|
22722
|
-
|
|
22723
|
-
"
|
|
22724
|
-
|
|
22725
|
-
|
|
22726
|
-
|
|
22727
|
-
"postman-api-key",
|
|
22728
|
-
"credential-preflight",
|
|
22729
|
-
"postman-team-id",
|
|
22730
|
-
"github-token",
|
|
22731
|
-
"poll-timeout-seconds",
|
|
22732
|
-
"poll-interval-seconds",
|
|
22733
|
-
"postman-region",
|
|
22734
|
-
"postman-stack"
|
|
22735
|
-
];
|
|
22954
|
+
if (argv.includes("--help") || argv.includes("-h")) {
|
|
22955
|
+
return { kind: "help" };
|
|
22956
|
+
}
|
|
22957
|
+
if (argv.includes("--version") || argv.includes("-V")) {
|
|
22958
|
+
return { kind: "version" };
|
|
22959
|
+
}
|
|
22960
|
+
const allowed = /* @__PURE__ */ new Set([...INPUT_NAMES, ...OUTPUT_OPTION_NAMES]);
|
|
22961
|
+
const seen = /* @__PURE__ */ new Set();
|
|
22736
22962
|
const inputEnv = { ...env };
|
|
22737
|
-
|
|
22738
|
-
|
|
22739
|
-
|
|
22740
|
-
|
|
22963
|
+
let resultJsonPath;
|
|
22964
|
+
let dotenvPath;
|
|
22965
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
22966
|
+
const arg = argv[index];
|
|
22967
|
+
if (!arg) {
|
|
22968
|
+
continue;
|
|
22741
22969
|
}
|
|
22970
|
+
if (!arg.startsWith("--")) {
|
|
22971
|
+
throw new Error(`Unexpected positional argument: ${arg}`);
|
|
22972
|
+
}
|
|
22973
|
+
const equalsIndex = arg.indexOf("=");
|
|
22974
|
+
const name = equalsIndex >= 0 ? arg.slice(2, equalsIndex) : arg.slice(2);
|
|
22975
|
+
if (!allowed.has(name)) {
|
|
22976
|
+
throw new Error(`Unknown option: --${name}`);
|
|
22977
|
+
}
|
|
22978
|
+
if (seen.has(name)) {
|
|
22979
|
+
throw new Error(`Duplicate option: --${name}`);
|
|
22980
|
+
}
|
|
22981
|
+
let value;
|
|
22982
|
+
if (equalsIndex >= 0) {
|
|
22983
|
+
value = arg.slice(equalsIndex + 1);
|
|
22984
|
+
} else {
|
|
22985
|
+
const next = argv[index + 1];
|
|
22986
|
+
if (next === void 0 || next.startsWith("--")) {
|
|
22987
|
+
throw new Error(`Missing value for --${name}`);
|
|
22988
|
+
}
|
|
22989
|
+
value = next;
|
|
22990
|
+
index += 1;
|
|
22991
|
+
}
|
|
22992
|
+
if (value.length === 0) {
|
|
22993
|
+
throw new Error(`Missing value for --${name}`);
|
|
22994
|
+
}
|
|
22995
|
+
seen.add(name);
|
|
22996
|
+
if (name === "result-json") {
|
|
22997
|
+
resultJsonPath = value;
|
|
22998
|
+
continue;
|
|
22999
|
+
}
|
|
23000
|
+
if (name === "dotenv-path") {
|
|
23001
|
+
dotenvPath = value;
|
|
23002
|
+
continue;
|
|
23003
|
+
}
|
|
23004
|
+
const normalizedName = normalizedInputEnvName(name);
|
|
23005
|
+
delete inputEnv[runnerInputEnvName(name)];
|
|
23006
|
+
delete inputEnv[normalizedName];
|
|
23007
|
+
inputEnv[normalizedName] = value;
|
|
22742
23008
|
}
|
|
22743
23009
|
return {
|
|
23010
|
+
kind: "run",
|
|
22744
23011
|
inputEnv,
|
|
22745
|
-
resultJsonPath
|
|
22746
|
-
dotenvPath
|
|
23012
|
+
resultJsonPath,
|
|
23013
|
+
dotenvPath
|
|
22747
23014
|
};
|
|
22748
23015
|
}
|
|
22749
23016
|
function toDotenv(outputs) {
|
|
@@ -22752,18 +23019,63 @@ function toDotenv(outputs) {
|
|
|
22752
23019
|
value
|
|
22753
23020
|
]).map(([key, value]) => `${key}=${JSON.stringify(value)}`).join("\n");
|
|
22754
23021
|
}
|
|
22755
|
-
|
|
23022
|
+
function assertWithinWorkspace(workspaceRoot, resolved, filePath) {
|
|
23023
|
+
const relative2 = import_node_path2.default.relative(workspaceRoot, resolved);
|
|
23024
|
+
if (relative2 === ".." || relative2.startsWith(`..${import_node_path2.default.sep}`) || import_node_path2.default.isAbsolute(relative2)) {
|
|
23025
|
+
throw new Error(`Output path must stay within workspace: ${filePath}`);
|
|
23026
|
+
}
|
|
23027
|
+
}
|
|
23028
|
+
async function findExistingAncestor(candidate) {
|
|
23029
|
+
let current = candidate;
|
|
23030
|
+
while (true) {
|
|
23031
|
+
try {
|
|
23032
|
+
return await (0, import_promises.realpath)(current);
|
|
23033
|
+
} catch (error2) {
|
|
23034
|
+
if (error2.code !== "ENOENT") {
|
|
23035
|
+
throw error2;
|
|
23036
|
+
}
|
|
23037
|
+
const parent = import_node_path2.default.dirname(current);
|
|
23038
|
+
if (parent === current) {
|
|
23039
|
+
throw error2;
|
|
23040
|
+
}
|
|
23041
|
+
current = parent;
|
|
23042
|
+
}
|
|
23043
|
+
}
|
|
23044
|
+
}
|
|
23045
|
+
async function validateOutputPath(filePath) {
|
|
22756
23046
|
if (!filePath) {
|
|
22757
23047
|
return;
|
|
22758
23048
|
}
|
|
22759
|
-
const workspaceRoot =
|
|
22760
|
-
const resolved =
|
|
22761
|
-
|
|
22762
|
-
|
|
22763
|
-
|
|
23049
|
+
const workspaceRoot = await (0, import_promises.realpath)(process.cwd());
|
|
23050
|
+
const resolved = import_node_path2.default.resolve(workspaceRoot, filePath);
|
|
23051
|
+
assertWithinWorkspace(workspaceRoot, resolved, filePath);
|
|
23052
|
+
const existingParent = await findExistingAncestor(import_node_path2.default.dirname(resolved));
|
|
23053
|
+
assertWithinWorkspace(workspaceRoot, existingParent, filePath);
|
|
23054
|
+
}
|
|
23055
|
+
async function writeAtomicFile(filePath, content) {
|
|
23056
|
+
const workspaceRoot = await (0, import_promises.realpath)(process.cwd());
|
|
23057
|
+
const resolved = import_node_path2.default.resolve(workspaceRoot, filePath);
|
|
23058
|
+
assertWithinWorkspace(workspaceRoot, resolved, filePath);
|
|
23059
|
+
await (0, import_promises.mkdir)(import_node_path2.default.dirname(resolved), { recursive: true });
|
|
23060
|
+
const resolvedParent = await (0, import_promises.realpath)(import_node_path2.default.dirname(resolved));
|
|
23061
|
+
assertWithinWorkspace(workspaceRoot, resolvedParent, filePath);
|
|
23062
|
+
const safeTarget = import_node_path2.default.join(resolvedParent, import_node_path2.default.basename(resolved));
|
|
23063
|
+
const tempPath = import_node_path2.default.join(
|
|
23064
|
+
resolvedParent,
|
|
23065
|
+
`.${import_node_path2.default.basename(resolved)}.${process.pid}.${(0, import_node_crypto2.randomUUID)()}.tmp`
|
|
23066
|
+
);
|
|
23067
|
+
try {
|
|
23068
|
+
await (0, import_promises.writeFile)(tempPath, content, { encoding: "utf8", flag: "wx", mode: 384 });
|
|
23069
|
+
await (0, import_promises.rename)(tempPath, safeTarget);
|
|
23070
|
+
} finally {
|
|
23071
|
+
await (0, import_promises.rm)(tempPath, { force: true });
|
|
22764
23072
|
}
|
|
22765
|
-
|
|
22766
|
-
|
|
23073
|
+
}
|
|
23074
|
+
async function writeOptionalFile(filePath, content) {
|
|
23075
|
+
if (!filePath) {
|
|
23076
|
+
return;
|
|
23077
|
+
}
|
|
23078
|
+
await writeAtomicFile(filePath, content);
|
|
22767
23079
|
}
|
|
22768
23080
|
function toOutputs(result) {
|
|
22769
23081
|
return {
|
|
@@ -22777,10 +23089,30 @@ function toOutputs(result) {
|
|
|
22777
23089
|
}
|
|
22778
23090
|
async function runCli(argv = process.argv.slice(2), runtime = {}) {
|
|
22779
23091
|
const env = runtime.env ?? process.env;
|
|
22780
|
-
const
|
|
23092
|
+
const writeStdout = runtime.writeStdout ?? ((chunk) => process.stdout.write(chunk));
|
|
23093
|
+
const parsed = parseCliArgs(argv, env);
|
|
23094
|
+
if (parsed.kind === "help") {
|
|
23095
|
+
writeStdout(renderHelp());
|
|
23096
|
+
return;
|
|
23097
|
+
}
|
|
23098
|
+
if (parsed.kind === "version") {
|
|
23099
|
+
writeStdout(`${resolvePackageVersion()}
|
|
23100
|
+
`);
|
|
23101
|
+
return;
|
|
23102
|
+
}
|
|
23103
|
+
const config = parsed;
|
|
23104
|
+
await validateOutputPath(config.resultJsonPath);
|
|
23105
|
+
await validateOutputPath(config.dotenvPath);
|
|
22781
23106
|
const inputs = resolveInputs(config.inputEnv);
|
|
22782
23107
|
const reporter = new ConsoleReporter();
|
|
22783
|
-
|
|
23108
|
+
const mintHolder = {
|
|
23109
|
+
postmanAccessToken: inputs.postmanAccessToken,
|
|
23110
|
+
postmanApiKey: inputs.postmanApiKey,
|
|
23111
|
+
postmanApiBase: inputs.postmanApiBase
|
|
23112
|
+
};
|
|
23113
|
+
await mintAccessTokenIfNeeded(mintHolder, reporter, (secret) => reporter.setSecret(secret));
|
|
23114
|
+
inputs.postmanAccessToken = mintHolder.postmanAccessToken;
|
|
23115
|
+
if (inputs.postmanAccessToken) reporter.setSecret(inputs.postmanAccessToken);
|
|
22784
23116
|
if (inputs.postmanApiKey) {
|
|
22785
23117
|
reporter.setSecret(inputs.postmanApiKey);
|
|
22786
23118
|
}
|
|
@@ -22805,7 +23137,7 @@ async function runCli(argv = process.argv.slice(2), runtime = {}) {
|
|
|
22805
23137
|
apiBaseUrl: inputs.postmanApiBase || DEFAULT_POSTMAN_API_BASE,
|
|
22806
23138
|
onToken: (token) => reporter.setSecret(token)
|
|
22807
23139
|
}) : tokenProvider;
|
|
22808
|
-
const telemetry = createTelemetryContext({ action: "postman-insights-onboarding-action", logger: reporter });
|
|
23140
|
+
const telemetry = createTelemetryContext({ action: "postman-insights-onboarding-action", actionVersion: resolveActionVersion2(), logger: reporter });
|
|
22809
23141
|
telemetry.setTeamId(inputs.postmanTeamId || pmakIdentity?.teamId);
|
|
22810
23142
|
if (apiKey) {
|
|
22811
23143
|
reporter.setSecret(apiKey);
|
|
@@ -22839,7 +23171,6 @@ async function runCli(argv = process.argv.slice(2), runtime = {}) {
|
|
|
22839
23171
|
const jsonOutput = JSON.stringify(outputs, null, 2);
|
|
22840
23172
|
await writeOptionalFile(config.resultJsonPath, jsonOutput);
|
|
22841
23173
|
await writeOptionalFile(config.dotenvPath, toDotenv(outputs));
|
|
22842
|
-
const writeStdout = runtime.writeStdout ?? ((chunk) => process.stdout.write(chunk));
|
|
22843
23174
|
writeStdout(`${jsonOutput}
|
|
22844
23175
|
`);
|
|
22845
23176
|
}
|
|
@@ -22850,9 +23181,9 @@ function isEntrypoint(currentPath, entrypointPath) {
|
|
|
22850
23181
|
return false;
|
|
22851
23182
|
}
|
|
22852
23183
|
try {
|
|
22853
|
-
return (0,
|
|
23184
|
+
return (0, import_node_fs2.realpathSync)(currentPath) === (0, import_node_fs2.realpathSync)(entrypointPath);
|
|
22854
23185
|
} catch {
|
|
22855
|
-
return
|
|
23186
|
+
return import_node_path2.default.resolve(currentPath) === import_node_path2.default.resolve(entrypointPath);
|
|
22856
23187
|
}
|
|
22857
23188
|
}
|
|
22858
23189
|
if (isEntrypoint(currentModulePath, entrypoint)) {
|