@hasna/todos 0.11.59 → 0.11.61
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 +112 -11
- package/dist/contracts.js +112 -11
- package/dist/index.js +112 -11
- package/dist/lib/shared-events.d.ts.map +1 -1
- package/dist/mcp/index.js +112 -11
- package/dist/registry.js +112 -11
- package/dist/release-provenance.json +3 -3
- package/dist/server/index.js +112 -11
- package/dist/storage.js +112 -11
- 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.11/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";
|
|
@@ -6256,6 +6256,19 @@ function getPathValue(input, path) {
|
|
|
6256
6256
|
return;
|
|
6257
6257
|
}, input);
|
|
6258
6258
|
}
|
|
6259
|
+
function getFieldValues(input, path) {
|
|
6260
|
+
const values = [];
|
|
6261
|
+
const push = (value) => {
|
|
6262
|
+
if (!values.some((item) => Object.is(item, value)))
|
|
6263
|
+
values.push(value);
|
|
6264
|
+
};
|
|
6265
|
+
if (path.includes(".") && path in input)
|
|
6266
|
+
push(input[path]);
|
|
6267
|
+
const nestedValue = getPathValue(input, path);
|
|
6268
|
+
if (nestedValue !== undefined || !path.includes("."))
|
|
6269
|
+
push(nestedValue);
|
|
6270
|
+
return values;
|
|
6271
|
+
}
|
|
6259
6272
|
function wildcardToRegExp(pattern, options = {}) {
|
|
6260
6273
|
let body = "";
|
|
6261
6274
|
for (let index = 0;index < pattern.length; index += 1) {
|
|
@@ -6285,15 +6298,41 @@ function matchRecord(input, matcher) {
|
|
|
6285
6298
|
if (!matcher)
|
|
6286
6299
|
return true;
|
|
6287
6300
|
return Object.entries(matcher).every(([path, expected]) => {
|
|
6288
|
-
const
|
|
6289
|
-
|
|
6290
|
-
return matchString(actual === undefined ? undefined : String(actual), expected, {
|
|
6291
|
-
segmentSafe: path.endsWith("_path") || path.endsWith(".path")
|
|
6292
|
-
});
|
|
6293
|
-
}
|
|
6294
|
-
return actual === expected;
|
|
6301
|
+
const actualValues = getFieldValues(input, path);
|
|
6302
|
+
return matchField(actualValues, expected, path);
|
|
6295
6303
|
});
|
|
6296
6304
|
}
|
|
6305
|
+
function matchField(actualValues, expected, path) {
|
|
6306
|
+
if (isNegativeMatcher(expected)) {
|
|
6307
|
+
return !actualValues.some((actual) => matchPositiveField(actual, expected.not, path));
|
|
6308
|
+
}
|
|
6309
|
+
return actualValues.some((actual) => matchPositiveField(actual, expected, path));
|
|
6310
|
+
}
|
|
6311
|
+
function matchPositiveField(actual, expected, path) {
|
|
6312
|
+
if (typeof expected === "string" || Array.isArray(expected)) {
|
|
6313
|
+
return stringCandidates(actual).some((candidate) => matchString(candidate, expected, {
|
|
6314
|
+
segmentSafe: path.endsWith("_path") || path.endsWith(".path")
|
|
6315
|
+
}));
|
|
6316
|
+
}
|
|
6317
|
+
if (Array.isArray(actual)) {
|
|
6318
|
+
return actual.some((item) => item === expected);
|
|
6319
|
+
}
|
|
6320
|
+
return actual === expected;
|
|
6321
|
+
}
|
|
6322
|
+
function stringCandidates(actual) {
|
|
6323
|
+
if (actual === undefined)
|
|
6324
|
+
return [];
|
|
6325
|
+
if (Array.isArray(actual)) {
|
|
6326
|
+
return actual.flatMap((item) => isPrimitiveFieldValue(item) ? [String(item)] : []);
|
|
6327
|
+
}
|
|
6328
|
+
return [String(actual)];
|
|
6329
|
+
}
|
|
6330
|
+
function isPrimitiveFieldValue(value) {
|
|
6331
|
+
return value === null || typeof value === "string" || typeof value === "number" || typeof value === "boolean";
|
|
6332
|
+
}
|
|
6333
|
+
function isNegativeMatcher(value) {
|
|
6334
|
+
return Boolean(value && typeof value === "object" && !Array.isArray(value) && "not" in value);
|
|
6335
|
+
}
|
|
6297
6336
|
function eventMatchesFilter(event, filter) {
|
|
6298
6337
|
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
6338
|
}
|
|
@@ -6901,9 +6940,66 @@ function taskEventData(task, extra = {}) {
|
|
|
6901
6940
|
started_at: task.started_at,
|
|
6902
6941
|
completed_at: task.completed_at,
|
|
6903
6942
|
due_at: task.due_at,
|
|
6943
|
+
requires_approval: task.requires_approval,
|
|
6944
|
+
approved_by: task.approved_by,
|
|
6945
|
+
approved_at: task.approved_at,
|
|
6904
6946
|
...extra
|
|
6905
6947
|
};
|
|
6906
6948
|
}
|
|
6949
|
+
function booleanField(value) {
|
|
6950
|
+
if (typeof value === "boolean")
|
|
6951
|
+
return value;
|
|
6952
|
+
if (typeof value === "number") {
|
|
6953
|
+
if (value === 1)
|
|
6954
|
+
return true;
|
|
6955
|
+
if (value === 0)
|
|
6956
|
+
return false;
|
|
6957
|
+
}
|
|
6958
|
+
if (typeof value === "string") {
|
|
6959
|
+
const normalized = value.trim().toLowerCase();
|
|
6960
|
+
if (["true", "1", "yes", "on"].includes(normalized))
|
|
6961
|
+
return true;
|
|
6962
|
+
if (["false", "0", "no", "off"].includes(normalized))
|
|
6963
|
+
return false;
|
|
6964
|
+
}
|
|
6965
|
+
return;
|
|
6966
|
+
}
|
|
6967
|
+
function objectField(value) {
|
|
6968
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : undefined;
|
|
6969
|
+
}
|
|
6970
|
+
function firstBoolean(records, keys) {
|
|
6971
|
+
for (const record of records) {
|
|
6972
|
+
for (const key of keys) {
|
|
6973
|
+
const value = booleanField(record[key]);
|
|
6974
|
+
if (value !== undefined)
|
|
6975
|
+
return value;
|
|
6976
|
+
}
|
|
6977
|
+
}
|
|
6978
|
+
return;
|
|
6979
|
+
}
|
|
6980
|
+
function routingAutomationMetadata(task) {
|
|
6981
|
+
const automation = objectField(task.metadata.automation);
|
|
6982
|
+
const records = [task.metadata];
|
|
6983
|
+
if (automation)
|
|
6984
|
+
records.push(automation);
|
|
6985
|
+
const result = {};
|
|
6986
|
+
const aliases = [
|
|
6987
|
+
["allowed", ["allowed", "automation_allowed", "automationAllowed"]],
|
|
6988
|
+
["no_auto", ["no_auto", "noAuto"]],
|
|
6989
|
+
["manual", ["manual"]],
|
|
6990
|
+
["manual_required", ["manual_required", "manualRequired"]],
|
|
6991
|
+
["requires_approval", ["requires_approval", "requiresApproval"]],
|
|
6992
|
+
["approval_required", ["approval_required", "approvalRequired"]]
|
|
6993
|
+
];
|
|
6994
|
+
for (const [canonical, keys] of aliases) {
|
|
6995
|
+
const value = firstBoolean(records, keys);
|
|
6996
|
+
if (value !== undefined)
|
|
6997
|
+
result[canonical] = value;
|
|
6998
|
+
}
|
|
6999
|
+
if (task.requires_approval)
|
|
7000
|
+
result.requires_approval = true;
|
|
7001
|
+
return Object.keys(result).length > 0 ? result : undefined;
|
|
7002
|
+
}
|
|
6907
7003
|
function taskEventMetadata(task) {
|
|
6908
7004
|
const metadata = {
|
|
6909
7005
|
package: "@hasna/todos",
|
|
@@ -6914,6 +7010,14 @@ function taskEventMetadata(task) {
|
|
|
6914
7010
|
task_list_id: task.task_list_id,
|
|
6915
7011
|
working_dir: task.working_dir
|
|
6916
7012
|
};
|
|
7013
|
+
const routeEnabled = booleanField(task.metadata.route_enabled);
|
|
7014
|
+
if (routeEnabled !== undefined) {
|
|
7015
|
+
metadata.route_enabled = routeEnabled;
|
|
7016
|
+
}
|
|
7017
|
+
const automation = routingAutomationMetadata(task);
|
|
7018
|
+
if (automation) {
|
|
7019
|
+
metadata.automation = automation;
|
|
7020
|
+
}
|
|
6917
7021
|
try {
|
|
6918
7022
|
const project = task.project_id ? getProject(task.project_id) : null;
|
|
6919
7023
|
const projectPath = project ? readMachineLocalPath(project) ?? project.path : task.working_dir;
|
|
@@ -6931,9 +7035,6 @@ function taskEventMetadata(task) {
|
|
|
6931
7035
|
if (projectPath) {
|
|
6932
7036
|
metadata.project_kind = classifyProjectKind(projectPath);
|
|
6933
7037
|
metadata.project_is_worktree = isWorktreePath(projectPath);
|
|
6934
|
-
if (typeof task.metadata.route_enabled === "boolean") {
|
|
6935
|
-
metadata.route_enabled = task.metadata.route_enabled;
|
|
6936
|
-
}
|
|
6937
7038
|
metadata.working_dir = task.working_dir ?? projectPath;
|
|
6938
7039
|
}
|
|
6939
7040
|
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.11/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";
|
|
@@ -6972,6 +6972,19 @@ function getPathValue(input, path) {
|
|
|
6972
6972
|
return;
|
|
6973
6973
|
}, input);
|
|
6974
6974
|
}
|
|
6975
|
+
function getFieldValues(input, path) {
|
|
6976
|
+
const values = [];
|
|
6977
|
+
const push = (value) => {
|
|
6978
|
+
if (!values.some((item) => Object.is(item, value)))
|
|
6979
|
+
values.push(value);
|
|
6980
|
+
};
|
|
6981
|
+
if (path.includes(".") && path in input)
|
|
6982
|
+
push(input[path]);
|
|
6983
|
+
const nestedValue = getPathValue(input, path);
|
|
6984
|
+
if (nestedValue !== undefined || !path.includes("."))
|
|
6985
|
+
push(nestedValue);
|
|
6986
|
+
return values;
|
|
6987
|
+
}
|
|
6975
6988
|
function wildcardToRegExp(pattern, options = {}) {
|
|
6976
6989
|
let body = "";
|
|
6977
6990
|
for (let index = 0;index < pattern.length; index += 1) {
|
|
@@ -7001,15 +7014,41 @@ function matchRecord(input, matcher) {
|
|
|
7001
7014
|
if (!matcher)
|
|
7002
7015
|
return true;
|
|
7003
7016
|
return Object.entries(matcher).every(([path, expected]) => {
|
|
7004
|
-
const
|
|
7005
|
-
|
|
7006
|
-
return matchString(actual === undefined ? undefined : String(actual), expected, {
|
|
7007
|
-
segmentSafe: path.endsWith("_path") || path.endsWith(".path")
|
|
7008
|
-
});
|
|
7009
|
-
}
|
|
7010
|
-
return actual === expected;
|
|
7017
|
+
const actualValues = getFieldValues(input, path);
|
|
7018
|
+
return matchField(actualValues, expected, path);
|
|
7011
7019
|
});
|
|
7012
7020
|
}
|
|
7021
|
+
function matchField(actualValues, expected, path) {
|
|
7022
|
+
if (isNegativeMatcher(expected)) {
|
|
7023
|
+
return !actualValues.some((actual) => matchPositiveField(actual, expected.not, path));
|
|
7024
|
+
}
|
|
7025
|
+
return actualValues.some((actual) => matchPositiveField(actual, expected, path));
|
|
7026
|
+
}
|
|
7027
|
+
function matchPositiveField(actual, expected, path) {
|
|
7028
|
+
if (typeof expected === "string" || Array.isArray(expected)) {
|
|
7029
|
+
return stringCandidates(actual).some((candidate) => matchString(candidate, expected, {
|
|
7030
|
+
segmentSafe: path.endsWith("_path") || path.endsWith(".path")
|
|
7031
|
+
}));
|
|
7032
|
+
}
|
|
7033
|
+
if (Array.isArray(actual)) {
|
|
7034
|
+
return actual.some((item) => item === expected);
|
|
7035
|
+
}
|
|
7036
|
+
return actual === expected;
|
|
7037
|
+
}
|
|
7038
|
+
function stringCandidates(actual) {
|
|
7039
|
+
if (actual === undefined)
|
|
7040
|
+
return [];
|
|
7041
|
+
if (Array.isArray(actual)) {
|
|
7042
|
+
return actual.flatMap((item) => isPrimitiveFieldValue(item) ? [String(item)] : []);
|
|
7043
|
+
}
|
|
7044
|
+
return [String(actual)];
|
|
7045
|
+
}
|
|
7046
|
+
function isPrimitiveFieldValue(value) {
|
|
7047
|
+
return value === null || typeof value === "string" || typeof value === "number" || typeof value === "boolean";
|
|
7048
|
+
}
|
|
7049
|
+
function isNegativeMatcher(value) {
|
|
7050
|
+
return Boolean(value && typeof value === "object" && !Array.isArray(value) && "not" in value);
|
|
7051
|
+
}
|
|
7013
7052
|
function eventMatchesFilter(event, filter) {
|
|
7014
7053
|
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
7054
|
}
|
|
@@ -7616,9 +7655,66 @@ function taskEventData(task, extra = {}) {
|
|
|
7616
7655
|
started_at: task.started_at,
|
|
7617
7656
|
completed_at: task.completed_at,
|
|
7618
7657
|
due_at: task.due_at,
|
|
7658
|
+
requires_approval: task.requires_approval,
|
|
7659
|
+
approved_by: task.approved_by,
|
|
7660
|
+
approved_at: task.approved_at,
|
|
7619
7661
|
...extra
|
|
7620
7662
|
};
|
|
7621
7663
|
}
|
|
7664
|
+
function booleanField(value) {
|
|
7665
|
+
if (typeof value === "boolean")
|
|
7666
|
+
return value;
|
|
7667
|
+
if (typeof value === "number") {
|
|
7668
|
+
if (value === 1)
|
|
7669
|
+
return true;
|
|
7670
|
+
if (value === 0)
|
|
7671
|
+
return false;
|
|
7672
|
+
}
|
|
7673
|
+
if (typeof value === "string") {
|
|
7674
|
+
const normalized = value.trim().toLowerCase();
|
|
7675
|
+
if (["true", "1", "yes", "on"].includes(normalized))
|
|
7676
|
+
return true;
|
|
7677
|
+
if (["false", "0", "no", "off"].includes(normalized))
|
|
7678
|
+
return false;
|
|
7679
|
+
}
|
|
7680
|
+
return;
|
|
7681
|
+
}
|
|
7682
|
+
function objectField(value) {
|
|
7683
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : undefined;
|
|
7684
|
+
}
|
|
7685
|
+
function firstBoolean(records, keys) {
|
|
7686
|
+
for (const record of records) {
|
|
7687
|
+
for (const key of keys) {
|
|
7688
|
+
const value = booleanField(record[key]);
|
|
7689
|
+
if (value !== undefined)
|
|
7690
|
+
return value;
|
|
7691
|
+
}
|
|
7692
|
+
}
|
|
7693
|
+
return;
|
|
7694
|
+
}
|
|
7695
|
+
function routingAutomationMetadata(task) {
|
|
7696
|
+
const automation = objectField(task.metadata.automation);
|
|
7697
|
+
const records = [task.metadata];
|
|
7698
|
+
if (automation)
|
|
7699
|
+
records.push(automation);
|
|
7700
|
+
const result = {};
|
|
7701
|
+
const aliases = [
|
|
7702
|
+
["allowed", ["allowed", "automation_allowed", "automationAllowed"]],
|
|
7703
|
+
["no_auto", ["no_auto", "noAuto"]],
|
|
7704
|
+
["manual", ["manual"]],
|
|
7705
|
+
["manual_required", ["manual_required", "manualRequired"]],
|
|
7706
|
+
["requires_approval", ["requires_approval", "requiresApproval"]],
|
|
7707
|
+
["approval_required", ["approval_required", "approvalRequired"]]
|
|
7708
|
+
];
|
|
7709
|
+
for (const [canonical, keys] of aliases) {
|
|
7710
|
+
const value = firstBoolean(records, keys);
|
|
7711
|
+
if (value !== undefined)
|
|
7712
|
+
result[canonical] = value;
|
|
7713
|
+
}
|
|
7714
|
+
if (task.requires_approval)
|
|
7715
|
+
result.requires_approval = true;
|
|
7716
|
+
return Object.keys(result).length > 0 ? result : undefined;
|
|
7717
|
+
}
|
|
7622
7718
|
function taskEventMetadata(task) {
|
|
7623
7719
|
const metadata = {
|
|
7624
7720
|
package: "@hasna/todos",
|
|
@@ -7629,6 +7725,14 @@ function taskEventMetadata(task) {
|
|
|
7629
7725
|
task_list_id: task.task_list_id,
|
|
7630
7726
|
working_dir: task.working_dir
|
|
7631
7727
|
};
|
|
7728
|
+
const routeEnabled = booleanField(task.metadata.route_enabled);
|
|
7729
|
+
if (routeEnabled !== undefined) {
|
|
7730
|
+
metadata.route_enabled = routeEnabled;
|
|
7731
|
+
}
|
|
7732
|
+
const automation = routingAutomationMetadata(task);
|
|
7733
|
+
if (automation) {
|
|
7734
|
+
metadata.automation = automation;
|
|
7735
|
+
}
|
|
7632
7736
|
try {
|
|
7633
7737
|
const project = task.project_id ? getProject(task.project_id) : null;
|
|
7634
7738
|
const projectPath = project ? readMachineLocalPath(project) ?? project.path : task.working_dir;
|
|
@@ -7646,9 +7750,6 @@ function taskEventMetadata(task) {
|
|
|
7646
7750
|
if (projectPath) {
|
|
7647
7751
|
metadata.project_kind = classifyProjectKind(projectPath);
|
|
7648
7752
|
metadata.project_is_worktree = isWorktreePath(projectPath);
|
|
7649
|
-
if (typeof task.metadata.route_enabled === "boolean") {
|
|
7650
|
-
metadata.route_enabled = task.metadata.route_enabled;
|
|
7651
|
-
}
|
|
7652
7753
|
metadata.working_dir = task.working_dir ?? projectPath;
|
|
7653
7754
|
}
|
|
7654
7755
|
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.11/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";
|
|
@@ -7077,6 +7077,19 @@ function getPathValue(input, path) {
|
|
|
7077
7077
|
return;
|
|
7078
7078
|
}, input);
|
|
7079
7079
|
}
|
|
7080
|
+
function getFieldValues(input, path) {
|
|
7081
|
+
const values = [];
|
|
7082
|
+
const push = (value) => {
|
|
7083
|
+
if (!values.some((item) => Object.is(item, value)))
|
|
7084
|
+
values.push(value);
|
|
7085
|
+
};
|
|
7086
|
+
if (path.includes(".") && path in input)
|
|
7087
|
+
push(input[path]);
|
|
7088
|
+
const nestedValue = getPathValue(input, path);
|
|
7089
|
+
if (nestedValue !== undefined || !path.includes("."))
|
|
7090
|
+
push(nestedValue);
|
|
7091
|
+
return values;
|
|
7092
|
+
}
|
|
7080
7093
|
function wildcardToRegExp(pattern, options = {}) {
|
|
7081
7094
|
let body = "";
|
|
7082
7095
|
for (let index = 0;index < pattern.length; index += 1) {
|
|
@@ -7106,15 +7119,41 @@ function matchRecord(input, matcher) {
|
|
|
7106
7119
|
if (!matcher)
|
|
7107
7120
|
return true;
|
|
7108
7121
|
return Object.entries(matcher).every(([path, expected]) => {
|
|
7109
|
-
const
|
|
7110
|
-
|
|
7111
|
-
return matchString(actual === undefined ? undefined : String(actual), expected, {
|
|
7112
|
-
segmentSafe: path.endsWith("_path") || path.endsWith(".path")
|
|
7113
|
-
});
|
|
7114
|
-
}
|
|
7115
|
-
return actual === expected;
|
|
7122
|
+
const actualValues = getFieldValues(input, path);
|
|
7123
|
+
return matchField(actualValues, expected, path);
|
|
7116
7124
|
});
|
|
7117
7125
|
}
|
|
7126
|
+
function matchField(actualValues, expected, path) {
|
|
7127
|
+
if (isNegativeMatcher(expected)) {
|
|
7128
|
+
return !actualValues.some((actual) => matchPositiveField(actual, expected.not, path));
|
|
7129
|
+
}
|
|
7130
|
+
return actualValues.some((actual) => matchPositiveField(actual, expected, path));
|
|
7131
|
+
}
|
|
7132
|
+
function matchPositiveField(actual, expected, path) {
|
|
7133
|
+
if (typeof expected === "string" || Array.isArray(expected)) {
|
|
7134
|
+
return stringCandidates(actual).some((candidate) => matchString(candidate, expected, {
|
|
7135
|
+
segmentSafe: path.endsWith("_path") || path.endsWith(".path")
|
|
7136
|
+
}));
|
|
7137
|
+
}
|
|
7138
|
+
if (Array.isArray(actual)) {
|
|
7139
|
+
return actual.some((item) => item === expected);
|
|
7140
|
+
}
|
|
7141
|
+
return actual === expected;
|
|
7142
|
+
}
|
|
7143
|
+
function stringCandidates(actual) {
|
|
7144
|
+
if (actual === undefined)
|
|
7145
|
+
return [];
|
|
7146
|
+
if (Array.isArray(actual)) {
|
|
7147
|
+
return actual.flatMap((item) => isPrimitiveFieldValue(item) ? [String(item)] : []);
|
|
7148
|
+
}
|
|
7149
|
+
return [String(actual)];
|
|
7150
|
+
}
|
|
7151
|
+
function isPrimitiveFieldValue(value) {
|
|
7152
|
+
return value === null || typeof value === "string" || typeof value === "number" || typeof value === "boolean";
|
|
7153
|
+
}
|
|
7154
|
+
function isNegativeMatcher(value) {
|
|
7155
|
+
return Boolean(value && typeof value === "object" && !Array.isArray(value) && "not" in value);
|
|
7156
|
+
}
|
|
7118
7157
|
function eventMatchesFilter(event, filter) {
|
|
7119
7158
|
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
7159
|
}
|
|
@@ -7721,9 +7760,66 @@ function taskEventData(task, extra = {}) {
|
|
|
7721
7760
|
started_at: task.started_at,
|
|
7722
7761
|
completed_at: task.completed_at,
|
|
7723
7762
|
due_at: task.due_at,
|
|
7763
|
+
requires_approval: task.requires_approval,
|
|
7764
|
+
approved_by: task.approved_by,
|
|
7765
|
+
approved_at: task.approved_at,
|
|
7724
7766
|
...extra
|
|
7725
7767
|
};
|
|
7726
7768
|
}
|
|
7769
|
+
function booleanField(value) {
|
|
7770
|
+
if (typeof value === "boolean")
|
|
7771
|
+
return value;
|
|
7772
|
+
if (typeof value === "number") {
|
|
7773
|
+
if (value === 1)
|
|
7774
|
+
return true;
|
|
7775
|
+
if (value === 0)
|
|
7776
|
+
return false;
|
|
7777
|
+
}
|
|
7778
|
+
if (typeof value === "string") {
|
|
7779
|
+
const normalized = value.trim().toLowerCase();
|
|
7780
|
+
if (["true", "1", "yes", "on"].includes(normalized))
|
|
7781
|
+
return true;
|
|
7782
|
+
if (["false", "0", "no", "off"].includes(normalized))
|
|
7783
|
+
return false;
|
|
7784
|
+
}
|
|
7785
|
+
return;
|
|
7786
|
+
}
|
|
7787
|
+
function objectField(value) {
|
|
7788
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : undefined;
|
|
7789
|
+
}
|
|
7790
|
+
function firstBoolean(records, keys) {
|
|
7791
|
+
for (const record of records) {
|
|
7792
|
+
for (const key of keys) {
|
|
7793
|
+
const value = booleanField(record[key]);
|
|
7794
|
+
if (value !== undefined)
|
|
7795
|
+
return value;
|
|
7796
|
+
}
|
|
7797
|
+
}
|
|
7798
|
+
return;
|
|
7799
|
+
}
|
|
7800
|
+
function routingAutomationMetadata(task) {
|
|
7801
|
+
const automation = objectField(task.metadata.automation);
|
|
7802
|
+
const records = [task.metadata];
|
|
7803
|
+
if (automation)
|
|
7804
|
+
records.push(automation);
|
|
7805
|
+
const result = {};
|
|
7806
|
+
const aliases = [
|
|
7807
|
+
["allowed", ["allowed", "automation_allowed", "automationAllowed"]],
|
|
7808
|
+
["no_auto", ["no_auto", "noAuto"]],
|
|
7809
|
+
["manual", ["manual"]],
|
|
7810
|
+
["manual_required", ["manual_required", "manualRequired"]],
|
|
7811
|
+
["requires_approval", ["requires_approval", "requiresApproval"]],
|
|
7812
|
+
["approval_required", ["approval_required", "approvalRequired"]]
|
|
7813
|
+
];
|
|
7814
|
+
for (const [canonical, keys] of aliases) {
|
|
7815
|
+
const value = firstBoolean(records, keys);
|
|
7816
|
+
if (value !== undefined)
|
|
7817
|
+
result[canonical] = value;
|
|
7818
|
+
}
|
|
7819
|
+
if (task.requires_approval)
|
|
7820
|
+
result.requires_approval = true;
|
|
7821
|
+
return Object.keys(result).length > 0 ? result : undefined;
|
|
7822
|
+
}
|
|
7727
7823
|
function taskEventMetadata(task) {
|
|
7728
7824
|
const metadata = {
|
|
7729
7825
|
package: "@hasna/todos",
|
|
@@ -7734,6 +7830,14 @@ function taskEventMetadata(task) {
|
|
|
7734
7830
|
task_list_id: task.task_list_id,
|
|
7735
7831
|
working_dir: task.working_dir
|
|
7736
7832
|
};
|
|
7833
|
+
const routeEnabled = booleanField(task.metadata.route_enabled);
|
|
7834
|
+
if (routeEnabled !== undefined) {
|
|
7835
|
+
metadata.route_enabled = routeEnabled;
|
|
7836
|
+
}
|
|
7837
|
+
const automation = routingAutomationMetadata(task);
|
|
7838
|
+
if (automation) {
|
|
7839
|
+
metadata.automation = automation;
|
|
7840
|
+
}
|
|
7737
7841
|
try {
|
|
7738
7842
|
const project = task.project_id ? getProject(task.project_id) : null;
|
|
7739
7843
|
const projectPath = project ? readMachineLocalPath(project) ?? project.path : task.working_dir;
|
|
@@ -7751,9 +7855,6 @@ function taskEventMetadata(task) {
|
|
|
7751
7855
|
if (projectPath) {
|
|
7752
7856
|
metadata.project_kind = classifyProjectKind(projectPath);
|
|
7753
7857
|
metadata.project_is_worktree = isWorktreePath(projectPath);
|
|
7754
|
-
if (typeof task.metadata.route_enabled === "boolean") {
|
|
7755
|
-
metadata.route_enabled = task.metadata.route_enabled;
|
|
7756
|
-
}
|
|
7757
7858
|
metadata.working_dir = task.working_dir ?? projectPath;
|
|
7758
7859
|
}
|
|
7759
7860
|
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.11/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";
|
|
@@ -8755,6 +8755,19 @@ function getPathValue(input, path) {
|
|
|
8755
8755
|
return;
|
|
8756
8756
|
}, input);
|
|
8757
8757
|
}
|
|
8758
|
+
function getFieldValues(input, path) {
|
|
8759
|
+
const values = [];
|
|
8760
|
+
const push = (value) => {
|
|
8761
|
+
if (!values.some((item) => Object.is(item, value)))
|
|
8762
|
+
values.push(value);
|
|
8763
|
+
};
|
|
8764
|
+
if (path.includes(".") && path in input)
|
|
8765
|
+
push(input[path]);
|
|
8766
|
+
const nestedValue = getPathValue(input, path);
|
|
8767
|
+
if (nestedValue !== undefined || !path.includes("."))
|
|
8768
|
+
push(nestedValue);
|
|
8769
|
+
return values;
|
|
8770
|
+
}
|
|
8758
8771
|
function wildcardToRegExp(pattern, options = {}) {
|
|
8759
8772
|
let body = "";
|
|
8760
8773
|
for (let index = 0;index < pattern.length; index += 1) {
|
|
@@ -8784,15 +8797,41 @@ function matchRecord(input, matcher) {
|
|
|
8784
8797
|
if (!matcher)
|
|
8785
8798
|
return true;
|
|
8786
8799
|
return Object.entries(matcher).every(([path, expected]) => {
|
|
8787
|
-
const
|
|
8788
|
-
|
|
8789
|
-
return matchString(actual === undefined ? undefined : String(actual), expected, {
|
|
8790
|
-
segmentSafe: path.endsWith("_path") || path.endsWith(".path")
|
|
8791
|
-
});
|
|
8792
|
-
}
|
|
8793
|
-
return actual === expected;
|
|
8800
|
+
const actualValues = getFieldValues(input, path);
|
|
8801
|
+
return matchField(actualValues, expected, path);
|
|
8794
8802
|
});
|
|
8795
8803
|
}
|
|
8804
|
+
function matchField(actualValues, expected, path) {
|
|
8805
|
+
if (isNegativeMatcher(expected)) {
|
|
8806
|
+
return !actualValues.some((actual) => matchPositiveField(actual, expected.not, path));
|
|
8807
|
+
}
|
|
8808
|
+
return actualValues.some((actual) => matchPositiveField(actual, expected, path));
|
|
8809
|
+
}
|
|
8810
|
+
function matchPositiveField(actual, expected, path) {
|
|
8811
|
+
if (typeof expected === "string" || Array.isArray(expected)) {
|
|
8812
|
+
return stringCandidates(actual).some((candidate) => matchString(candidate, expected, {
|
|
8813
|
+
segmentSafe: path.endsWith("_path") || path.endsWith(".path")
|
|
8814
|
+
}));
|
|
8815
|
+
}
|
|
8816
|
+
if (Array.isArray(actual)) {
|
|
8817
|
+
return actual.some((item) => item === expected);
|
|
8818
|
+
}
|
|
8819
|
+
return actual === expected;
|
|
8820
|
+
}
|
|
8821
|
+
function stringCandidates(actual) {
|
|
8822
|
+
if (actual === undefined)
|
|
8823
|
+
return [];
|
|
8824
|
+
if (Array.isArray(actual)) {
|
|
8825
|
+
return actual.flatMap((item) => isPrimitiveFieldValue(item) ? [String(item)] : []);
|
|
8826
|
+
}
|
|
8827
|
+
return [String(actual)];
|
|
8828
|
+
}
|
|
8829
|
+
function isPrimitiveFieldValue(value) {
|
|
8830
|
+
return value === null || typeof value === "string" || typeof value === "number" || typeof value === "boolean";
|
|
8831
|
+
}
|
|
8832
|
+
function isNegativeMatcher(value) {
|
|
8833
|
+
return Boolean(value && typeof value === "object" && !Array.isArray(value) && "not" in value);
|
|
8834
|
+
}
|
|
8796
8835
|
function eventMatchesFilter(event, filter) {
|
|
8797
8836
|
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
8837
|
}
|
|
@@ -9400,9 +9439,66 @@ function taskEventData(task, extra = {}) {
|
|
|
9400
9439
|
started_at: task.started_at,
|
|
9401
9440
|
completed_at: task.completed_at,
|
|
9402
9441
|
due_at: task.due_at,
|
|
9442
|
+
requires_approval: task.requires_approval,
|
|
9443
|
+
approved_by: task.approved_by,
|
|
9444
|
+
approved_at: task.approved_at,
|
|
9403
9445
|
...extra
|
|
9404
9446
|
};
|
|
9405
9447
|
}
|
|
9448
|
+
function booleanField(value) {
|
|
9449
|
+
if (typeof value === "boolean")
|
|
9450
|
+
return value;
|
|
9451
|
+
if (typeof value === "number") {
|
|
9452
|
+
if (value === 1)
|
|
9453
|
+
return true;
|
|
9454
|
+
if (value === 0)
|
|
9455
|
+
return false;
|
|
9456
|
+
}
|
|
9457
|
+
if (typeof value === "string") {
|
|
9458
|
+
const normalized = value.trim().toLowerCase();
|
|
9459
|
+
if (["true", "1", "yes", "on"].includes(normalized))
|
|
9460
|
+
return true;
|
|
9461
|
+
if (["false", "0", "no", "off"].includes(normalized))
|
|
9462
|
+
return false;
|
|
9463
|
+
}
|
|
9464
|
+
return;
|
|
9465
|
+
}
|
|
9466
|
+
function objectField(value) {
|
|
9467
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : undefined;
|
|
9468
|
+
}
|
|
9469
|
+
function firstBoolean(records, keys) {
|
|
9470
|
+
for (const record of records) {
|
|
9471
|
+
for (const key of keys) {
|
|
9472
|
+
const value = booleanField(record[key]);
|
|
9473
|
+
if (value !== undefined)
|
|
9474
|
+
return value;
|
|
9475
|
+
}
|
|
9476
|
+
}
|
|
9477
|
+
return;
|
|
9478
|
+
}
|
|
9479
|
+
function routingAutomationMetadata(task) {
|
|
9480
|
+
const automation = objectField(task.metadata.automation);
|
|
9481
|
+
const records = [task.metadata];
|
|
9482
|
+
if (automation)
|
|
9483
|
+
records.push(automation);
|
|
9484
|
+
const result = {};
|
|
9485
|
+
const aliases = [
|
|
9486
|
+
["allowed", ["allowed", "automation_allowed", "automationAllowed"]],
|
|
9487
|
+
["no_auto", ["no_auto", "noAuto"]],
|
|
9488
|
+
["manual", ["manual"]],
|
|
9489
|
+
["manual_required", ["manual_required", "manualRequired"]],
|
|
9490
|
+
["requires_approval", ["requires_approval", "requiresApproval"]],
|
|
9491
|
+
["approval_required", ["approval_required", "approvalRequired"]]
|
|
9492
|
+
];
|
|
9493
|
+
for (const [canonical, keys] of aliases) {
|
|
9494
|
+
const value = firstBoolean(records, keys);
|
|
9495
|
+
if (value !== undefined)
|
|
9496
|
+
result[canonical] = value;
|
|
9497
|
+
}
|
|
9498
|
+
if (task.requires_approval)
|
|
9499
|
+
result.requires_approval = true;
|
|
9500
|
+
return Object.keys(result).length > 0 ? result : undefined;
|
|
9501
|
+
}
|
|
9406
9502
|
function taskEventMetadata(task) {
|
|
9407
9503
|
const metadata = {
|
|
9408
9504
|
package: "@hasna/todos",
|
|
@@ -9413,6 +9509,14 @@ function taskEventMetadata(task) {
|
|
|
9413
9509
|
task_list_id: task.task_list_id,
|
|
9414
9510
|
working_dir: task.working_dir
|
|
9415
9511
|
};
|
|
9512
|
+
const routeEnabled = booleanField(task.metadata.route_enabled);
|
|
9513
|
+
if (routeEnabled !== undefined) {
|
|
9514
|
+
metadata.route_enabled = routeEnabled;
|
|
9515
|
+
}
|
|
9516
|
+
const automation = routingAutomationMetadata(task);
|
|
9517
|
+
if (automation) {
|
|
9518
|
+
metadata.automation = automation;
|
|
9519
|
+
}
|
|
9416
9520
|
try {
|
|
9417
9521
|
const project = task.project_id ? getProject(task.project_id) : null;
|
|
9418
9522
|
const projectPath = project ? readMachineLocalPath(project) ?? project.path : task.working_dir;
|
|
@@ -9430,9 +9534,6 @@ function taskEventMetadata(task) {
|
|
|
9430
9534
|
if (projectPath) {
|
|
9431
9535
|
metadata.project_kind = classifyProjectKind(projectPath);
|
|
9432
9536
|
metadata.project_is_worktree = isWorktreePath(projectPath);
|
|
9433
|
-
if (typeof task.metadata.route_enabled === "boolean") {
|
|
9434
|
-
metadata.route_enabled = task.metadata.route_enabled;
|
|
9435
|
-
}
|
|
9436
9537
|
metadata.working_dir = task.working_dir ?? projectPath;
|
|
9437
9538
|
}
|
|
9438
9539
|
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.11/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";
|
|
@@ -6972,6 +6972,19 @@ function getPathValue(input, path) {
|
|
|
6972
6972
|
return;
|
|
6973
6973
|
}, input);
|
|
6974
6974
|
}
|
|
6975
|
+
function getFieldValues(input, path) {
|
|
6976
|
+
const values = [];
|
|
6977
|
+
const push = (value) => {
|
|
6978
|
+
if (!values.some((item) => Object.is(item, value)))
|
|
6979
|
+
values.push(value);
|
|
6980
|
+
};
|
|
6981
|
+
if (path.includes(".") && path in input)
|
|
6982
|
+
push(input[path]);
|
|
6983
|
+
const nestedValue = getPathValue(input, path);
|
|
6984
|
+
if (nestedValue !== undefined || !path.includes("."))
|
|
6985
|
+
push(nestedValue);
|
|
6986
|
+
return values;
|
|
6987
|
+
}
|
|
6975
6988
|
function wildcardToRegExp(pattern, options = {}) {
|
|
6976
6989
|
let body = "";
|
|
6977
6990
|
for (let index = 0;index < pattern.length; index += 1) {
|
|
@@ -7001,15 +7014,41 @@ function matchRecord(input, matcher) {
|
|
|
7001
7014
|
if (!matcher)
|
|
7002
7015
|
return true;
|
|
7003
7016
|
return Object.entries(matcher).every(([path, expected]) => {
|
|
7004
|
-
const
|
|
7005
|
-
|
|
7006
|
-
return matchString(actual === undefined ? undefined : String(actual), expected, {
|
|
7007
|
-
segmentSafe: path.endsWith("_path") || path.endsWith(".path")
|
|
7008
|
-
});
|
|
7009
|
-
}
|
|
7010
|
-
return actual === expected;
|
|
7017
|
+
const actualValues = getFieldValues(input, path);
|
|
7018
|
+
return matchField(actualValues, expected, path);
|
|
7011
7019
|
});
|
|
7012
7020
|
}
|
|
7021
|
+
function matchField(actualValues, expected, path) {
|
|
7022
|
+
if (isNegativeMatcher(expected)) {
|
|
7023
|
+
return !actualValues.some((actual) => matchPositiveField(actual, expected.not, path));
|
|
7024
|
+
}
|
|
7025
|
+
return actualValues.some((actual) => matchPositiveField(actual, expected, path));
|
|
7026
|
+
}
|
|
7027
|
+
function matchPositiveField(actual, expected, path) {
|
|
7028
|
+
if (typeof expected === "string" || Array.isArray(expected)) {
|
|
7029
|
+
return stringCandidates(actual).some((candidate) => matchString(candidate, expected, {
|
|
7030
|
+
segmentSafe: path.endsWith("_path") || path.endsWith(".path")
|
|
7031
|
+
}));
|
|
7032
|
+
}
|
|
7033
|
+
if (Array.isArray(actual)) {
|
|
7034
|
+
return actual.some((item) => item === expected);
|
|
7035
|
+
}
|
|
7036
|
+
return actual === expected;
|
|
7037
|
+
}
|
|
7038
|
+
function stringCandidates(actual) {
|
|
7039
|
+
if (actual === undefined)
|
|
7040
|
+
return [];
|
|
7041
|
+
if (Array.isArray(actual)) {
|
|
7042
|
+
return actual.flatMap((item) => isPrimitiveFieldValue(item) ? [String(item)] : []);
|
|
7043
|
+
}
|
|
7044
|
+
return [String(actual)];
|
|
7045
|
+
}
|
|
7046
|
+
function isPrimitiveFieldValue(value) {
|
|
7047
|
+
return value === null || typeof value === "string" || typeof value === "number" || typeof value === "boolean";
|
|
7048
|
+
}
|
|
7049
|
+
function isNegativeMatcher(value) {
|
|
7050
|
+
return Boolean(value && typeof value === "object" && !Array.isArray(value) && "not" in value);
|
|
7051
|
+
}
|
|
7013
7052
|
function eventMatchesFilter(event, filter) {
|
|
7014
7053
|
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
7054
|
}
|
|
@@ -7616,9 +7655,66 @@ function taskEventData(task, extra = {}) {
|
|
|
7616
7655
|
started_at: task.started_at,
|
|
7617
7656
|
completed_at: task.completed_at,
|
|
7618
7657
|
due_at: task.due_at,
|
|
7658
|
+
requires_approval: task.requires_approval,
|
|
7659
|
+
approved_by: task.approved_by,
|
|
7660
|
+
approved_at: task.approved_at,
|
|
7619
7661
|
...extra
|
|
7620
7662
|
};
|
|
7621
7663
|
}
|
|
7664
|
+
function booleanField(value) {
|
|
7665
|
+
if (typeof value === "boolean")
|
|
7666
|
+
return value;
|
|
7667
|
+
if (typeof value === "number") {
|
|
7668
|
+
if (value === 1)
|
|
7669
|
+
return true;
|
|
7670
|
+
if (value === 0)
|
|
7671
|
+
return false;
|
|
7672
|
+
}
|
|
7673
|
+
if (typeof value === "string") {
|
|
7674
|
+
const normalized = value.trim().toLowerCase();
|
|
7675
|
+
if (["true", "1", "yes", "on"].includes(normalized))
|
|
7676
|
+
return true;
|
|
7677
|
+
if (["false", "0", "no", "off"].includes(normalized))
|
|
7678
|
+
return false;
|
|
7679
|
+
}
|
|
7680
|
+
return;
|
|
7681
|
+
}
|
|
7682
|
+
function objectField(value) {
|
|
7683
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : undefined;
|
|
7684
|
+
}
|
|
7685
|
+
function firstBoolean(records, keys) {
|
|
7686
|
+
for (const record of records) {
|
|
7687
|
+
for (const key of keys) {
|
|
7688
|
+
const value = booleanField(record[key]);
|
|
7689
|
+
if (value !== undefined)
|
|
7690
|
+
return value;
|
|
7691
|
+
}
|
|
7692
|
+
}
|
|
7693
|
+
return;
|
|
7694
|
+
}
|
|
7695
|
+
function routingAutomationMetadata(task) {
|
|
7696
|
+
const automation = objectField(task.metadata.automation);
|
|
7697
|
+
const records = [task.metadata];
|
|
7698
|
+
if (automation)
|
|
7699
|
+
records.push(automation);
|
|
7700
|
+
const result = {};
|
|
7701
|
+
const aliases = [
|
|
7702
|
+
["allowed", ["allowed", "automation_allowed", "automationAllowed"]],
|
|
7703
|
+
["no_auto", ["no_auto", "noAuto"]],
|
|
7704
|
+
["manual", ["manual"]],
|
|
7705
|
+
["manual_required", ["manual_required", "manualRequired"]],
|
|
7706
|
+
["requires_approval", ["requires_approval", "requiresApproval"]],
|
|
7707
|
+
["approval_required", ["approval_required", "approvalRequired"]]
|
|
7708
|
+
];
|
|
7709
|
+
for (const [canonical, keys] of aliases) {
|
|
7710
|
+
const value = firstBoolean(records, keys);
|
|
7711
|
+
if (value !== undefined)
|
|
7712
|
+
result[canonical] = value;
|
|
7713
|
+
}
|
|
7714
|
+
if (task.requires_approval)
|
|
7715
|
+
result.requires_approval = true;
|
|
7716
|
+
return Object.keys(result).length > 0 ? result : undefined;
|
|
7717
|
+
}
|
|
7622
7718
|
function taskEventMetadata(task) {
|
|
7623
7719
|
const metadata = {
|
|
7624
7720
|
package: "@hasna/todos",
|
|
@@ -7629,6 +7725,14 @@ function taskEventMetadata(task) {
|
|
|
7629
7725
|
task_list_id: task.task_list_id,
|
|
7630
7726
|
working_dir: task.working_dir
|
|
7631
7727
|
};
|
|
7728
|
+
const routeEnabled = booleanField(task.metadata.route_enabled);
|
|
7729
|
+
if (routeEnabled !== undefined) {
|
|
7730
|
+
metadata.route_enabled = routeEnabled;
|
|
7731
|
+
}
|
|
7732
|
+
const automation = routingAutomationMetadata(task);
|
|
7733
|
+
if (automation) {
|
|
7734
|
+
metadata.automation = automation;
|
|
7735
|
+
}
|
|
7632
7736
|
try {
|
|
7633
7737
|
const project = task.project_id ? getProject(task.project_id) : null;
|
|
7634
7738
|
const projectPath = project ? readMachineLocalPath(project) ?? project.path : task.working_dir;
|
|
@@ -7646,9 +7750,6 @@ function taskEventMetadata(task) {
|
|
|
7646
7750
|
if (projectPath) {
|
|
7647
7751
|
metadata.project_kind = classifyProjectKind(projectPath);
|
|
7648
7752
|
metadata.project_is_worktree = isWorktreePath(projectPath);
|
|
7649
|
-
if (typeof task.metadata.route_enabled === "boolean") {
|
|
7650
|
-
metadata.route_enabled = task.metadata.route_enabled;
|
|
7651
|
-
}
|
|
7652
7753
|
metadata.working_dir = task.working_dir ?? projectPath;
|
|
7653
7754
|
}
|
|
7654
7755
|
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.61",
|
|
4
4
|
"repository": "https://github.com/hasna/todos.git",
|
|
5
|
-
"gitCommit": "
|
|
6
|
-
"generatedAt": "2026-06-
|
|
5
|
+
"gitCommit": "7dced09f93029fe58db334ff712e10094beefae2",
|
|
6
|
+
"generatedAt": "2026-06-27T14:41:53.504Z"
|
|
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.11/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";
|
|
@@ -4116,6 +4116,19 @@ function getPathValue(input, path) {
|
|
|
4116
4116
|
return;
|
|
4117
4117
|
}, input);
|
|
4118
4118
|
}
|
|
4119
|
+
function getFieldValues(input, path) {
|
|
4120
|
+
const values = [];
|
|
4121
|
+
const push = (value) => {
|
|
4122
|
+
if (!values.some((item) => Object.is(item, value)))
|
|
4123
|
+
values.push(value);
|
|
4124
|
+
};
|
|
4125
|
+
if (path.includes(".") && path in input)
|
|
4126
|
+
push(input[path]);
|
|
4127
|
+
const nestedValue = getPathValue(input, path);
|
|
4128
|
+
if (nestedValue !== undefined || !path.includes("."))
|
|
4129
|
+
push(nestedValue);
|
|
4130
|
+
return values;
|
|
4131
|
+
}
|
|
4119
4132
|
function wildcardToRegExp(pattern, options = {}) {
|
|
4120
4133
|
let body = "";
|
|
4121
4134
|
for (let index = 0;index < pattern.length; index += 1) {
|
|
@@ -4145,15 +4158,41 @@ function matchRecord(input, matcher) {
|
|
|
4145
4158
|
if (!matcher)
|
|
4146
4159
|
return true;
|
|
4147
4160
|
return Object.entries(matcher).every(([path, expected]) => {
|
|
4148
|
-
const
|
|
4149
|
-
|
|
4150
|
-
return matchString(actual === undefined ? undefined : String(actual), expected, {
|
|
4151
|
-
segmentSafe: path.endsWith("_path") || path.endsWith(".path")
|
|
4152
|
-
});
|
|
4153
|
-
}
|
|
4154
|
-
return actual === expected;
|
|
4161
|
+
const actualValues = getFieldValues(input, path);
|
|
4162
|
+
return matchField(actualValues, expected, path);
|
|
4155
4163
|
});
|
|
4156
4164
|
}
|
|
4165
|
+
function matchField(actualValues, expected, path) {
|
|
4166
|
+
if (isNegativeMatcher(expected)) {
|
|
4167
|
+
return !actualValues.some((actual) => matchPositiveField(actual, expected.not, path));
|
|
4168
|
+
}
|
|
4169
|
+
return actualValues.some((actual) => matchPositiveField(actual, expected, path));
|
|
4170
|
+
}
|
|
4171
|
+
function matchPositiveField(actual, expected, path) {
|
|
4172
|
+
if (typeof expected === "string" || Array.isArray(expected)) {
|
|
4173
|
+
return stringCandidates(actual).some((candidate) => matchString(candidate, expected, {
|
|
4174
|
+
segmentSafe: path.endsWith("_path") || path.endsWith(".path")
|
|
4175
|
+
}));
|
|
4176
|
+
}
|
|
4177
|
+
if (Array.isArray(actual)) {
|
|
4178
|
+
return actual.some((item) => item === expected);
|
|
4179
|
+
}
|
|
4180
|
+
return actual === expected;
|
|
4181
|
+
}
|
|
4182
|
+
function stringCandidates(actual) {
|
|
4183
|
+
if (actual === undefined)
|
|
4184
|
+
return [];
|
|
4185
|
+
if (Array.isArray(actual)) {
|
|
4186
|
+
return actual.flatMap((item) => isPrimitiveFieldValue(item) ? [String(item)] : []);
|
|
4187
|
+
}
|
|
4188
|
+
return [String(actual)];
|
|
4189
|
+
}
|
|
4190
|
+
function isPrimitiveFieldValue(value) {
|
|
4191
|
+
return value === null || typeof value === "string" || typeof value === "number" || typeof value === "boolean";
|
|
4192
|
+
}
|
|
4193
|
+
function isNegativeMatcher(value) {
|
|
4194
|
+
return Boolean(value && typeof value === "object" && !Array.isArray(value) && "not" in value);
|
|
4195
|
+
}
|
|
4157
4196
|
function eventMatchesFilter(event, filter) {
|
|
4158
4197
|
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
4198
|
}
|
|
@@ -4761,9 +4800,66 @@ function taskEventData(task, extra = {}) {
|
|
|
4761
4800
|
started_at: task.started_at,
|
|
4762
4801
|
completed_at: task.completed_at,
|
|
4763
4802
|
due_at: task.due_at,
|
|
4803
|
+
requires_approval: task.requires_approval,
|
|
4804
|
+
approved_by: task.approved_by,
|
|
4805
|
+
approved_at: task.approved_at,
|
|
4764
4806
|
...extra
|
|
4765
4807
|
};
|
|
4766
4808
|
}
|
|
4809
|
+
function booleanField(value) {
|
|
4810
|
+
if (typeof value === "boolean")
|
|
4811
|
+
return value;
|
|
4812
|
+
if (typeof value === "number") {
|
|
4813
|
+
if (value === 1)
|
|
4814
|
+
return true;
|
|
4815
|
+
if (value === 0)
|
|
4816
|
+
return false;
|
|
4817
|
+
}
|
|
4818
|
+
if (typeof value === "string") {
|
|
4819
|
+
const normalized = value.trim().toLowerCase();
|
|
4820
|
+
if (["true", "1", "yes", "on"].includes(normalized))
|
|
4821
|
+
return true;
|
|
4822
|
+
if (["false", "0", "no", "off"].includes(normalized))
|
|
4823
|
+
return false;
|
|
4824
|
+
}
|
|
4825
|
+
return;
|
|
4826
|
+
}
|
|
4827
|
+
function objectField(value) {
|
|
4828
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : undefined;
|
|
4829
|
+
}
|
|
4830
|
+
function firstBoolean(records, keys) {
|
|
4831
|
+
for (const record of records) {
|
|
4832
|
+
for (const key of keys) {
|
|
4833
|
+
const value = booleanField(record[key]);
|
|
4834
|
+
if (value !== undefined)
|
|
4835
|
+
return value;
|
|
4836
|
+
}
|
|
4837
|
+
}
|
|
4838
|
+
return;
|
|
4839
|
+
}
|
|
4840
|
+
function routingAutomationMetadata(task) {
|
|
4841
|
+
const automation = objectField(task.metadata.automation);
|
|
4842
|
+
const records = [task.metadata];
|
|
4843
|
+
if (automation)
|
|
4844
|
+
records.push(automation);
|
|
4845
|
+
const result = {};
|
|
4846
|
+
const aliases = [
|
|
4847
|
+
["allowed", ["allowed", "automation_allowed", "automationAllowed"]],
|
|
4848
|
+
["no_auto", ["no_auto", "noAuto"]],
|
|
4849
|
+
["manual", ["manual"]],
|
|
4850
|
+
["manual_required", ["manual_required", "manualRequired"]],
|
|
4851
|
+
["requires_approval", ["requires_approval", "requiresApproval"]],
|
|
4852
|
+
["approval_required", ["approval_required", "approvalRequired"]]
|
|
4853
|
+
];
|
|
4854
|
+
for (const [canonical, keys] of aliases) {
|
|
4855
|
+
const value = firstBoolean(records, keys);
|
|
4856
|
+
if (value !== undefined)
|
|
4857
|
+
result[canonical] = value;
|
|
4858
|
+
}
|
|
4859
|
+
if (task.requires_approval)
|
|
4860
|
+
result.requires_approval = true;
|
|
4861
|
+
return Object.keys(result).length > 0 ? result : undefined;
|
|
4862
|
+
}
|
|
4767
4863
|
function taskEventMetadata(task) {
|
|
4768
4864
|
const metadata = {
|
|
4769
4865
|
package: "@hasna/todos",
|
|
@@ -4774,6 +4870,14 @@ function taskEventMetadata(task) {
|
|
|
4774
4870
|
task_list_id: task.task_list_id,
|
|
4775
4871
|
working_dir: task.working_dir
|
|
4776
4872
|
};
|
|
4873
|
+
const routeEnabled = booleanField(task.metadata.route_enabled);
|
|
4874
|
+
if (routeEnabled !== undefined) {
|
|
4875
|
+
metadata.route_enabled = routeEnabled;
|
|
4876
|
+
}
|
|
4877
|
+
const automation = routingAutomationMetadata(task);
|
|
4878
|
+
if (automation) {
|
|
4879
|
+
metadata.automation = automation;
|
|
4880
|
+
}
|
|
4777
4881
|
try {
|
|
4778
4882
|
const project = task.project_id ? getProject(task.project_id) : null;
|
|
4779
4883
|
const projectPath = project ? readMachineLocalPath(project) ?? project.path : task.working_dir;
|
|
@@ -4791,9 +4895,6 @@ function taskEventMetadata(task) {
|
|
|
4791
4895
|
if (projectPath) {
|
|
4792
4896
|
metadata.project_kind = classifyProjectKind(projectPath);
|
|
4793
4897
|
metadata.project_is_worktree = isWorktreePath(projectPath);
|
|
4794
|
-
if (typeof task.metadata.route_enabled === "boolean") {
|
|
4795
|
-
metadata.route_enabled = task.metadata.route_enabled;
|
|
4796
|
-
}
|
|
4797
4898
|
metadata.working_dir = task.working_dir ?? projectPath;
|
|
4798
4899
|
}
|
|
4799
4900
|
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.11/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";
|
|
@@ -4520,6 +4520,19 @@ function getPathValue(input, path) {
|
|
|
4520
4520
|
return;
|
|
4521
4521
|
}, input);
|
|
4522
4522
|
}
|
|
4523
|
+
function getFieldValues(input, path) {
|
|
4524
|
+
const values = [];
|
|
4525
|
+
const push = (value) => {
|
|
4526
|
+
if (!values.some((item) => Object.is(item, value)))
|
|
4527
|
+
values.push(value);
|
|
4528
|
+
};
|
|
4529
|
+
if (path.includes(".") && path in input)
|
|
4530
|
+
push(input[path]);
|
|
4531
|
+
const nestedValue = getPathValue(input, path);
|
|
4532
|
+
if (nestedValue !== undefined || !path.includes("."))
|
|
4533
|
+
push(nestedValue);
|
|
4534
|
+
return values;
|
|
4535
|
+
}
|
|
4523
4536
|
function wildcardToRegExp(pattern, options = {}) {
|
|
4524
4537
|
let body = "";
|
|
4525
4538
|
for (let index = 0;index < pattern.length; index += 1) {
|
|
@@ -4549,15 +4562,41 @@ function matchRecord(input, matcher) {
|
|
|
4549
4562
|
if (!matcher)
|
|
4550
4563
|
return true;
|
|
4551
4564
|
return Object.entries(matcher).every(([path, expected]) => {
|
|
4552
|
-
const
|
|
4553
|
-
|
|
4554
|
-
return matchString(actual === undefined ? undefined : String(actual), expected, {
|
|
4555
|
-
segmentSafe: path.endsWith("_path") || path.endsWith(".path")
|
|
4556
|
-
});
|
|
4557
|
-
}
|
|
4558
|
-
return actual === expected;
|
|
4565
|
+
const actualValues = getFieldValues(input, path);
|
|
4566
|
+
return matchField(actualValues, expected, path);
|
|
4559
4567
|
});
|
|
4560
4568
|
}
|
|
4569
|
+
function matchField(actualValues, expected, path) {
|
|
4570
|
+
if (isNegativeMatcher(expected)) {
|
|
4571
|
+
return !actualValues.some((actual) => matchPositiveField(actual, expected.not, path));
|
|
4572
|
+
}
|
|
4573
|
+
return actualValues.some((actual) => matchPositiveField(actual, expected, path));
|
|
4574
|
+
}
|
|
4575
|
+
function matchPositiveField(actual, expected, path) {
|
|
4576
|
+
if (typeof expected === "string" || Array.isArray(expected)) {
|
|
4577
|
+
return stringCandidates(actual).some((candidate) => matchString(candidate, expected, {
|
|
4578
|
+
segmentSafe: path.endsWith("_path") || path.endsWith(".path")
|
|
4579
|
+
}));
|
|
4580
|
+
}
|
|
4581
|
+
if (Array.isArray(actual)) {
|
|
4582
|
+
return actual.some((item) => item === expected);
|
|
4583
|
+
}
|
|
4584
|
+
return actual === expected;
|
|
4585
|
+
}
|
|
4586
|
+
function stringCandidates(actual) {
|
|
4587
|
+
if (actual === undefined)
|
|
4588
|
+
return [];
|
|
4589
|
+
if (Array.isArray(actual)) {
|
|
4590
|
+
return actual.flatMap((item) => isPrimitiveFieldValue(item) ? [String(item)] : []);
|
|
4591
|
+
}
|
|
4592
|
+
return [String(actual)];
|
|
4593
|
+
}
|
|
4594
|
+
function isPrimitiveFieldValue(value) {
|
|
4595
|
+
return value === null || typeof value === "string" || typeof value === "number" || typeof value === "boolean";
|
|
4596
|
+
}
|
|
4597
|
+
function isNegativeMatcher(value) {
|
|
4598
|
+
return Boolean(value && typeof value === "object" && !Array.isArray(value) && "not" in value);
|
|
4599
|
+
}
|
|
4561
4600
|
function eventMatchesFilter(event, filter) {
|
|
4562
4601
|
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
4602
|
}
|
|
@@ -5164,9 +5203,66 @@ function taskEventData(task, extra = {}) {
|
|
|
5164
5203
|
started_at: task.started_at,
|
|
5165
5204
|
completed_at: task.completed_at,
|
|
5166
5205
|
due_at: task.due_at,
|
|
5206
|
+
requires_approval: task.requires_approval,
|
|
5207
|
+
approved_by: task.approved_by,
|
|
5208
|
+
approved_at: task.approved_at,
|
|
5167
5209
|
...extra
|
|
5168
5210
|
};
|
|
5169
5211
|
}
|
|
5212
|
+
function booleanField(value) {
|
|
5213
|
+
if (typeof value === "boolean")
|
|
5214
|
+
return value;
|
|
5215
|
+
if (typeof value === "number") {
|
|
5216
|
+
if (value === 1)
|
|
5217
|
+
return true;
|
|
5218
|
+
if (value === 0)
|
|
5219
|
+
return false;
|
|
5220
|
+
}
|
|
5221
|
+
if (typeof value === "string") {
|
|
5222
|
+
const normalized = value.trim().toLowerCase();
|
|
5223
|
+
if (["true", "1", "yes", "on"].includes(normalized))
|
|
5224
|
+
return true;
|
|
5225
|
+
if (["false", "0", "no", "off"].includes(normalized))
|
|
5226
|
+
return false;
|
|
5227
|
+
}
|
|
5228
|
+
return;
|
|
5229
|
+
}
|
|
5230
|
+
function objectField(value) {
|
|
5231
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : undefined;
|
|
5232
|
+
}
|
|
5233
|
+
function firstBoolean(records, keys) {
|
|
5234
|
+
for (const record of records) {
|
|
5235
|
+
for (const key of keys) {
|
|
5236
|
+
const value = booleanField(record[key]);
|
|
5237
|
+
if (value !== undefined)
|
|
5238
|
+
return value;
|
|
5239
|
+
}
|
|
5240
|
+
}
|
|
5241
|
+
return;
|
|
5242
|
+
}
|
|
5243
|
+
function routingAutomationMetadata(task) {
|
|
5244
|
+
const automation = objectField(task.metadata.automation);
|
|
5245
|
+
const records = [task.metadata];
|
|
5246
|
+
if (automation)
|
|
5247
|
+
records.push(automation);
|
|
5248
|
+
const result = {};
|
|
5249
|
+
const aliases = [
|
|
5250
|
+
["allowed", ["allowed", "automation_allowed", "automationAllowed"]],
|
|
5251
|
+
["no_auto", ["no_auto", "noAuto"]],
|
|
5252
|
+
["manual", ["manual"]],
|
|
5253
|
+
["manual_required", ["manual_required", "manualRequired"]],
|
|
5254
|
+
["requires_approval", ["requires_approval", "requiresApproval"]],
|
|
5255
|
+
["approval_required", ["approval_required", "approvalRequired"]]
|
|
5256
|
+
];
|
|
5257
|
+
for (const [canonical, keys] of aliases) {
|
|
5258
|
+
const value = firstBoolean(records, keys);
|
|
5259
|
+
if (value !== undefined)
|
|
5260
|
+
result[canonical] = value;
|
|
5261
|
+
}
|
|
5262
|
+
if (task.requires_approval)
|
|
5263
|
+
result.requires_approval = true;
|
|
5264
|
+
return Object.keys(result).length > 0 ? result : undefined;
|
|
5265
|
+
}
|
|
5170
5266
|
function taskEventMetadata(task) {
|
|
5171
5267
|
const metadata = {
|
|
5172
5268
|
package: "@hasna/todos",
|
|
@@ -5177,6 +5273,14 @@ function taskEventMetadata(task) {
|
|
|
5177
5273
|
task_list_id: task.task_list_id,
|
|
5178
5274
|
working_dir: task.working_dir
|
|
5179
5275
|
};
|
|
5276
|
+
const routeEnabled = booleanField(task.metadata.route_enabled);
|
|
5277
|
+
if (routeEnabled !== undefined) {
|
|
5278
|
+
metadata.route_enabled = routeEnabled;
|
|
5279
|
+
}
|
|
5280
|
+
const automation = routingAutomationMetadata(task);
|
|
5281
|
+
if (automation) {
|
|
5282
|
+
metadata.automation = automation;
|
|
5283
|
+
}
|
|
5180
5284
|
try {
|
|
5181
5285
|
const project = task.project_id ? getProject(task.project_id) : null;
|
|
5182
5286
|
const projectPath = project ? readMachineLocalPath(project) ?? project.path : task.working_dir;
|
|
@@ -5194,9 +5298,6 @@ function taskEventMetadata(task) {
|
|
|
5194
5298
|
if (projectPath) {
|
|
5195
5299
|
metadata.project_kind = classifyProjectKind(projectPath);
|
|
5196
5300
|
metadata.project_is_worktree = isWorktreePath(projectPath);
|
|
5197
|
-
if (typeof task.metadata.route_enabled === "boolean") {
|
|
5198
|
-
metadata.route_enabled = task.metadata.route_enabled;
|
|
5199
|
-
}
|
|
5200
5301
|
metadata.working_dir = task.working_dir ?? projectPath;
|
|
5201
5302
|
}
|
|
5202
5303
|
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.61",
|
|
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.11",
|
|
93
93
|
"@modelcontextprotocol/sdk": "^1.12.1",
|
|
94
94
|
"chalk": "^5.4.1",
|
|
95
95
|
"commander": "^13.1.0",
|