@wastedcode/memex 0.1.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.
Files changed (98) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +291 -0
  3. package/dist/cli/client.d.ts +35 -0
  4. package/dist/cli/client.js +183 -0
  5. package/dist/cli/client.js.map +1 -0
  6. package/dist/cli/commands/chown.d.ts +2 -0
  7. package/dist/cli/commands/chown.js +22 -0
  8. package/dist/cli/commands/chown.js.map +1 -0
  9. package/dist/cli/commands/config.d.ts +2 -0
  10. package/dist/cli/commands/config.js +132 -0
  11. package/dist/cli/commands/config.js.map +1 -0
  12. package/dist/cli/commands/create.d.ts +2 -0
  13. package/dist/cli/commands/create.js +21 -0
  14. package/dist/cli/commands/create.js.map +1 -0
  15. package/dist/cli/commands/destroy.d.ts +2 -0
  16. package/dist/cli/commands/destroy.js +34 -0
  17. package/dist/cli/commands/destroy.js.map +1 -0
  18. package/dist/cli/commands/ingest.d.ts +2 -0
  19. package/dist/cli/commands/ingest.js +74 -0
  20. package/dist/cli/commands/ingest.js.map +1 -0
  21. package/dist/cli/commands/lint.d.ts +2 -0
  22. package/dist/cli/commands/lint.js +46 -0
  23. package/dist/cli/commands/lint.js.map +1 -0
  24. package/dist/cli/commands/list.d.ts +2 -0
  25. package/dist/cli/commands/list.js +28 -0
  26. package/dist/cli/commands/list.js.map +1 -0
  27. package/dist/cli/commands/login.d.ts +2 -0
  28. package/dist/cli/commands/login.js +51 -0
  29. package/dist/cli/commands/login.js.map +1 -0
  30. package/dist/cli/commands/logs.d.ts +2 -0
  31. package/dist/cli/commands/logs.js +26 -0
  32. package/dist/cli/commands/logs.js.map +1 -0
  33. package/dist/cli/commands/query.d.ts +2 -0
  34. package/dist/cli/commands/query.js +48 -0
  35. package/dist/cli/commands/query.js.map +1 -0
  36. package/dist/cli/commands/serve.d.ts +2 -0
  37. package/dist/cli/commands/serve.js +14 -0
  38. package/dist/cli/commands/serve.js.map +1 -0
  39. package/dist/cli/commands/status.d.ts +2 -0
  40. package/dist/cli/commands/status.js +66 -0
  41. package/dist/cli/commands/status.js.map +1 -0
  42. package/dist/daemon/auth.d.ts +31 -0
  43. package/dist/daemon/auth.js +84 -0
  44. package/dist/daemon/auth.js.map +1 -0
  45. package/dist/daemon/db.d.ts +36 -0
  46. package/dist/daemon/db.js +181 -0
  47. package/dist/daemon/db.js.map +1 -0
  48. package/dist/daemon/namespace.d.ts +34 -0
  49. package/dist/daemon/namespace.js +74 -0
  50. package/dist/daemon/namespace.js.map +1 -0
  51. package/dist/daemon/peercred.d.ts +15 -0
  52. package/dist/daemon/peercred.js +19 -0
  53. package/dist/daemon/peercred.js.map +1 -0
  54. package/dist/daemon/queue.d.ts +26 -0
  55. package/dist/daemon/queue.js +126 -0
  56. package/dist/daemon/queue.js.map +1 -0
  57. package/dist/daemon/routes.d.ts +38 -0
  58. package/dist/daemon/routes.js +258 -0
  59. package/dist/daemon/routes.js.map +1 -0
  60. package/dist/daemon/runner.d.ts +25 -0
  61. package/dist/daemon/runner.js +195 -0
  62. package/dist/daemon/runner.js.map +1 -0
  63. package/dist/daemon/scaffold.d.ts +38 -0
  64. package/dist/daemon/scaffold.js +141 -0
  65. package/dist/daemon/scaffold.js.map +1 -0
  66. package/dist/daemon/server.d.ts +11 -0
  67. package/dist/daemon/server.js +145 -0
  68. package/dist/daemon/server.js.map +1 -0
  69. package/dist/daemon.d.ts +1 -0
  70. package/dist/daemon.js +55 -0
  71. package/dist/daemon.js.map +1 -0
  72. package/dist/index.d.ts +2 -0
  73. package/dist/index.js +36 -0
  74. package/dist/index.js.map +1 -0
  75. package/dist/lib/constants.d.ts +17 -0
  76. package/dist/lib/constants.js +30 -0
  77. package/dist/lib/constants.js.map +1 -0
  78. package/dist/lib/errors.d.ts +32 -0
  79. package/dist/lib/errors.js +64 -0
  80. package/dist/lib/errors.js.map +1 -0
  81. package/dist/lib/prompts/ingest.d.ts +9 -0
  82. package/dist/lib/prompts/ingest.js +48 -0
  83. package/dist/lib/prompts/ingest.js.map +1 -0
  84. package/dist/lib/prompts/lint.d.ts +8 -0
  85. package/dist/lib/prompts/lint.js +62 -0
  86. package/dist/lib/prompts/lint.js.map +1 -0
  87. package/dist/lib/prompts/query.d.ts +8 -0
  88. package/dist/lib/prompts/query.js +37 -0
  89. package/dist/lib/prompts/query.js.map +1 -0
  90. package/dist/lib/prompts/wiki.d.ts +11 -0
  91. package/dist/lib/prompts/wiki.js +112 -0
  92. package/dist/lib/prompts/wiki.js.map +1 -0
  93. package/dist/lib/types.d.ts +76 -0
  94. package/dist/lib/types.js +3 -0
  95. package/dist/lib/types.js.map +1 -0
  96. package/dist/standalone/memex.mjs +2313 -0
  97. package/dist/standalone/memex.mjs.map +7 -0
  98. package/package.json +54 -0
@@ -0,0 +1,195 @@
1
+ import { spawn } from 'node:child_process';
2
+ import { existsSync, readFileSync } from 'node:fs';
3
+ import { join } from 'node:path';
4
+ import { JOB_LIMITS, SIGTERM_GRACE_MS, BASE_ALLOWED_TOOLS, ALLOWED_TOOLS_WHITELIST, DEFAULT_MODEL, WORKSPACE_MOUNT, } from '../lib/constants.js';
5
+ import { buildIngestPrompt } from '../lib/prompts/ingest.js';
6
+ import { buildQueryPrompt } from '../lib/prompts/query.js';
7
+ import { buildLintPrompt } from '../lib/prompts/lint.js';
8
+ import { getWikiSystemPrompt } from '../lib/prompts/wiki.js';
9
+ export class ClaudeRunner {
10
+ namespace;
11
+ auth;
12
+ db;
13
+ wikisDir;
14
+ active = new Map();
15
+ constructor(namespace, auth, db, wikisDir) {
16
+ this.namespace = namespace;
17
+ this.auth = auth;
18
+ this.db = db;
19
+ this.wikisDir = wikisDir;
20
+ }
21
+ async run(job) {
22
+ const startTime = Date.now();
23
+ const wikiId = job.wiki_id;
24
+ const wiki = this.db.getWiki(wikiId);
25
+ const model = wiki?.default_model ?? DEFAULT_MODEL;
26
+ const limits = JOB_LIMITS[job.type];
27
+ // Build the task prompt
28
+ const prompt = this.buildPrompt(job);
29
+ // Resolve credentials
30
+ const credEnv = this.auth.resolveCredentials(wikiId);
31
+ // Resolve allowed tools
32
+ const tools = this.resolveTools(wikiId);
33
+ // Build claude -p command args
34
+ const claudeArgs = this.buildClaudeArgs(prompt, model, limits.max_turns, tools, wikiId);
35
+ // Wrap in a per-job mount namespace
36
+ const wrapped = this.namespace.wrapCommand(wikiId, claudeArgs);
37
+ // Build environment
38
+ const env = {
39
+ ...filterEnv(process.env),
40
+ ...credEnv,
41
+ HOME: WORKSPACE_MOUNT,
42
+ };
43
+ // If CLAUDE_CONFIG_DIR was set by auth, remap it to namespace path
44
+ if (credEnv['CLAUDE_CONFIG_DIR']) {
45
+ env['CLAUDE_CONFIG_DIR'] = `${WORKSPACE_MOUNT}/.claude`;
46
+ }
47
+ return new Promise((resolve) => {
48
+ const child = spawn(wrapped.command, wrapped.args, {
49
+ env,
50
+ stdio: ['pipe', 'pipe', 'pipe'],
51
+ });
52
+ // Close stdin immediately — prompt is passed via -p flag, not stdin
53
+ child.stdin.end();
54
+ this.active.set(job.id, child);
55
+ const stdout = [];
56
+ const stderr = [];
57
+ child.stdout.on('data', (chunk) => stdout.push(chunk));
58
+ child.stderr.on('data', (chunk) => stderr.push(chunk));
59
+ // Timeout management: SIGTERM → grace period → SIGKILL
60
+ const timeout = setTimeout(() => {
61
+ console.warn(`[runner] Job #${job.id} timed out after ${limits.timeout_ms}ms, sending SIGTERM`);
62
+ child.kill('SIGTERM');
63
+ setTimeout(() => {
64
+ if (!child.killed) {
65
+ console.warn(`[runner] Job #${job.id} did not exit after SIGTERM grace period, sending SIGKILL`);
66
+ child.kill('SIGKILL');
67
+ }
68
+ }, SIGTERM_GRACE_MS);
69
+ }, limits.timeout_ms);
70
+ child.on('close', (code) => {
71
+ clearTimeout(timeout);
72
+ this.active.delete(job.id);
73
+ const duration_ms = Date.now() - startTime;
74
+ const rawOut = Buffer.concat(stdout).toString('utf-8');
75
+ const rawErr = Buffer.concat(stderr).toString('utf-8');
76
+ // Parse JSON output from claude --output-format json
77
+ // Claude wraps output in { result: "...", ... }
78
+ let output = rawOut;
79
+ try {
80
+ const envelope = JSON.parse(rawOut);
81
+ const text = envelope.result ?? '';
82
+ // Try to parse the result text as structured JSON from the prompt
83
+ try {
84
+ output = JSON.stringify(JSON.parse(text));
85
+ }
86
+ catch {
87
+ output = text.trim() || rawOut;
88
+ }
89
+ }
90
+ catch {
91
+ // Claude output wasn't JSON — use raw stdout
92
+ }
93
+ if (rawErr) {
94
+ console.error(`[runner] Job #${job.id} stderr: ${rawErr.slice(0, 500)}`);
95
+ }
96
+ // On failure, include stderr in output so the error is visible
97
+ const finalOutput = code === 0
98
+ ? output
99
+ : [output, rawErr].filter(Boolean).join('\n').trim() || `claude exited with code ${code}`;
100
+ resolve({
101
+ success: code === 0,
102
+ output: finalOutput,
103
+ exit_code: code ?? 1,
104
+ duration_ms,
105
+ });
106
+ });
107
+ });
108
+ }
109
+ /**
110
+ * Kill an active job's process. Used during graceful shutdown.
111
+ */
112
+ kill(jobId) {
113
+ const child = this.active.get(jobId);
114
+ if (child) {
115
+ child.kill('SIGTERM');
116
+ setTimeout(() => {
117
+ if (!child.killed)
118
+ child.kill('SIGKILL');
119
+ }, SIGTERM_GRACE_MS);
120
+ }
121
+ }
122
+ /**
123
+ * Kill all active processes. Used during daemon shutdown.
124
+ */
125
+ killAll() {
126
+ for (const [jobId] of this.active) {
127
+ this.kill(jobId);
128
+ }
129
+ }
130
+ get activeCount() {
131
+ return this.active.size;
132
+ }
133
+ // ── Private ────────────────────────────────────────────────────────────
134
+ buildPrompt(job) {
135
+ const payload = JSON.parse(job.payload);
136
+ switch (job.type) {
137
+ case 'ingest':
138
+ return buildIngestPrompt(payload);
139
+ case 'query':
140
+ return buildQueryPrompt(payload);
141
+ case 'lint':
142
+ return buildLintPrompt();
143
+ default:
144
+ return `Unknown job type: ${job.type}`;
145
+ }
146
+ }
147
+ resolveTools(wikiId) {
148
+ const tools = [...BASE_ALLOWED_TOOLS];
149
+ const allowedPath = join(this.wikisDir, wikiId, '.tools', 'allowed-tools.txt');
150
+ if (existsSync(allowedPath)) {
151
+ const extra = readFileSync(allowedPath, 'utf-8')
152
+ .split('\n')
153
+ .map(l => l.trim())
154
+ .filter(l => l && !l.startsWith('#') && ALLOWED_TOOLS_WHITELIST.has(l));
155
+ tools.push(...extra);
156
+ }
157
+ return [...new Set(tools)]; // dedupe
158
+ }
159
+ buildClaudeArgs(prompt, model, maxTurns, tools, wikiId) {
160
+ const toolStr = tools.join(',');
161
+ const args = [
162
+ 'claude',
163
+ '-p', prompt,
164
+ '--model', model,
165
+ '--max-turns', String(maxTurns),
166
+ '--output-format', 'json',
167
+ '--tools', toolStr, // restrict which tools EXIST (hard boundary)
168
+ '--allowedTools', toolStr, // pre-approve those tools (no interactive prompts)
169
+ ];
170
+ // Append wiki system prompt
171
+ const systemPrompt = getWikiSystemPrompt();
172
+ args.push('--append-system-prompt', systemPrompt);
173
+ // MCP config (if the wiki has one)
174
+ const mcpPath = join(this.wikisDir, wikiId, '.tools', 'mcp.json');
175
+ if (existsSync(mcpPath)) {
176
+ // Inside the namespace, this will be at /workspace/.tools/mcp.json
177
+ args.push('--mcp-config', `${WORKSPACE_MOUNT}/.tools/mcp.json`);
178
+ }
179
+ return args;
180
+ }
181
+ }
182
+ /**
183
+ * Filter process.env to only include safe environment variables.
184
+ * Exclude anything that could leak host info or interfere with Claude.
185
+ */
186
+ function filterEnv(env) {
187
+ const safe = {};
188
+ const keep = ['PATH', 'LANG', 'LC_ALL', 'TERM', 'NODE_ENV'];
189
+ for (const key of keep) {
190
+ if (env[key])
191
+ safe[key] = env[key];
192
+ }
193
+ return safe;
194
+ }
195
+ //# sourceMappingURL=runner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runner.js","sourceRoot":"","sources":["../../src/daemon/runner.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,38 @@
1
+ export declare class WikiScaffold {
2
+ private wikisDir;
3
+ constructor(wikisDir?: string);
4
+ /**
5
+ * Create the full directory structure and default files for a new wiki.
6
+ */
7
+ create(wikiId: string): void;
8
+ /**
9
+ * Remove a wiki's directory tree.
10
+ */
11
+ destroy(wikiId: string, keepData?: boolean): void;
12
+ /**
13
+ * Get the host filesystem path for a wiki's root directory.
14
+ */
15
+ wikiDir(wikiId: string): string;
16
+ /**
17
+ * Write a file into the wiki's wiki/raw/ directory.
18
+ * Prefixes with a timestamp to avoid collisions.
19
+ * Returns the stored filename (relative to wiki/raw/).
20
+ */
21
+ writeRawFile(wikiId: string, filename: string, content: Buffer): string;
22
+ /**
23
+ * Read the wiki's .claude.md content.
24
+ */
25
+ readClaudeMd(wikiId: string): string;
26
+ /**
27
+ * Write the allowed-tools.txt file for a wiki.
28
+ */
29
+ writeAllowedTools(wikiId: string, tools: string[]): void;
30
+ /**
31
+ * Read the current allowed-tools.txt for a wiki.
32
+ */
33
+ readAllowedTools(wikiId: string): string[];
34
+ /**
35
+ * Check if a wiki directory exists on disk.
36
+ */
37
+ exists(wikiId: string): boolean;
38
+ }
@@ -0,0 +1,141 @@
1
+ import { mkdirSync, writeFileSync, rmSync, readFileSync, existsSync } from 'node:fs';
2
+ import { join, basename } from 'node:path';
3
+ import { WIKIS_DIR } from '../lib/constants.js';
4
+ const DEFAULT_CLAUDE_MD = `# Wiki Agent — Conventions
5
+
6
+ This file is auto-discovered by Claude Code and extends the base system prompt.
7
+ Use it to define wiki-specific conventions, domain vocabulary, and wiki structure.
8
+
9
+ The base system prompt handles core wiki behavior (index, schema, connections, log).
10
+ This file is for YOUR customizations on top of that.
11
+
12
+ ## Domain
13
+
14
+ _(Describe what this knowledge base is about)_
15
+
16
+ ## Conventions
17
+
18
+ _(Add wiki-specific filing rules, vocabulary, categories, and preferences here)_
19
+
20
+ ## Things to ignore
21
+
22
+ _(Topics, patterns, or noise that should be skipped during ingestion)_
23
+ `;
24
+ // Schema is intentionally minimal — the LLM will create a proper one on first ingest
25
+ // when it sees the actual content and can establish meaningful conventions.
26
+ const DEFAULT_SCHEMA_MD = `# Schema
27
+
28
+ This file documents the conventions for this knowledge base.
29
+ It will be created and maintained by the wiki agent as content is ingested.
30
+
31
+ _(This is a new knowledge base. The schema will be populated on first ingest.)_
32
+ `;
33
+ const DEFAULT_INDEX_MD = `# Index
34
+
35
+ One-line summary of every wiki page, organized by category.
36
+ A reader should understand the shape of the knowledge base from this file alone.
37
+
38
+ _(No pages yet. The index will be populated as content is ingested.)_
39
+ `;
40
+ const DEFAULT_LOG_MD = `# Activity Log
41
+
42
+ Chronological record of knowledge base activity.
43
+
44
+ ---
45
+ `;
46
+ export class WikiScaffold {
47
+ wikisDir;
48
+ constructor(wikisDir = WIKIS_DIR) {
49
+ this.wikisDir = wikisDir;
50
+ }
51
+ /**
52
+ * Create the full directory structure and default files for a new wiki.
53
+ */
54
+ create(wikiId) {
55
+ const base = this.wikiDir(wikiId);
56
+ // Directories
57
+ mkdirSync(join(base, '.claude'), { recursive: true, mode: 0o700 });
58
+ mkdirSync(join(base, '.tools'), { recursive: true });
59
+ mkdirSync(join(base, 'wiki', 'raw'), { recursive: true });
60
+ // Default files
61
+ writeFileSync(join(base, '.claude.md'), DEFAULT_CLAUDE_MD);
62
+ writeFileSync(join(base, 'wiki', '_schema.md'), DEFAULT_SCHEMA_MD);
63
+ writeFileSync(join(base, 'wiki', '_index.md'), DEFAULT_INDEX_MD);
64
+ writeFileSync(join(base, 'wiki', '_log.md'), DEFAULT_LOG_MD);
65
+ }
66
+ /**
67
+ * Remove a wiki's directory tree.
68
+ */
69
+ destroy(wikiId, keepData = false) {
70
+ if (keepData)
71
+ return;
72
+ const dir = this.wikiDir(wikiId);
73
+ if (existsSync(dir)) {
74
+ rmSync(dir, { recursive: true, force: true });
75
+ }
76
+ }
77
+ /**
78
+ * Get the host filesystem path for a wiki's root directory.
79
+ */
80
+ wikiDir(wikiId) {
81
+ return join(this.wikisDir, wikiId);
82
+ }
83
+ /**
84
+ * Write a file into the wiki's wiki/raw/ directory.
85
+ * Prefixes with a timestamp to avoid collisions.
86
+ * Returns the stored filename (relative to wiki/raw/).
87
+ */
88
+ writeRawFile(wikiId, filename, content) {
89
+ const rawDir = join(this.wikiDir(wikiId), 'wiki', 'raw');
90
+ mkdirSync(rawDir, { recursive: true });
91
+ const ts = new Date().toISOString().replace(/[-:]/g, '').replace(/\.\d+Z$/, '');
92
+ const stored = `${ts}-${sanitizeFilename(basename(filename))}`;
93
+ writeFileSync(join(rawDir, stored), content);
94
+ return stored;
95
+ }
96
+ /**
97
+ * Read the wiki's .claude.md content.
98
+ */
99
+ readClaudeMd(wikiId) {
100
+ const p = join(this.wikiDir(wikiId), '.claude.md');
101
+ if (!existsSync(p))
102
+ return '';
103
+ return readFileSync(p, 'utf-8');
104
+ }
105
+ /**
106
+ * Write the allowed-tools.txt file for a wiki.
107
+ */
108
+ writeAllowedTools(wikiId, tools) {
109
+ const toolsDir = join(this.wikiDir(wikiId), '.tools');
110
+ mkdirSync(toolsDir, { recursive: true });
111
+ const content = tools.length > 0
112
+ ? tools.join('\n') + '\n'
113
+ : '';
114
+ writeFileSync(join(toolsDir, 'allowed-tools.txt'), content);
115
+ }
116
+ /**
117
+ * Read the current allowed-tools.txt for a wiki.
118
+ */
119
+ readAllowedTools(wikiId) {
120
+ const p = join(this.wikiDir(wikiId), '.tools', 'allowed-tools.txt');
121
+ if (!existsSync(p))
122
+ return [];
123
+ return readFileSync(p, 'utf-8')
124
+ .split('\n')
125
+ .map(l => l.trim())
126
+ .filter(l => l && !l.startsWith('#'));
127
+ }
128
+ /**
129
+ * Check if a wiki directory exists on disk.
130
+ */
131
+ exists(wikiId) {
132
+ return existsSync(this.wikiDir(wikiId));
133
+ }
134
+ }
135
+ /**
136
+ * Strip dangerous characters from filenames while preserving the extension.
137
+ */
138
+ function sanitizeFilename(name) {
139
+ return name.replace(/[^a-zA-Z0-9._-]/g, '_');
140
+ }
141
+ //# sourceMappingURL=scaffold.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scaffold.js","sourceRoot":"","sources":["../../src/daemon/scaffold.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrF,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAC3C,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAEhD,MAAM,iBAAiB,GAAG;;;;;;;;;;;;;;;;;;;CAmBzB,CAAC;AAEF,qFAAqF;AACrF,4EAA4E;AAC5E,MAAM,iBAAiB,GAAG;;;;;;CAMzB,CAAC;AAEF,MAAM,gBAAgB,GAAG;;;;;;CAMxB,CAAC;AAEF,MAAM,cAAc,GAAG;;;;;CAKtB,CAAC;AAEF,MAAM,OAAO,YAAY;IACH;IAApB,YAAoB,WAAmB,SAAS;QAA5B,aAAQ,GAAR,QAAQ,CAAoB;IAAG,CAAC;IAEpD;;OAEG;IACH,MAAM,CAAC,MAAc;QACnB,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;QAElC,cAAc;QACd,SAAS,CAAC,IAAI,CAAC,IAAI,EAAE,SAAS,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;QACnE,SAAS,CAAC,IAAI,CAAC,IAAI,EAAE,QAAQ,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACrD,SAAS,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAE1D,gBAAgB;QAChB,aAAa,CAAC,IAAI,CAAC,IAAI,EAAE,YAAY,CAAC,EAAE,iBAAiB,CAAC,CAAC;QAC3D,aAAa,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,iBAAiB,CAAC,CAAC;QACnE,aAAa,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,gBAAgB,CAAC,CAAC;QACjE,aAAa,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,cAAc,CAAC,CAAC;IAC/D,CAAC;IAED;;OAEG;IACH,OAAO,CAAC,MAAc,EAAE,WAAoB,KAAK;QAC/C,IAAI,QAAQ;YAAE,OAAO;QACrB,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;QACjC,IAAI,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;YACpB,MAAM,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QAChD,CAAC;IACH,CAAC;IAED;;OAEG;IACH,OAAO,CAAC,MAAc;QACpB,OAAO,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;IACrC,CAAC;IAED;;;;OAIG;IACH,YAAY,CAAC,MAAc,EAAE,QAAgB,EAAE,OAAe;QAC5D,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,CAAC;QACzD,SAAS,CAAC,MAAM,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAEvC,MAAM,EAAE,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC;QAChF,MAAM,MAAM,GAAG,GAAG,EAAE,IAAI,gBAAgB,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,EAAE,CAAC;QAC/D,aAAa,CAAC,IAAI,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,CAAC,CAAC;QAC7C,OAAO,MAAM,CAAC;IAChB,CAAC;IAED;;OAEG;IACH,YAAY,CAAC,MAAc;QACzB,MAAM,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,YAAY,CAAC,CAAC;QACnD,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC;YAAE,OAAO,EAAE,CAAC;QAC9B,OAAO,YAAY,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC;IAClC,CAAC;IAED;;OAEG;IACH,iBAAiB,CAAC,MAAc,EAAE,KAAe;QAC/C,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,QAAQ,CAAC,CAAC;QACtD,SAAS,CAAC,QAAQ,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACzC,MAAM,OAAO,GAAG,KAAK,CAAC,MAAM,GAAG,CAAC;YAC9B,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI;YACzB,CAAC,CAAC,EAAE,CAAC;QACP,aAAa,CAAC,IAAI,CAAC,QAAQ,EAAE,mBAAmB,CAAC,EAAE,OAAO,CAAC,CAAC;IAC9D,CAAC;IAED;;OAEG;IACH,gBAAgB,CAAC,MAAc;QAC7B,MAAM,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,QAAQ,EAAE,mBAAmB,CAAC,CAAC;QACpE,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC;YAAE,OAAO,EAAE,CAAC;QAC9B,OAAO,YAAY,CAAC,CAAC,EAAE,OAAO,CAAC;aAC5B,KAAK,CAAC,IAAI,CAAC;aACX,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;aAClB,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC;IAC1C,CAAC;IAED;;OAEG;IACH,MAAM,CAAC,MAAc;QACnB,OAAO,UAAU,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC;IAC1C,CAAC;CACF;AAED;;GAEG;AACH,SAAS,gBAAgB,CAAC,IAAY;IACpC,OAAO,IAAI,CAAC,OAAO,CAAC,kBAAkB,EAAE,GAAG,CAAC,CAAC;AAC/C,CAAC"}
@@ -0,0 +1,11 @@
1
+ import type { RouteHandler } from './routes.js';
2
+ export declare class DaemonServer {
3
+ private socketPath;
4
+ private routes;
5
+ private server;
6
+ private socketUids;
7
+ constructor(socketPath: string, routes: RouteHandler);
8
+ start(): Promise<void>;
9
+ stop(): Promise<void>;
10
+ private handleRequest;
11
+ }
@@ -0,0 +1,145 @@
1
+ import { createServer } from 'node:http';
2
+ import { existsSync, unlinkSync, chmodSync } from 'node:fs';
3
+ import { getPeerCred } from './peercred.js';
4
+ export class DaemonServer {
5
+ socketPath;
6
+ routes;
7
+ server;
8
+ socketUids = new WeakMap();
9
+ constructor(socketPath, routes) {
10
+ this.socketPath = socketPath;
11
+ this.routes = routes;
12
+ this.server = createServer((req, res) => {
13
+ this.handleRequest(req, res).catch(err => {
14
+ console.error('[server] Unhandled error:', err);
15
+ if (!res.headersSent) {
16
+ sendJson(res, 500, { ok: false, error: 'Internal server error' });
17
+ }
18
+ });
19
+ });
20
+ // Extract peer credentials at connection time (before any HTTP parsing).
21
+ // SO_PEERCRED is set by the kernel at connect() — it's immutable and unspoofable.
22
+ this.server.on('connection', (socket) => {
23
+ try {
24
+ const cred = getPeerCred(socket);
25
+ this.socketUids.set(socket, cred.uid);
26
+ }
27
+ catch (err) {
28
+ console.error('[server] Failed to get peer credentials:', err);
29
+ socket.destroy();
30
+ }
31
+ });
32
+ }
33
+ async start() {
34
+ // Remove stale socket file
35
+ if (existsSync(this.socketPath)) {
36
+ unlinkSync(this.socketPath);
37
+ }
38
+ return new Promise((resolve, reject) => {
39
+ this.server.on('error', reject);
40
+ this.server.listen(this.socketPath, () => {
41
+ chmodSync(this.socketPath, 0o666);
42
+ console.log(`[server] Listening on ${this.socketPath}`);
43
+ resolve();
44
+ });
45
+ });
46
+ }
47
+ async stop() {
48
+ return new Promise((resolve) => {
49
+ this.server.close(() => {
50
+ if (existsSync(this.socketPath)) {
51
+ try {
52
+ unlinkSync(this.socketPath);
53
+ }
54
+ catch { /* ignore */ }
55
+ }
56
+ resolve();
57
+ });
58
+ });
59
+ }
60
+ async handleRequest(req, res) {
61
+ const method = (req.method ?? 'GET').toUpperCase();
62
+ const url = req.url ?? '/';
63
+ // Parse body for POST/PUT
64
+ let body = undefined;
65
+ if (method === 'POST' || method === 'PUT') {
66
+ body = await readBody(req);
67
+ }
68
+ // Check for streaming (wait) mode on job submission
69
+ const urlObj = new URL(url, 'http://localhost');
70
+ const wait = urlObj.searchParams.get('wait') === 'true';
71
+ // Retrieve the caller's UID from the underlying socket
72
+ const socket = req.socket;
73
+ const callerUid = this.socketUids.get(socket);
74
+ if (callerUid === undefined) {
75
+ sendJson(res, 500, { ok: false, error: 'Could not determine caller identity' });
76
+ return;
77
+ }
78
+ try {
79
+ const result = await this.routes.handle(method, urlObj.pathname, body, {
80
+ wait,
81
+ res, // pass response for streaming login output
82
+ callerUid,
83
+ });
84
+ // If the route already handled the response (e.g., streaming), skip
85
+ if (res.writableEnded)
86
+ return;
87
+ sendJson(res, result.status, result.body);
88
+ }
89
+ catch (err) {
90
+ if (res.writableEnded)
91
+ return;
92
+ if (err && typeof err === 'object' && 'statusCode' in err) {
93
+ const memexErr = err;
94
+ sendJson(res, memexErr.statusCode, {
95
+ ok: false,
96
+ error: memexErr.message,
97
+ });
98
+ }
99
+ else {
100
+ console.error('[server] Error handling request:', err);
101
+ sendJson(res, 500, { ok: false, error: 'Internal server error' });
102
+ }
103
+ }
104
+ }
105
+ }
106
+ function sendJson(res, status, body) {
107
+ const json = JSON.stringify(body);
108
+ res.writeHead(status, {
109
+ 'Content-Type': 'application/json',
110
+ 'Content-Length': Buffer.byteLength(json),
111
+ });
112
+ res.end(json);
113
+ }
114
+ // 100 MB — enough for large PDFs/images encoded as base64
115
+ const MAX_BODY_BYTES = 100 * 1024 * 1024;
116
+ async function readBody(req) {
117
+ return new Promise((resolve, reject) => {
118
+ const chunks = [];
119
+ let totalSize = 0;
120
+ req.on('data', (chunk) => {
121
+ totalSize += chunk.length;
122
+ if (totalSize > MAX_BODY_BYTES) {
123
+ req.destroy();
124
+ reject(new Error(`Request body exceeds ${MAX_BODY_BYTES} bytes`));
125
+ return;
126
+ }
127
+ chunks.push(chunk);
128
+ });
129
+ req.on('end', () => {
130
+ const raw = Buffer.concat(chunks).toString('utf-8');
131
+ if (!raw) {
132
+ resolve(undefined);
133
+ return;
134
+ }
135
+ try {
136
+ resolve(JSON.parse(raw));
137
+ }
138
+ catch {
139
+ reject(new Error('Invalid JSON body'));
140
+ }
141
+ });
142
+ req.on('error', reject);
143
+ });
144
+ }
145
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/daemon/server.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export declare function startDaemon(): Promise<void>;
package/dist/daemon.js ADDED
@@ -0,0 +1,55 @@
1
+ import { mkdirSync } from 'node:fs';
2
+ import { Database } from './daemon/db.js';
3
+ import { NamespaceManager } from './daemon/namespace.js';
4
+ import { WikiScaffold } from './daemon/scaffold.js';
5
+ import { AuthManager } from './daemon/auth.js';
6
+ import { ClaudeRunner } from './daemon/runner.js';
7
+ import { QueueManager } from './daemon/queue.js';
8
+ import { RouteHandler } from './daemon/routes.js';
9
+ import { DaemonServer } from './daemon/server.js';
10
+ import { DATA_DIR, RUN_DIR, SOCKET_PATH, DB_PATH, WIKIS_DIR, AUTO_LINT_INTERVAL, } from './lib/constants.js';
11
+ export async function startDaemon() {
12
+ console.log('[memex] Starting daemon...');
13
+ // ── Ensure directories ─────────────────────────────────────────────────
14
+ for (const dir of [DATA_DIR, WIKIS_DIR, RUN_DIR]) {
15
+ mkdirSync(dir, { recursive: true });
16
+ }
17
+ // ── Database ───────────────────────────────────────────────────────────
18
+ const db = new Database(DB_PATH);
19
+ db.initialize();
20
+ const staleCount = db.resetStaleJobs();
21
+ if (staleCount > 0) {
22
+ console.log(`[memex] Reset ${staleCount} stale job(s) from previous run`);
23
+ }
24
+ // ── Namespace manager ──────────────────────────────────────────────────
25
+ const namespace = new NamespaceManager(WIKIS_DIR);
26
+ namespace.checkCapabilities();
27
+ namespace.ensureDirectories();
28
+ // ── Components ─────────────────────────────────────────────────────────
29
+ const scaffold = new WikiScaffold(WIKIS_DIR);
30
+ const auth = new AuthManager(WIKIS_DIR, process.env['ANTHROPIC_API_KEY']);
31
+ const runner = new ClaudeRunner(namespace, auth, db, WIKIS_DIR);
32
+ const queue = new QueueManager(db, runner, AUTO_LINT_INTERVAL);
33
+ const routes = new RouteHandler(db, scaffold, namespace, queue, auth);
34
+ const server = new DaemonServer(SOCKET_PATH, routes);
35
+ // ── Start ──────────────────────────────────────────────────────────────
36
+ await server.start();
37
+ queue.start();
38
+ const wikiCount = db.listWikis().length;
39
+ console.log(`[memex] Daemon ready (PID ${process.pid})`);
40
+ console.log(`[memex] Socket: ${SOCKET_PATH}`);
41
+ console.log(`[memex] Data: ${DATA_DIR}`);
42
+ console.log(`[memex] Wikis: ${wikiCount}`);
43
+ // ── Graceful shutdown ──────────────────────────────────────────────────
44
+ const shutdown = async (signal) => {
45
+ console.log(`\n[memex] Received ${signal}, shutting down...`);
46
+ await queue.stop();
47
+ await server.stop();
48
+ db.close();
49
+ console.log('[memex] Shutdown complete.');
50
+ process.exit(0);
51
+ };
52
+ process.on('SIGTERM', () => shutdown('SIGTERM'));
53
+ process.on('SIGINT', () => shutdown('SIGINT'));
54
+ }
55
+ //# sourceMappingURL=daemon.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"daemon.js","sourceRoot":"","sources":["../src/daemon.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};