securewithtrace 0.37.0 → 0.39.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 (3) hide show
  1. package/README.md +40 -1
  2. package/dist/index.js +1618 -381
  3. 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, (c58) => c58 === "-" ? "+" : "/");
6279
+ const base643 = data.replace(/[-_]/g, (c59) => c59 === "-" ? "+" : "/");
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 c58 = inst._zod.bag;
16324
- inst.minDate = c58.minimum ? new Date(c58.minimum) : null;
16325
- inst.maxDate = c58.maximum ? new Date(c58.maximum) : null;
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;
16326
16326
  });
16327
16327
  function date3(params) {
16328
16328
  return _date(ZodDate, params);
@@ -21998,7 +21998,7 @@ var PentestEditActionValues = [
21998
21998
  "DELIVER"
21999
21999
  ];
22000
22000
  var PentestEditAction = external_exports.enum(PentestEditActionValues);
22001
- var PentestAuditActorTypeValues = ["USER", "SYSTEM", "AGENT"];
22001
+ var PentestAuditActorTypeValues = ["USER", "SYSTEM", "AGENT", "API_KEY"];
22002
22002
  var PentestAuditActorType = external_exports.enum(PentestAuditActorTypeValues);
22003
22003
  var PentestHypothesisStatusValues = [
22004
22004
  "DRAFTED",
@@ -23008,6 +23008,263 @@ var LinearTicketContextResponseSchema = external_exports.object({
23008
23008
  var AttestationClassificationValues = ["PUBLIC", "CONFIDENTIAL"];
23009
23009
  var AttestationClassification = external_exports.enum(AttestationClassificationValues);
23010
23010
 
23011
+ // ../../packages/api-contracts/src/schemas/api/v1/repository.api.ts
23012
+ var PlatformValues = ["GITHUB", "GITLAB", "BITBUCKET", "UPLOAD"];
23013
+ var RepositoryStatusValues = ["CONNECTED", "DISCONNECTED"];
23014
+ var RepositoryIdParamsSchema = OrgIdParamsSchema.extend({
23015
+ repositoryId: external_exports.uuid()
23016
+ });
23017
+ var CreateRepositoryRequestSchema = external_exports.object({
23018
+ repositoryUrl: external_exports.url(),
23019
+ platform: external_exports.enum(PlatformValues),
23020
+ githubDefaultBranch: external_exports.string().min(1).max(255).optional(),
23021
+ status: external_exports.enum(RepositoryStatusValues).optional()
23022
+ });
23023
+ var UpdateRepositoryRequestSchema = external_exports.object({
23024
+ repositoryUrl: external_exports.url().optional(),
23025
+ platform: external_exports.enum(PlatformValues).optional(),
23026
+ githubDefaultBranch: external_exports.string().min(1).max(255).optional(),
23027
+ status: external_exports.enum(RepositoryStatusValues).optional()
23028
+ });
23029
+ var RepositoryResponseSchema = external_exports.object({
23030
+ repositoryId: external_exports.uuid(),
23031
+ organizationId: external_exports.uuid(),
23032
+ repositoryUrl: external_exports.url(),
23033
+ platform: external_exports.enum(PlatformValues),
23034
+ status: external_exports.enum(RepositoryStatusValues),
23035
+ githubDefaultBranch: external_exports.string(),
23036
+ scanningBranch: external_exports.string().nullable(),
23037
+ primaryLanguage: external_exports.string().nullable().optional(),
23038
+ /** Staff-set opt-in: automated PR scans run on this repo even without a FULL SAST baseline. */
23039
+ prScansEnabled: external_exports.boolean(),
23040
+ createdAt: external_exports.date(),
23041
+ updatedAt: external_exports.date()
23042
+ });
23043
+ var ListRepositoriesResponseSchema = external_exports.array(RepositoryResponseSchema);
23044
+ var SetPrScansEnabledRequestSchema = external_exports.object({
23045
+ enabled: external_exports.boolean()
23046
+ });
23047
+ var StaffRepositoryPrScanSettingSchema = external_exports.object({
23048
+ repositoryId: external_exports.uuid(),
23049
+ /** GitHub owner/name when synced; null for rows created before the sync backfill. */
23050
+ fullName: external_exports.string().nullable(),
23051
+ repositoryUrl: external_exports.url(),
23052
+ /** The staff-set opt-in arm of the gate. */
23053
+ prScansEnabled: external_exports.boolean(),
23054
+ /**
23055
+ * The implicit arm: a completed FULL SAST scan exists for this repo (any
23056
+ * branch). With the org capability granted, either arm open means PR scans run.
23057
+ */
23058
+ hasSastBaseline: external_exports.boolean()
23059
+ });
23060
+ var StaffPrScanSettingsResponseSchema = external_exports.object({
23061
+ /** Org-level gate: without AUTO_PR_SCANS the per-repo switches are inert. */
23062
+ orgHasAutoPrScans: external_exports.boolean(),
23063
+ repositories: external_exports.array(StaffRepositoryPrScanSettingSchema)
23064
+ });
23065
+ var CloneTokenRequestSchema = external_exports.object({
23066
+ repositoryUrl: external_exports.string().url()
23067
+ });
23068
+ var CloneTokenResponseSchema = external_exports.object({
23069
+ token: external_exports.string(),
23070
+ expiresAt: external_exports.string().datetime(),
23071
+ // Git username scheme to pair with this token. GitHub installation tokens
23072
+ // use `x-access-token`; Bitbucket app tokens use `x-token-auth`. Optional
23073
+ // for back-compat with CLIs that predate Bitbucket support and hardcode
23074
+ // `x-access-token` (the correct value for the GitHub-only flow they gate on).
23075
+ username: external_exports.enum(["x-access-token", "x-token-auth"]).optional()
23076
+ });
23077
+ var ScanningBranchHistoryEntrySchema = external_exports.object({
23078
+ historyId: external_exports.uuid(),
23079
+ branch: external_exports.string(),
23080
+ configuredAt: external_exports.date(),
23081
+ replacedAt: external_exports.date(),
23082
+ configuredByUserId: external_exports.uuid().nullable()
23083
+ });
23084
+ var ScanningBranchStateSchema = external_exports.object({
23085
+ scanningBranch: external_exports.string().nullable(),
23086
+ githubDefaultBranch: external_exports.string(),
23087
+ history: external_exports.array(ScanningBranchHistoryEntrySchema)
23088
+ });
23089
+ var SetScanningBranchRequestSchema = external_exports.object({
23090
+ branch: external_exports.string().min(1).max(255),
23091
+ migrateOpenVulnerabilities: external_exports.boolean(),
23092
+ /**
23093
+ * Optional compare-and-swap guard. When provided, the switch only commits
23094
+ * if the repo's current `scanningBranch` matches. Mismatch → HTTP 409.
23095
+ * `null` means "expect this repo to still be unconfigured."
23096
+ */
23097
+ expectedCurrentBranch: external_exports.string().nullable().optional()
23098
+ });
23099
+ var SetScanningBranchResponseSchema = external_exports.object({
23100
+ previousBranch: external_exports.string().nullable(),
23101
+ newBranch: external_exports.string(),
23102
+ relabeledVulnerabilityCount: external_exports.number().int().min(0),
23103
+ /** Null when previousBranch was null (initial configuration). */
23104
+ historyId: external_exports.uuid().nullable()
23105
+ });
23106
+ var BulkSetScanningBranchBranchSchema = external_exports.discriminatedUnion("kind", [
23107
+ external_exports.object({ kind: external_exports.literal("literal"), value: external_exports.string().min(1).max(255) }),
23108
+ external_exports.object({ kind: external_exports.literal("github-default") })
23109
+ ]);
23110
+ var BulkSetScanningBranchRequestSchema = external_exports.object({
23111
+ branch: BulkSetScanningBranchBranchSchema,
23112
+ repositoryIds: external_exports.array(external_exports.uuid()).min(1).max(100),
23113
+ migrateOpenVulnerabilities: external_exports.boolean()
23114
+ });
23115
+ var BulkSetScanningBranchResultStatusValues = [
23116
+ "switched",
23117
+ "branch-missing-on-github",
23118
+ "forbidden",
23119
+ "already-on-branch",
23120
+ "not-found",
23121
+ "error"
23122
+ ];
23123
+ var BulkSetScanningBranchResultSchema = external_exports.object({
23124
+ repositoryId: external_exports.uuid(),
23125
+ status: external_exports.enum(BulkSetScanningBranchResultStatusValues),
23126
+ relabeledVulnerabilityCount: external_exports.number().int().min(0).optional(),
23127
+ historyId: external_exports.uuid().nullable().optional(),
23128
+ errorMessage: external_exports.string().optional()
23129
+ });
23130
+ var BulkSetScanningBranchResponseSchema = external_exports.object({
23131
+ results: external_exports.array(BulkSetScanningBranchResultSchema)
23132
+ });
23133
+ var ScanningBranchPreferenceSchema = external_exports.object({
23134
+ autoConfigureScanningBranch: external_exports.boolean()
23135
+ });
23136
+ var ConfirmScanningBranchDefaultsResponseSchema = external_exports.object({
23137
+ autoConfigureScanningBranch: external_exports.literal(true),
23138
+ /** Repos the sweep configured to their default-branch mirror. */
23139
+ configuredCount: external_exports.number().int().min(0),
23140
+ /**
23141
+ * Rows skipped by the sweep's domain guards (empty or >255-char mirror).
23142
+ * They stay NULL and keep the "waiting on a scanning branch" banner alive.
23143
+ */
23144
+ skippedCount: external_exports.number().int().min(0)
23145
+ });
23146
+
23147
+ // ../../packages/api-contracts/src/schemas/api/v1/application.api.ts
23148
+ var ApplicationTypeValues = ["WEB", "CLI", "MOBILE", "DESKTOP", "API_SERVICE"];
23149
+ var APPLICATION_NOTES_MAX_CHARS = 2e3;
23150
+ var ApplicationRepositoryRoleValues = [
23151
+ "FRONTEND",
23152
+ "BACKEND_API",
23153
+ "AUTH_SERVICE",
23154
+ "WORKER_JOB_SERVICE",
23155
+ "SHARED_PACKAGE_SDK",
23156
+ "INFRASTRUCTURE_IAC",
23157
+ "MOBILE_CLIENT",
23158
+ "MONOREPO",
23159
+ "OTHER"
23160
+ ];
23161
+ var ApplicationIdParamsSchema = OrgIdParamsSchema.extend({
23162
+ applicationId: external_exports.uuid()
23163
+ });
23164
+ var ApplicationRepositoryIdParamsSchema = ApplicationIdParamsSchema.extend({
23165
+ applicationRepositoryId: external_exports.uuid()
23166
+ });
23167
+ var ApplicationRepositorySummarySchema = external_exports.object({
23168
+ applicationRepositoryId: external_exports.uuid().nullable(),
23169
+ applicationId: external_exports.uuid(),
23170
+ organizationId: external_exports.uuid(),
23171
+ repositoryId: external_exports.uuid(),
23172
+ repositoryName: external_exports.string(),
23173
+ repositoryUrl: external_exports.string(),
23174
+ repositoryStatus: external_exports.enum(RepositoryStatusValues),
23175
+ htmlUrl: external_exports.string().nullable(),
23176
+ createdAt: external_exports.date(),
23177
+ updatedAt: external_exports.date()
23178
+ });
23179
+ var ApplicationRepositoryRolesSchema = external_exports.array(external_exports.enum(ApplicationRepositoryRoleValues)).max(ApplicationRepositoryRoleValues.length);
23180
+ var ApplicationRepositoryLinkResponseSchema = ApplicationRepositorySummarySchema.extend({
23181
+ // First of `roles` (enum order), or null when unspecified. Kept so a new
23182
+ // client reading a pre-this-release API during a rolling deploy still parses.
23183
+ role: external_exports.enum(ApplicationRepositoryRoleValues).nullable(),
23184
+ // Source of truth. Defaulted empty so a new client reading a pre-this-release
23185
+ // API that omitted the field doesn't reject the row; the client then falls
23186
+ // back to wrapping `role`.
23187
+ roles: ApplicationRepositoryRolesSchema.default([]),
23188
+ roleLabel: external_exports.string().max(120).nullable(),
23189
+ repositoryPath: external_exports.string().max(500).nullable(),
23190
+ scanEnabled: external_exports.boolean()
23191
+ });
23192
+ var ListApplicationRepositoryLinksResponseSchema = external_exports.array(
23193
+ ApplicationRepositoryLinkResponseSchema
23194
+ );
23195
+ var ApplicationResponseSchema = external_exports.object({
23196
+ applicationId: external_exports.uuid(),
23197
+ organizationId: external_exports.uuid(),
23198
+ name: external_exports.string(),
23199
+ // `.default('WEB')` keeps responses backward-compatible: a pre-P1.3 API that
23200
+ // never emitted `type` parses cleanly (existing apps are semantically WEB),
23201
+ // so a new frontend reading an old API during a mixed-version rollout doesn't
23202
+ // reject application rows.
23203
+ type: external_exports.enum(ApplicationTypeValues).default("WEB"),
23204
+ applicationUrl: external_exports.string().nullable(),
23205
+ // Customer-authored free-text context, any type ("Advanced → Notes").
23206
+ notes: external_exports.string().nullable(),
23207
+ helpCenterUrl: external_exports.string().nullable(),
23208
+ documentationUrl: external_exports.string().nullable(),
23209
+ repositories: external_exports.array(ApplicationRepositorySummarySchema),
23210
+ // Registrable domains RECON is authorized to discover subdomains under. The
23211
+ // Environments-tab panel hydrates from this payload (no client GET). Defaulted
23212
+ // (like `type`) so a new client reading a pre-this-release API during a rolling
23213
+ // deploy doesn't reject application rows that predate the field.
23214
+ // Signed S3 URL for the Overview screenshot. Defaulted null so a new client
23215
+ // reading a pre-this-release API during a rolling deploy doesn't reject rows
23216
+ // that omit the field. The raw S3 key is never on the wire.
23217
+ screenshotUrl: external_exports.url().nullable().default(null),
23218
+ // Defaulted null so a new client reading a pre-this-release API during a
23219
+ // rolling deploy doesn't reject application rows that omit the field.
23220
+ credentialsNudgeDismissedAt: external_exports.date().nullable().default(null),
23221
+ createdAt: external_exports.date(),
23222
+ updatedAt: external_exports.date()
23223
+ });
23224
+ var ListApplicationsResponseSchema = external_exports.array(ApplicationResponseSchema);
23225
+ var CreateApplicationRequestSchema = external_exports.object({
23226
+ name: external_exports.string().min(1).max(200),
23227
+ type: external_exports.enum(ApplicationTypeValues).default("WEB"),
23228
+ applicationUrl: external_exports.url().max(2048).optional(),
23229
+ notes: external_exports.string().max(APPLICATION_NOTES_MAX_CHARS).optional(),
23230
+ repositoryId: external_exports.uuid().optional(),
23231
+ repositoryPath: external_exports.string().max(500).optional(),
23232
+ helpCenterUrl: external_exports.url().max(2048).optional(),
23233
+ documentationUrl: external_exports.url().max(2048).optional()
23234
+ }).strict().refine((data) => !isUrlBearingApplicationType(data.type) || data.applicationUrl != null, {
23235
+ path: ["applicationUrl"],
23236
+ message: "A web or API application requires an application URL."
23237
+ }).refine((data) => isUrlBearingApplicationType(data.type) || data.applicationUrl == null, {
23238
+ path: ["applicationUrl"],
23239
+ message: "Only web or API applications can have an application URL."
23240
+ });
23241
+ var CreateApplicationRepositoryLinkRequestSchema = external_exports.object({
23242
+ repositoryId: external_exports.uuid(),
23243
+ // Legacy single-role write. Ignored when `roles` is also sent.
23244
+ role: external_exports.enum(ApplicationRepositoryRoleValues).nullable().optional(),
23245
+ roles: ApplicationRepositoryRolesSchema.optional(),
23246
+ roleLabel: external_exports.string().max(120).nullable().optional(),
23247
+ repositoryPath: external_exports.string().max(500).nullable().optional(),
23248
+ scanEnabled: external_exports.boolean().optional()
23249
+ }).strict();
23250
+ var UpdateApplicationRepositoryLinkRequestSchema = external_exports.object({
23251
+ // Legacy single-role write. Ignored when `roles` is also sent.
23252
+ role: external_exports.enum(ApplicationRepositoryRoleValues).nullable().optional(),
23253
+ roles: ApplicationRepositoryRolesSchema.optional(),
23254
+ roleLabel: external_exports.string().max(120).nullable().optional(),
23255
+ repositoryPath: external_exports.string().max(500).nullable().optional(),
23256
+ scanEnabled: external_exports.boolean().optional()
23257
+ }).strict();
23258
+ var UpdateApplicationRequestSchema = external_exports.object({
23259
+ name: external_exports.string().min(1).max(200).optional(),
23260
+ applicationUrl: external_exports.url().max(2048).optional(),
23261
+ notes: external_exports.string().max(APPLICATION_NOTES_MAX_CHARS).nullable().optional(),
23262
+ repositoryId: external_exports.uuid().nullable().optional(),
23263
+ repositoryPath: external_exports.string().max(500).nullable().optional(),
23264
+ helpCenterUrl: external_exports.url().max(2048).nullable().optional(),
23265
+ documentationUrl: external_exports.url().max(2048).nullable().optional()
23266
+ }).strict();
23267
+
23011
23268
  // ../../packages/api-contracts/src/schemas/api/v1/pentest-report-pdf.api.ts
23012
23269
  var ReportPdfPentestParamsSchema = OrgIdParamsSchema.extend({
23013
23270
  pentestId: external_exports.uuid()
@@ -23093,6 +23350,28 @@ var PartnerNarrativeJsonSchema = external_exports.object({
23093
23350
  notExploitable: external_exports.string().max(4e3).optional()
23094
23351
  })
23095
23352
  });
23353
+ var BskNarrativeSectionIdValues = [
23354
+ "engagement_context",
23355
+ "platform_and_data",
23356
+ "overall_assessment",
23357
+ "findings_overview",
23358
+ "target",
23359
+ "approach",
23360
+ "limitations",
23361
+ "not_exploitable"
23362
+ ];
23363
+ var BskNarrativeSectionId = external_exports.enum(BskNarrativeSectionIdValues);
23364
+ var TraceNarrativeSectionIdValues = [
23365
+ "executive_summary",
23366
+ "scope_summary",
23367
+ "key_themes"
23368
+ ];
23369
+ var ReportWriteSectionIdValues = [
23370
+ ...TraceNarrativeSectionIdValues,
23371
+ ...BskNarrativeSectionIdValues
23372
+ ];
23373
+ var ReportWriteSectionId = external_exports.enum(ReportWriteSectionIdValues);
23374
+ var REPORT_SECTION_CONTENT_MAX_CHARS = 4e3;
23096
23375
  var SeverityCountsSchema = external_exports.object({
23097
23376
  critical: external_exports.int().min(0),
23098
23377
  high: external_exports.int().min(0),
@@ -23415,6 +23694,8 @@ var ApplyReportSettingsRequestSchema = external_exports.object({
23415
23694
  includeRemediationTimeline: external_exports.boolean(),
23416
23695
  expectedUpdatedAt: external_exports.coerce.date().nullable()
23417
23696
  });
23697
+ var CustomerReportDownloadFormatValues = ["pdf", "bundle"];
23698
+ var CustomerReportDownloadFormatSchema = external_exports.enum(CustomerReportDownloadFormatValues);
23418
23699
  var CustomerReportPdfResponseSchema = ReportPdfResponseSchema.omit({
23419
23700
  organizationId: true,
23420
23701
  pentestId: true,
@@ -23446,7 +23727,14 @@ var CustomerReportPdfResponseSchema = ReportPdfResponseSchema.omit({
23446
23727
  * before the API's, so a new bundle briefly reads an old API's response, where
23447
23728
  * this field does not exist. Consumers default it to false.
23448
23729
  */
23449
- canViewInline: external_exports.boolean().optional()
23730
+ canViewInline: external_exports.boolean().optional(),
23731
+ /**
23732
+ * What the Download action hands the customer: the PDF alone, or one zip
23733
+ * carrying the PDF, the markdown report, and the evidence files (partner
23734
+ * templates whose tooling consumes the markdown). Preview always opens the
23735
+ * PDF. Optional for the deploy-order reason above; consumers default to 'pdf'.
23736
+ */
23737
+ downloadFormat: CustomerReportDownloadFormatSchema.optional()
23450
23738
  });
23451
23739
  var GetCustomerReportPdfResponseSchema = external_exports.object({
23452
23740
  report: CustomerReportPdfResponseSchema.nullable()
@@ -23566,6 +23854,12 @@ var IssuedReportSnapshotSchema = external_exports.object({
23566
23854
  * parses those rows for the changelog diff — a required field would throw
23567
23855
  * there and break issuing a new version of any pre-feature report. */
23568
23856
  testType: PentestTestType.optional(),
23857
+ /** Types of the applications bound at issuance: the archival record of which
23858
+ * OWASP families the delivered PDF named. Optional for the same reason as
23859
+ * `testType` above: a pre-feature snapshot has no value, and
23860
+ * `latestIssuedSnapshot` parses those rows for the changelog diff. Absent
23861
+ * re-renders as the web family, matching what those reports contain. */
23862
+ applicationTypes: external_exports.array(external_exports.enum(ApplicationTypeValues)).optional(),
23569
23863
  versionLabel: external_exports.string(),
23570
23864
  revisionCount: external_exports.int().min(1)
23571
23865
  }),
@@ -24390,15 +24684,34 @@ var StartProgressPentestFindingRequestSchema = external_exports.object({});
24390
24684
  var ReportRemediationPentestFindingRequestSchema = external_exports.object({
24391
24685
  note: external_exports.string().trim().min(1).max(1e4).optional()
24392
24686
  });
24687
+ var AutomationFindingTransitionSchema = external_exports.enum(["IN_PROGRESS", "REMEDIATED"]);
24688
+ var AutomationStatusUpdateRequestSchema = external_exports.object({
24689
+ status: AutomationFindingTransitionSchema,
24690
+ note: external_exports.string().trim().min(1).max(1e4).optional()
24691
+ }).refine((value) => value.note === void 0 || value.status === "REMEDIATED", {
24692
+ message: "note is only accepted when reporting remediation (status REMEDIATED)",
24693
+ path: ["note"]
24694
+ });
24393
24695
  var ReopenPentestFindingTargetStatusSchema = external_exports.enum(["OPEN", "IN_PROGRESS"]);
24394
24696
  var ReopenPentestFindingRequestSchema = external_exports.object({
24395
24697
  targetStatus: ReopenPentestFindingTargetStatusSchema,
24396
24698
  noteContent: external_exports.string().trim().min(1).max(1e4)
24397
24699
  });
24700
+ var retestVerificationOnlyOnFixed = (value, ctx) => {
24701
+ if (value.outcome !== "FIXED" && value.verification !== void 0) {
24702
+ ctx.addIssue({
24703
+ code: "custom",
24704
+ message: "verification report prose is only allowed when outcome is FIXED",
24705
+ path: ["verification"]
24706
+ });
24707
+ }
24708
+ };
24398
24709
  var CreatePentestFindingRetestRequestSchema = external_exports.object({
24399
24710
  outcome: ManualRetestOutcome,
24400
- notes: external_exports.string().trim().min(1).max(1e4)
24711
+ notes: external_exports.string().trim().min(1).max(1e4),
24712
+ verification: external_exports.string().trim().min(1).max(8e3).optional()
24401
24713
  });
24714
+ var CreatePentestFindingRetestBodySchema = CreatePentestFindingRetestRequestSchema.superRefine(retestVerificationOnlyOnFixed);
24402
24715
  var CreatePentestFindingNoteRequestSchema = external_exports.object({
24403
24716
  content: external_exports.string().min(1).max(1e4)
24404
24717
  });
@@ -24592,263 +24905,6 @@ var PromotePentestHypothesisRequestSchema = external_exports.object({
24592
24905
  manualFindingIds: HypothesisSourceIdsSchema.optional()
24593
24906
  }).strict().optional();
24594
24907
 
24595
- // ../../packages/api-contracts/src/schemas/api/v1/repository.api.ts
24596
- var PlatformValues = ["GITHUB", "GITLAB", "BITBUCKET", "UPLOAD"];
24597
- var RepositoryStatusValues = ["CONNECTED", "DISCONNECTED"];
24598
- var RepositoryIdParamsSchema = OrgIdParamsSchema.extend({
24599
- repositoryId: external_exports.uuid()
24600
- });
24601
- var CreateRepositoryRequestSchema = external_exports.object({
24602
- repositoryUrl: external_exports.url(),
24603
- platform: external_exports.enum(PlatformValues),
24604
- githubDefaultBranch: external_exports.string().min(1).max(255).optional(),
24605
- status: external_exports.enum(RepositoryStatusValues).optional()
24606
- });
24607
- var UpdateRepositoryRequestSchema = external_exports.object({
24608
- repositoryUrl: external_exports.url().optional(),
24609
- platform: external_exports.enum(PlatformValues).optional(),
24610
- githubDefaultBranch: external_exports.string().min(1).max(255).optional(),
24611
- status: external_exports.enum(RepositoryStatusValues).optional()
24612
- });
24613
- var RepositoryResponseSchema = external_exports.object({
24614
- repositoryId: external_exports.uuid(),
24615
- organizationId: external_exports.uuid(),
24616
- repositoryUrl: external_exports.url(),
24617
- platform: external_exports.enum(PlatformValues),
24618
- status: external_exports.enum(RepositoryStatusValues),
24619
- githubDefaultBranch: external_exports.string(),
24620
- scanningBranch: external_exports.string().nullable(),
24621
- primaryLanguage: external_exports.string().nullable().optional(),
24622
- /** Staff-set opt-in: automated PR scans run on this repo even without a FULL SAST baseline. */
24623
- prScansEnabled: external_exports.boolean(),
24624
- createdAt: external_exports.date(),
24625
- updatedAt: external_exports.date()
24626
- });
24627
- var ListRepositoriesResponseSchema = external_exports.array(RepositoryResponseSchema);
24628
- var SetPrScansEnabledRequestSchema = external_exports.object({
24629
- enabled: external_exports.boolean()
24630
- });
24631
- var StaffRepositoryPrScanSettingSchema = external_exports.object({
24632
- repositoryId: external_exports.uuid(),
24633
- /** GitHub owner/name when synced; null for rows created before the sync backfill. */
24634
- fullName: external_exports.string().nullable(),
24635
- repositoryUrl: external_exports.url(),
24636
- /** The staff-set opt-in arm of the gate. */
24637
- prScansEnabled: external_exports.boolean(),
24638
- /**
24639
- * The implicit arm: a completed FULL SAST scan exists for this repo (any
24640
- * branch). With the org capability granted, either arm open means PR scans run.
24641
- */
24642
- hasSastBaseline: external_exports.boolean()
24643
- });
24644
- var StaffPrScanSettingsResponseSchema = external_exports.object({
24645
- /** Org-level gate: without AUTO_PR_SCANS the per-repo switches are inert. */
24646
- orgHasAutoPrScans: external_exports.boolean(),
24647
- repositories: external_exports.array(StaffRepositoryPrScanSettingSchema)
24648
- });
24649
- var CloneTokenRequestSchema = external_exports.object({
24650
- repositoryUrl: external_exports.string().url()
24651
- });
24652
- var CloneTokenResponseSchema = external_exports.object({
24653
- token: external_exports.string(),
24654
- expiresAt: external_exports.string().datetime(),
24655
- // Git username scheme to pair with this token. GitHub installation tokens
24656
- // use `x-access-token`; Bitbucket app tokens use `x-token-auth`. Optional
24657
- // for back-compat with CLIs that predate Bitbucket support and hardcode
24658
- // `x-access-token` (the correct value for the GitHub-only flow they gate on).
24659
- username: external_exports.enum(["x-access-token", "x-token-auth"]).optional()
24660
- });
24661
- var ScanningBranchHistoryEntrySchema = external_exports.object({
24662
- historyId: external_exports.uuid(),
24663
- branch: external_exports.string(),
24664
- configuredAt: external_exports.date(),
24665
- replacedAt: external_exports.date(),
24666
- configuredByUserId: external_exports.uuid().nullable()
24667
- });
24668
- var ScanningBranchStateSchema = external_exports.object({
24669
- scanningBranch: external_exports.string().nullable(),
24670
- githubDefaultBranch: external_exports.string(),
24671
- history: external_exports.array(ScanningBranchHistoryEntrySchema)
24672
- });
24673
- var SetScanningBranchRequestSchema = external_exports.object({
24674
- branch: external_exports.string().min(1).max(255),
24675
- migrateOpenVulnerabilities: external_exports.boolean(),
24676
- /**
24677
- * Optional compare-and-swap guard. When provided, the switch only commits
24678
- * if the repo's current `scanningBranch` matches. Mismatch → HTTP 409.
24679
- * `null` means "expect this repo to still be unconfigured."
24680
- */
24681
- expectedCurrentBranch: external_exports.string().nullable().optional()
24682
- });
24683
- var SetScanningBranchResponseSchema = external_exports.object({
24684
- previousBranch: external_exports.string().nullable(),
24685
- newBranch: external_exports.string(),
24686
- relabeledVulnerabilityCount: external_exports.number().int().min(0),
24687
- /** Null when previousBranch was null (initial configuration). */
24688
- historyId: external_exports.uuid().nullable()
24689
- });
24690
- var BulkSetScanningBranchBranchSchema = external_exports.discriminatedUnion("kind", [
24691
- external_exports.object({ kind: external_exports.literal("literal"), value: external_exports.string().min(1).max(255) }),
24692
- external_exports.object({ kind: external_exports.literal("github-default") })
24693
- ]);
24694
- var BulkSetScanningBranchRequestSchema = external_exports.object({
24695
- branch: BulkSetScanningBranchBranchSchema,
24696
- repositoryIds: external_exports.array(external_exports.uuid()).min(1).max(100),
24697
- migrateOpenVulnerabilities: external_exports.boolean()
24698
- });
24699
- var BulkSetScanningBranchResultStatusValues = [
24700
- "switched",
24701
- "branch-missing-on-github",
24702
- "forbidden",
24703
- "already-on-branch",
24704
- "not-found",
24705
- "error"
24706
- ];
24707
- var BulkSetScanningBranchResultSchema = external_exports.object({
24708
- repositoryId: external_exports.uuid(),
24709
- status: external_exports.enum(BulkSetScanningBranchResultStatusValues),
24710
- relabeledVulnerabilityCount: external_exports.number().int().min(0).optional(),
24711
- historyId: external_exports.uuid().nullable().optional(),
24712
- errorMessage: external_exports.string().optional()
24713
- });
24714
- var BulkSetScanningBranchResponseSchema = external_exports.object({
24715
- results: external_exports.array(BulkSetScanningBranchResultSchema)
24716
- });
24717
- var ScanningBranchPreferenceSchema = external_exports.object({
24718
- autoConfigureScanningBranch: external_exports.boolean()
24719
- });
24720
- var ConfirmScanningBranchDefaultsResponseSchema = external_exports.object({
24721
- autoConfigureScanningBranch: external_exports.literal(true),
24722
- /** Repos the sweep configured to their default-branch mirror. */
24723
- configuredCount: external_exports.number().int().min(0),
24724
- /**
24725
- * Rows skipped by the sweep's domain guards (empty or >255-char mirror).
24726
- * They stay NULL and keep the "waiting on a scanning branch" banner alive.
24727
- */
24728
- skippedCount: external_exports.number().int().min(0)
24729
- });
24730
-
24731
- // ../../packages/api-contracts/src/schemas/api/v1/application.api.ts
24732
- var ApplicationTypeValues = ["WEB", "CLI", "MOBILE", "DESKTOP", "API_SERVICE"];
24733
- var APPLICATION_NOTES_MAX_CHARS = 2e3;
24734
- var ApplicationRepositoryRoleValues = [
24735
- "FRONTEND",
24736
- "BACKEND_API",
24737
- "AUTH_SERVICE",
24738
- "WORKER_JOB_SERVICE",
24739
- "SHARED_PACKAGE_SDK",
24740
- "INFRASTRUCTURE_IAC",
24741
- "MOBILE_CLIENT",
24742
- "MONOREPO",
24743
- "OTHER"
24744
- ];
24745
- var ApplicationIdParamsSchema = OrgIdParamsSchema.extend({
24746
- applicationId: external_exports.uuid()
24747
- });
24748
- var ApplicationRepositoryIdParamsSchema = ApplicationIdParamsSchema.extend({
24749
- applicationRepositoryId: external_exports.uuid()
24750
- });
24751
- var ApplicationRepositorySummarySchema = external_exports.object({
24752
- applicationRepositoryId: external_exports.uuid().nullable(),
24753
- applicationId: external_exports.uuid(),
24754
- organizationId: external_exports.uuid(),
24755
- repositoryId: external_exports.uuid(),
24756
- repositoryName: external_exports.string(),
24757
- repositoryUrl: external_exports.string(),
24758
- repositoryStatus: external_exports.enum(RepositoryStatusValues),
24759
- htmlUrl: external_exports.string().nullable(),
24760
- createdAt: external_exports.date(),
24761
- updatedAt: external_exports.date()
24762
- });
24763
- var ApplicationRepositoryRolesSchema = external_exports.array(external_exports.enum(ApplicationRepositoryRoleValues)).max(ApplicationRepositoryRoleValues.length);
24764
- var ApplicationRepositoryLinkResponseSchema = ApplicationRepositorySummarySchema.extend({
24765
- // First of `roles` (enum order), or null when unspecified. Kept so a new
24766
- // client reading a pre-this-release API during a rolling deploy still parses.
24767
- role: external_exports.enum(ApplicationRepositoryRoleValues).nullable(),
24768
- // Source of truth. Defaulted empty so a new client reading a pre-this-release
24769
- // API that omitted the field doesn't reject the row; the client then falls
24770
- // back to wrapping `role`.
24771
- roles: ApplicationRepositoryRolesSchema.default([]),
24772
- roleLabel: external_exports.string().max(120).nullable(),
24773
- repositoryPath: external_exports.string().max(500).nullable(),
24774
- scanEnabled: external_exports.boolean()
24775
- });
24776
- var ListApplicationRepositoryLinksResponseSchema = external_exports.array(
24777
- ApplicationRepositoryLinkResponseSchema
24778
- );
24779
- var ApplicationResponseSchema = external_exports.object({
24780
- applicationId: external_exports.uuid(),
24781
- organizationId: external_exports.uuid(),
24782
- name: external_exports.string(),
24783
- // `.default('WEB')` keeps responses backward-compatible: a pre-P1.3 API that
24784
- // never emitted `type` parses cleanly (existing apps are semantically WEB),
24785
- // so a new frontend reading an old API during a mixed-version rollout doesn't
24786
- // reject application rows.
24787
- type: external_exports.enum(ApplicationTypeValues).default("WEB"),
24788
- applicationUrl: external_exports.string().nullable(),
24789
- // Customer-authored free-text context, any type ("Advanced → Notes").
24790
- notes: external_exports.string().nullable(),
24791
- helpCenterUrl: external_exports.string().nullable(),
24792
- documentationUrl: external_exports.string().nullable(),
24793
- repositories: external_exports.array(ApplicationRepositorySummarySchema),
24794
- // Registrable domains RECON is authorized to discover subdomains under. The
24795
- // Environments-tab panel hydrates from this payload (no client GET). Defaulted
24796
- // (like `type`) so a new client reading a pre-this-release API during a rolling
24797
- // deploy doesn't reject application rows that predate the field.
24798
- // Signed S3 URL for the Overview screenshot. Defaulted null so a new client
24799
- // reading a pre-this-release API during a rolling deploy doesn't reject rows
24800
- // that omit the field. The raw S3 key is never on the wire.
24801
- screenshotUrl: external_exports.url().nullable().default(null),
24802
- // Defaulted null so a new client reading a pre-this-release API during a
24803
- // rolling deploy doesn't reject application rows that omit the field.
24804
- credentialsNudgeDismissedAt: external_exports.date().nullable().default(null),
24805
- createdAt: external_exports.date(),
24806
- updatedAt: external_exports.date()
24807
- });
24808
- var ListApplicationsResponseSchema = external_exports.array(ApplicationResponseSchema);
24809
- var CreateApplicationRequestSchema = external_exports.object({
24810
- name: external_exports.string().min(1).max(200),
24811
- type: external_exports.enum(ApplicationTypeValues).default("WEB"),
24812
- applicationUrl: external_exports.url().max(2048).optional(),
24813
- notes: external_exports.string().max(APPLICATION_NOTES_MAX_CHARS).optional(),
24814
- repositoryId: external_exports.uuid().optional(),
24815
- repositoryPath: external_exports.string().max(500).optional(),
24816
- helpCenterUrl: external_exports.url().max(2048).optional(),
24817
- documentationUrl: external_exports.url().max(2048).optional()
24818
- }).strict().refine((data) => !isUrlBearingApplicationType(data.type) || data.applicationUrl != null, {
24819
- path: ["applicationUrl"],
24820
- message: "A web or API application requires an application URL."
24821
- }).refine((data) => isUrlBearingApplicationType(data.type) || data.applicationUrl == null, {
24822
- path: ["applicationUrl"],
24823
- message: "Only web or API applications can have an application URL."
24824
- });
24825
- var CreateApplicationRepositoryLinkRequestSchema = external_exports.object({
24826
- repositoryId: external_exports.uuid(),
24827
- // Legacy single-role write. Ignored when `roles` is also sent.
24828
- role: external_exports.enum(ApplicationRepositoryRoleValues).nullable().optional(),
24829
- roles: ApplicationRepositoryRolesSchema.optional(),
24830
- roleLabel: external_exports.string().max(120).nullable().optional(),
24831
- repositoryPath: external_exports.string().max(500).nullable().optional(),
24832
- scanEnabled: external_exports.boolean().optional()
24833
- }).strict();
24834
- var UpdateApplicationRepositoryLinkRequestSchema = external_exports.object({
24835
- // Legacy single-role write. Ignored when `roles` is also sent.
24836
- role: external_exports.enum(ApplicationRepositoryRoleValues).nullable().optional(),
24837
- roles: ApplicationRepositoryRolesSchema.optional(),
24838
- roleLabel: external_exports.string().max(120).nullable().optional(),
24839
- repositoryPath: external_exports.string().max(500).nullable().optional(),
24840
- scanEnabled: external_exports.boolean().optional()
24841
- }).strict();
24842
- var UpdateApplicationRequestSchema = external_exports.object({
24843
- name: external_exports.string().min(1).max(200).optional(),
24844
- applicationUrl: external_exports.url().max(2048).optional(),
24845
- notes: external_exports.string().max(APPLICATION_NOTES_MAX_CHARS).nullable().optional(),
24846
- repositoryId: external_exports.uuid().nullable().optional(),
24847
- repositoryPath: external_exports.string().max(500).nullable().optional(),
24848
- helpCenterUrl: external_exports.url().max(2048).nullable().optional(),
24849
- documentationUrl: external_exports.url().max(2048).nullable().optional()
24850
- }).strict();
24851
-
24852
24908
  // ../../packages/api-contracts/src/schemas/api/v1/pentest.api.ts
24853
24909
  var PentestIdParamsSchema = OrgIdParamsSchema.extend({
24854
24910
  pentestId: external_exports.string().min(1)
@@ -25289,13 +25345,13 @@ var StorageStateSchema = external_exports.object({
25289
25345
  } catch {
25290
25346
  return;
25291
25347
  }
25292
- s.cookies.forEach((c58, i2) => {
25293
- const d = c58.domain.replace(/^\./, "").toLowerCase();
25348
+ s.cookies.forEach((c59, i2) => {
25349
+ const d = c59.domain.replace(/^\./, "").toLowerCase();
25294
25350
  if (d !== host && !host.endsWith(`.${d}`)) {
25295
25351
  ctx.addIssue({
25296
25352
  code: "custom",
25297
25353
  path: ["cookies", i2, "domain"],
25298
- message: `cookie domain "${c58.domain}" is not the capture origin host "${host}" or a parent domain of it`
25354
+ message: `cookie domain "${c59.domain}" is not the capture origin host "${host}" or a parent domain of it`
25299
25355
  });
25300
25356
  }
25301
25357
  });
@@ -25993,7 +26049,19 @@ var AgentSessionLoginCodeResponseSchema = external_exports.object({
25993
26049
  var AgentSessionTotpCodeRequestSchema = external_exports.object({
25994
26050
  runVersion: external_exports.number().int().nonnegative(),
25995
26051
  /** Which account (by `Credential.label`); omit if exactly one is enrolled. */
25996
- credentialLabel: external_exports.string().min(1).max(200).optional()
26052
+ credentialLabel: external_exports.string().min(1).max(200).optional(),
26053
+ /**
26054
+ * Which account by ID, which is what a DESKTOP session holds.
26055
+ *
26056
+ * `Credential.label` is unique per ENVIRONMENT, not per pentest, so an
26057
+ * engagement spanning staging and production can carry two accounts labelled
26058
+ * `admin` and the label identifies neither. The desktop credential catalog
26059
+ * hands the agent an unambiguous id, and without this the only way to use an
26060
+ * enrolled desktop account was to ask staff to relabel one mid-engagement.
26061
+ *
26062
+ * Wins over `credentialLabel` when both are given.
26063
+ */
26064
+ credentialId: external_exports.uuid().optional()
25997
26065
  });
25998
26066
  var AgentSessionTotpCodeResponseSchema = external_exports.object({
25999
26067
  /** The account the code belongs to, echoed so the agent can name it. */
@@ -26393,17 +26461,9 @@ var OperatorRecordFindingRetestRequestSchema = CreatePentestFindingRetestRequest
26393
26461
  runVersion: operatorRunVersion,
26394
26462
  reason: operatorReason,
26395
26463
  pentestFindingId: external_exports.uuid(),
26396
- outcome: RetestOutcome,
26397
- verification: external_exports.string().min(1).max(8e3).optional()
26398
- }).superRefine((v, ctx) => {
26399
- if (v.outcome !== "FIXED" && v.verification !== void 0) {
26400
- ctx.addIssue({
26401
- code: "custom",
26402
- message: "verification report prose is only allowed when outcome is FIXED",
26403
- path: ["verification"]
26404
- });
26405
- }
26406
- });
26464
+ // Widened from the dashboard's 3 values; `verification` comes from the base.
26465
+ outcome: RetestOutcome
26466
+ }).superRefine(retestVerificationOnlyOnFixed);
26407
26467
  var OperatorRetestAckSchema = OperatorFindingReadSchema.extend({
26408
26468
  retestId: external_exports.uuid()
26409
26469
  });
@@ -26564,7 +26624,21 @@ var ReportSessionCoverSchema = external_exports.object({
26564
26624
  classification: AttestationClassification,
26565
26625
  documentId: external_exports.string(),
26566
26626
  testingWindowStart: external_exports.string().nullable(),
26567
- testingWindowEnd: external_exports.string().nullable()
26627
+ testingWindowEnd: external_exports.string().nullable(),
26628
+ /**
26629
+ * The engagement approach in the exact words the rendered document prints
26630
+ * ("white-box" / "authenticated grey-box" / "black-box", from the pentest's
26631
+ * `testType`).
26632
+ *
26633
+ * Here because the agent writing an Approach section otherwise cannot know it,
26634
+ * while the BSK document's methodology appendix prints it three paragraphs
26635
+ * later — one page apart and flatly contradictory in a partner deliverable.
26636
+ *
26637
+ * Optional for the reason the whole view is: the host reads this response with
26638
+ * a raw cast and no validation, so a host running against an older api would
26639
+ * read a required-typed field as `undefined`.
26640
+ */
26641
+ approachLabel: external_exports.string().optional()
26568
26642
  });
26569
26643
  var ReportSessionViewSchema = external_exports.object({
26570
26644
  rendered: external_exports.boolean(),
@@ -26575,7 +26649,19 @@ var ReportSessionViewSchema = external_exports.object({
26575
26649
  keyThemes: external_exports.array(KeyThemeSchema)
26576
26650
  }),
26577
26651
  scopeRows: external_exports.array(ScopeRowSchema),
26578
- updatedAt: external_exports.string().nullable()
26652
+ updatedAt: external_exports.string().nullable(),
26653
+ /** Which template this org's report renders with. Absent = TRACE (older api). */
26654
+ reportTemplate: PentestReportTemplateSchema.optional(),
26655
+ /**
26656
+ * The BSK partner narrative as stored; null when nothing is written, absent on
26657
+ * a TRACE org (or an older api).
26658
+ *
26659
+ * A section key ABSENT from `sections` has not been written yet. That is the
26660
+ * BSK agent's only channel for "which of my eight sections already exist", and
26661
+ * every write returns this view, so a follow-up or resumed session reads it
26662
+ * back rather than re-authoring all eight.
26663
+ */
26664
+ partnerNarrative: PartnerNarrativeJsonSchema.nullable().optional()
26579
26665
  });
26580
26666
  var reportRunVersion = external_exports.number().int().nonnegative();
26581
26667
  var reportReason = external_exports.string().min(1).max(2e3);
@@ -26587,8 +26673,8 @@ var ReportWriteSectionRequestSchema = external_exports.object({
26587
26673
  runVersion: reportRunVersion,
26588
26674
  reason: reportReason,
26589
26675
  expectedUpdatedAt: reportExpectedUpdatedAt,
26590
- section: external_exports.enum(["executive_summary", "scope_summary", "key_themes"]),
26591
- content: external_exports.string().min(1).max(2e3).optional(),
26676
+ section: ReportWriteSectionId,
26677
+ content: external_exports.string().min(1).max(REPORT_SECTION_CONTENT_MAX_CHARS).optional(),
26592
26678
  themes: external_exports.array(KeyThemeSchema).max(4).optional()
26593
26679
  });
26594
26680
  var ReportUpdateScopeRequestSchema = external_exports.object({
@@ -26602,13 +26688,24 @@ var RequestDesktopTargetRequestSchema = external_exports.object({
26602
26688
  /** Only Windows exists; a literal keeps the macOS follow-on additive. */
26603
26689
  platform: external_exports.literal("WINDOWS")
26604
26690
  });
26691
+ var DesktopCredentialRefSchema = external_exports.object({
26692
+ credentialId: external_exports.uuid(),
26693
+ label: external_exports.string(),
26694
+ credentialType: external_exports.enum(CredentialTypeValues),
26695
+ environmentId: external_exports.uuid(),
26696
+ /** Present when the account has a second factor enrolled. `get_totp_code`
26697
+ * resolves by LABEL, which is not unique per pentest, so an enrolled desktop
26698
+ * account would otherwise dead-end. */
26699
+ mfaKind: CredentialMfaKind.nullable()
26700
+ });
26605
26701
  var RequestDesktopTargetResponseSchema = external_exports.object({
26606
26702
  desktopTargetId: external_exports.uuid(),
26607
26703
  status: external_exports.enum(DesktopTargetStatusValues),
26608
26704
  /** True when this session already owned a live box, so a second call in one
26609
26705
  * assistant turn cannot bill a second instance. */
26610
26706
  reused: external_exports.boolean(),
26611
- screen: external_exports.object({ width: external_exports.number().int().positive(), height: external_exports.number().int().positive() })
26707
+ screen: external_exports.object({ width: external_exports.number().int().positive(), height: external_exports.number().int().positive() }),
26708
+ credentials: external_exports.array(DesktopCredentialRefSchema)
26612
26709
  });
26613
26710
  var DesktopTargetStatusRequestSchema = external_exports.object({
26614
26711
  runVersion: external_exports.number().int().min(0),
@@ -26654,7 +26751,15 @@ var DesktopActionViewSchema = external_exports.object({
26654
26751
  * so a caller-chosen name is how an unredacted clip would publish as a
26655
26752
  * customer-facing screenshot.
26656
26753
  */
26657
- artifactName: external_exports.string().max(256).nullable()
26754
+ artifactName: external_exports.string().max(256).nullable(),
26755
+ /**
26756
+ * Shape-only reason a FAILED action carries. Load-bearing for `type_secret`:
26757
+ * claim-time substitution fails the action with one of three codes
26758
+ * (`credential_not_found`, `credential_field_absent`, `decrypt_failed`), and
26759
+ * without this projection the agent could not tell those apart from a box
26760
+ * that crashed.
26761
+ */
26762
+ failureDetails: external_exports.string().nullable()
26658
26763
  });
26659
26764
  var ReadDesktopActionRequestSchema = external_exports.object({
26660
26765
  runVersion: external_exports.number().int().min(0),
@@ -31977,7 +32082,8 @@ var OrgCapabilityValues = [
31977
32082
  "SCAN_SECRETS",
31978
32083
  "SCAN_DAST",
31979
32084
  "AUTO_PR_SCANS",
31980
- "AUTO_PUSH_SCANS"
32085
+ "AUTO_PUSH_SCANS",
32086
+ "PENTEST_FINDING_AUTOMATION"
31981
32087
  ];
31982
32088
  var UpdateOrganizationRequestSchema = external_exports.object({
31983
32089
  name: external_exports.string().min(1).max(255).optional(),
@@ -34270,6 +34376,28 @@ var pentestFindingContract = c35.router({
34270
34376
  500: ErrorResponseSchema
34271
34377
  }
34272
34378
  },
34379
+ // ─── Automation lifecycle transition (API keys) ───
34380
+ // A separate route rather than a widened `start-progress` / `report-remediation`:
34381
+ // those stay human-only, and everything that makes a machine-driven write
34382
+ // different lives here. API-key principals only, gated on the org owning
34383
+ // PENTEST_FINDING_AUTOMATION, and audited as API_KEY with the key's name.
34384
+ automationStatusUpdate: {
34385
+ method: "POST",
34386
+ path: "/organizations/:organizationId/customer-pentests/:pentestId/findings/:pentestFindingId/automation/status",
34387
+ pathParams: CustomerPentestFindingIdParamsSchema,
34388
+ body: AutomationStatusUpdateRequestSchema,
34389
+ responses: {
34390
+ 200: CustomerPentestFindingResponseSchema,
34391
+ 400: ErrorResponseSchema,
34392
+ 401: ErrorResponseSchema,
34393
+ 403: ErrorResponseSchema,
34394
+ 404: ErrorResponseSchema,
34395
+ // FINDING_TRANSITION_NOT_ALLOWED (bad source status), FINDING_STALE
34396
+ // (concurrent write), or PENTEST_CLOSED.
34397
+ 409: ErrorResponseSchema,
34398
+ 500: ErrorResponseSchema
34399
+ }
34400
+ },
34273
34401
  // Customer opens a dispute (PR 3) → finding status CONTESTED. Same auth as the
34274
34402
  // other customer transitions (VIEW_RESULTS + user principal + customer visibility).
34275
34403
  contest: {
@@ -34362,7 +34490,7 @@ var pentestFindingContract = c35.router({
34362
34490
  method: "POST",
34363
34491
  path: "/organizations/:organizationId/pentests/:pentestId/findings/:pentestFindingId/retests",
34364
34492
  pathParams: PentestFindingIdParamsSchema,
34365
- body: CreatePentestFindingRetestRequestSchema,
34493
+ body: CreatePentestFindingRetestBodySchema,
34366
34494
  responses: {
34367
34495
  201: PentestFindingRetestResponseSchema,
34368
34496
  400: ErrorResponseSchema,
@@ -35466,6 +35594,128 @@ var staffPentestStatsContract = c42.router({
35466
35594
  }
35467
35595
  });
35468
35596
 
35597
+ // ../../packages/api-contracts/src/schemas/api/v1/staff-queue.api.ts
35598
+ var StaffQueueItemKindValues = [
35599
+ "RETEST_OWED",
35600
+ "CONTESTED",
35601
+ "SESSION_WAITING",
35602
+ "SESSION_FAILED",
35603
+ "REVIEW_GATE",
35604
+ "INTAKE_SUBMITTED"
35605
+ ];
35606
+ var StaffQueueItemKind = external_exports.enum(StaffQueueItemKindValues);
35607
+ var STAFF_QUEUE_EXCERPT_MAX = 280;
35608
+ var since = external_exports.iso.datetime();
35609
+ var findingIdentity = {
35610
+ pentestFindingId: external_exports.uuid(),
35611
+ /** Null until published; the finding is then named by title alone. */
35612
+ displayId: external_exports.int().positive().nullable(),
35613
+ title: external_exports.string(),
35614
+ severity: PentestFindingSeverity,
35615
+ /** False for a finding the customer has never seen; they are not the one waiting. */
35616
+ published: external_exports.boolean()
35617
+ };
35618
+ var StaffQueueRetestOwedItemSchema = external_exports.object({
35619
+ kind: external_exports.literal("RETEST_OWED"),
35620
+ since,
35621
+ ...findingIdentity,
35622
+ /** The most recent automatic retest, when one ran and did not settle it. */
35623
+ lastAutoRetest: external_exports.object({ outcome: RetestOutcome, testedAt: external_exports.iso.datetime() }).nullable()
35624
+ }).strict();
35625
+ var StaffQueueContestedItemSchema = external_exports.object({
35626
+ kind: external_exports.literal("CONTESTED"),
35627
+ since,
35628
+ ...findingIdentity,
35629
+ /** The open contest row. Null only if a finding is CONTESTED with no open row. */
35630
+ contest: external_exports.object({
35631
+ reason: ContestReason,
35632
+ justification: external_exports.string().max(STAFF_QUEUE_EXCERPT_MAX),
35633
+ contestedBy: external_exports.string()
35634
+ }).strict().nullable()
35635
+ }).strict();
35636
+ var StaffQueueSessionWaitingItemSchema = external_exports.object({
35637
+ kind: external_exports.literal("SESSION_WAITING"),
35638
+ since,
35639
+ agentSessionId: external_exports.uuid(),
35640
+ sessionType: AgentSessionType,
35641
+ waitingReason: AgentSessionWaitingReason.nullable(),
35642
+ /** The console's session list names sessions by objective, so the row carries it. */
35643
+ objective: external_exports.string().max(STAFF_QUEUE_EXCERPT_MAX),
35644
+ /** The agent's question when the park carries one. */
35645
+ question: external_exports.string().max(STAFF_QUEUE_EXCERPT_MAX).nullable()
35646
+ }).strict();
35647
+ var StaffQueueSessionFailedItemSchema = external_exports.object({
35648
+ kind: external_exports.literal("SESSION_FAILED"),
35649
+ since,
35650
+ agentSessionId: external_exports.uuid(),
35651
+ sessionType: AgentSessionType,
35652
+ objective: external_exports.string().max(STAFF_QUEUE_EXCERPT_MAX),
35653
+ failureReason: AgentSessionFailureReason.nullable()
35654
+ }).strict();
35655
+ var StaffQueueReviewGateItemSchema = external_exports.object({
35656
+ kind: external_exports.literal("REVIEW_GATE"),
35657
+ since,
35658
+ stage: PentestStage
35659
+ }).strict();
35660
+ var StaffQueueIntakeSubmittedItemSchema = external_exports.object({
35661
+ kind: external_exports.literal("INTAKE_SUBMITTED"),
35662
+ since
35663
+ }).strict();
35664
+ var StaffQueueItemSchema = external_exports.discriminatedUnion("kind", [
35665
+ StaffQueueRetestOwedItemSchema,
35666
+ StaffQueueContestedItemSchema,
35667
+ StaffQueueSessionWaitingItemSchema,
35668
+ StaffQueueSessionFailedItemSchema,
35669
+ StaffQueueReviewGateItemSchema,
35670
+ StaffQueueIntakeSubmittedItemSchema
35671
+ ]);
35672
+ var StaffQueueEngagementSchema = external_exports.object({
35673
+ organization: external_exports.object({
35674
+ organizationId: external_exports.uuid(),
35675
+ slug: external_exports.string(),
35676
+ name: external_exports.string(),
35677
+ logoUrl: external_exports.string().nullable(),
35678
+ darkModeLogoUrl: external_exports.string().nullable()
35679
+ }).strict(),
35680
+ pentest: external_exports.object({
35681
+ pentestId: external_exports.uuid(),
35682
+ displayId: external_exports.int().positive(),
35683
+ title: external_exports.string(),
35684
+ status: PentestStatus,
35685
+ currentStage: PentestStage
35686
+ }).strict(),
35687
+ /** Oldest first within the engagement. */
35688
+ items: external_exports.array(StaffQueueItemSchema).min(1),
35689
+ /** The `since` of the oldest item; engagements sort by it ascending. */
35690
+ oldestSince: external_exports.iso.datetime()
35691
+ }).strict();
35692
+ var StaffQueueCountsSchema = external_exports.object(
35693
+ Object.fromEntries(StaffQueueItemKindValues.map((kind) => [kind, external_exports.int().nonnegative()]))
35694
+ ).strict();
35695
+ var StaffQueueResponseSchema = external_exports.object({
35696
+ generatedAt: external_exports.iso.datetime(),
35697
+ /** Non-deleted, non-closed pentests considered, so an empty queue can prove the query ran. */
35698
+ liveEngagementCount: external_exports.int().nonnegative(),
35699
+ counts: StaffQueueCountsSchema,
35700
+ engagements: external_exports.array(StaffQueueEngagementSchema)
35701
+ }).strict();
35702
+
35703
+ // ../../packages/api-contracts/src/contracts/v1/staff-queue.ts
35704
+ var c43 = initContract();
35705
+ var staffQueueContract = c43.router({
35706
+ getStaffQueue: {
35707
+ method: "GET",
35708
+ path: "/staff/queue",
35709
+ responses: {
35710
+ 200: StaffQueueResponseSchema,
35711
+ 401: ErrorResponseSchema,
35712
+ 403: ErrorResponseSchema,
35713
+ 500: ErrorResponseSchema
35714
+ },
35715
+ summary: "Engagements where the next move is staff\u2019s, grouped by engagement"
35716
+ }
35717
+ });
35718
+
35469
35719
  // ../../packages/api-contracts/src/schemas/api/v1/test-integration.api.ts
35470
35720
  var NOTES_MAX = 2e3;
35471
35721
  var TestIntegrationIdParamsSchema = external_exports.object({
@@ -35522,8 +35772,8 @@ var AttachTestIntegrationRequestSchema = external_exports.object({
35522
35772
  }).strict();
35523
35773
 
35524
35774
  // ../../packages/api-contracts/src/contracts/v1/test-integration.ts
35525
- var c43 = initContract();
35526
- var testIntegrationContract = c43.router({
35775
+ var c44 = initContract();
35776
+ var testIntegrationContract = c44.router({
35527
35777
  // -- Fixture CRUD (org-less, staff-only) ----------------------------------
35528
35778
  listTestIntegrations: {
35529
35779
  method: "GET",
@@ -35655,8 +35905,8 @@ var MyContextResponseSchema = external_exports.object({
35655
35905
  });
35656
35906
 
35657
35907
  // ../../packages/api-contracts/src/contracts/v1/user.ts
35658
- var c44 = initContract();
35659
- var userContract = c44.router({
35908
+ var c45 = initContract();
35909
+ var userContract = c45.router({
35660
35910
  getStaffProfile: {
35661
35911
  method: "GET",
35662
35912
  path: "/staff/profile",
@@ -35771,8 +36021,8 @@ var VantaIntegrationResponseSchema = external_exports.object({
35771
36021
  });
35772
36022
 
35773
36023
  // ../../packages/api-contracts/src/contracts/v1/vanta-integration.ts
35774
- var c45 = initContract();
35775
- var vantaIntegrationContract = c45.router({
36024
+ var c46 = initContract();
36025
+ var vantaIntegrationContract = c46.router({
35776
36026
  createVantaIntegration: {
35777
36027
  method: "POST",
35778
36028
  path: "/organizations/:organizationId/vanta-integration",
@@ -36104,8 +36354,8 @@ var VerifySharedVulnerabilityEmailCodeResponseSchema = external_exports.object({
36104
36354
  });
36105
36355
 
36106
36356
  // ../../packages/api-contracts/src/contracts/v1/vulnerability.ts
36107
- var c46 = initContract();
36108
- var vulnerabilityContract = c46.router({
36357
+ var c47 = initContract();
36358
+ var vulnerabilityContract = c47.router({
36109
36359
  filterVulnerabilities: {
36110
36360
  method: "POST",
36111
36361
  path: "/organizations/:organizationId/vulnerabilities/filter",
@@ -36313,8 +36563,8 @@ var UpdateVulnerabilityNoteRequestSchema = external_exports.object({
36313
36563
  });
36314
36564
 
36315
36565
  // ../../packages/api-contracts/src/contracts/v1/vulnerability-note.ts
36316
- var c47 = initContract();
36317
- var vulnerabilityNoteContract = c47.router({
36566
+ var c48 = initContract();
36567
+ var vulnerabilityNoteContract = c48.router({
36318
36568
  listNotes: {
36319
36569
  method: "GET",
36320
36570
  path: "/organizations/:organizationId/vulnerabilities/:vulnerabilityId/notes",
@@ -36495,8 +36745,8 @@ var ListWebhookDeliveriesResponseSchema = external_exports.object({
36495
36745
  });
36496
36746
 
36497
36747
  // ../../packages/api-contracts/src/contracts/v1/webhook-settings.ts
36498
- var c48 = initContract();
36499
- var webhookSettingsContract = c48.router({
36748
+ var c49 = initContract();
36749
+ var webhookSettingsContract = c49.router({
36500
36750
  listWebhookEndpoints: {
36501
36751
  method: "GET",
36502
36752
  path: "/organizations/:organizationId/webhook-settings/endpoints",
@@ -36634,8 +36884,8 @@ var webhookSettingsContract = c48.router({
36634
36884
  });
36635
36885
 
36636
36886
  // ../../packages/api-contracts/src/contracts/v1/index.ts
36637
- var c49 = initContract();
36638
- var v1Contract = c49.router(
36887
+ var c50 = initContract();
36888
+ var v1Contract = c50.router(
36639
36889
  {
36640
36890
  admin: adminContract,
36641
36891
  cloudIntegration: cloudIntegrationContract,
@@ -36684,7 +36934,8 @@ var v1Contract = c49.router(
36684
36934
  engagement: engagementContract,
36685
36935
  testIntegration: testIntegrationContract,
36686
36936
  pentestEgress: pentestEgressContract,
36687
- staffPentestStats: staffPentestStatsContract
36937
+ staffPentestStats: staffPentestStatsContract,
36938
+ staffQueue: staffQueueContract
36688
36939
  },
36689
36940
  {
36690
36941
  pathPrefix: "/v1"
@@ -36692,8 +36943,8 @@ var v1Contract = c49.router(
36692
36943
  );
36693
36944
 
36694
36945
  // ../../packages/api-contracts/src/contracts/index.ts
36695
- var c50 = initContract();
36696
- var contract = c50.router({
36946
+ var c51 = initContract();
36947
+ var contract = c51.router({
36697
36948
  v1: v1Contract
36698
36949
  });
36699
36950
 
@@ -36703,8 +36954,8 @@ var GitHubWebhookResponseSchema = external_exports.object({
36703
36954
  });
36704
36955
 
36705
36956
  // ../../packages/api-contracts/src/contracts/webhooks/github.ts
36706
- var c51 = initContract();
36707
- var githubWebhookContract = c51.router(
36957
+ var c52 = initContract();
36958
+ var githubWebhookContract = c52.router(
36708
36959
  {
36709
36960
  handleWebhook: {
36710
36961
  method: "POST",
@@ -36722,7 +36973,7 @@ var githubWebhookContract = c51.router(
36722
36973
  pathPrefix: "/webhooks"
36723
36974
  }
36724
36975
  );
36725
- var githubReadonlyWebhookContract = c51.router(
36976
+ var githubReadonlyWebhookContract = c52.router(
36726
36977
  {
36727
36978
  handleWebhook: {
36728
36979
  method: "POST",
@@ -36742,9 +36993,9 @@ var githubReadonlyWebhookContract = c51.router(
36742
36993
  );
36743
36994
 
36744
36995
  // ../../packages/api-contracts/src/contracts/webhooks/linear.ts
36745
- var c52 = initContract();
36996
+ var c53 = initContract();
36746
36997
  var LinearWebhookResponseSchema = external_exports.object({ ok: external_exports.boolean() });
36747
- var linearWebhookContract = c52.router(
36998
+ var linearWebhookContract = c53.router(
36748
36999
  {
36749
37000
  handleWebhook: {
36750
37001
  method: "POST",
@@ -36764,7 +37015,7 @@ var linearWebhookContract = c52.router(
36764
37015
  );
36765
37016
 
36766
37017
  // ../../packages/api-contracts/src/contracts/forge/bitbucket.ts
36767
- var c53 = initContract();
37018
+ var c54 = initContract();
36768
37019
  var InstallEventSchema = external_exports.object({
36769
37020
  eventType: external_exports.enum(["avi:forge:installed:app", "avi:forge:upgraded:app"]),
36770
37021
  context: external_exports.object({ cloudId: external_exports.string().min(1) }).loose(),
@@ -36793,7 +37044,7 @@ var ForgePairingCodeResponseSchema = external_exports.discriminatedUnion("state"
36793
37044
  expiresInSeconds: external_exports.number().int()
36794
37045
  })
36795
37046
  ]);
36796
- var forgeBitbucketContract = c53.router(
37047
+ var forgeBitbucketContract = c54.router(
36797
37048
  {
36798
37049
  lifecycle: {
36799
37050
  method: "POST",
@@ -36840,8 +37091,8 @@ var HealthResponseSchema = external_exports.object({
36840
37091
  });
36841
37092
 
36842
37093
  // ../../packages/api-contracts/src/contracts/health.ts
36843
- var c54 = initContract();
36844
- var healthContract = c54.router({
37094
+ var c55 = initContract();
37095
+ var healthContract = c55.router({
36845
37096
  getHealth: {
36846
37097
  method: "GET",
36847
37098
  path: "/health",
@@ -36853,8 +37104,8 @@ var healthContract = c54.router({
36853
37104
  });
36854
37105
 
36855
37106
  // ../../packages/api-contracts/src/contracts/v1/vulnerability-share.ts
36856
- var c55 = initContract();
36857
- var vulnerabilityShareContract = c55.router(
37107
+ var c56 = initContract();
37108
+ var vulnerabilityShareContract = c56.router(
36858
37109
  {
36859
37110
  resolveShareLink: {
36860
37111
  method: "GET",
@@ -36941,8 +37192,8 @@ var UpdatePrVulnerabilityRequestSchema = external_exports.object({
36941
37192
  });
36942
37193
 
36943
37194
  // ../../packages/api-contracts/src/contracts/v1/pr-vulnerability.ts
36944
- var c56 = initContract();
36945
- var prVulnerabilityContract = c56.router(
37195
+ var c57 = initContract();
37196
+ var prVulnerabilityContract = c57.router(
36946
37197
  {
36947
37198
  listPrVulnerabilities: {
36948
37199
  method: "GET",
@@ -37678,18 +37929,18 @@ var proto = Object.defineProperties(() => {
37678
37929
  }
37679
37930
  }
37680
37931
  });
37681
- var createStyler = (open, close, parent) => {
37932
+ var createStyler = (open2, close, parent) => {
37682
37933
  let openAll;
37683
37934
  let closeAll;
37684
37935
  if (parent === void 0) {
37685
- openAll = open;
37936
+ openAll = open2;
37686
37937
  closeAll = close;
37687
37938
  } else {
37688
- openAll = parent.openAll + open;
37939
+ openAll = parent.openAll + open2;
37689
37940
  closeAll = close + parent.closeAll;
37690
37941
  }
37691
37942
  return {
37692
- open,
37943
+ open: open2,
37693
37944
  close,
37694
37945
  openAll,
37695
37946
  closeAll,
@@ -37811,6 +38062,10 @@ function getApiBaseUrl() {
37811
38062
  }
37812
38063
  return raw.replace(/\/+$/, "");
37813
38064
  }
38065
+ function requireAuth() {
38066
+ const credential = loadCredentialOrExit();
38067
+ return { token: credential.token, kind: credential.kind };
38068
+ }
37814
38069
  function loadCredentialOrExit() {
37815
38070
  const credential = resolveCredentialOrExit();
37816
38071
  if (!credential) {
@@ -39404,6 +39659,7 @@ function sanitizeDisplay(value) {
39404
39659
  }
39405
39660
  var SUCCESS_STATUS = 200;
39406
39661
  var CREATED_STATUS = 201;
39662
+ var ACCEPTED_STATUS = 202;
39407
39663
  var NO_CONTENT_STATUS = 204;
39408
39664
  var UNAUTHORIZED_STATUS2 = 401;
39409
39665
  function parseOutput(value) {
@@ -39646,12 +39902,12 @@ function buildApplicationsTable(apps) {
39646
39902
  repos: String(a2.repositories.length),
39647
39903
  url: a2.applicationUrl ? sanitizeDisplay(a2.applicationUrl) : "\u2014"
39648
39904
  }));
39649
- const idWidth = Math.max(2, ...cells.map((c58) => c58.id.length));
39650
- const nameWidth = Math.max(4, ...cells.map((c58) => c58.name.length));
39651
- const typeWidth = Math.max(4, ...cells.map((c58) => c58.type.length));
39905
+ const idWidth = Math.max(2, ...cells.map((c59) => c59.id.length));
39906
+ const nameWidth = Math.max(4, ...cells.map((c59) => c59.name.length));
39907
+ const typeWidth = Math.max(4, ...cells.map((c59) => c59.type.length));
39652
39908
  const header = `${"ID".padEnd(idWidth)} ${"NAME".padEnd(nameWidth)} ${"TYPE".padEnd(typeWidth)} REPOS URL`;
39653
39909
  const rows = cells.map(
39654
- (c58) => `${c58.id.padEnd(idWidth)} ${c58.name.padEnd(nameWidth)} ${c58.type.padEnd(typeWidth)} ${c58.repos.padStart(5)} ${c58.url}`
39910
+ (c59) => `${c59.id.padEnd(idWidth)} ${c59.name.padEnd(nameWidth)} ${c59.type.padEnd(typeWidth)} ${c59.repos.padStart(5)} ${c59.url}`
39655
39911
  );
39656
39912
  return { header, rows };
39657
39913
  }
@@ -39671,12 +39927,12 @@ function buildApplicationRepositoriesTable(links) {
39671
39927
  path: l2.repositoryPath ? sanitizeDisplay(l2.repositoryPath) : "\u2014",
39672
39928
  status: formatStatus(l2.repositoryStatus)
39673
39929
  }));
39674
- const nameWidth = Math.max(10, ...cells.map((c58) => c58.name.length));
39675
- const roleWidth = Math.max(4, ...cells.map((c58) => c58.role.length));
39676
- const pathWidth = Math.max(4, ...cells.map((c58) => c58.path.length));
39930
+ const nameWidth = Math.max(10, ...cells.map((c59) => c59.name.length));
39931
+ const roleWidth = Math.max(4, ...cells.map((c59) => c59.role.length));
39932
+ const pathWidth = Math.max(4, ...cells.map((c59) => c59.path.length));
39677
39933
  const header = `${"REPOSITORY".padEnd(nameWidth)} SCAN ${"ROLE".padEnd(roleWidth)} ${"PATH".padEnd(pathWidth)} STATUS`;
39678
39934
  const rows = cells.map(
39679
- (c58) => `${c58.name.padEnd(nameWidth)} ${c58.scan.padEnd(4)} ${c58.role.padEnd(roleWidth)} ${c58.path.padEnd(pathWidth)} ${c58.status}`
39935
+ (c59) => `${c59.name.padEnd(nameWidth)} ${c59.scan.padEnd(4)} ${c59.role.padEnd(roleWidth)} ${c59.path.padEnd(pathWidth)} ${c59.status}`
39680
39936
  );
39681
39937
  return { header, rows };
39682
39938
  }
@@ -40461,8 +40717,8 @@ function wrapTextWithPrefix(e, r2, o2, t2 = o2, s = o2, n3) {
40461
40717
  hard: true,
40462
40718
  trim: false
40463
40719
  }).split(`
40464
- `).map((c58, i2, m2) => {
40465
- const d = n3 ? n3(c58, i2) : c58;
40720
+ `).map((c59, i2, m2) => {
40721
+ const d = n3 ? n3(c59, i2) : c59;
40466
40722
  return i2 === 0 ? `${t2}${d}` : i2 === m2.length - 1 ? `${s}${d}` : `${o2}${d}`;
40467
40723
  }).join(`
40468
40724
  `);
@@ -40960,14 +41216,14 @@ var limitOptions = ({
40960
41216
  Math.min(l2 - a2 + 3, e.length - a2),
40961
41217
  0
40962
41218
  ));
40963
- let d = a2 < e.length && f > 0, c58 = a2 < e.length && f + a2 < e.length;
41219
+ let d = a2 < e.length && f > 0, c59 = a2 < e.length && f + a2 < e.length;
40964
41220
  const W = Math.min(
40965
41221
  f + a2,
40966
41222
  e.length
40967
41223
  ), s = [];
40968
41224
  let g = 0;
40969
- d && g++, c58 && g++;
40970
- const T = f + (d ? 1 : 0), y = W - (c58 ? 1 : 0);
41225
+ d && g++, c59 && g++;
41226
+ const T = f + (d ? 1 : 0), y = W - (c59 ? 1 : 0);
40971
41227
  for (let t2 = T; t2 < y; t2++) {
40972
41228
  const n3 = e[t2], o2 = n3 ? w(n3, t2 === l2) : "", h2 = wrapAnsi(o2, i2, {
40973
41229
  hard: true,
@@ -40988,14 +41244,14 @@ var limitOptions = ({
40988
41244
  u4,
40989
41245
  true
40990
41246
  );
40991
- d ? ({ lineCount: o2, removals: t2 } = L(), o2 > u4 && (c58 || (u4 -= 1), { lineCount: o2, removals: n3 } = E())) : (c58 || (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 && (c58 = true, s.splice(s.length - n3, n3));
41247
+ 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));
40992
41248
  }
40993
41249
  const x = [];
40994
41250
  d && x.push(M);
40995
41251
  for (const t2 of s)
40996
41252
  for (const n3 of t2)
40997
41253
  x.push(n3);
40998
- return c58 && x.push(M), x;
41254
+ return c59 && x.push(M), x;
40999
41255
  };
41000
41256
  var confirm = (i2) => {
41001
41257
  const a2 = i2.active ?? "Yes", s = i2.inactive ?? "No";
@@ -41014,15 +41270,15 @@ var confirm = (i2) => {
41014
41270
  u4
41015
41271
  ), o2 = `${e ? `${styleText2("gray", S_BAR)}
41016
41272
  ` : ""}${f}
41017
- `, c58 = this.value ? a2 : s;
41273
+ `, c59 = this.value ? a2 : s;
41018
41274
  switch (this.state) {
41019
41275
  case "submit": {
41020
41276
  const r2 = e ? `${styleText2("gray", S_BAR)} ` : "";
41021
- return `${o2}${r2}${styleText2("dim", c58)}`;
41277
+ return `${o2}${r2}${styleText2("dim", c59)}`;
41022
41278
  }
41023
41279
  case "cancel": {
41024
41280
  const r2 = e ? `${styleText2("gray", S_BAR)} ` : "";
41025
- return `${o2}${r2}${styleText2(["strikethrough", "dim"], c58)}${e ? `
41281
+ return `${o2}${r2}${styleText2(["strikethrough", "dim"], c59)}${e ? `
41026
41282
  ${styleText2("gray", S_BAR)}` : ""}`;
41027
41283
  }
41028
41284
  default: {
@@ -41170,13 +41426,13 @@ var multiline = (e) => new h({
41170
41426
  `, m2 = e.placeholder && e.placeholder.length > 0 ? (
41171
41427
  // biome-ignore lint/style/noNonNullAssertion: guarded by placeholder.length > 0
41172
41428
  styleText2("inverse", e.placeholder[0]) + styleText2("dim", e.placeholder.slice(1))
41173
- ) : styleText2(["inverse", "hidden"], "_"), a2 = this.userInput ? this.userInputWithCursor : m2, l2 = this.value ?? "", c58 = e.showSubmit ? `
41429
+ ) : styleText2(["inverse", "hidden"], "_"), a2 = this.userInput ? this.userInputWithCursor : m2, l2 = this.value ?? "", c59 = e.showSubmit ? `
41174
41430
  ${styleText2(this.focused === "submit" ? "cyan" : "dim", "[ submit ]")}` : "";
41175
41431
  switch (this.state) {
41176
41432
  case "error": {
41177
41433
  const n3 = `${styleText2("yellow", S_BAR)} `, r2 = i2 ? wrapTextWithPrefix(e.output, a2, n3, void 0) : a2, u4 = styleText2("yellow", S_BAR_END);
41178
41434
  return `${o2}${r2}
41179
- ${u4} ${styleText2("yellow", this.error)}${c58}
41435
+ ${u4} ${styleText2("yellow", this.error)}${c59}
41180
41436
  `;
41181
41437
  }
41182
41438
  case "submit": {
@@ -41204,7 +41460,7 @@ ${u4} ${styleText2("yellow", this.error)}${c58}
41204
41460
  default: {
41205
41461
  const n3 = i2 ? `${styleText2("cyan", S_BAR)} ` : "", r2 = i2 ? styleText2("cyan", S_BAR_END) : "", u4 = i2 ? wrapTextWithPrefix(e.output, a2, n3) : a2;
41206
41462
  return `${o2}${u4}
41207
- ${r2}${c58}
41463
+ ${r2}${c59}
41208
41464
  `;
41209
41465
  }
41210
41466
  }
@@ -41219,7 +41475,7 @@ var password = (r2) => new u$1({
41219
41475
  render() {
41220
41476
  const e = r2.withGuide ?? settings.withGuide, o2 = `${e ? `${styleText2("gray", S_BAR)}
41221
41477
  ` : ""}${symbol2(this.state)} ${r2.message}
41222
- `, c58 = this.userInputWithCursor, i2 = this.masked;
41478
+ `, c59 = this.userInputWithCursor, i2 = this.masked;
41223
41479
  switch (this.state) {
41224
41480
  case "error": {
41225
41481
  const s = e ? `${styleText2("yellow", S_BAR)} ` : "", n3 = e ? `${styleText2("yellow", S_BAR_END)} ` : "", l2 = i2 ?? "";
@@ -41239,7 +41495,7 @@ ${styleText2("gray", S_BAR)}` : ""}`;
41239
41495
  }
41240
41496
  default: {
41241
41497
  const s = e ? `${styleText2("cyan", S_BAR)} ` : "", n3 = e ? styleText2("cyan", S_BAR_END) : "";
41242
- return `${o2}${s}${c58}
41498
+ return `${o2}${s}${c59}
41243
41499
  ${n3}
41244
41500
  `;
41245
41501
  }
@@ -41255,7 +41511,7 @@ var SELECT_INSTRUCTIONS = [
41255
41511
  `${styleText2("dim", "\u2191/\u2193")} to navigate`,
41256
41512
  `${styleText2("dim", "Enter:")} confirm`
41257
41513
  ];
41258
- var c57 = (t2, o2) => t2.includes(`
41514
+ var c58 = (t2, o2) => t2.includes(`
41259
41515
  `) ? t2.split(`
41260
41516
  `).map((d) => o2(d)).join(`
41261
41517
  `) : o2(t2);
@@ -41266,15 +41522,15 @@ var select = (t2) => {
41266
41522
  const s = n3.label ?? String(n3.value);
41267
41523
  switch (m2) {
41268
41524
  case "disabled":
41269
- return `${styleText2("gray", S_RADIO_INACTIVE)} ${c57(s, (i2) => styleText2("gray", i2))}${n3.hint ? ` ${styleText2("dim", `(${n3.hint ?? "disabled"})`)}` : ""}`;
41525
+ return `${styleText2("gray", S_RADIO_INACTIVE)} ${c58(s, (i2) => styleText2("gray", i2))}${n3.hint ? ` ${styleText2("dim", `(${n3.hint ?? "disabled"})`)}` : ""}`;
41270
41526
  case "selected":
41271
- return `${c57(s, (i2) => styleText2("dim", i2))}`;
41527
+ return `${c58(s, (i2) => styleText2("dim", i2))}`;
41272
41528
  case "active":
41273
41529
  return `${styleText2("green", S_RADIO_ACTIVE)} ${s}${n3.hint ? ` ${styleText2("dim", `(${n3.hint})`)}` : ""}`;
41274
41530
  case "cancelled":
41275
- return `${c57(s, (i2) => styleText2(["strikethrough", "dim"], i2))}`;
41531
+ return `${c58(s, (i2) => styleText2(["strikethrough", "dim"], i2))}`;
41276
41532
  default:
41277
- return `${styleText2("dim", S_RADIO_INACTIVE)} ${c57(s, (i2) => styleText2("dim", i2))}`;
41533
+ return `${styleText2("dim", S_RADIO_INACTIVE)} ${c58(s, (i2) => styleText2("dim", i2))}`;
41278
41534
  }
41279
41535
  }, d = t2.showInstructions ?? true;
41280
41536
  return new n$1({
@@ -42334,7 +42590,7 @@ function extractCommandTree(cmd) {
42334
42590
  description: o2.description,
42335
42591
  takesValue: o2.required || o2.optional
42336
42592
  }));
42337
- const subcommands = cmd.commands.filter((c58) => !c58._hidden).map((c58) => extractCommandTree(c58));
42593
+ const subcommands = cmd.commands.filter((c59) => !c59._hidden).map((c59) => extractCommandTree(c59));
42338
42594
  return {
42339
42595
  name: cmd.name(),
42340
42596
  description: cmd.description(),
@@ -42400,7 +42656,7 @@ function generateBashCompletion(tree, binName) {
42400
42656
  }
42401
42657
  function collectBashCases(tree, prefix) {
42402
42658
  const cases = /* @__PURE__ */ new Map();
42403
- const subcommandNames = tree.subcommands.map((c58) => c58.name);
42659
+ const subcommandNames = tree.subcommands.map((c59) => c59.name);
42404
42660
  const optionNames = tree.options.flatMap((o2) => o2.short ? [o2.long, o2.short] : [o2.long]);
42405
42661
  const extraOptions = prefix === "" ? ["--help", "--version"] : ["--help"];
42406
42662
  const allOptions = [.../* @__PURE__ */ new Set([...optionNames, ...extraOptions])];
@@ -42815,9 +43071,9 @@ function buildInboxesTable(inboxes) {
42815
43071
  address: sanitizeDisplay(inbox.address),
42816
43072
  label: sanitizeDisplay(inbox.label)
42817
43073
  }));
42818
- const addressWidth = Math.max(7, ...cells.map((c58) => c58.address.length));
43074
+ const addressWidth = Math.max(7, ...cells.map((c59) => c59.address.length));
42819
43075
  const header = `${"ADDRESS".padEnd(addressWidth)} LABEL`;
42820
- const rows = cells.map((c58) => `${c58.address.padEnd(addressWidth)} ${c58.label}`);
43076
+ const rows = cells.map((c59) => `${c59.address.padEnd(addressWidth)} ${c59.label}`);
42821
43077
  return { header, rows };
42822
43078
  }
42823
43079
  function formatInboxQuota(quota) {
@@ -42841,14 +43097,14 @@ function buildInboxEmailsTable(messages) {
42841
43097
  subject: sanitizeDisplay(message.subject ?? ""),
42842
43098
  unread: message.unread ? "yes" : "no"
42843
43099
  }));
42844
- const idWidth = Math.max(2, ...cells.map((c58) => c58.id.length));
42845
- const receivedWidth = Math.max(8, ...cells.map((c58) => c58.received.length));
42846
- const fromWidth = Math.max(4, ...cells.map((c58) => c58.from.length));
42847
- const toWidth = Math.max(2, ...cells.map((c58) => c58.to.length));
42848
- const subjectWidth = Math.max(7, ...cells.map((c58) => c58.subject.length));
43100
+ const idWidth = Math.max(2, ...cells.map((c59) => c59.id.length));
43101
+ const receivedWidth = Math.max(8, ...cells.map((c59) => c59.received.length));
43102
+ const fromWidth = Math.max(4, ...cells.map((c59) => c59.from.length));
43103
+ const toWidth = Math.max(2, ...cells.map((c59) => c59.to.length));
43104
+ const subjectWidth = Math.max(7, ...cells.map((c59) => c59.subject.length));
42849
43105
  const header = `${"ID".padEnd(idWidth)} ${"RECEIVED".padEnd(receivedWidth)} ${"FROM".padEnd(fromWidth)} ${"TO".padEnd(toWidth)} ${"SUBJECT".padEnd(subjectWidth)} UNREAD`;
42850
43106
  const rows = cells.map(
42851
- (c58) => `${c58.id.padEnd(idWidth)} ${c58.received.padEnd(receivedWidth)} ${c58.from.padEnd(fromWidth)} ${c58.to.padEnd(toWidth)} ${c58.subject.padEnd(subjectWidth)} ${c58.unread}`
43107
+ (c59) => `${c59.id.padEnd(idWidth)} ${c59.received.padEnd(receivedWidth)} ${c59.from.padEnd(fromWidth)} ${c59.to.padEnd(toWidth)} ${c59.subject.padEnd(subjectWidth)} ${c59.unread}`
42852
43108
  );
42853
43109
  return { header, rows };
42854
43110
  }
@@ -43076,14 +43332,14 @@ async function runEmails(label, options) {
43076
43332
  const output = parseOutput(options.output);
43077
43333
  requireInboxLabel(label);
43078
43334
  const limit = parseEmailLimit(options.limit);
43079
- const since = parseEmailSince(options.since);
43335
+ const since2 = parseEmailSince(options.since);
43080
43336
  const organizationId = await resolveOrganizationId();
43081
43337
  const result = await authenticatedRequest(
43082
43338
  (client) => client.inbox.listInboxMessages({
43083
43339
  params: { organizationId, label },
43084
43340
  query: {
43085
43341
  ...limit !== void 0 ? { limit } : {},
43086
- ...since !== void 0 ? { since } : {}
43342
+ ...since2 !== void 0 ? { since: since2 } : {}
43087
43343
  }
43088
43344
  })
43089
43345
  );
@@ -43576,16 +43832,16 @@ function printCredentialsTsv(creds) {
43576
43832
  }
43577
43833
  }
43578
43834
  function buildCredentialsTable(creds) {
43579
- const cells = creds.map((c58) => ({
43580
- id: c58.credentialId,
43581
- label: sanitizeDisplay(c58.label),
43582
- type: c58.credentialType
43835
+ const cells = creds.map((c59) => ({
43836
+ id: c59.credentialId,
43837
+ label: sanitizeDisplay(c59.label),
43838
+ type: c59.credentialType
43583
43839
  }));
43584
- const idWidth = Math.max(2, ...cells.map((c58) => c58.id.length));
43585
- const labelWidth = Math.max(5, ...cells.map((c58) => c58.label.length));
43840
+ const idWidth = Math.max(2, ...cells.map((c59) => c59.id.length));
43841
+ const labelWidth = Math.max(5, ...cells.map((c59) => c59.label.length));
43586
43842
  const header = `${"ID".padEnd(idWidth)} ${"LABEL".padEnd(labelWidth)} TYPE`;
43587
43843
  const rows = cells.map(
43588
- (c58) => `${c58.id.padEnd(idWidth)} ${c58.label.padEnd(labelWidth)} ${c58.type}`
43844
+ (c59) => `${c59.id.padEnd(idWidth)} ${c59.label.padEnd(labelWidth)} ${c59.type}`
43589
43845
  );
43590
43846
  return { header, rows };
43591
43847
  }
@@ -44313,14 +44569,14 @@ function buildEnvironmentTable(envs, leading) {
44313
44569
  url: e.primaryUrl != null ? sanitizeDisplay(e.primaryUrl) : "\u2014"
44314
44570
  }));
44315
44571
  const leadingWidths = leading.map(
44316
- (column, i2) => Math.max(column.header.length, ...cells.map((c58) => c58.leading[i2]?.length ?? 0))
44572
+ (column, i2) => Math.max(column.header.length, ...cells.map((c59) => c59.leading[i2]?.length ?? 0))
44317
44573
  );
44318
- const nameWidth = Math.max(4, ...cells.map((c58) => c58.name.length));
44574
+ const nameWidth = Math.max(4, ...cells.map((c59) => c59.name.length));
44319
44575
  const padLeading = (values) => values.map((v, i2) => v.padEnd(leadingWidths[i2] ?? 0)).join(" ");
44320
44576
  return {
44321
- header: `${padLeading(leading.map((c58) => c58.header))} ${"NAME".padEnd(nameWidth)} PROD URL`,
44577
+ header: `${padLeading(leading.map((c59) => c59.header))} ${"NAME".padEnd(nameWidth)} PROD URL`,
44322
44578
  rows: cells.map(
44323
- (c58) => `${padLeading(c58.leading)} ${c58.name.padEnd(nameWidth)} ${c58.prod.padEnd(4)} ${c58.url}`
44579
+ (c59) => `${padLeading(c59.leading)} ${c59.name.padEnd(nameWidth)} ${c59.prod.padEnd(4)} ${c59.url}`
44324
44580
  )
44325
44581
  };
44326
44582
  }
@@ -45317,11 +45573,26 @@ function isPentestIdentifier(value) {
45317
45573
  function isFindingIdentifier(value) {
45318
45574
  return FINDING_IDENTIFIER_PATTERN.test(value);
45319
45575
  }
45320
- function failStatusUpdate(status, body) {
45576
+ function failStatusUpdate(status, body, viaApiKey) {
45321
45577
  const { message, errorCode } = readErrorFields(body);
45322
45578
  if (status === UNAUTHORIZED_STATUS2) {
45323
45579
  exitAuthFailed();
45324
45580
  }
45581
+ if (status === 403 && viaApiKey) {
45582
+ console.error(
45583
+ colorError(
45584
+ `${message ?? "Request failed (status 403)."}
45585
+ API keys can update finding status once Trace enables finding automation for your organization. Ask Trace to turn it on, or run this with \`tracecli auth login\`.`
45586
+ )
45587
+ );
45588
+ process.exit(1);
45589
+ }
45590
+ if (status === 409 && errorCode === "FINDING_STALE") {
45591
+ console.error(
45592
+ colorError(message ?? "The finding changed while this update was in flight. Retry it.")
45593
+ );
45594
+ process.exit(1);
45595
+ }
45325
45596
  if (status === 404) {
45326
45597
  console.error(colorError(message ?? "Pentest or finding not found."));
45327
45598
  process.exit(1);
@@ -45398,13 +45669,19 @@ async function runStatusUpdate(options) {
45398
45669
  );
45399
45670
  process.exit(1);
45400
45671
  }
45672
+ const viaApiKey = requireAuth().kind === "api_key";
45401
45673
  const organizationId = await resolveOrganizationId();
45402
45674
  const params = {
45403
45675
  organizationId,
45404
45676
  pentestId: options.pentest,
45405
45677
  pentestFindingId: options.finding
45406
45678
  };
45407
- const result = target === "IN_PROGRESS" ? await authenticatedRequest(
45679
+ const result = viaApiKey ? await authenticatedRequest(
45680
+ (client) => client.pentestFinding.automationStatusUpdate({
45681
+ params,
45682
+ body: { status: target, ...note !== void 0 ? { note } : {} }
45683
+ })
45684
+ ) : target === "IN_PROGRESS" ? await authenticatedRequest(
45408
45685
  (client) => client.pentestFinding.startProgress({ params, body: {} })
45409
45686
  ) : await authenticatedRequest(
45410
45687
  (client) => client.pentestFinding.reportRemediation({
@@ -45413,7 +45690,7 @@ async function runStatusUpdate(options) {
45413
45690
  })
45414
45691
  );
45415
45692
  if (result.status !== 200) {
45416
- failStatusUpdate(result.status, result.body);
45693
+ failStatusUpdate(result.status, result.body, viaApiKey);
45417
45694
  }
45418
45695
  if (options.output === "json") {
45419
45696
  console.log(JSON.stringify(result.body, null, 2));
@@ -45436,8 +45713,12 @@ fix is ready for Trace to re-test. Leaving the finding IN_PROGRESS (and not
45436
45713
  sending REMEDIATED) keeps it out of the retest queue.
45437
45714
 
45438
45715
  IN_PROGRESS is allowed from OPEN or VERIFIED_OPEN. REMEDIATED is allowed from
45439
- OPEN, IN_PROGRESS, or VERIFIED_OPEN. Requires \`tracecli auth login\` (API keys
45440
- are rejected \u2014 the change is attributed to the signed-in user).
45716
+ OPEN, IN_PROGRESS, or VERIFIED_OPEN.
45717
+
45718
+ A signed-in session (\`tracecli auth login\`) attributes the change to you. A
45719
+ Trace API key works too, once Trace enables finding automation for your
45720
+ organization: those writes are recorded as \`api-key:<key name>\` and show up as
45721
+ yours in the finding's history.
45441
45722
 
45442
45723
  Examples:
45443
45724
  tracecli pentest findings status-update --pentest PT-3 --finding 7 --status IN_PROGRESS
@@ -46693,6 +46974,961 @@ Examples:
46693
46974
  });
46694
46975
  }
46695
46976
 
46977
+ // src/upload/prepare.ts
46978
+ import { createHash } from "crypto";
46979
+ import { createReadStream, statSync as statSync3 } from "fs";
46980
+ import { open } from "fs/promises";
46981
+ import { basename as basename2 } from "path";
46982
+ var CodeUploadPrepareError = class extends Error {
46983
+ constructor(message) {
46984
+ super(message);
46985
+ this.name = "CodeUploadPrepareError";
46986
+ }
46987
+ };
46988
+ var KIND_BY_INPUT = {
46989
+ source: "SOURCE_ARCHIVE",
46990
+ code: "SOURCE_ARCHIVE",
46991
+ "source-archive": "SOURCE_ARCHIVE",
46992
+ source_archive: "SOURCE_ARCHIVE",
46993
+ SOURCE_ARCHIVE: "SOURCE_ARCHIVE",
46994
+ apk: "ANDROID_APK",
46995
+ android: "ANDROID_APK",
46996
+ "android-apk": "ANDROID_APK",
46997
+ android_apk: "ANDROID_APK",
46998
+ ANDROID_APK: "ANDROID_APK",
46999
+ ipa: "IOS_IPA",
47000
+ ios: "IOS_IPA",
47001
+ "ios-ipa": "IOS_IPA",
47002
+ ios_ipa: "IOS_IPA",
47003
+ IOS_IPA: "IOS_IPA"
47004
+ };
47005
+ function normalizeArtifactKind(input) {
47006
+ const trimmed = input.trim();
47007
+ return KIND_BY_INPUT[trimmed] ?? KIND_BY_INPUT[trimmed.toLowerCase()] ?? null;
47008
+ }
47009
+ function inferArtifactKindFromFilename(filename) {
47010
+ const lower = filename.toLowerCase();
47011
+ if (lower.endsWith(".zip")) return "SOURCE_ARCHIVE";
47012
+ if (lower.endsWith(".apk")) return "ANDROID_APK";
47013
+ if (lower.endsWith(".ipa")) return "IOS_IPA";
47014
+ return null;
47015
+ }
47016
+ function requiredExtensionForKind(kind) {
47017
+ if (kind === "ANDROID_APK") return ".apk";
47018
+ if (kind === "IOS_IPA") return ".ipa";
47019
+ return ".zip";
47020
+ }
47021
+ function defaultDisplayName(originalFilename) {
47022
+ const base = originalFilename.replace(/\.(zip|apk|ipa)$/i, "").trim();
47023
+ return base.slice(0, 200) || "source";
47024
+ }
47025
+ function formatArchiveBytes(bytes) {
47026
+ if (bytes < 1024) return `${bytes} B`;
47027
+ if (bytes < 1024 * 1024) {
47028
+ const kb = bytes / 1024;
47029
+ return `${kb.toFixed(Number.isInteger(kb) ? 0 : 1)} KB`;
47030
+ }
47031
+ if (bytes < 1024 * 1024 * 1024) {
47032
+ const mb = bytes / (1024 * 1024);
47033
+ return `${mb.toFixed(Number.isInteger(mb) ? 0 : 1)} MB`;
47034
+ }
47035
+ const gb = bytes / (1024 * 1024 * 1024);
47036
+ return `${gb.toFixed(Number.isInteger(gb) ? 0 : 1)} GB`;
47037
+ }
47038
+ function archiveTooLargeMessage() {
47039
+ return `The file must be ${formatArchiveBytes(CODE_UPLOAD_MAX_BYTES)} or smaller.`;
47040
+ }
47041
+ async function hashFileSha256(filePath) {
47042
+ const hash2 = createHash("sha256");
47043
+ const stream = createReadStream(filePath);
47044
+ for await (const chunk of stream) {
47045
+ hash2.update(chunk);
47046
+ }
47047
+ return hash2.digest("hex");
47048
+ }
47049
+ async function readFileSlice(filePath, start, endExclusive) {
47050
+ const length = endExclusive - start;
47051
+ if (!Number.isSafeInteger(start) || !Number.isSafeInteger(length) || start < 0 || length < 1) {
47052
+ throw new Error(
47053
+ `Invalid file slice: start=${String(start)} endExclusive=${String(endExclusive)}.`
47054
+ );
47055
+ }
47056
+ const buffer = Buffer.alloc(length);
47057
+ const handle = await open(filePath, "r");
47058
+ try {
47059
+ const { bytesRead } = await handle.read(buffer, 0, length, start);
47060
+ if (bytesRead !== length) {
47061
+ throw new Error(
47062
+ `Expected to read ${String(length)} bytes at offset ${String(start)}, got ${String(bytesRead)}.`
47063
+ );
47064
+ }
47065
+ return buffer;
47066
+ } finally {
47067
+ await handle.close();
47068
+ }
47069
+ }
47070
+ function assertPreparedMatchesUpload(upload, prepared) {
47071
+ if (upload.sizeBytes !== prepared.sizeBytes || upload.archiveSha256 !== prepared.archiveSha256) {
47072
+ throw new CodeUploadPrepareError(
47073
+ "The file does not match the interrupted upload (size or SHA-256 differs)."
47074
+ );
47075
+ }
47076
+ }
47077
+ async function prepareUploadFile(filePath, options = {}) {
47078
+ let sizeBytes;
47079
+ try {
47080
+ const stats = statSync3(filePath);
47081
+ if (!stats.isFile()) {
47082
+ throw new CodeUploadPrepareError("Path is not a regular file. Pass a .zip, .apk, or .ipa.");
47083
+ }
47084
+ sizeBytes = stats.size;
47085
+ } catch (error48) {
47086
+ if (error48 instanceof CodeUploadPrepareError) throw error48;
47087
+ throw new CodeUploadPrepareError("Cannot read that file.");
47088
+ }
47089
+ if (!Number.isSafeInteger(sizeBytes) || sizeBytes < 1) {
47090
+ throw new CodeUploadPrepareError("The file is empty.");
47091
+ }
47092
+ if (sizeBytes > CODE_UPLOAD_MAX_BYTES) {
47093
+ throw new CodeUploadPrepareError(archiveTooLargeMessage());
47094
+ }
47095
+ const originalFilename = basename2(filePath);
47096
+ const inferred = inferArtifactKindFromFilename(originalFilename);
47097
+ const artifactKind = options.artifactKind ?? inferred;
47098
+ if (artifactKind == null) {
47099
+ throw new CodeUploadPrepareError("Pass a .zip source archive, an .apk, or an .ipa.");
47100
+ }
47101
+ const expected = requiredExtensionForKind(artifactKind);
47102
+ if (!originalFilename.toLowerCase().endsWith(expected)) {
47103
+ const label = isApplicationBuildArtifactKind(artifactKind) ? artifactKind === "ANDROID_APK" ? "Android APK" : "iOS package" : "source archive";
47104
+ throw new CodeUploadPrepareError(`A ${label} must end in ${expected}.`);
47105
+ }
47106
+ const displayName = (options.displayName ?? defaultDisplayName(originalFilename)).trim();
47107
+ if (displayName.length < 1 || displayName.length > 200) {
47108
+ throw new CodeUploadPrepareError("Display name must be 1 to 200 characters.");
47109
+ }
47110
+ return {
47111
+ path: filePath,
47112
+ displayName,
47113
+ originalFilename,
47114
+ sizeBytes,
47115
+ archiveSha256: await hashFileSha256(filePath),
47116
+ artifactKind
47117
+ };
47118
+ }
47119
+
47120
+ // src/upload/multipart.ts
47121
+ var MAX_CONCURRENT_UPLOADS = 3;
47122
+ var MAX_PART_ATTEMPTS = 3;
47123
+ var MAX_URL_REFRESHES = 12;
47124
+ async function uploadCodeArchiveParts(filePath, totalBytes, partSizeBytes, parts, options = {}) {
47125
+ assertUploadPlan(totalBytes, partSizeBytes, parts);
47126
+ const fetchPart = options.fetch ?? globalThis.fetch;
47127
+ const wait = options.wait ?? defaultWait;
47128
+ let uploadedBytes = options.initialUploadedBytes ?? 0;
47129
+ if (!Number.isSafeInteger(uploadedBytes) || uploadedBytes < 0 || uploadedBytes > totalBytes) {
47130
+ throw new Error("Invalid multipart progress: uploaded byte count is outside the archive.");
47131
+ }
47132
+ options.onProgress?.(progress(uploadedBytes, totalBytes));
47133
+ if (parts.length === 0) return;
47134
+ const urls = new Map(parts.map((part) => [part.partNumber, part.uploadUrl]));
47135
+ const refreshUrls = createUrlRefresher(urls, options.refreshParts);
47136
+ let cursor3 = 0;
47137
+ const workerCount = Math.min(MAX_CONCURRENT_UPLOADS, parts.length);
47138
+ await Promise.all(
47139
+ Array.from({ length: workerCount }, async () => {
47140
+ while (cursor3 < parts.length) {
47141
+ const part = parts[cursor3];
47142
+ cursor3 += 1;
47143
+ if (!part) return;
47144
+ const start = (part.partNumber - 1) * partSizeBytes;
47145
+ const end = Math.min(start + partSizeBytes, totalBytes);
47146
+ const body = await readFileSlice(filePath, start, end);
47147
+ await uploadPartWithRetry(part.partNumber, urls, body, fetchPart, wait, refreshUrls);
47148
+ uploadedBytes += body.byteLength;
47149
+ options.onProgress?.(progress(uploadedBytes, totalBytes));
47150
+ }
47151
+ })
47152
+ );
47153
+ }
47154
+ function createUrlRefresher(urls, refreshParts) {
47155
+ if (!refreshParts) return async () => false;
47156
+ let inflight = null;
47157
+ let rounds = 0;
47158
+ return async () => {
47159
+ if (rounds >= MAX_URL_REFRESHES) return false;
47160
+ if (!inflight) {
47161
+ rounds += 1;
47162
+ inflight = (async () => {
47163
+ const fresh = await refreshParts();
47164
+ for (const part of fresh) {
47165
+ assertPresignedUploadUrl(part.uploadUrl);
47166
+ urls.set(part.partNumber, part.uploadUrl);
47167
+ }
47168
+ })().finally(() => {
47169
+ inflight = null;
47170
+ });
47171
+ }
47172
+ await inflight;
47173
+ return true;
47174
+ };
47175
+ }
47176
+ function assertUploadPlan(totalBytes, partSizeBytes, parts) {
47177
+ if (!Number.isSafeInteger(totalBytes) || totalBytes < 1) {
47178
+ throw new Error("Invalid multipart upload: archive size must be a positive integer.");
47179
+ }
47180
+ if (!Number.isSafeInteger(partSizeBytes) || partSizeBytes < 1) {
47181
+ throw new Error("Invalid multipart upload: part size must be a positive integer.");
47182
+ }
47183
+ const totalParts = Math.ceil(totalBytes / partSizeBytes);
47184
+ const seen = /* @__PURE__ */ new Set();
47185
+ for (const part of parts) {
47186
+ if (!Number.isSafeInteger(part.partNumber) || part.partNumber < 1 || part.partNumber > totalParts || seen.has(part.partNumber)) {
47187
+ throw new Error("Invalid multipart upload: part numbers must be unique and in range.");
47188
+ }
47189
+ assertPresignedUploadUrl(part.uploadUrl);
47190
+ seen.add(part.partNumber);
47191
+ }
47192
+ }
47193
+ async function uploadPartWithRetry(partNumber, urls, body, fetchPart, wait, refreshUrls) {
47194
+ let lastStatus = null;
47195
+ for (let attempt = 1; attempt <= MAX_PART_ATTEMPTS; attempt += 1) {
47196
+ const uploadUrl = urls.get(partNumber);
47197
+ if (!uploadUrl) {
47198
+ throw new Error(`Source upload part ${partNumber.toString()} has no upload URL.`);
47199
+ }
47200
+ try {
47201
+ const response = await fetchPart(uploadUrl, {
47202
+ method: "PUT",
47203
+ body: new Uint8Array(body),
47204
+ redirect: "error"
47205
+ });
47206
+ if (response.ok) return;
47207
+ lastStatus = response.status;
47208
+ if (response.status === 403 && await refreshUrls()) {
47209
+ attempt -= 1;
47210
+ continue;
47211
+ }
47212
+ if (response.status >= 400 && response.status < 500 && response.status !== 408) break;
47213
+ } catch {
47214
+ lastStatus = null;
47215
+ }
47216
+ if (attempt < MAX_PART_ATTEMPTS) {
47217
+ await wait(250 * 2 ** (attempt - 1));
47218
+ }
47219
+ }
47220
+ const statusDetail = lastStatus === null ? "network error" : `status ${lastStatus.toString()}`;
47221
+ throw new Error(
47222
+ `Source upload part ${partNumber.toString()} failed after retries (${statusDetail}).`
47223
+ );
47224
+ }
47225
+ function assertPresignedUploadUrl(value) {
47226
+ let url2;
47227
+ try {
47228
+ url2 = new URL(value);
47229
+ } catch {
47230
+ throw new Error("Invalid multipart upload: the API returned an invalid part URL.");
47231
+ }
47232
+ const localHttp = url2.protocol === "http:" && (url2.hostname === "localhost" || url2.hostname === "127.0.0.1" || url2.hostname === "host.docker.internal");
47233
+ if (url2.protocol !== "https:" && !localHttp) {
47234
+ throw new Error("Invalid multipart upload: part URLs must use HTTPS.");
47235
+ }
47236
+ }
47237
+ function progress(uploadedBytes, totalBytes) {
47238
+ return {
47239
+ uploadedBytes,
47240
+ totalBytes,
47241
+ fraction: totalBytes === 0 ? 0 : Math.min(1, uploadedBytes / totalBytes)
47242
+ };
47243
+ }
47244
+ function defaultWait(milliseconds) {
47245
+ return new Promise((resolve) => setTimeout(resolve, milliseconds));
47246
+ }
47247
+
47248
+ // src/upload/commands.ts
47249
+ var TERMINAL_STATUSES = /* @__PURE__ */ new Set(["READY", "REJECTED", "FAILED", "DELETING"]);
47250
+ var DEFAULT_RUNTIME = {
47251
+ wait: (milliseconds) => new Promise((resolve) => setTimeout(resolve, milliseconds)),
47252
+ pollIntervalMs: 3e3,
47253
+ fetch: (input, init) => globalThis.fetch(input, init)
47254
+ };
47255
+ function resolveRuntime(runtime) {
47256
+ return {
47257
+ wait: runtime.wait ?? DEFAULT_RUNTIME.wait,
47258
+ pollIntervalMs: runtime.pollIntervalMs ?? DEFAULT_RUNTIME.pollIntervalMs,
47259
+ fetch: runtime.fetch ?? DEFAULT_RUNTIME.fetch
47260
+ };
47261
+ }
47262
+ function isTerminalCodeUploadStatus(status) {
47263
+ return TERMINAL_STATUSES.has(status);
47264
+ }
47265
+ function requireUuid(value, label) {
47266
+ if (!isUuid(value)) {
47267
+ console.error(colorError(`Invalid ${label} '${sanitizeDisplay(value)}'. Expected a UUID.`));
47268
+ process.exit(1);
47269
+ }
47270
+ return value;
47271
+ }
47272
+ function parseKindOption(value) {
47273
+ if (value === void 0) return void 0;
47274
+ const kind = normalizeArtifactKind(value);
47275
+ if (kind == null) {
47276
+ console.error(
47277
+ colorError(`Invalid --kind '${sanitizeDisplay(value)}'. Use source (code ZIP), apk, or ipa.`)
47278
+ );
47279
+ process.exit(1);
47280
+ }
47281
+ return kind;
47282
+ }
47283
+ function failPrepare(error48) {
47284
+ const message = error48 instanceof CodeUploadPrepareError ? error48.message : error48 instanceof Error ? error48.message : "Failed to prepare the file.";
47285
+ console.error(colorError(message));
47286
+ process.exit(1);
47287
+ }
47288
+ function failWriteOp5(action, status, body) {
47289
+ const { message, errorCode } = readErrorFields(body);
47290
+ if (status === UNAUTHORIZED_STATUS2) {
47291
+ exitAuthFailed();
47292
+ }
47293
+ if (status === 403) {
47294
+ console.error(
47295
+ colorError(
47296
+ `Only an organization admin (or Trace staff) on a pentest-enabled organization can ${action} source uploads.`
47297
+ )
47298
+ );
47299
+ process.exit(1);
47300
+ }
47301
+ if (status === 404) {
47302
+ console.error(colorError(message ?? "No code upload with that id."));
47303
+ process.exit(1);
47304
+ }
47305
+ if (status === 400) {
47306
+ console.error(colorError(message ?? "Invalid request (status 400)."));
47307
+ process.exit(1);
47308
+ }
47309
+ if (status === 409) {
47310
+ if (errorCode === "CODE_UPLOAD_NOT_READY") {
47311
+ console.error(
47312
+ colorError(
47313
+ message ?? "The upload must be ready and pass malware scanning before it can be linked. Run `tracecli upload wait <id>`."
47314
+ )
47315
+ );
47316
+ } else if (errorCode === "CODE_UPLOAD_NOT_SOURCE") {
47317
+ console.error(
47318
+ colorError(message ?? "APK and IPA uploads cannot be linked as source repositories.")
47319
+ );
47320
+ } else {
47321
+ console.error(colorError(message ?? "Conflict (status 409)."));
47322
+ }
47323
+ process.exit(1);
47324
+ }
47325
+ if (status === 503) {
47326
+ console.error(
47327
+ colorError(message ?? "Source archive uploads are not configured on this server.")
47328
+ );
47329
+ process.exit(1);
47330
+ }
47331
+ console.error(
47332
+ colorError(`Failed to ${action} upload (status ${status})${message ? `: ${message}` : "."}`)
47333
+ );
47334
+ process.exit(1);
47335
+ }
47336
+ function failReadOp5(action, status, body) {
47337
+ const { message } = readErrorFields(body);
47338
+ if (status === UNAUTHORIZED_STATUS2) {
47339
+ exitAuthFailed();
47340
+ }
47341
+ if (status === 403) {
47342
+ console.error(
47343
+ colorError(
47344
+ "Only an organization admin (or Trace staff) on a pentest-enabled organization can view source uploads."
47345
+ )
47346
+ );
47347
+ process.exit(1);
47348
+ }
47349
+ if (status === 404) {
47350
+ console.error(colorError(message ?? "No code upload with that id."));
47351
+ process.exit(1);
47352
+ }
47353
+ if (status === 503) {
47354
+ console.error(
47355
+ colorError(message ?? "Source archive uploads are not configured on this server.")
47356
+ );
47357
+ process.exit(1);
47358
+ }
47359
+ console.error(
47360
+ colorError(`Failed to ${action} (status ${status})${message ? `: ${message}` : "."}`)
47361
+ );
47362
+ process.exit(1);
47363
+ }
47364
+ function resumeHint(codeUploadId, filePath) {
47365
+ return `Resume with: tracecli upload resume ${codeUploadId} ${filePath}`;
47366
+ }
47367
+ function buildUploadsTable(uploads) {
47368
+ const cells = uploads.map((upload) => ({
47369
+ name: sanitizeDisplay(upload.displayName),
47370
+ kind: formatStatus(upload.artifactKind),
47371
+ status: formatStatus(upload.status),
47372
+ size: formatArchiveBytes(upload.sizeBytes),
47373
+ id: upload.codeUploadId
47374
+ }));
47375
+ const nameWidth = Math.max(4, ...cells.map((c59) => c59.name.length));
47376
+ const kindWidth = Math.max(4, ...cells.map((c59) => c59.kind.length));
47377
+ const statusWidth = Math.max(6, ...cells.map((c59) => c59.status.length));
47378
+ const sizeWidth = Math.max(4, ...cells.map((c59) => c59.size.length));
47379
+ const header = `${"NAME".padEnd(nameWidth)} ${"KIND".padEnd(kindWidth)} ${"STATUS".padEnd(statusWidth)} ${"SIZE".padEnd(sizeWidth)} ID`;
47380
+ const rows = cells.map(
47381
+ (c59) => `${c59.name.padEnd(nameWidth)} ${c59.kind.padEnd(kindWidth)} ${c59.status.padEnd(statusWidth)} ${c59.size.padEnd(sizeWidth)} ${c59.id}`
47382
+ );
47383
+ return { header, rows };
47384
+ }
47385
+ function renderUploadDetail(upload) {
47386
+ const pad = 18;
47387
+ const linked = upload.linkedApplications.length === 0 ? "\u2014" : upload.linkedApplications.map((app) => `${sanitizeDisplay(app.name)} (${app.applicationId})`).join(", ");
47388
+ return [
47389
+ `${"Name:".padEnd(pad)} ${sanitizeDisplay(upload.displayName)}`,
47390
+ `${"Filename:".padEnd(pad)} ${sanitizeDisplay(upload.originalFilename)}`,
47391
+ `${"Kind:".padEnd(pad)} ${formatStatus(upload.artifactKind)}`,
47392
+ `${"Status:".padEnd(pad)} ${formatStatus(upload.status)}`,
47393
+ `${"Size:".padEnd(pad)} ${formatArchiveBytes(upload.sizeBytes)}`,
47394
+ `${"SHA-256:".padEnd(pad)} ${upload.archiveSha256}`,
47395
+ `${"Upload ID:".padEnd(pad)} ${upload.codeUploadId}`,
47396
+ `${"Repository ID:".padEnd(pad)} ${upload.repositoryId}`,
47397
+ `${"Malware:".padEnd(pad)} ${upload.malwareScanResult ? formatStatus(upload.malwareScanResult) : "\u2014"}`,
47398
+ `${"Rejection:".padEnd(pad)} ${upload.rejectionReason ? formatStatus(upload.rejectionReason) : "\u2014"}`,
47399
+ `${"Linked apps:".padEnd(pad)} ${linked}`,
47400
+ `${"Created:".padEnd(pad)} ${formatDate(upload.createdAt)}`,
47401
+ `${"Ready:".padEnd(pad)} ${formatDate(upload.readyAt)}`
47402
+ ].join("\n");
47403
+ }
47404
+ function printUploadsTsv(uploads) {
47405
+ console.log(
47406
+ [
47407
+ "codeUploadId",
47408
+ "repositoryId",
47409
+ "displayName",
47410
+ "originalFilename",
47411
+ "artifactKind",
47412
+ "status",
47413
+ "sizeBytes",
47414
+ "archiveSha256",
47415
+ "malwareScanResult",
47416
+ "rejectionReason",
47417
+ "applicationId",
47418
+ "createdAt",
47419
+ "readyAt"
47420
+ ].join(" ")
47421
+ );
47422
+ for (const upload of uploads) {
47423
+ console.log(
47424
+ [
47425
+ tsvSafe(upload.codeUploadId),
47426
+ tsvSafe(upload.repositoryId),
47427
+ tsvSafe(sanitizeDisplay(upload.displayName)),
47428
+ tsvSafe(sanitizeDisplay(upload.originalFilename)),
47429
+ tsvSafe(upload.artifactKind),
47430
+ tsvSafe(upload.status),
47431
+ String(upload.sizeBytes),
47432
+ tsvSafe(upload.archiveSha256),
47433
+ tsvSafe(upload.malwareScanResult ?? ""),
47434
+ tsvSafe(upload.rejectionReason ?? ""),
47435
+ tsvSafe(upload.applicationId ?? ""),
47436
+ tsvSafe(toIso(upload.createdAt)),
47437
+ tsvSafe(upload.readyAt ? toIso(upload.readyAt) : "")
47438
+ ].join(" ")
47439
+ );
47440
+ }
47441
+ }
47442
+ function outputUpload(upload, output) {
47443
+ if (output === "json") {
47444
+ console.log(JSON.stringify(upload, null, 2));
47445
+ return;
47446
+ }
47447
+ if (output === "tsv") {
47448
+ printUploadsTsv([upload]);
47449
+ return;
47450
+ }
47451
+ console.log(renderUploadDetail(upload));
47452
+ }
47453
+ function writeProgress(progress2) {
47454
+ if (!process.stderr.isTTY) return;
47455
+ const pct = Math.floor(progress2.fraction * 100);
47456
+ process.stderr.write(
47457
+ `\rUploading ${pct}% (${formatArchiveBytes(progress2.uploadedBytes)} / ${formatArchiveBytes(progress2.totalBytes)})`
47458
+ );
47459
+ }
47460
+ function finishProgress() {
47461
+ if (process.stderr.isTTY) process.stderr.write("\n");
47462
+ }
47463
+ async function listUploads(organizationId, query = {}) {
47464
+ const result = await authenticatedRequest(
47465
+ (client) => client.codeUpload.listCodeUploads({
47466
+ params: { organizationId },
47467
+ query
47468
+ })
47469
+ );
47470
+ if (result.status !== SUCCESS_STATUS) {
47471
+ failReadOp5("list uploads", result.status, result.body);
47472
+ }
47473
+ return result.body.uploads;
47474
+ }
47475
+ async function requireUpload(organizationId, codeUploadId, query = {}) {
47476
+ const uploads = await listUploads(organizationId, query);
47477
+ const match = uploads.find((upload) => upload.codeUploadId === codeUploadId);
47478
+ if (!match) {
47479
+ console.error(colorError(`No code upload '${codeUploadId}'.`));
47480
+ process.exit(1);
47481
+ }
47482
+ return match;
47483
+ }
47484
+ async function refreshRemainingParts(organizationId, codeUploadId) {
47485
+ const result = await authenticatedRequest(
47486
+ (client) => client.codeUpload.resumeCodeUpload({
47487
+ params: { organizationId, codeUploadId },
47488
+ body: {}
47489
+ })
47490
+ );
47491
+ if (result.status !== SUCCESS_STATUS) {
47492
+ failWriteOp5("resume", result.status, result.body);
47493
+ }
47494
+ return result.body.multipart.parts;
47495
+ }
47496
+ async function completeUpload(organizationId, codeUploadId) {
47497
+ const result = await authenticatedRequest(
47498
+ (client) => client.codeUpload.completeCodeUpload({
47499
+ params: { organizationId, codeUploadId },
47500
+ body: {}
47501
+ })
47502
+ );
47503
+ if (result.status !== ACCEPTED_STATUS) {
47504
+ failWriteOp5("complete", result.status, result.body);
47505
+ }
47506
+ return result.body;
47507
+ }
47508
+ async function waitForTerminal(organizationId, codeUploadId, runtime) {
47509
+ for (; ; ) {
47510
+ const upload = await requireUpload(organizationId, codeUploadId);
47511
+ if (isTerminalCodeUploadStatus(upload.status)) {
47512
+ return upload;
47513
+ }
47514
+ if (process.stderr.isTTY) {
47515
+ process.stderr.write(`\rWaiting for malware scan (${formatStatus(upload.status)})...`);
47516
+ }
47517
+ await runtime.wait(runtime.pollIntervalMs);
47518
+ }
47519
+ }
47520
+ function printWaitOutcome(upload, output) {
47521
+ if (output !== "table") {
47522
+ outputUpload(upload, output);
47523
+ if (upload.status !== "READY") process.exit(1);
47524
+ return;
47525
+ }
47526
+ if (upload.status === "READY") {
47527
+ console.log(colorSuccess(`Upload ${upload.codeUploadId} is ready.`));
47528
+ outputUpload(upload, output);
47529
+ if (upload.artifactKind === "SOURCE_ARCHIVE") {
47530
+ console.log(
47531
+ `Link it with: tracecli upload link --app <applicationId> --upload ${upload.codeUploadId}`
47532
+ );
47533
+ }
47534
+ return;
47535
+ }
47536
+ const reason = upload.rejectionReason ? ` (${formatStatus(upload.rejectionReason)})` : "";
47537
+ console.error(
47538
+ colorError(`Upload ${upload.codeUploadId} ended as ${formatStatus(upload.status)}${reason}.`)
47539
+ );
47540
+ outputUpload(upload, output);
47541
+ process.exit(1);
47542
+ }
47543
+ async function runTransfer(filePath, options, runtime) {
47544
+ const output = parseOutput(options.output);
47545
+ let prepared;
47546
+ try {
47547
+ prepared = await prepareUploadFile(filePath, {
47548
+ displayName: options.name,
47549
+ artifactKind: options.artifactKind
47550
+ });
47551
+ } catch (error48) {
47552
+ failPrepare(error48);
47553
+ }
47554
+ const organizationId = await resolveOrganizationId();
47555
+ const initialized = await authenticatedRequest(
47556
+ (client) => client.codeUpload.initializeCodeUpload({
47557
+ params: { organizationId },
47558
+ body: {
47559
+ displayName: prepared.displayName,
47560
+ originalFilename: prepared.originalFilename,
47561
+ sizeBytes: prepared.sizeBytes,
47562
+ archiveSha256: prepared.archiveSha256,
47563
+ artifactKind: prepared.artifactKind,
47564
+ ...options.applicationId !== void 0 ? { applicationId: options.applicationId } : {}
47565
+ }
47566
+ })
47567
+ );
47568
+ if (initialized.status !== CREATED_STATUS) {
47569
+ failWriteOp5("create", initialized.status, initialized.body);
47570
+ }
47571
+ const codeUploadId = initialized.body.upload.codeUploadId;
47572
+ if (output === "table") {
47573
+ console.error(
47574
+ `Initialized upload ${codeUploadId} (${formatArchiveBytes(prepared.sizeBytes)}).`
47575
+ );
47576
+ }
47577
+ try {
47578
+ await uploadCodeArchiveParts(
47579
+ prepared.path,
47580
+ prepared.sizeBytes,
47581
+ initialized.body.multipart.partSizeBytes,
47582
+ initialized.body.multipart.parts,
47583
+ {
47584
+ fetch: runtime.fetch,
47585
+ wait: runtime.wait,
47586
+ onProgress: writeProgress,
47587
+ refreshParts: () => refreshRemainingParts(organizationId, codeUploadId)
47588
+ }
47589
+ );
47590
+ finishProgress();
47591
+ } catch (error48) {
47592
+ finishProgress();
47593
+ const detail = error48 instanceof Error ? error48.message : "Upload to storage failed.";
47594
+ console.error(colorError(detail));
47595
+ console.error(resumeHint(codeUploadId, filePath));
47596
+ process.exit(1);
47597
+ }
47598
+ const completed = await completeUpload(organizationId, codeUploadId);
47599
+ const upload = options.wait ? await waitForTerminal(organizationId, codeUploadId, runtime) : completed;
47600
+ if (options.wait) {
47601
+ if (process.stderr.isTTY) process.stderr.write("\n");
47602
+ printWaitOutcome(upload, output);
47603
+ return;
47604
+ }
47605
+ if (output === "table") {
47606
+ console.log(colorSuccess(`Uploaded ${sanitizeDisplay(upload.displayName)}.`));
47607
+ console.log(
47608
+ `Malware scanning is in progress. Check with: tracecli upload wait ${codeUploadId}`
47609
+ );
47610
+ }
47611
+ outputUpload(upload, output);
47612
+ }
47613
+ async function runCreate5(filePath, options, runtime) {
47614
+ const inferred = inferArtifactKindFromFilename(filePath);
47615
+ if (inferred != null && isApplicationBuildArtifactKind(inferred)) {
47616
+ console.error(
47617
+ colorError(
47618
+ "That file is a mobile build, not a source archive. Attach it to an existing application with:"
47619
+ )
47620
+ );
47621
+ console.error(" tracecli upload build <file> --app <applicationId>");
47622
+ process.exit(1);
47623
+ }
47624
+ await runTransfer(
47625
+ filePath,
47626
+ {
47627
+ ...options.name !== void 0 ? { name: options.name } : {},
47628
+ ...options.wait !== void 0 ? { wait: options.wait } : {},
47629
+ output: options.output,
47630
+ artifactKind: "SOURCE_ARCHIVE"
47631
+ },
47632
+ runtime
47633
+ );
47634
+ }
47635
+ async function runBuild(filePath, options, runtime) {
47636
+ const applicationId = requireUuid(options.app, "--app");
47637
+ const inferred = inferArtifactKindFromFilename(filePath);
47638
+ if (inferred == null || !isApplicationBuildArtifactKind(inferred)) {
47639
+ console.error(
47640
+ colorError(
47641
+ "Build uploads are an APK or IPA attached to an existing application. For a source ZIP, use `tracecli upload create <file>`."
47642
+ )
47643
+ );
47644
+ process.exit(1);
47645
+ }
47646
+ await runTransfer(
47647
+ filePath,
47648
+ {
47649
+ ...options.name !== void 0 ? { name: options.name } : {},
47650
+ ...options.wait !== void 0 ? { wait: options.wait } : {},
47651
+ output: options.output,
47652
+ applicationId,
47653
+ artifactKind: inferred
47654
+ },
47655
+ runtime
47656
+ );
47657
+ }
47658
+ async function runResume(codeUploadIdArg, filePath, options, runtime) {
47659
+ const output = parseOutput(options.output);
47660
+ const codeUploadId = requireUuid(codeUploadIdArg, "code upload id");
47661
+ let prepared;
47662
+ try {
47663
+ prepared = await prepareUploadFile(filePath);
47664
+ } catch (error48) {
47665
+ failPrepare(error48);
47666
+ }
47667
+ const organizationId = await resolveOrganizationId();
47668
+ const resumed = await authenticatedRequest(
47669
+ (client) => client.codeUpload.resumeCodeUpload({
47670
+ params: { organizationId, codeUploadId },
47671
+ body: {}
47672
+ })
47673
+ );
47674
+ if (resumed.status !== SUCCESS_STATUS) {
47675
+ failWriteOp5("resume", resumed.status, resumed.body);
47676
+ }
47677
+ try {
47678
+ assertPreparedMatchesUpload(resumed.body.upload, prepared);
47679
+ } catch (error48) {
47680
+ failPrepare(error48);
47681
+ }
47682
+ const uploadedBytes = resumed.body.multipart.uploadedParts.reduce(
47683
+ (sum, part) => sum + part.sizeBytes,
47684
+ 0
47685
+ );
47686
+ if (output === "table") {
47687
+ console.error(
47688
+ `Resuming upload ${codeUploadId} (${formatArchiveBytes(uploadedBytes)} already stored).`
47689
+ );
47690
+ }
47691
+ try {
47692
+ await uploadCodeArchiveParts(
47693
+ prepared.path,
47694
+ prepared.sizeBytes,
47695
+ resumed.body.multipart.partSizeBytes,
47696
+ resumed.body.multipart.parts,
47697
+ {
47698
+ initialUploadedBytes: uploadedBytes,
47699
+ fetch: runtime.fetch,
47700
+ wait: runtime.wait,
47701
+ onProgress: writeProgress,
47702
+ refreshParts: () => refreshRemainingParts(organizationId, codeUploadId)
47703
+ }
47704
+ );
47705
+ finishProgress();
47706
+ } catch (error48) {
47707
+ finishProgress();
47708
+ const detail = error48 instanceof Error ? error48.message : "Upload to storage failed.";
47709
+ console.error(colorError(detail));
47710
+ console.error(resumeHint(codeUploadId, filePath));
47711
+ process.exit(1);
47712
+ }
47713
+ const completed = await completeUpload(organizationId, codeUploadId);
47714
+ const upload = options.wait ? await waitForTerminal(organizationId, codeUploadId, runtime) : completed;
47715
+ if (options.wait) {
47716
+ if (process.stderr.isTTY) process.stderr.write("\n");
47717
+ printWaitOutcome(upload, output);
47718
+ return;
47719
+ }
47720
+ if (output === "table") {
47721
+ console.log(colorSuccess(`Uploaded ${sanitizeDisplay(upload.displayName)}.`));
47722
+ console.log(
47723
+ `Malware scanning is in progress. Check with: tracecli upload wait ${codeUploadId}`
47724
+ );
47725
+ }
47726
+ outputUpload(upload, output);
47727
+ }
47728
+ async function runList5(options) {
47729
+ const output = parseOutput(options.output);
47730
+ if (options.web) {
47731
+ const slug = await resolveOrganizationSlug();
47732
+ const url2 = `${getAppBaseUrl()}/dashboard/${slug}/settings/integrations/sources`;
47733
+ const opened = await openBrowser(url2);
47734
+ if (!opened) console.log(url2);
47735
+ return;
47736
+ }
47737
+ const kind = parseKindOption(options.kind);
47738
+ const applicationId = options.app !== void 0 ? requireUuid(options.app, "--app") : void 0;
47739
+ const organizationId = await resolveOrganizationId();
47740
+ const uploads = await listUploads(organizationId, {
47741
+ ...kind !== void 0 ? { artifactKind: kind } : {},
47742
+ ...applicationId !== void 0 ? { applicationId } : {}
47743
+ });
47744
+ if (output === "json") {
47745
+ console.log(JSON.stringify({ uploads }, null, 2));
47746
+ return;
47747
+ }
47748
+ if (output === "tsv") {
47749
+ printUploadsTsv(uploads);
47750
+ return;
47751
+ }
47752
+ if (uploads.length === 0) {
47753
+ console.log("No source uploads found.");
47754
+ return;
47755
+ }
47756
+ const { header, rows } = buildUploadsTable(uploads);
47757
+ await runInteractivePager({
47758
+ items: uploads,
47759
+ header,
47760
+ renderItem: (_upload, i2) => [rows[i2] ?? ""],
47761
+ disabled: options.pager === false
47762
+ });
47763
+ }
47764
+ async function runGet4(codeUploadIdArg, options) {
47765
+ const output = parseOutput(options.output);
47766
+ const codeUploadId = requireUuid(codeUploadIdArg, "code upload id");
47767
+ const organizationId = await resolveOrganizationId();
47768
+ const upload = await requireUpload(organizationId, codeUploadId);
47769
+ outputUpload(upload, output);
47770
+ }
47771
+ async function runWait(codeUploadIdArg, options, runtime) {
47772
+ const output = parseOutput(options.output);
47773
+ const codeUploadId = requireUuid(codeUploadIdArg, "code upload id");
47774
+ const organizationId = await resolveOrganizationId();
47775
+ const upload = await waitForTerminal(organizationId, codeUploadId, runtime);
47776
+ if (process.stderr.isTTY) process.stderr.write("\n");
47777
+ printWaitOutcome(upload, output);
47778
+ }
47779
+ async function runDelete5(codeUploadIdArg, options) {
47780
+ const codeUploadId = requireUuid(codeUploadIdArg, "code upload id");
47781
+ const confirmed = await confirmDeletion(`upload ${codeUploadId}`, options.yes);
47782
+ if (!confirmed) {
47783
+ console.log("Aborted.");
47784
+ return;
47785
+ }
47786
+ const organizationId = await resolveOrganizationId();
47787
+ const result = await authenticatedRequest(
47788
+ (client) => client.codeUpload.deleteCodeUpload({
47789
+ params: { organizationId, codeUploadId }
47790
+ })
47791
+ );
47792
+ if (result.status !== NO_CONTENT_STATUS) {
47793
+ failWriteOp5("delete", result.status, result.body);
47794
+ }
47795
+ console.log(colorSuccess(`Deleted upload ${codeUploadId}`));
47796
+ }
47797
+ async function runLink(options) {
47798
+ const output = parseOutput(options.output);
47799
+ const applicationId = requireUuid(options.app, "--app");
47800
+ const codeUploadId = requireUuid(options.upload, "--upload");
47801
+ const organizationId = await resolveOrganizationId();
47802
+ const upload = await requireUpload(organizationId, codeUploadId);
47803
+ if (isApplicationBuildArtifactKind(upload.artifactKind)) {
47804
+ console.error(colorError("APK and IPA uploads cannot be linked as source repositories."));
47805
+ process.exit(1);
47806
+ }
47807
+ if (upload.status !== "READY" || upload.malwareScanResult !== "NO_THREATS_FOUND") {
47808
+ console.error(
47809
+ colorError(
47810
+ `Upload is ${formatStatus(upload.status)}; it must be ready and pass malware scanning before it can be linked.`
47811
+ )
47812
+ );
47813
+ process.exit(1);
47814
+ }
47815
+ const result = await authenticatedRequest(
47816
+ (client) => client.application.createApplicationRepository({
47817
+ params: { organizationId, applicationId },
47818
+ body: {
47819
+ repositoryId: upload.repositoryId,
47820
+ ...options.path !== void 0 ? { repositoryPath: options.path } : {}
47821
+ }
47822
+ })
47823
+ );
47824
+ if (result.status !== CREATED_STATUS) {
47825
+ failWriteOp5("link", result.status, result.body);
47826
+ }
47827
+ if (output === "json") {
47828
+ console.log(JSON.stringify(result.body, null, 2));
47829
+ return;
47830
+ }
47831
+ if (output === "tsv") {
47832
+ console.log(["applicationId", "repositoryId", "codeUploadId"].join(" "));
47833
+ console.log([applicationId, upload.repositoryId, codeUploadId].join(" "));
47834
+ return;
47835
+ }
47836
+ console.log(
47837
+ colorSuccess(`Linked ${sanitizeDisplay(upload.displayName)} to application ${applicationId}.`)
47838
+ );
47839
+ }
47840
+ function createCreateCommand5(runtime) {
47841
+ return new Command("create").alias("source").description("Upload a source ZIP as a code archive (not a mobile build)").argument("<file>", "Path to a .zip source archive").option("--name <displayName>", "Display name (defaults to the filename without extension)").option("--wait", "Wait until malware scanning finishes").option("-o, --output <format>", "Output format: table, json, tsv (default: table)", "table").addHelpText(
47842
+ "after",
47843
+ `
47844
+ This is a code upload: a source ZIP that becomes an uploaded repository after
47845
+ it is ready. Link it with \`upload link\`. APK and IPA files belong on
47846
+ \`upload build --app\`, which attaches a build to an existing application.
47847
+
47848
+ Requires a human login (\`tracecli auth login\`) on a pentest-enabled organization.
47849
+ API keys are rejected.
47850
+
47851
+ Examples:
47852
+ tracecli upload create ./checkout.zip
47853
+ tracecli upload create ./checkout.zip --name "Checkout" --wait`
47854
+ ).action(async (file2, options) => {
47855
+ await runCreate5(file2, options, runtime);
47856
+ });
47857
+ }
47858
+ function createBuildCommand(runtime) {
47859
+ return new Command("build").description("Upload an APK or IPA and attach it to an existing application").argument("<file>", "Path to an .apk or .ipa").requiredOption("--app <applicationId>", "Application that owns this build").option("--name <displayName>", "Display name (defaults to the filename without extension)").option("--wait", "Wait until malware scanning finishes").option("-o, --output <format>", "Output format: table, json, tsv (default: table)", "table").addHelpText(
47860
+ "after",
47861
+ `
47862
+ Build uploads are not source archives. They attach to --app immediately and
47863
+ cannot be linked as a repository. For a source ZIP, use \`upload create\`.
47864
+
47865
+ Examples:
47866
+ tracecli upload build ./app-release.apk --app <applicationId>
47867
+ tracecli upload build ./app.ipa --app <applicationId> --name "iOS build"`
47868
+ ).action(async (file2, options) => {
47869
+ await runBuild(file2, options, runtime);
47870
+ });
47871
+ }
47872
+ function createListCommand7() {
47873
+ return new Command("list").description("List source and build uploads in the active organization").option("--kind <kind>", "Filter: source (code ZIP), apk, or ipa").option("--app <applicationId>", "Filter build uploads for one application").option("-o, --output <format>", "Output format: table, json, tsv (default: table)", "table").option("--no-pager", "Disable the interactive pager for table output").option("-w, --web", "Open the source uploads page in a browser").action(async (options) => {
47874
+ await runList5(options);
47875
+ });
47876
+ }
47877
+ function createGetCommand4() {
47878
+ return new Command("get").description("Show a single upload").argument("<codeUploadId>", "Upload UUID").option("-o, --output <format>", "Output format: table, json, tsv (default: table)", "table").action(async (codeUploadId, options) => {
47879
+ await runGet4(codeUploadId, options);
47880
+ });
47881
+ }
47882
+ function createResumeCommand(runtime) {
47883
+ return new Command("resume").description("Resume an interrupted upload with the same file").argument("<codeUploadId>", "Upload UUID from the interrupted create").argument("<file>", "The same .zip, .apk, or .ipa file").option("--wait", "Wait until malware scanning finishes").option("-o, --output <format>", "Output format: table, json, tsv (default: table)", "table").addHelpText(
47884
+ "after",
47885
+ `
47886
+ The file must match the original size and SHA-256. Trace skips parts that
47887
+ already landed in storage.
47888
+
47889
+ Examples:
47890
+ tracecli upload resume <codeUploadId> ./checkout.zip`
47891
+ ).action(async (codeUploadId, file2, options) => {
47892
+ await runResume(codeUploadId, file2, options, runtime);
47893
+ });
47894
+ }
47895
+ function createWaitCommand(runtime) {
47896
+ return new Command("wait").description("Wait until an upload finishes malware scanning and validation").argument("<codeUploadId>", "Upload UUID").option("-o, --output <format>", "Output format: table, json, tsv (default: table)", "table").action(async (codeUploadId, options) => {
47897
+ await runWait(codeUploadId, options, runtime);
47898
+ });
47899
+ }
47900
+ function createDeleteCommand5() {
47901
+ return new Command("delete").description("Delete an uploaded archive and its stored bytes").argument("<codeUploadId>", "Upload UUID").option("-y, --yes", "Skip the confirmation prompt (required in non-interactive shells)").action(async (codeUploadId, options) => {
47902
+ await runDelete5(codeUploadId, options);
47903
+ });
47904
+ }
47905
+ function createLinkCommand() {
47906
+ return new Command("link").description("Link a ready source upload to an application").requiredOption("--app <applicationId>", "Application UUID").requiredOption("--upload <codeUploadId>", "Upload UUID (must be a ready source ZIP)").option("--path <repositoryPath>", "Path within the archive").option("-o, --output <format>", "Output format: table, json, tsv (default: table)", "table").addHelpText(
47907
+ "after",
47908
+ `
47909
+ Only a READY source archive that passed malware scanning can be linked.
47910
+ APK and IPA builds attach to an application with \`upload build --app\`.
47911
+
47912
+ Examples:
47913
+ tracecli upload link --app <applicationId> --upload <codeUploadId>`
47914
+ ).action(async (options) => {
47915
+ await runLink(options);
47916
+ });
47917
+ }
47918
+ function createUploadCommand(runtime = {}) {
47919
+ const resolved = resolveRuntime(runtime);
47920
+ const upload = new Command("upload").alias("code-upload").description("Upload source archives (and separately, mobile builds) for pentest");
47921
+ upload.addCommand(createCreateCommand5(resolved));
47922
+ upload.addCommand(createBuildCommand(resolved));
47923
+ upload.addCommand(createListCommand7());
47924
+ upload.addCommand(createGetCommand4());
47925
+ upload.addCommand(createResumeCommand(resolved));
47926
+ upload.addCommand(createWaitCommand(resolved));
47927
+ upload.addCommand(createDeleteCommand5());
47928
+ upload.addCommand(createLinkCommand());
47929
+ return upload;
47930
+ }
47931
+
46696
47932
  // src/vulnerabilities/commands.ts
46697
47933
  var DEFAULT_LIMIT3 = 30;
46698
47934
  var MAX_LIMIT3 = 100;
@@ -46869,7 +48105,7 @@ function printVulnerabilityDetailTsv(vulnerability) {
46869
48105
  ].map(sanitizeTsvField);
46870
48106
  console.log(cols.join(" "));
46871
48107
  }
46872
- function createGetCommand4() {
48108
+ function createGetCommand5() {
46873
48109
  return new Command("get").description("Get a vulnerability by display ID or UUID").argument("<id>", "Vulnerability display ID (e.g. TRACE-123 or 123) or UUID").option("-o, --output <format>", "Output format: table, json, tsv (default: table)", "table").option("-w, --web", "Open the vulnerability detail page in a browser").addHelpText(
46874
48110
  "after",
46875
48111
  `
@@ -46928,7 +48164,7 @@ Run \`tracecli vulnerabilities list\` to see available vulnerabilities.`
46928
48164
  printVulnerabilityDetail(result.body);
46929
48165
  });
46930
48166
  }
46931
- function createListCommand7() {
48167
+ function createListCommand8() {
46932
48168
  return new Command("list").description("List vulnerabilities for your organization").option("--repo <repo>", "Filter by repository name or owner/repo").option(
46933
48169
  "--severity <severity>",
46934
48170
  "Filter by severity (CRITICAL, HIGH, MEDIUM, LOW)",
@@ -47061,8 +48297,8 @@ Examples:
47061
48297
  }
47062
48298
  function createVulnerabilitiesCommand() {
47063
48299
  const vulnerabilities = new Command("vulnerabilities").description("Manage vulnerabilities");
47064
- vulnerabilities.addCommand(createGetCommand4());
47065
- vulnerabilities.addCommand(createListCommand7());
48300
+ vulnerabilities.addCommand(createGetCommand5());
48301
+ vulnerabilities.addCommand(createListCommand8());
47066
48302
  return vulnerabilities;
47067
48303
  }
47068
48304
 
@@ -47085,6 +48321,7 @@ program2.addCommand(createInboxCommand());
47085
48321
  program2.addCommand(createRepoCommand());
47086
48322
  program2.addCommand(createVulnerabilitiesCommand());
47087
48323
  program2.addCommand(createCodeSizeCommand());
48324
+ program2.addCommand(createUploadCommand());
47088
48325
  program2.addCommand(createPentestCommand());
47089
48326
  program2.addCommand(createCompletionCommand(program2));
47090
48327
  program2.addCommand(createUpgradeCommand());