@hasna/conversations 0.1.29 → 0.1.30
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 +15 -1
- package/bin/mcp.js +15 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.js +94 -1
- package/dist/lib/locks.d.ts +20 -0
- package/dist/lib/locks.test.d.ts +1 -0
- package/package.json +1 -1
package/bin/hook.js
CHANGED
|
@@ -125,6 +125,20 @@ function getDb() {
|
|
|
125
125
|
metadata TEXT
|
|
126
126
|
)
|
|
127
127
|
`);
|
|
128
|
+
db.exec(`
|
|
129
|
+
CREATE TABLE IF NOT EXISTS resource_locks (
|
|
130
|
+
resource_type TEXT NOT NULL,
|
|
131
|
+
resource_id TEXT NOT NULL,
|
|
132
|
+
agent_id TEXT NOT NULL,
|
|
133
|
+
lock_type TEXT NOT NULL DEFAULT 'advisory',
|
|
134
|
+
locked_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%f', 'now')),
|
|
135
|
+
expires_at TEXT NOT NULL,
|
|
136
|
+
UNIQUE(resource_type, resource_id, lock_type)
|
|
137
|
+
)
|
|
138
|
+
`);
|
|
139
|
+
db.exec("CREATE INDEX IF NOT EXISTS idx_locks_resource ON resource_locks(resource_type, resource_id)");
|
|
140
|
+
db.exec("CREATE INDEX IF NOT EXISTS idx_locks_agent ON resource_locks(agent_id)");
|
|
141
|
+
db.exec("CREATE INDEX IF NOT EXISTS idx_locks_expires ON resource_locks(expires_at)");
|
|
128
142
|
db.exec(`
|
|
129
143
|
CREATE TABLE IF NOT EXISTS reactions (
|
|
130
144
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
package/bin/index.js
CHANGED
|
@@ -1979,6 +1979,20 @@ function getDb() {
|
|
|
1979
1979
|
metadata TEXT
|
|
1980
1980
|
)
|
|
1981
1981
|
`);
|
|
1982
|
+
db.exec(`
|
|
1983
|
+
CREATE TABLE IF NOT EXISTS resource_locks (
|
|
1984
|
+
resource_type TEXT NOT NULL,
|
|
1985
|
+
resource_id TEXT NOT NULL,
|
|
1986
|
+
agent_id TEXT NOT NULL,
|
|
1987
|
+
lock_type TEXT NOT NULL DEFAULT 'advisory',
|
|
1988
|
+
locked_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%f', 'now')),
|
|
1989
|
+
expires_at TEXT NOT NULL,
|
|
1990
|
+
UNIQUE(resource_type, resource_id, lock_type)
|
|
1991
|
+
)
|
|
1992
|
+
`);
|
|
1993
|
+
db.exec("CREATE INDEX IF NOT EXISTS idx_locks_resource ON resource_locks(resource_type, resource_id)");
|
|
1994
|
+
db.exec("CREATE INDEX IF NOT EXISTS idx_locks_agent ON resource_locks(agent_id)");
|
|
1995
|
+
db.exec("CREATE INDEX IF NOT EXISTS idx_locks_expires ON resource_locks(expires_at)");
|
|
1982
1996
|
db.exec(`
|
|
1983
1997
|
CREATE TABLE IF NOT EXISTS reactions (
|
|
1984
1998
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
@@ -3599,7 +3613,7 @@ var init_poll = __esm(() => {
|
|
|
3599
3613
|
var require_package = __commonJS((exports, module) => {
|
|
3600
3614
|
module.exports = {
|
|
3601
3615
|
name: "@hasna/conversations",
|
|
3602
|
-
version: "0.1.
|
|
3616
|
+
version: "0.1.30",
|
|
3603
3617
|
description: "Real-time CLI messaging for AI agents",
|
|
3604
3618
|
type: "module",
|
|
3605
3619
|
bin: {
|
package/bin/mcp.js
CHANGED
|
@@ -6611,6 +6611,20 @@ function getDb() {
|
|
|
6611
6611
|
metadata TEXT
|
|
6612
6612
|
)
|
|
6613
6613
|
`);
|
|
6614
|
+
db.exec(`
|
|
6615
|
+
CREATE TABLE IF NOT EXISTS resource_locks (
|
|
6616
|
+
resource_type TEXT NOT NULL,
|
|
6617
|
+
resource_id TEXT NOT NULL,
|
|
6618
|
+
agent_id TEXT NOT NULL,
|
|
6619
|
+
lock_type TEXT NOT NULL DEFAULT 'advisory',
|
|
6620
|
+
locked_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%f', 'now')),
|
|
6621
|
+
expires_at TEXT NOT NULL,
|
|
6622
|
+
UNIQUE(resource_type, resource_id, lock_type)
|
|
6623
|
+
)
|
|
6624
|
+
`);
|
|
6625
|
+
db.exec("CREATE INDEX IF NOT EXISTS idx_locks_resource ON resource_locks(resource_type, resource_id)");
|
|
6626
|
+
db.exec("CREATE INDEX IF NOT EXISTS idx_locks_agent ON resource_locks(agent_id)");
|
|
6627
|
+
db.exec("CREATE INDEX IF NOT EXISTS idx_locks_expires ON resource_locks(expires_at)");
|
|
6614
6628
|
db.exec(`
|
|
6615
6629
|
CREATE TABLE IF NOT EXISTS reactions (
|
|
6616
6630
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
@@ -29872,7 +29886,7 @@ function renameAgent(oldName, newName) {
|
|
|
29872
29886
|
// package.json
|
|
29873
29887
|
var package_default = {
|
|
29874
29888
|
name: "@hasna/conversations",
|
|
29875
|
-
version: "0.1.
|
|
29889
|
+
version: "0.1.30",
|
|
29876
29890
|
description: "Real-time CLI messaging for AI agents",
|
|
29877
29891
|
type: "module",
|
|
29878
29892
|
bin: {
|
package/dist/index.d.ts
CHANGED
|
@@ -19,4 +19,6 @@ export { resolveIdentity, requireIdentity, } from "./lib/identity.js";
|
|
|
19
19
|
export { addReaction, removeReaction, getReactions, getReactionSummary, } from "./lib/reactions.js";
|
|
20
20
|
export { fireWebhooks, } from "./lib/webhooks.js";
|
|
21
21
|
export { heartbeat, registerAgent, isAgentConflict, getPresence, listAgents, removePresence, renameAgent, } from "./lib/presence.js";
|
|
22
|
+
export { acquireLock, releaseLock, checkLock, cleanExpiredLocks, listLocks, } from "./lib/locks.js";
|
|
23
|
+
export type { ResourceLock } from "./lib/locks.js";
|
|
22
24
|
export type { Message, Session, Space, SpaceInfo, SpaceMember, Project, ProjectInfo, Priority, SendMessageOptions, ReadMessagesOptions, SearchMessagesOptions, AgentPresence, AgentConflictError, RegisterAgentResult, Reaction, Attachment, } from "./types.js";
|
package/dist/index.js
CHANGED
|
@@ -138,6 +138,20 @@ function getDb() {
|
|
|
138
138
|
metadata TEXT
|
|
139
139
|
)
|
|
140
140
|
`);
|
|
141
|
+
db.exec(`
|
|
142
|
+
CREATE TABLE IF NOT EXISTS resource_locks (
|
|
143
|
+
resource_type TEXT NOT NULL,
|
|
144
|
+
resource_id TEXT NOT NULL,
|
|
145
|
+
agent_id TEXT NOT NULL,
|
|
146
|
+
lock_type TEXT NOT NULL DEFAULT 'advisory',
|
|
147
|
+
locked_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%f', 'now')),
|
|
148
|
+
expires_at TEXT NOT NULL,
|
|
149
|
+
UNIQUE(resource_type, resource_id, lock_type)
|
|
150
|
+
)
|
|
151
|
+
`);
|
|
152
|
+
db.exec("CREATE INDEX IF NOT EXISTS idx_locks_resource ON resource_locks(resource_type, resource_id)");
|
|
153
|
+
db.exec("CREATE INDEX IF NOT EXISTS idx_locks_agent ON resource_locks(agent_id)");
|
|
154
|
+
db.exec("CREATE INDEX IF NOT EXISTS idx_locks_expires ON resource_locks(expires_at)");
|
|
141
155
|
db.exec(`
|
|
142
156
|
CREATE TABLE IF NOT EXISTS reactions (
|
|
143
157
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
@@ -3557,6 +3571,80 @@ function renameAgent(oldName, newName) {
|
|
|
3557
3571
|
db2.prepare("UPDATE agent_presence SET agent = ? WHERE LOWER(agent) = ?").run(normalizedNew, normalizedOld);
|
|
3558
3572
|
return true;
|
|
3559
3573
|
}
|
|
3574
|
+
// src/lib/locks.ts
|
|
3575
|
+
init_db();
|
|
3576
|
+
var DEFAULT_LOCK_EXPIRY_MS = 5 * 60 * 1000;
|
|
3577
|
+
function acquireLock(resourceType, resourceId, agentId, lockType = "advisory", expiryMs = DEFAULT_LOCK_EXPIRY_MS) {
|
|
3578
|
+
const db2 = getDb();
|
|
3579
|
+
return db2.transaction(() => {
|
|
3580
|
+
cleanExpiredLocks();
|
|
3581
|
+
const existing = db2.prepare(`
|
|
3582
|
+
SELECT * FROM resource_locks
|
|
3583
|
+
WHERE resource_type = ? AND resource_id = ? AND lock_type = ?
|
|
3584
|
+
`).get(resourceType, resourceId, lockType);
|
|
3585
|
+
if (existing) {
|
|
3586
|
+
if (existing.agent_id !== agentId) {
|
|
3587
|
+
return { acquired: false, lock: null, held_by: existing.agent_id };
|
|
3588
|
+
}
|
|
3589
|
+
const expiresAt = new Date(Date.now() + expiryMs).toISOString().replace("T", "T").replace("Z", "");
|
|
3590
|
+
db2.prepare(`
|
|
3591
|
+
UPDATE resource_locks SET expires_at = ?, locked_at = strftime('%Y-%m-%dT%H:%M:%f', 'now')
|
|
3592
|
+
WHERE resource_type = ? AND resource_id = ? AND lock_type = ?
|
|
3593
|
+
`).run(expiresAt, resourceType, resourceId, lockType);
|
|
3594
|
+
} else {
|
|
3595
|
+
const expiresAt = new Date(Date.now() + expiryMs).toISOString().slice(0, -1);
|
|
3596
|
+
db2.prepare(`
|
|
3597
|
+
INSERT INTO resource_locks (resource_type, resource_id, agent_id, lock_type, locked_at, expires_at)
|
|
3598
|
+
VALUES (?, ?, ?, ?, strftime('%Y-%m-%dT%H:%M:%f', 'now'), ?)
|
|
3599
|
+
`).run(resourceType, resourceId, agentId, lockType, expiresAt);
|
|
3600
|
+
}
|
|
3601
|
+
const lock = db2.prepare(`
|
|
3602
|
+
SELECT * FROM resource_locks WHERE resource_type = ? AND resource_id = ? AND lock_type = ?
|
|
3603
|
+
`).get(resourceType, resourceId, lockType);
|
|
3604
|
+
return { acquired: true, lock };
|
|
3605
|
+
}).immediate();
|
|
3606
|
+
}
|
|
3607
|
+
function releaseLock(resourceType, resourceId, agentId) {
|
|
3608
|
+
const db2 = getDb();
|
|
3609
|
+
const result = db2.prepare(`
|
|
3610
|
+
DELETE FROM resource_locks
|
|
3611
|
+
WHERE resource_type = ? AND resource_id = ? AND agent_id = ?
|
|
3612
|
+
`).run(resourceType, resourceId, agentId);
|
|
3613
|
+
return result.changes > 0;
|
|
3614
|
+
}
|
|
3615
|
+
function checkLock(resourceType, resourceId) {
|
|
3616
|
+
const db2 = getDb();
|
|
3617
|
+
cleanExpiredLocks();
|
|
3618
|
+
return db2.prepare(`
|
|
3619
|
+
SELECT * FROM resource_locks
|
|
3620
|
+
WHERE resource_type = ? AND resource_id = ?
|
|
3621
|
+
ORDER BY locked_at ASC
|
|
3622
|
+
LIMIT 1
|
|
3623
|
+
`).get(resourceType, resourceId);
|
|
3624
|
+
}
|
|
3625
|
+
function cleanExpiredLocks() {
|
|
3626
|
+
const db2 = getDb();
|
|
3627
|
+
const result = db2.prepare(`
|
|
3628
|
+
DELETE FROM resource_locks WHERE expires_at < strftime('%Y-%m-%dT%H:%M:%f', 'now')
|
|
3629
|
+
`).run();
|
|
3630
|
+
return result.changes;
|
|
3631
|
+
}
|
|
3632
|
+
function listLocks(opts) {
|
|
3633
|
+
const db2 = getDb();
|
|
3634
|
+
cleanExpiredLocks();
|
|
3635
|
+
let query = "SELECT * FROM resource_locks WHERE 1=1";
|
|
3636
|
+
const params = [];
|
|
3637
|
+
if (opts?.resource_type) {
|
|
3638
|
+
query += " AND resource_type = ?";
|
|
3639
|
+
params.push(opts.resource_type);
|
|
3640
|
+
}
|
|
3641
|
+
if (opts?.agent_id) {
|
|
3642
|
+
query += " AND agent_id = ?";
|
|
3643
|
+
params.push(opts.agent_id);
|
|
3644
|
+
}
|
|
3645
|
+
query += " ORDER BY locked_at ASC";
|
|
3646
|
+
return db2.prepare(query).all(...params);
|
|
3647
|
+
}
|
|
3560
3648
|
export {
|
|
3561
3649
|
useSpaceMessages,
|
|
3562
3650
|
updateSpace,
|
|
@@ -3571,6 +3659,7 @@ export {
|
|
|
3571
3659
|
renameAgent,
|
|
3572
3660
|
removeReaction,
|
|
3573
3661
|
removePresence,
|
|
3662
|
+
releaseLock,
|
|
3574
3663
|
registerAgent,
|
|
3575
3664
|
readMessages,
|
|
3576
3665
|
pinMessage,
|
|
@@ -3581,6 +3670,7 @@ export {
|
|
|
3581
3670
|
listSpaces,
|
|
3582
3671
|
listSessions,
|
|
3583
3672
|
listProjects,
|
|
3673
|
+
listLocks,
|
|
3584
3674
|
listAgents,
|
|
3585
3675
|
leaveSpace,
|
|
3586
3676
|
joinSpace,
|
|
@@ -3610,6 +3700,9 @@ export {
|
|
|
3610
3700
|
createSpace,
|
|
3611
3701
|
createProject,
|
|
3612
3702
|
closeDb,
|
|
3703
|
+
cleanExpiredLocks,
|
|
3704
|
+
checkLock,
|
|
3613
3705
|
archiveSpace,
|
|
3614
|
-
addReaction
|
|
3706
|
+
addReaction,
|
|
3707
|
+
acquireLock
|
|
3615
3708
|
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export interface ResourceLock {
|
|
2
|
+
resource_type: string;
|
|
3
|
+
resource_id: string;
|
|
4
|
+
agent_id: string;
|
|
5
|
+
lock_type: "advisory" | "exclusive";
|
|
6
|
+
locked_at: string;
|
|
7
|
+
expires_at: string;
|
|
8
|
+
}
|
|
9
|
+
export declare function acquireLock(resourceType: string, resourceId: string, agentId: string, lockType?: "advisory" | "exclusive", expiryMs?: number): {
|
|
10
|
+
acquired: boolean;
|
|
11
|
+
lock: ResourceLock | null;
|
|
12
|
+
held_by?: string;
|
|
13
|
+
};
|
|
14
|
+
export declare function releaseLock(resourceType: string, resourceId: string, agentId: string): boolean;
|
|
15
|
+
export declare function checkLock(resourceType: string, resourceId: string): ResourceLock | null;
|
|
16
|
+
export declare function cleanExpiredLocks(): number;
|
|
17
|
+
export declare function listLocks(opts?: {
|
|
18
|
+
resource_type?: string;
|
|
19
|
+
agent_id?: string;
|
|
20
|
+
}): ResourceLock[];
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|