@hasna/todos 0.15.47 → 0.15.50
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/commands/query-commands.d.ts.map +1 -1
- package/dist/cli/index.js +296 -81
- package/dist/contracts.js +56 -5
- package/dist/db/task-crud.d.ts +6 -0
- package/dist/db/task-crud.d.ts.map +1 -1
- package/dist/db/tasks.d.ts +1 -1
- package/dist/db/tasks.d.ts.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +187 -70
- package/dist/lib/dedupe-projection.d.ts +48 -0
- package/dist/lib/dedupe-projection.d.ts.map +1 -0
- package/dist/lib/redaction.d.ts.map +1 -1
- package/dist/lib/task-dedupe.d.ts +5 -9
- package/dist/lib/task-dedupe.d.ts.map +1 -1
- package/dist/mcp/index.js +246 -80
- package/dist/mcp/tools/task-crud.d.ts.map +1 -1
- package/dist/mcp.js +3 -2
- package/dist/project-registration.js +128 -68
- package/dist/registry.js +56 -5
- package/dist/release-provenance.json +5 -5
- package/dist/server/cloud.d.ts.map +1 -1
- package/dist/server/index.js +248 -82
- package/dist/server/routes.d.ts.map +1 -1
- package/dist/server/v1.d.ts.map +1 -1
- package/dist/storage/postgres-adapter.d.ts.map +1 -1
- package/dist/storage/postgres-errors.d.ts +15 -0
- package/dist/storage/postgres-errors.d.ts.map +1 -0
- package/dist/storage/postgres-sync.d.ts +7 -0
- package/dist/storage/postgres-sync.d.ts.map +1 -1
- package/dist/storage/shadow-outbox.d.ts.map +1 -1
- package/dist/storage/shadow.d.ts.map +1 -1
- package/dist/storage.js +139 -66
- package/dist/task-manifest.js +118 -47
- package/dist/task-subtree-transfer.js +326 -44
- package/dist/types/index.d.ts +2 -2
- package/dist/types/index.d.ts.map +1 -1
- package/package.json +3 -2
package/dist/mcp/index.js
CHANGED
|
@@ -9582,13 +9582,15 @@ function upsertSecretSafetyConfig(input) {
|
|
|
9582
9582
|
saveConfig({ ...config, secret_safety: next });
|
|
9583
9583
|
return next;
|
|
9584
9584
|
}
|
|
9585
|
-
var DEFAULT_SECRET_PATTERNS, DEFAULT_SECRET_KEY_PATTERN, NON_SECRET_USAGE_KEYS, REDACTION_PLACEHOLDER;
|
|
9585
|
+
var XAI_PREFIX, DEFAULT_SECRET_PATTERNS, DEFAULT_SECRET_KEY_PATTERN, NON_SECRET_USAGE_KEYS, REDACTION_PLACEHOLDER;
|
|
9586
9586
|
var init_redaction = __esm(() => {
|
|
9587
9587
|
init_config2();
|
|
9588
|
+
XAI_PREFIX = ["x", "ai", "-"].join("");
|
|
9588
9589
|
DEFAULT_SECRET_PATTERNS = [
|
|
9589
9590
|
{ name: "aws-access-key", regex: /\b(AKIA|ASIA)[0-9A-Z]{16}\b/g, replacement: "[REDACTED_AWS_KEY]" },
|
|
9590
9591
|
{ name: "private-key", regex: /-----BEGIN (?:RSA |EC |OPENSSH |)PRIVATE KEY-----[\s\S]*?-----END (?:RSA |EC |OPENSSH |)PRIVATE KEY-----/g, replacement: "[REDACTED_PRIVATE_KEY]" },
|
|
9591
9592
|
{ name: "openai-token", regex: /\bsk-[A-Za-z0-9_-]{12,}\b/g, replacement: "[REDACTED_TOKEN]" },
|
|
9593
|
+
{ name: `${XAI_PREFIX}token`, regex: /\bxai[-][A-Za-z0-9]{20,80}\b/g, replacement: "[REDACTED_TOKEN]" },
|
|
9592
9594
|
{ name: "npm-token", regex: /\bnpm_[A-Za-z0-9]{20,}\b/g, replacement: "[REDACTED_NPM_TOKEN]" },
|
|
9593
9595
|
{ name: "github-fine-grained-token", regex: /\bgithub_pat_[A-Za-z0-9_]{20,}\b/g, replacement: "[REDACTED_GITHUB_TOKEN]" },
|
|
9594
9596
|
{ name: "github-token", regex: /\bgh[opsu]_[A-Za-z0-9]{20,}\b/g, replacement: "[REDACTED_GITHUB_TOKEN]" },
|
|
@@ -14571,6 +14573,12 @@ function listTasks(filter = {}, db) {
|
|
|
14571
14573
|
const d = db || getDatabase();
|
|
14572
14574
|
const { clearExpiredLocks: clearExpiredLocks2 } = (init_database(), __toCommonJS(exports_database));
|
|
14573
14575
|
clearExpiredLocks2(d);
|
|
14576
|
+
if (filter.limit !== undefined) {
|
|
14577
|
+
const limit = filter.limit;
|
|
14578
|
+
if (!Number.isSafeInteger(limit) || limit <= 0 || limit > MAX_TASK_LIST_LIMIT) {
|
|
14579
|
+
throw new TypeError(`listTasks limit must be a positive integer between 1 and ${MAX_TASK_LIST_LIMIT}; got ${String(limit)}`);
|
|
14580
|
+
}
|
|
14581
|
+
}
|
|
14574
14582
|
const conditions = [];
|
|
14575
14583
|
const params = [];
|
|
14576
14584
|
if (filter.project_id) {
|
|
@@ -15104,6 +15112,7 @@ function deleteTask(id, db) {
|
|
|
15104
15112
|
}
|
|
15105
15113
|
return result.changes > 0;
|
|
15106
15114
|
}
|
|
15115
|
+
var MAX_TASK_LIST_LIMIT = 1e5;
|
|
15107
15116
|
var init_task_crud = __esm(() => {
|
|
15108
15117
|
init_types();
|
|
15109
15118
|
init_database();
|
|
@@ -18018,7 +18027,8 @@ __export(exports_tasks, {
|
|
|
18018
18027
|
buildTaskBoardSnapshot: () => buildTaskBoardSnapshot,
|
|
18019
18028
|
archiveTasks: () => archiveTasks,
|
|
18020
18029
|
archiveCompletedTasks: () => archiveCompletedTasks,
|
|
18021
|
-
addDependency: () => addDependency
|
|
18030
|
+
addDependency: () => addDependency,
|
|
18031
|
+
MAX_TASK_LIST_LIMIT: () => MAX_TASK_LIST_LIMIT
|
|
18022
18032
|
});
|
|
18023
18033
|
var init_tasks = __esm(() => {
|
|
18024
18034
|
init_task_crud();
|
|
@@ -22441,8 +22451,8 @@ function registerTaskCrudTools(server, ctx) {
|
|
|
22441
22451
|
tags: exports_external.array(exports_external.string()).optional().describe("Filter by tags (AND logic)"),
|
|
22442
22452
|
created_after: exports_external.string().optional().describe("ISO date \u2014 tasks created after this date"),
|
|
22443
22453
|
created_before: exports_external.string().optional().describe("ISO date \u2014 tasks created before this date"),
|
|
22444
|
-
limit: exports_external.number().optional().describe("Max results (default: 50, max 500)"),
|
|
22445
|
-
offset: exports_external.number().optional().describe("Pagination offset"),
|
|
22454
|
+
limit: exports_external.number().int().min(1).optional().describe("Max results (default: 50, max 500)"),
|
|
22455
|
+
offset: exports_external.number().int().min(0).optional().describe("Pagination offset"),
|
|
22446
22456
|
metadata: exports_external.record(exports_external.unknown()).optional().describe("Exact top-level metadata filters")
|
|
22447
22457
|
}, async (params) => {
|
|
22448
22458
|
try {
|
|
@@ -25792,6 +25802,48 @@ var init_task_relationships = __esm(() => {
|
|
|
25792
25802
|
];
|
|
25793
25803
|
});
|
|
25794
25804
|
|
|
25805
|
+
// src/lib/dedupe-projection.ts
|
|
25806
|
+
function projectTasksForDedupe(tasks) {
|
|
25807
|
+
return tasks.map((task) => {
|
|
25808
|
+
const metadata = {};
|
|
25809
|
+
for (const key of DEDUPE_SOURCE_KEY_ALLOWLIST) {
|
|
25810
|
+
if (key in task.metadata) {
|
|
25811
|
+
metadata[key] = redactValue(task.metadata[key]);
|
|
25812
|
+
}
|
|
25813
|
+
}
|
|
25814
|
+
return {
|
|
25815
|
+
id: task.id,
|
|
25816
|
+
short_id: task.short_id,
|
|
25817
|
+
title: redactEvidenceText(task.title),
|
|
25818
|
+
description: task.description === null || task.description === undefined ? null : redactEvidenceText(task.description),
|
|
25819
|
+
status: task.status,
|
|
25820
|
+
created_at: task.created_at,
|
|
25821
|
+
updated_at: task.updated_at,
|
|
25822
|
+
project_id: task.project_id,
|
|
25823
|
+
task_list_id: task.task_list_id,
|
|
25824
|
+
assigned_to: task.assigned_to,
|
|
25825
|
+
priority: task.priority,
|
|
25826
|
+
metadata
|
|
25827
|
+
};
|
|
25828
|
+
});
|
|
25829
|
+
}
|
|
25830
|
+
var DEDUPE_SOURCE_KEY_ALLOWLIST;
|
|
25831
|
+
var init_dedupe_projection = __esm(() => {
|
|
25832
|
+
init_redaction();
|
|
25833
|
+
DEDUPE_SOURCE_KEY_ALLOWLIST = [
|
|
25834
|
+
"github_url",
|
|
25835
|
+
"github_issue_url",
|
|
25836
|
+
"github_pr_url",
|
|
25837
|
+
"source_url",
|
|
25838
|
+
"url",
|
|
25839
|
+
"external_url",
|
|
25840
|
+
"issue_url",
|
|
25841
|
+
"github_owner",
|
|
25842
|
+
"github_repo",
|
|
25843
|
+
"github_number"
|
|
25844
|
+
];
|
|
25845
|
+
});
|
|
25846
|
+
|
|
25795
25847
|
// src/lib/task-dedupe.ts
|
|
25796
25848
|
function asObject(value) {
|
|
25797
25849
|
return value && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
@@ -25956,10 +26008,10 @@ function olderFirst(left, right) {
|
|
|
25956
26008
|
function findDuplicateTasks(options = {}, db) {
|
|
25957
26009
|
const d = db || getDatabase();
|
|
25958
26010
|
const threshold = options.threshold ?? DEFAULT_THRESHOLD;
|
|
25959
|
-
const fingerprints = listTasks({
|
|
26011
|
+
const fingerprints = projectTasksForDedupe(listTasks({
|
|
25960
26012
|
include_archived: Boolean(options.include_archived),
|
|
25961
26013
|
limit: options.limit ?? 1000
|
|
25962
|
-
}, d).map(fingerprint);
|
|
26014
|
+
}, d)).map(fingerprint);
|
|
25963
26015
|
const candidates = [];
|
|
25964
26016
|
for (let i = 0;i < fingerprints.length; i++) {
|
|
25965
26017
|
for (let j = i + 1;j < fingerprints.length; j++) {
|
|
@@ -26194,6 +26246,7 @@ var init_task_dedupe = __esm(() => {
|
|
|
26194
26246
|
init_database();
|
|
26195
26247
|
init_task_relationships();
|
|
26196
26248
|
init_tasks();
|
|
26249
|
+
init_dedupe_projection();
|
|
26197
26250
|
STOP_WORDS = new Set([
|
|
26198
26251
|
"a",
|
|
26199
26252
|
"an",
|
|
@@ -36364,7 +36417,7 @@ var package_default;
|
|
|
36364
36417
|
var init_package = __esm(() => {
|
|
36365
36418
|
package_default = {
|
|
36366
36419
|
name: "@hasna/todos",
|
|
36367
|
-
version: "0.15.
|
|
36420
|
+
version: "0.15.50",
|
|
36368
36421
|
description: "Universal task management for AI coding agents - CLI + MCP server + interactive TUI",
|
|
36369
36422
|
type: "module",
|
|
36370
36423
|
main: "dist/index.js",
|
|
@@ -36428,6 +36481,7 @@ var init_package = __esm(() => {
|
|
|
36428
36481
|
],
|
|
36429
36482
|
scripts: {
|
|
36430
36483
|
build: "rm -rf dist dashboard/dist && cd dashboard && bun install --frozen-lockfile && bun run build && cd .. && bun build src/cli/index.tsx --outdir dist/cli --target bun --external ink --external react --external chalk --external @modelcontextprotocol/sdk --external '@hasna/contracts' --external '@hasna/contracts/*' && bun build src/mcp/index.ts --outdir dist/mcp --target bun --external @modelcontextprotocol/sdk --external '@hasna/contracts' --external '@hasna/contracts/*' && bun build src/server/index.ts --outdir dist/server --target bun && bun build src/sdk/index.ts --outdir dist/sdk --target bun --external '@hasna/contracts' --external '@hasna/contracts/*' && bun build src/index.ts src/mcp.ts src/registry.ts src/contracts.ts src/storage.ts src/testing.ts src/project-registration.ts src/task-manifest.ts src/task-subtree-transfer.ts --outdir dist --root src --target bun --external '@hasna/contracts' --external '@hasna/contracts/*' && tsc --emitDeclarationOnly --outDir dist",
|
|
36484
|
+
"build:js": "rm -rf dist && bun build src/cli/index.tsx --outdir dist/cli --target bun --external ink --external react --external chalk --external @modelcontextprotocol/sdk --external '@hasna/contracts' --external '@hasna/contracts/*' && bun build src/mcp/index.ts --outdir dist/mcp --target bun --external @modelcontextprotocol/sdk --external '@hasna/contracts' --external '@hasna/contracts/*' && bun build src/server/index.ts --outdir dist/server --target bun && bun build src/sdk/index.ts --outdir dist/sdk --target bun --external '@hasna/contracts' --external '@hasna/contracts/*' && bun build src/index.ts src/mcp.ts src/registry.ts src/contracts.ts src/storage.ts src/testing.ts src/project-registration.ts src/task-manifest.ts src/task-subtree-transfer.ts --outdir dist --root src --target bun --external '@hasna/contracts' --external '@hasna/contracts/*' && tsc --emitDeclarationOnly --outDir dist",
|
|
36431
36485
|
"build:server": "rm -rf dist && bun build src/cli/index.tsx --outdir dist/cli --target bun --external ink --external react --external chalk --external @modelcontextprotocol/sdk --external '@hasna/contracts' --external '@hasna/contracts/*' && bun build src/mcp/index.ts --outdir dist/mcp --target bun --external @modelcontextprotocol/sdk --external '@hasna/contracts' --external '@hasna/contracts/*' && bun build src/server/index.ts --outdir dist/server --target bun && bun build src/sdk/index.ts --outdir dist/sdk --target bun --external '@hasna/contracts' --external '@hasna/contracts/*' && bun build src/index.ts src/mcp.ts src/registry.ts src/contracts.ts src/storage.ts src/testing.ts src/project-registration.ts src/task-manifest.ts src/task-subtree-transfer.ts --outdir dist --target bun --external '@hasna/contracts' --external '@hasna/contracts/*'",
|
|
36432
36486
|
migrate: "bun run src/server/index.ts migrate",
|
|
36433
36487
|
"backfill:comment-redaction": "bun run src/server/index.ts redact-comments",
|
|
@@ -36482,7 +36536,7 @@ var init_package = __esm(() => {
|
|
|
36482
36536
|
author: "Andrei Hasna <andrei@hasna.com>",
|
|
36483
36537
|
license: "Apache-2.0",
|
|
36484
36538
|
dependencies: {
|
|
36485
|
-
"@hasna/contracts": "0.
|
|
36539
|
+
"@hasna/contracts": "0.14.0",
|
|
36486
36540
|
"@hasna/events": "^0.1.11",
|
|
36487
36541
|
"@modelcontextprotocol/sdk": "^1.12.1",
|
|
36488
36542
|
chalk: "^5.4.1",
|
|
@@ -46738,6 +46792,27 @@ var init_local_sqlite = __esm(() => {
|
|
|
46738
46792
|
TASK_UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
46739
46793
|
});
|
|
46740
46794
|
|
|
46795
|
+
// src/storage/postgres-errors.ts
|
|
46796
|
+
function isPostgresUniqueViolation(error) {
|
|
46797
|
+
if (typeof error !== "object" || error === null)
|
|
46798
|
+
return false;
|
|
46799
|
+
const candidate = error;
|
|
46800
|
+
const states = [candidate.code, candidate.errno, candidate.sqlState, candidate.sqlstate];
|
|
46801
|
+
if (typeof candidate.cause === "object" && candidate.cause !== null) {
|
|
46802
|
+
const cause = candidate.cause;
|
|
46803
|
+
states.push(cause.code, cause.errno, cause.sqlState, cause.sqlstate);
|
|
46804
|
+
}
|
|
46805
|
+
return states.some((state) => String(state) === "23505");
|
|
46806
|
+
}
|
|
46807
|
+
function postgresConstraintName(error) {
|
|
46808
|
+
if (typeof error !== "object" || error === null)
|
|
46809
|
+
return "";
|
|
46810
|
+
const candidate = error;
|
|
46811
|
+
const cause = typeof candidate.cause === "object" && candidate.cause !== null ? candidate.cause : undefined;
|
|
46812
|
+
const constraint = candidate.constraint ?? candidate.constraint_name ?? cause?.constraint ?? cause?.constraint_name;
|
|
46813
|
+
return typeof constraint === "string" ? constraint : "";
|
|
46814
|
+
}
|
|
46815
|
+
|
|
46741
46816
|
// src/storage/postgres-sync.ts
|
|
46742
46817
|
function postgresTodosSyncSchemaSql(tableName = DEFAULT_TODOS_POSTGRES_SYNC_TABLE, cursorTableName = DEFAULT_TODOS_POSTGRES_CURSOR_TABLE) {
|
|
46743
46818
|
assertSafeIdentifier(tableName);
|
|
@@ -46932,53 +47007,98 @@ class PostgresTodosSyncStore {
|
|
|
46932
47007
|
if (routingErrors.length > 0) {
|
|
46933
47008
|
throw new Error(`Invalid snapshot routing metadata: ${routingErrors.join("; ")}`);
|
|
46934
47009
|
}
|
|
46935
|
-
const
|
|
46936
|
-
|
|
46937
|
-
|
|
46938
|
-
|
|
46939
|
-
|
|
46940
|
-
|
|
46941
|
-
const
|
|
46942
|
-
|
|
46943
|
-
|
|
46944
|
-
|
|
46945
|
-
|
|
46946
|
-
|
|
46947
|
-
|
|
46948
|
-
|
|
46949
|
-
|
|
46950
|
-
|
|
46951
|
-
|
|
46952
|
-
|
|
46953
|
-
|
|
46954
|
-
|
|
46955
|
-
|
|
46956
|
-
|
|
46957
|
-
|
|
46958
|
-
|
|
46959
|
-
|
|
46960
|
-
|
|
46961
|
-
|
|
46962
|
-
|
|
46963
|
-
|
|
46964
|
-
|
|
46965
|
-
|
|
46966
|
-
|
|
46967
|
-
|
|
46968
|
-
|
|
46969
|
-
|
|
46970
|
-
|
|
46971
|
-
|
|
46972
|
-
|
|
46973
|
-
|
|
46974
|
-
|
|
46975
|
-
|
|
46976
|
-
|
|
46977
|
-
|
|
46978
|
-
|
|
46979
|
-
|
|
47010
|
+
const push = async (client) => {
|
|
47011
|
+
const existing = await client.query(`SELECT object_type, object_id, payload, updated_at, deleted_at, source_machine_id, version
|
|
47012
|
+
FROM ${this.tableName}
|
|
47013
|
+
WHERE service = $1 AND object_type IN ($2, $3) AND deleted_at IS NULL`, [this.service, "projects", "task_lists"]);
|
|
47014
|
+
const existingProjects = [];
|
|
47015
|
+
const existingTaskLists = [];
|
|
47016
|
+
for (const row of existing.rows) {
|
|
47017
|
+
const payload = payloadRecord(row.payload);
|
|
47018
|
+
if (row.object_type === "projects")
|
|
47019
|
+
existingProjects.push(payload);
|
|
47020
|
+
if (row.object_type === "task_lists")
|
|
47021
|
+
existingTaskLists.push(payload);
|
|
47022
|
+
}
|
|
47023
|
+
const destinationErrors = validateSnapshotRoutingDestinationConflicts(snapshot.projects, snapshot.taskLists, existingProjects, existingTaskLists);
|
|
47024
|
+
if (destinationErrors.length > 0) {
|
|
47025
|
+
throw new ResourceConflictError("SNAPSHOT_DESTINATION_CONFLICT", `Snapshot routing conflicts with destination: ${destinationErrors.join("; ")}`);
|
|
47026
|
+
}
|
|
47027
|
+
const result = { records: 0, objectTypes: {} };
|
|
47028
|
+
const sourceMachineId = context.requestId ?? this.sourceMachineId ?? null;
|
|
47029
|
+
for (const entry of snapshotEntries(snapshot)) {
|
|
47030
|
+
if (entry.deletedAt === null)
|
|
47031
|
+
assertCanonicalScopedSlugEntry(entry);
|
|
47032
|
+
try {
|
|
47033
|
+
await client.query(`INSERT INTO ${this.tableName} (
|
|
47034
|
+
service, object_type, object_id, payload, updated_at,
|
|
47035
|
+
deleted_at, source_machine_id, version
|
|
47036
|
+
) VALUES ($1, $2, $3, $4::jsonb, $5::timestamptz, $6::timestamptz, $7, $8)
|
|
47037
|
+
ON CONFLICT (service, object_type, object_id) DO UPDATE SET
|
|
47038
|
+
payload = EXCLUDED.payload,
|
|
47039
|
+
updated_at = EXCLUDED.updated_at,
|
|
47040
|
+
deleted_at = EXCLUDED.deleted_at,
|
|
47041
|
+
source_machine_id = EXCLUDED.source_machine_id,
|
|
47042
|
+
version = EXCLUDED.version
|
|
47043
|
+
WHERE ${this.tableName}.updated_at < EXCLUDED.updated_at
|
|
47044
|
+
OR (${this.tableName}.updated_at = EXCLUDED.updated_at
|
|
47045
|
+
AND COALESCE(${this.tableName}.version, 0) <= COALESCE(EXCLUDED.version, 0))`, [
|
|
47046
|
+
this.service,
|
|
47047
|
+
entry.type,
|
|
47048
|
+
entry.id,
|
|
47049
|
+
entry.payload,
|
|
47050
|
+
entry.updatedAt,
|
|
47051
|
+
entry.deletedAt,
|
|
47052
|
+
sourceMachineId,
|
|
47053
|
+
entry.version
|
|
47054
|
+
]);
|
|
47055
|
+
} catch (error) {
|
|
47056
|
+
await this.classifySyncInsertConflict(error, entry);
|
|
47057
|
+
}
|
|
47058
|
+
result.records += 1;
|
|
47059
|
+
result.objectTypes[entry.type] = (result.objectTypes[entry.type] ?? 0) + 1;
|
|
47060
|
+
}
|
|
47061
|
+
return result;
|
|
47062
|
+
};
|
|
47063
|
+
if (typeof this.client.transaction === "function") {
|
|
47064
|
+
return this.client.transaction((client) => push(client));
|
|
46980
47065
|
}
|
|
46981
|
-
return
|
|
47066
|
+
return push(this.client);
|
|
47067
|
+
}
|
|
47068
|
+
async classifySyncInsertConflict(error, entry) {
|
|
47069
|
+
if (!isPostgresUniqueViolation(error))
|
|
47070
|
+
throw error;
|
|
47071
|
+
const payload = entry.payload;
|
|
47072
|
+
const taskListSlug = String(payload["slug"] ?? "");
|
|
47073
|
+
const projectSlug = String(payload["task_list_id"] ?? "");
|
|
47074
|
+
const constraintName = postgresConstraintName(error);
|
|
47075
|
+
if (entry.type === "projects" && constraintName.includes("project_task_list_slug_uidx")) {
|
|
47076
|
+
throw new ResourceConflictError("PROJECT_SLUG_CONFLICT", `Project slug "${projectSlug}" already exists`);
|
|
47077
|
+
}
|
|
47078
|
+
if (entry.type === "task_lists" && constraintName.includes("task_list_scope_slug_uidx")) {
|
|
47079
|
+
throw new ResourceConflictError("TASK_LIST_SLUG_CONFLICT", `Task list with slug "${taskListSlug}" already exists in this scope`);
|
|
47080
|
+
}
|
|
47081
|
+
if (entry.type === "task_lists") {
|
|
47082
|
+
const scope = typeof payload["project_id"] === "string" ? payload["project_id"] : "";
|
|
47083
|
+
const conflict = await this.client.query(`/* todos:classify-sync-task-list-conflict */ SELECT EXISTS (
|
|
47084
|
+
SELECT 1 FROM ${this.tableName}
|
|
47085
|
+
WHERE service = $1 AND object_type = 'task_lists' AND object_id <> $2
|
|
47086
|
+
AND deleted_at IS NULL AND COALESCE(payload->>'project_id','') = $3 AND payload->>'slug' = $4
|
|
47087
|
+
) AS conflict`, [this.service, entry.id, scope, taskListSlug]);
|
|
47088
|
+
if (conflict.rows[0]?.conflict) {
|
|
47089
|
+
throw new ResourceConflictError("TASK_LIST_SLUG_CONFLICT", `Task list with slug "${taskListSlug}" already exists in this scope`);
|
|
47090
|
+
}
|
|
47091
|
+
} else if (entry.type === "projects") {
|
|
47092
|
+
const conflict = await this.client.query(`/* todos:classify-sync-project-conflict */ SELECT EXISTS (
|
|
47093
|
+
SELECT 1 FROM ${this.tableName}
|
|
47094
|
+
WHERE service = $1 AND object_type = 'projects' AND object_id <> $2
|
|
47095
|
+
AND deleted_at IS NULL AND payload->>'task_list_id' = $3
|
|
47096
|
+
) AS conflict`, [this.service, entry.id, projectSlug]);
|
|
47097
|
+
if (conflict.rows[0]?.conflict) {
|
|
47098
|
+
throw new ResourceConflictError("PROJECT_SLUG_CONFLICT", `Project slug "${projectSlug}" already exists`);
|
|
47099
|
+
}
|
|
47100
|
+
}
|
|
47101
|
+
throw error;
|
|
46982
47102
|
}
|
|
46983
47103
|
async pullSnapshot(options = {}) {
|
|
46984
47104
|
const params = [this.service];
|
|
@@ -47135,6 +47255,7 @@ function assertSafeIdentifier(value) {
|
|
|
47135
47255
|
}
|
|
47136
47256
|
var DEFAULT_TODOS_POSTGRES_SYNC_TABLE = "todos_sync_records", DEFAULT_TODOS_POSTGRES_CURSOR_TABLE = "todos_sync_cursors", PostgresScopedSlugMigrationConflictError, PostgresScopedSlugIndexBuildError;
|
|
47137
47257
|
var init_postgres_sync = __esm(() => {
|
|
47258
|
+
init_types();
|
|
47138
47259
|
PostgresScopedSlugMigrationConflictError = class PostgresScopedSlugMigrationConflictError extends Error {
|
|
47139
47260
|
conflicts;
|
|
47140
47261
|
constructor(conflicts) {
|
|
@@ -47280,6 +47401,12 @@ class TodosShadowOutbox {
|
|
|
47280
47401
|
this.onEvent?.({ type: "mirrored", objectType: row.object_type, id: row.object_id, lagMs });
|
|
47281
47402
|
} catch (error) {
|
|
47282
47403
|
const message = error instanceof Error ? error.message : String(error);
|
|
47404
|
+
if (error instanceof ResourceConflictError) {
|
|
47405
|
+
this.metrics.lastError = message;
|
|
47406
|
+
this.db.run(`UPDATE shadow_outbox SET attempts=?, last_error=?, status='failed' WHERE seq=? AND revision=?`, [row.attempts + 1, message, row.seq, row.revision]);
|
|
47407
|
+
this.onEvent?.({ type: "parked", objectType: row.object_type, id: row.object_id, error: message });
|
|
47408
|
+
return;
|
|
47409
|
+
}
|
|
47283
47410
|
this.metrics.retries += 1;
|
|
47284
47411
|
this.metrics.lastError = message;
|
|
47285
47412
|
const attempts = row.attempts + 1;
|
|
@@ -47402,6 +47529,7 @@ function emptySnapshot() {
|
|
|
47402
47529
|
}
|
|
47403
47530
|
var MAX_BACKOFF_MS;
|
|
47404
47531
|
var init_shadow_outbox = __esm(() => {
|
|
47532
|
+
init_types();
|
|
47405
47533
|
init_local_sqlite();
|
|
47406
47534
|
init_postgres_sync();
|
|
47407
47535
|
init_shadow_outbox_schema();
|
|
@@ -50090,25 +50218,6 @@ async function retryOnTransientPostgresError(fn, attempts = 2, delayMs = 150) {
|
|
|
50090
50218
|
}
|
|
50091
50219
|
throw lastError;
|
|
50092
50220
|
}
|
|
50093
|
-
function isPostgresUniqueViolation(error) {
|
|
50094
|
-
if (typeof error !== "object" || error === null)
|
|
50095
|
-
return false;
|
|
50096
|
-
const candidate = error;
|
|
50097
|
-
const states = [candidate.code, candidate.errno, candidate.sqlState, candidate.sqlstate];
|
|
50098
|
-
if (typeof candidate.cause === "object" && candidate.cause !== null) {
|
|
50099
|
-
const cause = candidate.cause;
|
|
50100
|
-
states.push(cause.code, cause.errno, cause.sqlState, cause.sqlstate);
|
|
50101
|
-
}
|
|
50102
|
-
return states.some((state) => String(state) === "23505");
|
|
50103
|
-
}
|
|
50104
|
-
function postgresConstraintName(error) {
|
|
50105
|
-
if (typeof error !== "object" || error === null)
|
|
50106
|
-
return "";
|
|
50107
|
-
const candidate = error;
|
|
50108
|
-
const cause = typeof candidate.cause === "object" && candidate.cause !== null ? candidate.cause : undefined;
|
|
50109
|
-
const constraint = candidate.constraint ?? candidate.constraint_name ?? cause?.constraint ?? cause?.constraint_name;
|
|
50110
|
-
return typeof constraint === "string" ? constraint : "";
|
|
50111
|
-
}
|
|
50112
50221
|
var CLOUD_LOCK_EXPIRY_MINUTES = 30, TASK_ORDER_TIEBREAK = "CASE payload->>'priority' WHEN 'critical' THEN 0 WHEN 'high' THEN 1 WHEN 'medium' THEN 2 WHEN 'low' THEN 3 ELSE 4 END ASC, payload->>'created_at' ASC, payload->>'id' ASC", TASK_ORDER_BY, TRANSIENT_POSTGRES_SQLSTATES, TRANSIENT_POSTGRES_MESSAGE_MARKERS;
|
|
50113
50222
|
var init_postgres_adapter = __esm(() => {
|
|
50114
50223
|
init_types();
|
|
@@ -56008,6 +56117,13 @@ function resolveSigningSecret(env = process.env) {
|
|
|
56008
56117
|
function isPostgresBackendConfigured(env = process.env) {
|
|
56009
56118
|
return Boolean(resolveCloudDatabaseUrl(env));
|
|
56010
56119
|
}
|
|
56120
|
+
function schemaRetryMinIntervalMs(env = process.env) {
|
|
56121
|
+
const raw = env.HASNA_TODOS_SCHEMA_RETRY_MIN_MS;
|
|
56122
|
+
if (!raw)
|
|
56123
|
+
return DEFAULT_SCHEMA_RETRY_MIN_MS;
|
|
56124
|
+
const parsed = Number(raw);
|
|
56125
|
+
return Number.isFinite(parsed) && parsed >= 0 ? parsed : DEFAULT_SCHEMA_RETRY_MIN_MS;
|
|
56126
|
+
}
|
|
56011
56127
|
function getCloudTenantId() {
|
|
56012
56128
|
return process.env.HASNA_TODOS_TENANT_ID ?? "default";
|
|
56013
56129
|
}
|
|
@@ -56103,6 +56219,10 @@ function getCloudVerifier() {
|
|
|
56103
56219
|
async function ensureCloudSchema() {
|
|
56104
56220
|
if (schemaEnsured)
|
|
56105
56221
|
return schemaEnsured;
|
|
56222
|
+
if (lastSchemaFailure !== null && Date.now() - lastSchemaAttemptAtMs < schemaRetryMinIntervalMs()) {
|
|
56223
|
+
throw lastSchemaFailure;
|
|
56224
|
+
}
|
|
56225
|
+
lastSchemaAttemptAtMs = Date.now();
|
|
56106
56226
|
schemaEnsured = (async () => {
|
|
56107
56227
|
const client = getClient();
|
|
56108
56228
|
for (const sql of postgresTodosSyncSchemaSql()) {
|
|
@@ -56123,6 +56243,7 @@ async function ensureCloudSchema() {
|
|
|
56123
56243
|
await getApiKeyStore().ensureSchema();
|
|
56124
56244
|
})().catch((error) => {
|
|
56125
56245
|
schemaEnsured = null;
|
|
56246
|
+
lastSchemaFailure = error;
|
|
56126
56247
|
throw error;
|
|
56127
56248
|
});
|
|
56128
56249
|
return schemaEnsured;
|
|
@@ -56170,8 +56291,10 @@ async function closeCloud() {
|
|
|
56170
56291
|
cachedTaskManifestAuthority = null;
|
|
56171
56292
|
cachedTaskSubtreeTransferAuthority = null;
|
|
56172
56293
|
schemaEnsured = null;
|
|
56294
|
+
lastSchemaAttemptAtMs = 0;
|
|
56295
|
+
lastSchemaFailure = null;
|
|
56173
56296
|
}
|
|
56174
|
-
var TODOS_APP_SLUG = "todos", cachedClient = null, cachedAdapter = null, cachedStore = null, cachedVerifier = null, cachedPrGroupLedger = null, cachedProjectRegistrationAuthority = null, cachedTaskManifestAuthority = null, cachedTaskSubtreeTransferAuthority = null, schemaEnsured = null;
|
|
56297
|
+
var TODOS_APP_SLUG = "todos", cachedClient = null, cachedAdapter = null, cachedStore = null, cachedVerifier = null, cachedPrGroupLedger = null, cachedProjectRegistrationAuthority = null, cachedTaskManifestAuthority = null, cachedTaskSubtreeTransferAuthority = null, schemaEnsured = null, lastSchemaAttemptAtMs = 0, lastSchemaFailure = null, DEFAULT_SCHEMA_RETRY_MIN_MS = 1e4;
|
|
56175
56298
|
var init_cloud = __esm(() => {
|
|
56176
56299
|
init_cloud_client();
|
|
56177
56300
|
init_postgres_adapter();
|
|
@@ -56695,6 +56818,19 @@ function taskStatusQueryParam(url) {
|
|
|
56695
56818
|
return { ok: false, message: result.message };
|
|
56696
56819
|
return { ok: true, value: collapseEnumValues(result.values) };
|
|
56697
56820
|
}
|
|
56821
|
+
function parsePaginationQueryParam(url, name) {
|
|
56822
|
+
const raw = url.searchParams.get(name);
|
|
56823
|
+
if (raw === null)
|
|
56824
|
+
return { ok: true, value: undefined };
|
|
56825
|
+
const min = name === "limit" ? 1 : 0;
|
|
56826
|
+
const message = name === "limit" ? "limit must be a positive integer" : "offset must be a non-negative integer";
|
|
56827
|
+
if (!/^\d+$/.test(raw))
|
|
56828
|
+
return { ok: false, message };
|
|
56829
|
+
const value = Number(raw);
|
|
56830
|
+
if (!Number.isSafeInteger(value) || value < min)
|
|
56831
|
+
return { ok: false, message };
|
|
56832
|
+
return { ok: true, value };
|
|
56833
|
+
}
|
|
56698
56834
|
async function handleListTasks(_req, url, _ctx, json5, taskToSummary2) {
|
|
56699
56835
|
const statusParam = taskStatusQueryParam(url);
|
|
56700
56836
|
if (!statusParam.ok)
|
|
@@ -56702,16 +56838,20 @@ async function handleListTasks(_req, url, _ctx, json5, taskToSummary2) {
|
|
|
56702
56838
|
const projectId = url.searchParams.get("project_id") || undefined;
|
|
56703
56839
|
const sessionId = url.searchParams.get("session_id") || undefined;
|
|
56704
56840
|
const agentId = url.searchParams.get("agent_id") || undefined;
|
|
56705
|
-
const limitParam = url
|
|
56706
|
-
|
|
56841
|
+
const limitParam = parsePaginationQueryParam(url, "limit");
|
|
56842
|
+
if (!limitParam.ok)
|
|
56843
|
+
return json5({ error: limitParam.message }, 400);
|
|
56844
|
+
const offsetParam = parsePaginationQueryParam(url, "offset");
|
|
56845
|
+
if (!offsetParam.ok)
|
|
56846
|
+
return json5({ error: offsetParam.message }, 400);
|
|
56707
56847
|
const fields = parseFieldsParam(url);
|
|
56708
56848
|
const tasks = listTasks({
|
|
56709
56849
|
status: statusParam.value,
|
|
56710
56850
|
project_id: projectId,
|
|
56711
56851
|
session_id: sessionId,
|
|
56712
56852
|
agent_id: agentId,
|
|
56713
|
-
limit: limitParam
|
|
56714
|
-
offset: offsetParam
|
|
56853
|
+
limit: limitParam.value,
|
|
56854
|
+
offset: offsetParam.value
|
|
56715
56855
|
});
|
|
56716
56856
|
return json5(tasks.map((t) => taskToSummary2(t, fields)));
|
|
56717
56857
|
}
|
|
@@ -61580,6 +61720,26 @@ function parseSinceCursor(raw) {
|
|
|
61580
61720
|
}
|
|
61581
61721
|
return { ok: true, value: new Date(parsed).toISOString() };
|
|
61582
61722
|
}
|
|
61723
|
+
function paginationQueryParam(url, name) {
|
|
61724
|
+
const raw = url.searchParams.get(name);
|
|
61725
|
+
if (raw === null)
|
|
61726
|
+
return { ok: true, value: undefined };
|
|
61727
|
+
const min = name === "limit" ? 1 : 0;
|
|
61728
|
+
if (!/^\d+$/.test(raw)) {
|
|
61729
|
+
return {
|
|
61730
|
+
ok: false,
|
|
61731
|
+
response: error(400, name === "limit" ? "limit must be a positive integer" : "offset must be a non-negative integer")
|
|
61732
|
+
};
|
|
61733
|
+
}
|
|
61734
|
+
const value = Number(raw);
|
|
61735
|
+
if (!Number.isSafeInteger(value) || value < min) {
|
|
61736
|
+
return {
|
|
61737
|
+
ok: false,
|
|
61738
|
+
response: error(400, name === "limit" ? "limit must be a positive integer" : "offset must be a non-negative integer")
|
|
61739
|
+
};
|
|
61740
|
+
}
|
|
61741
|
+
return { ok: true, value };
|
|
61742
|
+
}
|
|
61583
61743
|
function validateTaskCompletion(value) {
|
|
61584
61744
|
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
61585
61745
|
return { ok: false, message: "completion body must be an object" };
|
|
@@ -62108,6 +62268,12 @@ async function handleV1Request(req, url, dependencies = {}) {
|
|
|
62108
62268
|
if (updatedAfter !== null && !updatedAfter.ok) {
|
|
62109
62269
|
return error(400, updatedAfter.message);
|
|
62110
62270
|
}
|
|
62271
|
+
const limitParam = paginationQueryParam(url, "limit");
|
|
62272
|
+
if (!limitParam.ok)
|
|
62273
|
+
return limitParam.response;
|
|
62274
|
+
const offsetParam = paginationQueryParam(url, "offset");
|
|
62275
|
+
if (!offsetParam.ok)
|
|
62276
|
+
return offsetParam.response;
|
|
62111
62277
|
const filter = {
|
|
62112
62278
|
...updatedAfter !== null && updatedAfter.ok ? { updated_after: updatedAfter.value } : {},
|
|
62113
62279
|
...url.searchParams.get("q") ? { query: url.searchParams.get("q") } : {},
|
|
@@ -62124,8 +62290,8 @@ async function handleV1Request(req, url, dependencies = {}) {
|
|
|
62124
62290
|
...url.searchParams.get("tags") ? {
|
|
62125
62291
|
tags: url.searchParams.get("tags").split(",").map((tag) => tag.trim()).filter(Boolean)
|
|
62126
62292
|
} : {},
|
|
62127
|
-
...
|
|
62128
|
-
...
|
|
62293
|
+
...limitParam.value !== undefined ? { limit: limitParam.value } : {},
|
|
62294
|
+
...offsetParam.value !== undefined ? { offset: offsetParam.value } : {}
|
|
62129
62295
|
};
|
|
62130
62296
|
const tasks = await store.tasks.list(filter);
|
|
62131
62297
|
const { limit: _l, offset: _o, ...countFilter } = filter;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"task-crud.d.ts","sourceRoot":"","sources":["../../../src/mcp/tools/task-crud.ts"],"names":[],"mappings":"AACA;;;GAGG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEpE,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,sBAAsB,CAAC;AAqBjD,UAAU,eAAe;IACvB,kBAAkB,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC;IAC9C,SAAS,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC;IACzD,WAAW,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,MAAM,CAAC;IACxC,UAAU,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,MAAM,CAAC;IACnC,gBAAgB,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,mBAAmB,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC;IACvE,aAAa,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,SAAS,CAAC;IAC1F,UAAU,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,OAAO,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;CACrE;AAuBD,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,SAAS,EAAE,GAAG,EAAE,eAAe,
|
|
1
|
+
{"version":3,"file":"task-crud.d.ts","sourceRoot":"","sources":["../../../src/mcp/tools/task-crud.ts"],"names":[],"mappings":"AACA;;;GAGG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEpE,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,sBAAsB,CAAC;AAqBjD,UAAU,eAAe;IACvB,kBAAkB,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC;IAC9C,SAAS,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC;IACzD,WAAW,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,MAAM,CAAC;IACxC,UAAU,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,MAAM,CAAC;IACnC,gBAAgB,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,mBAAmB,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC;IACvE,aAAa,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,SAAS,CAAC;IAC1F,UAAU,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,OAAO,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;CACrE;AAuBD,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,SAAS,EAAE,GAAG,EAAE,eAAe,QAqa5E"}
|
package/dist/mcp.js
CHANGED
|
@@ -41,7 +41,7 @@ var __require = import.meta.require;
|
|
|
41
41
|
// package.json
|
|
42
42
|
var package_default = {
|
|
43
43
|
name: "@hasna/todos",
|
|
44
|
-
version: "0.15.
|
|
44
|
+
version: "0.15.50",
|
|
45
45
|
description: "Universal task management for AI coding agents - CLI + MCP server + interactive TUI",
|
|
46
46
|
type: "module",
|
|
47
47
|
main: "dist/index.js",
|
|
@@ -105,6 +105,7 @@ var package_default = {
|
|
|
105
105
|
],
|
|
106
106
|
scripts: {
|
|
107
107
|
build: "rm -rf dist dashboard/dist && cd dashboard && bun install --frozen-lockfile && bun run build && cd .. && bun build src/cli/index.tsx --outdir dist/cli --target bun --external ink --external react --external chalk --external @modelcontextprotocol/sdk --external '@hasna/contracts' --external '@hasna/contracts/*' && bun build src/mcp/index.ts --outdir dist/mcp --target bun --external @modelcontextprotocol/sdk --external '@hasna/contracts' --external '@hasna/contracts/*' && bun build src/server/index.ts --outdir dist/server --target bun && bun build src/sdk/index.ts --outdir dist/sdk --target bun --external '@hasna/contracts' --external '@hasna/contracts/*' && bun build src/index.ts src/mcp.ts src/registry.ts src/contracts.ts src/storage.ts src/testing.ts src/project-registration.ts src/task-manifest.ts src/task-subtree-transfer.ts --outdir dist --root src --target bun --external '@hasna/contracts' --external '@hasna/contracts/*' && tsc --emitDeclarationOnly --outDir dist",
|
|
108
|
+
"build:js": "rm -rf dist && bun build src/cli/index.tsx --outdir dist/cli --target bun --external ink --external react --external chalk --external @modelcontextprotocol/sdk --external '@hasna/contracts' --external '@hasna/contracts/*' && bun build src/mcp/index.ts --outdir dist/mcp --target bun --external @modelcontextprotocol/sdk --external '@hasna/contracts' --external '@hasna/contracts/*' && bun build src/server/index.ts --outdir dist/server --target bun && bun build src/sdk/index.ts --outdir dist/sdk --target bun --external '@hasna/contracts' --external '@hasna/contracts/*' && bun build src/index.ts src/mcp.ts src/registry.ts src/contracts.ts src/storage.ts src/testing.ts src/project-registration.ts src/task-manifest.ts src/task-subtree-transfer.ts --outdir dist --root src --target bun --external '@hasna/contracts' --external '@hasna/contracts/*' && tsc --emitDeclarationOnly --outDir dist",
|
|
108
109
|
"build:server": "rm -rf dist && bun build src/cli/index.tsx --outdir dist/cli --target bun --external ink --external react --external chalk --external @modelcontextprotocol/sdk --external '@hasna/contracts' --external '@hasna/contracts/*' && bun build src/mcp/index.ts --outdir dist/mcp --target bun --external @modelcontextprotocol/sdk --external '@hasna/contracts' --external '@hasna/contracts/*' && bun build src/server/index.ts --outdir dist/server --target bun && bun build src/sdk/index.ts --outdir dist/sdk --target bun --external '@hasna/contracts' --external '@hasna/contracts/*' && bun build src/index.ts src/mcp.ts src/registry.ts src/contracts.ts src/storage.ts src/testing.ts src/project-registration.ts src/task-manifest.ts src/task-subtree-transfer.ts --outdir dist --target bun --external '@hasna/contracts' --external '@hasna/contracts/*'",
|
|
109
110
|
migrate: "bun run src/server/index.ts migrate",
|
|
110
111
|
"backfill:comment-redaction": "bun run src/server/index.ts redact-comments",
|
|
@@ -159,7 +160,7 @@ var package_default = {
|
|
|
159
160
|
author: "Andrei Hasna <andrei@hasna.com>",
|
|
160
161
|
license: "Apache-2.0",
|
|
161
162
|
dependencies: {
|
|
162
|
-
"@hasna/contracts": "0.
|
|
163
|
+
"@hasna/contracts": "0.14.0",
|
|
163
164
|
"@hasna/events": "^0.1.11",
|
|
164
165
|
"@modelcontextprotocol/sdk": "^1.12.1",
|
|
165
166
|
chalk: "^5.4.1",
|