@hasna/todos 0.8.0 → 0.9.0
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 +2 -23
- package/dist/cli/components/App.d.ts +2 -0
- package/dist/cli/components/App.d.ts.map +1 -0
- package/dist/cli/components/Header.d.ts +8 -0
- package/dist/cli/components/Header.d.ts.map +1 -0
- package/dist/cli/components/ProjectList.d.ts +8 -0
- package/dist/cli/components/ProjectList.d.ts.map +1 -0
- package/dist/cli/components/SearchView.d.ts +10 -0
- package/dist/cli/components/SearchView.d.ts.map +1 -0
- package/dist/cli/components/TaskDetail.d.ts +7 -0
- package/dist/cli/components/TaskDetail.d.ts.map +1 -0
- package/dist/cli/components/TaskForm.d.ts +15 -0
- package/dist/cli/components/TaskForm.d.ts.map +1 -0
- package/dist/cli/components/TaskList.d.ts +8 -0
- package/dist/cli/components/TaskList.d.ts.map +1 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +561 -7192
- package/dist/db/agents.d.ts +13 -0
- package/dist/db/agents.d.ts.map +1 -0
- package/dist/db/comments.d.ts +7 -0
- package/dist/db/comments.d.ts.map +1 -0
- package/dist/db/database.d.ts +12 -0
- package/dist/db/database.d.ts.map +1 -0
- package/dist/db/plans.d.ts +8 -0
- package/dist/db/plans.d.ts.map +1 -0
- package/dist/db/projects.d.ts +11 -0
- package/dist/db/projects.d.ts.map +1 -0
- package/dist/db/sessions.d.ts +8 -0
- package/dist/db/sessions.d.ts.map +1 -0
- package/dist/db/task-lists.d.ts +10 -0
- package/dist/db/task-lists.d.ts.map +1 -0
- package/dist/db/tasks.d.ts +17 -0
- package/dist/db/tasks.d.ts.map +1 -0
- package/dist/index.d.ts +16 -299
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +194 -272
- package/dist/lib/agent-tasks.d.ts +11 -0
- package/dist/lib/agent-tasks.d.ts.map +1 -0
- package/dist/lib/claude-tasks.d.ts +20 -0
- package/dist/lib/claude-tasks.d.ts.map +1 -0
- package/dist/lib/config.d.ts +21 -0
- package/dist/lib/config.d.ts.map +1 -0
- package/dist/lib/search.d.ts +4 -0
- package/dist/lib/search.d.ts.map +1 -0
- package/dist/lib/sync-types.d.ts +16 -0
- package/dist/lib/sync-types.d.ts.map +1 -0
- package/dist/lib/sync-utils.d.ts +12 -0
- package/dist/lib/sync-utils.d.ts.map +1 -0
- package/dist/lib/sync.d.ts +9 -0
- package/dist/lib/sync.d.ts.map +1 -0
- package/dist/mcp/index.d.ts +3 -0
- package/dist/mcp/index.d.ts.map +1 -0
- package/dist/mcp/index.js +343 -67
- package/dist/types/index.d.ts +275 -0
- package/dist/types/index.d.ts.map +1 -0
- package/package.json +3 -6
package/dist/index.js
CHANGED
|
@@ -166,74 +166,34 @@ var MIGRATIONS = [
|
|
|
166
166
|
INSERT OR IGNORE INTO _migrations (id) VALUES (4);
|
|
167
167
|
`,
|
|
168
168
|
`
|
|
169
|
-
CREATE TABLE IF NOT EXISTS
|
|
169
|
+
CREATE TABLE IF NOT EXISTS agents (
|
|
170
170
|
id TEXT PRIMARY KEY,
|
|
171
|
-
name TEXT NOT NULL,
|
|
172
|
-
|
|
173
|
-
|
|
171
|
+
name TEXT NOT NULL UNIQUE,
|
|
172
|
+
description TEXT,
|
|
173
|
+
metadata TEXT DEFAULT '{}',
|
|
174
174
|
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
175
|
-
|
|
176
|
-
expires_at TEXT
|
|
177
|
-
);
|
|
178
|
-
CREATE INDEX IF NOT EXISTS idx_api_keys_hash ON api_keys(key_hash);
|
|
179
|
-
INSERT OR IGNORE INTO _migrations (id) VALUES (5);
|
|
180
|
-
`,
|
|
181
|
-
`
|
|
182
|
-
CREATE TABLE IF NOT EXISTS audit_log (
|
|
183
|
-
id TEXT PRIMARY KEY,
|
|
184
|
-
entity_type TEXT NOT NULL CHECK(entity_type IN ('task', 'plan', 'project', 'api_key', 'comment')),
|
|
185
|
-
entity_id TEXT NOT NULL,
|
|
186
|
-
action TEXT NOT NULL CHECK(action IN ('create', 'update', 'delete', 'start', 'complete', 'lock', 'unlock')),
|
|
187
|
-
actor TEXT,
|
|
188
|
-
changes TEXT,
|
|
189
|
-
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
175
|
+
last_seen_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
190
176
|
);
|
|
191
|
-
CREATE INDEX IF NOT EXISTS
|
|
192
|
-
CREATE INDEX IF NOT EXISTS idx_audit_created ON audit_log(created_at);
|
|
177
|
+
CREATE INDEX IF NOT EXISTS idx_agents_name ON agents(name);
|
|
193
178
|
|
|
194
|
-
CREATE TABLE IF NOT EXISTS
|
|
179
|
+
CREATE TABLE IF NOT EXISTS task_lists (
|
|
195
180
|
id TEXT PRIMARY KEY,
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
);
|
|
202
|
-
|
|
203
|
-
CREATE TABLE IF NOT EXISTS rate_limits (
|
|
204
|
-
key TEXT PRIMARY KEY,
|
|
205
|
-
count INTEGER NOT NULL DEFAULT 0,
|
|
206
|
-
window_start TEXT NOT NULL DEFAULT (datetime('now'))
|
|
207
|
-
);
|
|
208
|
-
|
|
209
|
-
INSERT OR IGNORE INTO _migrations (id) VALUES (6);
|
|
210
|
-
`,
|
|
211
|
-
`
|
|
212
|
-
CREATE TABLE IF NOT EXISTS billing_customers (
|
|
213
|
-
id TEXT PRIMARY KEY,
|
|
214
|
-
stripe_customer_id TEXT UNIQUE,
|
|
215
|
-
email TEXT,
|
|
216
|
-
name TEXT,
|
|
217
|
-
plan TEXT NOT NULL DEFAULT 'free' CHECK(plan IN ('free', 'pro', 'team', 'enterprise')),
|
|
218
|
-
stripe_subscription_id TEXT,
|
|
219
|
-
subscription_status TEXT DEFAULT 'active' CHECK(subscription_status IN ('active', 'past_due', 'canceled', 'trialing', 'incomplete')),
|
|
220
|
-
current_period_end TEXT,
|
|
181
|
+
project_id TEXT REFERENCES projects(id) ON DELETE SET NULL,
|
|
182
|
+
slug TEXT NOT NULL,
|
|
183
|
+
name TEXT NOT NULL,
|
|
184
|
+
description TEXT,
|
|
185
|
+
metadata TEXT DEFAULT '{}',
|
|
221
186
|
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
222
|
-
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
187
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
188
|
+
UNIQUE(project_id, slug)
|
|
223
189
|
);
|
|
224
|
-
CREATE INDEX IF NOT EXISTS
|
|
190
|
+
CREATE INDEX IF NOT EXISTS idx_task_lists_project ON task_lists(project_id);
|
|
191
|
+
CREATE INDEX IF NOT EXISTS idx_task_lists_slug ON task_lists(slug);
|
|
225
192
|
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
customer_id TEXT REFERENCES billing_customers(id),
|
|
229
|
-
metric TEXT NOT NULL,
|
|
230
|
-
count INTEGER NOT NULL DEFAULT 0,
|
|
231
|
-
period TEXT NOT NULL,
|
|
232
|
-
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
233
|
-
);
|
|
234
|
-
CREATE INDEX IF NOT EXISTS idx_usage_customer ON usage_records(customer_id, period);
|
|
193
|
+
ALTER TABLE tasks ADD COLUMN task_list_id TEXT REFERENCES task_lists(id) ON DELETE SET NULL;
|
|
194
|
+
CREATE INDEX IF NOT EXISTS idx_tasks_task_list ON tasks(task_list_id);
|
|
235
195
|
|
|
236
|
-
INSERT OR IGNORE INTO _migrations (id) VALUES (
|
|
196
|
+
INSERT OR IGNORE INTO _migrations (id) VALUES (5);
|
|
237
197
|
`
|
|
238
198
|
];
|
|
239
199
|
var _db = null;
|
|
@@ -403,6 +363,24 @@ class LockError extends Error {
|
|
|
403
363
|
}
|
|
404
364
|
}
|
|
405
365
|
|
|
366
|
+
class AgentNotFoundError extends Error {
|
|
367
|
+
agentId;
|
|
368
|
+
constructor(agentId) {
|
|
369
|
+
super(`Agent not found: ${agentId}`);
|
|
370
|
+
this.agentId = agentId;
|
|
371
|
+
this.name = "AgentNotFoundError";
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
class TaskListNotFoundError extends Error {
|
|
376
|
+
taskListId;
|
|
377
|
+
constructor(taskListId) {
|
|
378
|
+
super(`Task list not found: ${taskListId}`);
|
|
379
|
+
this.taskListId = taskListId;
|
|
380
|
+
this.name = "TaskListNotFoundError";
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
|
|
406
384
|
class DependencyCycleError extends Error {
|
|
407
385
|
taskId;
|
|
408
386
|
dependsOn;
|
|
@@ -442,12 +420,13 @@ function createTask(input, db) {
|
|
|
442
420
|
const id = uuid();
|
|
443
421
|
const timestamp = now();
|
|
444
422
|
const tags = input.tags || [];
|
|
445
|
-
d.run(`INSERT INTO tasks (id, project_id, parent_id, plan_id, title, description, status, priority, agent_id, assigned_to, session_id, working_dir, tags, metadata, version, created_at, updated_at)
|
|
446
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 1, ?, ?)`, [
|
|
423
|
+
d.run(`INSERT INTO tasks (id, project_id, parent_id, plan_id, task_list_id, title, description, status, priority, agent_id, assigned_to, session_id, working_dir, tags, metadata, version, created_at, updated_at)
|
|
424
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 1, ?, ?)`, [
|
|
447
425
|
id,
|
|
448
426
|
input.project_id || null,
|
|
449
427
|
input.parent_id || null,
|
|
450
428
|
input.plan_id || null,
|
|
429
|
+
input.task_list_id || null,
|
|
451
430
|
input.title,
|
|
452
431
|
input.description || null,
|
|
453
432
|
input.status || "pending",
|
|
@@ -555,6 +534,10 @@ function listTasks(filter = {}, db) {
|
|
|
555
534
|
conditions.push("plan_id = ?");
|
|
556
535
|
params.push(filter.plan_id);
|
|
557
536
|
}
|
|
537
|
+
if (filter.task_list_id) {
|
|
538
|
+
conditions.push("task_list_id = ?");
|
|
539
|
+
params.push(filter.task_list_id);
|
|
540
|
+
}
|
|
558
541
|
const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
559
542
|
const limitVal = filter.limit || 100;
|
|
560
543
|
const offsetVal = filter.offset || 0;
|
|
@@ -610,6 +593,10 @@ function updateTask(id, input, db) {
|
|
|
610
593
|
sets.push("plan_id = ?");
|
|
611
594
|
params.push(input.plan_id);
|
|
612
595
|
}
|
|
596
|
+
if (input.task_list_id !== undefined) {
|
|
597
|
+
sets.push("task_list_id = ?");
|
|
598
|
+
params.push(input.task_list_id);
|
|
599
|
+
}
|
|
613
600
|
params.push(id, input.version);
|
|
614
601
|
const result = d.run(`UPDATE tasks SET ${sets.join(", ")} WHERE id = ? AND version = ?`, params);
|
|
615
602
|
if (result.changes === 0) {
|
|
@@ -887,6 +874,129 @@ function deleteComment(id, db) {
|
|
|
887
874
|
const result = d.run("DELETE FROM task_comments WHERE id = ?", [id]);
|
|
888
875
|
return result.changes > 0;
|
|
889
876
|
}
|
|
877
|
+
// src/db/agents.ts
|
|
878
|
+
function shortUuid() {
|
|
879
|
+
return crypto.randomUUID().slice(0, 8);
|
|
880
|
+
}
|
|
881
|
+
function rowToAgent(row) {
|
|
882
|
+
return {
|
|
883
|
+
...row,
|
|
884
|
+
metadata: JSON.parse(row.metadata || "{}")
|
|
885
|
+
};
|
|
886
|
+
}
|
|
887
|
+
function registerAgent(input, db) {
|
|
888
|
+
const d = db || getDatabase();
|
|
889
|
+
const existing = getAgentByName(input.name, d);
|
|
890
|
+
if (existing) {
|
|
891
|
+
d.run("UPDATE agents SET last_seen_at = ? WHERE id = ?", [now(), existing.id]);
|
|
892
|
+
return getAgent(existing.id, d);
|
|
893
|
+
}
|
|
894
|
+
const id = shortUuid();
|
|
895
|
+
const timestamp = now();
|
|
896
|
+
d.run(`INSERT INTO agents (id, name, description, metadata, created_at, last_seen_at)
|
|
897
|
+
VALUES (?, ?, ?, ?, ?, ?)`, [id, input.name, input.description || null, JSON.stringify(input.metadata || {}), timestamp, timestamp]);
|
|
898
|
+
return getAgent(id, d);
|
|
899
|
+
}
|
|
900
|
+
function getAgent(id, db) {
|
|
901
|
+
const d = db || getDatabase();
|
|
902
|
+
const row = d.query("SELECT * FROM agents WHERE id = ?").get(id);
|
|
903
|
+
return row ? rowToAgent(row) : null;
|
|
904
|
+
}
|
|
905
|
+
function getAgentByName(name, db) {
|
|
906
|
+
const d = db || getDatabase();
|
|
907
|
+
const row = d.query("SELECT * FROM agents WHERE name = ?").get(name);
|
|
908
|
+
return row ? rowToAgent(row) : null;
|
|
909
|
+
}
|
|
910
|
+
function listAgents(db) {
|
|
911
|
+
const d = db || getDatabase();
|
|
912
|
+
return d.query("SELECT * FROM agents ORDER BY name").all().map(rowToAgent);
|
|
913
|
+
}
|
|
914
|
+
function updateAgentActivity(id, db) {
|
|
915
|
+
const d = db || getDatabase();
|
|
916
|
+
d.run("UPDATE agents SET last_seen_at = ? WHERE id = ?", [now(), id]);
|
|
917
|
+
}
|
|
918
|
+
function deleteAgent(id, db) {
|
|
919
|
+
const d = db || getDatabase();
|
|
920
|
+
return d.run("DELETE FROM agents WHERE id = ?", [id]).changes > 0;
|
|
921
|
+
}
|
|
922
|
+
// src/db/task-lists.ts
|
|
923
|
+
function rowToTaskList(row) {
|
|
924
|
+
return {
|
|
925
|
+
...row,
|
|
926
|
+
metadata: JSON.parse(row.metadata || "{}")
|
|
927
|
+
};
|
|
928
|
+
}
|
|
929
|
+
function createTaskList(input, db) {
|
|
930
|
+
const d = db || getDatabase();
|
|
931
|
+
const id = uuid();
|
|
932
|
+
const timestamp = now();
|
|
933
|
+
const slug = input.slug || slugify(input.name);
|
|
934
|
+
if (!input.project_id) {
|
|
935
|
+
const existing = d.query("SELECT id FROM task_lists WHERE project_id IS NULL AND slug = ?").get(slug);
|
|
936
|
+
if (existing) {
|
|
937
|
+
throw new Error(`Standalone task list with slug "${slug}" already exists`);
|
|
938
|
+
}
|
|
939
|
+
}
|
|
940
|
+
d.run(`INSERT INTO task_lists (id, project_id, slug, name, description, metadata, created_at, updated_at)
|
|
941
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`, [id, input.project_id || null, slug, input.name, input.description || null, JSON.stringify(input.metadata || {}), timestamp, timestamp]);
|
|
942
|
+
return getTaskList(id, d);
|
|
943
|
+
}
|
|
944
|
+
function getTaskList(id, db) {
|
|
945
|
+
const d = db || getDatabase();
|
|
946
|
+
const row = d.query("SELECT * FROM task_lists WHERE id = ?").get(id);
|
|
947
|
+
return row ? rowToTaskList(row) : null;
|
|
948
|
+
}
|
|
949
|
+
function getTaskListBySlug(slug, projectId, db) {
|
|
950
|
+
const d = db || getDatabase();
|
|
951
|
+
let row;
|
|
952
|
+
if (projectId) {
|
|
953
|
+
row = d.query("SELECT * FROM task_lists WHERE slug = ? AND project_id = ?").get(slug, projectId);
|
|
954
|
+
} else {
|
|
955
|
+
row = d.query("SELECT * FROM task_lists WHERE slug = ? AND project_id IS NULL").get(slug);
|
|
956
|
+
}
|
|
957
|
+
return row ? rowToTaskList(row) : null;
|
|
958
|
+
}
|
|
959
|
+
function listTaskLists(projectId, db) {
|
|
960
|
+
const d = db || getDatabase();
|
|
961
|
+
if (projectId) {
|
|
962
|
+
return d.query("SELECT * FROM task_lists WHERE project_id = ? ORDER BY name").all(projectId).map(rowToTaskList);
|
|
963
|
+
}
|
|
964
|
+
return d.query("SELECT * FROM task_lists ORDER BY name").all().map(rowToTaskList);
|
|
965
|
+
}
|
|
966
|
+
function updateTaskList(id, input, db) {
|
|
967
|
+
const d = db || getDatabase();
|
|
968
|
+
const existing = getTaskList(id, d);
|
|
969
|
+
if (!existing)
|
|
970
|
+
throw new TaskListNotFoundError(id);
|
|
971
|
+
const sets = ["updated_at = ?"];
|
|
972
|
+
const params = [now()];
|
|
973
|
+
if (input.name !== undefined) {
|
|
974
|
+
sets.push("name = ?");
|
|
975
|
+
params.push(input.name);
|
|
976
|
+
}
|
|
977
|
+
if (input.description !== undefined) {
|
|
978
|
+
sets.push("description = ?");
|
|
979
|
+
params.push(input.description);
|
|
980
|
+
}
|
|
981
|
+
if (input.metadata !== undefined) {
|
|
982
|
+
sets.push("metadata = ?");
|
|
983
|
+
params.push(JSON.stringify(input.metadata));
|
|
984
|
+
}
|
|
985
|
+
params.push(id);
|
|
986
|
+
d.run(`UPDATE task_lists SET ${sets.join(", ")} WHERE id = ?`, params);
|
|
987
|
+
return getTaskList(id, d);
|
|
988
|
+
}
|
|
989
|
+
function deleteTaskList(id, db) {
|
|
990
|
+
const d = db || getDatabase();
|
|
991
|
+
return d.run("DELETE FROM task_lists WHERE id = ?", [id]).changes > 0;
|
|
992
|
+
}
|
|
993
|
+
function ensureTaskList(name, slug, projectId, db) {
|
|
994
|
+
const d = db || getDatabase();
|
|
995
|
+
const existing = getTaskListBySlug(slug, projectId, d);
|
|
996
|
+
if (existing)
|
|
997
|
+
return existing;
|
|
998
|
+
return createTaskList({ name, slug, project_id: projectId }, d);
|
|
999
|
+
}
|
|
890
1000
|
// src/db/sessions.ts
|
|
891
1001
|
function rowToSession(row) {
|
|
892
1002
|
return {
|
|
@@ -931,56 +1041,6 @@ function deleteSession(id, db) {
|
|
|
931
1041
|
const result = d.run("DELETE FROM sessions WHERE id = ?", [id]);
|
|
932
1042
|
return result.changes > 0;
|
|
933
1043
|
}
|
|
934
|
-
// src/db/api-keys.ts
|
|
935
|
-
function generateApiKey() {
|
|
936
|
-
const bytes = new Uint8Array(32);
|
|
937
|
-
crypto.getRandomValues(bytes);
|
|
938
|
-
return "td_" + Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
939
|
-
}
|
|
940
|
-
async function hashKey(key) {
|
|
941
|
-
const encoder = new TextEncoder;
|
|
942
|
-
const data = encoder.encode(key);
|
|
943
|
-
const hash = await crypto.subtle.digest("SHA-256", data);
|
|
944
|
-
return Array.from(new Uint8Array(hash)).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
945
|
-
}
|
|
946
|
-
async function createApiKey(input, db) {
|
|
947
|
-
const d = db || getDatabase();
|
|
948
|
-
const id = uuid();
|
|
949
|
-
const timestamp = now();
|
|
950
|
-
const key = generateApiKey();
|
|
951
|
-
const keyHash = await hashKey(key);
|
|
952
|
-
const keyPrefix = key.slice(0, 10) + "...";
|
|
953
|
-
d.run(`INSERT INTO api_keys (id, name, key_hash, key_prefix, created_at, expires_at)
|
|
954
|
-
VALUES (?, ?, ?, ?, ?, ?)`, [id, input.name, keyHash, keyPrefix, timestamp, input.expires_at || null]);
|
|
955
|
-
const row = d.query("SELECT id, name, key_prefix, created_at, last_used_at, expires_at FROM api_keys WHERE id = ?").get(id);
|
|
956
|
-
return { ...row, key };
|
|
957
|
-
}
|
|
958
|
-
function listApiKeys(db) {
|
|
959
|
-
const d = db || getDatabase();
|
|
960
|
-
return d.query("SELECT id, name, key_prefix, created_at, last_used_at, expires_at FROM api_keys ORDER BY created_at DESC").all();
|
|
961
|
-
}
|
|
962
|
-
function deleteApiKey(id, db) {
|
|
963
|
-
const d = db || getDatabase();
|
|
964
|
-
const result = d.run("DELETE FROM api_keys WHERE id = ?", [id]);
|
|
965
|
-
return result.changes > 0;
|
|
966
|
-
}
|
|
967
|
-
async function validateApiKey(key, db) {
|
|
968
|
-
const d = db || getDatabase();
|
|
969
|
-
const keyHash = await hashKey(key);
|
|
970
|
-
const row = d.query("SELECT id, name, key_prefix, created_at, last_used_at, expires_at FROM api_keys WHERE key_hash = ?").get(keyHash);
|
|
971
|
-
if (!row)
|
|
972
|
-
return null;
|
|
973
|
-
if (row.expires_at && new Date(row.expires_at) < new Date) {
|
|
974
|
-
return null;
|
|
975
|
-
}
|
|
976
|
-
d.run("UPDATE api_keys SET last_used_at = ? WHERE id = ?", [now(), row.id]);
|
|
977
|
-
return row;
|
|
978
|
-
}
|
|
979
|
-
function hasAnyApiKeys(db) {
|
|
980
|
-
const d = db || getDatabase();
|
|
981
|
-
const row = d.query("SELECT COUNT(*) as count FROM api_keys").get();
|
|
982
|
-
return (row?.count ?? 0) > 0;
|
|
983
|
-
}
|
|
984
1044
|
// src/lib/search.ts
|
|
985
1045
|
function rowToTask2(row) {
|
|
986
1046
|
return {
|
|
@@ -991,7 +1051,7 @@ function rowToTask2(row) {
|
|
|
991
1051
|
priority: row.priority
|
|
992
1052
|
};
|
|
993
1053
|
}
|
|
994
|
-
function searchTasks(query, projectId, db) {
|
|
1054
|
+
function searchTasks(query, projectId, taskListId, db) {
|
|
995
1055
|
const d = db || getDatabase();
|
|
996
1056
|
clearExpiredLocks(d);
|
|
997
1057
|
const pattern = `%${query}%`;
|
|
@@ -1001,6 +1061,10 @@ function searchTasks(query, projectId, db) {
|
|
|
1001
1061
|
sql += " AND project_id = ?";
|
|
1002
1062
|
params.push(projectId);
|
|
1003
1063
|
}
|
|
1064
|
+
if (taskListId) {
|
|
1065
|
+
sql += " AND task_list_id = ?";
|
|
1066
|
+
params.push(taskListId);
|
|
1067
|
+
}
|
|
1004
1068
|
sql += ` ORDER BY
|
|
1005
1069
|
CASE priority WHEN 'critical' THEN 0 WHEN 'high' THEN 1 WHEN 'medium' THEN 2 WHEN 'low' THEN 3 END,
|
|
1006
1070
|
created_at DESC`;
|
|
@@ -1619,155 +1683,15 @@ function syncWithAgents(agents, taskListIdByAgent, projectId, direction = "both"
|
|
|
1619
1683
|
}
|
|
1620
1684
|
return { pushed, pulled, errors };
|
|
1621
1685
|
}
|
|
1622
|
-
// src/db/audit.ts
|
|
1623
|
-
function rowToEntry(row) {
|
|
1624
|
-
return {
|
|
1625
|
-
...row,
|
|
1626
|
-
changes: row.changes ? JSON.parse(row.changes) : null
|
|
1627
|
-
};
|
|
1628
|
-
}
|
|
1629
|
-
function logAudit(entityType, entityId, action, actor, changes, db) {
|
|
1630
|
-
const d = db || getDatabase();
|
|
1631
|
-
d.run(`INSERT INTO audit_log (id, entity_type, entity_id, action, actor, changes, created_at)
|
|
1632
|
-
VALUES (?, ?, ?, ?, ?, ?, ?)`, [uuid(), entityType, entityId, action, actor || null, changes ? JSON.stringify(changes) : null, now()]);
|
|
1633
|
-
}
|
|
1634
|
-
function getAuditLog(entityType, entityId, limit = 50, offset = 0, db) {
|
|
1635
|
-
const d = db || getDatabase();
|
|
1636
|
-
const conditions = [];
|
|
1637
|
-
const params = [];
|
|
1638
|
-
if (entityType) {
|
|
1639
|
-
conditions.push("entity_type = ?");
|
|
1640
|
-
params.push(entityType);
|
|
1641
|
-
}
|
|
1642
|
-
if (entityId) {
|
|
1643
|
-
conditions.push("entity_id = ?");
|
|
1644
|
-
params.push(entityId);
|
|
1645
|
-
}
|
|
1646
|
-
const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
1647
|
-
params.push(limit, offset);
|
|
1648
|
-
const rows = d.query(`SELECT * FROM audit_log ${where} ORDER BY created_at DESC LIMIT ? OFFSET ?`).all(...params);
|
|
1649
|
-
return rows.map(rowToEntry);
|
|
1650
|
-
}
|
|
1651
|
-
// src/db/webhooks.ts
|
|
1652
|
-
function rowToWebhook(row) {
|
|
1653
|
-
return {
|
|
1654
|
-
...row,
|
|
1655
|
-
events: JSON.parse(row.events),
|
|
1656
|
-
active: row.active === 1
|
|
1657
|
-
};
|
|
1658
|
-
}
|
|
1659
|
-
function createWebhook(input, db) {
|
|
1660
|
-
const d = db || getDatabase();
|
|
1661
|
-
const id = uuid();
|
|
1662
|
-
d.run(`INSERT INTO webhooks (id, url, events, secret, created_at)
|
|
1663
|
-
VALUES (?, ?, ?, ?, ?)`, [id, input.url, JSON.stringify(input.events || []), input.secret || null, now()]);
|
|
1664
|
-
return getWebhook(id, d);
|
|
1665
|
-
}
|
|
1666
|
-
function getWebhook(id, db) {
|
|
1667
|
-
const d = db || getDatabase();
|
|
1668
|
-
const row = d.query("SELECT * FROM webhooks WHERE id = ?").get(id);
|
|
1669
|
-
return row ? rowToWebhook(row) : null;
|
|
1670
|
-
}
|
|
1671
|
-
function listWebhooks(db) {
|
|
1672
|
-
const d = db || getDatabase();
|
|
1673
|
-
return d.query("SELECT * FROM webhooks ORDER BY created_at DESC").all().map(rowToWebhook);
|
|
1674
|
-
}
|
|
1675
|
-
function deleteWebhook(id, db) {
|
|
1676
|
-
const d = db || getDatabase();
|
|
1677
|
-
return d.run("DELETE FROM webhooks WHERE id = ?", [id]).changes > 0;
|
|
1678
|
-
}
|
|
1679
|
-
async function dispatchWebhooks(event, payload, db) {
|
|
1680
|
-
const d = db || getDatabase();
|
|
1681
|
-
const rows = d.query("SELECT * FROM webhooks WHERE active = 1").all();
|
|
1682
|
-
const webhooks = rows.map(rowToWebhook).filter((w) => w.events.length === 0 || w.events.includes(event));
|
|
1683
|
-
for (const webhook of webhooks) {
|
|
1684
|
-
try {
|
|
1685
|
-
const headers = { "Content-Type": "application/json" };
|
|
1686
|
-
if (webhook.secret) {
|
|
1687
|
-
const encoder = new TextEncoder;
|
|
1688
|
-
const key = await crypto.subtle.importKey("raw", encoder.encode(webhook.secret), { name: "HMAC", hash: "SHA-256" }, false, ["sign"]);
|
|
1689
|
-
const body = JSON.stringify({ event, data: payload, timestamp: now() });
|
|
1690
|
-
const sig = await crypto.subtle.sign("HMAC", key, encoder.encode(body));
|
|
1691
|
-
headers["X-Webhook-Signature"] = Array.from(new Uint8Array(sig)).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
1692
|
-
fetch(webhook.url, {
|
|
1693
|
-
method: "POST",
|
|
1694
|
-
headers,
|
|
1695
|
-
body
|
|
1696
|
-
}).catch(() => {});
|
|
1697
|
-
} else {
|
|
1698
|
-
fetch(webhook.url, {
|
|
1699
|
-
method: "POST",
|
|
1700
|
-
headers,
|
|
1701
|
-
body: JSON.stringify({ event, data: payload, timestamp: now() })
|
|
1702
|
-
}).catch(() => {});
|
|
1703
|
-
}
|
|
1704
|
-
} catch {}
|
|
1705
|
-
}
|
|
1706
|
-
}
|
|
1707
|
-
// src/db/billing.ts
|
|
1708
|
-
function getOrCreateCustomer(db) {
|
|
1709
|
-
const d = db || getDatabase();
|
|
1710
|
-
const existing = d.query("SELECT * FROM billing_customers LIMIT 1").get();
|
|
1711
|
-
if (existing)
|
|
1712
|
-
return existing;
|
|
1713
|
-
const id = uuid();
|
|
1714
|
-
const timestamp = now();
|
|
1715
|
-
d.run(`INSERT INTO billing_customers (id, plan, created_at, updated_at) VALUES (?, 'free', ?, ?)`, [id, timestamp, timestamp]);
|
|
1716
|
-
return d.query("SELECT * FROM billing_customers WHERE id = ?").get(id);
|
|
1717
|
-
}
|
|
1718
|
-
function updateCustomer(id, input, db) {
|
|
1719
|
-
const d = db || getDatabase();
|
|
1720
|
-
const sets = ["updated_at = ?"];
|
|
1721
|
-
const params = [now()];
|
|
1722
|
-
for (const [key, value] of Object.entries(input)) {
|
|
1723
|
-
if (value !== undefined) {
|
|
1724
|
-
sets.push(`${key} = ?`);
|
|
1725
|
-
params.push(value);
|
|
1726
|
-
}
|
|
1727
|
-
}
|
|
1728
|
-
params.push(id);
|
|
1729
|
-
d.run(`UPDATE billing_customers SET ${sets.join(", ")} WHERE id = ?`, params);
|
|
1730
|
-
return d.query("SELECT * FROM billing_customers WHERE id = ?").get(id);
|
|
1731
|
-
}
|
|
1732
|
-
function getCustomerByStripeId(stripeCustomerId, db) {
|
|
1733
|
-
const d = db || getDatabase();
|
|
1734
|
-
return d.query("SELECT * FROM billing_customers WHERE stripe_customer_id = ?").get(stripeCustomerId);
|
|
1735
|
-
}
|
|
1736
|
-
function getUsage(customerId, period, db) {
|
|
1737
|
-
const d = db || getDatabase();
|
|
1738
|
-
return d.query("SELECT * FROM usage_records WHERE customer_id = ? AND period = ?").all(customerId, period);
|
|
1739
|
-
}
|
|
1740
|
-
function trackUsage(customerId, metric, count = 1, db) {
|
|
1741
|
-
const d = db || getDatabase();
|
|
1742
|
-
const period = new Date().toISOString().slice(0, 7);
|
|
1743
|
-
const existing = d.query("SELECT * FROM usage_records WHERE customer_id = ? AND metric = ? AND period = ?").get(customerId, metric, period);
|
|
1744
|
-
if (existing) {
|
|
1745
|
-
d.run("UPDATE usage_records SET count = count + ? WHERE id = ?", [count, existing.id]);
|
|
1746
|
-
} else {
|
|
1747
|
-
d.run("INSERT INTO usage_records (id, customer_id, metric, count, period, created_at) VALUES (?, ?, ?, ?, ?, ?)", [uuid(), customerId, metric, count, period, now()]);
|
|
1748
|
-
}
|
|
1749
|
-
}
|
|
1750
|
-
var PLAN_LIMITS = {
|
|
1751
|
-
free: { tasks: 100, projects: 3, plans: 5, api_keys: 1, webhooks: 0 },
|
|
1752
|
-
pro: { tasks: 1e4, projects: 50, plans: 100, api_keys: 10, webhooks: 10 },
|
|
1753
|
-
team: { tasks: 1e5, projects: 500, plans: 1000, api_keys: 50, webhooks: 50 },
|
|
1754
|
-
enterprise: { tasks: -1, projects: -1, plans: -1, api_keys: -1, webhooks: -1 }
|
|
1755
|
-
};
|
|
1756
|
-
var PLAN_PRICES = {
|
|
1757
|
-
free: { monthly: 0, yearly: 0 },
|
|
1758
|
-
pro: { monthly: 9, yearly: 84 },
|
|
1759
|
-
team: { monthly: 29, yearly: 276 },
|
|
1760
|
-
enterprise: { monthly: 99, yearly: 948 }
|
|
1761
|
-
};
|
|
1762
1686
|
export {
|
|
1763
|
-
|
|
1687
|
+
uuid,
|
|
1688
|
+
updateTaskList,
|
|
1764
1689
|
updateTask,
|
|
1765
1690
|
updateSessionActivity,
|
|
1766
1691
|
updateProject,
|
|
1767
1692
|
updatePlan,
|
|
1768
|
-
|
|
1693
|
+
updateAgentActivity,
|
|
1769
1694
|
unlockTask,
|
|
1770
|
-
trackUsage,
|
|
1771
1695
|
syncWithAgents,
|
|
1772
1696
|
syncWithAgent,
|
|
1773
1697
|
startTask,
|
|
@@ -1776,20 +1700,20 @@ export {
|
|
|
1776
1700
|
resolvePartialId,
|
|
1777
1701
|
resetDatabase,
|
|
1778
1702
|
removeDependency,
|
|
1779
|
-
|
|
1703
|
+
registerAgent,
|
|
1704
|
+
now,
|
|
1780
1705
|
lockTask,
|
|
1781
1706
|
loadConfig,
|
|
1782
|
-
listWebhooks,
|
|
1783
1707
|
listTasks,
|
|
1708
|
+
listTaskLists,
|
|
1784
1709
|
listSessions,
|
|
1785
1710
|
listProjects,
|
|
1786
1711
|
listPlans,
|
|
1787
1712
|
listComments,
|
|
1788
|
-
|
|
1789
|
-
hasAnyApiKeys,
|
|
1790
|
-
getWebhook,
|
|
1791
|
-
getUsage,
|
|
1713
|
+
listAgents,
|
|
1792
1714
|
getTaskWithRelations,
|
|
1715
|
+
getTaskListBySlug,
|
|
1716
|
+
getTaskList,
|
|
1793
1717
|
getTaskDependents,
|
|
1794
1718
|
getTaskDependencies,
|
|
1795
1719
|
getTask,
|
|
@@ -1797,40 +1721,38 @@ export {
|
|
|
1797
1721
|
getProjectByPath,
|
|
1798
1722
|
getProject,
|
|
1799
1723
|
getPlan,
|
|
1800
|
-
getOrCreateCustomer,
|
|
1801
1724
|
getDatabase,
|
|
1802
|
-
getCustomerByStripeId,
|
|
1803
1725
|
getComment,
|
|
1804
|
-
|
|
1726
|
+
getAgentByName,
|
|
1727
|
+
getAgent,
|
|
1728
|
+
ensureTaskList,
|
|
1805
1729
|
ensureProject,
|
|
1806
|
-
|
|
1807
|
-
deleteWebhook,
|
|
1730
|
+
deleteTaskList,
|
|
1808
1731
|
deleteTask,
|
|
1809
1732
|
deleteSession,
|
|
1810
1733
|
deleteProject,
|
|
1811
1734
|
deletePlan,
|
|
1812
1735
|
deleteComment,
|
|
1813
|
-
|
|
1736
|
+
deleteAgent,
|
|
1814
1737
|
defaultSyncAgents,
|
|
1815
|
-
|
|
1738
|
+
createTaskList,
|
|
1816
1739
|
createTask,
|
|
1817
1740
|
createSession,
|
|
1818
1741
|
createProject,
|
|
1819
1742
|
createPlan,
|
|
1820
|
-
createApiKey,
|
|
1821
1743
|
completeTask,
|
|
1822
1744
|
closeDatabase,
|
|
1823
1745
|
addDependency,
|
|
1824
1746
|
addComment,
|
|
1825
1747
|
VersionConflictError,
|
|
1826
1748
|
TaskNotFoundError,
|
|
1749
|
+
TaskListNotFoundError,
|
|
1827
1750
|
TASK_STATUSES,
|
|
1828
1751
|
TASK_PRIORITIES,
|
|
1829
1752
|
ProjectNotFoundError,
|
|
1830
1753
|
PlanNotFoundError,
|
|
1831
1754
|
PLAN_STATUSES,
|
|
1832
|
-
PLAN_PRICES,
|
|
1833
|
-
PLAN_LIMITS,
|
|
1834
1755
|
LockError,
|
|
1835
|
-
DependencyCycleError
|
|
1756
|
+
DependencyCycleError,
|
|
1757
|
+
AgentNotFoundError
|
|
1836
1758
|
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { SyncPrefer, SyncResult } from "./sync-types.js";
|
|
2
|
+
export declare function pushToAgentTaskList(agent: string, taskListId: string, projectId?: string, options?: {
|
|
3
|
+
prefer?: SyncPrefer;
|
|
4
|
+
}): SyncResult;
|
|
5
|
+
export declare function pullFromAgentTaskList(agent: string, taskListId: string, projectId?: string, options?: {
|
|
6
|
+
prefer?: SyncPrefer;
|
|
7
|
+
}): SyncResult;
|
|
8
|
+
export declare function syncAgentTaskList(agent: string, taskListId: string, projectId?: string, options?: {
|
|
9
|
+
prefer?: SyncPrefer;
|
|
10
|
+
}): SyncResult;
|
|
11
|
+
//# sourceMappingURL=agent-tasks.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent-tasks.d.ts","sourceRoot":"","sources":["../../src/lib/agent-tasks.ts"],"names":[],"mappings":"AAiBA,OAAO,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAwD9D,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,MAAM,EACb,UAAU,EAAE,MAAM,EAClB,SAAS,CAAC,EAAE,MAAM,EAClB,OAAO,GAAE;IAAE,MAAM,CAAC,EAAE,UAAU,CAAA;CAAO,GACpC,UAAU,CAuFZ;AAED,wBAAgB,qBAAqB,CACnC,KAAK,EAAE,MAAM,EACb,UAAU,EAAE,MAAM,EAClB,SAAS,CAAC,EAAE,MAAM,EAClB,OAAO,GAAE;IAAE,MAAM,CAAC,EAAE,UAAU,CAAA;CAAO,GACpC,UAAU,CAoFZ;AAED,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,MAAM,EACb,UAAU,EAAE,MAAM,EAClB,SAAS,CAAC,EAAE,MAAM,EAClB,OAAO,GAAE;IAAE,MAAM,CAAC,EAAE,UAAU,CAAA;CAAO,GACpC,UAAU,CAQZ"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { SyncPrefer, SyncResult } from "./sync-types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Push all SQLite tasks to a Claude Code task list directory.
|
|
4
|
+
*/
|
|
5
|
+
export declare function pushToClaudeTaskList(taskListId: string, projectId?: string, options?: {
|
|
6
|
+
prefer?: SyncPrefer;
|
|
7
|
+
}): SyncResult;
|
|
8
|
+
/**
|
|
9
|
+
* Pull tasks from a Claude Code task list into SQLite.
|
|
10
|
+
*/
|
|
11
|
+
export declare function pullFromClaudeTaskList(taskListId: string, projectId?: string, options?: {
|
|
12
|
+
prefer?: SyncPrefer;
|
|
13
|
+
}): SyncResult;
|
|
14
|
+
/**
|
|
15
|
+
* Bidirectional sync: pull first, then push.
|
|
16
|
+
*/
|
|
17
|
+
export declare function syncClaudeTaskList(taskListId: string, projectId?: string, options?: {
|
|
18
|
+
prefer?: SyncPrefer;
|
|
19
|
+
}): SyncResult;
|
|
20
|
+
//# sourceMappingURL=claude-tasks.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claude-tasks.d.ts","sourceRoot":"","sources":["../../src/lib/claude-tasks.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAwF9D;;GAEG;AACH,wBAAgB,oBAAoB,CAClC,UAAU,EAAE,MAAM,EAClB,SAAS,CAAC,EAAE,MAAM,EAClB,OAAO,GAAE;IAAE,MAAM,CAAC,EAAE,UAAU,CAAA;CAAO,GACpC,UAAU,CA4GZ;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CACpC,UAAU,EAAE,MAAM,EAClB,SAAS,CAAC,EAAE,MAAM,EAClB,OAAO,GAAE;IAAE,MAAM,CAAC,EAAE,UAAU,CAAA;CAAO,GACpC,UAAU,CAwFZ;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAChC,UAAU,EAAE,MAAM,EAClB,SAAS,CAAC,EAAE,MAAM,EAClB,OAAO,GAAE;IAAE,MAAM,CAAC,EAAE,UAAU,CAAA;CAAO,GACpC,UAAU,CAQZ"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export interface AgentConfig {
|
|
2
|
+
task_list_id?: string;
|
|
3
|
+
tasks_dir?: string;
|
|
4
|
+
}
|
|
5
|
+
export interface TaskPrefixConfig {
|
|
6
|
+
prefix: string;
|
|
7
|
+
start_from?: number;
|
|
8
|
+
}
|
|
9
|
+
export interface TodosConfig {
|
|
10
|
+
sync_agents?: string[] | string;
|
|
11
|
+
task_list_id?: string;
|
|
12
|
+
agent_tasks_dir?: string;
|
|
13
|
+
agents?: Record<string, AgentConfig>;
|
|
14
|
+
task_prefix?: TaskPrefixConfig;
|
|
15
|
+
}
|
|
16
|
+
export declare function loadConfig(): TodosConfig;
|
|
17
|
+
export declare function getSyncAgentsFromConfig(): string[] | null;
|
|
18
|
+
export declare function getAgentTaskListId(agent: string): string | null;
|
|
19
|
+
export declare function getAgentTasksDir(agent: string): string | null;
|
|
20
|
+
export declare function getTaskPrefixConfig(): TaskPrefixConfig | null;
|
|
21
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/lib/config.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,WAAW;IAC1B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,WAAW;IAC1B,WAAW,CAAC,EAAE,MAAM,EAAE,GAAG,MAAM,CAAC;IAChC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;IACrC,WAAW,CAAC,EAAE,gBAAgB,CAAC;CAChC;AASD,wBAAgB,UAAU,IAAI,WAAW,CAYxC;AAED,wBAAgB,uBAAuB,IAAI,MAAM,EAAE,GAAG,IAAI,CAKzD;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAM/D;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAM7D;AAED,wBAAgB,mBAAmB,IAAI,gBAAgB,GAAG,IAAI,CAG7D"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../../src/lib/search.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAoB,MAAM,YAAY,CAAC;AAC7D,OAAO,KAAK,EAAE,IAAI,EAAW,MAAM,mBAAmB,CAAC;AAavD,wBAAgB,WAAW,CACzB,KAAK,EAAE,MAAM,EACb,SAAS,CAAC,EAAE,MAAM,EAClB,UAAU,CAAC,EAAE,MAAM,EACnB,EAAE,CAAC,EAAE,QAAQ,GACZ,IAAI,EAAE,CAwBR"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export interface SyncResult {
|
|
2
|
+
pushed: number;
|
|
3
|
+
pulled: number;
|
|
4
|
+
errors: string[];
|
|
5
|
+
}
|
|
6
|
+
export type SyncPrefer = "local" | "remote";
|
|
7
|
+
export interface SyncConflict {
|
|
8
|
+
agent: string;
|
|
9
|
+
direction: "push" | "pull";
|
|
10
|
+
prefer: SyncPrefer;
|
|
11
|
+
local_updated_at?: string;
|
|
12
|
+
remote_updated_at?: string;
|
|
13
|
+
detected_at: string;
|
|
14
|
+
notes?: string;
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=sync-types.d.ts.map
|