oh-my-adhd 0.2.12 → 0.2.13
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/dist/mcp/lib/brain.js
CHANGED
|
@@ -286,8 +286,9 @@ export async function getThreads() {
|
|
|
286
286
|
});
|
|
287
287
|
return sorted;
|
|
288
288
|
}
|
|
289
|
+
const UUID_RE_STRICT = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
289
290
|
export async function getThread(threadId) {
|
|
290
|
-
if (!threadId ||
|
|
291
|
+
if (!threadId || !UUID_RE_STRICT.test(threadId))
|
|
291
292
|
return null;
|
|
292
293
|
try {
|
|
293
294
|
return await fs.readFile(path.join(THREADS_DIR, `${threadId}.md`), "utf-8");
|
|
@@ -23,13 +23,22 @@ export function registerWikiExport(server) {
|
|
|
23
23
|
const date = new Date().toISOString().slice(0, 10);
|
|
24
24
|
const defaultPath = path.join(os.homedir(), `oh-my-adhd-export-${date}.json`);
|
|
25
25
|
const resolved = outputPath ? path.resolve(outputPath) : defaultPath;
|
|
26
|
-
// Require .json extension — prevents LLM-controlled path from clobbering
|
|
26
|
+
// Require .json extension — prevents LLM-controlled path from clobbering non-JSON config files
|
|
27
27
|
if (!resolved.endsWith(".json")) {
|
|
28
28
|
return {
|
|
29
29
|
content: [{ type: "text", text: "오류: outputPath는 .json 확장자로 끝나야 합니다." }],
|
|
30
30
|
isError: true,
|
|
31
31
|
};
|
|
32
32
|
}
|
|
33
|
+
// Block writes into known sensitive dirs (~/.ssh, ~/.aws, ~/.gnupg)
|
|
34
|
+
const sensitivePatterns = [".ssh/", ".aws/", ".gnupg/", ".config/git/"];
|
|
35
|
+
const homeDir = os.homedir();
|
|
36
|
+
if (sensitivePatterns.some(p => resolved.startsWith(path.join(homeDir, p)))) {
|
|
37
|
+
return {
|
|
38
|
+
content: [{ type: "text", text: "오류: 보안상 해당 경로에는 내보낼 수 없습니다." }],
|
|
39
|
+
isError: true,
|
|
40
|
+
};
|
|
41
|
+
}
|
|
33
42
|
const filePath = resolved;
|
|
34
43
|
const tmp = filePath + ".tmp";
|
|
35
44
|
await fs.writeFile(tmp, JSON.stringify(exportData, null, 2), "utf-8");
|
|
@@ -122,24 +122,24 @@ export function registerWikiImport(server) {
|
|
|
122
122
|
const tmp = manifestFile + ".tmp";
|
|
123
123
|
await fs.writeFile(tmp, JSON.stringify(manifest, null, 2), "utf-8");
|
|
124
124
|
await fs.rename(tmp, manifestFile);
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
125
|
+
// Import pages inside lock for consistency with concurrent dump+import
|
|
126
|
+
if (exportData.pages && Array.isArray(exportData.pages)) {
|
|
127
|
+
for (const rawPage of exportData.pages) {
|
|
128
|
+
if (typeof rawPage !== "object" || rawPage === null)
|
|
129
|
+
continue;
|
|
130
|
+
const page = rawPage;
|
|
131
|
+
const slug = typeof page.slug === "string" ? page.slug : "";
|
|
132
|
+
const content = typeof page.content === "string" ? page.content : "";
|
|
133
|
+
if (!SLUG_RE.test(slug) || !content)
|
|
134
|
+
continue;
|
|
135
|
+
const pageFile = path.join(pagesDir, `${slug}.md`);
|
|
136
|
+
const pageTmp = pageFile + ".tmp";
|
|
137
|
+
await fs.writeFile(pageTmp, content, "utf-8");
|
|
138
|
+
await fs.rename(pageTmp, pageFile);
|
|
139
|
+
importedPages++;
|
|
140
|
+
}
|
|
141
141
|
}
|
|
142
|
-
}
|
|
142
|
+
});
|
|
143
143
|
return {
|
|
144
144
|
content: [{
|
|
145
145
|
type: "text",
|
package/package.json
CHANGED
|
@@ -8,13 +8,15 @@ const BRAIN_DIR = process.env.OH_MY_ADHD_DIR ?? join(homedir(), ".oh-my-adhd");
|
|
|
8
8
|
const MANIFEST = join(BRAIN_DIR, "threads", ".manifest.json");
|
|
9
9
|
|
|
10
10
|
// Use parent PID (= Claude Code instance) as session discriminator — no singleton file needed
|
|
11
|
-
const ppid = process.ppid
|
|
11
|
+
const ppid = process.ppid;
|
|
12
12
|
|
|
13
|
-
// Write per-session start marker
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
13
|
+
// Write per-session start marker (skip if ppid is unavailable — avoids shared .session-start-0)
|
|
14
|
+
if (ppid) {
|
|
15
|
+
try {
|
|
16
|
+
mkdirSync(BRAIN_DIR, { recursive: true });
|
|
17
|
+
writeFileSync(join(BRAIN_DIR, `.session-start-${ppid}`), String(Date.now()));
|
|
18
|
+
} catch { /* non-fatal */ }
|
|
19
|
+
}
|
|
18
20
|
|
|
19
21
|
// GC stale session files older than 24h (runs on every new session)
|
|
20
22
|
try {
|
package/scripts/stop-hook.mjs
CHANGED
|
@@ -11,7 +11,9 @@ const BRAIN_DIR = process.env.OH_MY_ADHD_DIR ?? join(homedir(), ".oh-my-adhd");
|
|
|
11
11
|
const MANIFEST = join(BRAIN_DIR, "threads", ".manifest.json");
|
|
12
12
|
|
|
13
13
|
// Use parent PID (= Claude Code instance) as session discriminator
|
|
14
|
-
const ppid = process.ppid
|
|
14
|
+
const ppid = process.ppid;
|
|
15
|
+
// If ppid is unavailable, skip protection rather than risk a shared-file collision
|
|
16
|
+
if (!ppid) process.exit(0);
|
|
15
17
|
const SESSION_START_FILE = join(BRAIN_DIR, `.session-start-${ppid}`);
|
|
16
18
|
const LAST_DUMP_FILE = join(BRAIN_DIR, `.last-dump-${ppid}`);
|
|
17
19
|
|