@hasna/conversations 0.2.0 → 0.2.2
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 +18 -8
- package/bin/index.js +434 -26
- package/bin/mcp.js +318 -23
- package/dist/index.d.ts +2 -2
- package/dist/index.js +122 -9
- package/dist/lib/locks.d.ts +33 -0
- package/dist/lib/messages.d.ts +27 -0
- package/package.json +1 -1
- package/dashboard/dist/assets/index-Bw0wMcXE.js +0 -186
- package/dashboard/dist/assets/index-CF_GDtNp.css +0 -1
- package/dashboard/dist/index.html +0 -13
- package/dashboard/dist/logo.jpg +0 -0
package/dist/index.js
CHANGED
|
@@ -5,39 +5,60 @@ var __defProp = Object.defineProperty;
|
|
|
5
5
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
6
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
7
7
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
function __accessProp(key) {
|
|
9
|
+
return this[key];
|
|
10
|
+
}
|
|
11
|
+
var __toESMCache_node;
|
|
12
|
+
var __toESMCache_esm;
|
|
8
13
|
var __toESM = (mod, isNodeMode, target) => {
|
|
14
|
+
var canCache = mod != null && typeof mod === "object";
|
|
15
|
+
if (canCache) {
|
|
16
|
+
var cache = isNodeMode ? __toESMCache_node ??= new WeakMap : __toESMCache_esm ??= new WeakMap;
|
|
17
|
+
var cached = cache.get(mod);
|
|
18
|
+
if (cached)
|
|
19
|
+
return cached;
|
|
20
|
+
}
|
|
9
21
|
target = mod != null ? __create(__getProtoOf(mod)) : {};
|
|
10
22
|
const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
|
|
11
23
|
for (let key of __getOwnPropNames(mod))
|
|
12
24
|
if (!__hasOwnProp.call(to, key))
|
|
13
25
|
__defProp(to, key, {
|
|
14
|
-
get: (
|
|
26
|
+
get: __accessProp.bind(mod, key),
|
|
15
27
|
enumerable: true
|
|
16
28
|
});
|
|
29
|
+
if (canCache)
|
|
30
|
+
cache.set(mod, to);
|
|
17
31
|
return to;
|
|
18
32
|
};
|
|
19
|
-
var __moduleCache = /* @__PURE__ */ new WeakMap;
|
|
20
33
|
var __toCommonJS = (from) => {
|
|
21
|
-
var entry = __moduleCache.get(from), desc;
|
|
34
|
+
var entry = (__moduleCache ??= new WeakMap).get(from), desc;
|
|
22
35
|
if (entry)
|
|
23
36
|
return entry;
|
|
24
37
|
entry = __defProp({}, "__esModule", { value: true });
|
|
25
|
-
if (from && typeof from === "object" || typeof from === "function")
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
38
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
39
|
+
for (var key of __getOwnPropNames(from))
|
|
40
|
+
if (!__hasOwnProp.call(entry, key))
|
|
41
|
+
__defProp(entry, key, {
|
|
42
|
+
get: __accessProp.bind(from, key),
|
|
43
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
44
|
+
});
|
|
45
|
+
}
|
|
30
46
|
__moduleCache.set(from, entry);
|
|
31
47
|
return entry;
|
|
32
48
|
};
|
|
49
|
+
var __moduleCache;
|
|
33
50
|
var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
|
|
51
|
+
var __returnValue = (v) => v;
|
|
52
|
+
function __exportSetter(name, newValue) {
|
|
53
|
+
this[name] = __returnValue.bind(null, newValue);
|
|
54
|
+
}
|
|
34
55
|
var __export = (target, all) => {
|
|
35
56
|
for (var name in all)
|
|
36
57
|
__defProp(target, name, {
|
|
37
58
|
get: all[name],
|
|
38
59
|
enumerable: true,
|
|
39
60
|
configurable: true,
|
|
40
|
-
set: (
|
|
61
|
+
set: __exportSetter.bind(all, name)
|
|
41
62
|
});
|
|
42
63
|
};
|
|
43
64
|
var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
@@ -3646,10 +3667,12 @@ function renameAgent(oldName, newName) {
|
|
|
3646
3667
|
// src/lib/locks.ts
|
|
3647
3668
|
init_db();
|
|
3648
3669
|
var DEFAULT_LOCK_EXPIRY_MS = 5 * 60 * 1000;
|
|
3670
|
+
var STALE_HEARTBEAT_SECONDS = 30 * 60;
|
|
3649
3671
|
function acquireLock(resourceType, resourceId, agentId, lockType = "advisory", expiryMs = DEFAULT_LOCK_EXPIRY_MS) {
|
|
3650
3672
|
const db2 = getDb();
|
|
3651
3673
|
return db2.transaction(() => {
|
|
3652
3674
|
cleanExpiredLocks();
|
|
3675
|
+
releaseStaleAgentLocks();
|
|
3653
3676
|
const existing = db2.prepare(`
|
|
3654
3677
|
SELECT * FROM resource_locks
|
|
3655
3678
|
WHERE resource_type = ? AND resource_id = ? AND lock_type = ?
|
|
@@ -3676,6 +3699,55 @@ function acquireLock(resourceType, resourceId, agentId, lockType = "advisory", e
|
|
|
3676
3699
|
return { acquired: true, lock };
|
|
3677
3700
|
}).immediate();
|
|
3678
3701
|
}
|
|
3702
|
+
function bulkAcquireLock(resources, agentId) {
|
|
3703
|
+
const db2 = getDb();
|
|
3704
|
+
return db2.transaction(() => {
|
|
3705
|
+
cleanExpiredLocks();
|
|
3706
|
+
releaseStaleAgentLocks();
|
|
3707
|
+
const acquired = [];
|
|
3708
|
+
for (const { resource_type, resource_id, lock_type = "advisory", expiry_ms = DEFAULT_LOCK_EXPIRY_MS } of resources) {
|
|
3709
|
+
const existing = db2.prepare(`
|
|
3710
|
+
SELECT * FROM resource_locks
|
|
3711
|
+
WHERE resource_type = ? AND resource_id = ? AND lock_type = ?
|
|
3712
|
+
`).get(resource_type, resource_id, lock_type);
|
|
3713
|
+
if (existing && existing.agent_id !== agentId) {
|
|
3714
|
+
throw { _bulkConflict: true, resource_type, resource_id, held_by: existing.agent_id };
|
|
3715
|
+
}
|
|
3716
|
+
const expiresAt = new Date(Date.now() + expiry_ms).toISOString().slice(0, -1);
|
|
3717
|
+
if (existing) {
|
|
3718
|
+
db2.prepare(`
|
|
3719
|
+
UPDATE resource_locks SET expires_at = ?, locked_at = strftime('%Y-%m-%dT%H:%M:%f', 'now')
|
|
3720
|
+
WHERE resource_type = ? AND resource_id = ? AND lock_type = ?
|
|
3721
|
+
`).run(expiresAt, resource_type, resource_id, lock_type);
|
|
3722
|
+
} else {
|
|
3723
|
+
db2.prepare(`
|
|
3724
|
+
INSERT INTO resource_locks (resource_type, resource_id, agent_id, lock_type, locked_at, expires_at)
|
|
3725
|
+
VALUES (?, ?, ?, ?, strftime('%Y-%m-%dT%H:%M:%f', 'now'), ?)
|
|
3726
|
+
`).run(resource_type, resource_id, agentId, lock_type, expiresAt);
|
|
3727
|
+
}
|
|
3728
|
+
const lock = db2.prepare(`
|
|
3729
|
+
SELECT * FROM resource_locks WHERE resource_type = ? AND resource_id = ? AND lock_type = ?
|
|
3730
|
+
`).get(resource_type, resource_id, lock_type);
|
|
3731
|
+
acquired.push(lock);
|
|
3732
|
+
}
|
|
3733
|
+
return { acquired: true, locks: acquired };
|
|
3734
|
+
}).immediate();
|
|
3735
|
+
}
|
|
3736
|
+
function tryBulkAcquireLock(resources, agentId) {
|
|
3737
|
+
try {
|
|
3738
|
+
return bulkAcquireLock(resources, agentId);
|
|
3739
|
+
} catch (err) {
|
|
3740
|
+
const e = err;
|
|
3741
|
+
if (e?._bulkConflict) {
|
|
3742
|
+
return {
|
|
3743
|
+
acquired: false,
|
|
3744
|
+
locks: [],
|
|
3745
|
+
blocked_by: { resource_type: e.resource_type, resource_id: e.resource_id, held_by: e.held_by }
|
|
3746
|
+
};
|
|
3747
|
+
}
|
|
3748
|
+
throw err;
|
|
3749
|
+
}
|
|
3750
|
+
}
|
|
3679
3751
|
function releaseLock(resourceType, resourceId, agentId) {
|
|
3680
3752
|
const db2 = getDb();
|
|
3681
3753
|
const result = db2.prepare(`
|
|
@@ -3687,6 +3759,7 @@ function releaseLock(resourceType, resourceId, agentId) {
|
|
|
3687
3759
|
function checkLock(resourceType, resourceId) {
|
|
3688
3760
|
const db2 = getDb();
|
|
3689
3761
|
cleanExpiredLocks();
|
|
3762
|
+
releaseStaleAgentLocks();
|
|
3690
3763
|
return db2.prepare(`
|
|
3691
3764
|
SELECT * FROM resource_locks
|
|
3692
3765
|
WHERE resource_type = ? AND resource_id = ?
|
|
@@ -3694,6 +3767,17 @@ function checkLock(resourceType, resourceId) {
|
|
|
3694
3767
|
LIMIT 1
|
|
3695
3768
|
`).get(resourceType, resourceId);
|
|
3696
3769
|
}
|
|
3770
|
+
function releaseStaleAgentLocks() {
|
|
3771
|
+
const db2 = getDb();
|
|
3772
|
+
const result = db2.prepare(`
|
|
3773
|
+
DELETE FROM resource_locks
|
|
3774
|
+
WHERE LOWER(agent_id) IN (
|
|
3775
|
+
SELECT LOWER(agent) FROM agent_presence
|
|
3776
|
+
WHERE last_seen_at < strftime('%Y-%m-%dT%H:%M:%f', 'now', '-${STALE_HEARTBEAT_SECONDS} seconds')
|
|
3777
|
+
)
|
|
3778
|
+
`).run();
|
|
3779
|
+
return result.changes;
|
|
3780
|
+
}
|
|
3697
3781
|
function cleanExpiredLocks() {
|
|
3698
3782
|
const db2 = getDb();
|
|
3699
3783
|
const result = db2.prepare(`
|
|
@@ -3704,6 +3788,7 @@ function cleanExpiredLocks() {
|
|
|
3704
3788
|
function listLocks(opts) {
|
|
3705
3789
|
const db2 = getDb();
|
|
3706
3790
|
cleanExpiredLocks();
|
|
3791
|
+
releaseStaleAgentLocks();
|
|
3707
3792
|
let query = "SELECT * FROM resource_locks WHERE 1=1";
|
|
3708
3793
|
const params = [];
|
|
3709
3794
|
if (opts?.resource_type) {
|
|
@@ -3717,6 +3802,31 @@ function listLocks(opts) {
|
|
|
3717
3802
|
query += " ORDER BY locked_at ASC";
|
|
3718
3803
|
return db2.prepare(query).all(...params);
|
|
3719
3804
|
}
|
|
3805
|
+
function listLocksEnriched(opts) {
|
|
3806
|
+
const locks = listLocks(opts);
|
|
3807
|
+
const db2 = getDb();
|
|
3808
|
+
const nowMs = Date.now();
|
|
3809
|
+
return locks.map((lock) => {
|
|
3810
|
+
const lockedMs = new Date(lock.locked_at + "Z").getTime();
|
|
3811
|
+
const expiresMs = new Date(lock.expires_at + "Z").getTime();
|
|
3812
|
+
const presenceRow = db2.prepare(`
|
|
3813
|
+
SELECT role, status, last_seen_at, project_id FROM agent_presence WHERE LOWER(agent) = LOWER(?)
|
|
3814
|
+
`).get(lock.agent_id);
|
|
3815
|
+
const agent = presenceRow ? {
|
|
3816
|
+
role: presenceRow.role ?? null,
|
|
3817
|
+
status: presenceRow.status ?? null,
|
|
3818
|
+
online: presenceRow.last_seen_at ? nowMs - new Date(presenceRow.last_seen_at + "Z").getTime() < 60000 : false,
|
|
3819
|
+
last_seen_at: presenceRow.last_seen_at ?? null,
|
|
3820
|
+
project_id: presenceRow.project_id ?? null
|
|
3821
|
+
} : null;
|
|
3822
|
+
return {
|
|
3823
|
+
...lock,
|
|
3824
|
+
locked_seconds_ago: Math.round((nowMs - lockedMs) / 1000),
|
|
3825
|
+
expires_in_seconds: Math.round((expiresMs - nowMs) / 1000),
|
|
3826
|
+
agent
|
|
3827
|
+
};
|
|
3828
|
+
});
|
|
3829
|
+
}
|
|
3720
3830
|
// src/lib/hot.ts
|
|
3721
3831
|
init_db();
|
|
3722
3832
|
function computeHotness(sessionId) {
|
|
@@ -4201,6 +4311,7 @@ export {
|
|
|
4201
4311
|
updateProject,
|
|
4202
4312
|
unpinMessage,
|
|
4203
4313
|
unarchiveSpace,
|
|
4314
|
+
tryBulkAcquireLock,
|
|
4204
4315
|
startPolling,
|
|
4205
4316
|
sendMessage,
|
|
4206
4317
|
searchMessages,
|
|
@@ -4209,6 +4320,7 @@ export {
|
|
|
4209
4320
|
renameAgent,
|
|
4210
4321
|
removeReaction,
|
|
4211
4322
|
removePresence,
|
|
4323
|
+
releaseStaleAgentLocks,
|
|
4212
4324
|
releaseLock,
|
|
4213
4325
|
registerAgent,
|
|
4214
4326
|
readMessages,
|
|
@@ -4220,6 +4332,7 @@ export {
|
|
|
4220
4332
|
listSpaces,
|
|
4221
4333
|
listSessions,
|
|
4222
4334
|
listProjects,
|
|
4335
|
+
listLocksEnriched,
|
|
4223
4336
|
listLocks,
|
|
4224
4337
|
listHotSessions,
|
|
4225
4338
|
listAgents,
|
package/dist/lib/locks.d.ts
CHANGED
|
@@ -6,15 +6,48 @@ export interface ResourceLock {
|
|
|
6
6
|
locked_at: string;
|
|
7
7
|
expires_at: string;
|
|
8
8
|
}
|
|
9
|
+
export interface BulkLockRequest {
|
|
10
|
+
resource_type: string;
|
|
11
|
+
resource_id: string;
|
|
12
|
+
lock_type?: "advisory" | "exclusive";
|
|
13
|
+
expiry_ms?: number;
|
|
14
|
+
}
|
|
15
|
+
export interface BulkAcquireResult {
|
|
16
|
+
acquired: boolean;
|
|
17
|
+
locks: ResourceLock[];
|
|
18
|
+
blocked_by?: {
|
|
19
|
+
resource_type: string;
|
|
20
|
+
resource_id: string;
|
|
21
|
+
held_by: string;
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
export interface EnrichedLock extends ResourceLock {
|
|
25
|
+
locked_seconds_ago: number;
|
|
26
|
+
expires_in_seconds: number;
|
|
27
|
+
agent: {
|
|
28
|
+
role: string | null;
|
|
29
|
+
status: string | null;
|
|
30
|
+
online: boolean;
|
|
31
|
+
last_seen_at: string | null;
|
|
32
|
+
project_id: string | null;
|
|
33
|
+
} | null;
|
|
34
|
+
}
|
|
9
35
|
export declare function acquireLock(resourceType: string, resourceId: string, agentId: string, lockType?: "advisory" | "exclusive", expiryMs?: number): {
|
|
10
36
|
acquired: boolean;
|
|
11
37
|
lock: ResourceLock | null;
|
|
12
38
|
held_by?: string;
|
|
13
39
|
};
|
|
40
|
+
export declare function bulkAcquireLock(resources: BulkLockRequest[], agentId: string): BulkAcquireResult;
|
|
41
|
+
export declare function tryBulkAcquireLock(resources: BulkLockRequest[], agentId: string): BulkAcquireResult;
|
|
14
42
|
export declare function releaseLock(resourceType: string, resourceId: string, agentId: string): boolean;
|
|
15
43
|
export declare function checkLock(resourceType: string, resourceId: string): ResourceLock | null;
|
|
44
|
+
export declare function releaseStaleAgentLocks(): number;
|
|
16
45
|
export declare function cleanExpiredLocks(): number;
|
|
17
46
|
export declare function listLocks(opts?: {
|
|
18
47
|
resource_type?: string;
|
|
19
48
|
agent_id?: string;
|
|
20
49
|
}): ResourceLock[];
|
|
50
|
+
export declare function listLocksEnriched(opts?: {
|
|
51
|
+
resource_type?: string;
|
|
52
|
+
agent_id?: string;
|
|
53
|
+
}): EnrichedLock[];
|
package/dist/lib/messages.d.ts
CHANGED
|
@@ -7,7 +7,34 @@ export declare function markRead(ids: number[], reader: string): number;
|
|
|
7
7
|
export declare function markSessionRead(sessionId: string, reader: string): number;
|
|
8
8
|
export declare function markSpaceRead(spaceName: string, reader: string): number;
|
|
9
9
|
export declare function getMessageById(id: number): Message | null;
|
|
10
|
+
export declare function markReadByIds(ids: number[]): number;
|
|
10
11
|
export declare function markAllRead(agent: string): number;
|
|
12
|
+
export interface DigestMessage {
|
|
13
|
+
id: number;
|
|
14
|
+
from: string;
|
|
15
|
+
created_at: string;
|
|
16
|
+
preview: string;
|
|
17
|
+
priority: string;
|
|
18
|
+
has_attachments: boolean;
|
|
19
|
+
space?: string | null;
|
|
20
|
+
to?: string | null;
|
|
21
|
+
unread: boolean;
|
|
22
|
+
}
|
|
23
|
+
export interface DigestResult {
|
|
24
|
+
messages: DigestMessage[];
|
|
25
|
+
total_unread: number;
|
|
26
|
+
shown: number;
|
|
27
|
+
}
|
|
28
|
+
export interface ReadDigestOptions {
|
|
29
|
+
space?: string;
|
|
30
|
+
session_id?: string;
|
|
31
|
+
to?: string;
|
|
32
|
+
since?: string;
|
|
33
|
+
limit?: number;
|
|
34
|
+
unread_only?: boolean;
|
|
35
|
+
project_id?: string;
|
|
36
|
+
}
|
|
37
|
+
export declare function readDigest(opts?: ReadDigestOptions): DigestResult;
|
|
11
38
|
export interface ExportMessagesOptions {
|
|
12
39
|
space?: string;
|
|
13
40
|
session_id?: string;
|