@gopherine/obsidian-mcp 0.1.1
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 +60 -0
- package/LICENSE +661 -0
- package/README.md +235 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +805 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/brainstorm.d.ts +9 -0
- package/dist/commands/brainstorm.js +37 -0
- package/dist/commands/brainstorm.js.map +1 -0
- package/dist/commands/brainstorm.test.d.ts +1 -0
- package/dist/commands/brainstorm.test.js +110 -0
- package/dist/commands/brainstorm.test.js.map +1 -0
- package/dist/commands/context.d.ts +18 -0
- package/dist/commands/context.js +88 -0
- package/dist/commands/context.js.map +1 -0
- package/dist/commands/context.test.d.ts +1 -0
- package/dist/commands/context.test.js +230 -0
- package/dist/commands/context.test.js.map +1 -0
- package/dist/commands/decide.d.ts +12 -0
- package/dist/commands/decide.js +40 -0
- package/dist/commands/decide.js.map +1 -0
- package/dist/commands/decide.test.d.ts +1 -0
- package/dist/commands/decide.test.js +109 -0
- package/dist/commands/decide.test.js.map +1 -0
- package/dist/commands/graph.d.ts +15 -0
- package/dist/commands/graph.js +89 -0
- package/dist/commands/graph.js.map +1 -0
- package/dist/commands/graph.test.d.ts +1 -0
- package/dist/commands/graph.test.js +215 -0
- package/dist/commands/graph.test.js.map +1 -0
- package/dist/commands/init.d.ts +11 -0
- package/dist/commands/init.js +256 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/learn.d.ts +25 -0
- package/dist/commands/learn.js +92 -0
- package/dist/commands/learn.js.map +1 -0
- package/dist/commands/learn.test.d.ts +1 -0
- package/dist/commands/learn.test.js +241 -0
- package/dist/commands/learn.test.js.map +1 -0
- package/dist/commands/prune.d.ts +58 -0
- package/dist/commands/prune.js +246 -0
- package/dist/commands/prune.js.map +1 -0
- package/dist/commands/read.d.ts +8 -0
- package/dist/commands/read.js +7 -0
- package/dist/commands/read.js.map +1 -0
- package/dist/commands/read.test.d.ts +1 -0
- package/dist/commands/read.test.js +48 -0
- package/dist/commands/read.test.js.map +1 -0
- package/dist/commands/resume.d.ts +20 -0
- package/dist/commands/resume.js +138 -0
- package/dist/commands/resume.js.map +1 -0
- package/dist/commands/resume.test.d.ts +1 -0
- package/dist/commands/resume.test.js +243 -0
- package/dist/commands/resume.test.js.map +1 -0
- package/dist/commands/search.d.ts +8 -0
- package/dist/commands/search.js +25 -0
- package/dist/commands/search.js.map +1 -0
- package/dist/commands/search.test.d.ts +1 -0
- package/dist/commands/search.test.js +61 -0
- package/dist/commands/search.test.js.map +1 -0
- package/dist/commands/session.d.ts +22 -0
- package/dist/commands/session.js +79 -0
- package/dist/commands/session.js.map +1 -0
- package/dist/commands/session.test.d.ts +1 -0
- package/dist/commands/session.test.js +185 -0
- package/dist/commands/session.test.js.map +1 -0
- package/dist/commands/skill/index.d.ts +30 -0
- package/dist/commands/skill/index.js +61 -0
- package/dist/commands/skill/index.js.map +1 -0
- package/dist/commands/skill/index.test.d.ts +1 -0
- package/dist/commands/skill/index.test.js +137 -0
- package/dist/commands/skill/index.test.js.map +1 -0
- package/dist/commands/skill/install.d.ts +14 -0
- package/dist/commands/skill/install.js +107 -0
- package/dist/commands/skill/install.js.map +1 -0
- package/dist/commands/skill/install.test.d.ts +1 -0
- package/dist/commands/skill/install.test.js +225 -0
- package/dist/commands/skill/install.test.js.map +1 -0
- package/dist/commands/skill/list.d.ts +6 -0
- package/dist/commands/skill/list.js +5 -0
- package/dist/commands/skill/list.js.map +1 -0
- package/dist/commands/skill/list.test.d.ts +1 -0
- package/dist/commands/skill/list.test.js +101 -0
- package/dist/commands/skill/list.test.js.map +1 -0
- package/dist/commands/skill/schema.d.ts +27 -0
- package/dist/commands/skill/schema.js +55 -0
- package/dist/commands/skill/schema.js.map +1 -0
- package/dist/commands/skill/schema.test.d.ts +1 -0
- package/dist/commands/skill/schema.test.js +142 -0
- package/dist/commands/skill/schema.test.js.map +1 -0
- package/dist/commands/skill/validate.d.ts +10 -0
- package/dist/commands/skill/validate.js +39 -0
- package/dist/commands/skill/validate.js.map +1 -0
- package/dist/commands/skill/validate.test.d.ts +1 -0
- package/dist/commands/skill/validate.test.js +171 -0
- package/dist/commands/skill/validate.test.js.map +1 -0
- package/dist/commands/task.d.ts +34 -0
- package/dist/commands/task.js +160 -0
- package/dist/commands/task.js.map +1 -0
- package/dist/commands/task.test.d.ts +1 -0
- package/dist/commands/task.test.js +395 -0
- package/dist/commands/task.test.js.map +1 -0
- package/dist/commands/todo.d.ts +15 -0
- package/dist/commands/todo.js +99 -0
- package/dist/commands/todo.js.map +1 -0
- package/dist/commands/todo.test.d.ts +1 -0
- package/dist/commands/todo.test.js +324 -0
- package/dist/commands/todo.test.js.map +1 -0
- package/dist/commands/write.d.ts +12 -0
- package/dist/commands/write.js +40 -0
- package/dist/commands/write.js.map +1 -0
- package/dist/commands/write.test.d.ts +1 -0
- package/dist/commands/write.test.js +79 -0
- package/dist/commands/write.test.js.map +1 -0
- package/dist/config.d.ts +15 -0
- package/dist/config.js +48 -0
- package/dist/config.js.map +1 -0
- package/dist/config.test.d.ts +1 -0
- package/dist/config.test.js +147 -0
- package/dist/config.test.js.map +1 -0
- package/dist/core/executor.d.ts +11 -0
- package/dist/core/executor.js +42 -0
- package/dist/core/executor.js.map +1 -0
- package/dist/core/executor.test.d.ts +1 -0
- package/dist/core/executor.test.js +206 -0
- package/dist/core/executor.test.js.map +1 -0
- package/dist/core/middleware/error-handler.d.ts +2 -0
- package/dist/core/middleware/error-handler.js +29 -0
- package/dist/core/middleware/error-handler.js.map +1 -0
- package/dist/core/middleware/index.d.ts +2 -0
- package/dist/core/middleware/index.js +3 -0
- package/dist/core/middleware/index.js.map +1 -0
- package/dist/core/middleware/logging.d.ts +2 -0
- package/dist/core/middleware/logging.js +18 -0
- package/dist/core/middleware/logging.js.map +1 -0
- package/dist/core/registry.d.ts +12 -0
- package/dist/core/registry.js +407 -0
- package/dist/core/registry.js.map +1 -0
- package/dist/core/registry.test.d.ts +1 -0
- package/dist/core/registry.test.js +162 -0
- package/dist/core/registry.test.js.map +1 -0
- package/dist/core/types.d.ts +38 -0
- package/dist/core/types.js +2 -0
- package/dist/core/types.js.map +1 -0
- package/dist/integration/coordination.test.d.ts +1 -0
- package/dist/integration/coordination.test.js +286 -0
- package/dist/integration/coordination.test.js.map +1 -0
- package/dist/integration/project-artifacts.test.d.ts +1 -0
- package/dist/integration/project-artifacts.test.js +275 -0
- package/dist/integration/project-artifacts.test.js.map +1 -0
- package/dist/integration/registry-graph.test.d.ts +1 -0
- package/dist/integration/registry-graph.test.js +157 -0
- package/dist/integration/registry-graph.test.js.map +1 -0
- package/dist/integration/vault-lifecycle.test.d.ts +1 -0
- package/dist/integration/vault-lifecycle.test.js +183 -0
- package/dist/integration/vault-lifecycle.test.js.map +1 -0
- package/dist/lib/auto-number.d.ts +10 -0
- package/dist/lib/auto-number.js +33 -0
- package/dist/lib/auto-number.js.map +1 -0
- package/dist/lib/auto-number.test.d.ts +1 -0
- package/dist/lib/auto-number.test.js +88 -0
- package/dist/lib/auto-number.test.js.map +1 -0
- package/dist/lib/escape-regex.d.ts +4 -0
- package/dist/lib/escape-regex.js +7 -0
- package/dist/lib/escape-regex.js.map +1 -0
- package/dist/lib/escape-regex.test.d.ts +1 -0
- package/dist/lib/escape-regex.test.js +27 -0
- package/dist/lib/escape-regex.test.js.map +1 -0
- package/dist/lib/frontmatter.d.ts +34 -0
- package/dist/lib/frontmatter.js +74 -0
- package/dist/lib/frontmatter.js.map +1 -0
- package/dist/lib/frontmatter.test.d.ts +1 -0
- package/dist/lib/frontmatter.test.js +192 -0
- package/dist/lib/frontmatter.test.js.map +1 -0
- package/dist/lib/project-detector.d.ts +12 -0
- package/dist/lib/project-detector.js +123 -0
- package/dist/lib/project-detector.js.map +1 -0
- package/dist/lib/project-detector.test.d.ts +1 -0
- package/dist/lib/project-detector.test.js +117 -0
- package/dist/lib/project-detector.test.js.map +1 -0
- package/dist/lib/safe-external-path.d.ts +10 -0
- package/dist/lib/safe-external-path.js +46 -0
- package/dist/lib/safe-external-path.js.map +1 -0
- package/dist/lib/safe-external-path.test.d.ts +1 -0
- package/dist/lib/safe-external-path.test.js +99 -0
- package/dist/lib/safe-external-path.test.js.map +1 -0
- package/dist/lib/search-engine.d.ts +19 -0
- package/dist/lib/search-engine.js +157 -0
- package/dist/lib/search-engine.js.map +1 -0
- package/dist/lib/search-engine.test.d.ts +1 -0
- package/dist/lib/search-engine.test.js +120 -0
- package/dist/lib/search-engine.test.js.map +1 -0
- package/dist/lib/session-registry.d.ts +59 -0
- package/dist/lib/session-registry.js +230 -0
- package/dist/lib/session-registry.js.map +1 -0
- package/dist/lib/session-registry.test.d.ts +1 -0
- package/dist/lib/session-registry.test.js +199 -0
- package/dist/lib/session-registry.test.js.map +1 -0
- package/dist/lib/skill-registry.d.ts +13 -0
- package/dist/lib/skill-registry.js +76 -0
- package/dist/lib/skill-registry.js.map +1 -0
- package/dist/lib/token-estimator.d.ts +19 -0
- package/dist/lib/token-estimator.js +58 -0
- package/dist/lib/token-estimator.js.map +1 -0
- package/dist/lib/token-estimator.test.d.ts +1 -0
- package/dist/lib/token-estimator.test.js +65 -0
- package/dist/lib/token-estimator.test.js.map +1 -0
- package/dist/lib/vault-fs.d.ts +39 -0
- package/dist/lib/vault-fs.js +183 -0
- package/dist/lib/vault-fs.js.map +1 -0
- package/dist/lib/vault-fs.test.d.ts +1 -0
- package/dist/lib/vault-fs.test.js +210 -0
- package/dist/lib/vault-fs.test.js.map +1 -0
- package/dist/mcp-server.d.ts +2 -0
- package/dist/mcp-server.js +385 -0
- package/dist/mcp-server.js.map +1 -0
- package/dist/test-helpers.d.ts +20 -0
- package/dist/test-helpers.js +46 -0
- package/dist/test-helpers.js.map +1 -0
- package/dist/test-helpers.test.d.ts +1 -0
- package/dist/test-helpers.test.js +90 -0
- package/dist/test-helpers.test.js.map +1 -0
- package/package.json +72 -0
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
import { readFile, writeFile, mkdir, unlink, stat, open } from "fs/promises";
|
|
2
|
+
import { resolve, dirname } from "path";
|
|
3
|
+
import { randomBytes } from "crypto";
|
|
4
|
+
/**
|
|
5
|
+
* Manages the session registry for multi-agent coordination.
|
|
6
|
+
* Uses PID lockfiles for safe concurrent access.
|
|
7
|
+
*/
|
|
8
|
+
export class SessionRegistryManager {
|
|
9
|
+
ttlHours;
|
|
10
|
+
registryPath;
|
|
11
|
+
locksDir;
|
|
12
|
+
constructor(vaultPath, ttlHours) {
|
|
13
|
+
this.ttlHours = ttlHours;
|
|
14
|
+
this.registryPath = resolve(vaultPath, "coordination/session-registry.json");
|
|
15
|
+
this.locksDir = resolve(vaultPath, "coordination/locks");
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Register a new session. Returns session ID and any conflicts.
|
|
19
|
+
*/
|
|
20
|
+
async register(tool, project, taskSummary = null, filesTouched = []) {
|
|
21
|
+
const sessionId = `${tool}-${randomBytes(4).toString("hex")}`;
|
|
22
|
+
const now = new Date().toISOString();
|
|
23
|
+
const session = {
|
|
24
|
+
id: sessionId,
|
|
25
|
+
tool,
|
|
26
|
+
project,
|
|
27
|
+
task_summary: taskSummary,
|
|
28
|
+
files_touched: filesTouched,
|
|
29
|
+
started_at: now,
|
|
30
|
+
last_heartbeat: now,
|
|
31
|
+
completed_at: null,
|
|
32
|
+
status: "active",
|
|
33
|
+
};
|
|
34
|
+
return await this.withLock(async () => {
|
|
35
|
+
const registry = await this.readRegistry();
|
|
36
|
+
this.cleanStale(registry);
|
|
37
|
+
this.cleanOldCompleted(registry);
|
|
38
|
+
// Detect conflicts (use Set for O(n+m) intersection)
|
|
39
|
+
const conflicts = [];
|
|
40
|
+
if (filesTouched.length > 0) {
|
|
41
|
+
const touchedSet = new Set(filesTouched);
|
|
42
|
+
for (const existing of registry.sessions) {
|
|
43
|
+
if (existing.status !== "active")
|
|
44
|
+
continue;
|
|
45
|
+
const overlap = existing.files_touched.filter((f) => touchedSet.has(f));
|
|
46
|
+
if (overlap.length > 0) {
|
|
47
|
+
conflicts.push({
|
|
48
|
+
session_id: existing.id,
|
|
49
|
+
tool: existing.tool,
|
|
50
|
+
overlapping_files: overlap,
|
|
51
|
+
task_summary: existing.task_summary,
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
registry.sessions.push(session);
|
|
57
|
+
await this.writeRegistry(registry);
|
|
58
|
+
return { session_id: sessionId, conflicts };
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Update heartbeat for a session. Returns false if session not found.
|
|
63
|
+
*/
|
|
64
|
+
async heartbeat(sessionId) {
|
|
65
|
+
return await this.withLock(async () => {
|
|
66
|
+
const registry = await this.readRegistry();
|
|
67
|
+
const session = registry.sessions.find((s) => s.id === sessionId);
|
|
68
|
+
if (!session)
|
|
69
|
+
return false;
|
|
70
|
+
session.last_heartbeat = new Date().toISOString();
|
|
71
|
+
await this.writeRegistry(registry);
|
|
72
|
+
return true;
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Mark a session as completed.
|
|
77
|
+
*/
|
|
78
|
+
async complete(sessionId, summary) {
|
|
79
|
+
await this.withLock(async () => {
|
|
80
|
+
const registry = await this.readRegistry();
|
|
81
|
+
const session = registry.sessions.find((s) => s.id === sessionId);
|
|
82
|
+
if (session) {
|
|
83
|
+
session.status = "completed";
|
|
84
|
+
session.completed_at = new Date().toISOString();
|
|
85
|
+
if (summary)
|
|
86
|
+
session.task_summary = summary;
|
|
87
|
+
}
|
|
88
|
+
this.cleanOldCompleted(registry);
|
|
89
|
+
await this.writeRegistry(registry);
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* List active sessions. Persists stale status changes.
|
|
94
|
+
*/
|
|
95
|
+
async listActive() {
|
|
96
|
+
return await this.withLock(async () => {
|
|
97
|
+
const registry = await this.readRegistry();
|
|
98
|
+
const hadStale = this.cleanStale(registry);
|
|
99
|
+
if (hadStale) {
|
|
100
|
+
await this.writeRegistry(registry);
|
|
101
|
+
}
|
|
102
|
+
return registry.sessions.filter((s) => s.status === "active");
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
cleanOldCompleted(registry) {
|
|
106
|
+
const cutoff = Date.now() - 24 * 60 * 60 * 1000;
|
|
107
|
+
registry.sessions = registry.sessions.filter((s) => {
|
|
108
|
+
if (s.status !== "completed")
|
|
109
|
+
return true;
|
|
110
|
+
const ts = s.completed_at ?? s.started_at;
|
|
111
|
+
if (typeof ts !== "string")
|
|
112
|
+
return false;
|
|
113
|
+
const time = new Date(ts).getTime();
|
|
114
|
+
return !isNaN(time) && time > cutoff;
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
cleanStale(registry) {
|
|
118
|
+
let changed = false;
|
|
119
|
+
const cutoff = Date.now() - this.ttlHours * 60 * 60 * 1000;
|
|
120
|
+
for (const session of registry.sessions) {
|
|
121
|
+
if (session.status === "active" &&
|
|
122
|
+
new Date(session.last_heartbeat).getTime() < cutoff) {
|
|
123
|
+
session.status = "stale";
|
|
124
|
+
changed = true;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
return changed;
|
|
128
|
+
}
|
|
129
|
+
async readRegistry() {
|
|
130
|
+
try {
|
|
131
|
+
const raw = await readFile(this.registryPath, "utf-8");
|
|
132
|
+
const parsed = JSON.parse(raw);
|
|
133
|
+
// Schema validation: ensure sessions is an array of valid objects
|
|
134
|
+
if (!parsed || !Array.isArray(parsed.sessions)) {
|
|
135
|
+
return { sessions: [] };
|
|
136
|
+
}
|
|
137
|
+
// Filter out malformed entries
|
|
138
|
+
const validSessions = parsed.sessions.filter((s) => typeof s === "object" &&
|
|
139
|
+
s !== null &&
|
|
140
|
+
typeof s.id === "string" &&
|
|
141
|
+
typeof s.tool === "string" &&
|
|
142
|
+
typeof s.status === "string" &&
|
|
143
|
+
typeof s.started_at === "string" &&
|
|
144
|
+
typeof s.last_heartbeat === "string" &&
|
|
145
|
+
!isNaN(new Date(s.last_heartbeat).getTime()) &&
|
|
146
|
+
Array.isArray(s.files_touched));
|
|
147
|
+
return { sessions: validSessions };
|
|
148
|
+
}
|
|
149
|
+
catch (e) {
|
|
150
|
+
if (e?.code !== "ENOENT") {
|
|
151
|
+
console.error("[session-registry] Error reading registry:", e.message);
|
|
152
|
+
}
|
|
153
|
+
return { sessions: [] };
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
async writeRegistry(registry) {
|
|
157
|
+
await mkdir(dirname(this.registryPath), { recursive: true });
|
|
158
|
+
await writeFile(this.registryPath, JSON.stringify(registry, null, 2), "utf-8");
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* PID-based lockfile for safe concurrent writes.
|
|
162
|
+
* Times out after 2 seconds with retry.
|
|
163
|
+
*/
|
|
164
|
+
async withLock(fn) {
|
|
165
|
+
const lockPath = resolve(this.locksDir, "session-registry.lock");
|
|
166
|
+
await mkdir(this.locksDir, { recursive: true });
|
|
167
|
+
const maxRetries = 8;
|
|
168
|
+
const retryDelay = 100;
|
|
169
|
+
for (let attempt = 0; attempt < maxRetries; attempt++) {
|
|
170
|
+
try {
|
|
171
|
+
const lockContent = JSON.stringify({
|
|
172
|
+
pid: process.pid,
|
|
173
|
+
tool: "obsidian-mcp",
|
|
174
|
+
timestamp: new Date().toISOString(),
|
|
175
|
+
});
|
|
176
|
+
const fd = await open(lockPath, "wx");
|
|
177
|
+
try {
|
|
178
|
+
await fd.write(Buffer.from(lockContent, "utf-8"));
|
|
179
|
+
}
|
|
180
|
+
finally {
|
|
181
|
+
await fd.close();
|
|
182
|
+
}
|
|
183
|
+
// Lock acquired — run the function
|
|
184
|
+
try {
|
|
185
|
+
return await fn();
|
|
186
|
+
}
|
|
187
|
+
finally {
|
|
188
|
+
await unlink(lockPath).catch(() => { });
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
catch (e) {
|
|
192
|
+
if (e.code === "EEXIST") {
|
|
193
|
+
// Lock exists — check if it's stale
|
|
194
|
+
try {
|
|
195
|
+
const lockStat = await stat(lockPath);
|
|
196
|
+
const lockAge = Date.now() - lockStat.mtimeMs;
|
|
197
|
+
if (lockAge > 5000) {
|
|
198
|
+
// Lock is stale (>5s), force remove
|
|
199
|
+
await unlink(lockPath).catch(() => { });
|
|
200
|
+
continue;
|
|
201
|
+
}
|
|
202
|
+
// Lock is fresh — check if PID is alive
|
|
203
|
+
const lockData = JSON.parse(await readFile(lockPath, "utf-8"));
|
|
204
|
+
try {
|
|
205
|
+
process.kill(lockData.pid, 0);
|
|
206
|
+
// PID is alive — wait and retry
|
|
207
|
+
}
|
|
208
|
+
catch {
|
|
209
|
+
// PID is dead — remove stale lock
|
|
210
|
+
await unlink(lockPath).catch(() => { });
|
|
211
|
+
continue;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
catch {
|
|
215
|
+
// Can't read lock — try to remove it
|
|
216
|
+
await unlink(lockPath).catch(() => { });
|
|
217
|
+
continue;
|
|
218
|
+
}
|
|
219
|
+
// Wait before retrying
|
|
220
|
+
await new Promise((r) => setTimeout(r, retryDelay));
|
|
221
|
+
}
|
|
222
|
+
else {
|
|
223
|
+
throw e;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
throw new Error("Failed to acquire lock after retries");
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
//# sourceMappingURL=session-registry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-registry.js","sourceRoot":"","sources":["../../src/lib/session-registry.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach } from "vitest";
|
|
2
|
+
import { mkdir, writeFile as fspWriteFile, rm, readFile as fspReadFile } from "fs/promises";
|
|
3
|
+
import { tmpdir } from "os";
|
|
4
|
+
import { join } from "path";
|
|
5
|
+
import { SessionRegistryManager } from "./session-registry.js";
|
|
6
|
+
describe("SessionRegistryManager", () => {
|
|
7
|
+
let vaultRoot;
|
|
8
|
+
let manager;
|
|
9
|
+
beforeEach(async () => {
|
|
10
|
+
vaultRoot = join(tmpdir(), `session-test-${Date.now()}-${Math.random().toString(36).slice(2)}`);
|
|
11
|
+
await mkdir(vaultRoot, { recursive: true });
|
|
12
|
+
manager = new SessionRegistryManager(vaultRoot, 1);
|
|
13
|
+
});
|
|
14
|
+
afterEach(async () => {
|
|
15
|
+
await rm(vaultRoot, { recursive: true, force: true });
|
|
16
|
+
});
|
|
17
|
+
describe("register", () => {
|
|
18
|
+
it("creates session with generated ID", async () => {
|
|
19
|
+
const result = await manager.register("claude-code", "test-project");
|
|
20
|
+
expect(result.session_id).toMatch(/^claude-code-[a-f0-9]{8}$/);
|
|
21
|
+
});
|
|
22
|
+
it("creates session with all fields", async () => {
|
|
23
|
+
const result = await manager.register("opencode", "my-project", "Doing work", ["file1.ts", "file2.ts"]);
|
|
24
|
+
expect(result.session_id).toMatch(/^opencode-/);
|
|
25
|
+
});
|
|
26
|
+
it("detects file conflicts", async () => {
|
|
27
|
+
await manager.register("claude-code", "proj", "Task 1", ["a.ts", "b.ts"]);
|
|
28
|
+
const result = await manager.register("opencode", "proj", "Task 2", ["b.ts", "c.ts"]);
|
|
29
|
+
expect(result.conflicts.length).toBe(1);
|
|
30
|
+
expect(result.conflicts[0].overlapping_files).toContain("b.ts");
|
|
31
|
+
});
|
|
32
|
+
it("returns empty conflicts when no overlap", async () => {
|
|
33
|
+
await manager.register("claude-code", "proj", "Task 1", ["a.ts"]);
|
|
34
|
+
const result = await manager.register("opencode", "proj", "Task 2", ["b.ts"]);
|
|
35
|
+
expect(result.conflicts).toEqual([]);
|
|
36
|
+
});
|
|
37
|
+
it("returns empty conflicts when first session has no files", async () => {
|
|
38
|
+
await manager.register("claude-code", "proj", "Task 1", []);
|
|
39
|
+
const result = await manager.register("opencode", "proj", "Task 2", ["b.ts"]);
|
|
40
|
+
expect(result.conflicts).toEqual([]);
|
|
41
|
+
});
|
|
42
|
+
it("cleans stale sessions on register", async () => {
|
|
43
|
+
const oldManager = new SessionRegistryManager(vaultRoot, 0);
|
|
44
|
+
await oldManager.register("claude-code", "proj", "Old session");
|
|
45
|
+
await new Promise(r => setTimeout(r, 10));
|
|
46
|
+
const newManager = new SessionRegistryManager(vaultRoot, 0.000001);
|
|
47
|
+
await newManager.register("opencode", "proj", "New session");
|
|
48
|
+
const active = await newManager.listActive();
|
|
49
|
+
expect(active.every(s => s.status === "active")).toBe(true);
|
|
50
|
+
});
|
|
51
|
+
it("persists session to registry file", async () => {
|
|
52
|
+
await manager.register("claude-code", "test-project");
|
|
53
|
+
const registryPath = join(vaultRoot, "coordination/session-registry.json");
|
|
54
|
+
const raw = await fspReadFile(registryPath, "utf-8");
|
|
55
|
+
const parsed = JSON.parse(raw);
|
|
56
|
+
expect(parsed.sessions.length).toBe(1);
|
|
57
|
+
expect(parsed.sessions[0].tool).toBe("claude-code");
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
describe("heartbeat", () => {
|
|
61
|
+
it("updates heartbeat timestamp", async () => {
|
|
62
|
+
const { session_id } = await manager.register("claude-code", "proj");
|
|
63
|
+
await new Promise(r => setTimeout(r, 10));
|
|
64
|
+
const result = await manager.heartbeat(session_id);
|
|
65
|
+
expect(result).toBe(true);
|
|
66
|
+
});
|
|
67
|
+
it("returns false for missing session", async () => {
|
|
68
|
+
const result = await manager.heartbeat("nonexistent-id");
|
|
69
|
+
expect(result).toBe(false);
|
|
70
|
+
});
|
|
71
|
+
it("persists heartbeat to file", async () => {
|
|
72
|
+
const { session_id } = await manager.register("claude-code", "proj");
|
|
73
|
+
await manager.heartbeat(session_id);
|
|
74
|
+
const registryPath = join(vaultRoot, "coordination/session-registry.json");
|
|
75
|
+
const raw = await fspReadFile(registryPath, "utf-8");
|
|
76
|
+
const parsed = JSON.parse(raw);
|
|
77
|
+
const session = parsed.sessions.find((s) => s.id === session_id);
|
|
78
|
+
expect(session.last_heartbeat).toBeDefined();
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
describe("complete", () => {
|
|
82
|
+
it("marks session as completed", async () => {
|
|
83
|
+
const { session_id } = await manager.register("claude-code", "proj");
|
|
84
|
+
await manager.complete(session_id);
|
|
85
|
+
const registryPath = join(vaultRoot, "coordination/session-registry.json");
|
|
86
|
+
const raw = await fspReadFile(registryPath, "utf-8");
|
|
87
|
+
const parsed = JSON.parse(raw);
|
|
88
|
+
const session = parsed.sessions.find((s) => s.id === session_id);
|
|
89
|
+
expect(session.status).toBe("completed");
|
|
90
|
+
expect(session.completed_at).toBeDefined();
|
|
91
|
+
});
|
|
92
|
+
it("updates task summary when provided", async () => {
|
|
93
|
+
const { session_id } = await manager.register("claude-code", "proj", "Old summary");
|
|
94
|
+
await manager.complete(session_id, "New summary");
|
|
95
|
+
const registryPath = join(vaultRoot, "coordination/session-registry.json");
|
|
96
|
+
const raw = await fspReadFile(registryPath, "utf-8");
|
|
97
|
+
const parsed = JSON.parse(raw);
|
|
98
|
+
const session = parsed.sessions.find((s) => s.id === session_id);
|
|
99
|
+
expect(session.task_summary).toBe("New summary");
|
|
100
|
+
});
|
|
101
|
+
it("removes old completed sessions", async () => {
|
|
102
|
+
const { session_id } = await manager.register("claude-code", "proj");
|
|
103
|
+
await manager.complete(session_id);
|
|
104
|
+
const registryPath = join(vaultRoot, "coordination/session-registry.json");
|
|
105
|
+
let raw = await fspReadFile(registryPath, "utf-8");
|
|
106
|
+
let parsed = JSON.parse(raw);
|
|
107
|
+
const session = parsed.sessions.find((s) => s.id === session_id);
|
|
108
|
+
session.completed_at = new Date(Date.now() - 25 * 60 * 60 * 1000).toISOString();
|
|
109
|
+
await fspWriteFile(registryPath, JSON.stringify(parsed));
|
|
110
|
+
await manager.register("opencode", "proj");
|
|
111
|
+
raw = await fspReadFile(registryPath, "utf-8");
|
|
112
|
+
parsed = JSON.parse(raw);
|
|
113
|
+
expect(parsed.sessions.find((s) => s.id === session_id)).toBeUndefined();
|
|
114
|
+
});
|
|
115
|
+
});
|
|
116
|
+
describe("listActive", () => {
|
|
117
|
+
it("returns only active sessions", async () => {
|
|
118
|
+
const { session_id } = await manager.register("claude-code", "proj");
|
|
119
|
+
await manager.register("opencode", "proj2");
|
|
120
|
+
await manager.complete(session_id);
|
|
121
|
+
const active = await manager.listActive();
|
|
122
|
+
expect(active.length).toBe(1);
|
|
123
|
+
expect(active[0].tool).toBe("opencode");
|
|
124
|
+
});
|
|
125
|
+
it("returns empty array when no active sessions", async () => {
|
|
126
|
+
const active = await manager.listActive();
|
|
127
|
+
expect(active).toEqual([]);
|
|
128
|
+
});
|
|
129
|
+
it("persists stale status changes", async () => {
|
|
130
|
+
const staleManager = new SessionRegistryManager(vaultRoot, 0.000001);
|
|
131
|
+
await staleManager.register("claude-code", "proj");
|
|
132
|
+
await new Promise(r => setTimeout(r, 20));
|
|
133
|
+
const active = await staleManager.listActive();
|
|
134
|
+
expect(active).toEqual([]);
|
|
135
|
+
const registryPath = join(vaultRoot, "coordination/session-registry.json");
|
|
136
|
+
const raw = await fspReadFile(registryPath, "utf-8");
|
|
137
|
+
const parsed = JSON.parse(raw);
|
|
138
|
+
expect(parsed.sessions[0].status).toBe("stale");
|
|
139
|
+
});
|
|
140
|
+
});
|
|
141
|
+
describe("withLock", () => {
|
|
142
|
+
it("prevents concurrent access", async () => {
|
|
143
|
+
const promises = [];
|
|
144
|
+
for (let i = 0; i < 5; i++) {
|
|
145
|
+
promises.push(manager.register(`tool-${i}`, "proj"));
|
|
146
|
+
}
|
|
147
|
+
const results = await Promise.all(promises);
|
|
148
|
+
expect(results.every(r => r.session_id)).toBe(true);
|
|
149
|
+
const registryPath = join(vaultRoot, "coordination/session-registry.json");
|
|
150
|
+
const raw = await fspReadFile(registryPath, "utf-8");
|
|
151
|
+
const parsed = JSON.parse(raw);
|
|
152
|
+
expect(parsed.sessions.length).toBe(5);
|
|
153
|
+
});
|
|
154
|
+
it("removes stale locks", async () => {
|
|
155
|
+
const locksDir = join(vaultRoot, "coordination/locks");
|
|
156
|
+
await mkdir(locksDir, { recursive: true });
|
|
157
|
+
const lockPath = join(locksDir, "session-registry.lock");
|
|
158
|
+
await fspWriteFile(lockPath, JSON.stringify({ pid: 999999999, timestamp: new Date().toISOString() }));
|
|
159
|
+
const { session_id } = await manager.register("claude-code", "proj");
|
|
160
|
+
expect(session_id).toBeDefined();
|
|
161
|
+
});
|
|
162
|
+
});
|
|
163
|
+
describe("readRegistry validation", () => {
|
|
164
|
+
it("handles malformed session entries", async () => {
|
|
165
|
+
const registryPath = join(vaultRoot, "coordination/session-registry.json");
|
|
166
|
+
await mkdir(join(vaultRoot, "coordination"), { recursive: true });
|
|
167
|
+
const now = new Date().toISOString();
|
|
168
|
+
const malformedData = {
|
|
169
|
+
sessions: [
|
|
170
|
+
{ id: "valid-1", tool: "test", status: "active", started_at: now, last_heartbeat: now, files_touched: [] },
|
|
171
|
+
{ id: 123, tool: "bad" },
|
|
172
|
+
null,
|
|
173
|
+
"string not object",
|
|
174
|
+
{ id: "valid-2", tool: "test", status: "active", started_at: "bad-date", last_heartbeat: "2024-01-01T00:00:00Z", files_touched: [] },
|
|
175
|
+
]
|
|
176
|
+
};
|
|
177
|
+
await fspWriteFile(registryPath, JSON.stringify(malformedData));
|
|
178
|
+
const active = await manager.listActive();
|
|
179
|
+
const validIds = active.map(s => s.id);
|
|
180
|
+
expect(validIds).toContain("valid-1");
|
|
181
|
+
expect(validIds).not.toContain("valid-2");
|
|
182
|
+
});
|
|
183
|
+
it("handles non-array sessions field", async () => {
|
|
184
|
+
const registryPath = join(vaultRoot, "coordination/session-registry.json");
|
|
185
|
+
await mkdir(join(vaultRoot, "coordination"), { recursive: true });
|
|
186
|
+
await fspWriteFile(registryPath, JSON.stringify({ sessions: "not-an-array" }));
|
|
187
|
+
const active = await manager.listActive();
|
|
188
|
+
expect(active).toEqual([]);
|
|
189
|
+
});
|
|
190
|
+
it("handles missing sessions field", async () => {
|
|
191
|
+
const registryPath = join(vaultRoot, "coordination/session-registry.json");
|
|
192
|
+
await mkdir(join(vaultRoot, "coordination"), { recursive: true });
|
|
193
|
+
await fspWriteFile(registryPath, JSON.stringify({}));
|
|
194
|
+
const active = await manager.listActive();
|
|
195
|
+
expect(active).toEqual([]);
|
|
196
|
+
});
|
|
197
|
+
});
|
|
198
|
+
});
|
|
199
|
+
//# sourceMappingURL=session-registry.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-registry.test.js","sourceRoot":"","sources":["../../src/lib/session-registry.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { SkillRegistry, InstalledSkill } from "../commands/skill/schema.js";
|
|
2
|
+
export declare class SkillRegistryManager {
|
|
3
|
+
private readonly registryPath;
|
|
4
|
+
private readonly installedDir;
|
|
5
|
+
constructor(vaultPath: string);
|
|
6
|
+
read(): Promise<SkillRegistry>;
|
|
7
|
+
write(registry: SkillRegistry): Promise<void>;
|
|
8
|
+
add(skill: InstalledSkill): Promise<void>;
|
|
9
|
+
remove(name: string): Promise<boolean>;
|
|
10
|
+
get(name: string): Promise<InstalledSkill | undefined>;
|
|
11
|
+
list(): Promise<InstalledSkill[]>;
|
|
12
|
+
getInstalledPath(skillName: string): string;
|
|
13
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { readFile, writeFile, mkdir } from "fs/promises";
|
|
2
|
+
import { resolve, dirname } from "path";
|
|
3
|
+
export class SkillRegistryManager {
|
|
4
|
+
registryPath;
|
|
5
|
+
installedDir;
|
|
6
|
+
constructor(vaultPath) {
|
|
7
|
+
this.registryPath = resolve(vaultPath, "skills/registry.json");
|
|
8
|
+
this.installedDir = resolve(vaultPath, "skills/installed");
|
|
9
|
+
}
|
|
10
|
+
async read() {
|
|
11
|
+
try {
|
|
12
|
+
const raw = await readFile(this.registryPath, "utf-8");
|
|
13
|
+
const parsed = JSON.parse(raw);
|
|
14
|
+
if (!parsed || !Array.isArray(parsed.skills)) {
|
|
15
|
+
return { skills: [] };
|
|
16
|
+
}
|
|
17
|
+
const validSkills = parsed.skills.filter((s) => {
|
|
18
|
+
if (typeof s !== 'object' || s === null)
|
|
19
|
+
return false;
|
|
20
|
+
const obj = s;
|
|
21
|
+
return (typeof obj.name === 'string' &&
|
|
22
|
+
typeof obj.source === 'string' &&
|
|
23
|
+
typeof obj.version === 'string' &&
|
|
24
|
+
typeof obj.installed_at === 'string' &&
|
|
25
|
+
typeof obj.updated_at === 'string' &&
|
|
26
|
+
typeof obj.status === 'string' &&
|
|
27
|
+
(obj.status === 'active' || obj.status === 'deprecated') &&
|
|
28
|
+
typeof obj.auto_update === 'boolean');
|
|
29
|
+
});
|
|
30
|
+
return { skills: validSkills };
|
|
31
|
+
}
|
|
32
|
+
catch (e) {
|
|
33
|
+
if (e.code !== "ENOENT") {
|
|
34
|
+
console.error("[skill-registry] Error reading registry:", e.message);
|
|
35
|
+
}
|
|
36
|
+
return { skills: [] };
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
async write(registry) {
|
|
40
|
+
await mkdir(dirname(this.registryPath), { recursive: true });
|
|
41
|
+
await writeFile(this.registryPath, JSON.stringify(registry, null, 2), "utf-8");
|
|
42
|
+
}
|
|
43
|
+
async add(skill) {
|
|
44
|
+
const registry = await this.read();
|
|
45
|
+
const existingIndex = registry.skills.findIndex(s => s.name === skill.name);
|
|
46
|
+
if (existingIndex >= 0) {
|
|
47
|
+
registry.skills[existingIndex] = skill;
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
registry.skills.push(skill);
|
|
51
|
+
}
|
|
52
|
+
await this.write(registry);
|
|
53
|
+
}
|
|
54
|
+
async remove(name) {
|
|
55
|
+
const registry = await this.read();
|
|
56
|
+
const index = registry.skills.findIndex(s => s.name === name);
|
|
57
|
+
if (index < 0) {
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
registry.skills.splice(index, 1);
|
|
61
|
+
await this.write(registry);
|
|
62
|
+
return true;
|
|
63
|
+
}
|
|
64
|
+
async get(name) {
|
|
65
|
+
const registry = await this.read();
|
|
66
|
+
return registry.skills.find(s => s.name === name);
|
|
67
|
+
}
|
|
68
|
+
async list() {
|
|
69
|
+
const registry = await this.read();
|
|
70
|
+
return registry.skills;
|
|
71
|
+
}
|
|
72
|
+
getInstalledPath(skillName) {
|
|
73
|
+
return resolve(this.installedDir, skillName, "SKILL.md");
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=skill-registry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"skill-registry.js","sourceRoot":"","sources":["../../src/lib/skill-registry.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rough token estimation using chars/4 heuristic with a 15% safety margin.
|
|
3
|
+
*
|
|
4
|
+
* Accuracy notes:
|
|
5
|
+
* - English prose: typically within ±10%
|
|
6
|
+
* - Code-heavy content (short tokens like brackets, operators): may undercount by 20-30%
|
|
7
|
+
* - The 1.15 multiplier provides a safety buffer; actual tokenizer counts (e.g., tiktoken)
|
|
8
|
+
* may differ significantly. For precise budgeting, use a proper tokenizer.
|
|
9
|
+
*/
|
|
10
|
+
export declare function estimateTokens(text: string): number;
|
|
11
|
+
/**
|
|
12
|
+
* Truncate text to fit within a token budget.
|
|
13
|
+
* Truncates at the last section boundary (## heading) before the limit.
|
|
14
|
+
* Falls back to the last paragraph boundary, then last newline.
|
|
15
|
+
*/
|
|
16
|
+
export declare function truncateToTokenBudget(text: string, maxTokens: number): {
|
|
17
|
+
text: string;
|
|
18
|
+
truncated: boolean;
|
|
19
|
+
};
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rough token estimation using chars/4 heuristic with a 15% safety margin.
|
|
3
|
+
*
|
|
4
|
+
* Accuracy notes:
|
|
5
|
+
* - English prose: typically within ±10%
|
|
6
|
+
* - Code-heavy content (short tokens like brackets, operators): may undercount by 20-30%
|
|
7
|
+
* - The 1.15 multiplier provides a safety buffer; actual tokenizer counts (e.g., tiktoken)
|
|
8
|
+
* may differ significantly. For precise budgeting, use a proper tokenizer.
|
|
9
|
+
*/
|
|
10
|
+
export function estimateTokens(text) {
|
|
11
|
+
return Math.ceil(text.length / 4 * 1.15);
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Truncate text to fit within a token budget.
|
|
15
|
+
* Truncates at the last section boundary (## heading) before the limit.
|
|
16
|
+
* Falls back to the last paragraph boundary, then last newline.
|
|
17
|
+
*/
|
|
18
|
+
export function truncateToTokenBudget(text, maxTokens) {
|
|
19
|
+
const estimated = estimateTokens(text);
|
|
20
|
+
if (estimated <= maxTokens) {
|
|
21
|
+
return { text, truncated: false };
|
|
22
|
+
}
|
|
23
|
+
// Reserve ~20 tokens for the truncation suffix indicator
|
|
24
|
+
const reservedTokens = 20;
|
|
25
|
+
const effectiveBudget = Math.max(0, maxTokens - reservedTokens);
|
|
26
|
+
// Convert token budget to approximate character limit
|
|
27
|
+
const charLimit = Math.floor(effectiveBudget / 1.15 * 4);
|
|
28
|
+
// Try to cut at a section boundary
|
|
29
|
+
const truncated = text.slice(0, charLimit);
|
|
30
|
+
const lastSection = truncated.lastIndexOf("\n## ");
|
|
31
|
+
if (lastSection > charLimit * 0.5) {
|
|
32
|
+
return {
|
|
33
|
+
text: truncated.slice(0, lastSection) + "\n\n[truncated — use vault_project_context for full details]",
|
|
34
|
+
truncated: true,
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
// Fall back to paragraph boundary
|
|
38
|
+
const lastParagraph = truncated.lastIndexOf("\n\n");
|
|
39
|
+
if (lastParagraph > charLimit * 0.3) {
|
|
40
|
+
return {
|
|
41
|
+
text: truncated.slice(0, lastParagraph) + "\n\n[truncated — use vault_project_context for full details]",
|
|
42
|
+
truncated: true,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
// Fall back to last newline
|
|
46
|
+
const lastNewline = truncated.lastIndexOf("\n");
|
|
47
|
+
if (lastNewline > 0) {
|
|
48
|
+
return {
|
|
49
|
+
text: truncated.slice(0, lastNewline) + "\n\n[truncated — use vault_project_context for full details]",
|
|
50
|
+
truncated: true,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
return {
|
|
54
|
+
text: truncated + "\n\n[truncated — use vault_project_context for full details]",
|
|
55
|
+
truncated: true,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=token-estimator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"token-estimator.js","sourceRoot":"","sources":["../../src/lib/token-estimator.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,MAAM,UAAU,cAAc,CAAC,IAAY;IACzC,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,GAAG,IAAI,CAAC,CAAC;AAC3C,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,qBAAqB,CAAC,IAAY,EAAE,SAAiB;IACnE,MAAM,SAAS,GAAG,cAAc,CAAC,IAAI,CAAC,CAAC;IACvC,IAAI,SAAS,IAAI,SAAS,EAAE,CAAC;QAC3B,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC;IACpC,CAAC;IAED,yDAAyD;IACzD,MAAM,cAAc,GAAG,EAAE,CAAC;IAC1B,MAAM,eAAe,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,SAAS,GAAG,cAAc,CAAC,CAAC;IAEhE,sDAAsD;IACtD,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,eAAe,GAAG,IAAI,GAAG,CAAC,CAAC,CAAC;IAEzD,mCAAmC;IACnC,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC;IAC3C,MAAM,WAAW,GAAG,SAAS,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC;IACnD,IAAI,WAAW,GAAG,SAAS,GAAG,GAAG,EAAE,CAAC;QAClC,OAAO;YACL,IAAI,EAAE,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,WAAW,CAAC,GAAG,8DAA8D;YACtG,SAAS,EAAE,IAAI;SAChB,CAAC;IACJ,CAAC;IAED,kCAAkC;IAClC,MAAM,aAAa,GAAG,SAAS,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;IACpD,IAAI,aAAa,GAAG,SAAS,GAAG,GAAG,EAAE,CAAC;QACpC,OAAO;YACL,IAAI,EAAE,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,aAAa,CAAC,GAAG,8DAA8D;YACxG,SAAS,EAAE,IAAI;SAChB,CAAC;IACJ,CAAC;IAED,4BAA4B;IAC5B,MAAM,WAAW,GAAG,SAAS,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;IAChD,IAAI,WAAW,GAAG,CAAC,EAAE,CAAC;QACpB,OAAO;YACL,IAAI,EAAE,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,WAAW,CAAC,GAAG,8DAA8D;YACtG,SAAS,EAAE,IAAI;SAChB,CAAC;IACJ,CAAC;IAED,OAAO;QACL,IAAI,EAAE,SAAS,GAAG,8DAA8D;QAChF,SAAS,EAAE,IAAI;KAChB,CAAC;AACJ,CAAC"}
|