@hasna/testers 0.0.29 → 0.0.30

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
@@ -29,6 +29,38 @@ Passing a URL as the first argument will, by default, crawl the site and auto-ge
29
29
  - `--overall-timeout <ms>` — hard timeout for the whole run (default: 10 minutes; CI safety net).
30
30
  - `--github-comment` — post a pass/fail summary as a comment on the current GitHub PR.
31
31
 
32
+ ### Secure Production Debugging
33
+
34
+ Use `prod-debug` when an agent or support engineer needs to inspect a production issue without handling customer passwords, raw cookies, bearer tokens, or OAuth codes:
35
+
36
+ ```bash
37
+ testers prod-debug "https://alumia.com/acme/projects/project-123?agent=agent-id" --reason "connector auth error"
38
+ testers prod-debug "req_abc123" --logs --json
39
+ testers prod-debug "https://app.example.com/org/projects/p1" --profile app --json
40
+ testers prod-debug "https://app.example.com/org/projects/p1" --support-url "https://support.example.com/scoped/session" --support-grant support-grant-123
41
+ ```
42
+
43
+ The command parses the target, redacts sensitive URL parameters, emits safe browser/API/log checks, and blocks user-scoped browser reproduction until the target app provides an audited support browser/session URL or a configured profile that can resolve one. It is app-generic: add a profile in `~/.hasna/testers/config.json` for each production app rather than hardcoding app-specific behavior in the CLI.
44
+
45
+ ```json
46
+ {
47
+ "prodDebug": {
48
+ "apps": {
49
+ "app": {
50
+ "name": "App",
51
+ "origins": ["https://app.example.com", "*.app.example.org"],
52
+ "supportGrantRef": "$APP_SUPPORT_GRANT",
53
+ "supportUrlTemplate": "https://support.example.com/scoped/session?grant={supportGrant}&target={targetUrlEncoded}",
54
+ "piiOrigin": "https://api.app.example.com",
55
+ "logCommand": "app logs --project {project} --session {session} --request {request}"
56
+ }
57
+ }
58
+ }
59
+ }
60
+ ```
61
+
62
+ Credential-bearing profile values can point at environment variables (`$APP_SUPPORT_GRANT`) or the local Hasna secrets vault (`@secrets:division/app/support/grant`). Generated plans redact token, grant, session, key, password, OAuth code, and bearer values before printing or writing output.
63
+
32
64
  ### Exit Codes
33
65
 
34
66
  | Code | Meaning |
package/dist/cli/index.js CHANGED
@@ -13861,7 +13861,8 @@ function loadConfig() {
13861
13861
  judgeModel: fileConfig.judgeModel,
13862
13862
  judgeProvider: fileConfig.judgeProvider,
13863
13863
  selfHeal: fileConfig.selfHeal ?? false,
13864
- conversationsSpace: fileConfig.conversationsSpace
13864
+ conversationsSpace: fileConfig.conversationsSpace,
13865
+ prodDebug: fileConfig.prodDebug
13865
13866
  };
13866
13867
  const envModel = process.env["TESTERS_MODEL"];
13867
13868
  if (envModel) {
@@ -16223,6 +16224,11 @@ function resolveCredential(value) {
16223
16224
  }
16224
16225
  return value;
16225
16226
  }
16227
+ function isCredentialReference(value) {
16228
+ if (!value)
16229
+ return false;
16230
+ return value.startsWith("@secrets:") || value.startsWith("$");
16231
+ }
16226
16232
  var init_secrets_resolver = () => {};
16227
16233
 
16228
16234
  // src/lib/persona-auth.ts
@@ -27744,7 +27750,8 @@ async function runHybridScenario(scenario, options) {
27744
27750
  createdAt: new Date().toISOString(),
27745
27751
  updatedAt: new Date().toISOString(),
27746
27752
  lastPassedAt: null,
27747
- lastPassedUrl: null
27753
+ lastPassedUrl: null,
27754
+ parameters: null
27748
27755
  };
27749
27756
  try {
27750
27757
  const agentResult = await runAgentLoop({
@@ -27835,7 +27842,7 @@ import chalk6 from "chalk";
27835
27842
  // package.json
27836
27843
  var package_default = {
27837
27844
  name: "@hasna/testers",
27838
- version: "0.0.29",
27845
+ version: "0.0.30",
27839
27846
  description: "AI-powered QA testing CLI \u2014 spawns cheap AI agents to test web apps with headless browsers",
27840
27847
  type: "module",
27841
27848
  main: "dist/index.js",
@@ -27900,7 +27907,7 @@ var package_default = {
27900
27907
  },
27901
27908
  repository: {
27902
27909
  type: "git",
27903
- url: "https://github.com/hasna/open-testers.git"
27910
+ url: "https://github.com/hasna/testers.git"
27904
27911
  },
27905
27912
  license: "Apache-2.0",
27906
27913
  keywords: [
@@ -28965,6 +28972,383 @@ function generateLatestReport() {
28965
28972
 
28966
28973
  // src/cli/index.tsx
28967
28974
  init_costs();
28975
+
28976
+ // src/lib/prod-debug.ts
28977
+ init_secrets_resolver();
28978
+ var UUID_RE = /\b[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}\b/i;
28979
+ var SENSITIVE_PARAM_RE = /token|secret|key|password|code|state|cookie|session|grant|credential|auth|jwt|access/i;
28980
+ var SENSITIVE_TEXT_RE = /\b(Bearer\s+[A-Za-z0-9._-]{12,}|sk-[A-Za-z0-9]{12,}|pk_[A-Za-z0-9]{12,}|eyJ[A-Za-z0-9._-]{12,})\b/g;
28981
+ var URL_TEXT_RE = /https?:\/\/[^\s"'<>]+/g;
28982
+ function safeUrl(raw) {
28983
+ try {
28984
+ const url = new URL(raw);
28985
+ if (url.protocol !== "http:" && url.protocol !== "https:")
28986
+ return null;
28987
+ return url;
28988
+ } catch {
28989
+ return null;
28990
+ }
28991
+ }
28992
+ function normalizeOrigin(raw) {
28993
+ const url = safeUrl(raw);
28994
+ if (url)
28995
+ return url.origin;
28996
+ const hostUrl = safeUrl(`https://${raw}`);
28997
+ return hostUrl?.origin ?? null;
28998
+ }
28999
+ function redactProdDebugText(value) {
29000
+ return value.replace(URL_TEXT_RE, (match) => {
29001
+ const url = safeUrl(match);
29002
+ return url ? redactUrl(url) : match;
29003
+ }).replace(SENSITIVE_TEXT_RE, (match) => {
29004
+ if (match.startsWith("Bearer "))
29005
+ return "Bearer [redacted]";
29006
+ return "[redacted]";
29007
+ });
29008
+ }
29009
+ function redactUrl(url) {
29010
+ const clone = new URL(url.toString());
29011
+ for (const key of Array.from(clone.searchParams.keys())) {
29012
+ if (SENSITIVE_PARAM_RE.test(key)) {
29013
+ clone.searchParams.set(key, "[redacted]");
29014
+ }
29015
+ }
29016
+ return clone.toString();
29017
+ }
29018
+ function redactUrlString(value) {
29019
+ const url = safeUrl(value);
29020
+ return url ? redactUrl(url) : redactProdDebugText(value);
29021
+ }
29022
+ function parseProdDebugTarget(target) {
29023
+ const input = target.trim();
29024
+ const url = safeUrl(input);
29025
+ if (!url) {
29026
+ const id = (input.match(UUID_RE)?.[0] ?? input) || null;
29027
+ return {
29028
+ url: null,
29029
+ origin: null,
29030
+ orgSlug: null,
29031
+ projectRef: null,
29032
+ sessionId: null,
29033
+ agentId: null,
29034
+ requestId: input.startsWith("req_") ? input : null,
29035
+ rawId: id
29036
+ };
29037
+ }
29038
+ const parts = url.pathname.split("/").filter(Boolean);
29039
+ const projectsIndex = parts.indexOf("projects");
29040
+ const sessionsIndex = parts.indexOf("sessions");
29041
+ const orgSlug = projectsIndex > 0 ? parts[0] ?? null : null;
29042
+ const projectRef = projectsIndex >= 0 ? parts[projectsIndex + 1] ?? null : null;
29043
+ const sessionId = url.searchParams.get("session") ?? (sessionsIndex >= 0 ? parts[sessionsIndex + 1] ?? null : null);
29044
+ return {
29045
+ url: redactUrl(url),
29046
+ origin: url.origin,
29047
+ orgSlug,
29048
+ projectRef,
29049
+ sessionId,
29050
+ agentId: url.searchParams.get("agent"),
29051
+ requestId: url.searchParams.get("requestId") ?? url.searchParams.get("request_id"),
29052
+ rawId: input.match(UUID_RE)?.[0] ?? null
29053
+ };
29054
+ }
29055
+ function boundedTtl(value) {
29056
+ if (!Number.isFinite(value))
29057
+ return 15;
29058
+ return Math.min(Math.max(Math.round(value ?? 15), 1), 60);
29059
+ }
29060
+ function makeCommand(command) {
29061
+ return command.replace(/\s+/g, " ").trim();
29062
+ }
29063
+ function hostnameFromOrigin(origin) {
29064
+ if (!origin)
29065
+ return null;
29066
+ return safeUrl(origin)?.hostname ?? null;
29067
+ }
29068
+ function originMatches(pattern, origin) {
29069
+ if (!origin)
29070
+ return false;
29071
+ const normalizedPattern = normalizeOrigin(pattern);
29072
+ const normalizedOrigin = normalizeOrigin(origin);
29073
+ if (!normalizedOrigin)
29074
+ return false;
29075
+ if (normalizedPattern === normalizedOrigin)
29076
+ return true;
29077
+ const targetHost = hostnameFromOrigin(normalizedOrigin);
29078
+ const patternHost = normalizedPattern ? hostnameFromOrigin(normalizedPattern) : pattern.replace(/^https?:\/\//, "");
29079
+ if (!targetHost || !patternHost)
29080
+ return false;
29081
+ if (patternHost.startsWith("*.")) {
29082
+ const suffix = patternHost.slice(1);
29083
+ return targetHost.endsWith(suffix);
29084
+ }
29085
+ return targetHost === patternHost;
29086
+ }
29087
+ function resolveProfile(input, target, config) {
29088
+ const apps = config?.apps ?? {};
29089
+ const explicitKey = input.profile?.trim() || input.app?.trim() || config?.defaultProfile;
29090
+ if (explicitKey && apps[explicitKey]) {
29091
+ return {
29092
+ key: explicitKey,
29093
+ profile: apps[explicitKey],
29094
+ matchedOrigin: target.origin
29095
+ };
29096
+ }
29097
+ for (const [key, profile] of Object.entries(apps)) {
29098
+ const match = profile.origins?.find((origin) => originMatches(origin, target.origin));
29099
+ if (match) {
29100
+ return { key, profile, matchedOrigin: match };
29101
+ }
29102
+ }
29103
+ return { key: null, profile: null, matchedOrigin: null };
29104
+ }
29105
+ function firstResolvedCredential(...values) {
29106
+ for (const value of values) {
29107
+ if (!value?.trim())
29108
+ continue;
29109
+ const resolved = resolveCredential(value);
29110
+ if (resolved)
29111
+ return resolved;
29112
+ }
29113
+ return null;
29114
+ }
29115
+ function displayCredential(value, source) {
29116
+ if (!value)
29117
+ return null;
29118
+ if (source && isCredentialReference(source))
29119
+ return "[configured]";
29120
+ return redactProdDebugText(value);
29121
+ }
29122
+ function replacementValues(target, input, supportGrant) {
29123
+ const values = {
29124
+ targetUrl: target.url ?? input.target,
29125
+ origin: target.origin ?? "",
29126
+ org: target.orgSlug ?? "",
29127
+ project: target.projectRef ?? "",
29128
+ session: target.sessionId ?? "",
29129
+ agent: target.agentId ?? "",
29130
+ request: target.requestId ?? "",
29131
+ rawId: target.rawId ?? "",
29132
+ reason: input.reason ?? "",
29133
+ supportGrant: supportGrant ?? ""
29134
+ };
29135
+ for (const [key, value] of Object.entries({ ...values })) {
29136
+ values[`${key}Encoded`] = encodeURIComponent(value);
29137
+ }
29138
+ return values;
29139
+ }
29140
+ function renderTemplate(template, values) {
29141
+ return template.replace(/\{([a-zA-Z0-9_]+)\}/g, (_match, key) => values[key] ?? "");
29142
+ }
29143
+ function resolveSupportGrant(input, profile) {
29144
+ if (input.supportGrantId?.trim()) {
29145
+ return {
29146
+ value: input.supportGrantId.trim(),
29147
+ display: displayCredential(input.supportGrantId.trim()),
29148
+ source: "input"
29149
+ };
29150
+ }
29151
+ const source = profile?.supportGrantRef ?? profile?.supportGrantId ?? null;
29152
+ const value = firstResolvedCredential(profile?.supportGrantRef, profile?.supportGrantId);
29153
+ return { value, display: displayCredential(value, source ?? undefined), source };
29154
+ }
29155
+ function resolveSupportUrl(input, target, profile, supportGrant) {
29156
+ if (input.supportUrl?.trim())
29157
+ return input.supportUrl.trim();
29158
+ const direct = firstResolvedCredential(profile?.supportUrlRef, profile?.supportUrl);
29159
+ if (direct)
29160
+ return direct;
29161
+ if (profile?.supportUrlTemplate) {
29162
+ const rendered = renderTemplate(profile.supportUrlTemplate, replacementValues(target, input, supportGrant)).trim();
29163
+ return rendered || null;
29164
+ }
29165
+ return null;
29166
+ }
29167
+ function resolvePiiOrigin(profile, target) {
29168
+ if (!profile?.piiOrigin)
29169
+ return target.origin;
29170
+ return redactUrlString(renderTemplate(profile.piiOrigin, replacementValues(target, { target: target.url ?? "" }, null)));
29171
+ }
29172
+ function resolveSupportRunTarget(supportUrl, input, target) {
29173
+ if (supportUrl)
29174
+ return redactUrlString(supportUrl);
29175
+ return target.url ?? target.origin ?? redactProdDebugText(input.target);
29176
+ }
29177
+ function supportScenarioDescription(reason) {
29178
+ return `Prod debug: ${reason}. Reproduce the user-visible issue, capture console and network errors, and do not enter secrets.`;
29179
+ }
29180
+ function configuredMissing(profile, supportUrl, supportGrant, includeLogs) {
29181
+ const missing = [];
29182
+ if (!profile) {
29183
+ missing.push("optional: add prodDebug.apps.<profile>.origins to match this app automatically");
29184
+ }
29185
+ if (!supportUrl) {
29186
+ missing.push("supportUrl/supportUrlRef/supportUrlTemplate for scoped browser debugging");
29187
+ }
29188
+ if (!supportGrant) {
29189
+ missing.push("supportGrantId/supportGrantRef for auditable support access");
29190
+ }
29191
+ if (includeLogs && !profile?.logCommand) {
29192
+ missing.push("logCommand for sanitized app/provider log lookup");
29193
+ }
29194
+ return missing;
29195
+ }
29196
+ function createProdDebugPlan(input, config) {
29197
+ const target = parseProdDebugTarget(input.target);
29198
+ const browserRequested = input.includeBrowser !== false;
29199
+ const resolvedProfile = resolveProfile(input, target, config);
29200
+ const supportGrant = resolveSupportGrant(input, resolvedProfile.profile);
29201
+ const supportUrl = resolveSupportUrl(input, target, resolvedProfile.profile, supportGrant.value);
29202
+ const supportBrowserReady = Boolean(supportUrl);
29203
+ const app = input.app?.trim() || resolvedProfile.profile?.name || resolvedProfile.key || (target.origin ? new URL(target.origin).hostname : "app");
29204
+ const reason = input.reason?.trim() || "production debug requested";
29205
+ const actor = input.actor?.trim() || process.env["USER"] || "agent";
29206
+ const ttlMinutes = boundedTtl(input.ttlMinutes);
29207
+ const piiOrigin = resolvePiiOrigin(resolvedProfile.profile, target);
29208
+ const logCommand = resolvedProfile.profile?.logCommand ? redactUrlString(renderTemplate(resolvedProfile.profile.logCommand, replacementValues(target, { ...input, reason }, supportGrant.value))) : null;
29209
+ const safety = [
29210
+ "read-only by default",
29211
+ "no customer passwords or raw cookies",
29212
+ "redact tokens, OAuth codes, session values, support grants, and secrets",
29213
+ "verify org/user/session scope before reading data",
29214
+ "require explicit approval for production writes",
29215
+ `support access TTL capped at ${ttlMinutes} minutes`
29216
+ ];
29217
+ const checks = [];
29218
+ const blocked = [];
29219
+ if (target.url) {
29220
+ checks.push({
29221
+ id: "public-route-smoke",
29222
+ status: "ready",
29223
+ description: "Open the supplied production URL and capture console/network errors without credentials.",
29224
+ command: makeCommand(`testers scan all ${JSON.stringify(target.url)} --json`)
29225
+ });
29226
+ }
29227
+ checks.push({
29228
+ id: "pii-redaction-scan",
29229
+ status: piiOrigin ? "ready" : "blocked",
29230
+ description: "Scan public/API responses for accidental sensitive data leakage.",
29231
+ command: piiOrigin ? makeCommand(`testers scan pii ${JSON.stringify(piiOrigin)} --json`) : undefined,
29232
+ reason: piiOrigin ? undefined : "Need a URL origin or prodDebug app profile piiOrigin to run the PII scan."
29233
+ });
29234
+ if (browserRequested) {
29235
+ if (supportBrowserReady) {
29236
+ checks.push({
29237
+ id: "support-browser-repro",
29238
+ status: "ready",
29239
+ description: "Use an audited support browser/session URL to reproduce the user-visible issue.",
29240
+ command: makeCommand(`testers run ${JSON.stringify(resolveSupportRunTarget(supportUrl, input, target))} ${JSON.stringify(supportScenarioDescription(reason))} --headed --json --overall-timeout 600000`)
29241
+ });
29242
+ } else {
29243
+ const reasonText = supportGrant.value ? "An audited support grant was supplied, but open-testers still needs supportUrl/supportUrlRef/supportUrlTemplate or an app adapter to open a scoped browser session." : "No audited support browser/session grant was supplied. Do not use customer passwords, copied cookies, bearer tokens, or magic links.";
29244
+ blocked.push(reasonText);
29245
+ checks.push({
29246
+ id: "support-browser-repro",
29247
+ status: "blocked",
29248
+ description: "Browser reproduction as the target user requires a short-lived audited support session.",
29249
+ reason: reasonText
29250
+ });
29251
+ }
29252
+ }
29253
+ if (input.includeLogs) {
29254
+ if (logCommand) {
29255
+ checks.push({
29256
+ id: "log-timeline",
29257
+ status: "ready",
29258
+ description: "Read sanitized app/provider logs by request ID, session ID, project ID, or support access ID.",
29259
+ command: makeCommand(logCommand)
29260
+ });
29261
+ } else {
29262
+ checks.push({
29263
+ id: "log-timeline",
29264
+ status: "blocked",
29265
+ description: "Read sanitized app/provider logs by request ID, session ID, project ID, or support access ID.",
29266
+ reason: "Configure prodDebug.apps.<profile>.logCommand or use an app-specific log MCP. Do not paste raw provider logs with headers/secrets."
29267
+ });
29268
+ }
29269
+ }
29270
+ if (input.allowWrites) {
29271
+ blocked.push("Production writes are not part of prod-debug. Require a separate explicit approval and app-specific write tool.");
29272
+ }
29273
+ return {
29274
+ target,
29275
+ app,
29276
+ actor,
29277
+ reason,
29278
+ ttlMinutes,
29279
+ setup: {
29280
+ profile: resolvedProfile.key,
29281
+ matchedOrigin: resolvedProfile.matchedOrigin,
29282
+ configured: {
29283
+ supportUrl: Boolean(supportUrl),
29284
+ supportGrant: Boolean(supportGrant.value),
29285
+ piiOrigin: Boolean(piiOrigin),
29286
+ logCommand: Boolean(logCommand)
29287
+ },
29288
+ missing: configuredMissing(resolvedProfile.profile, supportUrl, supportGrant.value, Boolean(input.includeLogs))
29289
+ },
29290
+ supportAccess: {
29291
+ required: browserRequested,
29292
+ grantId: supportGrant.display,
29293
+ browserReady: supportBrowserReady,
29294
+ note: supportBrowserReady ? "Use the provided audited support access; never print token/cookie values." : "Configure an audited support-browser/session URL, URL ref, or template before user-scoped browser debugging."
29295
+ },
29296
+ safety,
29297
+ checks,
29298
+ blocked
29299
+ };
29300
+ }
29301
+ function formatProdDebugPlan(plan) {
29302
+ const lines = [];
29303
+ lines.push(`Prod debug plan for ${plan.app}`);
29304
+ lines.push("");
29305
+ lines.push("Target");
29306
+ lines.push(`- url: ${plan.target.url ?? "(none)"}`);
29307
+ lines.push(`- org: ${plan.target.orgSlug ?? "(unknown)"}`);
29308
+ lines.push(`- project: ${plan.target.projectRef ?? "(unknown)"}`);
29309
+ lines.push(`- session: ${plan.target.sessionId ?? "(unknown)"}`);
29310
+ lines.push(`- agent: ${plan.target.agentId ?? "(unknown)"}`);
29311
+ lines.push(`- request: ${plan.target.requestId ?? "(unknown)"}`);
29312
+ lines.push("");
29313
+ lines.push("Setup");
29314
+ lines.push(`- profile: ${plan.setup.profile ?? "(none)"}`);
29315
+ lines.push(`- matched origin: ${plan.setup.matchedOrigin ?? "(none)"}`);
29316
+ if (plan.setup.missing.length > 0) {
29317
+ for (const item of plan.setup.missing)
29318
+ lines.push(`- missing: ${item}`);
29319
+ }
29320
+ lines.push("");
29321
+ lines.push("Support access");
29322
+ lines.push(`- actor: ${plan.actor}`);
29323
+ lines.push(`- reason: ${plan.reason}`);
29324
+ lines.push(`- ttl: ${plan.ttlMinutes} minutes`);
29325
+ lines.push(`- grant: ${plan.supportAccess.grantId ?? "(none)"}`);
29326
+ lines.push(`- browser ready: ${plan.supportAccess.browserReady ? "yes" : "no"}`);
29327
+ lines.push(`- note: ${plan.supportAccess.note}`);
29328
+ lines.push("");
29329
+ lines.push("Checks");
29330
+ for (const check of plan.checks) {
29331
+ lines.push(`- ${check.id}: ${check.status} - ${check.description}`);
29332
+ if (check.command)
29333
+ lines.push(` command: ${check.command}`);
29334
+ if (check.reason)
29335
+ lines.push(` blocked: ${check.reason}`);
29336
+ }
29337
+ if (plan.blocked.length > 0) {
29338
+ lines.push("");
29339
+ lines.push("Blocked");
29340
+ for (const item of plan.blocked)
29341
+ lines.push(`- ${item}`);
29342
+ }
29343
+ lines.push("");
29344
+ lines.push("Safety");
29345
+ for (const item of plan.safety)
29346
+ lines.push(`- ${item}`);
29347
+ return lines.join(`
29348
+ `);
29349
+ }
29350
+
29351
+ // src/cli/index.tsx
28968
29352
  init_personas();
28969
29353
  init_api_checks();
28970
29354
 
@@ -29737,6 +30121,29 @@ function logError(...args) {
29737
30121
  console.error(...args);
29738
30122
  }
29739
30123
  program2.name("testers").version(package_default.version).description("AI-powered browser testing CLI").option("-q, --quiet", "Suppress all output", false).option("--no-color", "Disable color output");
30124
+ program2.command("prod-debug <target>").description("Create a safe production debug plan for a URL/session/request without leaking secrets").option("--app <name>", "App name for reporting").option("--profile <name>", "prodDebug app profile from testers config").option("--actor <name>", "Operator/agent identity for support audit context").option("--reason <text>", "Debug reason or support context").option("--support-url <url>", "Audited support browser/session URL minted by the target app").option("--support-grant <id>", "Audited support access grant ID").option("--ttl <minutes>", "Support access TTL in minutes, capped at 60", "15").option("--no-browser", "Do not include user-scoped browser reproduction").option("--logs", "Include log timeline adapter requirement", false).option("--allow-writes", "Document that a separate explicit approval is required for writes", false).option("--json", "Output JSON", false).option("-o, --output <filepath>", "Write plan to file").action((target, opts) => {
30125
+ const config = loadConfig();
30126
+ const plan = createProdDebugPlan({
30127
+ target,
30128
+ app: opts.app,
30129
+ profile: opts.profile,
30130
+ actor: opts.actor,
30131
+ reason: opts.reason,
30132
+ supportUrl: opts.supportUrl,
30133
+ supportGrantId: opts.supportGrant,
30134
+ ttlMinutes: parseInt(opts.ttl, 10),
30135
+ includeBrowser: opts.browser,
30136
+ includeLogs: opts.logs,
30137
+ allowWrites: opts.allowWrites
30138
+ }, config.prodDebug);
30139
+ const output = opts.json ? JSON.stringify(plan, null, 2) : formatProdDebugPlan(plan);
30140
+ if (opts.output) {
30141
+ writeFileSync4(resolve(opts.output), output + `
30142
+ `);
30143
+ } else {
30144
+ log(output);
30145
+ }
30146
+ });
29740
30147
  var CONFIG_DIR5 = getTestersDir();
29741
30148
  var CONFIG_PATH3 = join16(CONFIG_DIR5, "config.json");
29742
30149
  function getActiveProject() {
package/dist/index.d.ts CHANGED
@@ -39,5 +39,7 @@ export type { CaptureResult } from "./lib/screenshotter.js";
39
39
  export { resolveCredential, isCredentialReference } from "./lib/secrets-resolver.js";
40
40
  export { ensurePersonaAuthenticated, loginWithAuthConfig } from "./lib/persona-auth.js";
41
41
  export type { LoginResult } from "./lib/persona-auth.js";
42
+ export { createProdDebugPlan, formatProdDebugPlan, parseProdDebugTarget, redactProdDebugText, } from "./lib/prod-debug.js";
43
+ export type { ProdDebugAppProfile, ProdDebugCheck, ProdDebugConfig, ProdDebugIdentifiers, ProdDebugInput, ProdDebugPlan, } from "./lib/prod-debug.js";
42
44
  export { generateGitHubActionsWorkflow, formatPRComment, postGitHubComment, resolvePullRequestNumber, } from "./lib/ci.js";
43
45
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}