@hasna/todos 0.13.7 → 0.13.9

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.
@@ -1 +1 @@
1
- {"version":3,"file":"task-crud.d.ts","sourceRoot":"","sources":["../../../src/mcp/tools/task-crud.ts"],"names":[],"mappings":"AACA;;;GAGG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEpE,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,sBAAsB,CAAC;AAgBjD,UAAU,eAAe;IACvB,kBAAkB,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC;IAC9C,SAAS,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC;IACzD,WAAW,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,MAAM,CAAC;IACxC,UAAU,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,MAAM,CAAC;IACnC,gBAAgB,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,mBAAmB,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC;IACvE,aAAa,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,SAAS,CAAC;CAC3F;AAED,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,SAAS,EAAE,GAAG,EAAE,eAAe,QAmX5E"}
1
+ {"version":3,"file":"task-crud.d.ts","sourceRoot":"","sources":["../../../src/mcp/tools/task-crud.ts"],"names":[],"mappings":"AACA;;;GAGG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEpE,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,sBAAsB,CAAC;AAoBjD,UAAU,eAAe;IACvB,kBAAkB,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC;IAC9C,SAAS,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC;IACzD,WAAW,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,MAAM,CAAC;IACxC,UAAU,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,MAAM,CAAC;IACnC,gBAAgB,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,mBAAmB,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC;IACvE,aAAa,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,SAAS,CAAC;CAC3F;AAuBD,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,SAAS,EAAE,GAAG,EAAE,eAAe,QAkY5E"}
package/dist/mcp.js CHANGED
@@ -41,7 +41,7 @@ var __require = import.meta.require;
41
41
  // package.json
42
42
  var package_default = {
43
43
  name: "@hasna/todos",
44
- version: "0.13.7",
44
+ version: "0.13.9",
45
45
  description: "Universal task management for AI coding agents - CLI + MCP server + interactive TUI",
46
46
  type: "module",
47
47
  main: "dist/index.js",
package/dist/registry.js CHANGED
@@ -4336,6 +4336,7 @@ var init_database = __esm(() => {
4336
4336
 
4337
4337
  // src/lib/sync-utils.ts
4338
4338
  import { existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync, readdirSync, statSync, writeFileSync } from "fs";
4339
+ import { createHash } from "crypto";
4339
4340
  import { join as join2 } from "path";
4340
4341
  function getHomeDir() {
4341
4342
  return process.env["HOME"] || process.env["USERPROFILE"] || "~";
@@ -4391,7 +4392,40 @@ function appendSyncConflict(metadata, conflict, limit = 5) {
4391
4392
  const next = [conflict, ...current].slice(0, limit);
4392
4393
  return { ...metadata, sync_conflicts: next };
4393
4394
  }
4394
- var HOME;
4395
+ function canonicalize(value) {
4396
+ if (Array.isArray(value))
4397
+ return value.map(canonicalize);
4398
+ if (!value || typeof value !== "object")
4399
+ return value;
4400
+ const entries = Object.entries(value).filter(([, entryValue]) => entryValue !== undefined).sort(([a], [b]) => a.localeCompare(b));
4401
+ return Object.fromEntries(entries.map(([key, entryValue]) => [key, canonicalize(entryValue)]));
4402
+ }
4403
+ function withoutSyncFingerprintMetadata(metadata) {
4404
+ const { [TODO_SYNC_FINGERPRINT_KEY]: _fingerprint, ...rest } = metadata;
4405
+ return rest;
4406
+ }
4407
+ function syncFingerprint(record) {
4408
+ const metadata = withoutSyncFingerprintMetadata(record.metadata || {});
4409
+ const canonical = canonicalize({ ...record, metadata });
4410
+ return `sha256:${createHash("sha256").update(JSON.stringify(canonical)).digest("hex")}`;
4411
+ }
4412
+ function withSyncFingerprint(record) {
4413
+ const metadata = withoutSyncFingerprintMetadata(record.metadata);
4414
+ return {
4415
+ ...record,
4416
+ metadata: {
4417
+ ...metadata,
4418
+ [TODO_SYNC_FINGERPRINT_KEY]: syncFingerprint({ ...record, metadata })
4419
+ }
4420
+ };
4421
+ }
4422
+ function hasSyncFingerprintChanged(record) {
4423
+ const stored = record.metadata?.[TODO_SYNC_FINGERPRINT_KEY];
4424
+ if (typeof stored !== "string" || stored.length === 0)
4425
+ return null;
4426
+ return stored !== syncFingerprint(record);
4427
+ }
4428
+ var TODO_SYNC_FINGERPRINT_KEY = "todos_sync_fingerprint", HOME;
4395
4429
  var init_sync_utils = __esm(() => {
4396
4430
  HOME = process.env["HOME"] || process.env["USERPROFILE"] || "~";
4397
4431
  });
@@ -5502,7 +5536,7 @@ var init_runner_sandbox = __esm(() => {
5502
5536
  });
5503
5537
 
5504
5538
  // src/lib/event-hooks.ts
5505
- import { createHash, randomUUID } from "crypto";
5539
+ import { createHash as createHash2, randomUUID } from "crypto";
5506
5540
  import { appendFileSync, mkdirSync as mkdirSync3 } from "fs";
5507
5541
  import { dirname as dirname3, resolve as resolve6 } from "path";
5508
5542
  import { createConnection } from "net";
@@ -5569,7 +5603,7 @@ function buildEnvelope(type, payload, timestamp2 = new Date().toISOString()) {
5569
5603
  payload: redactValue(payload ?? {}),
5570
5604
  source: { package: "@hasna/todos", local_only: true }
5571
5605
  };
5572
- const digest = createHash("sha256").update(canonicalEvent(base)).digest("hex");
5606
+ const digest = createHash2("sha256").update(canonicalEvent(base)).digest("hex");
5573
5607
  return { ...base, integrity: { algorithm: "sha256", digest } };
5574
5608
  }
5575
5609
  function summarize(value) {
@@ -7479,6 +7513,14 @@ var init_checklists = __esm(() => {
7479
7513
  init_database();
7480
7514
  });
7481
7515
 
7516
+ // src/lib/creator-identity.ts
7517
+ function canonicalAgentRef(value) {
7518
+ return value.trim().toLowerCase();
7519
+ }
7520
+ var init_creator_identity = __esm(() => {
7521
+ init_sync_utils();
7522
+ });
7523
+
7482
7524
  // src/lib/recurrence.ts
7483
7525
  function parseRecurrenceRule(rule) {
7484
7526
  const normalized = rule.trim().toLowerCase();
@@ -8185,6 +8227,11 @@ __export(exports_task_lifecycle, {
8185
8227
  claimOrSteal: () => claimOrSteal,
8186
8228
  claimNextTask: () => claimNextTask
8187
8229
  });
8230
+ function sameHolder(stored, incoming) {
8231
+ if (!stored || !incoming)
8232
+ return false;
8233
+ return canonicalAgentRef(stored) === canonicalAgentRef(incoming);
8234
+ }
8188
8235
  function lockExpiresAt(lockedAt) {
8189
8236
  if (!lockedAt)
8190
8237
  return null;
@@ -8235,13 +8282,13 @@ function startTask(id, agentId, db) {
8235
8282
  const cutoff = lockExpiryCutoff();
8236
8283
  const timestamp2 = now();
8237
8284
  const result = d.run(`UPDATE tasks SET status = 'in_progress', assigned_to = ?, locked_by = ?, locked_at = ?, started_at = COALESCE(started_at, ?), version = version + 1, updated_at = ?
8238
- WHERE id = ? AND status IN ('pending', 'in_progress') AND (locked_by IS NULL OR locked_by = ? OR locked_at < ?)`, [agentId, agentId, timestamp2, timestamp2, timestamp2, id, agentId, cutoff]);
8285
+ WHERE id = ? AND status IN ('pending', 'in_progress') AND (locked_by IS NULL OR LOWER(TRIM(locked_by)) = LOWER(TRIM(?)) OR locked_at < ?)`, [agentId, agentId, timestamp2, timestamp2, timestamp2, id, agentId, cutoff]);
8239
8286
  if (result.changes === 0) {
8240
8287
  const current = getTask(id, d);
8241
8288
  if (!current)
8242
8289
  throw new TaskNotFoundError(id);
8243
8290
  assertStartable(current, agentId);
8244
- if (current.locked_by && current.locked_by !== agentId && !isLockExpired(current.locked_at)) {
8291
+ if (current.locked_by && !sameHolder(current.locked_by, agentId) && !isLockExpired(current.locked_at)) {
8245
8292
  throw new LockError(id, current.locked_by);
8246
8293
  }
8247
8294
  throw new Error(`Task ${id} could not be started because it changed during claim`);
@@ -8266,7 +8313,7 @@ function completeTask(id, agentId, db, options) {
8266
8313
  if (task.status === "cancelled") {
8267
8314
  throw new Error(`Task ${id} is cancelled and cannot be completed`);
8268
8315
  }
8269
- if (agentId && task.locked_by && task.locked_by !== agentId && !isLockExpired(task.locked_at)) {
8316
+ if (agentId && task.locked_by && !sameHolder(task.locked_by, agentId) && !isLockExpired(task.locked_at)) {
8270
8317
  throw new LockError(id, task.locked_by);
8271
8318
  }
8272
8319
  checkCompletionGuard(task, agentId || null, d);
@@ -8388,16 +8435,16 @@ function lockTask(id, agentId, db) {
8388
8435
  error: `Task is ${task.status} and cannot be locked`
8389
8436
  };
8390
8437
  }
8391
- if (task.locked_by === agentId && !isLockExpired(task.locked_at)) {
8438
+ if (sameHolder(task.locked_by, agentId) && !isLockExpired(task.locked_at)) {
8392
8439
  const timestamp3 = now();
8393
- d.run(`UPDATE tasks SET locked_at = ?, updated_at = ?, version = version + 1 WHERE id = ? AND locked_by = ?`, [timestamp3, timestamp3, id, agentId]);
8440
+ d.run(`UPDATE tasks SET locked_at = ?, updated_at = ?, version = version + 1 WHERE id = ? AND LOWER(TRIM(locked_by)) = LOWER(TRIM(?))`, [timestamp3, timestamp3, id, agentId]);
8394
8441
  logTaskChange(id, "lock_renew", "locked_by", agentId, agentId, agentId, d);
8395
8442
  return { success: true, locked_by: agentId, locked_at: timestamp3, expires_at: lockExpiresAt(timestamp3) };
8396
8443
  }
8397
8444
  const cutoff = lockExpiryCutoff();
8398
8445
  const timestamp2 = now();
8399
8446
  const result = d.run(`UPDATE tasks SET locked_by = ?, locked_at = ?, version = version + 1, updated_at = ?
8400
- WHERE id = ? AND status NOT IN ('completed', 'cancelled') AND (locked_by IS NULL OR locked_by = ? OR locked_at < ?)`, [agentId, timestamp2, timestamp2, id, agentId, cutoff]);
8447
+ WHERE id = ? AND status NOT IN ('completed', 'cancelled') AND (locked_by IS NULL OR LOWER(TRIM(locked_by)) = LOWER(TRIM(?)) OR locked_at < ?)`, [agentId, timestamp2, timestamp2, id, agentId, cutoff]);
8401
8448
  if (result.changes === 0) {
8402
8449
  const current = getTask(id, d);
8403
8450
  if (!current)
@@ -8429,7 +8476,7 @@ function unlockTask(id, agentId, db) {
8429
8476
  const task = getTask(id, d);
8430
8477
  if (!task)
8431
8478
  throw new TaskNotFoundError(id);
8432
- if (agentId && task.locked_by && task.locked_by !== agentId) {
8479
+ if (agentId && task.locked_by && !sameHolder(task.locked_by, agentId)) {
8433
8480
  throw new LockError(id, task.locked_by);
8434
8481
  }
8435
8482
  const timestamp2 = now();
@@ -8722,6 +8769,7 @@ var MAX_SPAWN_DEPTH = 10;
8722
8769
  var init_task_lifecycle = __esm(() => {
8723
8770
  init_types();
8724
8771
  init_database();
8772
+ init_creator_identity();
8725
8773
  init_completion_guard();
8726
8774
  init_event_emission_safety();
8727
8775
  init_event_hooks();
@@ -10490,7 +10538,7 @@ var init_boards = __esm(() => {
10490
10538
  });
10491
10539
 
10492
10540
  // src/lib/artifact-store.ts
10493
- import { createHash as createHash2 } from "crypto";
10541
+ import { createHash as createHash3 } from "crypto";
10494
10542
  import { existsSync as existsSync7, mkdirSync as mkdirSync4, readFileSync as readFileSync3, rmSync, statSync as statSync2, writeFileSync as writeFileSync2 } from "fs";
10495
10543
  import { basename, dirname as dirname4, join as join5, resolve as resolve7 } from "path";
10496
10544
  import { tmpdir as tmpdir2 } from "os";
@@ -10515,7 +10563,7 @@ function artifactStorePath(relativePath) {
10515
10563
  return join5(artifactStoreRoot(), normalized);
10516
10564
  }
10517
10565
  function sha256(buffer) {
10518
- return createHash2("sha256").update(buffer).digest("hex");
10566
+ return createHash3("sha256").update(buffer).digest("hex");
10519
10567
  }
10520
10568
  function isTextLike(buffer, path) {
10521
10569
  if (buffer.includes(0))
@@ -11925,7 +11973,7 @@ var init_tasks = __esm(() => {
11925
11973
  // package.json
11926
11974
  var package_default = {
11927
11975
  name: "@hasna/todos",
11928
- version: "0.13.7",
11976
+ version: "0.13.9",
11929
11977
  description: "Universal task management for AI coding agents - CLI + MCP server + interactive TUI",
11930
11978
  type: "module",
11931
11979
  main: "dist/index.js",
@@ -15640,7 +15688,7 @@ function importOnboardingFixture(options = {}) {
15640
15688
  }
15641
15689
  // src/lib/local-backups.ts
15642
15690
  init_database();
15643
- import { createHash as createHash3 } from "crypto";
15691
+ import { createHash as createHash4 } from "crypto";
15644
15692
  import { readFileSync as readFileSync4, writeFileSync as writeFileSync4 } from "fs";
15645
15693
  import { dirname as dirname5, resolve as resolve8 } from "path";
15646
15694
  import { mkdirSync as mkdirSync6 } from "fs";
@@ -15658,7 +15706,7 @@ function stableJson(value) {
15658
15706
  return `{${Object.keys(record).sort().map((key) => `${JSON.stringify(key)}:${stableJson(record[key])}`).join(",")}}`;
15659
15707
  }
15660
15708
  function sha2562(value) {
15661
- return createHash3("sha256").update(stableJson(value)).digest("hex");
15709
+ return createHash4("sha256").update(stableJson(value)).digest("hex");
15662
15710
  }
15663
15711
  function sqliteIntegrity(db) {
15664
15712
  let quick = "unknown";
@@ -15936,7 +15984,7 @@ function checkLocalIntegrity(options = {}, db) {
15936
15984
  }
15937
15985
  // src/lib/local-snapshots.ts
15938
15986
  init_database();
15939
- import { createHash as createHash4 } from "crypto";
15987
+ import { createHash as createHash5 } from "crypto";
15940
15988
 
15941
15989
  // src/lib/activity-timeline.ts
15942
15990
  init_database();
@@ -16212,7 +16260,7 @@ function stable(value) {
16212
16260
  return Object.fromEntries(Object.entries(value).sort(([left], [right]) => left.localeCompare(right)).map(([key, item]) => [key, stable(item)]));
16213
16261
  }
16214
16262
  function sha2563(value) {
16215
- return createHash4("sha256").update(JSON.stringify(stable(value))).digest("hex");
16263
+ return createHash5("sha256").update(JSON.stringify(stable(value))).digest("hex");
16216
16264
  }
16217
16265
  function latestTimestamp(items, fallback) {
16218
16266
  const timestamps = [];
@@ -19424,20 +19472,20 @@ init_database();
19424
19472
  init_task_runs();
19425
19473
  init_config2();
19426
19474
  init_redaction();
19427
- import { createHash as createHash5 } from "crypto";
19475
+ import { createHash as createHash6 } from "crypto";
19428
19476
  var LOCAL_AUDIT_LEDGER_SCHEMA_VERSION = 1;
19429
19477
  var LOCAL_AUDIT_LEDGER_HASH_ALGORITHM = "sha256";
19430
19478
  var LOCAL_AUDIT_LEDGER_INITIAL_HASH = "0".repeat(64);
19431
- function canonicalize(value) {
19479
+ function canonicalize2(value) {
19432
19480
  if (value === null || typeof value !== "object")
19433
19481
  return JSON.stringify(value);
19434
19482
  if (Array.isArray(value))
19435
- return `[${value.map(canonicalize).join(",")}]`;
19483
+ return `[${value.map(canonicalize2).join(",")}]`;
19436
19484
  const object = value;
19437
- return `{${Object.keys(object).sort().map((key) => `${JSON.stringify(key)}:${canonicalize(object[key])}`).join(",")}}`;
19485
+ return `{${Object.keys(object).sort().map((key) => `${JSON.stringify(key)}:${canonicalize2(object[key])}`).join(",")}}`;
19438
19486
  }
19439
19487
  function hash(value) {
19440
- return createHash5("sha256").update(value).digest("hex");
19488
+ return createHash6("sha256").update(value).digest("hex");
19441
19489
  }
19442
19490
  function parsePayload2(value) {
19443
19491
  if (!value)
@@ -19570,7 +19618,7 @@ function toLedgerEntries(rows) {
19570
19618
  let previous = LOCAL_AUDIT_LEDGER_INITIAL_HASH;
19571
19619
  return ordered.map((row, index) => {
19572
19620
  const payload = parsePayload2(row.payload_json);
19573
- const payloadHash = hash(canonicalize({ source: row.source, source_id: row.source_id, created_at: row.created_at, payload }));
19621
+ const payloadHash = hash(canonicalize2({ source: row.source, source_id: row.source_id, created_at: row.created_at, payload }));
19574
19622
  const chainHash = hash(`${previous}
19575
19623
  ${payloadHash}`);
19576
19624
  const entry = {
@@ -19957,7 +20005,7 @@ function renderReleaseCompatibilityMarkdown(report) {
19957
20005
  `);
19958
20006
  }
19959
20007
  // src/db/inbox.ts
19960
- import { createHash as createHash6 } from "crypto";
20008
+ import { createHash as createHash7 } from "crypto";
19961
20009
 
19962
20010
  // src/lib/github.ts
19963
20011
  import { execFileSync } from "child_process";
@@ -20042,7 +20090,7 @@ function compactWhitespace(value) {
20042
20090
  function fingerprintInboxInput(input) {
20043
20091
  const sourceType = input.source_type || detectInboxSourceType(input.body, input.source_url);
20044
20092
  const normalized = compactWhitespace(sanitizePreWriteText(input.body, "inbox.fingerprint")).slice(0, 8000);
20045
- return createHash6("sha256").update(`${sourceType}
20093
+ return createHash7("sha256").update(`${sourceType}
20046
20094
  ${input.source_url || ""}
20047
20095
  ${normalized}`).digest("hex");
20048
20096
  }
@@ -21159,7 +21207,7 @@ function importExternalIssues(input, db) {
21159
21207
  init_database();
21160
21208
  init_tasks();
21161
21209
  init_redaction();
21162
- import { createHash as createHash7 } from "crypto";
21210
+ import { createHash as createHash8 } from "crypto";
21163
21211
  var TESTERS_ISSUE_REPORT_SCHEMA_VERSION = "testers.issue_report.v1";
21164
21212
  var TESTERS_ISSUE_REPORT_RESULT_SCHEMA_VERSION = "todos.tester_issue_report_result.v1";
21165
21213
  var TESTERS_ISSUE_REPORT_BATCH_RESULT_SCHEMA_VERSION = "todos.tester_issue_report_batch_result.v1";
@@ -21348,7 +21396,7 @@ function fingerprintTesterIssueReport(report) {
21348
21396
  normalizeText2(report.failure?.message || report.summary || report.title).slice(0, 240),
21349
21397
  normalizeText2(stackTop).slice(0, 160)
21350
21398
  ].join("::");
21351
- return `testers:${createHash7("sha256").update(raw).digest("hex").slice(0, 16)}`;
21399
+ return `testers:${createHash8("sha256").update(raw).digest("hex").slice(0, 16)}`;
21352
21400
  }
21353
21401
  function priorityForSeverity(severity, fallback) {
21354
21402
  return PRIORITIES3.includes(severity) ? severity : fallback;
@@ -22800,7 +22848,7 @@ function renderLocalReportMarkdown(report) {
22800
22848
  init_config2();
22801
22849
  init_redaction();
22802
22850
  init_prewrite_secrets();
22803
- import { createCipheriv, createDecipheriv, createHash as createHash8, randomBytes, scryptSync, timingSafeEqual as timingSafeEqual2 } from "crypto";
22851
+ import { createCipheriv, createDecipheriv, createHash as createHash9, randomBytes, scryptSync, timingSafeEqual as timingSafeEqual2 } from "crypto";
22804
22852
  var TODOS_ENCRYPTED_VALUE_KIND = "hasna.todos.encrypted-value";
22805
22853
  var TODOS_ENCRYPTED_BRIDGE_KIND = "hasna.todos.encrypted-bridge";
22806
22854
  var TODOS_ENCRYPTION_SCHEMA_VERSION = 1;
@@ -22826,7 +22874,7 @@ function now3() {
22826
22874
  return new Date().toISOString();
22827
22875
  }
22828
22876
  function sha2564(value) {
22829
- return createHash8("sha256").update(value).digest("hex");
22877
+ return createHash9("sha256").update(value).digest("hex");
22830
22878
  }
22831
22879
  function normalizeProfileName(value) {
22832
22880
  const name = (value || DEFAULT_ENCRYPTION_PROFILE).trim();
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "packageName": "@hasna/todos",
3
- "packageVersion": "0.13.7",
3
+ "packageVersion": "0.13.9",
4
4
  "repository": "https://github.com/hasna/todos.git",
5
- "gitCommit": "aa3d008fc49ace7658ecef4863568757b74bb746",
6
- "gitTree": "39730eb0fa5cdc58f5433b152ac1c64a1b63eb81",
7
- "sourceTreeSha256": "e7e24d6bc56270c1cfabb0ed47b856cd2143b0ab1085c41a6de18a2eb1ff127b",
8
- "generatedAt": "2026-07-31T21:21:34.000Z"
5
+ "gitCommit": "0877f637ea98eb762347fc78d43d033cf4999ef6",
6
+ "gitTree": "458f99b7674b48d37f779b7bc1329369385b6ce5",
7
+ "sourceTreeSha256": "49de4d648f9049fe5fdab5edfb8d47e17b5c5241b655c203f17fe0046b02e07f",
8
+ "generatedAt": "2026-08-01T10:18:22.000Z"
9
9
  }