securewithtrace 0.43.0 → 0.44.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +448 -94
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -3683,6 +3683,22 @@ function linkRolesDisplay(roles, role, roleLabel) {
3683
3683
  // ../../packages/core/src/application/environment-urls.ts
3684
3684
  var ENVIRONMENT_ADDITIONAL_URLS_MAX = 20;
3685
3685
 
3686
+ // ../../packages/core/src/pentest/attack-chain-status.ts
3687
+ var ATTACK_CHAIN_STATUS_VALUES = ["OPEN", "REMEDIATED", "VERIFIED_FIXED"];
3688
+ var ATTACK_CHAIN_STEP_STATE_VALUES = ATTACK_CHAIN_STATUS_VALUES;
3689
+
3690
+ // ../../packages/core/src/pentest/finding-status.ts
3691
+ var PENTEST_FINDING_STATUS_VALUES = [
3692
+ "OPEN",
3693
+ "IN_PROGRESS",
3694
+ "CONTESTED",
3695
+ "REMEDIATED",
3696
+ "VERIFIED_FIXED",
3697
+ "VERIFIED_OPEN",
3698
+ "ACCEPTED_WITH_CONTROLS",
3699
+ "DISMISSED"
3700
+ ];
3701
+
3686
3702
  // ../../node_modules/.bun/zod@4.3.6/node_modules/zod/v4/classic/external.js
3687
3703
  var external_exports = {};
3688
3704
  __export(external_exports, {
@@ -21884,16 +21900,7 @@ var PentestStatusValues = ["SCOPING", "IN_PROGRESS", "REMEDIATION", "CLOSED"];
21884
21900
  var PentestStatus = external_exports.enum(PentestStatusValues);
21885
21901
  var PentestTestTypeValues = ["BLACK_BOX", "GREY_BOX", "WHITE_BOX"];
21886
21902
  var PentestTestType = external_exports.enum(PentestTestTypeValues);
21887
- var PentestFindingStatusValues = [
21888
- "OPEN",
21889
- "IN_PROGRESS",
21890
- "CONTESTED",
21891
- "REMEDIATED",
21892
- "VERIFIED_FIXED",
21893
- "VERIFIED_OPEN",
21894
- "ACCEPTED_WITH_CONTROLS",
21895
- "DISMISSED"
21896
- ];
21903
+ var PentestFindingStatusValues = PENTEST_FINDING_STATUS_VALUES;
21897
21904
  var PentestFindingStatus = external_exports.enum(PentestFindingStatusValues);
21898
21905
  var PentestFindingDismissReasonValues = [
21899
21906
  "FALSE_POSITIVE",
@@ -22846,6 +22853,78 @@ var ListAgentTasksQuerySchema = external_exports.object({
22846
22853
  });
22847
22854
  var ListAgentTasksResponseSchema = external_exports.array(AgentTaskResponseSchema);
22848
22855
 
22856
+ // ../../packages/api-contracts/src/schemas/api/v1/inbox.api.ts
22857
+ var InboxLabelSchema = external_exports.string().regex(
22858
+ /^[a-z][a-z0-9-]{0,39}$/,
22859
+ "Inbox label must be lowercase letters/digits/dashes, starting with a letter, at most 40 characters"
22860
+ );
22861
+ var MAX_INBOX_LABEL_LENGTH = 40;
22862
+ var MAX_LOCAL_PART_LENGTH = 64;
22863
+ var MAX_INBOX_ADDRESS_PREFIX_LENGTH = MAX_LOCAL_PART_LENGTH - MAX_INBOX_LABEL_LENGTH - 1;
22864
+ var MANAGED_INBOX_DOMAINS = [
22865
+ "pentest.securewithtrace.com",
22866
+ "pentest.rmrflabs.com"
22867
+ ];
22868
+ var ManagedInboxDomainSchema = external_exports.enum(MANAGED_INBOX_DOMAINS);
22869
+ var DEFAULT_MANAGED_INBOX_DOMAIN = MANAGED_INBOX_DOMAINS[0];
22870
+ var ManagedInboxResponseSchema = external_exports.object({
22871
+ /** Full derived address, e.g. `acme-qa-admin@pentest.securewithtrace.com`. */
22872
+ address: external_exports.email(),
22873
+ label: external_exports.string(),
22874
+ createdAt: external_exports.date()
22875
+ });
22876
+ var ListManagedInboxesResponseSchema = external_exports.object({
22877
+ inboxes: external_exports.array(ManagedInboxResponseSchema),
22878
+ quota: external_exports.object({
22879
+ used: external_exports.number().int().nonnegative(),
22880
+ limit: external_exports.number().int().positive()
22881
+ })
22882
+ });
22883
+ var CreateManagedInboxRequestSchema = external_exports.object({
22884
+ label: InboxLabelSchema
22885
+ }).strict();
22886
+ var ManagedInboxLabelParamsSchema = OrgIdParamsSchema.extend({
22887
+ label: external_exports.string().min(1).max(64)
22888
+ });
22889
+ var ManagedInboxMessageParamsSchema = ManagedInboxLabelParamsSchema.extend({
22890
+ messageId: external_exports.string().min(1).max(500)
22891
+ });
22892
+ var ListInboxMessagesQuerySchema = external_exports.object({
22893
+ /** Max messages, newest first. The provider may clamp further. */
22894
+ limit: external_exports.coerce.number().int().positive().max(50).optional(),
22895
+ /** Only messages received strictly after this instant. */
22896
+ since: external_exports.coerce.date().optional()
22897
+ }).strict();
22898
+ var ManagedInboxMessageSummarySchema = external_exports.object({
22899
+ messageId: external_exports.string().min(1).max(500),
22900
+ from: external_exports.string().max(500),
22901
+ /** Recipients as the provider returned them (display-name form allowed). */
22902
+ to: external_exports.array(external_exports.string().max(500)),
22903
+ subject: external_exports.string().max(1e3).nullable(),
22904
+ preview: external_exports.string().max(2e3).nullable(),
22905
+ receivedAt: external_exports.coerce.date(),
22906
+ unread: external_exports.boolean()
22907
+ });
22908
+ var ListInboxMessagesResponseSchema = external_exports.object({
22909
+ address: external_exports.email(),
22910
+ label: external_exports.string(),
22911
+ messages: external_exports.array(ManagedInboxMessageSummarySchema)
22912
+ });
22913
+ var GetInboxMessageResponseSchema = external_exports.object({
22914
+ address: external_exports.email(),
22915
+ label: external_exports.string(),
22916
+ message: ManagedInboxMessageSummarySchema.extend({
22917
+ body: external_exports.string(),
22918
+ truncated: external_exports.boolean(),
22919
+ /**
22920
+ * http(s) links extracted from the message (hrefs first, then inline
22921
+ * text URLs). Button-style mail often carries the magic link only in
22922
+ * `href`, which a tag-stripped body can still miss if extraction fails.
22923
+ */
22924
+ links: external_exports.array(external_exports.string())
22925
+ })
22926
+ });
22927
+
22849
22928
  // ../../packages/api-contracts/src/schemas/api/v1/pentest-attack-chain.api.ts
22850
22929
  var PentestAttackChainParamsSchema = OrgIdParamsSchema.extend({
22851
22930
  pentestId: external_exports.uuid()
@@ -22876,6 +22955,23 @@ var UpdatePentestAttackChainRequestSchema = CreatePentestAttackChainRequestSchem
22876
22955
  var PublishPentestAttackChainRequestSchema = external_exports.object({
22877
22956
  expectedUpdatedAt: external_exports.coerce.date()
22878
22957
  });
22958
+ var PublishAllAttackChainsRequestSchema = external_exports.object({
22959
+ attackChainIds: external_exports.array(external_exports.uuid()).max(1e3).optional()
22960
+ });
22961
+ var PublishedAttackChainSchema = external_exports.object({
22962
+ attackChainId: external_exports.uuid(),
22963
+ displayId: external_exports.int()
22964
+ });
22965
+ var SkippedAttackChainReason = external_exports.enum(["TOO_SHORT", "FINDING_UNPUBLISHED"]);
22966
+ var SkippedAttackChainSchema = external_exports.object({
22967
+ attackChainId: external_exports.uuid(),
22968
+ title: external_exports.string(),
22969
+ reason: SkippedAttackChainReason
22970
+ });
22971
+ var PublishAllAttackChainsResponseSchema = external_exports.object({
22972
+ published: external_exports.array(PublishedAttackChainSchema),
22973
+ skipped: external_exports.array(SkippedAttackChainSchema)
22974
+ });
22879
22975
  var AttackChainStepFindingSchema = external_exports.object({
22880
22976
  pentestFindingId: external_exports.uuid(),
22881
22977
  displayId: external_exports.number().int().nullable(),
@@ -22884,12 +22980,15 @@ var AttackChainStepFindingSchema = external_exports.object({
22884
22980
  status: PentestFindingStatus,
22885
22981
  publishedAt: external_exports.coerce.date().nullable()
22886
22982
  });
22983
+ var AttackChainStatus = external_exports.enum(ATTACK_CHAIN_STATUS_VALUES);
22984
+ var AttackChainStepState = external_exports.enum(ATTACK_CHAIN_STEP_STATE_VALUES);
22887
22985
  var AttackChainStepSchema = external_exports.object({
22888
22986
  stepId: external_exports.uuid(),
22889
22987
  stepOrder: external_exports.number().int(),
22890
22988
  title: external_exports.string(),
22891
22989
  narrative: external_exports.string(),
22892
- findings: external_exports.array(AttackChainStepFindingSchema)
22990
+ findings: external_exports.array(AttackChainStepFindingSchema),
22991
+ remediationState: AttackChainStepState
22893
22992
  });
22894
22993
  var PentestAttackChainSchema = external_exports.object({
22895
22994
  attackChainId: external_exports.uuid(),
@@ -22900,6 +22999,7 @@ var PentestAttackChainSchema = external_exports.object({
22900
22999
  severity: PentestFindingSeverity,
22901
23000
  cvssVector: external_exports.string().nullable(),
22902
23001
  cvssScore: external_exports.number().nullable(),
23002
+ status: AttackChainStatus,
22903
23003
  publishedAt: external_exports.coerce.date().nullable(),
22904
23004
  createdAt: external_exports.coerce.date(),
22905
23005
  updatedAt: external_exports.coerce.date(),
@@ -23449,6 +23549,14 @@ var FindingRewriteJsonSchema = external_exports.object({
23449
23549
  * falls back to live `f.status` in either case.
23450
23550
  */
23451
23551
  statusAtRewrite: PentestFindingStatus.nullish(),
23552
+ /**
23553
+ * Plain-language lead, two or three sentences, rendered before Description.
23554
+ * The cap is a paragraph, not a section: a summary that needs more room is a
23555
+ * description. `.default(null)` keeps rewrites persisted before the field
23556
+ * shipped parseable (the JSON has no key) while the output type stays
23557
+ * `string | null`, so every reader handles "no summary" the same way.
23558
+ */
23559
+ summary: external_exports.string().min(1).max(1e3).nullable().default(null),
23452
23560
  description: external_exports.string().min(1).max(12e3),
23453
23561
  rootCauseAnalysis: external_exports.string().min(1).max(12e3).nullable(),
23454
23562
  impact: external_exports.string().min(1).max(8e3),
@@ -23480,6 +23588,7 @@ var FindingRewritesJsonSchema = external_exports.object({
23480
23588
  path: ["rewrites"]
23481
23589
  });
23482
23590
  var FindingRewriteCorrectionFieldValues = [
23591
+ "summary",
23483
23592
  "description",
23484
23593
  "rootCauseAnalysis",
23485
23594
  "impact",
@@ -24198,6 +24307,9 @@ var PentestFindingRetestIdParamsSchema = PentestFindingIdParamsSchema.extend({
24198
24307
  var PentestFindingEvidenceIdParamsSchema = PentestFindingIdParamsSchema.extend({
24199
24308
  artifactId: external_exports.uuid()
24200
24309
  });
24310
+ var PentestFindingTimelineEntryParamsSchema = PentestFindingIdParamsSchema.extend({
24311
+ entryId: external_exports.uuid()
24312
+ });
24201
24313
  var CustomerExploitArtifactResponseSchema = external_exports.object({
24202
24314
  artifactId: external_exports.uuid(),
24203
24315
  kind: PentestExploitArtifactKind,
@@ -24275,6 +24387,8 @@ var PentestFindingResponseSchema = external_exports.object({
24275
24387
  */
24276
24388
  displayId: external_exports.number().int().positive().nullable(),
24277
24389
  title: external_exports.string(),
24390
+ /** Plain-language lead rendered before `description`; null on findings authored before it existed. */
24391
+ summary: external_exports.string().nullable(),
24278
24392
  description: external_exports.string(),
24279
24393
  severity: PentestFindingSeverity,
24280
24394
  category: external_exports.string(),
@@ -24310,6 +24424,7 @@ var PentestFindingResponseSchema = external_exports.object({
24310
24424
  externalTicket: ExternalTicketSummarySchema.nullable().default(null)
24311
24425
  });
24312
24426
  var PentestFindingListItemSchema = PentestFindingResponseSchema.omit({
24427
+ summary: true,
24313
24428
  description: true,
24314
24429
  proofOfConcept: true,
24315
24430
  remediation: true,
@@ -24432,14 +24547,29 @@ var ResolutionTimelineVia = external_exports.object({
24432
24547
  identifier: external_exports.string(),
24433
24548
  url: external_exports.url()
24434
24549
  }).nullable().default(null);
24550
+ var ResolutionTimelineEntrySource = external_exports.enum(["NOTE", "CONTEST_RESOLUTION", "RETEST"]);
24551
+ var ResolutionTimelineEditHandleSchema = external_exports.object({
24552
+ source: ResolutionTimelineEntrySource,
24553
+ id: external_exports.uuid()
24554
+ }).nullable().default(null);
24435
24555
  var ResolutionTimelineEntrySchema = external_exports.discriminatedUnion("kind", [
24556
+ // The finding became customer-visible. Derived from `publishedAt`, not from
24557
+ // an audit row, so every published finding carries exactly one regardless of
24558
+ // which path (batch publish, attack chain, legacy backfill) stamped it.
24559
+ external_exports.object({
24560
+ kind: external_exports.literal("published"),
24561
+ at: external_exports.coerce.date(),
24562
+ actor: external_exports.literal("TRACE").default("TRACE"),
24563
+ actorName: ResolutionTimelineActorName
24564
+ }),
24436
24565
  external_exports.object({
24437
24566
  kind: external_exports.literal("remediation_reported"),
24438
24567
  at: external_exports.coerce.date(),
24439
24568
  actor: ResolutionTimelineActor,
24440
24569
  actorName: ResolutionTimelineActorName,
24441
24570
  note: external_exports.string().nullable(),
24442
- via: ResolutionTimelineVia
24571
+ via: ResolutionTimelineVia,
24572
+ editHandle: ResolutionTimelineEditHandleSchema
24443
24573
  }),
24444
24574
  // Retests are run by Trace (staff or a Trace agent), so the actor is fixed;
24445
24575
  // the default keeps a web build ahead of the api parsing older payloads.
@@ -24449,14 +24579,16 @@ var ResolutionTimelineEntrySchema = external_exports.discriminatedUnion("kind",
24449
24579
  actor: external_exports.literal("TRACE").default("TRACE"),
24450
24580
  actorName: ResolutionTimelineActorName,
24451
24581
  outcome: RetestOutcome,
24452
- note: external_exports.string().nullable()
24582
+ note: external_exports.string().nullable(),
24583
+ editHandle: ResolutionTimelineEditHandleSchema
24453
24584
  }),
24454
24585
  external_exports.object({
24455
24586
  kind: external_exports.literal("accepted_with_controls"),
24456
24587
  at: external_exports.coerce.date(),
24457
24588
  actor: ResolutionTimelineActor,
24458
24589
  actorName: ResolutionTimelineActorName,
24459
- note: external_exports.string()
24590
+ note: external_exports.string(),
24591
+ editHandle: ResolutionTimelineEditHandleSchema
24460
24592
  }),
24461
24593
  external_exports.object({
24462
24594
  kind: external_exports.literal("dismissed"),
@@ -24464,7 +24596,8 @@ var ResolutionTimelineEntrySchema = external_exports.discriminatedUnion("kind",
24464
24596
  actor: ResolutionTimelineActor,
24465
24597
  actorName: ResolutionTimelineActorName,
24466
24598
  reason: PentestFindingDismissReason,
24467
- note: external_exports.string()
24599
+ note: external_exports.string(),
24600
+ editHandle: ResolutionTimelineEditHandleSchema
24468
24601
  }),
24469
24602
  // `note` nullable: webhook-driven reopens carry no customer note (the via
24470
24603
  // badge is the payload); human reopens always have one.
@@ -24475,7 +24608,8 @@ var ResolutionTimelineEntrySchema = external_exports.discriminatedUnion("kind",
24475
24608
  actorName: ResolutionTimelineActorName,
24476
24609
  toStatus: external_exports.enum(["OPEN", "IN_PROGRESS"]),
24477
24610
  note: external_exports.string().nullable(),
24478
- via: ResolutionTimelineVia
24611
+ via: ResolutionTimelineVia,
24612
+ editHandle: ResolutionTimelineEditHandleSchema
24479
24613
  }),
24480
24614
  // Legacy/unattributed note whose transition isn't on the WORM audit log.
24481
24615
  external_exports.object({
@@ -24483,7 +24617,8 @@ var ResolutionTimelineEntrySchema = external_exports.discriminatedUnion("kind",
24483
24617
  at: external_exports.coerce.date(),
24484
24618
  actor: ResolutionTimelineActor,
24485
24619
  actorName: ResolutionTimelineActorName,
24486
- note: external_exports.string()
24620
+ note: external_exports.string(),
24621
+ editHandle: ResolutionTimelineEditHandleSchema
24487
24622
  }),
24488
24623
  // Customer opened a dispute (PR 3). `note` = the customer's justification.
24489
24624
  external_exports.object({
@@ -24503,7 +24638,8 @@ var ResolutionTimelineEntrySchema = external_exports.discriminatedUnion("kind",
24503
24638
  actor: ResolutionTimelineActor,
24504
24639
  actorName: ResolutionTimelineActorName,
24505
24640
  resultStatus: PentestFindingStatus.nullable(),
24506
- note: external_exports.string()
24641
+ note: external_exports.string(),
24642
+ editHandle: ResolutionTimelineEditHandleSchema
24507
24643
  }),
24508
24644
  // An external ticket was created from this finding (PR 2). No `note` — the
24509
24645
  // identifier + url are the payload; actor is the customer who pushed it.
@@ -24650,7 +24786,24 @@ var RecoveredFindingResultSchema = external_exports.object({
24650
24786
  var RecoverStrandedFindingsResponseSchema = external_exports.object({
24651
24787
  results: external_exports.array(RecoveredFindingResultSchema)
24652
24788
  });
24789
+ var DraftMissingFindingSummariesRequestSchema = external_exports.object({
24790
+ /** Limit the run to these findings; omit for every finding in the pentest missing a summary. */
24791
+ pentestFindingIds: external_exports.array(external_exports.uuid()).max(500).optional()
24792
+ });
24793
+ var DraftedFindingSummaryResultSchema = external_exports.object({
24794
+ pentestFindingId: external_exports.uuid(),
24795
+ displayId: external_exports.int().positive().nullable(),
24796
+ status: external_exports.enum(["written", "failed"]),
24797
+ /** Failure shape only (no prose): what the drafter returned and what was expected. */
24798
+ reason: external_exports.string().optional()
24799
+ });
24800
+ var DraftMissingFindingSummariesResponseSchema = external_exports.object({
24801
+ /** Findings that already had a summary and were not touched. */
24802
+ skipped: external_exports.int().min(0),
24803
+ results: external_exports.array(DraftedFindingSummaryResultSchema)
24804
+ });
24653
24805
  var FindingProseSchema = FindingRewriteJsonSchema.pick({
24806
+ summary: true,
24654
24807
  description: true,
24655
24808
  rootCauseAnalysis: true,
24656
24809
  impact: true,
@@ -24756,6 +24909,11 @@ var ResolvePentestFindingContestRequestSchema = external_exports.object({
24756
24909
  dismissReason: PentestFindingDismissReason.optional(),
24757
24910
  note: external_exports.string().min(1).max(1e4)
24758
24911
  });
24912
+ var UpdateResolutionTimelineEntryRequestSchema = external_exports.object({
24913
+ source: ResolutionTimelineEntrySource,
24914
+ note: external_exports.string().trim().min(1).max(1e4)
24915
+ });
24916
+ var UpdateResolutionTimelineEntryResponseSchema = external_exports.array(ResolutionTimelineEntrySchema);
24759
24917
  var PENTEST_FINDING_FEEDBACK_COMMENT_MAX_LENGTH = 5e3;
24760
24918
  var CreatePentestFindingFeedbackRequestSchema = external_exports.object({
24761
24919
  rating: PentestFindingFeedbackRating.nullable().default(null),
@@ -25531,8 +25689,19 @@ var SshPrivateKeySecretSchema = external_exports.object({
25531
25689
  privateKeyPem: SshPrivateKeyPemSchema,
25532
25690
  passphrase: external_exports.string().min(1).max(SSH_PASSPHRASE_MAX_CHARS).optional()
25533
25691
  }).strict();
25692
+ var PasskeySecretSchema = external_exports.object({
25693
+ /** ECDSA P-256 private key, PKCS#8, base64. */
25694
+ privateKey: external_exports.string().min(1).max(4096),
25695
+ /** Opaque RP-assigned user id, base64 (≤64 bytes decoded). */
25696
+ userHandle: external_exports.string().max(128),
25697
+ userName: external_exports.string().max(320).optional(),
25698
+ userDisplayName: external_exports.string().max(320).optional(),
25699
+ /** largeBlob extension payload, base64. */
25700
+ largeBlob: external_exports.string().max(8192).optional()
25701
+ }).strict();
25534
25702
  var CredentialMfaKindValues = ["TOTP"];
25535
25703
  var CredentialMfaKind = external_exports.enum(CredentialMfaKindValues);
25704
+ var MFA_CAPABLE_CREDENTIAL_TYPES = ["USERNAME_PASSWORD", "MAGIC_AUTH"];
25536
25705
  var TotpSetupKeySchema = external_exports.string().min(1).max(TOTP_MAX_SECRET_CHARS * 4).superRefine((value, ctx) => {
25537
25706
  const message = totpSetupKeyError(value);
25538
25707
  if (message !== null) ctx.addIssue({ code: "custom", message });
@@ -25999,6 +26168,9 @@ var ValidationResultProseSchema = external_exports.object({
25999
26168
  // Optional/nullable on the wire for rolling-deploy safety (a pre-title host omits
26000
26169
  // it); the api falls back to the candidate hypothesis title. The host tool requires it.
26001
26170
  title: external_exports.string().min(1).max(300).nullable().optional(),
26171
+ // Same rolling-deploy shape as `title`: the host tool requires it, the wire
26172
+ // accepts its absence so a pre-summary host image still lands its verdict.
26173
+ summary: external_exports.string().min(1).max(1e3).nullable().optional(),
26002
26174
  description: external_exports.string().min(1).max(12e3),
26003
26175
  impact: external_exports.string().min(1).max(8e3),
26004
26176
  remediation: external_exports.string().min(1).max(12e3),
@@ -26345,7 +26517,22 @@ var AgentSessionCreateTestEmailRequestSchema = external_exports.object({
26345
26517
  * `too_big` blob for a purpose it then rephrased to something shorter and
26346
26518
  * equivalent. Validate what the target requires, not what our storage prefers.
26347
26519
  */
26348
- purpose: external_exports.string().min(1).max(200)
26520
+ purpose: external_exports.string().min(1).max(200),
26521
+ /**
26522
+ * Which Trace-managed mail domain to mint on. Omitted means the server's
26523
+ * default (`pentest.securewithtrace.com` unless overridden per environment).
26524
+ *
26525
+ * An enum rather than free text: the address is registered on the customer's
26526
+ * target as a login identity, so the set of domains mail can be minted on is
26527
+ * a Trace decision, not an agent one. The alternates exist for a target that
26528
+ * blocks or rate-limits one domain, or a check that needs two accounts whose
26529
+ * email domains differ.
26530
+ *
26531
+ * Reuse of an unsaved address is per domain: asking for the alternate never
26532
+ * hands back an address minted on the default, since the agent asked for a
26533
+ * different domain precisely because that one did not do.
26534
+ */
26535
+ domain: ManagedInboxDomainSchema.optional()
26349
26536
  });
26350
26537
  var AgentSessionCreateTestEmailResponseSchema = external_exports.object({
26351
26538
  /**
@@ -26528,6 +26715,74 @@ var AgentSessionSessionStateResponseSchema = external_exports.object({
26528
26715
  /** The decrypted, parsed captured browser session. */
26529
26716
  state: StorageStateSchema
26530
26717
  });
26718
+ var AgentSessionGetSshPrivateKeyRequestSchema = external_exports.object({
26719
+ runVersion: external_exports.number().int().nonnegative(),
26720
+ /** Which SSH_PRIVATE_KEY credential to fetch (from the non-secret env reference). */
26721
+ credentialId: external_exports.uuid()
26722
+ });
26723
+ var AgentSessionSshPrivateKeyResponseSchema = external_exports.object({
26724
+ credentialId: external_exports.uuid(),
26725
+ /** `Credential.updatedAt` (ISO): logged so a run names the revision it loaded. */
26726
+ revision: external_exports.iso.datetime(),
26727
+ /**
26728
+ * The decrypted key material. Returned VERBATIM as stored: the host seeds its
26729
+ * redactor with these exact bytes, and `redactString` matches exact substrings,
26730
+ * so normalizing line endings here would silently defeat redaction downstream.
26731
+ */
26732
+ secret: SshPrivateKeySecretSchema
26733
+ });
26734
+ var AgentSessionPasskeySchema = external_exports.object({
26735
+ /** Relying party id (a hostname). */
26736
+ rpId: external_exports.string().min(1).max(253),
26737
+ /** WebAuthn credential id, base64 (≤1023 bytes decoded). */
26738
+ webauthnCredentialId: external_exports.string().min(1).max(1400),
26739
+ /** ECDSA P-256 private key, PKCS#8, base64. */
26740
+ privateKey: external_exports.string().min(1).max(4096).optional(),
26741
+ /** Opaque RP-assigned user id, base64. */
26742
+ userHandle: external_exports.string().max(128).optional(),
26743
+ userName: external_exports.string().max(320).optional(),
26744
+ userDisplayName: external_exports.string().max(320).optional(),
26745
+ largeBlob: external_exports.string().max(8192).optional(),
26746
+ /** Signature counter as the authenticator reports it. Never -1 on this wire. */
26747
+ signCount: external_exports.number().int().min(0),
26748
+ backupEligibility: external_exports.boolean(),
26749
+ backupState: external_exports.boolean()
26750
+ });
26751
+ var AgentSessionListPasskeysRequestSchema = external_exports.object({
26752
+ runVersion: external_exports.number().int().nonnegative(),
26753
+ /** Which account (by `Credential.label`). Required: the host persists under it. */
26754
+ credentialLabel: external_exports.string().min(1).max(200)
26755
+ });
26756
+ var AgentSessionStoredPasskeySchema = AgentSessionPasskeySchema.extend({
26757
+ credentialPasskeyId: external_exports.uuid(),
26758
+ privateKey: external_exports.string().min(1).max(4096),
26759
+ userHandle: external_exports.string().max(128)
26760
+ });
26761
+ var AgentSessionListPasskeysResponseSchema = external_exports.object({
26762
+ credentialId: external_exports.uuid(),
26763
+ credentialLabel: external_exports.string().max(200),
26764
+ /** The account types a browser logs in as; the same set that can carry a second factor. */
26765
+ credentialType: external_exports.enum(MFA_CAPABLE_CREDENTIAL_TYPES),
26766
+ passkeys: external_exports.array(AgentSessionStoredPasskeySchema),
26767
+ /**
26768
+ * Stored rows whose envelope could not be read. Reported rather than swallowed so a
26769
+ * partial list is never presented to the agent as the whole store, and rather than
26770
+ * failing the call so the readable passkeys still load.
26771
+ */
26772
+ unreadable: external_exports.number().int().min(0)
26773
+ });
26774
+ var AgentSessionUpsertPasskeyRequestSchema = external_exports.object({
26775
+ runVersion: external_exports.number().int().nonnegative(),
26776
+ /** From the list response, never agent-supplied. */
26777
+ credentialId: external_exports.uuid(),
26778
+ passkey: AgentSessionPasskeySchema
26779
+ });
26780
+ var AgentSessionUpsertPasskeyResponseSchema = external_exports.object({
26781
+ credentialPasskeyId: external_exports.uuid(),
26782
+ created: external_exports.boolean(),
26783
+ /** The stored counter after the write (never lower than before). */
26784
+ signCount: external_exports.number().int().min(0)
26785
+ });
26531
26786
  var ParkAgentSessionRequestSchema = external_exports.object({
26532
26787
  runVersion: external_exports.number().int().nonnegative(),
26533
26788
  outcome: external_exports.enum(["IDLE", "WAITING_ON_STAFF", "FAILED"]),
@@ -27239,9 +27494,8 @@ var agentSessionInternalContract = c4.router({
27239
27494
  // Session type receives no account credentials, or its pinned toolset does
27240
27495
  // not carry these tools.
27241
27496
  403: ErrorResponseSchema,
27242
- // RUN_REVOKED only. Provider-side label collisions are resolved by
27243
- // re-suffixing rather than surfaced, and quota is a 422 — a bare 409 here
27244
- // would read as run revocation and kill the session.
27497
+ // RUN_REVOKED or INBOX_ALREADY_EXISTS. The host treats only RUN_REVOKED
27498
+ // as fatal; a concurrent label claim is a recoverable conflict.
27245
27499
  409: ErrorResponseSchema,
27246
27500
  // The org is at its managed-inbox quota (INBOX_QUOTA_EXCEEDED) — the agent
27247
27501
  // has no delete tool, so it must escalate rather than retry. Also
@@ -27344,11 +27598,81 @@ var agentSessionInternalContract = c4.router({
27344
27598
  401: ErrorResponseSchema,
27345
27599
  409: ErrorResponseSchema,
27346
27600
  // Credential not found, not SESSION_STATE, or outside the run's
27347
- // org/pentest/environments — no cross-tenant existence leak.
27601
+ // org/pentest/environments: no cross-tenant existence leak.
27348
27602
  404: ErrorResponseSchema,
27349
27603
  500: ErrorResponseSchema
27350
27604
  }
27351
27605
  },
27606
+ // get_ssh_private_key: the SESSION_STATE counterpart for SSH_PRIVATE_KEY. Same
27607
+ // scoping (org + pentest environments from the run token), same 404 for anything
27608
+ // outside it. The host calls this once per reference at boot and pipes the result
27609
+ // into ssh-add; nothing downstream can read the key back.
27610
+ getSshPrivateKey: {
27611
+ method: "POST",
27612
+ path: "/internal/v1/agent-sessions/:agentSessionId/ssh-private-key",
27613
+ pathParams: AgentSessionInternalParamsSchema,
27614
+ body: AgentSessionGetSshPrivateKeyRequestSchema,
27615
+ responses: {
27616
+ 200: AgentSessionSshPrivateKeyResponseSchema,
27617
+ 400: ErrorResponseSchema,
27618
+ 401: ErrorResponseSchema,
27619
+ 409: ErrorResponseSchema,
27620
+ // Credential not found, not SSH_PRIVATE_KEY, or outside the run's
27621
+ // org/pentest/environments: no cross-tenant existence leak.
27622
+ 404: ErrorResponseSchema,
27623
+ 500: ErrorResponseSchema
27624
+ }
27625
+ },
27626
+ // provision_passkey, read half: every passkey Trace holds for one of the run's
27627
+ // accounts, decrypted, so the host can load them into the virtual authenticator
27628
+ // it arms on the agent's browser. Same resolution and ownership gate as
27629
+ // enroll-mfa (org + pentest from the run token; label must resolve to exactly
27630
+ // one launch-visible credential). Returns 200 with an empty array for an
27631
+ // account with no passkeys yet: that is how the host learns the credentialId
27632
+ // it will persist a new one under.
27633
+ listPasskeys: {
27634
+ method: "POST",
27635
+ path: "/internal/v1/agent-sessions/:agentSessionId/passkeys",
27636
+ pathParams: AgentSessionInternalParamsSchema,
27637
+ body: AgentSessionListPasskeysRequestSchema,
27638
+ responses: {
27639
+ 200: AgentSessionListPasskeysResponseSchema,
27640
+ // Label on more than one environment (terminal), or a Trace fixture label.
27641
+ 400: ErrorResponseSchema,
27642
+ 401: ErrorResponseSchema,
27643
+ // Session type receives no account credentials.
27644
+ 403: ErrorResponseSchema,
27645
+ // No launch-visible credential with that label; same message for another
27646
+ // tenant's.
27647
+ 404: ErrorResponseSchema,
27648
+ // RUN_REVOKED, including the liveness recheck inside the service.
27649
+ 409: ErrorResponseSchema,
27650
+ // The credential's type is not an account a browser logs in as.
27651
+ 422: ErrorResponseSchema,
27652
+ 500: ErrorResponseSchema
27653
+ }
27654
+ },
27655
+ // provision_passkey, write half: the host observed a passkey on the armed
27656
+ // authenticator (new, or its counter moved) and persists it. Keyed by the
27657
+ // credentialId the list call returned, never by label, and idempotent on
27658
+ // (credentialId, rpId, webauthnCredentialId) because the host retries on 5xx.
27659
+ upsertPasskey: {
27660
+ method: "POST",
27661
+ path: "/internal/v1/agent-sessions/:agentSessionId/passkeys/upsert",
27662
+ pathParams: AgentSessionInternalParamsSchema,
27663
+ body: AgentSessionUpsertPasskeyRequestSchema,
27664
+ responses: {
27665
+ 200: AgentSessionUpsertPasskeyResponseSchema,
27666
+ // PASSKEY_SECRET_REQUIRED: no row for the natural key and no key in the body.
27667
+ 400: ErrorResponseSchema,
27668
+ 401: ErrorResponseSchema,
27669
+ 403: ErrorResponseSchema,
27670
+ // credentialId not on this run's pentest under this org.
27671
+ 404: ErrorResponseSchema,
27672
+ 409: ErrorResponseSchema,
27673
+ 500: ErrorResponseSchema
27674
+ }
27675
+ },
27352
27676
  // renew_cloud_credentials (GCP integration PR 7): agent-invoked re-mint of the
27353
27677
  // ~1 h AWS/GCP recon creds. Run-token authed; org+pentest from the session row;
27354
27678
  // 403 for a non-credentialed session type (ADHOC/CODE_REVIEW/REPORT).
@@ -30704,7 +31028,13 @@ var VulnerabilityTypeValues = [
30704
31028
  "MISCONFIGURATION"
30705
31029
  ];
30706
31030
  var VulnerabilityType = external_exports.enum(VulnerabilityTypeValues);
30707
- var DismissReasonValues = ["ACCEPTED_RISK", "FALSE_POSITIVE", "OTHER"];
31031
+ var DismissReasonValues = [
31032
+ "ACCEPTED_RISK",
31033
+ "FALSE_POSITIVE",
31034
+ "OTHER",
31035
+ "INTENDED_BEHAVIOR",
31036
+ "DEFERRED"
31037
+ ];
30708
31038
  var DismissReason = external_exports.enum(DismissReasonValues);
30709
31039
 
30710
31040
  // ../../packages/api-contracts/src/schemas/api/v1/engagement.api.ts
@@ -31274,72 +31604,6 @@ var staffGithubContract = c18.router({
31274
31604
  }
31275
31605
  });
31276
31606
 
31277
- // ../../packages/api-contracts/src/schemas/api/v1/inbox.api.ts
31278
- var InboxLabelSchema = external_exports.string().regex(
31279
- /^[a-z][a-z0-9-]{0,39}$/,
31280
- "Inbox label must be lowercase letters/digits/dashes, starting with a letter, at most 40 characters"
31281
- );
31282
- var MAX_INBOX_LABEL_LENGTH = 40;
31283
- var MAX_LOCAL_PART_LENGTH = 64;
31284
- var MAX_INBOX_ADDRESS_PREFIX_LENGTH = MAX_LOCAL_PART_LENGTH - MAX_INBOX_LABEL_LENGTH - 1;
31285
- var ManagedInboxResponseSchema = external_exports.object({
31286
- /** Full derived address, e.g. `acme-qa-admin@pentest.securewithtrace.com`. */
31287
- address: external_exports.email(),
31288
- label: external_exports.string(),
31289
- createdAt: external_exports.date()
31290
- });
31291
- var ListManagedInboxesResponseSchema = external_exports.object({
31292
- inboxes: external_exports.array(ManagedInboxResponseSchema),
31293
- quota: external_exports.object({
31294
- used: external_exports.number().int().nonnegative(),
31295
- limit: external_exports.number().int().positive()
31296
- })
31297
- });
31298
- var CreateManagedInboxRequestSchema = external_exports.object({
31299
- label: InboxLabelSchema
31300
- }).strict();
31301
- var ManagedInboxLabelParamsSchema = OrgIdParamsSchema.extend({
31302
- label: external_exports.string().min(1).max(64)
31303
- });
31304
- var ManagedInboxMessageParamsSchema = ManagedInboxLabelParamsSchema.extend({
31305
- messageId: external_exports.string().min(1).max(500)
31306
- });
31307
- var ListInboxMessagesQuerySchema = external_exports.object({
31308
- /** Max messages, newest first. The provider may clamp further. */
31309
- limit: external_exports.coerce.number().int().positive().max(50).optional(),
31310
- /** Only messages received strictly after this instant. */
31311
- since: external_exports.coerce.date().optional()
31312
- }).strict();
31313
- var ManagedInboxMessageSummarySchema = external_exports.object({
31314
- messageId: external_exports.string().min(1).max(500),
31315
- from: external_exports.string().max(500),
31316
- /** Recipients as the provider returned them (display-name form allowed). */
31317
- to: external_exports.array(external_exports.string().max(500)),
31318
- subject: external_exports.string().max(1e3).nullable(),
31319
- preview: external_exports.string().max(2e3).nullable(),
31320
- receivedAt: external_exports.coerce.date(),
31321
- unread: external_exports.boolean()
31322
- });
31323
- var ListInboxMessagesResponseSchema = external_exports.object({
31324
- address: external_exports.email(),
31325
- label: external_exports.string(),
31326
- messages: external_exports.array(ManagedInboxMessageSummarySchema)
31327
- });
31328
- var GetInboxMessageResponseSchema = external_exports.object({
31329
- address: external_exports.email(),
31330
- label: external_exports.string(),
31331
- message: ManagedInboxMessageSummarySchema.extend({
31332
- body: external_exports.string(),
31333
- truncated: external_exports.boolean(),
31334
- /**
31335
- * http(s) links extracted from the message (hrefs first, then inline
31336
- * text URLs). Button-style mail often carries the magic link only in
31337
- * `href`, which a tag-stripped body can still miss if extraction fails.
31338
- */
31339
- links: external_exports.array(external_exports.string())
31340
- })
31341
- });
31342
-
31343
31607
  // ../../packages/api-contracts/src/contracts/v1/inbox.ts
31344
31608
  var c19 = initContract();
31345
31609
  var inboxContract = c19.router({
@@ -32517,9 +32781,35 @@ var PrScanAnalyticsMonthSchema = external_exports.object({
32517
32781
  /** Same metrics as the month, split by PR author. Sorted by findings, then PRs. */
32518
32782
  developers: external_exports.array(PrScanAnalyticsDeveloperSchema)
32519
32783
  });
32784
+ var PrScanAnalyticsClassCountSchema = external_exports.object({
32785
+ label: external_exports.string(),
32786
+ count: external_exports.number().int().nonnegative()
32787
+ });
32788
+ var PrScanAnalyticsTimeToFixSchema = external_exports.object({
32789
+ severity: VulnerabilitySeverity,
32790
+ fixedCount: external_exports.number().int().nonnegative(),
32791
+ /** Median minutes from the finding's first detection to the fixing scan; null when nothing was fixed. */
32792
+ medianMinutes: external_exports.number().nonnegative().nullable(),
32793
+ fixedWithinDayCount: external_exports.number().int().nonnegative()
32794
+ });
32795
+ var PrScanAnalyticsSummarySchema = external_exports.object({
32796
+ /** First completed PR scan on a tracked PR in the window; null when none. */
32797
+ since: external_exports.iso.datetime().nullable(),
32798
+ prCount: external_exports.number().int().nonnegative(),
32799
+ findingsCount: external_exports.number().int().nonnegative(),
32800
+ criticalHighCount: external_exports.number().int().nonnegative(),
32801
+ criticalHighFixedBeforeMergeCount: external_exports.number().int().nonnegative(),
32802
+ /** Critical/high fixed on a PR that is still open. */
32803
+ criticalHighFixedAwaitingMergeCount: external_exports.number().int().nonnegative(),
32804
+ /** Critical/high by weakness class, largest first, at most five rows with "Other" last. */
32805
+ criticalHighByClass: external_exports.array(PrScanAnalyticsClassCountSchema),
32806
+ /** One row per severity, CRITICAL first. */
32807
+ timeToFix: external_exports.array(PrScanAnalyticsTimeToFixSchema).length(VulnerabilitySeverityValues.length)
32808
+ });
32520
32809
  var PrScanAnalyticsResponseSchema = external_exports.object({
32521
32810
  generatedAt: external_exports.iso.datetime(),
32522
- months: external_exports.array(PrScanAnalyticsMonthSchema).length(12)
32811
+ months: external_exports.array(PrScanAnalyticsMonthSchema).length(12),
32812
+ summary: PrScanAnalyticsSummarySchema
32523
32813
  });
32524
32814
  var ProductPlanParamsSchema = external_exports.object({
32525
32815
  organizationId: external_exports.uuid(),
@@ -33793,6 +34083,26 @@ var pentestAttackChainContract = c29.router({
33793
34083
  500: ErrorResponseSchema
33794
34084
  }
33795
34085
  },
34086
+ /**
34087
+ * The "Publish all" control. Mirrors `pentestFindingContract.publishAllFindings`
34088
+ * (`/findings/publications`): a collection resource of publications rather than a verb
34089
+ * on the chain list.
34090
+ */
34091
+ publishAllAttackChains: {
34092
+ method: "POST",
34093
+ path: "/organizations/:organizationId/pentests/:pentestId/attack-chains/publications",
34094
+ pathParams: PentestAttackChainParamsSchema,
34095
+ body: PublishAllAttackChainsRequestSchema,
34096
+ responses: {
34097
+ 200: PublishAllAttackChainsResponseSchema,
34098
+ 400: ErrorResponseSchema,
34099
+ 401: ErrorResponseSchema,
34100
+ 403: ErrorResponseSchema,
34101
+ 404: ErrorResponseSchema,
34102
+ 409: ErrorResponseSchema,
34103
+ 500: ErrorResponseSchema
34104
+ }
34105
+ },
33796
34106
  unpublishAttackChain: {
33797
34107
  method: "POST",
33798
34108
  path: "/organizations/:organizationId/pentests/:pentestId/attack-chains/:attackChainId/unpublish",
@@ -34647,6 +34957,22 @@ var pentestFindingContract = c36.router({
34647
34957
  500: ErrorResponseSchema
34648
34958
  }
34649
34959
  },
34960
+ draftMissingFindingSummaries: {
34961
+ method: "POST",
34962
+ path: "/organizations/:organizationId/pentests/:pentestId/findings/summaries/draft",
34963
+ pathParams: PentestFindingParamsSchema,
34964
+ body: DraftMissingFindingSummariesRequestSchema,
34965
+ responses: {
34966
+ 200: DraftMissingFindingSummariesResponseSchema,
34967
+ 400: ErrorResponseSchema,
34968
+ 401: ErrorResponseSchema,
34969
+ 403: ErrorResponseSchema,
34970
+ 404: ErrorResponseSchema,
34971
+ // PENTEST_CLOSED (reopen first).
34972
+ 409: ErrorResponseSchema,
34973
+ 500: ErrorResponseSchema
34974
+ }
34975
+ },
34650
34976
  applyFindingCvss: {
34651
34977
  method: "PUT",
34652
34978
  path: "/organizations/:organizationId/pentests/:pentestId/findings/:pentestFindingId/cvss",
@@ -34924,6 +35250,26 @@ var pentestFindingContract = c36.router({
34924
35250
  },
34925
35251
  // Note CRUD removed in PR 2 — the resolution timeline replaces the read
34926
35252
  // surface; resolution workflows + the operator agent write notes directly.
35253
+ // ─── Resolution timeline ───
35254
+ // Staff-only: rewrite the customer-visible note behind one history entry.
35255
+ // `entryId` + `body.source` come from the entry's `editHandle` on the staff GET.
35256
+ updateTimelineEntry: {
35257
+ method: "PATCH",
35258
+ path: "/organizations/:organizationId/pentests/:pentestId/findings/:pentestFindingId/timeline-entries/:entryId",
35259
+ pathParams: PentestFindingTimelineEntryParamsSchema,
35260
+ body: UpdateResolutionTimelineEntryRequestSchema,
35261
+ responses: {
35262
+ 200: UpdateResolutionTimelineEntryResponseSchema,
35263
+ 400: ErrorResponseSchema,
35264
+ 401: ErrorResponseSchema,
35265
+ // Non-staff caller, or the entry is customer-authored.
35266
+ 403: ErrorResponseSchema,
35267
+ 404: ErrorResponseSchema,
35268
+ // PENTEST_CLOSED.
35269
+ 409: ErrorResponseSchema,
35270
+ 500: ErrorResponseSchema
35271
+ }
35272
+ },
34927
35273
  // ─── AI parsing ───
34928
35274
  parseFindingMarkdown: {
34929
35275
  method: "POST",
@@ -45425,7 +45771,7 @@ function collectAdditionalUrl(value, previous) {
45425
45771
  function buildCreateEnvironmentBody(input) {
45426
45772
  return {
45427
45773
  name: input.name,
45428
- primaryUrl: input.primaryUrl,
45774
+ ...input.primaryUrl !== void 0 ? { primaryUrl: input.primaryUrl } : {},
45429
45775
  ...input.additionalUrls !== void 0 && input.additionalUrls.length > 0 ? { additionalUrls: input.additionalUrls } : {},
45430
45776
  ...input.description !== void 0 ? { description: input.description } : {},
45431
45777
  ...input.isProduction !== void 0 ? { isProduction: input.isProduction } : {}
@@ -45909,13 +46255,20 @@ async function runDelete4(environmentId, options) {
45909
46255
  console.log(colorSuccess(`Deleted environment ${environmentId}`));
45910
46256
  }
45911
46257
  function createCreateCommand4() {
45912
- return new Command("create").description("Create an environment for an application").argument("<name>", "Environment name (lowercase letters/digits/dashes)").requiredOption("--app <applicationId>", "Parent application UUID").requiredOption("--url <url>", "Primary URL for the environment").option("--additional-url <url>", "Extra probe URL (repeatable)", collectAdditionalUrl).option("--description <text>", "Description").option("--production", "Mark as the production environment").option("-o, --output <format>", "Output format: table, json, tsv (default: table)", "table").addHelpText(
46258
+ return new Command("create").description("Create an environment for an application").argument("<name>", "Environment name (lowercase letters/digits/dashes)").requiredOption("--app <applicationId>", "Parent application UUID").option(
46259
+ "--url <url>",
46260
+ "Primary URL for the environment (web and API applications only; omit for CLI and desktop)"
46261
+ ).option("--additional-url <url>", "Extra probe URL (repeatable)", collectAdditionalUrl).option("--description <text>", "Description").option("--production", "Mark as the production environment").option("-o, --output <format>", "Output format: table, json, tsv (default: table)", "table").addHelpText(
45913
46262
  "after",
45914
46263
  `
46264
+ A web or API application requires --url. A CLI or desktop application is a
46265
+ credential context with no network target and must omit it.
46266
+
45915
46267
  Examples:
45916
46268
  tracecli environment create staging --app <appId> --url https://staging.acme.com
45917
46269
  tracecli environment create production --app <appId> --url https://acme.com --production
45918
- tracecli environment create production --app <appId> --url https://app.acme.com --additional-url https://api.acme.com`
46270
+ tracecli environment create production --app <appId> --url https://app.acme.com --additional-url https://api.acme.com
46271
+ tracecli environment create pentest-sandbox --app <cliAppId>`
45919
46272
  ).action(async (name, options) => {
45920
46273
  await runCreate4(name, options);
45921
46274
  });
@@ -46672,6 +47025,7 @@ function isOutputFormat2(value) {
46672
47025
  return OUTPUT_FORMATS2.includes(value);
46673
47026
  }
46674
47027
  var PROSE_SECTIONS = [
47028
+ { key: "summary", heading: "Summary" },
46675
47029
  { key: "description", heading: "Description" },
46676
47030
  { key: "impact", heading: "Impact" },
46677
47031
  { key: "attackScenario", heading: "Attack Scenario" },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "securewithtrace",
3
- "version": "0.43.0",
3
+ "version": "0.44.0",
4
4
  "description": "Official Trace CLI for vulnerability intelligence in your terminal",
5
5
  "type": "module",
6
6
  "bin": {