@abyss-project/tools 2.2.0 → 2.3.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 (64) hide show
  1. package/dist/api/index.d.ts +2 -0
  2. package/dist/api/index.js +2 -0
  3. package/dist/api/index.js.map +1 -1
  4. package/dist/api/probe-usage.api.d.ts +2 -0
  5. package/dist/api/probe-usage.api.js +24 -0
  6. package/dist/api/probe-usage.api.js.map +1 -0
  7. package/dist/api/probe-usage.application.api.d.ts +2 -0
  8. package/dist/api/probe-usage.application.api.js +12 -0
  9. package/dist/api/probe-usage.application.api.js.map +1 -0
  10. package/dist/api/probe-webhook.api.d.ts +3 -1
  11. package/dist/api/probe-webhook.api.js +8 -1
  12. package/dist/api/probe-webhook.api.js.map +1 -1
  13. package/dist/api/probe-webhook.application.api.d.ts +3 -1
  14. package/dist/api/probe-webhook.application.api.js +14 -1
  15. package/dist/api/probe-webhook.application.api.js.map +1 -1
  16. package/dist/index.d.ts +12 -0
  17. package/dist/index.js +12 -0
  18. package/dist/index.js.map +1 -1
  19. package/dist/types/interface/api/requests/index.d.ts +1 -0
  20. package/dist/types/interface/api/requests/index.js +1 -0
  21. package/dist/types/interface/api/requests/index.js.map +1 -1
  22. package/dist/types/interface/api/requests/probe-usage.request.d.ts +3 -0
  23. package/dist/types/interface/api/requests/probe-usage.request.js +3 -0
  24. package/dist/types/interface/api/requests/probe-usage.request.js.map +1 -0
  25. package/dist/types/interface/api/requests/probe-webhook.request.d.ts +8 -0
  26. package/dist/types/interface/api/responses/index.d.ts +1 -0
  27. package/dist/types/interface/api/responses/index.js +1 -0
  28. package/dist/types/interface/api/responses/index.js.map +1 -1
  29. package/dist/types/interface/api/responses/probe-scan.response.d.ts +1 -0
  30. package/dist/types/interface/api/responses/probe-usage.response.d.ts +7 -0
  31. package/dist/types/interface/api/responses/probe-usage.response.js +3 -0
  32. package/dist/types/interface/api/responses/probe-usage.response.js.map +1 -0
  33. package/dist/types/interface/api/responses/probe-webhook.response.d.ts +4 -1
  34. package/dist/types/interface/models/probe-scan.dto.d.ts +8 -0
  35. package/dist/types/interface/models/probe-webhook-delivery.dto.d.ts +12 -0
  36. package/dist/types/interface/models/probe-webhook.dto.d.ts +2 -3
  37. package/dist/types/tests/probe.dto.test.js +40 -1
  38. package/dist/types/tests/probe.dto.test.js.map +1 -1
  39. package/dist/utils/index.d.ts +1 -0
  40. package/dist/utils/index.js +1 -0
  41. package/dist/utils/index.js.map +1 -1
  42. package/dist/utils/probe-scan-cost.utils.d.ts +5 -0
  43. package/dist/utils/probe-scan-cost.utils.js +21 -0
  44. package/dist/utils/probe-scan-cost.utils.js.map +1 -0
  45. package/package.json +1 -1
  46. package/src/api/index.ts +2 -0
  47. package/src/api/probe-usage.api.ts +47 -0
  48. package/src/api/probe-usage.application.api.ts +25 -0
  49. package/src/api/probe-webhook.api.ts +43 -5
  50. package/src/api/probe-webhook.application.api.ts +58 -4
  51. package/src/index.ts +28 -0
  52. package/src/types/interface/api/requests/index.ts +1 -0
  53. package/src/types/interface/api/requests/probe-usage.request.ts +7 -0
  54. package/src/types/interface/api/requests/probe-webhook.request.ts +23 -2
  55. package/src/types/interface/api/responses/index.ts +1 -0
  56. package/src/types/interface/api/responses/probe-scan.response.ts +7 -0
  57. package/src/types/interface/api/responses/probe-usage.response.ts +25 -0
  58. package/src/types/interface/api/responses/probe-webhook.response.ts +10 -3
  59. package/src/types/interface/models/probe-scan.dto.ts +27 -8
  60. package/src/types/interface/models/probe-webhook-delivery.dto.ts +47 -0
  61. package/src/types/interface/models/probe-webhook.dto.ts +25 -11
  62. package/src/types/tests/probe.dto.test.ts +73 -11
  63. package/src/utils/index.ts +1 -0
  64. package/src/utils/probe-scan-cost.utils.ts +101 -0
@@ -0,0 +1,21 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.estimateProbeScanCost = exports.PROBE_SCAN_COST_MODE_WEIGHT = exports.PROBE_SCAN_COST_K_MB = exports.PROBE_SCAN_COST_BASE = void 0;
4
+ const types_1 = require("../types");
5
+ exports.PROBE_SCAN_COST_BASE = 1;
6
+ exports.PROBE_SCAN_COST_K_MB = 1;
7
+ exports.PROBE_SCAN_COST_MODE_WEIGHT = {
8
+ [types_1.ProbeScanMode.ASYNC]: 1,
9
+ [types_1.ProbeScanMode.SYNC]: 3,
10
+ };
11
+ const ONE_MB = 1024 * 1024;
12
+ const estimateProbeScanCost = (sizeOctet, mode) => {
13
+ if (!Number.isFinite(sizeOctet) || sizeOctet <= 0) {
14
+ return 0;
15
+ }
16
+ const megabytes = Math.ceil(sizeOctet / ONE_MB);
17
+ const weight = exports.PROBE_SCAN_COST_MODE_WEIGHT[mode] ?? exports.PROBE_SCAN_COST_MODE_WEIGHT[types_1.ProbeScanMode.ASYNC];
18
+ return Math.round((exports.PROBE_SCAN_COST_BASE + exports.PROBE_SCAN_COST_K_MB * megabytes) * weight);
19
+ };
20
+ exports.estimateProbeScanCost = estimateProbeScanCost;
21
+ //# sourceMappingURL=probe-scan-cost.utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"probe-scan-cost.utils.js","sourceRoot":"","sources":["../../src/utils/probe-scan-cost.utils.ts"],"names":[],"mappings":";;;AAuCA,oCAAyC;AAG5B,QAAA,oBAAoB,GAAG,CAAC,CAAC;AAGzB,QAAA,oBAAoB,GAAG,CAAC,CAAC;AAUzB,QAAA,2BAA2B,GAAkC;IACxE,CAAC,qBAAa,CAAC,KAAK,CAAC,EAAE,CAAC;IACxB,CAAC,qBAAa,CAAC,IAAI,CAAC,EAAE,CAAC;CACxB,CAAC;AAGF,MAAM,MAAM,GAAG,IAAI,GAAG,IAAI,CAAC;AA4BpB,MAAM,qBAAqB,GAAG,CACnC,SAAiB,EACjB,IAAsC,EAC9B,EAAE;IACV,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,SAAS,CAAC,IAAI,SAAS,IAAI,CAAC,EAAE,CAAC;QAClD,OAAO,CAAC,CAAC;IACX,CAAC;IACD,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,GAAG,MAAM,CAAC,CAAC;IAChD,MAAM,MAAM,GACV,mCAA2B,CAAC,IAAI,CAAC,IAAI,mCAA2B,CAAC,qBAAa,CAAC,KAAK,CAAC,CAAC;IACxF,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC,4BAAoB,GAAG,4BAAoB,GAAG,SAAS,CAAC,GAAG,MAAM,CAAC,CAAC;AACxF,CAAC,CAAC;AAXW,QAAA,qBAAqB,yBAWhC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@abyss-project/tools",
3
- "version": "2.2.0",
3
+ "version": "2.3.0",
4
4
  "description": "Core package to interact with Abyss-Project Tools",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
package/src/api/index.ts CHANGED
@@ -60,3 +60,5 @@ export * from './probe-scan.api';
60
60
  export * from './probe-webhook.api';
61
61
  export * from './probe-scan.application.api';
62
62
  export * from './probe-webhook.application.api';
63
+ export * from './probe-usage.api';
64
+ export * from './probe-usage.application.api';
@@ -0,0 +1,47 @@
1
+ // ──────────────────────────────────────────────
2
+ // AbyssProbe usage API — WS5-3 (AbyssProbe UX rework). `GET /usage`: current
3
+ // ACCOUNT_AV_SCAN_UNITS ("tokens") used/limit/remaining for the caller's
4
+ // scope, via the same billing check the quota gate itself already uses (see
5
+ // apps/api/src/utils/probe-scan-billing.util.ts#getProbeScanUsage). Mirrors
6
+ // apps/api/src/api/controllers/probe-usage.controller.ts EXACTLY. No
7
+ // `probe/` path prefix — `@Controller({ prefixPath: '/usage' })` mounts
8
+ // directly at `usage`.
9
+ //
10
+ // See probe-usage.application.api.ts for the application/API-key-auth
11
+ // counterpart (`/application/:applicationId/usage`, Phase 3a).
12
+ // ──────────────────────────────────────────────
13
+
14
+ import { AbyssToolsCore } from '..';
15
+ import type { IProbeUsageScopeQuery, IProbeUsageResponse } from '../types';
16
+
17
+ const DEFAULT_API_KEY_HEADER = 'abyss-api-key';
18
+
19
+ function authHeader(apiKey?: string): Record<string, string> {
20
+ return apiKey ? { [DEFAULT_API_KEY_HEADER]: apiKey } : {};
21
+ }
22
+
23
+ /** Renames `organizationId` to the wire's `organization_id` (see probe-scan.api.ts). */
24
+ function serializeProbeQuery<T extends object>(query: T): Record<string, unknown> {
25
+ const out: Record<string, unknown> = {};
26
+ for (const [key, value] of Object.entries(query as Record<string, unknown>)) {
27
+ if (value === undefined || value === null) continue;
28
+ const wireKey = key === 'organizationId' ? 'organization_id' : key;
29
+ out[wireKey] = value;
30
+ }
31
+ return out;
32
+ }
33
+
34
+ /**
35
+ * `GET /usage` — current ACCOUNT_AV_SCAN_UNITS consumption for the caller's
36
+ * scope (personal by default, `organizationId` for an organization scope).
37
+ */
38
+ export const getProbeUsage = async (
39
+ scope: IProbeUsageScopeQuery = {},
40
+ apiKey?: string,
41
+ ): Promise<IProbeUsageResponse> =>
42
+ (
43
+ await AbyssToolsCore.axios.get<IProbeUsageResponse>('usage', {
44
+ params: serializeProbeQuery(scope),
45
+ headers: authHeader(apiKey),
46
+ })
47
+ ).data;
@@ -0,0 +1,25 @@
1
+ // ──────────────────────────────────────────────
2
+ // AbyssProbe APPLICATION usage API — WS5-3 (AbyssProbe UX rework). `GET
3
+ // /application/:applicationId/usage`: current ACCOUNT_AV_SCAN_UNITS
4
+ // consumption for the calling application's resolved billing organization.
5
+ // Mirrors apps/api/src/api/controllers/probe-usage-application.controller.ts
6
+ // EXACTLY — the application counterpart of probe-usage.api.ts.
7
+ // ──────────────────────────────────────────────
8
+
9
+ import { AbyssToolsCore, API_KEY_APPLICATION_HEADER } from '..';
10
+ import type { IProbeUsageResponse } from '../types';
11
+
12
+ function authHeaderApplication(applicationApiKey?: string): Record<string, string> {
13
+ return applicationApiKey ? { [API_KEY_APPLICATION_HEADER]: applicationApiKey } : {};
14
+ }
15
+
16
+ /** `GET /application/:applicationId/usage` — current ACCOUNT_AV_SCAN_UNITS consumption for the calling application. */
17
+ export const getProbeUsageApplication = async (
18
+ applicationId: string,
19
+ applicationApiKey?: string,
20
+ ): Promise<IProbeUsageResponse> =>
21
+ (
22
+ await AbyssToolsCore.axios.get<IProbeUsageResponse>(`application/${applicationId}/usage`, {
23
+ headers: authHeaderApplication(applicationApiKey),
24
+ })
25
+ ).data;
@@ -1,10 +1,9 @@
1
1
  // ──────────────────────────────────────────────
2
2
  // AbyssProbe webhook subscription API — CRUD over registered `probe_webhooks`
3
- // (create / list / delete only — no update, no reset-secret: mirrors
4
- // apps/api/src/api/controllers/probe-webhook.controller.ts's deliberately
5
- // small surface EXACTLY, see that file's header comment). No `probe/` path
6
- // prefix — `@Controller({ prefixPath: '/webhook' })` mounts directly at
7
- // `webhook`.
3
+ // (create / list / delete / update, + paginated delivery history — mirrors
4
+ // apps/api/src/api/controllers/probe-webhook.controller.ts EXACTLY, see that
5
+ // file's header comment). No `probe/` path prefix — `@Controller({
6
+ // prefixPath: '/webhook' })` mounts directly at `webhook`.
8
7
  //
9
8
  // See probe-webhook.application.api.ts for the application/API-key-auth
10
9
  // counterpart of this client (`/application/:applicationId/webhook...`,
@@ -15,10 +14,14 @@
15
14
  import { AbyssToolsCore } from '..';
16
15
  import type {
17
16
  ICreateProbeWebhookBody,
17
+ IUpdateProbeWebhookBody,
18
18
  IProbeWebhookScopeQuery,
19
+ IListProbeWebhookDeliveriesQuery,
19
20
  ICreateProbeWebhookResponse,
20
21
  IListProbeWebhookResponse,
21
22
  IDeleteProbeWebhookResponse,
23
+ IUpdateProbeWebhookResponse,
24
+ IListProbeWebhookDeliveriesResponse,
22
25
  } from '../types';
23
26
 
24
27
  const DEFAULT_API_KEY_HEADER = 'abyss-api-key';
@@ -79,3 +82,38 @@ export const deleteProbeWebhook = async (
79
82
  headers: authHeader(apiKey),
80
83
  })
81
84
  ).data;
85
+
86
+ /**
87
+ * `PATCH /webhook/:webhookId` (WS5-5) — edit `callbackUrl` and/or `active`.
88
+ * At least one field is required. Scoped like {@link deleteProbeWebhook}
89
+ * (404, never 403, for a webhookId outside the resolved scope). The response
90
+ * includes `secret` — re-displayable now, see {@link IProbeWebhook}.
91
+ */
92
+ export const updateProbeWebhook = async (
93
+ webhookId: string,
94
+ body: IUpdateProbeWebhookBody,
95
+ scope: IProbeWebhookScopeQuery = {},
96
+ apiKey?: string,
97
+ ): Promise<IUpdateProbeWebhookResponse> =>
98
+ (
99
+ await AbyssToolsCore.axios.patch<IUpdateProbeWebhookResponse>(`webhook/${webhookId}`, body, {
100
+ params: serializeProbeQuery(scope),
101
+ headers: authHeader(apiKey),
102
+ })
103
+ ).data;
104
+
105
+ /**
106
+ * `GET /webhook/:webhookId/deliveries` (WS5-6) — paginated delivery-attempt
107
+ * history for one registered webhook. Scoped like {@link deleteProbeWebhook}.
108
+ */
109
+ export const listProbeWebhookDeliveries = async (
110
+ webhookId: string,
111
+ query: IListProbeWebhookDeliveriesQuery = {},
112
+ apiKey?: string,
113
+ ): Promise<IListProbeWebhookDeliveriesResponse> =>
114
+ (
115
+ await AbyssToolsCore.axios.get<IListProbeWebhookDeliveriesResponse>(
116
+ `webhook/${webhookId}/deliveries`,
117
+ { params: serializeProbeQuery(query), headers: authHeader(apiKey) },
118
+ )
119
+ ).data;
@@ -1,8 +1,8 @@
1
1
  // ──────────────────────────────────────────────
2
2
  // AbyssProbe APPLICATION webhook subscription API — CRUD over registered
3
- // `probe_webhooks` owned by a calling application (create / list / delete
4
- // only — no update, no reset-secret: same deliberately small surface as the
5
- // personal/org client, see probe-webhook.api.ts's header comment). Mirrors
3
+ // `probe_webhooks` owned by a calling application (create / list / delete /
4
+ // update, + paginated delivery history — same surface as the personal/org
5
+ // client, see probe-webhook.api.ts's header comment). Mirrors
6
6
  // apps/api/src/api/controllers/probe-webhook-application.controller.ts
7
7
  // EXACTLY — the application counterpart of probe-webhook.api.ts, for
8
8
  // inter-service callers (abyss-storage, external integrations)
@@ -10,7 +10,9 @@
10
10
  //
11
11
  // Reuses every request/response TYPE verbatim (`ICreateProbeWebhookBody`,
12
12
  // `ICreateProbeWebhookResponse`, `IListProbeWebhookResponse`,
13
- // `IDeleteProbeWebhookResponse`) — never redefined here. No scope
13
+ // `IDeleteProbeWebhookResponse`, `IUpdateProbeWebhookBody`,
14
+ // `IUpdateProbeWebhookResponse`, `IListProbeWebhookDeliveriesQuery`,
15
+ // `IListProbeWebhookDeliveriesResponse`) — never redefined here. No scope
14
16
  // argument/query on any of these calls (unlike the personal/org client's
15
17
  // `IProbeWebhookScopeQuery`) — an application caller's scope is entirely its
16
18
  // `:applicationId` URL segment, resolved server-side by
@@ -25,15 +27,29 @@
25
27
  import { AbyssToolsCore, API_KEY_APPLICATION_HEADER } from '..';
26
28
  import type {
27
29
  ICreateProbeWebhookBody,
30
+ IUpdateProbeWebhookBody,
31
+ IListProbeWebhookDeliveriesQuery,
28
32
  ICreateProbeWebhookResponse,
29
33
  IListProbeWebhookResponse,
30
34
  IDeleteProbeWebhookResponse,
35
+ IUpdateProbeWebhookResponse,
36
+ IListProbeWebhookDeliveriesResponse,
31
37
  } from '../types';
32
38
 
33
39
  function authHeaderApplication(applicationApiKey?: string): Record<string, string> {
34
40
  return applicationApiKey ? { [API_KEY_APPLICATION_HEADER]: applicationApiKey } : {};
35
41
  }
36
42
 
43
+ /** Renames nothing (no `organizationId` ever travels on an application call) — drops undefined/null and ISO-stringifies `Date`s, mirroring probe-scan.application.api.ts's own local helper. */
44
+ function serializeProbeQuery<T extends object>(query: T): Record<string, unknown> {
45
+ const out: Record<string, unknown> = {};
46
+ for (const [key, value] of Object.entries(query as Record<string, unknown>)) {
47
+ if (value === undefined || value === null) continue;
48
+ out[key] = value instanceof Date ? value.toISOString() : value;
49
+ }
50
+ return out;
51
+ }
52
+
37
53
  /**
38
54
  * `POST /application/:applicationId/webhook` — register a callbackUrl owned
39
55
  * by the calling application. The response's `secret` field (HMAC signing
@@ -85,3 +101,41 @@ export const deleteProbeWebhookApplication = async (
85
101
  { headers: authHeaderApplication(applicationApiKey) },
86
102
  )
87
103
  ).data;
104
+
105
+ /**
106
+ * `PATCH /application/:applicationId/webhook/:webhookId` (WS5-5) — edit
107
+ * `callbackUrl` and/or `active` on a webhook owned by the calling
108
+ * application. Cross-tenant guarded server-side (404, never 403). The
109
+ * response includes `secret` — re-displayable now, see {@link IProbeWebhook}.
110
+ */
111
+ export const updateProbeWebhookApplication = async (
112
+ applicationId: string,
113
+ webhookId: string,
114
+ body: IUpdateProbeWebhookBody,
115
+ applicationApiKey?: string,
116
+ ): Promise<IUpdateProbeWebhookResponse> =>
117
+ (
118
+ await AbyssToolsCore.axios.patch<IUpdateProbeWebhookResponse>(
119
+ `application/${applicationId}/webhook/${webhookId}`,
120
+ body,
121
+ { headers: authHeaderApplication(applicationApiKey) },
122
+ )
123
+ ).data;
124
+
125
+ /**
126
+ * `GET /application/:applicationId/webhook/:webhookId/deliveries` (WS5-6) —
127
+ * paginated delivery-attempt history for one webhook owned by the calling
128
+ * application. Cross-tenant guarded server-side (404, never 403).
129
+ */
130
+ export const listProbeWebhookDeliveriesApplication = async (
131
+ applicationId: string,
132
+ webhookId: string,
133
+ query: Omit<IListProbeWebhookDeliveriesQuery, 'organizationId'> = {},
134
+ applicationApiKey?: string,
135
+ ): Promise<IListProbeWebhookDeliveriesResponse> =>
136
+ (
137
+ await AbyssToolsCore.axios.get<IListProbeWebhookDeliveriesResponse>(
138
+ `application/${applicationId}/webhook/${webhookId}/deliveries`,
139
+ { params: serializeProbeQuery(query), headers: authHeaderApplication(applicationApiKey) },
140
+ )
141
+ ).data;
package/src/index.ts CHANGED
@@ -36,6 +36,8 @@ import * as probeScanApi from './api/probe-scan.api';
36
36
  import * as probeWebhookApi from './api/probe-webhook.api';
37
37
  import * as probeScanApplicationApi from './api/probe-scan.application.api';
38
38
  import * as probeWebhookApplicationApi from './api/probe-webhook.application.api';
39
+ import * as probeUsageApi from './api/probe-usage.api';
40
+ import * as probeUsageApplicationApi from './api/probe-usage.application.api';
39
41
 
40
42
  export * from './api';
41
43
  export * from './types';
@@ -347,10 +349,18 @@ type AbyssToolsCoreSDK = {
347
349
  getScan: typeof probeScanApi.getScan;
348
350
  /** `GET /scan` — paginated history/search. */
349
351
  listScans: typeof probeScanApi.listScans;
352
+ /** `GET /usage` — current ACCOUNT_AV_SCAN_UNITS used/limit/remaining for the caller's scope. */
353
+ usage: {
354
+ get: typeof probeUsageApi.getProbeUsage;
355
+ };
350
356
  webhook: {
351
357
  create: typeof probeWebhookApi.createProbeWebhook;
352
358
  list: typeof probeWebhookApi.listProbeWebhook;
353
359
  delete: typeof probeWebhookApi.deleteProbeWebhook;
360
+ /** `PATCH /webhook/:webhookId` — edit callbackUrl and/or active. */
361
+ update: typeof probeWebhookApi.updateProbeWebhook;
362
+ /** `GET /webhook/:webhookId/deliveries` — paginated delivery-attempt history. */
363
+ listDeliveries: typeof probeWebhookApi.listProbeWebhookDeliveries;
354
364
  };
355
365
  /**
356
366
  * Application/API-key-auth counterpart (Phase 3a) — for inter-service
@@ -371,10 +381,18 @@ type AbyssToolsCoreSDK = {
371
381
  getScan: typeof probeScanApplicationApi.getScanApplication;
372
382
  /** `GET /application/:applicationId/scan` — paginated history/search. */
373
383
  listScans: typeof probeScanApplicationApi.listScansApplication;
384
+ /** `GET /application/:applicationId/usage` — current ACCOUNT_AV_SCAN_UNITS used/limit/remaining for the calling application. */
385
+ usage: {
386
+ get: typeof probeUsageApplicationApi.getProbeUsageApplication;
387
+ };
374
388
  webhook: {
375
389
  create: typeof probeWebhookApplicationApi.createProbeWebhookApplication;
376
390
  list: typeof probeWebhookApplicationApi.listProbeWebhookApplication;
377
391
  delete: typeof probeWebhookApplicationApi.deleteProbeWebhookApplication;
392
+ /** `PATCH /application/:applicationId/webhook/:webhookId` — edit callbackUrl and/or active. */
393
+ update: typeof probeWebhookApplicationApi.updateProbeWebhookApplication;
394
+ /** `GET /application/:applicationId/webhook/:webhookId/deliveries` — paginated delivery-attempt history. */
395
+ listDeliveries: typeof probeWebhookApplicationApi.listProbeWebhookDeliveriesApplication;
378
396
  };
379
397
  };
380
398
  };
@@ -775,10 +793,15 @@ export class AbyssToolsCore {
775
793
  scanAsync: probeScanApi.scanAsync,
776
794
  getScan: probeScanApi.getScan,
777
795
  listScans: probeScanApi.listScans,
796
+ usage: {
797
+ get: probeUsageApi.getProbeUsage,
798
+ },
778
799
  webhook: {
779
800
  create: probeWebhookApi.createProbeWebhook,
780
801
  list: probeWebhookApi.listProbeWebhook,
781
802
  delete: probeWebhookApi.deleteProbeWebhook,
803
+ update: probeWebhookApi.updateProbeWebhook,
804
+ listDeliveries: probeWebhookApi.listProbeWebhookDeliveries,
782
805
  },
783
806
  application: {
784
807
  scan: probeScanApplicationApi.scanApplication,
@@ -786,10 +809,15 @@ export class AbyssToolsCore {
786
809
  scanAsync: probeScanApplicationApi.scanAsyncApplication,
787
810
  getScan: probeScanApplicationApi.getScanApplication,
788
811
  listScans: probeScanApplicationApi.listScansApplication,
812
+ usage: {
813
+ get: probeUsageApplicationApi.getProbeUsageApplication,
814
+ },
789
815
  webhook: {
790
816
  create: probeWebhookApplicationApi.createProbeWebhookApplication,
791
817
  list: probeWebhookApplicationApi.listProbeWebhookApplication,
792
818
  delete: probeWebhookApplicationApi.deleteProbeWebhookApplication,
819
+ update: probeWebhookApplicationApi.updateProbeWebhookApplication,
820
+ listDeliveries: probeWebhookApplicationApi.listProbeWebhookDeliveriesApplication,
793
821
  },
794
822
  },
795
823
  },
@@ -29,3 +29,4 @@ export * from './transfer.request';
29
29
  export * from './extraction.request';
30
30
  export * from './probe-scan.request';
31
31
  export * from './probe-webhook.request';
32
+ export * from './probe-usage.request';
@@ -0,0 +1,7 @@
1
+ // WS5-3 (AbyssProbe UX rework) — `GET /usage` / `GET /application/:applicationId/usage`.
2
+ // Mirrors apps/api/src/api/validators/probe-usage.validator.ts's Joi schema.
3
+
4
+ /** `GET /usage` query — personal scope (default) or an organization. Not needed for the application variant (its scope is the `:applicationId` URL segment). */
5
+ export interface IProbeUsageScopeQuery {
6
+ organizationId?: string;
7
+ }
@@ -1,7 +1,8 @@
1
1
  // Mirrors apps/api/src/api/controllers/probe-webhook.controller.ts's
2
- // ScopeQuery/CreateBody and probe-webhook.validator.ts's Joi schemas.
2
+ // ScopeQuery/CreateBody/UpdateBody and probe-webhook.validator.ts's Joi
3
+ // schemas.
3
4
 
4
- /** Shared by create/list/delete — personal scope (default) or an organization. */
5
+ /** Shared by create/list/delete/update — personal scope (default) or an organization. */
5
6
  export interface IProbeWebhookScopeQuery {
6
7
  organizationId?: string;
7
8
  }
@@ -10,3 +11,23 @@ export interface ICreateProbeWebhookBody {
10
11
  /** HTTPS endpoint the verdict is POSTed to. Required, `https://` only. */
11
12
  callbackUrl: string;
12
13
  }
14
+
15
+ /**
16
+ * `PATCH /webhook/:webhookId` body (WS5-5) — at least one field required
17
+ * (mirrors probe-webhook.validator.ts#updateBody's `.min(1)`); omitted
18
+ * fields are left unchanged.
19
+ */
20
+ export interface IUpdateProbeWebhookBody {
21
+ /** HTTPS endpoint the verdict is POSTed to, `https://` only. */
22
+ callbackUrl?: string;
23
+ /** Disabled webhooks are skipped by dispatch (soft-off, not deleted). */
24
+ active?: boolean;
25
+ }
26
+
27
+ /** `GET /webhook/:webhookId/deliveries` query (WS5-6) — paginated. */
28
+ export interface IListProbeWebhookDeliveriesQuery extends IProbeWebhookScopeQuery {
29
+ /** @default 1 */
30
+ page?: number;
31
+ /** @default 20 */
32
+ limit?: number;
33
+ }
@@ -33,3 +33,4 @@ export * from './extraction.response';
33
33
  export * from './monitor.response';
34
34
  export * from './probe-scan.response';
35
35
  export * from './probe-webhook.response';
36
+ export * from './probe-usage.response';
@@ -12,6 +12,13 @@ export interface IScanSyncData {
12
12
  scanId: string;
13
13
  verdict: ProbeScanVerdict;
14
14
  viruses: string[];
15
+ /**
16
+ * `ACCOUNT_AV_SCAN_UNITS` this scan consumed (WS5-1/2 — see
17
+ * `estimateProbeScanCost`). Always a number here (never null): a SYNC
18
+ * response is only ever sent once the scan has reached a verdict, at
19
+ * which point the cost is always known and already billed.
20
+ */
21
+ costUnits: number;
15
22
  }
16
23
  export type IScanSyncResponse = IResponse<IScanSyncData>;
17
24
 
@@ -0,0 +1,25 @@
1
+ import { IResponse } from '../type-message/response';
2
+
3
+ // WS5-3 (AbyssProbe UX rework) — mirrors
4
+ // apps/api/src/api/controllers/probe-usage.controller.ts's local response
5
+ // shape EXACTLY. Current ACCOUNT_AV_SCAN_UNITS ("tokens") consumption for the
6
+ // caller's scope, via the same billing check the quota gate itself uses.
7
+
8
+ export interface IProbeUsageData {
9
+ /** Units consumed so far in the current billing period. */
10
+ used: number;
11
+ /**
12
+ * Plan cap. `-1` is this codebase's established "unlimited" sentinel (see
13
+ * `utils/billing-emit.util.ts#UNLIMITED_EXPIRY_DAYS` server-side) — an
14
+ * unmetered plan tier reports `-1` here rather than `Infinity`/`null`, so
15
+ * treat a negative value as "no cap", never a literal negative quota.
16
+ */
17
+ limit: number;
18
+ /**
19
+ * `limit - used`, floored at 0 — except it mirrors `limit`'s own `-1`
20
+ * sentinel unchanged when `limit` is unlimited (never a finite number in
21
+ * that case).
22
+ */
23
+ remaining: number;
24
+ }
25
+ export type IProbeUsageResponse = IResponse<IProbeUsageData>;
@@ -1,13 +1,14 @@
1
1
  import { IResponse } from '../type-message/response';
2
- import { IProbeWebhook, IProbeWebhookCreated } from '../../models';
2
+ import { BasePaginate } from '../type-message/base-paginate';
3
+ import { IProbeWebhook, IProbeWebhookCreated, IProbeWebhookDelivery } from '../../models';
3
4
 
4
5
  // Mirrors apps/api/src/api/controllers/probe-webhook.controller.ts's local
5
6
  // response shapes EXACTLY.
6
7
 
7
- /** `POST /webhook` — HTTP 201. The only response that ever carries `secret`. */
8
+ /** `POST /webhook` — HTTP 201. `secret` is re-displayable (WS5-7) — also returned by GET/PATCH, not just here. */
8
9
  export type ICreateProbeWebhookResponse = IResponse<IProbeWebhookCreated>;
9
10
 
10
- /** `GET /webhook` — HTTP 200. A flat scope-filtered list, NOT paginated. */
11
+ /** `GET /webhook` — HTTP 200. A flat scope-filtered list, NOT paginated. `secret` included per row (WS5-7). */
11
12
  export interface IListProbeWebhookData {
12
13
  webhooks: IProbeWebhook[];
13
14
  }
@@ -15,3 +16,9 @@ export type IListProbeWebhookResponse = IResponse<IListProbeWebhookData>;
15
16
 
16
17
  /** `DELETE /webhook/:webhookId` — HTTP 200. */
17
18
  export type IDeleteProbeWebhookResponse = IResponse<{ ok: true }>;
19
+
20
+ /** `PATCH /webhook/:webhookId` — HTTP 200 (WS5-5). `secret` included, same as every other webhook read. */
21
+ export type IUpdateProbeWebhookResponse = IResponse<IProbeWebhook>;
22
+
23
+ /** `GET /webhook/:webhookId/deliveries` — HTTP 200, paginated (WS5-6). */
24
+ export type IListProbeWebhookDeliveriesResponse = IResponse<BasePaginate<IProbeWebhookDelivery>>;
@@ -8,9 +8,13 @@ import { ProbeScanMode, ProbeScanStatus, ProbeScanVerdict } from '../../enum';
8
8
 
9
9
  /**
10
10
  * `GET /scan/:scanId` shape (poll the queued -> scanning -> done/error
11
- * progression). Deliberately minimal: no fileName/fileSize/mimeType/mode/
12
- * timestamps, no internal ownership columns (userId/organizationId/
13
- * callbackUrl) — a caller polling by id already knows what it submitted.
11
+ * progression). WS5-4 (AbyssProbe UX rework) widened this to full metadata
12
+ * parity with {@link IProbeScanHistoryItem} (fileName/fileSize/mimeType/
13
+ * mode/startedAt/finishedAt/costUnits) — the scanner UI's "Détails" section
14
+ * needs the same info whether it got there via the list or via a direct
15
+ * poll — still never echoing internal ownership columns (userId,
16
+ * organizationId, callbackUrl) — a caller polling by id already knows what
17
+ * it submitted.
14
18
  */
15
19
  export interface IProbeScanDetail {
16
20
  scanId: string;
@@ -21,14 +25,27 @@ export interface IProbeScanDetail {
21
25
  viruses: string[] | null;
22
26
  /** Free-form caller reference echoed back verbatim (see IProbeScanHistoryItem#context). */
23
27
  context: Record<string, unknown> | null;
28
+ fileName: string;
29
+ fileSize: number;
30
+ mimeType: string;
31
+ mode: ProbeScanMode;
32
+ /** Set the moment the engine call starts (SYNC: row-creation time; ASYNC: when a worker picks the job off the queue). */
33
+ startedAt: Date;
34
+ /** Set once the engine call returns. null while status is not DONE. */
35
+ finishedAt: Date | null;
36
+ /**
37
+ * `ACCOUNT_AV_SCAN_UNITS` this scan consumed (see `estimateProbeScanCost`
38
+ * — this package's own shared pure cost model). null until `status` is
39
+ * DONE; stays null forever for a scan that errors out (no verdict was ever
40
+ * reached, so nothing was ever billed).
41
+ */
42
+ costUnits: number | null;
24
43
  }
25
44
 
26
45
  /**
27
- * One row of `GET /scan`'s paginated history/search list. Richer than
28
- * {@link IProbeScanDetail} (adds fileName/fileSize/mimeType/mode/timestamps)
29
- * but still never echoes internal ownership columns (userId, organizationId,
30
- * callbackUrl) — a caller already knows their own scope from the request
31
- * they made.
46
+ * One row of `GET /scan`'s paginated history/search list. Never echoes
47
+ * internal ownership columns (userId, organizationId, callbackUrl) — a
48
+ * caller already knows their own scope from the request they made.
32
49
  */
33
50
  export interface IProbeScanHistoryItem {
34
51
  scanId: string;
@@ -44,5 +61,7 @@ export interface IProbeScanHistoryItem {
44
61
  startedAt: Date;
45
62
  /** Set once the engine call returns. null while status is not DONE. */
46
63
  finishedAt: Date | null;
64
+ /** See {@link IProbeScanDetail#costUnits}. */
65
+ costUnits: number | null;
47
66
  createdAt: Date;
48
67
  }
@@ -23,4 +23,51 @@ export interface IProbeWebhookDeliveryPayload {
23
23
  verdict: ProbeScanVerdict;
24
24
  /** [] when clean. */
25
25
  viruses: string[];
26
+ /**
27
+ * `ACCOUNT_AV_SCAN_UNITS` this scan consumed (WS5-1). Delivery only ever
28
+ * happens on a DONE scan, where the cost is always already computed and
29
+ * billed — see {@link IProbeScanHistoryItem#costUnits}.
30
+ */
31
+ costUnits: number | null;
32
+ }
33
+
34
+ // ─── Delivery HISTORY (WS5-6) ────────────────
35
+ //
36
+ // The interface above is the PAYLOAD a webhook receiver gets POSTed. This
37
+ // section is a DIFFERENT concept: the persisted AUDIT ROW AbyssProbe keeps
38
+ // of its OWN delivery attempts (one row per attempt — mirrors
39
+ // apps/api/src/database/models/probe-webhook-delivery.model.ts EXACTLY,
40
+ // itself modelled after this package's own extraction-webhook
41
+ // delivery-history shape, trimmed to AbyssProbe's simpler
42
+ // one-attempt-per-row reality — see that model's header comment). Read via
43
+ // `GET /webhook/:webhookId/deliveries` (`sdk.probe.webhook.listDeliveries`).
44
+
45
+ /**
46
+ * One persisted delivery-attempt row, as returned (paginated) by
47
+ * `GET /webhook/:webhookId/deliveries`.
48
+ */
49
+ export interface IProbeWebhookDelivery {
50
+ id: string;
51
+ /**
52
+ * The registered webhook this delivery targeted. null for a delivery to a
53
+ * scan's own ad-hoc per-request `callbackUrl` that matched no registered
54
+ * webhook (see the server model's own doc comment) — such a row is never
55
+ * reachable via this webhook-scoped endpoint in practice (there is no
56
+ * webhookId to query it under), but the field stays nullable to mirror the
57
+ * server column's real type.
58
+ */
59
+ webhookId: string | null;
60
+ /** The scan whose verdict this delivery carried. */
61
+ scanId: string;
62
+ /** The exact URL this attempt POSTed to. */
63
+ url: string;
64
+ /** HTTP status code received. null when the request never got a response (network error / timeout / abort). */
65
+ statusCode: number | null;
66
+ /** true only for a 2xx response. */
67
+ success: boolean;
68
+ /** When this attempt was made. */
69
+ attemptedAt: Date;
70
+ /** Failure reason (HTTP non-2xx status, or the caught network/timeout error's message). null on success. */
71
+ error: string | null;
72
+ createdAt: Date;
26
73
  }
@@ -1,25 +1,39 @@
1
1
  // Mirrors apps/api/src/api/controllers/probe-webhook.controller.ts's local
2
2
  // WebhookResponseData / CreateWebhookResponseData. Public shape of a
3
- // registered `probe_webhooks` row — never carries `secret` except right
4
- // after creation (see IProbeWebhookCreated below); never echoes the
5
- // internal ownership columns (userId/organizationId/applicationId) either,
6
- // same discipline as the scan models.
3
+ // registered `probe_webhooks` row — never echoes the internal ownership
4
+ // columns (userId/organizationId/applicationId), same discipline as the
5
+ // scan models.
6
+ //
7
+ // WS5-7 (AbyssProbe UX rework): `secret` is ALWAYS re-displayable now —
8
+ // returned on every read (GET /webhook list, and PATCH /webhook/:id), not
9
+ // just once on create. Mirrors how AbyssExtract's own webhook model
10
+ // (`IExtractionWebhookDelivery`'s sibling `ExtractionWebhook` — see
11
+ // apps/api/src/database/models/extraction-webhook.model.ts /
12
+ // extraction-webhook.controller.ts) has always exposed its
13
+ // `webhookSecret` column on every response, never a once-only reveal. This
14
+ // lets a consumer (e.g. abyss-storage's own webhook registration) read its
15
+ // secret back at any time instead of relying on a log-once bootstrap.
7
16
 
8
17
  export interface IProbeWebhook {
9
18
  id: string;
10
19
  /** HTTPS endpoint the verdict is POSTed to. */
11
20
  callbackUrl: string;
21
+ /**
22
+ * Server-generated HMAC-SHA256 signing secret. Re-displayable: returned on
23
+ * every read (list/create/update), not once-only — see this interface's
24
+ * own header note (WS5-7).
25
+ */
26
+ secret: string;
12
27
  /** Disabled webhooks are skipped by dispatch (soft-off, not deleted). */
13
28
  active: boolean;
14
29
  createdAt: Date;
15
30
  }
16
31
 
17
32
  /**
18
- * Create-only response shape: the ONE time `secret` is ever returned by the
19
- * API ("Secret generated server-side, returned once on create"). Store it
20
- * immediately — there is no reset-secret / re-reveal endpoint (see this
21
- * package's header note on the deliberately small CRUD surface).
33
+ * @deprecated Structurally identical to {@link IProbeWebhook} now that
34
+ * `secret` is always re-displayable (WS5-7) — kept as a source-compatible
35
+ * alias (no removal, no consumer breakage) for existing imports that named
36
+ * this type explicitly for the old "only right after creation" contract.
37
+ * Prefer {@link IProbeWebhook} directly in new code.
22
38
  */
23
- export interface IProbeWebhookCreated extends IProbeWebhook {
24
- secret: string;
25
- }
39
+ export type IProbeWebhookCreated = IProbeWebhook;