securewithtrace 0.31.0 → 0.33.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +2066 -567
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -6322,29 +6322,29 @@ var ZodFunction = class _ZodFunction extends ZodType {
6322
6322
  const params = { errorMap: ctx.common.contextualErrorMap };
6323
6323
  const fn = ctx.data;
6324
6324
  if (this._def.returns instanceof ZodPromise) {
6325
- const me = this;
6325
+ const me2 = this;
6326
6326
  return OK(async function(...args) {
6327
6327
  const error48 = new ZodError([]);
6328
- const parsedArgs = await me._def.args.parseAsync(args, params).catch((e2) => {
6328
+ const parsedArgs = await me2._def.args.parseAsync(args, params).catch((e2) => {
6329
6329
  error48.addIssue(makeArgsIssue(args, e2));
6330
6330
  throw error48;
6331
6331
  });
6332
6332
  const result = await Reflect.apply(fn, this, parsedArgs);
6333
- const parsedReturns = await me._def.returns._def.type.parseAsync(result, params).catch((e2) => {
6333
+ const parsedReturns = await me2._def.returns._def.type.parseAsync(result, params).catch((e2) => {
6334
6334
  error48.addIssue(makeReturnsIssue(result, e2));
6335
6335
  throw error48;
6336
6336
  });
6337
6337
  return parsedReturns;
6338
6338
  });
6339
6339
  } else {
6340
- const me = this;
6340
+ const me2 = this;
6341
6341
  return OK(function(...args) {
6342
- const parsedArgs = me._def.args.safeParse(args, params);
6342
+ const parsedArgs = me2._def.args.safeParse(args, params);
6343
6343
  if (!parsedArgs.success) {
6344
6344
  throw new ZodError([makeArgsIssue(args, parsedArgs.error)]);
6345
6345
  }
6346
6346
  const result = Reflect.apply(fn, this, parsedArgs.data);
6347
- const parsedReturns = me._def.returns.safeParse(result, params);
6347
+ const parsedReturns = me2._def.returns.safeParse(result, params);
6348
6348
  if (!parsedReturns.success) {
6349
6349
  throw new ZodError([makeReturnsIssue(result, parsedReturns.error)]);
6350
6350
  }
@@ -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, (c50) => c50 === "-" ? "+" : "/");
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 c50 = inst._zod.bag;
20224
- inst.minDate = c50.minimum ? new Date(c50.minimum) : null;
20225
- inst.maxDate = c50.maximum ? new Date(c50.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);
@@ -21504,7 +21504,8 @@ var PentestEditEntityValues = [
21504
21504
  "PENTEST_TOOL_RUN",
21505
21505
  "PENTEST_ATTESTATION",
21506
21506
  "PENTEST_REPORT",
21507
- "PENTEST_EXPLOIT_ARTIFACT"
21507
+ "PENTEST_EXPLOIT_ARTIFACT",
21508
+ "PENTEST_FINDING_RETEST"
21508
21509
  ];
21509
21510
  var PentestEditEntity = external_exports.enum(PentestEditEntityValues);
21510
21511
  var PentestEditActionValues = [
@@ -21688,10 +21689,11 @@ var AgentSessionIdParamsSchema = AgentSessionsParamsSchema.extend({
21688
21689
  var AgentSessionEventIdParamsSchema = AgentSessionIdParamsSchema.extend({
21689
21690
  eventId: external_exports.uuid()
21690
21691
  });
21692
+ var MAX_AGENT_SESSION_MAX_USD = 1500;
21691
21693
  var CreateAgentSessionCommonSchema = external_exports.object({
21692
21694
  provider: AgentSessionProvider.optional(),
21693
21695
  model: external_exports.string().min(1).max(200).optional(),
21694
- maxUsd: external_exports.number().positive().max(500).optional(),
21696
+ maxUsd: external_exports.number().positive().max(MAX_AGENT_SESSION_MAX_USD).optional(),
21695
21697
  maxWallClockMin: external_exports.number().int().positive().max(24 * 60).optional()
21696
21698
  });
21697
21699
  var CreateAgentSessionRequestSchema = external_exports.discriminatedUnion("type", [
@@ -21701,7 +21703,17 @@ var CreateAgentSessionRequestSchema = external_exports.discriminatedUnion("type"
21701
21703
  }),
21702
21704
  CreateAgentSessionCommonSchema.extend({
21703
21705
  type: external_exports.literal("CODE_REVIEW"),
21704
- focus: external_exports.string().min(1).max(2e3).optional()
21706
+ focus: external_exports.string().min(1).max(2e3).optional(),
21707
+ /**
21708
+ * Which bound application this review reads the codebase through (multi-application
21709
+ * PR 4). REQUIRED once 2+ applications are bound — omitting it there is
21710
+ * `APPLICATION_REQUIRED`, because the silent alternative is N reviews all scoping
21711
+ * their threat model to the first binding and overwriting each other. Defaults to
21712
+ * the sole binding at 1, and is legitimately absent for a bindingless engagement.
21713
+ * Only CODE_REVIEW and PRODUCT_RECON carry it: the other types are engagement-wide
21714
+ * by design, and the discriminated union is what keeps it off them.
21715
+ */
21716
+ applicationId: external_exports.uuid().optional()
21705
21717
  }),
21706
21718
  CreateAgentSessionCommonSchema.extend({
21707
21719
  type: external_exports.literal("VALIDATION"),
@@ -21736,7 +21748,10 @@ var CreateAgentSessionRequestSchema = external_exports.discriminatedUnion("type"
21736
21748
  // / VALIDATION / RECON.
21737
21749
  CreateAgentSessionCommonSchema.extend({
21738
21750
  type: external_exports.literal("PRODUCT_RECON"),
21739
- focus: external_exports.string().min(1).max(2e3).optional()
21751
+ focus: external_exports.string().min(1).max(2e3).optional(),
21752
+ /** Which bound application this recon learns. Same rules as CODE_REVIEW's, except
21753
+ * a bindingless engagement is already rejected at prep (PRODUCT_RECON_NO_APPLICATION). */
21754
+ applicationId: external_exports.uuid().optional()
21740
21755
  }),
21741
21756
  // REPORT (report-generation-session RS3): the report-authoring session. The
21742
21757
  // objective is server-built from the engagement (no repo/creds); `focus` is
@@ -21752,8 +21767,10 @@ var SendAgentSessionMessageRequestSchema = external_exports.object({
21752
21767
  var ResumeAgentSessionRequestSchema = external_exports.object({
21753
21768
  /** Manual failover: relaunch on the other provider (cold cache accepted). */
21754
21769
  provider: AgentSessionProvider.optional(),
21755
- /** Budget raise — required to leave WAITING_ON_STAFF(BUDGET_EXHAUSTED). */
21756
- maxUsd: external_exports.number().positive().max(500).optional()
21770
+ /** Budget raise — required to leave WAITING_ON_STAFF(BUDGET_EXHAUSTED). Same
21771
+ * lifetime ceiling as create: a raise past it is not expressible, which is why
21772
+ * the create cap had to move for the engagement-wide validator. */
21773
+ maxUsd: external_exports.number().positive().max(MAX_AGENT_SESSION_MAX_USD).optional()
21757
21774
  });
21758
21775
  var ListAgentSessionEventsQuerySchema = external_exports.object({
21759
21776
  /** Return events with seq > sinceSeq (poll cursor). */
@@ -21765,6 +21782,12 @@ var AgentSessionSummarySchema = external_exports.object({
21765
21782
  organizationId: external_exports.uuid(),
21766
21783
  pentestId: external_exports.uuid(),
21767
21784
  type: AgentSessionType,
21785
+ /**
21786
+ * The application this session is scoped to, or null for engagement-wide (PR 4).
21787
+ * Id only — `toSummary` is a pure row mapper feeding six call sites, and the staff
21788
+ * console already holds the bound applications it needs to resolve a name.
21789
+ */
21790
+ applicationId: external_exports.uuid().nullable(),
21768
21791
  status: AgentSessionStatus,
21769
21792
  waitingReason: AgentSessionWaitingReason.nullable(),
21770
21793
  failureReason: AgentSessionFailureReason.nullable(),
@@ -21861,15 +21884,35 @@ var OperatorPentestContextSchema = external_exports.object({
21861
21884
  customerPriorityCategories: external_exports.array(external_exports.string()),
21862
21885
  status: PentestStatus,
21863
21886
  currentStage: PentestStage,
21887
+ /**
21888
+ * The lowest-ordinal binding. RETAINED rather than replaced by `applications`:
21889
+ * `apps/agent-session-host` mirrors this interface by hand and raw-casts the JSON
21890
+ * without a schema parse (`api-client.ts:858-872`), so dropping this field would
21891
+ * give a parked or in-flight session `undefined` for its application block with no
21892
+ * parse error and no log. It retires a release after no parked session predates
21893
+ * `applications`.
21894
+ */
21864
21895
  primaryApplication: external_exports.object({
21865
21896
  name: external_exports.string(),
21866
21897
  type: external_exports.string(),
21867
21898
  applicationUrl: external_exports.string().nullable()
21868
21899
  }).nullable(),
21900
+ /**
21901
+ * Every bound application, ordinal-ascending. Optional for the same host
21902
+ * raw-cast reason: an older host image ignores what it does not know about.
21903
+ */
21904
+ applications: external_exports.array(
21905
+ external_exports.object({
21906
+ name: external_exports.string(),
21907
+ type: external_exports.string(),
21908
+ applicationUrl: external_exports.string().nullable()
21909
+ })
21910
+ ).optional(),
21869
21911
  environments: external_exports.array(
21870
21912
  external_exports.object({
21871
21913
  name: external_exports.string(),
21872
- primaryUrl: external_exports.string(),
21914
+ // Null for repo-only application types (CLI, MOBILE, DESKTOP).
21915
+ primaryUrl: external_exports.string().nullable(),
21873
21916
  isProduction: external_exports.boolean()
21874
21917
  })
21875
21918
  ),
@@ -22197,36 +22240,9 @@ function roleDisplay(role, roleLabel) {
22197
22240
  return APPLICATION_REPOSITORY_ROLE_LABELS[role] ?? null;
22198
22241
  }
22199
22242
 
22200
- // ../../packages/core/src/ai-tooling/profile.ts
22201
- var AI_TOOL_DETECTION = ["claude-code", "codex", "both", "neither"];
22202
- var AI_TOOL_OVERRIDE = ["claude-code", "codex"];
22203
- var Sha40 = external_exports.string().regex(/^[a-f0-9]{40}$/, "expected a 40-char hex git SHA");
22204
- var AiToolingProfileSchema = external_exports.object({
22205
- schemaVersion: external_exports.literal(1),
22206
- detectedTool: external_exports.enum(AI_TOOL_DETECTION),
22207
- overrideTool: external_exports.enum(AI_TOOL_OVERRIDE).nullable(),
22208
- /** null = no detection has run yet (override-only seed); refresh on next scan. */
22209
- detectedAt: external_exports.iso.datetime().nullable(),
22210
- /** Scan commit-sha the detection ran against. null when detectedAt is null. */
22211
- detectedCommitSha: Sha40.nullable(),
22212
- evidence: external_exports.object({
22213
- claudeMdSha: Sha40.nullable(),
22214
- agentsMdSha: Sha40.nullable(),
22215
- claudeMdMtime: external_exports.iso.datetime().nullable(),
22216
- agentsMdMtime: external_exports.iso.datetime().nullable()
22217
- }).strict()
22218
- }).strict();
22219
-
22220
22243
  // ../../packages/api-contracts/src/schemas/api/v1/repository.api.ts
22221
22244
  var PlatformValues = ["GITHUB", "GITLAB", "BITBUCKET"];
22222
22245
  var RepositoryStatusValues = ["CONNECTED", "DISCONNECTED"];
22223
- var SetAiToolingProfileOverrideRequestSchema = external_exports.object({
22224
- /** null clears the override and reverts to detection-driven resolution. */
22225
- overrideTool: external_exports.enum(AI_TOOL_OVERRIDE).nullable()
22226
- }).strict();
22227
- var SetAiToolingProfileOverrideResponseSchema = external_exports.object({
22228
- aiToolingProfile: AiToolingProfileSchema.nullable()
22229
- }).strict();
22230
22246
  var RepositoryIdParamsSchema = OrgIdParamsSchema.extend({
22231
22247
  repositoryId: external_exports.uuid()
22232
22248
  });
@@ -22251,13 +22267,6 @@ var RepositoryResponseSchema = external_exports.object({
22251
22267
  githubDefaultBranch: external_exports.string(),
22252
22268
  scanningBranch: external_exports.string().nullable(),
22253
22269
  primaryLanguage: external_exports.string().nullable().optional(),
22254
- /**
22255
- * Null until the first post-scan detection writes a row, OR when the raw
22256
- * JSONB fails strict-schema parsing (mapper falls back to null so the
22257
- * response stays valid). PATCH override writes always preserve the live
22258
- * `overrideTool` via `parseOverrideTool`.
22259
- */
22260
- aiToolingProfile: AiToolingProfileSchema.nullable(),
22261
22270
  createdAt: external_exports.date(),
22262
22271
  updatedAt: external_exports.date()
22263
22272
  });
@@ -22345,7 +22354,8 @@ var ConfirmScanningBranchDefaultsResponseSchema = external_exports.object({
22345
22354
  });
22346
22355
 
22347
22356
  // ../../packages/api-contracts/src/schemas/api/v1/application.api.ts
22348
- var ApplicationTypeValues = ["WEB", "CLI", "MOBILE", "API_SERVICE"];
22357
+ var ApplicationTypeValues = ["WEB", "CLI", "MOBILE", "DESKTOP", "API_SERVICE"];
22358
+ var APPLICATION_NOTES_MAX_CHARS = 2e3;
22349
22359
  var ApplicationRepositoryRoleValues = [
22350
22360
  "FRONTEND",
22351
22361
  "BACKEND_API",
@@ -22393,9 +22403,19 @@ var ApplicationResponseSchema = external_exports.object({
22393
22403
  // reject application rows.
22394
22404
  type: external_exports.enum(ApplicationTypeValues).default("WEB"),
22395
22405
  applicationUrl: external_exports.string().nullable(),
22406
+ // Customer-authored free-text context, any type ("Advanced → Notes").
22407
+ notes: external_exports.string().nullable(),
22396
22408
  helpCenterUrl: external_exports.string().nullable(),
22397
22409
  documentationUrl: external_exports.string().nullable(),
22398
22410
  repositories: external_exports.array(ApplicationRepositorySummarySchema),
22411
+ // Registrable domains RECON is authorized to discover subdomains under. The
22412
+ // Environments-tab panel hydrates from this payload (no client GET). Defaulted
22413
+ // (like `type`) so a new client reading a pre-this-release API during a rolling
22414
+ // deploy doesn't reject application rows that predate the field.
22415
+ // Signed S3 URL for the Overview screenshot. Defaulted null so a new client
22416
+ // reading a pre-this-release API during a rolling deploy doesn't reject rows
22417
+ // that omit the field. The raw S3 key is never on the wire.
22418
+ screenshotUrl: external_exports.url().nullable().default(null),
22399
22419
  createdAt: external_exports.date(),
22400
22420
  updatedAt: external_exports.date()
22401
22421
  });
@@ -22404,6 +22424,7 @@ var CreateApplicationRequestSchema = external_exports.object({
22404
22424
  name: external_exports.string().min(1).max(200),
22405
22425
  type: external_exports.enum(ApplicationTypeValues).default("WEB"),
22406
22426
  applicationUrl: external_exports.url().max(2048).optional(),
22427
+ notes: external_exports.string().max(APPLICATION_NOTES_MAX_CHARS).optional(),
22407
22428
  repositoryId: external_exports.uuid().optional(),
22408
22429
  repositoryPath: external_exports.string().max(500).optional(),
22409
22430
  helpCenterUrl: external_exports.url().max(2048).optional(),
@@ -22431,6 +22452,7 @@ var UpdateApplicationRepositoryLinkRequestSchema = external_exports.object({
22431
22452
  var UpdateApplicationRequestSchema = external_exports.object({
22432
22453
  name: external_exports.string().min(1).max(200).optional(),
22433
22454
  applicationUrl: external_exports.url().max(2048).optional(),
22455
+ notes: external_exports.string().max(APPLICATION_NOTES_MAX_CHARS).nullable().optional(),
22434
22456
  repositoryId: external_exports.uuid().nullable().optional(),
22435
22457
  repositoryPath: external_exports.string().max(500).nullable().optional(),
22436
22458
  helpCenterUrl: external_exports.url().max(2048).nullable().optional(),
@@ -22794,7 +22816,7 @@ var chatSessionContract = c6.router({
22794
22816
  });
22795
22817
 
22796
22818
  // ../../packages/api-contracts/src/schemas/api/v1/cloud-integration.api.ts
22797
- var CloudProviderValues = ["AWS", "GCP", "AZURE", "VERCEL"];
22819
+ var CloudProviderValues = ["AWS", "GCP", "AZURE", "VERCEL", "CONVEX"];
22798
22820
  var CloudIntegrationStatusValues = [
22799
22821
  "PENDING",
22800
22822
  "ACTIVE",
@@ -23304,6 +23326,107 @@ var DisconnectGcpCloudIntegrationResponseSchema = external_exports.object({
23304
23326
  var CancelPendingGcpCloudIntegrationResponseSchema = external_exports.object({
23305
23327
  deleted: external_exports.literal(true)
23306
23328
  }).strict();
23329
+ var ConvexAccountIdParamsSchema = CloudIntegrationIdParamsSchema.extend({
23330
+ cloudProviderAccountId: external_exports.uuid()
23331
+ });
23332
+ var ConvexDeploymentIdParamsSchema = CloudIntegrationIdParamsSchema.extend({
23333
+ convexLinkedDeploymentId: external_exports.uuid()
23334
+ });
23335
+ var CreateConvexInstallIntentRequestSchema = external_exports.object({
23336
+ displayName: external_exports.string().trim().min(1).max(120).optional()
23337
+ }).strict();
23338
+ var FinalizeConvexCallbackRequestSchema = external_exports.object({
23339
+ code: external_exports.string().trim().min(1).max(4096),
23340
+ state: external_exports.string().trim().min(32).max(512)
23341
+ }).strict();
23342
+ var ConvexCloudIntegrationSummarySchema = external_exports.object({
23343
+ cloudIntegrationId: external_exports.uuid(),
23344
+ organizationId: external_exports.uuid(),
23345
+ provider: external_exports.literal("CONVEX"),
23346
+ status: CloudIntegrationStatusSchema,
23347
+ displayName: external_exports.string(),
23348
+ templateVersion: external_exports.string(),
23349
+ verifiedAt: external_exports.date().nullable(),
23350
+ disconnectedAt: external_exports.date().nullable(),
23351
+ disconnectedById: external_exports.uuid().nullable(),
23352
+ lastErrorCode: external_exports.string().nullable(),
23353
+ lastErrorMessage: external_exports.string().nullable(),
23354
+ createdAt: external_exports.date(),
23355
+ updatedAt: external_exports.date()
23356
+ }).strict();
23357
+ var ConvexLinkedDeploymentSummarySchema = external_exports.object({
23358
+ convexLinkedDeploymentId: external_exports.uuid(),
23359
+ organizationId: external_exports.uuid(),
23360
+ cloudIntegrationId: external_exports.uuid(),
23361
+ cloudProviderAccountId: external_exports.uuid(),
23362
+ cloudProviderConnectionId: external_exports.uuid(),
23363
+ convexProjectId: external_exports.number().int(),
23364
+ deploymentName: external_exports.string(),
23365
+ deploymentType: external_exports.string(),
23366
+ deploymentRegion: external_exports.string().nullable(),
23367
+ convexCloudUrl: external_exports.string(),
23368
+ convexSiteUrl: external_exports.string().nullable(),
23369
+ deploymentExpiresAt: external_exports.date().nullable(),
23370
+ /**
23371
+ * Whether a usable credential envelope is stored. NOT a scope claim: the key is
23372
+ * full-access, because Convex cannot mint a scoped one for an OAuth app. Read-only
23373
+ * is enforced by the broker that holds the key, never by the credential.
23374
+ */
23375
+ hasStoredKey: external_exports.boolean(),
23376
+ status: CloudIntegrationStatusSchema,
23377
+ lastVerifiedAt: external_exports.date().nullable(),
23378
+ lastAuthErrorCode: external_exports.string().nullable(),
23379
+ createdAt: external_exports.date(),
23380
+ updatedAt: external_exports.date()
23381
+ }).strict();
23382
+ var ConvexCloudIntegrationListItemSchema = external_exports.object({
23383
+ integration: ConvexCloudIntegrationSummarySchema,
23384
+ deployments: external_exports.array(ConvexLinkedDeploymentSummarySchema)
23385
+ }).strict();
23386
+ var ListConvexCloudIntegrationsResponseSchema = external_exports.object({
23387
+ integrations: external_exports.array(ConvexCloudIntegrationListItemSchema)
23388
+ }).strict();
23389
+ var CreateConvexInstallIntentResponseSchema = external_exports.object({
23390
+ integration: ConvexCloudIntegrationSummarySchema,
23391
+ installUrl: external_exports.url()
23392
+ }).strict();
23393
+ var FinalizeConvexCallbackResponseSchema = external_exports.object({
23394
+ integration: ConvexCloudIntegrationSummarySchema,
23395
+ deployments: external_exports.array(ConvexLinkedDeploymentSummarySchema),
23396
+ /** Deployments beyond the CONVEX_MAX_DEPLOYMENTS cap that were not connected. */
23397
+ droppedDeploymentCount: external_exports.number().int().nonnegative()
23398
+ }).strict();
23399
+ var DisconnectConvexCloudIntegrationResponseSchema = external_exports.object({
23400
+ integration: ConvexCloudIntegrationSummarySchema,
23401
+ deploymentCount: external_exports.number().int().nonnegative(),
23402
+ clearedKeyCount: external_exports.number().int().nonnegative(),
23403
+ /**
23404
+ * Trace cannot revoke a Convex deploy key: there is no deployment-plane endpoint,
23405
+ * the key is not returned by list_deploy_keys, and delete_deploy_key needs an id
23406
+ * the mint never returns. Removing the Trace OAuth app is the only lever the
23407
+ * customer has. A closed union, so PR 2 owns the wording.
23408
+ */
23409
+ revocationHint: external_exports.literal("REVOKE_OAUTH_APP"),
23410
+ convexApplicationsUrl: external_exports.url().nullable()
23411
+ }).strict();
23412
+ var DisconnectConvexProjectResponseSchema = external_exports.object({
23413
+ integration: ConvexCloudIntegrationSummarySchema,
23414
+ convexProjectId: external_exports.number().int(),
23415
+ deploymentCount: external_exports.number().int().nonnegative(),
23416
+ clearedKeyCount: external_exports.number().int().nonnegative(),
23417
+ revocationHint: external_exports.literal("REVOKE_OAUTH_APP"),
23418
+ convexApplicationsUrl: external_exports.url().nullable()
23419
+ }).strict();
23420
+ var DisconnectConvexDeploymentResponseSchema = external_exports.object({
23421
+ integration: ConvexCloudIntegrationSummarySchema,
23422
+ deployment: ConvexLinkedDeploymentSummarySchema,
23423
+ revocationHint: external_exports.literal("REVOKE_OAUTH_APP"),
23424
+ convexApplicationsUrl: external_exports.url().nullable()
23425
+ }).strict();
23426
+ var ReverifyConvexDeploymentResponseSchema = external_exports.object({
23427
+ integration: ConvexCloudIntegrationSummarySchema,
23428
+ deployment: ConvexLinkedDeploymentSummarySchema
23429
+ }).strict();
23307
23430
 
23308
23431
  // ../../packages/api-contracts/src/contracts/v1/cloud-integration.ts
23309
23432
  var c7 = initContract();
@@ -23604,6 +23727,165 @@ var cloudIntegrationContract = c7.router({
23604
23727
  503: ErrorResponseSchema,
23605
23728
  500: ErrorResponseSchema
23606
23729
  }
23730
+ },
23731
+ listConvexIntegrations: {
23732
+ method: "GET",
23733
+ path: "/organizations/:organizationId/cloud-integrations/convex",
23734
+ pathParams: OrgIdParamsSchema,
23735
+ responses: {
23736
+ 200: ListConvexCloudIntegrationsResponseSchema,
23737
+ 401: ErrorResponseSchema,
23738
+ 403: ErrorResponseSchema,
23739
+ 404: ErrorResponseSchema,
23740
+ 500: ErrorResponseSchema
23741
+ }
23742
+ },
23743
+ createConvexInstallIntent: {
23744
+ method: "POST",
23745
+ path: "/organizations/:organizationId/cloud-integrations/convex/install-intents",
23746
+ pathParams: OrgIdParamsSchema,
23747
+ body: CreateConvexInstallIntentRequestSchema,
23748
+ responses: {
23749
+ 201: CreateConvexInstallIntentResponseSchema,
23750
+ 400: ErrorResponseSchema,
23751
+ 401: ErrorResponseSchema,
23752
+ 403: ErrorResponseSchema,
23753
+ 404: ErrorResponseSchema,
23754
+ 409: ErrorResponseSchema,
23755
+ 500: ErrorResponseSchema
23756
+ }
23757
+ },
23758
+ finalizeConvexCallback: {
23759
+ method: "POST",
23760
+ path: "/organizations/:organizationId/cloud-integrations/convex/callback",
23761
+ pathParams: OrgIdParamsSchema,
23762
+ body: FinalizeConvexCallbackRequestSchema,
23763
+ responses: {
23764
+ 200: FinalizeConvexCallbackResponseSchema,
23765
+ 400: ErrorResponseSchema,
23766
+ 401: ErrorResponseSchema,
23767
+ 403: ErrorResponseSchema,
23768
+ 404: ErrorResponseSchema,
23769
+ 409: ErrorResponseSchema,
23770
+ 503: ErrorResponseSchema,
23771
+ 500: ErrorResponseSchema
23772
+ }
23773
+ },
23774
+ disconnectConvexIntegration: {
23775
+ method: "DELETE",
23776
+ path: "/organizations/:organizationId/cloud-integrations/:cloudIntegrationId/convex",
23777
+ pathParams: CloudIntegrationIdParamsSchema,
23778
+ responses: {
23779
+ 200: DisconnectConvexCloudIntegrationResponseSchema,
23780
+ 401: ErrorResponseSchema,
23781
+ 403: ErrorResponseSchema,
23782
+ 404: ErrorResponseSchema,
23783
+ 500: ErrorResponseSchema
23784
+ }
23785
+ },
23786
+ disconnectConvexProject: {
23787
+ method: "DELETE",
23788
+ path: "/organizations/:organizationId/cloud-integrations/:cloudIntegrationId/convex/accounts/:cloudProviderAccountId",
23789
+ pathParams: ConvexAccountIdParamsSchema,
23790
+ responses: {
23791
+ 200: DisconnectConvexProjectResponseSchema,
23792
+ 400: ErrorResponseSchema,
23793
+ 401: ErrorResponseSchema,
23794
+ 403: ErrorResponseSchema,
23795
+ 404: ErrorResponseSchema,
23796
+ 500: ErrorResponseSchema
23797
+ }
23798
+ },
23799
+ disconnectConvexDeployment: {
23800
+ method: "DELETE",
23801
+ path: "/organizations/:organizationId/cloud-integrations/:cloudIntegrationId/convex/deployments/:convexLinkedDeploymentId",
23802
+ pathParams: ConvexDeploymentIdParamsSchema,
23803
+ responses: {
23804
+ 200: DisconnectConvexDeploymentResponseSchema,
23805
+ 400: ErrorResponseSchema,
23806
+ 401: ErrorResponseSchema,
23807
+ 403: ErrorResponseSchema,
23808
+ 404: ErrorResponseSchema,
23809
+ 500: ErrorResponseSchema
23810
+ }
23811
+ },
23812
+ reverifyConvexDeployment: {
23813
+ method: "POST",
23814
+ path: "/organizations/:organizationId/cloud-integrations/:cloudIntegrationId/convex/deployments/:convexLinkedDeploymentId/reverify",
23815
+ pathParams: ConvexDeploymentIdParamsSchema,
23816
+ body: c7.noBody(),
23817
+ responses: {
23818
+ 200: ReverifyConvexDeploymentResponseSchema,
23819
+ 400: ErrorResponseSchema,
23820
+ 401: ErrorResponseSchema,
23821
+ 403: ErrorResponseSchema,
23822
+ 404: ErrorResponseSchema,
23823
+ 409: ErrorResponseSchema,
23824
+ 503: ErrorResponseSchema,
23825
+ 500: ErrorResponseSchema
23826
+ }
23827
+ }
23828
+ });
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
+
23875
+ // ../../packages/api-contracts/src/contracts/v1/code-size.ts
23876
+ var c8 = initContract();
23877
+ var codeSizeContract = c8.router({
23878
+ getOrganizationCodeSize: {
23879
+ method: "GET",
23880
+ path: "/organizations/:organizationId/code-size",
23881
+ pathParams: OrgIdParamsSchema,
23882
+ responses: {
23883
+ 200: OrganizationCodeSizeResponseSchema,
23884
+ 401: ErrorResponseSchema,
23885
+ 403: ErrorResponseSchema,
23886
+ 404: ErrorResponseSchema,
23887
+ 500: ErrorResponseSchema
23888
+ }
23607
23889
  }
23608
23890
  });
23609
23891
 
@@ -23714,13 +23996,13 @@ var StorageStateSchema = external_exports.object({
23714
23996
  } catch {
23715
23997
  return;
23716
23998
  }
23717
- s.cookies.forEach((c50, i) => {
23718
- const d2 = c50.domain.replace(/^\./, "").toLowerCase();
23999
+ s.cookies.forEach((c51, i) => {
24000
+ const d2 = c51.domain.replace(/^\./, "").toLowerCase();
23719
24001
  if (d2 !== host && !host.endsWith(`.${d2}`)) {
23720
24002
  ctx.addIssue({
23721
24003
  code: "custom",
23722
24004
  path: ["cookies", i, "domain"],
23723
- message: `cookie domain "${c50.domain}" is not the capture origin host "${host}" or a parent domain of it`
24005
+ message: `cookie domain "${c51.domain}" is not the capture origin host "${host}" or a parent domain of it`
23724
24006
  });
23725
24007
  }
23726
24008
  });
@@ -23751,10 +24033,42 @@ var CredentialIdParamsSchema = OrgIdParamsSchema.extend({
23751
24033
  var EnvironmentCredentialsParamsSchema = OrgIdParamsSchema.extend({
23752
24034
  environmentId: external_exports.uuid()
23753
24035
  });
24036
+ var OPAQUE_SECRET_SHAPE = { secretValue: external_exports.string().min(1).max(8192) };
24037
+ var SessionStateSecretValueSchema = external_exports.string().min(1).refine((s) => Buffer.byteLength(s, "utf8") <= 262144, "session state must be \u2264256KB").superRefine((s, ctx) => {
24038
+ const parsed = StorageStateSchema.safeParse(parseJsonOrUndefined(s));
24039
+ if (parsed.success) return;
24040
+ if (parsed.error.issues.length === 0) {
24041
+ ctx.addIssue({
24042
+ code: "custom",
24043
+ message: "secretValue is not a valid StorageState capture"
24044
+ });
24045
+ return;
24046
+ }
24047
+ for (const issue2 of parsed.error.issues) {
24048
+ ctx.addIssue({ code: "custom", message: issue2.message, path: issue2.path });
24049
+ }
24050
+ });
24051
+ var SECRET_SHAPES = {
24052
+ BEARER_TOKEN: OPAQUE_SECRET_SHAPE,
24053
+ JWT_TOKEN: OPAQUE_SECRET_SHAPE,
24054
+ API_KEY: OPAQUE_SECRET_SHAPE,
24055
+ PASSPHRASE: OPAQUE_SECRET_SHAPE,
24056
+ COOKIE: { secretValue: external_exports.string().min(1).max(16384) },
24057
+ // secretValue IS the test-account email (login id + inbox ref). Validated as
24058
+ // an address so a typo is rejected at create/rotate time rather than failing
24059
+ // later when the agent uses it as a login/inbox. 320 = RFC 5321 max length.
24060
+ MAGIC_AUTH: { secretValue: external_exports.email().max(320) },
24061
+ USERNAME_PASSWORD: {
24062
+ username: external_exports.string().min(1).max(500),
24063
+ password: external_exports.string().min(1).max(2048)
24064
+ },
24065
+ SESSION_STATE: { secretValue: SessionStateSecretValueSchema }
24066
+ };
24067
+ var CreateLabelSchema = external_exports.string().max(200).optional();
23754
24068
  var CreateBearerSchema = external_exports.object({
23755
24069
  credentialType: external_exports.literal("BEARER_TOKEN"),
23756
- label: external_exports.string().min(1).max(200),
23757
- secretValue: external_exports.string().min(1).max(8192),
24070
+ label: CreateLabelSchema,
24071
+ ...SECRET_SHAPES.BEARER_TOKEN,
23758
24072
  authConfig: external_exports.object({
23759
24073
  type: external_exports.literal("BEARER_TOKEN"),
23760
24074
  headerName: external_exports.string().min(1).max(200).optional(),
@@ -23763,8 +24077,8 @@ var CreateBearerSchema = external_exports.object({
23763
24077
  }).strict();
23764
24078
  var CreateJwtSchema = external_exports.object({
23765
24079
  credentialType: external_exports.literal("JWT_TOKEN"),
23766
- label: external_exports.string().min(1).max(200),
23767
- secretValue: external_exports.string().min(1).max(8192),
24080
+ label: CreateLabelSchema,
24081
+ ...SECRET_SHAPES.JWT_TOKEN,
23768
24082
  authConfig: external_exports.object({
23769
24083
  type: external_exports.literal("JWT_TOKEN"),
23770
24084
  headerName: external_exports.string().min(1).max(200).optional(),
@@ -23773,8 +24087,8 @@ var CreateJwtSchema = external_exports.object({
23773
24087
  }).strict();
23774
24088
  var CreateApiKeySchema = external_exports.object({
23775
24089
  credentialType: external_exports.literal("API_KEY"),
23776
- label: external_exports.string().min(1).max(200),
23777
- secretValue: external_exports.string().min(1).max(8192),
24090
+ label: CreateLabelSchema,
24091
+ ...SECRET_SHAPES.API_KEY,
23778
24092
  authConfig: external_exports.object({
23779
24093
  type: external_exports.literal("API_KEY"),
23780
24094
  placement: external_exports.enum(["header", "query"]),
@@ -23783,16 +24097,15 @@ var CreateApiKeySchema = external_exports.object({
23783
24097
  }).strict();
23784
24098
  var CreatePassphraseSchema = external_exports.object({
23785
24099
  credentialType: external_exports.literal("PASSPHRASE"),
23786
- label: external_exports.string().min(1).max(200),
23787
- secretValue: external_exports.string().min(1).max(8192),
24100
+ label: CreateLabelSchema,
24101
+ ...SECRET_SHAPES.PASSPHRASE,
23788
24102
  // Passphrases are non-HTTP context — authConfig is empty by design.
23789
24103
  authConfig: external_exports.object({ type: external_exports.literal("PASSPHRASE") }).strict().optional()
23790
24104
  }).strict();
23791
24105
  var CreateUsernamePasswordSchema = external_exports.object({
23792
24106
  credentialType: external_exports.literal("USERNAME_PASSWORD"),
23793
- label: external_exports.string().min(1).max(200),
23794
- username: external_exports.string().min(1).max(500),
23795
- password: external_exports.string().min(1).max(2048),
24107
+ label: CreateLabelSchema,
24108
+ ...SECRET_SHAPES.USERNAME_PASSWORD,
23796
24109
  authConfig: external_exports.object({
23797
24110
  type: external_exports.literal("USERNAME_PASSWORD"),
23798
24111
  method: external_exports.enum(["form", "basic", "json"]),
@@ -23806,8 +24119,8 @@ var CreateUsernamePasswordSchema = external_exports.object({
23806
24119
  }).strict();
23807
24120
  var CreateCookieSchema = external_exports.object({
23808
24121
  credentialType: external_exports.literal("COOKIE"),
23809
- label: external_exports.string().min(1).max(200),
23810
- secretValue: external_exports.string().min(1).max(16384),
24122
+ label: CreateLabelSchema,
24123
+ ...SECRET_SHAPES.COOKIE,
23811
24124
  authConfig: external_exports.object({
23812
24125
  type: external_exports.literal("COOKIE"),
23813
24126
  cookieName: external_exports.string().min(1).max(200),
@@ -23817,11 +24130,8 @@ var CreateCookieSchema = external_exports.object({
23817
24130
  }).strict();
23818
24131
  var CreateMagicAuthSchema = external_exports.object({
23819
24132
  credentialType: external_exports.literal("MAGIC_AUTH"),
23820
- label: external_exports.string().min(1).max(200),
23821
- // secretValue IS the test-account email (login id + inbox ref). Validate as
23822
- // an address so a typo is rejected at create time rather than failing later
23823
- // when the agent uses it as a login/inbox. 320 = RFC 5321 max length.
23824
- secretValue: external_exports.email().max(320),
24133
+ label: CreateLabelSchema,
24134
+ ...SECRET_SHAPES.MAGIC_AUTH,
23825
24135
  authConfig: external_exports.object({
23826
24136
  type: external_exports.literal("MAGIC_AUTH"),
23827
24137
  /** @deprecated inert — retained for back-compat only. */
@@ -23830,21 +24140,8 @@ var CreateMagicAuthSchema = external_exports.object({
23830
24140
  }).strict();
23831
24141
  var CreateSessionStateSchema = external_exports.object({
23832
24142
  credentialType: external_exports.literal("SESSION_STATE"),
23833
- label: external_exports.string().min(1).max(200),
23834
- secretValue: external_exports.string().min(1).refine((s) => Buffer.byteLength(s, "utf8") <= 262144, "session state must be \u2264256KB").superRefine((s, ctx) => {
23835
- const parsed = StorageStateSchema.safeParse(parseJsonOrUndefined(s));
23836
- if (parsed.success) return;
23837
- if (parsed.error.issues.length === 0) {
23838
- ctx.addIssue({
23839
- code: "custom",
23840
- message: "secretValue is not a valid StorageState capture"
23841
- });
23842
- return;
23843
- }
23844
- for (const issue2 of parsed.error.issues) {
23845
- ctx.addIssue({ code: "custom", message: issue2.message, path: issue2.path });
23846
- }
23847
- })
24143
+ label: CreateLabelSchema,
24144
+ ...SECRET_SHAPES.SESSION_STATE
23848
24145
  }).strict();
23849
24146
  var CreateCredentialRequestSchema = external_exports.discriminatedUnion("credentialType", [
23850
24147
  CreateBearerSchema,
@@ -23864,7 +24161,15 @@ var CredentialResponseSchema = external_exports.object({
23864
24161
  credentialType: CredentialType,
23865
24162
  authConfig: CredentialAuthConfigSchema.nullable(),
23866
24163
  createdAt: external_exports.date(),
23867
- updatedAt: external_exports.date()
24164
+ updatedAt: external_exports.date(),
24165
+ /**
24166
+ * When the encrypted secret was last REPLACED. Null for a credential never
24167
+ * rotated (including every one created before rotation existed). Distinct
24168
+ * from `updatedAt`, which also moves on label / environment / authConfig
24169
+ * edits — so only this field can evidence a secret change. Not secret
24170
+ * material: a timestamp, safe on the read path.
24171
+ */
24172
+ secretRotatedAt: external_exports.date().nullable()
23868
24173
  }).strict();
23869
24174
  var ListCredentialsResponseSchema = external_exports.array(CredentialResponseSchema);
23870
24175
  var UpdateCredentialRequestSchema = external_exports.object({
@@ -23872,10 +24177,41 @@ var UpdateCredentialRequestSchema = external_exports.object({
23872
24177
  environmentId: external_exports.uuid().optional(),
23873
24178
  authConfig: CredentialAuthConfigSchema.nullable().optional()
23874
24179
  }).strict();
24180
+ var RotateCredentialSecretRequestSchema = external_exports.discriminatedUnion("credentialType", [
24181
+ external_exports.object({ credentialType: external_exports.literal("BEARER_TOKEN"), ...SECRET_SHAPES.BEARER_TOKEN }).strict(),
24182
+ external_exports.object({ credentialType: external_exports.literal("JWT_TOKEN"), ...SECRET_SHAPES.JWT_TOKEN }).strict(),
24183
+ external_exports.object({ credentialType: external_exports.literal("API_KEY"), ...SECRET_SHAPES.API_KEY }).strict(),
24184
+ external_exports.object({ credentialType: external_exports.literal("PASSPHRASE"), ...SECRET_SHAPES.PASSPHRASE }).strict(),
24185
+ external_exports.object({ credentialType: external_exports.literal("COOKIE"), ...SECRET_SHAPES.COOKIE }).strict(),
24186
+ external_exports.object({ credentialType: external_exports.literal("MAGIC_AUTH"), ...SECRET_SHAPES.MAGIC_AUTH }).strict(),
24187
+ external_exports.object({
24188
+ credentialType: external_exports.literal("USERNAME_PASSWORD"),
24189
+ ...SECRET_SHAPES.USERNAME_PASSWORD
24190
+ }).strict(),
24191
+ external_exports.object({ credentialType: external_exports.literal("SESSION_STATE"), ...SECRET_SHAPES.SESSION_STATE }).strict()
24192
+ ]);
24193
+ var RevealCredentialSecretResponseSchema = external_exports.discriminatedUnion("credentialType", [
24194
+ external_exports.object({
24195
+ credentialType: external_exports.literal("USERNAME_PASSWORD"),
24196
+ username: external_exports.string(),
24197
+ password: external_exports.string()
24198
+ }).strict(),
24199
+ external_exports.object({
24200
+ credentialType: external_exports.enum([
24201
+ "BEARER_TOKEN",
24202
+ "JWT_TOKEN",
24203
+ "API_KEY",
24204
+ "PASSPHRASE",
24205
+ "COOKIE",
24206
+ "MAGIC_AUTH"
24207
+ ]),
24208
+ secretValue: external_exports.string()
24209
+ }).strict()
24210
+ ]);
23875
24211
 
23876
24212
  // ../../packages/api-contracts/src/contracts/v1/credential.ts
23877
- var c8 = initContract();
23878
- var credentialContract = c8.router({
24213
+ var c9 = initContract();
24214
+ var credentialContract = c9.router({
23879
24215
  listCredentials: {
23880
24216
  method: "GET",
23881
24217
  path: "/organizations/:organizationId/environments/:environmentId/credentials",
@@ -23930,6 +24266,55 @@ var credentialContract = c8.router({
23930
24266
  500: ErrorResponseSchema
23931
24267
  }
23932
24268
  },
24269
+ // Replace the stored secret IN PLACE — same credentialId, new KMS envelope.
24270
+ // Separate from PATCH so every route touching KMS or plaintext is greppable
24271
+ // at its own URL. PUT (not PATCH) because the body carries the COMPLETE
24272
+ // secret for the type, so the same request twice yields the same end state.
24273
+ //
24274
+ // Deliberately NOT subject to the CREDENTIAL_IN_USE guard that blocks delete
24275
+ // and env-move during a live pentest: rotation preserves credentialId, so the
24276
+ // engagement's credential set and its ScanCredential rows are untouched —
24277
+ // which is the invariant that guard protects. Mid-engagement is precisely when
24278
+ // a wrong password needs fixing.
24279
+ rotateCredentialSecret: {
24280
+ method: "PUT",
24281
+ path: "/organizations/:organizationId/environments/:environmentId/credentials/:credentialId/secret",
24282
+ pathParams: CredentialIdParamsSchema,
24283
+ body: RotateCredentialSecretRequestSchema,
24284
+ responses: {
24285
+ // Unchanged response shape — a rotate NEVER echoes the secret back.
24286
+ 200: CredentialResponseSchema,
24287
+ 400: ErrorResponseSchema,
24288
+ 401: ErrorResponseSchema,
24289
+ 403: ErrorResponseSchema,
24290
+ 404: ErrorResponseSchema,
24291
+ // 422 — body's credentialType differs from the stored row's
24292
+ // (errorCode CREDENTIAL_ROTATE_TYPE_MISMATCH). Type changes stay
24293
+ // delete + recreate.
24294
+ 422: ErrorResponseSchema,
24295
+ 500: ErrorResponseSchema
24296
+ }
24297
+ },
24298
+ // Return the stored plaintext to an authorized human. POST, not GET: it must
24299
+ // never sit in an access log, a browser history entry, or a cacheable read.
24300
+ // Requires a user principal — API keys are rejected, so a leaked trace_sk_*
24301
+ // cannot exfiltrate customer credentials.
24302
+ revealCredentialSecret: {
24303
+ method: "POST",
24304
+ path: "/organizations/:organizationId/environments/:environmentId/credentials/:credentialId/reveal",
24305
+ pathParams: CredentialIdParamsSchema,
24306
+ body: c9.noBody(),
24307
+ responses: {
24308
+ 200: RevealCredentialSecretResponseSchema,
24309
+ 401: ErrorResponseSchema,
24310
+ 403: ErrorResponseSchema,
24311
+ 404: ErrorResponseSchema,
24312
+ // 422 — SESSION_STATE has no reveal variant by construction
24313
+ // (errorCode CREDENTIAL_REVEAL_UNSUPPORTED).
24314
+ 422: ErrorResponseSchema,
24315
+ 500: ErrorResponseSchema
24316
+ }
24317
+ },
23933
24318
  deleteCredential: {
23934
24319
  method: "DELETE",
23935
24320
  path: "/organizations/:organizationId/environments/:environmentId/credentials/:credentialId",
@@ -24145,8 +24530,8 @@ var DependencySummaryResponseSchema = external_exports.object({
24145
24530
  });
24146
24531
 
24147
24532
  // ../../packages/api-contracts/src/contracts/v1/dependency.ts
24148
- var c9 = initContract();
24149
- var dependencyContract = c9.router({
24533
+ var c10 = initContract();
24534
+ var dependencyContract = c10.router({
24150
24535
  /** Cross-repo dependency search. Two-phase: pg_trgm on Package, then join repos. */
24151
24536
  searchDependencies: {
24152
24537
  method: "POST",
@@ -24291,8 +24676,8 @@ var DockerScoutIntegrationResponseSchema = external_exports.object({
24291
24676
  });
24292
24677
 
24293
24678
  // ../../packages/api-contracts/src/contracts/v1/docker-scout-integration.ts
24294
- var c10 = initContract();
24295
- var dockerScoutIntegrationContract = c10.router({
24679
+ var c11 = initContract();
24680
+ var dockerScoutIntegrationContract = c11.router({
24296
24681
  createDockerScoutIntegration: {
24297
24682
  method: "POST",
24298
24683
  path: "/organizations/:organizationId/docker-scout-integration",
@@ -24481,12 +24866,12 @@ var EngagementResponseSchema = external_exports.object({
24481
24866
  });
24482
24867
 
24483
24868
  // ../../packages/api-contracts/src/contracts/v1/engagement.ts
24484
- var c11 = initContract();
24869
+ var c12 = initContract();
24485
24870
  var RecordDashboardSessionRequest = external_exports.object({
24486
24871
  sessionId: external_exports.string().min(1).max(100)
24487
24872
  });
24488
24873
  var RecordDashboardSessionResponse = external_exports.object({});
24489
- var engagementContract = c11.router({
24874
+ var engagementContract = c12.router({
24490
24875
  recordDashboardSession: {
24491
24876
  method: "POST",
24492
24877
  // `pathPrefix: '/v1'` is applied at the router level (see
@@ -24535,7 +24920,7 @@ var EnvironmentResponseSchema = external_exports.object({
24535
24920
  applicationId: external_exports.uuid(),
24536
24921
  organizationId: external_exports.uuid(),
24537
24922
  name: external_exports.string(),
24538
- primaryUrl: external_exports.string(),
24923
+ primaryUrl: external_exports.string().nullable(),
24539
24924
  description: external_exports.string().nullable(),
24540
24925
  isProduction: external_exports.boolean(),
24541
24926
  createdAt: external_exports.date(),
@@ -24544,7 +24929,7 @@ var EnvironmentResponseSchema = external_exports.object({
24544
24929
  var ListEnvironmentsResponseSchema = external_exports.array(EnvironmentResponseSchema);
24545
24930
  var CreateEnvironmentRequestSchema = external_exports.object({
24546
24931
  name: EnvNameSchema,
24547
- primaryUrl: external_exports.url().max(2048),
24932
+ primaryUrl: external_exports.url().max(2048).optional(),
24548
24933
  description: external_exports.string().max(1e3).optional(),
24549
24934
  isProduction: external_exports.boolean().optional().default(false)
24550
24935
  }).strict();
@@ -24556,8 +24941,8 @@ var UpdateEnvironmentRequestSchema = external_exports.object({
24556
24941
  }).strict();
24557
24942
 
24558
24943
  // ../../packages/api-contracts/src/contracts/v1/environment.ts
24559
- var c12 = initContract();
24560
- var environmentContract = c12.router({
24944
+ var c13 = initContract();
24945
+ var environmentContract = c13.router({
24561
24946
  listEnvironments: {
24562
24947
  method: "GET",
24563
24948
  path: "/organizations/:organizationId/applications/:applicationId/environments",
@@ -24807,8 +25192,8 @@ var GitHubRepoSchema = external_exports.object({
24807
25192
  var ListGitHubInstallationReposResponseSchema = external_exports.array(GitHubRepoSchema);
24808
25193
 
24809
25194
  // ../../packages/api-contracts/src/contracts/v1/github.ts
24810
- var c13 = initContract();
24811
- var githubContract = c13.router({
25195
+ var c14 = initContract();
25196
+ var githubContract = c14.router({
24812
25197
  // Tier chooser data: which app tiers this env can connect + the org's
24813
25198
  // entitlement default. Pure read (auth context + server config).
24814
25199
  getConnectOptions: {
@@ -24907,7 +25292,7 @@ var githubContract = c13.router({
24907
25292
  method: "POST",
24908
25293
  path: "/organizations/:organizationId/github/install-requests/:requestId/cancel",
24909
25294
  pathParams: GitHubInstallRequestParamsSchema,
24910
- body: c13.type(),
25295
+ body: c14.type(),
24911
25296
  responses: {
24912
25297
  200: GitHubInstallRequestSchema,
24913
25298
  401: ErrorResponseSchema,
@@ -24918,7 +25303,7 @@ var githubContract = c13.router({
24918
25303
  }
24919
25304
  }
24920
25305
  });
24921
- var staffGithubContract = c13.router({
25306
+ var staffGithubContract = c14.router({
24922
25307
  listStaffInstallations: {
24923
25308
  method: "GET",
24924
25309
  path: "/staff/github/installations",
@@ -24961,8 +25346,8 @@ var ManagedInboxLabelParamsSchema = OrgIdParamsSchema.extend({
24961
25346
  });
24962
25347
 
24963
25348
  // ../../packages/api-contracts/src/contracts/v1/inbox.ts
24964
- var c14 = initContract();
24965
- var inboxContract = c14.router({
25349
+ var c15 = initContract();
25350
+ var inboxContract = c15.router({
24966
25351
  listInboxes: {
24967
25352
  method: "GET",
24968
25353
  path: "/organizations/:organizationId/inboxes",
@@ -25039,8 +25424,8 @@ var UpdateKnowledgeBaseResponseSchema = external_exports.object({
25039
25424
  });
25040
25425
 
25041
25426
  // ../../packages/api-contracts/src/contracts/v1/knowledge-base.ts
25042
- var c15 = initContract();
25043
- var knowledgeBaseContract = c15.router({
25427
+ var c16 = initContract();
25428
+ var knowledgeBaseContract = c16.router({
25044
25429
  getKnowledgeBase: {
25045
25430
  method: "GET",
25046
25431
  path: "/organizations/:organizationId/knowledge-base",
@@ -25261,8 +25646,8 @@ var UpdateRepoDocumentContentResponseSchema = external_exports.object({
25261
25646
  });
25262
25647
 
25263
25648
  // ../../packages/api-contracts/src/contracts/v1/knowledge-document.ts
25264
- var c16 = initContract();
25265
- var knowledgeDocumentContract = c16.router({
25649
+ var c17 = initContract();
25650
+ var knowledgeDocumentContract = c17.router({
25266
25651
  createDocument: {
25267
25652
  method: "POST",
25268
25653
  path: "/organizations/:organizationId/knowledge-documents",
@@ -25588,8 +25973,8 @@ var LinearMetadataResponseSchema = external_exports.object({
25588
25973
  });
25589
25974
 
25590
25975
  // ../../packages/api-contracts/src/contracts/v1/linear-integration.ts
25591
- var c17 = initContract();
25592
- var linearIntegrationContract = c17.router({
25976
+ var c18 = initContract();
25977
+ var linearIntegrationContract = c18.router({
25593
25978
  getLinearIntegration: {
25594
25979
  method: "GET",
25595
25980
  path: "/organizations/:organizationId/integrations/linear",
@@ -25607,7 +25992,7 @@ var linearIntegrationContract = c17.router({
25607
25992
  method: "POST",
25608
25993
  path: "/organizations/:organizationId/integrations/linear/connect",
25609
25994
  pathParams: OrgIdParamsSchema,
25610
- body: c17.noBody(),
25995
+ body: c18.noBody(),
25611
25996
  responses: {
25612
25997
  200: LinearConnectResponseSchema,
25613
25998
  401: ErrorResponseSchema,
@@ -25622,7 +26007,7 @@ var linearIntegrationContract = c17.router({
25622
26007
  method: "POST",
25623
26008
  path: "/organizations/:organizationId/integrations/linear/disconnect",
25624
26009
  pathParams: OrgIdParamsSchema,
25625
- body: c17.noBody(),
26010
+ body: c18.noBody(),
25626
26011
  responses: {
25627
26012
  200: LinearIntegrationResponseSchema,
25628
26013
  401: ErrorResponseSchema,
@@ -25714,7 +26099,7 @@ var CreateLinearTicketsParamsSchema = OrgIdParamsSchema.extend({
25714
26099
  });
25715
26100
  var CreateLinearTicketsRequestSchema = external_exports.object({
25716
26101
  /** UUID or PTF-N display id. Cap sized to the ~30s synchronous request budget. */
25717
- findingIds: external_exports.array(external_exports.string().min(1).max(64)).min(1).max(10),
26102
+ findingIds: external_exports.array(external_exports.string().min(1).max(64)).min(1).max(50),
25718
26103
  /** Single-finding requests only — a title with >1 finding is a 400. */
25719
26104
  title: external_exports.string().min(1).max(255).optional(),
25720
26105
  /** Destination overrides — ADMIN ONLY (MANAGE_ORGANIZATION); a non-admin sending any is a 403. */
@@ -25784,8 +26169,8 @@ var LinearTicketContextResponseSchema = external_exports.object({
25784
26169
  });
25785
26170
 
25786
26171
  // ../../packages/api-contracts/src/contracts/v1/linear-tickets.ts
25787
- var c18 = initContract();
25788
- var linearTicketContract = c18.router({
26172
+ var c19 = initContract();
26173
+ var linearTicketContract = c19.router({
25789
26174
  /**
25790
26175
  * Push one or more pentest findings to Linear as tickets. Customer-side
25791
26176
  * (VIEW_RESULTS + PENTEST capability); non-admins create into the org's
@@ -25889,8 +26274,8 @@ var LlmProviderKeyResponseSchema = external_exports.object({
25889
26274
  var ListLlmProviderKeysResponseSchema = external_exports.array(LlmProviderKeyResponseSchema);
25890
26275
 
25891
26276
  // ../../packages/api-contracts/src/contracts/v1/llm-provider-key.ts
25892
- var c19 = initContract();
25893
- var llmProviderKeyContract = c19.router({
26277
+ var c20 = initContract();
26278
+ var llmProviderKeyContract = c20.router({
25894
26279
  createLlmProviderKey: {
25895
26280
  method: "POST",
25896
26281
  path: "/organizations/:organizationId/llm-provider-keys",
@@ -25965,8 +26350,7 @@ var OrgCapabilityValues = [
25965
26350
  "SCAN_SECRETS",
25966
26351
  "SCAN_DAST",
25967
26352
  "AUTO_PR_SCANS",
25968
- "AUTO_PUSH_SCANS",
25969
- "AUTO_FIX_PR"
26353
+ "AUTO_PUSH_SCANS"
25970
26354
  ];
25971
26355
  var UpdateOrganizationRequestSchema = external_exports.object({
25972
26356
  name: external_exports.string().min(1).max(255).optional(),
@@ -26053,6 +26437,14 @@ var StaffOrganizationSummarySchema = external_exports.object({
26053
26437
  createdAt: external_exports.date()
26054
26438
  });
26055
26439
  var ListAllOrganizationsResponseSchema = external_exports.array(StaffOrganizationSummarySchema);
26440
+ var CreateOrganizationRequestSchema = external_exports.object({
26441
+ name: external_exports.string().trim().min(2, "Organization name must be at least 2 characters").max(100, "Organization name must be 100 characters or fewer")
26442
+ });
26443
+ var CreateOrganizationResponseSchema = external_exports.object({
26444
+ organizationId: external_exports.uuid(),
26445
+ slug: external_exports.string(),
26446
+ name: external_exports.string()
26447
+ });
26056
26448
  var OnboardingItemValues = [
26057
26449
  "CONNECT_CODE",
26058
26450
  "CONNECT_CLOUD",
@@ -26077,8 +26469,8 @@ var LogoDeleteRequestSchema = external_exports.object({
26077
26469
  });
26078
26470
 
26079
26471
  // ../../packages/api-contracts/src/contracts/v1/organization.ts
26080
- var c20 = initContract();
26081
- var organizationContract = c20.router({
26472
+ var c21 = initContract();
26473
+ var organizationContract = c21.router({
26082
26474
  listOrganizations: {
26083
26475
  method: "GET",
26084
26476
  path: "/organizations",
@@ -26109,6 +26501,19 @@ var organizationContract = c20.router({
26109
26501
  },
26110
26502
  summary: "List every organization (Trace staff only)"
26111
26503
  },
26504
+ createOrganization: {
26505
+ method: "POST",
26506
+ path: "/staff/organizations",
26507
+ body: CreateOrganizationRequestSchema,
26508
+ responses: {
26509
+ 201: CreateOrganizationResponseSchema,
26510
+ 400: ErrorResponseSchema,
26511
+ 401: ErrorResponseSchema,
26512
+ 403: ErrorResponseSchema,
26513
+ 500: ErrorResponseSchema
26514
+ },
26515
+ summary: "Create an organization (Trace staff only)"
26516
+ },
26112
26517
  getOrganization: {
26113
26518
  method: "GET",
26114
26519
  path: "/organizations/:organizationId",
@@ -26269,7 +26674,7 @@ var organizationContract = c20.router({
26269
26674
  method: "POST",
26270
26675
  path: "/organizations/:organizationId/logos-from-homepage",
26271
26676
  pathParams: OrgIdParamsSchema,
26272
- body: c20.type(),
26677
+ body: c21.type(),
26273
26678
  responses: {
26274
26679
  200: OrganizationResponseSchema,
26275
26680
  // 400 — no homepage set on the organization.
@@ -26525,10 +26930,189 @@ var CustomerPriorityCategoryValues = [
26525
26930
  ];
26526
26931
  var CustomerPriorityCategory = external_exports.enum(CustomerPriorityCategoryValues);
26527
26932
 
26933
+ // ../../packages/api-contracts/src/schemas/domain/pentest-customer-intake.schema.ts
26934
+ var IntakeSurfaceValues = [
26935
+ "WEB",
26936
+ "PUBLIC_API",
26937
+ "INTERNAL_API",
26938
+ "GRAPHQL",
26939
+ "WEBHOOKS",
26940
+ "MCP",
26941
+ "AI_AGENTS",
26942
+ "MOBILE",
26943
+ "DESKTOP",
26944
+ "CLI",
26945
+ "BACKGROUND_JOBS"
26946
+ ];
26947
+ var IntakeSurface = external_exports.enum(IntakeSurfaceValues);
26948
+ var PentestCustomerIntakeAnswersShape = external_exports.object({
26949
+ issueClasses: external_exports.array(CustomerPriorityCategory).max(11).optional(),
26950
+ issueClassesNotes: external_exports.string().max(5e3).optional(),
26951
+ focusAreas: external_exports.string().max(5e3).optional(),
26952
+ rolesAndPermissions: external_exports.string().max(1e4).optional(),
26953
+ authenticationSetup: external_exports.string().max(5e3).optional(),
26954
+ tenantIsolationExpectation: external_exports.string().max(5e3).optional(),
26955
+ surfaces: external_exports.array(IntakeSurface).max(11).optional(),
26956
+ surfacesNotes: external_exports.string().max(5e3).optional(),
26957
+ thirdParties: external_exports.string().max(5e3).optional(),
26958
+ sensitiveData: external_exports.string().max(5e3).optional(),
26959
+ neverTouch: external_exports.string().max(5e3).optional(),
26960
+ knownRisks: external_exports.string().max(5e3).optional(),
26961
+ tribalKnowledge: external_exports.string().max(1e4).optional()
26962
+ });
26963
+ var PentestCustomerIntakeAnswersSchema = PentestCustomerIntakeAnswersShape.strict();
26964
+ var PentestCustomerIntakeStoredAnswersSchema = PentestCustomerIntakeAnswersShape;
26965
+ var PentestCustomerIntakeWriteAnswersSchema = PentestCustomerIntakeAnswersShape.extend({
26966
+ /** @deprecated Retired question. Accepted for in-flight clients, never stored. */
26967
+ testEnvironment: external_exports.string().max(100).optional(),
26968
+ /** @deprecated Retired question. Accepted for in-flight clients, never stored. */
26969
+ reposAndBranches: external_exports.string().max(5e3).optional()
26970
+ }).strict();
26971
+ var PENTEST_CUSTOMER_INTAKE_ANSWER_KEYS = [
26972
+ "issueClasses",
26973
+ "issueClassesNotes",
26974
+ "focusAreas",
26975
+ "rolesAndPermissions",
26976
+ "authenticationSetup",
26977
+ "tenantIsolationExpectation",
26978
+ "surfaces",
26979
+ "surfacesNotes",
26980
+ "thirdParties",
26981
+ "sensitiveData",
26982
+ "neverTouch",
26983
+ "knownRisks",
26984
+ "tribalKnowledge"
26985
+ ];
26986
+ var PENTEST_CUSTOMER_INTAKE_LABELS = {
26987
+ issueClasses: "Issue classes to prioritize",
26988
+ issueClassesNotes: "Issue classes, additional notes",
26989
+ focusAreas: "Focus areas",
26990
+ rolesAndPermissions: "Roles and permissions",
26991
+ authenticationSetup: "Authentication setup",
26992
+ tenantIsolationExpectation: "Tenant isolation expectations",
26993
+ surfaces: "Surfaces in scope",
26994
+ surfacesNotes: "Surfaces, additional notes",
26995
+ thirdParties: "Third parties and integrations",
26996
+ sensitiveData: "Sensitive data",
26997
+ neverTouch: "Out of bounds / never touch",
26998
+ knownRisks: "Known risks",
26999
+ tribalKnowledge: "Tribal knowledge"
27000
+ };
27001
+ var PENTEST_CUSTOMER_INTAKE_QUESTIONS = [
27002
+ {
27003
+ kind: "multiSelect",
27004
+ key: "issueClasses",
27005
+ notesKey: "issueClassesNotes",
27006
+ question: "Which issue classes worry you most?",
27007
+ helper: "(authn bypass / broken access control / injection / data exposure / supply chain / infra exposure / multi-tenant isolation / crypto / business logic / SSRF / XSS)",
27008
+ notesQuestion: "Anything else about issue classes?"
27009
+ },
27010
+ {
27011
+ kind: "text",
27012
+ key: "focusAreas",
27013
+ question: "Is there any particular application or area you want us to spend extra focus on?"
27014
+ },
27015
+ {
27016
+ kind: "text",
27017
+ key: "rolesAndPermissions",
27018
+ question: "What roles/permissions can users take in your application?",
27019
+ helper: "Explain each role and what it should/shouldn't be able to do."
27020
+ },
27021
+ {
27022
+ kind: "text",
27023
+ key: "authenticationSetup",
27024
+ question: "Anything we should know about your authentication setup?",
27025
+ helper: "(e.g. mid-migration between providers, legacy login still live; otherwise we'll figure it out from the code)"
27026
+ },
27027
+ {
27028
+ kind: "text",
27029
+ key: "tenantIsolationExpectation",
27030
+ question: "What's your expectation for isolation between tenants/organizations?",
27031
+ helper: "What should never be reachable across that boundary?"
27032
+ },
27033
+ {
27034
+ kind: "multiSelect",
27035
+ key: "surfaces",
27036
+ notesKey: "surfacesNotes",
27037
+ question: "Which surfaces exist?",
27038
+ helper: "(web / public API / internal API / GraphQL / webhooks / MCP / AI agents / mobile / desktop / CLI / background jobs)",
27039
+ notesQuestion: "Anything else about your surfaces?"
27040
+ },
27041
+ {
27042
+ kind: "text",
27043
+ key: "thirdParties",
27044
+ question: "Which third parties do you connect to, and should we mock them or stand them up?",
27045
+ helper: "(e.g. CRMs, payment/bank APIs, identity providers, email/SMS, LLM providers)"
27046
+ },
27047
+ {
27048
+ kind: "text",
27049
+ key: "sensitiveData",
27050
+ question: "What data must no attacker ever access?",
27051
+ helper: "And what data do you not care about?"
27052
+ },
27053
+ {
27054
+ kind: "text",
27055
+ key: "neverTouch",
27056
+ question: "What must we never touch or trigger?",
27057
+ helper: "(destructive endpoints, real customer emails/SMS, third-party prod accounts, specific tenants)"
27058
+ },
27059
+ {
27060
+ kind: "text",
27061
+ key: "knownRisks",
27062
+ question: "What do you already know is broken or risky (i.e. what keeps you up at night)?"
27063
+ },
27064
+ {
27065
+ kind: "text",
27066
+ key: "tribalKnowledge",
27067
+ question: "What tribal knowledge would we only learn by asking?",
27068
+ helper: "Anything intentionally weird, counter-intuitive, or likely to trip us up; behavior that looks like a bug but isn't, dead code that's still deployed, a service that isn't what its name suggests."
27069
+ }
27070
+ ];
27071
+ var PENTEST_CUSTOMER_INTAKE_QUESTION_COUNT = PENTEST_CUSTOMER_INTAKE_QUESTIONS.length;
27072
+ var INTAKE_ISSUE_CLASS_LABELS = {
27073
+ AUTHN_BYPASS: "Authn bypass",
27074
+ AUTHZ_IDOR: "Broken access control",
27075
+ INJECTION: "Injection",
27076
+ DATA_EXPOSURE: "Data exposure",
27077
+ SUPPLY_CHAIN: "Supply chain",
27078
+ INFRA_EXPOSURE: "Infra exposure",
27079
+ MULTI_TENANT_ISOLATION: "Multi-tenant isolation",
27080
+ CRYPTO_WEAKNESS: "Crypto",
27081
+ BUSINESS_LOGIC: "Business logic",
27082
+ SSRF: "SSRF",
27083
+ XSS: "XSS"
27084
+ };
27085
+ var INTAKE_SURFACE_LABELS = {
27086
+ WEB: "Web",
27087
+ PUBLIC_API: "Public API",
27088
+ INTERNAL_API: "Internal API",
27089
+ GRAPHQL: "GraphQL",
27090
+ WEBHOOKS: "Webhooks",
27091
+ MCP: "MCP",
27092
+ AI_AGENTS: "AI agents",
27093
+ MOBILE: "Mobile",
27094
+ DESKTOP: "Desktop",
27095
+ CLI: "CLI",
27096
+ BACKGROUND_JOBS: "Background jobs"
27097
+ };
27098
+ function normalizePentestCustomerIntakeAnswers(answers) {
27099
+ const next = { ...answers };
27100
+ if (next.issueClasses !== void 0) {
27101
+ next.issueClasses = Array.from(new Set(next.issueClasses)).sort();
27102
+ }
27103
+ if (next.surfaces !== void 0) {
27104
+ next.surfaces = Array.from(new Set(next.surfaces)).sort();
27105
+ }
27106
+ return next;
27107
+ }
27108
+
26528
27109
  // ../../packages/api-contracts/src/schemas/api/v1/pentest.api.ts
26529
27110
  var PentestIdParamsSchema = OrgIdParamsSchema.extend({
26530
27111
  pentestId: external_exports.string().min(1)
26531
27112
  });
27113
+ var PentestApplicationParamsSchema = PentestIdParamsSchema.extend({
27114
+ applicationId: external_exports.uuid()
27115
+ });
26532
27116
  var CustomerPentestIdParamsSchema = OrgIdParamsSchema.extend({
26533
27117
  pentestId: PentestIdOrDisplayIdSchema
26534
27118
  });
@@ -26541,6 +27125,21 @@ var CustomerPentestStatusValues = [
26541
27125
  "PAUSED"
26542
27126
  ];
26543
27127
  var CustomerPentestStatus = external_exports.enum(CustomerPentestStatusValues);
27128
+ var BoundPentestApplicationSchema = external_exports.object({
27129
+ applicationId: external_exports.uuid(),
27130
+ name: external_exports.string(),
27131
+ type: external_exports.enum(ApplicationTypeValues),
27132
+ // `z.string()`, not `z.url()` — every other URL field on this surface's RESPONSE
27133
+ // side is a plain string. Writes already validate with `z.url()`
27134
+ // (`application.api.ts`), so re-validating stored data here buys nothing and would
27135
+ // let one legacy row 404 an entire staff page through `safeParse`.
27136
+ applicationUrl: external_exports.string().nullable(),
27137
+ /**
27138
+ * Bind order. Never renumbered on unbind, so gaps are normal and only the
27139
+ * relative order carries meaning — the lowest ordinal is the compat mirror.
27140
+ */
27141
+ ordinal: external_exports.number().int()
27142
+ });
26544
27143
  var PentestResponseSchema = external_exports.object({
26545
27144
  pentestId: external_exports.uuid(),
26546
27145
  organizationId: external_exports.uuid(),
@@ -26557,8 +27156,21 @@ var PentestResponseSchema = external_exports.object({
26557
27156
  scope: external_exports.string().nullable(),
26558
27157
  /** Granular stage state. Wizard surfaces this; worker transitions write it. */
26559
27158
  currentStage: PentestStage,
26560
- /** v1 supports one application per pentest. M:N (PentestApplication) deferred to v1.5. */
27159
+ /**
27160
+ * The LOWEST-ORDINAL bound application — a compat mirror of the authoritative
27161
+ * `PentestApplication` join, not a distinct concept. Read `applications` when you
27162
+ * need the engagement's real scope; a multi-application engagement reports its
27163
+ * first binding here and would otherwise understate what it covers.
27164
+ */
26561
27165
  primaryApplicationId: external_exports.uuid().nullable(),
27166
+ /**
27167
+ * Every bound application, ordinal-ascending. `.optional()` for rolling-deploy
27168
+ * safety: `apps/web` (Vercel) deploys ahead of `apps/api` (ECS), so a new web
27169
+ * build briefly reads responses from an API that does not send this yet — the
27170
+ * precedent is `PentestDetailResponseSchema` below. Absent means "this API is
27171
+ * older", NOT "no applications bound"; fall back to `primaryApplicationId`.
27172
+ */
27173
+ applications: external_exports.array(BoundPentestApplicationSchema).optional(),
26562
27174
  /** Customer threat-model context — free text. Used by hypothesis-gen prompt. */
26563
27175
  customerConcernsContext: external_exports.string().nullable(),
26564
27176
  customerPriorityCategories: external_exports.array(CustomerPriorityCategory),
@@ -26587,7 +27199,44 @@ var PentestDetailResponseSchema = PentestResponseSchema.extend({
26587
27199
  systemUnderTest: external_exports.string().optional(),
26588
27200
  testingWindowStart: external_exports.coerce.date().nullable().optional(),
26589
27201
  testingWindowEnd: external_exports.coerce.date().nullable().optional(),
26590
- engagementMetadataOverrides: EngagementMetadataOverridesSchema.optional()
27202
+ engagementMetadataOverrides: EngagementMetadataOverridesSchema.optional(),
27203
+ /**
27204
+ * True once the engagement's repository scope has been snapshotted, which
27205
+ * PERMANENTLY refuses bind and unbind (`repositoryScopes > 0` in
27206
+ * `requireBindableInLock`).
27207
+ *
27208
+ * This one refusal only — that guard also refuses on `PENTEST_CLOSED` and on a
27209
+ * non-scoping stage, both of which the client can already read off `status` and
27210
+ * `currentStage` on this same response. So `false` means "not frozen by the
27211
+ * snapshot", NOT "a bind will succeed"; a consumer that gates a control must check
27212
+ * the stage and closed state too.
27213
+ *
27214
+ * Needed as its OWN field because nothing else on the wire answers it: the scope
27215
+ * freezes at the first scope-consuming session create, and only VALIDATION moves
27216
+ * the stage, so RECON / CODE_REVIEW / PRODUCT_RECON leave the engagement in
27217
+ * `DRAFT` with permanently frozen bindings. `getRepositoryScope` reports
27218
+ * `source: 'PENTEST_SCOPE'` only PAST the scoping stages, so a frozen `DRAFT`
27219
+ * engagement still returns live application links there.
27220
+ *
27221
+ * Scoped to BINDINGS, not to the whole target: environment add/remove is gated on
27222
+ * stage alone (`PentestEnvironmentService`, `NON_SCOPING_PENTEST_STAGES`), and
27223
+ * `DRAFT` is a scoping stage — so a consumer must not disable environment editing
27224
+ * on this flag. Doing so would remove the one provisioning action a frozen
27225
+ * multi-application engagement still legitimately needs.
27226
+ *
27227
+ * Detail-only (the list schema deliberately carries no per-pentest aggregates) and
27228
+ * `.optional()` for the same rolling-deploy reason as the fields above.
27229
+ */
27230
+ applicationBindingFrozen: external_exports.boolean().optional(),
27231
+ /**
27232
+ * Optional customer intake questionnaire answers. Staff detail only.
27233
+ * `.optional()` for rolling-deploy safety (Vercel ahead of ECS).
27234
+ *
27235
+ * Read with the lenient schema for the same reason: an API that hasn't
27236
+ * deployed yet still answers with keys from a retired question, and a strict
27237
+ * parse here would fail the whole response instead of ignoring them.
27238
+ */
27239
+ customerIntakeAnswers: PentestCustomerIntakeStoredAnswersSchema.nullable().optional()
26591
27240
  });
26592
27241
  var CustomerPentestShellResponseSchema = external_exports.object({
26593
27242
  pentestId: external_exports.uuid(),
@@ -26596,6 +27245,24 @@ var CustomerPentestShellResponseSchema = external_exports.object({
26596
27245
  title: external_exports.string(),
26597
27246
  description: external_exports.string().nullable(),
26598
27247
  customerStatus: CustomerPentestStatus,
27248
+ /** The LOWEST-ORDINAL application under test — a compat mirror of the join. Null =
27249
+ * engagement not bound to an application. `.optional()` for rolling-deploy
27250
+ * safety — apps/web (Vercel) deploys ahead of apps/api (ECS).
27251
+ *
27252
+ * Read `applicationIds` for the homepage "In scope" chips: this field names one
27253
+ * application, so using it there marked the other bindings of a multi-application
27254
+ * engagement "Not in current scope" — a false statement on a customer surface. */
27255
+ primaryApplicationId: external_exports.uuid().nullable().optional(),
27256
+ /**
27257
+ * EVERY application this engagement covers, in bind order. Ids only — the customer
27258
+ * homepage already has the org's applications and resolves names locally, and this
27259
+ * surface deliberately shows no more application detail than it did.
27260
+ *
27261
+ * `.optional()` for the same rolling-deploy reason: absent means "this API is
27262
+ * older", NOT "nothing bound", so a consumer must fall back to
27263
+ * `primaryApplicationId` rather than claim the engagement covers nothing.
27264
+ */
27265
+ applicationIds: external_exports.array(external_exports.uuid()).optional(),
26599
27266
  startedAt: external_exports.coerce.date().nullable(),
26600
27267
  endedAt: external_exports.coerce.date().nullable(),
26601
27268
  createdAt: external_exports.coerce.date(),
@@ -26614,8 +27281,35 @@ var ListCustomerPentestsResponseSchema = external_exports.array(CustomerPentestR
26614
27281
  var CustomerPentestDetailResponseSchema = CustomerPentestResponseSchema.extend({
26615
27282
  systemUnderTest: external_exports.string().optional(),
26616
27283
  testingWindowStart: external_exports.coerce.date().nullable().optional(),
26617
- testingWindowEnd: external_exports.coerce.date().nullable().optional()
27284
+ testingWindowEnd: external_exports.coerce.date().nullable().optional(),
27285
+ /**
27286
+ * Discovery flags for the questionnaire header CTA. Answer bodies live on
27287
+ * GET …/questionnaire. `.optional()` for rolling-deploy safety.
27288
+ */
27289
+ questionnaireEditable: external_exports.boolean().optional(),
27290
+ hasQuestionnaireAnswers: external_exports.boolean().optional()
26618
27291
  });
27292
+ var CustomerPentestQuestionnaireResponseSchema = external_exports.object({
27293
+ pentestId: external_exports.uuid(),
27294
+ /** True iff currentStage ∈ SCOPING_PENTEST_STAGES (DRAFT | READY_TO_START). */
27295
+ editable: external_exports.boolean(),
27296
+ /**
27297
+ * Lenient on unknown keys: during a rolling deploy the dashboard runs against
27298
+ * an API that still answers with a retired question's key, and a strict parse
27299
+ * would turn that into a broken page instead of an ignored field. Writes stay
27300
+ * strict — see the upsert request below.
27301
+ */
27302
+ answers: PentestCustomerIntakeStoredAnswersSchema.nullable(),
27303
+ updatedAt: external_exports.coerce.date()
27304
+ });
27305
+ var UpsertCustomerPentestQuestionnaireRequestSchema = external_exports.object({
27306
+ /**
27307
+ * Full answers object, or null to clear. Keys of retired questions are
27308
+ * accepted and dropped so a tab opened before the deploy can still save;
27309
+ * keys that were never questions are still rejected.
27310
+ */
27311
+ answers: PentestCustomerIntakeWriteAnswersSchema.nullable()
27312
+ }).strict();
26619
27313
  var ReadPentestResponseSchema = external_exports.union([
26620
27314
  PentestDetailResponseSchema,
26621
27315
  CustomerPentestShellResponseSchema
@@ -26675,6 +27369,14 @@ var UpdatePentestRequestSchema = external_exports.object({
26675
27369
  scope: external_exports.string().max(5e4).nullable().optional(),
26676
27370
  startedAt: external_exports.coerce.date().nullable().optional(),
26677
27371
  endedAt: external_exports.coerce.date().nullable().optional(),
27372
+ /**
27373
+ * SINGLE-APPLICATION COMPAT PATH ONLY: means "replace the bound set with exactly
27374
+ * this application", or clear it on `null`. Rejected with 409
27375
+ * `PENTEST_MULTI_APPLICATION_USE_BIND_ENDPOINT` when 2+ applications are already
27376
+ * bound — otherwise one PATCH would silently unbind N−1 of them and bypass the
27377
+ * per-application unbind guard. Use `bindApplication` / `unbindApplication` for
27378
+ * anything multi-application.
27379
+ */
26678
27380
  primaryApplicationId: external_exports.uuid().nullable().optional(),
26679
27381
  customerConcernsContext: external_exports.string().max(2e4).nullable().optional(),
26680
27382
  customerPriorityCategories: external_exports.array(CustomerPriorityCategory).max(20).optional(),
@@ -26688,6 +27390,9 @@ var UpdatePentestRequestSchema = external_exports.object({
26688
27390
  perHypothesisBudgetCapUsd: external_exports.number().min(0).max(1e5).optional(),
26689
27391
  lifecycleReason: external_exports.string().trim().min(1).max(2e3).optional()
26690
27392
  });
27393
+ var BindPentestApplicationRequestSchema = external_exports.object({
27394
+ applicationId: external_exports.uuid()
27395
+ }).strict();
26691
27396
 
26692
27397
  // ../../packages/api-contracts/src/schemas/api/v1/pentest-stage-transition.api.ts
26693
27398
  var StageTransitionResponseSchema = external_exports.object({
@@ -26742,8 +27447,8 @@ var ScanReuseSuggestionsResponseSchema = external_exports.object({
26742
27447
  });
26743
27448
 
26744
27449
  // ../../packages/api-contracts/src/contracts/v1/pentest.ts
26745
- var c21 = initContract();
26746
- var pentestContract = c21.router({
27450
+ var c22 = initContract();
27451
+ var pentestContract = c22.router({
26747
27452
  listPentests: {
26748
27453
  method: "GET",
26749
27454
  path: "/organizations/:organizationId/pentests",
@@ -26793,6 +27498,33 @@ var pentestContract = c21.router({
26793
27498
  500: ErrorResponseSchema
26794
27499
  }
26795
27500
  },
27501
+ getCustomerPentestQuestionnaire: {
27502
+ method: "GET",
27503
+ path: "/organizations/:organizationId/customer-pentests/:pentestId/questionnaire",
27504
+ pathParams: CustomerPentestIdParamsSchema,
27505
+ responses: {
27506
+ 200: CustomerPentestQuestionnaireResponseSchema,
27507
+ 401: ErrorResponseSchema,
27508
+ 403: ErrorResponseSchema,
27509
+ 404: ErrorResponseSchema,
27510
+ 500: ErrorResponseSchema
27511
+ }
27512
+ },
27513
+ upsertCustomerPentestQuestionnaire: {
27514
+ method: "PUT",
27515
+ path: "/organizations/:organizationId/customer-pentests/:pentestId/questionnaire",
27516
+ pathParams: CustomerPentestIdParamsSchema,
27517
+ body: UpsertCustomerPentestQuestionnaireRequestSchema,
27518
+ responses: {
27519
+ 200: CustomerPentestQuestionnaireResponseSchema,
27520
+ 400: ErrorResponseSchema,
27521
+ 401: ErrorResponseSchema,
27522
+ 403: ErrorResponseSchema,
27523
+ 404: ErrorResponseSchema,
27524
+ 409: ErrorResponseSchema,
27525
+ 500: ErrorResponseSchema
27526
+ }
27527
+ },
26796
27528
  createPentest: {
26797
27529
  method: "POST",
26798
27530
  path: "/organizations/:organizationId/pentests",
@@ -26849,6 +27581,49 @@ var pentestContract = c21.router({
26849
27581
  500: ErrorResponseSchema
26850
27582
  }
26851
27583
  },
27584
+ // POST/DELETE-per-row rather than a batch set on PATCH, mirroring
27585
+ // `pentest-environment.ts`: every rejection stays attributable to ONE
27586
+ // application, where a set-diff would fail an entire save on one blocked
27587
+ // removal with no way to say which.
27588
+ bindApplication: {
27589
+ method: "POST",
27590
+ path: "/organizations/:organizationId/pentests/:pentestId/applications",
27591
+ pathParams: PentestIdParamsSchema,
27592
+ body: BindPentestApplicationRequestSchema,
27593
+ responses: {
27594
+ // The resulting bound set, NOT the detail shape. The 201 body is assembled purely
27595
+ // inside the bind transaction, so it cannot describe a state the write did not
27596
+ // produce; deriving the engagement cover here would mean a fallible read AFTER the
27597
+ // commit. Callers that need the cover GET the pentest.
27598
+ 201: PentestResponseSchema,
27599
+ 400: ErrorResponseSchema,
27600
+ 401: ErrorResponseSchema,
27601
+ 403: ErrorResponseSchema,
27602
+ 404: ErrorResponseSchema,
27603
+ // 409 — PENTEST_APPLICATION_ALREADY_BOUND (unique violation),
27604
+ // PENTEST_APPLICATION_FROZEN_AT_STAGE (past scoping), or
27605
+ // PENTEST_APPLICATION_SCOPE_FROZEN (repository scope already snapshotted,
27606
+ // so a new binding could never enter it).
27607
+ 409: ErrorResponseSchema,
27608
+ 500: ErrorResponseSchema
27609
+ }
27610
+ },
27611
+ unbindApplication: {
27612
+ method: "DELETE",
27613
+ path: "/organizations/:organizationId/pentests/:pentestId/applications/:applicationId",
27614
+ pathParams: PentestApplicationParamsSchema,
27615
+ responses: {
27616
+ 204: DeleteResponseSchema,
27617
+ 400: ErrorResponseSchema,
27618
+ 401: ErrorResponseSchema,
27619
+ 403: ErrorResponseSchema,
27620
+ 404: ErrorResponseSchema,
27621
+ // 409 — PENTEST_APPLICATION_UNBIND_BLOCKED (this application still has
27622
+ // linked environments), or either frozen guard as on bind.
27623
+ 409: ErrorResponseSchema,
27624
+ 500: ErrorResponseSchema
27625
+ }
27626
+ },
26852
27627
  getScanReuseSuggestions: {
26853
27628
  method: "GET",
26854
27629
  path: "/organizations/:organizationId/pentests/:pentestId/scan-reuse-suggestions",
@@ -27109,8 +27884,8 @@ var ReadAttestationDownloadResponseSchema = external_exports.union([
27109
27884
  ]);
27110
27885
 
27111
27886
  // ../../packages/api-contracts/src/contracts/v1/pentest-attestation.ts
27112
- var c22 = initContract();
27113
- var pentestAttestationContract = c22.router({
27887
+ var c23 = initContract();
27888
+ var pentestAttestationContract = c23.router({
27114
27889
  getDefaults: {
27115
27890
  method: "GET",
27116
27891
  path: "/organizations/:organizationId/pentests/:pentestId/attestations/defaults",
@@ -27252,8 +28027,8 @@ var ListPentestAuditLogResponseSchema = external_exports.object({
27252
28027
  });
27253
28028
 
27254
28029
  // ../../packages/api-contracts/src/contracts/v1/pentest-audit-log.ts
27255
- var c23 = initContract();
27256
- var pentestAuditLogContract = c23.router({
28030
+ var c24 = initContract();
28031
+ var pentestAuditLogContract = c24.router({
27257
28032
  listAuditLog: {
27258
28033
  method: "GET",
27259
28034
  path: "/organizations/:organizationId/pentests/:pentestId/audit-log",
@@ -27269,6 +28044,86 @@ var pentestAuditLogContract = c23.router({
27269
28044
  }
27270
28045
  });
27271
28046
 
28047
+ // ../../packages/api-contracts/src/schemas/api/v1/pentest-authorized-domain.api.ts
28048
+ var PentestAuthorizedDomainIdParamsSchema = PentestIdParamsSchema.extend({
28049
+ pentestAuthorizedDomainId: external_exports.uuid()
28050
+ });
28051
+ var AuthorizedDomainOriginValues = ["DERIVED", "ENGAGEMENT"];
28052
+ var AuthorizedDomainOrigin = external_exports.enum(AuthorizedDomainOriginValues);
28053
+ var PentestAuthorizedDomainResponseSchema = external_exports.object({
28054
+ /**
28055
+ * NULL for a domain contributed by a bound application while the engagement is
28056
+ * still tracking — there is no row yet, and nothing to address by id.
28057
+ */
28058
+ pentestAuthorizedDomainId: external_exports.uuid().nullable(),
28059
+ pentestId: external_exports.uuid(),
28060
+ organizationId: external_exports.uuid(),
28061
+ domain: external_exports.string(),
28062
+ applicationIds: external_exports.array(external_exports.uuid()),
28063
+ origin: AuthorizedDomainOrigin,
28064
+ /** NULL when the row was written by materialization rather than by a person. */
28065
+ addedById: external_exports.uuid().nullable(),
28066
+ addedAt: external_exports.date().nullable()
28067
+ });
28068
+ var PentestAuthorizedScopeResponseSchema = external_exports.object({
28069
+ domains: external_exports.array(PentestAuthorizedDomainResponseSchema),
28070
+ /** Null while the engagement still tracks its bound applications. */
28071
+ materializedAt: external_exports.date().nullable()
28072
+ });
28073
+ var CreatePentestAuthorizedDomainRequestSchema = external_exports.object({
28074
+ domain: external_exports.string().trim().min(1).max(253)
28075
+ }).strict();
28076
+
28077
+ // ../../packages/api-contracts/src/contracts/v1/pentest-authorized-domain.ts
28078
+ var c25 = initContract();
28079
+ var pentestAuthorizedDomainContract = c25.router({
28080
+ getPentestAuthorizedScope: {
28081
+ method: "GET",
28082
+ path: "/organizations/:organizationId/pentests/:pentestId/authorized-scope",
28083
+ pathParams: PentestIdParamsSchema,
28084
+ responses: {
28085
+ 200: PentestAuthorizedScopeResponseSchema,
28086
+ 401: ErrorResponseSchema,
28087
+ 403: ErrorResponseSchema,
28088
+ 404: ErrorResponseSchema,
28089
+ 500: ErrorResponseSchema
28090
+ }
28091
+ },
28092
+ addPentestAuthorizedDomain: {
28093
+ method: "POST",
28094
+ path: "/organizations/:organizationId/pentests/:pentestId/authorized-scope/domains",
28095
+ pathParams: PentestIdParamsSchema,
28096
+ body: CreatePentestAuthorizedDomainRequestSchema,
28097
+ responses: {
28098
+ 201: PentestAuthorizedDomainResponseSchema,
28099
+ // 400 = INVALID_DOMAIN (not a registrable domain / IP / bare suffix);
28100
+ // 409 = DOMAIN_ALREADY_AUTHORIZED or PENTEST_CLOSED.
28101
+ 400: ErrorResponseSchema,
28102
+ 401: ErrorResponseSchema,
28103
+ 403: ErrorResponseSchema,
28104
+ 404: ErrorResponseSchema,
28105
+ 409: ErrorResponseSchema,
28106
+ 500: ErrorResponseSchema
28107
+ }
28108
+ },
28109
+ removePentestAuthorizedDomain: {
28110
+ method: "DELETE",
28111
+ path: "/organizations/:organizationId/pentests/:pentestId/authorized-scope/domains/:pentestAuthorizedDomainId",
28112
+ pathParams: PentestAuthorizedDomainIdParamsSchema,
28113
+ responses: {
28114
+ 204: DeleteResponseSchema,
28115
+ // 409 = DOMAIN_FROM_ENVIRONMENT (derived from a selected environment's
28116
+ // Target URL, so removing the row here would not remove it from scope) or
28117
+ // PENTEST_CLOSED.
28118
+ 401: ErrorResponseSchema,
28119
+ 403: ErrorResponseSchema,
28120
+ 404: ErrorResponseSchema,
28121
+ 409: ErrorResponseSchema,
28122
+ 500: ErrorResponseSchema
28123
+ }
28124
+ }
28125
+ });
28126
+
27272
28127
  // ../../packages/api-contracts/src/schemas/api/v1/pentest-engagement-exclusion.api.ts
27273
28128
  var PentestEngagementExclusionsParamsSchema = OrgIdParamsSchema.extend({
27274
28129
  pentestId: external_exports.string().min(1)
@@ -27294,8 +28149,8 @@ var CreatePentestEngagementExclusionRequestSchema = external_exports.object({
27294
28149
  }).strict();
27295
28150
 
27296
28151
  // ../../packages/api-contracts/src/contracts/v1/pentest-engagement-exclusion.ts
27297
- var c24 = initContract();
27298
- var pentestEngagementExclusionContract = c24.router({
28152
+ var c26 = initContract();
28153
+ var pentestEngagementExclusionContract = c26.router({
27299
28154
  listPentestEngagementExclusions: {
27300
28155
  method: "GET",
27301
28156
  path: "/organizations/:organizationId/pentests/:pentestId/engagement-exclusions",
@@ -27359,8 +28214,8 @@ var CreatePentestEnvironmentRequestSchema = external_exports.object({
27359
28214
  }).strict();
27360
28215
 
27361
28216
  // ../../packages/api-contracts/src/contracts/v1/pentest-environment.ts
27362
- var c25 = initContract();
27363
- var pentestEnvironmentContract = c25.router({
28217
+ var c27 = initContract();
28218
+ var pentestEnvironmentContract = c27.router({
27364
28219
  listPentestEnvironments: {
27365
28220
  method: "GET",
27366
28221
  path: "/organizations/:organizationId/pentests/:pentestId/environments",
@@ -27427,7 +28282,22 @@ var ScopeRowSchema = external_exports.object({
27427
28282
  type: ScopeRowType,
27428
28283
  label: external_exports.string().min(1).max(200),
27429
28284
  description: external_exports.string().max(500),
27430
- inScope: external_exports.boolean()
28285
+ inScope: external_exports.boolean(),
28286
+ /**
28287
+ * Which bound applications this asset belongs to — DISPLAY NAMES in bind order
28288
+ * (multi-application PR 7).
28289
+ *
28290
+ * A SNAPSHOT, like `PentestRepositoryScope.purposeSnapshot`, not a reference: the
28291
+ * report row is a frozen customer-facing document, and resolving application ids at
28292
+ * render time would let a regenerate after an unbind silently shrink a published
28293
+ * scope column. A name that no longer matches a bound application is therefore
28294
+ * legitimate history, not an error.
28295
+ *
28296
+ * Absent or empty means "provenance unknown" — the renderer omits the cell rather
28297
+ * than printing "none" (the PR 1b hand-off rule, shared with the session briefs).
28298
+ * Capped at the row cap so it can never be the binding constraint below it.
28299
+ */
28300
+ applications: external_exports.array(external_exports.string().min(1).max(200)).max(100).optional()
27431
28301
  });
27432
28302
  var ScopeRowsJsonSchema = external_exports.object({
27433
28303
  version: external_exports.literal(1),
@@ -27650,7 +28520,13 @@ var ReportCoverBlockSchema = external_exports.object({
27650
28520
  testingWindowStart: NullableCoverFieldSchema,
27651
28521
  testingWindowEnd: NullableCoverFieldSchema,
27652
28522
  classification: CoverFieldSchema,
27653
- documentId: CoverFieldSchema
28523
+ documentId: CoverFieldSchema,
28524
+ /** Report attribution (derive-with-override like the rest; the derived
28525
+ * value is a firm-default constant, so `value` is always present).
28526
+ * testerName renders as "<name> · <firm>" in the cover's Tested-by row;
28527
+ * approverName is the "Approved by" row (conventionally "Name, Title"). */
28528
+ testerName: CoverFieldSchema,
28529
+ approverName: CoverFieldSchema
27654
28530
  });
27655
28531
  var ReportCoverFieldNameValues = [
27656
28532
  "customerOrgName",
@@ -27660,7 +28536,9 @@ var ReportCoverFieldNameValues = [
27660
28536
  "testingWindowStart",
27661
28537
  "testingWindowEnd",
27662
28538
  "classification",
27663
- "documentId"
28539
+ "documentId",
28540
+ "testerName",
28541
+ "approverName"
27664
28542
  ];
27665
28543
  var ReportCoverFieldName = external_exports.enum(ReportCoverFieldNameValues);
27666
28544
  var ApplyReportCoverRequestSchema = external_exports.object({
@@ -27719,6 +28597,20 @@ var ReportCanvasResponseSchema = external_exports.object({
27719
28597
  /** Per-field override provenance for the cover grid (RS2). */
27720
28598
  cover: ReportCoverBlockSchema,
27721
28599
  scopeRows: external_exports.array(ScopeRowSchema),
28600
+ /**
28601
+ * Every bound application's name, in bind order — what the scope editor offers as
28602
+ * per-row application toggles (multi-application PR 7). Empty for an unbound
28603
+ * engagement; a single entry means the editor stays hidden, matching the report's
28604
+ * own rule that one application needs no attribution column.
28605
+ *
28606
+ * OPTIONAL for the wire, not because the server ever omits it: `apps/web` deploys on
28607
+ * merge while the API deploys after it, and the ts-rest client does not validate
28608
+ * response bodies (`apps/web/lib/api-client.ts` sets no `validateResponse`), so a new
28609
+ * bundle briefly reads an old API's response. A required field would be typed as
28610
+ * present while arriving `undefined` — the same reason
28611
+ * `AgentSessionSpawnContextSchema.applications` is optional. Consumers default it.
28612
+ */
28613
+ boundApplications: external_exports.array(external_exports.string()).optional(),
27722
28614
  /** null = undrafted, or stored JSON was unparseable (degraded — never 500). */
27723
28615
  narrative: StoredNarrativeJsonSchema.nullable(),
27724
28616
  /** Render Section 05 (Remediation Timeline) in the PDF. Lazy/no-row previews
@@ -27862,6 +28754,11 @@ var IssuedReportSnapshotSchema = external_exports.object({
27862
28754
  * pre-feature `input_json` still parses (absent → undefined → standard
27863
28755
  * branding). */
27864
28756
  coBrandName: external_exports.string().nullable().optional(),
28757
+ /** Report attribution frozen at issuance. Optional so every pre-feature
28758
+ * `input_json` still parses (absent → legacy rendering: firm-only
28759
+ * Tested-by row, no Approved-by row). */
28760
+ testerName: external_exports.string().optional(),
28761
+ approverName: external_exports.string().optional(),
27865
28762
  engagementTitle: external_exports.string(),
27866
28763
  engagementSubtitle: external_exports.string(),
27867
28764
  systemUnderTest: external_exports.string(),
@@ -28189,7 +29086,18 @@ var PentestFindingResponseSchema = external_exports.object({
28189
29086
  pentestFindingId: external_exports.uuid(),
28190
29087
  organizationId: external_exports.uuid(),
28191
29088
  pentestId: external_exports.uuid(),
28192
- displayId: external_exports.number(),
29089
+ /**
29090
+ * Customer-facing finding number (the N in `PTF-N`). NULL until the finding is
29091
+ * published: a finding never shown to a customer has no customer-facing number yet.
29092
+ * Staff and agents identify an unpublished finding by `pentestFindingId` + `title`.
29093
+ *
29094
+ * Nullable here so it cascades to the staff and AGENT derivations below — every
29095
+ * agent-session type except REPORT reads findings with no visibility filter
29096
+ * (agent-session.service.ts operatorListFindings/operatorGetFinding), so a draft
29097
+ * reaches them and a non-null schema would be a Zod 500. The customer derivations
29098
+ * re-narrow it to non-null, because customer visibility implies published.
29099
+ */
29100
+ displayId: external_exports.number().int().positive().nullable(),
28193
29101
  title: external_exports.string(),
28194
29102
  description: external_exports.string(),
28195
29103
  severity: PentestFindingSeverity,
@@ -28263,9 +29171,11 @@ var OperatorFindingReadSchema = PentestFindingResponseSchema.omit({
28263
29171
  externalTicket: true
28264
29172
  });
28265
29173
  var OperatorFindingRetestSchema = external_exports.object({
29174
+ retestId: external_exports.uuid(),
28266
29175
  outcome: RetestOutcome,
28267
29176
  notes: external_exports.string().nullable(),
28268
29177
  testedAt: external_exports.coerce.date(),
29178
+ editedAt: external_exports.coerce.date().nullable(),
28269
29179
  source: PentestFindingRetestSource
28270
29180
  });
28271
29181
  var OperatorFindingDetailSchema = OperatorFindingReadSchema.extend({
@@ -28295,10 +29205,20 @@ var CustomerPentestFindingResponseSchema = PentestFindingResponseSchema.omit({
28295
29205
  reportProseAt: true,
28296
29206
  reportProseStatus: true
28297
29207
  }).extend({
29208
+ // Customer findings are published-only (customerVisiblePentestFindingWhere), and
29209
+ // publishing is what assigns the number, so it always exists here. Re-narrowed from
29210
+ // the nullable root so the customer dashboard never needs a draft affordance; the
29211
+ // handler mapper throws if the invariant is ever violated rather than coercing.
29212
+ displayId: external_exports.number().int().positive(),
28298
29213
  // Customer-safe prose/rendering status. This preserves report/PDF heading
28299
29214
  // semantics without exposing the staff-only provenance marker pair.
28300
29215
  renderedStatus: PentestFindingStatus,
28301
- repositories: external_exports.array(CustomerPentestFindingRepositorySchema)
29216
+ repositories: external_exports.array(CustomerPentestFindingRepositorySchema),
29217
+ // True when the customer has an unresolved dispute on this finding. Drives the
29218
+ // customer "dispute under review" hint + suppresses the re-dispute affordance,
29219
+ // even after a retest moved the finding off CONTESTED. Only this derived
29220
+ // boolean crosses the trust boundary — never the staff contest list.
29221
+ hasOpenContest: external_exports.boolean()
28302
29222
  });
28303
29223
  var CustomerPentestFindingListItemSchema = PentestFindingListItemSchema.omit({
28304
29224
  organizationId: true,
@@ -28310,6 +29230,9 @@ var CustomerPentestFindingListItemSchema = PentestFindingListItemSchema.omit({
28310
29230
  reportProseAt: true,
28311
29231
  reportProseStatus: true
28312
29232
  }).extend({
29233
+ // Published-only, same as the customer detail schema above — re-narrowed from the
29234
+ // nullable root.
29235
+ displayId: external_exports.number().int().positive(),
28313
29236
  repositories: external_exports.array(CustomerPentestFindingRepositorySchema)
28314
29237
  });
28315
29238
  var ListCustomerPentestFindingsResponseSchema = external_exports.array(
@@ -28455,7 +29378,12 @@ var RecoveredFindingResultSchema = external_exports.object({
28455
29378
  status: external_exports.enum(["recovered", "skipped"]),
28456
29379
  reason: external_exports.string().optional(),
28457
29380
  pentestFindingId: external_exports.uuid().optional(),
28458
- findingDisplayId: external_exports.int().optional(),
29381
+ /**
29382
+ * NULL when the recovered finding has no number yet. Recovered findings land
29383
+ * UNPUBLISHED (see the docstring above), and publishing is what assigns the number,
29384
+ * so `.optional()` alone would reject the `null` the service returns.
29385
+ */
29386
+ findingDisplayId: external_exports.int().positive().nullish(),
28459
29387
  /** Preview-only (dryRun): the title + severity the finding WOULD get. */
28460
29388
  findingTitle: external_exports.string().optional(),
28461
29389
  severity: PentestFindingSeverity.nullable().optional(),
@@ -28552,8 +29480,9 @@ var PentestFindingContestResponseSchema = external_exports.object({
28552
29480
  resolvedById: external_exports.uuid().nullable(),
28553
29481
  resolvedByName: external_exports.string().nullable(),
28554
29482
  resolvedAt: external_exports.coerce.date().nullable(),
28555
- // The finding status the resolution moved the finding to (PR 3): OPEN =
28556
- // dispute rejected; DISMISSED / ACCEPTED_WITH_CONTROLS = upheld. NULL while open.
29483
+ // The finding's ACTUAL status at resolution time (any active status for a
29484
+ // status-neutral reject). DISMISSED / ACCEPTED_WITH_CONTROLS ⇔ the dispute
29485
+ // was accepted; anything else ⇔ rejected. NULL while open.
28557
29486
  resultStatus: PentestFindingStatus.nullable()
28558
29487
  });
28559
29488
  var ListPentestFindingContestsResponseSchema = external_exports.array(
@@ -28578,6 +29507,9 @@ var PentestFindingRetestResponseSchema = external_exports.object({
28578
29507
  testedById: external_exports.uuid(),
28579
29508
  testedByName: external_exports.string(),
28580
29509
  testedAt: external_exports.coerce.date(),
29510
+ // Null = never corrected. Staff-only: the customer schema below is an explicit
29511
+ // allowlist, so this cannot reach a customer by omission.
29512
+ editedAt: external_exports.coerce.date().nullable(),
28581
29513
  source: PentestFindingRetestSource,
28582
29514
  retestRunId: external_exports.uuid().nullable(),
28583
29515
  performedByActorType: PentestAuditActorType,
@@ -28658,8 +29590,8 @@ var GetParseTaskStatusResponseSchema = external_exports.object({
28658
29590
  });
28659
29591
 
28660
29592
  // ../../packages/api-contracts/src/contracts/v1/pentest-finding.ts
28661
- var c26 = initContract();
28662
- var pentestFindingContract = c26.router({
29593
+ var c28 = initContract();
29594
+ var pentestFindingContract = c28.router({
28663
29595
  // ─── Findings ───
28664
29596
  listFindings: {
28665
29597
  method: "GET",
@@ -29024,7 +29956,8 @@ var pentestFindingContract = c26.router({
29024
29956
  401: ErrorResponseSchema,
29025
29957
  403: ErrorResponseSchema,
29026
29958
  404: ErrorResponseSchema,
29027
- // CONTEST_ALREADY_RESOLVED, non-CONTESTED finding, or PENTEST_CLOSED.
29959
+ // CONTEST_ALREADY_RESOLVED, CONTEST_REJECT_NOT_ALLOWED (reject on a
29960
+ // dismissed/accepted finding), or PENTEST_CLOSED.
29028
29961
  409: ErrorResponseSchema,
29029
29962
  500: ErrorResponseSchema
29030
29963
  }
@@ -29247,8 +30180,8 @@ var PromotePentestHypothesisRequestSchema = external_exports.object({
29247
30180
  }).strict().optional();
29248
30181
 
29249
30182
  // ../../packages/api-contracts/src/contracts/v1/pentest-hypothesis.ts
29250
- var c27 = initContract();
29251
- var pentestHypothesisContract = c27.router({
30183
+ var c29 = initContract();
30184
+ var pentestHypothesisContract = c29.router({
29252
30185
  listHypotheses: {
29253
30186
  method: "GET",
29254
30187
  path: "/organizations/:organizationId/pentests/:pentestId/hypotheses",
@@ -29351,8 +30284,8 @@ var pentestHypothesisContract = c27.router({
29351
30284
  });
29352
30285
 
29353
30286
  // ../../packages/api-contracts/src/contracts/v1/pentest-manual-finding.ts
29354
- var c28 = initContract();
29355
- var pentestManualFindingContract = c28.router({
30287
+ var c30 = initContract();
30288
+ var pentestManualFindingContract = c30.router({
29356
30289
  listManualFindings: {
29357
30290
  method: "GET",
29358
30291
  path: "/organizations/:organizationId/pentests/:pentestId/manual-findings",
@@ -29439,8 +30372,8 @@ var pentestManualFindingContract = c28.router({
29439
30372
  });
29440
30373
 
29441
30374
  // ../../packages/api-contracts/src/contracts/v1/pentest-report-pdf.ts
29442
- var c29 = initContract();
29443
- var pentestReportPdfContract = c29.router({
30375
+ var c31 = initContract();
30376
+ var pentestReportPdfContract = c31.router({
29444
30377
  getCurrent: {
29445
30378
  method: "GET",
29446
30379
  path: "/organizations/:organizationId/pentests/:pentestId/report-pdf",
@@ -29533,7 +30466,7 @@ var pentestReportPdfContract = c29.router({
29533
30466
  // No body: the render always reads current DB state (cover derived via
29534
30467
  // deriveReportCover(), narrative/scope from the row's columns). The legacy
29535
30468
  // dialog payload was retired with the Generate dialog (report-session RS5).
29536
- body: c29.noBody(),
30469
+ body: c31.noBody(),
29537
30470
  responses: {
29538
30471
  202: StartReportPdfGenerationTaskResponseSchema,
29539
30472
  400: ErrorResponseSchema,
@@ -29724,8 +30657,8 @@ var pentestReportPdfContract = c29.router({
29724
30657
  });
29725
30658
 
29726
30659
  // ../../packages/api-contracts/src/contracts/v1/pentest-validation.ts
29727
- var c30 = initContract();
29728
- var pentestValidationContract = c30.router({
30660
+ var c32 = initContract();
30661
+ var pentestValidationContract = c32.router({
29729
30662
  listValidationRuns: {
29730
30663
  method: "GET",
29731
30664
  path: "/organizations/:organizationId/pentests/:pentestId/validation-runs",
@@ -29833,8 +30766,8 @@ var pentestValidationContract = c30.router({
29833
30766
  });
29834
30767
 
29835
30768
  // ../../packages/api-contracts/src/contracts/v1/repository.ts
29836
- var c31 = initContract();
29837
- var repositoryContract = c31.router({
30769
+ var c33 = initContract();
30770
+ var repositoryContract = c33.router({
29838
30771
  createRepository: {
29839
30772
  method: "POST",
29840
30773
  path: "/organizations/:organizationId/repositories",
@@ -29991,7 +30924,7 @@ var repositoryContract = c31.router({
29991
30924
  method: "POST",
29992
30925
  path: "/organizations/:organizationId/repositories/scanning-branch/confirm-defaults",
29993
30926
  pathParams: OrgIdParamsSchema,
29994
- body: c31.noBody(),
30927
+ body: c33.noBody(),
29995
30928
  responses: {
29996
30929
  200: ConfirmScanningBranchDefaultsResponseSchema,
29997
30930
  401: ErrorResponseSchema,
@@ -30000,28 +30933,6 @@ var repositoryContract = c31.router({
30000
30933
  409: ErrorResponseSchema,
30001
30934
  500: ErrorResponseSchema
30002
30935
  }
30003
- },
30004
- /**
30005
- * Set / clear the per-repo AI coding-agent override (PR 5).
30006
- * Body `{overrideTool}`: 'claude-code'|'codex' sets, `null` clears.
30007
- * Response `{aiToolingProfile}` is nullable: clearing on an undetected
30008
- * repo writes NULL to the column and returns `{aiToolingProfile: null}`.
30009
- * 422 = `PLATFORM_VALIDATION_UNSUPPORTED` for non-GITHUB platforms.
30010
- */
30011
- setAiToolingProfileOverride: {
30012
- method: "PATCH",
30013
- path: "/organizations/:organizationId/repositories/:repositoryId/ai-tooling-profile",
30014
- pathParams: RepositoryIdParamsSchema,
30015
- body: SetAiToolingProfileOverrideRequestSchema,
30016
- responses: {
30017
- 200: SetAiToolingProfileOverrideResponseSchema,
30018
- 400: ErrorResponseSchema,
30019
- 401: ErrorResponseSchema,
30020
- 403: ErrorResponseSchema,
30021
- 404: ErrorResponseSchema,
30022
- 422: ErrorResponseSchema,
30023
- 500: ErrorResponseSchema
30024
- }
30025
30936
  }
30026
30937
  });
30027
30938
 
@@ -30147,8 +31058,8 @@ var ListScanLogsResponseSchema = external_exports.object({
30147
31058
  });
30148
31059
 
30149
31060
  // ../../packages/api-contracts/src/contracts/v1/scan.ts
30150
- var c32 = initContract();
30151
- var scanContract = c32.router({
31061
+ var c34 = initContract();
31062
+ var scanContract = c34.router({
30152
31063
  createScan: {
30153
31064
  method: "POST",
30154
31065
  path: "/organizations/:organizationId/scans",
@@ -30310,8 +31221,8 @@ var AttachTestIntegrationRequestSchema = external_exports.object({
30310
31221
  }).strict();
30311
31222
 
30312
31223
  // ../../packages/api-contracts/src/contracts/v1/test-integration.ts
30313
- var c33 = initContract();
30314
- var testIntegrationContract = c33.router({
31224
+ var c35 = initContract();
31225
+ var testIntegrationContract = c35.router({
30315
31226
  // -- Fixture CRUD (org-less, staff-only) ----------------------------------
30316
31227
  listTestIntegrations: {
30317
31228
  method: "GET",
@@ -30438,8 +31349,8 @@ var UpdateStaffProfileRequestSchema = external_exports.object({
30438
31349
  });
30439
31350
 
30440
31351
  // ../../packages/api-contracts/src/contracts/v1/user.ts
30441
- var c34 = initContract();
30442
- var userContract = c34.router({
31352
+ var c36 = initContract();
31353
+ var userContract = c36.router({
30443
31354
  getStaffProfile: {
30444
31355
  method: "GET",
30445
31356
  path: "/staff/profile",
@@ -30536,8 +31447,8 @@ var VantaIntegrationResponseSchema = external_exports.object({
30536
31447
  });
30537
31448
 
30538
31449
  // ../../packages/api-contracts/src/contracts/v1/vanta-integration.ts
30539
- var c35 = initContract();
30540
- var vantaIntegrationContract = c35.router({
31450
+ var c37 = initContract();
31451
+ var vantaIntegrationContract = c37.router({
30541
31452
  createVantaIntegration: {
30542
31453
  method: "POST",
30543
31454
  path: "/organizations/:organizationId/vanta-integration",
@@ -30869,8 +31780,8 @@ var VerifySharedVulnerabilityEmailCodeResponseSchema = external_exports.object({
30869
31780
  });
30870
31781
 
30871
31782
  // ../../packages/api-contracts/src/contracts/v1/vulnerability.ts
30872
- var c36 = initContract();
30873
- var vulnerabilityContract = c36.router({
31783
+ var c38 = initContract();
31784
+ var vulnerabilityContract = c38.router({
30874
31785
  filterVulnerabilities: {
30875
31786
  method: "POST",
30876
31787
  path: "/organizations/:organizationId/vulnerabilities/filter",
@@ -31051,122 +31962,6 @@ var vulnerabilityContract = c36.router({
31051
31962
  }
31052
31963
  });
31053
31964
 
31054
- // ../../packages/api-contracts/src/schemas/api/v1/vulnerability-fix-run.api.ts
31055
- var AGENT_FLAVORS = ["claude-code", "codex"];
31056
- var VULNERABILITY_FIX_RUN_STATUSES = [
31057
- "QUEUED",
31058
- "RUNNING",
31059
- "ARTIFACTS_READY",
31060
- "NO_SAFE_FIX",
31061
- "PATCH_TOO_LARGE",
31062
- "TESTS_FAILED",
31063
- "PR_OPENED",
31064
- "INFRA_FAILED",
31065
- "STALE_VULN",
31066
- "TIMED_OUT",
31067
- "CANCELLED"
31068
- ];
31069
- var VulnerabilityFixRunPathParamsSchema = VulnerabilityLookupIdParamsSchema.extend({
31070
- runId: external_exports.uuid()
31071
- });
31072
- var TriggerFixRunRequestSchema = external_exports.object({
31073
- agentFlavor: external_exports.enum(AGENT_FLAVORS).optional()
31074
- });
31075
- var VulnerabilityFixRunResponseSchema = external_exports.object({
31076
- runId: external_exports.uuid(),
31077
- organizationId: external_exports.uuid(),
31078
- vulnerabilityId: external_exports.uuid(),
31079
- repositoryId: external_exports.uuid(),
31080
- status: external_exports.enum(VULNERABILITY_FIX_RUN_STATUSES),
31081
- agentFlavor: external_exports.enum(AGENT_FLAVORS),
31082
- triggeredByUserId: external_exports.uuid().nullable(),
31083
- triggeredByApiKeyId: external_exports.uuid().nullable(),
31084
- /**
31085
- * Display name + avatar of the triggering staff user, resolved
31086
- * server-side (mirrors `ScanResponseSchema.initiatedByName`). Populated by
31087
- * every endpoint (list, trigger, get, cancel) so the polled active row in
31088
- * the runs table keeps the resolved name. `null` only when the run has no
31089
- * triggering user (api-key path) or the user no longer exists.
31090
- */
31091
- triggeredByName: external_exports.string().nullable(),
31092
- triggeredByProfilePictureUrl: external_exports.string().nullable(),
31093
- prUrl: external_exports.string().nullable(),
31094
- summaryStatus: external_exports.string().nullable(),
31095
- modelUsed: external_exports.string().nullable(),
31096
- addedLines: external_exports.number().int().nullable(),
31097
- removedLines: external_exports.number().int().nullable(),
31098
- filesChanged: external_exports.number().int().nullable(),
31099
- statusDetails: external_exports.string().nullable(),
31100
- queuedAt: external_exports.iso.datetime(),
31101
- claimedAt: external_exports.iso.datetime().nullable(),
31102
- deadlineAt: external_exports.iso.datetime().nullable(),
31103
- completedAt: external_exports.iso.datetime().nullable(),
31104
- updatedAt: external_exports.iso.datetime()
31105
- });
31106
- var ListVulnerabilityFixRunsResponseSchema = external_exports.array(VulnerabilityFixRunResponseSchema);
31107
-
31108
- // ../../packages/api-contracts/src/contracts/v1/vulnerability-fix-run.ts
31109
- var c37 = initContract();
31110
- var vulnerabilityFixRunContract = c37.router({
31111
- triggerRun: {
31112
- method: "POST",
31113
- path: "/organizations/:organizationId/vulnerabilities/:vulnerabilityId/fix-pr-runs",
31114
- pathParams: VulnerabilityLookupIdParamsSchema,
31115
- body: TriggerFixRunRequestSchema,
31116
- responses: {
31117
- 201: VulnerabilityFixRunResponseSchema,
31118
- 400: ErrorResponseSchema,
31119
- 401: ErrorResponseSchema,
31120
- 403: ErrorResponseSchema,
31121
- 404: ErrorResponseSchema,
31122
- 409: ErrorResponseSchema,
31123
- 429: ErrorResponseSchema,
31124
- 500: ErrorResponseSchema
31125
- }
31126
- },
31127
- getRun: {
31128
- method: "GET",
31129
- path: "/organizations/:organizationId/vulnerabilities/:vulnerabilityId/fix-pr-runs/:runId",
31130
- pathParams: VulnerabilityFixRunPathParamsSchema,
31131
- responses: {
31132
- 200: VulnerabilityFixRunResponseSchema,
31133
- 400: ErrorResponseSchema,
31134
- 401: ErrorResponseSchema,
31135
- 403: ErrorResponseSchema,
31136
- 404: ErrorResponseSchema,
31137
- 500: ErrorResponseSchema
31138
- }
31139
- },
31140
- cancelRun: {
31141
- method: "POST",
31142
- path: "/organizations/:organizationId/vulnerabilities/:vulnerabilityId/fix-pr-runs/:runId/cancel",
31143
- pathParams: VulnerabilityFixRunPathParamsSchema,
31144
- body: null,
31145
- responses: {
31146
- 200: VulnerabilityFixRunResponseSchema,
31147
- 400: ErrorResponseSchema,
31148
- 401: ErrorResponseSchema,
31149
- 403: ErrorResponseSchema,
31150
- 404: ErrorResponseSchema,
31151
- 409: ErrorResponseSchema,
31152
- 500: ErrorResponseSchema
31153
- }
31154
- },
31155
- listRuns: {
31156
- method: "GET",
31157
- path: "/organizations/:organizationId/vulnerabilities/:vulnerabilityId/fix-pr-runs",
31158
- pathParams: VulnerabilityLookupIdParamsSchema,
31159
- responses: {
31160
- 200: ListVulnerabilityFixRunsResponseSchema,
31161
- 400: ErrorResponseSchema,
31162
- 401: ErrorResponseSchema,
31163
- 403: ErrorResponseSchema,
31164
- 404: ErrorResponseSchema,
31165
- 500: ErrorResponseSchema
31166
- }
31167
- }
31168
- });
31169
-
31170
31965
  // ../../packages/api-contracts/src/schemas/api/v1/vulnerability-note.api.ts
31171
31966
  var VulnerabilityLookupNoteParamsSchema = OrgIdParamsSchema.extend({
31172
31967
  vulnerabilityId: VulnerabilityLookupIdSchema
@@ -31194,8 +31989,8 @@ var UpdateVulnerabilityNoteRequestSchema = external_exports.object({
31194
31989
  });
31195
31990
 
31196
31991
  // ../../packages/api-contracts/src/contracts/v1/vulnerability-note.ts
31197
- var c38 = initContract();
31198
- var vulnerabilityNoteContract = c38.router({
31992
+ var c39 = initContract();
31993
+ var vulnerabilityNoteContract = c39.router({
31199
31994
  listNotes: {
31200
31995
  method: "GET",
31201
31996
  path: "/organizations/:organizationId/vulnerabilities/:vulnerabilityId/notes",
@@ -31376,8 +32171,8 @@ var ListWebhookDeliveriesResponseSchema = external_exports.object({
31376
32171
  });
31377
32172
 
31378
32173
  // ../../packages/api-contracts/src/contracts/v1/webhook-settings.ts
31379
- var c39 = initContract();
31380
- var webhookSettingsContract = c39.router({
32174
+ var c40 = initContract();
32175
+ var webhookSettingsContract = c40.router({
31381
32176
  listWebhookEndpoints: {
31382
32177
  method: "GET",
31383
32178
  path: "/organizations/:organizationId/webhook-settings/endpoints",
@@ -31515,11 +32310,12 @@ var webhookSettingsContract = c39.router({
31515
32310
  });
31516
32311
 
31517
32312
  // ../../packages/api-contracts/src/contracts/v1/index.ts
31518
- var c40 = initContract();
31519
- var v1Contract = c40.router(
32313
+ var c41 = initContract();
32314
+ var v1Contract = c41.router(
31520
32315
  {
31521
32316
  admin: adminContract,
31522
32317
  cloudIntegration: cloudIntegrationContract,
32318
+ codeSize: codeSizeContract,
31523
32319
  scan: scanContract,
31524
32320
  organization: organizationContract,
31525
32321
  user: userContract,
@@ -31536,7 +32332,6 @@ var v1Contract = c40.router(
31536
32332
  knowledgeDocument: knowledgeDocumentContract,
31537
32333
  vulnerability: vulnerabilityContract,
31538
32334
  vulnerabilityNote: vulnerabilityNoteContract,
31539
- vulnerabilityFixRun: vulnerabilityFixRunContract,
31540
32335
  webhookSettings: webhookSettingsContract,
31541
32336
  application: applicationContract,
31542
32337
  chatSession: chatSessionContract,
@@ -31547,6 +32342,7 @@ var v1Contract = c40.router(
31547
32342
  pentestAuditLog: pentestAuditLogContract,
31548
32343
  pentestEnvironment: pentestEnvironmentContract,
31549
32344
  pentestEngagementExclusion: pentestEngagementExclusionContract,
32345
+ pentestAuthorizedDomain: pentestAuthorizedDomainContract,
31550
32346
  pentestFinding: pentestFindingContract,
31551
32347
  pentestHypothesis: pentestHypothesisContract,
31552
32348
  pentestValidation: pentestValidationContract,
@@ -31565,8 +32361,8 @@ var v1Contract = c40.router(
31565
32361
  );
31566
32362
 
31567
32363
  // ../../packages/api-contracts/src/contracts/index.ts
31568
- var c41 = initContract();
31569
- var contract = c41.router({
32364
+ var c42 = initContract();
32365
+ var contract = c42.router({
31570
32366
  v1: v1Contract
31571
32367
  });
31572
32368
 
@@ -31615,7 +32411,7 @@ var ComplianceRegimeValues = [
31615
32411
  var ComplianceRegime = external_exports.enum(ComplianceRegimeValues);
31616
32412
 
31617
32413
  // ../../packages/api-contracts/src/contracts/v1/agent-session-internal.ts
31618
- var c42 = initContract();
32414
+ var c43 = initContract();
31619
32415
  var AgentSessionInternalParamsSchema = external_exports.object({
31620
32416
  agentSessionId: external_exports.uuid()
31621
32417
  });
@@ -31729,8 +32525,13 @@ var RecordCandidateVulnRequestSchema = external_exports.object({
31729
32525
  /** Idempotency key, stable across POST retries of one tool invocation. */
31730
32526
  sourceKey: external_exports.string().min(1).max(256),
31731
32527
  candidate: external_exports.object({
31732
- title: external_exports.string().min(1).max(200),
31733
- description: external_exports.string().min(1).max(5e3),
32528
+ // Kept in lockstep with the record_candidate_vuln tool schema in
32529
+ // apps/agent-session-host/src/recording.ts — a tool cap looser than this
32530
+ // one turns a model-visible limit into an opaque API rejection. title 250 /
32531
+ // description 6000 were raised from 200/5000, which observed usage had
32532
+ // saturated (median description 4154 of 5000).
32533
+ title: external_exports.string().min(1).max(250),
32534
+ description: external_exports.string().min(1).max(6e3),
31734
32535
  attackChain: external_exports.string().min(1).max(5e3),
31735
32536
  exploitTechnique: external_exports.string().min(1).max(5e3),
31736
32537
  prerequisiteAccess: external_exports.string().min(1).max(5e3).optional(),
@@ -31773,7 +32574,7 @@ var RecordProductContextResponseSchema = external_exports.object({
31773
32574
  disputed: external_exports.number().int().nonnegative(),
31774
32575
  rejected: external_exports.number().int().nonnegative()
31775
32576
  });
31776
- var ThreatModelStatement = external_exports.string().min(1).max(8e3);
32577
+ var ThreatModelStatement = external_exports.string().min(1).max(2e3);
31777
32578
  var RepoScopedThreatItem = external_exports.object({
31778
32579
  repoAlias: external_exports.string().min(1).max(200),
31779
32580
  statement: ThreatModelStatement
@@ -31924,17 +32725,71 @@ var RecordValidationResultRequestSchema = external_exports.object({
31924
32725
  * customerFacing labels. The api stores them on the `PentestExploitArtifact` row (caption →
31925
32726
  * `description`) and links them to the promoted finding.
31926
32727
  */
31927
- evidence: external_exports.array(RecordValidationEvidenceItemSchema).max(32).optional()
32728
+ evidence: external_exports.array(RecordValidationEvidenceItemSchema).max(32).optional(),
32729
+ /**
32730
+ * PROVEN-only. The EXISTING finding this proof belongs to — the same root-cause
32731
+ * weakness, already reported by an earlier session. Set it INSTEAD of promoting:
32732
+ * the covered candidate(s) attach to that finding and no second finding is created.
32733
+ *
32734
+ * When set, `prose` is that finding's COMPLETE prose with this investigation's
32735
+ * addition folded in (read it with get_finding first), not a fragment — the api
32736
+ * writes the prose columns verbatim. Scoring fields (cvssVector / cweIds / category /
32737
+ * owaspCategory / severity) are REJECTED on a bind rather than silently dropped; a
32738
+ * second proof of a known weakness must not re-score a finding staff already
32739
+ * triaged. `repositories` IS applied, additively.
32740
+ */
32741
+ bindToPentestFindingId: external_exports.uuid().optional(),
32742
+ /**
32743
+ * The bound finding's `updatedAt` as returned by get_finding. Optimistic lock: a
32744
+ * staff edit landing between the read and this call returns BIND_STALE_FINDING
32745
+ * instead of silently clobbering their text. Required whenever
32746
+ * `bindToPentestFindingId` is set AND `prose` is supplied.
32747
+ */
32748
+ bindExpectedUpdatedAt: external_exports.coerce.date().optional()
31928
32749
  }).refine((body) => (body.hypothesisIds?.length ?? 0) > 0 || body.hypothesisId !== void 0, {
31929
32750
  message: "record_validation_result requires hypothesisIds (or the legacy hypothesisId)",
31930
32751
  path: ["hypothesisIds"]
31931
- });
32752
+ }).refine(
32753
+ (body) => body.bindToPentestFindingId === void 0 || body.prose == null || body.bindExpectedUpdatedAt !== void 0,
32754
+ {
32755
+ message: "bindExpectedUpdatedAt is required when binding with prose (pass the finding updatedAt from get_finding)",
32756
+ path: ["bindExpectedUpdatedAt"]
32757
+ }
32758
+ );
31932
32759
  var RecordValidationResultResponseSchema = external_exports.object({
31933
32760
  attemptId: external_exports.uuid(),
31934
32761
  verdict: ValidationVerdict,
31935
32762
  /** True when a PentestFinding was created from this PROVEN result. */
31936
32763
  promoted: external_exports.boolean(),
31937
- findingDisplayId: external_exports.number().int().positive().optional(),
32764
+ /**
32765
+ * The promoted finding's customer-facing number, or NULL when it has none yet — a
32766
+ * freshly promoted finding is UNPUBLISHED, and publishing is what assigns the number.
32767
+ * `promoted: true` with `findingDisplayId: null` means "finding created, not yet
32768
+ * numbered".
32769
+ */
32770
+ findingDisplayId: external_exports.number().int().positive().nullish(),
32771
+ /**
32772
+ * The promoted finding's durable id, present whenever `promoted` is true. This is the
32773
+ * handle the agent acts on — and once numbering moves to publish it is the ONLY handle
32774
+ * a freshly promoted finding has, since `findingDisplayId` is null until then.
32775
+ */
32776
+ pentestFindingId: external_exports.uuid().optional(),
32777
+ /**
32778
+ * True when the verdict attached to an EXISTING finding (`bindToPentestFindingId`)
32779
+ * instead of promoting a new one. `promoted` stays false in that case, while
32780
+ * `pentestFindingId` carries the bound finding — so the agent still holds the handle
32781
+ * for the finding it just acted on.
32782
+ */
32783
+ bound: external_exports.boolean().optional(),
32784
+ /**
32785
+ * How the bind's addition landed. REPORT_PROSE = merged into the finding's report prose
32786
+ * and its repository links (it was unpublished). FINDING_NOTE = filed as a staff note
32787
+ * instead, because the finding is PUBLISHED: nothing customer-facing on it is
32788
+ * agent-editable, which covers the prose AND any cited repository locations. NONE =
32789
+ * there was nothing to apply — no prose and no citations, or a replay, whose original
32790
+ * call already applied whatever it carried. Present whenever `bound`.
32791
+ */
32792
+ deltaApplied: external_exports.enum(["REPORT_PROSE", "FINDING_NOTE", "NONE"]).optional(),
31938
32793
  /**
31939
32794
  * The candidate(s) the verdict was bound to (PR A) — each as {displayId, title} so the
31940
32795
  * agent can notice a mis-tag by comparing the echoed TITLE against what it actually
@@ -31999,6 +32854,55 @@ var AgentSessionLoginCodeResponseSchema = external_exports.object({
31999
32854
  value: external_exports.string().optional(),
32000
32855
  receivedAt: external_exports.coerce.date().optional()
32001
32856
  });
32857
+ var AgentSessionListInboxMessagesRequestSchema = external_exports.object({
32858
+ runVersion: external_exports.number().int().nonnegative(),
32859
+ /** Which account's inbox (by `Credential.label`); omit if exactly one is readable. */
32860
+ credentialLabel: external_exports.string().min(1).max(200).optional(),
32861
+ /** Max messages, newest first. Clamped server-side. */
32862
+ limit: external_exports.number().int().positive().max(50).optional()
32863
+ });
32864
+ var InboxMessageSummarySchema = external_exports.object({
32865
+ messageId: external_exports.string().min(1).max(500),
32866
+ from: external_exports.string().max(500),
32867
+ subject: external_exports.string().max(1e3).nullable(),
32868
+ /** Provider-generated short body preview. */
32869
+ preview: external_exports.string().max(2e3).nullable(),
32870
+ receivedAt: external_exports.coerce.date(),
32871
+ unread: external_exports.boolean()
32872
+ });
32873
+ var AgentSessionListInboxMessagesResponseSchema = external_exports.object({
32874
+ /** The account whose inbox was read, echoed so the agent can pass it to get_email. */
32875
+ credentialLabel: external_exports.string().max(200),
32876
+ messages: external_exports.array(InboxMessageSummarySchema)
32877
+ });
32878
+ var AgentSessionGetInboxMessageRequestSchema = external_exports.object({
32879
+ runVersion: external_exports.number().int().nonnegative(),
32880
+ /** Message id from a `list_emails` result. */
32881
+ messageId: external_exports.string().min(1).max(500),
32882
+ credentialLabel: external_exports.string().min(1).max(200).optional()
32883
+ });
32884
+ var AgentSessionGetInboxMessageResponseSchema = external_exports.object({
32885
+ credentialLabel: external_exports.string().max(200),
32886
+ message: InboxMessageSummarySchema.extend({
32887
+ /**
32888
+ * The body as plain text, prompt-injection-neutralized and length-capped
32889
+ * ({@link INBOX_MESSAGE_BODY_MAX_CHARS}). Deliberately NOT run through the
32890
+ * credential redactor — that would rewrite the `?token=…` in a magic link,
32891
+ * which is the artifact the agent needs.
32892
+ */
32893
+ body: external_exports.string(),
32894
+ /** True when the body was truncated at the cap. */
32895
+ truncated: external_exports.boolean()
32896
+ }),
32897
+ /**
32898
+ * http(s) links extracted from the message in priority order (hrefs first,
32899
+ * then inline text URLs), pre-computed so the agent doesn't have to parse the
32900
+ * body. Same extraction and same URL-safety guards the login flow used.
32901
+ */
32902
+ links: external_exports.array(external_exports.string()),
32903
+ /** Detected one-time code, if the message carried one. */
32904
+ otp: external_exports.string().nullable()
32905
+ });
32002
32906
  var RenewCloudCredentialsRequestSchema = external_exports.object({
32003
32907
  runVersion: external_exports.number().int().nonnegative()
32004
32908
  });
@@ -32014,6 +32918,42 @@ var RenewCloudCredentialsResponseSchema = external_exports.object({
32014
32918
  accessToken: external_exports.string().min(1)
32015
32919
  }).nullable()
32016
32920
  });
32921
+ var ConvexReconRequestSchema = external_exports.object({
32922
+ runVersion: external_exports.number().int().nonnegative()
32923
+ });
32924
+ var ConvexReconInventorySchema = external_exports.discriminatedUnion("status", [
32925
+ /**
32926
+ * Convex's `apiSpec` payload, forwarded verbatim: per function the identifier,
32927
+ * type, visibility (public/internal) and argument validators, plus httpRoutes
32928
+ * and cronSpecs. Deliberately `unknown` — it describes the customer's own
32929
+ * deployment, not a Trace domain object, so pinning its shape would break when
32930
+ * Convex extends it.
32931
+ */
32932
+ external_exports.object({ status: external_exports.literal("ok"), functions: external_exports.unknown() }),
32933
+ /** Convex answered HTTP 200 with `status: "error"` — a function-level error. */
32934
+ external_exports.object({ status: external_exports.literal("error"), message: external_exports.string() }),
32935
+ /** Convex 401/403 — the grant is gone; the row was flipped ACCESS_DENIED. */
32936
+ external_exports.object({ status: external_exports.literal("access_denied") }),
32937
+ /** Convex 429/5xx/timeout, a decrypt failure, or the response budget spent. */
32938
+ external_exports.object({ status: external_exports.literal("unavailable") }),
32939
+ /** Over the per-deployment byte cap. NEVER truncated — a half inventory read as
32940
+ * complete is worse than an explicit gap. */
32941
+ external_exports.object({ status: external_exports.literal("too_large"), bytes: external_exports.number().int().nonnegative() })
32942
+ ]);
32943
+ var ConvexReconDeploymentSchema = external_exports.object({
32944
+ deploymentName: external_exports.string().min(1),
32945
+ /** Convex's DeploymentType verbatim (dev/prod/preview/custom) — never an enum. */
32946
+ deploymentType: external_exports.string().min(1),
32947
+ /** Region-qualified, from the stored row. NEVER synthesised from deploymentName. */
32948
+ convexCloudUrl: external_exports.string().min(1),
32949
+ /** Null when get_canonical_urls failed at connect; the host is not derivable. */
32950
+ convexSiteUrl: external_exports.string().min(1).nullable(),
32951
+ inventory: ConvexReconInventorySchema
32952
+ });
32953
+ var ConvexReconResponseSchema = external_exports.object({
32954
+ /** Empty when the org has no connected, key-bearing Convex deployment. */
32955
+ deployments: external_exports.array(ConvexReconDeploymentSchema)
32956
+ });
32017
32957
  var AgentSessionGetSessionStateRequestSchema = external_exports.object({
32018
32958
  runVersion: external_exports.number().int().nonnegative(),
32019
32959
  /** Which SESSION_STATE credential to fetch (from the non-secret env reference). */
@@ -32154,6 +33094,32 @@ var OperatorRecordFindingRetestRequestSchema = CreatePentestFindingRetestRequest
32154
33094
  });
32155
33095
  }
32156
33096
  });
33097
+ var OperatorRetestAckSchema = OperatorFindingReadSchema.extend({
33098
+ retestId: external_exports.uuid()
33099
+ });
33100
+ var OperatorEditFindingRetestRequestSchema = external_exports.object({
33101
+ runVersion: operatorRunVersion,
33102
+ reason: operatorReason,
33103
+ retestId: external_exports.uuid(),
33104
+ outcome: RetestOutcome.optional(),
33105
+ notes: external_exports.string().trim().min(1).max(1e4).optional(),
33106
+ verification: external_exports.string().min(1).max(8e3).optional()
33107
+ }).superRefine((v2, ctx) => {
33108
+ if (v2.outcome === void 0 && v2.notes === void 0) {
33109
+ ctx.addIssue({
33110
+ code: "custom",
33111
+ message: "provide outcome, notes, or both \u2014 an edit must change something",
33112
+ path: ["notes"]
33113
+ });
33114
+ }
33115
+ if (v2.verification !== void 0 && v2.outcome !== "FIXED") {
33116
+ ctx.addIssue({
33117
+ code: "custom",
33118
+ message: "verification report prose is only allowed when outcome is FIXED",
33119
+ path: ["verification"]
33120
+ });
33121
+ }
33122
+ });
32157
33123
  var OperatorMarkRemediationReportedRequestSchema = MarkPentestFindingRemediationReportedRequestSchema.extend({
32158
33124
  runVersion: operatorRunVersion,
32159
33125
  reason: operatorReason,
@@ -32300,7 +33266,7 @@ var ReportUpdateScopeRequestSchema = external_exports.object({
32300
33266
  expectedUpdatedAt: reportExpectedUpdatedAt,
32301
33267
  rows: external_exports.array(ScopeRowSchema).max(100)
32302
33268
  });
32303
- var agentSessionInternalContract = c42.router({
33269
+ var agentSessionInternalContract = c43.router({
32304
33270
  postEvents: {
32305
33271
  method: "POST",
32306
33272
  path: "/internal/v1/agent-sessions/:agentSessionId/events",
@@ -32457,6 +33423,10 @@ var agentSessionInternalContract = c42.router({
32457
33423
  200: ReattributeValidationResultResponseSchema,
32458
33424
  400: ErrorResponseSchema,
32459
33425
  401: ErrorResponseSchema,
33426
+ // The attempt or the target hypothesis does not exist in this pentest — in practice a
33427
+ // mistyped id in the agent's call. Declared because it is the likeliest failure here and
33428
+ // a reader who assumes it cannot 404 mis-diagnoses the retry loop it causes.
33429
+ 404: ErrorResponseSchema,
32460
33430
  // runVersion/status guard, stage mismatch, R-not-approved, R-already-promoted,
32461
33431
  // or attempt-not-in-run.
32462
33432
  409: ErrorResponseSchema,
@@ -32478,7 +33448,53 @@ var agentSessionInternalContract = c42.router({
32478
33448
  409: ErrorResponseSchema,
32479
33449
  // No MAGIC_AUTH credential on the pentest's environments.
32480
33450
  404: ErrorResponseSchema,
32481
- 500: ErrorResponseSchema
33451
+ 500: ErrorResponseSchema,
33452
+ // Mail provider unavailable — RETRYABLE, and distinct from 500 so the host
33453
+ // can tell "we could not look" from "this will never work".
33454
+ 503: ErrorResponseSchema
33455
+ }
33456
+ },
33457
+ // list_emails: newest-first metadata for the pentest's readable Trace-managed
33458
+ // inbox. Same resolution + ownership gate as login-code (org + pentest
33459
+ // environments + provider-side inbox tenancy); no bodies cross the wire here.
33460
+ listInboxMessages: {
33461
+ method: "POST",
33462
+ path: "/internal/v1/agent-sessions/:agentSessionId/inbox-messages",
33463
+ pathParams: AgentSessionInternalParamsSchema,
33464
+ body: AgentSessionListInboxMessagesRequestSchema,
33465
+ responses: {
33466
+ 200: AgentSessionListInboxMessagesResponseSchema,
33467
+ // Ambiguous credentialLabel (several readable inboxes, none named).
33468
+ 400: ErrorResponseSchema,
33469
+ 401: ErrorResponseSchema,
33470
+ 409: ErrorResponseSchema,
33471
+ // No readable Trace-managed inbox on the pentest's environments, or the
33472
+ // resolved address is not owned by this run's organization.
33473
+ 404: ErrorResponseSchema,
33474
+ 500: ErrorResponseSchema,
33475
+ // Mail provider unavailable — RETRYABLE, and distinct from 500 so the host
33476
+ // can tell "we could not look" from "this will never work".
33477
+ 503: ErrorResponseSchema
33478
+ }
33479
+ },
33480
+ // get_email: one message from that inbox, body included. The body is
33481
+ // third-party text — the api neutralizes and length-caps it before it can
33482
+ // reach the agent's context.
33483
+ getInboxMessage: {
33484
+ method: "POST",
33485
+ path: "/internal/v1/agent-sessions/:agentSessionId/inbox-message",
33486
+ pathParams: AgentSessionInternalParamsSchema,
33487
+ body: AgentSessionGetInboxMessageRequestSchema,
33488
+ responses: {
33489
+ 200: AgentSessionGetInboxMessageResponseSchema,
33490
+ 400: ErrorResponseSchema,
33491
+ 401: ErrorResponseSchema,
33492
+ 409: ErrorResponseSchema,
33493
+ // Unknown inbox (as above) OR the message id is not in that inbox.
33494
+ 404: ErrorResponseSchema,
33495
+ 500: ErrorResponseSchema,
33496
+ // Mail provider unavailable — RETRYABLE (see listInboxMessages).
33497
+ 503: ErrorResponseSchema
32482
33498
  }
32483
33499
  },
32484
33500
  // get_session_state (SESSION_STATE, spec §2/§3): api-mediated JIT fetch — the api
@@ -32518,6 +33534,26 @@ var agentSessionInternalContract = c42.router({
32518
33534
  500: ErrorResponseSchema
32519
33535
  }
32520
33536
  },
33537
+ // get_convex_deployments (Convex integration PR 3): the read-only broker. Every
33538
+ // connected Convex deployment for the run's OWN org, each with its function
33539
+ // inventory. No request identifier at all — see the schema block above. No 404:
33540
+ // there is no agent-supplied id to miss. No 503: a Convex outage is the
33541
+ // per-deployment `unavailable` status, so one bad deployment never fails the rest.
33542
+ convexRecon: {
33543
+ method: "POST",
33544
+ path: "/internal/v1/agent-sessions/:agentSessionId/convex/recon",
33545
+ pathParams: AgentSessionInternalParamsSchema,
33546
+ body: ConvexReconRequestSchema,
33547
+ responses: {
33548
+ 200: ConvexReconResponseSchema,
33549
+ 400: ErrorResponseSchema,
33550
+ 401: ErrorResponseSchema,
33551
+ // Non-credentialed session type — this run may never reach customer clouds.
33552
+ 403: ErrorResponseSchema,
33553
+ 409: ErrorResponseSchema,
33554
+ 500: ErrorResponseSchema
33555
+ }
33556
+ },
32521
33557
  // Operator reads (operator-session PR 1 §2b). All run-token authed; org+pentest
32522
33558
  // derived from the session row. Detail ops 404 when the id is not in this pentest.
32523
33559
  getPentestContext: {
@@ -32714,7 +33750,21 @@ var agentSessionInternalContract = c42.router({
32714
33750
  pathParams: AgentSessionInternalParamsSchema,
32715
33751
  body: OperatorRecordFindingRetestRequestSchema,
32716
33752
  responses: {
32717
- 200: OperatorFindingReadSchema,
33753
+ 200: OperatorRetestAckSchema,
33754
+ 400: ErrorResponseSchema,
33755
+ 401: ErrorResponseSchema,
33756
+ 404: ErrorResponseSchema,
33757
+ 409: ErrorResponseSchema,
33758
+ 500: ErrorResponseSchema
33759
+ }
33760
+ },
33761
+ editFindingRetest: {
33762
+ method: "POST",
33763
+ path: "/internal/v1/agent-sessions/:agentSessionId/operator/findings/retest/edit",
33764
+ pathParams: AgentSessionInternalParamsSchema,
33765
+ body: OperatorEditFindingRetestRequestSchema,
33766
+ responses: {
33767
+ 200: OperatorRetestAckSchema,
32718
33768
  400: ErrorResponseSchema,
32719
33769
  401: ErrorResponseSchema,
32720
33770
  404: ErrorResponseSchema,
@@ -32916,8 +33966,8 @@ var GitHubWebhookResponseSchema = external_exports.object({
32916
33966
  });
32917
33967
 
32918
33968
  // ../../packages/api-contracts/src/contracts/webhooks/github.ts
32919
- var c43 = initContract();
32920
- var githubWebhookContract = c43.router(
33969
+ var c44 = initContract();
33970
+ var githubWebhookContract = c44.router(
32921
33971
  {
32922
33972
  handleWebhook: {
32923
33973
  method: "POST",
@@ -32935,7 +33985,7 @@ var githubWebhookContract = c43.router(
32935
33985
  pathPrefix: "/webhooks"
32936
33986
  }
32937
33987
  );
32938
- var githubReadonlyWebhookContract = c43.router(
33988
+ var githubReadonlyWebhookContract = c44.router(
32939
33989
  {
32940
33990
  handleWebhook: {
32941
33991
  method: "POST",
@@ -32955,9 +34005,9 @@ var githubReadonlyWebhookContract = c43.router(
32955
34005
  );
32956
34006
 
32957
34007
  // ../../packages/api-contracts/src/contracts/webhooks/linear.ts
32958
- var c44 = initContract();
34008
+ var c45 = initContract();
32959
34009
  var LinearWebhookResponseSchema = external_exports.object({ ok: external_exports.boolean() });
32960
- var linearWebhookContract = c44.router(
34010
+ var linearWebhookContract = c45.router(
32961
34011
  {
32962
34012
  handleWebhook: {
32963
34013
  method: "POST",
@@ -32977,7 +34027,7 @@ var linearWebhookContract = c44.router(
32977
34027
  );
32978
34028
 
32979
34029
  // ../../packages/api-contracts/src/contracts/forge/bitbucket.ts
32980
- var c45 = initContract();
34030
+ var c46 = initContract();
32981
34031
  var InstallEventSchema = external_exports.object({
32982
34032
  eventType: external_exports.enum(["avi:forge:installed:app", "avi:forge:upgraded:app"]),
32983
34033
  context: external_exports.object({ cloudId: external_exports.string().min(1) }).loose(),
@@ -33006,7 +34056,7 @@ var ForgePairingCodeResponseSchema = external_exports.discriminatedUnion("state"
33006
34056
  expiresInSeconds: external_exports.number().int()
33007
34057
  })
33008
34058
  ]);
33009
- var forgeBitbucketContract = c45.router(
34059
+ var forgeBitbucketContract = c46.router(
33010
34060
  {
33011
34061
  lifecycle: {
33012
34062
  method: "POST",
@@ -33053,8 +34103,8 @@ var HealthResponseSchema = external_exports.object({
33053
34103
  });
33054
34104
 
33055
34105
  // ../../packages/api-contracts/src/contracts/health.ts
33056
- var c46 = initContract();
33057
- var healthContract = c46.router({
34106
+ var c47 = initContract();
34107
+ var healthContract = c47.router({
33058
34108
  getHealth: {
33059
34109
  method: "GET",
33060
34110
  path: "/health",
@@ -33066,8 +34116,8 @@ var healthContract = c46.router({
33066
34116
  });
33067
34117
 
33068
34118
  // ../../packages/api-contracts/src/contracts/v1/vulnerability-share.ts
33069
- var c47 = initContract();
33070
- var vulnerabilityShareContract = c47.router(
34119
+ var c48 = initContract();
34120
+ var vulnerabilityShareContract = c48.router(
33071
34121
  {
33072
34122
  resolveShareLink: {
33073
34123
  method: "GET",
@@ -33154,8 +34204,8 @@ var UpdatePrVulnerabilityRequestSchema = external_exports.object({
33154
34204
  });
33155
34205
 
33156
34206
  // ../../packages/api-contracts/src/contracts/v1/pr-vulnerability.ts
33157
- var c48 = initContract();
33158
- var prVulnerabilityContract = c48.router(
34207
+ var c49 = initContract();
34208
+ var prVulnerabilityContract = c49.router(
33159
34209
  {
33160
34210
  listPrVulnerabilities: {
33161
34211
  method: "GET",
@@ -35541,6 +36591,7 @@ var APPLICATION_TYPE_BY_INPUT = {
35541
36591
  web: "WEB",
35542
36592
  cli: "CLI",
35543
36593
  mobile: "MOBILE",
36594
+ desktop: "DESKTOP",
35544
36595
  "api-service": "API_SERVICE",
35545
36596
  api_service: "API_SERVICE"
35546
36597
  };
@@ -35707,12 +36758,12 @@ function buildApplicationsTable(apps) {
35707
36758
  repos: String(a.repositories.length),
35708
36759
  url: a.applicationUrl ? sanitizeDisplay(a.applicationUrl) : "\u2014"
35709
36760
  }));
35710
- const idWidth = Math.max(2, ...cells.map((c50) => c50.id.length));
35711
- const nameWidth = Math.max(4, ...cells.map((c50) => c50.name.length));
35712
- const typeWidth = Math.max(4, ...cells.map((c50) => c50.type.length));
36761
+ const idWidth = Math.max(2, ...cells.map((c51) => c51.id.length));
36762
+ const nameWidth = Math.max(4, ...cells.map((c51) => c51.name.length));
36763
+ const typeWidth = Math.max(4, ...cells.map((c51) => c51.type.length));
35713
36764
  const header = `${"ID".padEnd(idWidth)} ${"NAME".padEnd(nameWidth)} ${"TYPE".padEnd(typeWidth)} REPOS URL`;
35714
36765
  const rows = cells.map(
35715
- (c50) => `${c50.id.padEnd(idWidth)} ${c50.name.padEnd(nameWidth)} ${c50.type.padEnd(typeWidth)} ${c50.repos.padStart(5)} ${c50.url}`
36766
+ (c51) => `${c51.id.padEnd(idWidth)} ${c51.name.padEnd(nameWidth)} ${c51.type.padEnd(typeWidth)} ${c51.repos.padStart(5)} ${c51.url}`
35716
36767
  );
35717
36768
  return { header, rows };
35718
36769
  }
@@ -35732,12 +36783,12 @@ function buildApplicationRepositoriesTable(links) {
35732
36783
  path: l.repositoryPath ? sanitizeDisplay(l.repositoryPath) : "\u2014",
35733
36784
  status: formatStatus(l.repositoryStatus)
35734
36785
  }));
35735
- const nameWidth = Math.max(10, ...cells.map((c50) => c50.name.length));
35736
- const roleWidth = Math.max(4, ...cells.map((c50) => c50.role.length));
35737
- const pathWidth = Math.max(4, ...cells.map((c50) => c50.path.length));
36786
+ const nameWidth = Math.max(10, ...cells.map((c51) => c51.name.length));
36787
+ const roleWidth = Math.max(4, ...cells.map((c51) => c51.role.length));
36788
+ const pathWidth = Math.max(4, ...cells.map((c51) => c51.path.length));
35738
36789
  const header = `${"REPOSITORY".padEnd(nameWidth)} SCAN ${"ROLE".padEnd(roleWidth)} ${"PATH".padEnd(pathWidth)} STATUS`;
35739
36790
  const rows = cells.map(
35740
- (c50) => `${c50.name.padEnd(nameWidth)} ${c50.scan.padEnd(4)} ${c50.role.padEnd(roleWidth)} ${c50.path.padEnd(pathWidth)} ${c50.status}`
36791
+ (c51) => `${c51.name.padEnd(nameWidth)} ${c51.scan.padEnd(4)} ${c51.role.padEnd(roleWidth)} ${c51.path.padEnd(pathWidth)} ${c51.status}`
35741
36792
  );
35742
36793
  return { header, rows };
35743
36794
  }
@@ -35999,7 +37050,7 @@ async function runDelete(applicationId, options) {
35999
37050
  console.log(colorSuccess(`Deleted application ${applicationId}`));
36000
37051
  }
36001
37052
  function createCreateCommand() {
36002
- return new Command("create").description("Create a new application").argument("<name>", "Application name").option("--type <type>", "Application type: web, cli, mobile, api-service", "web").option("--url <url>", "Application URL (required for web/api-service)").option("--repo <id-or-url>", "Link a repository at creation (UUID, URL, or owner/repo)").option("--repo-path <path>", "Path within the linked repository").option("--help-center-url <url>", "Help center URL").option("--docs-url <url>", "Documentation URL").option("-o, --output <format>", "Output format: table, json, tsv (default: table)", "table").addHelpText(
37053
+ return new Command("create").description("Create a new application").argument("<name>", "Application name").option("--type <type>", "Application type: web, cli, mobile, desktop, api-service", "web").option("--url <url>", "Application URL (required for web/api-service)").option("--repo <id-or-url>", "Link a repository at creation (UUID, URL, or owner/repo)").option("--repo-path <path>", "Path within the linked repository").option("--help-center-url <url>", "Help center URL").option("--docs-url <url>", "Documentation URL").option("-o, --output <format>", "Output format: table, json, tsv (default: table)", "table").addHelpText(
36003
37054
  "after",
36004
37055
  `
36005
37056
  Examples:
@@ -36418,6 +37469,21 @@ function d(r, t, s) {
36418
37469
  const e2 = r + t, i = Math.max(s.length - 1, 0), n = e2 < 0 ? i : e2 > i ? 0 : e2;
36419
37470
  return s[n].disabled ? d(n, t < 0 ? -1 : 1, s) : n;
36420
37471
  }
37472
+ function I(r, t, s, e2) {
37473
+ const i = e2.split(`
37474
+ `);
37475
+ let n = 0, o = r;
37476
+ for (const a of i) {
37477
+ if (o <= a.length) break;
37478
+ o -= a.length + 1, n++;
37479
+ }
37480
+ for (n = Math.max(0, Math.min(i.length - 1, n + s)), o = Math.min(o, i[n].length) + t; o < 0 && n > 0; ) n--, o += i[n].length + 1;
37481
+ for (; o > i[n].length && n < i.length - 1; ) o -= i[n].length + 1, n++;
37482
+ o = Math.max(0, Math.min(i[n].length, o));
37483
+ let u = 0;
37484
+ for (let a = 0; a < n; a++) u += i[a].length + 1;
37485
+ return u + o;
37486
+ }
36421
37487
  var G = ["up", "down", "left", "right", "space", "enter", "cancel"];
36422
37488
  var K = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"];
36423
37489
  var h = { actions: new Set(G), aliases: /* @__PURE__ */ new Map([["k", "up"], ["j", "down"], ["h", "left"], ["l", "right"], ["", "cancel"], ["escape", "cancel"]]), messages: { cancel: "Canceled", error: "Something went wrong" }, withGuide: true, date: { monthNames: [...K], messages: { required: "Please enter a valid date", invalidMonth: "There are only 12 months in a year", invalidDay: (r, t) => `There are only ${r} days in ${t}`, afterMin: (r) => `Date must be on or after ${r.toISOString().slice(0, 10)}`, beforeMax: (r) => `Date must be on or before ${r.toISOString().slice(0, 10)}` } } };
@@ -36579,6 +37645,145 @@ var p = class {
36579
37645
  }
36580
37646
  }
36581
37647
  };
37648
+ var X = class extends p {
37649
+ get cursor() {
37650
+ return this.value ? 0 : 1;
37651
+ }
37652
+ get _value() {
37653
+ return this.cursor === 0;
37654
+ }
37655
+ constructor(t) {
37656
+ super(t, false), this.value = !!t.initialValue, this.on("userInput", () => {
37657
+ this.value = this._value;
37658
+ }), this.on("confirm", (s) => {
37659
+ this.output.write(import_sisteransi.cursor.move(0, -1)), this.value = s, this.state = "submit", this.close();
37660
+ }), this.on("cursor", () => {
37661
+ this.value = !this.value;
37662
+ });
37663
+ }
37664
+ };
37665
+ var rt = class extends p {
37666
+ #s = false;
37667
+ #r;
37668
+ focused = "editor";
37669
+ get userInputWithCursor() {
37670
+ if (this.state === "submit") return this.userInput;
37671
+ const t = this.userInput;
37672
+ if (this.cursor >= t.length) return `${t}\u2588`;
37673
+ const s = t.slice(0, this.cursor), e2 = t[this.cursor], i = t.slice(this.cursor + 1);
37674
+ return e2 === `
37675
+ ` ? `${s}\u2588
37676
+ ${i}` : `${s}${v("inverse", e2)}${i}`;
37677
+ }
37678
+ get cursor() {
37679
+ return this._cursor;
37680
+ }
37681
+ #t(t) {
37682
+ if (this.userInput.length === 0) {
37683
+ this._setUserInput(t);
37684
+ return;
37685
+ }
37686
+ this._setUserInput(this.userInput.slice(0, this.cursor) + t + this.userInput.slice(this.cursor));
37687
+ }
37688
+ #n(t) {
37689
+ const s = this.value ?? "";
37690
+ switch (t) {
37691
+ case "up":
37692
+ this._cursor = I(this._cursor, 0, -1, s);
37693
+ return;
37694
+ case "down":
37695
+ this._cursor = I(this._cursor, 0, 1, s);
37696
+ return;
37697
+ case "left":
37698
+ this._cursor = I(this._cursor, -1, 0, s);
37699
+ return;
37700
+ case "right":
37701
+ this._cursor = I(this._cursor, 1, 0, s);
37702
+ return;
37703
+ }
37704
+ }
37705
+ _shouldSubmit(t, s) {
37706
+ if (this.#r) return this.focused === "submit" ? true : (this.#t(`
37707
+ `), this._cursor++, false);
37708
+ const e2 = this.#s;
37709
+ return this.#s = true, e2 ? (this.userInput[this.cursor - 1] === `
37710
+ ` && (this._setUserInput(this.userInput.slice(0, this.cursor - 1) + this.userInput.slice(this.cursor)), this._cursor--), true) : (this.#t(`
37711
+ `), this._cursor++, false);
37712
+ }
37713
+ constructor(t) {
37714
+ super(t, false), this.#r = t.showSubmit ?? false, this.on("key", (s, e2) => {
37715
+ if (e2?.name && h.actions.has(e2.name)) {
37716
+ this.#n(e2.name);
37717
+ return;
37718
+ }
37719
+ if (s === " " && this.#r) {
37720
+ this.focused = this.focused === "editor" ? "submit" : "editor";
37721
+ return;
37722
+ }
37723
+ if (e2?.name !== "return") {
37724
+ if (this.#s = false, e2?.name === "backspace" && this.cursor > 0) {
37725
+ this._setUserInput(this.userInput.slice(0, this.cursor - 1) + this.userInput.slice(this.cursor)), this._cursor--;
37726
+ return;
37727
+ }
37728
+ if (e2?.name === "delete" && this.cursor < this.userInput.length) {
37729
+ this._setUserInput(this.userInput.slice(0, this.cursor) + this.userInput.slice(this.cursor + 1));
37730
+ return;
37731
+ }
37732
+ s && (this.#r && this.focused === "submit" && (this.focused = "editor"), this.#t(s ?? ""), this._cursor++);
37733
+ }
37734
+ }), this.on("userInput", (s) => {
37735
+ this._setValue(s);
37736
+ }), this.on("finalize", () => {
37737
+ this.value || (this.value = t.defaultValue), this.value === void 0 && (this.value = "");
37738
+ });
37739
+ }
37740
+ };
37741
+ var nt = class extends p {
37742
+ options;
37743
+ cursor = 0;
37744
+ get _value() {
37745
+ return this.options[this.cursor].value;
37746
+ }
37747
+ get _enabledOptions() {
37748
+ return this.options.filter((t) => t.disabled !== true);
37749
+ }
37750
+ toggleAll() {
37751
+ const t = this._enabledOptions, s = this.value !== void 0 && this.value.length === t.length;
37752
+ this.value = s ? [] : t.map((e2) => e2.value);
37753
+ }
37754
+ toggleInvert() {
37755
+ const t = this.value;
37756
+ if (!t) return;
37757
+ const s = this._enabledOptions.filter((e2) => !t.includes(e2.value));
37758
+ this.value = s.map((e2) => e2.value);
37759
+ }
37760
+ toggleValue() {
37761
+ this.value === void 0 && (this.value = []);
37762
+ const t = this.value.includes(this._value);
37763
+ this.value = t ? this.value.filter((s) => s !== this._value) : [...this.value, this._value];
37764
+ }
37765
+ constructor(t) {
37766
+ super(t, false), this.options = t.options, this.value = [...t.initialValues ?? []];
37767
+ const s = Math.max(this.options.findIndex(({ value: e2 }) => e2 === t.cursorAt), 0);
37768
+ this.cursor = this.options[s].disabled ? d(s, 1, this.options) : s, this.on("key", (e2) => {
37769
+ e2 === "a" && this.toggleAll(), e2 === "i" && this.toggleInvert();
37770
+ }), this.on("cursor", (e2) => {
37771
+ switch (e2) {
37772
+ case "left":
37773
+ case "up":
37774
+ this.cursor = d(this.cursor, -1, this.options);
37775
+ break;
37776
+ case "down":
37777
+ case "right":
37778
+ this.cursor = d(this.cursor, 1, this.options);
37779
+ break;
37780
+ case "space":
37781
+ this.toggleValue();
37782
+ break;
37783
+ }
37784
+ });
37785
+ }
37786
+ };
36582
37787
  var ot = class extends p {
36583
37788
  _mask = "\u2022";
36584
37789
  get cursor() {
@@ -36695,14 +37900,14 @@ var yt = (t) => {
36695
37900
  };
36696
37901
  var ee = (t, r, s, i, u) => {
36697
37902
  let n = r, o = 0;
36698
- for (let c50 = s; c50 < i; c50++) {
36699
- const a = t[c50];
37903
+ for (let c51 = s; c51 < i; c51++) {
37904
+ const a = t[c51];
36700
37905
  if (n = n - a.length, o++, n <= u) break;
36701
37906
  }
36702
37907
  return { lineCount: n, removals: o };
36703
37908
  };
36704
37909
  var Y2 = ({ cursor: t, options: r, style: s, output: i = process.stdout, maxItems: u = Number.POSITIVE_INFINITY, columnPadding: n = 0, rowPadding: o = 4 }) => {
36705
- const c50 = A(i) - n, a = L(i), l = e("dim", "..."), d2 = Math.max(a - o, 0), y = Math.max(Math.min(u, d2), 5);
37910
+ 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);
36706
37911
  let p2 = 0;
36707
37912
  t >= y - 3 && (p2 = Math.max(Math.min(t - y + 3, r.length - y), 0));
36708
37913
  let m = y < r.length && p2 > 0, g = y < r.length && p2 + y < r.length;
@@ -36711,7 +37916,7 @@ var Y2 = ({ cursor: t, options: r, style: s, output: i = process.stdout, maxItem
36711
37916
  m && f++, g && f++;
36712
37917
  const v2 = p2 + (m ? 1 : 0), T = S2 - (g ? 1 : 0);
36713
37918
  for (let b2 = v2; b2 < T; b2++) {
36714
- const G2 = wrapAnsi(s(r[b2], b2 === t), c50, { hard: true, trim: false }).split(`
37919
+ const G2 = wrapAnsi(s(r[b2], b2 === t), c51, { hard: true, trim: false }).split(`
36715
37920
  `);
36716
37921
  h2.push(G2), f += G2.length;
36717
37922
  }
@@ -36725,15 +37930,129 @@ var Y2 = ({ cursor: t, options: r, style: s, output: i = process.stdout, maxItem
36725
37930
  for (const b2 of h2) for (const G2 of b2) C2.push(G2);
36726
37931
  return g && C2.push(l), C2;
36727
37932
  };
37933
+ var ue = (t) => {
37934
+ const r = t.active ?? "Yes", s = t.inactive ?? "No";
37935
+ return new X({ active: r, inactive: s, signal: t.signal, input: t.input, output: t.output, initialValue: t.initialValue ?? true, render() {
37936
+ 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", $)}
37937
+ ` : ""}${o}
37938
+ `, a = this.value ? r : s;
37939
+ switch (this.state) {
37940
+ case "submit": {
37941
+ const l = i ? `${e("gray", $)} ` : "";
37942
+ return `${c51}${l}${e("dim", a)}`;
37943
+ }
37944
+ case "cancel": {
37945
+ const l = i ? `${e("gray", $)} ` : "";
37946
+ return `${c51}${l}${e(["strikethrough", "dim"], a)}${i ? `
37947
+ ${e("gray", $)}` : ""}`;
37948
+ }
37949
+ default: {
37950
+ const l = i ? `${e("cyan", $)} ` : "", d2 = i ? e("cyan", E2) : "";
37951
+ return `${c51}${l}${this.value ? `${e("green", z4)} ${r}` : `${e("dim", H)} ${e("dim", r)}`}${t.vertical ? i ? `
37952
+ ${e("cyan", $)} ` : `
37953
+ ` : ` ${e("dim", "/")} `}${this.value ? `${e("dim", H)} ${e("dim", s)}` : `${e("green", z4)} ${s}`}
37954
+ ${d2}
37955
+ `;
37956
+ }
37957
+ }
37958
+ } }).prompt();
37959
+ };
37960
+ var me = (t = "", r) => {
37961
+ const s = r?.output ?? process.stdout, i = r?.withGuide ?? h.withGuide ? `${e("gray", E2)} ` : "";
37962
+ s.write(`${i}${e("red", t)}
37963
+
37964
+ `);
37965
+ };
37966
+ var fe = (t) => new rt({ validate: t.validate, placeholder: t.placeholder, defaultValue: t.defaultValue, initialValue: t.initialValue, showSubmit: t.showSubmit, output: t.output, signal: t.signal, input: t.input, render() {
37967
+ const r = t?.withGuide ?? h.withGuide, s = `${`${r ? `${e("gray", $)}
37968
+ ` : ""}${M(this.state)} `}${t.message}
37969
+ `, i = t.placeholder ? e("inverse", t.placeholder[0]) + e("dim", t.placeholder.slice(1)) : e(["inverse", "hidden"], "_"), u = this.userInput ? this.userInputWithCursor : i, n = this.value ?? "", o = t.showSubmit ? `
37970
+ ${e(this.focused === "submit" ? "cyan" : "dim", "[ submit ]")}` : "";
37971
+ switch (this.state) {
37972
+ case "error": {
37973
+ const c51 = `${e("yellow", $)} `, a = r ? W(t.output, u, c51, void 0) : u, l = e("yellow", E2);
37974
+ return `${s}${a}
37975
+ ${l} ${e("yellow", this.error)}${o}
37976
+ `;
37977
+ }
37978
+ case "submit": {
37979
+ const c51 = `${e("gray", $)} `, a = r ? W(t.output, n, c51, void 0, (l) => e("dim", l)) : n ? e("dim", n) : "";
37980
+ return `${s}${a}`;
37981
+ }
37982
+ case "cancel": {
37983
+ const c51 = `${e("gray", $)} `, a = r ? W(t.output, n, c51, void 0, (l) => e(["strikethrough", "dim"], l)) : n ? e(["strikethrough", "dim"], n) : "";
37984
+ return `${s}${a}`;
37985
+ }
37986
+ default: {
37987
+ const c51 = r ? `${e("cyan", $)} ` : "", a = r ? e("cyan", E2) : "", l = r ? W(t.output, u, c51) : u;
37988
+ return `${s}${l}
37989
+ ${a}${o}
37990
+ `;
37991
+ }
37992
+ }
37993
+ } }).prompt();
37994
+ var Q2 = (t, r) => t.split(`
37995
+ `).map((s) => r(s)).join(`
37996
+ `);
37997
+ var ve = (t) => {
37998
+ const r = (i, u) => {
37999
+ const n = i.label ?? String(i.value);
38000
+ return u === "disabled" ? `${e("gray", J)} ${Q2(n, (o) => e(["strikethrough", "gray"], o))}${i.hint ? ` ${e("dim", `(${i.hint ?? "disabled"})`)}` : ""}` : u === "active" ? `${e("cyan", et2)} ${n}${i.hint ? ` ${e("dim", `(${i.hint})`)}` : ""}` : u === "selected" ? `${e("green", U)} ${Q2(n, (o) => e("dim", o))}${i.hint ? ` ${e("dim", `(${i.hint})`)}` : ""}` : u === "cancelled" ? `${Q2(n, (o) => e(["strikethrough", "dim"], o))}` : u === "active-selected" ? `${e("green", U)} ${n}${i.hint ? ` ${e("dim", `(${i.hint})`)}` : ""}` : u === "submitted" ? `${Q2(n, (o) => e("dim", o))}` : `${e("dim", J)} ${Q2(n, (o) => e("dim", o))}`;
38001
+ }, s = t.required ?? true;
38002
+ return new nt({ options: t.options, signal: t.signal, input: t.input, output: t.output, initialValues: t.initialValues, required: s, cursorAt: t.cursorAt, validate(i) {
38003
+ if (s && (i === void 0 || i.length === 0)) return `Please select at least one option.
38004
+ ${e("reset", e("dim", `Press ${e(["gray", "bgWhite", "inverse"], " space ")} to select, ${e("gray", e("bgWhite", e("inverse", " enter ")))} to submit`))}`;
38005
+ }, render() {
38006
+ const i = t.withGuide ?? h.withGuide, u = W(t.output, t.message, i ? `${yt(this.state)} ` : "", `${M(this.state)} `), n = `${i ? `${e("gray", $)}
38007
+ ` : ""}${u}
38008
+ `, o = this.value ?? [], c51 = (a, l) => {
38009
+ if (a.disabled) return r(a, "disabled");
38010
+ const d2 = o.includes(a.value);
38011
+ return l && d2 ? r(a, "active-selected") : d2 ? r(a, "selected") : r(a, l ? "active" : "inactive");
38012
+ };
38013
+ switch (this.state) {
38014
+ case "submit": {
38015
+ const a = this.options.filter(({ value: d2 }) => o.includes(d2)).map((d2) => r(d2, "submitted")).join(e("dim", ", ")) || e("dim", "none"), l = W(t.output, a, i ? `${e("gray", $)} ` : "");
38016
+ return `${n}${l}`;
38017
+ }
38018
+ case "cancel": {
38019
+ const a = this.options.filter(({ value: d2 }) => o.includes(d2)).map((d2) => r(d2, "cancelled")).join(e("dim", ", "));
38020
+ if (a.trim() === "") return `${n}${e("gray", $)}`;
38021
+ const l = W(t.output, a, i ? `${e("gray", $)} ` : "");
38022
+ return `${n}${l}${i ? `
38023
+ ${e("gray", $)}` : ""}`;
38024
+ }
38025
+ case "error": {
38026
+ const a = i ? `${e("yellow", $)} ` : "", l = this.error.split(`
38027
+ `).map((p2, m) => m === 0 ? `${i ? `${e("yellow", E2)} ` : ""}${e("yellow", p2)}` : ` ${p2}`).join(`
38028
+ `), d2 = n.split(`
38029
+ `).length, y = l.split(`
38030
+ `).length + 1;
38031
+ 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(`
38032
+ ${a}`)}
38033
+ ${l}
38034
+ `;
38035
+ }
38036
+ default: {
38037
+ const a = i ? `${e("cyan", $)} ` : "", l = n.split(`
38038
+ `).length, d2 = i ? 2 : 1;
38039
+ 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(`
38040
+ ${a}`)}
38041
+ ${i ? e("cyan", E2) : ""}
38042
+ `;
38043
+ }
38044
+ }
38045
+ } }).prompt();
38046
+ };
36728
38047
  var Ce = (t) => new ot({ validate: t.validate, mask: t.mask ?? Gt, signal: t.signal, input: t.input, output: t.output, render() {
36729
38048
  const r = t.withGuide ?? h.withGuide, s = `${r ? `${e("gray", $)}
36730
38049
  ` : ""}${M(this.state)} ${t.message}
36731
38050
  `, i = this.userInputWithCursor, u = this.masked;
36732
38051
  switch (this.state) {
36733
38052
  case "error": {
36734
- const n = r ? `${e("yellow", $)} ` : "", o = r ? `${e("yellow", E2)} ` : "", c50 = u ?? "";
38053
+ const n = r ? `${e("yellow", $)} ` : "", o = r ? `${e("yellow", E2)} ` : "", c51 = u ?? "";
36735
38054
  return t.clearOnError && this.clear(), `${s.trim()}
36736
- ${n}${c50}
38055
+ ${n}${c51}
36737
38056
  ${o}${e("yellow", this.error)}
36738
38057
  `;
36739
38058
  }
@@ -36781,19 +38100,19 @@ var Ee = (t) => {
36781
38100
  `;
36782
38101
  switch (this.state) {
36783
38102
  case "submit": {
36784
- const c50 = s ? `${e("gray", $)} ` : "", a = W(t.output, r(this.options[this.cursor], "selected"), c50);
38103
+ const c51 = s ? `${e("gray", $)} ` : "", a = W(t.output, r(this.options[this.cursor], "selected"), c51);
36785
38104
  return `${o}${a}`;
36786
38105
  }
36787
38106
  case "cancel": {
36788
- const c50 = s ? `${e("gray", $)} ` : "", a = W(t.output, r(this.options[this.cursor], "cancelled"), c50);
38107
+ const c51 = s ? `${e("gray", $)} ` : "", a = W(t.output, r(this.options[this.cursor], "cancelled"), c51);
36789
38108
  return `${o}${a}${s ? `
36790
38109
  ${e("gray", $)}` : ""}`;
36791
38110
  }
36792
38111
  default: {
36793
- const c50 = s ? `${e("cyan", $)} ` : "", a = s ? e("cyan", E2) : "", l = o.split(`
38112
+ const c51 = s ? `${e("cyan", $)} ` : "", a = s ? e("cyan", E2) : "", l = o.split(`
36794
38113
  `).length, d2 = s ? 2 : 1;
36795
- return `${o}${c50}${Y2({ output: t.output, cursor: this.cursor, options: this.options, maxItems: t.maxItems, columnPadding: c50.length, rowPadding: l + d2, style: (y, p2) => r(y, y.disabled ? "disabled" : p2 ? "active" : "inactive") }).join(`
36796
- ${c50}`)}
38114
+ 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(`
38115
+ ${c51}`)}
36797
38116
  ${a}
36798
38117
  `;
36799
38118
  }
@@ -37551,7 +38870,7 @@ function extractCommandTree(cmd) {
37551
38870
  description: o.description,
37552
38871
  takesValue: o.required || o.optional
37553
38872
  }));
37554
- const subcommands = cmd.commands.filter((c50) => !c50._hidden).map((c50) => extractCommandTree(c50));
38873
+ const subcommands = cmd.commands.filter((c51) => !c51._hidden).map((c51) => extractCommandTree(c51));
37555
38874
  return {
37556
38875
  name: cmd.name(),
37557
38876
  description: cmd.description(),
@@ -37617,7 +38936,7 @@ function generateBashCompletion(tree, binName) {
37617
38936
  }
37618
38937
  function collectBashCases(tree, prefix) {
37619
38938
  const cases = /* @__PURE__ */ new Map();
37620
- const subcommandNames = tree.subcommands.map((c50) => c50.name);
38939
+ const subcommandNames = tree.subcommands.map((c51) => c51.name);
37621
38940
  const optionNames = tree.options.flatMap((o) => o.short ? [o.long, o.short] : [o.long]);
37622
38941
  const extraOptions = prefix === "" ? ["--help", "--version"] : ["--help"];
37623
38942
  const allOptions = [.../* @__PURE__ */ new Set([...optionNames, ...extraOptions])];
@@ -38300,16 +39619,16 @@ function printCredentialsTsv(creds) {
38300
39619
  }
38301
39620
  }
38302
39621
  function buildCredentialsTable(creds) {
38303
- const cells = creds.map((c50) => ({
38304
- id: c50.credentialId,
38305
- label: sanitizeDisplay(c50.label),
38306
- type: c50.credentialType
39622
+ const cells = creds.map((c51) => ({
39623
+ id: c51.credentialId,
39624
+ label: sanitizeDisplay(c51.label),
39625
+ type: c51.credentialType
38307
39626
  }));
38308
- const idWidth = Math.max(2, ...cells.map((c50) => c50.id.length));
38309
- const labelWidth = Math.max(5, ...cells.map((c50) => c50.label.length));
39627
+ const idWidth = Math.max(2, ...cells.map((c51) => c51.id.length));
39628
+ const labelWidth = Math.max(5, ...cells.map((c51) => c51.label.length));
38310
39629
  const header = `${"ID".padEnd(idWidth)} ${"LABEL".padEnd(labelWidth)} TYPE`;
38311
39630
  const rows = cells.map(
38312
- (c50) => `${c50.id.padEnd(idWidth)} ${c50.label.padEnd(labelWidth)} ${c50.type}`
39631
+ (c51) => `${c51.id.padEnd(idWidth)} ${c51.label.padEnd(labelWidth)} ${c51.type}`
38313
39632
  );
38314
39633
  return { header, rows };
38315
39634
  }
@@ -38365,7 +39684,7 @@ async function runCreate2(options) {
38365
39684
  }
38366
39685
  const organizationId = await resolveOrganizationId();
38367
39686
  const params = { organizationId, environmentId: options.env };
38368
- const label = options.label;
39687
+ const label = options.label?.trim() ? options.label.trim() : void 0;
38369
39688
  let body;
38370
39689
  try {
38371
39690
  body = await buildCreateBody(type, label, options);
@@ -38388,6 +39707,7 @@ async function runCreate2(options) {
38388
39707
  outputCredential(result.body, output);
38389
39708
  }
38390
39709
  async function buildCreateBody(type, label, options) {
39710
+ const labelFields = label !== void 0 ? { label } : {};
38391
39711
  switch (type) {
38392
39712
  case "BEARER_TOKEN": {
38393
39713
  const ac = buildBearerAuthConfig(options);
@@ -38395,7 +39715,7 @@ async function buildCreateBody(type, label, options) {
38395
39715
  const secretValue = await resolveSecret("secret value", "secret", secretOpts(options));
38396
39716
  return {
38397
39717
  credentialType: "BEARER_TOKEN",
38398
- label,
39718
+ ...labelFields,
38399
39719
  secretValue,
38400
39720
  ...ac.value ? { authConfig: ac.value } : {}
38401
39721
  };
@@ -38406,7 +39726,7 @@ async function buildCreateBody(type, label, options) {
38406
39726
  const secretValue = await resolveSecret("secret value", "secret", secretOpts(options));
38407
39727
  return {
38408
39728
  credentialType: "JWT_TOKEN",
38409
- label,
39729
+ ...labelFields,
38410
39730
  secretValue,
38411
39731
  ...ac.value ? { authConfig: ac.value } : {}
38412
39732
  };
@@ -38417,14 +39737,14 @@ async function buildCreateBody(type, label, options) {
38417
39737
  const secretValue = await resolveSecret("secret value", "secret", secretOpts(options));
38418
39738
  return {
38419
39739
  credentialType: "API_KEY",
38420
- label,
39740
+ ...labelFields,
38421
39741
  secretValue,
38422
39742
  ...ac.value ? { authConfig: ac.value } : {}
38423
39743
  };
38424
39744
  }
38425
39745
  case "PASSPHRASE": {
38426
39746
  const secretValue = await resolveSecret("secret value", "secret", secretOpts(options));
38427
- return { credentialType: "PASSPHRASE", label, secretValue };
39747
+ return { credentialType: "PASSPHRASE", ...labelFields, secretValue };
38428
39748
  }
38429
39749
  case "SESSION_STATE": {
38430
39750
  const secretValue = await resolveSecret(
@@ -38432,7 +39752,7 @@ async function buildCreateBody(type, label, options) {
38432
39752
  "secret",
38433
39753
  secretOpts(options)
38434
39754
  );
38435
- return { credentialType: "SESSION_STATE", label, secretValue };
39755
+ return { credentialType: "SESSION_STATE", ...labelFields, secretValue };
38436
39756
  }
38437
39757
  case "COOKIE": {
38438
39758
  const ac = buildCookieAuthConfig(options);
@@ -38440,7 +39760,7 @@ async function buildCreateBody(type, label, options) {
38440
39760
  const secretValue = await resolveSecret("secret value", "secret", secretOpts(options));
38441
39761
  return {
38442
39762
  credentialType: "COOKIE",
38443
- label,
39763
+ ...labelFields,
38444
39764
  secretValue,
38445
39765
  ...ac.value ? { authConfig: ac.value } : {}
38446
39766
  };
@@ -38451,7 +39771,7 @@ async function buildCreateBody(type, label, options) {
38451
39771
  const secretValue = await resolveSecret("test-account email", "secret", secretOpts(options));
38452
39772
  return {
38453
39773
  credentialType: "MAGIC_AUTH",
38454
- label,
39774
+ ...labelFields,
38455
39775
  secretValue,
38456
39776
  ...ac.value ? { authConfig: ac.value } : {}
38457
39777
  };
@@ -38463,7 +39783,7 @@ async function buildCreateBody(type, label, options) {
38463
39783
  const password = await resolveSecret("password", "password", passwordOpts(options));
38464
39784
  return {
38465
39785
  credentialType: "USERNAME_PASSWORD",
38466
- label,
39786
+ ...labelFields,
38467
39787
  username,
38468
39788
  password,
38469
39789
  ...ac.value ? { authConfig: ac.value } : {}
@@ -38616,17 +39936,21 @@ function createCreateCommand2() {
38616
39936
  const command = new Command("create").description("Create a credential in an environment").requiredOption("--env <environmentId>", "Parent environment UUID").requiredOption(
38617
39937
  "--type <type>",
38618
39938
  "bearer-token, jwt-token, api-key, passphrase, username-password, cookie, magic-auth"
38619
- ).requiredOption("--label <label>", "Human-readable label").option("-o, --output <format>", "Output format: table, json, tsv (default: table)", "table");
39939
+ ).option(
39940
+ "--label <label>",
39941
+ "Human-readable label (optional; defaults from type / username + environment)"
39942
+ ).option("-o, --output <format>", "Output format: table, json, tsv (default: table)", "table");
38620
39943
  addSecretInputOptions(command);
38621
39944
  addAuthConfigOptions(command).addHelpText(
38622
39945
  "after",
38623
39946
  `
38624
39947
  Secrets are read from stdin/file/env (or a no-echo prompt) \u2014 never an inline flag.
39948
+ --label is optional; when omitted the API picks a unique default.
38625
39949
 
38626
39950
  Examples:
38627
- vault read -field=token secret/x | tracecli credential create --env <envId> --type bearer-token --label prod --secret-stdin
39951
+ vault read -field=token secret/x | tracecli credential create --env <envId> --type bearer-token --secret-stdin
38628
39952
  tracecli credential create --env <envId> --type api-key --label "prod key" --secret-env API_KEY --placement header --key-name X-API-Key
38629
- tracecli credential create --env <envId> --type username-password --label login --username-env U --password-env P --method form --login-url https://acme.com/login`
39953
+ tracecli credential create --env <envId> --type username-password --username-env U --password-env P --method form --login-url https://acme.com/login`
38630
39954
  );
38631
39955
  return command.action(async (options) => {
38632
39956
  await runCreate2(options);
@@ -38793,7 +40117,7 @@ function renderEnvironmentDetail(env2) {
38793
40117
  return [
38794
40118
  `${"ID:".padEnd(pad)} ${env2.environmentId}`,
38795
40119
  `${"Name:".padEnd(pad)} ${sanitizeDisplay(env2.name)}`,
38796
- `${"URL:".padEnd(pad)} ${sanitizeDisplay(env2.primaryUrl)}`,
40120
+ `${"URL:".padEnd(pad)} ${env2.primaryUrl != null ? sanitizeDisplay(env2.primaryUrl) : "\u2014"}`,
38797
40121
  `${"Production:".padEnd(pad)} ${env2.isProduction ? "yes" : "no"}`,
38798
40122
  `${"Description:".padEnd(pad)} ${env2.description ? sanitizeDisplay(env2.description) : "\u2014"}`,
38799
40123
  `${"Application:".padEnd(pad)} ${env2.applicationId}`,
@@ -38809,7 +40133,7 @@ function printEnvironmentsTsv(envs) {
38809
40133
  [
38810
40134
  tsvSafe(env2.environmentId),
38811
40135
  tsvSafe(sanitizeDisplay(env2.name)),
38812
- tsvSafe(sanitizeDisplay(env2.primaryUrl)),
40136
+ tsvSafe(sanitizeDisplay(env2.primaryUrl ?? "")),
38813
40137
  String(env2.isProduction),
38814
40138
  tsvSafe(sanitizeDisplay(env2.description ?? "")),
38815
40139
  tsvSafe(toIso(env2.createdAt))
@@ -38841,7 +40165,7 @@ function printOrgEnvironmentsTsv(envs) {
38841
40165
  tsvSafe(env2.applicationId),
38842
40166
  tsvSafe(sanitizeDisplay(env2.applicationName)),
38843
40167
  tsvSafe(sanitizeDisplay(env2.name)),
38844
- tsvSafe(sanitizeDisplay(env2.primaryUrl)),
40168
+ tsvSafe(sanitizeDisplay(env2.primaryUrl ?? "")),
38845
40169
  String(env2.isProduction),
38846
40170
  tsvSafe(toIso(env2.createdAt))
38847
40171
  ].join(" ")
@@ -38853,17 +40177,17 @@ function buildEnvironmentTable(envs, leading) {
38853
40177
  leading: leading.map((column) => sanitizeDisplay(column.value(e2))),
38854
40178
  name: sanitizeDisplay(e2.name),
38855
40179
  prod: e2.isProduction ? "yes" : "no",
38856
- url: sanitizeDisplay(e2.primaryUrl)
40180
+ url: e2.primaryUrl != null ? sanitizeDisplay(e2.primaryUrl) : "\u2014"
38857
40181
  }));
38858
40182
  const leadingWidths = leading.map(
38859
- (column, i) => Math.max(column.header.length, ...cells.map((c50) => c50.leading[i]?.length ?? 0))
40183
+ (column, i) => Math.max(column.header.length, ...cells.map((c51) => c51.leading[i]?.length ?? 0))
38860
40184
  );
38861
- const nameWidth = Math.max(4, ...cells.map((c50) => c50.name.length));
40185
+ const nameWidth = Math.max(4, ...cells.map((c51) => c51.name.length));
38862
40186
  const padLeading = (values) => values.map((v2, i) => v2.padEnd(leadingWidths[i] ?? 0)).join(" ");
38863
40187
  return {
38864
- header: `${padLeading(leading.map((c50) => c50.header))} ${"NAME".padEnd(nameWidth)} PROD URL`,
40188
+ header: `${padLeading(leading.map((c51) => c51.header))} ${"NAME".padEnd(nameWidth)} PROD URL`,
38865
40189
  rows: cells.map(
38866
- (c50) => `${padLeading(c50.leading)} ${c50.name.padEnd(nameWidth)} ${c50.prod.padEnd(4)} ${c50.url}`
40190
+ (c51) => `${padLeading(c51.leading)} ${c51.name.padEnd(nameWidth)} ${c51.prod.padEnd(4)} ${c51.url}`
38867
40191
  )
38868
40192
  };
38869
40193
  }
@@ -39225,6 +40549,120 @@ var packageMetadata = readPackageMetadata();
39225
40549
  var CLI_PACKAGE_NAME = packageMetadata.name;
39226
40550
  var CLI_VERSION = packageMetadata.version;
39227
40551
 
40552
+ // src/pentest/commands.ts
40553
+ import { readFileSync as readFileSync4 } from "fs";
40554
+
40555
+ // src/pentest/questionnaire.ts
40556
+ function buildUpsertQuestionnaireBody(answers) {
40557
+ if (answers === null) {
40558
+ return { answers: null };
40559
+ }
40560
+ return { answers: normalizePentestCustomerIntakeAnswers(answers) };
40561
+ }
40562
+ function parseQuestionnaireAnswersFile(raw, sourceLabel = "file") {
40563
+ let parsed;
40564
+ try {
40565
+ parsed = JSON.parse(raw);
40566
+ } catch {
40567
+ return { error: `Invalid JSON in ${sourceLabel}.` };
40568
+ }
40569
+ if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
40570
+ return {
40571
+ error: `${sourceLabel} must be a JSON object of questionnaire answers (not an array or null).`
40572
+ };
40573
+ }
40574
+ if ("answers" in parsed && Object.keys(parsed).length === 1) {
40575
+ return {
40576
+ error: `${sourceLabel} must contain the answers object directly, not an { answers } wrapper. The CLI wraps it for the API.`
40577
+ };
40578
+ }
40579
+ const result = PentestCustomerIntakeAnswersSchema.safeParse(parsed);
40580
+ if (!result.success) {
40581
+ const issue2 = result.error.issues[0];
40582
+ const path = issue2?.path.length ? issue2.path.join(".") : "answers";
40583
+ const message = issue2?.message ?? "invalid";
40584
+ return { error: `Invalid questionnaire answers in ${sourceLabel}: ${path} \u2014 ${message}` };
40585
+ }
40586
+ return { answers: normalizePentestCustomerIntakeAnswers(result.data) };
40587
+ }
40588
+ function applyTextIntakeAnswer(current, key, input) {
40589
+ const trimmed = input.trim();
40590
+ if (trimmed.length === 0) {
40591
+ return current;
40592
+ }
40593
+ return { ...current, [key]: trimmed };
40594
+ }
40595
+ function applyMultiSelectIntakeAnswer(current, key, selected) {
40596
+ if (selected.length === 0) {
40597
+ return current;
40598
+ }
40599
+ return {
40600
+ ...current,
40601
+ [key]: Array.from(new Set(selected)).sort()
40602
+ };
40603
+ }
40604
+ function formatIssueClasses(values) {
40605
+ return values.map((v2) => INTAKE_ISSUE_CLASS_LABELS[v2]).join(", ");
40606
+ }
40607
+ function formatSurfaces(values) {
40608
+ return values.map((v2) => INTAKE_SURFACE_LABELS[v2]).join(", ");
40609
+ }
40610
+ function formatAnswerValue(key, answers) {
40611
+ switch (key) {
40612
+ case "issueClasses": {
40613
+ const values = answers.issueClasses;
40614
+ if (values === void 0 || values.length === 0) {
40615
+ return null;
40616
+ }
40617
+ return formatIssueClasses(values);
40618
+ }
40619
+ case "surfaces": {
40620
+ const values = answers.surfaces;
40621
+ if (values === void 0 || values.length === 0) {
40622
+ return null;
40623
+ }
40624
+ return formatSurfaces(values);
40625
+ }
40626
+ default: {
40627
+ const value = answers[key];
40628
+ if (value === void 0 || value.trim().length === 0) {
40629
+ return null;
40630
+ }
40631
+ return value;
40632
+ }
40633
+ }
40634
+ }
40635
+ function formatQuestionnaireShow(input) {
40636
+ const lines = [
40637
+ `Editable: ${input.editable ? "yes (scoping window open)" : "no (editing closed after scoping)"}`
40638
+ ];
40639
+ if (input.answers === null || Object.keys(input.answers).length === 0) {
40640
+ lines.push("Answers: (none)");
40641
+ return lines.join("\n");
40642
+ }
40643
+ lines.push("Answers:");
40644
+ for (const key of PENTEST_CUSTOMER_INTAKE_ANSWER_KEYS) {
40645
+ const value = formatAnswerValue(key, input.answers);
40646
+ if (value === null) {
40647
+ continue;
40648
+ }
40649
+ lines.push(`${PENTEST_CUSTOMER_INTAKE_LABELS[key]}: ${value}`);
40650
+ }
40651
+ return lines.join("\n");
40652
+ }
40653
+ function issueClassSelectOptions() {
40654
+ return CustomerPriorityCategoryValues.map((value) => ({
40655
+ value,
40656
+ label: INTAKE_ISSUE_CLASS_LABELS[value]
40657
+ }));
40658
+ }
40659
+ function surfaceSelectOptions() {
40660
+ return IntakeSurfaceValues.map((value) => ({
40661
+ value,
40662
+ label: INTAKE_SURFACE_LABELS[value]
40663
+ }));
40664
+ }
40665
+
39228
40666
  // src/pentest/commands.ts
39229
40667
  var DEFAULT_LIMIT2 = 30;
39230
40668
  var MAX_LIMIT2 = 100;
@@ -39474,13 +40912,9 @@ Examples:
39474
40912
  console.error(`Failed to list findings (status ${result.status}).`);
39475
40913
  process.exit(1);
39476
40914
  }
39477
- let filteredFindings = result.body;
39478
- if (options.severity) {
39479
- filteredFindings = filteredFindings.filter((f) => f.severity === options.severity);
39480
- }
39481
- if (options.status) {
39482
- filteredFindings = filteredFindings.filter((f) => f.status === options.status);
39483
- }
40915
+ const filteredFindings = result.body.filter(
40916
+ (f) => (!options.severity || f.severity === options.severity) && (!options.status || f.status === options.status)
40917
+ );
39484
40918
  filteredFindings.sort(
39485
40919
  (a, b2) => (SEVERITY_ORDER[a.severity] ?? 99) - (SEVERITY_ORDER[b2.severity] ?? 99)
39486
40920
  );
@@ -39764,16 +41198,248 @@ Examples:
39764
41198
  await runUpdate4(pentestId, options);
39765
41199
  });
39766
41200
  }
41201
+ function failQuestionnaire(status, body, action, notFoundLabel) {
41202
+ const { message } = readErrorFields(body);
41203
+ if (status === UNAUTHORIZED_STATUS2) {
41204
+ exitAuthFailed();
41205
+ }
41206
+ if (status === 403) {
41207
+ if (action === "write") {
41208
+ console.error(
41209
+ colorError(
41210
+ "Questionnaire writes require an organization admin (or Trace staff) signed in with a browser session. API keys are rejected."
41211
+ )
41212
+ );
41213
+ } else {
41214
+ console.error(
41215
+ colorError(message ?? "You do not have permission to view this questionnaire.")
41216
+ );
41217
+ }
41218
+ process.exit(1);
41219
+ }
41220
+ if (status === 404) {
41221
+ console.error(
41222
+ colorError(notFoundLabel ? `Pentest not found: ${notFoundLabel}.` : message ?? "Not found.")
41223
+ );
41224
+ process.exit(1);
41225
+ }
41226
+ if (status === 400) {
41227
+ console.error(colorError(message ?? "Invalid questionnaire answers (status 400)."));
41228
+ process.exit(1);
41229
+ }
41230
+ if (status === 409) {
41231
+ console.error(
41232
+ colorError(
41233
+ message ?? "Questionnaire editing is closed after scoping starts (current stage is past DRAFT / READY_TO_START)."
41234
+ )
41235
+ );
41236
+ process.exit(1);
41237
+ }
41238
+ console.error(
41239
+ colorError(
41240
+ `Failed to ${action === "write" ? "save" : "load"} questionnaire (status ${status})${message ? `: ${message}` : "."}`
41241
+ )
41242
+ );
41243
+ process.exit(1);
41244
+ }
41245
+ async function fetchQuestionnaire(organizationId, pentestId, notFoundLabel) {
41246
+ const result = await authenticatedRequest(
41247
+ (client) => client.pentest.getCustomerPentestQuestionnaire({
41248
+ params: { organizationId, pentestId }
41249
+ })
41250
+ );
41251
+ if (result.status !== SUCCESS_STATUS) {
41252
+ failQuestionnaire(result.status, result.body, "read", notFoundLabel);
41253
+ }
41254
+ return result.body;
41255
+ }
41256
+ async function putQuestionnaire(organizationId, pentestId, answers, notFoundLabel) {
41257
+ const body = buildUpsertQuestionnaireBody(answers);
41258
+ const result = await authenticatedRequest(
41259
+ (client) => client.pentest.upsertCustomerPentestQuestionnaire({
41260
+ params: { organizationId, pentestId },
41261
+ body
41262
+ })
41263
+ );
41264
+ if (result.status !== SUCCESS_STATUS) {
41265
+ failQuestionnaire(result.status, result.body, "write", notFoundLabel);
41266
+ }
41267
+ return result.body;
41268
+ }
41269
+ function exitCancelled() {
41270
+ me("Cancelled.");
41271
+ process.exit(1);
41272
+ }
41273
+ async function promptQuestionnaireAnswers(existing) {
41274
+ let working = { ...existing ?? {} };
41275
+ const textPrompt = async (key, question) => {
41276
+ const previous = existing?.[key];
41277
+ const value = await fe({
41278
+ message: `${question} (empty keeps previous)`,
41279
+ initialValue: previous ?? "",
41280
+ ...previous ? {} : { placeholder: "Optional, press Enter to skip" }
41281
+ });
41282
+ if (q(value)) {
41283
+ exitCancelled();
41284
+ }
41285
+ working = applyTextIntakeAnswer(working, key, value);
41286
+ };
41287
+ for (const entry of PENTEST_CUSTOMER_INTAKE_QUESTIONS) {
41288
+ if (entry.kind === "text") {
41289
+ await textPrompt(entry.key, entry.question);
41290
+ continue;
41291
+ }
41292
+ const message = `${entry.question} (empty keeps previous)`;
41293
+ if (entry.key === "issueClasses") {
41294
+ const selected = await ve({
41295
+ message,
41296
+ options: issueClassSelectOptions(),
41297
+ required: false,
41298
+ ...existing?.issueClasses !== void 0 ? { initialValues: existing.issueClasses } : {}
41299
+ });
41300
+ if (q(selected)) {
41301
+ exitCancelled();
41302
+ }
41303
+ working = applyMultiSelectIntakeAnswer(working, "issueClasses", selected);
41304
+ } else {
41305
+ const selected = await ve({
41306
+ message,
41307
+ options: surfaceSelectOptions(),
41308
+ required: false,
41309
+ ...existing?.surfaces !== void 0 ? { initialValues: existing.surfaces } : {}
41310
+ });
41311
+ if (q(selected)) {
41312
+ exitCancelled();
41313
+ }
41314
+ working = applyMultiSelectIntakeAnswer(working, "surfaces", selected);
41315
+ }
41316
+ await textPrompt(entry.notesKey, entry.notesQuestion);
41317
+ }
41318
+ const confirmed = await ue({
41319
+ message: "Save questionnaire answers?",
41320
+ initialValue: true
41321
+ });
41322
+ if (q(confirmed) || !confirmed) {
41323
+ exitCancelled();
41324
+ }
41325
+ if (Object.keys(working).length === 0) {
41326
+ return existing === null ? null : working;
41327
+ }
41328
+ return working;
41329
+ }
41330
+ async function runQuestionnaire(pentestIdentifier, options) {
41331
+ const show = options.show === true;
41332
+ const filePath = options.file;
41333
+ if (show && filePath !== void 0) {
41334
+ console.error(colorError("Pass only one of --show / --file."));
41335
+ process.exit(1);
41336
+ }
41337
+ const isInteractiveTty = Boolean(process.stdin.isTTY && process.stdout.isTTY);
41338
+ if (!show && filePath === void 0 && !isInteractiveTty) {
41339
+ console.error(
41340
+ colorError(
41341
+ "Interactive questionnaire requires a TTY. Use --show to print answers, or --file <path> to upload an answers JSON object."
41342
+ )
41343
+ );
41344
+ process.exit(2);
41345
+ }
41346
+ const organizationId = await resolveOrganizationId();
41347
+ const pentestId = await resolvePentestId(organizationId, pentestIdentifier);
41348
+ if (show) {
41349
+ const current2 = await fetchQuestionnaire(organizationId, pentestId, pentestIdentifier);
41350
+ console.log(
41351
+ formatQuestionnaireShow({
41352
+ answers: current2.answers,
41353
+ editable: current2.editable
41354
+ })
41355
+ );
41356
+ return;
41357
+ }
41358
+ if (filePath !== void 0) {
41359
+ let raw;
41360
+ try {
41361
+ raw = readFileSync4(filePath, "utf8");
41362
+ } catch {
41363
+ console.error(colorError(`Could not read answers file: ${filePath}`));
41364
+ process.exit(1);
41365
+ }
41366
+ const parsed = parseQuestionnaireAnswersFile(raw, filePath);
41367
+ if ("error" in parsed) {
41368
+ console.error(colorError(parsed.error));
41369
+ process.exit(1);
41370
+ }
41371
+ const saved2 = await putQuestionnaire(
41372
+ organizationId,
41373
+ pentestId,
41374
+ parsed.answers,
41375
+ pentestIdentifier
41376
+ );
41377
+ console.log(colorSuccess("Saved questionnaire answers."));
41378
+ console.log(
41379
+ formatQuestionnaireShow({
41380
+ answers: saved2.answers,
41381
+ editable: saved2.editable
41382
+ })
41383
+ );
41384
+ return;
41385
+ }
41386
+ const current = await fetchQuestionnaire(organizationId, pentestId, pentestIdentifier);
41387
+ if (!current.editable) {
41388
+ console.error(
41389
+ colorError(
41390
+ "Questionnaire editing is closed after scoping starts. Use --show to view the saved answers."
41391
+ )
41392
+ );
41393
+ process.exit(1);
41394
+ }
41395
+ const merged = await promptQuestionnaireAnswers(current.answers);
41396
+ const saved = await putQuestionnaire(organizationId, pentestId, merged, pentestIdentifier);
41397
+ console.log(colorSuccess("Saved questionnaire answers."));
41398
+ console.log(
41399
+ formatQuestionnaireShow({
41400
+ answers: saved.answers,
41401
+ editable: saved.editable
41402
+ })
41403
+ );
41404
+ }
41405
+ function createQuestionnaireCommand() {
41406
+ 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(
41407
+ "--file <path>",
41408
+ "Upload answers from a JSON file (answers object only \u2014 not { answers }; whole-object replace)"
41409
+ ).addHelpText(
41410
+ "after",
41411
+ `
41412
+ Optional scoping questionnaire for a new pentest (editable while stage is DRAFT
41413
+ or READY_TO_START). Reads accept a browser session or API key; writes require an
41414
+ organization admin (or Trace staff) signed in via \`tracecli auth login\` \u2014 API
41415
+ keys are rejected.
41416
+
41417
+ Modes:
41418
+ --show GET and print key: value with shared customer-facing labels
41419
+ --file <path> PUT the file's answers object (CLI wraps { answers } for the API)
41420
+ (TTY default) load-merge-write: GET, prompt each question, confirm, PUT
41421
+
41422
+ Non-interactive shells must pass --show or --file (exit 2 otherwise).
41423
+
41424
+ Examples:
41425
+ tracecli pentest questionnaire PT-1 --show
41426
+ tracecli pentest questionnaire PT-1 --file answers.json
41427
+ tracecli pentest questionnaire PT-1`
41428
+ ).action(async (pentestId, options) => {
41429
+ await runQuestionnaire(pentestId, options);
41430
+ });
41431
+ }
39767
41432
  function createPentestCommand() {
39768
41433
  const pentest = new Command("pentest").description("Manage pentests");
39769
41434
  pentest.addCommand(createListCommand5());
39770
41435
  pentest.addCommand(createUpdateCommand4());
41436
+ pentest.addCommand(createQuestionnaireCommand());
39771
41437
  pentest.addCommand(createFindingsCommand());
39772
41438
  return pentest;
39773
41439
  }
39774
41440
 
39775
41441
  // src/update/commands.ts
39776
- import { existsSync as existsSync3, mkdirSync as mkdirSync3, readFileSync as readFileSync4, writeFileSync as writeFileSync4 } from "fs";
41442
+ import { existsSync as existsSync3, mkdirSync as mkdirSync3, readFileSync as readFileSync5, writeFileSync as writeFileSync4 } from "fs";
39777
41443
  import { dirname as dirname2, join as join5 } from "path";
39778
41444
  import { createInterface as createInterface4 } from "readline/promises";
39779
41445
  var UPDATE_CACHE_FILE = "update-check.json";
@@ -39791,7 +41457,7 @@ function readUpdateCheckCache() {
39791
41457
  return null;
39792
41458
  }
39793
41459
  try {
39794
- const rawCache = readFileSync4(cachePath, "utf-8");
41460
+ const rawCache = readFileSync5(cachePath, "utf-8");
39795
41461
  const parsed = JSON.parse(rawCache);
39796
41462
  if (typeof parsed !== "object" || parsed === null) {
39797
41463
  return null;
@@ -40521,177 +42187,10 @@ Examples:
40521
42187
  }
40522
42188
  );
40523
42189
  }
40524
- var FIX_PR_TERMINAL_STATUSES = /* @__PURE__ */ new Set([
40525
- "PR_OPENED",
40526
- "NO_SAFE_FIX",
40527
- "PATCH_TOO_LARGE",
40528
- "TESTS_FAILED",
40529
- "INFRA_FAILED",
40530
- "STALE_VULN",
40531
- "TIMED_OUT",
40532
- "CANCELLED"
40533
- ]);
40534
- var FIX_PR_AGENT_FLAVORS = ["claude-code", "codex"];
40535
- var FIX_PR_POLL_MS = 3e3;
40536
- function isFixPrAgentFlavor(value) {
40537
- return FIX_PR_AGENT_FLAVORS.includes(value);
40538
- }
40539
- function formatFixPrWatchLine(row) {
40540
- return `[${row.status}] run=${row.runId.slice(0, 8)} agent=${row.agentFlavor}`;
40541
- }
40542
- function formatFixPrTerminalLines(row) {
40543
- const link = row.prUrl ? ` ${row.prUrl}` : "";
40544
- const lines = [`Run ${row.runId} \u2192 ${row.status}${link}`];
40545
- if (row.statusDetails) {
40546
- lines.push(`Details: ${row.statusDetails}`);
40547
- }
40548
- return lines;
40549
- }
40550
- function createFixCommand() {
40551
- return new Command("fix").description("Trigger a Trace auto-fix PR for the vulnerability (staff-only dogfood)").argument("<id>", "Vulnerability display ID (e.g. TRACE-123 or 123) or UUID").option(
40552
- "--agent <flavor>",
40553
- `Override the agent flavor (one of: ${FIX_PR_AGENT_FLAVORS.join(", ")})`
40554
- ).option("--watch", "Poll the run until it reaches a terminal status", false).option("-o, --output <format>", "Output format: table, json (default: table)", "table").addHelpText(
40555
- "after",
40556
- `
40557
- Examples:
40558
- # Trigger and exit immediately (scripting):
40559
- tracecli vulnerabilities fix TRACE-123
40560
-
40561
- # Trigger and watch until terminal:
40562
- tracecli vulnerabilities fix TRACE-123 --watch
40563
-
40564
- # Trigger with codex agent (requires AZURE_OPENAI_ENABLED):
40565
- tracecli vulnerabilities fix TRACE-123 --agent codex
40566
-
40567
- # JSON output (final row to stdout; rolling status to stderr under --watch):
40568
- tracecli vulnerabilities fix TRACE-123 --watch --output json | jq .`
40569
- ).action(
40570
- async (identifier, options) => {
40571
- if (options.agent && !isFixPrAgentFlavor(options.agent)) {
40572
- console.error(
40573
- `Invalid --agent '${options.agent}'. Expected one of: ${FIX_PR_AGENT_FLAVORS.join(", ")}.`
40574
- );
40575
- process.exit(1);
40576
- }
40577
- if (options.output !== "table" && options.output !== "json") {
40578
- console.error(`Invalid --output '${options.output}'. Expected one of: table, json.`);
40579
- process.exit(1);
40580
- }
40581
- const organizationId = await resolveOrganizationId();
40582
- const triggerResult = await authenticatedRequest(
40583
- (client) => client.vulnerabilityFixRun.triggerRun({
40584
- params: { organizationId, vulnerabilityId: identifier },
40585
- body: options.agent ? { agentFlavor: options.agent } : {}
40586
- })
40587
- );
40588
- if (triggerResult.status === 401) {
40589
- console.error("Authentication failed. Run `tracecli auth login` to re-authenticate.");
40590
- process.exit(1);
40591
- }
40592
- if (triggerResult.status === 403) {
40593
- console.error(
40594
- "Forbidden \u2014 vulnerability fix-PR is a staff-only feature gated by the AUTO_FIX_PR capability."
40595
- );
40596
- process.exit(1);
40597
- }
40598
- if (triggerResult.status === 404) {
40599
- console.error(
40600
- `Vulnerability '${identifier}' not found.
40601
- Run \`tracecli vulnerabilities list\` to see available vulnerabilities.`
40602
- );
40603
- process.exit(1);
40604
- }
40605
- if (triggerResult.status === 429) {
40606
- const body = triggerResult.body;
40607
- const retry = body.details?.retryAfterSeconds;
40608
- console.error(
40609
- `Daily cap exceeded. ${retry ? `Try again in ${retry}s.` : body.message ?? ""}`
40610
- );
40611
- process.exit(1);
40612
- }
40613
- if (triggerResult.status === 400) {
40614
- const body = triggerResult.body;
40615
- console.error(`Bad request: ${body.message ?? "invalid input"}`);
40616
- process.exit(1);
40617
- }
40618
- if (triggerResult.status !== 201) {
40619
- console.error(`Failed to trigger fix-PR run (status ${triggerResult.status}).`);
40620
- process.exit(1);
40621
- }
40622
- const triggered = triggerResult.body;
40623
- if (!options.watch) {
40624
- if (options.output === "json") {
40625
- console.log(JSON.stringify(triggered, null, 2));
40626
- } else {
40627
- console.log(
40628
- `Triggered fix-PR run ${triggered.runId} (status=${triggered.status}, agent=${triggered.agentFlavor})`
40629
- );
40630
- }
40631
- process.exit(0);
40632
- }
40633
- let latest = triggered;
40634
- const isTTY = process.stderr.isTTY ?? false;
40635
- const onSigint = () => {
40636
- const msg = `
40637
- watch interrupted; run id=${latest.runId}`;
40638
- process.stderr.write(`${msg}
40639
- `);
40640
- process.exit(130);
40641
- };
40642
- process.on("SIGINT", onSigint);
40643
- const writeRolling = (row) => {
40644
- const line = formatFixPrWatchLine(row);
40645
- if (isTTY && options.output !== "json") {
40646
- process.stderr.write(`\r${line} `);
40647
- } else {
40648
- process.stderr.write(`${line}
40649
- `);
40650
- }
40651
- };
40652
- while (!FIX_PR_TERMINAL_STATUSES.has(latest.status)) {
40653
- writeRolling(latest);
40654
- await new Promise((resolve) => setTimeout(resolve, FIX_PR_POLL_MS));
40655
- const pollResult = await authenticatedRequest(
40656
- (client) => client.vulnerabilityFixRun.getRun({
40657
- params: {
40658
- organizationId,
40659
- vulnerabilityId: identifier,
40660
- runId: latest.runId
40661
- }
40662
- })
40663
- );
40664
- if (pollResult.status === 401) {
40665
- process.stderr.write("\n");
40666
- console.error("Authentication failed during watch. Run `tracecli auth login`.");
40667
- process.exit(1);
40668
- }
40669
- if (pollResult.status !== 200) {
40670
- process.stderr.write("\n");
40671
- console.error(`Failed to read run status (status ${pollResult.status}).`);
40672
- process.exit(1);
40673
- }
40674
- latest = pollResult.body;
40675
- }
40676
- if (isTTY && options.output !== "json") {
40677
- process.stderr.write("\n");
40678
- }
40679
- if (options.output === "json") {
40680
- console.log(JSON.stringify(latest, null, 2));
40681
- } else {
40682
- for (const line of formatFixPrTerminalLines(latest)) {
40683
- console.log(line);
40684
- }
40685
- }
40686
- process.exit(latest.status === "PR_OPENED" ? 0 : 2);
40687
- }
40688
- );
40689
- }
40690
42190
  function createVulnerabilitiesCommand() {
40691
42191
  const vulnerabilities = new Command("vulnerabilities").description("Manage vulnerabilities");
40692
42192
  vulnerabilities.addCommand(createGetCommand4());
40693
42193
  vulnerabilities.addCommand(createListCommand6());
40694
- vulnerabilities.addCommand(createFixCommand());
40695
42194
  return vulnerabilities;
40696
42195
  }
40697
42196