@hasna/todos 0.15.50 → 0.15.52

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/storage.js CHANGED
@@ -4282,23 +4282,118 @@ var init_identity_mapping = __esm(() => {
4282
4282
  });
4283
4283
  });
4284
4284
 
4285
- // src/lib/sync-utils.ts
4286
- import { existsSync as existsSync2, mkdirSync, readFileSync, readdirSync, statSync, writeFileSync } from "fs";
4287
- import { createHash } from "crypto";
4285
+ // node_modules/.bun/@hasna+paths@0.1.0/node_modules/@hasna/paths/dist/index.js
4288
4286
  import { homedir } from "os";
4289
4287
  import { join } from "path";
4288
+ function assertApp(app) {
4289
+ if (typeof app !== "string" || app.length === 0) {
4290
+ throw new TypeError("paths: app must be a non-empty string");
4291
+ }
4292
+ if (!APP_SLUG_RE.test(app)) {
4293
+ throw new TypeError(`paths: invalid app slug "${app}" \u2014 expected lowercase kebab-case ([a-z0-9]+(-[a-z0-9]+)*)`);
4294
+ }
4295
+ }
4296
+ function envOf(options) {
4297
+ return options.env ?? process.env;
4298
+ }
4299
+ function envValue(options, kind) {
4300
+ const value = envOf(options)[KIND_ENV[kind]];
4301
+ return typeof value === "string" && value.length > 0 ? value : undefined;
4302
+ }
4303
+ function isMacOS(platform) {
4304
+ return platform === "darwin";
4305
+ }
4306
+ function baseDir(kind, options) {
4307
+ const override = envValue(options, kind);
4308
+ if (override)
4309
+ return override;
4310
+ const home = options.home ?? homedir();
4311
+ const platform = options.platform ?? process.platform;
4312
+ if (isMacOS(platform)) {
4313
+ switch (kind) {
4314
+ case "config":
4315
+ case "data":
4316
+ return join(home, "Library", "Application Support", "Hasna");
4317
+ case "cache":
4318
+ return join(home, "Library", "Caches", "Hasna");
4319
+ case "state":
4320
+ return join(home, "Library", "Logs", "Hasna");
4321
+ }
4322
+ }
4323
+ switch (kind) {
4324
+ case "config":
4325
+ return join(home, ".config", "hasna");
4326
+ case "data":
4327
+ return join(home, ".local", "share", "hasna");
4328
+ case "state":
4329
+ return join(home, ".local", "state", "hasna");
4330
+ case "cache":
4331
+ return join(home, ".cache", "hasna");
4332
+ }
4333
+ }
4334
+ function resolvePath(kind, options) {
4335
+ assertApp(options.app);
4336
+ const appSegment = options.internal === true ? join("internal", options.app) : options.app;
4337
+ return join(baseDir(kind, options), appSegment);
4338
+ }
4339
+ function dataDir(options) {
4340
+ return resolvePath("data", options);
4341
+ }
4342
+ var KIND_ENV, APP_SLUG_RE;
4343
+ var init_dist = __esm(() => {
4344
+ KIND_ENV = {
4345
+ config: "HASNA_CONFIG_HOME",
4346
+ data: "HASNA_DATA_HOME",
4347
+ state: "HASNA_STATE_HOME",
4348
+ cache: "HASNA_CACHE_HOME"
4349
+ };
4350
+ APP_SLUG_RE = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
4351
+ });
4352
+
4353
+ // src/lib/paths.ts
4354
+ import { existsSync as existsSync2 } from "fs";
4355
+ import { homedir as homedir2 } from "os";
4356
+ import { join as join2, resolve as resolve2 } from "path";
4357
+ function effectiveHome(env = process.env) {
4358
+ return env.HOME || env.USERPROFILE || homedir2();
4359
+ }
4360
+ function legacyHomeDir(env = process.env) {
4361
+ return join2(effectiveHome(env), ".hasna", "todos");
4362
+ }
4363
+ function resolverHome(env = process.env) {
4364
+ return dataDir({ app: "todos", home: effectiveHome(env), env });
4365
+ }
4366
+ function adoptResolverHome(resolved, env = process.env) {
4367
+ const dataOverride = env.HASNA_DATA_HOME;
4368
+ if (typeof dataOverride === "string" && dataOverride.trim().length > 0)
4369
+ return true;
4370
+ return existsSync2(join2(resolved, "todos.db")) || existsSync2(join2(resolved, "config.json"));
4371
+ }
4372
+ function getTodosDir(env = process.env) {
4373
+ const resolved = resolverHome(env);
4374
+ return resolve2(adoptResolverHome(resolved, env) ? resolved : legacyHomeDir(env));
4375
+ }
4376
+ var init_paths = __esm(() => {
4377
+ init_dist();
4378
+ });
4379
+
4380
+ // src/lib/sync-utils.ts
4381
+ import { existsSync as existsSync3, mkdirSync, readFileSync, readdirSync, statSync, writeFileSync } from "fs";
4382
+ import { createHash } from "crypto";
4383
+ import { homedir as homedir3 } from "os";
4384
+ import { join as join3 } from "path";
4290
4385
  function getHomeDir() {
4291
- return process.env["HOME"] || process.env["USERPROFILE"] || homedir();
4386
+ return process.env["HOME"] || process.env["USERPROFILE"] || homedir3();
4292
4387
  }
4293
4388
  function getTodosGlobalDir() {
4294
- return join(getHomeDir(), ".hasna", "todos");
4389
+ return getTodosDir();
4295
4390
  }
4296
4391
  function ensureDir(dir) {
4297
- if (!existsSync2(dir))
4392
+ if (!existsSync3(dir))
4298
4393
  mkdirSync(dir, { recursive: true });
4299
4394
  }
4300
4395
  function listJsonFiles(dir) {
4301
- if (!existsSync2(dir))
4396
+ if (!existsSync3(dir))
4302
4397
  return [];
4303
4398
  return readdirSync(dir).filter((f) => f.endsWith(".json"));
4304
4399
  }
@@ -4314,14 +4409,14 @@ function writeJsonFile(path, data) {
4314
4409
  `);
4315
4410
  }
4316
4411
  function readHighWaterMark(dir) {
4317
- const path = join(dir, ".highwatermark");
4318
- if (!existsSync2(path))
4412
+ const path = join3(dir, ".highwatermark");
4413
+ if (!existsSync3(path))
4319
4414
  return 1;
4320
4415
  const val = parseInt(readFileSync(path, "utf-8").trim(), 10);
4321
4416
  return isNaN(val) ? 1 : val;
4322
4417
  }
4323
4418
  function writeHighWaterMark(dir, value) {
4324
- writeFileSync(join(dir, ".highwatermark"), String(value));
4419
+ writeFileSync(join3(dir, ".highwatermark"), String(value));
4325
4420
  }
4326
4421
  function getFileMtimeMs(path) {
4327
4422
  try {
@@ -4376,6 +4471,7 @@ function hasSyncFingerprintChanged(record) {
4376
4471
  }
4377
4472
  var TODO_SYNC_FINGERPRINT_KEY = "todos_sync_fingerprint", HOME;
4378
4473
  var init_sync_utils = __esm(() => {
4474
+ init_paths();
4379
4475
  HOME = getHomeDir();
4380
4476
  });
4381
4477
 
@@ -4479,18 +4575,18 @@ __export(exports_database, {
4479
4575
  LOCK_EXPIRY_MINUTES: () => LOCK_EXPIRY_MINUTES
4480
4576
  });
4481
4577
  import { Database } from "bun:sqlite";
4482
- import { existsSync as existsSync3, mkdirSync as mkdirSync2 } from "fs";
4483
- import { dirname, join as join2, resolve as resolve2 } from "path";
4578
+ import { existsSync as existsSync4, mkdirSync as mkdirSync2 } from "fs";
4579
+ import { dirname, join as join4, resolve as resolve3 } from "path";
4484
4580
  function isInMemoryDb(path) {
4485
4581
  return path === ":memory:" || path.startsWith("file::memory:");
4486
4582
  }
4487
4583
  function findNearestProjectDb(startDir) {
4488
4584
  const gitRoot = findGitRoot(startDir);
4489
- const stopAt = gitRoot ? resolve2(gitRoot) : resolve2(startDir);
4490
- let dir = resolve2(startDir);
4585
+ const stopAt = gitRoot ? resolve3(gitRoot) : resolve3(startDir);
4586
+ let dir = resolve3(startDir);
4491
4587
  while (true) {
4492
- const candidate = join2(dir, ".hasna", "todos", "todos.db");
4493
- if (existsSync3(candidate))
4588
+ const candidate = join4(dir, ".hasna", "todos", "todos.db");
4589
+ if (existsSync4(candidate))
4494
4590
  return candidate;
4495
4591
  if (dir === stopAt)
4496
4592
  break;
@@ -4502,9 +4598,9 @@ function findNearestProjectDb(startDir) {
4502
4598
  return null;
4503
4599
  }
4504
4600
  function findGitRoot(startDir) {
4505
- let dir = resolve2(startDir);
4601
+ let dir = resolve3(startDir);
4506
4602
  while (true) {
4507
- if (existsSync3(join2(dir, ".git")))
4603
+ if (existsSync4(join4(dir, ".git")))
4508
4604
  return dir;
4509
4605
  const parent = dirname(dir);
4510
4606
  if (parent === dir)
@@ -4514,7 +4610,7 @@ function findGitRoot(startDir) {
4514
4610
  return null;
4515
4611
  }
4516
4612
  function getGlobalDbPath() {
4517
- return join2(getHomeDir(), ".hasna", "todos", "todos.db");
4613
+ return join4(getTodosGlobalDir(), "todos.db");
4518
4614
  }
4519
4615
  function hasExplicitProjectArg(args = process.argv.slice(2)) {
4520
4616
  return args.some((arg) => arg === "--project" || arg.startsWith("--project="));
@@ -4552,7 +4648,7 @@ function getDbPath() {
4552
4648
  if (process.env["TODOS_DB_SCOPE"] === "project") {
4553
4649
  const gitRoot = findGitRoot(cwd);
4554
4650
  if (gitRoot && canCreateScopedProjectDb()) {
4555
- return join2(gitRoot, ".hasna", "todos", "todos.db");
4651
+ return join4(gitRoot, ".hasna", "todos", "todos.db");
4556
4652
  }
4557
4653
  }
4558
4654
  return getGlobalDbPath();
@@ -4563,8 +4659,8 @@ function getDatabasePath() {
4563
4659
  function ensureDir2(filePath) {
4564
4660
  if (isInMemoryDb(filePath))
4565
4661
  return;
4566
- const dir = dirname(resolve2(filePath));
4567
- if (!existsSync3(dir)) {
4662
+ const dir = dirname(resolve3(filePath));
4663
+ if (!existsSync4(dir)) {
4568
4664
  mkdirSync2(dir, { recursive: true });
4569
4665
  }
4570
4666
  }
@@ -4741,10 +4837,10 @@ var init_database = __esm(() => {
4741
4837
  });
4742
4838
 
4743
4839
  // src/lib/config.ts
4744
- import { existsSync as existsSync4, readFileSync as readFileSync2 } from "fs";
4745
- import { dirname as dirname2, join as join3 } from "path";
4840
+ import { existsSync as existsSync5, readFileSync as readFileSync2 } from "fs";
4841
+ import { dirname as dirname2, join as join5 } from "path";
4746
4842
  function getConfigPath() {
4747
- return join3(getTodosGlobalDir(), "config.json");
4843
+ return join5(getTodosGlobalDir(), "config.json");
4748
4844
  }
4749
4845
  function normalizeAgent(agent) {
4750
4846
  return agent.trim().toLowerCase();
@@ -4752,7 +4848,7 @@ function normalizeAgent(agent) {
4752
4848
  function loadConfig() {
4753
4849
  if (cached)
4754
4850
  return cached;
4755
- if (!existsSync4(getConfigPath())) {
4851
+ if (!existsSync5(getConfigPath())) {
4756
4852
  cached = {};
4757
4853
  return cached;
4758
4854
  }
@@ -4775,7 +4871,7 @@ function updateConfig(patch) {
4775
4871
  }
4776
4872
  function getTodosAiConfig() {
4777
4873
  const configPath = getConfigPath();
4778
- if (!existsSync4(configPath))
4874
+ if (!existsSync5(configPath))
4779
4875
  return {};
4780
4876
  const parsed = JSON.parse(readFileSync2(configPath, "utf8"));
4781
4877
  if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
@@ -5393,14 +5489,14 @@ var init_completion_guard = __esm(() => {
5393
5489
 
5394
5490
  // src/lib/event-emission-safety.ts
5395
5491
  import { tmpdir } from "os";
5396
- import { resolve as resolve3, sep } from "path";
5492
+ import { resolve as resolve4, sep } from "path";
5397
5493
  function envFlag(name) {
5398
5494
  const value = process.env[name]?.trim().toLowerCase();
5399
5495
  return value === "1" || value === "true" || value === "yes" || value === "on";
5400
5496
  }
5401
5497
  function isUnder(parent, child) {
5402
- const normalizedParent = resolve3(parent);
5403
- const normalizedChild = resolve3(child);
5498
+ const normalizedParent = resolve4(parent);
5499
+ const normalizedChild = resolve4(child);
5404
5500
  return normalizedChild === normalizedParent || normalizedChild.startsWith(`${normalizedParent}${sep}`);
5405
5501
  }
5406
5502
  function databasePathFromDatabase(db) {
@@ -5561,9 +5657,9 @@ var init_redaction = __esm(() => {
5561
5657
  });
5562
5658
 
5563
5659
  // src/lib/workspace-trust.ts
5564
- import { relative, resolve as resolve4 } from "path";
5660
+ import { relative, resolve as resolve5 } from "path";
5565
5661
  function normalizePath(path) {
5566
- return resolve4(path);
5662
+ return resolve5(path);
5567
5663
  }
5568
5664
  function unique2(values) {
5569
5665
  return Array.from(new Set((values || []).map((value) => value.trim()).filter(Boolean)));
@@ -5745,9 +5841,9 @@ var init_workspace_trust = __esm(() => {
5745
5841
  });
5746
5842
 
5747
5843
  // src/lib/runner-sandbox.ts
5748
- import { relative as relative2, resolve as resolve5 } from "path";
5844
+ import { relative as relative2, resolve as resolve6 } from "path";
5749
5845
  function normalizePath2(path) {
5750
- return resolve5(path);
5846
+ return resolve6(path);
5751
5847
  }
5752
5848
  function unique3(values) {
5753
5849
  return Array.from(new Set((values || []).map((value) => value.trim()).filter(Boolean)));
@@ -5940,7 +6036,7 @@ var init_runner_sandbox = __esm(() => {
5940
6036
  // src/lib/event-hooks.ts
5941
6037
  import { createHash as createHash2, randomUUID } from "crypto";
5942
6038
  import { appendFileSync, mkdirSync as mkdirSync3 } from "fs";
5943
- import { dirname as dirname3, resolve as resolve6 } from "path";
6039
+ import { dirname as dirname3, resolve as resolve7 } from "path";
5944
6040
  import { createConnection } from "net";
5945
6041
  function safeName(name) {
5946
6042
  const trimmed = name.trim();
@@ -6078,7 +6174,7 @@ async function deliverHook(hook, envelope) {
6078
6174
  if (hook.target === "stdout") {
6079
6175
  output = line.trim();
6080
6176
  } else if (hook.target === "file") {
6081
- const filePath = resolve6(hook.file_path);
6177
+ const filePath = resolve7(hook.file_path);
6082
6178
  mkdirSync3(dirname3(filePath), { recursive: true });
6083
6179
  appendFileSync(filePath, line);
6084
6180
  } else if (hook.target === "socket") {
@@ -6199,9 +6295,9 @@ var init_event_hooks = __esm(() => {
6199
6295
  // node_modules/.bun/@hasna+events@0.1.16/node_modules/@hasna/events/dist/index.js
6200
6296
  import { chmod, mkdir, readFile, rename, writeFile } from "fs/promises";
6201
6297
  import { Buffer as Buffer2 } from "buffer";
6202
- import { existsSync as existsSync5 } from "fs";
6203
- import { homedir as homedir2 } from "os";
6204
- import { join as join4 } from "path";
6298
+ import { existsSync as existsSync6 } from "fs";
6299
+ import { homedir as homedir4 } from "os";
6300
+ import { join as join6 } from "path";
6205
6301
  import { createHmac, timingSafeEqual } from "crypto";
6206
6302
  import { randomUUID as randomUUID2 } from "crypto";
6207
6303
  import { spawn } from "child_process";
@@ -6302,7 +6398,7 @@ function channelMatchesEvent(channel, event) {
6302
6398
  return channel.filters.some((filter) => eventMatchesFilter(event, filter));
6303
6399
  }
6304
6400
  function getEventsDataDir(override) {
6305
- return override || process.env[HASNA_EVENTS_DIR_ENV] || process.env[HASNA_EVENTS_HOME_ENV] || join4(homedir2(), ".hasna", "events");
6401
+ return override || process.env[HASNA_EVENTS_DIR_ENV] || process.env[HASNA_EVENTS_HOME_ENV] || join6(homedir4(), ".hasna", "events");
6306
6402
  }
6307
6403
 
6308
6404
  class JsonEventsStore {
@@ -6311,12 +6407,12 @@ class JsonEventsStore {
6311
6407
  channelsPath;
6312
6408
  eventsPath;
6313
6409
  deliveriesPath;
6314
- constructor(dataDir = getEventsDataDir()) {
6315
- this.dataDir = dataDir;
6316
- this.runtime = localJsonRuntime(dataDir);
6317
- this.channelsPath = join4(dataDir, "channels.json");
6318
- this.eventsPath = join4(dataDir, "events.json");
6319
- this.deliveriesPath = join4(dataDir, "deliveries.json");
6410
+ constructor(dataDir2 = getEventsDataDir()) {
6411
+ this.dataDir = dataDir2;
6412
+ this.runtime = localJsonRuntime(dataDir2);
6413
+ this.channelsPath = join6(dataDir2, "channels.json");
6414
+ this.eventsPath = join6(dataDir2, "events.json");
6415
+ this.deliveriesPath = join6(dataDir2, "deliveries.json");
6320
6416
  }
6321
6417
  async init() {
6322
6418
  await mkdir(this.dataDir, { recursive: true, mode: 448 });
@@ -6433,7 +6529,7 @@ class JsonEventsStore {
6433
6529
  };
6434
6530
  }
6435
6531
  async ensureArrayFile(path) {
6436
- if (!existsSync5(path)) {
6532
+ if (!existsSync6(path)) {
6437
6533
  await writeFile(path, `[]
6438
6534
  `, { encoding: "utf-8", mode: 384 });
6439
6535
  }
@@ -6463,7 +6559,7 @@ class JsonEventsStore {
6463
6559
  });
6464
6560
  }
6465
6561
  }
6466
- function localJsonRuntime(dataDir = getEventsDataDir()) {
6562
+ function localJsonRuntime(dataDir2 = getEventsDataDir()) {
6467
6563
  return {
6468
6564
  mode: "local-files",
6469
6565
  name: "json-events-store",
@@ -6476,7 +6572,7 @@ function localJsonRuntime(dataDir = getEventsDataDir()) {
6476
6572
  durable: true,
6477
6573
  idempotency: "best-effort-local",
6478
6574
  replayCursors: true,
6479
- description: `Local JSON files in ${dataDir}; no SQLite, Postgres, S3, or AWS runtime is configured by this store.`
6575
+ description: `Local JSON files in ${dataDir2}; no SQLite, Postgres, S3, or AWS runtime is configured by this store.`
6480
6576
  };
6481
6577
  }
6482
6578
  function encodeLocalJsonEventCursor(offset, options = {}) {
@@ -6655,7 +6751,7 @@ async function dispatchCommand(event, channel) {
6655
6751
  HASNA_EVENT_SCHEMA_VERSION: event.schemaVersion,
6656
6752
  HASNA_EVENT_JSON: eventJson
6657
6753
  };
6658
- return new Promise((resolve7) => {
6754
+ return new Promise((resolve8) => {
6659
6755
  const child = spawn(channel.command.command, channel.command.args ?? [], {
6660
6756
  cwd: channel.command.cwd,
6661
6757
  env,
@@ -6673,7 +6769,7 @@ async function dispatchCommand(event, channel) {
6673
6769
  });
6674
6770
  child.on("error", (error) => {
6675
6771
  clearTimeout(timeout);
6676
- resolve7({
6772
+ resolve8({
6677
6773
  attempt: 1,
6678
6774
  status: "failed",
6679
6775
  startedAt,
@@ -6686,7 +6782,7 @@ async function dispatchCommand(event, channel) {
6686
6782
  child.on("close", (code, signal) => {
6687
6783
  clearTimeout(timeout);
6688
6784
  const success = code === 0;
6689
- resolve7({
6785
+ resolve8({
6690
6786
  attempt: 1,
6691
6787
  status: success ? "success" : "failed",
6692
6788
  startedAt,
@@ -7028,7 +7124,7 @@ function normalizeRetryPolicy(policy) {
7028
7124
  };
7029
7125
  }
7030
7126
  var HASNA_EVENTS_DIR_ENV = "HASNA_EVENTS_DIR", HASNA_EVENTS_HOME_ENV = "HASNA_EVENTS_HOME", LOCAL_JSON_EVENT_CURSOR_PREFIX = "local-json-v1:", DEFAULT_EVENT_PAGE_LIMIT = 100, MAX_EVENT_PAGE_LIMIT = 1000, DEFAULT_SIGNATURE_TOLERANCE_MS, EventValidationError, defaultEventTypeCatalog, APP_EVENT_V1_MAX_DATA_BYTES;
7031
- var init_dist = __esm(() => {
7127
+ var init_dist2 = __esm(() => {
7032
7128
  DEFAULT_SIGNATURE_TOLERANCE_MS = 5 * 60 * 1000;
7033
7129
  EventValidationError = class EventValidationError extends Error {
7034
7130
  eventType;
@@ -7451,7 +7547,7 @@ function emitSharedTaskEventQuiet(input) {
7451
7547
  }
7452
7548
  var SOURCE = "todos";
7453
7549
  var init_shared_events = __esm(() => {
7454
- init_dist();
7550
+ init_dist2();
7455
7551
  init_database();
7456
7552
  init_projects();
7457
7553
  init_task_lists();
@@ -7459,7 +7555,7 @@ var init_shared_events = __esm(() => {
7459
7555
  });
7460
7556
 
7461
7557
  // src/lib/secret-redaction.ts
7462
- import { readFileSync as readFileSync3, existsSync as existsSync6 } from "fs";
7558
+ import { readFileSync as readFileSync3, existsSync as existsSync7 } from "fs";
7463
7559
  function registerCustomRedactor(fn) {
7464
7560
  customRedactors.push(fn);
7465
7561
  }
@@ -7524,7 +7620,7 @@ function scanAndRedactText(text, options = {}) {
7524
7620
  };
7525
7621
  }
7526
7622
  function scanFileForSecrets(path, options = {}) {
7527
- if (!existsSync6(path))
7623
+ if (!existsSync7(path))
7528
7624
  throw new Error(`File not found: ${path}`);
7529
7625
  const content = readFileSync3(path, "utf8");
7530
7626
  return scanAndRedactText(content, options);
@@ -9782,17 +9878,17 @@ function sanitizeCreateTaskInput(input) {
9782
9878
  return {
9783
9879
  ...input,
9784
9880
  title: sanitizePreWriteText(input.title, "task.title"),
9785
- description: input.description !== undefined ? sanitizePreWriteText(input.description, "task.description") : undefined,
9881
+ description: input.description == null ? input.description : sanitizePreWriteText(input.description, "task.description"),
9786
9882
  tags: input.tags !== undefined ? sanitizePreWriteValue(input.tags, "task.tags") : undefined,
9787
9883
  metadata: input.metadata !== undefined ? sanitizePreWriteValue(input.metadata, "task.metadata") : undefined,
9788
- reason: input.reason !== undefined ? sanitizePreWriteText(input.reason, "task.reason") : undefined
9884
+ reason: input.reason == null ? input.reason : sanitizePreWriteText(input.reason, "task.reason")
9789
9885
  };
9790
9886
  }
9791
9887
  function sanitizeUpdateTaskInput(input) {
9792
9888
  return {
9793
9889
  ...input,
9794
9890
  title: input.title !== undefined ? sanitizePreWriteText(input.title, "task.title") : undefined,
9795
- description: input.description !== undefined ? sanitizePreWriteText(input.description, "task.description") : undefined,
9891
+ description: input.description == null ? input.description : sanitizePreWriteText(input.description, "task.description"),
9796
9892
  tags: input.tags !== undefined ? sanitizePreWriteValue(input.tags, "task.tags") : undefined,
9797
9893
  metadata: input.metadata !== undefined ? sanitizePreWriteValue(input.metadata, "task.metadata") : undefined
9798
9894
  };
@@ -11646,28 +11742,28 @@ var init_boards = __esm(() => {
11646
11742
 
11647
11743
  // src/lib/artifact-store.ts
11648
11744
  import { createHash as createHash3 } from "crypto";
11649
- import { existsSync as existsSync7, mkdirSync as mkdirSync4, readFileSync as readFileSync4, rmSync, statSync as statSync2, writeFileSync as writeFileSync2 } from "fs";
11650
- import { basename, dirname as dirname4, join as join5, resolve as resolve7 } from "path";
11745
+ import { existsSync as existsSync8, mkdirSync as mkdirSync4, readFileSync as readFileSync4, rmSync, statSync as statSync2, writeFileSync as writeFileSync2 } from "fs";
11746
+ import { basename, dirname as dirname4, join as join7, resolve as resolve8 } from "path";
11651
11747
  import { tmpdir as tmpdir2 } from "os";
11652
11748
  function isInMemoryDb2(path) {
11653
11749
  return path === ":memory:" || path.startsWith("file::memory:");
11654
11750
  }
11655
11751
  function artifactStoreRoot() {
11656
11752
  if (process.env["HASNA_TODOS_ARTIFACTS_DIR"])
11657
- return resolve7(process.env["HASNA_TODOS_ARTIFACTS_DIR"]);
11753
+ return resolve8(process.env["HASNA_TODOS_ARTIFACTS_DIR"]);
11658
11754
  if (process.env["TODOS_ARTIFACTS_DIR"])
11659
- return resolve7(process.env["TODOS_ARTIFACTS_DIR"]);
11755
+ return resolve8(process.env["TODOS_ARTIFACTS_DIR"]);
11660
11756
  const dbPath = getDatabasePath();
11661
11757
  if (isInMemoryDb2(dbPath))
11662
- return join5(tmpdir2(), "hasna-todos-artifacts");
11663
- return join5(dirname4(resolve7(dbPath)), "artifacts");
11758
+ return join7(tmpdir2(), "hasna-todos-artifacts");
11759
+ return join7(dirname4(resolve8(dbPath)), "artifacts");
11664
11760
  }
11665
11761
  function artifactStorePath(relativePath) {
11666
11762
  const normalized = relativePath.replace(/\\/g, "/");
11667
11763
  if (normalized.includes("..") || normalized.startsWith("/") || normalized.length === 0) {
11668
11764
  throw new Error("Invalid artifact store path");
11669
11765
  }
11670
- return join5(artifactStoreRoot(), normalized);
11766
+ return join7(artifactStoreRoot(), normalized);
11671
11767
  }
11672
11768
  function sha256(buffer) {
11673
11769
  return createHash3("sha256").update(buffer).digest("hex");
@@ -11707,8 +11803,8 @@ function mediaTypeFor(path, textLike) {
11707
11803
  return "application/octet-stream";
11708
11804
  }
11709
11805
  function storeArtifactContent(input) {
11710
- const sourcePath = resolve7(input.path);
11711
- if (!existsSync7(sourcePath))
11806
+ const sourcePath = resolve8(input.path);
11807
+ if (!existsSync8(sourcePath))
11712
11808
  return null;
11713
11809
  const sourceStat = statSync2(sourcePath);
11714
11810
  if (!sourceStat.isFile())
@@ -11725,9 +11821,9 @@ function storeArtifactContent(input) {
11725
11821
  redactionStatus = "redacted";
11726
11822
  }
11727
11823
  const storedSha = sha256(storedBuffer);
11728
- const relativePath = join5("sha256", storedSha.slice(0, 2), storedSha).replace(/\\/g, "/");
11824
+ const relativePath = join7("sha256", storedSha.slice(0, 2), storedSha).replace(/\\/g, "/");
11729
11825
  const destination = artifactStorePath(relativePath);
11730
- if (!existsSync7(destination)) {
11826
+ if (!existsSync8(destination)) {
11731
11827
  mkdirSync4(dirname4(destination), { recursive: true });
11732
11828
  writeFileSync2(destination, storedBuffer);
11733
11829
  }
@@ -11787,7 +11883,7 @@ function verifyStoredArtifact(input) {
11787
11883
  };
11788
11884
  }
11789
11885
  const storedPath = artifactStorePath(store.relative_path);
11790
- if (!existsSync7(storedPath)) {
11886
+ if (!existsSync8(storedPath)) {
11791
11887
  return {
11792
11888
  id: input.id,
11793
11889
  path: input.path,
@@ -11862,20 +11958,20 @@ function importStoredArtifactContent(content) {
11862
11958
  }
11863
11959
  function getArtifactStoreRoot(dbPath) {
11864
11960
  if (process.env["HASNA_TODOS_ARTIFACTS_DIR"])
11865
- return resolve7(process.env["HASNA_TODOS_ARTIFACTS_DIR"]);
11961
+ return resolve8(process.env["HASNA_TODOS_ARTIFACTS_DIR"]);
11866
11962
  if (process.env["TODOS_ARTIFACTS_DIR"])
11867
- return resolve7(process.env["TODOS_ARTIFACTS_DIR"]);
11963
+ return resolve8(process.env["TODOS_ARTIFACTS_DIR"]);
11868
11964
  const path = dbPath ?? getDatabasePath();
11869
11965
  if (isInMemoryDb2(path))
11870
- return join5(tmpdir2(), "hasna-todos-artifacts");
11871
- return join5(dirname4(resolve7(path)), "artifacts");
11966
+ return join7(tmpdir2(), "hasna-todos-artifacts");
11967
+ return join7(dirname4(resolve8(path)), "artifacts");
11872
11968
  }
11873
11969
  function computeContentHash(path) {
11874
- return sha256(readFileSync4(resolve7(path)));
11970
+ return sha256(readFileSync4(resolve8(path)));
11875
11971
  }
11876
11972
  function storeArtifactFile(input) {
11877
- const sourcePath = resolve7(input.sourcePath);
11878
- if (!existsSync7(sourcePath)) {
11973
+ const sourcePath = resolve8(input.sourcePath);
11974
+ if (!existsSync8(sourcePath)) {
11879
11975
  throw new Error(`Source file not found: ${input.sourcePath}`);
11880
11976
  }
11881
11977
  if (!statSync2(sourcePath).isFile()) {
@@ -11888,7 +11984,7 @@ function storeArtifactFile(input) {
11888
11984
  let localPath = sourcePath;
11889
11985
  if (storageMode === "copy") {
11890
11986
  const fileName = input.name && input.name.trim().length > 0 ? basename(input.name) : basename(sourcePath);
11891
- const destination = join5(getArtifactStoreRoot(input.dbPath), input.artifactId, fileName);
11987
+ const destination = join7(getArtifactStoreRoot(input.dbPath), input.artifactId, fileName);
11892
11988
  mkdirSync4(dirname4(destination), { recursive: true });
11893
11989
  writeFileSync2(destination, buffer);
11894
11990
  localPath = destination;
@@ -11898,7 +11994,7 @@ function storeArtifactFile(input) {
11898
11994
  function deleteStoredArtifactFile(localPath, storageMode, _dbPath2) {
11899
11995
  if (storageMode === "reference")
11900
11996
  return false;
11901
- if (!localPath || !existsSync7(localPath))
11997
+ if (!localPath || !existsSync8(localPath))
11902
11998
  return false;
11903
11999
  rmSync(localPath, { force: true });
11904
12000
  try {
@@ -11923,7 +12019,7 @@ function buildArtifactExportManifest(artifacts, dbPath) {
11923
12019
  };
11924
12020
  }
11925
12021
  function writeArtifactExportManifest(manifest, outputPath) {
11926
- const destination = resolve7(outputPath);
12022
+ const destination = resolve8(outputPath);
11927
12023
  mkdirSync4(dirname4(destination), { recursive: true });
11928
12024
  writeFileSync2(destination, `${JSON.stringify(manifest, null, 2)}
11929
12025
  `);
@@ -18210,7 +18306,7 @@ async function retryOnTransientPostgresError(fn, attempts = 2, delayMs = 150) {
18210
18306
  lastError = error;
18211
18307
  if (!isTransientPostgresError(error) || attempt === attempts)
18212
18308
  throw error;
18213
- await new Promise((resolve8) => setTimeout(resolve8, delayMs * attempt));
18309
+ await new Promise((resolve9) => setTimeout(resolve9, delayMs * attempt));
18214
18310
  }
18215
18311
  }
18216
18312
  throw lastError;
@@ -18311,8 +18407,8 @@ class TodosShadowMirror {
18311
18407
  async flush() {
18312
18408
  if (this.idle())
18313
18409
  return;
18314
- await new Promise((resolve8) => {
18315
- this.idleResolvers.push(resolve8);
18410
+ await new Promise((resolve9) => {
18411
+ this.idleResolvers.push(resolve9);
18316
18412
  this.pump();
18317
18413
  });
18318
18414
  }
@@ -18324,8 +18420,8 @@ class TodosShadowMirror {
18324
18420
  return;
18325
18421
  const resolvers = this.idleResolvers;
18326
18422
  this.idleResolvers = [];
18327
- for (const resolve8 of resolvers)
18328
- resolve8();
18423
+ for (const resolve9 of resolvers)
18424
+ resolve9();
18329
18425
  }
18330
18426
  pump() {
18331
18427
  if (this.pumping)
@@ -18804,7 +18900,7 @@ class TodosShadowOutbox {
18804
18900
  const remaining = deadline - Date.now();
18805
18901
  if (remaining <= 0)
18806
18902
  break;
18807
- await new Promise((resolve8) => setTimeout(resolve8, Math.min(200, remaining)));
18903
+ await new Promise((resolve9) => setTimeout(resolve9, Math.min(200, remaining)));
18808
18904
  }
18809
18905
  }
18810
18906
  return this.getStats();