securewithtrace 0.51.0 → 0.54.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.
package/dist/index.js CHANGED
@@ -3427,10 +3427,11 @@ var Permission = {
3427
3427
  VIEW_RESULTS: "VIEW_RESULTS",
3428
3428
  EXPORT_ORG_DATA: "EXPORT_ORG_DATA",
3429
3429
  DELETE_ORG_DATA: "DELETE_ORG_DATA",
3430
- MANAGE_AI_INTEGRATION: "MANAGE_AI_INTEGRATION",
3430
+ /** Connecting and configuring third-party integrations. */
3431
+ MANAGE_INTEGRATIONS: "MANAGE_INTEGRATIONS",
3431
3432
  MANAGE_INTEGRATED_REPOSITORIES: "MANAGE_INTEGRATED_REPOSITORIES",
3432
3433
  MANAGE_USERS: "MANAGE_USERS",
3433
- /** Invite or remove org members (admin + analyst). Distinct from MANAGE_USERS (admin-only API keys / role changes). */
3434
+ /** Invite or remove org members. Distinct from MANAGE_USERS (role changes, API keys). */
3434
3435
  INVITE_USERS: "INVITE_USERS",
3435
3436
  MANAGE_ORGANIZATION: "MANAGE_ORGANIZATION",
3436
3437
  MANAGE_PENTESTS: "MANAGE_PENTESTS",
@@ -3440,16 +3441,15 @@ var Permission = {
3440
3441
  // ../../packages/core/src/auth/permissions.ts
3441
3442
  var ROLE_PERMISSIONS = {
3442
3443
  [Role.developer]: /* @__PURE__ */ new Set([Permission.RUN_SCAN, Permission.VIEW_RESULTS]),
3444
+ // Shown as Manager: sets up and runs testing. Billing, people and API keys stay admin-only.
3443
3445
  [Role.analyst]: /* @__PURE__ */ new Set([
3444
3446
  Permission.RUN_SCAN,
3445
3447
  Permission.VIEW_RESULTS,
3446
3448
  Permission.EXPORT_ORG_DATA,
3447
3449
  Permission.MANAGE_INTEGRATED_REPOSITORIES,
3448
- Permission.MANAGE_AI_INTEGRATION,
3449
- Permission.INVITE_USERS
3450
- // MANAGE_PENTESTS removed — pentest writes now `requireStaff`
3451
- // (PR-0b-i M3 / D14). Constant stays in the enum for backward compat
3452
- // until v2 cleanup; analysts no longer get it.
3450
+ Permission.MANAGE_INTEGRATIONS,
3451
+ Permission.MANAGE_APPLICATIONS,
3452
+ Permission.MANAGE_PENTESTS
3453
3453
  ]),
3454
3454
  [Role.admin]: new Set(Object.values(Permission))
3455
3455
  };
@@ -6283,7 +6283,7 @@ var $ZodBase64 = /* @__PURE__ */ $constructor("$ZodBase64", (inst, def) => {
6283
6283
  function isValidBase64URL(data) {
6284
6284
  if (!base64url.test(data))
6285
6285
  return false;
6286
- const base644 = data.replace(/[-_]/g, (c84) => c84 === "-" ? "+" : "/");
6286
+ const base644 = data.replace(/[-_]/g, (c86) => c86 === "-" ? "+" : "/");
6287
6287
  const padded = base644.padEnd(Math.ceil(base644.length / 4) * 4, "=");
6288
6288
  return isValidBase64(padded);
6289
6289
  }
@@ -16327,9 +16327,9 @@ var ZodDate = /* @__PURE__ */ $constructor("ZodDate", (inst, def) => {
16327
16327
  inst._zod.processJSONSchema = (ctx, json2, params3) => dateProcessor(inst, ctx, json2, params3);
16328
16328
  inst.min = (value, params3) => inst.check(_gte(value, params3));
16329
16329
  inst.max = (value, params3) => inst.check(_lte(value, params3));
16330
- const c84 = inst._zod.bag;
16331
- inst.minDate = c84.minimum ? new Date(c84.minimum) : null;
16332
- inst.maxDate = c84.maximum ? new Date(c84.maximum) : null;
16330
+ const c86 = inst._zod.bag;
16331
+ inst.minDate = c86.minimum ? new Date(c86.minimum) : null;
16332
+ inst.maxDate = c86.maximum ? new Date(c86.maximum) : null;
16333
16333
  });
16334
16334
  function date3(params3) {
16335
16335
  return _date(ZodDate, params3);
@@ -17460,6 +17460,7 @@ config(en_default());
17460
17460
 
17461
17461
  // ../../packages/core/src/text/prompt-sanitize.ts
17462
17462
  var CREDENTIAL_KEY_SOURCE = "password|secret|token|access[_ -]?token|refresh[_ -]?token|cookie|authorization|bearer|api[_ -]?key|private[_ -]?key|session|credential|(?<![A-Za-z])(?:passwd|pwd|pass)";
17463
+ var PROVIDER_KEY_SOURCE = String.raw`\bsk-[A-Za-z0-9_-]{16,}\b`;
17463
17464
  var HIGH_ENTROPY_RUN_SOURCE = String.raw`\b(sk-[A-Za-z0-9_-]{16,}|[A-Za-z0-9+/_=-]{40,})\b`;
17464
17465
 
17465
17466
  // ../../packages/core/src/text/evidence-redact.ts
@@ -17612,14 +17613,16 @@ var SEGMENT = new RegExp(`[A-Za-z0-9_+-]{${MIN_RUN},}`, "g");
17612
17613
 
17613
17614
  // ../../packages/core/src/agent-session/versions.ts
17614
17615
  var COORDINATOR_TOOLSET_VERSION = "coordinator-tools-v4";
17615
- var OPERATOR_REVIEW_PROMPT_VERSION = "operator-review-v7";
17616
- var OPERATOR_REVIEW_FALLBACK_PROMPT_VERSION = "operator-review-v8";
17616
+ var OPERATOR_REVIEW_PROMPT_VERSION = "operator-review-v9";
17617
+ var OPERATOR_REVIEW_FALLBACK_PROMPT_VERSION = "operator-review-v10";
17617
17618
  var OPERATOR_REVIEW_V1_PROMPT_VERSION = "operator-review-v1";
17618
17619
  var OPERATOR_REVIEW_V2_PROMPT_VERSION = "operator-review-v2";
17619
17620
  var OPERATOR_REVIEW_V3_PROMPT_VERSION = "operator-review-v3";
17620
17621
  var OPERATOR_REVIEW_V4_PROMPT_VERSION = "operator-review-v4";
17621
17622
  var OPERATOR_REVIEW_V5_PROMPT_VERSION = "operator-review-v5";
17622
17623
  var OPERATOR_REVIEW_V6_PROMPT_VERSION = "operator-review-v6";
17624
+ var OPERATOR_REVIEW_V7_PROMPT_VERSION = "operator-review-v7";
17625
+ var OPERATOR_REVIEW_V8_PROMPT_VERSION = "operator-review-v8";
17623
17626
  var OPERATOR_REVIEW_PROMPT_VERSIONS = [
17624
17627
  OPERATOR_REVIEW_V1_PROMPT_VERSION,
17625
17628
  OPERATOR_REVIEW_V2_PROMPT_VERSION,
@@ -17627,6 +17630,8 @@ var OPERATOR_REVIEW_PROMPT_VERSIONS = [
17627
17630
  OPERATOR_REVIEW_V4_PROMPT_VERSION,
17628
17631
  OPERATOR_REVIEW_V5_PROMPT_VERSION,
17629
17632
  OPERATOR_REVIEW_V6_PROMPT_VERSION,
17633
+ OPERATOR_REVIEW_V7_PROMPT_VERSION,
17634
+ OPERATOR_REVIEW_V8_PROMPT_VERSION,
17630
17635
  OPERATOR_REVIEW_PROMPT_VERSION,
17631
17636
  OPERATOR_REVIEW_FALLBACK_PROMPT_VERSION
17632
17637
  ];
@@ -17707,7 +17712,14 @@ var ENVIRONMENT_AWARE_PROMPT_VERSIONS = /* @__PURE__ */ new Set([
17707
17712
  "operator-openai-v26",
17708
17713
  "operator-openai-v27",
17709
17714
  "operator-openai-v28",
17710
- "operator-openai-v29"
17715
+ "operator-openai-v29",
17716
+ // The Claude operator pins every provider launches.
17717
+ "operator-v30",
17718
+ "operator-v31",
17719
+ "operator-v32",
17720
+ "operator-v33",
17721
+ "operator-v34",
17722
+ "operator-v35"
17711
17723
  ]);
17712
17724
  var OOB_PROFILE_SUCCESSIONS = [
17713
17725
  {
@@ -18243,6 +18255,16 @@ var REVIEW_OOB_PROFILE_SUCCESSIONS = [
18243
18255
  toolsetVersion: "operator-tools-v10"
18244
18256
  }
18245
18257
  },
18258
+ {
18259
+ successor: {
18260
+ promptVersion: OPERATOR_REVIEW_V7_PROMPT_VERSION,
18261
+ toolsetVersion: "operator-tools-v12"
18262
+ },
18263
+ predecessor: {
18264
+ promptVersion: OPERATOR_REVIEW_V8_PROMPT_VERSION,
18265
+ toolsetVersion: "operator-tools-v10"
18266
+ }
18267
+ },
18246
18268
  {
18247
18269
  successor: {
18248
18270
  promptVersion: OPERATOR_REVIEW_PROMPT_VERSION,
@@ -18284,6 +18306,26 @@ var STAGE_RETIRED_OOB_PROFILE_SUCCESSIONS = [
18284
18306
  }
18285
18307
  }
18286
18308
  ];
18309
+ var ENVIRONMENT_AWARE_OPERATOR_OOB_PROFILE_SUCCESSIONS = [
18310
+ {
18311
+ successor: { promptVersion: "operator-v30", toolsetVersion: "operator-tools-v14" },
18312
+ predecessor: { promptVersion: "operator-v31", toolsetVersion: "operator-tools-v15" }
18313
+ },
18314
+ {
18315
+ successor: { promptVersion: "operator-v32", toolsetVersion: "operator-tools-v16" },
18316
+ predecessor: { promptVersion: "operator-v31", toolsetVersion: "operator-tools-v15" }
18317
+ }
18318
+ ];
18319
+ var ACCOUNT_OPERATOR_OOB_PROFILE_SUCCESSIONS = [
18320
+ {
18321
+ successor: { promptVersion: "operator-v33", toolsetVersion: "operator-tools-v17" },
18322
+ predecessor: { promptVersion: "operator-v34", toolsetVersion: "operator-tools-v18" }
18323
+ },
18324
+ {
18325
+ successor: { promptVersion: "operator-v35", toolsetVersion: "operator-tools-v19" },
18326
+ predecessor: { promptVersion: "operator-v34", toolsetVersion: "operator-tools-v18" }
18327
+ }
18328
+ ];
18287
18329
  var ALL_OOB_PROFILE_SUCCESSIONS = [
18288
18330
  ...OOB_PROFILE_SUCCESSIONS,
18289
18331
  ...STAFF_NOTIFICATION_OOB_PROFILE_SUCCESSIONS,
@@ -18301,7 +18343,9 @@ var ALL_OOB_PROFILE_SUCCESSIONS = [
18301
18343
  ...VALIDATION_V40_OOB_PROFILE_SUCCESSIONS,
18302
18344
  ...WRITER_REPRO_OOB_PROFILE_SUCCESSIONS,
18303
18345
  ...RETEST_REPRO_OOB_PROFILE_SUCCESSIONS,
18304
- ...STAGE_RETIRED_OOB_PROFILE_SUCCESSIONS
18346
+ ...STAGE_RETIRED_OOB_PROFILE_SUCCESSIONS,
18347
+ ...ENVIRONMENT_AWARE_OPERATOR_OOB_PROFILE_SUCCESSIONS,
18348
+ ...ACCOUNT_OPERATOR_OOB_PROFILE_SUCCESSIONS
18305
18349
  ];
18306
18350
  var OOB_CAPABLE_TOOLSETS = new Set(
18307
18351
  ALL_OOB_PROFILE_SUCCESSIONS.map(({ successor }) => successor.toolsetVersion)
@@ -18326,9 +18370,10 @@ var STAFF_NOTIFICATION_TOOLSET_VERSIONS = /* @__PURE__ */ new Set([
18326
18370
  "report-tools-bsk-v3",
18327
18371
  "report-tools-v4",
18328
18372
  "report-tools-bsk-v4",
18329
- // API_COVERAGE_TOOLSET_VERSION and its predecessor, written out because they are declared below.
18373
+ // API_COVERAGE_TOOLSET_VERSION and its predecessors, written out because they are declared below.
18330
18374
  "api-coverage-tools-v1",
18331
18375
  "api-coverage-tools-v2",
18376
+ "api-coverage-tools-v3",
18332
18377
  // SETUP_TOOLSET_VERSION, written out for the same reason.
18333
18378
  "setup-tools-v1",
18334
18379
  ...STAFF_NOTIFICATION_OOB_PROFILE_SUCCESSIONS.flatMap(({ successor, predecessor }) => [
@@ -18343,6 +18388,10 @@ var STAFF_NOTIFICATION_TOOLSET_VERSIONS = /* @__PURE__ */ new Set([
18343
18388
  predecessor.toolsetVersion,
18344
18389
  successor.toolsetVersion
18345
18390
  ]),
18391
+ ...ACCOUNT_OPERATOR_OOB_PROFILE_SUCCESSIONS.flatMap(({ successor, predecessor }) => [
18392
+ predecessor.toolsetVersion,
18393
+ successor.toolsetVersion
18394
+ ]),
18346
18395
  ...FEATURE_COVERAGE_OOB_PROFILE_SUCCESSIONS.flatMap(({ successor, predecessor }) => [
18347
18396
  predecessor.toolsetVersion,
18348
18397
  successor.toolsetVersion
@@ -23294,8 +23343,8 @@ var CreateAgentSessionRequestSchema = external_exports.discriminatedUnion("type"
23294
23343
  codeUploadId: AndroidBuildUnderTestSchema,
23295
23344
  /**
23296
23345
  * REVIEW launches the review session: an operator session that premise-checks every
23297
- * unpublished finding before its first turn and does not publish. Claude providers only
23298
- * (`REVIEW_PROVIDER_UNSUPPORTED`).
23346
+ * unpublished finding before its first turn and does not publish. Its premise checks run on
23347
+ * the session's provider.
23299
23348
  */
23300
23349
  purpose: OperatorSessionPurpose.optional()
23301
23350
  }),
@@ -23382,6 +23431,10 @@ var ResumeAgentSessionRequestSchema = external_exports.object({
23382
23431
  * keeps the row's current target. */
23383
23432
  launchTarget: AgentSessionLaunchTarget.optional()
23384
23433
  });
23434
+ var SetAgentSessionBudgetRequestSchema = external_exports.object({
23435
+ /** The session's new lifetime ceiling, past spend included. Must exceed recorded spend. */
23436
+ maxUsd: external_exports.number().positive().max(MAX_AGENT_SESSION_MAX_USD)
23437
+ });
23385
23438
  var ClaimLocalLaunchResponseSchema = external_exports.object({
23386
23439
  agentSessionId: external_exports.uuid(),
23387
23440
  runAttemptId: external_exports.uuid(),
@@ -23592,6 +23645,20 @@ var OperatorPentestContextSchema = external_exports.object({
23592
23645
  reason: external_exports.string().nullable()
23593
23646
  })
23594
23647
  ),
23648
+ /**
23649
+ * Features excluded from this engagement, read at call time: an exclusion staff add after launch
23650
+ * reaches a running session here, never through its brief. `handle` is `<ordinal>/<slug>`, as
23651
+ * the brief's do-not-test list names it. Optional for the same host raw-cast reason as
23652
+ * `applications`.
23653
+ */
23654
+ excludedFeatures: external_exports.array(
23655
+ external_exports.object({
23656
+ handle: external_exports.string(),
23657
+ name: external_exports.string(),
23658
+ reason: external_exports.string(),
23659
+ endpoints: external_exports.array(external_exports.string())
23660
+ })
23661
+ ).optional(),
23595
23662
  // From the session's immutable repository pins (what the agent has checked out).
23596
23663
  repositories: external_exports.array(
23597
23664
  external_exports.object({
@@ -23894,6 +23961,22 @@ var agentSessionContract = c2.router({
23894
23961
  500: ErrorResponseSchema
23895
23962
  }
23896
23963
  },
23964
+ setBudget: {
23965
+ method: "POST",
23966
+ path: "/organizations/:organizationId/pentests/:pentestId/agent-sessions/:agentSessionId/budget",
23967
+ pathParams: AgentSessionIdParamsSchema,
23968
+ body: SetAgentSessionBudgetRequestSchema,
23969
+ responses: {
23970
+ 200: AgentSessionDetailSchema,
23971
+ 400: ErrorResponseSchema,
23972
+ 401: ErrorResponseSchema,
23973
+ 403: ErrorResponseSchema,
23974
+ 404: ErrorResponseSchema,
23975
+ // errorCode names the refusal; SESSION_NOT_LIVE means resume with the budget instead.
23976
+ 409: ErrorResponseSchema,
23977
+ 500: ErrorResponseSchema
23978
+ }
23979
+ },
23897
23980
  // The host image this environment's agent-session task definition runs. Reads
23898
23981
  // config, mints nothing: it exists so a staff CLI can pull the image BEFORE it
23899
23982
  // creates or resumes a LOCAL session, keeping the boot deadline off the pull.
@@ -25855,7 +25938,7 @@ var CreateLinearTicketsRequestSchema = external_exports.object({
25855
25938
  findingIds: external_exports.array(external_exports.string().min(1).max(64)).min(1).max(50),
25856
25939
  /** Single-finding requests only — a title with >1 finding is a 400. */
25857
25940
  title: external_exports.string().min(1).max(255).optional(),
25858
- /** Destination overrides — ADMIN ONLY (MANAGE_ORGANIZATION); a non-admin sending any is a 403. */
25941
+ /** Destination overrides need MANAGE_INTEGRATIONS; anyone else sending one gets a 403. */
25859
25942
  teamId: linearEntityId.optional(),
25860
25943
  projectId: linearEntityId.nullable().optional(),
25861
25944
  labelIds: external_exports.array(linearEntityId).max(10).optional()
@@ -25913,9 +25996,9 @@ var LinearTicketContextResponseSchema = external_exports.object({
25913
25996
  labelIds: external_exports.array(external_exports.string()),
25914
25997
  labelNames: external_exports.array(external_exports.string())
25915
25998
  }).nullable(),
25916
- /** True only for MANAGE_ORGANIZATION callers — gates the destination picker. */
25999
+ /** True only for MANAGE_INTEGRATIONS callers; gates the destination picker. */
25917
26000
  canOverrideDestination: external_exports.boolean(),
25918
- /** Populated only for admins (a non-admin gets the resolved defaults, not a workspace inventory). */
26001
+ /** Populated only when canOverrideDestination; anyone else gets the resolved defaults, not a workspace inventory. */
25919
26002
  teams: external_exports.array(TicketContextTeam),
25920
26003
  projects: external_exports.array(TicketContextProject),
25921
26004
  labels: external_exports.array(TicketContextLabel)
@@ -28864,13 +28947,13 @@ var StorageStateSchema = external_exports.object({
28864
28947
  } catch {
28865
28948
  return;
28866
28949
  }
28867
- s.cookies.forEach((c84, i2) => {
28868
- const d = c84.domain.replace(/^\./, "").toLowerCase();
28950
+ s.cookies.forEach((c86, i2) => {
28951
+ const d = c86.domain.replace(/^\./, "").toLowerCase();
28869
28952
  if (d !== host && !host.endsWith(`.${d}`)) {
28870
28953
  ctx.addIssue({
28871
28954
  code: "custom",
28872
28955
  path: ["cookies", i2, "domain"],
28873
- message: `cookie domain "${c84.domain}" is not the capture origin host "${host}" or a parent domain of it`
28956
+ message: `cookie domain "${c86.domain}" is not the capture origin host "${host}" or a parent domain of it`
28874
28957
  });
28875
28958
  }
28876
28959
  });
@@ -29476,7 +29559,7 @@ var AgentSessionGitSourceGrantSchema = external_exports.object({
29476
29559
  ref: external_exports.string().min(1).max(255),
29477
29560
  /** Immutable pin — the host checks out exactly this commit. */
29478
29561
  commitSha: external_exports.string().regex(/^[a-f0-9]{40}$/i),
29479
- /** Bare clone URL (no credentials — the host injects via URL userinfo). */
29562
+ /** Bare clone URL (no credentials — the host supplies the token via GIT_ASKPASS). */
29480
29563
  cloneUrl: external_exports.url(),
29481
29564
  /**
29482
29565
  * Short-lived scoped read token. Never logged, never placed in any environ;
@@ -29485,9 +29568,9 @@ var AgentSessionGitSourceGrantSchema = external_exports.object({
29485
29568
  */
29486
29569
  cloneToken: external_exports.string().min(1),
29487
29570
  /**
29488
- * Git credential username for the clone. GitHub (`x-access-token`) rides URL
29489
- * userinfo; Bitbucket (`x-token-auth`) and GitLab (`oauth2`) ride GIT_ASKPASS
29490
- * (never the URL). The default keeps a pre-Bitbucket host safe — but the host
29571
+ * Git credential username for the clone: `x-access-token` (GitHub),
29572
+ * `x-token-auth` (Bitbucket) or `oauth2` (GitLab). The token always rides
29573
+ * GIT_ASKPASS, never the URL. The default keeps a pre-Bitbucket host safe — but the host
29491
29574
  * raw-casts this response, so the load-bearing fallback is the host's
29492
29575
  * use-site default.
29493
29576
  */
@@ -30435,7 +30518,12 @@ var SessionCredentialsResponseSchema = external_exports.object({
30435
30518
  browserAllowedBaseUrls: external_exports.array(external_exports.string()),
30436
30519
  browserExclusions: external_exports.array(external_exports.string()),
30437
30520
  /** API_COVERAGE only; null otherwise. Range checks stay with the host's rate parser. */
30438
- requestRate: external_exports.object({ perSec: external_exports.number(), burst: external_exports.number() }).nullable()
30521
+ requestRate: external_exports.object({ perSec: external_exports.number(), burst: external_exports.number() }).nullable(),
30522
+ /**
30523
+ * Labels of credentials the resolver could not include this time (a failed decrypt). A host
30524
+ * refreshing mid-run keeps its previous entries for these instead of reading them as deleted.
30525
+ */
30526
+ skippedLabels: external_exports.array(external_exports.string()).default([])
30439
30527
  });
30440
30528
  var ConvexReconRequestSchema = external_exports.object({
30441
30529
  runVersion: external_exports.number().int().nonnegative()
@@ -41023,7 +41111,12 @@ var CoverageGridResponseSchema = external_exports.object({
41023
41111
  orphanFeatures: external_exports.array(CoverageOrphanFeatureSchema).default([]),
41024
41112
  unplaced: external_exports.array(CoverageUnplacedRowSchema).default([]),
41025
41113
  /** When the engagement started: a feature retired before it is not part of this engagement. */
41026
- pentestStartedAt: external_exports.coerce.date().nullable().default(null)
41114
+ pentestStartedAt: external_exports.coerce.date().nullable().default(null),
41115
+ /**
41116
+ * When a closed engagement ended; null while it is open. A feature first recorded after it
41117
+ * was never part of this engagement.
41118
+ */
41119
+ pentestClosedAt: external_exports.coerce.date().nullable().default(null)
41027
41120
  });
41028
41121
 
41029
41122
  // ../../packages/api-contracts/src/contracts/v1/pentest-coverage.ts
@@ -43623,6 +43716,26 @@ var SetStaffOrganizationPentestCustomerVisibilityRequestSchema = external_export
43623
43716
  var RenameStaffOrganizationPentestRequestSchema = external_exports.object({
43624
43717
  title: external_exports.string().trim().min(1, "Title is required").max(500, "Title must be 500 characters or fewer")
43625
43718
  }).strict();
43719
+ var SetStaffOrganizationPentestStatusRequestSchema = external_exports.object({
43720
+ status: PentestStatus,
43721
+ reason: external_exports.string().trim().min(1, "Reason cannot be blank").max(2e3, "Reason must be 2000 characters or fewer").optional()
43722
+ }).strict();
43723
+ var CreateStaffPocDraftRequestSchema = external_exports.object({
43724
+ homepageUrl: external_exports.url({ protocol: /^https?$/ }).max(2048).optional()
43725
+ }).strict();
43726
+ var LaunchStaffPocSetupRequestSchema = external_exports.object({
43727
+ notes: external_exports.string().trim().min(1).max(MAX_SETUP_NOTES_CHARS).optional()
43728
+ }).strict();
43729
+ var LaunchStaffPocSetupResponseSchema = external_exports.object({
43730
+ agentSessionId: external_exports.uuid(),
43731
+ /** Null when Slack is not configured in this environment. */
43732
+ slack: external_exports.object({
43733
+ channelId: external_exports.string(),
43734
+ /** Null when Slack did not return the channel's name. */
43735
+ channelName: external_exports.string().nullable(),
43736
+ url: external_exports.url()
43737
+ }).strict().nullable()
43738
+ }).strict();
43626
43739
 
43627
43740
  // ../../packages/api-contracts/src/contracts/v1/staff-pentests.ts
43628
43741
  var c58 = initContract();
@@ -43683,6 +43796,161 @@ var staffPentestsContract = c58.router({
43683
43796
  500: ErrorResponseSchema
43684
43797
  },
43685
43798
  summary: "Hide a pentest from the customer or show it again (Trace staff only)"
43799
+ },
43800
+ setStaffOrganizationPentestStatus: {
43801
+ method: "POST",
43802
+ path: "/organizations/:organizationId/staff/pentests/:pentestId/status",
43803
+ pathParams: StaffOrganizationPentestParamsSchema,
43804
+ body: SetStaffOrganizationPentestStatusRequestSchema,
43805
+ responses: {
43806
+ 200: StaffOrganizationPentestSummarySchema,
43807
+ 400: ErrorResponseSchema,
43808
+ 401: ErrorResponseSchema,
43809
+ 403: ErrorResponseSchema,
43810
+ 404: ErrorResponseSchema,
43811
+ // A lifecycle rule refused the move; the rules and their codes live in
43812
+ // `PentestService.update`.
43813
+ 409: ErrorResponseSchema,
43814
+ 500: ErrorResponseSchema
43815
+ },
43816
+ summary: "Set a pentest\u2019s lifecycle status from the admin console (Trace staff only)"
43817
+ },
43818
+ createStaffPocDraft: {
43819
+ method: "POST",
43820
+ path: "/organizations/:organizationId/staff/pentests/poc",
43821
+ pathParams: OrgIdParamsSchema,
43822
+ body: CreateStaffPocDraftRequestSchema,
43823
+ // Error codes: StaffPocErrorCodes (staff-pentests.api.ts).
43824
+ responses: {
43825
+ 201: StaffOrganizationPentestSummarySchema,
43826
+ 400: ErrorResponseSchema,
43827
+ 401: ErrorResponseSchema,
43828
+ 403: ErrorResponseSchema,
43829
+ 404: ErrorResponseSchema,
43830
+ 409: ErrorResponseSchema,
43831
+ 500: ErrorResponseSchema
43832
+ },
43833
+ summary: "Create a hidden PoC draft engagement (Trace staff only)"
43834
+ },
43835
+ launchStaffPocSetup: {
43836
+ method: "POST",
43837
+ path: "/organizations/:organizationId/staff/pentests/:pentestId/setup-session",
43838
+ pathParams: StaffOrganizationPentestParamsSchema,
43839
+ body: LaunchStaffPocSetupRequestSchema,
43840
+ // Error codes: StaffPocErrorCodes (staff-pentests.api.ts), plus the SETUP launch refusals
43841
+ // (SETUP_REQUIRES_SCOPING, SETUP_AFTER_TESTING_STARTED) and a 429 at the session cap.
43842
+ responses: {
43843
+ 201: LaunchStaffPocSetupResponseSchema,
43844
+ 400: ErrorResponseSchema,
43845
+ 401: ErrorResponseSchema,
43846
+ 403: ErrorResponseSchema,
43847
+ 404: ErrorResponseSchema,
43848
+ 409: ErrorResponseSchema,
43849
+ 429: ErrorResponseSchema,
43850
+ 500: ErrorResponseSchema,
43851
+ 503: ErrorResponseSchema
43852
+ },
43853
+ summary: "Bind a PoC engagement's Slack and launch its SETUP session (Trace staff only)"
43854
+ }
43855
+ });
43856
+
43857
+ // ../../packages/api-contracts/src/schemas/api/v1/staff-products.api.ts
43858
+ var StaffProductParamsSchema = OrgIdParamsSchema.extend({
43859
+ productGroupId: external_exports.uuid()
43860
+ });
43861
+ var StaffProductApplicationParamsSchema = OrgIdParamsSchema.extend({
43862
+ applicationId: external_exports.uuid()
43863
+ });
43864
+ var PRODUCT_NAME_MAX_CHARS = 200;
43865
+ var ProductNameSchema = external_exports.string().trim().min(1).max(PRODUCT_NAME_MAX_CHARS);
43866
+ var StaffProductApplicationSchema = external_exports.object({
43867
+ applicationId: external_exports.uuid(),
43868
+ name: external_exports.string(),
43869
+ type: external_exports.enum(ApplicationTypeValues),
43870
+ /** Tells same-named applications apart; null for types without a URL. */
43871
+ applicationUrl: external_exports.string().nullable(),
43872
+ /** Applications this one uses, sorted by their name. */
43873
+ uses: external_exports.array(external_exports.uuid())
43874
+ }).strict();
43875
+ var StaffProductSchema = external_exports.object({
43876
+ productGroupId: external_exports.uuid(),
43877
+ name: external_exports.string(),
43878
+ applications: external_exports.array(StaffProductApplicationSchema)
43879
+ }).strict();
43880
+ var StaffProductsResponseSchema = external_exports.object({
43881
+ products: external_exports.array(StaffProductSchema)
43882
+ }).strict();
43883
+ var RenameStaffProductRequestSchema = external_exports.object({ name: ProductNameSchema }).strict();
43884
+ var MoveStaffApplicationRequestSchema = external_exports.union([
43885
+ external_exports.object({ productGroupId: external_exports.uuid() }).strict(),
43886
+ external_exports.object({ newProductName: ProductNameSchema }).strict()
43887
+ ]);
43888
+ var SetStaffApplicationDependenciesRequestSchema = external_exports.object({
43889
+ toApplicationIds: external_exports.array(external_exports.uuid()).max(100).refine((ids) => new Set(ids).size === ids.length, {
43890
+ message: "Application ids must be unique"
43891
+ })
43892
+ }).strict();
43893
+
43894
+ // ../../packages/api-contracts/src/contracts/v1/staff-products.ts
43895
+ var c59 = initContract();
43896
+ var staffProductsContract = c59.router({
43897
+ listStaffProducts: {
43898
+ method: "GET",
43899
+ path: "/organizations/:organizationId/staff/products",
43900
+ pathParams: OrgIdParamsSchema,
43901
+ responses: {
43902
+ 200: StaffProductsResponseSchema,
43903
+ 401: ErrorResponseSchema,
43904
+ 403: ErrorResponseSchema,
43905
+ 404: ErrorResponseSchema,
43906
+ 500: ErrorResponseSchema
43907
+ },
43908
+ summary: "List an organization\u2019s products and their applications (Trace staff only)"
43909
+ },
43910
+ renameStaffProduct: {
43911
+ method: "PATCH",
43912
+ path: "/organizations/:organizationId/staff/products/:productGroupId",
43913
+ pathParams: StaffProductParamsSchema,
43914
+ body: RenameStaffProductRequestSchema,
43915
+ responses: {
43916
+ 200: StaffProductsResponseSchema,
43917
+ 400: ErrorResponseSchema,
43918
+ 401: ErrorResponseSchema,
43919
+ 403: ErrorResponseSchema,
43920
+ 404: ErrorResponseSchema,
43921
+ 500: ErrorResponseSchema
43922
+ },
43923
+ summary: "Rename a product (Trace staff only)"
43924
+ },
43925
+ moveStaffApplication: {
43926
+ method: "PUT",
43927
+ path: "/organizations/:organizationId/staff/applications/:applicationId/product",
43928
+ pathParams: StaffProductApplicationParamsSchema,
43929
+ body: MoveStaffApplicationRequestSchema,
43930
+ responses: {
43931
+ 200: StaffProductsResponseSchema,
43932
+ 400: ErrorResponseSchema,
43933
+ 401: ErrorResponseSchema,
43934
+ 403: ErrorResponseSchema,
43935
+ 404: ErrorResponseSchema,
43936
+ 500: ErrorResponseSchema
43937
+ },
43938
+ summary: "Move an application into another or a new product (Trace staff only)"
43939
+ },
43940
+ setStaffApplicationDependencies: {
43941
+ method: "PUT",
43942
+ path: "/organizations/:organizationId/staff/applications/:applicationId/dependencies",
43943
+ pathParams: StaffProductApplicationParamsSchema,
43944
+ body: SetStaffApplicationDependenciesRequestSchema,
43945
+ responses: {
43946
+ 200: StaffProductsResponseSchema,
43947
+ 400: ErrorResponseSchema,
43948
+ 401: ErrorResponseSchema,
43949
+ 403: ErrorResponseSchema,
43950
+ 404: ErrorResponseSchema,
43951
+ 500: ErrorResponseSchema
43952
+ },
43953
+ summary: "Replace the applications an application uses (Trace staff only)"
43686
43954
  }
43687
43955
  });
43688
43956
 
@@ -43793,8 +44061,8 @@ var StaffQueueResponseSchema = external_exports.object({
43793
44061
  }).strict();
43794
44062
 
43795
44063
  // ../../packages/api-contracts/src/contracts/v1/staff-queue.ts
43796
- var c59 = initContract();
43797
- var staffQueueContract = c59.router({
44064
+ var c60 = initContract();
44065
+ var staffQueueContract = c60.router({
43798
44066
  getStaffQueue: {
43799
44067
  method: "GET",
43800
44068
  path: "/staff/queue",
@@ -43858,8 +44126,8 @@ var UpdateTestIntegrationRequestSchema = external_exports.object({
43858
44126
  }).strict();
43859
44127
 
43860
44128
  // ../../packages/api-contracts/src/contracts/v1/test-integration.ts
43861
- var c60 = initContract();
43862
- var testIntegrationContract = c60.router({
44129
+ var c61 = initContract();
44130
+ var testIntegrationContract = c61.router({
43863
44131
  // -- Fixture CRUD (org-less, staff-only) ----------------------------------
43864
44132
  listTestIntegrations: {
43865
44133
  method: "GET",
@@ -43939,7 +44207,7 @@ var testIntegrationContract = c60.router({
43939
44207
  method: "POST",
43940
44208
  path: "/staff/test-integrations/:testIntegrationId/mfa/code",
43941
44209
  pathParams: TestIntegrationIdParamsSchema,
43942
- body: c60.noBody(),
44210
+ body: c61.noBody(),
43943
44211
  responses: {
43944
44212
  200: CredentialMfaCodeResponseSchema,
43945
44213
  401: ErrorResponseSchema,
@@ -44050,8 +44318,8 @@ var MyContextResponseSchema = external_exports.object({
44050
44318
  });
44051
44319
 
44052
44320
  // ../../packages/api-contracts/src/contracts/v1/user.ts
44053
- var c61 = initContract();
44054
- var userContract = c61.router({
44321
+ var c62 = initContract();
44322
+ var userContract = c62.router({
44055
44323
  getStaffProfile: {
44056
44324
  method: "GET",
44057
44325
  path: "/staff/profile",
@@ -44166,8 +44434,8 @@ var VantaIntegrationResponseSchema = external_exports.object({
44166
44434
  });
44167
44435
 
44168
44436
  // ../../packages/api-contracts/src/contracts/v1/vanta-integration.ts
44169
- var c62 = initContract();
44170
- var vantaIntegrationContract = c62.router({
44437
+ var c63 = initContract();
44438
+ var vantaIntegrationContract = c63.router({
44171
44439
  createVantaIntegration: {
44172
44440
  method: "POST",
44173
44441
  path: "/organizations/:organizationId/vanta-integration",
@@ -44528,8 +44796,8 @@ var VerifySharedVulnerabilityEmailCodeResponseSchema = external_exports.object({
44528
44796
  });
44529
44797
 
44530
44798
  // ../../packages/api-contracts/src/contracts/v1/vulnerability.ts
44531
- var c63 = initContract();
44532
- var vulnerabilityContract = c63.router({
44799
+ var c64 = initContract();
44800
+ var vulnerabilityContract = c64.router({
44533
44801
  filterVulnerabilities: {
44534
44802
  method: "POST",
44535
44803
  path: "/organizations/:organizationId/vulnerabilities/filter",
@@ -44754,8 +45022,8 @@ var UpdateVulnerabilityNoteRequestSchema = external_exports.object({
44754
45022
  });
44755
45023
 
44756
45024
  // ../../packages/api-contracts/src/contracts/v1/vulnerability-note.ts
44757
- var c64 = initContract();
44758
- var vulnerabilityNoteContract = c64.router({
45025
+ var c65 = initContract();
45026
+ var vulnerabilityNoteContract = c65.router({
44759
45027
  listNotes: {
44760
45028
  method: "GET",
44761
45029
  path: "/organizations/:organizationId/vulnerabilities/:vulnerabilityId/notes",
@@ -44936,8 +45204,8 @@ var ListWebhookDeliveriesResponseSchema = external_exports.object({
44936
45204
  });
44937
45205
 
44938
45206
  // ../../packages/api-contracts/src/contracts/v1/webhook-settings.ts
44939
- var c65 = initContract();
44940
- var webhookSettingsContract = c65.router({
45207
+ var c66 = initContract();
45208
+ var webhookSettingsContract = c66.router({
44941
45209
  listWebhookEndpoints: {
44942
45210
  method: "GET",
44943
45211
  path: "/organizations/:organizationId/webhook-settings/endpoints",
@@ -45074,9 +45342,31 @@ var webhookSettingsContract = c65.router({
45074
45342
  }
45075
45343
  });
45076
45344
 
45345
+ // ../../packages/api-contracts/src/contracts/v1/ws.ts
45346
+ var c67 = initContract();
45347
+ var WsTicketResponseSchema = external_exports.object({
45348
+ /** Opaque, single-use, bound to the caller and the request's organization. */
45349
+ ticket: external_exports.string(),
45350
+ expiresInSeconds: external_exports.number().int().positive()
45351
+ });
45352
+ var wsContract = c67.router({
45353
+ createTicket: {
45354
+ method: "POST",
45355
+ path: "/ws/ticket",
45356
+ body: c67.noBody(),
45357
+ responses: {
45358
+ 200: WsTicketResponseSchema,
45359
+ 401: ErrorResponseSchema,
45360
+ 403: ErrorResponseSchema,
45361
+ 503: ErrorResponseSchema
45362
+ },
45363
+ summary: "Mint a short-lived, single-use ticket that opens the scan-updates WebSocket"
45364
+ }
45365
+ });
45366
+
45077
45367
  // ../../packages/api-contracts/src/contracts/v1/index.ts
45078
- var c66 = initContract();
45079
- var v1Contract = c66.router(
45368
+ var c68 = initContract();
45369
+ var v1Contract = c68.router(
45080
45370
  {
45081
45371
  admin: adminContract,
45082
45372
  cloudIntegration: cloudIntegrationContract,
@@ -45086,6 +45376,7 @@ var v1Contract = c66.router(
45086
45376
  organization: organizationContract,
45087
45377
  oobListener: oobListenerContract,
45088
45378
  user: userContract,
45379
+ ws: wsContract,
45089
45380
  repository: repositoryContract,
45090
45381
  github: githubContract,
45091
45382
  staffGithub: staffGithubContract,
@@ -45141,6 +45432,7 @@ var v1Contract = c66.router(
45141
45432
  staffPentestStats: staffPentestStatsContract,
45142
45433
  staffKnowledgeFacts: staffKnowledgeFactsContract,
45143
45434
  staffPentests: staffPentestsContract,
45435
+ staffProducts: staffProductsContract,
45144
45436
  staffQueue: staffQueueContract,
45145
45437
  organizationDisposal: organizationDisposalContract
45146
45438
  },
@@ -45150,13 +45442,13 @@ var v1Contract = c66.router(
45150
45442
  );
45151
45443
 
45152
45444
  // ../../packages/api-contracts/src/contracts/index.ts
45153
- var c67 = initContract();
45154
- var contract = c67.router({
45445
+ var c69 = initContract();
45446
+ var contract = c69.router({
45155
45447
  v1: v1Contract
45156
45448
  });
45157
45449
 
45158
45450
  // ../../packages/api-contracts/src/contracts/v1/desktop-host-internal.ts
45159
- var c68 = initContract();
45451
+ var c70 = initContract();
45160
45452
  var MAX_VMS_PER_SYNC = 16;
45161
45453
  var DesktopHostSyncRequestSchema = external_exports.object({
45162
45454
  /** Integer protocol the runner speaks. Below the api's minimum, the host gets
@@ -45181,7 +45473,7 @@ var DesktopHostSyncResponseSchema = external_exports.object({
45181
45473
  * id not listed here is deleted. */
45182
45474
  targets: external_exports.array(DesktopHostSyncTargetSchema).max(MAX_VMS_PER_SYNC)
45183
45475
  });
45184
- var desktopHostInternalContract = c68.router({
45476
+ var desktopHostInternalContract = c70.router({
45185
45477
  sync: {
45186
45478
  method: "POST",
45187
45479
  path: "/internal/v1/desktop-hosts/sync",
@@ -45204,7 +45496,10 @@ var OPENAI_SESSION_TYPES = [
45204
45496
  "RETEST",
45205
45497
  "REPORT",
45206
45498
  "RECON",
45207
- "API_COVERAGE"
45499
+ "API_COVERAGE",
45500
+ "PRODUCT_RECON",
45501
+ "CODE_REVIEW",
45502
+ "REMEDIATION"
45208
45503
  ];
45209
45504
  var AGENT_SESSION_MODELS = [
45210
45505
  {
@@ -45278,8 +45573,8 @@ var GitHubWebhookResponseSchema = external_exports.object({
45278
45573
  });
45279
45574
 
45280
45575
  // ../../packages/api-contracts/src/contracts/webhooks/github.ts
45281
- var c69 = initContract();
45282
- var githubWebhookContract = c69.router(
45576
+ var c71 = initContract();
45577
+ var githubWebhookContract = c71.router(
45283
45578
  {
45284
45579
  handleWebhook: {
45285
45580
  method: "POST",
@@ -45297,7 +45592,7 @@ var githubWebhookContract = c69.router(
45297
45592
  pathPrefix: "/webhooks"
45298
45593
  }
45299
45594
  );
45300
- var githubReadonlyWebhookContract = c69.router(
45595
+ var githubReadonlyWebhookContract = c71.router(
45301
45596
  {
45302
45597
  handleWebhook: {
45303
45598
  method: "POST",
@@ -45317,9 +45612,9 @@ var githubReadonlyWebhookContract = c69.router(
45317
45612
  );
45318
45613
 
45319
45614
  // ../../packages/api-contracts/src/contracts/webhooks/linear.ts
45320
- var c70 = initContract();
45615
+ var c72 = initContract();
45321
45616
  var LinearWebhookResponseSchema = external_exports.object({ ok: external_exports.boolean() });
45322
- var linearWebhookContract = c70.router(
45617
+ var linearWebhookContract = c72.router(
45323
45618
  {
45324
45619
  handleWebhook: {
45325
45620
  method: "POST",
@@ -45339,7 +45634,7 @@ var linearWebhookContract = c70.router(
45339
45634
  );
45340
45635
 
45341
45636
  // ../../packages/api-contracts/src/contracts/forge/bitbucket.ts
45342
- var c71 = initContract();
45637
+ var c73 = initContract();
45343
45638
  var InstallEventSchema = external_exports.object({
45344
45639
  eventType: external_exports.enum(["avi:forge:installed:app", "avi:forge:upgraded:app"]),
45345
45640
  context: external_exports.object({ cloudId: external_exports.string().min(1) }).loose(),
@@ -45368,7 +45663,7 @@ var ForgePairingCodeResponseSchema = external_exports.discriminatedUnion("state"
45368
45663
  expiresInSeconds: external_exports.number().int()
45369
45664
  })
45370
45665
  ]);
45371
- var forgeBitbucketContract = c71.router(
45666
+ var forgeBitbucketContract = c73.router(
45372
45667
  {
45373
45668
  lifecycle: {
45374
45669
  method: "POST",
@@ -45415,8 +45710,8 @@ var HealthResponseSchema = external_exports.object({
45415
45710
  });
45416
45711
 
45417
45712
  // ../../packages/api-contracts/src/contracts/health.ts
45418
- var c72 = initContract();
45419
- var healthContract = c72.router({
45713
+ var c74 = initContract();
45714
+ var healthContract = c74.router({
45420
45715
  getHealth: {
45421
45716
  method: "GET",
45422
45717
  path: "/health",
@@ -45428,8 +45723,8 @@ var healthContract = c72.router({
45428
45723
  });
45429
45724
 
45430
45725
  // ../../packages/api-contracts/src/contracts/v1/vulnerability-share.ts
45431
- var c73 = initContract();
45432
- var vulnerabilityShareContract = c73.router(
45726
+ var c75 = initContract();
45727
+ var vulnerabilityShareContract = c75.router(
45433
45728
  {
45434
45729
  resolveShareLink: {
45435
45730
  method: "GET",
@@ -45455,7 +45750,8 @@ var vulnerabilityShareContract = c73.router(
45455
45750
  401: ErrorResponseSchema,
45456
45751
  403: ErrorResponseSchema,
45457
45752
  404: ErrorResponseSchema,
45458
- 500: ErrorResponseSchema
45753
+ 500: ErrorResponseSchema,
45754
+ 503: ErrorResponseSchema
45459
45755
  }
45460
45756
  },
45461
45757
  verifyShareEmailCode: {
@@ -45479,8 +45775,8 @@ var vulnerabilityShareContract = c73.router(
45479
45775
  );
45480
45776
 
45481
45777
  // ../../packages/api-contracts/src/contracts/v1/attestation-verify.ts
45482
- var c74 = initContract();
45483
- var attestationVerifyContract = c74.router(
45778
+ var c76 = initContract();
45779
+ var attestationVerifyContract = c76.router(
45484
45780
  {
45485
45781
  verifyAttestation: {
45486
45782
  method: "GET",
@@ -45536,8 +45832,8 @@ var UpdatePrVulnerabilityRequestSchema = external_exports.object({
45536
45832
  });
45537
45833
 
45538
45834
  // ../../packages/api-contracts/src/contracts/v1/pr-vulnerability.ts
45539
- var c75 = initContract();
45540
- var prVulnerabilityContract = c75.router(
45835
+ var c77 = initContract();
45836
+ var prVulnerabilityContract = c77.router(
45541
45837
  {
45542
45838
  listPrVulnerabilities: {
45543
45839
  method: "GET",
@@ -45701,8 +45997,8 @@ var CoordinatorSessionSearchResponseSchema = external_exports.strictObject({
45701
45997
  });
45702
45998
 
45703
45999
  // ../../packages/api-contracts/src/contracts/v1/coordinator-inspection.ts
45704
- var c76 = initContract();
45705
- var coordinatorInspectionContract = c76.router({
46000
+ var c78 = initContract();
46001
+ var coordinatorInspectionContract = c78.router({
45706
46002
  sessionWindow: {
45707
46003
  method: "POST",
45708
46004
  path: "/internal/v1/agent-sessions/:agentSessionId/coordinator/session-window",
@@ -45744,7 +46040,7 @@ var coordinatorInspectionContract = c76.router({
45744
46040
  });
45745
46041
 
45746
46042
  // ../../packages/api-contracts/src/contracts/v1/pentest-coordinator-internal.ts
45747
- var c77 = initContract();
46043
+ var c79 = initContract();
45748
46044
  var params2 = external_exports.strictObject({ agentSessionId: external_exports.uuid() });
45749
46045
  var errors3 = {
45750
46046
  400: ErrorResponseSchema,
@@ -45757,7 +46053,7 @@ var errors3 = {
45757
46053
  500: ErrorResponseSchema,
45758
46054
  503: ErrorResponseSchema
45759
46055
  };
45760
- var pentestCoordinatorInternalContract = c77.router({
46056
+ var pentestCoordinatorInternalContract = c79.router({
45761
46057
  state: {
45762
46058
  method: "POST",
45763
46059
  path: "/internal/v1/agent-sessions/:agentSessionId/coordinator/state",
@@ -46004,8 +46300,8 @@ var CoordinatorCoverageResponseSchema = external_exports.strictObject({
46004
46300
  );
46005
46301
 
46006
46302
  // ../../packages/api-contracts/src/contracts/v1/coordinator-domain-read.ts
46007
- var c78 = initContract();
46008
- var coordinatorDomainReadContract = c78.router({
46303
+ var c80 = initContract();
46304
+ var coordinatorDomainReadContract = c80.router({
46009
46305
  coverage: {
46010
46306
  method: "POST",
46011
46307
  path: "/internal/v1/agent-sessions/:agentSessionId/coordinator/coverage",
@@ -46210,7 +46506,7 @@ var CoordinatorPublicDocumentationResponseSchema = CoordinatorContextTextReadRes
46210
46506
  var CoordinatorCustomerDocumentResponseSchema = CoordinatorContextTextReadResponseSchema;
46211
46507
 
46212
46508
  // ../../packages/api-contracts/src/contracts/v1/coordinator-context-read.ts
46213
- var c79 = initContract();
46509
+ var c81 = initContract();
46214
46510
  var errors4 = {
46215
46511
  400: ErrorResponseSchema,
46216
46512
  401: ErrorResponseSchema,
@@ -46222,7 +46518,7 @@ var errors4 = {
46222
46518
  500: ErrorResponseSchema,
46223
46519
  503: ErrorResponseSchema
46224
46520
  };
46225
- var coordinatorContextReadContract = c79.router({
46521
+ var coordinatorContextReadContract = c81.router({
46226
46522
  contextSources: {
46227
46523
  method: "POST",
46228
46524
  path: "/internal/v1/agent-sessions/:agentSessionId/coordinator/context-sources",
@@ -46456,19 +46752,19 @@ var COVERAGE_CLASSES = [
46456
46752
  },
46457
46753
  { taxonomy: "RECON", id: "RECON-15", name: "Deployment-platform config", shortLabel: "Deploy" }
46458
46754
  ];
46459
- var BY_ID = new Map(COVERAGE_CLASSES.map((c84) => [c84.id, c84]));
46755
+ var BY_ID = new Map(COVERAGE_CLASSES.map((c86) => [c86.id, c86]));
46460
46756
  function idsWhere(keep) {
46461
- return [OTHER_CLASS_ID, ...COVERAGE_CLASSES.filter(keep).map((c84) => c84.id)];
46757
+ return [OTHER_CLASS_ID, ...COVERAGE_CLASSES.filter(keep).map((c86) => c86.id)];
46462
46758
  }
46463
- var HUNTING_CLASS_IDS = idsWhere((c84) => c84.taxonomy !== "RECON");
46464
- var RECON_CLASS_IDS = idsWhere((c84) => c84.taxonomy === "RECON");
46759
+ var HUNTING_CLASS_IDS = idsWhere((c86) => c86.taxonomy !== "RECON");
46760
+ var RECON_CLASS_IDS = idsWhere((c86) => c86.taxonomy === "RECON");
46465
46761
  var HUNTING_ID_SET = new Set(HUNTING_CLASS_IDS);
46466
46762
  var RECON_ID_SET = new Set(RECON_CLASS_IDS);
46467
46763
  var TAXONOMY_ORDER = new Map(CoverageTaxonomyValues.map((t2, i2) => [t2, i2]));
46468
- var CLASS_ORDER = new Map(COVERAGE_CLASSES.map((c84, i2) => [c84.id, i2]));
46764
+ var CLASS_ORDER = new Map(COVERAGE_CLASSES.map((c86, i2) => [c86.id, i2]));
46469
46765
 
46470
46766
  // ../../packages/api-contracts/src/contracts/v1/api-coverage-internal.ts
46471
- var c80 = initContract();
46767
+ var c82 = initContract();
46472
46768
  var pathParams = external_exports.strictObject({ agentSessionId: external_exports.uuid() });
46473
46769
  var errors5 = {
46474
46770
  400: ErrorResponseSchema,
@@ -46478,7 +46774,7 @@ var errors5 = {
46478
46774
  500: ErrorResponseSchema
46479
46775
  };
46480
46776
  var base = "/internal/v1/agent-sessions/:agentSessionId/api-coverage";
46481
- var apiCoverageInternalContract = c80.router({
46777
+ var apiCoverageInternalContract = c82.router({
46482
46778
  upsertApiOperations: {
46483
46779
  method: "POST",
46484
46780
  path: `${base}/operations`,
@@ -46617,7 +46913,7 @@ var SetupNotesRequestSchema = external_exports.object({ runVersion: RunVersionSc
46617
46913
  var SetupNotesResponseSchema = external_exports.object({ notes: external_exports.string().nullable() });
46618
46914
 
46619
46915
  // ../../packages/api-contracts/src/contracts/v1/setup-session-internal.ts
46620
- var c81 = initContract();
46916
+ var c83 = initContract();
46621
46917
  var pathParams2 = external_exports.strictObject({ agentSessionId: external_exports.uuid() });
46622
46918
  var errors6 = {
46623
46919
  400: ErrorResponseSchema,
@@ -46629,7 +46925,7 @@ var errors6 = {
46629
46925
  503: ErrorResponseSchema
46630
46926
  };
46631
46927
  var base2 = "/internal/v1/agent-sessions/:agentSessionId/setup";
46632
- var setupSessionInternalContract = c81.router({
46928
+ var setupSessionInternalContract = c83.router({
46633
46929
  addSetupTarget: {
46634
46930
  method: "POST",
46635
46931
  path: `${base2}/target`,
@@ -46756,8 +47052,8 @@ var ListReviewPremiseChecksResponseSchema = external_exports.object({
46756
47052
  });
46757
47053
 
46758
47054
  // ../../packages/api-contracts/src/contracts/v1/premise-check-internal.ts
46759
- var c82 = initContract();
46760
- var premiseCheckInternalContract = c82.router({
47055
+ var c84 = initContract();
47056
+ var premiseCheckInternalContract = c84.router({
46761
47057
  listReviewPremiseChecks: {
46762
47058
  method: "POST",
46763
47059
  path: "/internal/v1/agent-sessions/:agentSessionId/premise-checks/list",
@@ -47581,6 +47877,7 @@ var PRODUCTION_API_URL = "https://api.securewithtrace.com";
47581
47877
  var UNAUTHORIZED_STATUS = 401;
47582
47878
  var REFRESH_AUTH_FAILURE_STATUS_CODES = /* @__PURE__ */ new Set([400, 401]);
47583
47879
  var MAX_REFRESH_ROTATION_RETRIES = 3;
47880
+ var TRACE_API_HOST_SUFFIX = ".securewithtrace.com";
47584
47881
  function getApiBaseUrl() {
47585
47882
  const raw = process.env["TRACE_API_URL"];
47586
47883
  if (!raw) {
@@ -47599,16 +47896,15 @@ function getApiBaseUrl() {
47599
47896
  console.error(colorWarning("Invalid TRACE_API_URL: only https:// is allowed. Using default."));
47600
47897
  return PRODUCTION_API_URL;
47601
47898
  }
47602
- const hostname3 = parsed.hostname;
47603
- if (hostname3 === "localhost" || hostname3 === "127.0.0.1" || hostname3 === "0.0.0.0" || hostname3 === "::1" || hostname3.startsWith("10.") || hostname3.startsWith("192.168.") || /^172\.(1[6-9]|2[0-9]|3[01])\./.test(hostname3)) {
47899
+ if (!parsed.hostname.endsWith(TRACE_API_HOST_SUFFIX)) {
47604
47900
  console.error(
47605
47901
  colorWarning(
47606
- "Invalid TRACE_API_URL: private/reserved hostnames are not allowed. Using default."
47902
+ `Ignoring TRACE_API_URL: '${parsed.hostname}' is not a Trace API host (*${TRACE_API_HOST_SUFFIX}). Using default.`
47607
47903
  )
47608
47904
  );
47609
47905
  return PRODUCTION_API_URL;
47610
47906
  }
47611
- return raw.replace(/\/+$/, "");
47907
+ return `${parsed.origin}${parsed.pathname}`.replace(/\/+$/, "");
47612
47908
  }
47613
47909
  function requireAuth() {
47614
47910
  const credential = loadCredentialOrExit();
@@ -47692,6 +47988,60 @@ function exitHumanPrincipalRequired() {
47692
47988
  console.error(colorError("This command requires a human login; run `tracecli auth login`."));
47693
47989
  process.exit(1);
47694
47990
  }
47991
+ function transportErrorCode(error48) {
47992
+ if (!(error48 instanceof Error)) {
47993
+ return null;
47994
+ }
47995
+ const codeOf = (value) => {
47996
+ if (typeof value !== "object" || value === null || !("code" in value)) {
47997
+ return null;
47998
+ }
47999
+ return typeof value.code === "string" && value.code.length > 0 ? value.code : null;
48000
+ };
48001
+ const code = codeOf(error48) ?? codeOf(error48.cause);
48002
+ if (code !== null) {
48003
+ return code;
48004
+ }
48005
+ return error48.name === "TypeError" && /^fetch failed/i.test(error48.message) ? "FETCH_FAILED" : null;
48006
+ }
48007
+ var TRANSPORT_ERROR_HINTS = {
48008
+ ENOTFOUND: "DNS lookup failed",
48009
+ EAI_AGAIN: "DNS lookup timed out",
48010
+ ECONNREFUSED: "connection refused",
48011
+ ECONNRESET: "connection reset",
48012
+ ETIMEDOUT: "connection timed out",
48013
+ ConnectionRefused: "connection refused",
48014
+ ConnectionClosed: "connection closed before a response arrived",
48015
+ FailedToOpenSocket: "could not open a socket",
48016
+ CERT_HAS_EXPIRED: "the server certificate has expired",
48017
+ UNABLE_TO_VERIFY_LEAF_SIGNATURE: "the server certificate could not be verified",
48018
+ DEPTH_ZERO_SELF_SIGNED_CERT: "the server presented a self-signed certificate",
48019
+ FETCH_FAILED: "the request never received a response"
48020
+ };
48021
+ function unreachableApiMessage(baseUrl, code) {
48022
+ const host = new URL(baseUrl).host;
48023
+ const detail = TRANSPORT_ERROR_HINTS[code] ?? "the request failed";
48024
+ return [
48025
+ `Couldn't reach the Trace API at ${host}: ${detail} (${code}).`,
48026
+ "Check that this machine can reach the internet. Sandboxed agents and CI runners often block",
48027
+ `outbound network access; allow ${host} and retry.`
48028
+ ].join("\n");
48029
+ }
48030
+ function exitUnreachableApi(code) {
48031
+ console.error(colorError(unreachableApiMessage(getApiBaseUrl(), code)));
48032
+ process.exit(1);
48033
+ }
48034
+ async function requestOrExit(request, token) {
48035
+ try {
48036
+ return await request(createClient(token));
48037
+ } catch (error48) {
48038
+ const code = transportErrorCode(error48);
48039
+ if (code !== null) {
48040
+ exitUnreachableApi(code);
48041
+ }
48042
+ throw error48;
48043
+ }
48044
+ }
47695
48045
  function createClient(token) {
47696
48046
  return initClient(v1Contract, {
47697
48047
  baseUrl: getApiBaseUrl(),
@@ -47819,7 +48169,7 @@ async function refreshStoredAccessTokenForOrganization(workosOrganizationId) {
47819
48169
  }
47820
48170
  async function authenticatedRequest(request) {
47821
48171
  const credential = loadCredentialOrExit();
47822
- const firstResult = await request(createClient(credential.token));
48172
+ const firstResult = await requestOrExit(request, credential.token);
47823
48173
  if (credential.kind === "api_key") {
47824
48174
  if (firstResult.status === UNAUTHORIZED_STATUS) {
47825
48175
  exitApiKeyUnauthorized();
@@ -47836,7 +48186,7 @@ async function authenticatedRequest(request) {
47836
48186
  if (!refreshedToken) {
47837
48187
  return firstResult;
47838
48188
  }
47839
- return request(createClient(refreshedToken));
48189
+ return requestOrExit(request, refreshedToken);
47840
48190
  }
47841
48191
  async function authenticatedRequestSafe(request) {
47842
48192
  let credential;
@@ -49259,7 +49609,7 @@ function createSetScanningBranchCommand() {
49259
49609
  return new Command("set-scanning-branch").description("Switch the scanning branch for one or every repository in the org").argument(
49260
49610
  "[repositoryUrl]",
49261
49611
  "Repository URL (omit when using --all-repos to switch every repo)"
49262
- ).requiredOption("--branch <name>", "New scanning branch name").option("--all-repos", "Apply the switch to every connected repository in the organization").option(
49612
+ ).requiredOption("--branch <name>", "New scanning branch name").option("--all-repos", "Apply the switch to every GitHub repository in the organization").option(
49263
49613
  "--migrate-open-vulns",
49264
49614
  "Relabel open vulnerabilities from the previous branch to the new branch"
49265
49615
  ).option("-o, --output <format>", "Output format: table (default), json", "table").addHelpText(
@@ -49580,6 +49930,62 @@ function resolveClearableUrl(value, clear, flag) {
49580
49930
  }
49581
49931
  return { value };
49582
49932
  }
49933
+ function normalizeRepositoryRole(input) {
49934
+ const candidate = input.trim().toUpperCase().replace(/-/g, "_");
49935
+ return ApplicationRepositoryRoleValues.find((role) => role === candidate) ?? null;
49936
+ }
49937
+ var REPOSITORY_ROLE_INPUTS = ApplicationRepositoryRoleValues.map(
49938
+ (role) => role.toLowerCase().replace(/_/g, "-")
49939
+ );
49940
+ function parseRepositoryRoles(inputs) {
49941
+ const picked = /* @__PURE__ */ new Set();
49942
+ for (const input of inputs) {
49943
+ const role = normalizeRepositoryRole(input);
49944
+ if (role === null) {
49945
+ return {
49946
+ error: `Invalid --role '${input}'. Use one of: ${REPOSITORY_ROLE_INPUTS.join(", ")}.`
49947
+ };
49948
+ }
49949
+ picked.add(role);
49950
+ }
49951
+ return { roles: ApplicationRepositoryRoleValues.filter((role) => picked.has(role)) };
49952
+ }
49953
+ function buildLinkRepositoryBody(input) {
49954
+ return {
49955
+ repositoryId: input.repositoryId,
49956
+ ...input.roles !== void 0 && input.roles.length > 0 ? { roles: [...input.roles] } : {},
49957
+ ...input.roleLabel !== void 0 ? { roleLabel: input.roleLabel } : {},
49958
+ ...input.repositoryPath !== void 0 ? { repositoryPath: input.repositoryPath } : {},
49959
+ ...input.scanEnabled !== void 0 ? { scanEnabled: input.scanEnabled } : {}
49960
+ };
49961
+ }
49962
+ function buildUpdateLinkBody(input) {
49963
+ return {
49964
+ ...input.roles !== void 0 ? { roles: [...input.roles] } : {},
49965
+ ...input.roleLabel !== void 0 ? { roleLabel: input.roleLabel } : {},
49966
+ ...input.repositoryPath !== void 0 ? { repositoryPath: input.repositoryPath } : {},
49967
+ ...input.scanEnabled !== void 0 ? { scanEnabled: input.scanEnabled } : {}
49968
+ };
49969
+ }
49970
+ function isEmptyLinkUpdate(body) {
49971
+ return Object.keys(body).length === 0;
49972
+ }
49973
+ function validateRoleLabel(roles, roleLabel) {
49974
+ if (roles === void 0 || roleLabel === void 0 || roleLabel === null) {
49975
+ return null;
49976
+ }
49977
+ return roles.includes("OTHER") ? null : "--role-label requires --role other.";
49978
+ }
49979
+ function findLinkForRepo(links, input) {
49980
+ if (isUuid(input)) {
49981
+ const id = input.toLowerCase();
49982
+ return links.find(
49983
+ (link) => link.applicationRepositoryId?.toLowerCase() === id || link.repositoryId.toLowerCase() === id
49984
+ ) ?? null;
49985
+ }
49986
+ const target = normalizeRepoUrlForMatch(resolveRepoUrl(input));
49987
+ return links.find((link) => normalizeRepoUrlForMatch(link.repositoryUrl) === target) ?? null;
49988
+ }
49583
49989
  function failWriteOp(action, status, body, notFoundLabel) {
49584
49990
  const { message, errorCode } = readErrorFields(body);
49585
49991
  if (status === UNAUTHORIZED_STATUS2) {
@@ -49620,6 +50026,28 @@ function failWriteOp(action, status, body, notFoundLabel) {
49620
50026
  );
49621
50027
  process.exit(1);
49622
50028
  }
50029
+ function failLinkOp(action, status, body) {
50030
+ const { message } = readErrorFields(body);
50031
+ if (status === UNAUTHORIZED_STATUS2) {
50032
+ exitAuthFailed();
50033
+ }
50034
+ if (status === 403) {
50035
+ console.error(
50036
+ colorError(
50037
+ "Repository links require an organization admin or Trace staff on a pentest-enabled organization, signed in with a browser session (API keys are rejected)."
50038
+ )
50039
+ );
50040
+ process.exit(1);
50041
+ }
50042
+ if (status === 404 || status === 409 || status === 400) {
50043
+ console.error(colorError(message ?? `Failed to ${action} (status ${status}).`));
50044
+ process.exit(1);
50045
+ }
50046
+ console.error(
50047
+ colorError(`Failed to ${action} repository (status ${status})${message ? `: ${message}` : "."}`)
50048
+ );
50049
+ process.exit(1);
50050
+ }
49623
50051
  function failReadOp(action, status, body, notFoundLabel) {
49624
50052
  const { message } = readErrorFields(body);
49625
50053
  if (status === UNAUTHORIZED_STATUS2) {
@@ -49681,12 +50109,12 @@ function buildApplicationsTable(apps) {
49681
50109
  repos: String(a2.repositories.length),
49682
50110
  url: a2.applicationUrl ? sanitizeDisplay(a2.applicationUrl) : "\u2014"
49683
50111
  }));
49684
- const idWidth = Math.max(2, ...cells.map((c84) => c84.id.length));
49685
- const nameWidth = Math.max(4, ...cells.map((c84) => c84.name.length));
49686
- const typeWidth = Math.max(4, ...cells.map((c84) => c84.type.length));
50112
+ const idWidth = Math.max(2, ...cells.map((c86) => c86.id.length));
50113
+ const nameWidth = Math.max(4, ...cells.map((c86) => c86.name.length));
50114
+ const typeWidth = Math.max(4, ...cells.map((c86) => c86.type.length));
49687
50115
  const header = `${"ID".padEnd(idWidth)} ${"NAME".padEnd(nameWidth)} ${"TYPE".padEnd(typeWidth)} REPOS URL`;
49688
50116
  const rows = cells.map(
49689
- (c84) => `${c84.id.padEnd(idWidth)} ${c84.name.padEnd(nameWidth)} ${c84.type.padEnd(typeWidth)} ${c84.repos.padStart(5)} ${c84.url}`
50117
+ (c86) => `${c86.id.padEnd(idWidth)} ${c86.name.padEnd(nameWidth)} ${c86.type.padEnd(typeWidth)} ${c86.repos.padStart(5)} ${c86.url}`
49690
50118
  );
49691
50119
  return { header, rows };
49692
50120
  }
@@ -49706,12 +50134,12 @@ function buildApplicationRepositoriesTable(links) {
49706
50134
  path: l2.repositoryPath ? sanitizeDisplay(l2.repositoryPath) : "\u2014",
49707
50135
  status: formatStatus(l2.repositoryStatus)
49708
50136
  }));
49709
- const nameWidth = Math.max(10, ...cells.map((c84) => c84.name.length));
49710
- const roleWidth = Math.max(4, ...cells.map((c84) => c84.role.length));
49711
- const pathWidth = Math.max(4, ...cells.map((c84) => c84.path.length));
50137
+ const nameWidth = Math.max(10, ...cells.map((c86) => c86.name.length));
50138
+ const roleWidth = Math.max(4, ...cells.map((c86) => c86.role.length));
50139
+ const pathWidth = Math.max(4, ...cells.map((c86) => c86.path.length));
49712
50140
  const header = `${"REPOSITORY".padEnd(nameWidth)} SCAN ${"ROLE".padEnd(roleWidth)} ${"PATH".padEnd(pathWidth)} STATUS`;
49713
50141
  const rows = cells.map(
49714
- (c84) => `${c84.name.padEnd(nameWidth)} ${c84.scan.padEnd(4)} ${c84.role.padEnd(roleWidth)} ${c84.path.padEnd(pathWidth)} ${c84.status}`
50142
+ (c86) => `${c86.name.padEnd(nameWidth)} ${c86.scan.padEnd(4)} ${c86.role.padEnd(roleWidth)} ${c86.path.padEnd(pathWidth)} ${c86.status}`
49715
50143
  );
49716
50144
  return { header, rows };
49717
50145
  }
@@ -49974,6 +50402,205 @@ async function runDelete(applicationId, options) {
49974
50402
  }
49975
50403
  console.log(colorSuccess(`Deleted application ${applicationId}`));
49976
50404
  }
50405
+ function renderLinkDetail(link) {
50406
+ const pad = 11;
50407
+ return [
50408
+ `${"Link ID:".padEnd(pad)} ${link.applicationRepositoryId ?? "\u2014"}`,
50409
+ `${"Repository:".padEnd(pad)} ${sanitizeDisplay(link.repositoryName)} (${link.repositoryId})`,
50410
+ `${"URL:".padEnd(pad)} ${sanitizeDisplay(link.repositoryUrl)}`,
50411
+ `${"Scan:".padEnd(pad)} ${link.scanEnabled ? "yes" : "no"}`,
50412
+ `${"Role:".padEnd(pad)} ${sanitizeDisplay(formatLinkRole(link.role, link.roleLabel, link.roles))}`,
50413
+ `${"Path:".padEnd(pad)} ${link.repositoryPath ? sanitizeDisplay(link.repositoryPath) : "\u2014"}`,
50414
+ `${"Status:".padEnd(pad)} ${formatStatus(link.repositoryStatus)}`
50415
+ ].join("\n");
50416
+ }
50417
+ function outputLink(link, output) {
50418
+ if (output === "json") {
50419
+ printJson(link);
50420
+ return;
50421
+ }
50422
+ if (output === "tsv") {
50423
+ printApplicationRepositoriesTsv([link]);
50424
+ return;
50425
+ }
50426
+ console.log(renderLinkDetail(link));
50427
+ }
50428
+ function exitInvalidApplicationId(applicationId) {
50429
+ console.error(colorError(`Invalid application id '${applicationId}'. Expected a UUID.`));
50430
+ process.exit(1);
50431
+ }
50432
+ async function resolveExistingLink(organizationId, applicationId, repoInput) {
50433
+ const result = await authenticatedRequest(
50434
+ (client) => client.application.listApplicationRepositories({ params: { organizationId, applicationId } })
50435
+ );
50436
+ if (result.status !== SUCCESS_STATUS) {
50437
+ failReadOp("list application repositories", result.status, result.body, applicationId);
50438
+ }
50439
+ const link = findLinkForRepo(result.body, repoInput);
50440
+ if (!link) {
50441
+ console.error(
50442
+ colorError(
50443
+ `Repository ${repoInput} is not linked to application ${applicationId}. Run \`tracecli application repos ${applicationId}\` to see its links.`
50444
+ )
50445
+ );
50446
+ process.exit(1);
50447
+ }
50448
+ if (link.applicationRepositoryId === null) {
50449
+ console.error(
50450
+ colorError(
50451
+ `Repository ${repoInput} is linked without a link id, so it can't be changed from the CLI. Edit it in the dashboard.`
50452
+ )
50453
+ );
50454
+ process.exit(1);
50455
+ }
50456
+ return { ...link, applicationRepositoryId: link.applicationRepositoryId };
50457
+ }
50458
+ async function runLink(applicationId, options) {
50459
+ const output = parseOutput(options.output);
50460
+ if (!isUuid(applicationId)) {
50461
+ exitInvalidApplicationId(applicationId);
50462
+ }
50463
+ const parsedRoles = parseRepositoryRoles(options.role ?? []);
50464
+ if ("error" in parsedRoles) {
50465
+ console.error(colorError(parsedRoles.error));
50466
+ process.exit(1);
50467
+ }
50468
+ const roles = options.role !== void 0 ? parsedRoles.roles : void 0;
50469
+ const labelError = validateRoleLabel(roles ?? [], options.roleLabel);
50470
+ if (labelError) {
50471
+ console.error(colorError(labelError));
50472
+ process.exit(1);
50473
+ }
50474
+ const organizationId = await resolveOrganizationId();
50475
+ const repositoryId = await resolveRepositoryId(organizationId, options.repo);
50476
+ const body = buildLinkRepositoryBody({
50477
+ repositoryId,
50478
+ roles,
50479
+ roleLabel: options.roleLabel,
50480
+ repositoryPath: options.repoPath,
50481
+ // Commander defaults a lone `--no-scan` to true; only an explicit opt-out
50482
+ // goes on the wire so the API's own default applies otherwise.
50483
+ scanEnabled: options.scan === false ? false : void 0
50484
+ });
50485
+ const result = await authenticatedRequest(
50486
+ (client) => client.application.createApplicationRepository({
50487
+ params: { organizationId, applicationId },
50488
+ body
50489
+ })
50490
+ );
50491
+ if (result.status !== CREATED_STATUS) {
50492
+ failLinkOp("link", result.status, result.body);
50493
+ }
50494
+ if (output === "table") {
50495
+ console.log(
50496
+ colorSuccess(
50497
+ `Linked ${sanitizeDisplay(result.body.repositoryName)} to application ${applicationId}`
50498
+ )
50499
+ );
50500
+ }
50501
+ outputLink(result.body, output);
50502
+ }
50503
+ var UPDATE_LINK_FLAGS = "--role, --clear-roles, --role-label, --clear-role-label, --repo-path, --clear-repo-path, --scan, --no-scan";
50504
+ async function runUpdateLink(applicationId, options) {
50505
+ const output = parseOutput(options.output);
50506
+ if (!isUuid(applicationId)) {
50507
+ exitInvalidApplicationId(applicationId);
50508
+ }
50509
+ if (options.role !== void 0 && options.clearRoles) {
50510
+ console.error(colorError("Pass only one of --role / --clear-roles."));
50511
+ process.exit(1);
50512
+ }
50513
+ const parsedRoles = parseRepositoryRoles(options.role ?? []);
50514
+ if ("error" in parsedRoles) {
50515
+ console.error(colorError(parsedRoles.error));
50516
+ process.exit(1);
50517
+ }
50518
+ const roles = options.clearRoles ? [] : options.role !== void 0 ? parsedRoles.roles : void 0;
50519
+ const roleLabel = resolveClearableOption(options.roleLabel, options.clearRoleLabel, "role-label");
50520
+ if ("error" in roleLabel) {
50521
+ console.error(colorError(roleLabel.error));
50522
+ process.exit(1);
50523
+ }
50524
+ const repositoryPath = resolveClearableOption(
50525
+ options.repoPath,
50526
+ options.clearRepoPath,
50527
+ "repo-path"
50528
+ );
50529
+ if ("error" in repositoryPath) {
50530
+ console.error(colorError(repositoryPath.error));
50531
+ process.exit(1);
50532
+ }
50533
+ const labelError = validateRoleLabel(roles, roleLabel.value);
50534
+ if (labelError) {
50535
+ console.error(colorError(labelError));
50536
+ process.exit(1);
50537
+ }
50538
+ const body = buildUpdateLinkBody({
50539
+ roles,
50540
+ roleLabel: roleLabel.value,
50541
+ repositoryPath: repositoryPath.value,
50542
+ scanEnabled: options.scan
50543
+ });
50544
+ if (isEmptyLinkUpdate(body)) {
50545
+ console.error(colorError(`Nothing to update. Pass at least one of ${UPDATE_LINK_FLAGS}.`));
50546
+ process.exit(1);
50547
+ }
50548
+ const organizationId = await resolveOrganizationId();
50549
+ const existing = await resolveExistingLink(organizationId, applicationId, options.repo);
50550
+ const result = await authenticatedRequest(
50551
+ (client) => client.application.updateApplicationRepository({
50552
+ params: {
50553
+ organizationId,
50554
+ applicationId,
50555
+ applicationRepositoryId: existing.applicationRepositoryId
50556
+ },
50557
+ body
50558
+ })
50559
+ );
50560
+ if (result.status !== SUCCESS_STATUS) {
50561
+ failLinkOp("update link", result.status, result.body);
50562
+ }
50563
+ if (output === "table") {
50564
+ console.log(
50565
+ colorSuccess(
50566
+ `Updated link for ${sanitizeDisplay(result.body.repositoryName)} on application ${applicationId}`
50567
+ )
50568
+ );
50569
+ }
50570
+ outputLink(result.body, output);
50571
+ }
50572
+ async function runUnlink(applicationId, options) {
50573
+ if (!isUuid(applicationId)) {
50574
+ exitInvalidApplicationId(applicationId);
50575
+ }
50576
+ const organizationId = await resolveOrganizationId();
50577
+ const existing = await resolveExistingLink(organizationId, applicationId, options.repo);
50578
+ const confirmed = await confirmAction(
50579
+ `Unlink ${sanitizeDisplay(existing.repositoryName)} from application ${applicationId}?`,
50580
+ { skip: options.yes === true, verb: "unlink" }
50581
+ );
50582
+ if (!confirmed) {
50583
+ console.log("Aborted.");
50584
+ return;
50585
+ }
50586
+ const result = await authenticatedRequest(
50587
+ (client) => client.application.deleteApplicationRepository({
50588
+ params: {
50589
+ organizationId,
50590
+ applicationId,
50591
+ applicationRepositoryId: existing.applicationRepositoryId
50592
+ }
50593
+ })
50594
+ );
50595
+ if (result.status !== NO_CONTENT_STATUS) {
50596
+ failLinkOp("unlink", result.status, result.body);
50597
+ }
50598
+ console.log(
50599
+ colorSuccess(
50600
+ `Unlinked ${sanitizeDisplay(existing.repositoryName)} from application ${applicationId}`
50601
+ )
50602
+ );
50603
+ }
49977
50604
  function createCreateCommand() {
49978
50605
  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(
49979
50606
  "after",
@@ -50025,6 +50652,59 @@ Examples:
50025
50652
  await runUpdate(applicationId, options);
50026
50653
  });
50027
50654
  }
50655
+ var ROLE_FLAG_HELP = `Repository role (repeatable): ${REPOSITORY_ROLE_INPUTS.join(", ")}`;
50656
+ function createLinkCommand() {
50657
+ return new Command("link").description("Link a repository to an application").argument("<applicationId>", "Application UUID").requiredOption("--repo <id-or-url>", "Repository to link (UUID, URL, or owner/repo)").option("--role <role>", ROLE_FLAG_HELP, collectRepeatable).option("--role-label <label>", 'Custom label for the "other" role').option("--repo-path <path>", "Path within the repository").option("--no-scan", "Link without including the repository in scans").option("-o, --output <format>", "Output format: table, json, tsv (default: table)", "table").addHelpText(
50658
+ "after",
50659
+ `
50660
+ A link is scan-enabled unless --no-scan is passed. Scan-disabled links are
50661
+ skipped by scans and do not satisfy the repository precondition for starting a
50662
+ pentest.
50663
+
50664
+ Examples:
50665
+ tracecli application link <appId> --repo acme/store
50666
+ tracecli application link <appId> --repo acme/store --role frontend --role backend-api
50667
+ tracecli application link <appId> --repo acme/tools --role other --role-label "Design system"
50668
+ tracecli application link <appId> --repo acme/monorepo --repo-path apps/web --no-scan`
50669
+ ).action(async (applicationId, options) => {
50670
+ await runLink(applicationId, options);
50671
+ });
50672
+ }
50673
+ function createUpdateLinkCommand() {
50674
+ return new Command("update-link").description("Change an existing repository link's roles, label, path, or scan state").argument("<applicationId>", "Application UUID").requiredOption(
50675
+ "--repo <id-or-url>",
50676
+ "Linked repository to change (repository UUID, link UUID, URL, or owner/repo)"
50677
+ ).option("--role <role>", `${ROLE_FLAG_HELP}; replaces the current roles`, collectRepeatable).option("--clear-roles", "Remove every role from the link").option("--role-label <label>", 'Custom label for the "other" role').option("--clear-role-label", "Remove the custom role label").option("--repo-path <path>", "Path within the repository").option("--clear-repo-path", "Remove the repository path").option("--scan", "Include the repository in scans").option("--no-scan", "Exclude the repository from scans (the link stays)").option("-o, --output <format>", "Output format: table, json, tsv (default: table)", "table").addHelpText(
50678
+ "after",
50679
+ `
50680
+ The update is partial: only the flags you pass change. --role replaces the
50681
+ whole role set, so pass every role the link should keep.
50682
+
50683
+ Examples:
50684
+ tracecli application update-link <appId> --repo acme/store --role frontend --role backend-api
50685
+ tracecli application update-link <appId> --repo acme/store --repo-path apps/web
50686
+ tracecli application update-link <appId> --repo acme/store --no-scan
50687
+ tracecli application update-link <appId> --repo <repositoryId> --scan -o json`
50688
+ ).action(async (applicationId, options) => {
50689
+ await runUpdateLink(applicationId, options);
50690
+ });
50691
+ }
50692
+ function createUnlinkCommand() {
50693
+ return new Command("unlink").description("Remove a repository link from an application").argument("<applicationId>", "Application UUID").requiredOption(
50694
+ "--repo <id-or-url>",
50695
+ "Linked repository to remove (repository UUID, link UUID, URL, or owner/repo)"
50696
+ ).option("-y, --yes", "Skip the confirmation prompt (required in non-interactive shells)").addHelpText(
50697
+ "after",
50698
+ `
50699
+ Only the link is removed; the repository stays connected to the organization.
50700
+
50701
+ Examples:
50702
+ tracecli application unlink <appId> --repo acme/store
50703
+ tracecli application unlink <appId> --repo <repositoryId> --yes`
50704
+ ).action(async (applicationId, options) => {
50705
+ await runUnlink(applicationId, options);
50706
+ });
50707
+ }
50028
50708
  function createDeleteCommand() {
50029
50709
  return new Command("delete").description("Delete an application").argument("<applicationId>", "Application UUID").option("-y, --yes", "Skip the confirmation prompt (required in non-interactive shells)").action(async (applicationId, options) => {
50030
50710
  await runDelete(applicationId, options);
@@ -50036,6 +50716,9 @@ function createApplicationCommand() {
50036
50716
  application.addCommand(createListCommand2());
50037
50717
  application.addCommand(createGetCommand());
50038
50718
  application.addCommand(createReposCommand());
50719
+ application.addCommand(createLinkCommand());
50720
+ application.addCommand(createUpdateLinkCommand());
50721
+ application.addCommand(createUnlinkCommand());
50039
50722
  application.addCommand(createUpdateCommand());
50040
50723
  application.addCommand(createDeleteCommand());
50041
50724
  return application;
@@ -50496,8 +51179,8 @@ function wrapTextWithPrefix(e, r2, o2, t2 = o2, s = o2, n3) {
50496
51179
  hard: true,
50497
51180
  trim: false
50498
51181
  }).split(`
50499
- `).map((c84, i2, m2) => {
50500
- const d = n3 ? n3(c84, i2) : c84;
51182
+ `).map((c86, i2, m2) => {
51183
+ const d = n3 ? n3(c86, i2) : c86;
50501
51184
  return i2 === 0 ? `${t2}${d}` : i2 === m2.length - 1 ? `${s}${d}` : `${o2}${d}`;
50502
51185
  }).join(`
50503
51186
  `);
@@ -50995,14 +51678,14 @@ var limitOptions = ({
50995
51678
  Math.min(l2 - a2 + 3, e.length - a2),
50996
51679
  0
50997
51680
  ));
50998
- let d = a2 < e.length && f > 0, c84 = a2 < e.length && f + a2 < e.length;
51681
+ let d = a2 < e.length && f > 0, c86 = a2 < e.length && f + a2 < e.length;
50999
51682
  const W = Math.min(
51000
51683
  f + a2,
51001
51684
  e.length
51002
51685
  ), s = [];
51003
51686
  let g = 0;
51004
- d && g++, c84 && g++;
51005
- const T = f + (d ? 1 : 0), y = W - (c84 ? 1 : 0);
51687
+ d && g++, c86 && g++;
51688
+ const T = f + (d ? 1 : 0), y = W - (c86 ? 1 : 0);
51006
51689
  for (let t2 = T; t2 < y; t2++) {
51007
51690
  const n3 = e[t2], o2 = n3 ? w(n3, t2 === l2) : "", h2 = wrapAnsi(o2, i2, {
51008
51691
  hard: true,
@@ -51023,14 +51706,14 @@ var limitOptions = ({
51023
51706
  u4,
51024
51707
  true
51025
51708
  );
51026
- d ? ({ lineCount: o2, removals: t2 } = L(), o2 > u4 && (c84 || (u4 -= 1), { lineCount: o2, removals: n3 } = E())) : (c84 || (u4 -= 1), { lineCount: o2, removals: n3 } = E(), o2 > u4 && (u4 -= 1, { lineCount: o2, removals: t2 } = L())), t2 > 0 && (d = true, s.splice(0, t2)), n3 > 0 && (c84 = true, s.splice(s.length - n3, n3));
51709
+ d ? ({ lineCount: o2, removals: t2 } = L(), o2 > u4 && (c86 || (u4 -= 1), { lineCount: o2, removals: n3 } = E())) : (c86 || (u4 -= 1), { lineCount: o2, removals: n3 } = E(), o2 > u4 && (u4 -= 1, { lineCount: o2, removals: t2 } = L())), t2 > 0 && (d = true, s.splice(0, t2)), n3 > 0 && (c86 = true, s.splice(s.length - n3, n3));
51027
51710
  }
51028
51711
  const x = [];
51029
51712
  d && x.push(M);
51030
51713
  for (const t2 of s)
51031
51714
  for (const n3 of t2)
51032
51715
  x.push(n3);
51033
- return c84 && x.push(M), x;
51716
+ return c86 && x.push(M), x;
51034
51717
  };
51035
51718
  var confirm = (i2) => {
51036
51719
  const a2 = i2.active ?? "Yes", s = i2.inactive ?? "No";
@@ -51049,15 +51732,15 @@ var confirm = (i2) => {
51049
51732
  u4
51050
51733
  ), o2 = `${e ? `${styleText2("gray", S_BAR)}
51051
51734
  ` : ""}${f}
51052
- `, c84 = this.value ? a2 : s;
51735
+ `, c86 = this.value ? a2 : s;
51053
51736
  switch (this.state) {
51054
51737
  case "submit": {
51055
51738
  const r2 = e ? `${styleText2("gray", S_BAR)} ` : "";
51056
- return `${o2}${r2}${styleText2("dim", c84)}`;
51739
+ return `${o2}${r2}${styleText2("dim", c86)}`;
51057
51740
  }
51058
51741
  case "cancel": {
51059
51742
  const r2 = e ? `${styleText2("gray", S_BAR)} ` : "";
51060
- return `${o2}${r2}${styleText2(["strikethrough", "dim"], c84)}${e ? `
51743
+ return `${o2}${r2}${styleText2(["strikethrough", "dim"], c86)}${e ? `
51061
51744
  ${styleText2("gray", S_BAR)}` : ""}`;
51062
51745
  }
51063
51746
  default: {
@@ -51205,13 +51888,13 @@ var multiline = (e) => new h({
51205
51888
  `, m2 = e.placeholder && e.placeholder.length > 0 ? (
51206
51889
  // biome-ignore lint/style/noNonNullAssertion: guarded by placeholder.length > 0
51207
51890
  styleText2("inverse", e.placeholder[0]) + styleText2("dim", e.placeholder.slice(1))
51208
- ) : styleText2(["inverse", "hidden"], "_"), a2 = this.userInput ? this.userInputWithCursor : m2, l2 = this.value ?? "", c84 = e.showSubmit ? `
51891
+ ) : styleText2(["inverse", "hidden"], "_"), a2 = this.userInput ? this.userInputWithCursor : m2, l2 = this.value ?? "", c86 = e.showSubmit ? `
51209
51892
  ${styleText2(this.focused === "submit" ? "cyan" : "dim", "[ submit ]")}` : "";
51210
51893
  switch (this.state) {
51211
51894
  case "error": {
51212
51895
  const n3 = `${styleText2("yellow", S_BAR)} `, r2 = i2 ? wrapTextWithPrefix(e.output, a2, n3, void 0) : a2, u4 = styleText2("yellow", S_BAR_END);
51213
51896
  return `${o2}${r2}
51214
- ${u4} ${styleText2("yellow", this.error)}${c84}
51897
+ ${u4} ${styleText2("yellow", this.error)}${c86}
51215
51898
  `;
51216
51899
  }
51217
51900
  case "submit": {
@@ -51239,7 +51922,7 @@ ${u4} ${styleText2("yellow", this.error)}${c84}
51239
51922
  default: {
51240
51923
  const n3 = i2 ? `${styleText2("cyan", S_BAR)} ` : "", r2 = i2 ? styleText2("cyan", S_BAR_END) : "", u4 = i2 ? wrapTextWithPrefix(e.output, a2, n3) : a2;
51241
51924
  return `${o2}${u4}
51242
- ${r2}${c84}
51925
+ ${r2}${c86}
51243
51926
  `;
51244
51927
  }
51245
51928
  }
@@ -51254,7 +51937,7 @@ var password = (r2) => new u$1({
51254
51937
  render() {
51255
51938
  const e = r2.withGuide ?? settings.withGuide, o2 = `${e ? `${styleText2("gray", S_BAR)}
51256
51939
  ` : ""}${symbol2(this.state)} ${r2.message}
51257
- `, c84 = this.userInputWithCursor, i2 = this.masked;
51940
+ `, c86 = this.userInputWithCursor, i2 = this.masked;
51258
51941
  switch (this.state) {
51259
51942
  case "error": {
51260
51943
  const s = e ? `${styleText2("yellow", S_BAR)} ` : "", n3 = e ? `${styleText2("yellow", S_BAR_END)} ` : "", l2 = i2 ?? "";
@@ -51274,7 +51957,7 @@ ${styleText2("gray", S_BAR)}` : ""}`;
51274
51957
  }
51275
51958
  default: {
51276
51959
  const s = e ? `${styleText2("cyan", S_BAR)} ` : "", n3 = e ? styleText2("cyan", S_BAR_END) : "";
51277
- return `${o2}${s}${c84}
51960
+ return `${o2}${s}${c86}
51278
51961
  ${n3}
51279
51962
  `;
51280
51963
  }
@@ -51290,7 +51973,7 @@ var SELECT_INSTRUCTIONS = [
51290
51973
  `${styleText2("dim", "\u2191/\u2193")} to navigate`,
51291
51974
  `${styleText2("dim", "Enter:")} confirm`
51292
51975
  ];
51293
- var c83 = (t2, o2) => t2.includes(`
51976
+ var c85 = (t2, o2) => t2.includes(`
51294
51977
  `) ? t2.split(`
51295
51978
  `).map((d) => o2(d)).join(`
51296
51979
  `) : o2(t2);
@@ -51301,15 +51984,15 @@ var select = (t2) => {
51301
51984
  const s = n3.label ?? String(n3.value);
51302
51985
  switch (m2) {
51303
51986
  case "disabled":
51304
- return `${styleText2("gray", S_RADIO_INACTIVE)} ${c83(s, (i2) => styleText2("gray", i2))}${n3.hint ? ` ${styleText2("dim", `(${n3.hint ?? "disabled"})`)}` : ""}`;
51987
+ return `${styleText2("gray", S_RADIO_INACTIVE)} ${c85(s, (i2) => styleText2("gray", i2))}${n3.hint ? ` ${styleText2("dim", `(${n3.hint ?? "disabled"})`)}` : ""}`;
51305
51988
  case "selected":
51306
- return `${c83(s, (i2) => styleText2("dim", i2))}`;
51989
+ return `${c85(s, (i2) => styleText2("dim", i2))}`;
51307
51990
  case "active":
51308
51991
  return `${styleText2("green", S_RADIO_ACTIVE)} ${s}${n3.hint ? ` ${styleText2("dim", `(${n3.hint})`)}` : ""}`;
51309
51992
  case "cancelled":
51310
- return `${c83(s, (i2) => styleText2(["strikethrough", "dim"], i2))}`;
51993
+ return `${c85(s, (i2) => styleText2(["strikethrough", "dim"], i2))}`;
51311
51994
  default:
51312
- return `${styleText2("dim", S_RADIO_INACTIVE)} ${c83(s, (i2) => styleText2("dim", i2))}`;
51995
+ return `${styleText2("dim", S_RADIO_INACTIVE)} ${c85(s, (i2) => styleText2("dim", i2))}`;
51313
51996
  }
51314
51997
  }, d = t2.showInstructions ?? true;
51315
51998
  return new n$1({
@@ -51370,6 +52053,18 @@ var i = `${styleText2("gray", S_BAR)} `;
51370
52053
 
51371
52054
  // src/org/invite.ts
51372
52055
  var MEMBER_ROLE_SLUGS = MemberRoleSlugSchema.options;
52056
+ var ROLE_NAMES = {
52057
+ admin: "admin",
52058
+ analyst: "manager",
52059
+ developer: "developer"
52060
+ };
52061
+ var ROLE_BY_NAME = {
52062
+ admin: "admin",
52063
+ manager: "analyst",
52064
+ analyst: "analyst",
52065
+ developer: "developer"
52066
+ };
52067
+ var ROLE_CHOICES = MEMBER_ROLE_SLUGS.map((slug) => ROLE_NAMES[slug]).join(", ");
51373
52068
  var DEFAULT_INVITE_ROLE = CreateMemberInvitationRequestSchema.shape.roleSlug.parse(void 0);
51374
52069
  var InviteEmailSchema = CreateMemberInvitationRequestSchema.shape.email;
51375
52070
  function planInvite(emailArg, options) {
@@ -51380,14 +52075,13 @@ function planInvite(emailArg, options) {
51380
52075
  if (!InviteEmailSchema.safeParse(email3).success) {
51381
52076
  return { error: `'${sanitizeDisplay(email3)}' is not a valid email address.` };
51382
52077
  }
51383
- const role = options.role.trim().toLowerCase();
51384
- const parsedRole = MemberRoleSlugSchema.safeParse(role);
51385
- if (!parsedRole.success) {
52078
+ const roleSlug = ROLE_BY_NAME[options.role.trim().toLowerCase()];
52079
+ if (roleSlug === void 0) {
51386
52080
  return {
51387
- error: `Invalid --role '${sanitizeDisplay(options.role)}'. Use one of: ${MEMBER_ROLE_SLUGS.join(", ")}.`
52081
+ error: `Invalid --role '${sanitizeDisplay(options.role)}'. Use one of: ${ROLE_CHOICES}.`
51388
52082
  };
51389
52083
  }
51390
- return { plan: { email: email3, roleSlug: parsedRole.data } };
52084
+ return { plan: { email: email3, roleSlug } };
51391
52085
  }
51392
52086
  function failInvite(status, body) {
51393
52087
  const { message } = readErrorFields(body);
@@ -51395,7 +52089,7 @@ function failInvite(status, body) {
51395
52089
  exitAuthFailed();
51396
52090
  }
51397
52091
  if (status === 403) {
51398
- const hint = "Inviting members requires an organization admin or analyst signed in with `tracecli auth login` (API keys are rejected).";
52092
+ const hint = "Inviting members requires an organization admin signed in with `tracecli auth login` (API keys are rejected).";
51399
52093
  console.error(
51400
52094
  colorError(message === void 0 || message === "Insufficient permissions" ? hint : message)
51401
52095
  );
@@ -51440,7 +52134,7 @@ function outputInvitation(invitation, output) {
51440
52134
  }
51441
52135
  console.log(
51442
52136
  colorSuccess(
51443
- `Invitation sent to ${sanitizeDisplay(invitation.email)} as ${sanitizeDisplay(invitation.roleSlug)}`
52137
+ `Invitation sent to ${sanitizeDisplay(invitation.email)} as ${ROLE_NAMES[invitation.roleSlug]}`
51444
52138
  )
51445
52139
  );
51446
52140
  console.log(` Invitation ID: ${sanitizeDisplay(invitation.invitationId)}`);
@@ -51466,20 +52160,16 @@ async function runInvite(emailArg, options) {
51466
52160
  outputInvitation(result.body, output);
51467
52161
  }
51468
52162
  function createOrgInviteCommand() {
51469
- return new Command("invite").description("Invite someone to the active organization by email").argument("<email>", "Email address to invite").option(
51470
- "-r, --role <role>",
51471
- `Role to grant: ${MEMBER_ROLE_SLUGS.join(", ")}`,
51472
- DEFAULT_INVITE_ROLE
51473
- ).option("-o, --output <format>", "Output format: table, json, tsv (default: table)", "table").addHelpText(
52163
+ return new Command("invite").description("Invite someone to the active organization by email").argument("<email>", "Email address to invite").option("-r, --role <role>", `Role to grant: ${ROLE_CHOICES}`, DEFAULT_INVITE_ROLE).option("-o, --output <format>", "Output format: table, json, tsv (default: table)", "table").addHelpText(
51474
52164
  "after",
51475
52165
  `
51476
52166
  Sends the same invitation email as the dashboard's Settings > Members page.
51477
- Requires an organization admin or analyst signed in with \`tracecli auth login\`;
51478
- API keys are rejected. Only admins can invite someone as an admin.
52167
+ Requires an organization admin signed in with \`tracecli auth login\`;
52168
+ API keys are rejected.
51479
52169
 
51480
52170
  Examples:
51481
52171
  tracecli org invite dev@acme.com
51482
- tracecli org invite sec@acme.com --role analyst
52172
+ tracecli org invite sec@acme.com --role manager
51483
52173
  tracecli org invite cto@acme.com --role admin --output json`
51484
52174
  ).action(async (email3, options) => {
51485
52175
  await runInvite(email3, options);
@@ -52806,7 +53496,7 @@ function extractCommandTree(cmd) {
52806
53496
  description: o2.description,
52807
53497
  takesValue: o2.required || o2.optional
52808
53498
  }));
52809
- const subcommands = cmd.commands.filter((c84) => !c84._hidden).map((c84) => extractCommandTree(c84));
53499
+ const subcommands = cmd.commands.filter((c86) => !c86._hidden).map((c86) => extractCommandTree(c86));
52810
53500
  return {
52811
53501
  name: cmd.name(),
52812
53502
  description: cmd.description(),
@@ -52872,7 +53562,7 @@ function generateBashCompletion(tree, binName) {
52872
53562
  }
52873
53563
  function collectBashCases(tree, prefix) {
52874
53564
  const cases = /* @__PURE__ */ new Map();
52875
- const subcommandNames = tree.subcommands.map((c84) => c84.name);
53565
+ const subcommandNames = tree.subcommands.map((c86) => c86.name);
52876
53566
  const optionNames = tree.options.flatMap((o2) => o2.short ? [o2.long, o2.short] : [o2.long]);
52877
53567
  const extraOptions = prefix === "" ? ["--help", "--version"] : ["--help"];
52878
53568
  const allOptions = [.../* @__PURE__ */ new Set([...optionNames, ...extraOptions])];
@@ -53178,17 +53868,17 @@ function validateInboxLabel(label) {
53178
53868
  function buildCreateInboxBody(label) {
53179
53869
  return { label };
53180
53870
  }
53871
+ function forbiddenMessage(verb, needsEntitlement) {
53872
+ const where = needsEntitlement ? " on a pentest-enabled organization" : "";
53873
+ return `Only an organization admin or Manager (or Trace staff)${where} can ${verb}.`;
53874
+ }
53181
53875
  function failWriteOp2(action, status, body, notFoundLabel) {
53182
53876
  const { message, errorCode } = readErrorFields(body);
53183
53877
  if (status === UNAUTHORIZED_STATUS2) {
53184
53878
  exitAuthFailed();
53185
53879
  }
53186
53880
  if (status === 403) {
53187
- console.error(
53188
- colorError(
53189
- `Only an organization admin (or Trace staff) on a pentest-enabled organization can ${action} inboxes.`
53190
- )
53191
- );
53881
+ console.error(colorError(forbiddenMessage(`${action} inboxes`, true)));
53192
53882
  process.exit(1);
53193
53883
  }
53194
53884
  if (status === 404) {
@@ -53240,7 +53930,7 @@ function failReadOp2(action, status, body, notFoundLabel) {
53240
53930
  exitAuthFailed();
53241
53931
  }
53242
53932
  if (status === 403) {
53243
- console.error(colorError("You don't have access to view inboxes in this organization."));
53933
+ console.error(colorError(forbiddenMessage(action, action !== "list inboxes")));
53244
53934
  process.exit(1);
53245
53935
  }
53246
53936
  if (status === 404) {
@@ -53287,9 +53977,9 @@ function buildInboxesTable(inboxes) {
53287
53977
  address: sanitizeDisplay(inbox.address),
53288
53978
  label: sanitizeDisplay(inbox.label)
53289
53979
  }));
53290
- const addressWidth = Math.max(7, ...cells.map((c84) => c84.address.length));
53980
+ const addressWidth = Math.max(7, ...cells.map((c86) => c86.address.length));
53291
53981
  const header = `${"ADDRESS".padEnd(addressWidth)} LABEL`;
53292
- const rows = cells.map((c84) => `${c84.address.padEnd(addressWidth)} ${c84.label}`);
53982
+ const rows = cells.map((c86) => `${c86.address.padEnd(addressWidth)} ${c86.label}`);
53293
53983
  return { header, rows };
53294
53984
  }
53295
53985
  function formatInboxQuota(quota) {
@@ -53313,14 +54003,14 @@ function buildInboxEmailsTable(messages) {
53313
54003
  subject: sanitizeDisplay(message.subject ?? ""),
53314
54004
  unread: message.unread ? "yes" : "no"
53315
54005
  }));
53316
- const idWidth = Math.max(2, ...cells.map((c84) => c84.id.length));
53317
- const receivedWidth = Math.max(8, ...cells.map((c84) => c84.received.length));
53318
- const fromWidth = Math.max(4, ...cells.map((c84) => c84.from.length));
53319
- const toWidth = Math.max(2, ...cells.map((c84) => c84.to.length));
53320
- const subjectWidth = Math.max(7, ...cells.map((c84) => c84.subject.length));
54006
+ const idWidth = Math.max(2, ...cells.map((c86) => c86.id.length));
54007
+ const receivedWidth = Math.max(8, ...cells.map((c86) => c86.received.length));
54008
+ const fromWidth = Math.max(4, ...cells.map((c86) => c86.from.length));
54009
+ const toWidth = Math.max(2, ...cells.map((c86) => c86.to.length));
54010
+ const subjectWidth = Math.max(7, ...cells.map((c86) => c86.subject.length));
53321
54011
  const header = `${"ID".padEnd(idWidth)} ${"RECEIVED".padEnd(receivedWidth)} ${"FROM".padEnd(fromWidth)} ${"TO".padEnd(toWidth)} ${"SUBJECT".padEnd(subjectWidth)} UNREAD`;
53322
54012
  const rows = cells.map(
53323
- (c84) => `${c84.id.padEnd(idWidth)} ${c84.received.padEnd(receivedWidth)} ${c84.from.padEnd(fromWidth)} ${c84.to.padEnd(toWidth)} ${c84.subject.padEnd(subjectWidth)} ${c84.unread}`
54013
+ (c86) => `${c86.id.padEnd(idWidth)} ${c86.received.padEnd(receivedWidth)} ${c86.from.padEnd(fromWidth)} ${c86.to.padEnd(toWidth)} ${c86.subject.padEnd(subjectWidth)} ${c86.unread}`
53324
54014
  );
53325
54015
  return { header, rows };
53326
54016
  }
@@ -53974,6 +54664,46 @@ function anySecretFlag(opts) {
53974
54664
  function anyPasswordFlag(opts) {
53975
54665
  return opts.passwordStdin === true || opts.passwordFile !== void 0 || opts.passwordEnv !== void 0;
53976
54666
  }
54667
+ function anyUsernameFlag(opts) {
54668
+ return opts.usernameStdin === true || opts.usernameFile !== void 0 || opts.usernameEnv !== void 0;
54669
+ }
54670
+ function anySecretValueFlag(opts) {
54671
+ return opts.secretStdin === true || opts.secretFile !== void 0 || opts.secretEnv !== void 0;
54672
+ }
54673
+ function withArticle(kebab) {
54674
+ return `${/^(api|ssh)-/.test(kebab) ? "An" : "A"} ${kebab}`;
54675
+ }
54676
+ function rotateFlagProblem(type, opts) {
54677
+ const kebab = credentialTypeKebab(type);
54678
+ if (type === "USERNAME_PASSWORD") {
54679
+ if (anySecretValueFlag(opts)) {
54680
+ return `${withArticle(kebab)} credential takes --username-* and --password-*, not --secret-*.`;
54681
+ }
54682
+ if (!anyUsernameFlag(opts) || !anyPasswordFlag(opts)) {
54683
+ return "Pass both --username-* and --password-*: the login is replaced as a pair.";
54684
+ }
54685
+ if (usesStdin(usernameOpts(opts)) && usesStdin(passwordOpts(opts))) {
54686
+ return "Only one secret can come from stdin. Use --username-file/-env for the other.";
54687
+ }
54688
+ return null;
54689
+ }
54690
+ if (anyUsernameFlag(opts)) {
54691
+ return `${withArticle(kebab)} credential has no username. Pass the new secret with --secret-*.`;
54692
+ }
54693
+ if (type === "SSH_PRIVATE_KEY") {
54694
+ if (!anySecretValueFlag(opts)) {
54695
+ return "Pass the key with --secret-* too: an SSH key and its passphrase are replaced together.";
54696
+ }
54697
+ if (usesStdin(secretOpts(opts)) && usesStdin(passwordOpts(opts))) {
54698
+ return "Only one secret can come from stdin. Use --password-file/-env for the passphrase.";
54699
+ }
54700
+ return null;
54701
+ }
54702
+ if (anyPasswordFlag(opts)) {
54703
+ return `${withArticle(kebab)} credential has no password. Pass the new secret with --secret-*.`;
54704
+ }
54705
+ return null;
54706
+ }
53977
54707
  function secretOpts(opts) {
53978
54708
  return { stdin: opts.secretStdin, file: opts.secretFile, env: opts.secretEnv };
53979
54709
  }
@@ -53989,7 +54719,11 @@ function failWriteOp3(action, status, body, notFoundLabel) {
53989
54719
  exitAuthFailed();
53990
54720
  }
53991
54721
  if (status === 403) {
53992
- console.error(colorError(`Only Trace staff can ${action} credentials.`));
54722
+ console.error(
54723
+ colorError(
54724
+ `You can't ${action} credentials here. That needs an organization admin signed in with \`tracecli auth login\`; API keys are rejected.`
54725
+ )
54726
+ );
53993
54727
  process.exit(1);
53994
54728
  }
53995
54729
  if (status === 404) {
@@ -54103,16 +54837,16 @@ function printCredentialsTsv(creds) {
54103
54837
  }
54104
54838
  }
54105
54839
  function buildCredentialsTable(creds) {
54106
- const cells = creds.map((c84) => ({
54107
- id: c84.credentialId,
54108
- label: sanitizeDisplay(c84.label),
54109
- type: c84.credentialType
54840
+ const cells = creds.map((c86) => ({
54841
+ id: c86.credentialId,
54842
+ label: sanitizeDisplay(c86.label),
54843
+ type: c86.credentialType
54110
54844
  }));
54111
- const idWidth = Math.max(2, ...cells.map((c84) => c84.id.length));
54112
- const labelWidth = Math.max(5, ...cells.map((c84) => c84.label.length));
54845
+ const idWidth = Math.max(2, ...cells.map((c86) => c86.id.length));
54846
+ const labelWidth = Math.max(5, ...cells.map((c86) => c86.label.length));
54113
54847
  const header = `${"ID".padEnd(idWidth)} ${"LABEL".padEnd(labelWidth)} TYPE`;
54114
54848
  const rows = cells.map(
54115
- (c84) => `${c84.id.padEnd(idWidth)} ${c84.label.padEnd(labelWidth)} ${c84.type}`
54849
+ (c86) => `${c86.id.padEnd(idWidth)} ${c86.label.padEnd(labelWidth)} ${c86.type}`
54116
54850
  );
54117
54851
  return { header, rows };
54118
54852
  }
@@ -54306,6 +55040,43 @@ async function buildCreateBody(type, label, options) {
54306
55040
  }
54307
55041
  }
54308
55042
  }
55043
+ async function buildRotateBody(type, options) {
55044
+ switch (type) {
55045
+ case "USERNAME_PASSWORD": {
55046
+ const username = await resolveSecret("username", "username", usernameOpts(options));
55047
+ const password2 = await resolveSecret("password", "password", passwordOpts(options));
55048
+ return { credentialType: type, username, password: password2 };
55049
+ }
55050
+ case "SSH_PRIVATE_KEY": {
55051
+ const privateKeyPem = await resolveSecret("private key PEM", "secret", secretOpts(options));
55052
+ const passphrase = anyPasswordFlag(options) ? await resolveSecret("key passphrase", "password", passwordOpts(options)) : void 0;
55053
+ return {
55054
+ credentialType: type,
55055
+ privateKeyPem,
55056
+ ...passphrase !== void 0 ? { passphrase } : {}
55057
+ };
55058
+ }
55059
+ case "SESSION_STATE":
55060
+ return {
55061
+ credentialType: type,
55062
+ secretValue: await resolveSecret("session state capture", "secret", secretOpts(options))
55063
+ };
55064
+ case "MAGIC_AUTH":
55065
+ return {
55066
+ credentialType: type,
55067
+ secretValue: await resolveSecret("test-account email", "secret", secretOpts(options))
55068
+ };
55069
+ case "BEARER_TOKEN":
55070
+ case "JWT_TOKEN":
55071
+ case "API_KEY":
55072
+ case "PASSPHRASE":
55073
+ case "COOKIE":
55074
+ return {
55075
+ credentialType: type,
55076
+ secretValue: await resolveSecret("secret value", "secret", secretOpts(options))
55077
+ };
55078
+ }
55079
+ }
54309
55080
  async function runList3(options) {
54310
55081
  const output = parseOutput(options.output);
54311
55082
  requireEnvId(options.env);
@@ -54356,12 +55127,7 @@ async function runUpdate3(credentialId, options) {
54356
55127
  const output = parseOutput(options.output);
54357
55128
  requireEnvId(options.env);
54358
55129
  requireCredId(credentialId);
54359
- if (anySecretFlag(options)) {
54360
- console.error(
54361
- colorError("Secrets are immutable on update. Delete and recreate the credential to rotate.")
54362
- );
54363
- process.exit(1);
54364
- }
55130
+ const rotating = anySecretFlag(options);
54365
55131
  if (options.clearAuthConfig && anyAuthFlag(options)) {
54366
55132
  console.error(colorError("Pass either --clear-auth-config or auth-config flags, not both."));
54367
55133
  process.exit(1);
@@ -54374,16 +55140,32 @@ async function runUpdate3(credentialId, options) {
54374
55140
  }
54375
55141
  const organizationId = await resolveOrganizationId();
54376
55142
  const params3 = { organizationId, environmentId: options.env, credentialId };
55143
+ const current = rotating || !options.clearAuthConfig && anyAuthFlag(options) ? await authenticatedRequest((client) => client.credential.getCredential({ params: params3 })) : null;
55144
+ if (current && current.status !== SUCCESS_STATUS) {
55145
+ failReadOp3("get credential", current.status, current.body, credentialId);
55146
+ }
55147
+ let rotateBody;
55148
+ if (current && rotating) {
55149
+ const type = current.body.credentialType;
55150
+ const problem = rotateFlagProblem(type, options) ?? (options.clearAuthConfig && (type === "SSH_PRIVATE_KEY" || type === "SESSION_STATE") ? `${withArticle(credentialTypeKebab(type))} credential's auth config can't be cleared, so --clear-auth-config would fail after the secret had already changed.` : null);
55151
+ if (problem) {
55152
+ console.error(colorError(problem));
55153
+ process.exit(1);
55154
+ }
55155
+ try {
55156
+ rotateBody = await buildRotateBody(type, options);
55157
+ } catch (error48) {
55158
+ if (error48 instanceof SecretInputError) {
55159
+ console.error(colorError(error48.message));
55160
+ process.exit(1);
55161
+ }
55162
+ throw error48;
55163
+ }
55164
+ }
54377
55165
  let authConfig;
54378
55166
  if (options.clearAuthConfig) {
54379
55167
  authConfig = null;
54380
- } else if (anyAuthFlag(options)) {
54381
- const current = await authenticatedRequest(
54382
- (client) => client.credential.getCredential({ params: params3 })
54383
- );
54384
- if (current.status !== SUCCESS_STATUS) {
54385
- failReadOp3("get credential", current.status, current.body, credentialId);
54386
- }
55168
+ } else if (current && anyAuthFlag(options)) {
54387
55169
  const type = current.body.credentialType;
54388
55170
  const foreign = rejectForeignAuthFlags(type, options);
54389
55171
  if (foreign) {
@@ -54403,24 +55185,44 @@ async function runUpdate3(credentialId, options) {
54403
55185
  ...options.moveEnv !== void 0 ? { environmentId: options.moveEnv } : {},
54404
55186
  ...authConfig !== void 0 ? { authConfig } : {}
54405
55187
  };
54406
- if (Object.keys(body).length === 0) {
55188
+ const patching = Object.keys(body).length > 0;
55189
+ if (!rotateBody && !patching) {
54407
55190
  console.error(
54408
55191
  colorError(
54409
- "Nothing to update. Pass --label, --move-env, auth-config flags, or --clear-auth-config."
55192
+ "Nothing to update. Pass --label, --move-env, auth-config flags, --clear-auth-config, or a new secret."
54410
55193
  )
54411
55194
  );
54412
55195
  process.exit(1);
54413
55196
  }
54414
- const result = await authenticatedRequest(
54415
- (client) => client.credential.updateCredential({ params: params3, body })
54416
- );
54417
- if (result.status !== SUCCESS_STATUS) {
54418
- failWriteOp3("update", result.status, result.body, credentialId);
55197
+ let updated;
55198
+ if (rotateBody) {
55199
+ const secret = rotateBody;
55200
+ const rotated = await authenticatedRequest(
55201
+ (client) => client.credential.rotateCredentialSecret({ params: params3, body: secret })
55202
+ );
55203
+ if (rotated.status !== SUCCESS_STATUS) {
55204
+ failWriteOp3("update", rotated.status, rotated.body, credentialId);
55205
+ }
55206
+ updated = rotated.body;
54419
55207
  }
55208
+ if (patching) {
55209
+ const result = await authenticatedRequest(
55210
+ (client) => client.credential.updateCredential({ params: params3, body })
55211
+ );
55212
+ if (result.status !== SUCCESS_STATUS) {
55213
+ if (rotateBody) {
55214
+ console.error(colorError("The secret was rotated, but the other changes were not saved."));
55215
+ }
55216
+ failWriteOp3("update", result.status, result.body, credentialId);
55217
+ }
55218
+ updated = result.body;
55219
+ }
55220
+ if (!updated) return;
54420
55221
  if (output === "table") {
54421
- console.log(colorSuccess(`Updated credential ${sanitizeDisplay(result.body.label)}`));
55222
+ const verb = rotateBody ? patching ? "Rotated the secret and updated" : "Rotated the secret of" : "Updated";
55223
+ console.log(colorSuccess(`${verb} credential ${sanitizeDisplay(updated.label)}`));
54422
55224
  }
54423
- outputCredential(result.body, output);
55225
+ outputCredential(updated, output);
54424
55226
  }
54425
55227
  function failMfaOp(action, status, body, credentialId) {
54426
55228
  const { message, errorCode } = readErrorFields(body);
@@ -54547,7 +55349,7 @@ async function runDelete3(credentialId, options) {
54547
55349
  }
54548
55350
  console.log(colorSuccess(`Deleted credential ${credentialId}`));
54549
55351
  }
54550
- function failLinkOp(op, status, body, notFoundLabel) {
55352
+ function failLinkOp2(op, status, body, notFoundLabel) {
54551
55353
  const { message, errorCode } = readErrorFields(body);
54552
55354
  if (status === UNAUTHORIZED_STATUS2) {
54553
55355
  exitAuthFailed();
@@ -54654,13 +55456,13 @@ function buildCredentialLinksTable(links) {
54654
55456
  source: sourceDisplay(link.source),
54655
55457
  via: link.environmentCredentialSourceId ? "link-all" : ""
54656
55458
  }));
54657
- const idWidth = Math.max("LINK ID".length, ...cells.map((c84) => c84.id.length));
54658
- const labelWidth = Math.max("LABEL".length, ...cells.map((c84) => c84.label.length));
54659
- const typeWidth = Math.max("TYPE".length, ...cells.map((c84) => c84.type.length));
54660
- const sourceWidth = Math.max("SOURCE".length, ...cells.map((c84) => c84.source.length));
55459
+ const idWidth = Math.max("LINK ID".length, ...cells.map((c86) => c86.id.length));
55460
+ const labelWidth = Math.max("LABEL".length, ...cells.map((c86) => c86.label.length));
55461
+ const typeWidth = Math.max("TYPE".length, ...cells.map((c86) => c86.type.length));
55462
+ const sourceWidth = Math.max("SOURCE".length, ...cells.map((c86) => c86.source.length));
54661
55463
  const header = `${"LINK ID".padEnd(idWidth)} ${"LABEL".padEnd(labelWidth)} ${"TYPE".padEnd(typeWidth)} ${"SOURCE".padEnd(sourceWidth)} VIA`;
54662
55464
  const rows = cells.map(
54663
- (c84) => `${c84.id.padEnd(idWidth)} ${c84.label.padEnd(labelWidth)} ${c84.type.padEnd(typeWidth)} ${c84.source.padEnd(sourceWidth)} ${c84.via}`.trimEnd()
55465
+ (c86) => `${c86.id.padEnd(idWidth)} ${c86.label.padEnd(labelWidth)} ${c86.type.padEnd(typeWidth)} ${c86.source.padEnd(sourceWidth)} ${c86.via}`.trimEnd()
54664
55466
  );
54665
55467
  return { header, rows };
54666
55468
  }
@@ -54714,17 +55516,17 @@ function buildLinkableCredentialsTable(creds) {
54714
55516
  }),
54715
55517
  linked: cred.alreadyLinked ? "linked" : ""
54716
55518
  }));
54717
- const idWidth = Math.max("ID".length, ...cells.map((c84) => c84.id.length));
54718
- const labelWidth = Math.max("LABEL".length, ...cells.map((c84) => c84.label.length));
54719
- const typeWidth = Math.max("TYPE".length, ...cells.map((c84) => c84.type.length));
54720
- const sourceWidth = Math.max("SOURCE".length, ...cells.map((c84) => c84.source.length));
55519
+ const idWidth = Math.max("ID".length, ...cells.map((c86) => c86.id.length));
55520
+ const labelWidth = Math.max("LABEL".length, ...cells.map((c86) => c86.label.length));
55521
+ const typeWidth = Math.max("TYPE".length, ...cells.map((c86) => c86.type.length));
55522
+ const sourceWidth = Math.max("SOURCE".length, ...cells.map((c86) => c86.source.length));
54721
55523
  const header = `${"ID".padEnd(idWidth)} ${"LABEL".padEnd(labelWidth)} ${"TYPE".padEnd(typeWidth)} ${"SOURCE".padEnd(sourceWidth)} STATUS`;
54722
55524
  const rows = cells.map(
54723
- (c84) => `${c84.id.padEnd(idWidth)} ${c84.label.padEnd(labelWidth)} ${c84.type.padEnd(typeWidth)} ${c84.source.padEnd(sourceWidth)} ${c84.linked}`.trimEnd()
55525
+ (c86) => `${c86.id.padEnd(idWidth)} ${c86.label.padEnd(labelWidth)} ${c86.type.padEnd(typeWidth)} ${c86.source.padEnd(sourceWidth)} ${c86.linked}`.trimEnd()
54724
55526
  );
54725
55527
  return { header, rows };
54726
55528
  }
54727
- async function runLink(credentialId, options) {
55529
+ async function runLink2(credentialId, options) {
54728
55530
  const output = parseOutput(options.output);
54729
55531
  requireEnvId(options.env);
54730
55532
  requireCredId(credentialId);
@@ -54736,7 +55538,7 @@ async function runLink(credentialId, options) {
54736
55538
  })
54737
55539
  );
54738
55540
  if (result.status !== CREATED_STATUS) {
54739
- failLinkOp("link", result.status, result.body, credentialId);
55541
+ failLinkOp2("link", result.status, result.body, credentialId);
54740
55542
  }
54741
55543
  if (output === "table") {
54742
55544
  console.log(
@@ -54747,7 +55549,7 @@ async function runLink(credentialId, options) {
54747
55549
  }
54748
55550
  outputCredentialLink(result.body, output);
54749
55551
  }
54750
- async function runUnlink(credentialLinkId, options) {
55552
+ async function runUnlink2(credentialLinkId, options) {
54751
55553
  requireEnvId(options.env);
54752
55554
  requireLinkId(credentialLinkId);
54753
55555
  const confirmed = await confirmAction(
@@ -54768,7 +55570,7 @@ async function runUnlink(credentialLinkId, options) {
54768
55570
  if (result.status === 409 && readErrorFields(result.body).errorCode === "CREDENTIAL_LINKED_BY_RULE") {
54769
55571
  await failLinkedByRule(organizationId, options.env, result.body);
54770
55572
  }
54771
- failLinkOp("unlink", result.status, result.body, credentialLinkId);
55573
+ failLinkOp2("unlink", result.status, result.body, credentialLinkId);
54772
55574
  }
54773
55575
  console.log(colorSuccess(`Unlinked credential link ${credentialLinkId}`));
54774
55576
  }
@@ -54916,12 +55718,12 @@ function buildLinkableEnvironmentsTable(envs) {
54916
55718
  count: String(env2.credentialCount),
54917
55719
  linked: env2.alreadyLinked ? "linked" : ""
54918
55720
  }));
54919
- const idWidth = Math.max("ENVIRONMENT ID".length, ...cells.map((c84) => c84.id.length));
54920
- const nameWidth = Math.max("ENVIRONMENT".length, ...cells.map((c84) => c84.name.length));
55721
+ const idWidth = Math.max("ENVIRONMENT ID".length, ...cells.map((c86) => c86.id.length));
55722
+ const nameWidth = Math.max("ENVIRONMENT".length, ...cells.map((c86) => c86.name.length));
54921
55723
  const countWidth = "CREDENTIALS".length;
54922
55724
  const header = `${"ENVIRONMENT ID".padEnd(idWidth)} ${"ENVIRONMENT".padEnd(nameWidth)} ${"CREDENTIALS".padEnd(countWidth)} STATUS`;
54923
55725
  const rows = cells.map(
54924
- (c84) => `${c84.id.padEnd(idWidth)} ${c84.name.padEnd(nameWidth)} ${c84.count.padEnd(countWidth)} ${c84.linked}`.trimEnd()
55726
+ (c86) => `${c86.id.padEnd(idWidth)} ${c86.name.padEnd(nameWidth)} ${c86.count.padEnd(countWidth)} ${c86.linked}`.trimEnd()
54925
55727
  );
54926
55728
  return { header, rows };
54927
55729
  }
@@ -54980,7 +55782,7 @@ async function runLinkAll(sourceEnvironmentId, options) {
54980
55782
  })
54981
55783
  );
54982
55784
  if (result.status !== CREATED_STATUS) {
54983
- failLinkOp("link all", result.status, result.body, sourceEnvironmentId);
55785
+ failLinkOp2("link all", result.status, result.body, sourceEnvironmentId);
54984
55786
  }
54985
55787
  const rule = result.body;
54986
55788
  if (output === "json") {
@@ -55045,7 +55847,7 @@ async function runUnlinkAll(sourceEnvironmentId, options) {
55045
55847
  })
55046
55848
  );
55047
55849
  if (result.status !== NO_CONTENT_STATUS) {
55048
- failLinkOp("unlink all", result.status, result.body, rule.environmentCredentialSourceId);
55850
+ failLinkOp2("unlink all", result.status, result.body, rule.environmentCredentialSourceId);
55049
55851
  }
55050
55852
  console.log(
55051
55853
  colorSuccess(
@@ -55101,18 +55903,22 @@ function createGetCommand2() {
55101
55903
  });
55102
55904
  }
55103
55905
  function createUpdateCommand2() {
55104
- const command = new Command("update").description("Update a credential (label, environment, or auth config; secrets are immutable)").argument("<credentialId>", "Credential UUID").requiredOption("--env <environmentId>", "Current parent environment UUID").option("--label <label>", "New label").option("--move-env <environmentId>", "Move to a different environment (same application)").option("--clear-auth-config", "Remove the auth config (sends null)").option("-o, --output <format>", "Output format: table, json, tsv (default: table)", "table");
55906
+ const command = new Command("update").description("Update a credential (label, environment, auth config, or secret)").argument("<credentialId>", "Credential UUID").requiredOption("--env <environmentId>", "Current parent environment UUID").option("--label <label>", "New label").option("--move-env <environmentId>", "Move to a different environment (same application)").option("--clear-auth-config", "Remove the auth config (sends null)").option("-o, --output <format>", "Output format: table, json, tsv (default: table)", "table");
55105
55907
  addSecretInputOptions(command);
55106
55908
  addAuthConfigOptions(command).addHelpText(
55107
55909
  "after",
55108
55910
  `
55109
55911
  Auth-config flags must match the credential's existing type (looked up automatically).
55110
- Secrets cannot be changed here \u2014 delete and recreate to rotate.
55912
+ A secret flag rotates the secret in place: the credential keeps its ID and scan history.
55913
+ A username/password credential takes both --username-* and --password-*; an SSH key
55914
+ takes --secret-* for the key and --password-* for its passphrase, if it has one.
55111
55915
 
55112
55916
  Examples:
55113
55917
  tracecli credential update <id> --env <envId> --label "prod (rotated 2026-06)"
55114
55918
  tracecli credential update <id> --env <envId> --login-url https://acme.com/signin
55115
- tracecli credential update <id> --env <envId> --clear-auth-config`
55919
+ tracecli credential update <id> --env <envId> --clear-auth-config
55920
+ vault read -field=token secret/x | tracecli credential update <id> --env <envId> --secret-stdin
55921
+ tracecli credential update <id> --env <envId> --username-env LOGIN_USER --password-env LOGIN_PASS`
55116
55922
  );
55117
55923
  return command.action(async (credentialId, options) => {
55118
55924
  await runUpdate3(credentialId, options);
@@ -55163,7 +55969,7 @@ left \u2014 if the current window is nearly over, the API waits for the next one
55163
55969
  await runMfaCode(credentialId, options);
55164
55970
  });
55165
55971
  }
55166
- function createLinkCommand() {
55972
+ function createLinkCommand2() {
55167
55973
  return new Command("link").description("Link another application's credential into an environment").argument("<credentialId>", "UUID of the credential to link (owned by another environment)").requiredOption("--env <environmentId>", "Environment UUID to link it into").option("-o, --output <format>", "Output format: table, json, tsv (default: table)", "table").addHelpText(
55168
55974
  "after",
55169
55975
  `
@@ -55180,10 +55986,10 @@ Examples:
55180
55986
  tracecli credential linkable --env <envId>
55181
55987
  tracecli credential link <credentialId> --env <envId>`
55182
55988
  ).action(async (credentialId, options) => {
55183
- await runLink(credentialId, options);
55989
+ await runLink2(credentialId, options);
55184
55990
  });
55185
55991
  }
55186
- function createUnlinkCommand() {
55992
+ function createUnlinkCommand2() {
55187
55993
  return new Command("unlink").description("Remove a credential link from an environment (the source credential is kept)").argument("<credentialLinkId>", "Credential link UUID (the LINK ID from `credential links`)").requiredOption("--env <environmentId>", "Environment UUID the credential is linked into").option("-y, --yes", "Skip the confirmation prompt (required in non-interactive shells)").addHelpText(
55188
55994
  "after",
55189
55995
  `
@@ -55192,7 +55998,7 @@ the environment's own credentials. A credential whose whole environment is
55192
55998
  linked here (VIA link-all in \`credential links\`) stays until that is removed
55193
55999
  with \`credential unlink-all\`.`
55194
56000
  ).action(async (credentialLinkId, options) => {
55195
- await runUnlink(credentialLinkId, options);
56001
+ await runUnlink2(credentialLinkId, options);
55196
56002
  });
55197
56003
  }
55198
56004
  function createLinksCommand() {
@@ -55256,8 +56062,8 @@ function createCredentialCommand() {
55256
56062
  credential.addCommand(createSetMfaCommand());
55257
56063
  credential.addCommand(createClearMfaCommand());
55258
56064
  credential.addCommand(createMfaCodeCommand());
55259
- credential.addCommand(createLinkCommand());
55260
- credential.addCommand(createUnlinkCommand());
56065
+ credential.addCommand(createLinkCommand2());
56066
+ credential.addCommand(createUnlinkCommand2());
55261
56067
  credential.addCommand(createLinkAllCommand());
55262
56068
  credential.addCommand(createUnlinkAllCommand());
55263
56069
  credential.addCommand(createLinksCommand());
@@ -55477,14 +56283,14 @@ function buildEnvironmentTable(envs, leading) {
55477
56283
  url: e.primaryUrl != null ? sanitizeDisplay(e.primaryUrl) : "\u2014"
55478
56284
  }));
55479
56285
  const leadingWidths = leading.map(
55480
- (column, i2) => Math.max(column.header.length, ...cells.map((c84) => c84.leading[i2]?.length ?? 0))
56286
+ (column, i2) => Math.max(column.header.length, ...cells.map((c86) => c86.leading[i2]?.length ?? 0))
55481
56287
  );
55482
- const nameWidth = Math.max(4, ...cells.map((c84) => c84.name.length));
56288
+ const nameWidth = Math.max(4, ...cells.map((c86) => c86.name.length));
55483
56289
  const padLeading = (values) => values.map((v, i2) => v.padEnd(leadingWidths[i2] ?? 0)).join(" ");
55484
56290
  return {
55485
- header: `${padLeading(leading.map((c84) => c84.header))} ${"NAME".padEnd(nameWidth)} PROD URL`,
56291
+ header: `${padLeading(leading.map((c86) => c86.header))} ${"NAME".padEnd(nameWidth)} PROD URL`,
55486
56292
  rows: cells.map(
55487
- (c84) => `${padLeading(c84.leading)} ${c84.name.padEnd(nameWidth)} ${c84.prod.padEnd(4)} ${c84.url}`
56293
+ (c86) => `${padLeading(c86.leading)} ${c86.name.padEnd(nameWidth)} ${c86.prod.padEnd(4)} ${c86.url}`
55488
56294
  )
55489
56295
  };
55490
56296
  }
@@ -59119,7 +59925,7 @@ function createFindingsCommand() {
59119
59925
  findings.addCommand(createGetFindingCommand());
59120
59926
  findings.addCommand(createGetFindingReproCommand());
59121
59927
  findings.addCommand(createStatusUpdateCommand());
59122
- findings.addCommand(createAttachEvidenceCommand());
59928
+ findings.addCommand(createAttachEvidenceCommand(), { hidden: true });
59123
59929
  findings.addCommand(createCreateFindingCommand(), { hidden: true });
59124
59930
  findings.addCommand(createCopyFindingsCommand(), { hidden: true });
59125
59931
  findings.addCommand(createPublishFindingsCommand(), { hidden: true });
@@ -59503,7 +60309,7 @@ function resolveQuestionnaireMode(options) {
59503
60309
  ];
59504
60310
  const chosen = exclusive.filter((entry) => entry.set);
59505
60311
  if (chosen.length > 1) {
59506
- console.error(colorError(`Pass only one of ${chosen.map((c84) => c84.flag).join(" / ")}.`));
60312
+ console.error(colorError(`Pass only one of ${chosen.map((c86) => c86.flag).join(" / ")}.`));
59507
60313
  process.exit(1);
59508
60314
  }
59509
60315
  const only = chosen[0];
@@ -59671,7 +60477,7 @@ Examples:
59671
60477
  function createPentestCommand() {
59672
60478
  const pentest = new Command("pentest").description("Manage pentests");
59673
60479
  pentest.addCommand(createListCommand7());
59674
- pentest.addCommand(createUpdateCommand4());
60480
+ pentest.addCommand(createUpdateCommand4(), { hidden: true });
59675
60481
  pentest.addCommand(createCreatePentestCommand(), { hidden: true });
59676
60482
  pentest.addCommand(createVisibilityCommand(), { hidden: true });
59677
60483
  pentest.addCommand(createQuestionnaireCommand());
@@ -60737,13 +61543,13 @@ function buildUploadsTable(uploads) {
60737
61543
  size: formatArchiveBytes(upload.sizeBytes),
60738
61544
  id: upload.codeUploadId
60739
61545
  }));
60740
- const nameWidth = Math.max(4, ...cells.map((c84) => c84.name.length));
60741
- const kindWidth = Math.max(4, ...cells.map((c84) => c84.kind.length));
60742
- const statusWidth = Math.max(6, ...cells.map((c84) => c84.status.length));
60743
- const sizeWidth = Math.max(4, ...cells.map((c84) => c84.size.length));
61546
+ const nameWidth = Math.max(4, ...cells.map((c86) => c86.name.length));
61547
+ const kindWidth = Math.max(4, ...cells.map((c86) => c86.kind.length));
61548
+ const statusWidth = Math.max(6, ...cells.map((c86) => c86.status.length));
61549
+ const sizeWidth = Math.max(4, ...cells.map((c86) => c86.size.length));
60744
61550
  const header = `${"NAME".padEnd(nameWidth)} ${"KIND".padEnd(kindWidth)} ${"STATUS".padEnd(statusWidth)} ${"SIZE".padEnd(sizeWidth)} ID`;
60745
61551
  const rows = cells.map(
60746
- (c84) => `${c84.name.padEnd(nameWidth)} ${c84.kind.padEnd(kindWidth)} ${c84.status.padEnd(statusWidth)} ${c84.size.padEnd(sizeWidth)} ${c84.id}`
61552
+ (c86) => `${c86.name.padEnd(nameWidth)} ${c86.kind.padEnd(kindWidth)} ${c86.status.padEnd(statusWidth)} ${c86.size.padEnd(sizeWidth)} ${c86.id}`
60747
61553
  );
60748
61554
  return { header, rows };
60749
61555
  }
@@ -61230,7 +62036,7 @@ async function runDelete5(codeUploadIdArg, options) {
61230
62036
  }
61231
62037
  console.log(colorSuccess(`Deleted upload ${codeUploadId}`));
61232
62038
  }
61233
- async function runLink2(options) {
62039
+ async function runLink3(options) {
61234
62040
  const output = parseOutput(options.output);
61235
62041
  const applicationId = requireUuid(options.app, "--app");
61236
62042
  const codeUploadId = requireUuid(options.upload, "--upload");
@@ -61362,7 +62168,7 @@ function createDeleteCommand5() {
61362
62168
  await runDelete5(codeUploadId, options);
61363
62169
  });
61364
62170
  }
61365
- function createLinkCommand2() {
62171
+ function createLinkCommand3() {
61366
62172
  return new Command("link").description("Link a ready source upload to an application").requiredOption("--app <applicationId>", "Application UUID").requiredOption("--upload <codeUploadId>", "Upload UUID (must be a ready source ZIP)").option("--path <repositoryPath>", "Path within the archive").option("-o, --output <format>", "Output format: table, json, tsv (default: table)", "table").addHelpText(
61367
62173
  "after",
61368
62174
  `
@@ -61372,7 +62178,7 @@ APK and IPA builds attach to an application with \`upload build --app\`.
61372
62178
  Examples:
61373
62179
  tracecli upload link --app <applicationId> --upload <codeUploadId>`
61374
62180
  ).action(async (options) => {
61375
- await runLink2(options);
62181
+ await runLink3(options);
61376
62182
  });
61377
62183
  }
61378
62184
  function createUploadCommand(runtime = {}) {
@@ -61386,7 +62192,7 @@ function createUploadCommand(runtime = {}) {
61386
62192
  upload.addCommand(createResumeCommand(resolved));
61387
62193
  upload.addCommand(createWaitCommand(resolved));
61388
62194
  upload.addCommand(createDeleteCommand5());
61389
- upload.addCommand(createLinkCommand2());
62195
+ upload.addCommand(createLinkCommand3());
61390
62196
  return upload;
61391
62197
  }
61392
62198
 
@@ -61405,7 +62211,7 @@ function parseVulnerabilityFilter(expression) {
61405
62211
  const match = expression.match(/^([^!=]+)(!=|=)(.+)$/);
61406
62212
  if (!match?.[1] || !match[2] || !match[3]) {
61407
62213
  return {
61408
- error: `Invalid filter expression '${expression}'. Expected format: field=value or field!=value (e.g. cweId=CWE-89).`
62214
+ error: `Invalid filter expression '${expression}'. Expected format: field=value or field!=value (e.g. scanMode=SAST).`
61409
62215
  };
61410
62216
  }
61411
62217
  const field = match[1].trim();
@@ -61646,7 +62452,7 @@ function createListCommand9() {
61646
62452
  }
61647
62453
  ).option(
61648
62454
  "-f, --filter <expression>",
61649
- "Additional filter expression: field=value or field!=value. Repeatable. Supported fields: vulnerabilityType, cweId, scanMode (and any other API-supported field).",
62455
+ "Additional filter expression: field=value or field!=value. Repeatable. Supported fields: scanMode (SAST, SCA, SECRETS, DAST), severity, status, repositoryId.",
61650
62456
  (value, previous) => [...previous, value],
61651
62457
  []
61652
62458
  ).option("-o, --output <format>", "Output format: table, json, tsv (default: table)", "table").option("-w, --web", "Open the vulnerabilities dashboard in a browser").option("--no-pager", "Disable the interactive arrow-key pager for table output").addHelpText(
@@ -61656,8 +62462,7 @@ Examples:
61656
62462
  tracecli vulnerabilities list
61657
62463
  tracecli vulnerabilities list --severity CRITICAL --status OPEN
61658
62464
  tracecli vulnerabilities list --repo securewithtrace/trace --limit 50
61659
- tracecli vulnerabilities list --filter cweId=CWE-89
61660
- tracecli vulnerabilities list --filter vulnerabilityType=SAST --filter scanMode=sast
62465
+ tracecli vulnerabilities list --filter scanMode=SECRETS
61661
62466
  tracecli vulnerabilities list --output json | jq '.[].title'
61662
62467
  tracecli vulnerabilities list --output tsv | cut -f1,7
61663
62468
  tracecli vulnerabilities list --no-pager
@@ -61780,7 +62585,7 @@ program2.addCommand(createEnvironmentCommand());
61780
62585
  program2.addCommand(createCredentialCommand());
61781
62586
  program2.addCommand(createInboxCommand());
61782
62587
  program2.addCommand(createRepoCommand());
61783
- program2.addCommand(createVulnerabilitiesCommand());
62588
+ program2.addCommand(createVulnerabilitiesCommand(), { hidden: true });
61784
62589
  program2.addCommand(createCodeSizeCommand());
61785
62590
  program2.addCommand(createUploadCommand());
61786
62591
  program2.addCommand(createPentestCommand());