@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/cli/index.js +202 -98
- package/dist/contracts.js +201 -17
- package/dist/index.js +201 -98
- package/dist/lib/shared-events.d.ts.map +1 -1
- package/dist/mcp/index.js +202 -98
- package/dist/registry.js +201 -17
- package/dist/release-provenance.json +3 -3
- package/dist/server/index.js +202 -98
- package/dist/storage.js +199 -96
- package/package.json +2 -2
package/dist/server/index.js
CHANGED
|
@@ -4014,7 +4014,7 @@ var init_event_hooks = __esm(() => {
|
|
|
4014
4014
|
VALID_TARGETS = new Set(["stdout", "file", "socket", "script"]);
|
|
4015
4015
|
});
|
|
4016
4016
|
|
|
4017
|
-
// node_modules/.bun/@hasna+events@0.1.
|
|
4017
|
+
// node_modules/.bun/@hasna+events@0.1.9/node_modules/@hasna/events/dist/index.js
|
|
4018
4018
|
import { chmod, mkdir, readFile, rename, writeFile } from "fs/promises";
|
|
4019
4019
|
import { existsSync as existsSync6 } from "fs";
|
|
4020
4020
|
import { homedir } from "os";
|
|
@@ -4031,17 +4031,30 @@ function getPathValue(input, path) {
|
|
|
4031
4031
|
return;
|
|
4032
4032
|
}, input);
|
|
4033
4033
|
}
|
|
4034
|
-
function wildcardToRegExp(pattern) {
|
|
4035
|
-
|
|
4036
|
-
|
|
4034
|
+
function wildcardToRegExp(pattern, options = {}) {
|
|
4035
|
+
let body = "";
|
|
4036
|
+
for (let index = 0;index < pattern.length; index += 1) {
|
|
4037
|
+
const char = pattern[index];
|
|
4038
|
+
if (char === "*") {
|
|
4039
|
+
if (pattern[index + 1] === "*") {
|
|
4040
|
+
body += ".*";
|
|
4041
|
+
index += 1;
|
|
4042
|
+
} else {
|
|
4043
|
+
body += options.segmentSafe ? "[^/]*" : ".*";
|
|
4044
|
+
}
|
|
4045
|
+
} else {
|
|
4046
|
+
body += char.replace(/[|\\{}()[\]^$+?.]/g, "\\$&");
|
|
4047
|
+
}
|
|
4048
|
+
}
|
|
4049
|
+
return new RegExp(`^${body}$`);
|
|
4037
4050
|
}
|
|
4038
|
-
function matchString(value, matcher) {
|
|
4051
|
+
function matchString(value, matcher, options = {}) {
|
|
4039
4052
|
if (matcher === undefined)
|
|
4040
4053
|
return true;
|
|
4041
4054
|
if (value === undefined)
|
|
4042
4055
|
return false;
|
|
4043
4056
|
const matchers = Array.isArray(matcher) ? matcher : [matcher];
|
|
4044
|
-
return matchers.some((item) => wildcardToRegExp(item).test(value));
|
|
4057
|
+
return matchers.some((item) => wildcardToRegExp(item, options).test(value));
|
|
4045
4058
|
}
|
|
4046
4059
|
function matchRecord(input, matcher) {
|
|
4047
4060
|
if (!matcher)
|
|
@@ -4049,7 +4062,9 @@ function matchRecord(input, matcher) {
|
|
|
4049
4062
|
return Object.entries(matcher).every(([path, expected]) => {
|
|
4050
4063
|
const actual = getPathValue(input, path);
|
|
4051
4064
|
if (typeof expected === "string" || Array.isArray(expected)) {
|
|
4052
|
-
return matchString(actual === undefined ? undefined : String(actual), expected
|
|
4065
|
+
return matchString(actual === undefined ? undefined : String(actual), expected, {
|
|
4066
|
+
segmentSafe: path.endsWith("_path") || path.endsWith(".path")
|
|
4067
|
+
});
|
|
4053
4068
|
}
|
|
4054
4069
|
return actual === expected;
|
|
4055
4070
|
});
|
|
@@ -4403,7 +4418,7 @@ class EventsClient {
|
|
|
4403
4418
|
}
|
|
4404
4419
|
return deliveries;
|
|
4405
4420
|
}
|
|
4406
|
-
async
|
|
4421
|
+
async matchChannel(id, input = {}) {
|
|
4407
4422
|
const channel = await this.store.getChannel(id);
|
|
4408
4423
|
if (!channel)
|
|
4409
4424
|
throw new Error(`Channel not found: ${id}`);
|
|
@@ -4420,6 +4435,34 @@ class EventsClient {
|
|
|
4420
4435
|
time: input.time,
|
|
4421
4436
|
id: input.id
|
|
4422
4437
|
});
|
|
4438
|
+
const matched = channelMatchesEvent(channel, event);
|
|
4439
|
+
return {
|
|
4440
|
+
channelId: channel.id,
|
|
4441
|
+
matched,
|
|
4442
|
+
event,
|
|
4443
|
+
filters: channel.filters,
|
|
4444
|
+
reason: matched ? undefined : channel.enabled ? "event did not match channel filters" : "channel is disabled"
|
|
4445
|
+
};
|
|
4446
|
+
}
|
|
4447
|
+
async testChannel(id, input = {}, options = {}) {
|
|
4448
|
+
const channel = await this.store.getChannel(id);
|
|
4449
|
+
if (!channel)
|
|
4450
|
+
throw new Error(`Channel not found: ${id}`);
|
|
4451
|
+
const match = await this.matchChannel(id, input);
|
|
4452
|
+
const event = match.event;
|
|
4453
|
+
if (options.honorFilters && !match.matched) {
|
|
4454
|
+
const timestamp = new Date().toISOString();
|
|
4455
|
+
const result2 = createDeliveryResult(event, channel, [{
|
|
4456
|
+
attempt: 1,
|
|
4457
|
+
status: "skipped",
|
|
4458
|
+
startedAt: timestamp,
|
|
4459
|
+
completedAt: timestamp,
|
|
4460
|
+
error: match.reason
|
|
4461
|
+
}]);
|
|
4462
|
+
result2.metadata = { reason: "filter_mismatch" };
|
|
4463
|
+
await this.store.appendDelivery(result2);
|
|
4464
|
+
return result2;
|
|
4465
|
+
}
|
|
4423
4466
|
const eventForChannel = await this.applyRedaction(event, channel);
|
|
4424
4467
|
const result = await this.deliverWithRetry(eventForChannel, channel);
|
|
4425
4468
|
await this.store.appendDelivery(result);
|
|
@@ -4523,6 +4566,90 @@ var init_dist = __esm(() => {
|
|
|
4523
4566
|
DEFAULT_SIGNATURE_TOLERANCE_MS = 5 * 60 * 1000;
|
|
4524
4567
|
});
|
|
4525
4568
|
|
|
4569
|
+
// src/db/task-lists.ts
|
|
4570
|
+
function rowToTaskList(row) {
|
|
4571
|
+
return {
|
|
4572
|
+
...row,
|
|
4573
|
+
metadata: JSON.parse(row.metadata || "{}")
|
|
4574
|
+
};
|
|
4575
|
+
}
|
|
4576
|
+
function createTaskList(input, db) {
|
|
4577
|
+
const d = db || getDatabase();
|
|
4578
|
+
const id = uuid();
|
|
4579
|
+
const timestamp = now();
|
|
4580
|
+
const slug = input.slug || slugify(input.name);
|
|
4581
|
+
if (!input.project_id) {
|
|
4582
|
+
const existing = d.query("SELECT id FROM task_lists WHERE project_id IS NULL AND slug = ?").get(slug);
|
|
4583
|
+
if (existing) {
|
|
4584
|
+
throw new Error(`Standalone task list with slug "${slug}" already exists`);
|
|
4585
|
+
}
|
|
4586
|
+
}
|
|
4587
|
+
d.run(`INSERT INTO task_lists (id, project_id, slug, name, description, metadata, created_at, updated_at)
|
|
4588
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`, [id, input.project_id || null, slug, input.name, input.description || null, JSON.stringify(input.metadata || {}), timestamp, timestamp]);
|
|
4589
|
+
return getTaskList(id, d);
|
|
4590
|
+
}
|
|
4591
|
+
function getTaskList(id, db) {
|
|
4592
|
+
const d = db || getDatabase();
|
|
4593
|
+
const row = d.query("SELECT * FROM task_lists WHERE id = ?").get(id);
|
|
4594
|
+
return row ? rowToTaskList(row) : null;
|
|
4595
|
+
}
|
|
4596
|
+
function getTaskListBySlug(slug, projectId, db) {
|
|
4597
|
+
const d = db || getDatabase();
|
|
4598
|
+
let row;
|
|
4599
|
+
if (projectId) {
|
|
4600
|
+
row = d.query("SELECT * FROM task_lists WHERE slug = ? AND project_id = ?").get(slug, projectId);
|
|
4601
|
+
} else {
|
|
4602
|
+
row = d.query("SELECT * FROM task_lists WHERE slug = ? AND project_id IS NULL").get(slug);
|
|
4603
|
+
}
|
|
4604
|
+
return row ? rowToTaskList(row) : null;
|
|
4605
|
+
}
|
|
4606
|
+
function listTaskLists(projectId, db) {
|
|
4607
|
+
const d = db || getDatabase();
|
|
4608
|
+
if (projectId) {
|
|
4609
|
+
return d.query("SELECT * FROM task_lists WHERE project_id = ? ORDER BY name").all(projectId).map(rowToTaskList);
|
|
4610
|
+
}
|
|
4611
|
+
return d.query("SELECT * FROM task_lists ORDER BY name").all().map(rowToTaskList);
|
|
4612
|
+
}
|
|
4613
|
+
function updateTaskList(id, input, db) {
|
|
4614
|
+
const d = db || getDatabase();
|
|
4615
|
+
const existing = getTaskList(id, d);
|
|
4616
|
+
if (!existing)
|
|
4617
|
+
throw new TaskListNotFoundError(id);
|
|
4618
|
+
const sets = ["updated_at = ?"];
|
|
4619
|
+
const params = [now()];
|
|
4620
|
+
if (input.name !== undefined) {
|
|
4621
|
+
sets.push("name = ?");
|
|
4622
|
+
params.push(input.name);
|
|
4623
|
+
}
|
|
4624
|
+
if (input.description !== undefined) {
|
|
4625
|
+
sets.push("description = ?");
|
|
4626
|
+
params.push(input.description);
|
|
4627
|
+
}
|
|
4628
|
+
if (input.metadata !== undefined) {
|
|
4629
|
+
sets.push("metadata = ?");
|
|
4630
|
+
params.push(JSON.stringify(input.metadata));
|
|
4631
|
+
}
|
|
4632
|
+
params.push(id);
|
|
4633
|
+
d.run(`UPDATE task_lists SET ${sets.join(", ")} WHERE id = ?`, params);
|
|
4634
|
+
return getTaskList(id, d);
|
|
4635
|
+
}
|
|
4636
|
+
function deleteTaskList(id, db) {
|
|
4637
|
+
const d = db || getDatabase();
|
|
4638
|
+
return d.run("DELETE FROM task_lists WHERE id = ?", [id]).changes > 0;
|
|
4639
|
+
}
|
|
4640
|
+
function ensureTaskList(name, slug, projectId, db) {
|
|
4641
|
+
const d = db || getDatabase();
|
|
4642
|
+
const existing = getTaskListBySlug(slug, projectId, d);
|
|
4643
|
+
if (existing)
|
|
4644
|
+
return existing;
|
|
4645
|
+
return createTaskList({ name, slug, project_id: projectId }, d);
|
|
4646
|
+
}
|
|
4647
|
+
var init_task_lists = __esm(() => {
|
|
4648
|
+
init_types();
|
|
4649
|
+
init_database();
|
|
4650
|
+
init_projects();
|
|
4651
|
+
});
|
|
4652
|
+
|
|
4526
4653
|
// src/lib/shared-events.ts
|
|
4527
4654
|
function taskEventData(task, extra = {}) {
|
|
4528
4655
|
return {
|
|
@@ -4552,6 +4679,69 @@ function taskEventData(task, extra = {}) {
|
|
|
4552
4679
|
...extra
|
|
4553
4680
|
};
|
|
4554
4681
|
}
|
|
4682
|
+
function taskEventMetadata(task) {
|
|
4683
|
+
const metadata = {
|
|
4684
|
+
package: "@hasna/todos",
|
|
4685
|
+
todos_event_schema_version: 1,
|
|
4686
|
+
task_id: task.id,
|
|
4687
|
+
task_short_id: task.short_id,
|
|
4688
|
+
project_id: task.project_id,
|
|
4689
|
+
task_list_id: task.task_list_id,
|
|
4690
|
+
working_dir: task.working_dir
|
|
4691
|
+
};
|
|
4692
|
+
try {
|
|
4693
|
+
const project = task.project_id ? getProject(task.project_id) : null;
|
|
4694
|
+
const projectPath = project ? readMachineLocalPath(project) ?? project.path : task.working_dir;
|
|
4695
|
+
if (project) {
|
|
4696
|
+
metadata.project_id = project.id;
|
|
4697
|
+
metadata.project_name = project.name;
|
|
4698
|
+
metadata.project_path = projectPath;
|
|
4699
|
+
metadata.project_canonical_path = project.path;
|
|
4700
|
+
metadata.project_default_task_list_slug = project.task_list_id;
|
|
4701
|
+
metadata.root_project_id = inferRootProjectId(project);
|
|
4702
|
+
} else if (projectPath) {
|
|
4703
|
+
metadata.project_path = projectPath;
|
|
4704
|
+
metadata.project_canonical_path = projectPath;
|
|
4705
|
+
}
|
|
4706
|
+
if (projectPath) {
|
|
4707
|
+
metadata.project_kind = classifyProjectKind(projectPath);
|
|
4708
|
+
metadata.project_is_worktree = isWorktreePath(projectPath);
|
|
4709
|
+
if (typeof task.metadata.route_enabled === "boolean") {
|
|
4710
|
+
metadata.route_enabled = task.metadata.route_enabled;
|
|
4711
|
+
}
|
|
4712
|
+
metadata.working_dir = task.working_dir ?? projectPath;
|
|
4713
|
+
}
|
|
4714
|
+
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;
|
|
4715
|
+
if (taskList) {
|
|
4716
|
+
metadata.task_list_id = taskList.id;
|
|
4717
|
+
metadata.task_list_slug = taskList.slug;
|
|
4718
|
+
metadata.task_list_name = taskList.name;
|
|
4719
|
+
metadata.task_list_project_id = taskList.project_id;
|
|
4720
|
+
metadata.task_list_is_project_default = Boolean(project?.task_list_id && taskList.slug === project.task_list_id);
|
|
4721
|
+
}
|
|
4722
|
+
} catch {}
|
|
4723
|
+
return metadata;
|
|
4724
|
+
}
|
|
4725
|
+
function classifyProjectKind(path) {
|
|
4726
|
+
return path.includes("/hasna/opensource/") ? "open-source" : "unknown";
|
|
4727
|
+
}
|
|
4728
|
+
function isWorktreePath(path) {
|
|
4729
|
+
return path.includes("/.codewith/worktrees/") || path.includes("/.worktrees/");
|
|
4730
|
+
}
|
|
4731
|
+
function inferRootProjectId(project) {
|
|
4732
|
+
return isWorktreePath(project.path) ? null : project.id;
|
|
4733
|
+
}
|
|
4734
|
+
function readMachineLocalPath(project) {
|
|
4735
|
+
const machineId = process.env["TODOS_MACHINE_ID"];
|
|
4736
|
+
if (!machineId)
|
|
4737
|
+
return null;
|
|
4738
|
+
try {
|
|
4739
|
+
const row = getDatabase().query("SELECT path FROM project_machine_paths WHERE project_id = ? AND machine_id = ?").get(project.id, machineId);
|
|
4740
|
+
return row?.path ?? null;
|
|
4741
|
+
} catch {
|
|
4742
|
+
return null;
|
|
4743
|
+
}
|
|
4744
|
+
}
|
|
4555
4745
|
async function emitSharedTaskEvent(input) {
|
|
4556
4746
|
const data = taskEventData(input.task, input.data);
|
|
4557
4747
|
await new EventsClient().emit({
|
|
@@ -4562,12 +4752,7 @@ async function emitSharedTaskEvent(input) {
|
|
|
4562
4752
|
message: input.message ?? `${input.type}: ${input.task.title}`,
|
|
4563
4753
|
data,
|
|
4564
4754
|
dedupeKey: input.dedupeKey ?? `${input.type}:${input.task.id}:${input.task.version}`,
|
|
4565
|
-
metadata:
|
|
4566
|
-
package: "@hasna/todos",
|
|
4567
|
-
task_id: input.task.id,
|
|
4568
|
-
project_id: input.task.project_id,
|
|
4569
|
-
task_list_id: input.task.task_list_id
|
|
4570
|
-
}
|
|
4755
|
+
metadata: taskEventMetadata(input.task)
|
|
4571
4756
|
}, { deliver: true, dedupe: true });
|
|
4572
4757
|
}
|
|
4573
4758
|
function emitSharedTaskEventQuiet(input) {
|
|
@@ -4578,6 +4763,9 @@ function emitSharedTaskEventQuiet(input) {
|
|
|
4578
4763
|
var SOURCE = "todos";
|
|
4579
4764
|
var init_shared_events = __esm(() => {
|
|
4580
4765
|
init_dist();
|
|
4766
|
+
init_database();
|
|
4767
|
+
init_projects();
|
|
4768
|
+
init_task_lists();
|
|
4581
4769
|
});
|
|
4582
4770
|
|
|
4583
4771
|
// src/lib/secret-redaction.ts
|
|
@@ -31823,90 +32011,6 @@ var init_dispatch = __esm(() => {
|
|
|
31823
32011
|
init_tmux();
|
|
31824
32012
|
});
|
|
31825
32013
|
|
|
31826
|
-
// src/db/task-lists.ts
|
|
31827
|
-
function rowToTaskList(row) {
|
|
31828
|
-
return {
|
|
31829
|
-
...row,
|
|
31830
|
-
metadata: JSON.parse(row.metadata || "{}")
|
|
31831
|
-
};
|
|
31832
|
-
}
|
|
31833
|
-
function createTaskList(input, db) {
|
|
31834
|
-
const d = db || getDatabase();
|
|
31835
|
-
const id = uuid();
|
|
31836
|
-
const timestamp = now();
|
|
31837
|
-
const slug = input.slug || slugify(input.name);
|
|
31838
|
-
if (!input.project_id) {
|
|
31839
|
-
const existing = d.query("SELECT id FROM task_lists WHERE project_id IS NULL AND slug = ?").get(slug);
|
|
31840
|
-
if (existing) {
|
|
31841
|
-
throw new Error(`Standalone task list with slug "${slug}" already exists`);
|
|
31842
|
-
}
|
|
31843
|
-
}
|
|
31844
|
-
d.run(`INSERT INTO task_lists (id, project_id, slug, name, description, metadata, created_at, updated_at)
|
|
31845
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`, [id, input.project_id || null, slug, input.name, input.description || null, JSON.stringify(input.metadata || {}), timestamp, timestamp]);
|
|
31846
|
-
return getTaskList(id, d);
|
|
31847
|
-
}
|
|
31848
|
-
function getTaskList(id, db) {
|
|
31849
|
-
const d = db || getDatabase();
|
|
31850
|
-
const row = d.query("SELECT * FROM task_lists WHERE id = ?").get(id);
|
|
31851
|
-
return row ? rowToTaskList(row) : null;
|
|
31852
|
-
}
|
|
31853
|
-
function getTaskListBySlug(slug, projectId, db) {
|
|
31854
|
-
const d = db || getDatabase();
|
|
31855
|
-
let row;
|
|
31856
|
-
if (projectId) {
|
|
31857
|
-
row = d.query("SELECT * FROM task_lists WHERE slug = ? AND project_id = ?").get(slug, projectId);
|
|
31858
|
-
} else {
|
|
31859
|
-
row = d.query("SELECT * FROM task_lists WHERE slug = ? AND project_id IS NULL").get(slug);
|
|
31860
|
-
}
|
|
31861
|
-
return row ? rowToTaskList(row) : null;
|
|
31862
|
-
}
|
|
31863
|
-
function listTaskLists(projectId, db) {
|
|
31864
|
-
const d = db || getDatabase();
|
|
31865
|
-
if (projectId) {
|
|
31866
|
-
return d.query("SELECT * FROM task_lists WHERE project_id = ? ORDER BY name").all(projectId).map(rowToTaskList);
|
|
31867
|
-
}
|
|
31868
|
-
return d.query("SELECT * FROM task_lists ORDER BY name").all().map(rowToTaskList);
|
|
31869
|
-
}
|
|
31870
|
-
function updateTaskList(id, input, db) {
|
|
31871
|
-
const d = db || getDatabase();
|
|
31872
|
-
const existing = getTaskList(id, d);
|
|
31873
|
-
if (!existing)
|
|
31874
|
-
throw new TaskListNotFoundError(id);
|
|
31875
|
-
const sets = ["updated_at = ?"];
|
|
31876
|
-
const params = [now()];
|
|
31877
|
-
if (input.name !== undefined) {
|
|
31878
|
-
sets.push("name = ?");
|
|
31879
|
-
params.push(input.name);
|
|
31880
|
-
}
|
|
31881
|
-
if (input.description !== undefined) {
|
|
31882
|
-
sets.push("description = ?");
|
|
31883
|
-
params.push(input.description);
|
|
31884
|
-
}
|
|
31885
|
-
if (input.metadata !== undefined) {
|
|
31886
|
-
sets.push("metadata = ?");
|
|
31887
|
-
params.push(JSON.stringify(input.metadata));
|
|
31888
|
-
}
|
|
31889
|
-
params.push(id);
|
|
31890
|
-
d.run(`UPDATE task_lists SET ${sets.join(", ")} WHERE id = ?`, params);
|
|
31891
|
-
return getTaskList(id, d);
|
|
31892
|
-
}
|
|
31893
|
-
function deleteTaskList(id, db) {
|
|
31894
|
-
const d = db || getDatabase();
|
|
31895
|
-
return d.run("DELETE FROM task_lists WHERE id = ?", [id]).changes > 0;
|
|
31896
|
-
}
|
|
31897
|
-
function ensureTaskList(name, slug, projectId, db) {
|
|
31898
|
-
const d = db || getDatabase();
|
|
31899
|
-
const existing = getTaskListBySlug(slug, projectId, d);
|
|
31900
|
-
if (existing)
|
|
31901
|
-
return existing;
|
|
31902
|
-
return createTaskList({ name, slug, project_id: projectId }, d);
|
|
31903
|
-
}
|
|
31904
|
-
var init_task_lists = __esm(() => {
|
|
31905
|
-
init_types();
|
|
31906
|
-
init_database();
|
|
31907
|
-
init_projects();
|
|
31908
|
-
});
|
|
31909
|
-
|
|
31910
32014
|
// src/mcp/tools/dispatch.ts
|
|
31911
32015
|
function registerDispatchTools(server, { shouldRegisterTool, resolveId, formatError: formatError2 }) {
|
|
31912
32016
|
if (shouldRegisterTool("dispatch_tasks")) {
|