@zksecurity/zkao-sdk 0.2.0 → 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -73,6 +73,50 @@ interface paths {
73
73
  patch?: never;
74
74
  trace?: never;
75
75
  };
76
+ "/projects/{projectId}/repositories/{repositoryId}/audit-areas": {
77
+ parameters: {
78
+ query?: never;
79
+ header?: never;
80
+ path?: never;
81
+ cookie?: never;
82
+ };
83
+ /**
84
+ * List a repository's audit areas
85
+ * @description Requires scope: `read`. Audit areas are the named subsystems a scan can be scoped to (`auditAreaKeys` on launch). Each scan that maps the repository keeps the list current; sizes come from one branch's map, so an area that map no longer names is returned without one.
86
+ */
87
+ get: operations["listAuditAreas"];
88
+ put?: never;
89
+ /**
90
+ * Add a custom audit area
91
+ * @description Requires scope: `guidance:write`. An area is a name and a description, never a file list: a scan resolves it against the code at the commit it runs on. The `key` is derived from the name and disambiguated on collision.
92
+ */
93
+ post: operations["createAuditArea"];
94
+ delete?: never;
95
+ options?: never;
96
+ head?: never;
97
+ patch?: never;
98
+ trace?: never;
99
+ };
100
+ "/projects/{projectId}/repositories/{repositoryId}/audit-areas/{areaKey}": {
101
+ parameters: {
102
+ query?: never;
103
+ header?: never;
104
+ path?: never;
105
+ cookie?: never;
106
+ };
107
+ get?: never;
108
+ put?: never;
109
+ post?: never;
110
+ /**
111
+ * Delete a custom audit area
112
+ * @description Requires scope: `guidance:write`. Only an area with `source: custom` can be deleted; a discovered one is the map's own account of the repository and is refused with `409`. Scans that already ran keep the scope they ran with.
113
+ */
114
+ delete: operations["deleteAuditArea"];
115
+ options?: never;
116
+ head?: never;
117
+ patch?: never;
118
+ trace?: never;
119
+ };
76
120
  "/projects/{projectId}/scans": {
77
121
  parameters: {
78
122
  query?: never;
@@ -93,6 +137,10 @@ interface paths {
93
137
  * ACTIVE. Discover valid `presetRef` / `optInFlowIds` via the discovery
94
138
  * endpoints. Per-token spend limits are enforced, and the project must have
95
139
  * enough prepaid credits to cover the budget (otherwise `402`).
140
+ *
141
+ * A repository added moments ago may still be analyzing, in which case
142
+ * this returns `409 repository_initializing`. Wait for its `readiness` to
143
+ * be `ready` in `GET /repositories` and launch again.
96
144
  */
97
145
  post: operations["launchScan"];
98
146
  delete?: never;
@@ -330,7 +378,7 @@ interface paths {
330
378
  };
331
379
  /**
332
380
  * List opt-in flows available at launch
333
- * @description Requires scope: `read`. Pass the chosen ids as `optInFlowIds` when launching.
381
+ * @description Requires scope: `read`. Pass the chosen ids as `optInFlowIds` when launching. A flow marked `rotating` may run without being asked for.
334
382
  */
335
383
  get: operations["listOptionalFlows"];
336
384
  put?: never;
@@ -445,7 +493,7 @@ interface components {
445
493
  Error: {
446
494
  error: {
447
495
  /** @enum {string} */
448
- code: "unauthorized" | "forbidden" | "not_found" | "bad_request" | "conflict" | "insufficient_credits" | "rate_limited" | "internal";
496
+ code: "unauthorized" | "forbidden" | "not_found" | "bad_request" | "conflict" | "insufficient_credits" | "repository_initializing" | "rate_limited" | "internal";
449
497
  message: string;
450
498
  };
451
499
  };
@@ -465,6 +513,11 @@ interface components {
465
513
  defaultBranch?: string | null;
466
514
  /** @description ACTIVE | MARKED_FOR_DELETION */
467
515
  status: string;
516
+ /**
517
+ * @description Whether a scan can start on this repository. A newly added repository is `analyzing` until a one-time pass over it finishes; launching a scan meanwhile fails with `repository_initializing`. Poll this endpoint rather than retrying the launch blind.
518
+ * @enum {string}
519
+ */
520
+ readiness: "ready" | "analyzing";
468
521
  };
469
522
  Scan: {
470
523
  id: string;
@@ -557,7 +610,7 @@ interface components {
557
610
  ref: string;
558
611
  name: string;
559
612
  description?: string | null;
560
- /** @description 0 = use the default floor. */
613
+ /** @description Minimum budget for a launch that names no optInFlowIds. Adding a flow raises it. 0 = use the default floor. */
561
614
  minCredits: number;
562
615
  modelTier: string;
563
616
  };
@@ -567,6 +620,8 @@ interface components {
567
620
  flowId: string;
568
621
  title: string;
569
622
  description: string;
623
+ /** @description True when this flow belongs to a group the preset rotates through. Some of the group runs on every scan without being asked for, and which members depends on the repository. Naming it in `optInFlowIds` adds it whether or not the rotation picked it, and raises the scan's minimum budget. */
624
+ rotating: boolean;
570
625
  };
571
626
  LaunchScanRequest: {
572
627
  repositoryId: string;
@@ -579,8 +634,10 @@ interface components {
579
634
  /** @description Pin a specific commit SHA (7-40 hex chars). */
580
635
  commitHash?: string | null;
581
636
  commitMessage?: string | null;
582
- /** @description Optional-flow ids from /optional-flows. */
637
+ /** @description Optional-flow ids from /optional-flows, added to the scan on top of whatever a rotating group already contributes. */
583
638
  optInFlowIds?: string[];
639
+ /** @description Area keys from /audit-areas to scope this scan to. Omit or send an empty array to scan the whole repository. A key the repository's current map no longer names fails the launch rather than being dropped, so a scan budgeted for one subsystem never silently runs against everything. */
640
+ auditAreaKeys?: string[];
584
641
  /** @description Guidance for this scan only, replacing the repository's configured guidance layer (it is still layered over any committed zkao.md). Omit the field to inherit the repository's guidance; send null to scan with no guidance layer. */
585
642
  guidance?: string | null;
586
643
  };
@@ -611,6 +668,44 @@ interface components {
611
668
  /** @description True when the content already matched (no revision recorded). */
612
669
  unchanged: boolean;
613
670
  };
671
+ AuditArea: {
672
+ /** @description Stable slug, unique per repository. Pass it in `auditAreaKeys` when launching a scan. */
673
+ key: string;
674
+ name: string;
675
+ description: string | null;
676
+ /**
677
+ * @description `discovered` was named by a scan's map of the repository; `custom` was added through this API or the app.
678
+ * @enum {string}
679
+ */
680
+ source: "discovered" | "custom";
681
+ /** @description Whether the branch's latest map still names this area. */
682
+ inLatestMap: boolean;
683
+ /** @description Files this area covers at that map. Null when the map does not name it. */
684
+ files: number | null;
685
+ /** @description Lines this area covers at that map. Null when the map does not name it. */
686
+ lines: number | null;
687
+ };
688
+ AuditAreaList: {
689
+ repositoryId: string;
690
+ /** @description Branch the sizes were read from. */
691
+ branch: string | null;
692
+ /** @description Whether that branch has a stored map at all. */
693
+ mapped: boolean;
694
+ totalFiles: number | null;
695
+ totalLines: number | null;
696
+ /** @description The map's own audit order first, then the areas it no longer names. */
697
+ areas: components["schemas"]["AuditArea"][];
698
+ };
699
+ CreateAuditAreaRequest: {
700
+ name: string;
701
+ /** @description What this part of the code does. */
702
+ description?: string | null;
703
+ };
704
+ DeleteAuditAreaResult: {
705
+ repositoryId: string;
706
+ key: string;
707
+ deleted: boolean;
708
+ };
614
709
  CancelScanResult: {
615
710
  scanId: string;
616
711
  /** @description Terminal status after cancellation (always CANCELLED). */
@@ -696,6 +791,15 @@ interface components {
696
791
  "application/json": components["schemas"]["Error"];
697
792
  };
698
793
  };
794
+ /** @description The repository is still being analyzed and cannot be scanned yet. The same request succeeds once `GET /repositories` reports its `readiness` as `ready`. (code `repository_initializing`) */
795
+ RepositoryInitializing: {
796
+ headers: {
797
+ [name: string]: unknown;
798
+ };
799
+ content: {
800
+ "application/json": components["schemas"]["Error"];
801
+ };
802
+ };
699
803
  /** @description The token lacks the required scope */
700
804
  Forbidden: {
701
805
  headers: {
@@ -738,6 +842,7 @@ interface components {
738
842
  parameters: {
739
843
  ProjectId: string;
740
844
  ScanId: string;
845
+ /** @description The finding id, or the `ZK-` label shown on the finding page (the id's last eight characters, prefix optional). A label that matches more than one finding in the project is refused with `409 conflict`; use the full id. */
741
846
  FindingId: string;
742
847
  RepositoryId: string;
743
848
  Page: number;
@@ -833,6 +938,97 @@ interface operations {
833
938
  409: components["responses"]["Conflict"];
834
939
  };
835
940
  };
941
+ listAuditAreas: {
942
+ parameters: {
943
+ query?: {
944
+ /** @description Branch whose map the sizes come from. Defaults to the repository's default branch. */
945
+ branch?: string;
946
+ };
947
+ header?: never;
948
+ path: {
949
+ projectId: components["parameters"]["ProjectId"];
950
+ repositoryId: components["parameters"]["RepositoryId"];
951
+ };
952
+ cookie?: never;
953
+ };
954
+ requestBody?: never;
955
+ responses: {
956
+ /** @description OK */
957
+ 200: {
958
+ headers: {
959
+ [name: string]: unknown;
960
+ };
961
+ content: {
962
+ "application/json": components["schemas"]["AuditAreaList"];
963
+ };
964
+ };
965
+ 401: components["responses"]["Unauthorized"];
966
+ 403: components["responses"]["Forbidden"];
967
+ 404: components["responses"]["NotFound"];
968
+ };
969
+ };
970
+ createAuditArea: {
971
+ parameters: {
972
+ query?: never;
973
+ header?: never;
974
+ path: {
975
+ projectId: components["parameters"]["ProjectId"];
976
+ repositoryId: components["parameters"]["RepositoryId"];
977
+ };
978
+ cookie?: never;
979
+ };
980
+ requestBody: {
981
+ content: {
982
+ "application/json": components["schemas"]["CreateAuditAreaRequest"];
983
+ };
984
+ };
985
+ responses: {
986
+ /** @description Created */
987
+ 201: {
988
+ headers: {
989
+ [name: string]: unknown;
990
+ };
991
+ content: {
992
+ "application/json": {
993
+ area: components["schemas"]["AuditArea"];
994
+ };
995
+ };
996
+ };
997
+ 400: components["responses"]["BadRequest"];
998
+ 401: components["responses"]["Unauthorized"];
999
+ 403: components["responses"]["Forbidden"];
1000
+ 404: components["responses"]["NotFound"];
1001
+ 409: components["responses"]["Conflict"];
1002
+ };
1003
+ };
1004
+ deleteAuditArea: {
1005
+ parameters: {
1006
+ query?: never;
1007
+ header?: never;
1008
+ path: {
1009
+ projectId: components["parameters"]["ProjectId"];
1010
+ repositoryId: components["parameters"]["RepositoryId"];
1011
+ areaKey: string;
1012
+ };
1013
+ cookie?: never;
1014
+ };
1015
+ requestBody?: never;
1016
+ responses: {
1017
+ /** @description OK */
1018
+ 200: {
1019
+ headers: {
1020
+ [name: string]: unknown;
1021
+ };
1022
+ content: {
1023
+ "application/json": components["schemas"]["DeleteAuditAreaResult"];
1024
+ };
1025
+ };
1026
+ 401: components["responses"]["Unauthorized"];
1027
+ 403: components["responses"]["Forbidden"];
1028
+ 404: components["responses"]["NotFound"];
1029
+ 409: components["responses"]["Conflict"];
1030
+ };
1031
+ };
836
1032
  listScans: {
837
1033
  parameters: {
838
1034
  query?: {
@@ -890,6 +1086,7 @@ interface operations {
890
1086
  402: components["responses"]["InsufficientCredits"];
891
1087
  403: components["responses"]["Forbidden"];
892
1088
  404: components["responses"]["NotFound"];
1089
+ 409: components["responses"]["RepositoryInitializing"];
893
1090
  429: components["responses"]["RateLimited"];
894
1091
  };
895
1092
  };
@@ -988,6 +1185,7 @@ interface operations {
988
1185
  header?: never;
989
1186
  path: {
990
1187
  projectId: components["parameters"]["ProjectId"];
1188
+ /** @description The finding id, or the `ZK-` label shown on the finding page (the id's last eight characters, prefix optional). A label that matches more than one finding in the project is refused with `409 conflict`; use the full id. */
991
1189
  findingId: components["parameters"]["FindingId"];
992
1190
  };
993
1191
  cookie?: never;
@@ -1008,6 +1206,7 @@ interface operations {
1008
1206
  401: components["responses"]["Unauthorized"];
1009
1207
  403: components["responses"]["Forbidden"];
1010
1208
  404: components["responses"]["NotFound"];
1209
+ 409: components["responses"]["Conflict"];
1011
1210
  };
1012
1211
  };
1013
1212
  addFindingNote: {
@@ -1016,6 +1215,7 @@ interface operations {
1016
1215
  header?: never;
1017
1216
  path: {
1018
1217
  projectId: components["parameters"]["ProjectId"];
1218
+ /** @description The finding id, or the `ZK-` label shown on the finding page (the id's last eight characters, prefix optional). A label that matches more than one finding in the project is refused with `409 conflict`; use the full id. */
1019
1219
  findingId: components["parameters"]["FindingId"];
1020
1220
  };
1021
1221
  cookie?: never;
@@ -1042,6 +1242,7 @@ interface operations {
1042
1242
  401: components["responses"]["Unauthorized"];
1043
1243
  403: components["responses"]["Forbidden"];
1044
1244
  404: components["responses"]["NotFound"];
1245
+ 409: components["responses"]["Conflict"];
1045
1246
  };
1046
1247
  };
1047
1248
  pinFindingNote: {
@@ -1050,6 +1251,7 @@ interface operations {
1050
1251
  header?: never;
1051
1252
  path: {
1052
1253
  projectId: components["parameters"]["ProjectId"];
1254
+ /** @description The finding id, or the `ZK-` label shown on the finding page (the id's last eight characters, prefix optional). A label that matches more than one finding in the project is refused with `409 conflict`; use the full id. */
1053
1255
  findingId: components["parameters"]["FindingId"];
1054
1256
  noteId: string;
1055
1257
  };
@@ -1077,6 +1279,7 @@ interface operations {
1077
1279
  401: components["responses"]["Unauthorized"];
1078
1280
  403: components["responses"]["Forbidden"];
1079
1281
  404: components["responses"]["NotFound"];
1282
+ 409: components["responses"]["Conflict"];
1080
1283
  };
1081
1284
  };
1082
1285
  updateFindingSeverity: {
@@ -1085,6 +1288,7 @@ interface operations {
1085
1288
  header?: never;
1086
1289
  path: {
1087
1290
  projectId: components["parameters"]["ProjectId"];
1291
+ /** @description The finding id, or the `ZK-` label shown on the finding page (the id's last eight characters, prefix optional). A label that matches more than one finding in the project is refused with `409 conflict`; use the full id. */
1088
1292
  findingId: components["parameters"]["FindingId"];
1089
1293
  };
1090
1294
  cookie?: never;
@@ -1111,6 +1315,7 @@ interface operations {
1111
1315
  401: components["responses"]["Unauthorized"];
1112
1316
  403: components["responses"]["Forbidden"];
1113
1317
  404: components["responses"]["NotFound"];
1318
+ 409: components["responses"]["Conflict"];
1114
1319
  };
1115
1320
  };
1116
1321
  updateFindingResolution: {
@@ -1119,6 +1324,7 @@ interface operations {
1119
1324
  header?: never;
1120
1325
  path: {
1121
1326
  projectId: components["parameters"]["ProjectId"];
1327
+ /** @description The finding id, or the `ZK-` label shown on the finding page (the id's last eight characters, prefix optional). A label that matches more than one finding in the project is refused with `409 conflict`; use the full id. */
1122
1328
  findingId: components["parameters"]["FindingId"];
1123
1329
  };
1124
1330
  cookie?: never;
@@ -1146,6 +1352,7 @@ interface operations {
1146
1352
  401: components["responses"]["Unauthorized"];
1147
1353
  403: components["responses"]["Forbidden"];
1148
1354
  404: components["responses"]["NotFound"];
1355
+ 409: components["responses"]["Conflict"];
1149
1356
  };
1150
1357
  };
1151
1358
  publishFinding: {
@@ -1154,6 +1361,7 @@ interface operations {
1154
1361
  header?: never;
1155
1362
  path: {
1156
1363
  projectId: components["parameters"]["ProjectId"];
1364
+ /** @description The finding id, or the `ZK-` label shown on the finding page (the id's last eight characters, prefix optional). A label that matches more than one finding in the project is refused with `409 conflict`; use the full id. */
1157
1365
  findingId: components["parameters"]["FindingId"];
1158
1366
  };
1159
1367
  cookie?: never;
@@ -1177,6 +1385,7 @@ interface operations {
1177
1385
  401: components["responses"]["Unauthorized"];
1178
1386
  403: components["responses"]["Forbidden"];
1179
1387
  404: components["responses"]["NotFound"];
1388
+ 409: components["responses"]["Conflict"];
1180
1389
  };
1181
1390
  };
1182
1391
  publishScan: {
@@ -1368,6 +1577,9 @@ type TriageStatus = Schemas["TriageStatus"];
1368
1577
  type ScanStatus = Schemas["ScanStatus"];
1369
1578
  type PublishArtifactResult = Schemas["PublishArtifactResult"];
1370
1579
  type RepositoryGuidance = Schemas["RepositoryGuidance"];
1580
+ type AuditArea = Schemas["AuditArea"];
1581
+ type AuditAreaList = Schemas["AuditAreaList"];
1582
+ type DeleteAuditAreaResult = Schemas["DeleteAuditAreaResult"];
1371
1583
  type SetGuidanceResult = Schemas["SetGuidanceResult"];
1372
1584
  type BillingBalance = Schemas["BillingBalance"];
1373
1585
  type BillingUsage = Schemas["BillingUsage"];
@@ -1398,6 +1610,16 @@ type ZkaoClientOptions = {
1398
1610
  /** Terminal scan statuses: polling can stop once a scan reaches one of these. */
1399
1611
  declare const TERMINAL_SCAN_STATUSES: readonly ScanStatus[];
1400
1612
  declare function isTerminalScanStatus(status: ScanStatus): boolean;
1613
+ type WaitForRepositoryReadyOptions = {
1614
+ /** First poll delay, in ms (default 5000). Grows with backoff up to maxIntervalMs. */
1615
+ intervalMs?: number;
1616
+ /** Cap on the poll delay, in ms (default 30000). */
1617
+ maxIntervalMs?: number;
1618
+ /** Give up after this long, in ms (default 30m). Throws on timeout. */
1619
+ timeoutMs?: number;
1620
+ /** Abort the wait (e.g. on Ctrl-C). Rejects with the abort reason. */
1621
+ signal?: AbortSignal;
1622
+ };
1401
1623
  type WaitForScanOptions = {
1402
1624
  /** First poll delay, in ms (default 5000). Grows with backoff up to maxIntervalMs. */
1403
1625
  intervalMs?: number;
@@ -1426,6 +1648,15 @@ declare class ZkaoClient {
1426
1648
  constructor(options: ZkaoClientOptions);
1427
1649
  private get path();
1428
1650
  listRepositories(): Promise<Repository[]>;
1651
+ /**
1652
+ * Block until a repository is ready to scan, then return it.
1653
+ *
1654
+ * A repository added moments ago is still being analyzed, and launching a
1655
+ * scan against it fails with `repository_initializing`. Call this between
1656
+ * adding a repository and launching its first scan rather than retrying the
1657
+ * launch and swallowing the error.
1658
+ */
1659
+ waitForRepositoryReady(repositoryId: string, opts?: WaitForRepositoryReadyOptions): Promise<Repository>;
1429
1660
  /** Read a repository's configured guidance (requires the `read` scope). */
1430
1661
  getRepositoryGuidance(repositoryId: string): Promise<RepositoryGuidance>;
1431
1662
  /**
@@ -1438,6 +1669,30 @@ declare class ZkaoClient {
1438
1669
  setRepositoryGuidance(repositoryId: string, content: string | null, opts?: {
1439
1670
  expectedContent?: string | null;
1440
1671
  }): Promise<SetGuidanceResult>;
1672
+ /**
1673
+ * List a repository's audit areas (requires the `read` scope).
1674
+ *
1675
+ * An area is a named subsystem a scan can be scoped to: pass its `key` in
1676
+ * `auditAreaKeys` when launching. Sizes come from one branch's map, so an
1677
+ * area that map no longer names is returned without one. Defaults to the
1678
+ * repository's default branch.
1679
+ */
1680
+ listAuditAreas(repositoryId: string, opts?: {
1681
+ branch?: string;
1682
+ }): Promise<AuditAreaList>;
1683
+ /**
1684
+ * Add a custom audit area (requires the `guidance:write` scope).
1685
+ *
1686
+ * An area is a name and a description, never a file list: a scan resolves it
1687
+ * against the code at the commit it runs on. The returned `key` is derived
1688
+ * from the name.
1689
+ */
1690
+ createAuditArea(repositoryId: string, name: string, description?: string | null): Promise<AuditArea>;
1691
+ /**
1692
+ * Delete a custom audit area (requires the `guidance:write` scope). An area
1693
+ * a scan's map named is refused with a `409`.
1694
+ */
1695
+ deleteAuditArea(repositoryId: string, areaKey: string): Promise<DeleteAuditAreaResult>;
1441
1696
  listScans(opts?: {
1442
1697
  page?: number;
1443
1698
  limit?: number;
@@ -1530,4 +1785,4 @@ declare class ZkaoClient {
1530
1785
  }): Promise<UsageMonthSummary[]>;
1531
1786
  }
1532
1787
 
1533
- export { type BillingBalance, type BillingSummary, type BillingUsage, type CancelScanResult, type ChangeNote, DEFAULT_BASE_URL, type Finding, type FindingDetail, type FindingNote, type LaunchScanRequest, type LaunchScanResult, type OptionalFlow, type Paginated, type PublishArtifactResult, type Repository, type RepositoryGuidance, type ResolutionReason, type ResolutionStatus, type Scan, type ScanDetail, type ScanPreset, type ScanStatus, type SetGuidanceResult, type Severity, TERMINAL_SCAN_STATUSES, type TriageStatus, type UsageEvent, type UsageEventType, type UsageMonthSummary, type WaitForScanOptions, ZkaoApiError, ZkaoClient, type ZkaoClientOptions, type components, isTerminalScanStatus, normalizeBaseUrl, type paths, resolveBaseUrlFromEnv };
1788
+ export { type BillingBalance, type BillingSummary, type BillingUsage, type CancelScanResult, type ChangeNote, DEFAULT_BASE_URL, type Finding, type FindingDetail, type FindingNote, type LaunchScanRequest, type LaunchScanResult, type OptionalFlow, type Paginated, type PublishArtifactResult, type Repository, type RepositoryGuidance, type ResolutionReason, type ResolutionStatus, type Scan, type ScanDetail, type ScanPreset, type ScanStatus, type SetGuidanceResult, type Severity, TERMINAL_SCAN_STATUSES, type TriageStatus, type UsageEvent, type UsageEventType, type UsageMonthSummary, type WaitForRepositoryReadyOptions, type WaitForScanOptions, ZkaoApiError, ZkaoClient, type ZkaoClientOptions, type components, isTerminalScanStatus, normalizeBaseUrl, type paths, resolveBaseUrlFromEnv };
package/dist/index.js CHANGED
@@ -120,6 +120,55 @@ var ZkaoClient = class {
120
120
  });
121
121
  return unwrap(res).repositories;
122
122
  }
123
+ /**
124
+ * Block until a repository is ready to scan, then return it.
125
+ *
126
+ * A repository added moments ago is still being analyzed, and launching a
127
+ * scan against it fails with `repository_initializing`. Call this between
128
+ * adding a repository and launching its first scan rather than retrying the
129
+ * launch and swallowing the error.
130
+ */
131
+ async waitForRepositoryReady(repositoryId, opts = {}) {
132
+ const intervalMs = opts.intervalMs ?? 5e3;
133
+ const maxIntervalMs = opts.maxIntervalMs ?? 3e4;
134
+ const timeoutMs = opts.timeoutMs ?? 30 * 60 * 1e3;
135
+ const deadline = Date.now() + timeoutMs;
136
+ for (let attempt = 0; ; attempt++) {
137
+ throwIfAborted(opts.signal);
138
+ const res = await this.http.GET("/projects/{projectId}/repositories", {
139
+ params: { path: this.path },
140
+ signal: opts.signal
141
+ });
142
+ const retryAfterMs = parseRetryAfterMs(
143
+ res.response.headers.get("retry-after")
144
+ );
145
+ if (res.response.status !== 429) {
146
+ const repository = unwrap(res).repositories.find(
147
+ (candidate) => candidate.id === repositoryId
148
+ );
149
+ if (!repository) {
150
+ throw new Error(
151
+ `Repository ${repositoryId} is not in this project (or this token's allowlist)`
152
+ );
153
+ }
154
+ if (repository.readiness === "ready") {
155
+ return repository;
156
+ }
157
+ }
158
+ const backoffMs = Math.min(
159
+ maxIntervalMs,
160
+ Math.round(intervalMs * 1.5 ** attempt)
161
+ );
162
+ const baseMs = retryAfterMs ?? backoffMs;
163
+ const delayMs = baseMs + Math.round(Math.random() * baseMs * 0.2);
164
+ if (Date.now() + delayMs > deadline) {
165
+ throw new Error(
166
+ `Timed out waiting for repository ${repositoryId} to be ready after ${timeoutMs}ms`
167
+ );
168
+ }
169
+ await sleep(delayMs, opts.signal);
170
+ }
171
+ }
123
172
  /** Read a repository's configured guidance (requires the `read` scope). */
124
173
  async getRepositoryGuidance(repositoryId) {
125
174
  const res = await this.http.GET(
@@ -145,6 +194,55 @@ var ZkaoClient = class {
145
194
  );
146
195
  return unwrap(res);
147
196
  }
197
+ // --- Audit areas --------------------------------------------------------
198
+ /**
199
+ * List a repository's audit areas (requires the `read` scope).
200
+ *
201
+ * An area is a named subsystem a scan can be scoped to: pass its `key` in
202
+ * `auditAreaKeys` when launching. Sizes come from one branch's map, so an
203
+ * area that map no longer names is returned without one. Defaults to the
204
+ * repository's default branch.
205
+ */
206
+ async listAuditAreas(repositoryId, opts = {}) {
207
+ const res = await this.http.GET(
208
+ "/projects/{projectId}/repositories/{repositoryId}/audit-areas",
209
+ {
210
+ params: {
211
+ path: { ...this.path, repositoryId },
212
+ query: opts.branch === void 0 ? {} : { branch: opts.branch }
213
+ }
214
+ }
215
+ );
216
+ return unwrap(res);
217
+ }
218
+ /**
219
+ * Add a custom audit area (requires the `guidance:write` scope).
220
+ *
221
+ * An area is a name and a description, never a file list: a scan resolves it
222
+ * against the code at the commit it runs on. The returned `key` is derived
223
+ * from the name.
224
+ */
225
+ async createAuditArea(repositoryId, name, description) {
226
+ const res = await this.http.POST(
227
+ "/projects/{projectId}/repositories/{repositoryId}/audit-areas",
228
+ {
229
+ params: { path: { ...this.path, repositoryId } },
230
+ body: { name, description: description ?? null }
231
+ }
232
+ );
233
+ return unwrap(res).area;
234
+ }
235
+ /**
236
+ * Delete a custom audit area (requires the `guidance:write` scope). An area
237
+ * a scan's map named is refused with a `409`.
238
+ */
239
+ async deleteAuditArea(repositoryId, areaKey) {
240
+ const res = await this.http.DELETE(
241
+ "/projects/{projectId}/repositories/{repositoryId}/audit-areas/{areaKey}",
242
+ { params: { path: { ...this.path, repositoryId, areaKey } } }
243
+ );
244
+ return unwrap(res);
245
+ }
148
246
  // --- Scans --------------------------------------------------------------
149
247
  async listScans(opts = {}) {
150
248
  const res = await this.http.GET("/projects/{projectId}/scans", {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zksecurity/zkao-sdk",
3
- "version": "0.2.0",
3
+ "version": "0.5.1",
4
4
  "description": "TypeScript SDK for the zkao Project API",
5
5
  "license": "MIT",
6
6
  "author": "zkSecurity",
@@ -29,7 +29,11 @@
29
29
  "import": "./dist/index.js"
30
30
  }
31
31
  },
32
- "files": ["dist", "LICENSE", "README.md"],
32
+ "files": [
33
+ "dist",
34
+ "LICENSE",
35
+ "README.md"
36
+ ],
33
37
  "engines": {
34
38
  "node": ">=18"
35
39
  },