@hasna/conversations 0.2.42 → 0.2.44
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 +170 -20
- package/bin/index.js +478 -130
- package/bin/mcp.js +455 -107
- package/dist/index.js +385 -91
- package/dist/lib/messages.d.ts +1 -1
- package/dist/lib/presence.d.ts +2 -1
- package/dist/lib/webhooks.d.ts +1 -0
- package/package.json +1 -1
package/bin/index.js
CHANGED
|
@@ -6,39 +6,60 @@ var __defProp = Object.defineProperty;
|
|
|
6
6
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
7
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
8
8
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
|
+
function __accessProp(key) {
|
|
10
|
+
return this[key];
|
|
11
|
+
}
|
|
12
|
+
var __toESMCache_node;
|
|
13
|
+
var __toESMCache_esm;
|
|
9
14
|
var __toESM = (mod, isNodeMode, target) => {
|
|
15
|
+
var canCache = mod != null && typeof mod === "object";
|
|
16
|
+
if (canCache) {
|
|
17
|
+
var cache = isNodeMode ? __toESMCache_node ??= new WeakMap : __toESMCache_esm ??= new WeakMap;
|
|
18
|
+
var cached = cache.get(mod);
|
|
19
|
+
if (cached)
|
|
20
|
+
return cached;
|
|
21
|
+
}
|
|
10
22
|
target = mod != null ? __create(__getProtoOf(mod)) : {};
|
|
11
23
|
const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
|
|
12
24
|
for (let key of __getOwnPropNames(mod))
|
|
13
25
|
if (!__hasOwnProp.call(to, key))
|
|
14
26
|
__defProp(to, key, {
|
|
15
|
-
get: (
|
|
27
|
+
get: __accessProp.bind(mod, key),
|
|
16
28
|
enumerable: true
|
|
17
29
|
});
|
|
30
|
+
if (canCache)
|
|
31
|
+
cache.set(mod, to);
|
|
18
32
|
return to;
|
|
19
33
|
};
|
|
20
|
-
var __moduleCache = /* @__PURE__ */ new WeakMap;
|
|
21
34
|
var __toCommonJS = (from) => {
|
|
22
|
-
var entry = __moduleCache.get(from), desc;
|
|
35
|
+
var entry = (__moduleCache ??= new WeakMap).get(from), desc;
|
|
23
36
|
if (entry)
|
|
24
37
|
return entry;
|
|
25
38
|
entry = __defProp({}, "__esModule", { value: true });
|
|
26
|
-
if (from && typeof from === "object" || typeof from === "function")
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
39
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
40
|
+
for (var key of __getOwnPropNames(from))
|
|
41
|
+
if (!__hasOwnProp.call(entry, key))
|
|
42
|
+
__defProp(entry, key, {
|
|
43
|
+
get: __accessProp.bind(from, key),
|
|
44
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
45
|
+
});
|
|
46
|
+
}
|
|
31
47
|
__moduleCache.set(from, entry);
|
|
32
48
|
return entry;
|
|
33
49
|
};
|
|
50
|
+
var __moduleCache;
|
|
34
51
|
var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
|
|
52
|
+
var __returnValue = (v) => v;
|
|
53
|
+
function __exportSetter(name, newValue) {
|
|
54
|
+
this[name] = __returnValue.bind(null, newValue);
|
|
55
|
+
}
|
|
35
56
|
var __export = (target, all) => {
|
|
36
57
|
for (var name in all)
|
|
37
58
|
__defProp(target, name, {
|
|
38
59
|
get: all[name],
|
|
39
60
|
enumerable: true,
|
|
40
61
|
configurable: true,
|
|
41
|
-
set: (
|
|
62
|
+
set: __exportSetter.bind(all, name)
|
|
42
63
|
});
|
|
43
64
|
};
|
|
44
65
|
var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
@@ -2235,7 +2256,7 @@ var init_names = __esm(() => {
|
|
|
2235
2256
|
];
|
|
2236
2257
|
});
|
|
2237
2258
|
|
|
2238
|
-
//
|
|
2259
|
+
// node_modules/@hasna/cloud/dist/index.js
|
|
2239
2260
|
var exports_dist = {};
|
|
2240
2261
|
__export(exports_dist, {
|
|
2241
2262
|
translateSql: () => translateSql,
|
|
@@ -2330,11 +2351,11 @@ import { join as join5 } from "path";
|
|
|
2330
2351
|
import { join as join6, dirname } from "path";
|
|
2331
2352
|
import { existsSync as existsSync6, writeFileSync as writeFileSync2, unlinkSync, mkdirSync as mkdirSync3 } from "fs";
|
|
2332
2353
|
import { homedir as homedir5, platform } from "os";
|
|
2333
|
-
function
|
|
2354
|
+
function __accessProp2(key) {
|
|
2334
2355
|
return this[key];
|
|
2335
2356
|
}
|
|
2336
|
-
function
|
|
2337
|
-
this[name] =
|
|
2357
|
+
function __exportSetter2(name, newValue) {
|
|
2358
|
+
this[name] = __returnValue2.bind(null, newValue);
|
|
2338
2359
|
}
|
|
2339
2360
|
function translateSql(sql, dialect) {
|
|
2340
2361
|
if (dialect === "sqlite")
|
|
@@ -3647,9 +3668,9 @@ async function syncTransfer(source, target, options, _direction) {
|
|
|
3647
3668
|
const batch = rows.slice(offset, offset + batchSize);
|
|
3648
3669
|
try {
|
|
3649
3670
|
if (isAsyncAdapter(target)) {
|
|
3650
|
-
await batchUpsertPg(target, table, columns, updateCols, pkColumns, batch
|
|
3671
|
+
await batchUpsertPg(target, table, columns, updateCols, pkColumns, batch);
|
|
3651
3672
|
} else {
|
|
3652
|
-
batchUpsertSqlite(target, table, columns, updateCols, pkColumns, batch
|
|
3673
|
+
batchUpsertSqlite(target, table, columns, updateCols, pkColumns, batch);
|
|
3653
3674
|
}
|
|
3654
3675
|
result.rowsWritten += batch.length;
|
|
3655
3676
|
} catch (err) {
|
|
@@ -3696,7 +3717,7 @@ async function syncTransfer(source, target, options, _direction) {
|
|
|
3696
3717
|
}
|
|
3697
3718
|
return results;
|
|
3698
3719
|
}
|
|
3699
|
-
async function batchUpsertPg(target, table, columns, updateCols, primaryKeys, batch
|
|
3720
|
+
async function batchUpsertPg(target, table, columns, updateCols, primaryKeys, batch) {
|
|
3700
3721
|
if (batch.length === 0)
|
|
3701
3722
|
return;
|
|
3702
3723
|
const colList = columns.map((c) => `"${c}"`).join(", ");
|
|
@@ -3706,22 +3727,20 @@ async function batchUpsertPg(target, table, columns, updateCols, primaryKeys, ba
|
|
|
3706
3727
|
}).join(", ");
|
|
3707
3728
|
const pkList = primaryKeys.map((c) => `"${c}"`).join(", ");
|
|
3708
3729
|
const setClause = updateCols.length > 0 ? updateCols.map((c) => `"${c}" = EXCLUDED."${c}"`).join(", ") : `"${primaryKeys[0]}" = EXCLUDED."${primaryKeys[0]}"`;
|
|
3709
|
-
const whereClause = conflictColumn && updateCols.includes(conflictColumn) ? ` WHERE "${table}"."${conflictColumn}" IS NULL OR EXCLUDED."${conflictColumn}" >= "${table}"."${conflictColumn}"` : "";
|
|
3710
3730
|
const sql = `INSERT INTO "${table}" (${colList}) VALUES ${valuePlaceholders}
|
|
3711
|
-
ON CONFLICT (${pkList}) DO UPDATE SET ${setClause}
|
|
3731
|
+
ON CONFLICT (${pkList}) DO UPDATE SET ${setClause}`;
|
|
3712
3732
|
const params = batch.flatMap((row) => columns.map((c) => row[c] ?? null));
|
|
3713
3733
|
await target.run(sql, ...params);
|
|
3714
3734
|
}
|
|
3715
|
-
function batchUpsertSqlite(target, table, columns, updateCols, primaryKeys, batch
|
|
3735
|
+
function batchUpsertSqlite(target, table, columns, updateCols, primaryKeys, batch) {
|
|
3716
3736
|
if (batch.length === 0)
|
|
3717
3737
|
return;
|
|
3718
3738
|
const colList = columns.map((c) => `"${c}"`).join(", ");
|
|
3719
3739
|
const valuePlaceholders = batch.map(() => `(${columns.map(() => "?").join(", ")})`).join(", ");
|
|
3720
3740
|
const pkList = primaryKeys.map((c) => `"${c}"`).join(", ");
|
|
3721
3741
|
const setClause = updateCols.length > 0 ? updateCols.map((c) => `"${c}" = EXCLUDED."${c}"`).join(", ") : `"${primaryKeys[0]}" = EXCLUDED."${primaryKeys[0]}"`;
|
|
3722
|
-
const whereClause = conflictColumn && updateCols.includes(conflictColumn) ? ` WHERE "${table}"."${conflictColumn}" IS NULL OR EXCLUDED."${conflictColumn}" >= "${table}"."${conflictColumn}"` : "";
|
|
3723
3742
|
const sql = `INSERT INTO "${table}" (${colList}) VALUES ${valuePlaceholders}
|
|
3724
|
-
ON CONFLICT (${pkList}) DO UPDATE SET ${setClause}
|
|
3743
|
+
ON CONFLICT (${pkList}) DO UPDATE SET ${setClause}`;
|
|
3725
3744
|
const params = batch.flatMap((row) => columns.map((c) => coerceForSqlite(row[c])));
|
|
3726
3745
|
target.run(sql, ...params);
|
|
3727
3746
|
}
|
|
@@ -4791,7 +4810,7 @@ async function ensureAllPgDatabases() {
|
|
|
4791
4810
|
}
|
|
4792
4811
|
return results;
|
|
4793
4812
|
}
|
|
4794
|
-
function registerCloudTools(server, serviceName
|
|
4813
|
+
function registerCloudTools(server, serviceName) {
|
|
4795
4814
|
server.tool(`${serviceName}_cloud_status`, "Show cloud configuration and connection health", {}, async () => {
|
|
4796
4815
|
const config = getCloudConfig();
|
|
4797
4816
|
const lines = [
|
|
@@ -4824,13 +4843,8 @@ function registerCloudTools(server, serviceName, opts = {}) {
|
|
|
4824
4843
|
isError: true
|
|
4825
4844
|
};
|
|
4826
4845
|
}
|
|
4827
|
-
const local = new SqliteAdapter(
|
|
4846
|
+
const local = new SqliteAdapter(getDbPath(serviceName));
|
|
4828
4847
|
const cloud = new PgAdapterAsync(getConnectionString(serviceName));
|
|
4829
|
-
if (opts.migrations?.length) {
|
|
4830
|
-
for (const sql of opts.migrations) {
|
|
4831
|
-
await cloud.run(sql);
|
|
4832
|
-
}
|
|
4833
|
-
}
|
|
4834
4848
|
const tableList = tablesStr ? tablesStr.split(",").map((t) => t.trim()) : listSqliteTables(local);
|
|
4835
4849
|
const results = await syncPush(local, cloud, { tables: tableList });
|
|
4836
4850
|
local.close();
|
|
@@ -4852,7 +4866,7 @@ function registerCloudTools(server, serviceName, opts = {}) {
|
|
|
4852
4866
|
isError: true
|
|
4853
4867
|
};
|
|
4854
4868
|
}
|
|
4855
|
-
const local = new SqliteAdapter(
|
|
4869
|
+
const local = new SqliteAdapter(getDbPath(serviceName));
|
|
4856
4870
|
const cloud = new PgAdapterAsync(getConnectionString(serviceName));
|
|
4857
4871
|
let tableList;
|
|
4858
4872
|
if (tablesStr) {
|
|
@@ -4975,10 +4989,10 @@ function registerCloudCommands(program2, serviceName) {
|
|
|
4975
4989
|
}
|
|
4976
4990
|
});
|
|
4977
4991
|
}
|
|
4978
|
-
var __create2, __getProtoOf2, __defProp2, __getOwnPropNames2, __hasOwnProp2,
|
|
4992
|
+
var __create2, __getProtoOf2, __defProp2, __getOwnPropNames2, __hasOwnProp2, __toESMCache_node2, __toESMCache_esm2, __toESM2 = (mod, isNodeMode, target) => {
|
|
4979
4993
|
var canCache = mod != null && typeof mod === "object";
|
|
4980
4994
|
if (canCache) {
|
|
4981
|
-
var cache = isNodeMode ?
|
|
4995
|
+
var cache = isNodeMode ? __toESMCache_node2 ??= new WeakMap : __toESMCache_esm2 ??= new WeakMap;
|
|
4982
4996
|
var cached = cache.get(mod);
|
|
4983
4997
|
if (cached)
|
|
4984
4998
|
return cached;
|
|
@@ -4988,19 +5002,19 @@ var __create2, __getProtoOf2, __defProp2, __getOwnPropNames2, __hasOwnProp2, __t
|
|
|
4988
5002
|
for (let key of __getOwnPropNames2(mod))
|
|
4989
5003
|
if (!__hasOwnProp2.call(to, key))
|
|
4990
5004
|
__defProp2(to, key, {
|
|
4991
|
-
get:
|
|
5005
|
+
get: __accessProp2.bind(mod, key),
|
|
4992
5006
|
enumerable: true
|
|
4993
5007
|
});
|
|
4994
5008
|
if (canCache)
|
|
4995
5009
|
cache.set(mod, to);
|
|
4996
5010
|
return to;
|
|
4997
|
-
}, __commonJS2 = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports),
|
|
5011
|
+
}, __commonJS2 = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports), __returnValue2 = (v) => v, __export2 = (target, all) => {
|
|
4998
5012
|
for (var name in all)
|
|
4999
5013
|
__defProp2(target, name, {
|
|
5000
5014
|
get: all[name],
|
|
5001
5015
|
enumerable: true,
|
|
5002
5016
|
configurable: true,
|
|
5003
|
-
set:
|
|
5017
|
+
set: __exportSetter2.bind(all, name)
|
|
5004
5018
|
});
|
|
5005
5019
|
}, __esm2 = (fn, res) => () => (fn && (res = fn(fn = 0)), res), __require2, require_postgres_array, require_arrayParser, require_postgres_date, require_mutable, require_postgres_interval, require_postgres_bytea, require_textParsers, require_pg_int8, require_binaryParsers, require_builtins, require_pg_types, require_defaults, require_utils, require_utils_legacy, require_utils_webcrypto, require_utils2, require_cert_signatures, require_sasl, require_type_overrides, require_pg_connection_string, require_connection_parameters, require_result, require_query, require_messages, require_buffer_writer, require_serializer, require_buffer_reader, require_parser, require_dist, require_empty, require_stream, require_connection, require_split2, require_helper, require_lib, require_client, require_pg_pool, require_query2, require_client2, require_lib2, import_lib, Client, Pool, Connection, types, Query, DatabaseError, escapeIdentifier, escapeLiteral, Result, TypeOverrides, defaults, esm_default, init_esm, init_adapter, util, objectUtil, ZodParsedType, getParsedType = (data) => {
|
|
5006
5020
|
const t = typeof data;
|
|
@@ -13357,6 +13371,137 @@ function getDbPath2() {
|
|
|
13357
13371
|
return process.env.CONVERSATIONS_DB_PATH;
|
|
13358
13372
|
return join7(getDataDir2(), "messages.db");
|
|
13359
13373
|
}
|
|
13374
|
+
function parsePresenceTimestamp(value) {
|
|
13375
|
+
if (typeof value !== "string" || !value)
|
|
13376
|
+
return 0;
|
|
13377
|
+
return new Date(`${value}Z`).getTime() || 0;
|
|
13378
|
+
}
|
|
13379
|
+
function normalizePresenceText(value) {
|
|
13380
|
+
if (typeof value !== "string")
|
|
13381
|
+
return null;
|
|
13382
|
+
const normalized = value.trim();
|
|
13383
|
+
return normalized ? normalized : null;
|
|
13384
|
+
}
|
|
13385
|
+
function shouldRebuildAgentPresenceTable(columns) {
|
|
13386
|
+
const byName = new Map(columns.map((column) => [column.name, column]));
|
|
13387
|
+
const agentCol = byName.get("agent");
|
|
13388
|
+
const projectCol = byName.get("project_id");
|
|
13389
|
+
if (!agentCol)
|
|
13390
|
+
return false;
|
|
13391
|
+
if (!projectCol)
|
|
13392
|
+
return true;
|
|
13393
|
+
return agentCol.pk !== 1 || projectCol.pk !== 2 || projectCol.notnull !== 1 || byName.has("pid");
|
|
13394
|
+
}
|
|
13395
|
+
function rebuildLegacyAgentPresenceTable(db2) {
|
|
13396
|
+
const fallbackNow = db2.prepare("SELECT strftime('%Y-%m-%dT%H:%M:%f', 'now') AS now").get().now;
|
|
13397
|
+
const legacyRows = db2.prepare("SELECT rowid AS _rowid, * FROM agent_presence").all();
|
|
13398
|
+
legacyRows.sort((left, right) => {
|
|
13399
|
+
const lastSeenDelta = parsePresenceTimestamp(right.last_seen_at) - parsePresenceTimestamp(left.last_seen_at);
|
|
13400
|
+
if (lastSeenDelta !== 0)
|
|
13401
|
+
return lastSeenDelta;
|
|
13402
|
+
const createdDelta = parsePresenceTimestamp(right.created_at) - parsePresenceTimestamp(left.created_at);
|
|
13403
|
+
if (createdDelta !== 0)
|
|
13404
|
+
return createdDelta;
|
|
13405
|
+
const projectDelta = Number(Boolean(normalizePresenceText(right.project_id))) - Number(Boolean(normalizePresenceText(left.project_id)));
|
|
13406
|
+
if (projectDelta !== 0)
|
|
13407
|
+
return projectDelta;
|
|
13408
|
+
return right._rowid - left._rowid;
|
|
13409
|
+
});
|
|
13410
|
+
const dedupedRows = new Map;
|
|
13411
|
+
for (const row of legacyRows) {
|
|
13412
|
+
const normalizedAgent = normalizePresenceText(row.agent)?.toLowerCase();
|
|
13413
|
+
if (!normalizedAgent)
|
|
13414
|
+
continue;
|
|
13415
|
+
const storedProjectId = normalizePresenceText(row.project_id) ?? "";
|
|
13416
|
+
const dedupeKey = `${normalizedAgent}\x00${storedProjectId}`;
|
|
13417
|
+
if (dedupedRows.has(dedupeKey))
|
|
13418
|
+
continue;
|
|
13419
|
+
dedupedRows.set(dedupeKey, row);
|
|
13420
|
+
}
|
|
13421
|
+
db2.exec("BEGIN");
|
|
13422
|
+
try {
|
|
13423
|
+
db2.exec(`
|
|
13424
|
+
CREATE TABLE agent_presence_new (
|
|
13425
|
+
id TEXT NOT NULL,
|
|
13426
|
+
agent TEXT NOT NULL,
|
|
13427
|
+
session_id TEXT,
|
|
13428
|
+
role TEXT NOT NULL DEFAULT 'agent',
|
|
13429
|
+
project_id TEXT NOT NULL DEFAULT '',
|
|
13430
|
+
status TEXT NOT NULL DEFAULT 'online',
|
|
13431
|
+
last_seen_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%f', 'now')),
|
|
13432
|
+
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%f', 'now')),
|
|
13433
|
+
metadata TEXT,
|
|
13434
|
+
PRIMARY KEY (agent, project_id)
|
|
13435
|
+
)
|
|
13436
|
+
`);
|
|
13437
|
+
const insertPresence = db2.prepare(`
|
|
13438
|
+
INSERT INTO agent_presence_new (id, agent, session_id, role, project_id, status, last_seen_at, created_at, metadata)
|
|
13439
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
13440
|
+
`);
|
|
13441
|
+
for (const [dedupeKey, row] of dedupedRows) {
|
|
13442
|
+
const [agent, projectKey] = dedupeKey.split("\x00");
|
|
13443
|
+
const id = normalizePresenceText(row.id) ?? crypto.randomUUID().slice(0, 8);
|
|
13444
|
+
const sessionId = normalizePresenceText(row.session_id);
|
|
13445
|
+
const role = normalizePresenceText(row.role) ?? "agent";
|
|
13446
|
+
const projectId = projectKey;
|
|
13447
|
+
const status = normalizePresenceText(row.status) ?? "online";
|
|
13448
|
+
const lastSeenAt = normalizePresenceText(row.last_seen_at) ?? fallbackNow;
|
|
13449
|
+
const createdAt = normalizePresenceText(row.created_at) ?? lastSeenAt;
|
|
13450
|
+
const metadata = typeof row.metadata === "string" ? row.metadata : row.metadata == null ? null : JSON.stringify(row.metadata);
|
|
13451
|
+
insertPresence.run(id, agent, sessionId, role, projectId, status, lastSeenAt, createdAt, metadata);
|
|
13452
|
+
}
|
|
13453
|
+
db2.exec("DROP TABLE agent_presence");
|
|
13454
|
+
db2.exec("ALTER TABLE agent_presence_new RENAME TO agent_presence");
|
|
13455
|
+
db2.exec("COMMIT");
|
|
13456
|
+
} catch (error) {
|
|
13457
|
+
db2.exec("ROLLBACK");
|
|
13458
|
+
throw error;
|
|
13459
|
+
}
|
|
13460
|
+
}
|
|
13461
|
+
function collapseDuplicateAgentPresenceRows(db2) {
|
|
13462
|
+
const rows = db2.prepare("SELECT rowid AS _rowid, * FROM agent_presence").all();
|
|
13463
|
+
rows.sort((left, right) => {
|
|
13464
|
+
const lastSeenDelta = parsePresenceTimestamp(right.last_seen_at) - parsePresenceTimestamp(left.last_seen_at);
|
|
13465
|
+
if (lastSeenDelta !== 0)
|
|
13466
|
+
return lastSeenDelta;
|
|
13467
|
+
const createdDelta = parsePresenceTimestamp(right.created_at) - parsePresenceTimestamp(left.created_at);
|
|
13468
|
+
if (createdDelta !== 0)
|
|
13469
|
+
return createdDelta;
|
|
13470
|
+
const projectDelta = Number(Boolean(normalizePresenceText(right.project_id))) - Number(Boolean(normalizePresenceText(left.project_id)));
|
|
13471
|
+
if (projectDelta !== 0)
|
|
13472
|
+
return projectDelta;
|
|
13473
|
+
return right._rowid - left._rowid;
|
|
13474
|
+
});
|
|
13475
|
+
const rowIdsToDelete = [];
|
|
13476
|
+
const seenAgents = new Set;
|
|
13477
|
+
for (const row of rows) {
|
|
13478
|
+
const normalizedAgent = normalizePresenceText(row.agent)?.toLowerCase();
|
|
13479
|
+
if (!normalizedAgent)
|
|
13480
|
+
continue;
|
|
13481
|
+
if (seenAgents.has(normalizedAgent)) {
|
|
13482
|
+
rowIdsToDelete.push(row._rowid);
|
|
13483
|
+
continue;
|
|
13484
|
+
}
|
|
13485
|
+
seenAgents.add(normalizedAgent);
|
|
13486
|
+
}
|
|
13487
|
+
if (rowIdsToDelete.length === 0)
|
|
13488
|
+
return;
|
|
13489
|
+
db2.exec("BEGIN");
|
|
13490
|
+
try {
|
|
13491
|
+
const deleteRow = db2.prepare("DELETE FROM agent_presence WHERE rowid = ?");
|
|
13492
|
+
for (const rowId of rowIdsToDelete) {
|
|
13493
|
+
deleteRow.run(rowId);
|
|
13494
|
+
}
|
|
13495
|
+
db2.exec("COMMIT");
|
|
13496
|
+
} catch (error) {
|
|
13497
|
+
db2.exec("ROLLBACK");
|
|
13498
|
+
throw error;
|
|
13499
|
+
}
|
|
13500
|
+
}
|
|
13501
|
+
function ensureAgentPresenceAgentUniqueIndex(db2) {
|
|
13502
|
+
collapseDuplicateAgentPresenceRows(db2);
|
|
13503
|
+
db2.exec("CREATE UNIQUE INDEX IF NOT EXISTS idx_agent_presence_agent_unique ON agent_presence(agent)");
|
|
13504
|
+
}
|
|
13360
13505
|
function getDb() {
|
|
13361
13506
|
if (db)
|
|
13362
13507
|
return db;
|
|
@@ -13429,16 +13574,18 @@ function getDb() {
|
|
|
13429
13574
|
db.exec(`
|
|
13430
13575
|
CREATE TABLE IF NOT EXISTS agent_presence (
|
|
13431
13576
|
id TEXT NOT NULL,
|
|
13432
|
-
agent TEXT
|
|
13577
|
+
agent TEXT NOT NULL,
|
|
13433
13578
|
session_id TEXT,
|
|
13434
13579
|
role TEXT NOT NULL DEFAULT 'agent',
|
|
13435
|
-
project_id TEXT,
|
|
13580
|
+
project_id TEXT NOT NULL DEFAULT '',
|
|
13436
13581
|
status TEXT NOT NULL DEFAULT 'online',
|
|
13437
13582
|
last_seen_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%f', 'now')),
|
|
13438
13583
|
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%f', 'now')),
|
|
13439
|
-
metadata TEXT
|
|
13584
|
+
metadata TEXT,
|
|
13585
|
+
PRIMARY KEY (agent, project_id)
|
|
13440
13586
|
)
|
|
13441
13587
|
`);
|
|
13588
|
+
ensureAgentPresenceAgentUniqueIndex(db);
|
|
13442
13589
|
db.exec(`
|
|
13443
13590
|
CREATE TABLE IF NOT EXISTS resource_locks (
|
|
13444
13591
|
resource_type TEXT NOT NULL,
|
|
@@ -13540,8 +13687,13 @@ function getDb() {
|
|
|
13540
13687
|
db.exec("UPDATE messages SET uuid = lower(hex(randomblob(16))) WHERE uuid IS NULL");
|
|
13541
13688
|
db.exec("CREATE UNIQUE INDEX IF NOT EXISTS idx_messages_uuid ON messages(uuid)");
|
|
13542
13689
|
}
|
|
13543
|
-
|
|
13544
|
-
|
|
13690
|
+
let presenceCols = db.prepare("PRAGMA table_info(agent_presence)").all();
|
|
13691
|
+
let presenceColNames = presenceCols.map((c) => c.name);
|
|
13692
|
+
if (shouldRebuildAgentPresenceTable(presenceCols)) {
|
|
13693
|
+
rebuildLegacyAgentPresenceTable(db);
|
|
13694
|
+
presenceCols = db.prepare("PRAGMA table_info(agent_presence)").all();
|
|
13695
|
+
presenceColNames = presenceCols.map((c) => c.name);
|
|
13696
|
+
}
|
|
13545
13697
|
if (!presenceColNames.includes("id")) {
|
|
13546
13698
|
db.exec("ALTER TABLE agent_presence ADD COLUMN id TEXT NOT NULL DEFAULT ''");
|
|
13547
13699
|
const rows = db.prepare("SELECT agent FROM agent_presence").all();
|
|
@@ -13562,7 +13714,9 @@ function getDb() {
|
|
|
13562
13714
|
}
|
|
13563
13715
|
if (!presenceColNames.includes("project_id")) {
|
|
13564
13716
|
db.exec("ALTER TABLE agent_presence ADD COLUMN project_id TEXT");
|
|
13717
|
+
db.exec("UPDATE agent_presence SET project_id = '' WHERE project_id IS NULL");
|
|
13565
13718
|
}
|
|
13719
|
+
ensureAgentPresenceAgentUniqueIndex(db);
|
|
13566
13720
|
db.exec(`
|
|
13567
13721
|
CREATE TABLE IF NOT EXISTS message_read_receipts (
|
|
13568
13722
|
message_id INTEGER NOT NULL REFERENCES messages(id) ON DELETE CASCADE,
|
|
@@ -13969,6 +14123,8 @@ var init_spaces = __esm(() => {
|
|
|
13969
14123
|
// src/lib/webhooks.ts
|
|
13970
14124
|
import { readFileSync as readFileSync5 } from "fs";
|
|
13971
14125
|
import { join as join10 } from "path";
|
|
14126
|
+
import dns from "dns";
|
|
14127
|
+
import net from "net";
|
|
13972
14128
|
function getConfigPath2() {
|
|
13973
14129
|
return process.env.CONVERSATIONS_CONFIG_PATH || join10(getDataDir2(), "config.json");
|
|
13974
14130
|
}
|
|
@@ -14000,6 +14156,44 @@ function matchesEvent(webhook, msg) {
|
|
|
14000
14156
|
}
|
|
14001
14157
|
return false;
|
|
14002
14158
|
}
|
|
14159
|
+
function isPrivateIP(ip) {
|
|
14160
|
+
if (net.isIPv4(ip)) {
|
|
14161
|
+
const parts = ip.split(".").map(Number);
|
|
14162
|
+
if (parts[0] === 10)
|
|
14163
|
+
return true;
|
|
14164
|
+
if (parts[0] === 172 && parts[1] >= 16 && parts[1] <= 31)
|
|
14165
|
+
return true;
|
|
14166
|
+
if (parts[0] === 192 && parts[1] === 168)
|
|
14167
|
+
return true;
|
|
14168
|
+
if (parts[0] === 127)
|
|
14169
|
+
return true;
|
|
14170
|
+
if (parts[0] === 169 && parts[1] === 254)
|
|
14171
|
+
return true;
|
|
14172
|
+
if (parts[0] === 0)
|
|
14173
|
+
return true;
|
|
14174
|
+
} else if (net.isIPv6(ip)) {
|
|
14175
|
+
const lower = ip.toLowerCase();
|
|
14176
|
+
if (lower === "::1" || lower.startsWith("::ffff:") || lower.startsWith("fc") || lower.startsWith("fd"))
|
|
14177
|
+
return true;
|
|
14178
|
+
}
|
|
14179
|
+
return false;
|
|
14180
|
+
}
|
|
14181
|
+
async function validateWebhookUrl(urlStr) {
|
|
14182
|
+
try {
|
|
14183
|
+
const url = new URL(urlStr);
|
|
14184
|
+
if (url.protocol !== "https:")
|
|
14185
|
+
return false;
|
|
14186
|
+
const hostname2 = url.hostname;
|
|
14187
|
+
if (hostname2 === "localhost" || hostname2 === "0.0.0.0" || hostname2 === "127.0.0.1" || hostname2 === "::1")
|
|
14188
|
+
return false;
|
|
14189
|
+
const addresses = await dns.promises.lookup(hostname2, { all: true });
|
|
14190
|
+
if (!Array.isArray(addresses))
|
|
14191
|
+
return false;
|
|
14192
|
+
return !addresses.some((a) => isPrivateIP(a.address));
|
|
14193
|
+
} catch {
|
|
14194
|
+
return false;
|
|
14195
|
+
}
|
|
14196
|
+
}
|
|
14003
14197
|
function fireWebhooks(msg) {
|
|
14004
14198
|
const config = loadConfig();
|
|
14005
14199
|
if (!config.webhooks || config.webhooks.length === 0)
|
|
@@ -14009,20 +14203,24 @@ function fireWebhooks(msg) {
|
|
|
14009
14203
|
continue;
|
|
14010
14204
|
if (!matchesEvent(webhook, msg))
|
|
14011
14205
|
continue;
|
|
14012
|
-
|
|
14013
|
-
|
|
14014
|
-
|
|
14015
|
-
|
|
14016
|
-
|
|
14017
|
-
|
|
14018
|
-
|
|
14019
|
-
|
|
14020
|
-
|
|
14021
|
-
|
|
14022
|
-
|
|
14023
|
-
|
|
14024
|
-
|
|
14025
|
-
|
|
14206
|
+
validateWebhookUrl(webhook.url).then((valid) => {
|
|
14207
|
+
if (!valid)
|
|
14208
|
+
return;
|
|
14209
|
+
fetch(webhook.url, {
|
|
14210
|
+
method: "POST",
|
|
14211
|
+
headers: { "Content-Type": "application/json" },
|
|
14212
|
+
body: JSON.stringify({
|
|
14213
|
+
id: msg.id,
|
|
14214
|
+
from: msg.from_agent,
|
|
14215
|
+
to: msg.to_agent,
|
|
14216
|
+
space: msg.space,
|
|
14217
|
+
content: msg.content,
|
|
14218
|
+
priority: msg.priority,
|
|
14219
|
+
blocking: msg.blocking,
|
|
14220
|
+
created_at: msg.created_at
|
|
14221
|
+
})
|
|
14222
|
+
}).catch(() => {});
|
|
14223
|
+
});
|
|
14026
14224
|
}
|
|
14027
14225
|
}
|
|
14028
14226
|
var cachedConfig = null, configLoadedAt = 0, CONFIG_CACHE_MS = 1e4;
|
|
@@ -14032,8 +14230,8 @@ var init_webhooks = __esm(() => {
|
|
|
14032
14230
|
|
|
14033
14231
|
// src/lib/messages.ts
|
|
14034
14232
|
import { randomUUID } from "crypto";
|
|
14035
|
-
import { mkdirSync as mkdirSync7, copyFileSync as copyFileSync3, statSync as statSync2 } from "fs";
|
|
14036
|
-
import { join as join11 } from "path";
|
|
14233
|
+
import { mkdirSync as mkdirSync7, copyFileSync as copyFileSync3, statSync as statSync2, existsSync as existsSync9, realpathSync } from "fs";
|
|
14234
|
+
import { join as join11, basename, resolve } from "path";
|
|
14037
14235
|
function compactMessage(msg) {
|
|
14038
14236
|
const result = {};
|
|
14039
14237
|
for (const key of Object.keys(msg)) {
|
|
@@ -14074,6 +14272,22 @@ function getAttachmentsDir() {
|
|
|
14074
14272
|
return process.env.CONVERSATIONS_ATTACHMENTS_DIR;
|
|
14075
14273
|
return join11(getDataDir2(), "attachments");
|
|
14076
14274
|
}
|
|
14275
|
+
function validateAttachment(sourcePath, name) {
|
|
14276
|
+
const absolute = resolve(sourcePath);
|
|
14277
|
+
if (!existsSync9(absolute)) {
|
|
14278
|
+
throw new Error(`Attachment source not found: ${sourcePath}`);
|
|
14279
|
+
}
|
|
14280
|
+
const real = realpathSync(absolute);
|
|
14281
|
+
const stat = statSync2(real);
|
|
14282
|
+
if (!stat.isFile()) {
|
|
14283
|
+
throw new Error(`Attachment source must be a regular file: ${sourcePath}`);
|
|
14284
|
+
}
|
|
14285
|
+
const safeName = basename(name.replace(/\0/g, ""));
|
|
14286
|
+
if (!safeName || safeName.startsWith(".")) {
|
|
14287
|
+
throw new Error(`Invalid attachment name: ${name}`);
|
|
14288
|
+
}
|
|
14289
|
+
return { safeSource: real, safeName };
|
|
14290
|
+
}
|
|
14077
14291
|
function guessMimeType(name) {
|
|
14078
14292
|
const ext = name.split(".").pop()?.toLowerCase();
|
|
14079
14293
|
const mimeMap = {
|
|
@@ -14141,14 +14355,15 @@ function sendMessage(opts) {
|
|
|
14141
14355
|
mkdirSync7(attachmentsDir, { recursive: true });
|
|
14142
14356
|
const attachmentInfos = [];
|
|
14143
14357
|
for (const att of opts.attachments) {
|
|
14144
|
-
const
|
|
14145
|
-
|
|
14358
|
+
const { safeSource, safeName } = validateAttachment(att.source_path, att.name);
|
|
14359
|
+
const destPath = join11(attachmentsDir, safeName);
|
|
14360
|
+
copyFileSync3(safeSource, destPath);
|
|
14146
14361
|
const stat = statSync2(destPath);
|
|
14147
14362
|
attachmentInfos.push({
|
|
14148
|
-
name:
|
|
14363
|
+
name: safeName,
|
|
14149
14364
|
path: destPath,
|
|
14150
14365
|
size: stat.size,
|
|
14151
|
-
mime_type: guessMimeType(
|
|
14366
|
+
mime_type: guessMimeType(safeName)
|
|
14152
14367
|
});
|
|
14153
14368
|
}
|
|
14154
14369
|
const attachmentsJson = JSON.stringify(attachmentInfos);
|
|
@@ -14210,8 +14425,10 @@ function readMessages(opts = {}) {
|
|
|
14210
14425
|
const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
14211
14426
|
const resolvedLimit = isLatest ? Math.floor(opts.latest) : Number.isFinite(opts.limit) && opts.limit > 0 ? Math.floor(opts.limit) : 20;
|
|
14212
14427
|
const order = isLatest ? "DESC" : opts.order?.toLowerCase() === "desc" ? "DESC" : "ASC";
|
|
14213
|
-
const resolvedOffset =
|
|
14214
|
-
const
|
|
14428
|
+
const resolvedOffset = Number.isFinite(opts.offset) ? Math.floor(opts.offset) : 0;
|
|
14429
|
+
const safeLimit = Math.max(1, Math.min(resolvedLimit, 1e4));
|
|
14430
|
+
const safeOffset = Math.max(0, Math.floor(resolvedOffset));
|
|
14431
|
+
const rows = db2.prepare(`SELECT * FROM messages ${where} ORDER BY created_at ${order}, id ${order} LIMIT ${safeLimit} OFFSET ${safeOffset}`).all(...params);
|
|
14215
14432
|
let messages = rows.map(parseMessage);
|
|
14216
14433
|
if (opts.include_reply_counts && messages.length > 0) {
|
|
14217
14434
|
const db22 = getDb();
|
|
@@ -14257,10 +14474,20 @@ function getMessageById(id) {
|
|
|
14257
14474
|
const row = db2.prepare("SELECT * FROM messages WHERE id = ?").get(id);
|
|
14258
14475
|
return row ? parseMessage(row) : null;
|
|
14259
14476
|
}
|
|
14260
|
-
function markReadByIds(ids) {
|
|
14477
|
+
function markReadByIds(ids, agent) {
|
|
14261
14478
|
const db2 = getDb();
|
|
14262
14479
|
if (ids.length === 0)
|
|
14263
14480
|
return 0;
|
|
14481
|
+
if (agent) {
|
|
14482
|
+
const stmt2 = db2.prepare(`INSERT OR REPLACE INTO message_read_receipts (message_id, agent, read_at)
|
|
14483
|
+
VALUES (?, ?, strftime('%Y-%m-%dT%H:%M:%f', 'now'))`);
|
|
14484
|
+
const normalized = agent.toLowerCase();
|
|
14485
|
+
for (const id of ids)
|
|
14486
|
+
stmt2.run(id, normalized);
|
|
14487
|
+
const placeholders2 = ids.map(() => "?").join(", ");
|
|
14488
|
+
const update = db2.prepare(`UPDATE messages SET read_at = strftime('%Y-%m-%dT%H:%M:%f', 'now') WHERE id IN (${placeholders2}) AND read_at IS NULL`);
|
|
14489
|
+
return update.run(...ids).changes;
|
|
14490
|
+
}
|
|
14264
14491
|
const placeholders = ids.map(() => "?").join(", ");
|
|
14265
14492
|
const stmt = db2.prepare(`UPDATE messages SET read_at = strftime('%Y-%m-%dT%H:%M:%f', 'now') WHERE id IN (${placeholders}) AND read_at IS NULL`);
|
|
14266
14493
|
const result = stmt.run(...ids);
|
|
@@ -14408,8 +14635,9 @@ function getPinnedMessages(opts) {
|
|
|
14408
14635
|
params.push(opts.session_id);
|
|
14409
14636
|
}
|
|
14410
14637
|
const where = `WHERE ${conditions.join(" AND ")}`;
|
|
14411
|
-
const
|
|
14412
|
-
const
|
|
14638
|
+
const safeLimit = Number.isFinite(opts?.limit) && opts.limit > 0 ? Math.floor(opts.limit) : 0;
|
|
14639
|
+
const limitClause = safeLimit > 0 ? `LIMIT ${safeLimit}` : "";
|
|
14640
|
+
const rows = db2.prepare(`SELECT * FROM messages ${where} ORDER BY pinned_at DESC, id DESC ${limitClause}`).all(...params);
|
|
14413
14641
|
return rows.map(parseMessage);
|
|
14414
14642
|
}
|
|
14415
14643
|
function getUnreadBlockers(agent) {
|
|
@@ -14598,11 +14826,11 @@ function getMessagesForAgent(agent, opts) {
|
|
|
14598
14826
|
if (opts?.unread_only) {
|
|
14599
14827
|
conditions.push("mm.notified_at IS NULL");
|
|
14600
14828
|
}
|
|
14601
|
-
const
|
|
14829
|
+
const safeLimit = Math.max(1, Math.min(Math.floor(opts?.limit ?? 50), 1000));
|
|
14602
14830
|
const rows = db2.prepare(`SELECT m.*, mm.id AS mention_id FROM messages m
|
|
14603
14831
|
JOIN message_mentions mm ON mm.message_id = m.id
|
|
14604
14832
|
WHERE ${conditions.join(" AND ")}
|
|
14605
|
-
ORDER BY m.created_at DESC LIMIT ${
|
|
14833
|
+
ORDER BY m.created_at DESC LIMIT ${safeLimit}`).all(...params);
|
|
14606
14834
|
return rows.map(({ mention_id, ...row }) => ({ message: parseMessage(row), mention_id }));
|
|
14607
14835
|
}
|
|
14608
14836
|
function markMentionsRead(agent, space) {
|
|
@@ -14797,6 +15025,33 @@ var renderInline = (text) => {
|
|
|
14797
15025
|
var init_terminal_markdown = () => {};
|
|
14798
15026
|
|
|
14799
15027
|
// src/lib/presence.ts
|
|
15028
|
+
function normalizeAgentName(name) {
|
|
15029
|
+
return name.trim().toLowerCase();
|
|
15030
|
+
}
|
|
15031
|
+
function toStoredProjectId(projectId) {
|
|
15032
|
+
const normalized = projectId?.trim() ?? "";
|
|
15033
|
+
return normalized || "";
|
|
15034
|
+
}
|
|
15035
|
+
function fromStoredProjectId(projectId) {
|
|
15036
|
+
const normalized = typeof projectId === "string" ? projectId.trim() : "";
|
|
15037
|
+
return normalized || null;
|
|
15038
|
+
}
|
|
15039
|
+
function getPresenceByAgent(db2, agent) {
|
|
15040
|
+
return db2.prepare(`
|
|
15041
|
+
SELECT * FROM agent_presence
|
|
15042
|
+
WHERE LOWER(agent) = ?
|
|
15043
|
+
ORDER BY last_seen_at DESC
|
|
15044
|
+
LIMIT 1
|
|
15045
|
+
`).get(agent);
|
|
15046
|
+
}
|
|
15047
|
+
function getPresenceByAgentAndProject(db2, agent, projectId) {
|
|
15048
|
+
return db2.prepare(`
|
|
15049
|
+
SELECT * FROM agent_presence
|
|
15050
|
+
WHERE LOWER(agent) = ? AND COALESCE(project_id, '') = ?
|
|
15051
|
+
ORDER BY last_seen_at DESC
|
|
15052
|
+
LIMIT 1
|
|
15053
|
+
`).get(agent, projectId);
|
|
15054
|
+
}
|
|
14800
15055
|
function parsePresence(row) {
|
|
14801
15056
|
let metadata = null;
|
|
14802
15057
|
if (row.metadata) {
|
|
@@ -14815,7 +15070,7 @@ function parsePresence(row) {
|
|
|
14815
15070
|
agent: row.agent,
|
|
14816
15071
|
session_id: row.session_id ?? null,
|
|
14817
15072
|
role: row.role || "agent",
|
|
14818
|
-
project_id: row.project_id
|
|
15073
|
+
project_id: fromStoredProjectId(row.project_id),
|
|
14819
15074
|
status: row.status,
|
|
14820
15075
|
last_seen_at: lastSeenAt,
|
|
14821
15076
|
created_at: row.created_at || lastSeenAt,
|
|
@@ -14833,9 +15088,10 @@ function isAgentConflict(result) {
|
|
|
14833
15088
|
}
|
|
14834
15089
|
function registerAgent(name, sessionId, role, projectId) {
|
|
14835
15090
|
const db2 = getDb();
|
|
14836
|
-
const normalizedName = name
|
|
15091
|
+
const normalizedName = normalizeAgentName(name);
|
|
14837
15092
|
const result = db2.transaction(() => {
|
|
14838
|
-
const existing = db2
|
|
15093
|
+
const existing = getPresenceByAgent(db2, normalizedName);
|
|
15094
|
+
const storedProjectId = toStoredProjectId(projectId ?? existing?.project_id);
|
|
14839
15095
|
if (existing) {
|
|
14840
15096
|
const lastSeenAt = existing.last_seen_at;
|
|
14841
15097
|
const existingSessionId = existing.session_id;
|
|
@@ -14853,12 +15109,26 @@ function registerAgent(name, sessionId, role, projectId) {
|
|
|
14853
15109
|
};
|
|
14854
15110
|
}
|
|
14855
15111
|
const tookOver = existingSessionId !== sessionId;
|
|
14856
|
-
|
|
14857
|
-
|
|
14858
|
-
|
|
14859
|
-
|
|
14860
|
-
|
|
14861
|
-
|
|
15112
|
+
const existingId = existing.id;
|
|
15113
|
+
const target = getPresenceByAgentAndProject(db2, normalizedName, storedProjectId);
|
|
15114
|
+
if (target && target.id !== existingId) {
|
|
15115
|
+
db2.prepare(`
|
|
15116
|
+
UPDATE agent_presence
|
|
15117
|
+
SET session_id = ?, role = ?, status = 'online', last_seen_at = strftime('%Y-%m-%dT%H:%M:%f', 'now')
|
|
15118
|
+
WHERE id = ?
|
|
15119
|
+
`).run(sessionId, role || existing.role || "agent", target.id);
|
|
15120
|
+
db2.prepare("DELETE FROM agent_presence WHERE id = ?").run(existingId);
|
|
15121
|
+
} else {
|
|
15122
|
+
db2.prepare(`
|
|
15123
|
+
UPDATE agent_presence
|
|
15124
|
+
SET session_id = ?, role = ?, project_id = ?, status = 'online', last_seen_at = strftime('%Y-%m-%dT%H:%M:%f', 'now')
|
|
15125
|
+
WHERE id = ?
|
|
15126
|
+
`).run(sessionId, role || existing.role || "agent", storedProjectId, existingId);
|
|
15127
|
+
}
|
|
15128
|
+
const updated = getPresenceByAgentAndProject(db2, normalizedName, storedProjectId) ?? getPresenceByAgent(db2, normalizedName);
|
|
15129
|
+
if (!updated) {
|
|
15130
|
+
throw new Error(`Failed to update presence for agent "${normalizedName}"`);
|
|
15131
|
+
}
|
|
14862
15132
|
return { agent: parsePresence(updated), created: false, took_over: tookOver };
|
|
14863
15133
|
}
|
|
14864
15134
|
const id = crypto.randomUUID().slice(0, 8);
|
|
@@ -14866,33 +15136,60 @@ function registerAgent(name, sessionId, role, projectId) {
|
|
|
14866
15136
|
db2.prepare(`
|
|
14867
15137
|
INSERT INTO agent_presence (id, agent, session_id, role, project_id, status, last_seen_at, created_at)
|
|
14868
15138
|
VALUES (?, ?, ?, ?, ?, 'online', strftime('%Y-%m-%dT%H:%M:%f', 'now'), strftime('%Y-%m-%dT%H:%M:%f', 'now'))
|
|
14869
|
-
`).run(id, normalizedName, sessionId, resolvedRole,
|
|
14870
|
-
const created = db2
|
|
15139
|
+
`).run(id, normalizedName, sessionId, resolvedRole, storedProjectId);
|
|
15140
|
+
const created = getPresenceByAgentAndProject(db2, normalizedName, storedProjectId) ?? getPresenceByAgent(db2, normalizedName);
|
|
15141
|
+
if (!created) {
|
|
15142
|
+
throw new Error(`Failed to create presence for agent "${normalizedName}"`);
|
|
15143
|
+
}
|
|
14871
15144
|
return { agent: parsePresence(created), created: true, took_over: false };
|
|
14872
15145
|
});
|
|
14873
15146
|
return result;
|
|
14874
15147
|
}
|
|
14875
|
-
function heartbeat(agent, status, metadata, sessionId) {
|
|
15148
|
+
function heartbeat(agent, status, metadata, sessionId, projectId) {
|
|
14876
15149
|
const db2 = getDb();
|
|
14877
15150
|
const metadataJson = metadata ? JSON.stringify(metadata) : null;
|
|
14878
15151
|
const resolvedStatus = status || "online";
|
|
14879
|
-
const normalizedAgent = agent
|
|
14880
|
-
|
|
14881
|
-
|
|
14882
|
-
|
|
14883
|
-
|
|
14884
|
-
|
|
14885
|
-
|
|
14886
|
-
|
|
14887
|
-
|
|
14888
|
-
|
|
14889
|
-
|
|
14890
|
-
|
|
15152
|
+
const normalizedAgent = normalizeAgentName(agent);
|
|
15153
|
+
db2.transaction(() => {
|
|
15154
|
+
const existing = getPresenceByAgent(db2, normalizedAgent);
|
|
15155
|
+
const storedProjectId = toStoredProjectId(projectId ?? existing?.project_id);
|
|
15156
|
+
const id = existing?.id || crypto.randomUUID().slice(0, 8);
|
|
15157
|
+
if (existing) {
|
|
15158
|
+
const existingId = existing.id;
|
|
15159
|
+
const target = getPresenceByAgentAndProject(db2, normalizedAgent, storedProjectId);
|
|
15160
|
+
if (target && target.id !== existingId) {
|
|
15161
|
+
db2.prepare(`
|
|
15162
|
+
UPDATE agent_presence
|
|
15163
|
+
SET status = ?,
|
|
15164
|
+
last_seen_at = strftime('%Y-%m-%dT%H:%M:%f', 'now'),
|
|
15165
|
+
session_id = COALESCE(?, session_id),
|
|
15166
|
+
metadata = ?
|
|
15167
|
+
WHERE id = ?
|
|
15168
|
+
`).run(resolvedStatus, sessionId ?? null, metadataJson, target.id);
|
|
15169
|
+
db2.prepare("DELETE FROM agent_presence WHERE id = ?").run(existingId);
|
|
15170
|
+
return;
|
|
15171
|
+
}
|
|
15172
|
+
db2.prepare(`
|
|
15173
|
+
UPDATE agent_presence
|
|
15174
|
+
SET status = ?,
|
|
15175
|
+
last_seen_at = strftime('%Y-%m-%dT%H:%M:%f', 'now'),
|
|
15176
|
+
session_id = COALESCE(?, session_id),
|
|
15177
|
+
metadata = ?,
|
|
15178
|
+
project_id = ?
|
|
15179
|
+
WHERE id = ?
|
|
15180
|
+
`).run(resolvedStatus, sessionId ?? null, metadataJson, storedProjectId, existingId);
|
|
15181
|
+
return;
|
|
15182
|
+
}
|
|
15183
|
+
db2.prepare(`
|
|
15184
|
+
INSERT INTO agent_presence (id, agent, session_id, role, project_id, status, last_seen_at, created_at, metadata)
|
|
15185
|
+
VALUES (?, ?, ?, 'agent', ?, ?, strftime('%Y-%m-%dT%H:%M:%f', 'now'), strftime('%Y-%m-%dT%H:%M:%f', 'now'), ?)
|
|
15186
|
+
`).run(id, normalizedAgent, sessionId ?? null, storedProjectId, resolvedStatus, metadataJson);
|
|
15187
|
+
});
|
|
14891
15188
|
}
|
|
14892
15189
|
function getPresence(agent) {
|
|
14893
15190
|
const db2 = getDb();
|
|
14894
|
-
const normalizedAgent = agent
|
|
14895
|
-
const row = db2
|
|
15191
|
+
const normalizedAgent = normalizeAgentName(agent);
|
|
15192
|
+
const row = getPresenceByAgent(db2, normalizedAgent);
|
|
14896
15193
|
return row ? parsePresence(row) : null;
|
|
14897
15194
|
}
|
|
14898
15195
|
function listAgents(opts) {
|
|
@@ -14907,14 +15204,14 @@ function listAgents(opts) {
|
|
|
14907
15204
|
}
|
|
14908
15205
|
function removePresence(agent) {
|
|
14909
15206
|
const db2 = getDb();
|
|
14910
|
-
const normalizedAgent = agent
|
|
15207
|
+
const normalizedAgent = normalizeAgentName(agent);
|
|
14911
15208
|
const result = db2.prepare("DELETE FROM agent_presence WHERE LOWER(agent) = ?").run(normalizedAgent);
|
|
14912
15209
|
return result.changes > 0;
|
|
14913
15210
|
}
|
|
14914
15211
|
function renameAgent(oldName, newName) {
|
|
14915
15212
|
const db2 = getDb();
|
|
14916
|
-
const normalizedOld = oldName
|
|
14917
|
-
const normalizedNew = newName
|
|
15213
|
+
const normalizedOld = normalizeAgentName(oldName);
|
|
15214
|
+
const normalizedNew = normalizeAgentName(newName);
|
|
14918
15215
|
const existing = db2.prepare("SELECT agent FROM agent_presence WHERE LOWER(agent) = ?").get(normalizedOld);
|
|
14919
15216
|
if (!existing)
|
|
14920
15217
|
return false;
|
|
@@ -14924,6 +15221,40 @@ function renameAgent(oldName, newName) {
|
|
|
14924
15221
|
db2.prepare("UPDATE agent_presence SET agent = ? WHERE LOWER(agent) = ?").run(normalizedNew, normalizedOld);
|
|
14925
15222
|
return true;
|
|
14926
15223
|
}
|
|
15224
|
+
function setPresenceProject(agent, projectId) {
|
|
15225
|
+
const db2 = getDb();
|
|
15226
|
+
const normalizedAgent = normalizeAgentName(agent);
|
|
15227
|
+
const desiredProjectId = toStoredProjectId(projectId);
|
|
15228
|
+
const latest = getPresenceByAgent(db2, normalizedAgent);
|
|
15229
|
+
if (!latest) {
|
|
15230
|
+
heartbeat(normalizedAgent, "online", undefined, undefined, projectId);
|
|
15231
|
+
return;
|
|
15232
|
+
}
|
|
15233
|
+
const currentProjectId = toStoredProjectId(latest.project_id);
|
|
15234
|
+
if (currentProjectId === desiredProjectId)
|
|
15235
|
+
return;
|
|
15236
|
+
db2.transaction(() => {
|
|
15237
|
+
const latestId = latest.id;
|
|
15238
|
+
const target = getPresenceByAgentAndProject(db2, normalizedAgent, desiredProjectId);
|
|
15239
|
+
if (target && target.id !== latestId) {
|
|
15240
|
+
db2.prepare(`
|
|
15241
|
+
UPDATE agent_presence
|
|
15242
|
+
SET status = ?,
|
|
15243
|
+
last_seen_at = strftime('%Y-%m-%dT%H:%M:%f', 'now'),
|
|
15244
|
+
session_id = COALESCE(?, session_id),
|
|
15245
|
+
metadata = COALESCE(?, metadata)
|
|
15246
|
+
WHERE LOWER(agent) = ? AND COALESCE(project_id, '') = ?
|
|
15247
|
+
`).run(latest.status || target.status || "online", latest.session_id ?? null, latest.metadata ?? null, normalizedAgent, desiredProjectId);
|
|
15248
|
+
db2.prepare("DELETE FROM agent_presence WHERE id = ?").run(latestId);
|
|
15249
|
+
return;
|
|
15250
|
+
}
|
|
15251
|
+
db2.prepare(`
|
|
15252
|
+
UPDATE agent_presence
|
|
15253
|
+
SET project_id = ?, last_seen_at = strftime('%Y-%m-%dT%H:%M:%f', 'now')
|
|
15254
|
+
WHERE id = ?
|
|
15255
|
+
`).run(desiredProjectId, latestId);
|
|
15256
|
+
});
|
|
15257
|
+
}
|
|
14927
15258
|
var ONLINE_THRESHOLD_SECONDS = 60, CONFLICT_THRESHOLD_SECONDS;
|
|
14928
15259
|
var init_presence = __esm(() => {
|
|
14929
15260
|
init_db();
|
|
@@ -14934,7 +15265,7 @@ var init_presence = __esm(() => {
|
|
|
14934
15265
|
var require_package = __commonJS((exports, module) => {
|
|
14935
15266
|
module.exports = {
|
|
14936
15267
|
name: "@hasna/conversations",
|
|
14937
|
-
version: "0.2.
|
|
15268
|
+
version: "0.2.44",
|
|
14938
15269
|
description: "Real-time CLI messaging for AI agents",
|
|
14939
15270
|
type: "module",
|
|
14940
15271
|
bin: {
|
|
@@ -15732,7 +16063,7 @@ var init_graph = __esm(() => {
|
|
|
15732
16063
|
import chalk9 from "chalk";
|
|
15733
16064
|
import { execSync } from "child_process";
|
|
15734
16065
|
function sleep(ms) {
|
|
15735
|
-
return new Promise((
|
|
16066
|
+
return new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
15736
16067
|
}
|
|
15737
16068
|
function countLines(message) {
|
|
15738
16069
|
const matches = message.match(/\r?\n/g);
|
|
@@ -36508,7 +36839,7 @@ class Protocol {
|
|
|
36508
36839
|
return;
|
|
36509
36840
|
}
|
|
36510
36841
|
const pollInterval = task2.pollInterval ?? this._options?.defaultTaskPollInterval ?? 1000;
|
|
36511
|
-
await new Promise((
|
|
36842
|
+
await new Promise((resolve2) => setTimeout(resolve2, pollInterval));
|
|
36512
36843
|
options?.signal?.throwIfAborted();
|
|
36513
36844
|
}
|
|
36514
36845
|
} catch (error48) {
|
|
@@ -36520,7 +36851,7 @@ class Protocol {
|
|
|
36520
36851
|
}
|
|
36521
36852
|
request(request, resultSchema, options) {
|
|
36522
36853
|
const { relatedRequestId, resumptionToken, onresumptiontoken, task, relatedTask } = options ?? {};
|
|
36523
|
-
return new Promise((
|
|
36854
|
+
return new Promise((resolve2, reject) => {
|
|
36524
36855
|
const earlyReject = (error48) => {
|
|
36525
36856
|
reject(error48);
|
|
36526
36857
|
};
|
|
@@ -36598,7 +36929,7 @@ class Protocol {
|
|
|
36598
36929
|
if (!parseResult.success) {
|
|
36599
36930
|
reject(parseResult.error);
|
|
36600
36931
|
} else {
|
|
36601
|
-
|
|
36932
|
+
resolve2(parseResult.data);
|
|
36602
36933
|
}
|
|
36603
36934
|
} catch (error48) {
|
|
36604
36935
|
reject(error48);
|
|
@@ -36789,12 +37120,12 @@ class Protocol {
|
|
|
36789
37120
|
interval = task.pollInterval;
|
|
36790
37121
|
}
|
|
36791
37122
|
} catch {}
|
|
36792
|
-
return new Promise((
|
|
37123
|
+
return new Promise((resolve2, reject) => {
|
|
36793
37124
|
if (signal.aborted) {
|
|
36794
37125
|
reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
|
|
36795
37126
|
return;
|
|
36796
37127
|
}
|
|
36797
|
-
const timeoutId = setTimeout(
|
|
37128
|
+
const timeoutId = setTimeout(resolve2, interval);
|
|
36798
37129
|
signal.addEventListener("abort", () => {
|
|
36799
37130
|
clearTimeout(timeoutId);
|
|
36800
37131
|
reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
|
|
@@ -39779,7 +40110,7 @@ var require_compile = __commonJS((exports) => {
|
|
|
39779
40110
|
const schOrFunc = root.refs[ref];
|
|
39780
40111
|
if (schOrFunc)
|
|
39781
40112
|
return schOrFunc;
|
|
39782
|
-
let _sch =
|
|
40113
|
+
let _sch = resolve2.call(this, root, ref);
|
|
39783
40114
|
if (_sch === undefined) {
|
|
39784
40115
|
const schema = (_a2 = root.localRefs) === null || _a2 === undefined ? undefined : _a2[ref];
|
|
39785
40116
|
const { schemaId } = this.opts;
|
|
@@ -39806,7 +40137,7 @@ var require_compile = __commonJS((exports) => {
|
|
|
39806
40137
|
function sameSchemaEnv(s1, s2) {
|
|
39807
40138
|
return s1.schema === s2.schema && s1.root === s2.root && s1.baseId === s2.baseId;
|
|
39808
40139
|
}
|
|
39809
|
-
function
|
|
40140
|
+
function resolve2(root, ref) {
|
|
39810
40141
|
let sch;
|
|
39811
40142
|
while (typeof (sch = this.refs[ref]) == "string")
|
|
39812
40143
|
ref = sch;
|
|
@@ -40336,7 +40667,7 @@ var require_fast_uri = __commonJS((exports, module) => {
|
|
|
40336
40667
|
}
|
|
40337
40668
|
return uri;
|
|
40338
40669
|
}
|
|
40339
|
-
function
|
|
40670
|
+
function resolve2(baseURI, relativeURI, options) {
|
|
40340
40671
|
const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
|
|
40341
40672
|
const resolved = resolveComponent(parse6(baseURI, schemelessOptions), parse6(relativeURI, schemelessOptions), schemelessOptions, true);
|
|
40342
40673
|
schemelessOptions.skipEscape = true;
|
|
@@ -40564,7 +40895,7 @@ var require_fast_uri = __commonJS((exports, module) => {
|
|
|
40564
40895
|
var fastUri = {
|
|
40565
40896
|
SCHEMES,
|
|
40566
40897
|
normalize,
|
|
40567
|
-
resolve,
|
|
40898
|
+
resolve: resolve2,
|
|
40568
40899
|
resolveComponent,
|
|
40569
40900
|
equal,
|
|
40570
40901
|
serialize,
|
|
@@ -43152,7 +43483,7 @@ var require_formats = __commonJS((exports) => {
|
|
|
43152
43483
|
}
|
|
43153
43484
|
var TIME = /^(\d\d):(\d\d):(\d\d(?:\.\d+)?)(z|([+-])(\d\d)(?::?(\d\d))?)?$/i;
|
|
43154
43485
|
function getTime(strictTimeZone) {
|
|
43155
|
-
return function
|
|
43486
|
+
return function time3(str) {
|
|
43156
43487
|
const matches = TIME.exec(str);
|
|
43157
43488
|
if (!matches)
|
|
43158
43489
|
return false;
|
|
@@ -44090,7 +44421,7 @@ class McpServer {
|
|
|
44090
44421
|
let task = createTaskResult.task;
|
|
44091
44422
|
const pollInterval = task.pollInterval ?? 5000;
|
|
44092
44423
|
while (task.status !== "completed" && task.status !== "failed" && task.status !== "cancelled") {
|
|
44093
|
-
await new Promise((
|
|
44424
|
+
await new Promise((resolve2) => setTimeout(resolve2, pollInterval));
|
|
44094
44425
|
const updatedTask = await extra.taskStore.getTask(taskId);
|
|
44095
44426
|
if (!updatedTask) {
|
|
44096
44427
|
throw new McpError(ErrorCode.InternalError, `Task ${taskId} not found during polling`);
|
|
@@ -44714,12 +45045,12 @@ class StdioServerTransport {
|
|
|
44714
45045
|
this.onclose?.();
|
|
44715
45046
|
}
|
|
44716
45047
|
send(message) {
|
|
44717
|
-
return new Promise((
|
|
45048
|
+
return new Promise((resolve2) => {
|
|
44718
45049
|
const json2 = serializeMessage(message);
|
|
44719
45050
|
if (this._stdout.write(json2)) {
|
|
44720
|
-
|
|
45051
|
+
resolve2();
|
|
44721
45052
|
} else {
|
|
44722
|
-
this._stdout.once("drain",
|
|
45053
|
+
this._stdout.once("drain", resolve2);
|
|
44723
45054
|
}
|
|
44724
45055
|
});
|
|
44725
45056
|
}
|
|
@@ -44730,6 +45061,7 @@ var init_stdio2 = __esm(() => {
|
|
|
44730
45061
|
|
|
44731
45062
|
// src/mcp/tools/messaging.ts
|
|
44732
45063
|
function registerMessagingTools(server, resolveProjectId) {
|
|
45064
|
+
const _sessionInjectRate = new Map;
|
|
44733
45065
|
server.registerTool("send_message", {
|
|
44734
45066
|
description: "Send a DM to an agent by name, or to a specific agent-claude session by ID. When target_session_id is provided, the message is routed to that exact session and auto-injected into its conversation.",
|
|
44735
45067
|
inputSchema: {
|
|
@@ -44769,6 +45101,22 @@ function registerMessagingTools(server, resolveProjectId) {
|
|
|
44769
45101
|
}, async (args) => {
|
|
44770
45102
|
const { target_session_id, content, from: fromParam, priority } = args;
|
|
44771
45103
|
const from = resolveIdentity(fromParam);
|
|
45104
|
+
const rateKey = `session:${from}:${target_session_id}`;
|
|
45105
|
+
const now = Date.now();
|
|
45106
|
+
const windowMs = 60000;
|
|
45107
|
+
const maxPerWindow = 10;
|
|
45108
|
+
const rateEntry = _sessionInjectRate.get(rateKey);
|
|
45109
|
+
if (rateEntry && now - rateEntry.start < windowMs && rateEntry.count >= maxPerWindow) {
|
|
45110
|
+
return {
|
|
45111
|
+
content: [{ type: "text", text: `Rate limit: max ${maxPerWindow} session injections per minute. Try again soon.` }],
|
|
45112
|
+
isError: true
|
|
45113
|
+
};
|
|
45114
|
+
}
|
|
45115
|
+
if (!rateEntry || now - rateEntry.start >= windowMs) {
|
|
45116
|
+
_sessionInjectRate.set(rateKey, { count: 1, start: now });
|
|
45117
|
+
} else {
|
|
45118
|
+
rateEntry.count++;
|
|
45119
|
+
}
|
|
44772
45120
|
const msg = sendMessage({
|
|
44773
45121
|
from,
|
|
44774
45122
|
to: `session:${target_session_id}`,
|
|
@@ -44806,7 +45154,7 @@ function registerMessagingTools(server, resolveProjectId) {
|
|
|
44806
45154
|
project_id: args.project_id ?? resolveProjectId(undefined, agent)
|
|
44807
45155
|
});
|
|
44808
45156
|
if (args.mark_read !== false && messages.length > 0) {
|
|
44809
|
-
markReadByIds(messages.map((m) => m.id));
|
|
45157
|
+
markReadByIds(messages.map((m) => m.id), agent);
|
|
44810
45158
|
}
|
|
44811
45159
|
return {
|
|
44812
45160
|
content: [{ type: "text", text: JSON.stringify({ messages, count: messages.length, offset: args.offset ?? 0 }) }]
|
|
@@ -45850,8 +46198,7 @@ function registerAgentTools(server, agentFocus, getAgentFocus) {
|
|
|
45850
46198
|
const { project_id, from: fromParam } = args;
|
|
45851
46199
|
const agent = resolveIdentity(fromParam);
|
|
45852
46200
|
agentFocus.set(agent, { project_id });
|
|
45853
|
-
|
|
45854
|
-
db2.prepare("UPDATE agent_presence SET project_id = ? WHERE agent = ?").run(project_id, agent);
|
|
46201
|
+
setPresenceProject(agent, project_id);
|
|
45855
46202
|
return {
|
|
45856
46203
|
content: [{ type: "text", text: JSON.stringify({ agent, focused: true, project_id }) }]
|
|
45857
46204
|
};
|
|
@@ -45886,8 +46233,7 @@ function registerAgentTools(server, agentFocus, getAgentFocus) {
|
|
|
45886
46233
|
}, async (args) => {
|
|
45887
46234
|
const agent = resolveIdentity(args.from);
|
|
45888
46235
|
agentFocus.delete(agent);
|
|
45889
|
-
|
|
45890
|
-
db2.prepare("UPDATE agent_presence SET project_id = NULL WHERE agent = ?").run(agent);
|
|
46236
|
+
setPresenceProject(agent, null);
|
|
45891
46237
|
return {
|
|
45892
46238
|
content: [{ type: "text", text: JSON.stringify({ agent, focused: false, project_id: null }) }]
|
|
45893
46239
|
};
|
|
@@ -46674,15 +47020,17 @@ var init_pg_migrations = __esm(() => {
|
|
|
46674
47020
|
|
|
46675
47021
|
CREATE TABLE IF NOT EXISTS agent_presence (
|
|
46676
47022
|
id TEXT NOT NULL DEFAULT '',
|
|
46677
|
-
agent TEXT
|
|
47023
|
+
agent TEXT NOT NULL,
|
|
46678
47024
|
session_id TEXT,
|
|
46679
47025
|
role TEXT NOT NULL DEFAULT 'agent',
|
|
46680
|
-
project_id TEXT,
|
|
47026
|
+
project_id TEXT NOT NULL DEFAULT '',
|
|
46681
47027
|
status TEXT NOT NULL DEFAULT 'online',
|
|
46682
47028
|
last_seen_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
46683
47029
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
46684
|
-
metadata TEXT
|
|
47030
|
+
metadata TEXT,
|
|
47031
|
+
PRIMARY KEY (agent, project_id)
|
|
46685
47032
|
);
|
|
47033
|
+
CREATE UNIQUE INDEX IF NOT EXISTS idx_agent_presence_agent_unique ON agent_presence(agent);
|
|
46686
47034
|
|
|
46687
47035
|
CREATE TABLE IF NOT EXISTS resource_locks (
|
|
46688
47036
|
resource_type TEXT NOT NULL,
|
|
@@ -47160,7 +47508,7 @@ var init_telegram_channel = __esm(() => {
|
|
|
47160
47508
|
|
|
47161
47509
|
// src/mcp/tools/tmux.ts
|
|
47162
47510
|
function sleep2(ms) {
|
|
47163
|
-
return new Promise((
|
|
47511
|
+
return new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
47164
47512
|
}
|
|
47165
47513
|
function parseOptionalDelayMs(value) {
|
|
47166
47514
|
return typeof value === "number" && value >= 0 ? value : undefined;
|
|
@@ -47328,8 +47676,8 @@ var exports_serve = {};
|
|
|
47328
47676
|
__export(exports_serve, {
|
|
47329
47677
|
startDashboardServer: () => startDashboardServer
|
|
47330
47678
|
});
|
|
47331
|
-
import { join as join14, resolve, sep } from "path";
|
|
47332
|
-
import { existsSync as
|
|
47679
|
+
import { join as join14, resolve as resolve2, sep } from "path";
|
|
47680
|
+
import { existsSync as existsSync11 } from "fs";
|
|
47333
47681
|
function securityHeaders(base) {
|
|
47334
47682
|
const headers = new Headers(base);
|
|
47335
47683
|
if (!headers.has("X-Content-Type-Options"))
|
|
@@ -47427,7 +47775,7 @@ function startDashboardServer(port = 0, host) {
|
|
|
47427
47775
|
const resolvedPort = normalizePort(port, 0);
|
|
47428
47776
|
const resolvedHost = normalizeHost(host ?? process.env.CONVERSATIONS_DASHBOARD_HOST);
|
|
47429
47777
|
const dashboardDist = join14(import.meta.dir, "../../dashboard/dist");
|
|
47430
|
-
const hasDist =
|
|
47778
|
+
const hasDist = existsSync11(dashboardDist);
|
|
47431
47779
|
const server2 = Bun.serve({
|
|
47432
47780
|
port: resolvedPort,
|
|
47433
47781
|
hostname: resolvedHost,
|
|
@@ -47835,9 +48183,9 @@ function startDashboardServer(port = 0, host) {
|
|
|
47835
48183
|
}
|
|
47836
48184
|
}
|
|
47837
48185
|
if (hasDist) {
|
|
47838
|
-
const baseDir =
|
|
48186
|
+
const baseDir = resolve2(dashboardDist);
|
|
47839
48187
|
const safePath = path === "/" ? "index.html" : path.replace(/^\/+/, "");
|
|
47840
|
-
const filePath =
|
|
48188
|
+
const filePath = resolve2(baseDir, safePath);
|
|
47841
48189
|
if (!filePath.startsWith(baseDir + sep)) {
|
|
47842
48190
|
return new Response("Not Found", { status: 404 });
|
|
47843
48191
|
}
|
|
@@ -48906,13 +49254,13 @@ var gatherTrainingData = async (options = {}) => {
|
|
|
48906
49254
|
|
|
48907
49255
|
// src/lib/model-config.ts
|
|
48908
49256
|
init_db();
|
|
48909
|
-
import { readFileSync as readFileSync6, writeFileSync as writeFileSync5, mkdirSync as mkdirSync8, existsSync as
|
|
49257
|
+
import { readFileSync as readFileSync6, writeFileSync as writeFileSync5, mkdirSync as mkdirSync8, existsSync as existsSync10 } from "fs";
|
|
48910
49258
|
import { join as join12 } from "path";
|
|
48911
49259
|
var DEFAULT_MODEL = "gpt-4o-mini";
|
|
48912
49260
|
var CONFIG_DIR3 = getDataDir2();
|
|
48913
49261
|
var CONFIG_PATH2 = join12(CONFIG_DIR3, "config.json");
|
|
48914
49262
|
function readConfig() {
|
|
48915
|
-
if (!
|
|
49263
|
+
if (!existsSync10(CONFIG_PATH2))
|
|
48916
49264
|
return {};
|
|
48917
49265
|
try {
|
|
48918
49266
|
return JSON.parse(readFileSync6(CONFIG_PATH2, "utf-8"));
|
|
@@ -48921,7 +49269,7 @@ function readConfig() {
|
|
|
48921
49269
|
}
|
|
48922
49270
|
}
|
|
48923
49271
|
function writeConfig(config) {
|
|
48924
|
-
if (!
|
|
49272
|
+
if (!existsSync10(CONFIG_DIR3)) {
|
|
48925
49273
|
mkdirSync8(CONFIG_DIR3, { recursive: true });
|
|
48926
49274
|
}
|
|
48927
49275
|
writeFileSync5(CONFIG_PATH2, JSON.stringify(config, null, 2), "utf-8");
|
|
@@ -50140,7 +50488,7 @@ function registerAgentCommands(program2) {
|
|
|
50140
50488
|
console.error(chalk7.red(`Project "${projectArg}" not found.`));
|
|
50141
50489
|
process.exit(1);
|
|
50142
50490
|
}
|
|
50143
|
-
|
|
50491
|
+
setPresenceProject(agent, project.id);
|
|
50144
50492
|
if (opts.json) {
|
|
50145
50493
|
console.log(JSON.stringify({ agent, project_id: project.id, project_name: project.name, focused: true }));
|
|
50146
50494
|
} else {
|
|
@@ -50150,7 +50498,7 @@ function registerAgentCommands(program2) {
|
|
|
50150
50498
|
});
|
|
50151
50499
|
focus.command("clear").description("Clear your project focus").option("--from <agent>", "Agent identity").option("-j, --json", "Output as JSON").action((opts) => {
|
|
50152
50500
|
const agent = resolveIdentity(opts.from);
|
|
50153
|
-
|
|
50501
|
+
setPresenceProject(agent, null);
|
|
50154
50502
|
if (opts.json) {
|
|
50155
50503
|
console.log(JSON.stringify({ agent, project_id: null, focused: false }));
|
|
50156
50504
|
} else {
|