@earendil-works/pi-session-backend-sqlite-node 0.84.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +32 -0
- package/README.md +15 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +87 -0
- package/dist/index.js.map +1 -0
- package/dist/sqlite/branch-cache.d.ts +6 -0
- package/dist/sqlite/branch-cache.d.ts.map +1 -0
- package/dist/sqlite/branch-cache.js +71 -0
- package/dist/sqlite/branch-cache.js.map +1 -0
- package/dist/sqlite/index.d.ts +5 -0
- package/dist/sqlite/index.d.ts.map +1 -0
- package/dist/sqlite/index.js +4 -0
- package/dist/sqlite/index.js.map +1 -0
- package/dist/sqlite/migrations/001_initial.sql +123 -0
- package/dist/sqlite/migrations.d.ts +9 -0
- package/dist/sqlite/migrations.d.ts.map +1 -0
- package/dist/sqlite/migrations.js +38 -0
- package/dist/sqlite/migrations.js.map +1 -0
- package/dist/sqlite/repo.d.ts +41 -0
- package/dist/sqlite/repo.d.ts.map +1 -0
- package/dist/sqlite/repo.js +736 -0
- package/dist/sqlite/repo.js.map +1 -0
- package/dist/sqlite/search-backend.d.ts +9 -0
- package/dist/sqlite/search-backend.d.ts.map +1 -0
- package/dist/sqlite/search-backend.js +118 -0
- package/dist/sqlite/search-backend.js.map +1 -0
- package/dist/sqlite/storage/branch-entries.d.ts +35 -0
- package/dist/sqlite/storage/branch-entries.d.ts.map +1 -0
- package/dist/sqlite/storage/branch-entries.js +86 -0
- package/dist/sqlite/storage/branch-entries.js.map +1 -0
- package/dist/sqlite/storage/branch-tips.d.ts +7 -0
- package/dist/sqlite/storage/branch-tips.d.ts.map +1 -0
- package/dist/sqlite/storage/branch-tips.js +25 -0
- package/dist/sqlite/storage/branch-tips.js.map +1 -0
- package/dist/sqlite/storage/entries.d.ts +29 -0
- package/dist/sqlite/storage/entries.d.ts.map +1 -0
- package/dist/sqlite/storage/entries.js +38 -0
- package/dist/sqlite/storage/entries.js.map +1 -0
- package/dist/sqlite/storage/facts.d.ts +19 -0
- package/dist/sqlite/storage/facts.d.ts.map +1 -0
- package/dist/sqlite/storage/facts.js +41 -0
- package/dist/sqlite/storage/facts.js.map +1 -0
- package/dist/sqlite/storage/lanes.d.ts +34 -0
- package/dist/sqlite/storage/lanes.d.ts.map +1 -0
- package/dist/sqlite/storage/lanes.js +106 -0
- package/dist/sqlite/storage/lanes.js.map +1 -0
- package/dist/sqlite/storage/records.d.ts +38 -0
- package/dist/sqlite/storage/records.d.ts.map +1 -0
- package/dist/sqlite/storage/records.js +69 -0
- package/dist/sqlite/storage/records.js.map +1 -0
- package/dist/sqlite/storage/session-sequences.d.ts +7 -0
- package/dist/sqlite/storage/session-sequences.d.ts.map +1 -0
- package/dist/sqlite/storage/session-sequences.js +23 -0
- package/dist/sqlite/storage/session-sequences.js.map +1 -0
- package/dist/sqlite/storage/session-stats.d.ts +17 -0
- package/dist/sqlite/storage/session-stats.d.ts.map +1 -0
- package/dist/sqlite/storage/session-stats.js +45 -0
- package/dist/sqlite/storage/session-stats.js.map +1 -0
- package/dist/sqlite/storage/sessions.d.ts +26 -0
- package/dist/sqlite/storage/sessions.d.ts.map +1 -0
- package/dist/sqlite/storage/sessions.js +102 -0
- package/dist/sqlite/storage/sessions.js.map +1 -0
- package/dist/sqlite/storage/writer-leases.d.ts +15 -0
- package/dist/sqlite/storage/writer-leases.d.ts.map +1 -0
- package/dist/sqlite/storage/writer-leases.js +30 -0
- package/dist/sqlite/storage/writer-leases.js.map +1 -0
- package/dist/sqlite/types.d.ts +44 -0
- package/dist/sqlite/types.d.ts.map +1 -0
- package/dist/sqlite/types.js +2 -0
- package/dist/sqlite/types.js.map +1 -0
- package/package.json +44 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sessions.d.ts","sourceRoot":"","sources":["../../../src/sqlite/storage/sessions.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AAEzE,MAAM,WAAW,UAAU;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,GAAG,EAAE,MAAM,CAAC;IACZ,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,gBAAgB,EAAE,MAAM,CAAC;IACzB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B;AAED,MAAM,WAAW,aAAa;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,GAAG,EAAE,MAAM,CAAC;IACZ,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACnC;AAoBD,wBAAgB,aAAa,CAAC,EAAE,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,WAElE;AAWD,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,cAAc,EAAE,OAAO,EAAE,aAAa,QAQ1E;AAED,wBAAgB,cAAc,CAAC,EAAE,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,0BAmBnE;AAED,wBAAgB,eAAe,CAAC,EAAE,EAAE,cAAc,EAAE,OAAO,GAAE;IAAE,GAAG,CAAC,EAAE,MAAM,CAAA;CAAO,gBAqBjF;AAED,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,QAErE;AAsBD,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,GAAG,qBAAqB,CAY1F","sourcesContent":["import { assertJsonSerializable, SessionError } from \"@earendil-works/pi-agent-core\";\nimport type { SqliteDatabase, SqliteSessionMetadata } from \"../types.ts\";\n\nexport interface SessionRow {\n\tid: string;\n\tcreated_at: string;\n\tmetadata: string | null;\n\tcwd: string;\n\tparent_session_id: string | null;\n\thas_session_name: number;\n\tsession_name: string | null;\n}\n\nexport interface NewSessionRow {\n\tid: string;\n\tcreatedAt: string;\n\tcwd: string;\n\tparentSessionId?: string;\n\tmetadata?: Record<string, unknown>;\n}\n\nfunction parseMetadata(metadata: string | null, sessionId: string): Record<string, unknown> | undefined {\n\tif (metadata === null) return undefined;\n\tlet parsed: unknown;\n\ttry {\n\t\tparsed = JSON.parse(metadata);\n\t} catch (error) {\n\t\tthrow new SessionError(\n\t\t\t\"storage\",\n\t\t\t`Invalid SQLite session ${sessionId}: metadata is not valid JSON`,\n\t\t\terror instanceof Error ? error : undefined,\n\t\t);\n\t}\n\tif (typeof parsed !== \"object\" || parsed === null || Array.isArray(parsed)) {\n\t\tthrow new SessionError(\"storage\", `Invalid SQLite session ${sessionId}: metadata must be an object`);\n\t}\n\treturn parsed as Record<string, unknown>;\n}\n\nexport function sessionExists(db: SqliteDatabase, sessionId: string) {\n\treturn !!db.prepare(\"SELECT 1 AS found FROM sessions WHERE id = ?\").get<{ found: number }>(sessionId);\n}\n\nfunction serializeMetadata(metadata: Record<string, unknown> | undefined): string | null {\n\tif (metadata === undefined) return null;\n\tif (typeof metadata !== \"object\" || metadata === null || Array.isArray(metadata)) {\n\t\tthrow new SessionError(\"invalid_payload\", \"SQLite session metadata must be an object\");\n\t}\n\tassertJsonSerializable(metadata);\n\treturn JSON.stringify(metadata);\n}\n\nexport function insertSessionRow(db: SqliteDatabase, session: NewSessionRow) {\n\tdb.prepare(\"INSERT INTO sessions (id, created_at, metadata, cwd, parent_session_id) VALUES (?, ?, ?, ?, ?)\").run(\n\t\tsession.id,\n\t\tsession.createdAt,\n\t\tserializeMetadata(session.metadata),\n\t\tsession.cwd,\n\t\tsession.parentSessionId ?? null,\n\t);\n}\n\nexport function readSessionRow(db: SqliteDatabase, sessionId: string) {\n\treturn db\n\t\t.prepare(\n\t\t\t`SELECT s.id, s.created_at, s.metadata, s.cwd, s.parent_session_id,\n\t\t\t\tname_fact.seq IS NOT NULL AS has_session_name,\n\t\t\t\tname_fact.value AS session_name\n\t\t\tFROM sessions AS s\n\t\t\tLEFT JOIN facts AS name_fact\n\t\t\t\tON name_fact.session_id = s.id\n\t\t\t\tAND name_fact.kind = 'name'\n\t\t\t\tAND name_fact.key IS NULL\n\t\t\t\tAND name_fact.seq = (\n\t\t\t\t\tSELECT MAX(f.seq)\n\t\t\t\t\tFROM facts AS f\n\t\t\t\t\tWHERE f.session_id = s.id AND f.kind = 'name' AND f.key IS NULL\n\t\t\t\t)\n\t\t\tWHERE s.id = ?`,\n\t\t)\n\t\t.get<SessionRow>(sessionId);\n}\n\nexport function readSessionRows(db: SqliteDatabase, options: { cwd?: string } = {}) {\n\tconst where = options.cwd === undefined ? \"\" : \"WHERE s.cwd = ?\";\n\treturn db\n\t\t.prepare(\n\t\t\t`SELECT s.id, s.created_at, s.metadata, s.cwd, s.parent_session_id,\n\t\t\t\tname_fact.seq IS NOT NULL AS has_session_name,\n\t\t\t\tname_fact.value AS session_name\n\t\t\tFROM sessions AS s\n\t\t\tLEFT JOIN facts AS name_fact\n\t\t\t\tON name_fact.session_id = s.id\n\t\t\t\tAND name_fact.kind = 'name'\n\t\t\t\tAND name_fact.key IS NULL\n\t\t\t\tAND name_fact.seq = (\n\t\t\t\t\tSELECT MAX(f.seq)\n\t\t\t\t\tFROM facts AS f\n\t\t\t\t\tWHERE f.session_id = s.id AND f.kind = 'name' AND f.key IS NULL\n\t\t\t\t)\n\t\t\t${where}\n\t\t\tORDER BY s.created_at DESC`,\n\t\t)\n\t\t.all<SessionRow>(...(options.cwd === undefined ? [] : [options.cwd]));\n}\n\nexport function deleteSessionRow(db: SqliteDatabase, sessionId: string) {\n\tdb.prepare(\"DELETE FROM sessions WHERE id = ?\").run(sessionId);\n}\n\nfunction parseSessionName(value: string | null, sessionId: string): string {\n\tif (value === null) {\n\t\tthrow new SessionError(\"storage\", `Invalid SQLite session ${sessionId}: name must be a string`);\n\t}\n\tlet parsed: unknown;\n\ttry {\n\t\tparsed = JSON.parse(value);\n\t} catch (error) {\n\t\tthrow new SessionError(\n\t\t\t\"storage\",\n\t\t\t`Invalid SQLite session ${sessionId}: name is not valid JSON`,\n\t\t\terror instanceof Error ? error : undefined,\n\t\t);\n\t}\n\tif (typeof parsed !== \"string\") {\n\t\tthrow new SessionError(\"storage\", `Invalid SQLite session ${sessionId}: name must be a string`);\n\t}\n\treturn parsed;\n}\n\nexport function decodeSessionMetadata(row: SessionRow, path: string): SqliteSessionMetadata {\n\tconst metadata = parseMetadata(row.metadata, row.id);\n\tconst name = row.has_session_name === 0 ? undefined : parseSessionName(row.session_name, row.id);\n\treturn {\n\t\tid: row.id,\n\t\tcreatedAt: Date.parse(row.created_at),\n\t\t...(name === undefined ? {} : { name }),\n\t\tcwd: row.cwd,\n\t\tpath,\n\t\tparentSessionId: row.parent_session_id ?? undefined,\n\t\tmetadata,\n\t};\n}\n"]}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { assertJsonSerializable, SessionError } from "@earendil-works/pi-agent-core";
|
|
2
|
+
function parseMetadata(metadata, sessionId) {
|
|
3
|
+
if (metadata === null)
|
|
4
|
+
return undefined;
|
|
5
|
+
let parsed;
|
|
6
|
+
try {
|
|
7
|
+
parsed = JSON.parse(metadata);
|
|
8
|
+
}
|
|
9
|
+
catch (error) {
|
|
10
|
+
throw new SessionError("storage", `Invalid SQLite session ${sessionId}: metadata is not valid JSON`, error instanceof Error ? error : undefined);
|
|
11
|
+
}
|
|
12
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
13
|
+
throw new SessionError("storage", `Invalid SQLite session ${sessionId}: metadata must be an object`);
|
|
14
|
+
}
|
|
15
|
+
return parsed;
|
|
16
|
+
}
|
|
17
|
+
export function sessionExists(db, sessionId) {
|
|
18
|
+
return !!db.prepare("SELECT 1 AS found FROM sessions WHERE id = ?").get(sessionId);
|
|
19
|
+
}
|
|
20
|
+
function serializeMetadata(metadata) {
|
|
21
|
+
if (metadata === undefined)
|
|
22
|
+
return null;
|
|
23
|
+
if (typeof metadata !== "object" || metadata === null || Array.isArray(metadata)) {
|
|
24
|
+
throw new SessionError("invalid_payload", "SQLite session metadata must be an object");
|
|
25
|
+
}
|
|
26
|
+
assertJsonSerializable(metadata);
|
|
27
|
+
return JSON.stringify(metadata);
|
|
28
|
+
}
|
|
29
|
+
export function insertSessionRow(db, session) {
|
|
30
|
+
db.prepare("INSERT INTO sessions (id, created_at, metadata, cwd, parent_session_id) VALUES (?, ?, ?, ?, ?)").run(session.id, session.createdAt, serializeMetadata(session.metadata), session.cwd, session.parentSessionId ?? null);
|
|
31
|
+
}
|
|
32
|
+
export function readSessionRow(db, sessionId) {
|
|
33
|
+
return db
|
|
34
|
+
.prepare(`SELECT s.id, s.created_at, s.metadata, s.cwd, s.parent_session_id,
|
|
35
|
+
name_fact.seq IS NOT NULL AS has_session_name,
|
|
36
|
+
name_fact.value AS session_name
|
|
37
|
+
FROM sessions AS s
|
|
38
|
+
LEFT JOIN facts AS name_fact
|
|
39
|
+
ON name_fact.session_id = s.id
|
|
40
|
+
AND name_fact.kind = 'name'
|
|
41
|
+
AND name_fact.key IS NULL
|
|
42
|
+
AND name_fact.seq = (
|
|
43
|
+
SELECT MAX(f.seq)
|
|
44
|
+
FROM facts AS f
|
|
45
|
+
WHERE f.session_id = s.id AND f.kind = 'name' AND f.key IS NULL
|
|
46
|
+
)
|
|
47
|
+
WHERE s.id = ?`)
|
|
48
|
+
.get(sessionId);
|
|
49
|
+
}
|
|
50
|
+
export function readSessionRows(db, options = {}) {
|
|
51
|
+
const where = options.cwd === undefined ? "" : "WHERE s.cwd = ?";
|
|
52
|
+
return db
|
|
53
|
+
.prepare(`SELECT s.id, s.created_at, s.metadata, s.cwd, s.parent_session_id,
|
|
54
|
+
name_fact.seq IS NOT NULL AS has_session_name,
|
|
55
|
+
name_fact.value AS session_name
|
|
56
|
+
FROM sessions AS s
|
|
57
|
+
LEFT JOIN facts AS name_fact
|
|
58
|
+
ON name_fact.session_id = s.id
|
|
59
|
+
AND name_fact.kind = 'name'
|
|
60
|
+
AND name_fact.key IS NULL
|
|
61
|
+
AND name_fact.seq = (
|
|
62
|
+
SELECT MAX(f.seq)
|
|
63
|
+
FROM facts AS f
|
|
64
|
+
WHERE f.session_id = s.id AND f.kind = 'name' AND f.key IS NULL
|
|
65
|
+
)
|
|
66
|
+
${where}
|
|
67
|
+
ORDER BY s.created_at DESC`)
|
|
68
|
+
.all(...(options.cwd === undefined ? [] : [options.cwd]));
|
|
69
|
+
}
|
|
70
|
+
export function deleteSessionRow(db, sessionId) {
|
|
71
|
+
db.prepare("DELETE FROM sessions WHERE id = ?").run(sessionId);
|
|
72
|
+
}
|
|
73
|
+
function parseSessionName(value, sessionId) {
|
|
74
|
+
if (value === null) {
|
|
75
|
+
throw new SessionError("storage", `Invalid SQLite session ${sessionId}: name must be a string`);
|
|
76
|
+
}
|
|
77
|
+
let parsed;
|
|
78
|
+
try {
|
|
79
|
+
parsed = JSON.parse(value);
|
|
80
|
+
}
|
|
81
|
+
catch (error) {
|
|
82
|
+
throw new SessionError("storage", `Invalid SQLite session ${sessionId}: name is not valid JSON`, error instanceof Error ? error : undefined);
|
|
83
|
+
}
|
|
84
|
+
if (typeof parsed !== "string") {
|
|
85
|
+
throw new SessionError("storage", `Invalid SQLite session ${sessionId}: name must be a string`);
|
|
86
|
+
}
|
|
87
|
+
return parsed;
|
|
88
|
+
}
|
|
89
|
+
export function decodeSessionMetadata(row, path) {
|
|
90
|
+
const metadata = parseMetadata(row.metadata, row.id);
|
|
91
|
+
const name = row.has_session_name === 0 ? undefined : parseSessionName(row.session_name, row.id);
|
|
92
|
+
return {
|
|
93
|
+
id: row.id,
|
|
94
|
+
createdAt: Date.parse(row.created_at),
|
|
95
|
+
...(name === undefined ? {} : { name }),
|
|
96
|
+
cwd: row.cwd,
|
|
97
|
+
path,
|
|
98
|
+
parentSessionId: row.parent_session_id ?? undefined,
|
|
99
|
+
metadata,
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
//# sourceMappingURL=sessions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sessions.js","sourceRoot":"","sources":["../../../src/sqlite/storage/sessions.ts"],"names":[],"mappings":"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","sourcesContent":["import { assertJsonSerializable, SessionError } from \"@earendil-works/pi-agent-core\";\nimport type { SqliteDatabase, SqliteSessionMetadata } from \"../types.ts\";\n\nexport interface SessionRow {\n\tid: string;\n\tcreated_at: string;\n\tmetadata: string | null;\n\tcwd: string;\n\tparent_session_id: string | null;\n\thas_session_name: number;\n\tsession_name: string | null;\n}\n\nexport interface NewSessionRow {\n\tid: string;\n\tcreatedAt: string;\n\tcwd: string;\n\tparentSessionId?: string;\n\tmetadata?: Record<string, unknown>;\n}\n\nfunction parseMetadata(metadata: string | null, sessionId: string): Record<string, unknown> | undefined {\n\tif (metadata === null) return undefined;\n\tlet parsed: unknown;\n\ttry {\n\t\tparsed = JSON.parse(metadata);\n\t} catch (error) {\n\t\tthrow new SessionError(\n\t\t\t\"storage\",\n\t\t\t`Invalid SQLite session ${sessionId}: metadata is not valid JSON`,\n\t\t\terror instanceof Error ? error : undefined,\n\t\t);\n\t}\n\tif (typeof parsed !== \"object\" || parsed === null || Array.isArray(parsed)) {\n\t\tthrow new SessionError(\"storage\", `Invalid SQLite session ${sessionId}: metadata must be an object`);\n\t}\n\treturn parsed as Record<string, unknown>;\n}\n\nexport function sessionExists(db: SqliteDatabase, sessionId: string) {\n\treturn !!db.prepare(\"SELECT 1 AS found FROM sessions WHERE id = ?\").get<{ found: number }>(sessionId);\n}\n\nfunction serializeMetadata(metadata: Record<string, unknown> | undefined): string | null {\n\tif (metadata === undefined) return null;\n\tif (typeof metadata !== \"object\" || metadata === null || Array.isArray(metadata)) {\n\t\tthrow new SessionError(\"invalid_payload\", \"SQLite session metadata must be an object\");\n\t}\n\tassertJsonSerializable(metadata);\n\treturn JSON.stringify(metadata);\n}\n\nexport function insertSessionRow(db: SqliteDatabase, session: NewSessionRow) {\n\tdb.prepare(\"INSERT INTO sessions (id, created_at, metadata, cwd, parent_session_id) VALUES (?, ?, ?, ?, ?)\").run(\n\t\tsession.id,\n\t\tsession.createdAt,\n\t\tserializeMetadata(session.metadata),\n\t\tsession.cwd,\n\t\tsession.parentSessionId ?? null,\n\t);\n}\n\nexport function readSessionRow(db: SqliteDatabase, sessionId: string) {\n\treturn db\n\t\t.prepare(\n\t\t\t`SELECT s.id, s.created_at, s.metadata, s.cwd, s.parent_session_id,\n\t\t\t\tname_fact.seq IS NOT NULL AS has_session_name,\n\t\t\t\tname_fact.value AS session_name\n\t\t\tFROM sessions AS s\n\t\t\tLEFT JOIN facts AS name_fact\n\t\t\t\tON name_fact.session_id = s.id\n\t\t\t\tAND name_fact.kind = 'name'\n\t\t\t\tAND name_fact.key IS NULL\n\t\t\t\tAND name_fact.seq = (\n\t\t\t\t\tSELECT MAX(f.seq)\n\t\t\t\t\tFROM facts AS f\n\t\t\t\t\tWHERE f.session_id = s.id AND f.kind = 'name' AND f.key IS NULL\n\t\t\t\t)\n\t\t\tWHERE s.id = ?`,\n\t\t)\n\t\t.get<SessionRow>(sessionId);\n}\n\nexport function readSessionRows(db: SqliteDatabase, options: { cwd?: string } = {}) {\n\tconst where = options.cwd === undefined ? \"\" : \"WHERE s.cwd = ?\";\n\treturn db\n\t\t.prepare(\n\t\t\t`SELECT s.id, s.created_at, s.metadata, s.cwd, s.parent_session_id,\n\t\t\t\tname_fact.seq IS NOT NULL AS has_session_name,\n\t\t\t\tname_fact.value AS session_name\n\t\t\tFROM sessions AS s\n\t\t\tLEFT JOIN facts AS name_fact\n\t\t\t\tON name_fact.session_id = s.id\n\t\t\t\tAND name_fact.kind = 'name'\n\t\t\t\tAND name_fact.key IS NULL\n\t\t\t\tAND name_fact.seq = (\n\t\t\t\t\tSELECT MAX(f.seq)\n\t\t\t\t\tFROM facts AS f\n\t\t\t\t\tWHERE f.session_id = s.id AND f.kind = 'name' AND f.key IS NULL\n\t\t\t\t)\n\t\t\t${where}\n\t\t\tORDER BY s.created_at DESC`,\n\t\t)\n\t\t.all<SessionRow>(...(options.cwd === undefined ? [] : [options.cwd]));\n}\n\nexport function deleteSessionRow(db: SqliteDatabase, sessionId: string) {\n\tdb.prepare(\"DELETE FROM sessions WHERE id = ?\").run(sessionId);\n}\n\nfunction parseSessionName(value: string | null, sessionId: string): string {\n\tif (value === null) {\n\t\tthrow new SessionError(\"storage\", `Invalid SQLite session ${sessionId}: name must be a string`);\n\t}\n\tlet parsed: unknown;\n\ttry {\n\t\tparsed = JSON.parse(value);\n\t} catch (error) {\n\t\tthrow new SessionError(\n\t\t\t\"storage\",\n\t\t\t`Invalid SQLite session ${sessionId}: name is not valid JSON`,\n\t\t\terror instanceof Error ? error : undefined,\n\t\t);\n\t}\n\tif (typeof parsed !== \"string\") {\n\t\tthrow new SessionError(\"storage\", `Invalid SQLite session ${sessionId}: name must be a string`);\n\t}\n\treturn parsed;\n}\n\nexport function decodeSessionMetadata(row: SessionRow, path: string): SqliteSessionMetadata {\n\tconst metadata = parseMetadata(row.metadata, row.id);\n\tconst name = row.has_session_name === 0 ? undefined : parseSessionName(row.session_name, row.id);\n\treturn {\n\t\tid: row.id,\n\t\tcreatedAt: Date.parse(row.created_at),\n\t\t...(name === undefined ? {} : { name }),\n\t\tcwd: row.cwd,\n\t\tpath,\n\t\tparentSessionId: row.parent_session_id ?? undefined,\n\t\tmetadata,\n\t};\n}\n"]}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { SqliteDatabase } from "../types.ts";
|
|
2
|
+
export interface WriterLease {
|
|
3
|
+
ownerId: string;
|
|
4
|
+
fence: number;
|
|
5
|
+
expiresAtMs: number;
|
|
6
|
+
}
|
|
7
|
+
export declare function acquireWriterLease(db: SqliteDatabase, sessionId: string, ownerId: string, now: number, expiresAtMs: number): {
|
|
8
|
+
ownerId: string;
|
|
9
|
+
fence: number;
|
|
10
|
+
expiresAtMs: number;
|
|
11
|
+
} | undefined;
|
|
12
|
+
export declare function renewWriterLease(db: SqliteDatabase, sessionId: string, lease: WriterLease, now: number, expiresAtMs: number): boolean;
|
|
13
|
+
export declare function releaseWriterLease(db: SqliteDatabase, sessionId: string, lease: WriterLease): void;
|
|
14
|
+
export declare function deleteWriterLease(db: SqliteDatabase, sessionId: string): void;
|
|
15
|
+
//# sourceMappingURL=writer-leases.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"writer-leases.d.ts","sourceRoot":"","sources":["../../../src/sqlite/storage/writer-leases.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAElD,MAAM,WAAW,WAAW;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;CACpB;AAQD,wBAAgB,kBAAkB,CACjC,EAAE,EAAE,cAAc,EAClB,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,EACf,GAAG,EAAE,MAAM,EACX,WAAW,EAAE,MAAM;;;;cAenB;AAED,wBAAgB,gBAAgB,CAC/B,EAAE,EAAE,cAAc,EAClB,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,WAAW,EAClB,GAAG,EAAE,MAAM,EACX,WAAW,EAAE,MAAM,WAWnB;AAED,wBAAgB,kBAAkB,CAAC,EAAE,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW,QAM3F;AAED,wBAAgB,iBAAiB,CAAC,EAAE,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,QAEtE","sourcesContent":["import type { SqliteDatabase } from \"../types.ts\";\n\nexport interface WriterLease {\n\townerId: string;\n\tfence: number;\n\texpiresAtMs: number;\n}\n\ninterface WriterLeaseRow {\n\towner_id: string;\n\tfence: number;\n\texpires_at_ms: number;\n}\n\nexport function acquireWriterLease(\n\tdb: SqliteDatabase,\n\tsessionId: string,\n\townerId: string,\n\tnow: number,\n\texpiresAtMs: number,\n) {\n\tconst row = db\n\t\t.prepare(\n\t\t\t`INSERT INTO writer_leases (session_id, owner_id, fence, expires_at_ms)\n\t\t\tVALUES (?, ?, 1, ?)\n\t\t\tON CONFLICT(session_id) DO UPDATE SET\n\t\t\t\towner_id = excluded.owner_id,\n\t\t\t\tfence = writer_leases.fence + 1,\n\t\t\t\texpires_at_ms = excluded.expires_at_ms\n\t\t\tWHERE writer_leases.expires_at_ms <= ?\n\t\t\tRETURNING owner_id, fence, expires_at_ms`,\n\t\t)\n\t\t.get<WriterLeaseRow>(sessionId, ownerId, expiresAtMs, now);\n\treturn row === undefined ? undefined : { ownerId: row.owner_id, fence: row.fence, expiresAtMs: row.expires_at_ms };\n}\n\nexport function renewWriterLease(\n\tdb: SqliteDatabase,\n\tsessionId: string,\n\tlease: WriterLease,\n\tnow: number,\n\texpiresAtMs: number,\n) {\n\tconst result = db\n\t\t.prepare(\n\t\t\t`UPDATE writer_leases\n\t\t\tSET expires_at_ms = ?\n\t\t\tWHERE session_id = ? AND owner_id = ? AND fence = ? AND expires_at_ms > ?`,\n\t\t)\n\t\t.run(expiresAtMs, sessionId, lease.ownerId, lease.fence, now);\n\tif (result.changes === 1) lease.expiresAtMs = expiresAtMs;\n\treturn result.changes === 1;\n}\n\nexport function releaseWriterLease(db: SqliteDatabase, sessionId: string, lease: WriterLease) {\n\tdb.prepare(\"DELETE FROM writer_leases WHERE session_id = ? AND owner_id = ? AND fence = ?\").run(\n\t\tsessionId,\n\t\tlease.ownerId,\n\t\tlease.fence,\n\t);\n}\n\nexport function deleteWriterLease(db: SqliteDatabase, sessionId: string) {\n\tdb.prepare(\"DELETE FROM writer_leases WHERE session_id = ?\").run(sessionId);\n}\n"]}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
export function acquireWriterLease(db, sessionId, ownerId, now, expiresAtMs) {
|
|
2
|
+
const row = db
|
|
3
|
+
.prepare(`INSERT INTO writer_leases (session_id, owner_id, fence, expires_at_ms)
|
|
4
|
+
VALUES (?, ?, 1, ?)
|
|
5
|
+
ON CONFLICT(session_id) DO UPDATE SET
|
|
6
|
+
owner_id = excluded.owner_id,
|
|
7
|
+
fence = writer_leases.fence + 1,
|
|
8
|
+
expires_at_ms = excluded.expires_at_ms
|
|
9
|
+
WHERE writer_leases.expires_at_ms <= ?
|
|
10
|
+
RETURNING owner_id, fence, expires_at_ms`)
|
|
11
|
+
.get(sessionId, ownerId, expiresAtMs, now);
|
|
12
|
+
return row === undefined ? undefined : { ownerId: row.owner_id, fence: row.fence, expiresAtMs: row.expires_at_ms };
|
|
13
|
+
}
|
|
14
|
+
export function renewWriterLease(db, sessionId, lease, now, expiresAtMs) {
|
|
15
|
+
const result = db
|
|
16
|
+
.prepare(`UPDATE writer_leases
|
|
17
|
+
SET expires_at_ms = ?
|
|
18
|
+
WHERE session_id = ? AND owner_id = ? AND fence = ? AND expires_at_ms > ?`)
|
|
19
|
+
.run(expiresAtMs, sessionId, lease.ownerId, lease.fence, now);
|
|
20
|
+
if (result.changes === 1)
|
|
21
|
+
lease.expiresAtMs = expiresAtMs;
|
|
22
|
+
return result.changes === 1;
|
|
23
|
+
}
|
|
24
|
+
export function releaseWriterLease(db, sessionId, lease) {
|
|
25
|
+
db.prepare("DELETE FROM writer_leases WHERE session_id = ? AND owner_id = ? AND fence = ?").run(sessionId, lease.ownerId, lease.fence);
|
|
26
|
+
}
|
|
27
|
+
export function deleteWriterLease(db, sessionId) {
|
|
28
|
+
db.prepare("DELETE FROM writer_leases WHERE session_id = ?").run(sessionId);
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=writer-leases.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"writer-leases.js","sourceRoot":"","sources":["../../../src/sqlite/storage/writer-leases.ts"],"names":[],"mappings":"AAcA,MAAM,UAAU,kBAAkB,CACjC,EAAkB,EAClB,SAAiB,EACjB,OAAe,EACf,GAAW,EACX,WAAmB,EAClB;IACD,MAAM,GAAG,GAAG,EAAE;SACZ,OAAO,CACP;;;;;;;4CAOyC,CACzC;SACA,GAAG,CAAiB,SAAS,EAAE,OAAO,EAAE,WAAW,EAAE,GAAG,CAAC,CAAC;IAC5D,OAAO,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,GAAG,CAAC,QAAQ,EAAE,KAAK,EAAE,GAAG,CAAC,KAAK,EAAE,WAAW,EAAE,GAAG,CAAC,aAAa,EAAE,CAAC;AAAA,CACnH;AAED,MAAM,UAAU,gBAAgB,CAC/B,EAAkB,EAClB,SAAiB,EACjB,KAAkB,EAClB,GAAW,EACX,WAAmB,EAClB;IACD,MAAM,MAAM,GAAG,EAAE;SACf,OAAO,CACP;;6EAE0E,CAC1E;SACA,GAAG,CAAC,WAAW,EAAE,SAAS,EAAE,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;IAC/D,IAAI,MAAM,CAAC,OAAO,KAAK,CAAC;QAAE,KAAK,CAAC,WAAW,GAAG,WAAW,CAAC;IAC1D,OAAO,MAAM,CAAC,OAAO,KAAK,CAAC,CAAC;AAAA,CAC5B;AAED,MAAM,UAAU,kBAAkB,CAAC,EAAkB,EAAE,SAAiB,EAAE,KAAkB,EAAE;IAC7F,EAAE,CAAC,OAAO,CAAC,+EAA+E,CAAC,CAAC,GAAG,CAC9F,SAAS,EACT,KAAK,CAAC,OAAO,EACb,KAAK,CAAC,KAAK,CACX,CAAC;AAAA,CACF;AAED,MAAM,UAAU,iBAAiB,CAAC,EAAkB,EAAE,SAAiB,EAAE;IACxE,EAAE,CAAC,OAAO,CAAC,gDAAgD,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;AAAA,CAC5E","sourcesContent":["import type { SqliteDatabase } from \"../types.ts\";\n\nexport interface WriterLease {\n\townerId: string;\n\tfence: number;\n\texpiresAtMs: number;\n}\n\ninterface WriterLeaseRow {\n\towner_id: string;\n\tfence: number;\n\texpires_at_ms: number;\n}\n\nexport function acquireWriterLease(\n\tdb: SqliteDatabase,\n\tsessionId: string,\n\townerId: string,\n\tnow: number,\n\texpiresAtMs: number,\n) {\n\tconst row = db\n\t\t.prepare(\n\t\t\t`INSERT INTO writer_leases (session_id, owner_id, fence, expires_at_ms)\n\t\t\tVALUES (?, ?, 1, ?)\n\t\t\tON CONFLICT(session_id) DO UPDATE SET\n\t\t\t\towner_id = excluded.owner_id,\n\t\t\t\tfence = writer_leases.fence + 1,\n\t\t\t\texpires_at_ms = excluded.expires_at_ms\n\t\t\tWHERE writer_leases.expires_at_ms <= ?\n\t\t\tRETURNING owner_id, fence, expires_at_ms`,\n\t\t)\n\t\t.get<WriterLeaseRow>(sessionId, ownerId, expiresAtMs, now);\n\treturn row === undefined ? undefined : { ownerId: row.owner_id, fence: row.fence, expiresAtMs: row.expires_at_ms };\n}\n\nexport function renewWriterLease(\n\tdb: SqliteDatabase,\n\tsessionId: string,\n\tlease: WriterLease,\n\tnow: number,\n\texpiresAtMs: number,\n) {\n\tconst result = db\n\t\t.prepare(\n\t\t\t`UPDATE writer_leases\n\t\t\tSET expires_at_ms = ?\n\t\t\tWHERE session_id = ? AND owner_id = ? AND fence = ? AND expires_at_ms > ?`,\n\t\t)\n\t\t.run(expiresAtMs, sessionId, lease.ownerId, lease.fence, now);\n\tif (result.changes === 1) lease.expiresAtMs = expiresAtMs;\n\treturn result.changes === 1;\n}\n\nexport function releaseWriterLease(db: SqliteDatabase, sessionId: string, lease: WriterLease) {\n\tdb.prepare(\"DELETE FROM writer_leases WHERE session_id = ? AND owner_id = ? AND fence = ?\").run(\n\t\tsessionId,\n\t\tlease.ownerId,\n\t\tlease.fence,\n\t);\n}\n\nexport function deleteWriterLease(db: SqliteDatabase, sessionId: string) {\n\tdb.prepare(\"DELETE FROM writer_leases WHERE session_id = ?\").run(sessionId);\n}\n"]}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { FileSystem, SessionCreateOptions, SessionMetadata } from "@earendil-works/pi-agent-core";
|
|
2
|
+
/** Result of a prepared SQLite statement execution. */
|
|
3
|
+
export interface SqliteRunResult {
|
|
4
|
+
/** Number of rows changed by the statement. */
|
|
5
|
+
changes: number;
|
|
6
|
+
/** Inserted row id when the backend exposes one. */
|
|
7
|
+
lastInsertRowid?: number;
|
|
8
|
+
}
|
|
9
|
+
/** Prepared SQLite statement capability used by the SQLite session backend. */
|
|
10
|
+
export interface SqliteStatement {
|
|
11
|
+
run(...params: unknown[]): SqliteRunResult;
|
|
12
|
+
get<TRow extends object>(...params: unknown[]): TRow | undefined;
|
|
13
|
+
all<TRow extends object>(...params: unknown[]): TRow[];
|
|
14
|
+
}
|
|
15
|
+
/** SQLite database capability used by the SQLite session backend. */
|
|
16
|
+
export interface SqliteDatabase {
|
|
17
|
+
exec(sql: string): void;
|
|
18
|
+
prepare(sql: string): SqliteStatement;
|
|
19
|
+
/** Runs a synchronous write transaction. The callback must not return a promise. */
|
|
20
|
+
transaction<T>(fn: () => T): T;
|
|
21
|
+
close(): void;
|
|
22
|
+
}
|
|
23
|
+
export interface SqliteDatabaseFactory {
|
|
24
|
+
open(path: string): Promise<SqliteDatabase>;
|
|
25
|
+
}
|
|
26
|
+
export interface SqliteSessionMetadata extends SessionMetadata {
|
|
27
|
+
cwd: string;
|
|
28
|
+
path: string;
|
|
29
|
+
parentSessionId?: string;
|
|
30
|
+
/** Current session name projected from SQLite global facts. */
|
|
31
|
+
name?: string;
|
|
32
|
+
/** Opaque application-owned metadata. */
|
|
33
|
+
metadata?: Record<string, unknown>;
|
|
34
|
+
}
|
|
35
|
+
export interface SqliteSessionCreateOptions extends SessionCreateOptions {
|
|
36
|
+
cwd: string;
|
|
37
|
+
parentSessionId?: string;
|
|
38
|
+
metadata?: Record<string, unknown>;
|
|
39
|
+
}
|
|
40
|
+
export interface SqliteSessionListOptions {
|
|
41
|
+
cwd?: string;
|
|
42
|
+
}
|
|
43
|
+
export type SqliteSessionRepositoryEnv = Pick<FileSystem, "absolutePath" | "createDir" | "exists">;
|
|
44
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/sqlite/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,oBAAoB,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;AAEvG,uDAAuD;AACvD,MAAM,WAAW,eAAe;IAC/B,+CAA+C;IAC/C,OAAO,EAAE,MAAM,CAAC;IAChB,oDAAoD;IACpD,eAAe,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,+EAA+E;AAC/E,MAAM,WAAW,eAAe;IAC/B,GAAG,CAAC,GAAG,MAAM,EAAE,OAAO,EAAE,GAAG,eAAe,CAAC;IAC3C,GAAG,CAAC,IAAI,SAAS,MAAM,EAAE,GAAG,MAAM,EAAE,OAAO,EAAE,GAAG,IAAI,GAAG,SAAS,CAAC;IACjE,GAAG,CAAC,IAAI,SAAS,MAAM,EAAE,GAAG,MAAM,EAAE,OAAO,EAAE,GAAG,IAAI,EAAE,CAAC;CACvD;AAED,qEAAqE;AACrE,MAAM,WAAW,cAAc;IAC9B,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,eAAe,CAAC;IACtC,oFAAoF;IACpF,WAAW,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC;IAC/B,KAAK,IAAI,IAAI,CAAC;CACd;AAED,MAAM,WAAW,qBAAqB;IACrC,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC,CAAC;CAC5C;AAED,MAAM,WAAW,qBAAsB,SAAQ,eAAe;IAC7D,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,+DAA+D;IAC/D,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,yCAAyC;IACzC,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACnC;AAED,MAAM,WAAW,0BAA2B,SAAQ,oBAAoB;IACvE,GAAG,EAAE,MAAM,CAAC;IACZ,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACnC;AAED,MAAM,WAAW,wBAAwB;IACxC,GAAG,CAAC,EAAE,MAAM,CAAC;CACb;AAED,MAAM,MAAM,0BAA0B,GAAG,IAAI,CAAC,UAAU,EAAE,cAAc,GAAG,WAAW,GAAG,QAAQ,CAAC,CAAC","sourcesContent":["import type { FileSystem, SessionCreateOptions, SessionMetadata } from \"@earendil-works/pi-agent-core\";\n\n/** Result of a prepared SQLite statement execution. */\nexport interface SqliteRunResult {\n\t/** Number of rows changed by the statement. */\n\tchanges: number;\n\t/** Inserted row id when the backend exposes one. */\n\tlastInsertRowid?: number;\n}\n\n/** Prepared SQLite statement capability used by the SQLite session backend. */\nexport interface SqliteStatement {\n\trun(...params: unknown[]): SqliteRunResult;\n\tget<TRow extends object>(...params: unknown[]): TRow | undefined;\n\tall<TRow extends object>(...params: unknown[]): TRow[];\n}\n\n/** SQLite database capability used by the SQLite session backend. */\nexport interface SqliteDatabase {\n\texec(sql: string): void;\n\tprepare(sql: string): SqliteStatement;\n\t/** Runs a synchronous write transaction. The callback must not return a promise. */\n\ttransaction<T>(fn: () => T): T;\n\tclose(): void;\n}\n\nexport interface SqliteDatabaseFactory {\n\topen(path: string): Promise<SqliteDatabase>;\n}\n\nexport interface SqliteSessionMetadata extends SessionMetadata {\n\tcwd: string;\n\tpath: string;\n\tparentSessionId?: string;\n\t/** Current session name projected from SQLite global facts. */\n\tname?: string;\n\t/** Opaque application-owned metadata. */\n\tmetadata?: Record<string, unknown>;\n}\n\nexport interface SqliteSessionCreateOptions extends SessionCreateOptions {\n\tcwd: string;\n\tparentSessionId?: string;\n\tmetadata?: Record<string, unknown>;\n}\n\nexport interface SqliteSessionListOptions {\n\tcwd?: string;\n}\n\nexport type SqliteSessionRepositoryEnv = Pick<FileSystem, \"absolutePath\" | \"createDir\" | \"exists\">;\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/sqlite/types.ts"],"names":[],"mappings":"","sourcesContent":["import type { FileSystem, SessionCreateOptions, SessionMetadata } from \"@earendil-works/pi-agent-core\";\n\n/** Result of a prepared SQLite statement execution. */\nexport interface SqliteRunResult {\n\t/** Number of rows changed by the statement. */\n\tchanges: number;\n\t/** Inserted row id when the backend exposes one. */\n\tlastInsertRowid?: number;\n}\n\n/** Prepared SQLite statement capability used by the SQLite session backend. */\nexport interface SqliteStatement {\n\trun(...params: unknown[]): SqliteRunResult;\n\tget<TRow extends object>(...params: unknown[]): TRow | undefined;\n\tall<TRow extends object>(...params: unknown[]): TRow[];\n}\n\n/** SQLite database capability used by the SQLite session backend. */\nexport interface SqliteDatabase {\n\texec(sql: string): void;\n\tprepare(sql: string): SqliteStatement;\n\t/** Runs a synchronous write transaction. The callback must not return a promise. */\n\ttransaction<T>(fn: () => T): T;\n\tclose(): void;\n}\n\nexport interface SqliteDatabaseFactory {\n\topen(path: string): Promise<SqliteDatabase>;\n}\n\nexport interface SqliteSessionMetadata extends SessionMetadata {\n\tcwd: string;\n\tpath: string;\n\tparentSessionId?: string;\n\t/** Current session name projected from SQLite global facts. */\n\tname?: string;\n\t/** Opaque application-owned metadata. */\n\tmetadata?: Record<string, unknown>;\n}\n\nexport interface SqliteSessionCreateOptions extends SessionCreateOptions {\n\tcwd: string;\n\tparentSessionId?: string;\n\tmetadata?: Record<string, unknown>;\n}\n\nexport interface SqliteSessionListOptions {\n\tcwd?: string;\n}\n\nexport type SqliteSessionRepositoryEnv = Pick<FileSystem, \"absolutePath\" | \"createDir\" | \"exists\">;\n"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@earendil-works/pi-session-backend-sqlite-node",
|
|
3
|
+
"version": "0.84.0",
|
|
4
|
+
"description": "Node sqlite session backend for @earendil-works/pi-agent-core sessions",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.js"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"files": [
|
|
15
|
+
"dist",
|
|
16
|
+
"README.md",
|
|
17
|
+
"CHANGELOG.md"
|
|
18
|
+
],
|
|
19
|
+
"scripts": {
|
|
20
|
+
"clean": "node -e \"import('node:fs/promises').then(fs=>fs.rm('dist',{recursive:true,force:true}))\"",
|
|
21
|
+
"build": "tsgo -p tsconfig.build.json && node ./scripts/prepare-dist.mjs copy-sqlite-migrations",
|
|
22
|
+
"test": "vitest --run",
|
|
23
|
+
"coverage": "vitest --run --coverage",
|
|
24
|
+
"prepublishOnly": "npm run clean && npm run build"
|
|
25
|
+
},
|
|
26
|
+
"author": "Earendil Works",
|
|
27
|
+
"license": "MIT",
|
|
28
|
+
"repository": {
|
|
29
|
+
"type": "git",
|
|
30
|
+
"url": "git+https://github.com/earendil-works/pi.git",
|
|
31
|
+
"directory": "packages/session-backends/sqlite-node"
|
|
32
|
+
},
|
|
33
|
+
"engines": {
|
|
34
|
+
"node": ">=22.19.0"
|
|
35
|
+
},
|
|
36
|
+
"dependencies": {
|
|
37
|
+
"@earendil-works/pi-ai": "^0.84.0",
|
|
38
|
+
"@earendil-works/pi-agent-core": "^0.84.0"
|
|
39
|
+
},
|
|
40
|
+
"devDependencies": {
|
|
41
|
+
"@vitest/coverage-v8": "4.1.9",
|
|
42
|
+
"vitest": "4.1.9"
|
|
43
|
+
}
|
|
44
|
+
}
|