botholomew 0.12.5 → 0.14.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 +91 -68
- package/package.json +2 -2
- package/src/chat/agent.ts +59 -86
- package/src/chat/session.ts +29 -25
- package/src/commands/capabilities.ts +1 -1
- package/src/commands/context.ts +178 -926
- package/src/commands/db.ts +9 -13
- package/src/commands/init.ts +4 -1
- package/src/commands/nuke.ts +57 -90
- package/src/commands/schedule.ts +103 -124
- package/src/commands/skill.ts +2 -2
- package/src/commands/task.ts +86 -95
- package/src/commands/thread.ts +107 -112
- package/src/commands/worker.ts +88 -88
- package/src/constants.ts +93 -16
- package/src/context/capabilities.ts +10 -10
- package/src/context/fetcher.ts +9 -10
- package/src/context/reindex.ts +189 -0
- package/src/context/store.ts +803 -0
- package/src/db/doctor.ts +1 -8
- package/src/db/embeddings.ts +227 -175
- package/src/db/sql/19-disk_backed_index.sql +36 -0
- package/src/db/sql/20-drop_db_tables_for_files.sql +19 -0
- package/src/fs/atomic.ts +217 -0
- package/src/fs/compat.ts +86 -0
- package/src/fs/sandbox.ts +293 -0
- package/src/init/index.ts +69 -52
- package/src/init/templates.ts +1 -1
- package/src/mcpx/client.ts +1 -1
- package/src/schedules/schema.ts +19 -0
- package/src/schedules/store.ts +296 -0
- package/src/skills/commands.ts +1 -3
- package/src/tasks/schema.ts +47 -0
- package/src/tasks/store.ts +486 -0
- package/src/threads/store.ts +559 -0
- package/src/tools/capabilities/refresh.ts +42 -21
- package/src/tools/context/pipe.ts +15 -71
- package/src/tools/context/update-beliefs.ts +3 -3
- package/src/tools/context/update-goals.ts +3 -3
- package/src/tools/dir/create.ts +26 -23
- package/src/tools/dir/size.ts +46 -17
- package/src/tools/dir/tree.ts +74 -279
- package/src/tools/file/copy.ts +50 -24
- package/src/tools/file/count-lines.ts +34 -10
- package/src/tools/file/delete.ts +53 -23
- package/src/tools/file/edit.ts +39 -14
- package/src/tools/file/exists.ts +12 -26
- package/src/tools/file/info.ts +27 -85
- package/src/tools/file/move.ts +39 -24
- package/src/tools/file/read.ts +32 -80
- package/src/tools/file/write.ts +14 -91
- package/src/tools/registry.ts +8 -7
- package/src/tools/schedule/create.ts +2 -2
- package/src/tools/schedule/list.ts +7 -3
- package/src/tools/search/fuse.ts +12 -33
- package/src/tools/search/index.ts +36 -43
- package/src/tools/search/regexp.ts +29 -17
- package/src/tools/search/semantic.ts +137 -51
- package/src/tools/skill/delete.ts +1 -1
- package/src/tools/skill/list.ts +1 -1
- package/src/tools/skill/write.ts +1 -1
- package/src/tools/task/create.ts +41 -16
- package/src/tools/task/delete.ts +3 -3
- package/src/tools/task/list.ts +6 -3
- package/src/tools/task/update.ts +31 -9
- package/src/tools/task/view.ts +6 -6
- package/src/tools/thread/list.ts +2 -2
- package/src/tools/thread/search.ts +208 -0
- package/src/tools/thread/view.ts +50 -5
- package/src/tools/tool.ts +5 -0
- package/src/tools/util/sleep.ts +77 -0
- package/src/tools/worker/spawn.ts +28 -14
- package/src/tui/App.tsx +12 -19
- package/src/tui/components/ContextPanel.tsx +83 -316
- package/src/tui/components/SchedulePanel.tsx +34 -48
- package/src/tui/components/SleepProgress.tsx +70 -0
- package/src/tui/components/StatusBar.tsx +15 -15
- package/src/tui/components/TaskPanel.tsx +34 -38
- package/src/tui/components/ThreadPanel.tsx +29 -38
- package/src/tui/components/ToolCall.tsx +10 -0
- package/src/tui/components/WorkerPanel.tsx +21 -19
- package/src/tui/markdown.ts +2 -8
- package/src/utils/title.ts +5 -7
- package/src/utils/v7-date.ts +47 -0
- package/src/worker/heartbeat.ts +46 -24
- package/src/worker/index.ts +13 -15
- package/src/worker/llm.ts +30 -37
- package/src/worker/prompt.ts +19 -41
- package/src/worker/schedules.ts +48 -69
- package/src/worker/spawn.ts +11 -11
- package/src/worker/tick.ts +39 -43
- package/src/workers/store.ts +247 -0
- package/src/commands/tools.ts +0 -367
- package/src/context/describer.ts +0 -140
- package/src/context/drives.ts +0 -110
- package/src/context/ingest.ts +0 -162
- package/src/context/refresh.ts +0 -183
- package/src/db/context.ts +0 -637
- package/src/db/daemon-state.ts +0 -6
- package/src/db/reembed.ts +0 -113
- package/src/db/schedules.ts +0 -213
- package/src/db/tasks.ts +0 -347
- package/src/db/threads.ts +0 -276
- package/src/db/workers.ts +0 -212
- package/src/tools/context/list-drives.ts +0 -36
- package/src/tools/context/refresh.ts +0 -165
- package/src/tools/context/search.ts +0 -54
package/src/commands/task.ts
CHANGED
|
@@ -1,16 +1,15 @@
|
|
|
1
1
|
import ansis from "ansis";
|
|
2
2
|
import type { Command } from "commander";
|
|
3
|
-
import type { Task } from "../
|
|
3
|
+
import type { Task } from "../tasks/schema.ts";
|
|
4
4
|
import {
|
|
5
5
|
createTask,
|
|
6
6
|
deleteTask,
|
|
7
7
|
getTask,
|
|
8
8
|
listTasks,
|
|
9
|
-
resetTask,
|
|
10
9
|
updateTask,
|
|
11
|
-
|
|
10
|
+
updateTaskStatus,
|
|
11
|
+
} from "../tasks/store.ts";
|
|
12
12
|
import { logger } from "../utils/logger.ts";
|
|
13
|
-
import { withDb } from "./with-db.ts";
|
|
14
13
|
|
|
15
14
|
export function registerTaskCommand(program: Command) {
|
|
16
15
|
const task = program.command("task").description("Manage tasks");
|
|
@@ -22,61 +21,58 @@ export function registerTaskCommand(program: Command) {
|
|
|
22
21
|
.option("-p, --priority <priority>", "filter by priority")
|
|
23
22
|
.option("-l, --limit <n>", "max number of tasks", Number.parseInt)
|
|
24
23
|
.option("-o, --offset <n>", "skip first N tasks", Number.parseInt)
|
|
25
|
-
.action((opts) =>
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
);
|
|
24
|
+
.action(async (opts) => {
|
|
25
|
+
const dir = program.opts().dir;
|
|
26
|
+
const tasks = await listTasks(dir, {
|
|
27
|
+
status: opts.status,
|
|
28
|
+
priority: opts.priority,
|
|
29
|
+
limit: opts.limit,
|
|
30
|
+
offset: opts.offset,
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
if (tasks.length === 0) {
|
|
34
|
+
logger.dim("No tasks found.");
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const header = `${ansis.bold("ID".padEnd(36))} ${ansis.bold("Status".padEnd(11))} ${ansis.bold("Priority".padEnd(6))} ${ansis.bold("Created".padEnd(19))} ${ansis.bold("Updated".padEnd(19))} ${ansis.bold("Name")}`;
|
|
39
|
+
console.log(header);
|
|
40
|
+
console.log("-".repeat(120));
|
|
41
|
+
|
|
42
|
+
for (const t of tasks) {
|
|
43
|
+
printTask(t);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
console.log(`\n${ansis.dim(`${tasks.length} task(s)`)}`);
|
|
47
|
+
});
|
|
50
48
|
|
|
51
49
|
task
|
|
52
50
|
.command("add <name>")
|
|
53
51
|
.description("Create a new task")
|
|
54
52
|
.option("--description <text>", "task description", "")
|
|
55
53
|
.option("-p, --priority <priority>", "low, medium, or high", "medium")
|
|
56
|
-
.action((name, opts) =>
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
);
|
|
54
|
+
.action(async (name, opts) => {
|
|
55
|
+
const dir = program.opts().dir;
|
|
56
|
+
const t = await createTask(dir, {
|
|
57
|
+
name,
|
|
58
|
+
description: opts.description,
|
|
59
|
+
priority: opts.priority,
|
|
60
|
+
});
|
|
61
|
+
logger.success(`Created task: ${t.name} (${t.id})`);
|
|
62
|
+
});
|
|
66
63
|
|
|
67
64
|
task
|
|
68
65
|
.command("view <id>")
|
|
69
66
|
.description("View task details")
|
|
70
|
-
.action((id) =>
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
);
|
|
67
|
+
.action(async (id) => {
|
|
68
|
+
const dir = program.opts().dir;
|
|
69
|
+
const t = await getTask(dir, id);
|
|
70
|
+
if (!t) {
|
|
71
|
+
logger.error(`Task not found: ${id}`);
|
|
72
|
+
process.exit(1);
|
|
73
|
+
}
|
|
74
|
+
printTaskDetail(t);
|
|
75
|
+
});
|
|
80
76
|
|
|
81
77
|
task
|
|
82
78
|
.command("update <id>")
|
|
@@ -85,55 +81,53 @@ export function registerTaskCommand(program: Command) {
|
|
|
85
81
|
.option("--description <text>", "new description")
|
|
86
82
|
.option("-p, --priority <priority>", "low, medium, or high")
|
|
87
83
|
.option("-s, --status <status>", "new status")
|
|
88
|
-
.action((id, opts) =>
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
process.exit(1);
|
|
101
|
-
}
|
|
102
|
-
printTaskDetail(t);
|
|
103
|
-
} catch (err) {
|
|
104
|
-
logger.error(String(err));
|
|
84
|
+
.action(async (id, opts) => {
|
|
85
|
+
const dir = program.opts().dir;
|
|
86
|
+
const updates: Parameters<typeof updateTask>[2] = {};
|
|
87
|
+
if (opts.name) updates.name = opts.name;
|
|
88
|
+
if (opts.description) updates.description = opts.description;
|
|
89
|
+
if (opts.priority) updates.priority = opts.priority;
|
|
90
|
+
if (opts.status) updates.status = opts.status;
|
|
91
|
+
|
|
92
|
+
try {
|
|
93
|
+
const t = await updateTask(dir, id, updates);
|
|
94
|
+
if (!t) {
|
|
95
|
+
logger.error(`Task not found: ${id}`);
|
|
105
96
|
process.exit(1);
|
|
106
97
|
}
|
|
107
|
-
|
|
108
|
-
|
|
98
|
+
printTaskDetail(t);
|
|
99
|
+
} catch (err) {
|
|
100
|
+
logger.error(String(err));
|
|
101
|
+
process.exit(1);
|
|
102
|
+
}
|
|
103
|
+
});
|
|
109
104
|
|
|
110
105
|
task
|
|
111
106
|
.command("delete <id>")
|
|
112
107
|
.description("Delete a task")
|
|
113
|
-
.action((id) =>
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
);
|
|
108
|
+
.action(async (id) => {
|
|
109
|
+
const dir = program.opts().dir;
|
|
110
|
+
const deleted = await deleteTask(dir, id);
|
|
111
|
+
if (!deleted) {
|
|
112
|
+
logger.error(`Task not found: ${id}`);
|
|
113
|
+
process.exit(1);
|
|
114
|
+
}
|
|
115
|
+
logger.success(`Deleted task: ${id}`);
|
|
116
|
+
});
|
|
123
117
|
|
|
124
118
|
task
|
|
125
119
|
.command("reset <id>")
|
|
126
120
|
.description("Reset a stuck task back to pending")
|
|
127
|
-
.action((id) =>
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
})
|
|
136
|
-
);
|
|
121
|
+
.action(async (id) => {
|
|
122
|
+
const dir = program.opts().dir;
|
|
123
|
+
const t = await getTask(dir, id);
|
|
124
|
+
if (!t) {
|
|
125
|
+
logger.error(`Task not found: ${id}`);
|
|
126
|
+
process.exit(1);
|
|
127
|
+
}
|
|
128
|
+
await updateTaskStatus(dir, id, "pending", null, null);
|
|
129
|
+
logger.success(`Reset task: ${t.name} (${t.id})`);
|
|
130
|
+
});
|
|
137
131
|
}
|
|
138
132
|
|
|
139
133
|
function statusColor(status: Task["status"]): string {
|
|
@@ -162,11 +156,8 @@ function priorityColor(priority: Task["priority"]): string {
|
|
|
162
156
|
}
|
|
163
157
|
}
|
|
164
158
|
|
|
165
|
-
function formatTime(
|
|
166
|
-
return
|
|
167
|
-
.toISOString()
|
|
168
|
-
.replace("T", " ")
|
|
169
|
-
.replace(/\.\d{3}Z$/, "");
|
|
159
|
+
function formatTime(iso: string): string {
|
|
160
|
+
return iso.replace("T", " ").replace(/\.\d{3}Z$/, "");
|
|
170
161
|
}
|
|
171
162
|
|
|
172
163
|
function padColored(colored: string, raw: string, width: number): string {
|
|
@@ -196,6 +187,6 @@ function printTaskDetail(t: Task) {
|
|
|
196
187
|
if (t.claimed_by) console.log(` Claimed by: ${t.claimed_by}`);
|
|
197
188
|
if (t.blocked_by.length > 0)
|
|
198
189
|
console.log(` Blocked by: ${t.blocked_by.join(", ")}`);
|
|
199
|
-
console.log(` Created: ${t.created_at
|
|
200
|
-
console.log(` Updated: ${t.updated_at
|
|
190
|
+
console.log(` Created: ${t.created_at}`);
|
|
191
|
+
console.log(` Updated: ${t.updated_at}`);
|
|
201
192
|
}
|
package/src/commands/thread.ts
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
import ansis from "ansis";
|
|
2
2
|
import type { Command } from "commander";
|
|
3
|
-
import type { Interaction, Thread } from "../db/threads.ts";
|
|
4
3
|
import {
|
|
5
4
|
deleteThread,
|
|
6
5
|
getActiveThread,
|
|
7
6
|
getInteractionsAfter,
|
|
8
7
|
getThread,
|
|
8
|
+
type Interaction,
|
|
9
9
|
isThreadEnded,
|
|
10
10
|
listThreads,
|
|
11
|
-
|
|
11
|
+
type Thread,
|
|
12
|
+
} from "../threads/store.ts";
|
|
12
13
|
import { logger } from "../utils/logger.ts";
|
|
13
|
-
import { withDb } from "./with-db.ts";
|
|
14
14
|
|
|
15
15
|
export function registerThreadCommand(program: Command) {
|
|
16
16
|
const thread = program.command("thread").description("Manage chat threads");
|
|
@@ -21,24 +21,23 @@ export function registerThreadCommand(program: Command) {
|
|
|
21
21
|
.option("-t, --type <type>", "filter by type (worker_tick, chat_session)")
|
|
22
22
|
.option("-l, --limit <n>", "max number of threads", Number.parseInt)
|
|
23
23
|
.option("-o, --offset <n>", "skip first N threads", Number.parseInt)
|
|
24
|
-
.action((opts) =>
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
);
|
|
24
|
+
.action(async (opts) => {
|
|
25
|
+
const dir = program.opts().dir;
|
|
26
|
+
const threads = await listThreads(dir, {
|
|
27
|
+
type: opts.type,
|
|
28
|
+
limit: opts.limit,
|
|
29
|
+
offset: opts.offset,
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
if (threads.length === 0) {
|
|
33
|
+
logger.dim("No threads found.");
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
for (const t of threads) {
|
|
38
|
+
printThread(t);
|
|
39
|
+
}
|
|
40
|
+
});
|
|
42
41
|
|
|
43
42
|
thread
|
|
44
43
|
.command("view <id>")
|
|
@@ -47,111 +46,107 @@ export function registerThreadCommand(program: Command) {
|
|
|
47
46
|
"--only <roles>",
|
|
48
47
|
"show only these roles (comma-separated: user,assistant,tool,system)",
|
|
49
48
|
)
|
|
50
|
-
.action((id, opts) =>
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
);
|
|
49
|
+
.action(async (id, opts) => {
|
|
50
|
+
const dir = program.opts().dir;
|
|
51
|
+
const result = await getThread(dir, id);
|
|
52
|
+
if (!result) {
|
|
53
|
+
logger.error(`Thread not found: ${id}`);
|
|
54
|
+
process.exit(1);
|
|
55
|
+
}
|
|
56
|
+
const interactions = opts.only
|
|
57
|
+
? result.interactions.filter((i) =>
|
|
58
|
+
(opts.only as string).split(",").includes(i.role),
|
|
59
|
+
)
|
|
60
|
+
: result.interactions;
|
|
61
|
+
printThreadDetail(result.thread, interactions);
|
|
62
|
+
});
|
|
65
63
|
|
|
66
64
|
thread
|
|
67
65
|
.command("delete <id>")
|
|
68
66
|
.description("Delete a thread and its interactions")
|
|
69
|
-
.action((id) =>
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
);
|
|
67
|
+
.action(async (id) => {
|
|
68
|
+
const dir = program.opts().dir;
|
|
69
|
+
const deleted = await deleteThread(dir, id);
|
|
70
|
+
if (!deleted) {
|
|
71
|
+
logger.error(`Thread not found: ${id}`);
|
|
72
|
+
process.exit(1);
|
|
73
|
+
}
|
|
74
|
+
logger.success(`Deleted thread: ${id}`);
|
|
75
|
+
});
|
|
79
76
|
|
|
80
77
|
thread
|
|
81
78
|
.command("follow [id]")
|
|
82
79
|
.description("Follow a thread live (like tail -f)")
|
|
83
80
|
.option("-i, --interval <ms>", "poll interval in ms", parseInt)
|
|
84
|
-
.action((id, opts) =>
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
process.exit(1);
|
|
94
|
-
}
|
|
95
|
-
resolvedId = active.id;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
const result = await getThread(conn, resolvedId);
|
|
99
|
-
if (!result) {
|
|
100
|
-
logger.error(`Thread not found: ${resolvedId}`);
|
|
81
|
+
.action(async (id, opts) => {
|
|
82
|
+
const dir = program.opts().dir;
|
|
83
|
+
let resolvedId: string;
|
|
84
|
+
if (id) {
|
|
85
|
+
resolvedId = id;
|
|
86
|
+
} else {
|
|
87
|
+
const active = await getActiveThread(dir);
|
|
88
|
+
if (!active) {
|
|
89
|
+
logger.error("No active thread found.");
|
|
101
90
|
process.exit(1);
|
|
102
91
|
}
|
|
92
|
+
resolvedId = active.id;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const result = await getThread(dir, resolvedId);
|
|
96
|
+
if (!result) {
|
|
97
|
+
logger.error(`Thread not found: ${resolvedId}`);
|
|
98
|
+
process.exit(1);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
printThreadDetail(result.thread, result.interactions);
|
|
102
|
+
|
|
103
|
+
if (result.thread.ended_at) {
|
|
104
|
+
logger.dim("Thread already ended.");
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
let lastSequence =
|
|
109
|
+
result.interactions.length > 0
|
|
110
|
+
? (result.interactions[result.interactions.length - 1]?.sequence ?? 0)
|
|
111
|
+
: 0;
|
|
112
|
+
|
|
113
|
+
const pollMs = opts.interval ?? 500;
|
|
114
|
+
logger.info(
|
|
115
|
+
`Following thread ${ansis.dim(resolvedId)}... (Ctrl+C to stop)`,
|
|
116
|
+
);
|
|
117
|
+
|
|
118
|
+
const interval = setInterval(async () => {
|
|
119
|
+
try {
|
|
120
|
+
const newInteractions = await getInteractionsAfter(
|
|
121
|
+
dir,
|
|
122
|
+
resolvedId,
|
|
123
|
+
lastSequence,
|
|
124
|
+
);
|
|
125
|
+
for (const i of newInteractions) {
|
|
126
|
+
printInteraction(i);
|
|
127
|
+
lastSequence = i.sequence;
|
|
128
|
+
}
|
|
103
129
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
130
|
+
const ended = await isThreadEnded(dir, resolvedId);
|
|
131
|
+
if (ended) {
|
|
132
|
+
logger.dim("Thread ended.");
|
|
133
|
+
clearInterval(interval);
|
|
134
|
+
process.exit(0);
|
|
135
|
+
}
|
|
136
|
+
} catch {
|
|
137
|
+
// Transient FS errors — retry next tick
|
|
109
138
|
}
|
|
139
|
+
}, pollMs);
|
|
110
140
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
);
|
|
121
|
-
|
|
122
|
-
const interval = setInterval(async () => {
|
|
123
|
-
try {
|
|
124
|
-
const newInteractions = await getInteractionsAfter(
|
|
125
|
-
conn,
|
|
126
|
-
resolvedId,
|
|
127
|
-
lastSequence,
|
|
128
|
-
);
|
|
129
|
-
for (const i of newInteractions) {
|
|
130
|
-
printInteraction(i);
|
|
131
|
-
lastSequence = i.sequence;
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
const ended = await isThreadEnded(conn, resolvedId);
|
|
135
|
-
if (ended) {
|
|
136
|
-
logger.dim("Thread ended.");
|
|
137
|
-
clearInterval(interval);
|
|
138
|
-
process.exit(0);
|
|
139
|
-
}
|
|
140
|
-
} catch {
|
|
141
|
-
// Transient DB errors (e.g. SQLITE_BUSY) — retry next tick
|
|
142
|
-
}
|
|
143
|
-
}, pollMs);
|
|
144
|
-
|
|
145
|
-
process.on("SIGINT", () => {
|
|
146
|
-
clearInterval(interval);
|
|
147
|
-
console.log();
|
|
148
|
-
process.exit(0);
|
|
149
|
-
});
|
|
150
|
-
|
|
151
|
-
// Keep the process alive
|
|
152
|
-
await new Promise(() => {});
|
|
153
|
-
}),
|
|
154
|
-
);
|
|
141
|
+
process.on("SIGINT", () => {
|
|
142
|
+
clearInterval(interval);
|
|
143
|
+
console.log();
|
|
144
|
+
process.exit(0);
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
// Keep the process alive
|
|
148
|
+
await new Promise(() => {});
|
|
149
|
+
});
|
|
155
150
|
}
|
|
156
151
|
|
|
157
152
|
function typeColor(type: Thread["type"]): string {
|