agentic-canvas 0.7.0 → 0.9.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 +50 -14
- package/dist/{chunk-3QW4UF2R.js → chunk-33ANMQEY.js} +1 -1
- package/dist/{chunk-EH7WCMK6.js → chunk-JQCUVXXD.js} +253 -20
- package/dist/{chunk-GPSOLW2Y.js → chunk-M74ENUWR.js} +31 -7
- package/dist/chunk-NRLF6MGR.js +130 -0
- package/dist/cli.js +3 -3
- package/dist/client.d.ts +1 -1
- package/dist/embed.d.ts +1 -1
- package/dist/index.d.ts +4 -4
- package/dist/index.js +3 -3
- package/dist/live-worker.d.ts +2 -2
- package/dist/live-worker.js +2 -2
- package/dist/live.d.ts +2 -2
- package/dist/{read-CeWoSAAy.d.ts → read--Jd3kE13.d.ts} +3 -9
- package/dist/{service-D9WzMLH1.d.ts → service-OiTtNadu.d.ts} +6 -1
- package/dist/store-file.d.ts +1 -12
- package/dist/store-file.js +1 -1
- package/dist/ui.css +24 -24
- package/dist/worker.d.ts +90 -4
- package/dist/worker.js +4 -2
- package/package.json +8 -4
- package/dist/chunk-UKST5A57.js +0 -228
package/dist/chunk-UKST5A57.js
DELETED
|
@@ -1,228 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
FileStoreError
|
|
3
|
-
} from "./chunk-NWQED42L.js";
|
|
4
|
-
|
|
5
|
-
// src/store-file.ts
|
|
6
|
-
import { randomUUID } from "crypto";
|
|
7
|
-
import { mkdirSync } from "fs";
|
|
8
|
-
import { link, open, readFile, rename, unlink } from "fs/promises";
|
|
9
|
-
import { join } from "path";
|
|
10
|
-
import { assertCollection, assertId, createMemoryStore, parseRegion, StoreConflictError } from "agentic-identity";
|
|
11
|
-
var MAX_TRANSACTION_RUNS = 50;
|
|
12
|
-
var clone = (value) => JSON.parse(JSON.stringify(value));
|
|
13
|
-
var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
14
|
-
var codeOf = (error) => error?.code;
|
|
15
|
-
function processIsAlive(pid) {
|
|
16
|
-
try {
|
|
17
|
-
process.kill(pid, 0);
|
|
18
|
-
return true;
|
|
19
|
-
} catch (error) {
|
|
20
|
-
return codeOf(error) === "EPERM";
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
function lockIsStale(text, staleAfterMs) {
|
|
24
|
-
let held;
|
|
25
|
-
try {
|
|
26
|
-
held = JSON.parse(text);
|
|
27
|
-
} catch {
|
|
28
|
-
return false;
|
|
29
|
-
}
|
|
30
|
-
if (typeof held !== "object" || held === null) return false;
|
|
31
|
-
const { pid, at } = held;
|
|
32
|
-
if (typeof pid !== "number" || !Number.isInteger(pid) || pid < 1 || typeof at !== "number" || !Number.isFinite(at)) return false;
|
|
33
|
-
return !processIsAlive(pid) || Date.now() - at > staleAfterMs;
|
|
34
|
-
}
|
|
35
|
-
function createFileStore(path, options = {}) {
|
|
36
|
-
const region = parseRegion(options.region ?? "unknown");
|
|
37
|
-
const lockPath = `${path}.lock`;
|
|
38
|
-
const timeoutMs = options.lockTimeoutMs ?? 1e4;
|
|
39
|
-
const staleAfterMs = options.staleAfterMs ?? 3e4;
|
|
40
|
-
for (const ms of [timeoutMs, staleAfterMs]) if (typeof ms !== "number" || !Number.isFinite(ms) || ms < 0) throw new TypeError("lockTimeoutMs and staleAfterMs must be finite numbers of 0 or more");
|
|
41
|
-
let queue = Promise.resolve();
|
|
42
|
-
async function load() {
|
|
43
|
-
let text;
|
|
44
|
-
try {
|
|
45
|
-
text = await readFile(path, "utf8");
|
|
46
|
-
} catch (error) {
|
|
47
|
-
if (codeOf(error) === "ENOENT") return /* @__PURE__ */ new Map();
|
|
48
|
-
throw error;
|
|
49
|
-
}
|
|
50
|
-
try {
|
|
51
|
-
const data = JSON.parse(text);
|
|
52
|
-
if (typeof data !== "object" || data === null || Array.isArray(data)) throw new Error();
|
|
53
|
-
return new Map(
|
|
54
|
-
Object.entries(data).map(([name, docs]) => {
|
|
55
|
-
if (typeof docs !== "object" || docs === null || Array.isArray(docs)) throw new Error();
|
|
56
|
-
for (const doc of Object.values(docs)) if (typeof doc !== "object" || doc === null || Array.isArray(doc)) throw new Error();
|
|
57
|
-
return [name, new Map(Object.entries(docs))];
|
|
58
|
-
})
|
|
59
|
-
);
|
|
60
|
-
} catch {
|
|
61
|
-
throw new FileStoreError("DAMAGED", `the database file is not valid JSON. Repair or remove it`);
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
async function save(snapshot) {
|
|
65
|
-
const text = JSON.stringify(Object.fromEntries([...snapshot].map(([name, docs]) => [name, Object.fromEntries(docs)])));
|
|
66
|
-
const temp = `${path}.${process.pid}.${randomUUID()}.tmp`;
|
|
67
|
-
try {
|
|
68
|
-
const handle = await open(temp, "w");
|
|
69
|
-
try {
|
|
70
|
-
await handle.writeFile(text);
|
|
71
|
-
await handle.sync();
|
|
72
|
-
} finally {
|
|
73
|
-
await handle.close();
|
|
74
|
-
}
|
|
75
|
-
await rename(temp, path);
|
|
76
|
-
} catch (error) {
|
|
77
|
-
await unlink(temp).catch(() => void 0);
|
|
78
|
-
throw error;
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
async function takeOverIfStale() {
|
|
82
|
-
let seen;
|
|
83
|
-
try {
|
|
84
|
-
seen = await readFile(lockPath, "utf8");
|
|
85
|
-
} catch {
|
|
86
|
-
return;
|
|
87
|
-
}
|
|
88
|
-
if (!lockIsStale(seen, staleAfterMs)) return;
|
|
89
|
-
const aside = `${lockPath}.stale.${randomUUID()}`;
|
|
90
|
-
try {
|
|
91
|
-
await rename(lockPath, aside);
|
|
92
|
-
} catch {
|
|
93
|
-
return;
|
|
94
|
-
}
|
|
95
|
-
if (await readFile(aside, "utf8").catch(() => "") !== seen) await link(aside, lockPath).catch(() => void 0);
|
|
96
|
-
await unlink(aside).catch(() => void 0);
|
|
97
|
-
}
|
|
98
|
-
async function acquire() {
|
|
99
|
-
const token = randomUUID();
|
|
100
|
-
const deadline = Date.now() + timeoutMs;
|
|
101
|
-
for (; ; ) {
|
|
102
|
-
try {
|
|
103
|
-
const handle = await open(lockPath, "wx");
|
|
104
|
-
try {
|
|
105
|
-
await handle.writeFile(JSON.stringify({ pid: process.pid, at: Date.now(), token }));
|
|
106
|
-
} finally {
|
|
107
|
-
await handle.close();
|
|
108
|
-
}
|
|
109
|
-
return token;
|
|
110
|
-
} catch (error) {
|
|
111
|
-
if (codeOf(error) !== "EEXIST") throw error;
|
|
112
|
-
}
|
|
113
|
-
await takeOverIfStale();
|
|
114
|
-
if (Date.now() > deadline) throw new FileStoreError("LOCKED", `the database file is locked. If no agentic-canvas process runs, remove the .lock file next to it`);
|
|
115
|
-
await sleep(5 + Math.random() * 15);
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
async function release(token) {
|
|
119
|
-
const text = await readFile(lockPath, "utf8").catch(() => "");
|
|
120
|
-
if (text.includes(`"token":"${token}"`)) await unlink(lockPath).catch(() => void 0);
|
|
121
|
-
}
|
|
122
|
-
async function memoryOf(snapshot, only) {
|
|
123
|
-
const memory = createMemoryStore({ region });
|
|
124
|
-
for (const [name, docs] of snapshot) if (only === void 0 || only === name) for (const [id, doc] of docs) await memory.put(name, id, doc);
|
|
125
|
-
return memory;
|
|
126
|
-
}
|
|
127
|
-
const recording = (inner, log) => ({
|
|
128
|
-
get: (collection, id) => inner.get(collection, id),
|
|
129
|
-
query: (collection, query) => inner.query(collection, query),
|
|
130
|
-
put: async (collection, id, doc) => {
|
|
131
|
-
await inner.put(collection, id, doc);
|
|
132
|
-
log.push([collection, id, clone(doc)]);
|
|
133
|
-
},
|
|
134
|
-
delete: async (collection, id) => {
|
|
135
|
-
await inner.delete(collection, id);
|
|
136
|
-
log.push([collection, id, null]);
|
|
137
|
-
}
|
|
138
|
-
});
|
|
139
|
-
const locked = (body) => {
|
|
140
|
-
const run = queue.then(async () => {
|
|
141
|
-
const token = await acquire();
|
|
142
|
-
try {
|
|
143
|
-
return await body();
|
|
144
|
-
} finally {
|
|
145
|
-
await release(token);
|
|
146
|
-
}
|
|
147
|
-
});
|
|
148
|
-
queue = run.catch(() => void 0);
|
|
149
|
-
return run;
|
|
150
|
-
};
|
|
151
|
-
async function commit(snapshot, log) {
|
|
152
|
-
for (const [name, id, doc] of log) {
|
|
153
|
-
if (doc === null) snapshot.get(name)?.delete(id);
|
|
154
|
-
else snapshot.set(name, (snapshot.get(name) ?? /* @__PURE__ */ new Map()).set(id, doc));
|
|
155
|
-
}
|
|
156
|
-
if (log.length > 0) await save(snapshot);
|
|
157
|
-
}
|
|
158
|
-
const writeOne = (write) => locked(async () => {
|
|
159
|
-
const log = [];
|
|
160
|
-
await write(recording(createMemoryStore({ region }), log));
|
|
161
|
-
await commit(await load(), log);
|
|
162
|
-
});
|
|
163
|
-
const watching = (inner, seen) => ({
|
|
164
|
-
...inner,
|
|
165
|
-
async get(collection, id) {
|
|
166
|
-
const doc = await inner.get(collection, id);
|
|
167
|
-
if (!seen.has(`${collection}/${id}`)) seen.set(`${collection}/${id}`, JSON.stringify(doc));
|
|
168
|
-
return doc;
|
|
169
|
-
},
|
|
170
|
-
async query(collection, query) {
|
|
171
|
-
const page = await inner.query(collection, query);
|
|
172
|
-
for (const item of page.items) if (!seen.has(`${collection}/${item.id}`)) seen.set(`${collection}/${item.id}`, JSON.stringify(item.data));
|
|
173
|
-
return page;
|
|
174
|
-
}
|
|
175
|
-
});
|
|
176
|
-
const unchanged = (snapshot, seen) => [...seen].every(([key, text]) => {
|
|
177
|
-
const slash = key.indexOf("/");
|
|
178
|
-
return JSON.stringify(snapshot.get(key.slice(0, slash))?.get(key.slice(slash + 1)) ?? null) === text;
|
|
179
|
-
});
|
|
180
|
-
return {
|
|
181
|
-
region,
|
|
182
|
-
async get(collection, id) {
|
|
183
|
-
assertCollection(collection);
|
|
184
|
-
assertId(id);
|
|
185
|
-
const doc = (await load()).get(collection)?.get(id);
|
|
186
|
-
return doc ? clone(doc) : null;
|
|
187
|
-
},
|
|
188
|
-
async query(collection, query) {
|
|
189
|
-
assertCollection(collection);
|
|
190
|
-
return (await memoryOf(await load(), collection)).query(collection, query);
|
|
191
|
-
},
|
|
192
|
-
put: (collection, id, doc) => writeOne((ops) => ops.put(collection, id, doc)),
|
|
193
|
-
delete: (collection, id) => writeOne((ops) => ops.delete(collection, id)),
|
|
194
|
-
// Optimistic, as the port requires: run on a copy of the file with no lock, then take the lock and commit
|
|
195
|
-
// only if every doc the run read is unchanged. Else run again. The memory adapter keeps the rule that
|
|
196
|
-
// every read comes before the first write.
|
|
197
|
-
async transaction(fn) {
|
|
198
|
-
for (let run = 0; run < MAX_TRANSACTION_RUNS; run++) {
|
|
199
|
-
const log = [];
|
|
200
|
-
const seen = /* @__PURE__ */ new Map();
|
|
201
|
-
const result = await (await memoryOf(await load())).transaction(async (tx) => {
|
|
202
|
-
log.length = 0;
|
|
203
|
-
seen.clear();
|
|
204
|
-
return fn(watching(recording(tx, log), seen));
|
|
205
|
-
});
|
|
206
|
-
const done = log.length === 0 ? unchanged(await load(), seen) : await locked(async () => {
|
|
207
|
-
const current = await load();
|
|
208
|
-
if (!unchanged(current, seen)) return false;
|
|
209
|
-
await commit(current, log);
|
|
210
|
-
return true;
|
|
211
|
-
});
|
|
212
|
-
if (done) return result;
|
|
213
|
-
}
|
|
214
|
-
throw new StoreConflictError();
|
|
215
|
-
}
|
|
216
|
-
};
|
|
217
|
-
}
|
|
218
|
-
function openLocalStore(dir) {
|
|
219
|
-
mkdirSync(dir, { recursive: true });
|
|
220
|
-
return createFileStore(join(dir, STORE_FILE));
|
|
221
|
-
}
|
|
222
|
-
var STORE_FILE = "canvas.json";
|
|
223
|
-
|
|
224
|
-
export {
|
|
225
|
-
createFileStore,
|
|
226
|
-
openLocalStore,
|
|
227
|
-
STORE_FILE
|
|
228
|
-
};
|