@hasna/todos 0.11.57 → 0.11.58

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -6878,7 +6878,7 @@ async function testLocalEventHook(name, input) {
6878
6878
  return emitLocalEventHooks({ ...input, hooks: [hook] });
6879
6879
  }
6880
6880
 
6881
- // node_modules/.bun/@hasna+events@0.1.7/node_modules/@hasna/events/dist/index.js
6881
+ // node_modules/.bun/@hasna+events@0.1.9/node_modules/@hasna/events/dist/index.js
6882
6882
  import { chmod, mkdir, readFile, rename, writeFile } from "fs/promises";
6883
6883
  import { existsSync as existsSync6 } from "fs";
6884
6884
  import { homedir } from "os";
@@ -6895,17 +6895,30 @@ function getPathValue(input, path) {
6895
6895
  return;
6896
6896
  }, input);
6897
6897
  }
6898
- function wildcardToRegExp(pattern) {
6899
- const escaped = pattern.replace(/[|\\{}()[\]^$+?.]/g, "\\$&").replace(/\*/g, ".*");
6900
- return new RegExp(`^${escaped}$`);
6898
+ function wildcardToRegExp(pattern, options = {}) {
6899
+ let body = "";
6900
+ for (let index = 0;index < pattern.length; index += 1) {
6901
+ const char = pattern[index];
6902
+ if (char === "*") {
6903
+ if (pattern[index + 1] === "*") {
6904
+ body += ".*";
6905
+ index += 1;
6906
+ } else {
6907
+ body += options.segmentSafe ? "[^/]*" : ".*";
6908
+ }
6909
+ } else {
6910
+ body += char.replace(/[|\\{}()[\]^$+?.]/g, "\\$&");
6911
+ }
6912
+ }
6913
+ return new RegExp(`^${body}$`);
6901
6914
  }
6902
- function matchString(value, matcher) {
6915
+ function matchString(value, matcher, options = {}) {
6903
6916
  if (matcher === undefined)
6904
6917
  return true;
6905
6918
  if (value === undefined)
6906
6919
  return false;
6907
6920
  const matchers = Array.isArray(matcher) ? matcher : [matcher];
6908
- return matchers.some((item) => wildcardToRegExp(item).test(value));
6921
+ return matchers.some((item) => wildcardToRegExp(item, options).test(value));
6909
6922
  }
6910
6923
  function matchRecord(input, matcher) {
6911
6924
  if (!matcher)
@@ -6913,7 +6926,9 @@ function matchRecord(input, matcher) {
6913
6926
  return Object.entries(matcher).every(([path, expected]) => {
6914
6927
  const actual = getPathValue(input, path);
6915
6928
  if (typeof expected === "string" || Array.isArray(expected)) {
6916
- return matchString(actual === undefined ? undefined : String(actual), expected);
6929
+ return matchString(actual === undefined ? undefined : String(actual), expected, {
6930
+ segmentSafe: path.endsWith("_path") || path.endsWith(".path")
6931
+ });
6917
6932
  }
6918
6933
  return actual === expected;
6919
6934
  });
@@ -6933,7 +6948,6 @@ var HASNA_EVENTS_HOME_ENV = "HASNA_EVENTS_HOME";
6933
6948
  function getEventsDataDir(override) {
6934
6949
  return override || process.env[HASNA_EVENTS_DIR_ENV] || process.env[HASNA_EVENTS_HOME_ENV] || join5(homedir(), ".hasna", "events");
6935
6950
  }
6936
-
6937
6951
  class JsonEventsStore {
6938
6952
  dataDir;
6939
6953
  channelsPath;
@@ -7270,7 +7284,7 @@ class EventsClient {
7270
7284
  }
7271
7285
  return deliveries;
7272
7286
  }
7273
- async testChannel(id, input = {}) {
7287
+ async matchChannel(id, input = {}) {
7274
7288
  const channel = await this.store.getChannel(id);
7275
7289
  if (!channel)
7276
7290
  throw new Error(`Channel not found: ${id}`);
@@ -7287,6 +7301,34 @@ class EventsClient {
7287
7301
  time: input.time,
7288
7302
  id: input.id
7289
7303
  });
7304
+ const matched = channelMatchesEvent(channel, event);
7305
+ return {
7306
+ channelId: channel.id,
7307
+ matched,
7308
+ event,
7309
+ filters: channel.filters,
7310
+ reason: matched ? undefined : channel.enabled ? "event did not match channel filters" : "channel is disabled"
7311
+ };
7312
+ }
7313
+ async testChannel(id, input = {}, options = {}) {
7314
+ const channel = await this.store.getChannel(id);
7315
+ if (!channel)
7316
+ throw new Error(`Channel not found: ${id}`);
7317
+ const match = await this.matchChannel(id, input);
7318
+ const event = match.event;
7319
+ if (options.honorFilters && !match.matched) {
7320
+ const timestamp = new Date().toISOString();
7321
+ const result2 = createDeliveryResult(event, channel, [{
7322
+ attempt: 1,
7323
+ status: "skipped",
7324
+ startedAt: timestamp,
7325
+ completedAt: timestamp,
7326
+ error: match.reason
7327
+ }]);
7328
+ result2.metadata = { reason: "filter_mismatch" };
7329
+ await this.store.appendDelivery(result2);
7330
+ return result2;
7331
+ }
7290
7332
  const eventForChannel = await this.applyRedaction(event, channel);
7291
7333
  const result = await this.deliverWithRetry(eventForChannel, channel);
7292
7334
  await this.store.appendDelivery(result);
@@ -7386,6 +7428,90 @@ function normalizeRetryPolicy(policy) {
7386
7428
  };
7387
7429
  }
7388
7430
 
7431
+ // src/lib/shared-events.ts
7432
+ init_database();
7433
+
7434
+ // src/db/task-lists.ts
7435
+ init_types();
7436
+ init_database();
7437
+ function rowToTaskList(row) {
7438
+ return {
7439
+ ...row,
7440
+ metadata: JSON.parse(row.metadata || "{}")
7441
+ };
7442
+ }
7443
+ function createTaskList(input, db) {
7444
+ const d = db || getDatabase();
7445
+ const id = uuid();
7446
+ const timestamp = now();
7447
+ const slug = input.slug || slugify(input.name);
7448
+ if (!input.project_id) {
7449
+ const existing = d.query("SELECT id FROM task_lists WHERE project_id IS NULL AND slug = ?").get(slug);
7450
+ if (existing) {
7451
+ throw new Error(`Standalone task list with slug "${slug}" already exists`);
7452
+ }
7453
+ }
7454
+ d.run(`INSERT INTO task_lists (id, project_id, slug, name, description, metadata, created_at, updated_at)
7455
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?)`, [id, input.project_id || null, slug, input.name, input.description || null, JSON.stringify(input.metadata || {}), timestamp, timestamp]);
7456
+ return getTaskList(id, d);
7457
+ }
7458
+ function getTaskList(id, db) {
7459
+ const d = db || getDatabase();
7460
+ const row = d.query("SELECT * FROM task_lists WHERE id = ?").get(id);
7461
+ return row ? rowToTaskList(row) : null;
7462
+ }
7463
+ function getTaskListBySlug(slug, projectId, db) {
7464
+ const d = db || getDatabase();
7465
+ let row;
7466
+ if (projectId) {
7467
+ row = d.query("SELECT * FROM task_lists WHERE slug = ? AND project_id = ?").get(slug, projectId);
7468
+ } else {
7469
+ row = d.query("SELECT * FROM task_lists WHERE slug = ? AND project_id IS NULL").get(slug);
7470
+ }
7471
+ return row ? rowToTaskList(row) : null;
7472
+ }
7473
+ function listTaskLists(projectId, db) {
7474
+ const d = db || getDatabase();
7475
+ if (projectId) {
7476
+ return d.query("SELECT * FROM task_lists WHERE project_id = ? ORDER BY name").all(projectId).map(rowToTaskList);
7477
+ }
7478
+ return d.query("SELECT * FROM task_lists ORDER BY name").all().map(rowToTaskList);
7479
+ }
7480
+ function updateTaskList(id, input, db) {
7481
+ const d = db || getDatabase();
7482
+ const existing = getTaskList(id, d);
7483
+ if (!existing)
7484
+ throw new TaskListNotFoundError(id);
7485
+ const sets = ["updated_at = ?"];
7486
+ const params = [now()];
7487
+ if (input.name !== undefined) {
7488
+ sets.push("name = ?");
7489
+ params.push(input.name);
7490
+ }
7491
+ if (input.description !== undefined) {
7492
+ sets.push("description = ?");
7493
+ params.push(input.description);
7494
+ }
7495
+ if (input.metadata !== undefined) {
7496
+ sets.push("metadata = ?");
7497
+ params.push(JSON.stringify(input.metadata));
7498
+ }
7499
+ params.push(id);
7500
+ d.run(`UPDATE task_lists SET ${sets.join(", ")} WHERE id = ?`, params);
7501
+ return getTaskList(id, d);
7502
+ }
7503
+ function deleteTaskList(id, db) {
7504
+ const d = db || getDatabase();
7505
+ return d.run("DELETE FROM task_lists WHERE id = ?", [id]).changes > 0;
7506
+ }
7507
+ function ensureTaskList(name, slug, projectId, db) {
7508
+ const d = db || getDatabase();
7509
+ const existing = getTaskListBySlug(slug, projectId, d);
7510
+ if (existing)
7511
+ return existing;
7512
+ return createTaskList({ name, slug, project_id: projectId }, d);
7513
+ }
7514
+
7389
7515
  // src/lib/shared-events.ts
7390
7516
  var SOURCE = "todos";
7391
7517
  function taskEventData(task, extra = {}) {
@@ -7416,6 +7542,69 @@ function taskEventData(task, extra = {}) {
7416
7542
  ...extra
7417
7543
  };
7418
7544
  }
7545
+ function taskEventMetadata(task) {
7546
+ const metadata = {
7547
+ package: "@hasna/todos",
7548
+ todos_event_schema_version: 1,
7549
+ task_id: task.id,
7550
+ task_short_id: task.short_id,
7551
+ project_id: task.project_id,
7552
+ task_list_id: task.task_list_id,
7553
+ working_dir: task.working_dir
7554
+ };
7555
+ try {
7556
+ const project = task.project_id ? getProject(task.project_id) : null;
7557
+ const projectPath = project ? readMachineLocalPath(project) ?? project.path : task.working_dir;
7558
+ if (project) {
7559
+ metadata.project_id = project.id;
7560
+ metadata.project_name = project.name;
7561
+ metadata.project_path = projectPath;
7562
+ metadata.project_canonical_path = project.path;
7563
+ metadata.project_default_task_list_slug = project.task_list_id;
7564
+ metadata.root_project_id = inferRootProjectId(project);
7565
+ } else if (projectPath) {
7566
+ metadata.project_path = projectPath;
7567
+ metadata.project_canonical_path = projectPath;
7568
+ }
7569
+ if (projectPath) {
7570
+ metadata.project_kind = classifyProjectKind(projectPath);
7571
+ metadata.project_is_worktree = isWorktreePath(projectPath);
7572
+ if (typeof task.metadata.route_enabled === "boolean") {
7573
+ metadata.route_enabled = task.metadata.route_enabled;
7574
+ }
7575
+ metadata.working_dir = task.working_dir ?? projectPath;
7576
+ }
7577
+ const taskList = task.task_list_id ? getTaskList(task.task_list_id) ?? (project ? getTaskListBySlug(task.task_list_id, project.id) : null) : project?.task_list_id ? getTaskListBySlug(project.task_list_id, project.id) : null;
7578
+ if (taskList) {
7579
+ metadata.task_list_id = taskList.id;
7580
+ metadata.task_list_slug = taskList.slug;
7581
+ metadata.task_list_name = taskList.name;
7582
+ metadata.task_list_project_id = taskList.project_id;
7583
+ metadata.task_list_is_project_default = Boolean(project?.task_list_id && taskList.slug === project.task_list_id);
7584
+ }
7585
+ } catch {}
7586
+ return metadata;
7587
+ }
7588
+ function classifyProjectKind(path) {
7589
+ return path.includes("/hasna/opensource/") ? "open-source" : "unknown";
7590
+ }
7591
+ function isWorktreePath(path) {
7592
+ return path.includes("/.codewith/worktrees/") || path.includes("/.worktrees/");
7593
+ }
7594
+ function inferRootProjectId(project) {
7595
+ return isWorktreePath(project.path) ? null : project.id;
7596
+ }
7597
+ function readMachineLocalPath(project) {
7598
+ const machineId = process.env["TODOS_MACHINE_ID"];
7599
+ if (!machineId)
7600
+ return null;
7601
+ try {
7602
+ const row = getDatabase().query("SELECT path FROM project_machine_paths WHERE project_id = ? AND machine_id = ?").get(project.id, machineId);
7603
+ return row?.path ?? null;
7604
+ } catch {
7605
+ return null;
7606
+ }
7607
+ }
7419
7608
  async function emitSharedTaskEvent(input) {
7420
7609
  const data = taskEventData(input.task, input.data);
7421
7610
  await new EventsClient().emit({
@@ -7426,12 +7615,7 @@ async function emitSharedTaskEvent(input) {
7426
7615
  message: input.message ?? `${input.type}: ${input.task.title}`,
7427
7616
  data,
7428
7617
  dedupeKey: input.dedupeKey ?? `${input.type}:${input.task.id}:${input.task.version}`,
7429
- metadata: {
7430
- package: "@hasna/todos",
7431
- task_id: input.task.id,
7432
- project_id: input.task.project_id,
7433
- task_list_id: input.task.task_list_id
7434
- }
7618
+ metadata: taskEventMetadata(input.task)
7435
7619
  }, { deliver: true, dedupe: true });
7436
7620
  }
7437
7621
  function emitSharedTaskEventQuiet(input) {
@@ -11749,7 +11933,7 @@ function rowToTask2(row) {
11749
11933
  requires_approval: Boolean(row.requires_approval)
11750
11934
  };
11751
11935
  }
11752
- function rowToTaskList(row) {
11936
+ function rowToTaskList2(row) {
11753
11937
  return { ...row, metadata: parseJsonObject3(row.metadata) };
11754
11938
  }
11755
11939
  function rowWithMetadata(row) {
@@ -11785,7 +11969,7 @@ function createLocalBridgeBundle(options = {}, db) {
11785
11969
  const project = options.project_id ? d.query("SELECT * FROM projects WHERE id = ?").get(options.project_id) : null;
11786
11970
  const data = redactValue({
11787
11971
  projects: options.project_id ? project ? [project] : [] : d.query("SELECT * FROM projects ORDER BY name").all(),
11788
- task_lists: (options.project_id ? d.query("SELECT * FROM task_lists WHERE project_id = ? ORDER BY name").all(options.project_id) : d.query("SELECT * FROM task_lists ORDER BY name").all()).map(rowToTaskList),
11972
+ task_lists: (options.project_id ? d.query("SELECT * FROM task_lists WHERE project_id = ? ORDER BY name").all(options.project_id) : d.query("SELECT * FROM task_lists ORDER BY name").all()).map(rowToTaskList2),
11789
11973
  plans: options.project_id ? d.query("SELECT * FROM plans WHERE project_id = ? ORDER BY created_at").all(options.project_id) : d.query("SELECT * FROM plans ORDER BY created_at").all(),
11790
11974
  tasks: queryByTaskIds(d, "SELECT * FROM tasks WHERE id IN (__TASK_IDS__) ORDER BY created_at", taskIds).map(rowToTask2),
11791
11975
  task_dependencies: queryByTaskIds(d, "SELECT task_id, depends_on, external_project_id, external_task_id FROM task_dependencies WHERE task_id IN (__TASK_IDS__) ORDER BY task_id, depends_on", taskIds),
@@ -21174,87 +21358,6 @@ function getCapableAgents(capabilities, opts, db) {
21174
21358
  return opts?.limit ? scored.slice(0, opts.limit) : scored;
21175
21359
  }
21176
21360
 
21177
- // src/db/task-lists.ts
21178
- init_types();
21179
- init_database();
21180
- function rowToTaskList2(row) {
21181
- return {
21182
- ...row,
21183
- metadata: JSON.parse(row.metadata || "{}")
21184
- };
21185
- }
21186
- function createTaskList(input, db) {
21187
- const d = db || getDatabase();
21188
- const id = uuid();
21189
- const timestamp2 = now();
21190
- const slug = input.slug || slugify(input.name);
21191
- if (!input.project_id) {
21192
- const existing = d.query("SELECT id FROM task_lists WHERE project_id IS NULL AND slug = ?").get(slug);
21193
- if (existing) {
21194
- throw new Error(`Standalone task list with slug "${slug}" already exists`);
21195
- }
21196
- }
21197
- d.run(`INSERT INTO task_lists (id, project_id, slug, name, description, metadata, created_at, updated_at)
21198
- VALUES (?, ?, ?, ?, ?, ?, ?, ?)`, [id, input.project_id || null, slug, input.name, input.description || null, JSON.stringify(input.metadata || {}), timestamp2, timestamp2]);
21199
- return getTaskList(id, d);
21200
- }
21201
- function getTaskList(id, db) {
21202
- const d = db || getDatabase();
21203
- const row = d.query("SELECT * FROM task_lists WHERE id = ?").get(id);
21204
- return row ? rowToTaskList2(row) : null;
21205
- }
21206
- function getTaskListBySlug(slug, projectId, db) {
21207
- const d = db || getDatabase();
21208
- let row;
21209
- if (projectId) {
21210
- row = d.query("SELECT * FROM task_lists WHERE slug = ? AND project_id = ?").get(slug, projectId);
21211
- } else {
21212
- row = d.query("SELECT * FROM task_lists WHERE slug = ? AND project_id IS NULL").get(slug);
21213
- }
21214
- return row ? rowToTaskList2(row) : null;
21215
- }
21216
- function listTaskLists(projectId, db) {
21217
- const d = db || getDatabase();
21218
- if (projectId) {
21219
- return d.query("SELECT * FROM task_lists WHERE project_id = ? ORDER BY name").all(projectId).map(rowToTaskList2);
21220
- }
21221
- return d.query("SELECT * FROM task_lists ORDER BY name").all().map(rowToTaskList2);
21222
- }
21223
- function updateTaskList(id, input, db) {
21224
- const d = db || getDatabase();
21225
- const existing = getTaskList(id, d);
21226
- if (!existing)
21227
- throw new TaskListNotFoundError(id);
21228
- const sets = ["updated_at = ?"];
21229
- const params = [now()];
21230
- if (input.name !== undefined) {
21231
- sets.push("name = ?");
21232
- params.push(input.name);
21233
- }
21234
- if (input.description !== undefined) {
21235
- sets.push("description = ?");
21236
- params.push(input.description);
21237
- }
21238
- if (input.metadata !== undefined) {
21239
- sets.push("metadata = ?");
21240
- params.push(JSON.stringify(input.metadata));
21241
- }
21242
- params.push(id);
21243
- d.run(`UPDATE task_lists SET ${sets.join(", ")} WHERE id = ?`, params);
21244
- return getTaskList(id, d);
21245
- }
21246
- function deleteTaskList(id, db) {
21247
- const d = db || getDatabase();
21248
- return d.run("DELETE FROM task_lists WHERE id = ?", [id]).changes > 0;
21249
- }
21250
- function ensureTaskList(name, slug, projectId, db) {
21251
- const d = db || getDatabase();
21252
- const existing = getTaskListBySlug(slug, projectId, d);
21253
- if (existing)
21254
- return existing;
21255
- return createTaskList({ name, slug, project_id: projectId }, d);
21256
- }
21257
-
21258
21361
  // src/storage/local-sqlite.ts
21259
21362
  init_database();
21260
21363
 
@@ -1 +1 @@
1
- {"version":3,"file":"shared-events.d.ts","sourceRoot":"","sources":["../../src/lib/shared-events.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AACnD,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,mBAAmB,CAAC;AAI9C,MAAM,MAAM,oBAAoB,GAC5B,cAAc,GACd,cAAc,GACd,gBAAgB,GAChB,aAAa,GACb,eAAe,GACf,qBAAqB,GACrB,gBAAgB,CAAC;AAErB,wBAAgB,aAAa,CAAC,IAAI,EAAE,IAAI,EAAE,KAAK,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CA2BtG;AAED,wBAAsB,mBAAmB,CAAC,KAAK,EAAE;IAC/C,IAAI,EAAE,oBAAoB,CAAC;IAC3B,IAAI,EAAE,IAAI,CAAC;IACX,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,aAAa,CAAC;IACzB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,GAAG,OAAO,CAAC,IAAI,CAAC,CAoBhB;AAED,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,UAAU,CAAC,OAAO,mBAAmB,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,CAE/F"}
1
+ {"version":3,"file":"shared-events.d.ts","sourceRoot":"","sources":["../../src/lib/shared-events.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAKnD,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,mBAAmB,CAAC;AAI9C,MAAM,MAAM,oBAAoB,GAC5B,cAAc,GACd,cAAc,GACd,gBAAgB,GAChB,aAAa,GACb,eAAe,GACf,qBAAqB,GACrB,gBAAgB,CAAC;AAErB,wBAAgB,aAAa,CAAC,IAAI,EAAE,IAAI,EAAE,KAAK,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CA2BtG;AAgFD,wBAAsB,mBAAmB,CAAC,KAAK,EAAE;IAC/C,IAAI,EAAE,oBAAoB,CAAC;IAC3B,IAAI,EAAE,IAAI,CAAC;IACX,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,aAAa,CAAC;IACzB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,GAAG,OAAO,CAAC,IAAI,CAAC,CAehB;AAED,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,UAAU,CAAC,OAAO,mBAAmB,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,CAE/F"}