@hasna/conversations 0.1.16 → 0.1.18
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/bin/hook.js +14 -0
- package/bin/index.js +195 -28
- package/bin/mcp.js +85 -7
- package/dist/index.d.ts +2 -1
- package/dist/index.js +128 -7
- package/dist/lib/reactions.d.ts +10 -0
- package/dist/lib/reactions.test.d.ts +1 -0
- package/dist/types.d.ts +18 -0
- package/package.json +1 -1
package/bin/hook.js
CHANGED
|
@@ -120,6 +120,17 @@ function getDb() {
|
|
|
120
120
|
metadata TEXT
|
|
121
121
|
)
|
|
122
122
|
`);
|
|
123
|
+
db.exec(`
|
|
124
|
+
CREATE TABLE IF NOT EXISTS reactions (
|
|
125
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
126
|
+
message_id INTEGER NOT NULL REFERENCES messages(id) ON DELETE CASCADE,
|
|
127
|
+
agent TEXT NOT NULL,
|
|
128
|
+
emoji TEXT NOT NULL,
|
|
129
|
+
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%f', 'now')),
|
|
130
|
+
UNIQUE(message_id, agent, emoji)
|
|
131
|
+
)
|
|
132
|
+
`);
|
|
133
|
+
db.exec("CREATE INDEX IF NOT EXISTS idx_reactions_message ON reactions(message_id)");
|
|
123
134
|
const existingTables = db.prepare("SELECT name FROM sqlite_master WHERE type='table'").all();
|
|
124
135
|
const tableNames = existingTables.map((t) => t.name);
|
|
125
136
|
if (tableNames.includes("channels") && tableNames.includes("spaces")) {
|
|
@@ -177,6 +188,9 @@ function getDb() {
|
|
|
177
188
|
db.exec("ALTER TABLE messages ADD COLUMN blocking INTEGER NOT NULL DEFAULT 0");
|
|
178
189
|
db.exec("CREATE INDEX IF NOT EXISTS idx_messages_blocking ON messages(blocking)");
|
|
179
190
|
}
|
|
191
|
+
if (!colNames2.includes("attachments")) {
|
|
192
|
+
db.exec("ALTER TABLE messages ADD COLUMN attachments TEXT");
|
|
193
|
+
}
|
|
180
194
|
return db;
|
|
181
195
|
}
|
|
182
196
|
function closeDb() {
|
package/bin/index.js
CHANGED
|
@@ -1974,6 +1974,17 @@ function getDb() {
|
|
|
1974
1974
|
metadata TEXT
|
|
1975
1975
|
)
|
|
1976
1976
|
`);
|
|
1977
|
+
db.exec(`
|
|
1978
|
+
CREATE TABLE IF NOT EXISTS reactions (
|
|
1979
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
1980
|
+
message_id INTEGER NOT NULL REFERENCES messages(id) ON DELETE CASCADE,
|
|
1981
|
+
agent TEXT NOT NULL,
|
|
1982
|
+
emoji TEXT NOT NULL,
|
|
1983
|
+
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%f', 'now')),
|
|
1984
|
+
UNIQUE(message_id, agent, emoji)
|
|
1985
|
+
)
|
|
1986
|
+
`);
|
|
1987
|
+
db.exec("CREATE INDEX IF NOT EXISTS idx_reactions_message ON reactions(message_id)");
|
|
1977
1988
|
const existingTables = db.prepare("SELECT name FROM sqlite_master WHERE type='table'").all();
|
|
1978
1989
|
const tableNames = existingTables.map((t) => t.name);
|
|
1979
1990
|
if (tableNames.includes("channels") && tableNames.includes("spaces")) {
|
|
@@ -2031,6 +2042,9 @@ function getDb() {
|
|
|
2031
2042
|
db.exec("ALTER TABLE messages ADD COLUMN blocking INTEGER NOT NULL DEFAULT 0");
|
|
2032
2043
|
db.exec("CREATE INDEX IF NOT EXISTS idx_messages_blocking ON messages(blocking)");
|
|
2033
2044
|
}
|
|
2045
|
+
if (!colNames2.includes("attachments")) {
|
|
2046
|
+
db.exec("ALTER TABLE messages ADD COLUMN attachments TEXT");
|
|
2047
|
+
}
|
|
2034
2048
|
return db;
|
|
2035
2049
|
}
|
|
2036
2050
|
function closeDb() {
|
|
@@ -2044,6 +2058,9 @@ var init_db = () => {};
|
|
|
2044
2058
|
|
|
2045
2059
|
// src/lib/messages.ts
|
|
2046
2060
|
import { randomUUID } from "crypto";
|
|
2061
|
+
import { mkdirSync as mkdirSync2, copyFileSync, statSync } from "fs";
|
|
2062
|
+
import { join as join2 } from "path";
|
|
2063
|
+
import { homedir as homedir2 } from "os";
|
|
2047
2064
|
function parseMessage(row) {
|
|
2048
2065
|
let metadata = null;
|
|
2049
2066
|
if (row.metadata) {
|
|
@@ -2053,12 +2070,53 @@ function parseMessage(row) {
|
|
|
2053
2070
|
metadata = null;
|
|
2054
2071
|
}
|
|
2055
2072
|
}
|
|
2073
|
+
let attachments = null;
|
|
2074
|
+
if (row.attachments) {
|
|
2075
|
+
try {
|
|
2076
|
+
attachments = JSON.parse(row.attachments);
|
|
2077
|
+
} catch {
|
|
2078
|
+
attachments = null;
|
|
2079
|
+
}
|
|
2080
|
+
}
|
|
2056
2081
|
return {
|
|
2057
2082
|
...row,
|
|
2058
2083
|
metadata,
|
|
2084
|
+
attachments,
|
|
2059
2085
|
blocking: !!row.blocking
|
|
2060
2086
|
};
|
|
2061
2087
|
}
|
|
2088
|
+
function getAttachmentsDir() {
|
|
2089
|
+
if (process.env.CONVERSATIONS_ATTACHMENTS_DIR)
|
|
2090
|
+
return process.env.CONVERSATIONS_ATTACHMENTS_DIR;
|
|
2091
|
+
return join2(homedir2(), ".conversations", "attachments");
|
|
2092
|
+
}
|
|
2093
|
+
function guessMimeType(name) {
|
|
2094
|
+
const ext = name.split(".").pop()?.toLowerCase();
|
|
2095
|
+
const mimeMap = {
|
|
2096
|
+
txt: "text/plain",
|
|
2097
|
+
md: "text/markdown",
|
|
2098
|
+
json: "application/json",
|
|
2099
|
+
js: "text/javascript",
|
|
2100
|
+
ts: "text/typescript",
|
|
2101
|
+
py: "text/x-python",
|
|
2102
|
+
html: "text/html",
|
|
2103
|
+
css: "text/css",
|
|
2104
|
+
xml: "application/xml",
|
|
2105
|
+
png: "image/png",
|
|
2106
|
+
jpg: "image/jpeg",
|
|
2107
|
+
jpeg: "image/jpeg",
|
|
2108
|
+
gif: "image/gif",
|
|
2109
|
+
svg: "image/svg+xml",
|
|
2110
|
+
webp: "image/webp",
|
|
2111
|
+
pdf: "application/pdf",
|
|
2112
|
+
zip: "application/zip",
|
|
2113
|
+
gz: "application/gzip",
|
|
2114
|
+
csv: "text/csv",
|
|
2115
|
+
yaml: "text/yaml",
|
|
2116
|
+
yml: "text/yaml"
|
|
2117
|
+
};
|
|
2118
|
+
return mimeMap[ext || ""] || "application/octet-stream";
|
|
2119
|
+
}
|
|
2062
2120
|
function sendMessage(opts) {
|
|
2063
2121
|
const db2 = getDb();
|
|
2064
2122
|
const explicitSession = opts.session_id && opts.session_id.trim().length > 0 ? opts.session_id : undefined;
|
|
@@ -2072,7 +2130,27 @@ function sendMessage(opts) {
|
|
|
2072
2130
|
RETURNING *
|
|
2073
2131
|
`);
|
|
2074
2132
|
const row = stmt.get(sessionId, opts.from, opts.to, opts.space || null, opts.content, normalizedPriority, opts.working_dir || null, opts.repository || null, opts.branch || null, metadata, blocking);
|
|
2075
|
-
|
|
2133
|
+
const message = parseMessage(row);
|
|
2134
|
+
if (opts.attachments && opts.attachments.length > 0) {
|
|
2135
|
+
const attachmentsDir = join2(getAttachmentsDir(), String(message.id));
|
|
2136
|
+
mkdirSync2(attachmentsDir, { recursive: true });
|
|
2137
|
+
const attachmentInfos = [];
|
|
2138
|
+
for (const att of opts.attachments) {
|
|
2139
|
+
const destPath = join2(attachmentsDir, att.name);
|
|
2140
|
+
copyFileSync(att.source_path, destPath);
|
|
2141
|
+
const stat = statSync(destPath);
|
|
2142
|
+
attachmentInfos.push({
|
|
2143
|
+
name: att.name,
|
|
2144
|
+
path: destPath,
|
|
2145
|
+
size: stat.size,
|
|
2146
|
+
mime_type: guessMimeType(att.name)
|
|
2147
|
+
});
|
|
2148
|
+
}
|
|
2149
|
+
const attachmentsJson = JSON.stringify(attachmentInfos);
|
|
2150
|
+
db2.prepare("UPDATE messages SET attachments = ? WHERE id = ?").run(attachmentsJson, message.id);
|
|
2151
|
+
message.attachments = attachmentInfos;
|
|
2152
|
+
}
|
|
2153
|
+
return message;
|
|
2076
2154
|
}
|
|
2077
2155
|
function readMessages(opts = {}) {
|
|
2078
2156
|
const db2 = getDb();
|
|
@@ -3012,9 +3090,9 @@ var init_names = __esm(() => {
|
|
|
3012
3090
|
});
|
|
3013
3091
|
|
|
3014
3092
|
// src/lib/identity.ts
|
|
3015
|
-
import { readFileSync, writeFileSync, mkdirSync as
|
|
3016
|
-
import { join as
|
|
3017
|
-
import { homedir as
|
|
3093
|
+
import { readFileSync, writeFileSync, mkdirSync as mkdirSync3 } from "fs";
|
|
3094
|
+
import { join as join3, dirname as dirname2 } from "path";
|
|
3095
|
+
import { homedir as homedir3 } from "os";
|
|
3018
3096
|
function isNameTaken(name) {
|
|
3019
3097
|
try {
|
|
3020
3098
|
const { getDb: getDb2 } = (init_db(), __toCommonJS(exports_db));
|
|
@@ -3045,7 +3123,7 @@ function getAutoName() {
|
|
|
3045
3123
|
}
|
|
3046
3124
|
cachedAutoName = name;
|
|
3047
3125
|
try {
|
|
3048
|
-
|
|
3126
|
+
mkdirSync3(dirname2(AGENT_ID_FILE), { recursive: true });
|
|
3049
3127
|
writeFileSync(AGENT_ID_FILE, name + `
|
|
3050
3128
|
`, "utf-8");
|
|
3051
3129
|
} catch {}
|
|
@@ -3063,7 +3141,7 @@ function resolveIdentity(explicit) {
|
|
|
3063
3141
|
var AGENT_ID_FILE, cachedAutoName = null;
|
|
3064
3142
|
var init_identity = __esm(() => {
|
|
3065
3143
|
init_names();
|
|
3066
|
-
AGENT_ID_FILE =
|
|
3144
|
+
AGENT_ID_FILE = join3(homedir3(), ".conversations", "agent-id");
|
|
3067
3145
|
});
|
|
3068
3146
|
|
|
3069
3147
|
// src/lib/presence.ts
|
|
@@ -3101,6 +3179,11 @@ function heartbeat(agent, status, metadata) {
|
|
|
3101
3179
|
metadata = excluded.metadata
|
|
3102
3180
|
`).run(agent, resolvedStatus, metadataJson);
|
|
3103
3181
|
}
|
|
3182
|
+
function getPresence(agent) {
|
|
3183
|
+
const db2 = getDb();
|
|
3184
|
+
const row = db2.prepare("SELECT * FROM agent_presence WHERE agent = ?").get(agent);
|
|
3185
|
+
return row ? parsePresence(row) : null;
|
|
3186
|
+
}
|
|
3104
3187
|
function listAgents(opts) {
|
|
3105
3188
|
const db2 = getDb();
|
|
3106
3189
|
let query = "SELECT * FROM agent_presence";
|
|
@@ -3231,7 +3314,7 @@ var init_poll = __esm(() => {
|
|
|
3231
3314
|
var require_package = __commonJS((exports, module) => {
|
|
3232
3315
|
module.exports = {
|
|
3233
3316
|
name: "@hasna/conversations",
|
|
3234
|
-
version: "0.1.
|
|
3317
|
+
version: "0.1.18",
|
|
3235
3318
|
description: "Real-time CLI messaging for AI agents",
|
|
3236
3319
|
type: "module",
|
|
3237
3320
|
bin: {
|
|
@@ -32966,7 +33049,7 @@ var exports_serve = {};
|
|
|
32966
33049
|
__export(exports_serve, {
|
|
32967
33050
|
startDashboardServer: () => startDashboardServer
|
|
32968
33051
|
});
|
|
32969
|
-
import { join as
|
|
33052
|
+
import { join as join4, resolve, sep } from "path";
|
|
32970
33053
|
import { existsSync } from "fs";
|
|
32971
33054
|
function securityHeaders(base) {
|
|
32972
33055
|
const headers = new Headers(base);
|
|
@@ -33037,7 +33120,7 @@ function isSameOrigin(req) {
|
|
|
33037
33120
|
function startDashboardServer(port = 0, host) {
|
|
33038
33121
|
const resolvedPort = normalizePort(port, 0);
|
|
33039
33122
|
const resolvedHost = normalizeHost(host ?? process.env.CONVERSATIONS_DASHBOARD_HOST);
|
|
33040
|
-
const dashboardDist =
|
|
33123
|
+
const dashboardDist = join4(import.meta.dir, "../../dashboard/dist");
|
|
33041
33124
|
const hasDist = existsSync(dashboardDist);
|
|
33042
33125
|
const server2 = Bun.serve({
|
|
33043
33126
|
port: resolvedPort,
|
|
@@ -33421,7 +33504,7 @@ function startDashboardServer(port = 0, host) {
|
|
|
33421
33504
|
headers.set("Content-Type", file2.type);
|
|
33422
33505
|
return new Response(file2, { headers });
|
|
33423
33506
|
}
|
|
33424
|
-
file2 = Bun.file(
|
|
33507
|
+
file2 = Bun.file(join4(dashboardDist, "index.html"));
|
|
33425
33508
|
if (await file2.exists()) {
|
|
33426
33509
|
const headers = securityHeaders();
|
|
33427
33510
|
if (file2.type)
|
|
@@ -35235,6 +35318,38 @@ agents.command("rename").description("Rename an agent in the presence list").arg
|
|
|
35235
35318
|
}
|
|
35236
35319
|
closeDb();
|
|
35237
35320
|
});
|
|
35321
|
+
program2.command("whoami").description("Show current agent identity and online status").option("--from <agent>", "Explicit agent identity").action((opts) => {
|
|
35322
|
+
const envValue = process.env.CONVERSATIONS_AGENT_ID?.trim();
|
|
35323
|
+
const agent = resolveIdentity(opts.from);
|
|
35324
|
+
let source;
|
|
35325
|
+
if (opts.from) {
|
|
35326
|
+
source = "explicit (--from flag)";
|
|
35327
|
+
} else if (envValue) {
|
|
35328
|
+
source = "env var (CONVERSATIONS_AGENT_ID)";
|
|
35329
|
+
} else {
|
|
35330
|
+
const { join: join5 } = __require("path");
|
|
35331
|
+
const { homedir: homedir4 } = __require("os");
|
|
35332
|
+
const agentIdFile = join5(homedir4(), ".conversations", "agent-id");
|
|
35333
|
+
source = `auto-generated (${agentIdFile})`;
|
|
35334
|
+
}
|
|
35335
|
+
const presence = getPresence(agent);
|
|
35336
|
+
let onlineStatus;
|
|
35337
|
+
if (presence && presence.online) {
|
|
35338
|
+
const lastSeenMs = new Date(presence.last_seen_at + "Z").getTime();
|
|
35339
|
+
const agoMs = Date.now() - lastSeenMs;
|
|
35340
|
+
const agoSec = Math.floor(agoMs / 1000);
|
|
35341
|
+
const agoStr = agoSec < 60 ? `${agoSec}s ago` : `${Math.floor(agoSec / 60)}m ago`;
|
|
35342
|
+
onlineStatus = chalk2.green(`yes`) + chalk2.dim(` (last seen ${agoStr})`);
|
|
35343
|
+
} else if (presence) {
|
|
35344
|
+
onlineStatus = chalk2.red("no") + chalk2.dim(` (last seen ${presence.last_seen_at})`);
|
|
35345
|
+
} else {
|
|
35346
|
+
onlineStatus = chalk2.red("no") + chalk2.dim(" (no presence record)");
|
|
35347
|
+
}
|
|
35348
|
+
console.log(` ${chalk2.bold("Agent:")} ${chalk2.cyan(agent)}`);
|
|
35349
|
+
console.log(` ${chalk2.bold("Source:")} ${source}`);
|
|
35350
|
+
console.log(` ${chalk2.bold("Online:")} ${onlineStatus}`);
|
|
35351
|
+
closeDb();
|
|
35352
|
+
});
|
|
35238
35353
|
program2.command("blockers").description("Check for unread blocking messages").option("--from <agent>", "Agent to check blockers for").option("--json", "Output as JSON").action((opts) => {
|
|
35239
35354
|
const agent = resolveIdentity(opts.from);
|
|
35240
35355
|
const blockers = getUnreadBlockers(agent);
|
|
@@ -35257,14 +35372,21 @@ Acknowledge with: conversations mark-read ${blockers.map((b) => b.id).join(" ")}
|
|
|
35257
35372
|
}
|
|
35258
35373
|
closeDb();
|
|
35259
35374
|
});
|
|
35260
|
-
program2.command("watch").description("Watch for new messages with desktop notifications").option("--from <agent>", "Your agent identity").option("--space <name>", "Watch a specific space").option("--interval <ms>", "Poll interval in milliseconds", parseInt).action((opts) => {
|
|
35375
|
+
program2.command("watch").description("Watch for new messages with desktop notifications").option("--from <agent>", "Your agent identity").option("--space <name>", "Watch a specific space").option("--all", "Watch DMs and all subscribed spaces").option("--interval <ms>", "Poll interval in milliseconds", parseInt).action((opts) => {
|
|
35261
35376
|
const agent = resolveIdentity(opts.from);
|
|
35262
35377
|
heartbeat(agent);
|
|
35263
35378
|
const interval = Number.isFinite(opts.interval) && opts.interval > 0 ? opts.interval : 1000;
|
|
35264
35379
|
const cols = Math.min(process.stdout.columns || 80, 100);
|
|
35380
|
+
let agentSpaces = [];
|
|
35381
|
+
if (opts.all) {
|
|
35382
|
+
const db2 = getDb();
|
|
35383
|
+
const rows = db2.prepare("SELECT space FROM space_members WHERE agent = ?").all(agent);
|
|
35384
|
+
agentSpaces = rows.map((r) => r.space);
|
|
35385
|
+
}
|
|
35386
|
+
const modeLabel = opts.all ? `DMs + ${agentSpaces.length} space(s)` : opts.space ? `Space: #${opts.space}` : "All DMs";
|
|
35265
35387
|
console.log("");
|
|
35266
35388
|
console.log(chalk2.bold(` Conversations`) + chalk2.dim(` \u2014 watching as ${chalk2.cyan(agent)}`));
|
|
35267
|
-
console.log(chalk2.dim(` ${
|
|
35389
|
+
console.log(chalk2.dim(` ${modeLabel} \xB7 Poll: ${interval}ms \xB7 Ctrl+C to stop`));
|
|
35268
35390
|
console.log(chalk2.dim(" " + "\u2500".repeat(cols - 4)));
|
|
35269
35391
|
console.log("");
|
|
35270
35392
|
const { startPolling: startPolling2 } = (init_poll(), __toCommonJS(exports_poll));
|
|
@@ -35310,8 +35432,8 @@ program2.command("watch").description("Watch for new messages with desktop notif
|
|
|
35310
35432
|
if (process.platform === "darwin") {
|
|
35311
35433
|
try {
|
|
35312
35434
|
const { execSync } = __require("child_process");
|
|
35313
|
-
const t = title.replace(/"/g, "
|
|
35314
|
-
const b = body.replace(/"/g, "
|
|
35435
|
+
const t = title.replace(/['"\\]/g, " ");
|
|
35436
|
+
const b = body.replace(/['"\\]/g, " ").replace(/\n/g, " ").slice(0, 200);
|
|
35315
35437
|
execSync(`osascript -e 'display notification "${b}" with title "${t}"'`, { timeout: 3000 });
|
|
35316
35438
|
} catch {}
|
|
35317
35439
|
}
|
|
@@ -35331,21 +35453,62 @@ program2.command("watch").description("Watch for new messages with desktop notif
|
|
|
35331
35453
|
console.log(chalk2.dim(" " + "\xB7".repeat(Math.min(cols - 8, 60))));
|
|
35332
35454
|
console.log("");
|
|
35333
35455
|
};
|
|
35334
|
-
|
|
35335
|
-
|
|
35336
|
-
|
|
35337
|
-
|
|
35338
|
-
|
|
35339
|
-
|
|
35340
|
-
|
|
35341
|
-
|
|
35456
|
+
if (opts.all) {
|
|
35457
|
+
const dmRecent = readMessages({ to: agent, limit: 20, order: "asc" });
|
|
35458
|
+
const spaceRecent = [];
|
|
35459
|
+
for (const sp of agentSpaces) {
|
|
35460
|
+
spaceRecent.push(...readMessages({ space: sp, limit: 10, order: "asc" }));
|
|
35461
|
+
}
|
|
35462
|
+
const recent = [...dmRecent, ...spaceRecent].sort((a, b) => a.created_at.localeCompare(b.created_at)).slice(-20);
|
|
35463
|
+
if (recent.length > 0) {
|
|
35464
|
+
console.log(chalk2.dim(` \u2500\u2500 Recent messages (${recent.length}) \u2500\u2500
|
|
35465
|
+
`));
|
|
35466
|
+
for (const msg of recent) {
|
|
35342
35467
|
renderMessage(msg);
|
|
35343
|
-
const where = msg.space ? `#${msg.space}` : "DM";
|
|
35344
|
-
const preview = msg.content.replace(/[*#`~_>\-]/g, "").slice(0, 150);
|
|
35345
|
-
desktopNotify(`${msg.from_agent} (${where})`, preview);
|
|
35346
35468
|
}
|
|
35469
|
+
console.log(chalk2.dim(` \u2500\u2500 Live \u2500\u2500
|
|
35470
|
+
`));
|
|
35347
35471
|
}
|
|
35348
|
-
}
|
|
35472
|
+
} else {
|
|
35473
|
+
const recent = readMessages({
|
|
35474
|
+
to: opts.space ? undefined : agent,
|
|
35475
|
+
space: opts.space,
|
|
35476
|
+
limit: 20,
|
|
35477
|
+
order: "asc"
|
|
35478
|
+
});
|
|
35479
|
+
if (recent.length > 0) {
|
|
35480
|
+
console.log(chalk2.dim(` \u2500\u2500 Recent messages (${recent.length}) \u2500\u2500
|
|
35481
|
+
`));
|
|
35482
|
+
for (const msg of recent) {
|
|
35483
|
+
renderMessage(msg);
|
|
35484
|
+
}
|
|
35485
|
+
console.log(chalk2.dim(` \u2500\u2500 Live \u2500\u2500
|
|
35486
|
+
`));
|
|
35487
|
+
}
|
|
35488
|
+
}
|
|
35489
|
+
const onNewMessages = (messages) => {
|
|
35490
|
+
for (const msg of messages) {
|
|
35491
|
+
if (msg.from_agent === agent)
|
|
35492
|
+
continue;
|
|
35493
|
+
renderMessage(msg);
|
|
35494
|
+
const where = msg.space ? `#${msg.space}` : "DM";
|
|
35495
|
+
const preview = msg.content.replace(/[*#`~_>\-]/g, "").slice(0, 150);
|
|
35496
|
+
desktopNotify(`${msg.from_agent} (${where})`, preview);
|
|
35497
|
+
}
|
|
35498
|
+
};
|
|
35499
|
+
if (opts.all) {
|
|
35500
|
+
startPolling2({ to_agent: agent, interval_ms: interval, on_messages: onNewMessages });
|
|
35501
|
+
for (const sp of agentSpaces) {
|
|
35502
|
+
startPolling2({ space: sp, interval_ms: interval, on_messages: onNewMessages });
|
|
35503
|
+
}
|
|
35504
|
+
} else {
|
|
35505
|
+
startPolling2({
|
|
35506
|
+
to_agent: opts.space ? undefined : agent,
|
|
35507
|
+
space: opts.space,
|
|
35508
|
+
interval_ms: interval,
|
|
35509
|
+
on_messages: onNewMessages
|
|
35510
|
+
});
|
|
35511
|
+
}
|
|
35349
35512
|
process.on("SIGINT", () => {
|
|
35350
35513
|
console.log(chalk2.dim(`
|
|
35351
35514
|
Stopped watching.`));
|
|
@@ -35357,10 +35520,14 @@ program2.command("mcp").description("Start MCP server").action(async () => {
|
|
|
35357
35520
|
const { startMcpServer: startMcpServer2 } = await Promise.resolve().then(() => (init_mcp2(), exports_mcp));
|
|
35358
35521
|
await startMcpServer2();
|
|
35359
35522
|
});
|
|
35360
|
-
program2.command("dashboard").description("Start web dashboard").option("--port <port>", "Port to listen on", parseInt).option("--host <host>", "Host to bind (default: 127.0.0.1)").action(async (opts) => {
|
|
35523
|
+
program2.command("dashboard").description("Start web dashboard").option("--port <port>", "Port to listen on", parseInt).option("--host <host>", "Host to bind (default: 127.0.0.1)").option("--open", "Auto-open dashboard in browser").action(async (opts) => {
|
|
35361
35524
|
const { startDashboardServer: startDashboardServer2 } = await Promise.resolve().then(() => (init_serve(), exports_serve));
|
|
35362
35525
|
const port = Number.isFinite(opts.port) && opts.port >= 0 && opts.port <= 65535 ? opts.port : 0;
|
|
35363
|
-
startDashboardServer2(port, opts.host);
|
|
35526
|
+
const server2 = startDashboardServer2(port, opts.host);
|
|
35527
|
+
if (opts.open) {
|
|
35528
|
+
const { exec } = __require("child_process");
|
|
35529
|
+
exec(`open http://localhost:${server2.port}`);
|
|
35530
|
+
}
|
|
35364
35531
|
});
|
|
35365
35532
|
program2.action(() => {
|
|
35366
35533
|
if (!process.stdin.isTTY) {
|
package/bin/mcp.js
CHANGED
|
@@ -6606,6 +6606,17 @@ function getDb() {
|
|
|
6606
6606
|
metadata TEXT
|
|
6607
6607
|
)
|
|
6608
6608
|
`);
|
|
6609
|
+
db.exec(`
|
|
6610
|
+
CREATE TABLE IF NOT EXISTS reactions (
|
|
6611
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
6612
|
+
message_id INTEGER NOT NULL REFERENCES messages(id) ON DELETE CASCADE,
|
|
6613
|
+
agent TEXT NOT NULL,
|
|
6614
|
+
emoji TEXT NOT NULL,
|
|
6615
|
+
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%f', 'now')),
|
|
6616
|
+
UNIQUE(message_id, agent, emoji)
|
|
6617
|
+
)
|
|
6618
|
+
`);
|
|
6619
|
+
db.exec("CREATE INDEX IF NOT EXISTS idx_reactions_message ON reactions(message_id)");
|
|
6609
6620
|
const existingTables = db.prepare("SELECT name FROM sqlite_master WHERE type='table'").all();
|
|
6610
6621
|
const tableNames = existingTables.map((t) => t.name);
|
|
6611
6622
|
if (tableNames.includes("channels") && tableNames.includes("spaces")) {
|
|
@@ -6663,6 +6674,9 @@ function getDb() {
|
|
|
6663
6674
|
db.exec("ALTER TABLE messages ADD COLUMN blocking INTEGER NOT NULL DEFAULT 0");
|
|
6664
6675
|
db.exec("CREATE INDEX IF NOT EXISTS idx_messages_blocking ON messages(blocking)");
|
|
6665
6676
|
}
|
|
6677
|
+
if (!colNames2.includes("attachments")) {
|
|
6678
|
+
db.exec("ALTER TABLE messages ADD COLUMN attachments TEXT");
|
|
6679
|
+
}
|
|
6666
6680
|
return db;
|
|
6667
6681
|
}
|
|
6668
6682
|
function closeDb() {
|
|
@@ -28494,6 +28508,9 @@ class StdioServerTransport {
|
|
|
28494
28508
|
// src/lib/messages.ts
|
|
28495
28509
|
init_db();
|
|
28496
28510
|
import { randomUUID } from "crypto";
|
|
28511
|
+
import { mkdirSync as mkdirSync2, copyFileSync, statSync } from "fs";
|
|
28512
|
+
import { join as join2 } from "path";
|
|
28513
|
+
import { homedir as homedir2 } from "os";
|
|
28497
28514
|
function parseMessage(row) {
|
|
28498
28515
|
let metadata = null;
|
|
28499
28516
|
if (row.metadata) {
|
|
@@ -28503,12 +28520,53 @@ function parseMessage(row) {
|
|
|
28503
28520
|
metadata = null;
|
|
28504
28521
|
}
|
|
28505
28522
|
}
|
|
28523
|
+
let attachments = null;
|
|
28524
|
+
if (row.attachments) {
|
|
28525
|
+
try {
|
|
28526
|
+
attachments = JSON.parse(row.attachments);
|
|
28527
|
+
} catch {
|
|
28528
|
+
attachments = null;
|
|
28529
|
+
}
|
|
28530
|
+
}
|
|
28506
28531
|
return {
|
|
28507
28532
|
...row,
|
|
28508
28533
|
metadata,
|
|
28534
|
+
attachments,
|
|
28509
28535
|
blocking: !!row.blocking
|
|
28510
28536
|
};
|
|
28511
28537
|
}
|
|
28538
|
+
function getAttachmentsDir() {
|
|
28539
|
+
if (process.env.CONVERSATIONS_ATTACHMENTS_DIR)
|
|
28540
|
+
return process.env.CONVERSATIONS_ATTACHMENTS_DIR;
|
|
28541
|
+
return join2(homedir2(), ".conversations", "attachments");
|
|
28542
|
+
}
|
|
28543
|
+
function guessMimeType(name) {
|
|
28544
|
+
const ext = name.split(".").pop()?.toLowerCase();
|
|
28545
|
+
const mimeMap = {
|
|
28546
|
+
txt: "text/plain",
|
|
28547
|
+
md: "text/markdown",
|
|
28548
|
+
json: "application/json",
|
|
28549
|
+
js: "text/javascript",
|
|
28550
|
+
ts: "text/typescript",
|
|
28551
|
+
py: "text/x-python",
|
|
28552
|
+
html: "text/html",
|
|
28553
|
+
css: "text/css",
|
|
28554
|
+
xml: "application/xml",
|
|
28555
|
+
png: "image/png",
|
|
28556
|
+
jpg: "image/jpeg",
|
|
28557
|
+
jpeg: "image/jpeg",
|
|
28558
|
+
gif: "image/gif",
|
|
28559
|
+
svg: "image/svg+xml",
|
|
28560
|
+
webp: "image/webp",
|
|
28561
|
+
pdf: "application/pdf",
|
|
28562
|
+
zip: "application/zip",
|
|
28563
|
+
gz: "application/gzip",
|
|
28564
|
+
csv: "text/csv",
|
|
28565
|
+
yaml: "text/yaml",
|
|
28566
|
+
yml: "text/yaml"
|
|
28567
|
+
};
|
|
28568
|
+
return mimeMap[ext || ""] || "application/octet-stream";
|
|
28569
|
+
}
|
|
28512
28570
|
function sendMessage(opts) {
|
|
28513
28571
|
const db2 = getDb();
|
|
28514
28572
|
const explicitSession = opts.session_id && opts.session_id.trim().length > 0 ? opts.session_id : undefined;
|
|
@@ -28522,7 +28580,27 @@ function sendMessage(opts) {
|
|
|
28522
28580
|
RETURNING *
|
|
28523
28581
|
`);
|
|
28524
28582
|
const row = stmt.get(sessionId, opts.from, opts.to, opts.space || null, opts.content, normalizedPriority, opts.working_dir || null, opts.repository || null, opts.branch || null, metadata, blocking);
|
|
28525
|
-
|
|
28583
|
+
const message = parseMessage(row);
|
|
28584
|
+
if (opts.attachments && opts.attachments.length > 0) {
|
|
28585
|
+
const attachmentsDir = join2(getAttachmentsDir(), String(message.id));
|
|
28586
|
+
mkdirSync2(attachmentsDir, { recursive: true });
|
|
28587
|
+
const attachmentInfos = [];
|
|
28588
|
+
for (const att of opts.attachments) {
|
|
28589
|
+
const destPath = join2(attachmentsDir, att.name);
|
|
28590
|
+
copyFileSync(att.source_path, destPath);
|
|
28591
|
+
const stat = statSync(destPath);
|
|
28592
|
+
attachmentInfos.push({
|
|
28593
|
+
name: att.name,
|
|
28594
|
+
path: destPath,
|
|
28595
|
+
size: stat.size,
|
|
28596
|
+
mime_type: guessMimeType(att.name)
|
|
28597
|
+
});
|
|
28598
|
+
}
|
|
28599
|
+
const attachmentsJson = JSON.stringify(attachmentInfos);
|
|
28600
|
+
db2.prepare("UPDATE messages SET attachments = ? WHERE id = ?").run(attachmentsJson, message.id);
|
|
28601
|
+
message.attachments = attachmentInfos;
|
|
28602
|
+
}
|
|
28603
|
+
return message;
|
|
28526
28604
|
}
|
|
28527
28605
|
function readMessages(opts = {}) {
|
|
28528
28606
|
const db2 = getDb();
|
|
@@ -29085,9 +29163,9 @@ function deleteProject(id) {
|
|
|
29085
29163
|
}
|
|
29086
29164
|
|
|
29087
29165
|
// src/lib/identity.ts
|
|
29088
|
-
import { readFileSync, writeFileSync, mkdirSync as
|
|
29089
|
-
import { join as
|
|
29090
|
-
import { homedir as
|
|
29166
|
+
import { readFileSync, writeFileSync, mkdirSync as mkdirSync3 } from "fs";
|
|
29167
|
+
import { join as join3, dirname as dirname2 } from "path";
|
|
29168
|
+
import { homedir as homedir3 } from "os";
|
|
29091
29169
|
|
|
29092
29170
|
// src/lib/names.ts
|
|
29093
29171
|
var AGENT_NAMES = [
|
|
@@ -29439,7 +29517,7 @@ var AGENT_NAMES = [
|
|
|
29439
29517
|
];
|
|
29440
29518
|
|
|
29441
29519
|
// src/lib/identity.ts
|
|
29442
|
-
var AGENT_ID_FILE =
|
|
29520
|
+
var AGENT_ID_FILE = join3(homedir3(), ".conversations", "agent-id");
|
|
29443
29521
|
var cachedAutoName = null;
|
|
29444
29522
|
function isNameTaken(name) {
|
|
29445
29523
|
try {
|
|
@@ -29471,7 +29549,7 @@ function getAutoName() {
|
|
|
29471
29549
|
}
|
|
29472
29550
|
cachedAutoName = name;
|
|
29473
29551
|
try {
|
|
29474
|
-
|
|
29552
|
+
mkdirSync3(dirname2(AGENT_ID_FILE), { recursive: true });
|
|
29475
29553
|
writeFileSync(AGENT_ID_FILE, name + `
|
|
29476
29554
|
`, "utf-8");
|
|
29477
29555
|
} catch {}
|
|
@@ -29554,7 +29632,7 @@ function renameAgent(oldName, newName) {
|
|
|
29554
29632
|
// package.json
|
|
29555
29633
|
var package_default = {
|
|
29556
29634
|
name: "@hasna/conversations",
|
|
29557
|
-
version: "0.1.
|
|
29635
|
+
version: "0.1.18",
|
|
29558
29636
|
description: "Real-time CLI messaging for AI agents",
|
|
29559
29637
|
type: "module",
|
|
29560
29638
|
bin: {
|
package/dist/index.d.ts
CHANGED
|
@@ -16,5 +16,6 @@ export { createProject, listProjects, getProject, getProjectByName, updateProjec
|
|
|
16
16
|
export { getDb, getDbPath, closeDb, } from "./lib/db.js";
|
|
17
17
|
export { startPolling, useSpaceMessages, } from "./lib/poll.js";
|
|
18
18
|
export { resolveIdentity, requireIdentity, } from "./lib/identity.js";
|
|
19
|
+
export { addReaction, removeReaction, getReactions, getReactionSummary, } from "./lib/reactions.js";
|
|
19
20
|
export { heartbeat, getPresence, listAgents, removePresence, renameAgent, } from "./lib/presence.js";
|
|
20
|
-
export type { Message, Session, Space, SpaceInfo, SpaceMember, Project, ProjectInfo, Priority, SendMessageOptions, ReadMessagesOptions, SearchMessagesOptions, AgentPresence, } from "./types.js";
|
|
21
|
+
export type { Message, Session, Space, SpaceInfo, SpaceMember, Project, ProjectInfo, Priority, SendMessageOptions, ReadMessagesOptions, SearchMessagesOptions, AgentPresence, Reaction, Attachment, } from "./types.js";
|
package/dist/index.js
CHANGED
|
@@ -133,6 +133,17 @@ function getDb() {
|
|
|
133
133
|
metadata TEXT
|
|
134
134
|
)
|
|
135
135
|
`);
|
|
136
|
+
db.exec(`
|
|
137
|
+
CREATE TABLE IF NOT EXISTS reactions (
|
|
138
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
139
|
+
message_id INTEGER NOT NULL REFERENCES messages(id) ON DELETE CASCADE,
|
|
140
|
+
agent TEXT NOT NULL,
|
|
141
|
+
emoji TEXT NOT NULL,
|
|
142
|
+
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%f', 'now')),
|
|
143
|
+
UNIQUE(message_id, agent, emoji)
|
|
144
|
+
)
|
|
145
|
+
`);
|
|
146
|
+
db.exec("CREATE INDEX IF NOT EXISTS idx_reactions_message ON reactions(message_id)");
|
|
136
147
|
const existingTables = db.prepare("SELECT name FROM sqlite_master WHERE type='table'").all();
|
|
137
148
|
const tableNames = existingTables.map((t) => t.name);
|
|
138
149
|
if (tableNames.includes("channels") && tableNames.includes("spaces")) {
|
|
@@ -190,6 +201,9 @@ function getDb() {
|
|
|
190
201
|
db.exec("ALTER TABLE messages ADD COLUMN blocking INTEGER NOT NULL DEFAULT 0");
|
|
191
202
|
db.exec("CREATE INDEX IF NOT EXISTS idx_messages_blocking ON messages(blocking)");
|
|
192
203
|
}
|
|
204
|
+
if (!colNames2.includes("attachments")) {
|
|
205
|
+
db.exec("ALTER TABLE messages ADD COLUMN attachments TEXT");
|
|
206
|
+
}
|
|
193
207
|
return db;
|
|
194
208
|
}
|
|
195
209
|
function closeDb() {
|
|
@@ -2015,6 +2029,9 @@ var require_react = __commonJS((exports, module) => {
|
|
|
2015
2029
|
// src/lib/messages.ts
|
|
2016
2030
|
init_db();
|
|
2017
2031
|
import { randomUUID } from "crypto";
|
|
2032
|
+
import { mkdirSync as mkdirSync2, copyFileSync, statSync } from "fs";
|
|
2033
|
+
import { join as join2 } from "path";
|
|
2034
|
+
import { homedir as homedir2 } from "os";
|
|
2018
2035
|
function parseMessage(row) {
|
|
2019
2036
|
let metadata = null;
|
|
2020
2037
|
if (row.metadata) {
|
|
@@ -2024,12 +2041,53 @@ function parseMessage(row) {
|
|
|
2024
2041
|
metadata = null;
|
|
2025
2042
|
}
|
|
2026
2043
|
}
|
|
2044
|
+
let attachments = null;
|
|
2045
|
+
if (row.attachments) {
|
|
2046
|
+
try {
|
|
2047
|
+
attachments = JSON.parse(row.attachments);
|
|
2048
|
+
} catch {
|
|
2049
|
+
attachments = null;
|
|
2050
|
+
}
|
|
2051
|
+
}
|
|
2027
2052
|
return {
|
|
2028
2053
|
...row,
|
|
2029
2054
|
metadata,
|
|
2055
|
+
attachments,
|
|
2030
2056
|
blocking: !!row.blocking
|
|
2031
2057
|
};
|
|
2032
2058
|
}
|
|
2059
|
+
function getAttachmentsDir() {
|
|
2060
|
+
if (process.env.CONVERSATIONS_ATTACHMENTS_DIR)
|
|
2061
|
+
return process.env.CONVERSATIONS_ATTACHMENTS_DIR;
|
|
2062
|
+
return join2(homedir2(), ".conversations", "attachments");
|
|
2063
|
+
}
|
|
2064
|
+
function guessMimeType(name) {
|
|
2065
|
+
const ext = name.split(".").pop()?.toLowerCase();
|
|
2066
|
+
const mimeMap = {
|
|
2067
|
+
txt: "text/plain",
|
|
2068
|
+
md: "text/markdown",
|
|
2069
|
+
json: "application/json",
|
|
2070
|
+
js: "text/javascript",
|
|
2071
|
+
ts: "text/typescript",
|
|
2072
|
+
py: "text/x-python",
|
|
2073
|
+
html: "text/html",
|
|
2074
|
+
css: "text/css",
|
|
2075
|
+
xml: "application/xml",
|
|
2076
|
+
png: "image/png",
|
|
2077
|
+
jpg: "image/jpeg",
|
|
2078
|
+
jpeg: "image/jpeg",
|
|
2079
|
+
gif: "image/gif",
|
|
2080
|
+
svg: "image/svg+xml",
|
|
2081
|
+
webp: "image/webp",
|
|
2082
|
+
pdf: "application/pdf",
|
|
2083
|
+
zip: "application/zip",
|
|
2084
|
+
gz: "application/gzip",
|
|
2085
|
+
csv: "text/csv",
|
|
2086
|
+
yaml: "text/yaml",
|
|
2087
|
+
yml: "text/yaml"
|
|
2088
|
+
};
|
|
2089
|
+
return mimeMap[ext || ""] || "application/octet-stream";
|
|
2090
|
+
}
|
|
2033
2091
|
function sendMessage(opts) {
|
|
2034
2092
|
const db2 = getDb();
|
|
2035
2093
|
const explicitSession = opts.session_id && opts.session_id.trim().length > 0 ? opts.session_id : undefined;
|
|
@@ -2043,7 +2101,27 @@ function sendMessage(opts) {
|
|
|
2043
2101
|
RETURNING *
|
|
2044
2102
|
`);
|
|
2045
2103
|
const row = stmt.get(sessionId, opts.from, opts.to, opts.space || null, opts.content, normalizedPriority, opts.working_dir || null, opts.repository || null, opts.branch || null, metadata, blocking);
|
|
2046
|
-
|
|
2104
|
+
const message = parseMessage(row);
|
|
2105
|
+
if (opts.attachments && opts.attachments.length > 0) {
|
|
2106
|
+
const attachmentsDir = join2(getAttachmentsDir(), String(message.id));
|
|
2107
|
+
mkdirSync2(attachmentsDir, { recursive: true });
|
|
2108
|
+
const attachmentInfos = [];
|
|
2109
|
+
for (const att of opts.attachments) {
|
|
2110
|
+
const destPath = join2(attachmentsDir, att.name);
|
|
2111
|
+
copyFileSync(att.source_path, destPath);
|
|
2112
|
+
const stat = statSync(destPath);
|
|
2113
|
+
attachmentInfos.push({
|
|
2114
|
+
name: att.name,
|
|
2115
|
+
path: destPath,
|
|
2116
|
+
size: stat.size,
|
|
2117
|
+
mime_type: guessMimeType(att.name)
|
|
2118
|
+
});
|
|
2119
|
+
}
|
|
2120
|
+
const attachmentsJson = JSON.stringify(attachmentInfos);
|
|
2121
|
+
db2.prepare("UPDATE messages SET attachments = ? WHERE id = ?").run(attachmentsJson, message.id);
|
|
2122
|
+
message.attachments = attachmentInfos;
|
|
2123
|
+
}
|
|
2124
|
+
return message;
|
|
2047
2125
|
}
|
|
2048
2126
|
function readMessages(opts = {}) {
|
|
2049
2127
|
const db2 = getDb();
|
|
@@ -2720,9 +2798,9 @@ function useSpaceMessages(spaceName) {
|
|
|
2720
2798
|
return messages;
|
|
2721
2799
|
}
|
|
2722
2800
|
// src/lib/identity.ts
|
|
2723
|
-
import { readFileSync, writeFileSync, mkdirSync as
|
|
2724
|
-
import { join as
|
|
2725
|
-
import { homedir as
|
|
2801
|
+
import { readFileSync, writeFileSync, mkdirSync as mkdirSync3 } from "fs";
|
|
2802
|
+
import { join as join3, dirname as dirname2 } from "path";
|
|
2803
|
+
import { homedir as homedir3 } from "os";
|
|
2726
2804
|
|
|
2727
2805
|
// src/lib/names.ts
|
|
2728
2806
|
var AGENT_NAMES = [
|
|
@@ -3074,7 +3152,7 @@ var AGENT_NAMES = [
|
|
|
3074
3152
|
];
|
|
3075
3153
|
|
|
3076
3154
|
// src/lib/identity.ts
|
|
3077
|
-
var AGENT_ID_FILE =
|
|
3155
|
+
var AGENT_ID_FILE = join3(homedir3(), ".conversations", "agent-id");
|
|
3078
3156
|
var cachedAutoName = null;
|
|
3079
3157
|
function isNameTaken(name) {
|
|
3080
3158
|
try {
|
|
@@ -3106,7 +3184,7 @@ function getAutoName() {
|
|
|
3106
3184
|
}
|
|
3107
3185
|
cachedAutoName = name;
|
|
3108
3186
|
try {
|
|
3109
|
-
|
|
3187
|
+
mkdirSync3(dirname2(AGENT_ID_FILE), { recursive: true });
|
|
3110
3188
|
writeFileSync(AGENT_ID_FILE, name + `
|
|
3111
3189
|
`, "utf-8");
|
|
3112
3190
|
} catch {}
|
|
@@ -3130,6 +3208,45 @@ function requireIdentity(explicit) {
|
|
|
3130
3208
|
return envValue;
|
|
3131
3209
|
throw new Error("Agent identity required. Set CONVERSATIONS_AGENT_ID env var or pass --from flag.");
|
|
3132
3210
|
}
|
|
3211
|
+
// src/lib/reactions.ts
|
|
3212
|
+
init_db();
|
|
3213
|
+
function addReaction(messageId, agent, emoji) {
|
|
3214
|
+
const db2 = getDb();
|
|
3215
|
+
const stmt = db2.prepare(`
|
|
3216
|
+
INSERT INTO reactions (message_id, agent, emoji)
|
|
3217
|
+
VALUES (?, ?, ?)
|
|
3218
|
+
ON CONFLICT (message_id, agent, emoji) DO UPDATE SET agent = agent
|
|
3219
|
+
RETURNING *
|
|
3220
|
+
`);
|
|
3221
|
+
const row = stmt.get(messageId, agent, emoji);
|
|
3222
|
+
return row;
|
|
3223
|
+
}
|
|
3224
|
+
function removeReaction(messageId, agent, emoji) {
|
|
3225
|
+
const db2 = getDb();
|
|
3226
|
+
const stmt = db2.prepare("DELETE FROM reactions WHERE message_id = ? AND agent = ? AND emoji = ?");
|
|
3227
|
+
const result = stmt.run(messageId, agent, emoji);
|
|
3228
|
+
return result.changes > 0;
|
|
3229
|
+
}
|
|
3230
|
+
function getReactions(messageId) {
|
|
3231
|
+
const db2 = getDb();
|
|
3232
|
+
const rows = db2.prepare("SELECT * FROM reactions WHERE message_id = ? ORDER BY created_at ASC, id ASC").all(messageId);
|
|
3233
|
+
return rows;
|
|
3234
|
+
}
|
|
3235
|
+
function getReactionSummary(messageId) {
|
|
3236
|
+
const db2 = getDb();
|
|
3237
|
+
const rows = db2.prepare(`
|
|
3238
|
+
SELECT emoji, GROUP_CONCAT(agent) as agents, COUNT(*) as count
|
|
3239
|
+
FROM reactions
|
|
3240
|
+
WHERE message_id = ?
|
|
3241
|
+
GROUP BY emoji
|
|
3242
|
+
ORDER BY count DESC, MIN(created_at) ASC
|
|
3243
|
+
`).all(messageId);
|
|
3244
|
+
return rows.map((row) => ({
|
|
3245
|
+
emoji: row.emoji,
|
|
3246
|
+
count: row.count,
|
|
3247
|
+
agents: row.agents.split(",")
|
|
3248
|
+
}));
|
|
3249
|
+
}
|
|
3133
3250
|
// src/lib/presence.ts
|
|
3134
3251
|
init_db();
|
|
3135
3252
|
var ONLINE_THRESHOLD_SECONDS = 60;
|
|
@@ -3211,6 +3328,7 @@ export {
|
|
|
3211
3328
|
resolveIdentity,
|
|
3212
3329
|
requireIdentity,
|
|
3213
3330
|
renameAgent,
|
|
3331
|
+
removeReaction,
|
|
3214
3332
|
removePresence,
|
|
3215
3333
|
readMessages,
|
|
3216
3334
|
pinMessage,
|
|
@@ -3231,6 +3349,8 @@ export {
|
|
|
3231
3349
|
getSpaceDepth,
|
|
3232
3350
|
getSpace,
|
|
3233
3351
|
getSession,
|
|
3352
|
+
getReactions,
|
|
3353
|
+
getReactionSummary,
|
|
3234
3354
|
getProjectByName,
|
|
3235
3355
|
getProject,
|
|
3236
3356
|
getPresence,
|
|
@@ -3245,5 +3365,6 @@ export {
|
|
|
3245
3365
|
createSpace,
|
|
3246
3366
|
createProject,
|
|
3247
3367
|
closeDb,
|
|
3248
|
-
archiveSpace
|
|
3368
|
+
archiveSpace,
|
|
3369
|
+
addReaction
|
|
3249
3370
|
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Reaction } from "../types.js";
|
|
2
|
+
export declare function addReaction(messageId: number, agent: string, emoji: string): Reaction;
|
|
3
|
+
export declare function removeReaction(messageId: number, agent: string, emoji: string): boolean;
|
|
4
|
+
export declare function getReactions(messageId: number): Reaction[];
|
|
5
|
+
export interface ReactionSummary {
|
|
6
|
+
emoji: string;
|
|
7
|
+
count: number;
|
|
8
|
+
agents: string[];
|
|
9
|
+
}
|
|
10
|
+
export declare function getReactionSummary(messageId: number): ReactionSummary[];
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/types.d.ts
CHANGED
|
@@ -16,6 +16,20 @@ export interface Message {
|
|
|
16
16
|
edited_at: string | null;
|
|
17
17
|
pinned_at: string | null;
|
|
18
18
|
blocking: boolean;
|
|
19
|
+
attachments: Attachment[] | null;
|
|
20
|
+
}
|
|
21
|
+
export interface Reaction {
|
|
22
|
+
id: number;
|
|
23
|
+
message_id: number;
|
|
24
|
+
agent: string;
|
|
25
|
+
emoji: string;
|
|
26
|
+
created_at: string;
|
|
27
|
+
}
|
|
28
|
+
export interface Attachment {
|
|
29
|
+
name: string;
|
|
30
|
+
path: string;
|
|
31
|
+
size: number;
|
|
32
|
+
mime_type: string;
|
|
19
33
|
}
|
|
20
34
|
export interface Session {
|
|
21
35
|
session_id: string;
|
|
@@ -71,6 +85,10 @@ export interface SendMessageOptions {
|
|
|
71
85
|
branch?: string;
|
|
72
86
|
metadata?: Record<string, unknown>;
|
|
73
87
|
blocking?: boolean;
|
|
88
|
+
attachments?: {
|
|
89
|
+
name: string;
|
|
90
|
+
source_path: string;
|
|
91
|
+
}[];
|
|
74
92
|
}
|
|
75
93
|
export interface ReadMessagesOptions {
|
|
76
94
|
session_id?: string;
|