@cdot65/prisma-airs-cli 4.2.2 → 4.3.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/README.md CHANGED
@@ -5,7 +5,7 @@
5
5
  [![CI](https://github.com/cdot65/prisma-airs-cli/actions/workflows/ci.yml/badge.svg)](https://github.com/cdot65/prisma-airs-cli/actions/workflows/ci.yml)
6
6
  [![npm](https://img.shields.io/npm/v/@cdot65/prisma-airs-cli)](https://www.npmjs.com/package/@cdot65/prisma-airs-cli)
7
7
  [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
8
- [![Node 20+](https://img.shields.io/badge/node-%3E%3D20-brightgreen.svg)](https://nodejs.org/)
8
+ [![Node 20.17+ / 22.13+ / 24+](https://img.shields.io/badge/node-20.17%2B%20%7C%2022.13%2B%20%7C%2024%2B-brightgreen.svg)](https://nodejs.org/)
9
9
 
10
10
  **Full operational coverage over Palo Alto Prisma AIRS AI security — guardrail refinement, runtime scanning, AI red teaming, AI Gateway, and model security.**
11
11
 
@@ -30,7 +30,7 @@ npm install -g @cdot65/prisma-airs-cli
30
30
  airs --version
31
31
  ```
32
32
 
33
- Requires **Node.js >= 20**. Also available via `pnpm add -g`, `npx`, or as a [Docker image](https://github.com/cdot65/prisma-airs-cli/pkgs/container/prisma-airs-cli). See the [installation guide](https://cdot65.github.io/prisma-airs-cli/getting-started/installation/) for details.
33
+ Requires **Node.js 20.17+, 22.13+, or 24+** (exact engine range: `^20.17.0 || ^22.13.0 || >=23.5.0`). Also available via `pnpm add -g`, `npx`, or as a [Docker image](https://github.com/cdot65/prisma-airs-cli/pkgs/container/prisma-airs-cli). See the [installation guide](https://cdot65.github.io/prisma-airs-cli/getting-started/installation/) for details.
34
34
 
35
35
  ## Quick Start
36
36
 
package/dist/cli/index.js CHANGED
@@ -54,6 +54,7 @@ import {
54
54
 
55
55
  // src/airs/aigateway.ts
56
56
  import {
57
+ AIGatewayChartFiltersSchema,
57
58
  AIGatewayClient
58
59
  } from "@cdot65/prisma-airs-sdk";
59
60
  function toLimitArray(value) {
@@ -164,7 +165,7 @@ var SdkAiGatewayService = class {
164
165
  * Match a user-supplied ref against the workspace list so name | slug |
165
166
  * uuid all work. Unmatched refs pass through so the API's own error stands.
166
167
  */
167
- async resolveWorkspaceRef(ref, planes) {
168
+ async resolveWorkspaceRef(ref, planes, requireSlug = false) {
168
169
  for (const plane of planes) {
169
170
  let rows;
170
171
  try {
@@ -172,7 +173,8 @@ var SdkAiGatewayService = class {
172
173
  } catch {
173
174
  continue;
174
175
  }
175
- if (rows.some((w) => w.id === ref || w.slug === ref)) return ref;
176
+ const byRef = rows.find((w) => w.id === ref || w.slug === ref);
177
+ if (byRef) return requireSlug ? byRef.slug : ref;
176
178
  const byName = rows.filter((w) => w.name === ref);
177
179
  if (byName.length > 1) {
178
180
  throw new Error(
@@ -184,11 +186,15 @@ var SdkAiGatewayService = class {
184
186
  return ref;
185
187
  }
186
188
  async getTelemetryCost(opts) {
187
- const days = opts.days ?? 7;
188
- const workspaceSlug = await this.resolveWorkspaceRef(opts.workspaceSlug, ["data", "admin"]);
189
+ const { workspaceSlug: workspaceRef, days = 7, ...rawFilters } = opts;
190
+ const filters = AIGatewayChartFiltersSchema.parse(rawFilters);
191
+ if (!Number.isSafeInteger(days) || days <= 0)
192
+ throw new Error("Expected days to be a positive integer");
193
+ const workspaceSlug = await this.resolveWorkspaceRef(workspaceRef, ["data", "admin"], true);
189
194
  const raw = await this.client.telemetry.cost({
190
195
  workspaceSlug,
191
- days
196
+ days,
197
+ ...filters
192
198
  });
193
199
  return {
194
200
  workspaceSlug,
@@ -4543,6 +4549,52 @@ function registerAiGatewayInventory(root) {
4543
4549
  );
4544
4550
  }
4545
4551
 
4552
+ // src/cli/commands/aigateway/telemetry-filters.ts
4553
+ import { AIGatewayChartFiltersSchema as AIGatewayChartFiltersSchema2 } from "@cdot65/prisma-airs-sdk";
4554
+ function addChartFilterOptions(command) {
4555
+ return command.option("--trace-id <id>", "Filter by one trace ID").option("--metadata <json>", "Filter by string-valued metadata (JSON object)").option("--status-codes <codes>", "Comma-separated HTTP statuses (OR within the list)").option("--api-key-ids <ids>", "Comma-separated service key UUIDs (not secret key values)").option(
4556
+ "--ai-org-models <pairs>",
4557
+ "Comma-separated provider__model pairs, e.g. openai__gpt-5.6-terra"
4558
+ ).option("--total-units-min <n>", "Inclusive minimum total tokens (nonnegative integer)").option("--total-units-max <n>", "Inclusive maximum total tokens (nonnegative integer)").option("--cost-min <cents>", "Inclusive minimum cost in cents (fractional cents allowed)").option("--cost-max <cents>", "Inclusive maximum cost in cents (fractional cents allowed)");
4559
+ }
4560
+ function csv(value) {
4561
+ const values = value.split(",").map((entry) => entry.trim());
4562
+ if (values.some((entry) => entry.length === 0)) throw new CliUsageError("Empty CSV element");
4563
+ return values;
4564
+ }
4565
+ function decimal(value) {
4566
+ if (!/^-?(?:0|[1-9]\d*)(?:\.\d+)?(?:[eE][+-]?\d+)?$/.test(value)) {
4567
+ throw new CliUsageError("Expected a decimal number");
4568
+ }
4569
+ return Number(value);
4570
+ }
4571
+ function chartFiltersFrom(opts) {
4572
+ const filters = {};
4573
+ try {
4574
+ if (opts.traceId !== void 0) filters.traceId = opts.traceId;
4575
+ if (opts.metadata !== void 0) filters.metadata = parseJsonOption(opts.metadata);
4576
+ if (opts.statusCodes !== void 0)
4577
+ filters.statusCodes = csv(opts.statusCodes).map(parseIntegerOption);
4578
+ if (opts.apiKeyIds !== void 0) filters.apiKeyIds = csv(opts.apiKeyIds);
4579
+ if (opts.aiOrgModels !== void 0) filters.aiOrgModels = csv(opts.aiOrgModels);
4580
+ for (const key of ["totalUnitsMin", "totalUnitsMax"]) {
4581
+ if (opts[key] !== void 0) filters[key] = parseIntegerOption(opts[key]);
4582
+ }
4583
+ for (const key of ["costMin", "costMax"]) {
4584
+ if (opts[key] !== void 0) filters[key] = decimal(opts[key]);
4585
+ }
4586
+ } catch {
4587
+ throw new CliUsageError("Invalid chart filters: check JSON, CSV and numeric flag syntax");
4588
+ }
4589
+ const result = AIGatewayChartFiltersSchema2.safeParse(filters);
4590
+ if (!result.success) {
4591
+ throw new CliUsageError(
4592
+ "Invalid chart filters: check status codes, UUIDs, provider__model pairs, string metadata and inclusive nonnegative bounds"
4593
+ );
4594
+ }
4595
+ return result.data;
4596
+ }
4597
+
4546
4598
  // src/cli/commands/aigateway/telemetry.ts
4547
4599
  function addWindowOptions(command) {
4548
4600
  return addReadOutput(
@@ -4582,6 +4634,20 @@ function registerMetric(telemetry, name, description, method) {
4582
4634
  (opts) => runDetail(command, opts, (client) => client.telemetry[method](windowFrom(opts)))
4583
4635
  );
4584
4636
  }
4637
+ function registerFilteredMetric(telemetry, name, description) {
4638
+ const command = addChartFilterOptions(
4639
+ addWindowOptions(telemetry.command(name).description(description))
4640
+ );
4641
+ command.action(async (opts) => {
4642
+ let options;
4643
+ try {
4644
+ options = { ...windowFrom(opts), ...chartFiltersFrom(opts) };
4645
+ } catch (error) {
4646
+ failAiGateway(error);
4647
+ }
4648
+ await runDetail(command, opts, (client) => client.telemetry[name](options));
4649
+ });
4650
+ }
4585
4651
  function registerAiGatewayTelemetryReads(telemetry) {
4586
4652
  const cache = showHelpOnEmpty(telemetry.command("cache").description("Inspect cache telemetry"));
4587
4653
  const cacheSummary = addWindowOptions(cache.command("summary").description("Get cache totals"));
@@ -4622,7 +4688,7 @@ function registerAiGatewayTelemetryReads(telemetry) {
4622
4688
  })
4623
4689
  )
4624
4690
  );
4625
- registerMetric(telemetry, "latency", "Get latency telemetry", "latency");
4691
+ registerFilteredMetric(telemetry, "latency", "Get latency telemetry");
4626
4692
  const logs = showHelpOnEmpty(telemetry.command("logs").description("Inspect request logs"));
4627
4693
  const logsList = addWindowOptions(
4628
4694
  logs.command("list").description("List request logs").option("--page-size <n>", "Rows per response", "50").option("--status-code <code>", "Filter by HTTP status").option("--trace-id <id>", "Return one trace id")
@@ -4639,9 +4705,9 @@ function registerAiGatewayTelemetryReads(telemetry) {
4639
4705
  })
4640
4706
  )
4641
4707
  );
4642
- registerMetric(telemetry, "requests", "Get request count", "requests");
4708
+ registerFilteredMetric(telemetry, "requests", "Get request count");
4643
4709
  registerMetric(telemetry, "rescued-retries", "Get rescued retry telemetry", "rescuedRetries");
4644
- registerMetric(telemetry, "tokens", "Get token usage", "tokens");
4710
+ registerFilteredMetric(telemetry, "tokens", "Get token usage");
4645
4711
  registerMetric(telemetry, "user-trends", "Get user trends", "userTrends");
4646
4712
  registerMetric(telemetry, "users", "Get unique-user count", "users");
4647
4713
  }
@@ -4842,24 +4908,28 @@ function registerAiGatewayCommand(program) {
4842
4908
  workspace.command("delete <ref>", { hidden: true }).description("Deprecated compatibility command for archive").option("--force", "Skip confirmation prompt").action((ref, opts) => archiveWorkspace(ref, opts, true));
4843
4909
  const telemetry = aigateway.command("telemetry").description("AI Gateway runtime telemetry (data plane)").action(() => telemetry.outputHelp());
4844
4910
  registerAiGatewayTelemetryReads(telemetry);
4845
- const cost = telemetry.command("cost").description(
4846
- "Total and per-day spend for a workspace (API reports cents; pretty output shows dollars)"
4847
- ).requiredOption("--workspace <slug>", "Workspace slug (not UUID), e.g. ws-main-a-349e0e").option("--days <n>", "Rolling window in days, counted back from now", "7").option("--output <format>", "Output format: pretty, table, markdown, csv, json, yaml").addHelpText(
4848
- "after",
4849
- examples(
4850
- "airs aigateway telemetry cost --workspace ws-main-a-349e0e",
4851
- "airs aigateway telemetry cost --workspace ws-main-a-349e0e --days 30 --output json"
4911
+ const cost = addChartFilterOptions(
4912
+ telemetry.command("cost").description(
4913
+ "Total and per-day spend for a workspace (API reports cents; pretty output shows dollars)"
4914
+ ).requiredOption("--workspace <ref>", "Workspace slug, UUID, or unique display name").option("--days <n>", "Rolling window in days, counted back from now", "7").option("--output <format>", "Output format: pretty, table, markdown, csv, json, yaml").addHelpText(
4915
+ "after",
4916
+ examples(
4917
+ "airs aigateway telemetry cost --workspace ws-main-a-349e0e",
4918
+ "airs aigateway telemetry cost --workspace ws-main-a-349e0e --days 30 --output json"
4919
+ )
4852
4920
  )
4853
4921
  ).action(async (opts) => {
4854
4922
  try {
4923
+ const days = parsePositiveInteger2(opts.days, "--days");
4924
+ const filters = chartFiltersFrom(opts);
4855
4925
  const fmt = await resolveOutput(cost, opts);
4856
4926
  if (fmt === "pretty") renderAiGatewayHeader();
4857
- const days = Number.parseInt(opts.days, 10);
4858
- if (!Number.isFinite(days) || days <= 0) {
4859
- usageError(`Invalid --days '${opts.days}'. Expected a positive integer`);
4860
- }
4861
4927
  const service = await createService();
4862
- const report = await service.getTelemetryCost({ workspaceSlug: opts.workspace, days });
4928
+ const report = await service.getTelemetryCost({
4929
+ workspaceSlug: opts.workspace,
4930
+ days,
4931
+ ...filters
4932
+ });
4863
4933
  renderCostReport(report, fmt);
4864
4934
  } catch (err) {
4865
4935
  failWithGrantHint(err);
@@ -5150,17 +5220,23 @@ import { randomUUID } from "crypto";
5150
5220
  import { readFile as readFile4 } from "fs/promises";
5151
5221
  import { init, Scanner } from "@cdot65/prisma-airs-sdk";
5152
5222
  var DOCTOR_TIMEOUT_MS = 5e3;
5153
- var MIN_NODE_MAJOR = 20;
5223
+ var SUPPORTED_NODE_VERSIONS = "^20.17.0 || ^22.13.0 || >=23.5.0";
5154
5224
  function checkNodeVersion(version = process.version) {
5155
- const major = Number.parseInt(version.replace(/^v/, ""), 10);
5156
- if (Number.isFinite(major) && major >= MIN_NODE_MAJOR) {
5157
- return { name: "Node.js version", status: "pass", detail: `${version} (>= 20 required)` };
5225
+ const match = /^v?(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)$/.exec(version);
5226
+ const [major, minor, patch] = match ? match.slice(1).map(Number) : [];
5227
+ const supported = [major, minor, patch].every(Number.isSafeInteger) && (major === 20 && minor >= 17 || major === 22 && minor >= 13 || major === 23 && minor >= 5 || major >= 24);
5228
+ if (supported) {
5229
+ return {
5230
+ name: "Node.js version",
5231
+ status: "pass",
5232
+ detail: `${version} (${SUPPORTED_NODE_VERSIONS} required)`
5233
+ };
5158
5234
  }
5159
5235
  return {
5160
5236
  name: "Node.js version",
5161
5237
  status: "fail",
5162
- detail: `${version} is below the required Node ${MIN_NODE_MAJOR}`,
5163
- hint: `Upgrade to Node ${MIN_NODE_MAJOR}+ (e.g. via nvm or your package manager)`
5238
+ detail: `${version} does not satisfy ${SUPPORTED_NODE_VERSIONS}`,
5239
+ hint: "Use a supported Node runtime: 20.17+, 22.13+, or 24+ (e.g. via nvm or your package manager)"
5164
5240
  };
5165
5241
  }
5166
5242
  async function checkConfigFile(filePath) {
@@ -6594,9 +6670,9 @@ function registerRedteamCommand(program) {
6594
6670
  try {
6595
6671
  renderRedteamHeader();
6596
6672
  const service = await createPromptSetService();
6597
- const csv = await service.downloadTemplate(uuid);
6673
+ const csv2 = await service.downloadTemplate(uuid);
6598
6674
  const outPath = opts.outputFile || `${uuid}-template.csv`;
6599
- fs2.writeFileSync(outPath, csv, "utf-8");
6675
+ fs2.writeFileSync(outPath, csv2, "utf-8");
6600
6676
  ui.success(`Template saved to ${outPath}`);
6601
6677
  } catch (err) {
6602
6678
  fail(err);
@@ -8419,6 +8495,30 @@ function register2(dlp) {
8419
8495
  });
8420
8496
  }
8421
8497
 
8498
+ // src/cli/renderer/dlp-generate.ts
8499
+ function renderGenerateSummary(summary, output) {
8500
+ if (output === "json") {
8501
+ console.log(JSON.stringify(summary, null, 2));
8502
+ return;
8503
+ }
8504
+ ui.header("DLP Test-File Generation");
8505
+ ui.keyValue([
8506
+ ["Output", summary.out],
8507
+ ["Seed", summary.seed],
8508
+ ["Clean", summary.clean],
8509
+ ["Dirty", summary.dirty],
8510
+ ["Manifest", summary.manifestPath]
8511
+ ]);
8512
+ ui.table(
8513
+ [
8514
+ { key: "format", label: "Format" },
8515
+ { key: "clean", label: "Clean" },
8516
+ { key: "dirty", label: "Dirty" }
8517
+ ],
8518
+ Object.entries(summary.byFormat).map(([format, counts]) => ({ format, ...counts }))
8519
+ );
8520
+ }
8521
+
8422
8522
  // src/cli/commands/dlp/generate.ts
8423
8523
  var ALL_FORMATS = ["pdf", "png", "jpeg", "svg", "docx"];
8424
8524
  var OPTIONAL_DEPS_HINT = "DLP generate requires optional dependencies. Install them with: pnpm add sharp pdf-lib docx piexifjs";
@@ -8447,39 +8547,29 @@ function parseTypes(value) {
8447
8547
  return types;
8448
8548
  }
8449
8549
  function register3(parent) {
8450
- parent.command("generate").description(
8550
+ const command = parent.command("generate").description(
8451
8551
  "Generate clean + dirty DLP test files (synthetic sensitive data) across PDF/PNG/JPEG/SVG/DOCX"
8452
- ).option("--types <list>", "Comma list: pdf,png,jpeg,svg,docx (or all)", "all").option("--count <n>", "Clean files per type", "1").option("--out <dir>", "Output base directory", "./temp").option("--techniques <list>", "all or comma list of technique ids", "all").option("--seed <n>", "Seed for reproducible payloads").option("--output <format>", "Summary format: pretty or json", "pretty").action(async (opts) => {
8552
+ ).option("--types <list>", "Comma list: pdf,png,jpeg,svg,docx (or all)", "all").option("--count <n>", "Clean files per type", "1").option("--out <dir>", "Output base directory", "./temp").option("--techniques <list>", "all or comma list of technique ids", "all").option("--seed <n>", "Safe integer seed for reproducible payloads").option("--output <format>", "Summary format: pretty or json", "pretty").action(async (opts) => {
8453
8553
  let types;
8454
8554
  try {
8455
8555
  types = parseTypes(opts.types);
8456
8556
  } catch (err) {
8457
8557
  usageError(err instanceof Error ? err.message : String(err));
8458
8558
  }
8459
- const count = Number.parseInt(opts.count, 10);
8460
- if (!Number.isInteger(count) || count < 1) {
8461
- usageError("--count must be a positive integer");
8559
+ const count = Number(opts.count);
8560
+ if (!/^\d+$/.test(opts.count) || !Number.isSafeInteger(count) || count < 1) {
8561
+ usageError("--count must be a positive safe integer");
8462
8562
  }
8463
8563
  const techniques = opts.techniques === "all" ? "all" : opts.techniques.split(",").map((t) => t.trim());
8464
- const seed = opts.seed === void 0 ? void 0 : Number.parseInt(opts.seed, 10);
8564
+ const seed = opts.seed === void 0 ? void 0 : Number(opts.seed);
8565
+ if (seed !== void 0 && (!/^-?\d+$/.test(opts.seed) || !Number.isSafeInteger(seed))) {
8566
+ usageError("--seed must be a safe integer");
8567
+ }
8465
8568
  try {
8569
+ const output = await resolveOutput(command, opts, { allowed: ["pretty", "json"] });
8466
8570
  const generateCorpus = await loadGenerateCorpus();
8467
8571
  const summary = await generateCorpus({ types, count, out: opts.out, techniques, seed });
8468
- if (opts.output === "json") {
8469
- console.log(JSON.stringify(summary, null, 2));
8470
- return;
8471
- }
8472
- ui.header("DLP Test-File Generation");
8473
- ui.keyValue([
8474
- ["Output", summary.out],
8475
- ["Seed", summary.seed],
8476
- ["Clean", summary.clean],
8477
- ["Dirty", summary.dirty],
8478
- ["Manifest", summary.manifestPath]
8479
- ]);
8480
- for (const [fmt, counts] of Object.entries(summary.byFormat)) {
8481
- ui.dim(`${fmt.padEnd(5)} clean=${counts.clean} dirty=${counts.dirty}`);
8482
- }
8572
+ renderGenerateSummary(summary, output);
8483
8573
  } catch (err) {
8484
8574
  fail(err);
8485
8575
  }
@@ -9047,8 +9137,8 @@ function parseCsvLine(line) {
9047
9137
  }
9048
9138
  return fields;
9049
9139
  }
9050
- function loadPrompts(csv, onWarning) {
9051
- const lines = csv.split(/\r?\n/);
9140
+ function loadPrompts(csv2, onWarning) {
9141
+ const lines = csv2.split(/\r?\n/);
9052
9142
  const nonEmpty = lines.filter((l) => l.trim() !== "");
9053
9143
  if (nonEmpty.length === 0) {
9054
9144
  throw new Error("CSV is empty");
@@ -9245,12 +9335,12 @@ function registerSampleCommand(parent) {
9245
9335
  });
9246
9336
  cmd.action(async (opts) => {
9247
9337
  resolveDeprecatedAliases(cmd, opts);
9248
- const csv = buildSampleCsv();
9338
+ const csv2 = buildSampleCsv();
9249
9339
  if (opts.outputFile) {
9250
- await writeFile4(opts.outputFile, csv, "utf-8");
9340
+ await writeFile4(opts.outputFile, csv2, "utf-8");
9251
9341
  ui.success(`Sample CSV written to ${opts.outputFile}`);
9252
9342
  } else {
9253
- process.stdout.write(csv);
9343
+ process.stdout.write(csv2);
9254
9344
  }
9255
9345
  });
9256
9346
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@cdot65/prisma-airs-cli",
3
3
  "packageManager": "pnpm@10.6.5",
4
- "version": "4.2.2",
4
+ "version": "4.3.1",
5
5
  "description": "CLI and library for Palo Alto Prisma AIRS — guardrail refinement, AI red teaming, model security scanning, profile audits",
6
6
  "type": "module",
7
7
  "main": "dist/index.js",
@@ -14,11 +14,13 @@
14
14
  },
15
15
  "scripts": {
16
16
  "build": "tsup",
17
- "prepublishOnly": "pnpm run build",
17
+ "prepublishOnly": "pnpm audit:prod && pnpm run build",
18
18
  "dev": "tsx src/cli/index.ts",
19
19
  "test": "vitest run",
20
20
  "test:watch": "vitest",
21
21
  "test:coverage": "vitest run --coverage",
22
+ "test:release": "node --test .github/scripts/*.test.mjs",
23
+ "audit:prod": "pnpm audit --prod --audit-level low",
22
24
  "test:e2e": "vitest run --config vitest.e2e.config.ts",
23
25
  "test:e2e:aigateway": "vitest run --config vitest.e2e.config.ts tests/e2e/aigateway-cli.e2e.spec.ts",
24
26
  "lint": "biome check --config-path config .",
@@ -49,7 +51,7 @@
49
51
  "local:unlink": "pnpm uninstall --global @cdot65/prisma-airs-cli"
50
52
  },
51
53
  "engines": {
52
- "node": ">=20.0.0"
54
+ "node": "^20.17.0 || ^22.13.0 || >=23.5.0"
53
55
  },
54
56
  "repository": {
55
57
  "type": "git",
@@ -57,13 +59,13 @@
57
59
  },
58
60
  "license": "MIT",
59
61
  "dependencies": {
60
- "@cdot65/prisma-airs-sdk": "0.23.0",
62
+ "@cdot65/prisma-airs-sdk": "0.24.0",
61
63
  "@inquirer/prompts": "^8.3.0",
62
64
  "chalk": "^5.6.2",
63
65
  "commander": "^14.0.3",
64
66
  "dotenv": "^17.3.1",
65
- "js-yaml": "^4.1.1",
66
- "nanoid": "^5.0.0",
67
+ "js-yaml": "^4.3.2",
68
+ "nanoid": "^5.1.16",
67
69
  "p-limit": "^6.0.0",
68
70
  "zod": "^3.24.0"
69
71
  },
@@ -84,6 +86,6 @@
84
86
  "docx": "^9.6.1",
85
87
  "pdf-lib": "^1.17.1",
86
88
  "piexifjs": "^1.0.6",
87
- "sharp": "^0.34.5"
89
+ "sharp": "^0.35.4"
88
90
  }
89
91
  }