@ai-ecoverse/slicc-agent 1.1.0 → 1.2.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/README.md +1 -0
- package/dist/index.js +10 -0
- package/dist/index.js.map +2 -2
- package/dist/opfs-pool.js +55 -0
- package/dist/opfs-pool.js.map +7 -0
- package/dist/sqlite.js +158 -0
- package/dist/sqlite.js.map +7 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/opfs-pool.d.ts +9 -0
- package/dist/types/sqlite.d.ts +39 -0
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -16,6 +16,7 @@ await agent.prompt('What is in /home?');
|
|
|
16
16
|
```
|
|
17
17
|
|
|
18
18
|
- `openAgent({ models, model, storage?, registry?, settings? })` opens a durable Harness (in memory unless `storage` is given) and its root conversation. `prompt(text)` submits an input and resolves with the answer's text.
|
|
19
|
+
- `openOpfsSqliteStorage({ directory?, file? })` keeps the session in wasm SQLite ([`@sqlite.org/sqlite-wasm`](https://www.npmjs.com/package/@sqlite.org/sqlite-wasm)) in an `opfs-sahpool` pool, by default `/.slicc/agent/` in OPFS. It needs a dedicated worker. Only one worker holds a pool at a time: the opener takes a Web Lock per directory and waits until every file of the pool can be opened before sqlite-wasm installs it, because a failed install deletes the pool's directory. `openMemorySqliteStorage()` keeps the same database in memory. Pass either as `storage`.
|
|
19
20
|
- `serveAgent(endpoint, agent)` answers prompts that arrive on a worker or `MessagePort`; `connectAgent(endpoint)` is the page side.
|
|
20
21
|
|
|
21
22
|
## Develop
|
package/dist/index.js
CHANGED
|
@@ -3,10 +3,20 @@ import {
|
|
|
3
3
|
connectAgent,
|
|
4
4
|
serveAgent
|
|
5
5
|
} from "./port.js";
|
|
6
|
+
import {
|
|
7
|
+
AGENT_DIRECTORY,
|
|
8
|
+
openMemorySqliteStorage,
|
|
9
|
+
openOpfsSqliteStorage,
|
|
10
|
+
WasmSqliteDatabase
|
|
11
|
+
} from "./sqlite.js";
|
|
6
12
|
export {
|
|
13
|
+
AGENT_DIRECTORY,
|
|
14
|
+
WasmSqliteDatabase,
|
|
7
15
|
answerText,
|
|
8
16
|
connectAgent,
|
|
9
17
|
openAgent,
|
|
18
|
+
openMemorySqliteStorage,
|
|
19
|
+
openOpfsSqliteStorage,
|
|
10
20
|
serveAgent
|
|
11
21
|
};
|
|
12
22
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/index.ts"],
|
|
4
|
-
"sourcesContent": ["export { type Agent, type AgentOptions, answerText, openAgent } from './agent.ts';\nexport {\n type AgentClient,\n type AgentReply,\n type AgentRequest,\n connectAgent,\n type MessageEndpoint,\n serveAgent,\n} from './port.ts';\n"],
|
|
5
|
-
"mappings": "AAAA,SAAwC,YAAY,iBAAiB;AACrE;AAAA,EAIE;AAAA,EAEA;AAAA,OACK;",
|
|
4
|
+
"sourcesContent": ["export { type Agent, type AgentOptions, answerText, openAgent } from './agent.ts';\nexport {\n type AgentClient,\n type AgentReply,\n type AgentRequest,\n connectAgent,\n type MessageEndpoint,\n serveAgent,\n} from './port.ts';\nexport {\n AGENT_DIRECTORY,\n type OpfsSqliteOptions,\n openMemorySqliteStorage,\n openOpfsSqliteStorage,\n WasmSqliteDatabase,\n} from './sqlite.ts';\n"],
|
|
5
|
+
"mappings": "AAAA,SAAwC,YAAY,iBAAiB;AACrE;AAAA,EAIE;AAAA,EAEA;AAAA,OACK;AACP;AAAA,EACE;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,OACK;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
const names = /* @__PURE__ */ new Map();
|
|
2
|
+
function vfsName(directory) {
|
|
3
|
+
let name = names.get(directory);
|
|
4
|
+
if (!name) {
|
|
5
|
+
name = `slicc-agent-${names.size}`;
|
|
6
|
+
names.set(directory, name);
|
|
7
|
+
}
|
|
8
|
+
return name;
|
|
9
|
+
}
|
|
10
|
+
function holdLock(locks, name) {
|
|
11
|
+
return new Promise((acquired, failed) => {
|
|
12
|
+
locks.request(name, { mode: "exclusive" }, () => new Promise((release) => acquired(release))).catch(failed);
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
async function opaque(root, directory) {
|
|
16
|
+
let dir = root;
|
|
17
|
+
try {
|
|
18
|
+
for (const part of [...directory.split("/").filter(Boolean), ".opaque"]) {
|
|
19
|
+
dir = await dir.getDirectoryHandle(part);
|
|
20
|
+
}
|
|
21
|
+
} catch {
|
|
22
|
+
return void 0;
|
|
23
|
+
}
|
|
24
|
+
return dir;
|
|
25
|
+
}
|
|
26
|
+
async function acquirable(dir) {
|
|
27
|
+
for await (const handle of dir.values()) {
|
|
28
|
+
if (handle.kind !== "file") continue;
|
|
29
|
+
try {
|
|
30
|
+
const access = await handle.createSyncAccessHandle();
|
|
31
|
+
access.close();
|
|
32
|
+
} catch {
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return true;
|
|
37
|
+
}
|
|
38
|
+
const pause = (ms) => new Promise((resume) => setTimeout(resume, ms));
|
|
39
|
+
async function settlePool(directory, access = {}) {
|
|
40
|
+
const root = await (access.root ?? (() => navigator.storage.getDirectory()))();
|
|
41
|
+
const dir = await opaque(root, directory);
|
|
42
|
+
if (!dir) return;
|
|
43
|
+
const attempts = access.attempts ?? 50;
|
|
44
|
+
for (let attempt = 1; attempt <= attempts; attempt++) {
|
|
45
|
+
if (await acquirable(dir)) return;
|
|
46
|
+
if (attempt < attempts) await pause(access.delayMs ?? 100);
|
|
47
|
+
}
|
|
48
|
+
throw new Error(`SQLite pool ${directory} is still in use by another worker`);
|
|
49
|
+
}
|
|
50
|
+
export {
|
|
51
|
+
holdLock,
|
|
52
|
+
settlePool,
|
|
53
|
+
vfsName
|
|
54
|
+
};
|
|
55
|
+
//# sourceMappingURL=opfs-pool.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/opfs-pool.ts"],
|
|
4
|
+
"sourcesContent": ["export interface PoolAccess {\n root?: () => Promise<FileSystemDirectoryHandle>;\n locks?: LockManager;\n attempts?: number;\n delayMs?: number;\n}\n\ninterface SyncAccessible {\n createSyncAccessHandle(): Promise<{ close(): void }>;\n}\n\nconst names = new Map<string, string>();\n\nexport function vfsName(directory: string): string {\n let name = names.get(directory);\n if (!name) {\n name = `slicc-agent-${names.size}`;\n names.set(directory, name);\n }\n return name;\n}\n\nexport function holdLock(locks: LockManager, name: string): Promise<() => void> {\n return new Promise((acquired, failed) => {\n locks\n .request(name, { mode: 'exclusive' }, () => new Promise<void>((release) => acquired(release)))\n .catch(failed);\n });\n}\n\nasync function opaque(\n root: FileSystemDirectoryHandle,\n directory: string\n): Promise<FileSystemDirectoryHandle | undefined> {\n let dir = root;\n try {\n for (const part of [...directory.split('/').filter(Boolean), '.opaque']) {\n dir = await dir.getDirectoryHandle(part);\n }\n } catch {\n return undefined;\n }\n return dir;\n}\n\nasync function acquirable(dir: FileSystemDirectoryHandle): Promise<boolean> {\n for await (const handle of dir.values()) {\n if (handle.kind !== 'file') continue;\n try {\n const access = await (handle as unknown as SyncAccessible).createSyncAccessHandle();\n access.close();\n } catch {\n return false;\n }\n }\n return true;\n}\n\nconst pause = (ms: number) => new Promise((resume) => setTimeout(resume, ms));\n\nexport async function settlePool(directory: string, access: PoolAccess = {}): Promise<void> {\n const root = await (access.root ?? (() => navigator.storage.getDirectory()))();\n const dir = await opaque(root, directory);\n if (!dir) return;\n const attempts = access.attempts ?? 50;\n for (let attempt = 1; attempt <= attempts; attempt++) {\n if (await acquirable(dir)) return;\n if (attempt < attempts) await pause(access.delayMs ?? 100);\n }\n throw new Error(`SQLite pool ${directory} is still in use by another worker`);\n}\n"],
|
|
5
|
+
"mappings": "AAWA,MAAM,QAAQ,oBAAI,IAAoB;AAE/B,SAAS,QAAQ,WAA2B;AACjD,MAAI,OAAO,MAAM,IAAI,SAAS;AAC9B,MAAI,CAAC,MAAM;AACT,WAAO,eAAe,MAAM,IAAI;AAChC,UAAM,IAAI,WAAW,IAAI;AAAA,EAC3B;AACA,SAAO;AACT;AAEO,SAAS,SAAS,OAAoB,MAAmC;AAC9E,SAAO,IAAI,QAAQ,CAAC,UAAU,WAAW;AACvC,UACG,QAAQ,MAAM,EAAE,MAAM,YAAY,GAAG,MAAM,IAAI,QAAc,CAAC,YAAY,SAAS,OAAO,CAAC,CAAC,EAC5F,MAAM,MAAM;AAAA,EACjB,CAAC;AACH;AAEA,eAAe,OACb,MACA,WACgD;AAChD,MAAI,MAAM;AACV,MAAI;AACF,eAAW,QAAQ,CAAC,GAAG,UAAU,MAAM,GAAG,EAAE,OAAO,OAAO,GAAG,SAAS,GAAG;AACvE,YAAM,MAAM,IAAI,mBAAmB,IAAI;AAAA,IACzC;AAAA,EACF,QAAQ;AACN,WAAO;AAAA,EACT;AACA,SAAO;AACT;AAEA,eAAe,WAAW,KAAkD;AAC1E,mBAAiB,UAAU,IAAI,OAAO,GAAG;AACvC,QAAI,OAAO,SAAS,OAAQ;AAC5B,QAAI;AACF,YAAM,SAAS,MAAO,OAAqC,uBAAuB;AAClF,aAAO,MAAM;AAAA,IACf,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AACA,SAAO;AACT;AAEA,MAAM,QAAQ,CAAC,OAAe,IAAI,QAAQ,CAAC,WAAW,WAAW,QAAQ,EAAE,CAAC;AAE5E,eAAsB,WAAW,WAAmB,SAAqB,CAAC,GAAkB;AAC1F,QAAM,OAAO,OAAO,OAAO,SAAS,MAAM,UAAU,QAAQ,aAAa,IAAI;AAC7E,QAAM,MAAM,MAAM,OAAO,MAAM,SAAS;AACxC,MAAI,CAAC,IAAK;AACV,QAAM,WAAW,OAAO,YAAY;AACpC,WAAS,UAAU,GAAG,WAAW,UAAU,WAAW;AACpD,QAAI,MAAM,WAAW,GAAG,EAAG;AAC3B,QAAI,UAAU,SAAU,OAAM,MAAM,OAAO,WAAW,GAAG;AAAA,EAC3D;AACA,QAAM,IAAI,MAAM,eAAe,SAAS,oCAAoC;AAC9E;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
package/dist/sqlite.js
ADDED
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import {
|
|
2
|
+
SqliteStorage
|
|
3
|
+
} from "@earendil-works/pi-durable/storage/sqlite";
|
|
4
|
+
import init, {} from "@sqlite.org/sqlite-wasm";
|
|
5
|
+
import { holdLock, settlePool, vfsName } from "./opfs-pool.js";
|
|
6
|
+
class SerialQueue {
|
|
7
|
+
#tail = Promise.resolve();
|
|
8
|
+
run(operation) {
|
|
9
|
+
const result = this.#tail.then(operation);
|
|
10
|
+
this.#tail = result.catch(() => {
|
|
11
|
+
});
|
|
12
|
+
return result;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
class Statements {
|
|
16
|
+
#db;
|
|
17
|
+
#cache = /* @__PURE__ */ new Map();
|
|
18
|
+
constructor(db) {
|
|
19
|
+
this.#db = db;
|
|
20
|
+
}
|
|
21
|
+
bound(sql, params) {
|
|
22
|
+
let statement = this.#cache.get(sql);
|
|
23
|
+
if (!statement) {
|
|
24
|
+
statement = this.#db.prepare(sql);
|
|
25
|
+
this.#cache.set(sql, statement);
|
|
26
|
+
}
|
|
27
|
+
statement.reset(true);
|
|
28
|
+
if (params.length > 0) statement.bind([...params]);
|
|
29
|
+
return statement;
|
|
30
|
+
}
|
|
31
|
+
finalize() {
|
|
32
|
+
for (const statement of this.#cache.values()) statement.finalize();
|
|
33
|
+
this.#cache.clear();
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
class WasmSqliteExecutor {
|
|
37
|
+
db;
|
|
38
|
+
statements;
|
|
39
|
+
constructor(db, statements) {
|
|
40
|
+
this.db = db;
|
|
41
|
+
this.statements = statements;
|
|
42
|
+
}
|
|
43
|
+
async exec(sql) {
|
|
44
|
+
this.check();
|
|
45
|
+
this.db.exec(sql);
|
|
46
|
+
}
|
|
47
|
+
async run(sql, ...params) {
|
|
48
|
+
this.check();
|
|
49
|
+
this.statements.bound(sql, params).stepReset();
|
|
50
|
+
}
|
|
51
|
+
async get(sql, ...params) {
|
|
52
|
+
this.check();
|
|
53
|
+
const statement = this.statements.bound(sql, params);
|
|
54
|
+
const row = statement.step() ? statement.get({}) : void 0;
|
|
55
|
+
statement.reset();
|
|
56
|
+
return row;
|
|
57
|
+
}
|
|
58
|
+
async all(sql, ...params) {
|
|
59
|
+
this.check();
|
|
60
|
+
const statement = this.statements.bound(sql, params);
|
|
61
|
+
const rows = [];
|
|
62
|
+
while (statement.step()) rows.push(statement.get({}));
|
|
63
|
+
statement.reset();
|
|
64
|
+
return rows;
|
|
65
|
+
}
|
|
66
|
+
check() {
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
class WasmSqliteTransaction extends WasmSqliteExecutor {
|
|
70
|
+
active = true;
|
|
71
|
+
check() {
|
|
72
|
+
if (!this.active) throw new Error("SQLite transaction handle is no longer active");
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
class WasmSqliteDatabase extends WasmSqliteExecutor {
|
|
76
|
+
#queue = new SerialQueue();
|
|
77
|
+
#closed;
|
|
78
|
+
constructor(db, closed = () => {
|
|
79
|
+
}) {
|
|
80
|
+
super(db, new Statements(db));
|
|
81
|
+
this.#closed = closed;
|
|
82
|
+
}
|
|
83
|
+
exec(sql) {
|
|
84
|
+
return this.#queue.run(() => super.exec(sql));
|
|
85
|
+
}
|
|
86
|
+
run(sql, ...params) {
|
|
87
|
+
return this.#queue.run(() => super.run(sql, ...params));
|
|
88
|
+
}
|
|
89
|
+
get(sql, ...params) {
|
|
90
|
+
return this.#queue.run(() => super.get(sql, ...params));
|
|
91
|
+
}
|
|
92
|
+
all(sql, ...params) {
|
|
93
|
+
return this.#queue.run(() => super.all(sql, ...params));
|
|
94
|
+
}
|
|
95
|
+
transaction(callback) {
|
|
96
|
+
return this.#queue.run(async () => {
|
|
97
|
+
const transaction = new WasmSqliteTransaction(this.db, this.statements);
|
|
98
|
+
this.db.exec("BEGIN IMMEDIATE");
|
|
99
|
+
try {
|
|
100
|
+
const result = await callback(transaction);
|
|
101
|
+
this.db.exec("COMMIT");
|
|
102
|
+
return result;
|
|
103
|
+
} catch (error) {
|
|
104
|
+
this.#rollback(error);
|
|
105
|
+
throw error;
|
|
106
|
+
} finally {
|
|
107
|
+
transaction.active = false;
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
close() {
|
|
112
|
+
return this.#queue.run(() => {
|
|
113
|
+
try {
|
|
114
|
+
this.statements.finalize();
|
|
115
|
+
this.db.close();
|
|
116
|
+
} finally {
|
|
117
|
+
this.#closed();
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
#rollback(cause) {
|
|
122
|
+
try {
|
|
123
|
+
this.db.exec("ROLLBACK");
|
|
124
|
+
} catch (error) {
|
|
125
|
+
throw new AggregateError([cause, error], "SQLite rollback failed");
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
const AGENT_DIRECTORY = "/.slicc/agent";
|
|
130
|
+
async function openMemorySqliteStorage() {
|
|
131
|
+
const sqlite3 = await init();
|
|
132
|
+
return SqliteStorage.open(new WasmSqliteDatabase(new sqlite3.oo1.DB(":memory:", "c")));
|
|
133
|
+
}
|
|
134
|
+
async function openOpfsSqliteStorage(options = {}) {
|
|
135
|
+
const directory = options.directory ?? AGENT_DIRECTORY;
|
|
136
|
+
const release = await holdLock(
|
|
137
|
+
options.locks ?? navigator.locks,
|
|
138
|
+
`slicc-agent-sqlite:${directory}`
|
|
139
|
+
);
|
|
140
|
+
try {
|
|
141
|
+
await settlePool(directory, options);
|
|
142
|
+
const sqlite3 = await (options.load ?? init)();
|
|
143
|
+
const pool = await sqlite3.installOpfsSAHPoolVfs({ name: vfsName(directory), directory });
|
|
144
|
+
const db = new pool.OpfsSAHPoolDb(options.file ?? "/agent.sqlite");
|
|
145
|
+
db.exec("PRAGMA journal_mode = TRUNCATE");
|
|
146
|
+
return await SqliteStorage.open(new WasmSqliteDatabase(db, release));
|
|
147
|
+
} catch (error) {
|
|
148
|
+
release();
|
|
149
|
+
throw error;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
export {
|
|
153
|
+
AGENT_DIRECTORY,
|
|
154
|
+
WasmSqliteDatabase,
|
|
155
|
+
openMemorySqliteStorage,
|
|
156
|
+
openOpfsSqliteStorage
|
|
157
|
+
};
|
|
158
|
+
//# sourceMappingURL=sqlite.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/sqlite.ts"],
|
|
4
|
+
"sourcesContent": ["import type { Storage } from '@earendil-works/pi-durable';\nimport {\n type SqliteDatabase,\n type SqliteExecutor,\n SqliteStorage,\n type SqliteValue,\n} from '@earendil-works/pi-durable/storage/sqlite';\nimport init, { type Database, type PreparedStatement } from '@sqlite.org/sqlite-wasm';\nimport { holdLock, type PoolAccess, settlePool, vfsName } from './opfs-pool.ts';\n\ntype Row = Record<string, unknown>;\n\nclass SerialQueue {\n #tail: Promise<unknown> = Promise.resolve();\n\n run<T>(operation: () => T | Promise<T>): Promise<T> {\n const result = this.#tail.then(operation);\n this.#tail = result.catch(() => {});\n return result;\n }\n}\n\nclass Statements {\n readonly #db: Database;\n readonly #cache = new Map<string, PreparedStatement>();\n\n constructor(db: Database) {\n this.#db = db;\n }\n\n bound(sql: string, params: readonly SqliteValue[]): PreparedStatement {\n let statement = this.#cache.get(sql);\n if (!statement) {\n statement = this.#db.prepare(sql);\n this.#cache.set(sql, statement);\n }\n statement.reset(true);\n if (params.length > 0) statement.bind([...params]);\n return statement;\n }\n\n finalize(): void {\n for (const statement of this.#cache.values()) statement.finalize();\n this.#cache.clear();\n }\n}\n\nclass WasmSqliteExecutor implements SqliteExecutor {\n protected readonly db: Database;\n protected readonly statements: Statements;\n\n constructor(db: Database, statements: Statements) {\n this.db = db;\n this.statements = statements;\n }\n\n async exec(sql: string): Promise<void> {\n this.check();\n this.db.exec(sql);\n }\n\n async run(sql: string, ...params: SqliteValue[]): Promise<void> {\n this.check();\n this.statements.bound(sql, params).stepReset();\n }\n\n async get<T extends object>(sql: string, ...params: SqliteValue[]): Promise<T | undefined> {\n this.check();\n const statement = this.statements.bound(sql, params);\n const row = statement.step() ? (statement.get({}) as Row as T) : undefined;\n statement.reset();\n return row;\n }\n\n async all<T extends object>(sql: string, ...params: SqliteValue[]): Promise<T[]> {\n this.check();\n const statement = this.statements.bound(sql, params);\n const rows: T[] = [];\n while (statement.step()) rows.push(statement.get({}) as Row as T);\n statement.reset();\n return rows;\n }\n\n protected check(): void {}\n}\n\nclass WasmSqliteTransaction extends WasmSqliteExecutor {\n active = true;\n\n protected override check(): void {\n if (!this.active) throw new Error('SQLite transaction handle is no longer active');\n }\n}\n\nexport class WasmSqliteDatabase extends WasmSqliteExecutor implements SqliteDatabase {\n readonly #queue = new SerialQueue();\n readonly #closed: () => void;\n\n constructor(db: Database, closed: () => void = () => {}) {\n super(db, new Statements(db));\n this.#closed = closed;\n }\n\n override exec(sql: string): Promise<void> {\n return this.#queue.run(() => super.exec(sql));\n }\n\n override run(sql: string, ...params: SqliteValue[]): Promise<void> {\n return this.#queue.run(() => super.run(sql, ...params));\n }\n\n override get<T extends object>(sql: string, ...params: SqliteValue[]): Promise<T | undefined> {\n return this.#queue.run(() => super.get<T>(sql, ...params));\n }\n\n override all<T extends object>(sql: string, ...params: SqliteValue[]): Promise<T[]> {\n return this.#queue.run(() => super.all<T>(sql, ...params));\n }\n\n transaction<T>(callback: (transaction: SqliteExecutor) => Promise<T>): Promise<T> {\n return this.#queue.run(async () => {\n const transaction = new WasmSqliteTransaction(this.db, this.statements);\n this.db.exec('BEGIN IMMEDIATE');\n try {\n const result = await callback(transaction);\n this.db.exec('COMMIT');\n return result;\n } catch (error) {\n this.#rollback(error);\n throw error;\n } finally {\n transaction.active = false;\n }\n });\n }\n\n close(): Promise<void> {\n return this.#queue.run(() => {\n try {\n this.statements.finalize();\n this.db.close();\n } finally {\n this.#closed();\n }\n });\n }\n\n #rollback(cause: unknown): void {\n try {\n this.db.exec('ROLLBACK');\n } catch (error) {\n throw new AggregateError([cause, error], 'SQLite rollback failed');\n }\n }\n}\n\nexport interface OpfsSqliteOptions extends PoolAccess {\n directory?: string;\n file?: string;\n load?: typeof init;\n}\n\nexport const AGENT_DIRECTORY = '/.slicc/agent';\n\nexport async function openMemorySqliteStorage(): Promise<Storage> {\n const sqlite3 = await init();\n return SqliteStorage.open(new WasmSqliteDatabase(new sqlite3.oo1.DB(':memory:', 'c')));\n}\n\nexport async function openOpfsSqliteStorage(options: OpfsSqliteOptions = {}): Promise<Storage> {\n const directory = options.directory ?? AGENT_DIRECTORY;\n const release = await holdLock(\n options.locks ?? navigator.locks,\n `slicc-agent-sqlite:${directory}`\n );\n try {\n await settlePool(directory, options);\n const sqlite3 = await (options.load ?? init)();\n const pool = await sqlite3.installOpfsSAHPoolVfs({ name: vfsName(directory), directory });\n const db = new pool.OpfsSAHPoolDb(options.file ?? '/agent.sqlite');\n db.exec('PRAGMA journal_mode = TRUNCATE');\n return await SqliteStorage.open(new WasmSqliteDatabase(db, release));\n } catch (error) {\n release();\n throw error;\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
package/dist/types/index.d.ts
CHANGED
|
@@ -1,2 +1,3 @@
|
|
|
1
1
|
export { type Agent, type AgentOptions, answerText, openAgent } from './agent.ts';
|
|
2
2
|
export { type AgentClient, type AgentReply, type AgentRequest, connectAgent, type MessageEndpoint, serveAgent, } from './port.ts';
|
|
3
|
+
export { AGENT_DIRECTORY, type OpfsSqliteOptions, openMemorySqliteStorage, openOpfsSqliteStorage, WasmSqliteDatabase, } from './sqlite.ts';
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export interface PoolAccess {
|
|
2
|
+
root?: () => Promise<FileSystemDirectoryHandle>;
|
|
3
|
+
locks?: LockManager;
|
|
4
|
+
attempts?: number;
|
|
5
|
+
delayMs?: number;
|
|
6
|
+
}
|
|
7
|
+
export declare function vfsName(directory: string): string;
|
|
8
|
+
export declare function holdLock(locks: LockManager, name: string): Promise<() => void>;
|
|
9
|
+
export declare function settlePool(directory: string, access?: PoolAccess): Promise<void>;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { Storage } from '@earendil-works/pi-durable';
|
|
2
|
+
import { type SqliteDatabase, type SqliteExecutor, type SqliteValue } from '@earendil-works/pi-durable/storage/sqlite';
|
|
3
|
+
import init, { type Database, type PreparedStatement } from '@sqlite.org/sqlite-wasm';
|
|
4
|
+
import { type PoolAccess } from './opfs-pool.ts';
|
|
5
|
+
declare class Statements {
|
|
6
|
+
#private;
|
|
7
|
+
constructor(db: Database);
|
|
8
|
+
bound(sql: string, params: readonly SqliteValue[]): PreparedStatement;
|
|
9
|
+
finalize(): void;
|
|
10
|
+
}
|
|
11
|
+
declare class WasmSqliteExecutor implements SqliteExecutor {
|
|
12
|
+
protected readonly db: Database;
|
|
13
|
+
protected readonly statements: Statements;
|
|
14
|
+
constructor(db: Database, statements: Statements);
|
|
15
|
+
exec(sql: string): Promise<void>;
|
|
16
|
+
run(sql: string, ...params: SqliteValue[]): Promise<void>;
|
|
17
|
+
get<T extends object>(sql: string, ...params: SqliteValue[]): Promise<T | undefined>;
|
|
18
|
+
all<T extends object>(sql: string, ...params: SqliteValue[]): Promise<T[]>;
|
|
19
|
+
protected check(): void;
|
|
20
|
+
}
|
|
21
|
+
export declare class WasmSqliteDatabase extends WasmSqliteExecutor implements SqliteDatabase {
|
|
22
|
+
#private;
|
|
23
|
+
constructor(db: Database, closed?: () => void);
|
|
24
|
+
exec(sql: string): Promise<void>;
|
|
25
|
+
run(sql: string, ...params: SqliteValue[]): Promise<void>;
|
|
26
|
+
get<T extends object>(sql: string, ...params: SqliteValue[]): Promise<T | undefined>;
|
|
27
|
+
all<T extends object>(sql: string, ...params: SqliteValue[]): Promise<T[]>;
|
|
28
|
+
transaction<T>(callback: (transaction: SqliteExecutor) => Promise<T>): Promise<T>;
|
|
29
|
+
close(): Promise<void>;
|
|
30
|
+
}
|
|
31
|
+
export interface OpfsSqliteOptions extends PoolAccess {
|
|
32
|
+
directory?: string;
|
|
33
|
+
file?: string;
|
|
34
|
+
load?: typeof init;
|
|
35
|
+
}
|
|
36
|
+
export declare const AGENT_DIRECTORY = "/.slicc/agent";
|
|
37
|
+
export declare function openMemorySqliteStorage(): Promise<Storage>;
|
|
38
|
+
export declare function openOpfsSqliteStorage(options?: OpfsSqliteOptions): Promise<Storage>;
|
|
39
|
+
export {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ai-ecoverse/slicc-agent",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "SLICC's agent core on pi durable: an agent worker, session storage, remoting and pi extensions",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -59,6 +59,7 @@
|
|
|
59
59
|
"dependencies": {
|
|
60
60
|
"@earendil-works/chord": "1.0.4",
|
|
61
61
|
"@earendil-works/pi-ai": "1.0.4",
|
|
62
|
-
"@earendil-works/pi-durable": "1.0.4"
|
|
62
|
+
"@earendil-works/pi-durable": "1.0.4",
|
|
63
|
+
"@sqlite.org/sqlite-wasm": "3.53.4-build2"
|
|
63
64
|
}
|
|
64
65
|
}
|