securewithtrace 0.9.0 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +81 -6
  2. package/dist/index.js +911 -167
  3. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -3682,7 +3682,7 @@ function extractCommandTree(cmd) {
3682
3682
  description: o.description,
3683
3683
  takesValue: o.required || o.optional
3684
3684
  }));
3685
- const subcommands = cmd.commands.filter((c24) => !c24._hidden).map((c24) => extractCommandTree(c24));
3685
+ const subcommands = cmd.commands.filter((c25) => !c25._hidden).map((c25) => extractCommandTree(c25));
3686
3686
  return {
3687
3687
  name: cmd.name(),
3688
3688
  description: cmd.description(),
@@ -3748,7 +3748,7 @@ function generateBashCompletion(tree, binName) {
3748
3748
  }
3749
3749
  function collectBashCases(tree, prefix) {
3750
3750
  const cases = /* @__PURE__ */ new Map();
3751
- const subcommandNames = tree.subcommands.map((c24) => c24.name);
3751
+ const subcommandNames = tree.subcommands.map((c25) => c25.name);
3752
3752
  const optionNames = tree.options.flatMap((o) => o.short ? [o.long, o.short] : [o.long]);
3753
3753
  const extraOptions = prefix === "" ? ["--help", "--version"] : ["--help"];
3754
3754
  const allOptions = [.../* @__PURE__ */ new Set([...optionNames, ...extraOptions])];
@@ -10803,7 +10803,7 @@ var $ZodBase64 = /* @__PURE__ */ $constructor("$ZodBase64", (inst, def) => {
10803
10803
  function isValidBase64URL(data) {
10804
10804
  if (!base64url.test(data))
10805
10805
  return false;
10806
- const base643 = data.replace(/[-_]/g, (c24) => c24 === "-" ? "+" : "/");
10806
+ const base643 = data.replace(/[-_]/g, (c25) => c25 === "-" ? "+" : "/");
10807
10807
  const padded = base643.padEnd(Math.ceil(base643.length / 4) * 4, "=");
10808
10808
  return isValidBase64(padded);
10809
10809
  }
@@ -20847,9 +20847,9 @@ var ZodDate2 = /* @__PURE__ */ $constructor("ZodDate", (inst, def) => {
20847
20847
  inst._zod.processJSONSchema = (ctx, json2, params) => dateProcessor(inst, ctx, json2, params);
20848
20848
  inst.min = (value, params) => inst.check(_gte(value, params));
20849
20849
  inst.max = (value, params) => inst.check(_lte(value, params));
20850
- const c24 = inst._zod.bag;
20851
- inst.minDate = c24.minimum ? new Date(c24.minimum) : null;
20852
- inst.maxDate = c24.maximum ? new Date(c24.maximum) : null;
20850
+ const c25 = inst._zod.bag;
20851
+ inst.minDate = c25.minimum ? new Date(c25.minimum) : null;
20852
+ inst.maxDate = c25.maximum ? new Date(c25.maximum) : null;
20853
20853
  });
20854
20854
  function date3(params) {
20855
20855
  return _date(ZodDate2, params);
@@ -22161,6 +22161,331 @@ var chatSessionContract = c2.router({
22161
22161
  }
22162
22162
  });
22163
22163
 
22164
+ // ../../packages/api-contracts/src/schemas/domain/dependency.schema.ts
22165
+ var PackageEcosystemValues = [
22166
+ "NPM",
22167
+ "PYPI",
22168
+ "CARGO",
22169
+ "MAVEN",
22170
+ "GOLANG",
22171
+ "RUBYGEMS",
22172
+ "NUGET",
22173
+ "COMPOSER",
22174
+ "UNKNOWN"
22175
+ ];
22176
+ var PackageEcosystem = external_exports.enum(PackageEcosystemValues);
22177
+ var DependencyScopeValues = ["PRODUCTION", "DEV", "PEER", "OPTIONAL", "BUNDLED"];
22178
+ var DependencyScope = external_exports.enum(DependencyScopeValues);
22179
+ var DependencySourceValues = ["REGISTRY", "WORKSPACE", "GIT", "FILE", "URL"];
22180
+ var DependencySource = external_exports.enum(DependencySourceValues);
22181
+ var DependencyIndexStatusValues = [
22182
+ "PENDING",
22183
+ "INDEXING",
22184
+ "COMPLETED",
22185
+ "PARTIAL",
22186
+ "FAILED"
22187
+ ];
22188
+ var DependencyIndexStatus = external_exports.enum(DependencyIndexStatusValues);
22189
+
22190
+ // ../../packages/api-contracts/src/schemas/api/v1/dependency.api.ts
22191
+ var RepositoryDependencyParamsSchema = OrgIdParamsSchema.extend({
22192
+ repositoryId: external_exports.uuid()
22193
+ });
22194
+ var PackageDependencyParamsSchema = OrgIdParamsSchema.extend({
22195
+ packageId: external_exports.uuid()
22196
+ });
22197
+ var FailedManifestSchema = external_exports.object({
22198
+ filePath: external_exports.string(),
22199
+ ecosystem: PackageEcosystem,
22200
+ error: external_exports.string(),
22201
+ severity: external_exports.enum(["warning", "error"])
22202
+ });
22203
+ var VersionEntrySchema = external_exports.object({
22204
+ version: external_exports.string(),
22205
+ versionConstraint: external_exports.string().nullable(),
22206
+ isDirect: external_exports.boolean(),
22207
+ scope: DependencyScope,
22208
+ source: DependencySource,
22209
+ depthFromRoot: external_exports.number(),
22210
+ /** Manifest files where this specific version appears. */
22211
+ manifestFiles: external_exports.array(external_exports.string())
22212
+ });
22213
+ var DependencyIndexSchema = external_exports.object({
22214
+ indexId: external_exports.uuid(),
22215
+ status: DependencyIndexStatus,
22216
+ commitSha: external_exports.string().nullable(),
22217
+ packageCount: external_exports.number(),
22218
+ ecosystems: external_exports.array(PackageEcosystem),
22219
+ manifestFiles: external_exports.array(external_exports.string()),
22220
+ failedManifests: external_exports.array(FailedManifestSchema),
22221
+ durationMs: external_exports.number().nullable(),
22222
+ indexedAt: external_exports.coerce.date().nullable(),
22223
+ errorMessage: external_exports.string().nullable(),
22224
+ createdAt: external_exports.coerce.date()
22225
+ });
22226
+ var DependencySearchRequestSchema = external_exports.object({
22227
+ query: external_exports.string().min(1).max(200),
22228
+ ecosystem: PackageEcosystem.optional(),
22229
+ isDirect: external_exports.boolean().optional(),
22230
+ scope: DependencyScope.optional(),
22231
+ repositoryIds: external_exports.array(external_exports.uuid()).optional(),
22232
+ /** Exact version match, e.g. "1.2.3". */
22233
+ version: external_exports.string().max(100).optional(),
22234
+ /** Version prefix match, e.g. "1.2" matches "1.2.0", "1.2.3", etc. */
22235
+ versionPrefix: external_exports.string().max(50).optional(),
22236
+ page: external_exports.number().int().min(1).default(1).optional(),
22237
+ pageSize: external_exports.number().int().min(1).max(100).default(20).optional(),
22238
+ sortBy: external_exports.enum(["name", "repositoryCount"]).default("repositoryCount").optional(),
22239
+ sortOrder: external_exports.enum(["asc", "desc"]).default("desc").optional()
22240
+ });
22241
+ var PackageSearchResultSchema = external_exports.object({
22242
+ packageId: external_exports.uuid(),
22243
+ name: external_exports.string(),
22244
+ ecosystem: PackageEcosystem,
22245
+ repositories: external_exports.array(
22246
+ external_exports.object({
22247
+ repositoryId: external_exports.uuid(),
22248
+ fullName: external_exports.string(),
22249
+ /** All resolved versions of this package in this repository. */
22250
+ versions: external_exports.array(VersionEntrySchema)
22251
+ })
22252
+ )
22253
+ });
22254
+ var DependencySearchResponseSchema = external_exports.object({
22255
+ packages: external_exports.array(PackageSearchResultSchema),
22256
+ total: external_exports.number(),
22257
+ page: external_exports.number(),
22258
+ pageSize: external_exports.number()
22259
+ });
22260
+ var DependencyAutocompleteQuerySchema = external_exports.object({
22261
+ q: external_exports.string().min(1).max(200),
22262
+ ecosystem: external_exports.enum(PackageEcosystemValues).optional()
22263
+ });
22264
+ var AutocompleteSuggestionSchema = external_exports.object({
22265
+ packageId: external_exports.uuid(),
22266
+ name: external_exports.string(),
22267
+ ecosystem: PackageEcosystem,
22268
+ repositoryCount: external_exports.number()
22269
+ });
22270
+ var DependencyAutocompleteResponseSchema = external_exports.object({
22271
+ suggestions: external_exports.array(AutocompleteSuggestionSchema)
22272
+ });
22273
+ var ListRepoDependenciesQuerySchema = external_exports.object({
22274
+ search: external_exports.string().max(200).optional(),
22275
+ isDirect: external_exports.enum(["true", "false"]).transform((v) => v === "true").optional(),
22276
+ scope: DependencyScope.optional(),
22277
+ ecosystem: external_exports.enum(PackageEcosystemValues).optional(),
22278
+ /** Exact version match, e.g. "1.2.3". */
22279
+ version: external_exports.string().max(100).optional(),
22280
+ /** Version prefix match, e.g. "1.2" matches "1.2.0", "1.2.3", etc. */
22281
+ versionPrefix: external_exports.string().max(50).optional(),
22282
+ page: external_exports.coerce.number().int().min(1).default(1).optional(),
22283
+ pageSize: external_exports.coerce.number().int().min(1).max(200).default(50).optional(),
22284
+ sortBy: external_exports.enum(["name", "ecosystem"]).default("name").optional(),
22285
+ sortOrder: external_exports.enum(["asc", "desc"]).default("asc").optional()
22286
+ });
22287
+ var RepoDependencySchema = external_exports.object({
22288
+ packageId: external_exports.uuid(),
22289
+ name: external_exports.string(),
22290
+ ecosystem: PackageEcosystem,
22291
+ /** All resolved versions of this package in the repository. */
22292
+ versions: external_exports.array(VersionEntrySchema)
22293
+ });
22294
+ var ListRepoDependenciesResponseSchema = external_exports.object({
22295
+ dependencies: external_exports.array(RepoDependencySchema),
22296
+ total: external_exports.number(),
22297
+ page: external_exports.number(),
22298
+ pageSize: external_exports.number(),
22299
+ /** All manifest files discovered in this repository. */
22300
+ manifestFiles: external_exports.array(external_exports.string())
22301
+ });
22302
+ var PackageRepositoriesQuerySchema = external_exports.object({
22303
+ /** Exact version match, e.g. "1.2.3". */
22304
+ version: external_exports.string().max(100).optional(),
22305
+ /** Version prefix match, e.g. "1.2" matches "1.2.0", "1.2.3", etc. */
22306
+ versionPrefix: external_exports.string().max(50).optional(),
22307
+ page: external_exports.coerce.number().int().min(1).default(1).optional(),
22308
+ pageSize: external_exports.coerce.number().int().min(1).max(100).default(25).optional()
22309
+ });
22310
+ var PackageRepositoriesResponseSchema = external_exports.object({
22311
+ repositories: external_exports.array(
22312
+ external_exports.object({
22313
+ repositoryId: external_exports.uuid(),
22314
+ fullName: external_exports.string(),
22315
+ htmlUrl: external_exports.string().nullable(),
22316
+ /** All resolved versions of this package in this repository. */
22317
+ versions: external_exports.array(VersionEntrySchema)
22318
+ })
22319
+ ),
22320
+ total: external_exports.number(),
22321
+ page: external_exports.number(),
22322
+ pageSize: external_exports.number()
22323
+ });
22324
+ var DependencyIndexResponseSchema = external_exports.object({
22325
+ latestIndex: DependencyIndexSchema.nullable()
22326
+ });
22327
+ var TriggerReindexResponseSchema = external_exports.object({
22328
+ indexId: external_exports.uuid(),
22329
+ status: DependencyIndexStatus
22330
+ });
22331
+ var BulkIndexStatusResponseSchema = external_exports.object({
22332
+ repositories: external_exports.array(
22333
+ external_exports.object({
22334
+ repositoryId: external_exports.uuid(),
22335
+ fullName: external_exports.string(),
22336
+ status: DependencyIndexStatus.nullable(),
22337
+ packageCount: external_exports.number().nullable(),
22338
+ indexedAt: external_exports.coerce.date().nullable()
22339
+ })
22340
+ )
22341
+ });
22342
+ var DependencyFilterOptionsResponseSchema = external_exports.object({
22343
+ /** Repositories that have at least one indexed dependency. */
22344
+ repositories: external_exports.array(
22345
+ external_exports.object({
22346
+ repositoryId: external_exports.uuid(),
22347
+ name: external_exports.string()
22348
+ })
22349
+ ),
22350
+ ecosystems: external_exports.array(PackageEcosystem),
22351
+ scopes: external_exports.array(DependencyScope)
22352
+ });
22353
+ var DependencySummaryResponseSchema = external_exports.object({
22354
+ totalPackages: external_exports.number(),
22355
+ totalRepositories: external_exports.number(),
22356
+ ecosystemBreakdown: external_exports.record(PackageEcosystem, external_exports.number()),
22357
+ lastIndexedAt: external_exports.coerce.date().nullable(),
22358
+ indexingInProgress: external_exports.number()
22359
+ });
22360
+
22361
+ // ../../packages/api-contracts/src/contracts/v1/dependency.ts
22362
+ var c3 = initContract();
22363
+ var dependencyContract = c3.router({
22364
+ /** Cross-repo dependency search. Two-phase: pg_trgm on Package, then join repos. */
22365
+ searchDependencies: {
22366
+ method: "POST",
22367
+ path: "/organizations/:organizationId/dependencies/search",
22368
+ pathParams: OrgIdParamsSchema,
22369
+ body: DependencySearchRequestSchema,
22370
+ responses: {
22371
+ 200: DependencySearchResponseSchema,
22372
+ 400: ErrorResponseSchema,
22373
+ 401: ErrorResponseSchema,
22374
+ 403: ErrorResponseSchema,
22375
+ 404: ErrorResponseSchema,
22376
+ 500: ErrorResponseSchema
22377
+ }
22378
+ },
22379
+ /** Autocomplete for dependency names across all repos in org. */
22380
+ autocompleteDependencies: {
22381
+ method: "GET",
22382
+ path: "/organizations/:organizationId/dependencies/autocomplete",
22383
+ pathParams: OrgIdParamsSchema,
22384
+ query: DependencyAutocompleteQuerySchema,
22385
+ responses: {
22386
+ 200: DependencyAutocompleteResponseSchema,
22387
+ 401: ErrorResponseSchema,
22388
+ 403: ErrorResponseSchema,
22389
+ 404: ErrorResponseSchema,
22390
+ 500: ErrorResponseSchema
22391
+ }
22392
+ },
22393
+ /** List dependencies for a specific repository, deduplicated by package. */
22394
+ listRepositoryDependencies: {
22395
+ method: "GET",
22396
+ path: "/organizations/:organizationId/repositories/:repositoryId/dependencies",
22397
+ pathParams: RepositoryDependencyParamsSchema,
22398
+ query: ListRepoDependenciesQuerySchema,
22399
+ responses: {
22400
+ 200: ListRepoDependenciesResponseSchema,
22401
+ 401: ErrorResponseSchema,
22402
+ 403: ErrorResponseSchema,
22403
+ 404: ErrorResponseSchema,
22404
+ 500: ErrorResponseSchema
22405
+ }
22406
+ },
22407
+ /** Reverse lookup: which repositories use a given package. */
22408
+ listPackageRepositories: {
22409
+ method: "GET",
22410
+ path: "/organizations/:organizationId/dependencies/packages/:packageId/repositories",
22411
+ pathParams: PackageDependencyParamsSchema,
22412
+ query: PackageRepositoriesQuerySchema,
22413
+ responses: {
22414
+ 200: PackageRepositoriesResponseSchema,
22415
+ 401: ErrorResponseSchema,
22416
+ 403: ErrorResponseSchema,
22417
+ 404: ErrorResponseSchema,
22418
+ 500: ErrorResponseSchema
22419
+ }
22420
+ },
22421
+ /** Get dependency indexing status for a repository. */
22422
+ getDependencyIndex: {
22423
+ method: "GET",
22424
+ path: "/organizations/:organizationId/repositories/:repositoryId/dependency-index",
22425
+ pathParams: RepositoryDependencyParamsSchema,
22426
+ responses: {
22427
+ 200: DependencyIndexResponseSchema,
22428
+ 401: ErrorResponseSchema,
22429
+ 403: ErrorResponseSchema,
22430
+ 404: ErrorResponseSchema,
22431
+ 500: ErrorResponseSchema
22432
+ }
22433
+ },
22434
+ /** Trigger a manual re-index of a repository's dependencies. */
22435
+ triggerReindex: {
22436
+ method: "POST",
22437
+ path: "/organizations/:organizationId/repositories/:repositoryId/dependency-index",
22438
+ pathParams: RepositoryDependencyParamsSchema,
22439
+ body: null,
22440
+ responses: {
22441
+ 201: TriggerReindexResponseSchema,
22442
+ 401: ErrorResponseSchema,
22443
+ 403: ErrorResponseSchema,
22444
+ 404: ErrorResponseSchema,
22445
+ 500: ErrorResponseSchema
22446
+ }
22447
+ },
22448
+ /** Available filter options for dependency search dropdowns. */
22449
+ getFilterOptions: {
22450
+ method: "GET",
22451
+ path: "/organizations/:organizationId/dependencies/filter-options",
22452
+ pathParams: OrgIdParamsSchema,
22453
+ responses: {
22454
+ 200: DependencyFilterOptionsResponseSchema,
22455
+ 401: ErrorResponseSchema,
22456
+ 403: ErrorResponseSchema,
22457
+ 404: ErrorResponseSchema,
22458
+ 500: ErrorResponseSchema
22459
+ }
22460
+ },
22461
+ /** Bulk index status for all repos in org (dashboard view). */
22462
+ getBulkIndexStatus: {
22463
+ method: "GET",
22464
+ path: "/organizations/:organizationId/dependencies/index-status",
22465
+ pathParams: OrgIdParamsSchema,
22466
+ responses: {
22467
+ 200: BulkIndexStatusResponseSchema,
22468
+ 401: ErrorResponseSchema,
22469
+ 403: ErrorResponseSchema,
22470
+ 404: ErrorResponseSchema,
22471
+ 500: ErrorResponseSchema
22472
+ }
22473
+ },
22474
+ /** Org-wide dependency summary with ecosystem breakdown. */
22475
+ getDependencySummary: {
22476
+ method: "GET",
22477
+ path: "/organizations/:organizationId/dependencies/summary",
22478
+ pathParams: OrgIdParamsSchema,
22479
+ responses: {
22480
+ 200: DependencySummaryResponseSchema,
22481
+ 401: ErrorResponseSchema,
22482
+ 403: ErrorResponseSchema,
22483
+ 404: ErrorResponseSchema,
22484
+ 500: ErrorResponseSchema
22485
+ }
22486
+ }
22487
+ });
22488
+
22164
22489
  // ../../packages/api-contracts/src/schemas/api/v1/docker-scout-integration.api.ts
22165
22490
  var DockerScoutIntegrationIdParamsSchema = OrgIdParamsSchema.extend({
22166
22491
  dockerScoutIntegrationId: external_exports.uuid()
@@ -22180,8 +22505,8 @@ var DockerScoutIntegrationResponseSchema = external_exports.object({
22180
22505
  });
22181
22506
 
22182
22507
  // ../../packages/api-contracts/src/contracts/v1/docker-scout-integration.ts
22183
- var c3 = initContract();
22184
- var dockerScoutIntegrationContract = c3.router({
22508
+ var c4 = initContract();
22509
+ var dockerScoutIntegrationContract = c4.router({
22185
22510
  createDockerScoutIntegration: {
22186
22511
  method: "POST",
22187
22512
  path: "/organizations/:organizationId/docker-scout-integration",
@@ -22242,9 +22567,7 @@ var GitHubCallbackQuerySchema = external_exports.object({
22242
22567
  });
22243
22568
  var GitHubCallbackRequestSchema = external_exports.object({
22244
22569
  installationId: external_exports.number().int(),
22245
- setupAction: external_exports.enum(["install", "update", "request"]),
22246
- githubAccountId: external_exports.number().int().optional(),
22247
- githubAccountLogin: external_exports.string().min(1).optional()
22570
+ setupAction: external_exports.enum(["install", "update", "request"])
22248
22571
  });
22249
22572
  var GitHubInstallationResponseSchema = external_exports.object({
22250
22573
  installationId: external_exports.uuid(),
@@ -22274,13 +22597,14 @@ var GitHubRepoSchema = external_exports.object({
22274
22597
  fullName: external_exports.string(),
22275
22598
  private: external_exports.boolean(),
22276
22599
  htmlUrl: external_exports.string().url(),
22277
- defaultBranch: external_exports.string()
22600
+ defaultBranch: external_exports.string(),
22601
+ language: external_exports.string().nullable()
22278
22602
  });
22279
22603
  var ListGitHubInstallationReposResponseSchema = external_exports.array(GitHubRepoSchema);
22280
22604
 
22281
22605
  // ../../packages/api-contracts/src/contracts/v1/github.ts
22282
- var c4 = initContract();
22283
- var githubCallbackContract = c4.router(
22606
+ var c5 = initContract();
22607
+ var githubCallbackContract = c5.router(
22284
22608
  {
22285
22609
  handleCallback: {
22286
22610
  method: "POST",
@@ -22300,7 +22624,7 @@ var githubCallbackContract = c4.router(
22300
22624
  pathPrefix: "/v1/github"
22301
22625
  }
22302
22626
  );
22303
- var githubContract = c4.router({
22627
+ var githubContract = c5.router({
22304
22628
  generateInstallLink: {
22305
22629
  method: "POST",
22306
22630
  path: "/organizations/:organizationId/github/install-link",
@@ -22370,8 +22694,8 @@ var UpdateKnowledgeBaseResponseSchema = external_exports.object({
22370
22694
  });
22371
22695
 
22372
22696
  // ../../packages/api-contracts/src/contracts/v1/knowledge-base.ts
22373
- var c5 = initContract();
22374
- var knowledgeBaseContract = c5.router({
22697
+ var c6 = initContract();
22698
+ var knowledgeBaseContract = c6.router({
22375
22699
  getKnowledgeBase: {
22376
22700
  method: "GET",
22377
22701
  path: "/organizations/:organizationId/knowledge-base",
@@ -22592,8 +22916,8 @@ var UpdateRepoDocumentContentResponseSchema = external_exports.object({
22592
22916
  });
22593
22917
 
22594
22918
  // ../../packages/api-contracts/src/contracts/v1/knowledge-document.ts
22595
- var c6 = initContract();
22596
- var knowledgeDocumentContract = c6.router({
22919
+ var c7 = initContract();
22920
+ var knowledgeDocumentContract = c7.router({
22597
22921
  createDocument: {
22598
22922
  method: "POST",
22599
22923
  path: "/organizations/:organizationId/knowledge-documents",
@@ -22902,8 +23226,8 @@ var LlmProviderKeyResponseSchema = external_exports.object({
22902
23226
  var ListLlmProviderKeysResponseSchema = external_exports.array(LlmProviderKeyResponseSchema);
22903
23227
 
22904
23228
  // ../../packages/api-contracts/src/contracts/v1/llm-provider-key.ts
22905
- var c7 = initContract();
22906
- var llmProviderKeyContract = c7.router({
23229
+ var c8 = initContract();
23230
+ var llmProviderKeyContract = c8.router({
22907
23231
  createLlmProviderKey: {
22908
23232
  method: "POST",
22909
23233
  path: "/organizations/:organizationId/llm-provider-keys",
@@ -23018,8 +23342,8 @@ var LogoDeleteRequestSchema = external_exports.object({
23018
23342
  });
23019
23343
 
23020
23344
  // ../../packages/api-contracts/src/contracts/v1/organization.ts
23021
- var c8 = initContract();
23022
- var organizationContract = c8.router({
23345
+ var c9 = initContract();
23346
+ var organizationContract = c9.router({
23023
23347
  listOrganizations: {
23024
23348
  method: "GET",
23025
23349
  path: "/organizations",
@@ -23101,10 +23425,12 @@ var PentestStatusValues = ["SCOPING", "IN_PROGRESS", "REMEDIATION", "CLOSED"];
23101
23425
  var PentestStatus = external_exports.enum(PentestStatusValues);
23102
23426
  var PentestFindingStatusValues = [
23103
23427
  "OPEN",
23428
+ "IN_PROGRESS",
23104
23429
  "CONTESTED",
23105
23430
  "REMEDIATED",
23106
23431
  "VERIFIED_FIXED",
23107
- "VERIFIED_OPEN"
23432
+ "VERIFIED_OPEN",
23433
+ "DISMISSED"
23108
23434
  ];
23109
23435
  var PentestFindingStatus = external_exports.enum(PentestFindingStatusValues);
23110
23436
  var PentestFindingSeverityValues = [
@@ -23129,7 +23455,7 @@ var RetestOutcome = external_exports.enum(RetestOutcomeValues);
23129
23455
 
23130
23456
  // ../../packages/api-contracts/src/schemas/api/v1/pentest.api.ts
23131
23457
  var PentestIdParamsSchema = OrgIdParamsSchema.extend({
23132
- pentestId: external_exports.uuid()
23458
+ pentestId: external_exports.string().min(1)
23133
23459
  });
23134
23460
  var PentestResponseSchema = external_exports.object({
23135
23461
  pentestId: external_exports.uuid(),
@@ -23170,8 +23496,8 @@ var UpdatePentestRequestSchema = external_exports.object({
23170
23496
  });
23171
23497
 
23172
23498
  // ../../packages/api-contracts/src/contracts/v1/pentest.ts
23173
- var c9 = initContract();
23174
- var pentestContract = c9.router({
23499
+ var c10 = initContract();
23500
+ var pentestContract = c10.router({
23175
23501
  listPentests: {
23176
23502
  method: "GET",
23177
23503
  path: "/organizations/:organizationId/pentests",
@@ -23276,12 +23602,15 @@ var PentestFindingResponseSchema = external_exports.object({
23276
23602
  cvssVector: external_exports.string().nullable(),
23277
23603
  cvssScore: external_exports.number().nullable(),
23278
23604
  owaspCategory: external_exports.string().nullable(),
23605
+ proofOfConcept: external_exports.string().nullable(),
23606
+ remediation: external_exports.string().nullable(),
23279
23607
  publishedAt: external_exports.coerce.date().nullable(),
23280
23608
  createdById: external_exports.uuid(),
23281
23609
  createdByName: external_exports.string(),
23282
23610
  createdAt: external_exports.coerce.date(),
23283
23611
  updatedAt: external_exports.coerce.date(),
23284
- repositories: external_exports.array(PentestFindingRepositorySchema)
23612
+ repositories: external_exports.array(PentestFindingRepositorySchema),
23613
+ vulnerabilityIds: external_exports.array(external_exports.uuid())
23285
23614
  });
23286
23615
  var ListPentestFindingsResponseSchema = external_exports.array(PentestFindingResponseSchema);
23287
23616
  var CreatePentestFindingRequestSchema = external_exports.object({
@@ -23294,6 +23623,8 @@ var CreatePentestFindingRequestSchema = external_exports.object({
23294
23623
  cvssVector: external_exports.string().max(200).optional(),
23295
23624
  cvssScore: external_exports.number().min(0).max(10).optional(),
23296
23625
  owaspCategory: external_exports.string().max(200).optional(),
23626
+ proofOfConcept: external_exports.string().max(5e4).optional(),
23627
+ remediation: external_exports.string().max(5e4).optional(),
23297
23628
  repositories: external_exports.array(
23298
23629
  external_exports.object({
23299
23630
  repositoryId: external_exports.uuid(),
@@ -23302,7 +23633,8 @@ var CreatePentestFindingRequestSchema = external_exports.object({
23302
23633
  startLine: external_exports.number().int().optional(),
23303
23634
  endLine: external_exports.number().int().optional()
23304
23635
  })
23305
- ).optional()
23636
+ ).optional(),
23637
+ vulnerabilityIds: external_exports.array(external_exports.uuid()).optional()
23306
23638
  });
23307
23639
  var UpdatePentestFindingRequestSchema = external_exports.object({
23308
23640
  title: external_exports.string().min(1).max(500).optional(),
@@ -23314,6 +23646,8 @@ var UpdatePentestFindingRequestSchema = external_exports.object({
23314
23646
  cvssVector: external_exports.string().max(200).nullable().optional(),
23315
23647
  cvssScore: external_exports.number().min(0).max(10).nullable().optional(),
23316
23648
  owaspCategory: external_exports.string().max(200).nullable().optional(),
23649
+ proofOfConcept: external_exports.string().max(5e4).nullable().optional(),
23650
+ remediation: external_exports.string().max(5e4).nullable().optional(),
23317
23651
  publishedAt: external_exports.coerce.date().nullable().optional(),
23318
23652
  repositories: external_exports.array(
23319
23653
  external_exports.object({
@@ -23323,7 +23657,8 @@ var UpdatePentestFindingRequestSchema = external_exports.object({
23323
23657
  startLine: external_exports.number().int().optional(),
23324
23658
  endLine: external_exports.number().int().optional()
23325
23659
  })
23326
- ).optional()
23660
+ ).optional(),
23661
+ vulnerabilityIds: external_exports.array(external_exports.uuid()).optional()
23327
23662
  });
23328
23663
  var PentestFindingContestResponseSchema = external_exports.object({
23329
23664
  contestId: external_exports.uuid(),
@@ -23384,6 +23719,13 @@ var UpdatePentestFindingNoteRequestSchema = external_exports.object({
23384
23719
  var ParseFindingMarkdownRequestSchema = external_exports.object({
23385
23720
  markdown: external_exports.string().min(1).max(1e5)
23386
23721
  });
23722
+ var ParsedFindingVulnerabilitySchema = external_exports.object({
23723
+ title: external_exports.string(),
23724
+ description: external_exports.string().nullable(),
23725
+ affectedRepos: external_exports.array(external_exports.string()),
23726
+ codeSnippet: external_exports.string().nullable(),
23727
+ exploitConditions: external_exports.string().nullable()
23728
+ });
23387
23729
  var ParseFindingMarkdownResponseSchema = external_exports.object({
23388
23730
  title: external_exports.string(),
23389
23731
  description: external_exports.string(),
@@ -23393,12 +23735,28 @@ var ParseFindingMarkdownResponseSchema = external_exports.object({
23393
23735
  cweIds: external_exports.array(external_exports.string()),
23394
23736
  cvssVector: external_exports.string().nullable(),
23395
23737
  cvssScore: external_exports.number().nullable(),
23396
- owaspCategory: external_exports.string().nullable()
23738
+ owaspCategory: external_exports.string().nullable(),
23739
+ proofOfConcept: external_exports.string().nullable(),
23740
+ remediation: external_exports.string().nullable(),
23741
+ vulnerabilities: external_exports.array(ParsedFindingVulnerabilitySchema)
23742
+ });
23743
+ var ParseTaskStatusSchema = external_exports.enum(["pending", "completed", "failed"]);
23744
+ var StartParseTaskResponseSchema = external_exports.object({
23745
+ taskId: external_exports.string()
23746
+ });
23747
+ var ParseTaskIdParamsSchema = PentestFindingParamsSchema.extend({
23748
+ taskId: external_exports.string()
23749
+ });
23750
+ var GetParseTaskStatusResponseSchema = external_exports.object({
23751
+ taskId: external_exports.string(),
23752
+ status: ParseTaskStatusSchema,
23753
+ result: ParseFindingMarkdownResponseSchema.nullable(),
23754
+ error: external_exports.string().nullable()
23397
23755
  });
23398
23756
 
23399
23757
  // ../../packages/api-contracts/src/contracts/v1/pentest-finding.ts
23400
- var c10 = initContract();
23401
- var pentestFindingContract = c10.router({
23758
+ var c11 = initContract();
23759
+ var pentestFindingContract = c11.router({
23402
23760
  // ─── Findings ───
23403
23761
  listFindings: {
23404
23762
  method: "GET",
@@ -23599,6 +23957,33 @@ var pentestFindingContract = c10.router({
23599
23957
  404: ErrorResponseSchema,
23600
23958
  500: ErrorResponseSchema
23601
23959
  }
23960
+ },
23961
+ // ─── Async AI parsing (polling) ───
23962
+ startParseTask: {
23963
+ method: "POST",
23964
+ path: "/organizations/:organizationId/pentests/:pentestId/findings/parse-markdown/tasks",
23965
+ pathParams: PentestFindingParamsSchema,
23966
+ body: ParseFindingMarkdownRequestSchema,
23967
+ responses: {
23968
+ 202: StartParseTaskResponseSchema,
23969
+ 400: ErrorResponseSchema,
23970
+ 401: ErrorResponseSchema,
23971
+ 403: ErrorResponseSchema,
23972
+ 404: ErrorResponseSchema,
23973
+ 500: ErrorResponseSchema
23974
+ }
23975
+ },
23976
+ getParseTaskStatus: {
23977
+ method: "GET",
23978
+ path: "/organizations/:organizationId/pentests/:pentestId/findings/parse-markdown/tasks/:taskId",
23979
+ pathParams: ParseTaskIdParamsSchema,
23980
+ responses: {
23981
+ 200: GetParseTaskStatusResponseSchema,
23982
+ 401: ErrorResponseSchema,
23983
+ 403: ErrorResponseSchema,
23984
+ 404: ErrorResponseSchema,
23985
+ 500: ErrorResponseSchema
23986
+ }
23602
23987
  }
23603
23988
  });
23604
23989
 
@@ -23627,6 +24012,7 @@ var RepositoryResponseSchema = external_exports.object({
23627
24012
  platform: external_exports.enum(PlatformValues),
23628
24013
  status: external_exports.enum(RepositoryStatusValues),
23629
24014
  defaultBranch: external_exports.string(),
24015
+ primaryLanguage: external_exports.string().nullable().optional(),
23630
24016
  createdAt: external_exports.date(),
23631
24017
  updatedAt: external_exports.date()
23632
24018
  });
@@ -23640,8 +24026,8 @@ var CloneTokenResponseSchema = external_exports.object({
23640
24026
  });
23641
24027
 
23642
24028
  // ../../packages/api-contracts/src/contracts/v1/repository.ts
23643
- var c11 = initContract();
23644
- var repositoryContract = c11.router({
24029
+ var c12 = initContract();
24030
+ var repositoryContract = c12.router({
23645
24031
  createRepository: {
23646
24032
  method: "POST",
23647
24033
  path: "/organizations/:organizationId/repositories",
@@ -23839,8 +24225,8 @@ var ListScanLogsResponseSchema = external_exports.object({
23839
24225
  });
23840
24226
 
23841
24227
  // ../../packages/api-contracts/src/contracts/v1/scan.ts
23842
- var c12 = initContract();
23843
- var scanContract = c12.router({
24228
+ var c13 = initContract();
24229
+ var scanContract = c13.router({
23844
24230
  createScan: {
23845
24231
  method: "POST",
23846
24232
  path: "/organizations/:organizationId/scans",
@@ -23945,8 +24331,8 @@ var UserResponseSchema = external_exports.object({
23945
24331
  var ListUsersResponseSchema = external_exports.array(UserResponseSchema);
23946
24332
 
23947
24333
  // ../../packages/api-contracts/src/contracts/v1/user.ts
23948
- var c13 = initContract();
23949
- var userContract = c13.router({
24334
+ var c14 = initContract();
24335
+ var userContract = c14.router({
23950
24336
  listUsers: {
23951
24337
  method: "GET",
23952
24338
  path: "/organizations/:organizationId/users",
@@ -24018,8 +24404,8 @@ var VantaIntegrationResponseSchema = external_exports.object({
24018
24404
  });
24019
24405
 
24020
24406
  // ../../packages/api-contracts/src/contracts/v1/vanta-integration.ts
24021
- var c14 = initContract();
24022
- var vantaIntegrationContract = c14.router({
24407
+ var c15 = initContract();
24408
+ var vantaIntegrationContract = c15.router({
24023
24409
  createVantaIntegration: {
24024
24410
  method: "POST",
24025
24411
  path: "/organizations/:organizationId/vanta-integration",
@@ -24244,8 +24630,8 @@ var UpdateVulnerabilityRequestSchema = external_exports.object({
24244
24630
  });
24245
24631
 
24246
24632
  // ../../packages/api-contracts/src/contracts/v1/vulnerability.ts
24247
- var c15 = initContract();
24248
- var vulnerabilityContract = c15.router({
24633
+ var c16 = initContract();
24634
+ var vulnerabilityContract = c16.router({
24249
24635
  filterVulnerabilities: {
24250
24636
  method: "POST",
24251
24637
  path: "/organizations/:organizationId/vulnerabilities/filter",
@@ -24383,15 +24769,15 @@ var VulnerabilityNoteResponseSchema = external_exports.object({
24383
24769
  });
24384
24770
  var ListVulnerabilityNotesResponseSchema = external_exports.array(VulnerabilityNoteResponseSchema);
24385
24771
  var CreateVulnerabilityNoteRequestSchema = external_exports.object({
24386
- content: external_exports.string().min(1).max(1e4)
24772
+ content: external_exports.string().max(1e4)
24387
24773
  });
24388
24774
  var UpdateVulnerabilityNoteRequestSchema = external_exports.object({
24389
- content: external_exports.string().min(1).max(1e4)
24775
+ content: external_exports.string().max(1e4)
24390
24776
  });
24391
24777
 
24392
24778
  // ../../packages/api-contracts/src/contracts/v1/vulnerability-note.ts
24393
- var c16 = initContract();
24394
- var vulnerabilityNoteContract = c16.router({
24779
+ var c17 = initContract();
24780
+ var vulnerabilityNoteContract = c17.router({
24395
24781
  listNotes: {
24396
24782
  method: "GET",
24397
24783
  path: "/organizations/:organizationId/vulnerabilities/:vulnerabilityId/notes",
@@ -24463,8 +24849,8 @@ var WebApplicationResponseSchema = external_exports.object({
24463
24849
  var ListWebApplicationsResponseSchema = external_exports.array(WebApplicationResponseSchema);
24464
24850
 
24465
24851
  // ../../packages/api-contracts/src/contracts/v1/web-application.ts
24466
- var c17 = initContract();
24467
- var webApplicationContract = c17.router({
24852
+ var c18 = initContract();
24853
+ var webApplicationContract = c18.router({
24468
24854
  listWebApplications: {
24469
24855
  method: "GET",
24470
24856
  path: "/organizations/:organizationId/web-applications",
@@ -24603,8 +24989,8 @@ var ListWebhookDeliveriesResponseSchema = external_exports.object({
24603
24989
  });
24604
24990
 
24605
24991
  // ../../packages/api-contracts/src/contracts/v1/webhook-settings.ts
24606
- var c18 = initContract();
24607
- var webhookSettingsContract = c18.router({
24992
+ var c19 = initContract();
24993
+ var webhookSettingsContract = c19.router({
24608
24994
  listWebhookEndpoints: {
24609
24995
  method: "GET",
24610
24996
  path: "/organizations/:organizationId/webhook-settings/endpoints",
@@ -24742,8 +25128,8 @@ var webhookSettingsContract = c18.router({
24742
25128
  });
24743
25129
 
24744
25130
  // ../../packages/api-contracts/src/contracts/v1/index.ts
24745
- var c19 = initContract();
24746
- var v1Contract = c19.router(
25131
+ var c20 = initContract();
25132
+ var v1Contract = c20.router(
24747
25133
  {
24748
25134
  admin: adminContract,
24749
25135
  scan: scanContract,
@@ -24762,7 +25148,8 @@ var v1Contract = c19.router(
24762
25148
  webApplication: webApplicationContract,
24763
25149
  chatSession: chatSessionContract,
24764
25150
  pentest: pentestContract,
24765
- pentestFinding: pentestFindingContract
25151
+ pentestFinding: pentestFindingContract,
25152
+ dependency: dependencyContract
24766
25153
  },
24767
25154
  {
24768
25155
  pathPrefix: "/v1"
@@ -24770,8 +25157,8 @@ var v1Contract = c19.router(
24770
25157
  );
24771
25158
 
24772
25159
  // ../../packages/api-contracts/src/contracts/index.ts
24773
- var c20 = initContract();
24774
- var contract = c20.router({
25160
+ var c21 = initContract();
25161
+ var contract = c21.router({
24775
25162
  v1: v1Contract
24776
25163
  });
24777
25164
 
@@ -24781,8 +25168,8 @@ var GitHubWebhookResponseSchema = external_exports.object({
24781
25168
  });
24782
25169
 
24783
25170
  // ../../packages/api-contracts/src/contracts/webhooks/github.ts
24784
- var c21 = initContract();
24785
- var githubWebhookContract = c21.router(
25171
+ var c22 = initContract();
25172
+ var githubWebhookContract = c22.router(
24786
25173
  {
24787
25174
  handleWebhook: {
24788
25175
  method: "POST",
@@ -24807,8 +25194,8 @@ var HealthResponseSchema = external_exports.object({
24807
25194
  });
24808
25195
 
24809
25196
  // ../../packages/api-contracts/src/contracts/health.ts
24810
- var c22 = initContract();
24811
- var healthContract = c22.router({
25197
+ var c23 = initContract();
25198
+ var healthContract = c23.router({
24812
25199
  getHealth: {
24813
25200
  method: "GET",
24814
25201
  path: "/health",
@@ -24857,8 +25244,8 @@ var UpdatePrVulnerabilityRequestSchema = external_exports.object({
24857
25244
  });
24858
25245
 
24859
25246
  // ../../packages/api-contracts/src/contracts/v1/pr-vulnerability.ts
24860
- var c23 = initContract();
24861
- var prVulnerabilityContract = c23.router(
25247
+ var c24 = initContract();
25248
+ var prVulnerabilityContract = c24.router(
24862
25249
  {
24863
25250
  listPrVulnerabilities: {
24864
25251
  method: "GET",
@@ -25057,6 +25444,11 @@ function formatStatus(status) {
25057
25444
  }
25058
25445
 
25059
25446
  // src/pentest/commands.ts
25447
+ var DEFAULT_LIMIT = 30;
25448
+ var MAX_LIMIT = 100;
25449
+ function tsvSafe(value) {
25450
+ return value.replace(/[\t\n\r]/g, " ");
25451
+ }
25060
25452
  function formatDate(date5) {
25061
25453
  if (!date5) {
25062
25454
  return "\u2014";
@@ -25098,6 +25490,10 @@ async function resolvePentestId(organizationId, identifier) {
25098
25490
  params: { organizationId }
25099
25491
  })
25100
25492
  );
25493
+ if (result.status === 401) {
25494
+ console.error("Authentication failed. Run `tracecli auth login` to re-authenticate.");
25495
+ process.exit(1);
25496
+ }
25101
25497
  if (result.status !== 200) {
25102
25498
  console.error(`Failed to list pentests (status ${result.status}).`);
25103
25499
  process.exit(1);
@@ -25113,30 +25509,72 @@ Run \`tracecli pentest list\` to see available pentests.`
25113
25509
  return pentest.pentestId;
25114
25510
  }
25115
25511
  function createListCommand() {
25116
- return new Command("list").description("List pentests for your organization").option("--json", "Output as JSON").action(async (options) => {
25512
+ return new Command("list").description("List pentests for your organization").option(
25513
+ "-L, --limit <n>",
25514
+ `Max results to return (default: ${DEFAULT_LIMIT}, max: ${MAX_LIMIT})`,
25515
+ (v) => {
25516
+ const n = Number.parseInt(v, 10);
25517
+ if (Number.isNaN(n) || n < 1) {
25518
+ throw new Error("--limit must be a positive integer");
25519
+ }
25520
+ return Math.min(n, MAX_LIMIT);
25521
+ }
25522
+ ).option("-o, --output <format>", "Output format: table, json, tsv (default: table)", "table").addHelpText(
25523
+ "after",
25524
+ `
25525
+ Examples:
25526
+ tracecli pentest list
25527
+ tracecli pentest list --limit 10
25528
+ tracecli pentest list --output json | jq '.[].title'
25529
+ tracecli pentest list --output tsv | cut -f1,2`
25530
+ ).action(async (options) => {
25117
25531
  const organizationId = await resolveOrganizationId();
25118
25532
  const result = await authenticatedRequest(
25119
25533
  (client) => client.pentest.listPentests({
25120
25534
  params: { organizationId }
25121
25535
  })
25122
25536
  );
25537
+ if (result.status === 401) {
25538
+ console.error("Authentication failed. Run `tracecli auth login` to re-authenticate.");
25539
+ process.exit(1);
25540
+ }
25123
25541
  if (result.status !== 200) {
25124
25542
  console.error(`Failed to list pentests (status ${result.status}).`);
25125
25543
  process.exit(1);
25126
25544
  }
25127
- const pentests = result.body;
25545
+ const limit = options.limit ?? DEFAULT_LIMIT;
25546
+ const pentests = result.body.slice(0, limit);
25547
+ const isJson = options.output === "json";
25548
+ const isTsv = options.output === "tsv";
25128
25549
  if (pentests.length === 0) {
25129
- if (options.json) {
25550
+ if (isJson) {
25130
25551
  console.log("[]");
25552
+ } else if (isTsv) {
25553
+ console.log("displayId title status startedAt endedAt findingsTotal");
25131
25554
  } else {
25132
25555
  console.log("No pentests found.");
25133
25556
  }
25134
25557
  return;
25135
25558
  }
25136
- if (options.json) {
25559
+ if (isJson) {
25137
25560
  console.log(JSON.stringify(pentests, null, 2));
25138
25561
  return;
25139
25562
  }
25563
+ if (isTsv) {
25564
+ console.log("displayId title status startedAt endedAt findingsTotal");
25565
+ for (const pt of pentests) {
25566
+ const cols = [
25567
+ `PT-${pt.displayId}`,
25568
+ tsvSafe(pt.title),
25569
+ pt.status,
25570
+ pt.startedAt ? String(pt.startedAt) : "",
25571
+ pt.endedAt ? String(pt.endedAt) : "",
25572
+ String(pt.findingCounts.total)
25573
+ ];
25574
+ console.log(cols.join(" "));
25575
+ }
25576
+ return;
25577
+ }
25140
25578
  console.log(`Found ${pentests.length} pentest${pentests.length === 1 ? "" : "s"}:
25141
25579
  `);
25142
25580
  for (const pt of pentests) {
@@ -25155,7 +25593,14 @@ function createListCommand() {
25155
25593
  });
25156
25594
  }
25157
25595
  function createFindingsListCommand() {
25158
- return new Command("list").description("List findings for a pentest").requiredOption("--pentest <id>", "Pentest display ID (e.g. PT-3) or UUID").option("--json", "Output as JSON").action(async (options) => {
25596
+ return new Command("list").description("List findings for a pentest").requiredOption("--pentest <id>", "Pentest display ID (e.g. PT-3) or UUID").option("-o, --output <format>", "Output format: table, json, tsv (default: table)", "table").addHelpText(
25597
+ "after",
25598
+ `
25599
+ Examples:
25600
+ tracecli pentest findings list --pentest PT-3
25601
+ tracecli pentest findings list --pentest PT-3 --output json
25602
+ tracecli pentest findings list --pentest PT-3 --output tsv | cut -f1,4`
25603
+ ).action(async (options) => {
25159
25604
  const organizationId = await resolveOrganizationId();
25160
25605
  const pentestId = await resolvePentestId(organizationId, options.pentest);
25161
25606
  const result = await authenticatedRequest(
@@ -25163,28 +25608,57 @@ function createFindingsListCommand() {
25163
25608
  params: { organizationId, pentestId }
25164
25609
  })
25165
25610
  );
25611
+ if (result.status === 401) {
25612
+ console.error("Authentication failed. Run `tracecli auth login` to re-authenticate.");
25613
+ process.exit(1);
25614
+ }
25615
+ if (result.status === 404) {
25616
+ console.error(
25617
+ `Pentest '${options.pentest}' not found. Verify the ID with \`tracecli pentest list\`.`
25618
+ );
25619
+ process.exit(1);
25620
+ }
25166
25621
  if (result.status !== 200) {
25167
25622
  console.error(`Failed to list findings (status ${result.status}).`);
25168
25623
  process.exit(1);
25169
25624
  }
25170
25625
  const findings = result.body;
25626
+ const isJson = options.output === "json";
25627
+ const isTsv = options.output === "tsv";
25171
25628
  if (findings.length === 0) {
25172
- if (options.json) {
25629
+ if (isJson) {
25173
25630
  console.log("[]");
25631
+ } else if (isTsv) {
25632
+ console.log("severity status category title cweIds cvssScore");
25174
25633
  } else {
25175
25634
  console.log("No findings found for this pentest.");
25176
25635
  }
25177
25636
  return;
25178
25637
  }
25179
- if (options.json) {
25638
+ if (isJson) {
25180
25639
  console.log(JSON.stringify(findings, null, 2));
25181
25640
  return;
25182
25641
  }
25183
- console.log(`Found ${findings.length} finding${findings.length === 1 ? "" : "s"}:
25184
- `);
25185
25642
  const sorted = [...findings].sort(
25186
25643
  (a, b) => (SEVERITY_ORDER[a.severity] ?? 99) - (SEVERITY_ORDER[b.severity] ?? 99)
25187
25644
  );
25645
+ if (isTsv) {
25646
+ console.log("severity status category title cweIds cvssScore");
25647
+ for (const finding of sorted) {
25648
+ const cols = [
25649
+ finding.severity,
25650
+ finding.status,
25651
+ tsvSafe(finding.category),
25652
+ tsvSafe(finding.title),
25653
+ finding.cweIds.join(";"),
25654
+ finding.cvssScore != null ? String(finding.cvssScore) : ""
25655
+ ];
25656
+ console.log(cols.join(" "));
25657
+ }
25658
+ return;
25659
+ }
25660
+ console.log(`Found ${findings.length} finding${findings.length === 1 ? "" : "s"}:
25661
+ `);
25188
25662
  for (const finding of sorted) {
25189
25663
  const sev = formatFindingSeverity(finding.severity);
25190
25664
  const status = formatStatus(finding.status);
@@ -25262,6 +25736,9 @@ function extractRepoName(input) {
25262
25736
  return input;
25263
25737
  }
25264
25738
  var BRANCH_NAME_PATTERN = /^[a-zA-Z0-9._/+-]+$/;
25739
+ var TRACE_GIT_ASKPASS_TOKEN_ENV = "TRACE_GIT_ASKPASS_TOKEN";
25740
+ var REPOSITORY_PLATFORMS = ["GITHUB", "GITLAB", "BITBUCKET"];
25741
+ var REPOSITORY_STATUSES = ["CONNECTED", "DISCONNECTED"];
25265
25742
  function validateBranchName(branch) {
25266
25743
  if (branch.startsWith("-")) {
25267
25744
  throw new Error(`Invalid branch name: '${branch}' \u2014 branch names cannot start with a dash`);
@@ -25271,29 +25748,268 @@ function validateBranchName(branch) {
25271
25748
  }
25272
25749
  return branch;
25273
25750
  }
25274
- function createListCommand2() {
25275
- return new Command("list").description("List repositories in your organization").action(async () => {
25276
- const organizationId = await resolveOrganizationId();
25277
- const result = await authenticatedRequest(
25278
- (client) => client.repository.listRepositories({
25279
- params: { organizationId }
25280
- })
25751
+ function parseCloneDepth(value) {
25752
+ const depth = Number.parseInt(value, 10);
25753
+ if (!Number.isInteger(depth) || depth < 1) {
25754
+ throw new Error("--depth must be a positive integer");
25755
+ }
25756
+ return depth;
25757
+ }
25758
+ function normalizeForMatching(value) {
25759
+ return value.trim().toLowerCase();
25760
+ }
25761
+ function normalizePlatform(value, source) {
25762
+ const normalized = value.trim().toUpperCase();
25763
+ if (REPOSITORY_PLATFORMS.includes(normalized)) {
25764
+ return normalized;
25765
+ }
25766
+ throw new Error(
25767
+ `${source} must be one of: ${REPOSITORY_PLATFORMS.join(", ")}. Received '${value}'.`
25768
+ );
25769
+ }
25770
+ function normalizeStatus(value, source) {
25771
+ const normalized = value.trim().toUpperCase();
25772
+ if (REPOSITORY_STATUSES.includes(normalized)) {
25773
+ return normalized;
25774
+ }
25775
+ throw new Error(
25776
+ `${source} must be one of: ${REPOSITORY_STATUSES.join(", ")}. Received '${value}'.`
25777
+ );
25778
+ }
25779
+ function normalizeFilterField(rawField) {
25780
+ const field = rawField.trim().toLowerCase();
25781
+ switch (field) {
25782
+ case "platform":
25783
+ return "platform";
25784
+ case "status":
25785
+ return "status";
25786
+ case "url":
25787
+ case "repository":
25788
+ case "repositoryurl":
25789
+ case "repository_url":
25790
+ return "repository_url";
25791
+ case "repo":
25792
+ case "name":
25793
+ case "repository_name":
25794
+ return "repo_name";
25795
+ case "branch":
25796
+ case "defaultbranch":
25797
+ case "default_branch":
25798
+ return "default_branch";
25799
+ case "language":
25800
+ case "primarylanguage":
25801
+ case "primary_language":
25802
+ return "language";
25803
+ default:
25804
+ throw new Error(
25805
+ `Unknown repository filter field '${rawField}'. Supported fields: platform, status, url, repo, branch, language.`
25806
+ );
25807
+ }
25808
+ }
25809
+ function parseFilterOperator(rawOperator) {
25810
+ switch (rawOperator) {
25811
+ case "=":
25812
+ return "exact";
25813
+ case "!=":
25814
+ return "not_exact";
25815
+ case ":":
25816
+ return "contains";
25817
+ case "!:":
25818
+ return "not_contains";
25819
+ default:
25820
+ throw new Error(`Unsupported filter operator '${rawOperator}'. Use one of: =, !=, :, !:.`);
25821
+ }
25822
+ }
25823
+ function parseRepositoryFilterExpression(expression) {
25824
+ const trimmed = expression.trim();
25825
+ const match = /^([a-zA-Z][a-zA-Z0-9_-]*)(!=|!:|=|:)(.+)$/.exec(trimmed);
25826
+ if (!match) {
25827
+ throw new Error(
25828
+ `Invalid filter '${expression}'. Expected format: <field><operator><value> (e.g., platform=GITHUB, repo:trace, status!=DISCONNECTED).`
25281
25829
  );
25282
- if (result.status !== 200) {
25283
- console.error(`Failed to list repositories (status ${result.status}).`);
25284
- process.exit(1);
25830
+ }
25831
+ const rawField = match[1];
25832
+ const rawOperator = match[2];
25833
+ const rawValue = match[3];
25834
+ if (!rawField || !rawOperator || !rawValue) {
25835
+ throw new Error(`Invalid filter '${expression}'.`);
25836
+ }
25837
+ const value = rawValue.trim();
25838
+ if (!value) {
25839
+ throw new Error(`Invalid filter '${expression}': value cannot be empty.`);
25840
+ }
25841
+ const field = normalizeFilterField(rawField);
25842
+ const operator = parseFilterOperator(rawOperator);
25843
+ if (field === "platform") {
25844
+ normalizePlatform(value, "--filter");
25845
+ }
25846
+ if (field === "status") {
25847
+ normalizeStatus(value, "--filter");
25848
+ }
25849
+ return { field, operator, value };
25850
+ }
25851
+ function doesValueMatchFilter(candidate, filterValue, operator, exactMatchOnly) {
25852
+ const normalizedCandidate = normalizeForMatching(candidate);
25853
+ const normalizedFilter = normalizeForMatching(filterValue);
25854
+ if (exactMatchOnly || operator === "exact" || operator === "not_exact") {
25855
+ const matches2 = normalizedCandidate === normalizedFilter;
25856
+ return operator === "not_exact" ? !matches2 : matches2;
25857
+ }
25858
+ const matches = normalizedCandidate.includes(normalizedFilter);
25859
+ return operator === "not_contains" ? !matches : matches;
25860
+ }
25861
+ function matchParsedFilter(repo, parsedFilter) {
25862
+ switch (parsedFilter.field) {
25863
+ case "platform":
25864
+ return doesValueMatchFilter(repo.platform, parsedFilter.value, parsedFilter.operator, true);
25865
+ case "status":
25866
+ return doesValueMatchFilter(repo.status, parsedFilter.value, parsedFilter.operator, true);
25867
+ case "repository_url":
25868
+ return doesValueMatchFilter(
25869
+ repo.repositoryUrl,
25870
+ parsedFilter.value,
25871
+ parsedFilter.operator,
25872
+ false
25873
+ );
25874
+ case "repo_name":
25875
+ return doesValueMatchFilter(
25876
+ extractRepoName(repo.repositoryUrl),
25877
+ parsedFilter.value,
25878
+ parsedFilter.operator,
25879
+ false
25880
+ );
25881
+ case "default_branch":
25882
+ return doesValueMatchFilter(
25883
+ repo.defaultBranch,
25884
+ parsedFilter.value,
25885
+ parsedFilter.operator,
25886
+ false
25887
+ );
25888
+ case "language": {
25889
+ const language = repo.primaryLanguage ?? "";
25890
+ return doesValueMatchFilter(language, parsedFilter.value, parsedFilter.operator, false);
25285
25891
  }
25286
- const repos = result.body;
25287
- if (repos.length === 0) {
25288
- console.log("No repositories found.");
25289
- return;
25892
+ }
25893
+ }
25894
+ function filterRepositories(repositories, options) {
25895
+ const parsedFilters = (options.filters ?? []).map(
25896
+ (expression) => parseRepositoryFilterExpression(expression)
25897
+ );
25898
+ const normalizedPlatform = options.platform ? normalizePlatform(options.platform, "--platform") : void 0;
25899
+ const normalizedStatus = options.status ? normalizeStatus(options.status, "--status") : void 0;
25900
+ const normalizedQuery = options.query ? normalizeForMatching(options.query) : void 0;
25901
+ const normalizedDefaultBranch = options.defaultBranch ? normalizeForMatching(options.defaultBranch) : void 0;
25902
+ const normalizedLanguage = options.language ? normalizeForMatching(options.language) : void 0;
25903
+ return repositories.filter((repo) => {
25904
+ if (normalizedPlatform && repo.platform.toUpperCase() !== normalizedPlatform) {
25905
+ return false;
25906
+ }
25907
+ if (normalizedStatus && repo.status.toUpperCase() !== normalizedStatus) {
25908
+ return false;
25909
+ }
25910
+ if (normalizedDefaultBranch && !normalizeForMatching(repo.defaultBranch).includes(normalizedDefaultBranch)) {
25911
+ return false;
25290
25912
  }
25291
- for (const repo of repos) {
25292
- const status = repo.status === "CONNECTED" ? "connected" : "disconnected";
25293
- const url2 = stripAnsi(String(repo.repositoryUrl));
25294
- const platform = stripAnsi(String(repo.platform)).toLowerCase();
25295
- console.log(`${url2} (${platform}, ${status})`);
25913
+ const repoLanguage = normalizeForMatching(repo.primaryLanguage ?? "");
25914
+ if (normalizedLanguage && !repoLanguage.includes(normalizedLanguage)) {
25915
+ return false;
25916
+ }
25917
+ if (normalizedQuery) {
25918
+ const normalizedUrl = normalizeForMatching(repo.repositoryUrl);
25919
+ const normalizedBranch = normalizeForMatching(repo.defaultBranch);
25920
+ const normalizedName = normalizeForMatching(extractRepoName(repo.repositoryUrl));
25921
+ if (!normalizedUrl.includes(normalizedQuery) && !normalizedBranch.includes(normalizedQuery) && !repoLanguage.includes(normalizedQuery) && !normalizedName.includes(normalizedQuery)) {
25922
+ return false;
25923
+ }
25296
25924
  }
25925
+ for (const parsedFilter of parsedFilters) {
25926
+ if (!matchParsedFilter(repo, parsedFilter)) {
25927
+ return false;
25928
+ }
25929
+ }
25930
+ return true;
25931
+ });
25932
+ }
25933
+ function collectFilterExpression(value, previous) {
25934
+ previous.push(value);
25935
+ return previous;
25936
+ }
25937
+ function toRepositoryListFilterOptions(options) {
25938
+ return {
25939
+ ...options.platform ? { platform: options.platform } : {},
25940
+ ...options.status ? { status: options.status } : {},
25941
+ ...options.query ? { query: options.query } : {},
25942
+ ...options.defaultBranch ? { defaultBranch: options.defaultBranch } : {},
25943
+ ...options.language ? { language: options.language } : {},
25944
+ ...options.filter.length > 0 ? { filters: options.filter } : {}
25945
+ };
25946
+ }
25947
+ function addRepositoryFilterOptions(command) {
25948
+ return command.option(
25949
+ "--platform <platform>",
25950
+ "Filter by platform (GITHUB, GITLAB, BITBUCKET)",
25951
+ (value) => value.toUpperCase()
25952
+ ).option(
25953
+ "--status <status>",
25954
+ "Filter by status (CONNECTED, DISCONNECTED)",
25955
+ (value) => value.toUpperCase()
25956
+ ).option("--query <query>", "Case-insensitive search across URL, repo, branch, and language").option("--default-branch <branch>", "Filter by default branch (case-insensitive contains)").option("--language <language>", "Filter by primary language (case-insensitive contains)").option(
25957
+ "--filter <expression>",
25958
+ "Advanced filter expression. Repeatable. Examples: platform=GITHUB, repo:trace, status!=DISCONNECTED, language!:typescript",
25959
+ collectFilterExpression,
25960
+ []
25961
+ ).option("--json", "Output as JSON");
25962
+ }
25963
+ async function runRepositoryListCommand(options) {
25964
+ let filteredRepositories;
25965
+ const organizationId = await resolveOrganizationId();
25966
+ const result = await authenticatedRequest(
25967
+ (client) => client.repository.listRepositories({
25968
+ params: { organizationId }
25969
+ })
25970
+ );
25971
+ if (result.status !== 200) {
25972
+ console.error(`Failed to list repositories (status ${result.status}).`);
25973
+ process.exit(1);
25974
+ }
25975
+ try {
25976
+ filteredRepositories = filterRepositories(result.body, toRepositoryListFilterOptions(options));
25977
+ } catch (error48) {
25978
+ const message = error48 instanceof Error ? error48.message : "Unknown filter parsing error.";
25979
+ console.error(`Invalid repository filter: ${message}`);
25980
+ process.exit(1);
25981
+ }
25982
+ if (filteredRepositories.length === 0) {
25983
+ if (options.platform || options.status || options.query || options.defaultBranch || options.language || options.filter.length > 0) {
25984
+ console.log("No repositories found matching the provided filters.");
25985
+ return;
25986
+ }
25987
+ console.log("No repositories found.");
25988
+ return;
25989
+ }
25990
+ if (options.json) {
25991
+ console.log(JSON.stringify(filteredRepositories, null, 2));
25992
+ return;
25993
+ }
25994
+ for (const repo of filteredRepositories) {
25995
+ const status = repo.status === "CONNECTED" ? "connected" : "disconnected";
25996
+ const url2 = stripAnsi(String(repo.repositoryUrl));
25997
+ const platform = stripAnsi(String(repo.platform)).toLowerCase();
25998
+ console.log(`${url2} (${platform}, ${status})`);
25999
+ }
26000
+ }
26001
+ function createListCommand2() {
26002
+ return addRepositoryFilterOptions(
26003
+ new Command("list").description("List repositories in your organization")
26004
+ ).action(async (options) => {
26005
+ await runRepositoryListCommand(options);
26006
+ });
26007
+ }
26008
+ function createFilterCommand() {
26009
+ return addRepositoryFilterOptions(
26010
+ new Command("filter").description("Filter repositories in your organization")
26011
+ ).action(async (options) => {
26012
+ await runRepositoryListCommand(options);
25297
26013
  });
25298
26014
  }
25299
26015
  function urlWithUsername(httpsUrl) {
@@ -25302,14 +26018,21 @@ function urlWithUsername(httpsUrl) {
25302
26018
  url2.password = "";
25303
26019
  return url2.toString();
25304
26020
  }
25305
- function createAskPassScript(token) {
26021
+ function createAskPassScript() {
25306
26022
  const dir = mkdtempSync(join3(tmpdir(), "trace-askpass-"));
25307
26023
  const scriptPath = join3(dir, "askpass.sh");
25308
- writeFileSync3(scriptPath, `#!/bin/sh
25309
- echo '${token.replace(/'/g, "'\\''")}'
25310
- `, {
25311
- mode: 448
25312
- });
26024
+ writeFileSync3(
26025
+ scriptPath,
26026
+ `#!/bin/sh
26027
+ if [ -z "\${${TRACE_GIT_ASKPASS_TOKEN_ENV}:-}" ]; then
26028
+ exit 1
26029
+ fi
26030
+ printf '%s\\n' "$${TRACE_GIT_ASKPASS_TOKEN_ENV}"
26031
+ `,
26032
+ {
26033
+ mode: 448
26034
+ }
26035
+ );
25313
26036
  chmodSync(scriptPath, 448);
25314
26037
  return scriptPath;
25315
26038
  }
@@ -25329,7 +26052,8 @@ async function fetchCloneToken(repoUrl) {
25329
26052
  return null;
25330
26053
  }
25331
26054
  if (result.status === 200) {
25332
- return result.body.token;
26055
+ const token = result.body.token.trim();
26056
+ return token.length > 0 ? token : null;
25333
26057
  }
25334
26058
  return null;
25335
26059
  } catch {
@@ -25337,7 +26061,7 @@ async function fetchCloneToken(repoUrl) {
25337
26061
  }
25338
26062
  }
25339
26063
  function createCloneCommand() {
25340
- return new Command("clone").description("Clone a repository").argument("<repository>", "Repository URL or owner/repo shorthand").argument("[directory]", "Directory to clone into").option("--branch <branch>", "Branch to clone").option("--depth <depth>", "Shallow clone depth", Number.parseInt).action(
26064
+ return new Command("clone").description("Clone a repository").argument("<repository>", "Repository URL or owner/repo shorthand").argument("[directory]", "Directory to clone into").option("--branch <branch>", "Branch to clone").option("--depth <depth>", "Shallow clone depth", parseCloneDepth).action(
25341
26065
  async (repository, directory, options) => {
25342
26066
  const cloneUrl = resolveRepoUrl(repository);
25343
26067
  const targetDir = directory ?? extractRepoName(repository);
@@ -25359,8 +26083,9 @@ function createCloneCommand() {
25359
26083
  console.log(`Cloning ${cloneUrl} into ${targetDir}...`);
25360
26084
  const extraEnv = {};
25361
26085
  if (installationToken) {
25362
- askPassScript = createAskPassScript(installationToken);
26086
+ askPassScript = createAskPassScript();
25363
26087
  extraEnv["GIT_ASKPASS"] = askPassScript;
26088
+ extraEnv[TRACE_GIT_ASKPASS_TOKEN_ENV] = installationToken;
25364
26089
  extraEnv["GIT_TERMINAL_PROMPT"] = "0";
25365
26090
  }
25366
26091
  try {
@@ -25371,7 +26096,7 @@ function createCloneCommand() {
25371
26096
  });
25372
26097
  const exitCode = await proc.exited;
25373
26098
  if (exitCode !== 0) {
25374
- process.exit(exitCode);
26099
+ process.exit(1);
25375
26100
  }
25376
26101
  if (installationToken) {
25377
26102
  const resetProc = Bun.spawn(["git", "remote", "set-url", "origin", cloneUrl], {
@@ -25398,13 +26123,14 @@ function createCloneCommand() {
25398
26123
  function createRepoCommand() {
25399
26124
  const repo = new Command("repo").description("Manage repositories");
25400
26125
  repo.addCommand(createListCommand2());
26126
+ repo.addCommand(createFilterCommand());
25401
26127
  repo.addCommand(createCloneCommand());
25402
26128
  return repo;
25403
26129
  }
25404
26130
 
25405
26131
  // src/vulnerabilities/commands.ts
25406
- import { createInterface } from "readline";
25407
- var DEFAULT_PAGE_SIZE = 25;
26132
+ var DEFAULT_LIMIT2 = 30;
26133
+ var MAX_LIMIT2 = 100;
25408
26134
  async function resolveRepositoryIdAuthenticated(organizationId, repoIdentifier) {
25409
26135
  const result = await authenticatedRequest(
25410
26136
  (client) => client.repository.listRepositories({
@@ -25416,21 +26142,18 @@ async function resolveRepositoryIdAuthenticated(organizationId, repoIdentifier)
25416
26142
  process.exit(1);
25417
26143
  }
25418
26144
  const repos = result.body;
25419
- const match = repos.find((r) => {
25420
- const url2 = r.repositoryUrl;
25421
- if (url2.endsWith(`/${repoIdentifier}`)) {
25422
- return true;
25423
- }
25424
- if (url2.endsWith(`/${repoIdentifier}.git`)) {
25425
- return true;
25426
- }
25427
- if (url2 === repoIdentifier) {
25428
- return true;
26145
+ const isBareNameOnly = !repoIdentifier.includes("/") && !repoIdentifier.startsWith("http");
26146
+ if (!isBareNameOnly) {
26147
+ const match = repos.find((r) => {
26148
+ const url2 = r.repositoryUrl;
26149
+ if (url2.endsWith(`/${repoIdentifier}`)) return true;
26150
+ if (url2.endsWith(`/${repoIdentifier}.git`)) return true;
26151
+ if (url2 === repoIdentifier) return true;
26152
+ return false;
26153
+ });
26154
+ if (match) {
26155
+ return match.repositoryId;
25429
26156
  }
25430
- return false;
25431
- });
25432
- if (match) {
25433
- return match.repositoryId;
25434
26157
  }
25435
26158
  const byName = repos.filter((r) => {
25436
26159
  const segments = r.repositoryUrl.replace(/\.git$/, "").split("/");
@@ -25484,15 +26207,26 @@ function printVulnerabilities(vulnerabilities) {
25484
26207
  console.log("");
25485
26208
  }
25486
26209
  }
25487
- async function promptContinue(displayed, total) {
25488
- const rl = createInterface({ input: process.stdin, output: process.stdout });
25489
- return new Promise((resolve) => {
25490
- rl.question(`Showing ${displayed} of ${total}. Show more? [Y/n] `, (answer) => {
25491
- rl.close();
25492
- const trimmed = answer.trim().toLowerCase();
25493
- resolve(trimmed === "" || trimmed === "y" || trimmed === "yes");
25494
- });
25495
- });
26210
+ function sanitizeTsvField(value) {
26211
+ return value.replace(/[\t\n\r]/g, " ");
26212
+ }
26213
+ function printVulnerabilitiesTsv(vulnerabilities) {
26214
+ const sorted = [...vulnerabilities].sort(
26215
+ (a, b) => (SEVERITY_ORDER[a.severity] ?? 99) - (SEVERITY_ORDER[b.severity] ?? 99)
26216
+ );
26217
+ console.log("severity status repository file line traceId title");
26218
+ for (const vuln of sorted) {
26219
+ const cols = [
26220
+ vuln.severity,
26221
+ vuln.status,
26222
+ vuln.repositoryName,
26223
+ vuln.codeSnippet.filePath,
26224
+ String(vuln.codeSnippet.startLine),
26225
+ vuln.traceId,
26226
+ vuln.title
26227
+ ].map(sanitizeTsvField);
26228
+ console.log(cols.join(" "));
26229
+ }
25496
26230
  }
25497
26231
  function createListCommand3() {
25498
26232
  return new Command("list").description("List vulnerabilities for your organization").option("--repo <repo>", "Filter by repository name or owner/repo").option(
@@ -25503,13 +26237,26 @@ function createListCommand3() {
25503
26237
  "--status <status>",
25504
26238
  "Filter by status (OPEN, IN_PROGRESS, FIXED, IGNORED, FALSE_POSITIVE)",
25505
26239
  (value) => value.toUpperCase()
25506
- ).option("--page-size <size>", "Number of results per page", (v) => {
25507
- const n = Number.parseInt(v, 10);
25508
- if (Number.isNaN(n) || n < 1) {
25509
- throw new Error("--page-size must be a positive integer");
25510
- }
25511
- return Math.min(n, 200);
25512
- }).option("--json", "Output as JSON").action(
26240
+ ).option(
26241
+ "-L, --limit <n>",
26242
+ `Max results to return (default: ${DEFAULT_LIMIT2}, max: ${MAX_LIMIT2})`,
26243
+ (v) => {
26244
+ const n = Number.parseInt(v, 10);
26245
+ if (Number.isNaN(n) || n < 1) {
26246
+ throw new Error("--limit must be a positive integer");
26247
+ }
26248
+ return Math.min(n, MAX_LIMIT2);
26249
+ }
26250
+ ).option("-o, --output <format>", "Output format: table, json, tsv (default: table)", "table").addHelpText(
26251
+ "after",
26252
+ `
26253
+ Examples:
26254
+ tracecli vulnerabilities list
26255
+ tracecli vulnerabilities list --severity CRITICAL --status OPEN
26256
+ tracecli vulnerabilities list --repo securewithtrace/trace --limit 50
26257
+ tracecli vulnerabilities list --output json | jq '.[].title'
26258
+ tracecli vulnerabilities list --output tsv | cut -f1,7`
26259
+ ).action(
25513
26260
  async (options) => {
25514
26261
  const organizationId = await resolveOrganizationId();
25515
26262
  const conditions = [];
@@ -25529,57 +26276,54 @@ function createListCommand3() {
25529
26276
  sortBy: "severity",
25530
26277
  sortOrder: "desc"
25531
26278
  };
25532
- const pageSize = options.pageSize ?? DEFAULT_PAGE_SIZE;
26279
+ const limit = options.limit ?? DEFAULT_LIMIT2;
26280
+ const isJson = options.output === "json";
26281
+ const isTsv = options.output === "tsv";
26282
+ const collected = [];
25533
26283
  let page = 1;
25534
- let totalDisplayed = 0;
25535
- let total = 0;
25536
- const allJsonVulnerabilities = [];
25537
- while (true) {
26284
+ const pageSize = Math.min(limit, 100);
26285
+ while (collected.length < limit) {
25538
26286
  const result = await authenticatedRequest(
25539
26287
  (client) => client.vulnerability.filterVulnerabilities({
25540
26288
  params: { organizationId },
25541
26289
  body: { ...filterBody, page, pageSize }
25542
26290
  })
25543
26291
  );
26292
+ if (result.status === 401) {
26293
+ console.error("Authentication failed. Run `tracecli auth login` to re-authenticate.");
26294
+ process.exit(1);
26295
+ }
25544
26296
  if (result.status !== 200) {
25545
26297
  console.error(`Failed to list vulnerabilities (status ${result.status}).`);
25546
26298
  process.exit(1);
25547
26299
  }
25548
- total = result.body.total;
25549
26300
  const vulnerabilities = result.body.statusGroups.flatMap((g) => g.vulnerabilities);
25550
- if (page === 1 && vulnerabilities.length === 0) {
25551
- if (options.json) {
25552
- console.log("[]");
25553
- } else {
25554
- console.log("No vulnerabilities found.");
25555
- }
25556
- return;
25557
- }
25558
- if (options.json) {
25559
- allJsonVulnerabilities.push(...vulnerabilities);
25560
- } else {
25561
- if (page === 1) {
25562
- console.log(`Found ${total} vulnerabilit${total === 1 ? "y" : "ies"}:
25563
- `);
25564
- }
25565
- printVulnerabilities(vulnerabilities);
25566
- }
25567
- totalDisplayed += vulnerabilities.length;
25568
- if (totalDisplayed >= total || vulnerabilities.length < pageSize) {
25569
- break;
25570
- }
25571
- if (options.json) {
25572
- page++;
25573
- continue;
25574
- }
25575
- const wantMore = await promptContinue(totalDisplayed, total);
25576
- if (!wantMore) {
26301
+ collected.push(...vulnerabilities);
26302
+ if (vulnerabilities.length < pageSize || collected.length >= result.body.total) {
25577
26303
  break;
25578
26304
  }
25579
26305
  page++;
25580
26306
  }
25581
- if (options.json) {
25582
- console.log(JSON.stringify(allJsonVulnerabilities, null, 2));
26307
+ const items = collected.slice(0, limit);
26308
+ if (items.length === 0) {
26309
+ if (isJson) {
26310
+ console.log("[]");
26311
+ } else if (isTsv) {
26312
+ console.log("severity status repository file line traceId title");
26313
+ } else {
26314
+ console.log("No vulnerabilities found.");
26315
+ }
26316
+ return;
26317
+ }
26318
+ if (isJson) {
26319
+ console.log(JSON.stringify(items, null, 2));
26320
+ } else if (isTsv) {
26321
+ printVulnerabilitiesTsv(items);
26322
+ } else {
26323
+ const total = collected.length;
26324
+ console.log(`Found ${total} vulnerabilit${total === 1 ? "y" : "ies"}:
26325
+ `);
26326
+ printVulnerabilities(items);
25583
26327
  }
25584
26328
  }
25585
26329
  );