securewithtrace 0.41.0 → 0.42.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.
Files changed (2) hide show
  1. package/dist/index.js +896 -180
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -6276,7 +6276,7 @@ var $ZodBase64 = /* @__PURE__ */ $constructor("$ZodBase64", (inst, def) => {
6276
6276
  function isValidBase64URL(data) {
6277
6277
  if (!base64url.test(data))
6278
6278
  return false;
6279
- const base643 = data.replace(/[-_]/g, (c59) => c59 === "-" ? "+" : "/");
6279
+ const base643 = data.replace(/[-_]/g, (c62) => c62 === "-" ? "+" : "/");
6280
6280
  const padded = base643.padEnd(Math.ceil(base643.length / 4) * 4, "=");
6281
6281
  return isValidBase64(padded);
6282
6282
  }
@@ -16320,9 +16320,9 @@ var ZodDate = /* @__PURE__ */ $constructor("ZodDate", (inst, def) => {
16320
16320
  inst._zod.processJSONSchema = (ctx, json2, params) => dateProcessor(inst, ctx, json2, params);
16321
16321
  inst.min = (value, params) => inst.check(_gte(value, params));
16322
16322
  inst.max = (value, params) => inst.check(_lte(value, params));
16323
- const c59 = inst._zod.bag;
16324
- inst.minDate = c59.minimum ? new Date(c59.minimum) : null;
16325
- inst.maxDate = c59.maximum ? new Date(c59.maximum) : null;
16323
+ const c62 = inst._zod.bag;
16324
+ inst.minDate = c62.minimum ? new Date(c62.minimum) : null;
16325
+ inst.maxDate = c62.maximum ? new Date(c62.maximum) : null;
16326
16326
  });
16327
16327
  function date3(params) {
16328
16328
  return _date(ZodDate, params);
@@ -21920,6 +21920,17 @@ var ContestReasonValues = [
21920
21920
  "OTHER"
21921
21921
  ];
21922
21922
  var ContestReason = external_exports.enum(ContestReasonValues);
21923
+ var PentestFindingFeedbackRatingValues = ["THUMBS_UP", "THUMBS_DOWN"];
21924
+ var PentestFindingFeedbackRating = external_exports.enum(PentestFindingFeedbackRatingValues);
21925
+ var PentestFindingFeedbackReasonValues = [
21926
+ "FALSE_POSITIVE",
21927
+ "SEVERITY_TOO_HIGH",
21928
+ "SEVERITY_TOO_LOW",
21929
+ "UNCLEAR_WRITEUP",
21930
+ "REMEDIATION_NOT_ACTIONABLE",
21931
+ "OTHER"
21932
+ ];
21933
+ var PentestFindingFeedbackReason = external_exports.enum(PentestFindingFeedbackReasonValues);
21923
21934
  var ManualRetestOutcomeValues = ["FIXED", "PARTIALLY_FIXED", "NOT_FIXED"];
21924
21935
  var ManualRetestOutcome = external_exports.enum(ManualRetestOutcomeValues);
21925
21936
  var RetestOutcomeValues = [...ManualRetestOutcomeValues, "UNABLE_TO_VERIFY"];
@@ -21998,7 +22009,7 @@ var PentestEditActionValues = [
21998
22009
  "DELIVER"
21999
22010
  ];
22000
22011
  var PentestEditAction = external_exports.enum(PentestEditActionValues);
22001
- var PentestAuditActorTypeValues = ["USER", "SYSTEM", "AGENT", "API_KEY"];
22012
+ var PentestAuditActorTypeValues = ["USER", "SYSTEM", "AGENT", "API_KEY", "STAFF"];
22002
22013
  var PentestAuditActorType = external_exports.enum(PentestAuditActorTypeValues);
22003
22014
  var PentestHypothesisStatusValues = [
22004
22015
  "DRAFTED",
@@ -22362,6 +22373,20 @@ var AgentSessionEventPayloadUrlResponseSchema = external_exports.object({
22362
22373
  /** Presigned S3 GET for the overflow payload (short TTL). */
22363
22374
  url: external_exports.url()
22364
22375
  });
22376
+ var ExportAgentSessionTranscriptResponseSchema = external_exports.object({
22377
+ /** Presigned S3 GET with an attachment Content-Disposition (short TTL). */
22378
+ downloadUrl: external_exports.url(),
22379
+ expiresAt: external_exports.coerce.date(),
22380
+ fileName: external_exports.string(),
22381
+ /** Bytes of the exported document. */
22382
+ sizeBytes: external_exports.number().int().nonnegative(),
22383
+ eventCount: external_exports.number().int().nonnegative(),
22384
+ subagentCount: external_exports.number().int().nonnegative(),
22385
+ /** High-water mark captured by the export; a live session keeps moving past it. */
22386
+ lastEventSeq: external_exports.number().int().nonnegative(),
22387
+ /** Overflow payloads whose S3 object no longer exists; the export keeps the inline preview for those. */
22388
+ missingOverflowPayloadCount: external_exports.number().int().nonnegative()
22389
+ });
22365
22390
  var SendAgentSessionMessageResponseSchema = external_exports.object({
22366
22391
  event: AgentSessionEventResponseSchema,
22367
22392
  /** True when the message relaunched a parked session. */
@@ -22507,6 +22532,21 @@ var agentSessionContract = c2.router({
22507
22532
  500: ErrorResponseSchema
22508
22533
  }
22509
22534
  },
22535
+ // POST, not GET: each call assembles the transcript from Postgres + S3 overflow
22536
+ // objects and writes a new export object before presigning it.
22537
+ exportTranscript: {
22538
+ method: "POST",
22539
+ path: "/organizations/:organizationId/pentests/:pentestId/agent-sessions/:agentSessionId/transcript-export",
22540
+ pathParams: AgentSessionIdParamsSchema,
22541
+ body: null,
22542
+ responses: {
22543
+ 200: ExportAgentSessionTranscriptResponseSchema,
22544
+ 401: ErrorResponseSchema,
22545
+ 403: ErrorResponseSchema,
22546
+ 404: ErrorResponseSchema,
22547
+ 500: ErrorResponseSchema
22548
+ }
22549
+ },
22510
22550
  sendMessage: {
22511
22551
  method: "POST",
22512
22552
  path: "/organizations/:organizationId/pentests/:pentestId/agent-sessions/:agentSessionId/messages",
@@ -23731,11 +23771,19 @@ var CustomerReportPdfResponseSchema = ReportPdfResponseSchema.omit({
23731
23771
  canViewInline: external_exports.boolean().optional(),
23732
23772
  /**
23733
23773
  * What the Download action hands the customer: the PDF alone, or one zip
23734
- * carrying the PDF, the markdown report, and the evidence files (partner
23735
- * templates whose tooling consumes the markdown). Preview always opens the
23736
- * PDF. Optional for the deploy-order reason above; consumers default to 'pdf'.
23774
+ * carrying the PDF, the Word document, the markdown report, and the evidence
23775
+ * files (partner templates whose tooling consumes the markdown). Preview
23776
+ * always opens the PDF. Optional for the deploy-order reason above; consumers
23777
+ * default to 'pdf'.
23778
+ */
23779
+ downloadFormat: CustomerReportDownloadFormatSchema.optional(),
23780
+ /**
23781
+ * Whether a Word document (.docx) from the same render as the served PDF is
23782
+ * available through `downloadCustomer?artifact=docx`. Partner templates only;
23783
+ * resolved server-side from storage, never from the template alone. Optional
23784
+ * for the deploy-order reason above; consumers default to false.
23737
23785
  */
23738
- downloadFormat: CustomerReportDownloadFormatSchema.optional()
23786
+ docxAvailable: external_exports.boolean().optional()
23739
23787
  });
23740
23788
  var GetCustomerReportPdfResponseSchema = external_exports.object({
23741
23789
  report: CustomerReportPdfResponseSchema.nullable()
@@ -23749,8 +23797,13 @@ var DeleteReportPdfRequestSchema = external_exports.object({
23749
23797
  var DeleteReportIssueRequestSchema = external_exports.object({
23750
23798
  reason: external_exports.string().min(1).max(1e4).optional()
23751
23799
  }).strict().optional();
23800
+ var ReportPdfDownloadArtifactValues = ["pdf", "markdown", "docx"];
23801
+ var ReportPdfDownloadArtifactSchema = external_exports.enum(ReportPdfDownloadArtifactValues);
23752
23802
  var ReportPdfDownloadQuerySchema = external_exports.object({
23753
- artifact: external_exports.enum(["pdf", "markdown"]).optional()
23803
+ artifact: ReportPdfDownloadArtifactSchema.optional()
23804
+ });
23805
+ var CustomerReportPdfDownloadQuerySchema = PentestPdfDownloadQuerySchema.extend({
23806
+ artifact: external_exports.enum(["docx"]).optional()
23754
23807
  });
23755
23808
  var ReportPdfDownloadResponseSchema = external_exports.object({
23756
23809
  report: ReportPdfResponseSchema,
@@ -24318,6 +24371,10 @@ var OperatorFindingListSchema = PentestFindingListItemSchema.omit({
24318
24371
  var CustomerPentestFindingRepositorySchema = PentestFindingRepositorySchema.omit({
24319
24372
  repositoryId: true
24320
24373
  });
24374
+ var CustomerFindingPendingRetestSchema = external_exports.object({
24375
+ state: external_exports.enum(["QUEUED", "RUNNING"]),
24376
+ since: external_exports.coerce.date()
24377
+ }).nullable().default(null);
24321
24378
  var CustomerPentestFindingResponseSchema = PentestFindingResponseSchema.omit({
24322
24379
  organizationId: true,
24323
24380
  pentestId: true,
@@ -24341,7 +24398,8 @@ var CustomerPentestFindingResponseSchema = PentestFindingResponseSchema.omit({
24341
24398
  // customer "dispute under review" hint + suppresses the re-dispute affordance,
24342
24399
  // even after a retest moved the finding off CONTESTED. Only this derived
24343
24400
  // boolean crosses the trust boundary — never the staff contest list.
24344
- hasOpenContest: external_exports.boolean()
24401
+ hasOpenContest: external_exports.boolean(),
24402
+ pendingRetest: CustomerFindingPendingRetestSchema
24345
24403
  });
24346
24404
  var CustomerPentestFindingListItemSchema = PentestFindingListItemSchema.omit({
24347
24405
  organizationId: true,
@@ -24356,7 +24414,10 @@ var CustomerPentestFindingListItemSchema = PentestFindingListItemSchema.omit({
24356
24414
  // Published-only, same as the customer detail schema above — re-narrowed from the
24357
24415
  // nullable root.
24358
24416
  displayId: external_exports.number().int().positive(),
24359
- repositories: external_exports.array(CustomerPentestFindingRepositorySchema)
24417
+ repositories: external_exports.array(CustomerPentestFindingRepositorySchema),
24418
+ // On the list row too: the findings table is what the customer watches while a
24419
+ // retest they queued runs, and polling it is how the row flips without a reload.
24420
+ pendingRetest: CustomerFindingPendingRetestSchema
24360
24421
  });
24361
24422
  var ListCustomerPentestFindingsResponseSchema = external_exports.array(
24362
24423
  CustomerPentestFindingListItemSchema
@@ -24365,6 +24426,7 @@ var ListCustomerPentestFindingDeliveryResponseSchema = external_exports.array(
24365
24426
  CustomerPentestFindingListItemSchema
24366
24427
  );
24367
24428
  var ResolutionTimelineActor = external_exports.enum(["CUSTOMER", "TRACE"]);
24429
+ var ResolutionTimelineActorName = external_exports.string().nullable().default(null);
24368
24430
  var ResolutionTimelineVia = external_exports.object({
24369
24431
  provider: external_exports.enum(["LINEAR"]),
24370
24432
  identifier: external_exports.string(),
@@ -24375,12 +24437,17 @@ var ResolutionTimelineEntrySchema = external_exports.discriminatedUnion("kind",
24375
24437
  kind: external_exports.literal("remediation_reported"),
24376
24438
  at: external_exports.coerce.date(),
24377
24439
  actor: ResolutionTimelineActor,
24440
+ actorName: ResolutionTimelineActorName,
24378
24441
  note: external_exports.string().nullable(),
24379
24442
  via: ResolutionTimelineVia
24380
24443
  }),
24444
+ // Retests are run by Trace (staff or a Trace agent), so the actor is fixed;
24445
+ // the default keeps a web build ahead of the api parsing older payloads.
24381
24446
  external_exports.object({
24382
24447
  kind: external_exports.literal("retest"),
24383
24448
  at: external_exports.coerce.date(),
24449
+ actor: external_exports.literal("TRACE").default("TRACE"),
24450
+ actorName: ResolutionTimelineActorName,
24384
24451
  outcome: RetestOutcome,
24385
24452
  note: external_exports.string().nullable()
24386
24453
  }),
@@ -24388,12 +24455,14 @@ var ResolutionTimelineEntrySchema = external_exports.discriminatedUnion("kind",
24388
24455
  kind: external_exports.literal("accepted_with_controls"),
24389
24456
  at: external_exports.coerce.date(),
24390
24457
  actor: ResolutionTimelineActor,
24458
+ actorName: ResolutionTimelineActorName,
24391
24459
  note: external_exports.string()
24392
24460
  }),
24393
24461
  external_exports.object({
24394
24462
  kind: external_exports.literal("dismissed"),
24395
24463
  at: external_exports.coerce.date(),
24396
24464
  actor: ResolutionTimelineActor,
24465
+ actorName: ResolutionTimelineActorName,
24397
24466
  reason: PentestFindingDismissReason,
24398
24467
  note: external_exports.string()
24399
24468
  }),
@@ -24403,6 +24472,7 @@ var ResolutionTimelineEntrySchema = external_exports.discriminatedUnion("kind",
24403
24472
  kind: external_exports.literal("reopened"),
24404
24473
  at: external_exports.coerce.date(),
24405
24474
  actor: ResolutionTimelineActor,
24475
+ actorName: ResolutionTimelineActorName,
24406
24476
  toStatus: external_exports.enum(["OPEN", "IN_PROGRESS"]),
24407
24477
  note: external_exports.string().nullable(),
24408
24478
  via: ResolutionTimelineVia
@@ -24412,6 +24482,7 @@ var ResolutionTimelineEntrySchema = external_exports.discriminatedUnion("kind",
24412
24482
  kind: external_exports.literal("note"),
24413
24483
  at: external_exports.coerce.date(),
24414
24484
  actor: ResolutionTimelineActor,
24485
+ actorName: ResolutionTimelineActorName,
24415
24486
  note: external_exports.string()
24416
24487
  }),
24417
24488
  // Customer opened a dispute (PR 3). `note` = the customer's justification.
@@ -24419,6 +24490,7 @@ var ResolutionTimelineEntrySchema = external_exports.discriminatedUnion("kind",
24419
24490
  kind: external_exports.literal("contested"),
24420
24491
  at: external_exports.coerce.date(),
24421
24492
  actor: ResolutionTimelineActor,
24493
+ actorName: ResolutionTimelineActorName,
24422
24494
  reason: ContestReason,
24423
24495
  note: external_exports.string()
24424
24496
  }),
@@ -24429,6 +24501,7 @@ var ResolutionTimelineEntrySchema = external_exports.discriminatedUnion("kind",
24429
24501
  kind: external_exports.literal("contest_resolved"),
24430
24502
  at: external_exports.coerce.date(),
24431
24503
  actor: ResolutionTimelineActor,
24504
+ actorName: ResolutionTimelineActorName,
24432
24505
  resultStatus: PentestFindingStatus.nullable(),
24433
24506
  note: external_exports.string()
24434
24507
  }),
@@ -24438,22 +24511,61 @@ var ResolutionTimelineEntrySchema = external_exports.discriminatedUnion("kind",
24438
24511
  kind: external_exports.literal("ticket_created"),
24439
24512
  at: external_exports.coerce.date(),
24440
24513
  actor: ResolutionTimelineActor,
24514
+ actorName: ResolutionTimelineActorName,
24441
24515
  provider: external_exports.enum(["LINEAR"]),
24442
24516
  identifier: external_exports.string(),
24443
24517
  url: external_exports.url()
24518
+ }),
24519
+ // A reader rated the finding. `reason` is set only on a thumbs down; `note` is
24520
+ // the optional comment left with the vote.
24521
+ external_exports.object({
24522
+ kind: external_exports.literal("feedback_rated"),
24523
+ at: external_exports.coerce.date(),
24524
+ actor: ResolutionTimelineActor,
24525
+ actorName: ResolutionTimelineActorName,
24526
+ rating: PentestFindingFeedbackRating,
24527
+ reason: PentestFindingFeedbackReason.nullable(),
24528
+ note: external_exports.string().nullable()
24529
+ }),
24530
+ // A reader left a comment without a vote.
24531
+ external_exports.object({
24532
+ kind: external_exports.literal("feedback_commented"),
24533
+ at: external_exports.coerce.date(),
24534
+ actor: ResolutionTimelineActor,
24535
+ actorName: ResolutionTimelineActorName,
24536
+ note: external_exports.string()
24444
24537
  })
24445
24538
  ]);
24446
24539
  var ResolutionTimelineSchema = external_exports.array(ResolutionTimelineEntrySchema);
24540
+ var PentestFindingFeedbackSummarySchema = external_exports.object({
24541
+ thumbsUp: external_exports.number().int().nonnegative(),
24542
+ thumbsDown: external_exports.number().int().nonnegative(),
24543
+ viewerRating: PentestFindingFeedbackRating.nullable()
24544
+ });
24545
+ var PentestFindingFeedbackSummaryField = PentestFindingFeedbackSummarySchema.default({
24546
+ thumbsUp: 0,
24547
+ thumbsDown: 0,
24548
+ viewerRating: null
24549
+ });
24447
24550
  var PentestFindingDetailResponseSchema = PentestFindingResponseSchema.extend({
24448
- resolutionTimeline: ResolutionTimelineSchema
24551
+ resolutionTimeline: ResolutionTimelineSchema,
24552
+ feedbackSummary: PentestFindingFeedbackSummaryField
24449
24553
  });
24450
24554
  var CustomerPentestFindingDetailResponseSchema = CustomerPentestFindingResponseSchema.extend({
24451
- resolutionTimeline: ResolutionTimelineSchema
24555
+ resolutionTimeline: ResolutionTimelineSchema,
24556
+ feedbackSummary: PentestFindingFeedbackSummaryField
24452
24557
  });
24453
24558
  var ReadPentestFindingResponseSchema = external_exports.union([
24454
24559
  PentestFindingDetailResponseSchema,
24455
24560
  CustomerPentestFindingDetailResponseSchema
24456
24561
  ]);
24562
+ var PentestFindingPdfExportResponseSchema = external_exports.object({
24563
+ /** Download name the presigned URL carries, e.g. `PTF-12-idor-on-invoices.pdf`. */
24564
+ fileName: external_exports.string(),
24565
+ downloadUrl: external_exports.url(),
24566
+ /** False when an identical export already existed and was reused without a render. */
24567
+ rendered: external_exports.boolean()
24568
+ });
24457
24569
  var ListReadPentestFindingsResponseSchema = external_exports.union([
24458
24570
  ListPentestFindingsResponseSchema,
24459
24571
  ListCustomerPentestFindingsResponseSchema
@@ -24644,6 +24756,18 @@ var ResolvePentestFindingContestRequestSchema = external_exports.object({
24644
24756
  dismissReason: PentestFindingDismissReason.optional(),
24645
24757
  note: external_exports.string().min(1).max(1e4)
24646
24758
  });
24759
+ var PENTEST_FINDING_FEEDBACK_COMMENT_MAX_LENGTH = 5e3;
24760
+ var CreatePentestFindingFeedbackRequestSchema = external_exports.object({
24761
+ rating: PentestFindingFeedbackRating.nullable().default(null),
24762
+ reason: PentestFindingFeedbackReason.nullable().default(null),
24763
+ comment: external_exports.string().trim().max(PENTEST_FINDING_FEEDBACK_COMMENT_MAX_LENGTH).transform((value) => value.length === 0 ? null : value).nullable().default(null)
24764
+ }).refine((input) => input.rating !== null || input.comment !== null, {
24765
+ message: "Feedback needs a rating or a comment",
24766
+ path: ["comment"]
24767
+ }).refine((input) => input.reason === null || input.rating === "THUMBS_DOWN", {
24768
+ message: "A reason only accompanies a thumbs down",
24769
+ path: ["reason"]
24770
+ });
24647
24771
  var PentestFindingRetestResponseSchema = external_exports.object({
24648
24772
  retestId: external_exports.uuid(),
24649
24773
  pentestFindingId: external_exports.uuid(),
@@ -25220,7 +25344,8 @@ var CredentialTypeValues = [
25220
25344
  "USERNAME_PASSWORD",
25221
25345
  "COOKIE",
25222
25346
  "MAGIC_AUTH",
25223
- "SESSION_STATE"
25347
+ "SESSION_STATE",
25348
+ "SSH_PRIVATE_KEY"
25224
25349
  ];
25225
25350
  var CredentialType = external_exports.enum(CredentialTypeValues);
25226
25351
  var BearerAuthConfigSchema = external_exports.object({
@@ -25318,17 +25443,94 @@ var StorageStateSchema = external_exports.object({
25318
25443
  } catch {
25319
25444
  return;
25320
25445
  }
25321
- s.cookies.forEach((c59, i2) => {
25322
- const d = c59.domain.replace(/^\./, "").toLowerCase();
25446
+ s.cookies.forEach((c62, i2) => {
25447
+ const d = c62.domain.replace(/^\./, "").toLowerCase();
25323
25448
  if (d !== host && !host.endsWith(`.${d}`)) {
25324
25449
  ctx.addIssue({
25325
25450
  code: "custom",
25326
25451
  path: ["cookies", i2, "domain"],
25327
- message: `cookie domain "${c59.domain}" is not the capture origin host "${host}" or a parent domain of it`
25452
+ message: `cookie domain "${c62.domain}" is not the capture origin host "${host}" or a parent domain of it`
25328
25453
  });
25329
25454
  }
25330
25455
  });
25331
25456
  });
25457
+ var SSH_DEFAULT_PORT = 22;
25458
+ var SSH_HOST_MAX_CHARS = 253;
25459
+ var SSH_USERNAME_MAX_CHARS = 128;
25460
+ var SSH_PRIVATE_KEY_PEM_MAX_BYTES = 16384;
25461
+ var SSH_PASSPHRASE_MAX_CHARS = 1024;
25462
+ var SSH_HOST_PATTERN = /^[A-Za-z0-9][A-Za-z0-9.:-]*$/;
25463
+ var SSH_USERNAME_PATTERN = /^[^\s\p{Cc}]+$/u;
25464
+ var SSH_HOST_KEY_FINGERPRINT_PATTERN = /^SHA256:[A-Za-z0-9+/]{43}=?$/;
25465
+ var SshPrivateKeyAuthConfigSchema = external_exports.object({
25466
+ type: external_exports.literal("SSH_PRIVATE_KEY"),
25467
+ host: external_exports.string().min(1, "host is required").max(SSH_HOST_MAX_CHARS).regex(SSH_HOST_PATTERN, "host must be a hostname or IP address"),
25468
+ port: external_exports.number().int().min(1).max(65535).default(SSH_DEFAULT_PORT),
25469
+ username: external_exports.string().min(1, "username is required").max(SSH_USERNAME_MAX_CHARS).regex(SSH_USERNAME_PATTERN, "username must not contain whitespace or control characters"),
25470
+ hostKeyFingerprint: external_exports.string().regex(SSH_HOST_KEY_FINGERPRINT_PATTERN, "host key fingerprint must be SHA256:<base64>").optional()
25471
+ }).strict();
25472
+ var SSH_PRIVATE_KEY_PEM_LABELS = [
25473
+ "OPENSSH PRIVATE KEY",
25474
+ "RSA PRIVATE KEY",
25475
+ "EC PRIVATE KEY",
25476
+ "PRIVATE KEY",
25477
+ "ENCRYPTED PRIVATE KEY"
25478
+ ];
25479
+ var PEM_BEGIN_LINE = /^-----BEGIN ([A-Z ]+)-----$/;
25480
+ var PEM_END_LINE = /^-----END ([A-Z ]+)-----$/;
25481
+ var PEM_BASE64_LINE = /^[A-Za-z0-9+/]+={0,2}$/;
25482
+ var PEM_HEADER_LINE = /^[A-Za-z0-9-]+: [\x20-\x7e]*$/;
25483
+ function sshPrivateKeyPemError(value) {
25484
+ if (new TextEncoder().encode(value).length > SSH_PRIVATE_KEY_PEM_MAX_BYTES) {
25485
+ return `private key must be at most ${SSH_PRIVATE_KEY_PEM_MAX_BYTES} bytes`;
25486
+ }
25487
+ if (/[^\P{Cc}\r\n]/u.test(value)) {
25488
+ return "private key contains control characters";
25489
+ }
25490
+ const lines = value.replace(/\r\n/g, "\n").split("\n").map((line) => line.trimEnd());
25491
+ while (lines.length > 0 && lines[0] === "") lines.shift();
25492
+ while (lines.length > 0 && lines[lines.length - 1] === "") lines.pop();
25493
+ if (lines.length < 3) {
25494
+ return "private key must be a PEM block with BEGIN and END lines";
25495
+ }
25496
+ const begin = PEM_BEGIN_LINE.exec(lines[0] ?? "");
25497
+ if (begin === null) {
25498
+ return "private key must start with a -----BEGIN \u2026 PRIVATE KEY----- line";
25499
+ }
25500
+ const end = PEM_END_LINE.exec(lines[lines.length - 1] ?? "");
25501
+ if (end === null) {
25502
+ return "private key must end with a -----END \u2026 PRIVATE KEY----- line";
25503
+ }
25504
+ const label = begin[1];
25505
+ if (!SSH_PRIVATE_KEY_PEM_LABELS.includes(label ?? "")) {
25506
+ return `private key label must be one of: ${SSH_PRIVATE_KEY_PEM_LABELS.join(", ")}`;
25507
+ }
25508
+ if (end[1] !== label) {
25509
+ return "private key BEGIN and END labels do not match";
25510
+ }
25511
+ let sawBody = false;
25512
+ for (const line of lines.slice(1, -1)) {
25513
+ if (line === "") continue;
25514
+ if (PEM_BASE64_LINE.test(line)) {
25515
+ sawBody = true;
25516
+ continue;
25517
+ }
25518
+ if (PEM_HEADER_LINE.test(line) && !sawBody) continue;
25519
+ return "private key body must be base64 lines";
25520
+ }
25521
+ if (!sawBody) {
25522
+ return "private key body is empty";
25523
+ }
25524
+ return null;
25525
+ }
25526
+ var SshPrivateKeyPemSchema = external_exports.string().min(1, "private key is required").superRefine((value, ctx) => {
25527
+ const message = sshPrivateKeyPemError(value);
25528
+ if (message !== null) ctx.addIssue({ code: "custom", message });
25529
+ });
25530
+ var SshPrivateKeySecretSchema = external_exports.object({
25531
+ privateKeyPem: SshPrivateKeyPemSchema,
25532
+ passphrase: external_exports.string().min(1).max(SSH_PASSPHRASE_MAX_CHARS).optional()
25533
+ }).strict();
25332
25534
  var CredentialMfaKindValues = ["TOTP"];
25333
25535
  var CredentialMfaKind = external_exports.enum(CredentialMfaKindValues);
25334
25536
  var TotpSetupKeySchema = external_exports.string().min(1).max(TOTP_MAX_SECRET_CHARS * 4).superRefine((value, ctx) => {
@@ -25362,7 +25564,8 @@ var CredentialAuthConfigSchema = external_exports.discriminatedUnion("type", [
25362
25564
  UsernamePasswordAuthConfigSchema,
25363
25565
  PassphraseAuthConfigSchema,
25364
25566
  MagicAuthConfigSchema,
25365
- SessionStateAuthConfigSchema
25567
+ SessionStateAuthConfigSchema,
25568
+ SshPrivateKeyAuthConfigSchema
25366
25569
  ]);
25367
25570
 
25368
25571
  // ../../packages/api-contracts/src/schemas/domain/knowledge-fact.schema.ts
@@ -29716,7 +29919,13 @@ var SECRET_SHAPES = {
29716
29919
  username: external_exports.string().min(1).max(500),
29717
29920
  password: external_exports.string().min(1).max(2048)
29718
29921
  },
29719
- SESSION_STATE: { secretValue: SessionStateSecretValueSchema }
29922
+ SESSION_STATE: { secretValue: SessionStateSecretValueSchema },
29923
+ // The key and its passphrase are one secret: they are encrypted together and
29924
+ // replaced together (see SshPrivateKeySecretSchema for the stored shape).
29925
+ SSH_PRIVATE_KEY: {
29926
+ privateKeyPem: SshPrivateKeyPemSchema,
29927
+ passphrase: external_exports.string().min(1).max(SSH_PASSPHRASE_MAX_CHARS).optional()
29928
+ }
29720
29929
  };
29721
29930
  var CreateLabelSchema = external_exports.string().max(200).optional();
29722
29931
  var CreateBearerSchema = external_exports.object({
@@ -29797,6 +30006,12 @@ var CreateSessionStateSchema = external_exports.object({
29797
30006
  label: CreateLabelSchema,
29798
30007
  ...SECRET_SHAPES.SESSION_STATE
29799
30008
  }).strict();
30009
+ var CreateSshPrivateKeySchema = external_exports.object({
30010
+ credentialType: external_exports.literal("SSH_PRIVATE_KEY"),
30011
+ label: CreateLabelSchema,
30012
+ ...SECRET_SHAPES.SSH_PRIVATE_KEY,
30013
+ authConfig: SshPrivateKeyAuthConfigSchema
30014
+ }).strict();
29800
30015
  var CreateCredentialRequestSchema = external_exports.discriminatedUnion("credentialType", [
29801
30016
  CreateBearerSchema,
29802
30017
  CreateJwtSchema,
@@ -29805,7 +30020,8 @@ var CreateCredentialRequestSchema = external_exports.discriminatedUnion("credent
29805
30020
  CreateUsernamePasswordSchema,
29806
30021
  CreateCookieSchema,
29807
30022
  CreateMagicAuthSchema,
29808
- CreateSessionStateSchema
30023
+ CreateSessionStateSchema,
30024
+ CreateSshPrivateKeySchema
29809
30025
  ]);
29810
30026
  var CredentialResponseSchema = external_exports.object({
29811
30027
  credentialId: external_exports.uuid(),
@@ -29848,7 +30064,8 @@ var RotateCredentialSecretRequestSchema = external_exports.discriminatedUnion("c
29848
30064
  credentialType: external_exports.literal("USERNAME_PASSWORD"),
29849
30065
  ...SECRET_SHAPES.USERNAME_PASSWORD
29850
30066
  }).strict(),
29851
- external_exports.object({ credentialType: external_exports.literal("SESSION_STATE"), ...SECRET_SHAPES.SESSION_STATE }).strict()
30067
+ external_exports.object({ credentialType: external_exports.literal("SESSION_STATE"), ...SECRET_SHAPES.SESSION_STATE }).strict(),
30068
+ external_exports.object({ credentialType: external_exports.literal("SSH_PRIVATE_KEY"), ...SECRET_SHAPES.SSH_PRIVATE_KEY }).strict()
29852
30069
  ]);
29853
30070
  var SetCredentialMfaRequestSchema = external_exports.object({
29854
30071
  kind: external_exports.literal("TOTP"),
@@ -31200,6 +31417,159 @@ var inboxContract = c19.router({
31200
31417
  }
31201
31418
  });
31202
31419
 
31420
+ // ../../packages/api-contracts/src/schemas/api/v1/member.api.ts
31421
+ var MemberOrgParamsSchema = OrgIdParamsSchema;
31422
+ var MemberRoleSlugSchema = external_exports.enum(["admin", "analyst", "developer"]);
31423
+ var CreateMemberInvitationRequestSchema = external_exports.object({
31424
+ email: external_exports.email().max(320),
31425
+ roleSlug: MemberRoleSlugSchema.default("developer")
31426
+ });
31427
+ var CreateMemberInvitationResponseSchema = external_exports.object({
31428
+ invitationId: external_exports.string().min(1),
31429
+ email: external_exports.email(),
31430
+ roleSlug: MemberRoleSlugSchema
31431
+ });
31432
+
31433
+ // ../../packages/api-contracts/src/schemas/api/v1/invite-link.api.ts
31434
+ var INVITE_LINK_MIN_EXPIRES_IN_HOURS = 1;
31435
+ var INVITE_LINK_MAX_EXPIRES_IN_HOURS = 24 * 30;
31436
+ var INVITE_LINK_DEFAULT_EXPIRES_IN_HOURS = 24 * 7;
31437
+ var INVITE_LINK_MIN_USES = 1;
31438
+ var INVITE_LINK_MAX_USES = 100;
31439
+ var INVITE_LINK_DEFAULT_MAX_USES = 1;
31440
+ var InviteLinkOrgParamsSchema = OrgIdParamsSchema;
31441
+ var InviteLinkParamsSchema = OrgIdParamsSchema.extend({
31442
+ inviteLinkId: external_exports.uuid()
31443
+ });
31444
+ var InviteLinkTokenSchema = external_exports.string().min(32).max(128).regex(/^[A-Za-z0-9_-]+$/, {
31445
+ message: "token must contain only URL-safe base64 characters"
31446
+ });
31447
+ var InviteLinkTokenParamsSchema = external_exports.object({
31448
+ token: InviteLinkTokenSchema
31449
+ });
31450
+ var CreateInviteLinkRequestSchema = external_exports.object({
31451
+ roleSlug: MemberRoleSlugSchema.default("developer"),
31452
+ expiresInHours: external_exports.number().int().min(INVITE_LINK_MIN_EXPIRES_IN_HOURS).max(INVITE_LINK_MAX_EXPIRES_IN_HOURS).default(INVITE_LINK_DEFAULT_EXPIRES_IN_HOURS),
31453
+ maxUses: external_exports.number().int().min(INVITE_LINK_MIN_USES).max(INVITE_LINK_MAX_USES).default(INVITE_LINK_DEFAULT_MAX_USES)
31454
+ });
31455
+ var InviteLinkStatusSchema = external_exports.enum(["active", "expired", "exhausted", "revoked"]);
31456
+ var InviteLinkResponseSchema = external_exports.object({
31457
+ inviteLinkId: external_exports.uuid(),
31458
+ organizationId: external_exports.uuid(),
31459
+ roleSlug: MemberRoleSlugSchema,
31460
+ status: InviteLinkStatusSchema,
31461
+ expiresAt: external_exports.coerce.date(),
31462
+ maxUses: external_exports.number().int(),
31463
+ useCount: external_exports.number().int(),
31464
+ createdById: external_exports.uuid(),
31465
+ createdByName: external_exports.string(),
31466
+ createdByEmail: external_exports.string(),
31467
+ revokedAt: external_exports.coerce.date().nullable(),
31468
+ createdAt: external_exports.coerce.date()
31469
+ });
31470
+ var CreateInviteLinkResponseSchema = InviteLinkResponseSchema.extend({
31471
+ url: external_exports.url()
31472
+ });
31473
+ var ListInviteLinksResponseSchema = external_exports.array(InviteLinkResponseSchema);
31474
+ var RevokeInviteLinkResponseSchema = external_exports.object({
31475
+ inviteLinkId: external_exports.uuid(),
31476
+ revokedAt: external_exports.coerce.date()
31477
+ });
31478
+ var InviteLinkPreviewResponseSchema = external_exports.object({
31479
+ status: InviteLinkStatusSchema,
31480
+ organizationName: external_exports.string(),
31481
+ organizationLogoUrl: external_exports.string().nullable(),
31482
+ roleSlug: MemberRoleSlugSchema,
31483
+ expiresAt: external_exports.coerce.date()
31484
+ });
31485
+ var AcceptInviteLinkResponseSchema = external_exports.object({
31486
+ /** `joined` created a membership; `already_member` found an active one and consumed nothing. */
31487
+ outcome: external_exports.enum(["joined", "already_member"]),
31488
+ organizationSlug: external_exports.string(),
31489
+ organizationName: external_exports.string(),
31490
+ workosOrganizationId: external_exports.string(),
31491
+ roleSlug: MemberRoleSlugSchema
31492
+ });
31493
+ var AcceptInviteLinkRejectedResponseSchema = ErrorResponseSchema.extend({
31494
+ status: InviteLinkStatusSchema.optional()
31495
+ });
31496
+
31497
+ // ../../packages/api-contracts/src/contracts/v1/invite-link.ts
31498
+ var c20 = initContract();
31499
+ var inviteLinkContract = c20.router({
31500
+ createInviteLink: {
31501
+ method: "POST",
31502
+ path: "/organizations/:organizationId/invite-links",
31503
+ pathParams: InviteLinkOrgParamsSchema,
31504
+ body: CreateInviteLinkRequestSchema,
31505
+ responses: {
31506
+ 201: CreateInviteLinkResponseSchema,
31507
+ 400: ErrorResponseSchema,
31508
+ 401: ErrorResponseSchema,
31509
+ 403: ErrorResponseSchema,
31510
+ 404: ErrorResponseSchema,
31511
+ 500: ErrorResponseSchema
31512
+ }
31513
+ },
31514
+ listInviteLinks: {
31515
+ method: "GET",
31516
+ path: "/organizations/:organizationId/invite-links",
31517
+ pathParams: InviteLinkOrgParamsSchema,
31518
+ responses: {
31519
+ 200: ListInviteLinksResponseSchema,
31520
+ 401: ErrorResponseSchema,
31521
+ 403: ErrorResponseSchema,
31522
+ 404: ErrorResponseSchema,
31523
+ 500: ErrorResponseSchema
31524
+ }
31525
+ },
31526
+ revokeInviteLink: {
31527
+ method: "DELETE",
31528
+ path: "/organizations/:organizationId/invite-links/:inviteLinkId",
31529
+ pathParams: InviteLinkParamsSchema,
31530
+ responses: {
31531
+ 200: RevokeInviteLinkResponseSchema,
31532
+ 401: ErrorResponseSchema,
31533
+ 403: ErrorResponseSchema,
31534
+ 404: ErrorResponseSchema,
31535
+ 500: ErrorResponseSchema
31536
+ }
31537
+ }
31538
+ });
31539
+ var inviteLinkPublicContract = c20.router(
31540
+ {
31541
+ previewInviteLink: {
31542
+ method: "GET",
31543
+ path: "/invite-links/:token",
31544
+ pathParams: InviteLinkTokenParamsSchema,
31545
+ responses: {
31546
+ 200: InviteLinkPreviewResponseSchema,
31547
+ 400: ErrorResponseSchema,
31548
+ 404: ErrorResponseSchema,
31549
+ 500: ErrorResponseSchema
31550
+ }
31551
+ },
31552
+ acceptInviteLink: {
31553
+ method: "POST",
31554
+ path: "/invite-links/:token/accept",
31555
+ pathParams: InviteLinkTokenParamsSchema,
31556
+ body: c20.noBody(),
31557
+ responses: {
31558
+ 200: AcceptInviteLinkResponseSchema,
31559
+ 400: ErrorResponseSchema,
31560
+ 401: ErrorResponseSchema,
31561
+ 404: ErrorResponseSchema,
31562
+ 409: AcceptInviteLinkRejectedResponseSchema,
31563
+ 503: ErrorResponseSchema,
31564
+ 500: ErrorResponseSchema
31565
+ }
31566
+ }
31567
+ },
31568
+ {
31569
+ pathPrefix: "/v1"
31570
+ }
31571
+ );
31572
+
31203
31573
  // ../../packages/api-contracts/src/schemas/api/v1/knowledge-base.api.ts
31204
31574
  var KnowledgeBaseAuthorSchema = external_exports.object({
31205
31575
  userId: external_exports.string(),
@@ -31231,8 +31601,8 @@ var UpdateKnowledgeBaseResponseSchema = external_exports.object({
31231
31601
  });
31232
31602
 
31233
31603
  // ../../packages/api-contracts/src/contracts/v1/knowledge-base.ts
31234
- var c20 = initContract();
31235
- var knowledgeBaseContract = c20.router({
31604
+ var c21 = initContract();
31605
+ var knowledgeBaseContract = c21.router({
31236
31606
  getKnowledgeBase: {
31237
31607
  method: "GET",
31238
31608
  path: "/organizations/:organizationId/knowledge-base",
@@ -31453,8 +31823,8 @@ var UpdateRepoDocumentContentResponseSchema = external_exports.object({
31453
31823
  });
31454
31824
 
31455
31825
  // ../../packages/api-contracts/src/contracts/v1/knowledge-document.ts
31456
- var c21 = initContract();
31457
- var knowledgeDocumentContract = c21.router({
31826
+ var c22 = initContract();
31827
+ var knowledgeDocumentContract = c22.router({
31458
31828
  createDocument: {
31459
31829
  method: "POST",
31460
31830
  path: "/organizations/:organizationId/knowledge-documents",
@@ -31780,8 +32150,8 @@ var LinearMetadataResponseSchema = external_exports.object({
31780
32150
  });
31781
32151
 
31782
32152
  // ../../packages/api-contracts/src/contracts/v1/linear-integration.ts
31783
- var c22 = initContract();
31784
- var linearIntegrationContract = c22.router({
32153
+ var c23 = initContract();
32154
+ var linearIntegrationContract = c23.router({
31785
32155
  getLinearIntegration: {
31786
32156
  method: "GET",
31787
32157
  path: "/organizations/:organizationId/integrations/linear",
@@ -31799,7 +32169,7 @@ var linearIntegrationContract = c22.router({
31799
32169
  method: "POST",
31800
32170
  path: "/organizations/:organizationId/integrations/linear/connect",
31801
32171
  pathParams: OrgIdParamsSchema,
31802
- body: c22.noBody(),
32172
+ body: c23.noBody(),
31803
32173
  responses: {
31804
32174
  200: LinearConnectResponseSchema,
31805
32175
  401: ErrorResponseSchema,
@@ -31814,7 +32184,7 @@ var linearIntegrationContract = c22.router({
31814
32184
  method: "POST",
31815
32185
  path: "/organizations/:organizationId/integrations/linear/disconnect",
31816
32186
  pathParams: OrgIdParamsSchema,
31817
- body: c22.noBody(),
32187
+ body: c23.noBody(),
31818
32188
  responses: {
31819
32189
  200: LinearIntegrationResponseSchema,
31820
32190
  401: ErrorResponseSchema,
@@ -31876,8 +32246,8 @@ var linearIntegrationContract = c22.router({
31876
32246
  });
31877
32247
 
31878
32248
  // ../../packages/api-contracts/src/contracts/v1/linear-tickets.ts
31879
- var c23 = initContract();
31880
- var linearTicketContract = c23.router({
32249
+ var c24 = initContract();
32250
+ var linearTicketContract = c24.router({
31881
32251
  /**
31882
32252
  * Push one or more pentest findings to Linear as tickets. Customer-side
31883
32253
  * (VIEW_RESULTS + PENTEST capability); non-admins create into the org's
@@ -31981,8 +32351,8 @@ var LlmProviderKeyResponseSchema = external_exports.object({
31981
32351
  var ListLlmProviderKeysResponseSchema = external_exports.array(LlmProviderKeyResponseSchema);
31982
32352
 
31983
32353
  // ../../packages/api-contracts/src/contracts/v1/llm-provider-key.ts
31984
- var c24 = initContract();
31985
- var llmProviderKeyContract = c24.router({
32354
+ var c25 = initContract();
32355
+ var llmProviderKeyContract = c25.router({
31986
32356
  createLlmProviderKey: {
31987
32357
  method: "POST",
31988
32358
  path: "/organizations/:organizationId/llm-provider-keys",
@@ -32049,22 +32419,9 @@ var llmProviderKeyContract = c24.router({
32049
32419
  }
32050
32420
  });
32051
32421
 
32052
- // ../../packages/api-contracts/src/schemas/api/v1/member.api.ts
32053
- var MemberOrgParamsSchema = OrgIdParamsSchema;
32054
- var MemberRoleSlugSchema = external_exports.enum(["admin", "analyst", "developer"]);
32055
- var CreateMemberInvitationRequestSchema = external_exports.object({
32056
- email: external_exports.email().max(320),
32057
- roleSlug: MemberRoleSlugSchema.default("developer")
32058
- });
32059
- var CreateMemberInvitationResponseSchema = external_exports.object({
32060
- invitationId: external_exports.string().min(1),
32061
- email: external_exports.email(),
32062
- roleSlug: MemberRoleSlugSchema
32063
- });
32064
-
32065
32422
  // ../../packages/api-contracts/src/contracts/v1/member.ts
32066
- var c25 = initContract();
32067
- var memberContract = c25.router({
32423
+ var c26 = initContract();
32424
+ var memberContract = c26.router({
32068
32425
  createInvitation: {
32069
32426
  method: "POST",
32070
32427
  path: "/organizations/:organizationId/members/invitations",
@@ -32364,8 +32721,8 @@ var LogoDeleteRequestSchema = external_exports.object({
32364
32721
  });
32365
32722
 
32366
32723
  // ../../packages/api-contracts/src/contracts/v1/organization.ts
32367
- var c26 = initContract();
32368
- var organizationContract = c26.router({
32724
+ var c27 = initContract();
32725
+ var organizationContract = c27.router({
32369
32726
  listOrganizations: {
32370
32727
  method: "GET",
32371
32728
  path: "/organizations",
@@ -32410,6 +32767,23 @@ var organizationContract = c26.router({
32410
32767
  },
32411
32768
  summary: "Create an organization (Trace staff only)"
32412
32769
  },
32770
+ // Staff-only, cross-org: removes the tenant from Trace and WorkOS together.
32771
+ // 409 when members, repositories, or retained audit history still reference
32772
+ // the organization; those must be removed first (or cannot be, for WORM rows).
32773
+ deleteOrganizationAsStaff: {
32774
+ method: "DELETE",
32775
+ path: "/staff/organizations/:organizationId",
32776
+ pathParams: OrgIdParamsSchema,
32777
+ responses: {
32778
+ 204: DeleteResponseSchema,
32779
+ 401: ErrorResponseSchema,
32780
+ 403: ErrorResponseSchema,
32781
+ 404: ErrorResponseSchema,
32782
+ 409: ErrorResponseSchema,
32783
+ 500: ErrorResponseSchema
32784
+ },
32785
+ summary: "Delete an organization (Trace staff only)"
32786
+ },
32413
32787
  getOrganization: {
32414
32788
  method: "GET",
32415
32789
  path: "/organizations/:organizationId",
@@ -32720,7 +33094,7 @@ var organizationContract = c26.router({
32720
33094
  method: "POST",
32721
33095
  path: "/organizations/:organizationId/logos-from-homepage",
32722
33096
  pathParams: OrgIdParamsSchema,
32723
- body: c26.type(),
33097
+ body: c27.type(),
32724
33098
  responses: {
32725
33099
  200: OrganizationResponseSchema,
32726
33100
  // 400 — no homepage set on the organization.
@@ -33027,8 +33401,8 @@ var ScanReuseSuggestionsResponseSchema = external_exports.object({
33027
33401
  });
33028
33402
 
33029
33403
  // ../../packages/api-contracts/src/contracts/v1/pentest.ts
33030
- var c27 = initContract();
33031
- var pentestContract = c27.router({
33404
+ var c28 = initContract();
33405
+ var pentestContract = c28.router({
33032
33406
  listPentests: {
33033
33407
  method: "GET",
33034
33408
  path: "/organizations/:organizationId/pentests",
@@ -33338,8 +33712,8 @@ var pentestContract = c27.router({
33338
33712
  });
33339
33713
 
33340
33714
  // ../../packages/api-contracts/src/contracts/v1/pentest-attack-chain.ts
33341
- var c28 = initContract();
33342
- var pentestAttackChainContract = c28.router({
33715
+ var c29 = initContract();
33716
+ var pentestAttackChainContract = c29.router({
33343
33717
  listAttackChains: {
33344
33718
  method: "GET",
33345
33719
  path: "/organizations/:organizationId/pentests/:pentestId/attack-chains",
@@ -33438,7 +33812,7 @@ var pentestAttackChainContract = c28.router({
33438
33812
  method: "DELETE",
33439
33813
  path: "/organizations/:organizationId/pentests/:pentestId/attack-chains/:attackChainId",
33440
33814
  pathParams: PentestAttackChainIdParamsSchema,
33441
- body: c28.noBody(),
33815
+ body: c29.noBody(),
33442
33816
  responses: {
33443
33817
  200: DeleteResponseSchema,
33444
33818
  401: ErrorResponseSchema,
@@ -33599,8 +33973,8 @@ var ReadAttestationDownloadResponseSchema = external_exports.union([
33599
33973
  ]);
33600
33974
 
33601
33975
  // ../../packages/api-contracts/src/contracts/v1/pentest-attestation.ts
33602
- var c29 = initContract();
33603
- var pentestAttestationContract = c29.router({
33976
+ var c30 = initContract();
33977
+ var pentestAttestationContract = c30.router({
33604
33978
  getDefaults: {
33605
33979
  method: "GET",
33606
33980
  path: "/organizations/:organizationId/pentests/:pentestId/attestations/defaults",
@@ -33743,8 +34117,8 @@ var ListPentestAuditLogResponseSchema = external_exports.object({
33743
34117
  });
33744
34118
 
33745
34119
  // ../../packages/api-contracts/src/contracts/v1/pentest-audit-log.ts
33746
- var c30 = initContract();
33747
- var pentestAuditLogContract = c30.router({
34120
+ var c31 = initContract();
34121
+ var pentestAuditLogContract = c31.router({
33748
34122
  listAuditLog: {
33749
34123
  method: "GET",
33750
34124
  path: "/organizations/:organizationId/pentests/:pentestId/audit-log",
@@ -33791,8 +34165,8 @@ var CreatePentestAuthorizedDomainRequestSchema = external_exports.object({
33791
34165
  }).strict();
33792
34166
 
33793
34167
  // ../../packages/api-contracts/src/contracts/v1/pentest-authorized-domain.ts
33794
- var c31 = initContract();
33795
- var pentestAuthorizedDomainContract = c31.router({
34168
+ var c32 = initContract();
34169
+ var pentestAuthorizedDomainContract = c32.router({
33796
34170
  getPentestAuthorizedScope: {
33797
34171
  method: "GET",
33798
34172
  path: "/organizations/:organizationId/pentests/:pentestId/authorized-scope",
@@ -33846,8 +34220,8 @@ var PentestEgressResponseSchema = external_exports.object({
33846
34220
  }).strict();
33847
34221
 
33848
34222
  // ../../packages/api-contracts/src/contracts/v1/pentest-egress.ts
33849
- var c32 = initContract();
33850
- var pentestEgressContract = c32.router({
34223
+ var c33 = initContract();
34224
+ var pentestEgressContract = c33.router({
33851
34225
  getPentestEgress: {
33852
34226
  method: "GET",
33853
34227
  path: "/staff/pentest-egress",
@@ -33886,8 +34260,8 @@ var CreatePentestEngagementExclusionRequestSchema = external_exports.object({
33886
34260
  }).strict();
33887
34261
 
33888
34262
  // ../../packages/api-contracts/src/contracts/v1/pentest-engagement-exclusion.ts
33889
- var c33 = initContract();
33890
- var pentestEngagementExclusionContract = c33.router({
34263
+ var c34 = initContract();
34264
+ var pentestEngagementExclusionContract = c34.router({
33891
34265
  listPentestEngagementExclusions: {
33892
34266
  method: "GET",
33893
34267
  path: "/organizations/:organizationId/pentests/:pentestId/engagement-exclusions",
@@ -33951,8 +34325,8 @@ var CreatePentestEnvironmentRequestSchema = external_exports.object({
33951
34325
  }).strict();
33952
34326
 
33953
34327
  // ../../packages/api-contracts/src/contracts/v1/pentest-environment.ts
33954
- var c34 = initContract();
33955
- var pentestEnvironmentContract = c34.router({
34328
+ var c35 = initContract();
34329
+ var pentestEnvironmentContract = c35.router({
33956
34330
  listPentestEnvironments: {
33957
34331
  method: "GET",
33958
34332
  path: "/organizations/:organizationId/pentests/:pentestId/environments",
@@ -34001,8 +34375,8 @@ var pentestEnvironmentContract = c34.router({
34001
34375
  });
34002
34376
 
34003
34377
  // ../../packages/api-contracts/src/contracts/v1/pentest-finding.ts
34004
- var c35 = initContract();
34005
- var pentestFindingContract = c35.router({
34378
+ var c36 = initContract();
34379
+ var pentestFindingContract = c36.router({
34006
34380
  // ─── Findings ───
34007
34381
  listFindings: {
34008
34382
  method: "GET",
@@ -34106,6 +34480,24 @@ var pentestFindingContract = c35.router({
34106
34480
  500: ErrorResponseSchema
34107
34481
  }
34108
34482
  },
34483
+ // Same visibility as getCustomerFinding: published findings only, customer DTO,
34484
+ // so the document never carries staff identity or a draft.
34485
+ exportCustomerFindingPdf: {
34486
+ method: "GET",
34487
+ path: "/organizations/:organizationId/customer-pentests/:pentestId/findings/:pentestFindingId/pdf",
34488
+ pathParams: CustomerPentestFindingIdParamsSchema,
34489
+ summary: "Render one pentest finding as a Trace-branded PDF",
34490
+ description: "Renders the published finding on demand, stores the PDF, and returns a short-lived presigned download URL. An identical export from the same day is reused without a render. The document names the finding, its classification, and its affected repositories only: no people and no organization name appear on it. Rendering shares one browser per API task, so exports are admission-controlled per organization; a 429 means retry after the interval in `details.retryAfterSeconds`.",
34491
+ responses: {
34492
+ 200: PentestFindingPdfExportResponseSchema,
34493
+ 400: ErrorResponseSchema,
34494
+ 401: ErrorResponseSchema,
34495
+ 403: ErrorResponseSchema,
34496
+ 404: ErrorResponseSchema,
34497
+ 429: ErrorResponseSchema,
34498
+ 500: ErrorResponseSchema
34499
+ }
34500
+ },
34109
34501
  // ─── Evidence (PR 2: surface & curate) ───
34110
34502
  listCustomerFindingEvidence: {
34111
34503
  method: "GET",
@@ -34428,6 +34820,25 @@ var pentestFindingContract = c35.router({
34428
34820
  500: ErrorResponseSchema
34429
34821
  }
34430
34822
  },
34823
+ // Reader feedback on a published finding: a thumbs up/down, a comment, or both.
34824
+ // Append-only and never a status change (disputes are `contest`). Same auth as
34825
+ // the other customer writes: VIEW_RESULTS + user principal + customer visibility.
34826
+ submitFeedback: {
34827
+ method: "POST",
34828
+ path: "/organizations/:organizationId/customer-pentests/:pentestId/findings/:pentestFindingId/feedback",
34829
+ pathParams: CustomerPentestFindingIdParamsSchema,
34830
+ body: CreatePentestFindingFeedbackRequestSchema,
34831
+ summary: "Leave feedback on a pentest finding",
34832
+ description: "Records a thumbs up or thumbs down, an optional reason for a thumbs down, and an optional comment. Each reader counts once in the tallies: a new vote replaces the previous one in the count while the change stays in the resolution history. Returns the updated tallies.",
34833
+ responses: {
34834
+ 201: PentestFindingFeedbackSummarySchema,
34835
+ 400: ErrorResponseSchema,
34836
+ 401: ErrorResponseSchema,
34837
+ 403: ErrorResponseSchema,
34838
+ 404: ErrorResponseSchema,
34839
+ 500: ErrorResponseSchema
34840
+ }
34841
+ },
34431
34842
  // ─── Contests ───
34432
34843
  listContests: {
34433
34844
  method: "GET",
@@ -34558,8 +34969,8 @@ var pentestFindingContract = c35.router({
34558
34969
  });
34559
34970
 
34560
34971
  // ../../packages/api-contracts/src/contracts/v1/pentest-hypothesis.ts
34561
- var c36 = initContract();
34562
- var pentestHypothesisContract = c36.router({
34972
+ var c37 = initContract();
34973
+ var pentestHypothesisContract = c37.router({
34563
34974
  listHypotheses: {
34564
34975
  method: "GET",
34565
34976
  path: "/organizations/:organizationId/pentests/:pentestId/hypotheses",
@@ -34661,9 +35072,148 @@ var pentestHypothesisContract = c36.router({
34661
35072
  }
34662
35073
  });
34663
35074
 
35075
+ // ../../packages/api-contracts/src/schemas/api/v1/pentest-intake-document.api.ts
35076
+ var PentestIntakeDocumentTypeValues = [
35077
+ "PREVIOUS_PENTEST",
35078
+ "ARCHITECTURE_DIAGRAM",
35079
+ "THREAT_MODEL",
35080
+ "OTHER"
35081
+ ];
35082
+ var PentestIntakeDocumentTypeSchema = external_exports.enum(PentestIntakeDocumentTypeValues);
35083
+ var MAX_PENTEST_INTAKE_DOCUMENT_BYTES = 50 * 1024 * 1024;
35084
+ var PentestIntakeDocumentIdParamsSchema = CustomerPentestIdParamsSchema.extend({
35085
+ documentId: external_exports.uuid()
35086
+ });
35087
+ var PentestIntakeDocumentResponseSchema = external_exports.object({
35088
+ documentId: external_exports.uuid(),
35089
+ pentestId: external_exports.uuid(),
35090
+ documentType: PentestIntakeDocumentTypeSchema,
35091
+ fileName: external_exports.string(),
35092
+ fileType: external_exports.string(),
35093
+ fileSizeBytes: external_exports.number().int().nonnegative(),
35094
+ uploadedByName: external_exports.string().nullable(),
35095
+ createdAt: external_exports.coerce.date(),
35096
+ updatedAt: external_exports.coerce.date()
35097
+ });
35098
+ var ListPentestIntakeDocumentsResponseSchema = external_exports.object({
35099
+ /** Mirrors the questionnaire's `editable`: uploads and removals are refused once false. */
35100
+ editable: external_exports.boolean(),
35101
+ documents: external_exports.array(PentestIntakeDocumentResponseSchema)
35102
+ });
35103
+ var CreatePentestIntakeDocumentUploadRequestSchema = external_exports.object({
35104
+ fileName: external_exports.string().min(1).max(255),
35105
+ fileSizeBytes: external_exports.number().int().positive().max(MAX_PENTEST_INTAKE_DOCUMENT_BYTES)
35106
+ }).strict();
35107
+ var CreatePentestIntakeDocumentUploadResponseSchema = external_exports.object({
35108
+ /** Server-minted S3 key; hand it back unchanged on attach. */
35109
+ uploadKey: external_exports.string(),
35110
+ uploadUrl: external_exports.url(),
35111
+ expiresAt: external_exports.coerce.date()
35112
+ });
35113
+ var AttachPentestIntakeDocumentRequestSchema = external_exports.object({
35114
+ uploadKey: external_exports.string().min(1).max(1024),
35115
+ documentType: PentestIntakeDocumentTypeSchema,
35116
+ fileName: external_exports.string().min(1).max(255),
35117
+ fileType: external_exports.string().min(1).max(100)
35118
+ }).strict();
35119
+ var UpdatePentestIntakeDocumentRequestSchema = external_exports.object({
35120
+ documentType: PentestIntakeDocumentTypeSchema
35121
+ }).strict();
35122
+ var DownloadPentestIntakeDocumentResponseSchema = external_exports.object({
35123
+ document: PentestIntakeDocumentResponseSchema,
35124
+ downloadUrl: external_exports.url()
35125
+ });
35126
+
35127
+ // ../../packages/api-contracts/src/contracts/v1/pentest-intake-document.ts
35128
+ var c38 = initContract();
35129
+ var pentestIntakeDocumentContract = c38.router({
35130
+ listDocuments: {
35131
+ method: "GET",
35132
+ path: "/organizations/:organizationId/customer-pentests/:pentestId/questionnaire/documents",
35133
+ pathParams: CustomerPentestIdParamsSchema,
35134
+ responses: {
35135
+ 200: ListPentestIntakeDocumentsResponseSchema,
35136
+ 401: ErrorResponseSchema,
35137
+ 403: ErrorResponseSchema,
35138
+ 404: ErrorResponseSchema,
35139
+ 500: ErrorResponseSchema
35140
+ }
35141
+ },
35142
+ createUpload: {
35143
+ method: "POST",
35144
+ path: "/organizations/:organizationId/customer-pentests/:pentestId/questionnaire/documents/uploads",
35145
+ pathParams: CustomerPentestIdParamsSchema,
35146
+ body: CreatePentestIntakeDocumentUploadRequestSchema,
35147
+ responses: {
35148
+ 201: CreatePentestIntakeDocumentUploadResponseSchema,
35149
+ 400: ErrorResponseSchema,
35150
+ 401: ErrorResponseSchema,
35151
+ 403: ErrorResponseSchema,
35152
+ 404: ErrorResponseSchema,
35153
+ 409: ErrorResponseSchema,
35154
+ 500: ErrorResponseSchema
35155
+ }
35156
+ },
35157
+ attachDocument: {
35158
+ method: "POST",
35159
+ path: "/organizations/:organizationId/customer-pentests/:pentestId/questionnaire/documents",
35160
+ pathParams: CustomerPentestIdParamsSchema,
35161
+ body: AttachPentestIntakeDocumentRequestSchema,
35162
+ responses: {
35163
+ 201: PentestIntakeDocumentResponseSchema,
35164
+ 400: ErrorResponseSchema,
35165
+ 401: ErrorResponseSchema,
35166
+ 403: ErrorResponseSchema,
35167
+ 404: ErrorResponseSchema,
35168
+ 409: ErrorResponseSchema,
35169
+ 500: ErrorResponseSchema
35170
+ }
35171
+ },
35172
+ updateDocument: {
35173
+ method: "PATCH",
35174
+ path: "/organizations/:organizationId/customer-pentests/:pentestId/questionnaire/documents/:documentId",
35175
+ pathParams: PentestIntakeDocumentIdParamsSchema,
35176
+ body: UpdatePentestIntakeDocumentRequestSchema,
35177
+ responses: {
35178
+ 200: PentestIntakeDocumentResponseSchema,
35179
+ 400: ErrorResponseSchema,
35180
+ 401: ErrorResponseSchema,
35181
+ 403: ErrorResponseSchema,
35182
+ 404: ErrorResponseSchema,
35183
+ 409: ErrorResponseSchema,
35184
+ 500: ErrorResponseSchema
35185
+ }
35186
+ },
35187
+ downloadDocument: {
35188
+ method: "GET",
35189
+ path: "/organizations/:organizationId/customer-pentests/:pentestId/questionnaire/documents/:documentId/download",
35190
+ pathParams: PentestIntakeDocumentIdParamsSchema,
35191
+ responses: {
35192
+ 200: DownloadPentestIntakeDocumentResponseSchema,
35193
+ 401: ErrorResponseSchema,
35194
+ 403: ErrorResponseSchema,
35195
+ 404: ErrorResponseSchema,
35196
+ 500: ErrorResponseSchema
35197
+ }
35198
+ },
35199
+ deleteDocument: {
35200
+ method: "DELETE",
35201
+ path: "/organizations/:organizationId/customer-pentests/:pentestId/questionnaire/documents/:documentId",
35202
+ pathParams: PentestIntakeDocumentIdParamsSchema,
35203
+ responses: {
35204
+ 204: DeleteResponseSchema,
35205
+ 401: ErrorResponseSchema,
35206
+ 403: ErrorResponseSchema,
35207
+ 404: ErrorResponseSchema,
35208
+ 409: ErrorResponseSchema,
35209
+ 500: ErrorResponseSchema
35210
+ }
35211
+ }
35212
+ });
35213
+
34664
35214
  // ../../packages/api-contracts/src/contracts/v1/pentest-manual-finding.ts
34665
- var c37 = initContract();
34666
- var pentestManualFindingContract = c37.router({
35215
+ var c39 = initContract();
35216
+ var pentestManualFindingContract = c39.router({
34667
35217
  listManualFindings: {
34668
35218
  method: "GET",
34669
35219
  path: "/organizations/:organizationId/pentests/:pentestId/manual-findings",
@@ -34750,8 +35300,8 @@ var pentestManualFindingContract = c37.router({
34750
35300
  });
34751
35301
 
34752
35302
  // ../../packages/api-contracts/src/contracts/v1/pentest-report-pdf.ts
34753
- var c38 = initContract();
34754
- var pentestReportPdfContract = c38.router({
35303
+ var c40 = initContract();
35304
+ var pentestReportPdfContract = c40.router({
34755
35305
  getCurrent: {
34756
35306
  method: "GET",
34757
35307
  path: "/organizations/:organizationId/pentests/:pentestId/report-pdf",
@@ -34844,7 +35394,7 @@ var pentestReportPdfContract = c38.router({
34844
35394
  // No body: the render always reads current DB state (cover derived via
34845
35395
  // deriveReportCover(), narrative/scope from the row's columns). The legacy
34846
35396
  // dialog payload was retired with the Generate dialog (report-session RS5).
34847
- body: c38.noBody(),
35397
+ body: c40.noBody(),
34848
35398
  responses: {
34849
35399
  202: StartReportPdfGenerationTaskResponseSchema,
34850
35400
  400: ErrorResponseSchema,
@@ -34969,7 +35519,8 @@ var pentestReportPdfContract = c38.router({
34969
35519
  403: ErrorResponseSchema,
34970
35520
  404: ErrorResponseSchema,
34971
35521
  // REPORT_NOT_RENDERED — no PDF exists yet (spec-pr-rs1 §2c) — or
34972
- // BUNDLE_NOT_RENDERED for artifact=markdown with no bundle object.
35522
+ // BUNDLE_NOT_RENDERED / DOCX_NOT_RENDERED for artifact=markdown / docx
35523
+ // with no such object beside the current PDF.
34973
35524
  409: ErrorResponseSchema,
34974
35525
  500: ErrorResponseSchema
34975
35526
  }
@@ -35026,20 +35577,23 @@ var pentestReportPdfContract = c38.router({
35026
35577
  method: "GET",
35027
35578
  path: "/organizations/:organizationId/customer-pentests/:pentestId/report-pdf/download",
35028
35579
  pathParams: CustomerReportPdfPentestParamsSchema,
35029
- query: PentestPdfDownloadQuerySchema,
35580
+ query: CustomerReportPdfDownloadQuerySchema,
35030
35581
  responses: {
35031
35582
  200: CustomerReportPdfDownloadResponseSchema,
35032
35583
  401: ErrorResponseSchema,
35033
35584
  403: ErrorResponseSchema,
35034
35585
  404: ErrorResponseSchema,
35586
+ // DOCX_NOT_RENDERED for artifact=docx when no Word document from the
35587
+ // served render exists.
35588
+ 409: ErrorResponseSchema,
35035
35589
  500: ErrorResponseSchema
35036
35590
  }
35037
35591
  }
35038
35592
  });
35039
35593
 
35040
35594
  // ../../packages/api-contracts/src/contracts/v1/pentest-validation.ts
35041
- var c39 = initContract();
35042
- var pentestValidationContract = c39.router({
35595
+ var c41 = initContract();
35596
+ var pentestValidationContract = c41.router({
35043
35597
  listValidationRuns: {
35044
35598
  method: "GET",
35045
35599
  path: "/organizations/:organizationId/pentests/:pentestId/validation-runs",
@@ -35147,8 +35701,8 @@ var pentestValidationContract = c39.router({
35147
35701
  });
35148
35702
 
35149
35703
  // ../../packages/api-contracts/src/contracts/v1/repository.ts
35150
- var c40 = initContract();
35151
- var repositoryContract = c40.router({
35704
+ var c42 = initContract();
35705
+ var repositoryContract = c42.router({
35152
35706
  createRepository: {
35153
35707
  method: "POST",
35154
35708
  path: "/organizations/:organizationId/repositories",
@@ -35334,7 +35888,7 @@ var repositoryContract = c40.router({
35334
35888
  method: "POST",
35335
35889
  path: "/organizations/:organizationId/repositories/scanning-branch/confirm-defaults",
35336
35890
  pathParams: OrgIdParamsSchema,
35337
- body: c40.noBody(),
35891
+ body: c42.noBody(),
35338
35892
  responses: {
35339
35893
  200: ConfirmScanningBranchDefaultsResponseSchema,
35340
35894
  401: ErrorResponseSchema,
@@ -35468,8 +36022,8 @@ var ListScanLogsResponseSchema = external_exports.object({
35468
36022
  });
35469
36023
 
35470
36024
  // ../../packages/api-contracts/src/contracts/v1/scan.ts
35471
- var c41 = initContract();
35472
- var scanContract = c41.router({
36025
+ var c43 = initContract();
36026
+ var scanContract = c43.router({
35473
36027
  createScan: {
35474
36028
  method: "POST",
35475
36029
  path: "/organizations/:organizationId/scans",
@@ -35590,8 +36144,8 @@ var StaffPentestStatsResponseSchema = external_exports.object({
35590
36144
  }).strict();
35591
36145
 
35592
36146
  // ../../packages/api-contracts/src/contracts/v1/staff-pentest-stats.ts
35593
- var c42 = initContract();
35594
- var staffPentestStatsContract = c42.router({
36147
+ var c44 = initContract();
36148
+ var staffPentestStatsContract = c44.router({
35595
36149
  getStaffPentestStats: {
35596
36150
  method: "GET",
35597
36151
  path: "/staff/pentest-stats",
@@ -35605,6 +36159,59 @@ var staffPentestStatsContract = c42.router({
35605
36159
  }
35606
36160
  });
35607
36161
 
36162
+ // ../../packages/api-contracts/src/schemas/api/v1/staff-pentests.api.ts
36163
+ var StaffOrganizationPentestParamsSchema = OrgIdParamsSchema.extend({
36164
+ pentestId: external_exports.uuid()
36165
+ });
36166
+ var StaffOrganizationPentestSummarySchema = external_exports.object({
36167
+ pentestId: external_exports.uuid(),
36168
+ displayId: external_exports.int().positive(),
36169
+ title: external_exports.string(),
36170
+ status: PentestStatus,
36171
+ currentStage: PentestStage,
36172
+ testType: PentestTestType,
36173
+ /** Bound application names, ordinal-ascending. Empty for an unbound draft. */
36174
+ applicationNames: external_exports.array(external_exports.string()),
36175
+ findingCount: external_exports.int().nonnegative(),
36176
+ startedAt: external_exports.iso.datetime().nullable(),
36177
+ endedAt: external_exports.iso.datetime().nullable(),
36178
+ createdAt: external_exports.iso.datetime()
36179
+ }).strict();
36180
+ var ListStaffOrganizationPentestsResponseSchema = external_exports.array(
36181
+ StaffOrganizationPentestSummarySchema
36182
+ );
36183
+
36184
+ // ../../packages/api-contracts/src/contracts/v1/staff-pentests.ts
36185
+ var c45 = initContract();
36186
+ var staffPentestsContract = c45.router({
36187
+ listStaffOrganizationPentests: {
36188
+ method: "GET",
36189
+ path: "/organizations/:organizationId/staff/pentests",
36190
+ pathParams: OrgIdParamsSchema,
36191
+ responses: {
36192
+ 200: ListStaffOrganizationPentestsResponseSchema,
36193
+ 401: ErrorResponseSchema,
36194
+ 403: ErrorResponseSchema,
36195
+ 404: ErrorResponseSchema,
36196
+ 500: ErrorResponseSchema
36197
+ },
36198
+ summary: "List an organization\u2019s pentests (Trace staff only)"
36199
+ },
36200
+ deleteStaffOrganizationPentest: {
36201
+ method: "DELETE",
36202
+ path: "/organizations/:organizationId/staff/pentests/:pentestId",
36203
+ pathParams: StaffOrganizationPentestParamsSchema,
36204
+ responses: {
36205
+ 204: DeleteResponseSchema,
36206
+ 401: ErrorResponseSchema,
36207
+ 403: ErrorResponseSchema,
36208
+ 404: ErrorResponseSchema,
36209
+ 500: ErrorResponseSchema
36210
+ },
36211
+ summary: "Soft-delete a pentest from the admin console (Trace staff only)"
36212
+ }
36213
+ });
36214
+
35608
36215
  // ../../packages/api-contracts/src/schemas/api/v1/staff-queue.api.ts
35609
36216
  var StaffQueueItemKindValues = [
35610
36217
  "RETEST_OWED",
@@ -35712,8 +36319,8 @@ var StaffQueueResponseSchema = external_exports.object({
35712
36319
  }).strict();
35713
36320
 
35714
36321
  // ../../packages/api-contracts/src/contracts/v1/staff-queue.ts
35715
- var c43 = initContract();
35716
- var staffQueueContract = c43.router({
36322
+ var c46 = initContract();
36323
+ var staffQueueContract = c46.router({
35717
36324
  getStaffQueue: {
35718
36325
  method: "GET",
35719
36326
  path: "/staff/queue",
@@ -35783,8 +36390,8 @@ var AttachTestIntegrationRequestSchema = external_exports.object({
35783
36390
  }).strict();
35784
36391
 
35785
36392
  // ../../packages/api-contracts/src/contracts/v1/test-integration.ts
35786
- var c44 = initContract();
35787
- var testIntegrationContract = c44.router({
36393
+ var c47 = initContract();
36394
+ var testIntegrationContract = c47.router({
35788
36395
  // -- Fixture CRUD (org-less, staff-only) ----------------------------------
35789
36396
  listTestIntegrations: {
35790
36397
  method: "GET",
@@ -35916,8 +36523,8 @@ var MyContextResponseSchema = external_exports.object({
35916
36523
  });
35917
36524
 
35918
36525
  // ../../packages/api-contracts/src/contracts/v1/user.ts
35919
- var c45 = initContract();
35920
- var userContract = c45.router({
36526
+ var c48 = initContract();
36527
+ var userContract = c48.router({
35921
36528
  getStaffProfile: {
35922
36529
  method: "GET",
35923
36530
  path: "/staff/profile",
@@ -36032,8 +36639,8 @@ var VantaIntegrationResponseSchema = external_exports.object({
36032
36639
  });
36033
36640
 
36034
36641
  // ../../packages/api-contracts/src/contracts/v1/vanta-integration.ts
36035
- var c46 = initContract();
36036
- var vantaIntegrationContract = c46.router({
36642
+ var c49 = initContract();
36643
+ var vantaIntegrationContract = c49.router({
36037
36644
  createVantaIntegration: {
36038
36645
  method: "POST",
36039
36646
  path: "/organizations/:organizationId/vanta-integration",
@@ -36165,6 +36772,13 @@ var VulnerabilityResponseSchema = external_exports.object({
36165
36772
  updatedAt: external_exports.coerce.date()
36166
36773
  });
36167
36774
  var ListVulnerabilitiesResponseSchema = external_exports.array(VulnerabilityResponseSchema);
36775
+ var VulnerabilityPdfExportResponseSchema = external_exports.object({
36776
+ /** Download name the presigned URL carries, e.g. `TRACE-042-sql-injection.pdf`. */
36777
+ fileName: external_exports.string(),
36778
+ downloadUrl: external_exports.url(),
36779
+ /** False when an identical export already existed and was reused without a render. */
36780
+ rendered: external_exports.boolean()
36781
+ });
36168
36782
  var FilterValueSchema = external_exports.union([external_exports.string(), external_exports.number(), external_exports.boolean()]);
36169
36783
  var FilterConditionSchema = external_exports.object({
36170
36784
  field: external_exports.enum(["repositoryId", "severity", "vulnerabilityType", "status", "cweId", "scanMode"]),
@@ -36365,8 +36979,8 @@ var VerifySharedVulnerabilityEmailCodeResponseSchema = external_exports.object({
36365
36979
  });
36366
36980
 
36367
36981
  // ../../packages/api-contracts/src/contracts/v1/vulnerability.ts
36368
- var c47 = initContract();
36369
- var vulnerabilityContract = c47.router({
36982
+ var c50 = initContract();
36983
+ var vulnerabilityContract = c50.router({
36370
36984
  filterVulnerabilities: {
36371
36985
  method: "POST",
36372
36986
  path: "/organizations/:organizationId/vulnerabilities/filter",
@@ -36418,6 +37032,22 @@ var vulnerabilityContract = c47.router({
36418
37032
  500: ErrorResponseSchema
36419
37033
  }
36420
37034
  },
37035
+ exportVulnerabilityPdf: {
37036
+ method: "GET",
37037
+ path: "/organizations/:organizationId/vulnerabilities/:vulnerabilityId/pdf",
37038
+ pathParams: VulnerabilityLookupIdParamsSchema,
37039
+ summary: "Render one vulnerability as a Trace-branded PDF",
37040
+ description: "Renders the vulnerability on demand, stores the PDF, and returns a short-lived presigned download URL. An identical export from the same day is reused without a render. The document names the finding and its code location only: no people and no organization name appear on it. Rendering shares one browser per API task, so exports are admission-controlled per organization; a 429 means retry after the interval in `details.retryAfterSeconds`.",
37041
+ responses: {
37042
+ 200: VulnerabilityPdfExportResponseSchema,
37043
+ 400: ErrorResponseSchema,
37044
+ 401: ErrorResponseSchema,
37045
+ 403: ErrorResponseSchema,
37046
+ 404: ErrorResponseSchema,
37047
+ 429: ErrorResponseSchema,
37048
+ 500: ErrorResponseSchema
37049
+ }
37050
+ },
36421
37051
  updateVulnerability: {
36422
37052
  method: "PATCH",
36423
37053
  path: "/organizations/:organizationId/vulnerabilities/:vulnerabilityId",
@@ -36574,8 +37204,8 @@ var UpdateVulnerabilityNoteRequestSchema = external_exports.object({
36574
37204
  });
36575
37205
 
36576
37206
  // ../../packages/api-contracts/src/contracts/v1/vulnerability-note.ts
36577
- var c48 = initContract();
36578
- var vulnerabilityNoteContract = c48.router({
37207
+ var c51 = initContract();
37208
+ var vulnerabilityNoteContract = c51.router({
36579
37209
  listNotes: {
36580
37210
  method: "GET",
36581
37211
  path: "/organizations/:organizationId/vulnerabilities/:vulnerabilityId/notes",
@@ -36756,8 +37386,8 @@ var ListWebhookDeliveriesResponseSchema = external_exports.object({
36756
37386
  });
36757
37387
 
36758
37388
  // ../../packages/api-contracts/src/contracts/v1/webhook-settings.ts
36759
- var c49 = initContract();
36760
- var webhookSettingsContract = c49.router({
37389
+ var c52 = initContract();
37390
+ var webhookSettingsContract = c52.router({
36761
37391
  listWebhookEndpoints: {
36762
37392
  method: "GET",
36763
37393
  path: "/organizations/:organizationId/webhook-settings/endpoints",
@@ -36895,8 +37525,8 @@ var webhookSettingsContract = c49.router({
36895
37525
  });
36896
37526
 
36897
37527
  // ../../packages/api-contracts/src/contracts/v1/index.ts
36898
- var c50 = initContract();
36899
- var v1Contract = c50.router(
37528
+ var c53 = initContract();
37529
+ var v1Contract = c53.router(
36900
37530
  {
36901
37531
  admin: adminContract,
36902
37532
  cloudIntegration: cloudIntegrationContract,
@@ -36925,6 +37555,7 @@ var v1Contract = c50.router(
36925
37555
  agentTask: agentTaskContract,
36926
37556
  verifyApplicationSetup: verifyApplicationSetupContract,
36927
37557
  pentest: pentestContract,
37558
+ pentestIntakeDocument: pentestIntakeDocumentContract,
36928
37559
  pentestAttestation: pentestAttestationContract,
36929
37560
  pentestManualFinding: pentestManualFindingContract,
36930
37561
  pentestAuditLog: pentestAuditLogContract,
@@ -36942,10 +37573,12 @@ var v1Contract = c50.router(
36942
37573
  inbox: inboxContract,
36943
37574
  apiKey: apiKeyContract,
36944
37575
  member: memberContract,
37576
+ inviteLink: inviteLinkContract,
36945
37577
  engagement: engagementContract,
36946
37578
  testIntegration: testIntegrationContract,
36947
37579
  pentestEgress: pentestEgressContract,
36948
37580
  staffPentestStats: staffPentestStatsContract,
37581
+ staffPentests: staffPentestsContract,
36949
37582
  staffQueue: staffQueueContract
36950
37583
  },
36951
37584
  {
@@ -36954,8 +37587,8 @@ var v1Contract = c50.router(
36954
37587
  );
36955
37588
 
36956
37589
  // ../../packages/api-contracts/src/contracts/index.ts
36957
- var c51 = initContract();
36958
- var contract = c51.router({
37590
+ var c54 = initContract();
37591
+ var contract = c54.router({
36959
37592
  v1: v1Contract
36960
37593
  });
36961
37594
 
@@ -36965,8 +37598,8 @@ var GitHubWebhookResponseSchema = external_exports.object({
36965
37598
  });
36966
37599
 
36967
37600
  // ../../packages/api-contracts/src/contracts/webhooks/github.ts
36968
- var c52 = initContract();
36969
- var githubWebhookContract = c52.router(
37601
+ var c55 = initContract();
37602
+ var githubWebhookContract = c55.router(
36970
37603
  {
36971
37604
  handleWebhook: {
36972
37605
  method: "POST",
@@ -36984,7 +37617,7 @@ var githubWebhookContract = c52.router(
36984
37617
  pathPrefix: "/webhooks"
36985
37618
  }
36986
37619
  );
36987
- var githubReadonlyWebhookContract = c52.router(
37620
+ var githubReadonlyWebhookContract = c55.router(
36988
37621
  {
36989
37622
  handleWebhook: {
36990
37623
  method: "POST",
@@ -37004,9 +37637,9 @@ var githubReadonlyWebhookContract = c52.router(
37004
37637
  );
37005
37638
 
37006
37639
  // ../../packages/api-contracts/src/contracts/webhooks/linear.ts
37007
- var c53 = initContract();
37640
+ var c56 = initContract();
37008
37641
  var LinearWebhookResponseSchema = external_exports.object({ ok: external_exports.boolean() });
37009
- var linearWebhookContract = c53.router(
37642
+ var linearWebhookContract = c56.router(
37010
37643
  {
37011
37644
  handleWebhook: {
37012
37645
  method: "POST",
@@ -37026,7 +37659,7 @@ var linearWebhookContract = c53.router(
37026
37659
  );
37027
37660
 
37028
37661
  // ../../packages/api-contracts/src/contracts/forge/bitbucket.ts
37029
- var c54 = initContract();
37662
+ var c57 = initContract();
37030
37663
  var InstallEventSchema = external_exports.object({
37031
37664
  eventType: external_exports.enum(["avi:forge:installed:app", "avi:forge:upgraded:app"]),
37032
37665
  context: external_exports.object({ cloudId: external_exports.string().min(1) }).loose(),
@@ -37055,7 +37688,7 @@ var ForgePairingCodeResponseSchema = external_exports.discriminatedUnion("state"
37055
37688
  expiresInSeconds: external_exports.number().int()
37056
37689
  })
37057
37690
  ]);
37058
- var forgeBitbucketContract = c54.router(
37691
+ var forgeBitbucketContract = c57.router(
37059
37692
  {
37060
37693
  lifecycle: {
37061
37694
  method: "POST",
@@ -37102,8 +37735,8 @@ var HealthResponseSchema = external_exports.object({
37102
37735
  });
37103
37736
 
37104
37737
  // ../../packages/api-contracts/src/contracts/health.ts
37105
- var c55 = initContract();
37106
- var healthContract = c55.router({
37738
+ var c58 = initContract();
37739
+ var healthContract = c58.router({
37107
37740
  getHealth: {
37108
37741
  method: "GET",
37109
37742
  path: "/health",
@@ -37115,8 +37748,8 @@ var healthContract = c55.router({
37115
37748
  });
37116
37749
 
37117
37750
  // ../../packages/api-contracts/src/contracts/v1/vulnerability-share.ts
37118
- var c56 = initContract();
37119
- var vulnerabilityShareContract = c56.router(
37751
+ var c59 = initContract();
37752
+ var vulnerabilityShareContract = c59.router(
37120
37753
  {
37121
37754
  resolveShareLink: {
37122
37755
  method: "GET",
@@ -37203,8 +37836,8 @@ var UpdatePrVulnerabilityRequestSchema = external_exports.object({
37203
37836
  });
37204
37837
 
37205
37838
  // ../../packages/api-contracts/src/contracts/v1/pr-vulnerability.ts
37206
- var c57 = initContract();
37207
- var prVulnerabilityContract = c57.router(
37839
+ var c60 = initContract();
37840
+ var prVulnerabilityContract = c60.router(
37208
37841
  {
37209
37842
  listPrVulnerabilities: {
37210
37843
  method: "GET",
@@ -39913,12 +40546,12 @@ function buildApplicationsTable(apps) {
39913
40546
  repos: String(a2.repositories.length),
39914
40547
  url: a2.applicationUrl ? sanitizeDisplay(a2.applicationUrl) : "\u2014"
39915
40548
  }));
39916
- const idWidth = Math.max(2, ...cells.map((c59) => c59.id.length));
39917
- const nameWidth = Math.max(4, ...cells.map((c59) => c59.name.length));
39918
- const typeWidth = Math.max(4, ...cells.map((c59) => c59.type.length));
40549
+ const idWidth = Math.max(2, ...cells.map((c62) => c62.id.length));
40550
+ const nameWidth = Math.max(4, ...cells.map((c62) => c62.name.length));
40551
+ const typeWidth = Math.max(4, ...cells.map((c62) => c62.type.length));
39919
40552
  const header = `${"ID".padEnd(idWidth)} ${"NAME".padEnd(nameWidth)} ${"TYPE".padEnd(typeWidth)} REPOS URL`;
39920
40553
  const rows = cells.map(
39921
- (c59) => `${c59.id.padEnd(idWidth)} ${c59.name.padEnd(nameWidth)} ${c59.type.padEnd(typeWidth)} ${c59.repos.padStart(5)} ${c59.url}`
40554
+ (c62) => `${c62.id.padEnd(idWidth)} ${c62.name.padEnd(nameWidth)} ${c62.type.padEnd(typeWidth)} ${c62.repos.padStart(5)} ${c62.url}`
39922
40555
  );
39923
40556
  return { header, rows };
39924
40557
  }
@@ -39938,12 +40571,12 @@ function buildApplicationRepositoriesTable(links) {
39938
40571
  path: l2.repositoryPath ? sanitizeDisplay(l2.repositoryPath) : "\u2014",
39939
40572
  status: formatStatus(l2.repositoryStatus)
39940
40573
  }));
39941
- const nameWidth = Math.max(10, ...cells.map((c59) => c59.name.length));
39942
- const roleWidth = Math.max(4, ...cells.map((c59) => c59.role.length));
39943
- const pathWidth = Math.max(4, ...cells.map((c59) => c59.path.length));
40574
+ const nameWidth = Math.max(10, ...cells.map((c62) => c62.name.length));
40575
+ const roleWidth = Math.max(4, ...cells.map((c62) => c62.role.length));
40576
+ const pathWidth = Math.max(4, ...cells.map((c62) => c62.path.length));
39944
40577
  const header = `${"REPOSITORY".padEnd(nameWidth)} SCAN ${"ROLE".padEnd(roleWidth)} ${"PATH".padEnd(pathWidth)} STATUS`;
39945
40578
  const rows = cells.map(
39946
- (c59) => `${c59.name.padEnd(nameWidth)} ${c59.scan.padEnd(4)} ${c59.role.padEnd(roleWidth)} ${c59.path.padEnd(pathWidth)} ${c59.status}`
40579
+ (c62) => `${c62.name.padEnd(nameWidth)} ${c62.scan.padEnd(4)} ${c62.role.padEnd(roleWidth)} ${c62.path.padEnd(pathWidth)} ${c62.status}`
39947
40580
  );
39948
40581
  return { header, rows };
39949
40582
  }
@@ -40728,8 +41361,8 @@ function wrapTextWithPrefix(e, r2, o2, t2 = o2, s = o2, n3) {
40728
41361
  hard: true,
40729
41362
  trim: false
40730
41363
  }).split(`
40731
- `).map((c59, i2, m2) => {
40732
- const d = n3 ? n3(c59, i2) : c59;
41364
+ `).map((c62, i2, m2) => {
41365
+ const d = n3 ? n3(c62, i2) : c62;
40733
41366
  return i2 === 0 ? `${t2}${d}` : i2 === m2.length - 1 ? `${s}${d}` : `${o2}${d}`;
40734
41367
  }).join(`
40735
41368
  `);
@@ -41227,14 +41860,14 @@ var limitOptions = ({
41227
41860
  Math.min(l2 - a2 + 3, e.length - a2),
41228
41861
  0
41229
41862
  ));
41230
- let d = a2 < e.length && f > 0, c59 = a2 < e.length && f + a2 < e.length;
41863
+ let d = a2 < e.length && f > 0, c62 = a2 < e.length && f + a2 < e.length;
41231
41864
  const W = Math.min(
41232
41865
  f + a2,
41233
41866
  e.length
41234
41867
  ), s = [];
41235
41868
  let g = 0;
41236
- d && g++, c59 && g++;
41237
- const T = f + (d ? 1 : 0), y = W - (c59 ? 1 : 0);
41869
+ d && g++, c62 && g++;
41870
+ const T = f + (d ? 1 : 0), y = W - (c62 ? 1 : 0);
41238
41871
  for (let t2 = T; t2 < y; t2++) {
41239
41872
  const n3 = e[t2], o2 = n3 ? w(n3, t2 === l2) : "", h2 = wrapAnsi(o2, i2, {
41240
41873
  hard: true,
@@ -41255,14 +41888,14 @@ var limitOptions = ({
41255
41888
  u4,
41256
41889
  true
41257
41890
  );
41258
- d ? ({ lineCount: o2, removals: t2 } = L(), o2 > u4 && (c59 || (u4 -= 1), { lineCount: o2, removals: n3 } = E())) : (c59 || (u4 -= 1), { lineCount: o2, removals: n3 } = E(), o2 > u4 && (u4 -= 1, { lineCount: o2, removals: t2 } = L())), t2 > 0 && (d = true, s.splice(0, t2)), n3 > 0 && (c59 = true, s.splice(s.length - n3, n3));
41891
+ d ? ({ lineCount: o2, removals: t2 } = L(), o2 > u4 && (c62 || (u4 -= 1), { lineCount: o2, removals: n3 } = E())) : (c62 || (u4 -= 1), { lineCount: o2, removals: n3 } = E(), o2 > u4 && (u4 -= 1, { lineCount: o2, removals: t2 } = L())), t2 > 0 && (d = true, s.splice(0, t2)), n3 > 0 && (c62 = true, s.splice(s.length - n3, n3));
41259
41892
  }
41260
41893
  const x = [];
41261
41894
  d && x.push(M);
41262
41895
  for (const t2 of s)
41263
41896
  for (const n3 of t2)
41264
41897
  x.push(n3);
41265
- return c59 && x.push(M), x;
41898
+ return c62 && x.push(M), x;
41266
41899
  };
41267
41900
  var confirm = (i2) => {
41268
41901
  const a2 = i2.active ?? "Yes", s = i2.inactive ?? "No";
@@ -41281,15 +41914,15 @@ var confirm = (i2) => {
41281
41914
  u4
41282
41915
  ), o2 = `${e ? `${styleText2("gray", S_BAR)}
41283
41916
  ` : ""}${f}
41284
- `, c59 = this.value ? a2 : s;
41917
+ `, c62 = this.value ? a2 : s;
41285
41918
  switch (this.state) {
41286
41919
  case "submit": {
41287
41920
  const r2 = e ? `${styleText2("gray", S_BAR)} ` : "";
41288
- return `${o2}${r2}${styleText2("dim", c59)}`;
41921
+ return `${o2}${r2}${styleText2("dim", c62)}`;
41289
41922
  }
41290
41923
  case "cancel": {
41291
41924
  const r2 = e ? `${styleText2("gray", S_BAR)} ` : "";
41292
- return `${o2}${r2}${styleText2(["strikethrough", "dim"], c59)}${e ? `
41925
+ return `${o2}${r2}${styleText2(["strikethrough", "dim"], c62)}${e ? `
41293
41926
  ${styleText2("gray", S_BAR)}` : ""}`;
41294
41927
  }
41295
41928
  default: {
@@ -41437,13 +42070,13 @@ var multiline = (e) => new h({
41437
42070
  `, m2 = e.placeholder && e.placeholder.length > 0 ? (
41438
42071
  // biome-ignore lint/style/noNonNullAssertion: guarded by placeholder.length > 0
41439
42072
  styleText2("inverse", e.placeholder[0]) + styleText2("dim", e.placeholder.slice(1))
41440
- ) : styleText2(["inverse", "hidden"], "_"), a2 = this.userInput ? this.userInputWithCursor : m2, l2 = this.value ?? "", c59 = e.showSubmit ? `
42073
+ ) : styleText2(["inverse", "hidden"], "_"), a2 = this.userInput ? this.userInputWithCursor : m2, l2 = this.value ?? "", c62 = e.showSubmit ? `
41441
42074
  ${styleText2(this.focused === "submit" ? "cyan" : "dim", "[ submit ]")}` : "";
41442
42075
  switch (this.state) {
41443
42076
  case "error": {
41444
42077
  const n3 = `${styleText2("yellow", S_BAR)} `, r2 = i2 ? wrapTextWithPrefix(e.output, a2, n3, void 0) : a2, u4 = styleText2("yellow", S_BAR_END);
41445
42078
  return `${o2}${r2}
41446
- ${u4} ${styleText2("yellow", this.error)}${c59}
42079
+ ${u4} ${styleText2("yellow", this.error)}${c62}
41447
42080
  `;
41448
42081
  }
41449
42082
  case "submit": {
@@ -41471,7 +42104,7 @@ ${u4} ${styleText2("yellow", this.error)}${c59}
41471
42104
  default: {
41472
42105
  const n3 = i2 ? `${styleText2("cyan", S_BAR)} ` : "", r2 = i2 ? styleText2("cyan", S_BAR_END) : "", u4 = i2 ? wrapTextWithPrefix(e.output, a2, n3) : a2;
41473
42106
  return `${o2}${u4}
41474
- ${r2}${c59}
42107
+ ${r2}${c62}
41475
42108
  `;
41476
42109
  }
41477
42110
  }
@@ -41486,7 +42119,7 @@ var password = (r2) => new u$1({
41486
42119
  render() {
41487
42120
  const e = r2.withGuide ?? settings.withGuide, o2 = `${e ? `${styleText2("gray", S_BAR)}
41488
42121
  ` : ""}${symbol2(this.state)} ${r2.message}
41489
- `, c59 = this.userInputWithCursor, i2 = this.masked;
42122
+ `, c62 = this.userInputWithCursor, i2 = this.masked;
41490
42123
  switch (this.state) {
41491
42124
  case "error": {
41492
42125
  const s = e ? `${styleText2("yellow", S_BAR)} ` : "", n3 = e ? `${styleText2("yellow", S_BAR_END)} ` : "", l2 = i2 ?? "";
@@ -41506,7 +42139,7 @@ ${styleText2("gray", S_BAR)}` : ""}`;
41506
42139
  }
41507
42140
  default: {
41508
42141
  const s = e ? `${styleText2("cyan", S_BAR)} ` : "", n3 = e ? styleText2("cyan", S_BAR_END) : "";
41509
- return `${o2}${s}${c59}
42142
+ return `${o2}${s}${c62}
41510
42143
  ${n3}
41511
42144
  `;
41512
42145
  }
@@ -41522,7 +42155,7 @@ var SELECT_INSTRUCTIONS = [
41522
42155
  `${styleText2("dim", "\u2191/\u2193")} to navigate`,
41523
42156
  `${styleText2("dim", "Enter:")} confirm`
41524
42157
  ];
41525
- var c58 = (t2, o2) => t2.includes(`
42158
+ var c61 = (t2, o2) => t2.includes(`
41526
42159
  `) ? t2.split(`
41527
42160
  `).map((d) => o2(d)).join(`
41528
42161
  `) : o2(t2);
@@ -41533,15 +42166,15 @@ var select = (t2) => {
41533
42166
  const s = n3.label ?? String(n3.value);
41534
42167
  switch (m2) {
41535
42168
  case "disabled":
41536
- return `${styleText2("gray", S_RADIO_INACTIVE)} ${c58(s, (i2) => styleText2("gray", i2))}${n3.hint ? ` ${styleText2("dim", `(${n3.hint ?? "disabled"})`)}` : ""}`;
42169
+ return `${styleText2("gray", S_RADIO_INACTIVE)} ${c61(s, (i2) => styleText2("gray", i2))}${n3.hint ? ` ${styleText2("dim", `(${n3.hint ?? "disabled"})`)}` : ""}`;
41537
42170
  case "selected":
41538
- return `${c58(s, (i2) => styleText2("dim", i2))}`;
42171
+ return `${c61(s, (i2) => styleText2("dim", i2))}`;
41539
42172
  case "active":
41540
42173
  return `${styleText2("green", S_RADIO_ACTIVE)} ${s}${n3.hint ? ` ${styleText2("dim", `(${n3.hint})`)}` : ""}`;
41541
42174
  case "cancelled":
41542
- return `${c58(s, (i2) => styleText2(["strikethrough", "dim"], i2))}`;
42175
+ return `${c61(s, (i2) => styleText2(["strikethrough", "dim"], i2))}`;
41543
42176
  default:
41544
- return `${styleText2("dim", S_RADIO_INACTIVE)} ${c58(s, (i2) => styleText2("dim", i2))}`;
42177
+ return `${styleText2("dim", S_RADIO_INACTIVE)} ${c61(s, (i2) => styleText2("dim", i2))}`;
41545
42178
  }
41546
42179
  }, d = t2.showInstructions ?? true;
41547
42180
  return new n$1({
@@ -42921,7 +43554,7 @@ function extractCommandTree(cmd) {
42921
43554
  description: o2.description,
42922
43555
  takesValue: o2.required || o2.optional
42923
43556
  }));
42924
- const subcommands = cmd.commands.filter((c59) => !c59._hidden).map((c59) => extractCommandTree(c59));
43557
+ const subcommands = cmd.commands.filter((c62) => !c62._hidden).map((c62) => extractCommandTree(c62));
42925
43558
  return {
42926
43559
  name: cmd.name(),
42927
43560
  description: cmd.description(),
@@ -42987,7 +43620,7 @@ function generateBashCompletion(tree, binName) {
42987
43620
  }
42988
43621
  function collectBashCases(tree, prefix) {
42989
43622
  const cases = /* @__PURE__ */ new Map();
42990
- const subcommandNames = tree.subcommands.map((c59) => c59.name);
43623
+ const subcommandNames = tree.subcommands.map((c62) => c62.name);
42991
43624
  const optionNames = tree.options.flatMap((o2) => o2.short ? [o2.long, o2.short] : [o2.long]);
42992
43625
  const extraOptions = prefix === "" ? ["--help", "--version"] : ["--help"];
42993
43626
  const allOptions = [.../* @__PURE__ */ new Set([...optionNames, ...extraOptions])];
@@ -43402,9 +44035,9 @@ function buildInboxesTable(inboxes) {
43402
44035
  address: sanitizeDisplay(inbox.address),
43403
44036
  label: sanitizeDisplay(inbox.label)
43404
44037
  }));
43405
- const addressWidth = Math.max(7, ...cells.map((c59) => c59.address.length));
44038
+ const addressWidth = Math.max(7, ...cells.map((c62) => c62.address.length));
43406
44039
  const header = `${"ADDRESS".padEnd(addressWidth)} LABEL`;
43407
- const rows = cells.map((c59) => `${c59.address.padEnd(addressWidth)} ${c59.label}`);
44040
+ const rows = cells.map((c62) => `${c62.address.padEnd(addressWidth)} ${c62.label}`);
43408
44041
  return { header, rows };
43409
44042
  }
43410
44043
  function formatInboxQuota(quota) {
@@ -43428,14 +44061,14 @@ function buildInboxEmailsTable(messages) {
43428
44061
  subject: sanitizeDisplay(message.subject ?? ""),
43429
44062
  unread: message.unread ? "yes" : "no"
43430
44063
  }));
43431
- const idWidth = Math.max(2, ...cells.map((c59) => c59.id.length));
43432
- const receivedWidth = Math.max(8, ...cells.map((c59) => c59.received.length));
43433
- const fromWidth = Math.max(4, ...cells.map((c59) => c59.from.length));
43434
- const toWidth = Math.max(2, ...cells.map((c59) => c59.to.length));
43435
- const subjectWidth = Math.max(7, ...cells.map((c59) => c59.subject.length));
44064
+ const idWidth = Math.max(2, ...cells.map((c62) => c62.id.length));
44065
+ const receivedWidth = Math.max(8, ...cells.map((c62) => c62.received.length));
44066
+ const fromWidth = Math.max(4, ...cells.map((c62) => c62.from.length));
44067
+ const toWidth = Math.max(2, ...cells.map((c62) => c62.to.length));
44068
+ const subjectWidth = Math.max(7, ...cells.map((c62) => c62.subject.length));
43436
44069
  const header = `${"ID".padEnd(idWidth)} ${"RECEIVED".padEnd(receivedWidth)} ${"FROM".padEnd(fromWidth)} ${"TO".padEnd(toWidth)} ${"SUBJECT".padEnd(subjectWidth)} UNREAD`;
43437
44070
  const rows = cells.map(
43438
- (c59) => `${c59.id.padEnd(idWidth)} ${c59.received.padEnd(receivedWidth)} ${c59.from.padEnd(fromWidth)} ${c59.to.padEnd(toWidth)} ${c59.subject.padEnd(subjectWidth)} ${c59.unread}`
44071
+ (c62) => `${c62.id.padEnd(idWidth)} ${c62.received.padEnd(receivedWidth)} ${c62.from.padEnd(fromWidth)} ${c62.to.padEnd(toWidth)} ${c62.subject.padEnd(subjectWidth)} ${c62.unread}`
43439
44072
  );
43440
44073
  return { header, rows };
43441
44074
  }
@@ -43841,7 +44474,9 @@ var CREDENTIAL_TYPE_BY_INPUT = {
43841
44474
  "magic-auth": "MAGIC_AUTH",
43842
44475
  magic_auth: "MAGIC_AUTH",
43843
44476
  "session-state": "SESSION_STATE",
43844
- session_state: "SESSION_STATE"
44477
+ session_state: "SESSION_STATE",
44478
+ "ssh-private-key": "SSH_PRIVATE_KEY",
44479
+ ssh_private_key: "SSH_PRIVATE_KEY"
43845
44480
  };
43846
44481
  function normalizeCredentialType(input) {
43847
44482
  return CREDENTIAL_TYPE_BY_INPUT[input.trim().toLowerCase()] ?? null;
@@ -43863,7 +44498,11 @@ var AUTH_FLAG_KEBAB = {
43863
44498
  passwordField: "password-field",
43864
44499
  sessionCookieName: "session-cookie-name",
43865
44500
  csrfTokenSelector: "csrf-token-selector",
43866
- csrfFieldName: "csrf-field-name"
44501
+ csrfFieldName: "csrf-field-name",
44502
+ sshHost: "ssh-host",
44503
+ sshPort: "ssh-port",
44504
+ sshUsername: "ssh-username",
44505
+ sshHostKeyFingerprint: "ssh-host-key-fingerprint"
43867
44506
  };
43868
44507
  var ALL_AUTH_KEYS = Object.keys(AUTH_FLAG_KEBAB);
43869
44508
  var ALLOWED_AUTH_KEYS = {
@@ -43883,7 +44522,8 @@ var ALLOWED_AUTH_KEYS = {
43883
44522
  COOKIE: ["cookieName", "cookieDomain", "cookiePath"],
43884
44523
  MAGIC_AUTH: [],
43885
44524
  // SESSION_STATE takes no authConfig flags — origin is server-derived from the blob.
43886
- SESSION_STATE: []
44525
+ SESSION_STATE: [],
44526
+ SSH_PRIVATE_KEY: ["sshHost", "sshPort", "sshUsername", "sshHostKeyFingerprint"]
43887
44527
  };
43888
44528
  function rejectForeignAuthFlags(type, opts) {
43889
44529
  const allowed = new Set(ALLOWED_AUTH_KEYS[type]);
@@ -43927,6 +44567,13 @@ function authConfigToOptions(authConfig) {
43927
44567
  return {};
43928
44568
  case "SESSION_STATE":
43929
44569
  return {};
44570
+ case "SSH_PRIVATE_KEY":
44571
+ return {
44572
+ sshHost: authConfig.host,
44573
+ sshPort: String(authConfig.port),
44574
+ sshUsername: authConfig.username,
44575
+ ...authConfig.hostKeyFingerprint !== void 0 ? { sshHostKeyFingerprint: authConfig.hostKeyFingerprint } : {}
44576
+ };
43930
44577
  case "USERNAME_PASSWORD":
43931
44578
  return {
43932
44579
  method: authConfig.method,
@@ -44020,6 +44667,34 @@ function buildUsernamePasswordAuthConfig(opts) {
44020
44667
  }
44021
44668
  };
44022
44669
  }
44670
+ function buildSshPrivateKeyAuthConfig(opts) {
44671
+ const hasAny = opts.sshHost !== void 0 || opts.sshPort !== void 0 || opts.sshUsername !== void 0 || opts.sshHostKeyFingerprint !== void 0;
44672
+ if (!hasAny) {
44673
+ return { value: void 0 };
44674
+ }
44675
+ if (opts.sshHost === void 0 || opts.sshUsername === void 0) {
44676
+ return { error: "--ssh-host and --ssh-username are required for ssh-private-key." };
44677
+ }
44678
+ let port = SSH_DEFAULT_PORT;
44679
+ if (opts.sshPort !== void 0) {
44680
+ if (!/^\d{1,5}$/.test(opts.sshPort.trim())) {
44681
+ return { error: "--ssh-port must be a whole number from 1 to 65535." };
44682
+ }
44683
+ port = Number(opts.sshPort.trim());
44684
+ if (port < 1 || port > 65535) {
44685
+ return { error: "--ssh-port must be a whole number from 1 to 65535." };
44686
+ }
44687
+ }
44688
+ return {
44689
+ value: {
44690
+ type: "SSH_PRIVATE_KEY",
44691
+ host: opts.sshHost,
44692
+ port,
44693
+ username: opts.sshUsername,
44694
+ ...opts.sshHostKeyFingerprint !== void 0 ? { hostKeyFingerprint: opts.sshHostKeyFingerprint } : {}
44695
+ }
44696
+ };
44697
+ }
44023
44698
  function buildAuthConfig(type, opts) {
44024
44699
  switch (type) {
44025
44700
  case "BEARER_TOKEN":
@@ -44034,6 +44709,8 @@ function buildAuthConfig(type, opts) {
44034
44709
  return buildMagicAuthAuthConfig(opts);
44035
44710
  case "USERNAME_PASSWORD":
44036
44711
  return buildUsernamePasswordAuthConfig(opts);
44712
+ case "SSH_PRIVATE_KEY":
44713
+ return buildSshPrivateKeyAuthConfig(opts);
44037
44714
  case "PASSPHRASE":
44038
44715
  case "SESSION_STATE":
44039
44716
  return { value: void 0 };
@@ -44042,6 +44719,9 @@ function buildAuthConfig(type, opts) {
44042
44719
  function anySecretFlag(opts) {
44043
44720
  return opts.secretStdin === true || opts.secretFile !== void 0 || opts.secretEnv !== void 0 || opts.usernameStdin === true || opts.usernameFile !== void 0 || opts.usernameEnv !== void 0 || opts.passwordStdin === true || opts.passwordFile !== void 0 || opts.passwordEnv !== void 0;
44044
44721
  }
44722
+ function anyPasswordFlag(opts) {
44723
+ return opts.passwordStdin === true || opts.passwordFile !== void 0 || opts.passwordEnv !== void 0;
44724
+ }
44045
44725
  function secretOpts(opts) {
44046
44726
  return { stdin: opts.secretStdin, file: opts.secretFile, env: opts.secretEnv };
44047
44727
  }
@@ -44163,16 +44843,16 @@ function printCredentialsTsv(creds) {
44163
44843
  }
44164
44844
  }
44165
44845
  function buildCredentialsTable(creds) {
44166
- const cells = creds.map((c59) => ({
44167
- id: c59.credentialId,
44168
- label: sanitizeDisplay(c59.label),
44169
- type: c59.credentialType
44846
+ const cells = creds.map((c62) => ({
44847
+ id: c62.credentialId,
44848
+ label: sanitizeDisplay(c62.label),
44849
+ type: c62.credentialType
44170
44850
  }));
44171
- const idWidth = Math.max(2, ...cells.map((c59) => c59.id.length));
44172
- const labelWidth = Math.max(5, ...cells.map((c59) => c59.label.length));
44851
+ const idWidth = Math.max(2, ...cells.map((c62) => c62.id.length));
44852
+ const labelWidth = Math.max(5, ...cells.map((c62) => c62.label.length));
44173
44853
  const header = `${"ID".padEnd(idWidth)} ${"LABEL".padEnd(labelWidth)} TYPE`;
44174
44854
  const rows = cells.map(
44175
- (c59) => `${c59.id.padEnd(idWidth)} ${c59.label.padEnd(labelWidth)} ${c59.type}`
44855
+ (c62) => `${c62.id.padEnd(idWidth)} ${c62.label.padEnd(labelWidth)} ${c62.type}`
44176
44856
  );
44177
44857
  return { header, rows };
44178
44858
  }
@@ -44203,6 +44883,14 @@ function failAuthBuild(result) {
44203
44883
  console.error(colorError(result.error));
44204
44884
  process.exit(1);
44205
44885
  }
44886
+ function requireSshAuthConfig(options) {
44887
+ const ac = buildSshPrivateKeyAuthConfig(options);
44888
+ if ("error" in ac) failAuthBuild(ac);
44889
+ if (ac.value === void 0) {
44890
+ failAuthBuild({ error: "--ssh-host and --ssh-username are required for ssh-private-key." });
44891
+ }
44892
+ return ac.value;
44893
+ }
44206
44894
  async function runCreate3(options) {
44207
44895
  const output = parseOutput(options.output);
44208
44896
  requireEnvId(options.env);
@@ -44226,6 +44914,17 @@ async function runCreate3(options) {
44226
44914
  );
44227
44915
  process.exit(1);
44228
44916
  }
44917
+ if (type === "SSH_PRIVATE_KEY") {
44918
+ requireSshAuthConfig(options);
44919
+ if (usesStdin(secretOpts(options)) && usesStdin(passwordOpts(options))) {
44920
+ console.error(
44921
+ colorError(
44922
+ "Only one secret can come from stdin. Use --password-file/-env for the passphrase."
44923
+ )
44924
+ );
44925
+ process.exit(1);
44926
+ }
44927
+ }
44229
44928
  const organizationId = await resolveOrganizationId();
44230
44929
  const params = { organizationId, environmentId: options.env };
44231
44930
  const label = options.label?.trim() ? options.label.trim() : void 0;
@@ -44298,6 +44997,18 @@ async function buildCreateBody(type, label, options) {
44298
44997
  );
44299
44998
  return { credentialType: "SESSION_STATE", ...labelFields, secretValue };
44300
44999
  }
45000
+ case "SSH_PRIVATE_KEY": {
45001
+ const authConfig = requireSshAuthConfig(options);
45002
+ const privateKeyPem = await resolveSecret("private key PEM", "secret", secretOpts(options));
45003
+ const passphrase = anyPasswordFlag(options) ? await resolveSecret("key passphrase", "password", passwordOpts(options)) : void 0;
45004
+ return {
45005
+ credentialType: "SSH_PRIVATE_KEY",
45006
+ ...labelFields,
45007
+ privateKeyPem,
45008
+ ...passphrase !== void 0 ? { passphrase } : {},
45009
+ authConfig
45010
+ };
45011
+ }
44301
45012
  case "COOKIE": {
44302
45013
  const ac = buildCookieAuthConfig(options);
44303
45014
  if ("error" in ac) failAuthBuild(ac);
@@ -44580,12 +45291,15 @@ function addSecretInputOptions(command) {
44580
45291
  return command.option("--secret-stdin", "Read the secret value from stdin").option("--secret-file <path>", "Read the secret value from a file").option("--secret-env <name>", "Read the secret value from an env var").option("--username-stdin", "Read the username from stdin").option("--username-file <path>", "Read the username from a file").option("--username-env <name>", "Read the username from an env var").option("--password-stdin", "Read the password from stdin").option("--password-file <path>", "Read the password from a file").option("--password-env <name>", "Read the password from an env var");
44581
45292
  }
44582
45293
  function addAuthConfigOptions(command) {
44583
- return command.option("--header-name <name>", "authConfig: header name (bearer-token/jwt-token)").option("--token-prefix <prefix>", "authConfig: token prefix (bearer-token/jwt-token)").option("--placement <header|query>", "authConfig: API key placement (api-key)").option("--key-name <name>", "authConfig: API key name (api-key)").option("--cookie-name <name>", "authConfig: cookie name (cookie)").option("--cookie-domain <domain>", "authConfig: cookie domain (cookie)").option("--cookie-path <path>", "authConfig: cookie path (cookie)").option("--method <form|basic|json>", "authConfig: login method (username-password)").option("--login-url <url>", "authConfig: login URL (username-password)").option("--username-field <name>", "authConfig: username field (username-password)").option("--password-field <name>", "authConfig: password field (username-password)").option("--session-cookie-name <name>", "authConfig: session cookie (username-password)").option("--csrf-token-selector <sel>", "authConfig: CSRF token selector (username-password)").option("--csrf-field-name <name>", "authConfig: CSRF field name (username-password)");
45294
+ return command.option("--header-name <name>", "authConfig: header name (bearer-token/jwt-token)").option("--token-prefix <prefix>", "authConfig: token prefix (bearer-token/jwt-token)").option("--placement <header|query>", "authConfig: API key placement (api-key)").option("--key-name <name>", "authConfig: API key name (api-key)").option("--cookie-name <name>", "authConfig: cookie name (cookie)").option("--cookie-domain <domain>", "authConfig: cookie domain (cookie)").option("--cookie-path <path>", "authConfig: cookie path (cookie)").option("--method <form|basic|json>", "authConfig: login method (username-password)").option("--login-url <url>", "authConfig: login URL (username-password)").option("--username-field <name>", "authConfig: username field (username-password)").option("--password-field <name>", "authConfig: password field (username-password)").option("--session-cookie-name <name>", "authConfig: session cookie (username-password)").option("--csrf-token-selector <sel>", "authConfig: CSRF token selector (username-password)").option("--csrf-field-name <name>", "authConfig: CSRF field name (username-password)").option("--ssh-host <host>", "authConfig: SSH host (ssh-private-key, required)").option("--ssh-port <port>", "authConfig: SSH port, 1-65535 (ssh-private-key, default 22)").option("--ssh-username <user>", "authConfig: SSH username (ssh-private-key, required)").option(
45295
+ "--ssh-host-key-fingerprint <fp>",
45296
+ "authConfig: expected host key, SHA256:<base64> (ssh-private-key)"
45297
+ );
44584
45298
  }
44585
45299
  function createCreateCommand3() {
44586
45300
  const command = new Command("create").description("Create a credential in an environment").requiredOption("--env <environmentId>", "Parent environment UUID").requiredOption(
44587
45301
  "--type <type>",
44588
- "bearer-token, jwt-token, api-key, passphrase, username-password, cookie, magic-auth"
45302
+ "bearer-token, jwt-token, api-key, passphrase, username-password, cookie, magic-auth, session-state, ssh-private-key"
44589
45303
  ).option(
44590
45304
  "--label <label>",
44591
45305
  "Human-readable label (optional; defaults from type / username + environment)"
@@ -44602,7 +45316,9 @@ Examples:
44602
45316
  tracecli credential create --env <envId> --type api-key --label "prod key" --secret-env API_KEY --placement header --key-name X-API-Key
44603
45317
  tracecli credential create --env <envId> --type username-password --username-env U --password-env P --method form --login-url https://acme.com/login
44604
45318
  ADDRESS=$(tracecli inbox create portal -o json | jq -r .address)
44605
- echo "$ADDRESS" | tracecli credential create --env <envId> --type magic-auth --secret-stdin`
45319
+ echo "$ADDRESS" | tracecli credential create --env <envId> --type magic-auth --secret-stdin
45320
+ tracecli credential create --env <envId> --type ssh-private-key --secret-file ~/.ssh/bastion_ed25519 --ssh-host bastion.example.com --ssh-username deploy
45321
+ tracecli credential create --env <envId> --type ssh-private-key --secret-file ./key.pem --password-env KEY_PASSPHRASE --ssh-host 10.0.0.5 --ssh-port 2222 --ssh-username ops`
44606
45322
  );
44607
45323
  return command.action(async (options) => {
44608
45324
  await runCreate3(options);
@@ -44900,14 +45616,14 @@ function buildEnvironmentTable(envs, leading) {
44900
45616
  url: e.primaryUrl != null ? sanitizeDisplay(e.primaryUrl) : "\u2014"
44901
45617
  }));
44902
45618
  const leadingWidths = leading.map(
44903
- (column, i2) => Math.max(column.header.length, ...cells.map((c59) => c59.leading[i2]?.length ?? 0))
45619
+ (column, i2) => Math.max(column.header.length, ...cells.map((c62) => c62.leading[i2]?.length ?? 0))
44904
45620
  );
44905
- const nameWidth = Math.max(4, ...cells.map((c59) => c59.name.length));
45621
+ const nameWidth = Math.max(4, ...cells.map((c62) => c62.name.length));
44906
45622
  const padLeading = (values) => values.map((v, i2) => v.padEnd(leadingWidths[i2] ?? 0)).join(" ");
44907
45623
  return {
44908
- header: `${padLeading(leading.map((c59) => c59.header))} ${"NAME".padEnd(nameWidth)} PROD URL`,
45624
+ header: `${padLeading(leading.map((c62) => c62.header))} ${"NAME".padEnd(nameWidth)} PROD URL`,
44909
45625
  rows: cells.map(
44910
- (c59) => `${padLeading(c59.leading)} ${c59.name.padEnd(nameWidth)} ${c59.prod.padEnd(4)} ${c59.url}`
45626
+ (c62) => `${padLeading(c62.leading)} ${c62.name.padEnd(nameWidth)} ${c62.prod.padEnd(4)} ${c62.url}`
44911
45627
  )
44912
45628
  };
44913
45629
  }
@@ -47877,13 +48593,13 @@ function buildUploadsTable(uploads) {
47877
48593
  size: formatArchiveBytes(upload.sizeBytes),
47878
48594
  id: upload.codeUploadId
47879
48595
  }));
47880
- const nameWidth = Math.max(4, ...cells.map((c59) => c59.name.length));
47881
- const kindWidth = Math.max(4, ...cells.map((c59) => c59.kind.length));
47882
- const statusWidth = Math.max(6, ...cells.map((c59) => c59.status.length));
47883
- const sizeWidth = Math.max(4, ...cells.map((c59) => c59.size.length));
48596
+ const nameWidth = Math.max(4, ...cells.map((c62) => c62.name.length));
48597
+ const kindWidth = Math.max(4, ...cells.map((c62) => c62.kind.length));
48598
+ const statusWidth = Math.max(6, ...cells.map((c62) => c62.status.length));
48599
+ const sizeWidth = Math.max(4, ...cells.map((c62) => c62.size.length));
47884
48600
  const header = `${"NAME".padEnd(nameWidth)} ${"KIND".padEnd(kindWidth)} ${"STATUS".padEnd(statusWidth)} ${"SIZE".padEnd(sizeWidth)} ID`;
47885
48601
  const rows = cells.map(
47886
- (c59) => `${c59.name.padEnd(nameWidth)} ${c59.kind.padEnd(kindWidth)} ${c59.status.padEnd(statusWidth)} ${c59.size.padEnd(sizeWidth)} ${c59.id}`
48602
+ (c62) => `${c62.name.padEnd(nameWidth)} ${c62.kind.padEnd(kindWidth)} ${c62.status.padEnd(statusWidth)} ${c62.size.padEnd(sizeWidth)} ${c62.id}`
47887
48603
  );
47888
48604
  return { header, rows };
47889
48605
  }