securewithtrace 0.32.0 → 0.34.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 +1 -0
  2. package/dist/index.js +1237 -317
  3. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -10176,7 +10176,7 @@ var $ZodBase64 = /* @__PURE__ */ $constructor("$ZodBase64", (inst, def) => {
10176
10176
  function isValidBase64URL(data) {
10177
10177
  if (!base64url.test(data))
10178
10178
  return false;
10179
- const base643 = data.replace(/[-_]/g, (c49) => c49 === "-" ? "+" : "/");
10179
+ const base643 = data.replace(/[-_]/g, (c51) => c51 === "-" ? "+" : "/");
10180
10180
  const padded = base643.padEnd(Math.ceil(base643.length / 4) * 4, "=");
10181
10181
  return isValidBase64(padded);
10182
10182
  }
@@ -20220,9 +20220,9 @@ var ZodDate2 = /* @__PURE__ */ $constructor("ZodDate", (inst, def) => {
20220
20220
  inst._zod.processJSONSchema = (ctx, json2, params) => dateProcessor(inst, ctx, json2, params);
20221
20221
  inst.min = (value, params) => inst.check(_gte(value, params));
20222
20222
  inst.max = (value, params) => inst.check(_lte(value, params));
20223
- const c49 = inst._zod.bag;
20224
- inst.minDate = c49.minimum ? new Date(c49.minimum) : null;
20225
- inst.maxDate = c49.maximum ? new Date(c49.maximum) : null;
20223
+ const c51 = inst._zod.bag;
20224
+ inst.minDate = c51.minimum ? new Date(c51.minimum) : null;
20225
+ inst.maxDate = c51.maximum ? new Date(c51.maximum) : null;
20226
20226
  });
20227
20227
  function date3(params) {
20228
20228
  return _date(ZodDate2, params);
@@ -21911,7 +21911,8 @@ var OperatorPentestContextSchema = external_exports.object({
21911
21911
  environments: external_exports.array(
21912
21912
  external_exports.object({
21913
21913
  name: external_exports.string(),
21914
- primaryUrl: external_exports.string(),
21914
+ // Null for repo-only application types (CLI, MOBILE, DESKTOP).
21915
+ primaryUrl: external_exports.string().nullable(),
21915
21916
  isProduction: external_exports.boolean()
21916
21917
  })
21917
21918
  ),
@@ -22365,27 +22366,12 @@ var ApplicationRepositoryRoleValues = [
22365
22366
  "MOBILE_CLIENT",
22366
22367
  "OTHER"
22367
22368
  ];
22368
- var ScopeDomainSourceValues = ["DERIVED", "MANUAL"];
22369
22369
  var ApplicationIdParamsSchema = OrgIdParamsSchema.extend({
22370
22370
  applicationId: external_exports.uuid()
22371
22371
  });
22372
22372
  var ApplicationRepositoryIdParamsSchema = ApplicationIdParamsSchema.extend({
22373
22373
  applicationRepositoryId: external_exports.uuid()
22374
22374
  });
22375
- var AuthorizedDomainIdParamsSchema = ApplicationIdParamsSchema.extend({
22376
- applicationAuthorizedDomainId: external_exports.uuid()
22377
- });
22378
- var AuthorizedDomainResponseSchema = external_exports.object({
22379
- applicationAuthorizedDomainId: external_exports.uuid(),
22380
- applicationId: external_exports.uuid(),
22381
- organizationId: external_exports.uuid(),
22382
- domain: external_exports.string(),
22383
- source: external_exports.enum(ScopeDomainSourceValues),
22384
- createdAt: external_exports.date()
22385
- });
22386
- var CreateAuthorizedDomainRequestSchema = external_exports.object({
22387
- domain: external_exports.string().trim().min(1).max(253)
22388
- }).strict();
22389
22375
  var ApplicationRepositorySummarySchema = external_exports.object({
22390
22376
  applicationRepositoryId: external_exports.uuid().nullable(),
22391
22377
  applicationId: external_exports.uuid(),
@@ -22426,7 +22412,6 @@ var ApplicationResponseSchema = external_exports.object({
22426
22412
  // Environments-tab panel hydrates from this payload (no client GET). Defaulted
22427
22413
  // (like `type`) so a new client reading a pre-this-release API during a rolling
22428
22414
  // deploy doesn't reject application rows that predate the field.
22429
- authorizedDomains: external_exports.array(AuthorizedDomainResponseSchema).default([]),
22430
22415
  // Signed S3 URL for the Overview screenshot. Defaulted null so a new client
22431
22416
  // reading a pre-this-release API during a rolling deploy doesn't reject rows
22432
22417
  // that omit the field. The raw S3 key is never on the wire.
@@ -22602,35 +22587,6 @@ var applicationContract = c4.router({
22602
22587
  409: ErrorResponseSchema,
22603
22588
  500: ErrorResponseSchema
22604
22589
  }
22605
- },
22606
- addAuthorizedDomain: {
22607
- method: "POST",
22608
- path: "/organizations/:organizationId/applications/:applicationId/authorized-domains",
22609
- pathParams: ApplicationIdParamsSchema,
22610
- body: CreateAuthorizedDomainRequestSchema,
22611
- responses: {
22612
- 201: AuthorizedDomainResponseSchema,
22613
- // 400 = INVALID_DOMAIN (not a registrable domain / IP / bare suffix);
22614
- // 409 = DOMAIN_ALREADY_AUTHORIZED.
22615
- 400: ErrorResponseSchema,
22616
- 401: ErrorResponseSchema,
22617
- 403: ErrorResponseSchema,
22618
- 404: ErrorResponseSchema,
22619
- 409: ErrorResponseSchema,
22620
- 500: ErrorResponseSchema
22621
- }
22622
- },
22623
- deleteAuthorizedDomain: {
22624
- method: "DELETE",
22625
- path: "/organizations/:organizationId/applications/:applicationId/authorized-domains/:applicationAuthorizedDomainId",
22626
- pathParams: AuthorizedDomainIdParamsSchema,
22627
- responses: {
22628
- 204: DeleteResponseSchema,
22629
- 401: ErrorResponseSchema,
22630
- 403: ErrorResponseSchema,
22631
- 404: ErrorResponseSchema,
22632
- 500: ErrorResponseSchema
22633
- }
22634
22590
  }
22635
22591
  });
22636
22592
 
@@ -23871,6 +23827,140 @@ var cloudIntegrationContract = c7.router({
23871
23827
  }
23872
23828
  });
23873
23829
 
23830
+ // ../../packages/api-contracts/src/schemas/api/v1/code-size.api.ts
23831
+ var CodeSizeBucketTallySchema = external_exports.object({
23832
+ files: external_exports.number().int().nonnegative(),
23833
+ lines: external_exports.number().int().nonnegative(),
23834
+ code: external_exports.number().int().nonnegative()
23835
+ });
23836
+ var CodeSizeLanguageTallySchema = external_exports.object({
23837
+ language: external_exports.string(),
23838
+ files: external_exports.number().int().nonnegative(),
23839
+ code: external_exports.number().int().nonnegative()
23840
+ });
23841
+ var CodeSizePlatformValues = ["GITHUB", "BITBUCKET"];
23842
+ var CodeSizeRepositorySchema = external_exports.object({
23843
+ repositoryId: external_exports.uuid(),
23844
+ repositoryUrl: external_exports.url(),
23845
+ platform: external_exports.enum(CodeSizePlatformValues),
23846
+ /** Billable SLOC of the current resolved branch. null = not measured yet. */
23847
+ billableSloc: external_exports.number().int().nonnegative().nullable()
23848
+ });
23849
+ var CodeSizeBucketsSchema = external_exports.object({
23850
+ code: CodeSizeBucketTallySchema,
23851
+ tests: CodeSizeBucketTallySchema,
23852
+ docs: CodeSizeBucketTallySchema,
23853
+ config_data: CodeSizeBucketTallySchema,
23854
+ vendored_generated: CodeSizeBucketTallySchema
23855
+ });
23856
+ var OrganizationCodeSizeResponseSchema = external_exports.object({
23857
+ /** All CONNECTED GitHub/Bitbucket repos. Server-sorted: measured first by
23858
+ * billableSloc desc (ties repositoryUrl asc), then unmeasured by
23859
+ * repositoryUrl asc — clients render as-is (spec-pr-2 §3.3). */
23860
+ repositories: external_exports.array(CodeSizeRepositorySchema),
23861
+ /** Sums over measured repos only. All-zero when none are measured. */
23862
+ totals: external_exports.object({
23863
+ billableSloc: external_exports.number().int().nonnegative(),
23864
+ rawLines: external_exports.number().int().nonnegative(),
23865
+ buckets: CodeSizeBucketsSchema,
23866
+ /** Merged across repos, re-sorted code desc then language asc, capped
23867
+ * at 12 for display (stored lists are complete; spec-pr-2 §3.2). */
23868
+ codeByLanguage: external_exports.array(CodeSizeLanguageTallySchema),
23869
+ /** Distinct languages in the code bucket across measured repos — the
23870
+ * TRUE count, not codeByLanguage.length (which is display-capped). */
23871
+ languageCount: external_exports.number().int().nonnegative()
23872
+ })
23873
+ });
23874
+ var StaffCodeSizeRepositorySchema = external_exports.object({
23875
+ repositoryId: external_exports.uuid(),
23876
+ repositoryUrl: external_exports.url(),
23877
+ platform: external_exports.enum(CodeSizePlatformValues),
23878
+ /** Resolved branch the measurement is keyed on (scanningBranch ?? default). */
23879
+ branch: external_exports.string(),
23880
+ /** null = no successful measurement for the current resolved branch. */
23881
+ billableSloc: external_exports.number().int().nonnegative().nullable(),
23882
+ /** null exactly when `billableSloc` is null. */
23883
+ measuredAt: external_exports.iso.datetime().nullable()
23884
+ });
23885
+ var StaffOrganizationCodeSizeResponseSchema = external_exports.object({
23886
+ /** Same server-side sort as the customer wire: measured first by
23887
+ * billableSloc desc (ties repositoryUrl asc), then unmeasured by
23888
+ * repositoryUrl asc. */
23889
+ repositories: external_exports.array(StaffCodeSizeRepositorySchema),
23890
+ // No language breakdown here on purpose: this panel answers "is every
23891
+ // repository measured, and how fresh", and the language view already exists
23892
+ // on the customer panel staff can impersonate into.
23893
+ totals: external_exports.object({
23894
+ billableSloc: external_exports.number().int().nonnegative(),
23895
+ rawLines: external_exports.number().int().nonnegative(),
23896
+ /** Connected GitHub/Bitbucket repos — the re-measure `all` scope size. */
23897
+ repositoryCount: external_exports.number().int().nonnegative(),
23898
+ /** Repos with a measurement for their current resolved branch. */
23899
+ measuredCount: external_exports.number().int().nonnegative(),
23900
+ /** Newest measuredAt across the org; null when nothing is measured. */
23901
+ lastMeasuredAt: external_exports.iso.datetime().nullable()
23902
+ })
23903
+ });
23904
+ var StaffCodeSizeRemeasureScopeValues = ["all", "unmeasured"];
23905
+ var StaffCodeSizeRemeasureRequestSchema = external_exports.object({
23906
+ scope: external_exports.enum(StaffCodeSizeRemeasureScopeValues)
23907
+ });
23908
+ var StaffCodeSizeRemeasureResponseSchema = external_exports.object({
23909
+ scope: external_exports.enum(StaffCodeSizeRemeasureScopeValues),
23910
+ /** Repos the scope selected. */
23911
+ requested: external_exports.number().int().nonnegative(),
23912
+ /** Messages SQS accepted. Lower than `requested` means a partial send —
23913
+ * reported rather than masked (the panel states this number). */
23914
+ enqueued: external_exports.number().int().nonnegative()
23915
+ });
23916
+
23917
+ // ../../packages/api-contracts/src/contracts/v1/code-size.ts
23918
+ var c8 = initContract();
23919
+ var codeSizeContract = c8.router({
23920
+ getOrganizationCodeSize: {
23921
+ method: "GET",
23922
+ path: "/organizations/:organizationId/code-size",
23923
+ pathParams: OrgIdParamsSchema,
23924
+ responses: {
23925
+ 200: OrganizationCodeSizeResponseSchema,
23926
+ 401: ErrorResponseSchema,
23927
+ 403: ErrorResponseSchema,
23928
+ 404: ErrorResponseSchema,
23929
+ 500: ErrorResponseSchema
23930
+ }
23931
+ },
23932
+ // Staff-only: per-repo measurement freshness for the admin Controls panel.
23933
+ getStaffOrganizationCodeSize: {
23934
+ method: "GET",
23935
+ path: "/staff/organizations/:organizationId/code-size",
23936
+ pathParams: OrgIdParamsSchema,
23937
+ responses: {
23938
+ 200: StaffOrganizationCodeSizeResponseSchema,
23939
+ 401: ErrorResponseSchema,
23940
+ 403: ErrorResponseSchema,
23941
+ 404: ErrorResponseSchema,
23942
+ 500: ErrorResponseSchema
23943
+ }
23944
+ },
23945
+ // Staff-only: enqueue measurements for the scoped repos. Enqueue-only — the
23946
+ // worker measures asynchronously, so a 200 means "requested", not "measured".
23947
+ remeasureStaffOrganizationCodeSize: {
23948
+ method: "POST",
23949
+ path: "/staff/organizations/:organizationId/code-size/re-measure",
23950
+ pathParams: OrgIdParamsSchema,
23951
+ body: StaffCodeSizeRemeasureRequestSchema,
23952
+ responses: {
23953
+ 200: StaffCodeSizeRemeasureResponseSchema,
23954
+ 401: ErrorResponseSchema,
23955
+ 403: ErrorResponseSchema,
23956
+ 404: ErrorResponseSchema,
23957
+ 500: ErrorResponseSchema,
23958
+ // Measurement enqueueing is unconfigured (REPO_METADATA_QUEUE_URL unset).
23959
+ 503: ErrorResponseSchema
23960
+ }
23961
+ }
23962
+ });
23963
+
23874
23964
  // ../../packages/api-contracts/src/schemas/domain/credential.schema.ts
23875
23965
  var CredentialTypeValues = [
23876
23966
  "BEARER_TOKEN",
@@ -23978,13 +24068,13 @@ var StorageStateSchema = external_exports.object({
23978
24068
  } catch {
23979
24069
  return;
23980
24070
  }
23981
- s.cookies.forEach((c49, i) => {
23982
- const d2 = c49.domain.replace(/^\./, "").toLowerCase();
24071
+ s.cookies.forEach((c51, i) => {
24072
+ const d2 = c51.domain.replace(/^\./, "").toLowerCase();
23983
24073
  if (d2 !== host && !host.endsWith(`.${d2}`)) {
23984
24074
  ctx.addIssue({
23985
24075
  code: "custom",
23986
24076
  path: ["cookies", i, "domain"],
23987
- message: `cookie domain "${c49.domain}" is not the capture origin host "${host}" or a parent domain of it`
24077
+ message: `cookie domain "${c51.domain}" is not the capture origin host "${host}" or a parent domain of it`
23988
24078
  });
23989
24079
  }
23990
24080
  });
@@ -24192,8 +24282,8 @@ var RevealCredentialSecretResponseSchema = external_exports.discriminatedUnion("
24192
24282
  ]);
24193
24283
 
24194
24284
  // ../../packages/api-contracts/src/contracts/v1/credential.ts
24195
- var c8 = initContract();
24196
- var credentialContract = c8.router({
24285
+ var c9 = initContract();
24286
+ var credentialContract = c9.router({
24197
24287
  listCredentials: {
24198
24288
  method: "GET",
24199
24289
  path: "/organizations/:organizationId/environments/:environmentId/credentials",
@@ -24285,7 +24375,7 @@ var credentialContract = c8.router({
24285
24375
  method: "POST",
24286
24376
  path: "/organizations/:organizationId/environments/:environmentId/credentials/:credentialId/reveal",
24287
24377
  pathParams: CredentialIdParamsSchema,
24288
- body: c8.noBody(),
24378
+ body: c9.noBody(),
24289
24379
  responses: {
24290
24380
  200: RevealCredentialSecretResponseSchema,
24291
24381
  401: ErrorResponseSchema,
@@ -24512,8 +24602,8 @@ var DependencySummaryResponseSchema = external_exports.object({
24512
24602
  });
24513
24603
 
24514
24604
  // ../../packages/api-contracts/src/contracts/v1/dependency.ts
24515
- var c9 = initContract();
24516
- var dependencyContract = c9.router({
24605
+ var c10 = initContract();
24606
+ var dependencyContract = c10.router({
24517
24607
  /** Cross-repo dependency search. Two-phase: pg_trgm on Package, then join repos. */
24518
24608
  searchDependencies: {
24519
24609
  method: "POST",
@@ -24658,8 +24748,8 @@ var DockerScoutIntegrationResponseSchema = external_exports.object({
24658
24748
  });
24659
24749
 
24660
24750
  // ../../packages/api-contracts/src/contracts/v1/docker-scout-integration.ts
24661
- var c10 = initContract();
24662
- var dockerScoutIntegrationContract = c10.router({
24751
+ var c11 = initContract();
24752
+ var dockerScoutIntegrationContract = c11.router({
24663
24753
  createDockerScoutIntegration: {
24664
24754
  method: "POST",
24665
24755
  path: "/organizations/:organizationId/docker-scout-integration",
@@ -24848,12 +24938,12 @@ var EngagementResponseSchema = external_exports.object({
24848
24938
  });
24849
24939
 
24850
24940
  // ../../packages/api-contracts/src/contracts/v1/engagement.ts
24851
- var c11 = initContract();
24941
+ var c12 = initContract();
24852
24942
  var RecordDashboardSessionRequest = external_exports.object({
24853
24943
  sessionId: external_exports.string().min(1).max(100)
24854
24944
  });
24855
24945
  var RecordDashboardSessionResponse = external_exports.object({});
24856
- var engagementContract = c11.router({
24946
+ var engagementContract = c12.router({
24857
24947
  recordDashboardSession: {
24858
24948
  method: "POST",
24859
24949
  // `pathPrefix: '/v1'` is applied at the router level (see
@@ -24902,7 +24992,7 @@ var EnvironmentResponseSchema = external_exports.object({
24902
24992
  applicationId: external_exports.uuid(),
24903
24993
  organizationId: external_exports.uuid(),
24904
24994
  name: external_exports.string(),
24905
- primaryUrl: external_exports.string(),
24995
+ primaryUrl: external_exports.string().nullable(),
24906
24996
  description: external_exports.string().nullable(),
24907
24997
  isProduction: external_exports.boolean(),
24908
24998
  createdAt: external_exports.date(),
@@ -24911,7 +25001,7 @@ var EnvironmentResponseSchema = external_exports.object({
24911
25001
  var ListEnvironmentsResponseSchema = external_exports.array(EnvironmentResponseSchema);
24912
25002
  var CreateEnvironmentRequestSchema = external_exports.object({
24913
25003
  name: EnvNameSchema,
24914
- primaryUrl: external_exports.url().max(2048),
25004
+ primaryUrl: external_exports.url().max(2048).optional(),
24915
25005
  description: external_exports.string().max(1e3).optional(),
24916
25006
  isProduction: external_exports.boolean().optional().default(false)
24917
25007
  }).strict();
@@ -24923,8 +25013,8 @@ var UpdateEnvironmentRequestSchema = external_exports.object({
24923
25013
  }).strict();
24924
25014
 
24925
25015
  // ../../packages/api-contracts/src/contracts/v1/environment.ts
24926
- var c12 = initContract();
24927
- var environmentContract = c12.router({
25016
+ var c13 = initContract();
25017
+ var environmentContract = c13.router({
24928
25018
  listEnvironments: {
24929
25019
  method: "GET",
24930
25020
  path: "/organizations/:organizationId/applications/:applicationId/environments",
@@ -25174,8 +25264,8 @@ var GitHubRepoSchema = external_exports.object({
25174
25264
  var ListGitHubInstallationReposResponseSchema = external_exports.array(GitHubRepoSchema);
25175
25265
 
25176
25266
  // ../../packages/api-contracts/src/contracts/v1/github.ts
25177
- var c13 = initContract();
25178
- var githubContract = c13.router({
25267
+ var c14 = initContract();
25268
+ var githubContract = c14.router({
25179
25269
  // Tier chooser data: which app tiers this env can connect + the org's
25180
25270
  // entitlement default. Pure read (auth context + server config).
25181
25271
  getConnectOptions: {
@@ -25274,7 +25364,7 @@ var githubContract = c13.router({
25274
25364
  method: "POST",
25275
25365
  path: "/organizations/:organizationId/github/install-requests/:requestId/cancel",
25276
25366
  pathParams: GitHubInstallRequestParamsSchema,
25277
- body: c13.type(),
25367
+ body: c14.type(),
25278
25368
  responses: {
25279
25369
  200: GitHubInstallRequestSchema,
25280
25370
  401: ErrorResponseSchema,
@@ -25285,7 +25375,7 @@ var githubContract = c13.router({
25285
25375
  }
25286
25376
  }
25287
25377
  });
25288
- var staffGithubContract = c13.router({
25378
+ var staffGithubContract = c14.router({
25289
25379
  listStaffInstallations: {
25290
25380
  method: "GET",
25291
25381
  path: "/staff/github/installations",
@@ -25328,8 +25418,8 @@ var ManagedInboxLabelParamsSchema = OrgIdParamsSchema.extend({
25328
25418
  });
25329
25419
 
25330
25420
  // ../../packages/api-contracts/src/contracts/v1/inbox.ts
25331
- var c14 = initContract();
25332
- var inboxContract = c14.router({
25421
+ var c15 = initContract();
25422
+ var inboxContract = c15.router({
25333
25423
  listInboxes: {
25334
25424
  method: "GET",
25335
25425
  path: "/organizations/:organizationId/inboxes",
@@ -25406,8 +25496,8 @@ var UpdateKnowledgeBaseResponseSchema = external_exports.object({
25406
25496
  });
25407
25497
 
25408
25498
  // ../../packages/api-contracts/src/contracts/v1/knowledge-base.ts
25409
- var c15 = initContract();
25410
- var knowledgeBaseContract = c15.router({
25499
+ var c16 = initContract();
25500
+ var knowledgeBaseContract = c16.router({
25411
25501
  getKnowledgeBase: {
25412
25502
  method: "GET",
25413
25503
  path: "/organizations/:organizationId/knowledge-base",
@@ -25628,8 +25718,8 @@ var UpdateRepoDocumentContentResponseSchema = external_exports.object({
25628
25718
  });
25629
25719
 
25630
25720
  // ../../packages/api-contracts/src/contracts/v1/knowledge-document.ts
25631
- var c16 = initContract();
25632
- var knowledgeDocumentContract = c16.router({
25721
+ var c17 = initContract();
25722
+ var knowledgeDocumentContract = c17.router({
25633
25723
  createDocument: {
25634
25724
  method: "POST",
25635
25725
  path: "/organizations/:organizationId/knowledge-documents",
@@ -25955,8 +26045,8 @@ var LinearMetadataResponseSchema = external_exports.object({
25955
26045
  });
25956
26046
 
25957
26047
  // ../../packages/api-contracts/src/contracts/v1/linear-integration.ts
25958
- var c17 = initContract();
25959
- var linearIntegrationContract = c17.router({
26048
+ var c18 = initContract();
26049
+ var linearIntegrationContract = c18.router({
25960
26050
  getLinearIntegration: {
25961
26051
  method: "GET",
25962
26052
  path: "/organizations/:organizationId/integrations/linear",
@@ -25974,7 +26064,7 @@ var linearIntegrationContract = c17.router({
25974
26064
  method: "POST",
25975
26065
  path: "/organizations/:organizationId/integrations/linear/connect",
25976
26066
  pathParams: OrgIdParamsSchema,
25977
- body: c17.noBody(),
26067
+ body: c18.noBody(),
25978
26068
  responses: {
25979
26069
  200: LinearConnectResponseSchema,
25980
26070
  401: ErrorResponseSchema,
@@ -25989,7 +26079,7 @@ var linearIntegrationContract = c17.router({
25989
26079
  method: "POST",
25990
26080
  path: "/organizations/:organizationId/integrations/linear/disconnect",
25991
26081
  pathParams: OrgIdParamsSchema,
25992
- body: c17.noBody(),
26082
+ body: c18.noBody(),
25993
26083
  responses: {
25994
26084
  200: LinearIntegrationResponseSchema,
25995
26085
  401: ErrorResponseSchema,
@@ -26151,8 +26241,8 @@ var LinearTicketContextResponseSchema = external_exports.object({
26151
26241
  });
26152
26242
 
26153
26243
  // ../../packages/api-contracts/src/contracts/v1/linear-tickets.ts
26154
- var c18 = initContract();
26155
- var linearTicketContract = c18.router({
26244
+ var c19 = initContract();
26245
+ var linearTicketContract = c19.router({
26156
26246
  /**
26157
26247
  * Push one or more pentest findings to Linear as tickets. Customer-side
26158
26248
  * (VIEW_RESULTS + PENTEST capability); non-admins create into the org's
@@ -26256,8 +26346,8 @@ var LlmProviderKeyResponseSchema = external_exports.object({
26256
26346
  var ListLlmProviderKeysResponseSchema = external_exports.array(LlmProviderKeyResponseSchema);
26257
26347
 
26258
26348
  // ../../packages/api-contracts/src/contracts/v1/llm-provider-key.ts
26259
- var c19 = initContract();
26260
- var llmProviderKeyContract = c19.router({
26349
+ var c20 = initContract();
26350
+ var llmProviderKeyContract = c20.router({
26261
26351
  createLlmProviderKey: {
26262
26352
  method: "POST",
26263
26353
  path: "/organizations/:organizationId/llm-provider-keys",
@@ -26451,8 +26541,8 @@ var LogoDeleteRequestSchema = external_exports.object({
26451
26541
  });
26452
26542
 
26453
26543
  // ../../packages/api-contracts/src/contracts/v1/organization.ts
26454
- var c20 = initContract();
26455
- var organizationContract = c20.router({
26544
+ var c21 = initContract();
26545
+ var organizationContract = c21.router({
26456
26546
  listOrganizations: {
26457
26547
  method: "GET",
26458
26548
  path: "/organizations",
@@ -26656,7 +26746,7 @@ var organizationContract = c20.router({
26656
26746
  method: "POST",
26657
26747
  path: "/organizations/:organizationId/logos-from-homepage",
26658
26748
  pathParams: OrgIdParamsSchema,
26659
- body: c20.type(),
26749
+ body: c21.type(),
26660
26750
  responses: {
26661
26751
  200: OrganizationResponseSchema,
26662
26752
  // 400 — no homepage set on the organization.
@@ -27269,11 +27359,19 @@ var CustomerPentestDetailResponseSchema = CustomerPentestResponseSchema.extend({
27269
27359
  * GET …/questionnaire. `.optional()` for rolling-deploy safety.
27270
27360
  */
27271
27361
  questionnaireEditable: external_exports.boolean().optional(),
27272
- hasQuestionnaireAnswers: external_exports.boolean().optional()
27362
+ hasQuestionnaireAnswers: external_exports.boolean().optional(),
27363
+ /**
27364
+ * True once the customer has submitted the questionnaire (frozen). Optional
27365
+ * for rolling-deploy safety against an older API that does not send it.
27366
+ */
27367
+ questionnaireSubmitted: external_exports.boolean().optional()
27273
27368
  });
27274
27369
  var CustomerPentestQuestionnaireResponseSchema = external_exports.object({
27275
27370
  pentestId: external_exports.uuid(),
27276
- /** True iff currentStage ∈ SCOPING_PENTEST_STAGES (DRAFT | READY_TO_START). */
27371
+ /**
27372
+ * True iff the engagement is still in a scoping stage AND the questionnaire
27373
+ * has not been submitted. A submitted intake is frozen even during scoping.
27374
+ */
27277
27375
  editable: external_exports.boolean(),
27278
27376
  /**
27279
27377
  * Lenient on unknown keys: during a rolling deploy the dashboard runs against
@@ -27282,7 +27380,13 @@ var CustomerPentestQuestionnaireResponseSchema = external_exports.object({
27282
27380
  * strict — see the upsert request below.
27283
27381
  */
27284
27382
  answers: PentestCustomerIntakeStoredAnswersSchema.nullable(),
27285
- updatedAt: external_exports.coerce.date()
27383
+ updatedAt: external_exports.coerce.date(),
27384
+ /**
27385
+ * When the customer submitted (froze) the questionnaire. Null = still a
27386
+ * draft. Optional so an older API response still parses during a rolling
27387
+ * deploy; absence means "not submitted" to new clients.
27388
+ */
27389
+ submittedAt: external_exports.coerce.date().nullable().optional()
27286
27390
  });
27287
27391
  var UpsertCustomerPentestQuestionnaireRequestSchema = external_exports.object({
27288
27392
  /**
@@ -27292,6 +27396,14 @@ var UpsertCustomerPentestQuestionnaireRequestSchema = external_exports.object({
27292
27396
  */
27293
27397
  answers: PentestCustomerIntakeWriteAnswersSchema.nullable()
27294
27398
  }).strict();
27399
+ var SubmitCustomerPentestQuestionnaireRequestSchema = external_exports.object({
27400
+ /**
27401
+ * Optional last draft to persist atomically with the freeze. Omitted keeps
27402
+ * whatever is already stored. Retired keys are accepted and dropped, same
27403
+ * as the upsert path.
27404
+ */
27405
+ answers: PentestCustomerIntakeWriteAnswersSchema.optional()
27406
+ }).strict();
27295
27407
  var ReadPentestResponseSchema = external_exports.union([
27296
27408
  PentestDetailResponseSchema,
27297
27409
  CustomerPentestShellResponseSchema
@@ -27429,8 +27541,8 @@ var ScanReuseSuggestionsResponseSchema = external_exports.object({
27429
27541
  });
27430
27542
 
27431
27543
  // ../../packages/api-contracts/src/contracts/v1/pentest.ts
27432
- var c21 = initContract();
27433
- var pentestContract = c21.router({
27544
+ var c22 = initContract();
27545
+ var pentestContract = c22.router({
27434
27546
  listPentests: {
27435
27547
  method: "GET",
27436
27548
  path: "/organizations/:organizationId/pentests",
@@ -27507,6 +27619,21 @@ var pentestContract = c21.router({
27507
27619
  500: ErrorResponseSchema
27508
27620
  }
27509
27621
  },
27622
+ submitCustomerPentestQuestionnaire: {
27623
+ method: "POST",
27624
+ path: "/organizations/:organizationId/customer-pentests/:pentestId/questionnaire/submit",
27625
+ pathParams: CustomerPentestIdParamsSchema,
27626
+ body: SubmitCustomerPentestQuestionnaireRequestSchema,
27627
+ responses: {
27628
+ 200: CustomerPentestQuestionnaireResponseSchema,
27629
+ 400: ErrorResponseSchema,
27630
+ 401: ErrorResponseSchema,
27631
+ 403: ErrorResponseSchema,
27632
+ 404: ErrorResponseSchema,
27633
+ 409: ErrorResponseSchema,
27634
+ 500: ErrorResponseSchema
27635
+ }
27636
+ },
27510
27637
  createPentest: {
27511
27638
  method: "POST",
27512
27639
  path: "/organizations/:organizationId/pentests",
@@ -27866,8 +27993,8 @@ var ReadAttestationDownloadResponseSchema = external_exports.union([
27866
27993
  ]);
27867
27994
 
27868
27995
  // ../../packages/api-contracts/src/contracts/v1/pentest-attestation.ts
27869
- var c22 = initContract();
27870
- var pentestAttestationContract = c22.router({
27996
+ var c23 = initContract();
27997
+ var pentestAttestationContract = c23.router({
27871
27998
  getDefaults: {
27872
27999
  method: "GET",
27873
28000
  path: "/organizations/:organizationId/pentests/:pentestId/attestations/defaults",
@@ -28009,8 +28136,8 @@ var ListPentestAuditLogResponseSchema = external_exports.object({
28009
28136
  });
28010
28137
 
28011
28138
  // ../../packages/api-contracts/src/contracts/v1/pentest-audit-log.ts
28012
- var c23 = initContract();
28013
- var pentestAuditLogContract = c23.router({
28139
+ var c24 = initContract();
28140
+ var pentestAuditLogContract = c24.router({
28014
28141
  listAuditLog: {
28015
28142
  method: "GET",
28016
28143
  path: "/organizations/:organizationId/pentests/:pentestId/audit-log",
@@ -28026,6 +28153,86 @@ var pentestAuditLogContract = c23.router({
28026
28153
  }
28027
28154
  });
28028
28155
 
28156
+ // ../../packages/api-contracts/src/schemas/api/v1/pentest-authorized-domain.api.ts
28157
+ var PentestAuthorizedDomainIdParamsSchema = PentestIdParamsSchema.extend({
28158
+ pentestAuthorizedDomainId: external_exports.uuid()
28159
+ });
28160
+ var AuthorizedDomainOriginValues = ["DERIVED", "ENGAGEMENT"];
28161
+ var AuthorizedDomainOrigin = external_exports.enum(AuthorizedDomainOriginValues);
28162
+ var PentestAuthorizedDomainResponseSchema = external_exports.object({
28163
+ /**
28164
+ * NULL for a domain contributed by a bound application while the engagement is
28165
+ * still tracking — there is no row yet, and nothing to address by id.
28166
+ */
28167
+ pentestAuthorizedDomainId: external_exports.uuid().nullable(),
28168
+ pentestId: external_exports.uuid(),
28169
+ organizationId: external_exports.uuid(),
28170
+ domain: external_exports.string(),
28171
+ applicationIds: external_exports.array(external_exports.uuid()),
28172
+ origin: AuthorizedDomainOrigin,
28173
+ /** NULL when the row was written by materialization rather than by a person. */
28174
+ addedById: external_exports.uuid().nullable(),
28175
+ addedAt: external_exports.date().nullable()
28176
+ });
28177
+ var PentestAuthorizedScopeResponseSchema = external_exports.object({
28178
+ domains: external_exports.array(PentestAuthorizedDomainResponseSchema),
28179
+ /** Null while the engagement still tracks its bound applications. */
28180
+ materializedAt: external_exports.date().nullable()
28181
+ });
28182
+ var CreatePentestAuthorizedDomainRequestSchema = external_exports.object({
28183
+ domain: external_exports.string().trim().min(1).max(253)
28184
+ }).strict();
28185
+
28186
+ // ../../packages/api-contracts/src/contracts/v1/pentest-authorized-domain.ts
28187
+ var c25 = initContract();
28188
+ var pentestAuthorizedDomainContract = c25.router({
28189
+ getPentestAuthorizedScope: {
28190
+ method: "GET",
28191
+ path: "/organizations/:organizationId/pentests/:pentestId/authorized-scope",
28192
+ pathParams: PentestIdParamsSchema,
28193
+ responses: {
28194
+ 200: PentestAuthorizedScopeResponseSchema,
28195
+ 401: ErrorResponseSchema,
28196
+ 403: ErrorResponseSchema,
28197
+ 404: ErrorResponseSchema,
28198
+ 500: ErrorResponseSchema
28199
+ }
28200
+ },
28201
+ addPentestAuthorizedDomain: {
28202
+ method: "POST",
28203
+ path: "/organizations/:organizationId/pentests/:pentestId/authorized-scope/domains",
28204
+ pathParams: PentestIdParamsSchema,
28205
+ body: CreatePentestAuthorizedDomainRequestSchema,
28206
+ responses: {
28207
+ 201: PentestAuthorizedDomainResponseSchema,
28208
+ // 400 = INVALID_DOMAIN (not a registrable domain / IP / bare suffix);
28209
+ // 409 = DOMAIN_ALREADY_AUTHORIZED or PENTEST_CLOSED.
28210
+ 400: ErrorResponseSchema,
28211
+ 401: ErrorResponseSchema,
28212
+ 403: ErrorResponseSchema,
28213
+ 404: ErrorResponseSchema,
28214
+ 409: ErrorResponseSchema,
28215
+ 500: ErrorResponseSchema
28216
+ }
28217
+ },
28218
+ removePentestAuthorizedDomain: {
28219
+ method: "DELETE",
28220
+ path: "/organizations/:organizationId/pentests/:pentestId/authorized-scope/domains/:pentestAuthorizedDomainId",
28221
+ pathParams: PentestAuthorizedDomainIdParamsSchema,
28222
+ responses: {
28223
+ 204: DeleteResponseSchema,
28224
+ // 409 = DOMAIN_FROM_ENVIRONMENT (derived from a selected environment's
28225
+ // Target URL, so removing the row here would not remove it from scope) or
28226
+ // PENTEST_CLOSED.
28227
+ 401: ErrorResponseSchema,
28228
+ 403: ErrorResponseSchema,
28229
+ 404: ErrorResponseSchema,
28230
+ 409: ErrorResponseSchema,
28231
+ 500: ErrorResponseSchema
28232
+ }
28233
+ }
28234
+ });
28235
+
28029
28236
  // ../../packages/api-contracts/src/schemas/api/v1/pentest-engagement-exclusion.api.ts
28030
28237
  var PentestEngagementExclusionsParamsSchema = OrgIdParamsSchema.extend({
28031
28238
  pentestId: external_exports.string().min(1)
@@ -28051,8 +28258,8 @@ var CreatePentestEngagementExclusionRequestSchema = external_exports.object({
28051
28258
  }).strict();
28052
28259
 
28053
28260
  // ../../packages/api-contracts/src/contracts/v1/pentest-engagement-exclusion.ts
28054
- var c24 = initContract();
28055
- var pentestEngagementExclusionContract = c24.router({
28261
+ var c26 = initContract();
28262
+ var pentestEngagementExclusionContract = c26.router({
28056
28263
  listPentestEngagementExclusions: {
28057
28264
  method: "GET",
28058
28265
  path: "/organizations/:organizationId/pentests/:pentestId/engagement-exclusions",
@@ -28116,8 +28323,8 @@ var CreatePentestEnvironmentRequestSchema = external_exports.object({
28116
28323
  }).strict();
28117
28324
 
28118
28325
  // ../../packages/api-contracts/src/contracts/v1/pentest-environment.ts
28119
- var c25 = initContract();
28120
- var pentestEnvironmentContract = c25.router({
28326
+ var c27 = initContract();
28327
+ var pentestEnvironmentContract = c27.router({
28121
28328
  listPentestEnvironments: {
28122
28329
  method: "GET",
28123
28330
  path: "/organizations/:organizationId/pentests/:pentestId/environments",
@@ -28976,6 +29183,25 @@ var CurateFindingEvidenceRequestSchema = external_exports.object({
28976
29183
  }).strict().refine((v2) => Object.keys(v2).length > 0, {
28977
29184
  message: "At least one curation field must be provided"
28978
29185
  });
29186
+ var MAX_STAFF_EVIDENCE_UPLOAD_BYTES = 256 * 1024 * 1024;
29187
+ var CreateFindingEvidenceUploadRequestSchema = external_exports.object({
29188
+ filename: external_exports.string().min(1).max(255),
29189
+ sizeBytes: external_exports.number().int().positive().max(MAX_STAFF_EVIDENCE_UPLOAD_BYTES)
29190
+ }).strict();
29191
+ var CreateFindingEvidenceUploadResponseSchema = external_exports.object({
29192
+ key: external_exports.string(),
29193
+ uploadUrl: external_exports.url(),
29194
+ expiresAt: external_exports.coerce.date(),
29195
+ kind: PentestExploitArtifactKind,
29196
+ mimeType: external_exports.string()
29197
+ });
29198
+ var AttachFindingEvidenceRequestSchema = external_exports.object({
29199
+ key: external_exports.string().min(1).max(1024),
29200
+ kind: PentestExploitArtifactKind.optional(),
29201
+ caption: external_exports.string().min(1).max(500).optional(),
29202
+ section: ValidationEvidenceSection.optional(),
29203
+ customerFacing: external_exports.boolean().optional()
29204
+ }).strict();
28979
29205
  var PentestFindingRepositorySchema = external_exports.object({
28980
29206
  repositoryId: external_exports.uuid(),
28981
29207
  repositoryName: external_exports.string(),
@@ -29492,8 +29718,8 @@ var GetParseTaskStatusResponseSchema = external_exports.object({
29492
29718
  });
29493
29719
 
29494
29720
  // ../../packages/api-contracts/src/contracts/v1/pentest-finding.ts
29495
- var c26 = initContract();
29496
- var pentestFindingContract = c26.router({
29721
+ var c28 = initContract();
29722
+ var pentestFindingContract = c28.router({
29497
29723
  // ─── Findings ───
29498
29724
  listFindings: {
29499
29725
  method: "GET",
@@ -29582,6 +29808,43 @@ var pentestFindingContract = c26.router({
29582
29808
  500: ErrorResponseSchema
29583
29809
  }
29584
29810
  },
29811
+ // ─── Evidence (staff manual attach) ───
29812
+ //
29813
+ // Two calls, because the bytes never pass through the api: mint a presigned
29814
+ // PUT, upload straight to S3, then register the object as an exhibit. That
29815
+ // keeps a screen-recorded PoC off the request path entirely (no body limit, no
29816
+ // request timeout, no api memory) and mirrors how the operator agent session
29817
+ // already attaches evidence.
29818
+ createFindingEvidenceUpload: {
29819
+ method: "POST",
29820
+ path: "/organizations/:organizationId/pentests/:pentestId/findings/:pentestFindingId/evidence/uploads",
29821
+ pathParams: PentestFindingIdParamsSchema,
29822
+ body: CreateFindingEvidenceUploadRequestSchema,
29823
+ responses: {
29824
+ 201: CreateFindingEvidenceUploadResponseSchema,
29825
+ 400: ErrorResponseSchema,
29826
+ 401: ErrorResponseSchema,
29827
+ 403: ErrorResponseSchema,
29828
+ 404: ErrorResponseSchema,
29829
+ 409: ErrorResponseSchema,
29830
+ 500: ErrorResponseSchema
29831
+ }
29832
+ },
29833
+ attachFindingEvidence: {
29834
+ method: "POST",
29835
+ path: "/organizations/:organizationId/pentests/:pentestId/findings/:pentestFindingId/evidence",
29836
+ pathParams: PentestFindingIdParamsSchema,
29837
+ body: AttachFindingEvidenceRequestSchema,
29838
+ responses: {
29839
+ 201: PentestExploitArtifactMetadataResponseSchema,
29840
+ 400: ErrorResponseSchema,
29841
+ 401: ErrorResponseSchema,
29842
+ 403: ErrorResponseSchema,
29843
+ 404: ErrorResponseSchema,
29844
+ 409: ErrorResponseSchema,
29845
+ 500: ErrorResponseSchema
29846
+ }
29847
+ },
29585
29848
  curateFindingEvidence: {
29586
29849
  method: "PATCH",
29587
29850
  path: "/organizations/:organizationId/pentests/:pentestId/findings/:pentestFindingId/evidence/:artifactId",
@@ -29962,6 +30225,12 @@ var PentestHypothesisAttemptSummarySchema = external_exports.object({
29962
30225
  status: PentestHypothesisAttemptStatus,
29963
30226
  attemptOrdinal: external_exports.number().int().positive(),
29964
30227
  conclusion: external_exports.string().nullable(),
30228
+ // Optional for rolling-deploy safety: web (Vercel) can deploy this schema before the
30229
+ // API sends the field, and web strictly `.parse()`s the response. Absent → undefined,
30230
+ // which the web serializer coalesces to null. Carries the Blocked/Failed reason so the
30231
+ // hypothesis drawer's verdict panel can show it when the deciding attempt is only in
30232
+ // hand as this summary (a verdict from a prior run).
30233
+ blockerReason: external_exports.string().nullable().optional(),
29965
30234
  startedAt: external_exports.coerce.date().nullable(),
29966
30235
  completedAt: external_exports.coerce.date().nullable(),
29967
30236
  durationMs: external_exports.number().int().nonnegative().nullable(),
@@ -30082,8 +30351,8 @@ var PromotePentestHypothesisRequestSchema = external_exports.object({
30082
30351
  }).strict().optional();
30083
30352
 
30084
30353
  // ../../packages/api-contracts/src/contracts/v1/pentest-hypothesis.ts
30085
- var c27 = initContract();
30086
- var pentestHypothesisContract = c27.router({
30354
+ var c29 = initContract();
30355
+ var pentestHypothesisContract = c29.router({
30087
30356
  listHypotheses: {
30088
30357
  method: "GET",
30089
30358
  path: "/organizations/:organizationId/pentests/:pentestId/hypotheses",
@@ -30186,8 +30455,8 @@ var pentestHypothesisContract = c27.router({
30186
30455
  });
30187
30456
 
30188
30457
  // ../../packages/api-contracts/src/contracts/v1/pentest-manual-finding.ts
30189
- var c28 = initContract();
30190
- var pentestManualFindingContract = c28.router({
30458
+ var c30 = initContract();
30459
+ var pentestManualFindingContract = c30.router({
30191
30460
  listManualFindings: {
30192
30461
  method: "GET",
30193
30462
  path: "/organizations/:organizationId/pentests/:pentestId/manual-findings",
@@ -30274,8 +30543,8 @@ var pentestManualFindingContract = c28.router({
30274
30543
  });
30275
30544
 
30276
30545
  // ../../packages/api-contracts/src/contracts/v1/pentest-report-pdf.ts
30277
- var c29 = initContract();
30278
- var pentestReportPdfContract = c29.router({
30546
+ var c31 = initContract();
30547
+ var pentestReportPdfContract = c31.router({
30279
30548
  getCurrent: {
30280
30549
  method: "GET",
30281
30550
  path: "/organizations/:organizationId/pentests/:pentestId/report-pdf",
@@ -30368,7 +30637,7 @@ var pentestReportPdfContract = c29.router({
30368
30637
  // No body: the render always reads current DB state (cover derived via
30369
30638
  // deriveReportCover(), narrative/scope from the row's columns). The legacy
30370
30639
  // dialog payload was retired with the Generate dialog (report-session RS5).
30371
- body: c29.noBody(),
30640
+ body: c31.noBody(),
30372
30641
  responses: {
30373
30642
  202: StartReportPdfGenerationTaskResponseSchema,
30374
30643
  400: ErrorResponseSchema,
@@ -30559,8 +30828,8 @@ var pentestReportPdfContract = c29.router({
30559
30828
  });
30560
30829
 
30561
30830
  // ../../packages/api-contracts/src/contracts/v1/pentest-validation.ts
30562
- var c30 = initContract();
30563
- var pentestValidationContract = c30.router({
30831
+ var c32 = initContract();
30832
+ var pentestValidationContract = c32.router({
30564
30833
  listValidationRuns: {
30565
30834
  method: "GET",
30566
30835
  path: "/organizations/:organizationId/pentests/:pentestId/validation-runs",
@@ -30668,8 +30937,8 @@ var pentestValidationContract = c30.router({
30668
30937
  });
30669
30938
 
30670
30939
  // ../../packages/api-contracts/src/contracts/v1/repository.ts
30671
- var c31 = initContract();
30672
- var repositoryContract = c31.router({
30940
+ var c33 = initContract();
30941
+ var repositoryContract = c33.router({
30673
30942
  createRepository: {
30674
30943
  method: "POST",
30675
30944
  path: "/organizations/:organizationId/repositories",
@@ -30826,7 +31095,7 @@ var repositoryContract = c31.router({
30826
31095
  method: "POST",
30827
31096
  path: "/organizations/:organizationId/repositories/scanning-branch/confirm-defaults",
30828
31097
  pathParams: OrgIdParamsSchema,
30829
- body: c31.noBody(),
31098
+ body: c33.noBody(),
30830
31099
  responses: {
30831
31100
  200: ConfirmScanningBranchDefaultsResponseSchema,
30832
31101
  401: ErrorResponseSchema,
@@ -30960,8 +31229,8 @@ var ListScanLogsResponseSchema = external_exports.object({
30960
31229
  });
30961
31230
 
30962
31231
  // ../../packages/api-contracts/src/contracts/v1/scan.ts
30963
- var c32 = initContract();
30964
- var scanContract = c32.router({
31232
+ var c34 = initContract();
31233
+ var scanContract = c34.router({
30965
31234
  createScan: {
30966
31235
  method: "POST",
30967
31236
  path: "/organizations/:organizationId/scans",
@@ -31123,8 +31392,8 @@ var AttachTestIntegrationRequestSchema = external_exports.object({
31123
31392
  }).strict();
31124
31393
 
31125
31394
  // ../../packages/api-contracts/src/contracts/v1/test-integration.ts
31126
- var c33 = initContract();
31127
- var testIntegrationContract = c33.router({
31395
+ var c35 = initContract();
31396
+ var testIntegrationContract = c35.router({
31128
31397
  // -- Fixture CRUD (org-less, staff-only) ----------------------------------
31129
31398
  listTestIntegrations: {
31130
31399
  method: "GET",
@@ -31251,8 +31520,8 @@ var UpdateStaffProfileRequestSchema = external_exports.object({
31251
31520
  });
31252
31521
 
31253
31522
  // ../../packages/api-contracts/src/contracts/v1/user.ts
31254
- var c34 = initContract();
31255
- var userContract = c34.router({
31523
+ var c36 = initContract();
31524
+ var userContract = c36.router({
31256
31525
  getStaffProfile: {
31257
31526
  method: "GET",
31258
31527
  path: "/staff/profile",
@@ -31349,8 +31618,8 @@ var VantaIntegrationResponseSchema = external_exports.object({
31349
31618
  });
31350
31619
 
31351
31620
  // ../../packages/api-contracts/src/contracts/v1/vanta-integration.ts
31352
- var c35 = initContract();
31353
- var vantaIntegrationContract = c35.router({
31621
+ var c37 = initContract();
31622
+ var vantaIntegrationContract = c37.router({
31354
31623
  createVantaIntegration: {
31355
31624
  method: "POST",
31356
31625
  path: "/organizations/:organizationId/vanta-integration",
@@ -31682,8 +31951,8 @@ var VerifySharedVulnerabilityEmailCodeResponseSchema = external_exports.object({
31682
31951
  });
31683
31952
 
31684
31953
  // ../../packages/api-contracts/src/contracts/v1/vulnerability.ts
31685
- var c36 = initContract();
31686
- var vulnerabilityContract = c36.router({
31954
+ var c38 = initContract();
31955
+ var vulnerabilityContract = c38.router({
31687
31956
  filterVulnerabilities: {
31688
31957
  method: "POST",
31689
31958
  path: "/organizations/:organizationId/vulnerabilities/filter",
@@ -31891,8 +32160,8 @@ var UpdateVulnerabilityNoteRequestSchema = external_exports.object({
31891
32160
  });
31892
32161
 
31893
32162
  // ../../packages/api-contracts/src/contracts/v1/vulnerability-note.ts
31894
- var c37 = initContract();
31895
- var vulnerabilityNoteContract = c37.router({
32163
+ var c39 = initContract();
32164
+ var vulnerabilityNoteContract = c39.router({
31896
32165
  listNotes: {
31897
32166
  method: "GET",
31898
32167
  path: "/organizations/:organizationId/vulnerabilities/:vulnerabilityId/notes",
@@ -32073,8 +32342,8 @@ var ListWebhookDeliveriesResponseSchema = external_exports.object({
32073
32342
  });
32074
32343
 
32075
32344
  // ../../packages/api-contracts/src/contracts/v1/webhook-settings.ts
32076
- var c38 = initContract();
32077
- var webhookSettingsContract = c38.router({
32345
+ var c40 = initContract();
32346
+ var webhookSettingsContract = c40.router({
32078
32347
  listWebhookEndpoints: {
32079
32348
  method: "GET",
32080
32349
  path: "/organizations/:organizationId/webhook-settings/endpoints",
@@ -32212,11 +32481,12 @@ var webhookSettingsContract = c38.router({
32212
32481
  });
32213
32482
 
32214
32483
  // ../../packages/api-contracts/src/contracts/v1/index.ts
32215
- var c39 = initContract();
32216
- var v1Contract = c39.router(
32484
+ var c41 = initContract();
32485
+ var v1Contract = c41.router(
32217
32486
  {
32218
32487
  admin: adminContract,
32219
32488
  cloudIntegration: cloudIntegrationContract,
32489
+ codeSize: codeSizeContract,
32220
32490
  scan: scanContract,
32221
32491
  organization: organizationContract,
32222
32492
  user: userContract,
@@ -32243,6 +32513,7 @@ var v1Contract = c39.router(
32243
32513
  pentestAuditLog: pentestAuditLogContract,
32244
32514
  pentestEnvironment: pentestEnvironmentContract,
32245
32515
  pentestEngagementExclusion: pentestEngagementExclusionContract,
32516
+ pentestAuthorizedDomain: pentestAuthorizedDomainContract,
32246
32517
  pentestFinding: pentestFindingContract,
32247
32518
  pentestHypothesis: pentestHypothesisContract,
32248
32519
  pentestValidation: pentestValidationContract,
@@ -32261,8 +32532,8 @@ var v1Contract = c39.router(
32261
32532
  );
32262
32533
 
32263
32534
  // ../../packages/api-contracts/src/contracts/index.ts
32264
- var c40 = initContract();
32265
- var contract = c40.router({
32535
+ var c42 = initContract();
32536
+ var contract = c42.router({
32266
32537
  v1: v1Contract
32267
32538
  });
32268
32539
 
@@ -32311,7 +32582,7 @@ var ComplianceRegimeValues = [
32311
32582
  var ComplianceRegime = external_exports.enum(ComplianceRegimeValues);
32312
32583
 
32313
32584
  // ../../packages/api-contracts/src/contracts/v1/agent-session-internal.ts
32314
- var c41 = initContract();
32585
+ var c43 = initContract();
32315
32586
  var AgentSessionInternalParamsSchema = external_exports.object({
32316
32587
  agentSessionId: external_exports.uuid()
32317
32588
  });
@@ -33166,7 +33437,7 @@ var ReportUpdateScopeRequestSchema = external_exports.object({
33166
33437
  expectedUpdatedAt: reportExpectedUpdatedAt,
33167
33438
  rows: external_exports.array(ScopeRowSchema).max(100)
33168
33439
  });
33169
- var agentSessionInternalContract = c41.router({
33440
+ var agentSessionInternalContract = c43.router({
33170
33441
  postEvents: {
33171
33442
  method: "POST",
33172
33443
  path: "/internal/v1/agent-sessions/:agentSessionId/events",
@@ -33866,8 +34137,8 @@ var GitHubWebhookResponseSchema = external_exports.object({
33866
34137
  });
33867
34138
 
33868
34139
  // ../../packages/api-contracts/src/contracts/webhooks/github.ts
33869
- var c42 = initContract();
33870
- var githubWebhookContract = c42.router(
34140
+ var c44 = initContract();
34141
+ var githubWebhookContract = c44.router(
33871
34142
  {
33872
34143
  handleWebhook: {
33873
34144
  method: "POST",
@@ -33885,7 +34156,7 @@ var githubWebhookContract = c42.router(
33885
34156
  pathPrefix: "/webhooks"
33886
34157
  }
33887
34158
  );
33888
- var githubReadonlyWebhookContract = c42.router(
34159
+ var githubReadonlyWebhookContract = c44.router(
33889
34160
  {
33890
34161
  handleWebhook: {
33891
34162
  method: "POST",
@@ -33905,9 +34176,9 @@ var githubReadonlyWebhookContract = c42.router(
33905
34176
  );
33906
34177
 
33907
34178
  // ../../packages/api-contracts/src/contracts/webhooks/linear.ts
33908
- var c43 = initContract();
34179
+ var c45 = initContract();
33909
34180
  var LinearWebhookResponseSchema = external_exports.object({ ok: external_exports.boolean() });
33910
- var linearWebhookContract = c43.router(
34181
+ var linearWebhookContract = c45.router(
33911
34182
  {
33912
34183
  handleWebhook: {
33913
34184
  method: "POST",
@@ -33927,7 +34198,7 @@ var linearWebhookContract = c43.router(
33927
34198
  );
33928
34199
 
33929
34200
  // ../../packages/api-contracts/src/contracts/forge/bitbucket.ts
33930
- var c44 = initContract();
34201
+ var c46 = initContract();
33931
34202
  var InstallEventSchema = external_exports.object({
33932
34203
  eventType: external_exports.enum(["avi:forge:installed:app", "avi:forge:upgraded:app"]),
33933
34204
  context: external_exports.object({ cloudId: external_exports.string().min(1) }).loose(),
@@ -33956,7 +34227,7 @@ var ForgePairingCodeResponseSchema = external_exports.discriminatedUnion("state"
33956
34227
  expiresInSeconds: external_exports.number().int()
33957
34228
  })
33958
34229
  ]);
33959
- var forgeBitbucketContract = c44.router(
34230
+ var forgeBitbucketContract = c46.router(
33960
34231
  {
33961
34232
  lifecycle: {
33962
34233
  method: "POST",
@@ -34003,8 +34274,8 @@ var HealthResponseSchema = external_exports.object({
34003
34274
  });
34004
34275
 
34005
34276
  // ../../packages/api-contracts/src/contracts/health.ts
34006
- var c45 = initContract();
34007
- var healthContract = c45.router({
34277
+ var c47 = initContract();
34278
+ var healthContract = c47.router({
34008
34279
  getHealth: {
34009
34280
  method: "GET",
34010
34281
  path: "/health",
@@ -34016,8 +34287,8 @@ var healthContract = c45.router({
34016
34287
  });
34017
34288
 
34018
34289
  // ../../packages/api-contracts/src/contracts/v1/vulnerability-share.ts
34019
- var c46 = initContract();
34020
- var vulnerabilityShareContract = c46.router(
34290
+ var c48 = initContract();
34291
+ var vulnerabilityShareContract = c48.router(
34021
34292
  {
34022
34293
  resolveShareLink: {
34023
34294
  method: "GET",
@@ -34104,8 +34375,8 @@ var UpdatePrVulnerabilityRequestSchema = external_exports.object({
34104
34375
  });
34105
34376
 
34106
34377
  // ../../packages/api-contracts/src/contracts/v1/pr-vulnerability.ts
34107
- var c47 = initContract();
34108
- var prVulnerabilityContract = c47.router(
34378
+ var c49 = initContract();
34379
+ var prVulnerabilityContract = c49.router(
34109
34380
  {
34110
34381
  listPrVulnerabilities: {
34111
34382
  method: "GET",
@@ -36658,12 +36929,12 @@ function buildApplicationsTable(apps) {
36658
36929
  repos: String(a.repositories.length),
36659
36930
  url: a.applicationUrl ? sanitizeDisplay(a.applicationUrl) : "\u2014"
36660
36931
  }));
36661
- const idWidth = Math.max(2, ...cells.map((c49) => c49.id.length));
36662
- const nameWidth = Math.max(4, ...cells.map((c49) => c49.name.length));
36663
- const typeWidth = Math.max(4, ...cells.map((c49) => c49.type.length));
36932
+ const idWidth = Math.max(2, ...cells.map((c51) => c51.id.length));
36933
+ const nameWidth = Math.max(4, ...cells.map((c51) => c51.name.length));
36934
+ const typeWidth = Math.max(4, ...cells.map((c51) => c51.type.length));
36664
36935
  const header = `${"ID".padEnd(idWidth)} ${"NAME".padEnd(nameWidth)} ${"TYPE".padEnd(typeWidth)} REPOS URL`;
36665
36936
  const rows = cells.map(
36666
- (c49) => `${c49.id.padEnd(idWidth)} ${c49.name.padEnd(nameWidth)} ${c49.type.padEnd(typeWidth)} ${c49.repos.padStart(5)} ${c49.url}`
36937
+ (c51) => `${c51.id.padEnd(idWidth)} ${c51.name.padEnd(nameWidth)} ${c51.type.padEnd(typeWidth)} ${c51.repos.padStart(5)} ${c51.url}`
36667
36938
  );
36668
36939
  return { header, rows };
36669
36940
  }
@@ -36683,12 +36954,12 @@ function buildApplicationRepositoriesTable(links) {
36683
36954
  path: l.repositoryPath ? sanitizeDisplay(l.repositoryPath) : "\u2014",
36684
36955
  status: formatStatus(l.repositoryStatus)
36685
36956
  }));
36686
- const nameWidth = Math.max(10, ...cells.map((c49) => c49.name.length));
36687
- const roleWidth = Math.max(4, ...cells.map((c49) => c49.role.length));
36688
- const pathWidth = Math.max(4, ...cells.map((c49) => c49.path.length));
36957
+ const nameWidth = Math.max(10, ...cells.map((c51) => c51.name.length));
36958
+ const roleWidth = Math.max(4, ...cells.map((c51) => c51.role.length));
36959
+ const pathWidth = Math.max(4, ...cells.map((c51) => c51.path.length));
36689
36960
  const header = `${"REPOSITORY".padEnd(nameWidth)} SCAN ${"ROLE".padEnd(roleWidth)} ${"PATH".padEnd(pathWidth)} STATUS`;
36690
36961
  const rows = cells.map(
36691
- (c49) => `${c49.name.padEnd(nameWidth)} ${c49.scan.padEnd(4)} ${c49.role.padEnd(roleWidth)} ${c49.path.padEnd(pathWidth)} ${c49.status}`
36962
+ (c51) => `${c51.name.padEnd(nameWidth)} ${c51.scan.padEnd(4)} ${c51.role.padEnd(roleWidth)} ${c51.path.padEnd(pathWidth)} ${c51.status}`
36692
36963
  );
36693
36964
  return { header, rows };
36694
36965
  }
@@ -37800,14 +38071,14 @@ var yt = (t) => {
37800
38071
  };
37801
38072
  var ee = (t, r, s, i, u) => {
37802
38073
  let n = r, o = 0;
37803
- for (let c49 = s; c49 < i; c49++) {
37804
- const a = t[c49];
38074
+ for (let c51 = s; c51 < i; c51++) {
38075
+ const a = t[c51];
37805
38076
  if (n = n - a.length, o++, n <= u) break;
37806
38077
  }
37807
38078
  return { lineCount: n, removals: o };
37808
38079
  };
37809
38080
  var Y2 = ({ cursor: t, options: r, style: s, output: i = process.stdout, maxItems: u = Number.POSITIVE_INFINITY, columnPadding: n = 0, rowPadding: o = 4 }) => {
37810
- const c49 = A(i) - n, a = L(i), l = e("dim", "..."), d2 = Math.max(a - o, 0), y = Math.max(Math.min(u, d2), 5);
38081
+ const c51 = A(i) - n, a = L(i), l = e("dim", "..."), d2 = Math.max(a - o, 0), y = Math.max(Math.min(u, d2), 5);
37811
38082
  let p2 = 0;
37812
38083
  t >= y - 3 && (p2 = Math.max(Math.min(t - y + 3, r.length - y), 0));
37813
38084
  let m = y < r.length && p2 > 0, g = y < r.length && p2 + y < r.length;
@@ -37816,7 +38087,7 @@ var Y2 = ({ cursor: t, options: r, style: s, output: i = process.stdout, maxItem
37816
38087
  m && f++, g && f++;
37817
38088
  const v2 = p2 + (m ? 1 : 0), T = S2 - (g ? 1 : 0);
37818
38089
  for (let b2 = v2; b2 < T; b2++) {
37819
- const G2 = wrapAnsi(s(r[b2], b2 === t), c49, { hard: true, trim: false }).split(`
38090
+ const G2 = wrapAnsi(s(r[b2], b2 === t), c51, { hard: true, trim: false }).split(`
37820
38091
  `);
37821
38092
  h2.push(G2), f += G2.length;
37822
38093
  }
@@ -37833,22 +38104,22 @@ var Y2 = ({ cursor: t, options: r, style: s, output: i = process.stdout, maxItem
37833
38104
  var ue = (t) => {
37834
38105
  const r = t.active ?? "Yes", s = t.inactive ?? "No";
37835
38106
  return new X({ active: r, inactive: s, signal: t.signal, input: t.input, output: t.output, initialValue: t.initialValue ?? true, render() {
37836
- const i = t.withGuide ?? h.withGuide, u = `${M(this.state)} `, n = i ? `${e("gray", $)} ` : "", o = W(t.output, t.message, n, u), c49 = `${i ? `${e("gray", $)}
38107
+ const i = t.withGuide ?? h.withGuide, u = `${M(this.state)} `, n = i ? `${e("gray", $)} ` : "", o = W(t.output, t.message, n, u), c51 = `${i ? `${e("gray", $)}
37837
38108
  ` : ""}${o}
37838
38109
  `, a = this.value ? r : s;
37839
38110
  switch (this.state) {
37840
38111
  case "submit": {
37841
38112
  const l = i ? `${e("gray", $)} ` : "";
37842
- return `${c49}${l}${e("dim", a)}`;
38113
+ return `${c51}${l}${e("dim", a)}`;
37843
38114
  }
37844
38115
  case "cancel": {
37845
38116
  const l = i ? `${e("gray", $)} ` : "";
37846
- return `${c49}${l}${e(["strikethrough", "dim"], a)}${i ? `
38117
+ return `${c51}${l}${e(["strikethrough", "dim"], a)}${i ? `
37847
38118
  ${e("gray", $)}` : ""}`;
37848
38119
  }
37849
38120
  default: {
37850
38121
  const l = i ? `${e("cyan", $)} ` : "", d2 = i ? e("cyan", E2) : "";
37851
- return `${c49}${l}${this.value ? `${e("green", z4)} ${r}` : `${e("dim", H)} ${e("dim", r)}`}${t.vertical ? i ? `
38122
+ return `${c51}${l}${this.value ? `${e("green", z4)} ${r}` : `${e("dim", H)} ${e("dim", r)}`}${t.vertical ? i ? `
37852
38123
  ${e("cyan", $)} ` : `
37853
38124
  ` : ` ${e("dim", "/")} `}${this.value ? `${e("dim", H)} ${e("dim", s)}` : `${e("green", z4)} ${s}`}
37854
38125
  ${d2}
@@ -37870,21 +38141,21 @@ var fe = (t) => new rt({ validate: t.validate, placeholder: t.placeholder, defau
37870
38141
  ${e(this.focused === "submit" ? "cyan" : "dim", "[ submit ]")}` : "";
37871
38142
  switch (this.state) {
37872
38143
  case "error": {
37873
- const c49 = `${e("yellow", $)} `, a = r ? W(t.output, u, c49, void 0) : u, l = e("yellow", E2);
38144
+ const c51 = `${e("yellow", $)} `, a = r ? W(t.output, u, c51, void 0) : u, l = e("yellow", E2);
37874
38145
  return `${s}${a}
37875
38146
  ${l} ${e("yellow", this.error)}${o}
37876
38147
  `;
37877
38148
  }
37878
38149
  case "submit": {
37879
- const c49 = `${e("gray", $)} `, a = r ? W(t.output, n, c49, void 0, (l) => e("dim", l)) : n ? e("dim", n) : "";
38150
+ const c51 = `${e("gray", $)} `, a = r ? W(t.output, n, c51, void 0, (l) => e("dim", l)) : n ? e("dim", n) : "";
37880
38151
  return `${s}${a}`;
37881
38152
  }
37882
38153
  case "cancel": {
37883
- const c49 = `${e("gray", $)} `, a = r ? W(t.output, n, c49, void 0, (l) => e(["strikethrough", "dim"], l)) : n ? e(["strikethrough", "dim"], n) : "";
38154
+ const c51 = `${e("gray", $)} `, a = r ? W(t.output, n, c51, void 0, (l) => e(["strikethrough", "dim"], l)) : n ? e(["strikethrough", "dim"], n) : "";
37884
38155
  return `${s}${a}`;
37885
38156
  }
37886
38157
  default: {
37887
- const c49 = r ? `${e("cyan", $)} ` : "", a = r ? e("cyan", E2) : "", l = r ? W(t.output, u, c49) : u;
38158
+ const c51 = r ? `${e("cyan", $)} ` : "", a = r ? e("cyan", E2) : "", l = r ? W(t.output, u, c51) : u;
37888
38159
  return `${s}${l}
37889
38160
  ${a}${o}
37890
38161
  `;
@@ -37905,7 +38176,7 @@ ${e("reset", e("dim", `Press ${e(["gray", "bgWhite", "inverse"], " space ")} to
37905
38176
  }, render() {
37906
38177
  const i = t.withGuide ?? h.withGuide, u = W(t.output, t.message, i ? `${yt(this.state)} ` : "", `${M(this.state)} `), n = `${i ? `${e("gray", $)}
37907
38178
  ` : ""}${u}
37908
- `, o = this.value ?? [], c49 = (a, l) => {
38179
+ `, o = this.value ?? [], c51 = (a, l) => {
37909
38180
  if (a.disabled) return r(a, "disabled");
37910
38181
  const d2 = o.includes(a.value);
37911
38182
  return l && d2 ? r(a, "active-selected") : d2 ? r(a, "selected") : r(a, l ? "active" : "inactive");
@@ -37928,7 +38199,7 @@ ${e("gray", $)}` : ""}`;
37928
38199
  `), d2 = n.split(`
37929
38200
  `).length, y = l.split(`
37930
38201
  `).length + 1;
37931
- return `${n}${a}${Y2({ output: t.output, options: this.options, cursor: this.cursor, maxItems: t.maxItems, columnPadding: a.length, rowPadding: d2 + y, style: c49 }).join(`
38202
+ return `${n}${a}${Y2({ output: t.output, options: this.options, cursor: this.cursor, maxItems: t.maxItems, columnPadding: a.length, rowPadding: d2 + y, style: c51 }).join(`
37932
38203
  ${a}`)}
37933
38204
  ${l}
37934
38205
  `;
@@ -37936,7 +38207,7 @@ ${l}
37936
38207
  default: {
37937
38208
  const a = i ? `${e("cyan", $)} ` : "", l = n.split(`
37938
38209
  `).length, d2 = i ? 2 : 1;
37939
- return `${n}${a}${Y2({ output: t.output, options: this.options, cursor: this.cursor, maxItems: t.maxItems, columnPadding: a.length, rowPadding: l + d2, style: c49 }).join(`
38210
+ return `${n}${a}${Y2({ output: t.output, options: this.options, cursor: this.cursor, maxItems: t.maxItems, columnPadding: a.length, rowPadding: l + d2, style: c51 }).join(`
37940
38211
  ${a}`)}
37941
38212
  ${i ? e("cyan", E2) : ""}
37942
38213
  `;
@@ -37950,9 +38221,9 @@ var Ce = (t) => new ot({ validate: t.validate, mask: t.mask ?? Gt, signal: t.sig
37950
38221
  `, i = this.userInputWithCursor, u = this.masked;
37951
38222
  switch (this.state) {
37952
38223
  case "error": {
37953
- const n = r ? `${e("yellow", $)} ` : "", o = r ? `${e("yellow", E2)} ` : "", c49 = u ?? "";
38224
+ const n = r ? `${e("yellow", $)} ` : "", o = r ? `${e("yellow", E2)} ` : "", c51 = u ?? "";
37954
38225
  return t.clearOnError && this.clear(), `${s.trim()}
37955
- ${n}${c49}
38226
+ ${n}${c51}
37956
38227
  ${o}${e("yellow", this.error)}
37957
38228
  `;
37958
38229
  }
@@ -38000,19 +38271,19 @@ var Ee = (t) => {
38000
38271
  `;
38001
38272
  switch (this.state) {
38002
38273
  case "submit": {
38003
- const c49 = s ? `${e("gray", $)} ` : "", a = W(t.output, r(this.options[this.cursor], "selected"), c49);
38274
+ const c51 = s ? `${e("gray", $)} ` : "", a = W(t.output, r(this.options[this.cursor], "selected"), c51);
38004
38275
  return `${o}${a}`;
38005
38276
  }
38006
38277
  case "cancel": {
38007
- const c49 = s ? `${e("gray", $)} ` : "", a = W(t.output, r(this.options[this.cursor], "cancelled"), c49);
38278
+ const c51 = s ? `${e("gray", $)} ` : "", a = W(t.output, r(this.options[this.cursor], "cancelled"), c51);
38008
38279
  return `${o}${a}${s ? `
38009
38280
  ${e("gray", $)}` : ""}`;
38010
38281
  }
38011
38282
  default: {
38012
- const c49 = s ? `${e("cyan", $)} ` : "", a = s ? e("cyan", E2) : "", l = o.split(`
38283
+ const c51 = s ? `${e("cyan", $)} ` : "", a = s ? e("cyan", E2) : "", l = o.split(`
38013
38284
  `).length, d2 = s ? 2 : 1;
38014
- return `${o}${c49}${Y2({ output: t.output, cursor: this.cursor, options: this.options, maxItems: t.maxItems, columnPadding: c49.length, rowPadding: l + d2, style: (y, p2) => r(y, y.disabled ? "disabled" : p2 ? "active" : "inactive") }).join(`
38015
- ${c49}`)}
38285
+ return `${o}${c51}${Y2({ output: t.output, cursor: this.cursor, options: this.options, maxItems: t.maxItems, columnPadding: c51.length, rowPadding: l + d2, style: (y, p2) => r(y, y.disabled ? "disabled" : p2 ? "active" : "inactive") }).join(`
38286
+ ${c51}`)}
38016
38287
  ${a}
38017
38288
  `;
38018
38289
  }
@@ -38760,17 +39031,205 @@ function showBanner() {
38760
39031
  `);
38761
39032
  }
38762
39033
 
38763
- // src/completion/index.ts
38764
- function extractCommandTree(cmd) {
38765
- const BUILTIN_FLAGS = /* @__PURE__ */ new Set(["--help", "-h", "--version", "-V"]);
38766
- const options = cmd.options.filter((o) => !o.hidden).filter((o) => !BUILTIN_FLAGS.has(o.long ?? "") && !BUILTIN_FLAGS.has(o.short ?? "")).map((o) => ({
39034
+ // src/code-size/format.ts
39035
+ function formatCount(value) {
39036
+ return value.toLocaleString("en-US");
39037
+ }
39038
+ function repoDisplayName(repositoryUrl) {
39039
+ try {
39040
+ const path = new URL(repositoryUrl).pathname.replace(/^\//, "").replace(/\.git$/, "");
39041
+ return path || repositoryUrl;
39042
+ } catch {
39043
+ return repositoryUrl;
39044
+ }
39045
+ }
39046
+ function countedRows(totals) {
39047
+ const b2 = totals.buckets;
39048
+ return [
39049
+ { label: "Billable code", value: b2.code.code },
39050
+ { label: "Blank & comment lines in billable files", value: b2.code.lines - b2.code.code },
39051
+ { label: "Tests", value: b2.tests.lines },
39052
+ { label: "Documentation", value: b2.docs.lines },
39053
+ { label: "Configuration & data", value: b2.config_data.lines },
39054
+ { label: "Vendored & generated", value: b2.vendored_generated.lines }
39055
+ ];
39056
+ }
39057
+ function countedTotal(totals) {
39058
+ return countedRows(totals).reduce((sum, row) => sum + row.value, 0);
39059
+ }
39060
+ function padValues(rows) {
39061
+ const formatted = rows.map((row) => formatCount(row.value));
39062
+ const labelWidth = Math.max(...rows.map((row) => row.label.length));
39063
+ const valueWidth = Math.max(...formatted.map((value) => value.length));
39064
+ return rows.map(
39065
+ (row, i) => ` ${row.label.padEnd(labelWidth)} ${(formatted[i] ?? "").padStart(valueWidth)}`
39066
+ );
39067
+ }
39068
+ function renderCodeSizeTable(codeSize, options) {
39069
+ const { repositories, totals } = codeSize;
39070
+ const measured = repositories.filter((repo) => repo.billableSloc !== null);
39071
+ const lines = [`Code size for ${options.orgSlug}`, ""];
39072
+ if (measured.length === 0) {
39073
+ lines.push(
39074
+ " No measurements yet.",
39075
+ ` ${formatCount(repositories.length)} connected ${plural(repositories.length, "repository has", "repositories have")} not been measured.`
39076
+ );
39077
+ return lines;
39078
+ }
39079
+ lines.push(
39080
+ ` ${formatCount(totals.billableSloc)} billable SLOC`,
39081
+ ` of ${formatCount(totals.rawLines)} total lines across ${formatCount(measured.length)} measured ${plural(measured.length, "repository", "repositories")}`
39082
+ );
39083
+ const unmeasured = repositories.length - measured.length;
39084
+ if (unmeasured > 0) {
39085
+ lines.push(
39086
+ ` ${formatCount(unmeasured)} connected ${plural(unmeasured, "repository is", "repositories are")} not measured yet and not included`
39087
+ );
39088
+ }
39089
+ lines.push("", "What's counted", ...padValues(countedRows(totals)));
39090
+ const counted = countedTotal(totals);
39091
+ if (counted !== totals.rawLines) {
39092
+ lines.push(
39093
+ ` These sum to ${formatCount(counted)}, not the ${formatCount(totals.rawLines)} above.`,
39094
+ " Some measurement predates the current counting rules; re-measure to reconcile."
39095
+ );
39096
+ }
39097
+ lines.push("", "By repository");
39098
+ const shown = measured.slice(0, options.repoLimit);
39099
+ lines.push(
39100
+ ...padValues(
39101
+ shown.map((repo) => ({
39102
+ label: repoDisplayName(repo.repositoryUrl),
39103
+ value: repo.billableSloc ?? 0
39104
+ }))
39105
+ )
39106
+ );
39107
+ const hidden = measured.length - shown.length;
39108
+ if (hidden > 0) {
39109
+ lines.push(` ... ${formatCount(hidden)} more`);
39110
+ }
39111
+ return lines;
39112
+ }
39113
+ function renderCodeSizeTsv(codeSize) {
39114
+ return [
39115
+ "repository billableSloc",
39116
+ ...codeSize.repositories.map(
39117
+ (repo) => `${repoDisplayName(repo.repositoryUrl)} ${repo.billableSloc === null ? "" : repo.billableSloc}`
39118
+ )
39119
+ ];
39120
+ }
39121
+ function plural(count, one, many) {
39122
+ return count === 1 ? one : many;
39123
+ }
39124
+
39125
+ // src/code-size/commands.ts
39126
+ var DEFAULT_REPO_LIMIT = 20;
39127
+ var OUTPUT_FORMATS = ["table", "json", "tsv"];
39128
+ function isOutputFormat(value) {
39129
+ return OUTPUT_FORMATS.includes(value);
39130
+ }
39131
+ function parseRepoLimit(value) {
39132
+ if (!/^\d+$/.test(value.trim())) {
39133
+ console.error("--repos must be a non-negative whole number (0 shows every repository).");
39134
+ process.exit(1);
39135
+ }
39136
+ return Number.parseInt(value, 10);
39137
+ }
39138
+ function createCodeSizeCommand() {
39139
+ return new Command("code-size").description("Show the organization's billable lines of code").option("-o, --output <format>", "Output format: table, json, tsv (default: table)", "table").option(
39140
+ "-n, --repos <count>",
39141
+ `Per-repository rows to show in table output (default: ${DEFAULT_REPO_LIMIT}, 0 for all)`,
39142
+ parseRepoLimit
39143
+ ).option("-w, --web", "Open the code size panel in a browser").addHelpText(
39144
+ "after",
39145
+ `
39146
+ Billable SLOC is counted from each connected repository's current scanning
39147
+ branch. Blanks, comments, tests, docs, config and vendored code are excluded;
39148
+ "What's counted" reconciles the billable number to what plain scc reports.
39149
+ See ${getAppBaseUrl()}/docs/integrations/code-size
39150
+
39151
+ Examples:
39152
+ tracecli code-size
39153
+ tracecli code-size --repos 0
39154
+ tracecli code-size --output json | jq '.totals.billableSloc'
39155
+ tracecli code-size --output tsv | cut -f1,2
39156
+ tracecli code-size --web`
39157
+ ).action(async (options) => {
39158
+ if (!isOutputFormat(options.output)) {
39159
+ console.error(
39160
+ `Unsupported --output '${options.output}'. Expected one of: ${OUTPUT_FORMATS.join(", ")}.`
39161
+ );
39162
+ process.exit(1);
39163
+ }
39164
+ const listed = await authenticatedRequest(
39165
+ (client) => client.organization.listMyOrganizations()
39166
+ );
39167
+ if (listed.status === 401) {
39168
+ console.error("Authentication failed. Run `tracecli auth login` to re-authenticate.");
39169
+ process.exit(1);
39170
+ }
39171
+ if (listed.status !== 200) {
39172
+ console.error(`Failed to resolve organization (status ${listed.status}).`);
39173
+ process.exit(1);
39174
+ }
39175
+ const resolution = determineActiveOrganization(
39176
+ listed.body,
39177
+ readSelectedOrganizationId(),
39178
+ extractOrganizationIdFromToken(readToken())
39179
+ );
39180
+ if (!resolution) {
39181
+ console.error("No organization found for your account.");
39182
+ process.exit(1);
39183
+ }
39184
+ const { organizationId, slug } = resolution.organization;
39185
+ if (options.web) {
39186
+ const url2 = `${getAppBaseUrl()}/dashboard/${slug}/settings/integrations/github`;
39187
+ const opened = await openBrowser(url2);
39188
+ if (!opened) {
39189
+ console.log(`Open in browser: ${url2}`);
39190
+ }
39191
+ return;
39192
+ }
39193
+ const result = await authenticatedRequest(
39194
+ (client) => client.codeSize.getOrganizationCodeSize({ params: { organizationId } })
39195
+ );
39196
+ if (result.status === 401) {
39197
+ console.error("Authentication failed. Run `tracecli auth login` to re-authenticate.");
39198
+ process.exit(1);
39199
+ }
39200
+ if (result.status !== 200) {
39201
+ console.error(`Failed to read code size (status ${result.status}).`);
39202
+ process.exit(1);
39203
+ }
39204
+ const codeSize = result.body;
39205
+ if (options.output === "json") {
39206
+ console.log(JSON.stringify(codeSize, null, 2));
39207
+ return;
39208
+ }
39209
+ if (options.output === "tsv") {
39210
+ for (const line of renderCodeSizeTsv(codeSize)) {
39211
+ console.log(line);
39212
+ }
39213
+ return;
39214
+ }
39215
+ const repoLimit = options.repos === void 0 ? DEFAULT_REPO_LIMIT : options.repos === 0 ? Number.POSITIVE_INFINITY : options.repos;
39216
+ for (const line of renderCodeSizeTable(codeSize, { orgSlug: slug, repoLimit })) {
39217
+ console.log(line);
39218
+ }
39219
+ });
39220
+ }
39221
+
39222
+ // src/completion/index.ts
39223
+ function extractCommandTree(cmd) {
39224
+ const BUILTIN_FLAGS = /* @__PURE__ */ new Set(["--help", "-h", "--version", "-V"]);
39225
+ const options = cmd.options.filter((o) => !o.hidden).filter((o) => !BUILTIN_FLAGS.has(o.long ?? "") && !BUILTIN_FLAGS.has(o.short ?? "")).map((o) => ({
38767
39226
  flags: o.flags,
38768
39227
  long: o.long ?? "",
38769
39228
  short: o.short ?? null,
38770
39229
  description: o.description,
38771
39230
  takesValue: o.required || o.optional
38772
39231
  }));
38773
- const subcommands = cmd.commands.filter((c49) => !c49._hidden).map((c49) => extractCommandTree(c49));
39232
+ const subcommands = cmd.commands.filter((c51) => !c51._hidden).map((c51) => extractCommandTree(c51));
38774
39233
  return {
38775
39234
  name: cmd.name(),
38776
39235
  description: cmd.description(),
@@ -38836,7 +39295,7 @@ function generateBashCompletion(tree, binName) {
38836
39295
  }
38837
39296
  function collectBashCases(tree, prefix) {
38838
39297
  const cases = /* @__PURE__ */ new Map();
38839
- const subcommandNames = tree.subcommands.map((c49) => c49.name);
39298
+ const subcommandNames = tree.subcommands.map((c51) => c51.name);
38840
39299
  const optionNames = tree.options.flatMap((o) => o.short ? [o.long, o.short] : [o.long]);
38841
39300
  const extraOptions = prefix === "" ? ["--help", "--version"] : ["--help"];
38842
39301
  const allOptions = [.../* @__PURE__ */ new Set([...optionNames, ...extraOptions])];
@@ -39131,6 +39590,251 @@ Examples:
39131
39590
  });
39132
39591
  }
39133
39592
 
39593
+ // src/inbox/commands.ts
39594
+ function validateInboxLabel(label) {
39595
+ const parsed = InboxLabelSchema.safeParse(label);
39596
+ if (parsed.success) {
39597
+ return null;
39598
+ }
39599
+ return "Inbox label must be lowercase letters, digits, or dashes, starting with a letter (max 40 characters).";
39600
+ }
39601
+ function buildCreateInboxBody(label) {
39602
+ return { label };
39603
+ }
39604
+ function failWriteOp2(action, status, body, notFoundLabel) {
39605
+ const { message, errorCode } = readErrorFields(body);
39606
+ if (status === UNAUTHORIZED_STATUS2) {
39607
+ exitAuthFailed();
39608
+ }
39609
+ if (status === 403) {
39610
+ console.error(
39611
+ colorError(
39612
+ `Only an organization admin (or Trace staff) on a pentest-enabled organization can ${action} inboxes.`
39613
+ )
39614
+ );
39615
+ process.exit(1);
39616
+ }
39617
+ if (status === 404) {
39618
+ console.error(
39619
+ colorError(message ?? (notFoundLabel ? `No inbox labeled '${notFoundLabel}'.` : "Not found."))
39620
+ );
39621
+ process.exit(1);
39622
+ }
39623
+ if (status === 400) {
39624
+ console.error(colorError(message ?? "Invalid request (status 400)."));
39625
+ process.exit(1);
39626
+ }
39627
+ if (status === 409) {
39628
+ if (errorCode === "INBOX_ALREADY_EXISTS") {
39629
+ console.error(
39630
+ colorError(message ?? "An inbox with this label already exists in this organization.")
39631
+ );
39632
+ } else if (errorCode === "INBOX_BEING_DELETED") {
39633
+ console.error(
39634
+ colorError(
39635
+ message ?? "That name was recently removed and is still being cleaned up. Try again shortly, or choose another."
39636
+ )
39637
+ );
39638
+ } else if (errorCode === "INBOX_QUOTA_EXCEEDED") {
39639
+ console.error(
39640
+ colorError(message ?? "Inbox limit reached. Delete an unused inbox to create a new one.")
39641
+ );
39642
+ } else {
39643
+ console.error(colorError(message ?? "Conflict (status 409)."));
39644
+ }
39645
+ process.exit(1);
39646
+ }
39647
+ if (status === 503) {
39648
+ console.error(
39649
+ colorError(
39650
+ message ?? "Managed inboxes are not enabled on this server: no mail provider is configured."
39651
+ )
39652
+ );
39653
+ process.exit(1);
39654
+ }
39655
+ console.error(
39656
+ colorError(`Failed to ${action} inbox (status ${status})${message ? `: ${message}` : "."}`)
39657
+ );
39658
+ process.exit(1);
39659
+ }
39660
+ function failReadOp2(status, body) {
39661
+ const { message } = readErrorFields(body);
39662
+ if (status === UNAUTHORIZED_STATUS2) {
39663
+ exitAuthFailed();
39664
+ }
39665
+ if (status === 403) {
39666
+ console.error(colorError("You don't have access to view inboxes in this organization."));
39667
+ process.exit(1);
39668
+ }
39669
+ if (status === 503) {
39670
+ console.error(
39671
+ colorError(
39672
+ message ?? "Managed inboxes are not enabled on this server: no mail provider is configured."
39673
+ )
39674
+ );
39675
+ process.exit(1);
39676
+ }
39677
+ console.error(
39678
+ colorError(`Failed to list inboxes (status ${status})${message ? `: ${message}` : "."}`)
39679
+ );
39680
+ process.exit(1);
39681
+ }
39682
+ function renderInboxDetail(inbox) {
39683
+ const pad = 10;
39684
+ return [
39685
+ `${"Address:".padEnd(pad)} ${sanitizeDisplay(inbox.address)}`,
39686
+ `${"Label:".padEnd(pad)} ${sanitizeDisplay(inbox.label)}`,
39687
+ `${"Created:".padEnd(pad)} ${formatDate(inbox.createdAt)}`
39688
+ ].join("\n");
39689
+ }
39690
+ function printInboxesTsv(inboxes) {
39691
+ console.log(["address", "label", "createdAt"].join(" "));
39692
+ for (const inbox of inboxes) {
39693
+ console.log(
39694
+ [
39695
+ tsvSafe(sanitizeDisplay(inbox.address)),
39696
+ tsvSafe(sanitizeDisplay(inbox.label)),
39697
+ tsvSafe(toIso(inbox.createdAt))
39698
+ ].join(" ")
39699
+ );
39700
+ }
39701
+ }
39702
+ function buildInboxesTable(inboxes) {
39703
+ const cells = inboxes.map((inbox) => ({
39704
+ address: sanitizeDisplay(inbox.address),
39705
+ label: sanitizeDisplay(inbox.label)
39706
+ }));
39707
+ const addressWidth = Math.max(7, ...cells.map((c51) => c51.address.length));
39708
+ const header = `${"ADDRESS".padEnd(addressWidth)} LABEL`;
39709
+ const rows = cells.map((c51) => `${c51.address.padEnd(addressWidth)} ${c51.label}`);
39710
+ return { header, rows };
39711
+ }
39712
+ function formatInboxQuota(quota) {
39713
+ return `${quota.used} of ${quota.limit} inboxes`;
39714
+ }
39715
+ function outputInbox(inbox, output) {
39716
+ if (output === "json") {
39717
+ console.log(JSON.stringify(inbox, null, 2));
39718
+ return;
39719
+ }
39720
+ if (output === "tsv") {
39721
+ printInboxesTsv([inbox]);
39722
+ return;
39723
+ }
39724
+ console.log(renderInboxDetail(inbox));
39725
+ }
39726
+ function requireInboxLabel(label) {
39727
+ const error48 = validateInboxLabel(label);
39728
+ if (error48) {
39729
+ console.error(colorError(error48));
39730
+ process.exit(1);
39731
+ }
39732
+ }
39733
+ async function runCreate2(label, options) {
39734
+ const output = parseOutput(options.output);
39735
+ requireInboxLabel(label);
39736
+ const organizationId = await resolveOrganizationId();
39737
+ const result = await authenticatedRequest(
39738
+ (client) => client.inbox.createInbox({
39739
+ params: { organizationId },
39740
+ body: buildCreateInboxBody(label)
39741
+ })
39742
+ );
39743
+ if (result.status !== CREATED_STATUS) {
39744
+ failWriteOp2("create", result.status, result.body);
39745
+ }
39746
+ if (output === "table") {
39747
+ console.log(colorSuccess(`Created inbox ${sanitizeDisplay(result.body.address)}`));
39748
+ }
39749
+ outputInbox(result.body, output);
39750
+ }
39751
+ async function runList2(options) {
39752
+ const output = parseOutput(options.output);
39753
+ const organizationId = await resolveOrganizationId();
39754
+ const result = await authenticatedRequest(
39755
+ (client) => client.inbox.listInboxes({ params: { organizationId } })
39756
+ );
39757
+ if (result.status !== SUCCESS_STATUS) {
39758
+ failReadOp2(result.status, result.body);
39759
+ }
39760
+ const { inboxes, quota } = result.body;
39761
+ if (output === "json") {
39762
+ console.log(JSON.stringify({ inboxes, quota }, null, 2));
39763
+ return;
39764
+ }
39765
+ if (output === "tsv") {
39766
+ printInboxesTsv(inboxes);
39767
+ return;
39768
+ }
39769
+ if (inboxes.length === 0) {
39770
+ console.log(`No inboxes found. ${formatInboxQuota(quota)}.`);
39771
+ return;
39772
+ }
39773
+ const { header, rows } = buildInboxesTable(inboxes);
39774
+ await runInteractivePager({
39775
+ items: inboxes,
39776
+ header,
39777
+ renderItem: (_inbox, i) => [rows[i] ?? ""],
39778
+ disabled: options.pager === false
39779
+ });
39780
+ console.log(formatInboxQuota(quota));
39781
+ }
39782
+ async function runDelete2(label, options) {
39783
+ requireInboxLabel(label);
39784
+ const confirmed = await confirmDeletion(`inbox ${label}`, options.yes);
39785
+ if (!confirmed) {
39786
+ console.log("Aborted.");
39787
+ return;
39788
+ }
39789
+ const organizationId = await resolveOrganizationId();
39790
+ const result = await authenticatedRequest(
39791
+ (client) => client.inbox.deleteInbox({
39792
+ params: { organizationId, label }
39793
+ })
39794
+ );
39795
+ if (result.status !== NO_CONTENT_STATUS) {
39796
+ failWriteOp2("delete", result.status, result.body, label);
39797
+ }
39798
+ console.log(colorSuccess(`Deleted inbox ${sanitizeDisplay(label)}`));
39799
+ }
39800
+ function createCreateCommand2() {
39801
+ return new Command("create").description("Provision a Trace-hosted email inbox").argument("<label>", "Inbox name (lowercase letters/digits/dashes, max 40)").option("-o, --output <format>", "Output format: table, json, tsv (default: table)", "table").addHelpText(
39802
+ "after",
39803
+ `
39804
+ Trace hosts this inbox; the pentest agent reads its emails automatically.
39805
+ The minted address is {org-slug}-{label}@pentest.securewithtrace.com.
39806
+
39807
+ Use the printed address as the test-account email when creating a magic-auth
39808
+ or username-password credential.
39809
+
39810
+ Examples:
39811
+ tracecli credential inbox create portal
39812
+ ADDRESS=$(tracecli credential inbox create qa-admin -o json | jq -r .address)
39813
+ echo "$ADDRESS" | tracecli credential create --env <envId> --type magic-auth --secret-stdin`
39814
+ ).action(async (label, options) => {
39815
+ await runCreate2(label, options);
39816
+ });
39817
+ }
39818
+ function createListCommand3() {
39819
+ return new Command("list").description("List Trace-hosted email inboxes in the active organization").option("-o, --output <format>", "Output format: table, json, tsv (default: table)", "table").option("--no-pager", "Disable the interactive pager for table output").action(async (options) => {
39820
+ await runList2(options);
39821
+ });
39822
+ }
39823
+ function createDeleteCommand2() {
39824
+ return new Command("delete").description("Delete a Trace-hosted email inbox").argument("<label>", "Inbox label").option("-y, --yes", "Skip the confirmation prompt (required in non-interactive shells)").action(async (label, options) => {
39825
+ await runDelete2(label, options);
39826
+ });
39827
+ }
39828
+ function createInboxCommand() {
39829
+ const inbox = new Command("inbox").description(
39830
+ "Provision Trace-hosted email inboxes for test-account credentials"
39831
+ );
39832
+ inbox.addCommand(createCreateCommand2());
39833
+ inbox.addCommand(createListCommand3());
39834
+ inbox.addCommand(createDeleteCommand2());
39835
+ return inbox;
39836
+ }
39837
+
39134
39838
  // src/secret-input.ts
39135
39839
  import { readFileSync as readFileSync2 } from "fs";
39136
39840
  var SecretInputError = class extends Error {
@@ -39423,7 +40127,7 @@ function usernameOpts(opts) {
39423
40127
  function passwordOpts(opts) {
39424
40128
  return { stdin: opts.passwordStdin, file: opts.passwordFile, env: opts.passwordEnv };
39425
40129
  }
39426
- function failWriteOp2(action, status, body, notFoundLabel) {
40130
+ function failWriteOp3(action, status, body, notFoundLabel) {
39427
40131
  const { message, errorCode } = readErrorFields(body);
39428
40132
  if (status === UNAUTHORIZED_STATUS2) {
39429
40133
  exitAuthFailed();
@@ -39467,7 +40171,7 @@ function failWriteOp2(action, status, body, notFoundLabel) {
39467
40171
  );
39468
40172
  process.exit(1);
39469
40173
  }
39470
- function failReadOp2(action, status, body, notFoundLabel) {
40174
+ function failReadOp3(action, status, body, notFoundLabel) {
39471
40175
  const { message } = readErrorFields(body);
39472
40176
  if (status === UNAUTHORIZED_STATUS2) {
39473
40177
  exitAuthFailed();
@@ -39519,16 +40223,16 @@ function printCredentialsTsv(creds) {
39519
40223
  }
39520
40224
  }
39521
40225
  function buildCredentialsTable(creds) {
39522
- const cells = creds.map((c49) => ({
39523
- id: c49.credentialId,
39524
- label: sanitizeDisplay(c49.label),
39525
- type: c49.credentialType
40226
+ const cells = creds.map((c51) => ({
40227
+ id: c51.credentialId,
40228
+ label: sanitizeDisplay(c51.label),
40229
+ type: c51.credentialType
39526
40230
  }));
39527
- const idWidth = Math.max(2, ...cells.map((c49) => c49.id.length));
39528
- const labelWidth = Math.max(5, ...cells.map((c49) => c49.label.length));
40231
+ const idWidth = Math.max(2, ...cells.map((c51) => c51.id.length));
40232
+ const labelWidth = Math.max(5, ...cells.map((c51) => c51.label.length));
39529
40233
  const header = `${"ID".padEnd(idWidth)} ${"LABEL".padEnd(labelWidth)} TYPE`;
39530
40234
  const rows = cells.map(
39531
- (c49) => `${c49.id.padEnd(idWidth)} ${c49.label.padEnd(labelWidth)} ${c49.type}`
40235
+ (c51) => `${c51.id.padEnd(idWidth)} ${c51.label.padEnd(labelWidth)} ${c51.type}`
39532
40236
  );
39533
40237
  return { header, rows };
39534
40238
  }
@@ -39559,7 +40263,7 @@ function failAuthBuild(result) {
39559
40263
  console.error(colorError(result.error));
39560
40264
  process.exit(1);
39561
40265
  }
39562
- async function runCreate2(options) {
40266
+ async function runCreate3(options) {
39563
40267
  const output = parseOutput(options.output);
39564
40268
  requireEnvId(options.env);
39565
40269
  const type = normalizeCredentialType(options.type);
@@ -39599,7 +40303,7 @@ async function runCreate2(options) {
39599
40303
  (client) => client.credential.createCredential({ params, body })
39600
40304
  );
39601
40305
  if (result.status !== CREATED_STATUS) {
39602
- failWriteOp2("create", result.status, result.body);
40306
+ failWriteOp3("create", result.status, result.body);
39603
40307
  }
39604
40308
  if (output === "table") {
39605
40309
  console.log(colorSuccess(`Created credential ${sanitizeDisplay(result.body.label)}`));
@@ -39691,7 +40395,7 @@ async function buildCreateBody(type, label, options) {
39691
40395
  }
39692
40396
  }
39693
40397
  }
39694
- async function runList2(options) {
40398
+ async function runList3(options) {
39695
40399
  const output = parseOutput(options.output);
39696
40400
  requireEnvId(options.env);
39697
40401
  const organizationId = await resolveOrganizationId();
@@ -39699,7 +40403,7 @@ async function runList2(options) {
39699
40403
  (client) => client.credential.listCredentials({ params: { organizationId, environmentId: options.env } })
39700
40404
  );
39701
40405
  if (result.status !== SUCCESS_STATUS) {
39702
- failReadOp2("list credentials", result.status, result.body, options.env);
40406
+ failReadOp3("list credentials", result.status, result.body, options.env);
39703
40407
  }
39704
40408
  const creds = result.body;
39705
40409
  if (output === "json") {
@@ -39733,7 +40437,7 @@ async function runGet2(credentialId, options) {
39733
40437
  })
39734
40438
  );
39735
40439
  if (result.status !== SUCCESS_STATUS) {
39736
- failReadOp2("get credential", result.status, result.body, credentialId);
40440
+ failReadOp3("get credential", result.status, result.body, credentialId);
39737
40441
  }
39738
40442
  outputCredential(result.body, output);
39739
40443
  }
@@ -39767,7 +40471,7 @@ async function runUpdate2(credentialId, options) {
39767
40471
  (client) => client.credential.getCredential({ params })
39768
40472
  );
39769
40473
  if (current.status !== SUCCESS_STATUS) {
39770
- failReadOp2("get credential", current.status, current.body, credentialId);
40474
+ failReadOp3("get credential", current.status, current.body, credentialId);
39771
40475
  }
39772
40476
  const type = current.body.credentialType;
39773
40477
  const foreign = rejectForeignAuthFlags(type, options);
@@ -39800,14 +40504,14 @@ async function runUpdate2(credentialId, options) {
39800
40504
  (client) => client.credential.updateCredential({ params, body })
39801
40505
  );
39802
40506
  if (result.status !== SUCCESS_STATUS) {
39803
- failWriteOp2("update", result.status, result.body, credentialId);
40507
+ failWriteOp3("update", result.status, result.body, credentialId);
39804
40508
  }
39805
40509
  if (output === "table") {
39806
40510
  console.log(colorSuccess(`Updated credential ${sanitizeDisplay(result.body.label)}`));
39807
40511
  }
39808
40512
  outputCredential(result.body, output);
39809
40513
  }
39810
- async function runDelete2(credentialId, options) {
40514
+ async function runDelete3(credentialId, options) {
39811
40515
  requireEnvId(options.env);
39812
40516
  requireCredId(credentialId);
39813
40517
  const confirmed = await confirmDeletion(`credential ${credentialId}`, options.yes);
@@ -39822,7 +40526,7 @@ async function runDelete2(credentialId, options) {
39822
40526
  })
39823
40527
  );
39824
40528
  if (result.status !== NO_CONTENT_STATUS) {
39825
- failWriteOp2("delete", result.status, result.body, credentialId);
40529
+ failWriteOp3("delete", result.status, result.body, credentialId);
39826
40530
  }
39827
40531
  console.log(colorSuccess(`Deleted credential ${credentialId}`));
39828
40532
  }
@@ -39832,7 +40536,7 @@ function addSecretInputOptions(command) {
39832
40536
  function addAuthConfigOptions(command) {
39833
40537
  return command.option("--header-name <name>", "authConfig: header name (bearer-token/jwt-token)").option("--token-prefix <prefix>", "authConfig: token prefix (bearer-token/jwt-token)").option("--placement <header|query>", "authConfig: API key placement (api-key)").option("--key-name <name>", "authConfig: API key name (api-key)").option("--cookie-name <name>", "authConfig: cookie name (cookie)").option("--cookie-domain <domain>", "authConfig: cookie domain (cookie)").option("--cookie-path <path>", "authConfig: cookie path (cookie)").option("--method <form|basic|json>", "authConfig: login method (username-password)").option("--login-url <url>", "authConfig: login URL (username-password)").option("--username-field <name>", "authConfig: username field (username-password)").option("--password-field <name>", "authConfig: password field (username-password)").option("--session-cookie-name <name>", "authConfig: session cookie (username-password)").option("--csrf-token-selector <sel>", "authConfig: CSRF token selector (username-password)").option("--csrf-field-name <name>", "authConfig: CSRF field name (username-password)");
39834
40538
  }
39835
- function createCreateCommand2() {
40539
+ function createCreateCommand3() {
39836
40540
  const command = new Command("create").description("Create a credential in an environment").requiredOption("--env <environmentId>", "Parent environment UUID").requiredOption(
39837
40541
  "--type <type>",
39838
40542
  "bearer-token, jwt-token, api-key, passphrase, username-password, cookie, magic-auth"
@@ -39850,15 +40554,17 @@ Secrets are read from stdin/file/env (or a no-echo prompt) \u2014 never an inlin
39850
40554
  Examples:
39851
40555
  vault read -field=token secret/x | tracecli credential create --env <envId> --type bearer-token --secret-stdin
39852
40556
  tracecli credential create --env <envId> --type api-key --label "prod key" --secret-env API_KEY --placement header --key-name X-API-Key
39853
- tracecli credential create --env <envId> --type username-password --username-env U --password-env P --method form --login-url https://acme.com/login`
40557
+ tracecli credential create --env <envId> --type username-password --username-env U --password-env P --method form --login-url https://acme.com/login
40558
+ ADDRESS=$(tracecli credential inbox create portal -o json | jq -r .address)
40559
+ echo "$ADDRESS" | tracecli credential create --env <envId> --type magic-auth --secret-stdin`
39854
40560
  );
39855
40561
  return command.action(async (options) => {
39856
- await runCreate2(options);
40562
+ await runCreate3(options);
39857
40563
  });
39858
40564
  }
39859
- function createListCommand3() {
40565
+ function createListCommand4() {
39860
40566
  return new Command("list").description("List an environment's credentials (secrets are never shown)").requiredOption("--env <environmentId>", "Parent environment UUID").option("-o, --output <format>", "Output format: table, json, tsv (default: table)", "table").option("--no-pager", "Disable the interactive pager for table output").action(async (options) => {
39861
- await runList2(options);
40567
+ await runList3(options);
39862
40568
  });
39863
40569
  }
39864
40570
  function createGetCommand2() {
@@ -39884,18 +40590,19 @@ Examples:
39884
40590
  await runUpdate2(credentialId, options);
39885
40591
  });
39886
40592
  }
39887
- function createDeleteCommand2() {
40593
+ function createDeleteCommand3() {
39888
40594
  return new Command("delete").description("Delete a credential").argument("<credentialId>", "Credential UUID").requiredOption("--env <environmentId>", "Parent environment UUID").option("-y, --yes", "Skip the confirmation prompt (required in non-interactive shells)").action(async (credentialId, options) => {
39889
- await runDelete2(credentialId, options);
40595
+ await runDelete3(credentialId, options);
39890
40596
  });
39891
40597
  }
39892
40598
  function createCredentialCommand() {
39893
40599
  const credential = new Command("credential").alias("cred").description("Manage environment credentials");
39894
- credential.addCommand(createCreateCommand2());
39895
- credential.addCommand(createListCommand3());
40600
+ credential.addCommand(createCreateCommand3());
40601
+ credential.addCommand(createListCommand4());
39896
40602
  credential.addCommand(createGetCommand2());
39897
40603
  credential.addCommand(createUpdateCommand2());
39898
- credential.addCommand(createDeleteCommand2());
40604
+ credential.addCommand(createDeleteCommand3());
40605
+ credential.addCommand(createInboxCommand());
39899
40606
  return credential;
39900
40607
  }
39901
40608
 
@@ -39938,7 +40645,7 @@ function resolveProductionFlag(input) {
39938
40645
  }
39939
40646
  return { value: void 0 };
39940
40647
  }
39941
- function failWriteOp3(action, status, body, notFoundLabel) {
40648
+ function failWriteOp4(action, status, body, notFoundLabel) {
39942
40649
  const { message, errorCode } = readErrorFields(body);
39943
40650
  if (status === UNAUTHORIZED_STATUS2) {
39944
40651
  exitAuthFailed();
@@ -39986,7 +40693,7 @@ function failWriteOp3(action, status, body, notFoundLabel) {
39986
40693
  );
39987
40694
  process.exit(1);
39988
40695
  }
39989
- function failReadOp3(action, status, body, notFoundLabel) {
40696
+ function failReadOp4(action, status, body, notFoundLabel) {
39990
40697
  const { message } = readErrorFields(body);
39991
40698
  if (status === UNAUTHORIZED_STATUS2) {
39992
40699
  exitAuthFailed();
@@ -40017,7 +40724,7 @@ function renderEnvironmentDetail(env2) {
40017
40724
  return [
40018
40725
  `${"ID:".padEnd(pad)} ${env2.environmentId}`,
40019
40726
  `${"Name:".padEnd(pad)} ${sanitizeDisplay(env2.name)}`,
40020
- `${"URL:".padEnd(pad)} ${sanitizeDisplay(env2.primaryUrl)}`,
40727
+ `${"URL:".padEnd(pad)} ${env2.primaryUrl != null ? sanitizeDisplay(env2.primaryUrl) : "\u2014"}`,
40021
40728
  `${"Production:".padEnd(pad)} ${env2.isProduction ? "yes" : "no"}`,
40022
40729
  `${"Description:".padEnd(pad)} ${env2.description ? sanitizeDisplay(env2.description) : "\u2014"}`,
40023
40730
  `${"Application:".padEnd(pad)} ${env2.applicationId}`,
@@ -40033,7 +40740,7 @@ function printEnvironmentsTsv(envs) {
40033
40740
  [
40034
40741
  tsvSafe(env2.environmentId),
40035
40742
  tsvSafe(sanitizeDisplay(env2.name)),
40036
- tsvSafe(sanitizeDisplay(env2.primaryUrl)),
40743
+ tsvSafe(sanitizeDisplay(env2.primaryUrl ?? "")),
40037
40744
  String(env2.isProduction),
40038
40745
  tsvSafe(sanitizeDisplay(env2.description ?? "")),
40039
40746
  tsvSafe(toIso(env2.createdAt))
@@ -40065,7 +40772,7 @@ function printOrgEnvironmentsTsv(envs) {
40065
40772
  tsvSafe(env2.applicationId),
40066
40773
  tsvSafe(sanitizeDisplay(env2.applicationName)),
40067
40774
  tsvSafe(sanitizeDisplay(env2.name)),
40068
- tsvSafe(sanitizeDisplay(env2.primaryUrl)),
40775
+ tsvSafe(sanitizeDisplay(env2.primaryUrl ?? "")),
40069
40776
  String(env2.isProduction),
40070
40777
  tsvSafe(toIso(env2.createdAt))
40071
40778
  ].join(" ")
@@ -40077,17 +40784,17 @@ function buildEnvironmentTable(envs, leading) {
40077
40784
  leading: leading.map((column) => sanitizeDisplay(column.value(e2))),
40078
40785
  name: sanitizeDisplay(e2.name),
40079
40786
  prod: e2.isProduction ? "yes" : "no",
40080
- url: sanitizeDisplay(e2.primaryUrl)
40787
+ url: e2.primaryUrl != null ? sanitizeDisplay(e2.primaryUrl) : "\u2014"
40081
40788
  }));
40082
40789
  const leadingWidths = leading.map(
40083
- (column, i) => Math.max(column.header.length, ...cells.map((c49) => c49.leading[i]?.length ?? 0))
40790
+ (column, i) => Math.max(column.header.length, ...cells.map((c51) => c51.leading[i]?.length ?? 0))
40084
40791
  );
40085
- const nameWidth = Math.max(4, ...cells.map((c49) => c49.name.length));
40792
+ const nameWidth = Math.max(4, ...cells.map((c51) => c51.name.length));
40086
40793
  const padLeading = (values) => values.map((v2, i) => v2.padEnd(leadingWidths[i] ?? 0)).join(" ");
40087
40794
  return {
40088
- header: `${padLeading(leading.map((c49) => c49.header))} ${"NAME".padEnd(nameWidth)} PROD URL`,
40795
+ header: `${padLeading(leading.map((c51) => c51.header))} ${"NAME".padEnd(nameWidth)} PROD URL`,
40089
40796
  rows: cells.map(
40090
- (c49) => `${padLeading(c49.leading)} ${c49.name.padEnd(nameWidth)} ${c49.prod.padEnd(4)} ${c49.url}`
40797
+ (c51) => `${padLeading(c51.leading)} ${c51.name.padEnd(nameWidth)} ${c51.prod.padEnd(4)} ${c51.url}`
40091
40798
  )
40092
40799
  };
40093
40800
  }
@@ -40127,7 +40834,7 @@ function requireEnvId2(environmentId) {
40127
40834
  process.exit(1);
40128
40835
  }
40129
40836
  }
40130
- async function runCreate3(name, options) {
40837
+ async function runCreate4(name, options) {
40131
40838
  const output = parseOutput(options.output);
40132
40839
  requireAppId(options.app);
40133
40840
  const nameError = validateEnvName(name);
@@ -40149,7 +40856,7 @@ async function runCreate3(name, options) {
40149
40856
  })
40150
40857
  );
40151
40858
  if (result.status !== CREATED_STATUS) {
40152
- failWriteOp3("create", result.status, result.body);
40859
+ failWriteOp4("create", result.status, result.body);
40153
40860
  }
40154
40861
  if (output === "table") {
40155
40862
  console.log(colorSuccess(`Created environment ${sanitizeDisplay(result.body.name)}`));
@@ -40176,7 +40883,7 @@ function createOrgEnvironmentRequests(organizationId) {
40176
40883
  async function collectOrgEnvironments(requests) {
40177
40884
  const appsResult = await requests.listApplications();
40178
40885
  if (!appsResult.ok) {
40179
- failReadOp3("list applications", appsResult.status, appsResult.body);
40886
+ failReadOp4("list applications", appsResult.status, appsResult.body);
40180
40887
  }
40181
40888
  const apps = appsResult.data;
40182
40889
  const environments = [];
@@ -40248,7 +40955,7 @@ async function runListAcrossOrganization(output, pager) {
40248
40955
  disabled: pager === false
40249
40956
  });
40250
40957
  }
40251
- async function runList3(options) {
40958
+ async function runList4(options) {
40252
40959
  const output = parseOutput(options.output);
40253
40960
  if (options.app === void 0) {
40254
40961
  await runListAcrossOrganization(output, options.pager);
@@ -40263,7 +40970,7 @@ async function runList3(options) {
40263
40970
  })
40264
40971
  );
40265
40972
  if (result.status !== SUCCESS_STATUS) {
40266
- failReadOp3("list environments", result.status, result.body, applicationId);
40973
+ failReadOp4("list environments", result.status, result.body, applicationId);
40267
40974
  }
40268
40975
  const envs = result.body;
40269
40976
  if (output === "json") {
@@ -40297,7 +41004,7 @@ async function runGet3(environmentId, options) {
40297
41004
  })
40298
41005
  );
40299
41006
  if (result.status !== SUCCESS_STATUS) {
40300
- failReadOp3("get environment", result.status, result.body, environmentId);
41007
+ failReadOp4("get environment", result.status, result.body, environmentId);
40301
41008
  }
40302
41009
  outputEnvironment(result.body, output);
40303
41010
  }
@@ -40339,14 +41046,14 @@ async function runUpdate3(environmentId, options) {
40339
41046
  })
40340
41047
  );
40341
41048
  if (result.status !== SUCCESS_STATUS) {
40342
- failWriteOp3("update", result.status, result.body, environmentId);
41049
+ failWriteOp4("update", result.status, result.body, environmentId);
40343
41050
  }
40344
41051
  if (output === "table") {
40345
41052
  console.log(colorSuccess(`Updated environment ${sanitizeDisplay(result.body.name)}`));
40346
41053
  }
40347
41054
  outputEnvironment(result.body, output);
40348
41055
  }
40349
- async function runDelete3(environmentId, options) {
41056
+ async function runDelete4(environmentId, options) {
40350
41057
  requireAppId(options.app);
40351
41058
  requireEnvId2(environmentId);
40352
41059
  const confirmed = await confirmDeletion(`environment ${environmentId}`, options.yes);
@@ -40361,11 +41068,11 @@ async function runDelete3(environmentId, options) {
40361
41068
  })
40362
41069
  );
40363
41070
  if (result.status !== NO_CONTENT_STATUS) {
40364
- failWriteOp3("delete", result.status, result.body, environmentId);
41071
+ failWriteOp4("delete", result.status, result.body, environmentId);
40365
41072
  }
40366
41073
  console.log(colorSuccess(`Deleted environment ${environmentId}`));
40367
41074
  }
40368
- function createCreateCommand3() {
41075
+ function createCreateCommand4() {
40369
41076
  return new Command("create").description("Create an environment for an application").argument("<name>", "Environment name (lowercase letters/digits/dashes)").requiredOption("--app <applicationId>", "Parent application UUID").requiredOption("--url <url>", "Primary URL for the environment").option("--description <text>", "Description").option("--production", "Mark as the production environment").option("-o, --output <format>", "Output format: table, json, tsv (default: table)", "table").addHelpText(
40370
41077
  "after",
40371
41078
  `
@@ -40373,10 +41080,10 @@ Examples:
40373
41080
  tracecli environment create staging --app <appId> --url https://staging.acme.com
40374
41081
  tracecli environment create production --app <appId> --url https://acme.com --production`
40375
41082
  ).action(async (name, options) => {
40376
- await runCreate3(name, options);
41083
+ await runCreate4(name, options);
40377
41084
  });
40378
41085
  }
40379
- function createListCommand4() {
41086
+ function createListCommand5() {
40380
41087
  return new Command("list").description("List environments \u2014 an application's, or every one in the organization").option("--app <applicationId>", "Parent application UUID (omit to list the whole org)").option("-o, --output <format>", "Output format: table, json, tsv (default: table)", "table").option("--no-pager", "Disable the interactive pager for table output").addHelpText(
40381
41088
  "after",
40382
41089
  `
@@ -40388,7 +41095,7 @@ Examples:
40388
41095
  tracecli environment list --app <appId>
40389
41096
  tracecli environment list --output tsv | cut -f2,3,4`
40390
41097
  ).action(async (options) => {
40391
- await runList3(options);
41098
+ await runList4(options);
40392
41099
  });
40393
41100
  }
40394
41101
  function createGetCommand3() {
@@ -40407,18 +41114,18 @@ Examples:
40407
41114
  await runUpdate3(environmentId, options);
40408
41115
  });
40409
41116
  }
40410
- function createDeleteCommand3() {
41117
+ function createDeleteCommand4() {
40411
41118
  return new Command("delete").description("Delete an environment").argument("<environmentId>", "Environment UUID").requiredOption("--app <applicationId>", "Parent application UUID").option("-y, --yes", "Skip the confirmation prompt (required in non-interactive shells)").action(async (environmentId, options) => {
40412
- await runDelete3(environmentId, options);
41119
+ await runDelete4(environmentId, options);
40413
41120
  });
40414
41121
  }
40415
41122
  function createEnvironmentCommand() {
40416
41123
  const environment = new Command("environment").alias("env").description("Manage application environments");
40417
- environment.addCommand(createCreateCommand3());
40418
- environment.addCommand(createListCommand4());
41124
+ environment.addCommand(createCreateCommand4());
41125
+ environment.addCommand(createListCommand5());
40419
41126
  environment.addCommand(createGetCommand3());
40420
41127
  environment.addCommand(createUpdateCommand3());
40421
- environment.addCommand(createDeleteCommand3());
41128
+ environment.addCommand(createDeleteCommand4());
40422
41129
  return environment;
40423
41130
  }
40424
41131
 
@@ -40450,7 +41157,223 @@ var CLI_PACKAGE_NAME = packageMetadata.name;
40450
41157
  var CLI_VERSION = packageMetadata.version;
40451
41158
 
40452
41159
  // src/pentest/commands.ts
40453
- import { readFileSync as readFileSync4 } from "fs";
41160
+ import { readFileSync as readFileSync5 } from "fs";
41161
+
41162
+ // src/pentest/evidence.ts
41163
+ import { readFileSync as readFileSync4, statSync } from "fs";
41164
+ import { basename } from "path";
41165
+
41166
+ // src/pentest/resolve.ts
41167
+ var UUID_PATTERN2 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
41168
+ async function resolvePentestId(organizationId, identifier) {
41169
+ if (UUID_PATTERN2.test(identifier)) {
41170
+ return identifier;
41171
+ }
41172
+ const numericMatch = identifier.match(/^(?:PT-)?(\d+)$/i);
41173
+ if (!numericMatch?.[1]) {
41174
+ console.error(
41175
+ `Invalid pentest identifier '${identifier}'. Use a display ID (e.g. PT-3) or UUID.`
41176
+ );
41177
+ process.exit(1);
41178
+ }
41179
+ const displayId = Number.parseInt(numericMatch[1], 10);
41180
+ const result = await authenticatedRequest(
41181
+ (client) => client.pentest.listPentests({
41182
+ params: { organizationId }
41183
+ })
41184
+ );
41185
+ if (result.status === 401) {
41186
+ console.error("Authentication failed. Run `tracecli auth login` to re-authenticate.");
41187
+ process.exit(1);
41188
+ }
41189
+ if (result.status !== 200) {
41190
+ console.error(`Failed to list pentests (status ${result.status}).`);
41191
+ process.exit(1);
41192
+ }
41193
+ const pentest = result.body.find((p2) => p2.displayId === displayId);
41194
+ if (!pentest) {
41195
+ console.error(
41196
+ `Pentest with display ID ${displayId} not found.
41197
+ Run \`tracecli pentest list\` to see available pentests.`
41198
+ );
41199
+ process.exit(1);
41200
+ }
41201
+ return pentest.pentestId;
41202
+ }
41203
+
41204
+ // src/pentest/evidence.ts
41205
+ var SECTION_LIST = ValidationEvidenceSectionValues.join(", ");
41206
+ var KIND_LIST = PentestExploitArtifactKindValues.join(", ");
41207
+ function isSection(value) {
41208
+ return ValidationEvidenceSectionValues.includes(value);
41209
+ }
41210
+ function isKind(value) {
41211
+ return PentestExploitArtifactKindValues.includes(value);
41212
+ }
41213
+ function formatBytes(bytes) {
41214
+ if (bytes < 1024) return `${bytes} B`;
41215
+ if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(0)} KB`;
41216
+ return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
41217
+ }
41218
+ async function resolveFindingId(organizationId, pentestId, identifier) {
41219
+ if (isUuid(identifier)) {
41220
+ return identifier;
41221
+ }
41222
+ const numericMatch = identifier.match(/^(?:PTF-)?(\d+)$/i);
41223
+ if (!numericMatch?.[1]) {
41224
+ console.error(
41225
+ colorError(
41226
+ `Invalid finding identifier '${sanitizeDisplay(identifier)}'. Use a finding number (e.g. PTF-7) or UUID.`
41227
+ )
41228
+ );
41229
+ process.exit(1);
41230
+ }
41231
+ const displayId = Number.parseInt(numericMatch[1], 10);
41232
+ const result = await authenticatedRequest(
41233
+ (client) => client.pentestFinding.listFindings({ params: { organizationId, pentestId } })
41234
+ );
41235
+ if (result.status === UNAUTHORIZED_STATUS2) {
41236
+ exitAuthFailed();
41237
+ }
41238
+ if (result.status !== 200) {
41239
+ console.error(colorError(`Failed to list findings (status ${result.status}).`));
41240
+ process.exit(1);
41241
+ }
41242
+ const match = result.body.find((f) => f.displayId === displayId);
41243
+ if (!match || !("pentestFindingId" in match)) {
41244
+ console.error(
41245
+ colorError(
41246
+ `No finding PTF-${displayId} in this pentest. Unpublished findings have no number yet \u2014 pass the finding UUID instead (\`tracecli pentest findings list --pentest <id> --output json\`).`
41247
+ )
41248
+ );
41249
+ process.exit(1);
41250
+ }
41251
+ return match.pentestFindingId;
41252
+ }
41253
+ async function runAttachEvidence(options) {
41254
+ if (options.section !== void 0 && !isSection(options.section)) {
41255
+ console.error(colorError(`Invalid --section '${sanitizeDisplay(options.section)}'.`));
41256
+ console.error(`Valid sections: ${SECTION_LIST}`);
41257
+ process.exit(1);
41258
+ }
41259
+ if (options.kind !== void 0 && !isKind(options.kind)) {
41260
+ console.error(colorError(`Invalid --kind '${sanitizeDisplay(options.kind)}'.`));
41261
+ console.error(`Valid kinds: ${KIND_LIST}`);
41262
+ process.exit(1);
41263
+ }
41264
+ let sizeBytes;
41265
+ try {
41266
+ const stats = statSync(options.file);
41267
+ if (!stats.isFile()) {
41268
+ console.error(colorError(`--file '${sanitizeDisplay(options.file)}' is not a regular file.`));
41269
+ process.exit(1);
41270
+ }
41271
+ sizeBytes = stats.size;
41272
+ } catch {
41273
+ console.error(colorError(`Cannot read --file '${sanitizeDisplay(options.file)}'.`));
41274
+ process.exit(1);
41275
+ }
41276
+ if (sizeBytes === 0) {
41277
+ console.error(colorError(`--file '${sanitizeDisplay(options.file)}' is empty.`));
41278
+ process.exit(1);
41279
+ }
41280
+ if (sizeBytes > MAX_STAFF_EVIDENCE_UPLOAD_BYTES) {
41281
+ console.error(
41282
+ colorError(
41283
+ `File is ${formatBytes(sizeBytes)}; the limit for one exhibit is ${formatBytes(MAX_STAFF_EVIDENCE_UPLOAD_BYTES)}.`
41284
+ )
41285
+ );
41286
+ process.exit(1);
41287
+ }
41288
+ const organizationId = await resolveOrganizationId();
41289
+ const pentestId = await resolvePentestId(organizationId, options.pentest);
41290
+ const pentestFindingId = await resolveFindingId(organizationId, pentestId, options.finding);
41291
+ const params = { organizationId, pentestId, pentestFindingId };
41292
+ const filename = basename(options.file);
41293
+ const slot = await authenticatedRequest(
41294
+ (client) => client.pentestFinding.createFindingEvidenceUpload({
41295
+ params,
41296
+ body: { filename, sizeBytes }
41297
+ })
41298
+ );
41299
+ if (slot.status === UNAUTHORIZED_STATUS2) {
41300
+ exitAuthFailed();
41301
+ }
41302
+ if (slot.status !== 201) {
41303
+ const { message } = readErrorFields(slot.body);
41304
+ console.error(
41305
+ colorError(message ?? `Failed to start the evidence upload (status ${slot.status}).`)
41306
+ );
41307
+ process.exit(1);
41308
+ }
41309
+ const body = readFileSync4(options.file);
41310
+ const put = await fetch(slot.body.uploadUrl, { method: "PUT", body: new Uint8Array(body) });
41311
+ if (!put.ok) {
41312
+ console.error(
41313
+ colorError(
41314
+ `Upload to storage failed (status ${put.status}). The upload link is short-lived \u2014 re-run the command.`
41315
+ )
41316
+ );
41317
+ process.exit(1);
41318
+ }
41319
+ const attached = await authenticatedRequest(
41320
+ (client) => client.pentestFinding.attachFindingEvidence({
41321
+ params,
41322
+ body: {
41323
+ key: slot.body.key,
41324
+ ...options.caption !== void 0 ? { caption: options.caption } : {},
41325
+ ...options.section !== void 0 && isSection(options.section) ? { section: options.section } : {},
41326
+ ...options.kind !== void 0 && isKind(options.kind) ? { kind: options.kind } : {},
41327
+ ...options.customerFacing ? { customerFacing: true } : {}
41328
+ }
41329
+ })
41330
+ );
41331
+ if (attached.status === UNAUTHORIZED_STATUS2) {
41332
+ exitAuthFailed();
41333
+ }
41334
+ if (attached.status !== 201) {
41335
+ const { message } = readErrorFields(attached.body);
41336
+ console.error(
41337
+ colorError(message ?? `Failed to attach the evidence (status ${attached.status}).`)
41338
+ );
41339
+ process.exit(1);
41340
+ }
41341
+ if (options.output === "json") {
41342
+ console.log(JSON.stringify(attached.body, null, 2));
41343
+ return;
41344
+ }
41345
+ const visibility = attached.body.customerFacing ? "customer-facing" : "staff-internal";
41346
+ const placement = attached.body.section ? ` \xB7 ${attached.body.section}` : "";
41347
+ console.log(
41348
+ colorSuccess(
41349
+ `Attached ${attached.body.kind} \xB7 ${formatBytes(attached.body.sizeBytes)} \xB7 ${visibility}${placement}`
41350
+ )
41351
+ );
41352
+ console.log(` artifact ${attached.body.artifactId}`);
41353
+ if (attached.body.kind === "HAR") {
41354
+ console.log(" HAR captures stay internal \u2014 they can carry a live session token.");
41355
+ } else if (!attached.body.customerFacing) {
41356
+ console.log(" Publish it from the finding\u2019s evidence panel in the staff console.");
41357
+ }
41358
+ }
41359
+ function createAttachEvidenceCommand() {
41360
+ return new Command("attach-evidence").description("Attach a screenshot, recording, or capture to a pentest finding (staff)").requiredOption("--pentest <id>", "Pentest display ID (e.g. PT-3) or UUID").requiredOption("--finding <id>", "Finding number (e.g. PTF-7) or UUID").requiredOption("--file <path>", "Path to the evidence file").option("--caption <text>", "What the exhibit shows; rendered as its label").option("--section <section>", `Report section it backs (${SECTION_LIST})`).option("--kind <kind>", `Override the derived exhibit kind (${KIND_LIST})`).option("--customer-facing", "Publish to the customer (not allowed for HAR or video)").option("-o, --output <format>", "Output format: text, json (default: text)", "text").addHelpText(
41361
+ "after",
41362
+ `
41363
+ The file uploads straight to storage, so recordings of any reasonable size work.
41364
+ Videos and HAR captures always land staff-internal \u2014 publish a video with the
41365
+ toggle in the staff console's evidence panel after reviewing it for redaction.
41366
+
41367
+ Examples:
41368
+ tracecli pentest findings attach-evidence --pentest PT-3 --finding 7 --file ./poc.png
41369
+ tracecli pentest findings attach-evidence --pentest PT-3 --finding PTF-7 --file ./poc.mov \\
41370
+ --caption "Cookie survives logout, replayed 40s later" --section proofOfConcept
41371
+ tracecli pentest findings attach-evidence --pentest PT-3 --finding 7 --file ./roster.json \\
41372
+ --caption "Full member roster returned to a non-member" --customer-facing --output json`
41373
+ ).action(async (options) => {
41374
+ await runAttachEvidence(options);
41375
+ });
41376
+ }
40454
41377
 
40455
41378
  // src/pentest/questionnaire.ts
40456
41379
  function buildUpsertQuestionnaireBody(answers) {
@@ -40533,9 +41456,9 @@ function formatAnswerValue(key, answers) {
40533
41456
  }
40534
41457
  }
40535
41458
  function formatQuestionnaireShow(input) {
40536
- const lines = [
40537
- `Editable: ${input.editable ? "yes (scoping window open)" : "no (editing closed after scoping)"}`
40538
- ];
41459
+ const submitted = input.submitted === true;
41460
+ const editableLine = input.editable ? "yes (scoping window open)" : submitted ? "no (submitted)" : "no (editing closed after scoping)";
41461
+ const lines = [`Editable: ${editableLine}`, `Submitted: ${submitted ? "yes" : "no"}`];
40539
41462
  if (input.answers === null || Object.keys(input.answers).length === 0) {
40540
41463
  lines.push("Answers: (none)");
40541
41464
  return lines.join("\n");
@@ -40595,43 +41518,7 @@ function formatFindingSeverity(severity) {
40595
41518
  return severity;
40596
41519
  }
40597
41520
  }
40598
- var UUID_PATTERN2 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
40599
- async function resolvePentestId(organizationId, identifier) {
40600
- if (UUID_PATTERN2.test(identifier)) {
40601
- return identifier;
40602
- }
40603
- const numericMatch = identifier.match(/^(?:PT-)?(\d+)$/i);
40604
- if (!numericMatch?.[1]) {
40605
- console.error(
40606
- `Invalid pentest identifier '${identifier}'. Use a display ID (e.g. PT-3) or UUID.`
40607
- );
40608
- process.exit(1);
40609
- }
40610
- const displayId = Number.parseInt(numericMatch[1], 10);
40611
- const result = await authenticatedRequest(
40612
- (client) => client.pentest.listPentests({
40613
- params: { organizationId }
40614
- })
40615
- );
40616
- if (result.status === 401) {
40617
- console.error("Authentication failed. Run `tracecli auth login` to re-authenticate.");
40618
- process.exit(1);
40619
- }
40620
- if (result.status !== 200) {
40621
- console.error(`Failed to list pentests (status ${result.status}).`);
40622
- process.exit(1);
40623
- }
40624
- const pentest = result.body.find((p2) => p2.displayId === displayId);
40625
- if (!pentest) {
40626
- console.error(
40627
- `Pentest with display ID ${displayId} not found.
40628
- Run \`tracecli pentest list\` to see available pentests.`
40629
- );
40630
- process.exit(1);
40631
- }
40632
- return pentest.pentestId;
40633
- }
40634
- function createListCommand5() {
41521
+ function createListCommand6() {
40635
41522
  return new Command("list").description("List pentests for your organization").option(
40636
41523
  "--status <status>",
40637
41524
  "Filter by status (SCOPING, IN_PROGRESS, REMEDIATION, CLOSED)",
@@ -40881,6 +41768,7 @@ function renderPentestFindingLines(finding) {
40881
41768
  function createFindingsCommand() {
40882
41769
  const findings = new Command("findings").description("Manage pentest findings");
40883
41770
  findings.addCommand(createFindingsListCommand());
41771
+ findings.addCommand(createAttachEvidenceCommand());
40884
41772
  return findings;
40885
41773
  }
40886
41774
  var PRIORITY_SET = new Set(CustomerPriorityCategoryValues);
@@ -41151,7 +42039,7 @@ async function fetchQuestionnaire(organizationId, pentestId, notFoundLabel) {
41151
42039
  if (result.status !== SUCCESS_STATUS) {
41152
42040
  failQuestionnaire(result.status, result.body, "read", notFoundLabel);
41153
42041
  }
41154
- return result.body;
42042
+ return toQuestionnaireGetBody(result.body);
41155
42043
  }
41156
42044
  async function putQuestionnaire(organizationId, pentestId, answers, notFoundLabel) {
41157
42045
  const body = buildUpsertQuestionnaireBody(answers);
@@ -41164,7 +42052,35 @@ async function putQuestionnaire(organizationId, pentestId, answers, notFoundLabe
41164
42052
  if (result.status !== SUCCESS_STATUS) {
41165
42053
  failQuestionnaire(result.status, result.body, "write", notFoundLabel);
41166
42054
  }
41167
- return result.body;
42055
+ return toQuestionnaireGetBody(result.body);
42056
+ }
42057
+ async function submitQuestionnaire(organizationId, pentestId, answers, notFoundLabel) {
42058
+ const result = await authenticatedRequest(
42059
+ (client) => client.pentest.submitCustomerPentestQuestionnaire({
42060
+ params: { organizationId, pentestId },
42061
+ body: answers === void 0 ? {} : { answers }
42062
+ })
42063
+ );
42064
+ if (result.status !== SUCCESS_STATUS) {
42065
+ failQuestionnaire(result.status, result.body, "write", notFoundLabel);
42066
+ }
42067
+ return toQuestionnaireGetBody(result.body);
42068
+ }
42069
+ function toQuestionnaireGetBody(body) {
42070
+ return {
42071
+ pentestId: body.pentestId,
42072
+ editable: body.editable,
42073
+ answers: body.answers,
42074
+ updatedAt: body.updatedAt,
42075
+ ...body.submittedAt !== void 0 ? { submittedAt: body.submittedAt } : {}
42076
+ };
42077
+ }
42078
+ function questionnaireShowInput(body) {
42079
+ return {
42080
+ answers: body.answers,
42081
+ editable: body.editable,
42082
+ submitted: body.submittedAt != null
42083
+ };
41168
42084
  }
41169
42085
  function exitCancelled() {
41170
42086
  me("Cancelled.");
@@ -41230,15 +42146,20 @@ async function promptQuestionnaireAnswers(existing) {
41230
42146
  async function runQuestionnaire(pentestIdentifier, options) {
41231
42147
  const show = options.show === true;
41232
42148
  const filePath = options.file;
42149
+ const submit = options.submit === true;
41233
42150
  if (show && filePath !== void 0) {
41234
42151
  console.error(colorError("Pass only one of --show / --file."));
41235
42152
  process.exit(1);
41236
42153
  }
42154
+ if (show && submit) {
42155
+ console.error(colorError("Pass only one of --show / --submit."));
42156
+ process.exit(1);
42157
+ }
41237
42158
  const isInteractiveTty = Boolean(process.stdin.isTTY && process.stdout.isTTY);
41238
- if (!show && filePath === void 0 && !isInteractiveTty) {
42159
+ if (!show && filePath === void 0 && !submit && !isInteractiveTty) {
41239
42160
  console.error(
41240
42161
  colorError(
41241
- "Interactive questionnaire requires a TTY. Use --show to print answers, or --file <path> to upload an answers JSON object."
42162
+ "Interactive questionnaire requires a TTY. Use --show to print answers, --file <path> to upload an answers JSON object, or --submit to freeze the current answers."
41242
42163
  )
41243
42164
  );
41244
42165
  process.exit(2);
@@ -41247,18 +42168,13 @@ async function runQuestionnaire(pentestIdentifier, options) {
41247
42168
  const pentestId = await resolvePentestId(organizationId, pentestIdentifier);
41248
42169
  if (show) {
41249
42170
  const current2 = await fetchQuestionnaire(organizationId, pentestId, pentestIdentifier);
41250
- console.log(
41251
- formatQuestionnaireShow({
41252
- answers: current2.answers,
41253
- editable: current2.editable
41254
- })
41255
- );
42171
+ console.log(formatQuestionnaireShow(questionnaireShowInput(current2)));
41256
42172
  return;
41257
42173
  }
41258
42174
  if (filePath !== void 0) {
41259
42175
  let raw;
41260
42176
  try {
41261
- raw = readFileSync4(filePath, "utf8");
42177
+ raw = readFileSync5(filePath, "utf8");
41262
42178
  } catch {
41263
42179
  console.error(colorError(`Could not read answers file: ${filePath}`));
41264
42180
  process.exit(1);
@@ -41268,26 +42184,31 @@ async function runQuestionnaire(pentestIdentifier, options) {
41268
42184
  console.error(colorError(parsed.error));
41269
42185
  process.exit(1);
41270
42186
  }
41271
- const saved2 = await putQuestionnaire(
42187
+ const saved2 = submit ? await submitQuestionnaire(organizationId, pentestId, parsed.answers, pentestIdentifier) : await putQuestionnaire(organizationId, pentestId, parsed.answers, pentestIdentifier);
42188
+ console.log(
42189
+ colorSuccess(
42190
+ submit ? "Submitted questionnaire. Answers are locked." : "Saved questionnaire answers."
42191
+ )
42192
+ );
42193
+ console.log(formatQuestionnaireShow(questionnaireShowInput(saved2)));
42194
+ return;
42195
+ }
42196
+ if (submit) {
42197
+ const submitted = await submitQuestionnaire(
41272
42198
  organizationId,
41273
42199
  pentestId,
41274
- parsed.answers,
42200
+ void 0,
41275
42201
  pentestIdentifier
41276
42202
  );
41277
- console.log(colorSuccess("Saved questionnaire answers."));
41278
- console.log(
41279
- formatQuestionnaireShow({
41280
- answers: saved2.answers,
41281
- editable: saved2.editable
41282
- })
41283
- );
42203
+ console.log(colorSuccess("Submitted questionnaire. Answers are locked."));
42204
+ console.log(formatQuestionnaireShow(questionnaireShowInput(submitted)));
41284
42205
  return;
41285
42206
  }
41286
42207
  const current = await fetchQuestionnaire(organizationId, pentestId, pentestIdentifier);
41287
42208
  if (!current.editable) {
41288
42209
  console.error(
41289
42210
  colorError(
41290
- "Questionnaire editing is closed after scoping starts. Use --show to view the saved answers."
42211
+ current.submittedAt != null ? "Questionnaire has already been submitted. Use --show to view the saved answers." : "Questionnaire editing is closed after scoping starts. Use --show to view the saved answers."
41291
42212
  )
41292
42213
  );
41293
42214
  process.exit(1);
@@ -41295,35 +42216,33 @@ async function runQuestionnaire(pentestIdentifier, options) {
41295
42216
  const merged = await promptQuestionnaireAnswers(current.answers);
41296
42217
  const saved = await putQuestionnaire(organizationId, pentestId, merged, pentestIdentifier);
41297
42218
  console.log(colorSuccess("Saved questionnaire answers."));
41298
- console.log(
41299
- formatQuestionnaireShow({
41300
- answers: saved.answers,
41301
- editable: saved.editable
41302
- })
41303
- );
42219
+ console.log(formatQuestionnaireShow(questionnaireShowInput(saved)));
41304
42220
  }
41305
42221
  function createQuestionnaireCommand() {
41306
42222
  return new Command("questionnaire").description("View or fill the customer pentest intake questionnaire").argument("<pentestId>", "Pentest display ID (e.g. PT-3) or UUID").option("--show", "Print current answers using shared labels (GET)").option(
41307
42223
  "--file <path>",
41308
42224
  "Upload answers from a JSON file (answers object only \u2014 not { answers }; whole-object replace)"
41309
- ).addHelpText(
42225
+ ).option("--submit", "Freeze the questionnaire after saving (or freeze the current answers)").addHelpText(
41310
42226
  "after",
41311
42227
  `
41312
42228
  Optional scoping questionnaire for a new pentest (editable while stage is DRAFT
41313
- or READY_TO_START). Reads accept a browser session or API key; writes require an
41314
- organization admin (or Trace staff) signed in via \`tracecli auth login\` \u2014 API
41315
- keys are rejected.
42229
+ or READY_TO_START, until submitted). Reads accept a browser session or API key;
42230
+ writes require an organization admin (or Trace staff) signed in via \`tracecli
42231
+ auth login\` \u2014 API keys are rejected.
41316
42232
 
41317
42233
  Modes:
41318
42234
  --show GET and print key: value with shared customer-facing labels
41319
42235
  --file <path> PUT the file's answers object (CLI wraps { answers } for the API)
42236
+ --submit Freeze answers (combine with --file or TTY fill, or alone to freeze current)
41320
42237
  (TTY default) load-merge-write: GET, prompt each question, confirm, PUT
41321
42238
 
41322
- Non-interactive shells must pass --show or --file (exit 2 otherwise).
42239
+ Non-interactive shells must pass --show, --file, or --submit (exit 2 otherwise).
41323
42240
 
41324
42241
  Examples:
41325
42242
  tracecli pentest questionnaire PT-1 --show
41326
42243
  tracecli pentest questionnaire PT-1 --file answers.json
42244
+ tracecli pentest questionnaire PT-1 --file answers.json --submit
42245
+ tracecli pentest questionnaire PT-1 --submit
41327
42246
  tracecli pentest questionnaire PT-1`
41328
42247
  ).action(async (pentestId, options) => {
41329
42248
  await runQuestionnaire(pentestId, options);
@@ -41331,7 +42250,7 @@ Examples:
41331
42250
  }
41332
42251
  function createPentestCommand() {
41333
42252
  const pentest = new Command("pentest").description("Manage pentests");
41334
- pentest.addCommand(createListCommand5());
42253
+ pentest.addCommand(createListCommand6());
41335
42254
  pentest.addCommand(createUpdateCommand4());
41336
42255
  pentest.addCommand(createQuestionnaireCommand());
41337
42256
  pentest.addCommand(createFindingsCommand());
@@ -41339,7 +42258,7 @@ function createPentestCommand() {
41339
42258
  }
41340
42259
 
41341
42260
  // src/update/commands.ts
41342
- import { existsSync as existsSync3, mkdirSync as mkdirSync3, readFileSync as readFileSync5, writeFileSync as writeFileSync4 } from "fs";
42261
+ import { existsSync as existsSync3, mkdirSync as mkdirSync3, readFileSync as readFileSync6, writeFileSync as writeFileSync4 } from "fs";
41343
42262
  import { dirname as dirname2, join as join5 } from "path";
41344
42263
  import { createInterface as createInterface4 } from "readline/promises";
41345
42264
  var UPDATE_CACHE_FILE = "update-check.json";
@@ -41357,7 +42276,7 @@ function readUpdateCheckCache() {
41357
42276
  return null;
41358
42277
  }
41359
42278
  try {
41360
- const rawCache = readFileSync5(cachePath, "utf-8");
42279
+ const rawCache = readFileSync6(cachePath, "utf-8");
41361
42280
  const parsed = JSON.parse(rawCache);
41362
42281
  if (typeof parsed !== "object" || parsed === null) {
41363
42282
  return null;
@@ -41956,7 +42875,7 @@ Run \`tracecli vulnerabilities list\` to see available vulnerabilities.`
41956
42875
  printVulnerabilityDetail(result.body);
41957
42876
  });
41958
42877
  }
41959
- function createListCommand6() {
42878
+ function createListCommand7() {
41960
42879
  return new Command("list").description("List vulnerabilities for your organization").option("--repo <repo>", "Filter by repository name or owner/repo").option(
41961
42880
  "--severity <severity>",
41962
42881
  "Filter by severity (CRITICAL, HIGH, MEDIUM, LOW)",
@@ -42090,7 +43009,7 @@ Examples:
42090
43009
  function createVulnerabilitiesCommand() {
42091
43010
  const vulnerabilities = new Command("vulnerabilities").description("Manage vulnerabilities");
42092
43011
  vulnerabilities.addCommand(createGetCommand4());
42093
- vulnerabilities.addCommand(createListCommand6());
43012
+ vulnerabilities.addCommand(createListCommand7());
42094
43013
  return vulnerabilities;
42095
43014
  }
42096
43015
 
@@ -42111,6 +43030,7 @@ program2.addCommand(createEnvironmentCommand());
42111
43030
  program2.addCommand(createCredentialCommand());
42112
43031
  program2.addCommand(createRepoCommand());
42113
43032
  program2.addCommand(createVulnerabilitiesCommand());
43033
+ program2.addCommand(createCodeSizeCommand());
42114
43034
  program2.addCommand(createPentestCommand());
42115
43035
  program2.addCommand(createCompletionCommand(program2));
42116
43036
  program2.addCommand(createUpgradeCommand());