@hasna/todos 0.11.59 → 0.11.60
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 +28 -2
- package/dist/cli/index.js +98 -10
- package/dist/contracts.js +98 -10
- package/dist/index.js +98 -10
- package/dist/lib/shared-events.d.ts.map +1 -1
- package/dist/mcp/index.js +98 -10
- package/dist/registry.js +98 -10
- package/dist/release-provenance.json +3 -3
- package/dist/server/index.js +98 -10
- package/dist/storage.js +98 -10
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -412,7 +412,14 @@ polling:
|
|
|
412
412
|
todos webhooks add loops \
|
|
413
413
|
--id openloops-task-created \
|
|
414
414
|
--transport command \
|
|
415
|
+
--source todos \
|
|
415
416
|
--type task.created \
|
|
417
|
+
--metadata 'project_path=/home/hasna/workspace/hasna/opensource/*' \
|
|
418
|
+
--metadata-json 'route_enabled=true' \
|
|
419
|
+
--metadata-json 'automation.no_auto!=true' \
|
|
420
|
+
--metadata-json 'automation.manual_required!=true' \
|
|
421
|
+
--metadata-json 'automation.requires_approval!=true' \
|
|
422
|
+
--metadata-json 'automation.approval_required!=true' \
|
|
416
423
|
--arg=events \
|
|
417
424
|
--arg=handle \
|
|
418
425
|
--arg=todos-task \
|
|
@@ -432,8 +439,27 @@ When a task is created, `@hasna/events` sends the event JSON on stdin and in
|
|
|
432
439
|
`HASNA_EVENT_JSON`. OpenLoops uses that event to create a deduped one-shot
|
|
433
440
|
worker/verifier workflow for the task. The event data includes task identity,
|
|
434
441
|
title, description, project/list ids, working directory, tags, metadata, status,
|
|
435
|
-
priority, and timestamps.
|
|
436
|
-
|
|
442
|
+
priority, approval state, and timestamps. Event metadata includes routing-safe
|
|
443
|
+
project/list/path fields, `route_enabled` when the task metadata opts in, and an
|
|
444
|
+
`automation` object containing only boolean routing gates such as `no_auto`,
|
|
445
|
+
`manual_required`, `requires_approval`, and `approval_required`.
|
|
446
|
+
|
|
447
|
+
Production task-created routes should fail closed:
|
|
448
|
+
|
|
449
|
+
- Require one explicit opt-in, either task metadata `route_enabled=true` or an
|
|
450
|
+
approved routing tag such as `auto:route`.
|
|
451
|
+
- Add negative automation predicates so `no_auto`, manual, and approval-gated
|
|
452
|
+
tasks do not invoke the route.
|
|
453
|
+
- Scope by project path, task list, tags, or repo metadata before invoking
|
|
454
|
+
OpenLoops.
|
|
455
|
+
- Avoid overlapping opt-in channels for the same task family unless the target
|
|
456
|
+
handler is idempotent. `loops events handle todos-task` dedupes by task id and
|
|
457
|
+
event type, but a narrower subscription still avoids wasted invocations.
|
|
458
|
+
|
|
459
|
+
For tag opt-in, use a second route with the same deny predicates and
|
|
460
|
+
`--data 'tags=auto:route'` instead of `--metadata-json 'route_enabled=true'`.
|
|
461
|
+
Tasks without one of those opt-ins are intentionally no-route. Local event hooks
|
|
462
|
+
remain available for local-only JSONL/socket/script integrations.
|
|
437
463
|
|
|
438
464
|
## Local Terminal Notifications
|
|
439
465
|
|
package/dist/cli/index.js
CHANGED
|
@@ -6239,7 +6239,7 @@ var init_event_hooks = __esm(() => {
|
|
|
6239
6239
|
VALID_TARGETS = new Set(["stdout", "file", "socket", "script"]);
|
|
6240
6240
|
});
|
|
6241
6241
|
|
|
6242
|
-
// node_modules/.bun/@hasna+events@0.1.
|
|
6242
|
+
// node_modules/.bun/@hasna+events@0.1.10/node_modules/@hasna/events/dist/index.js
|
|
6243
6243
|
import { chmod, mkdir, readFile, rename, writeFile } from "fs/promises";
|
|
6244
6244
|
import { existsSync as existsSync6 } from "fs";
|
|
6245
6245
|
import { homedir } from "os";
|
|
@@ -6286,14 +6286,40 @@ function matchRecord(input, matcher) {
|
|
|
6286
6286
|
return true;
|
|
6287
6287
|
return Object.entries(matcher).every(([path, expected]) => {
|
|
6288
6288
|
const actual = getPathValue(input, path);
|
|
6289
|
-
|
|
6290
|
-
return matchString(actual === undefined ? undefined : String(actual), expected, {
|
|
6291
|
-
segmentSafe: path.endsWith("_path") || path.endsWith(".path")
|
|
6292
|
-
});
|
|
6293
|
-
}
|
|
6294
|
-
return actual === expected;
|
|
6289
|
+
return matchField(actual, expected, path);
|
|
6295
6290
|
});
|
|
6296
6291
|
}
|
|
6292
|
+
function matchField(actual, expected, path) {
|
|
6293
|
+
if (isNegativeMatcher(expected)) {
|
|
6294
|
+
return !matchPositiveField(actual, expected.not, path);
|
|
6295
|
+
}
|
|
6296
|
+
return matchPositiveField(actual, expected, path);
|
|
6297
|
+
}
|
|
6298
|
+
function matchPositiveField(actual, expected, path) {
|
|
6299
|
+
if (typeof expected === "string" || Array.isArray(expected)) {
|
|
6300
|
+
return stringCandidates(actual).some((candidate) => matchString(candidate, expected, {
|
|
6301
|
+
segmentSafe: path.endsWith("_path") || path.endsWith(".path")
|
|
6302
|
+
}));
|
|
6303
|
+
}
|
|
6304
|
+
if (Array.isArray(actual)) {
|
|
6305
|
+
return actual.some((item) => item === expected);
|
|
6306
|
+
}
|
|
6307
|
+
return actual === expected;
|
|
6308
|
+
}
|
|
6309
|
+
function stringCandidates(actual) {
|
|
6310
|
+
if (actual === undefined)
|
|
6311
|
+
return [];
|
|
6312
|
+
if (Array.isArray(actual)) {
|
|
6313
|
+
return actual.flatMap((item) => isPrimitiveFieldValue(item) ? [String(item)] : []);
|
|
6314
|
+
}
|
|
6315
|
+
return [String(actual)];
|
|
6316
|
+
}
|
|
6317
|
+
function isPrimitiveFieldValue(value) {
|
|
6318
|
+
return value === null || typeof value === "string" || typeof value === "number" || typeof value === "boolean";
|
|
6319
|
+
}
|
|
6320
|
+
function isNegativeMatcher(value) {
|
|
6321
|
+
return Boolean(value && typeof value === "object" && !Array.isArray(value) && "not" in value);
|
|
6322
|
+
}
|
|
6297
6323
|
function eventMatchesFilter(event, filter) {
|
|
6298
6324
|
return matchString(event.source, filter.source) && matchString(event.type, filter.type) && matchString(event.subject, filter.subject) && matchString(event.severity, filter.severity) && matchRecord(event.data, filter.data) && matchRecord(event.metadata, filter.metadata);
|
|
6299
6325
|
}
|
|
@@ -6901,9 +6927,66 @@ function taskEventData(task, extra = {}) {
|
|
|
6901
6927
|
started_at: task.started_at,
|
|
6902
6928
|
completed_at: task.completed_at,
|
|
6903
6929
|
due_at: task.due_at,
|
|
6930
|
+
requires_approval: task.requires_approval,
|
|
6931
|
+
approved_by: task.approved_by,
|
|
6932
|
+
approved_at: task.approved_at,
|
|
6904
6933
|
...extra
|
|
6905
6934
|
};
|
|
6906
6935
|
}
|
|
6936
|
+
function booleanField(value) {
|
|
6937
|
+
if (typeof value === "boolean")
|
|
6938
|
+
return value;
|
|
6939
|
+
if (typeof value === "number") {
|
|
6940
|
+
if (value === 1)
|
|
6941
|
+
return true;
|
|
6942
|
+
if (value === 0)
|
|
6943
|
+
return false;
|
|
6944
|
+
}
|
|
6945
|
+
if (typeof value === "string") {
|
|
6946
|
+
const normalized = value.trim().toLowerCase();
|
|
6947
|
+
if (["true", "1", "yes", "on"].includes(normalized))
|
|
6948
|
+
return true;
|
|
6949
|
+
if (["false", "0", "no", "off"].includes(normalized))
|
|
6950
|
+
return false;
|
|
6951
|
+
}
|
|
6952
|
+
return;
|
|
6953
|
+
}
|
|
6954
|
+
function objectField(value) {
|
|
6955
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : undefined;
|
|
6956
|
+
}
|
|
6957
|
+
function firstBoolean(records, keys) {
|
|
6958
|
+
for (const record of records) {
|
|
6959
|
+
for (const key of keys) {
|
|
6960
|
+
const value = booleanField(record[key]);
|
|
6961
|
+
if (value !== undefined)
|
|
6962
|
+
return value;
|
|
6963
|
+
}
|
|
6964
|
+
}
|
|
6965
|
+
return;
|
|
6966
|
+
}
|
|
6967
|
+
function routingAutomationMetadata(task) {
|
|
6968
|
+
const automation = objectField(task.metadata.automation);
|
|
6969
|
+
const records = [task.metadata];
|
|
6970
|
+
if (automation)
|
|
6971
|
+
records.push(automation);
|
|
6972
|
+
const result = {};
|
|
6973
|
+
const aliases = [
|
|
6974
|
+
["allowed", ["allowed", "automation_allowed", "automationAllowed"]],
|
|
6975
|
+
["no_auto", ["no_auto", "noAuto"]],
|
|
6976
|
+
["manual", ["manual"]],
|
|
6977
|
+
["manual_required", ["manual_required", "manualRequired"]],
|
|
6978
|
+
["requires_approval", ["requires_approval", "requiresApproval"]],
|
|
6979
|
+
["approval_required", ["approval_required", "approvalRequired"]]
|
|
6980
|
+
];
|
|
6981
|
+
for (const [canonical, keys] of aliases) {
|
|
6982
|
+
const value = firstBoolean(records, keys);
|
|
6983
|
+
if (value !== undefined)
|
|
6984
|
+
result[canonical] = value;
|
|
6985
|
+
}
|
|
6986
|
+
if (task.requires_approval)
|
|
6987
|
+
result.requires_approval = true;
|
|
6988
|
+
return Object.keys(result).length > 0 ? result : undefined;
|
|
6989
|
+
}
|
|
6907
6990
|
function taskEventMetadata(task) {
|
|
6908
6991
|
const metadata = {
|
|
6909
6992
|
package: "@hasna/todos",
|
|
@@ -6914,6 +6997,14 @@ function taskEventMetadata(task) {
|
|
|
6914
6997
|
task_list_id: task.task_list_id,
|
|
6915
6998
|
working_dir: task.working_dir
|
|
6916
6999
|
};
|
|
7000
|
+
const routeEnabled = booleanField(task.metadata.route_enabled);
|
|
7001
|
+
if (routeEnabled !== undefined) {
|
|
7002
|
+
metadata.route_enabled = routeEnabled;
|
|
7003
|
+
}
|
|
7004
|
+
const automation = routingAutomationMetadata(task);
|
|
7005
|
+
if (automation) {
|
|
7006
|
+
metadata.automation = automation;
|
|
7007
|
+
}
|
|
6917
7008
|
try {
|
|
6918
7009
|
const project = task.project_id ? getProject(task.project_id) : null;
|
|
6919
7010
|
const projectPath = project ? readMachineLocalPath(project) ?? project.path : task.working_dir;
|
|
@@ -6931,9 +7022,6 @@ function taskEventMetadata(task) {
|
|
|
6931
7022
|
if (projectPath) {
|
|
6932
7023
|
metadata.project_kind = classifyProjectKind(projectPath);
|
|
6933
7024
|
metadata.project_is_worktree = isWorktreePath(projectPath);
|
|
6934
|
-
if (typeof task.metadata.route_enabled === "boolean") {
|
|
6935
|
-
metadata.route_enabled = task.metadata.route_enabled;
|
|
6936
|
-
}
|
|
6937
7025
|
metadata.working_dir = task.working_dir ?? projectPath;
|
|
6938
7026
|
}
|
|
6939
7027
|
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;
|
package/dist/contracts.js
CHANGED
|
@@ -6955,7 +6955,7 @@ async function testLocalEventHook(name, input) {
|
|
|
6955
6955
|
return emitLocalEventHooks({ ...input, hooks: [hook] });
|
|
6956
6956
|
}
|
|
6957
6957
|
|
|
6958
|
-
// node_modules/.bun/@hasna+events@0.1.
|
|
6958
|
+
// node_modules/.bun/@hasna+events@0.1.10/node_modules/@hasna/events/dist/index.js
|
|
6959
6959
|
import { chmod, mkdir, readFile, rename, writeFile } from "fs/promises";
|
|
6960
6960
|
import { existsSync as existsSync6 } from "fs";
|
|
6961
6961
|
import { homedir } from "os";
|
|
@@ -7002,14 +7002,40 @@ function matchRecord(input, matcher) {
|
|
|
7002
7002
|
return true;
|
|
7003
7003
|
return Object.entries(matcher).every(([path, expected]) => {
|
|
7004
7004
|
const actual = getPathValue(input, path);
|
|
7005
|
-
|
|
7006
|
-
return matchString(actual === undefined ? undefined : String(actual), expected, {
|
|
7007
|
-
segmentSafe: path.endsWith("_path") || path.endsWith(".path")
|
|
7008
|
-
});
|
|
7009
|
-
}
|
|
7010
|
-
return actual === expected;
|
|
7005
|
+
return matchField(actual, expected, path);
|
|
7011
7006
|
});
|
|
7012
7007
|
}
|
|
7008
|
+
function matchField(actual, expected, path) {
|
|
7009
|
+
if (isNegativeMatcher(expected)) {
|
|
7010
|
+
return !matchPositiveField(actual, expected.not, path);
|
|
7011
|
+
}
|
|
7012
|
+
return matchPositiveField(actual, expected, path);
|
|
7013
|
+
}
|
|
7014
|
+
function matchPositiveField(actual, expected, path) {
|
|
7015
|
+
if (typeof expected === "string" || Array.isArray(expected)) {
|
|
7016
|
+
return stringCandidates(actual).some((candidate) => matchString(candidate, expected, {
|
|
7017
|
+
segmentSafe: path.endsWith("_path") || path.endsWith(".path")
|
|
7018
|
+
}));
|
|
7019
|
+
}
|
|
7020
|
+
if (Array.isArray(actual)) {
|
|
7021
|
+
return actual.some((item) => item === expected);
|
|
7022
|
+
}
|
|
7023
|
+
return actual === expected;
|
|
7024
|
+
}
|
|
7025
|
+
function stringCandidates(actual) {
|
|
7026
|
+
if (actual === undefined)
|
|
7027
|
+
return [];
|
|
7028
|
+
if (Array.isArray(actual)) {
|
|
7029
|
+
return actual.flatMap((item) => isPrimitiveFieldValue(item) ? [String(item)] : []);
|
|
7030
|
+
}
|
|
7031
|
+
return [String(actual)];
|
|
7032
|
+
}
|
|
7033
|
+
function isPrimitiveFieldValue(value) {
|
|
7034
|
+
return value === null || typeof value === "string" || typeof value === "number" || typeof value === "boolean";
|
|
7035
|
+
}
|
|
7036
|
+
function isNegativeMatcher(value) {
|
|
7037
|
+
return Boolean(value && typeof value === "object" && !Array.isArray(value) && "not" in value);
|
|
7038
|
+
}
|
|
7013
7039
|
function eventMatchesFilter(event, filter) {
|
|
7014
7040
|
return matchString(event.source, filter.source) && matchString(event.type, filter.type) && matchString(event.subject, filter.subject) && matchString(event.severity, filter.severity) && matchRecord(event.data, filter.data) && matchRecord(event.metadata, filter.metadata);
|
|
7015
7041
|
}
|
|
@@ -7616,9 +7642,66 @@ function taskEventData(task, extra = {}) {
|
|
|
7616
7642
|
started_at: task.started_at,
|
|
7617
7643
|
completed_at: task.completed_at,
|
|
7618
7644
|
due_at: task.due_at,
|
|
7645
|
+
requires_approval: task.requires_approval,
|
|
7646
|
+
approved_by: task.approved_by,
|
|
7647
|
+
approved_at: task.approved_at,
|
|
7619
7648
|
...extra
|
|
7620
7649
|
};
|
|
7621
7650
|
}
|
|
7651
|
+
function booleanField(value) {
|
|
7652
|
+
if (typeof value === "boolean")
|
|
7653
|
+
return value;
|
|
7654
|
+
if (typeof value === "number") {
|
|
7655
|
+
if (value === 1)
|
|
7656
|
+
return true;
|
|
7657
|
+
if (value === 0)
|
|
7658
|
+
return false;
|
|
7659
|
+
}
|
|
7660
|
+
if (typeof value === "string") {
|
|
7661
|
+
const normalized = value.trim().toLowerCase();
|
|
7662
|
+
if (["true", "1", "yes", "on"].includes(normalized))
|
|
7663
|
+
return true;
|
|
7664
|
+
if (["false", "0", "no", "off"].includes(normalized))
|
|
7665
|
+
return false;
|
|
7666
|
+
}
|
|
7667
|
+
return;
|
|
7668
|
+
}
|
|
7669
|
+
function objectField(value) {
|
|
7670
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : undefined;
|
|
7671
|
+
}
|
|
7672
|
+
function firstBoolean(records, keys) {
|
|
7673
|
+
for (const record of records) {
|
|
7674
|
+
for (const key of keys) {
|
|
7675
|
+
const value = booleanField(record[key]);
|
|
7676
|
+
if (value !== undefined)
|
|
7677
|
+
return value;
|
|
7678
|
+
}
|
|
7679
|
+
}
|
|
7680
|
+
return;
|
|
7681
|
+
}
|
|
7682
|
+
function routingAutomationMetadata(task) {
|
|
7683
|
+
const automation = objectField(task.metadata.automation);
|
|
7684
|
+
const records = [task.metadata];
|
|
7685
|
+
if (automation)
|
|
7686
|
+
records.push(automation);
|
|
7687
|
+
const result = {};
|
|
7688
|
+
const aliases = [
|
|
7689
|
+
["allowed", ["allowed", "automation_allowed", "automationAllowed"]],
|
|
7690
|
+
["no_auto", ["no_auto", "noAuto"]],
|
|
7691
|
+
["manual", ["manual"]],
|
|
7692
|
+
["manual_required", ["manual_required", "manualRequired"]],
|
|
7693
|
+
["requires_approval", ["requires_approval", "requiresApproval"]],
|
|
7694
|
+
["approval_required", ["approval_required", "approvalRequired"]]
|
|
7695
|
+
];
|
|
7696
|
+
for (const [canonical, keys] of aliases) {
|
|
7697
|
+
const value = firstBoolean(records, keys);
|
|
7698
|
+
if (value !== undefined)
|
|
7699
|
+
result[canonical] = value;
|
|
7700
|
+
}
|
|
7701
|
+
if (task.requires_approval)
|
|
7702
|
+
result.requires_approval = true;
|
|
7703
|
+
return Object.keys(result).length > 0 ? result : undefined;
|
|
7704
|
+
}
|
|
7622
7705
|
function taskEventMetadata(task) {
|
|
7623
7706
|
const metadata = {
|
|
7624
7707
|
package: "@hasna/todos",
|
|
@@ -7629,6 +7712,14 @@ function taskEventMetadata(task) {
|
|
|
7629
7712
|
task_list_id: task.task_list_id,
|
|
7630
7713
|
working_dir: task.working_dir
|
|
7631
7714
|
};
|
|
7715
|
+
const routeEnabled = booleanField(task.metadata.route_enabled);
|
|
7716
|
+
if (routeEnabled !== undefined) {
|
|
7717
|
+
metadata.route_enabled = routeEnabled;
|
|
7718
|
+
}
|
|
7719
|
+
const automation = routingAutomationMetadata(task);
|
|
7720
|
+
if (automation) {
|
|
7721
|
+
metadata.automation = automation;
|
|
7722
|
+
}
|
|
7632
7723
|
try {
|
|
7633
7724
|
const project = task.project_id ? getProject(task.project_id) : null;
|
|
7634
7725
|
const projectPath = project ? readMachineLocalPath(project) ?? project.path : task.working_dir;
|
|
@@ -7646,9 +7737,6 @@ function taskEventMetadata(task) {
|
|
|
7646
7737
|
if (projectPath) {
|
|
7647
7738
|
metadata.project_kind = classifyProjectKind(projectPath);
|
|
7648
7739
|
metadata.project_is_worktree = isWorktreePath(projectPath);
|
|
7649
|
-
if (typeof task.metadata.route_enabled === "boolean") {
|
|
7650
|
-
metadata.route_enabled = task.metadata.route_enabled;
|
|
7651
|
-
}
|
|
7652
7740
|
metadata.working_dir = task.working_dir ?? projectPath;
|
|
7653
7741
|
}
|
|
7654
7742
|
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;
|
package/dist/index.js
CHANGED
|
@@ -7060,7 +7060,7 @@ async function testLocalEventHook(name, input) {
|
|
|
7060
7060
|
return emitLocalEventHooks({ ...input, hooks: [hook] });
|
|
7061
7061
|
}
|
|
7062
7062
|
|
|
7063
|
-
// node_modules/.bun/@hasna+events@0.1.
|
|
7063
|
+
// node_modules/.bun/@hasna+events@0.1.10/node_modules/@hasna/events/dist/index.js
|
|
7064
7064
|
import { chmod, mkdir, readFile, rename, writeFile } from "fs/promises";
|
|
7065
7065
|
import { existsSync as existsSync6 } from "fs";
|
|
7066
7066
|
import { homedir } from "os";
|
|
@@ -7107,14 +7107,40 @@ function matchRecord(input, matcher) {
|
|
|
7107
7107
|
return true;
|
|
7108
7108
|
return Object.entries(matcher).every(([path, expected]) => {
|
|
7109
7109
|
const actual = getPathValue(input, path);
|
|
7110
|
-
|
|
7111
|
-
return matchString(actual === undefined ? undefined : String(actual), expected, {
|
|
7112
|
-
segmentSafe: path.endsWith("_path") || path.endsWith(".path")
|
|
7113
|
-
});
|
|
7114
|
-
}
|
|
7115
|
-
return actual === expected;
|
|
7110
|
+
return matchField(actual, expected, path);
|
|
7116
7111
|
});
|
|
7117
7112
|
}
|
|
7113
|
+
function matchField(actual, expected, path) {
|
|
7114
|
+
if (isNegativeMatcher(expected)) {
|
|
7115
|
+
return !matchPositiveField(actual, expected.not, path);
|
|
7116
|
+
}
|
|
7117
|
+
return matchPositiveField(actual, expected, path);
|
|
7118
|
+
}
|
|
7119
|
+
function matchPositiveField(actual, expected, path) {
|
|
7120
|
+
if (typeof expected === "string" || Array.isArray(expected)) {
|
|
7121
|
+
return stringCandidates(actual).some((candidate) => matchString(candidate, expected, {
|
|
7122
|
+
segmentSafe: path.endsWith("_path") || path.endsWith(".path")
|
|
7123
|
+
}));
|
|
7124
|
+
}
|
|
7125
|
+
if (Array.isArray(actual)) {
|
|
7126
|
+
return actual.some((item) => item === expected);
|
|
7127
|
+
}
|
|
7128
|
+
return actual === expected;
|
|
7129
|
+
}
|
|
7130
|
+
function stringCandidates(actual) {
|
|
7131
|
+
if (actual === undefined)
|
|
7132
|
+
return [];
|
|
7133
|
+
if (Array.isArray(actual)) {
|
|
7134
|
+
return actual.flatMap((item) => isPrimitiveFieldValue(item) ? [String(item)] : []);
|
|
7135
|
+
}
|
|
7136
|
+
return [String(actual)];
|
|
7137
|
+
}
|
|
7138
|
+
function isPrimitiveFieldValue(value) {
|
|
7139
|
+
return value === null || typeof value === "string" || typeof value === "number" || typeof value === "boolean";
|
|
7140
|
+
}
|
|
7141
|
+
function isNegativeMatcher(value) {
|
|
7142
|
+
return Boolean(value && typeof value === "object" && !Array.isArray(value) && "not" in value);
|
|
7143
|
+
}
|
|
7118
7144
|
function eventMatchesFilter(event, filter) {
|
|
7119
7145
|
return matchString(event.source, filter.source) && matchString(event.type, filter.type) && matchString(event.subject, filter.subject) && matchString(event.severity, filter.severity) && matchRecord(event.data, filter.data) && matchRecord(event.metadata, filter.metadata);
|
|
7120
7146
|
}
|
|
@@ -7721,9 +7747,66 @@ function taskEventData(task, extra = {}) {
|
|
|
7721
7747
|
started_at: task.started_at,
|
|
7722
7748
|
completed_at: task.completed_at,
|
|
7723
7749
|
due_at: task.due_at,
|
|
7750
|
+
requires_approval: task.requires_approval,
|
|
7751
|
+
approved_by: task.approved_by,
|
|
7752
|
+
approved_at: task.approved_at,
|
|
7724
7753
|
...extra
|
|
7725
7754
|
};
|
|
7726
7755
|
}
|
|
7756
|
+
function booleanField(value) {
|
|
7757
|
+
if (typeof value === "boolean")
|
|
7758
|
+
return value;
|
|
7759
|
+
if (typeof value === "number") {
|
|
7760
|
+
if (value === 1)
|
|
7761
|
+
return true;
|
|
7762
|
+
if (value === 0)
|
|
7763
|
+
return false;
|
|
7764
|
+
}
|
|
7765
|
+
if (typeof value === "string") {
|
|
7766
|
+
const normalized = value.trim().toLowerCase();
|
|
7767
|
+
if (["true", "1", "yes", "on"].includes(normalized))
|
|
7768
|
+
return true;
|
|
7769
|
+
if (["false", "0", "no", "off"].includes(normalized))
|
|
7770
|
+
return false;
|
|
7771
|
+
}
|
|
7772
|
+
return;
|
|
7773
|
+
}
|
|
7774
|
+
function objectField(value) {
|
|
7775
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : undefined;
|
|
7776
|
+
}
|
|
7777
|
+
function firstBoolean(records, keys) {
|
|
7778
|
+
for (const record of records) {
|
|
7779
|
+
for (const key of keys) {
|
|
7780
|
+
const value = booleanField(record[key]);
|
|
7781
|
+
if (value !== undefined)
|
|
7782
|
+
return value;
|
|
7783
|
+
}
|
|
7784
|
+
}
|
|
7785
|
+
return;
|
|
7786
|
+
}
|
|
7787
|
+
function routingAutomationMetadata(task) {
|
|
7788
|
+
const automation = objectField(task.metadata.automation);
|
|
7789
|
+
const records = [task.metadata];
|
|
7790
|
+
if (automation)
|
|
7791
|
+
records.push(automation);
|
|
7792
|
+
const result = {};
|
|
7793
|
+
const aliases = [
|
|
7794
|
+
["allowed", ["allowed", "automation_allowed", "automationAllowed"]],
|
|
7795
|
+
["no_auto", ["no_auto", "noAuto"]],
|
|
7796
|
+
["manual", ["manual"]],
|
|
7797
|
+
["manual_required", ["manual_required", "manualRequired"]],
|
|
7798
|
+
["requires_approval", ["requires_approval", "requiresApproval"]],
|
|
7799
|
+
["approval_required", ["approval_required", "approvalRequired"]]
|
|
7800
|
+
];
|
|
7801
|
+
for (const [canonical, keys] of aliases) {
|
|
7802
|
+
const value = firstBoolean(records, keys);
|
|
7803
|
+
if (value !== undefined)
|
|
7804
|
+
result[canonical] = value;
|
|
7805
|
+
}
|
|
7806
|
+
if (task.requires_approval)
|
|
7807
|
+
result.requires_approval = true;
|
|
7808
|
+
return Object.keys(result).length > 0 ? result : undefined;
|
|
7809
|
+
}
|
|
7727
7810
|
function taskEventMetadata(task) {
|
|
7728
7811
|
const metadata = {
|
|
7729
7812
|
package: "@hasna/todos",
|
|
@@ -7734,6 +7817,14 @@ function taskEventMetadata(task) {
|
|
|
7734
7817
|
task_list_id: task.task_list_id,
|
|
7735
7818
|
working_dir: task.working_dir
|
|
7736
7819
|
};
|
|
7820
|
+
const routeEnabled = booleanField(task.metadata.route_enabled);
|
|
7821
|
+
if (routeEnabled !== undefined) {
|
|
7822
|
+
metadata.route_enabled = routeEnabled;
|
|
7823
|
+
}
|
|
7824
|
+
const automation = routingAutomationMetadata(task);
|
|
7825
|
+
if (automation) {
|
|
7826
|
+
metadata.automation = automation;
|
|
7827
|
+
}
|
|
7737
7828
|
try {
|
|
7738
7829
|
const project = task.project_id ? getProject(task.project_id) : null;
|
|
7739
7830
|
const projectPath = project ? readMachineLocalPath(project) ?? project.path : task.working_dir;
|
|
@@ -7751,9 +7842,6 @@ function taskEventMetadata(task) {
|
|
|
7751
7842
|
if (projectPath) {
|
|
7752
7843
|
metadata.project_kind = classifyProjectKind(projectPath);
|
|
7753
7844
|
metadata.project_is_worktree = isWorktreePath(projectPath);
|
|
7754
|
-
if (typeof task.metadata.route_enabled === "boolean") {
|
|
7755
|
-
metadata.route_enabled = task.metadata.route_enabled;
|
|
7756
|
-
}
|
|
7757
7845
|
metadata.working_dir = task.working_dir ?? projectPath;
|
|
7758
7846
|
}
|
|
7759
7847
|
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;
|
|
@@ -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;AAKnD,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,mBAAmB,CAAC;AAI9C,MAAM,MAAM,oBAAoB,GAC5B,cAAc,GACd,cAAc,GACd,gBAAgB,GAChB,aAAa,GACb,cAAc,GACd,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,
|
|
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,cAAc,GACd,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,CA8BtG;AA0ID,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"}
|
package/dist/mcp/index.js
CHANGED
|
@@ -8738,7 +8738,7 @@ var init_event_hooks = __esm(() => {
|
|
|
8738
8738
|
VALID_TARGETS = new Set(["stdout", "file", "socket", "script"]);
|
|
8739
8739
|
});
|
|
8740
8740
|
|
|
8741
|
-
// node_modules/.bun/@hasna+events@0.1.
|
|
8741
|
+
// node_modules/.bun/@hasna+events@0.1.10/node_modules/@hasna/events/dist/index.js
|
|
8742
8742
|
import { chmod, mkdir, readFile, rename, writeFile } from "fs/promises";
|
|
8743
8743
|
import { existsSync as existsSync5 } from "fs";
|
|
8744
8744
|
import { homedir } from "os";
|
|
@@ -8785,14 +8785,40 @@ function matchRecord(input, matcher) {
|
|
|
8785
8785
|
return true;
|
|
8786
8786
|
return Object.entries(matcher).every(([path, expected]) => {
|
|
8787
8787
|
const actual = getPathValue(input, path);
|
|
8788
|
-
|
|
8789
|
-
return matchString(actual === undefined ? undefined : String(actual), expected, {
|
|
8790
|
-
segmentSafe: path.endsWith("_path") || path.endsWith(".path")
|
|
8791
|
-
});
|
|
8792
|
-
}
|
|
8793
|
-
return actual === expected;
|
|
8788
|
+
return matchField(actual, expected, path);
|
|
8794
8789
|
});
|
|
8795
8790
|
}
|
|
8791
|
+
function matchField(actual, expected, path) {
|
|
8792
|
+
if (isNegativeMatcher(expected)) {
|
|
8793
|
+
return !matchPositiveField(actual, expected.not, path);
|
|
8794
|
+
}
|
|
8795
|
+
return matchPositiveField(actual, expected, path);
|
|
8796
|
+
}
|
|
8797
|
+
function matchPositiveField(actual, expected, path) {
|
|
8798
|
+
if (typeof expected === "string" || Array.isArray(expected)) {
|
|
8799
|
+
return stringCandidates(actual).some((candidate) => matchString(candidate, expected, {
|
|
8800
|
+
segmentSafe: path.endsWith("_path") || path.endsWith(".path")
|
|
8801
|
+
}));
|
|
8802
|
+
}
|
|
8803
|
+
if (Array.isArray(actual)) {
|
|
8804
|
+
return actual.some((item) => item === expected);
|
|
8805
|
+
}
|
|
8806
|
+
return actual === expected;
|
|
8807
|
+
}
|
|
8808
|
+
function stringCandidates(actual) {
|
|
8809
|
+
if (actual === undefined)
|
|
8810
|
+
return [];
|
|
8811
|
+
if (Array.isArray(actual)) {
|
|
8812
|
+
return actual.flatMap((item) => isPrimitiveFieldValue(item) ? [String(item)] : []);
|
|
8813
|
+
}
|
|
8814
|
+
return [String(actual)];
|
|
8815
|
+
}
|
|
8816
|
+
function isPrimitiveFieldValue(value) {
|
|
8817
|
+
return value === null || typeof value === "string" || typeof value === "number" || typeof value === "boolean";
|
|
8818
|
+
}
|
|
8819
|
+
function isNegativeMatcher(value) {
|
|
8820
|
+
return Boolean(value && typeof value === "object" && !Array.isArray(value) && "not" in value);
|
|
8821
|
+
}
|
|
8796
8822
|
function eventMatchesFilter(event, filter) {
|
|
8797
8823
|
return matchString(event.source, filter.source) && matchString(event.type, filter.type) && matchString(event.subject, filter.subject) && matchString(event.severity, filter.severity) && matchRecord(event.data, filter.data) && matchRecord(event.metadata, filter.metadata);
|
|
8798
8824
|
}
|
|
@@ -9400,9 +9426,66 @@ function taskEventData(task, extra = {}) {
|
|
|
9400
9426
|
started_at: task.started_at,
|
|
9401
9427
|
completed_at: task.completed_at,
|
|
9402
9428
|
due_at: task.due_at,
|
|
9429
|
+
requires_approval: task.requires_approval,
|
|
9430
|
+
approved_by: task.approved_by,
|
|
9431
|
+
approved_at: task.approved_at,
|
|
9403
9432
|
...extra
|
|
9404
9433
|
};
|
|
9405
9434
|
}
|
|
9435
|
+
function booleanField(value) {
|
|
9436
|
+
if (typeof value === "boolean")
|
|
9437
|
+
return value;
|
|
9438
|
+
if (typeof value === "number") {
|
|
9439
|
+
if (value === 1)
|
|
9440
|
+
return true;
|
|
9441
|
+
if (value === 0)
|
|
9442
|
+
return false;
|
|
9443
|
+
}
|
|
9444
|
+
if (typeof value === "string") {
|
|
9445
|
+
const normalized = value.trim().toLowerCase();
|
|
9446
|
+
if (["true", "1", "yes", "on"].includes(normalized))
|
|
9447
|
+
return true;
|
|
9448
|
+
if (["false", "0", "no", "off"].includes(normalized))
|
|
9449
|
+
return false;
|
|
9450
|
+
}
|
|
9451
|
+
return;
|
|
9452
|
+
}
|
|
9453
|
+
function objectField(value) {
|
|
9454
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : undefined;
|
|
9455
|
+
}
|
|
9456
|
+
function firstBoolean(records, keys) {
|
|
9457
|
+
for (const record of records) {
|
|
9458
|
+
for (const key of keys) {
|
|
9459
|
+
const value = booleanField(record[key]);
|
|
9460
|
+
if (value !== undefined)
|
|
9461
|
+
return value;
|
|
9462
|
+
}
|
|
9463
|
+
}
|
|
9464
|
+
return;
|
|
9465
|
+
}
|
|
9466
|
+
function routingAutomationMetadata(task) {
|
|
9467
|
+
const automation = objectField(task.metadata.automation);
|
|
9468
|
+
const records = [task.metadata];
|
|
9469
|
+
if (automation)
|
|
9470
|
+
records.push(automation);
|
|
9471
|
+
const result = {};
|
|
9472
|
+
const aliases = [
|
|
9473
|
+
["allowed", ["allowed", "automation_allowed", "automationAllowed"]],
|
|
9474
|
+
["no_auto", ["no_auto", "noAuto"]],
|
|
9475
|
+
["manual", ["manual"]],
|
|
9476
|
+
["manual_required", ["manual_required", "manualRequired"]],
|
|
9477
|
+
["requires_approval", ["requires_approval", "requiresApproval"]],
|
|
9478
|
+
["approval_required", ["approval_required", "approvalRequired"]]
|
|
9479
|
+
];
|
|
9480
|
+
for (const [canonical, keys] of aliases) {
|
|
9481
|
+
const value = firstBoolean(records, keys);
|
|
9482
|
+
if (value !== undefined)
|
|
9483
|
+
result[canonical] = value;
|
|
9484
|
+
}
|
|
9485
|
+
if (task.requires_approval)
|
|
9486
|
+
result.requires_approval = true;
|
|
9487
|
+
return Object.keys(result).length > 0 ? result : undefined;
|
|
9488
|
+
}
|
|
9406
9489
|
function taskEventMetadata(task) {
|
|
9407
9490
|
const metadata = {
|
|
9408
9491
|
package: "@hasna/todos",
|
|
@@ -9413,6 +9496,14 @@ function taskEventMetadata(task) {
|
|
|
9413
9496
|
task_list_id: task.task_list_id,
|
|
9414
9497
|
working_dir: task.working_dir
|
|
9415
9498
|
};
|
|
9499
|
+
const routeEnabled = booleanField(task.metadata.route_enabled);
|
|
9500
|
+
if (routeEnabled !== undefined) {
|
|
9501
|
+
metadata.route_enabled = routeEnabled;
|
|
9502
|
+
}
|
|
9503
|
+
const automation = routingAutomationMetadata(task);
|
|
9504
|
+
if (automation) {
|
|
9505
|
+
metadata.automation = automation;
|
|
9506
|
+
}
|
|
9416
9507
|
try {
|
|
9417
9508
|
const project = task.project_id ? getProject(task.project_id) : null;
|
|
9418
9509
|
const projectPath = project ? readMachineLocalPath(project) ?? project.path : task.working_dir;
|
|
@@ -9430,9 +9521,6 @@ function taskEventMetadata(task) {
|
|
|
9430
9521
|
if (projectPath) {
|
|
9431
9522
|
metadata.project_kind = classifyProjectKind(projectPath);
|
|
9432
9523
|
metadata.project_is_worktree = isWorktreePath(projectPath);
|
|
9433
|
-
if (typeof task.metadata.route_enabled === "boolean") {
|
|
9434
|
-
metadata.route_enabled = task.metadata.route_enabled;
|
|
9435
|
-
}
|
|
9436
9524
|
metadata.working_dir = task.working_dir ?? projectPath;
|
|
9437
9525
|
}
|
|
9438
9526
|
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;
|
package/dist/registry.js
CHANGED
|
@@ -6955,7 +6955,7 @@ async function testLocalEventHook(name, input) {
|
|
|
6955
6955
|
return emitLocalEventHooks({ ...input, hooks: [hook] });
|
|
6956
6956
|
}
|
|
6957
6957
|
|
|
6958
|
-
// node_modules/.bun/@hasna+events@0.1.
|
|
6958
|
+
// node_modules/.bun/@hasna+events@0.1.10/node_modules/@hasna/events/dist/index.js
|
|
6959
6959
|
import { chmod, mkdir, readFile, rename, writeFile } from "fs/promises";
|
|
6960
6960
|
import { existsSync as existsSync6 } from "fs";
|
|
6961
6961
|
import { homedir } from "os";
|
|
@@ -7002,14 +7002,40 @@ function matchRecord(input, matcher) {
|
|
|
7002
7002
|
return true;
|
|
7003
7003
|
return Object.entries(matcher).every(([path, expected]) => {
|
|
7004
7004
|
const actual = getPathValue(input, path);
|
|
7005
|
-
|
|
7006
|
-
return matchString(actual === undefined ? undefined : String(actual), expected, {
|
|
7007
|
-
segmentSafe: path.endsWith("_path") || path.endsWith(".path")
|
|
7008
|
-
});
|
|
7009
|
-
}
|
|
7010
|
-
return actual === expected;
|
|
7005
|
+
return matchField(actual, expected, path);
|
|
7011
7006
|
});
|
|
7012
7007
|
}
|
|
7008
|
+
function matchField(actual, expected, path) {
|
|
7009
|
+
if (isNegativeMatcher(expected)) {
|
|
7010
|
+
return !matchPositiveField(actual, expected.not, path);
|
|
7011
|
+
}
|
|
7012
|
+
return matchPositiveField(actual, expected, path);
|
|
7013
|
+
}
|
|
7014
|
+
function matchPositiveField(actual, expected, path) {
|
|
7015
|
+
if (typeof expected === "string" || Array.isArray(expected)) {
|
|
7016
|
+
return stringCandidates(actual).some((candidate) => matchString(candidate, expected, {
|
|
7017
|
+
segmentSafe: path.endsWith("_path") || path.endsWith(".path")
|
|
7018
|
+
}));
|
|
7019
|
+
}
|
|
7020
|
+
if (Array.isArray(actual)) {
|
|
7021
|
+
return actual.some((item) => item === expected);
|
|
7022
|
+
}
|
|
7023
|
+
return actual === expected;
|
|
7024
|
+
}
|
|
7025
|
+
function stringCandidates(actual) {
|
|
7026
|
+
if (actual === undefined)
|
|
7027
|
+
return [];
|
|
7028
|
+
if (Array.isArray(actual)) {
|
|
7029
|
+
return actual.flatMap((item) => isPrimitiveFieldValue(item) ? [String(item)] : []);
|
|
7030
|
+
}
|
|
7031
|
+
return [String(actual)];
|
|
7032
|
+
}
|
|
7033
|
+
function isPrimitiveFieldValue(value) {
|
|
7034
|
+
return value === null || typeof value === "string" || typeof value === "number" || typeof value === "boolean";
|
|
7035
|
+
}
|
|
7036
|
+
function isNegativeMatcher(value) {
|
|
7037
|
+
return Boolean(value && typeof value === "object" && !Array.isArray(value) && "not" in value);
|
|
7038
|
+
}
|
|
7013
7039
|
function eventMatchesFilter(event, filter) {
|
|
7014
7040
|
return matchString(event.source, filter.source) && matchString(event.type, filter.type) && matchString(event.subject, filter.subject) && matchString(event.severity, filter.severity) && matchRecord(event.data, filter.data) && matchRecord(event.metadata, filter.metadata);
|
|
7015
7041
|
}
|
|
@@ -7616,9 +7642,66 @@ function taskEventData(task, extra = {}) {
|
|
|
7616
7642
|
started_at: task.started_at,
|
|
7617
7643
|
completed_at: task.completed_at,
|
|
7618
7644
|
due_at: task.due_at,
|
|
7645
|
+
requires_approval: task.requires_approval,
|
|
7646
|
+
approved_by: task.approved_by,
|
|
7647
|
+
approved_at: task.approved_at,
|
|
7619
7648
|
...extra
|
|
7620
7649
|
};
|
|
7621
7650
|
}
|
|
7651
|
+
function booleanField(value) {
|
|
7652
|
+
if (typeof value === "boolean")
|
|
7653
|
+
return value;
|
|
7654
|
+
if (typeof value === "number") {
|
|
7655
|
+
if (value === 1)
|
|
7656
|
+
return true;
|
|
7657
|
+
if (value === 0)
|
|
7658
|
+
return false;
|
|
7659
|
+
}
|
|
7660
|
+
if (typeof value === "string") {
|
|
7661
|
+
const normalized = value.trim().toLowerCase();
|
|
7662
|
+
if (["true", "1", "yes", "on"].includes(normalized))
|
|
7663
|
+
return true;
|
|
7664
|
+
if (["false", "0", "no", "off"].includes(normalized))
|
|
7665
|
+
return false;
|
|
7666
|
+
}
|
|
7667
|
+
return;
|
|
7668
|
+
}
|
|
7669
|
+
function objectField(value) {
|
|
7670
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : undefined;
|
|
7671
|
+
}
|
|
7672
|
+
function firstBoolean(records, keys) {
|
|
7673
|
+
for (const record of records) {
|
|
7674
|
+
for (const key of keys) {
|
|
7675
|
+
const value = booleanField(record[key]);
|
|
7676
|
+
if (value !== undefined)
|
|
7677
|
+
return value;
|
|
7678
|
+
}
|
|
7679
|
+
}
|
|
7680
|
+
return;
|
|
7681
|
+
}
|
|
7682
|
+
function routingAutomationMetadata(task) {
|
|
7683
|
+
const automation = objectField(task.metadata.automation);
|
|
7684
|
+
const records = [task.metadata];
|
|
7685
|
+
if (automation)
|
|
7686
|
+
records.push(automation);
|
|
7687
|
+
const result = {};
|
|
7688
|
+
const aliases = [
|
|
7689
|
+
["allowed", ["allowed", "automation_allowed", "automationAllowed"]],
|
|
7690
|
+
["no_auto", ["no_auto", "noAuto"]],
|
|
7691
|
+
["manual", ["manual"]],
|
|
7692
|
+
["manual_required", ["manual_required", "manualRequired"]],
|
|
7693
|
+
["requires_approval", ["requires_approval", "requiresApproval"]],
|
|
7694
|
+
["approval_required", ["approval_required", "approvalRequired"]]
|
|
7695
|
+
];
|
|
7696
|
+
for (const [canonical, keys] of aliases) {
|
|
7697
|
+
const value = firstBoolean(records, keys);
|
|
7698
|
+
if (value !== undefined)
|
|
7699
|
+
result[canonical] = value;
|
|
7700
|
+
}
|
|
7701
|
+
if (task.requires_approval)
|
|
7702
|
+
result.requires_approval = true;
|
|
7703
|
+
return Object.keys(result).length > 0 ? result : undefined;
|
|
7704
|
+
}
|
|
7622
7705
|
function taskEventMetadata(task) {
|
|
7623
7706
|
const metadata = {
|
|
7624
7707
|
package: "@hasna/todos",
|
|
@@ -7629,6 +7712,14 @@ function taskEventMetadata(task) {
|
|
|
7629
7712
|
task_list_id: task.task_list_id,
|
|
7630
7713
|
working_dir: task.working_dir
|
|
7631
7714
|
};
|
|
7715
|
+
const routeEnabled = booleanField(task.metadata.route_enabled);
|
|
7716
|
+
if (routeEnabled !== undefined) {
|
|
7717
|
+
metadata.route_enabled = routeEnabled;
|
|
7718
|
+
}
|
|
7719
|
+
const automation = routingAutomationMetadata(task);
|
|
7720
|
+
if (automation) {
|
|
7721
|
+
metadata.automation = automation;
|
|
7722
|
+
}
|
|
7632
7723
|
try {
|
|
7633
7724
|
const project = task.project_id ? getProject(task.project_id) : null;
|
|
7634
7725
|
const projectPath = project ? readMachineLocalPath(project) ?? project.path : task.working_dir;
|
|
@@ -7646,9 +7737,6 @@ function taskEventMetadata(task) {
|
|
|
7646
7737
|
if (projectPath) {
|
|
7647
7738
|
metadata.project_kind = classifyProjectKind(projectPath);
|
|
7648
7739
|
metadata.project_is_worktree = isWorktreePath(projectPath);
|
|
7649
|
-
if (typeof task.metadata.route_enabled === "boolean") {
|
|
7650
|
-
metadata.route_enabled = task.metadata.route_enabled;
|
|
7651
|
-
}
|
|
7652
7740
|
metadata.working_dir = task.working_dir ?? projectPath;
|
|
7653
7741
|
}
|
|
7654
7742
|
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;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"packageName": "@hasna/todos",
|
|
3
|
-
"packageVersion": "0.11.
|
|
3
|
+
"packageVersion": "0.11.60",
|
|
4
4
|
"repository": "https://github.com/hasna/todos.git",
|
|
5
|
-
"gitCommit": "
|
|
6
|
-
"generatedAt": "2026-06-
|
|
5
|
+
"gitCommit": "ab13bc3c275c0fd348e6ff5a2a1a6109002baf04",
|
|
6
|
+
"generatedAt": "2026-06-27T12:02:52.731Z"
|
|
7
7
|
}
|
package/dist/server/index.js
CHANGED
|
@@ -4099,7 +4099,7 @@ var init_event_hooks = __esm(() => {
|
|
|
4099
4099
|
VALID_TARGETS = new Set(["stdout", "file", "socket", "script"]);
|
|
4100
4100
|
});
|
|
4101
4101
|
|
|
4102
|
-
// node_modules/.bun/@hasna+events@0.1.
|
|
4102
|
+
// node_modules/.bun/@hasna+events@0.1.10/node_modules/@hasna/events/dist/index.js
|
|
4103
4103
|
import { chmod, mkdir, readFile, rename, writeFile } from "fs/promises";
|
|
4104
4104
|
import { existsSync as existsSync6 } from "fs";
|
|
4105
4105
|
import { homedir } from "os";
|
|
@@ -4146,14 +4146,40 @@ function matchRecord(input, matcher) {
|
|
|
4146
4146
|
return true;
|
|
4147
4147
|
return Object.entries(matcher).every(([path, expected]) => {
|
|
4148
4148
|
const actual = getPathValue(input, path);
|
|
4149
|
-
|
|
4150
|
-
return matchString(actual === undefined ? undefined : String(actual), expected, {
|
|
4151
|
-
segmentSafe: path.endsWith("_path") || path.endsWith(".path")
|
|
4152
|
-
});
|
|
4153
|
-
}
|
|
4154
|
-
return actual === expected;
|
|
4149
|
+
return matchField(actual, expected, path);
|
|
4155
4150
|
});
|
|
4156
4151
|
}
|
|
4152
|
+
function matchField(actual, expected, path) {
|
|
4153
|
+
if (isNegativeMatcher(expected)) {
|
|
4154
|
+
return !matchPositiveField(actual, expected.not, path);
|
|
4155
|
+
}
|
|
4156
|
+
return matchPositiveField(actual, expected, path);
|
|
4157
|
+
}
|
|
4158
|
+
function matchPositiveField(actual, expected, path) {
|
|
4159
|
+
if (typeof expected === "string" || Array.isArray(expected)) {
|
|
4160
|
+
return stringCandidates(actual).some((candidate) => matchString(candidate, expected, {
|
|
4161
|
+
segmentSafe: path.endsWith("_path") || path.endsWith(".path")
|
|
4162
|
+
}));
|
|
4163
|
+
}
|
|
4164
|
+
if (Array.isArray(actual)) {
|
|
4165
|
+
return actual.some((item) => item === expected);
|
|
4166
|
+
}
|
|
4167
|
+
return actual === expected;
|
|
4168
|
+
}
|
|
4169
|
+
function stringCandidates(actual) {
|
|
4170
|
+
if (actual === undefined)
|
|
4171
|
+
return [];
|
|
4172
|
+
if (Array.isArray(actual)) {
|
|
4173
|
+
return actual.flatMap((item) => isPrimitiveFieldValue(item) ? [String(item)] : []);
|
|
4174
|
+
}
|
|
4175
|
+
return [String(actual)];
|
|
4176
|
+
}
|
|
4177
|
+
function isPrimitiveFieldValue(value) {
|
|
4178
|
+
return value === null || typeof value === "string" || typeof value === "number" || typeof value === "boolean";
|
|
4179
|
+
}
|
|
4180
|
+
function isNegativeMatcher(value) {
|
|
4181
|
+
return Boolean(value && typeof value === "object" && !Array.isArray(value) && "not" in value);
|
|
4182
|
+
}
|
|
4157
4183
|
function eventMatchesFilter(event, filter) {
|
|
4158
4184
|
return matchString(event.source, filter.source) && matchString(event.type, filter.type) && matchString(event.subject, filter.subject) && matchString(event.severity, filter.severity) && matchRecord(event.data, filter.data) && matchRecord(event.metadata, filter.metadata);
|
|
4159
4185
|
}
|
|
@@ -4761,9 +4787,66 @@ function taskEventData(task, extra = {}) {
|
|
|
4761
4787
|
started_at: task.started_at,
|
|
4762
4788
|
completed_at: task.completed_at,
|
|
4763
4789
|
due_at: task.due_at,
|
|
4790
|
+
requires_approval: task.requires_approval,
|
|
4791
|
+
approved_by: task.approved_by,
|
|
4792
|
+
approved_at: task.approved_at,
|
|
4764
4793
|
...extra
|
|
4765
4794
|
};
|
|
4766
4795
|
}
|
|
4796
|
+
function booleanField(value) {
|
|
4797
|
+
if (typeof value === "boolean")
|
|
4798
|
+
return value;
|
|
4799
|
+
if (typeof value === "number") {
|
|
4800
|
+
if (value === 1)
|
|
4801
|
+
return true;
|
|
4802
|
+
if (value === 0)
|
|
4803
|
+
return false;
|
|
4804
|
+
}
|
|
4805
|
+
if (typeof value === "string") {
|
|
4806
|
+
const normalized = value.trim().toLowerCase();
|
|
4807
|
+
if (["true", "1", "yes", "on"].includes(normalized))
|
|
4808
|
+
return true;
|
|
4809
|
+
if (["false", "0", "no", "off"].includes(normalized))
|
|
4810
|
+
return false;
|
|
4811
|
+
}
|
|
4812
|
+
return;
|
|
4813
|
+
}
|
|
4814
|
+
function objectField(value) {
|
|
4815
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : undefined;
|
|
4816
|
+
}
|
|
4817
|
+
function firstBoolean(records, keys) {
|
|
4818
|
+
for (const record of records) {
|
|
4819
|
+
for (const key of keys) {
|
|
4820
|
+
const value = booleanField(record[key]);
|
|
4821
|
+
if (value !== undefined)
|
|
4822
|
+
return value;
|
|
4823
|
+
}
|
|
4824
|
+
}
|
|
4825
|
+
return;
|
|
4826
|
+
}
|
|
4827
|
+
function routingAutomationMetadata(task) {
|
|
4828
|
+
const automation = objectField(task.metadata.automation);
|
|
4829
|
+
const records = [task.metadata];
|
|
4830
|
+
if (automation)
|
|
4831
|
+
records.push(automation);
|
|
4832
|
+
const result = {};
|
|
4833
|
+
const aliases = [
|
|
4834
|
+
["allowed", ["allowed", "automation_allowed", "automationAllowed"]],
|
|
4835
|
+
["no_auto", ["no_auto", "noAuto"]],
|
|
4836
|
+
["manual", ["manual"]],
|
|
4837
|
+
["manual_required", ["manual_required", "manualRequired"]],
|
|
4838
|
+
["requires_approval", ["requires_approval", "requiresApproval"]],
|
|
4839
|
+
["approval_required", ["approval_required", "approvalRequired"]]
|
|
4840
|
+
];
|
|
4841
|
+
for (const [canonical, keys] of aliases) {
|
|
4842
|
+
const value = firstBoolean(records, keys);
|
|
4843
|
+
if (value !== undefined)
|
|
4844
|
+
result[canonical] = value;
|
|
4845
|
+
}
|
|
4846
|
+
if (task.requires_approval)
|
|
4847
|
+
result.requires_approval = true;
|
|
4848
|
+
return Object.keys(result).length > 0 ? result : undefined;
|
|
4849
|
+
}
|
|
4767
4850
|
function taskEventMetadata(task) {
|
|
4768
4851
|
const metadata = {
|
|
4769
4852
|
package: "@hasna/todos",
|
|
@@ -4774,6 +4857,14 @@ function taskEventMetadata(task) {
|
|
|
4774
4857
|
task_list_id: task.task_list_id,
|
|
4775
4858
|
working_dir: task.working_dir
|
|
4776
4859
|
};
|
|
4860
|
+
const routeEnabled = booleanField(task.metadata.route_enabled);
|
|
4861
|
+
if (routeEnabled !== undefined) {
|
|
4862
|
+
metadata.route_enabled = routeEnabled;
|
|
4863
|
+
}
|
|
4864
|
+
const automation = routingAutomationMetadata(task);
|
|
4865
|
+
if (automation) {
|
|
4866
|
+
metadata.automation = automation;
|
|
4867
|
+
}
|
|
4777
4868
|
try {
|
|
4778
4869
|
const project = task.project_id ? getProject(task.project_id) : null;
|
|
4779
4870
|
const projectPath = project ? readMachineLocalPath(project) ?? project.path : task.working_dir;
|
|
@@ -4791,9 +4882,6 @@ function taskEventMetadata(task) {
|
|
|
4791
4882
|
if (projectPath) {
|
|
4792
4883
|
metadata.project_kind = classifyProjectKind(projectPath);
|
|
4793
4884
|
metadata.project_is_worktree = isWorktreePath(projectPath);
|
|
4794
|
-
if (typeof task.metadata.route_enabled === "boolean") {
|
|
4795
|
-
metadata.route_enabled = task.metadata.route_enabled;
|
|
4796
|
-
}
|
|
4797
4885
|
metadata.working_dir = task.working_dir ?? projectPath;
|
|
4798
4886
|
}
|
|
4799
4887
|
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;
|
package/dist/storage.js
CHANGED
|
@@ -4503,7 +4503,7 @@ async function testLocalEventHook(name, input) {
|
|
|
4503
4503
|
return emitLocalEventHooks({ ...input, hooks: [hook] });
|
|
4504
4504
|
}
|
|
4505
4505
|
|
|
4506
|
-
// node_modules/.bun/@hasna+events@0.1.
|
|
4506
|
+
// node_modules/.bun/@hasna+events@0.1.10/node_modules/@hasna/events/dist/index.js
|
|
4507
4507
|
import { chmod, mkdir, readFile, rename, writeFile } from "fs/promises";
|
|
4508
4508
|
import { existsSync as existsSync5 } from "fs";
|
|
4509
4509
|
import { homedir } from "os";
|
|
@@ -4550,14 +4550,40 @@ function matchRecord(input, matcher) {
|
|
|
4550
4550
|
return true;
|
|
4551
4551
|
return Object.entries(matcher).every(([path, expected]) => {
|
|
4552
4552
|
const actual = getPathValue(input, path);
|
|
4553
|
-
|
|
4554
|
-
return matchString(actual === undefined ? undefined : String(actual), expected, {
|
|
4555
|
-
segmentSafe: path.endsWith("_path") || path.endsWith(".path")
|
|
4556
|
-
});
|
|
4557
|
-
}
|
|
4558
|
-
return actual === expected;
|
|
4553
|
+
return matchField(actual, expected, path);
|
|
4559
4554
|
});
|
|
4560
4555
|
}
|
|
4556
|
+
function matchField(actual, expected, path) {
|
|
4557
|
+
if (isNegativeMatcher(expected)) {
|
|
4558
|
+
return !matchPositiveField(actual, expected.not, path);
|
|
4559
|
+
}
|
|
4560
|
+
return matchPositiveField(actual, expected, path);
|
|
4561
|
+
}
|
|
4562
|
+
function matchPositiveField(actual, expected, path) {
|
|
4563
|
+
if (typeof expected === "string" || Array.isArray(expected)) {
|
|
4564
|
+
return stringCandidates(actual).some((candidate) => matchString(candidate, expected, {
|
|
4565
|
+
segmentSafe: path.endsWith("_path") || path.endsWith(".path")
|
|
4566
|
+
}));
|
|
4567
|
+
}
|
|
4568
|
+
if (Array.isArray(actual)) {
|
|
4569
|
+
return actual.some((item) => item === expected);
|
|
4570
|
+
}
|
|
4571
|
+
return actual === expected;
|
|
4572
|
+
}
|
|
4573
|
+
function stringCandidates(actual) {
|
|
4574
|
+
if (actual === undefined)
|
|
4575
|
+
return [];
|
|
4576
|
+
if (Array.isArray(actual)) {
|
|
4577
|
+
return actual.flatMap((item) => isPrimitiveFieldValue(item) ? [String(item)] : []);
|
|
4578
|
+
}
|
|
4579
|
+
return [String(actual)];
|
|
4580
|
+
}
|
|
4581
|
+
function isPrimitiveFieldValue(value) {
|
|
4582
|
+
return value === null || typeof value === "string" || typeof value === "number" || typeof value === "boolean";
|
|
4583
|
+
}
|
|
4584
|
+
function isNegativeMatcher(value) {
|
|
4585
|
+
return Boolean(value && typeof value === "object" && !Array.isArray(value) && "not" in value);
|
|
4586
|
+
}
|
|
4561
4587
|
function eventMatchesFilter(event, filter) {
|
|
4562
4588
|
return matchString(event.source, filter.source) && matchString(event.type, filter.type) && matchString(event.subject, filter.subject) && matchString(event.severity, filter.severity) && matchRecord(event.data, filter.data) && matchRecord(event.metadata, filter.metadata);
|
|
4563
4589
|
}
|
|
@@ -5164,9 +5190,66 @@ function taskEventData(task, extra = {}) {
|
|
|
5164
5190
|
started_at: task.started_at,
|
|
5165
5191
|
completed_at: task.completed_at,
|
|
5166
5192
|
due_at: task.due_at,
|
|
5193
|
+
requires_approval: task.requires_approval,
|
|
5194
|
+
approved_by: task.approved_by,
|
|
5195
|
+
approved_at: task.approved_at,
|
|
5167
5196
|
...extra
|
|
5168
5197
|
};
|
|
5169
5198
|
}
|
|
5199
|
+
function booleanField(value) {
|
|
5200
|
+
if (typeof value === "boolean")
|
|
5201
|
+
return value;
|
|
5202
|
+
if (typeof value === "number") {
|
|
5203
|
+
if (value === 1)
|
|
5204
|
+
return true;
|
|
5205
|
+
if (value === 0)
|
|
5206
|
+
return false;
|
|
5207
|
+
}
|
|
5208
|
+
if (typeof value === "string") {
|
|
5209
|
+
const normalized = value.trim().toLowerCase();
|
|
5210
|
+
if (["true", "1", "yes", "on"].includes(normalized))
|
|
5211
|
+
return true;
|
|
5212
|
+
if (["false", "0", "no", "off"].includes(normalized))
|
|
5213
|
+
return false;
|
|
5214
|
+
}
|
|
5215
|
+
return;
|
|
5216
|
+
}
|
|
5217
|
+
function objectField(value) {
|
|
5218
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : undefined;
|
|
5219
|
+
}
|
|
5220
|
+
function firstBoolean(records, keys) {
|
|
5221
|
+
for (const record of records) {
|
|
5222
|
+
for (const key of keys) {
|
|
5223
|
+
const value = booleanField(record[key]);
|
|
5224
|
+
if (value !== undefined)
|
|
5225
|
+
return value;
|
|
5226
|
+
}
|
|
5227
|
+
}
|
|
5228
|
+
return;
|
|
5229
|
+
}
|
|
5230
|
+
function routingAutomationMetadata(task) {
|
|
5231
|
+
const automation = objectField(task.metadata.automation);
|
|
5232
|
+
const records = [task.metadata];
|
|
5233
|
+
if (automation)
|
|
5234
|
+
records.push(automation);
|
|
5235
|
+
const result = {};
|
|
5236
|
+
const aliases = [
|
|
5237
|
+
["allowed", ["allowed", "automation_allowed", "automationAllowed"]],
|
|
5238
|
+
["no_auto", ["no_auto", "noAuto"]],
|
|
5239
|
+
["manual", ["manual"]],
|
|
5240
|
+
["manual_required", ["manual_required", "manualRequired"]],
|
|
5241
|
+
["requires_approval", ["requires_approval", "requiresApproval"]],
|
|
5242
|
+
["approval_required", ["approval_required", "approvalRequired"]]
|
|
5243
|
+
];
|
|
5244
|
+
for (const [canonical, keys] of aliases) {
|
|
5245
|
+
const value = firstBoolean(records, keys);
|
|
5246
|
+
if (value !== undefined)
|
|
5247
|
+
result[canonical] = value;
|
|
5248
|
+
}
|
|
5249
|
+
if (task.requires_approval)
|
|
5250
|
+
result.requires_approval = true;
|
|
5251
|
+
return Object.keys(result).length > 0 ? result : undefined;
|
|
5252
|
+
}
|
|
5170
5253
|
function taskEventMetadata(task) {
|
|
5171
5254
|
const metadata = {
|
|
5172
5255
|
package: "@hasna/todos",
|
|
@@ -5177,6 +5260,14 @@ function taskEventMetadata(task) {
|
|
|
5177
5260
|
task_list_id: task.task_list_id,
|
|
5178
5261
|
working_dir: task.working_dir
|
|
5179
5262
|
};
|
|
5263
|
+
const routeEnabled = booleanField(task.metadata.route_enabled);
|
|
5264
|
+
if (routeEnabled !== undefined) {
|
|
5265
|
+
metadata.route_enabled = routeEnabled;
|
|
5266
|
+
}
|
|
5267
|
+
const automation = routingAutomationMetadata(task);
|
|
5268
|
+
if (automation) {
|
|
5269
|
+
metadata.automation = automation;
|
|
5270
|
+
}
|
|
5180
5271
|
try {
|
|
5181
5272
|
const project = task.project_id ? getProject(task.project_id) : null;
|
|
5182
5273
|
const projectPath = project ? readMachineLocalPath(project) ?? project.path : task.working_dir;
|
|
@@ -5194,9 +5285,6 @@ function taskEventMetadata(task) {
|
|
|
5194
5285
|
if (projectPath) {
|
|
5195
5286
|
metadata.project_kind = classifyProjectKind(projectPath);
|
|
5196
5287
|
metadata.project_is_worktree = isWorktreePath(projectPath);
|
|
5197
|
-
if (typeof task.metadata.route_enabled === "boolean") {
|
|
5198
|
-
metadata.route_enabled = task.metadata.route_enabled;
|
|
5199
|
-
}
|
|
5200
5288
|
metadata.working_dir = task.working_dir ?? projectPath;
|
|
5201
5289
|
}
|
|
5202
5290
|
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;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hasna/todos",
|
|
3
|
-
"version": "0.11.
|
|
3
|
+
"version": "0.11.60",
|
|
4
4
|
"description": "Universal task management for AI coding agents - CLI + MCP server + interactive TUI",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -89,7 +89,7 @@
|
|
|
89
89
|
"author": "Andrei Hasna <andrei@hasna.com>",
|
|
90
90
|
"license": "Apache-2.0",
|
|
91
91
|
"dependencies": {
|
|
92
|
-
"@hasna/events": "^0.1.
|
|
92
|
+
"@hasna/events": "^0.1.10",
|
|
93
93
|
"@modelcontextprotocol/sdk": "^1.12.1",
|
|
94
94
|
"chalk": "^5.4.1",
|
|
95
95
|
"commander": "^13.1.0",
|