@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.
- package/LICENSE +21 -0
- package/README.md +291 -0
- package/dist/cli/client.d.ts +35 -0
- package/dist/cli/client.js +183 -0
- package/dist/cli/client.js.map +1 -0
- package/dist/cli/commands/chown.d.ts +2 -0
- package/dist/cli/commands/chown.js +22 -0
- package/dist/cli/commands/chown.js.map +1 -0
- package/dist/cli/commands/config.d.ts +2 -0
- package/dist/cli/commands/config.js +132 -0
- package/dist/cli/commands/config.js.map +1 -0
- package/dist/cli/commands/create.d.ts +2 -0
- package/dist/cli/commands/create.js +21 -0
- package/dist/cli/commands/create.js.map +1 -0
- package/dist/cli/commands/destroy.d.ts +2 -0
- package/dist/cli/commands/destroy.js +34 -0
- package/dist/cli/commands/destroy.js.map +1 -0
- package/dist/cli/commands/ingest.d.ts +2 -0
- package/dist/cli/commands/ingest.js +74 -0
- package/dist/cli/commands/ingest.js.map +1 -0
- package/dist/cli/commands/lint.d.ts +2 -0
- package/dist/cli/commands/lint.js +46 -0
- package/dist/cli/commands/lint.js.map +1 -0
- package/dist/cli/commands/list.d.ts +2 -0
- package/dist/cli/commands/list.js +28 -0
- package/dist/cli/commands/list.js.map +1 -0
- package/dist/cli/commands/login.d.ts +2 -0
- package/dist/cli/commands/login.js +51 -0
- package/dist/cli/commands/login.js.map +1 -0
- package/dist/cli/commands/logs.d.ts +2 -0
- package/dist/cli/commands/logs.js +26 -0
- package/dist/cli/commands/logs.js.map +1 -0
- package/dist/cli/commands/query.d.ts +2 -0
- package/dist/cli/commands/query.js +48 -0
- package/dist/cli/commands/query.js.map +1 -0
- package/dist/cli/commands/serve.d.ts +2 -0
- package/dist/cli/commands/serve.js +14 -0
- package/dist/cli/commands/serve.js.map +1 -0
- package/dist/cli/commands/status.d.ts +2 -0
- package/dist/cli/commands/status.js +66 -0
- package/dist/cli/commands/status.js.map +1 -0
- package/dist/daemon/auth.d.ts +31 -0
- package/dist/daemon/auth.js +84 -0
- package/dist/daemon/auth.js.map +1 -0
- package/dist/daemon/db.d.ts +36 -0
- package/dist/daemon/db.js +181 -0
- package/dist/daemon/db.js.map +1 -0
- package/dist/daemon/namespace.d.ts +34 -0
- package/dist/daemon/namespace.js +74 -0
- package/dist/daemon/namespace.js.map +1 -0
- package/dist/daemon/peercred.d.ts +15 -0
- package/dist/daemon/peercred.js +19 -0
- package/dist/daemon/peercred.js.map +1 -0
- package/dist/daemon/queue.d.ts +26 -0
- package/dist/daemon/queue.js +126 -0
- package/dist/daemon/queue.js.map +1 -0
- package/dist/daemon/routes.d.ts +38 -0
- package/dist/daemon/routes.js +258 -0
- package/dist/daemon/routes.js.map +1 -0
- package/dist/daemon/runner.d.ts +25 -0
- package/dist/daemon/runner.js +195 -0
- package/dist/daemon/runner.js.map +1 -0
- package/dist/daemon/scaffold.d.ts +38 -0
- package/dist/daemon/scaffold.js +141 -0
- package/dist/daemon/scaffold.js.map +1 -0
- package/dist/daemon/server.d.ts +11 -0
- package/dist/daemon/server.js +145 -0
- package/dist/daemon/server.js.map +1 -0
- package/dist/daemon.d.ts +1 -0
- package/dist/daemon.js +55 -0
- package/dist/daemon.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +36 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/constants.d.ts +17 -0
- package/dist/lib/constants.js +30 -0
- package/dist/lib/constants.js.map +1 -0
- package/dist/lib/errors.d.ts +32 -0
- package/dist/lib/errors.js +64 -0
- package/dist/lib/errors.js.map +1 -0
- package/dist/lib/prompts/ingest.d.ts +9 -0
- package/dist/lib/prompts/ingest.js +48 -0
- package/dist/lib/prompts/ingest.js.map +1 -0
- package/dist/lib/prompts/lint.d.ts +8 -0
- package/dist/lib/prompts/lint.js +62 -0
- package/dist/lib/prompts/lint.js.map +1 -0
- package/dist/lib/prompts/query.d.ts +8 -0
- package/dist/lib/prompts/query.js +37 -0
- package/dist/lib/prompts/query.js.map +1 -0
- package/dist/lib/prompts/wiki.d.ts +11 -0
- package/dist/lib/prompts/wiki.js +112 -0
- package/dist/lib/prompts/wiki.js.map +1 -0
- package/dist/lib/types.d.ts +76 -0
- package/dist/lib/types.js +3 -0
- package/dist/lib/types.js.map +1 -0
- package/dist/standalone/memex.mjs +2313 -0
- package/dist/standalone/memex.mjs.map +7 -0
- package/package.json +54 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Inder Singh
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
# Memex
|
|
2
|
+
|
|
3
|
+
Isolated, queued `claude -p` runtime for persistent knowledge bases.
|
|
4
|
+
|
|
5
|
+
Each wiki gets its own filesystem namespace, job queue, and customizable Claude configuration. A privileged daemon handles namespace isolation and process spawning; unprivileged users interact through the `memex` CLI.
|
|
6
|
+
|
|
7
|
+
## Why
|
|
8
|
+
|
|
9
|
+
`claude -p` gives you something the API can't: Claude Code's full file tool suite (Read, Write, Edit, Glob, Grep) running natively against a real filesystem. The wiki IS the filesystem, and Claude operates on it with the same tools a human developer would use.
|
|
10
|
+
|
|
11
|
+
The tradeoff is that `claude -p` is single-threaded and process-heavy. Memex compensates with per-wiki queuing and namespace isolation rather than fighting the CLI's concurrency model.
|
|
12
|
+
|
|
13
|
+
## Quick start
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
# Install
|
|
17
|
+
npm install -g memex
|
|
18
|
+
|
|
19
|
+
# Start the daemon (requires CAP_SYS_ADMIN for namespace isolation)
|
|
20
|
+
sudo memex serve
|
|
21
|
+
|
|
22
|
+
# Create a wiki
|
|
23
|
+
memex create my-wiki --name "My Knowledge Base"
|
|
24
|
+
|
|
25
|
+
# Authenticate Claude for this wiki
|
|
26
|
+
memex login my-wiki
|
|
27
|
+
|
|
28
|
+
# Ingest a document
|
|
29
|
+
memex ingest my-wiki notes.md report.pdf
|
|
30
|
+
|
|
31
|
+
# Ask a question
|
|
32
|
+
memex query my-wiki "What are the key themes across these documents?"
|
|
33
|
+
|
|
34
|
+
# Run a health check
|
|
35
|
+
memex lint my-wiki
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## How it works
|
|
39
|
+
|
|
40
|
+
```
|
|
41
|
+
┌──────────────────────────────────────────────────────────────┐
|
|
42
|
+
│ CLI (unprivileged) │
|
|
43
|
+
│ │
|
|
44
|
+
│ memex ingest acme notes.md │
|
|
45
|
+
│ │ │
|
|
46
|
+
│ ▼ │
|
|
47
|
+
│ /run/memex/memex.sock ───────────────────────────────── │
|
|
48
|
+
└──────────────────────────┼───────────────────────────────────┘
|
|
49
|
+
│
|
|
50
|
+
▼
|
|
51
|
+
┌──────────────────────────────────────────────────────────────┐
|
|
52
|
+
│ DAEMON (memex serve) privileged │
|
|
53
|
+
│ │
|
|
54
|
+
│ Per-Wiki Job Queues Wiki Registry (SQLite) │
|
|
55
|
+
│ ┌─────────────────┐ ┌─────────────────────┐ │
|
|
56
|
+
│ │ acme: [▶ingest] │ │ wikis, jobs, audit │ │
|
|
57
|
+
│ │ beta: (idle) │ └─────────────────────┘ │
|
|
58
|
+
│ └────────┬────────┘ │
|
|
59
|
+
│ │ │
|
|
60
|
+
│ ▼ │
|
|
61
|
+
│ unshare -m -- mount --bind .../wikis/acme /workspace \ │
|
|
62
|
+
│ claude -p "..." --tools Read,Write,Edit,Glob,Grep │
|
|
63
|
+
│ │
|
|
64
|
+
│ ┌────────────────────────────────────────────────────────┐ │
|
|
65
|
+
│ │ MOUNT NAMESPACE — what Claude sees │ │
|
|
66
|
+
│ │ /workspace/ │ │
|
|
67
|
+
│ │ .claude.md wiki/_schema.md │ │
|
|
68
|
+
│ │ .claude/ wiki/_index.md │ │
|
|
69
|
+
│ │ .tools/ wiki/_log.md │ │
|
|
70
|
+
│ │ wiki/ wiki/raw/ │ │
|
|
71
|
+
│ │ │ │
|
|
72
|
+
│ │ No /home. No /etc. No other wikis. │ │
|
|
73
|
+
│ └────────────────────────────────────────────────────────┘ │
|
|
74
|
+
└──────────────────────────────────────────────────────────────┘
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
**Serialize, don't parallelize.** Each wiki's queue processes jobs one at a time. This prevents concurrent writes to the same wiki files. Independent wikis run in parallel.
|
|
78
|
+
|
|
79
|
+
**Isolation through namespaces, not containers.** Mount namespaces give you filesystem isolation without Docker's overhead. Claude cannot see the host filesystem, other wikis, or anything outside `/workspace`.
|
|
80
|
+
|
|
81
|
+
## Commands
|
|
82
|
+
|
|
83
|
+
| Command | Description |
|
|
84
|
+
|---------|-------------|
|
|
85
|
+
| `memex serve` | Start the daemon |
|
|
86
|
+
| `memex create <wiki> [--name "..."]` | Create a new wiki |
|
|
87
|
+
| `memex destroy <wiki> [--keep-data]` | Destroy a wiki |
|
|
88
|
+
| `memex config <wiki> --edit` | Open `.claude.md` in `$EDITOR` |
|
|
89
|
+
| `memex config <wiki> --set-key` | Set API key for this wiki |
|
|
90
|
+
| `memex config <wiki> --model opus` | Set default model |
|
|
91
|
+
| `memex login <wiki>` | Authenticate Claude (OAuth) |
|
|
92
|
+
| `memex ingest <wiki> <files...>` | Ingest source files into the wiki |
|
|
93
|
+
| `memex query <wiki> "question"` | Ask a question against the wiki |
|
|
94
|
+
| `memex lint <wiki>` | Run wiki health check |
|
|
95
|
+
| `memex logs <wiki> [--tail N]` | View audit log |
|
|
96
|
+
| `memex list` | List all wikis |
|
|
97
|
+
| `memex status <wiki> [jobId]` | Check job status |
|
|
98
|
+
|
|
99
|
+
Most commands block until the job completes. Pass `--async` to get a job ID and check status later.
|
|
100
|
+
|
|
101
|
+
## Architecture
|
|
102
|
+
|
|
103
|
+
### Per-wiki workspace
|
|
104
|
+
|
|
105
|
+
Each wiki gets an isolated directory:
|
|
106
|
+
|
|
107
|
+
```
|
|
108
|
+
/var/lib/memex/wikis/{wikiId}/
|
|
109
|
+
.claude.md # Wiki-specific Claude conventions (user-editable)
|
|
110
|
+
.claude/ # Claude credentials (per-wiki, isolated)
|
|
111
|
+
.tools/
|
|
112
|
+
mcp.json # MCP server configuration (optional)
|
|
113
|
+
allowed-tools.txt # Extra tools beyond the base set (optional)
|
|
114
|
+
wiki/
|
|
115
|
+
_schema.md # Filing conventions — the LLM's institutional memory
|
|
116
|
+
_index.md # Table of contents — one-line summary of every page
|
|
117
|
+
_log.md # Chronological activity log
|
|
118
|
+
raw/ # Immutable source documents (never modified by Claude)
|
|
119
|
+
themes/ # (example — categories emerge from content)
|
|
120
|
+
customers/
|
|
121
|
+
...
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### Three layers
|
|
125
|
+
|
|
126
|
+
1. **Raw sources** (`wiki/raw/`) — Immutable. Claude reads from them but never modifies them. Your source of truth.
|
|
127
|
+
2. **The wiki** (`wiki/`) — LLM-generated markdown. Summaries, entity pages, concept pages, cross-references. Claude owns this layer entirely.
|
|
128
|
+
3. **The schema** (`_schema.md`, `.claude.md`) — How the wiki is structured, what conventions to follow. Co-evolved by user and LLM.
|
|
129
|
+
|
|
130
|
+
### Job types
|
|
131
|
+
|
|
132
|
+
| Type | What it does | Max turns | Timeout |
|
|
133
|
+
|------|-------------|-----------|---------|
|
|
134
|
+
| `ingest` | Process raw sources into wiki pages | 25 | 5 min |
|
|
135
|
+
| `query` | Search wiki and answer a question | 15 | 2 min |
|
|
136
|
+
| `lint` | Health check — find and fix issues | 30 | 10 min |
|
|
137
|
+
|
|
138
|
+
### Auto-lint
|
|
139
|
+
|
|
140
|
+
After every 10 ingests (configurable), the daemon automatically queues a lint job. This keeps the wiki healthy as it grows — finding contradictions, orphan pages, missing cross-references, and index drift.
|
|
141
|
+
|
|
142
|
+
## Security
|
|
143
|
+
|
|
144
|
+
**Namespace isolation** prevents Claude from accessing files outside the wiki's directory. This is the primary security boundary.
|
|
145
|
+
|
|
146
|
+
**Tool restriction** — Only `Read`, `Write`, `Edit`, `Glob`, `Grep` are available by default. No `Bash` (shell escape), no `WebSearch`/`WebFetch` (external calls), no `Agent` (sub-agents). Users can opt into higher-risk tools per-wiki via `.tools/allowed-tools.txt`.
|
|
147
|
+
|
|
148
|
+
**Per-wiki credentials** — Each wiki gets its own Claude authentication. Credentials are stored in the wiki's `.claude/` directory, isolated by the mount namespace.
|
|
149
|
+
|
|
150
|
+
**Audit log** — Every operation is recorded with the wiki, action, and detail.
|
|
151
|
+
|
|
152
|
+
## Credential setup
|
|
153
|
+
|
|
154
|
+
Each wiki needs Claude credentials. Three options:
|
|
155
|
+
|
|
156
|
+
**Copy existing OAuth credentials (recommended):**
|
|
157
|
+
```bash
|
|
158
|
+
# Uses ~/.claude/.credentials.json by default
|
|
159
|
+
memex login my-wiki
|
|
160
|
+
|
|
161
|
+
# Or specify a path
|
|
162
|
+
memex login my-wiki --credentials /path/to/.credentials.json
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
If you've already run `claude auth login` on this machine, your credentials are at `~/.claude/.credentials.json`. This copies them into the wiki's isolated config directory.
|
|
166
|
+
|
|
167
|
+
**API key:**
|
|
168
|
+
```bash
|
|
169
|
+
memex login my-wiki --api-key sk-ant-...
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
**Global fallback:**
|
|
173
|
+
```bash
|
|
174
|
+
# Set on the daemon's environment (e.g. in the systemd unit)
|
|
175
|
+
Environment=ANTHROPIC_API_KEY=sk-ant-...
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
Credentials are checked in order: per-wiki API key > per-wiki OAuth > global `ANTHROPIC_API_KEY` env var on the daemon.
|
|
179
|
+
|
|
180
|
+
## Customization
|
|
181
|
+
|
|
182
|
+
### `.claude.md`
|
|
183
|
+
|
|
184
|
+
The most important customization point. This file is auto-discovered by Claude Code and extends the base system prompt. Use it to define:
|
|
185
|
+
|
|
186
|
+
- Domain vocabulary
|
|
187
|
+
- Filing conventions specific to your wiki
|
|
188
|
+
- Categories and page structures
|
|
189
|
+
- What to ignore or deprioritize
|
|
190
|
+
- How to handle specific types of content
|
|
191
|
+
|
|
192
|
+
```bash
|
|
193
|
+
memex config my-wiki --edit
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
### `.tools/mcp.json`
|
|
197
|
+
|
|
198
|
+
Add external data sources via MCP servers:
|
|
199
|
+
|
|
200
|
+
```json
|
|
201
|
+
{
|
|
202
|
+
"mcpServers": {
|
|
203
|
+
"notion": {
|
|
204
|
+
"command": "npx",
|
|
205
|
+
"args": ["-y", "@notionhq/mcp-server"],
|
|
206
|
+
"env": { "NOTION_API_KEY": "secret_..." }
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
### `.tools/allowed-tools.txt`
|
|
213
|
+
|
|
214
|
+
Extend the tool set beyond the safe defaults:
|
|
215
|
+
|
|
216
|
+
```
|
|
217
|
+
Bash
|
|
218
|
+
WebSearch
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
Use with caution — `Bash` gives Claude shell access inside the namespace.
|
|
222
|
+
|
|
223
|
+
## Deployment
|
|
224
|
+
|
|
225
|
+
### systemd (recommended)
|
|
226
|
+
|
|
227
|
+
```bash
|
|
228
|
+
sudo cp systemd/memex.service /etc/systemd/system/
|
|
229
|
+
sudo systemctl enable --now memex
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
The unit file grants `CAP_SYS_ADMIN` via `AmbientCapabilities` and creates the required directories automatically.
|
|
233
|
+
|
|
234
|
+
```bash
|
|
235
|
+
# Lifecycle
|
|
236
|
+
systemctl start memex # Start the daemon
|
|
237
|
+
systemctl stop memex # Graceful shutdown (finishes current job)
|
|
238
|
+
systemctl restart memex # Stop + start
|
|
239
|
+
journalctl -u memex -f # Tail logs
|
|
240
|
+
|
|
241
|
+
# Status
|
|
242
|
+
systemctl status memex # Process state, recent log lines
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
### Why `memex serve` blocks
|
|
246
|
+
|
|
247
|
+
`memex serve` runs in the foreground deliberately. This is the standard pattern for systemd-managed daemons:
|
|
248
|
+
|
|
249
|
+
- **systemd owns the lifecycle** — backgrounding, restarts, boot ordering, and capability grants are its job, not the application's.
|
|
250
|
+
- **Logging is automatic** — stdout/stderr go straight to `journalctl`. No log file management needed.
|
|
251
|
+
- **Graceful shutdown works** — systemd sends SIGTERM, the daemon finishes the current job, closes the socket, and exits cleanly.
|
|
252
|
+
- **`Type=simple`** — systemd tracks the exact PID it spawned. Self-daemonizing (double-fork) breaks this and is a legacy SysV init pattern.
|
|
253
|
+
|
|
254
|
+
If you're running without systemd (development, containers), just background it yourself:
|
|
255
|
+
|
|
256
|
+
```bash
|
|
257
|
+
sudo memex serve & # Background in shell
|
|
258
|
+
sudo memex serve &>/dev/null & # Silent background
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
### Manual
|
|
262
|
+
|
|
263
|
+
```bash
|
|
264
|
+
sudo memex serve
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
Requires root or `CAP_SYS_ADMIN` for mount namespace creation.
|
|
268
|
+
|
|
269
|
+
## Environment variables
|
|
270
|
+
|
|
271
|
+
| Variable | Default | Description |
|
|
272
|
+
|----------|---------|-------------|
|
|
273
|
+
| `MEMEX_DATA_DIR` | `/var/lib/memex` | Data directory (wikis, database) |
|
|
274
|
+
| `MEMEX_RUN_DIR` | `/run/memex` | Runtime directory (socket, namespaces) |
|
|
275
|
+
| `MEMEX_SOCKET_PATH` | `/run/memex/memex.sock` | Unix socket path |
|
|
276
|
+
| `ANTHROPIC_API_KEY` | — | Global fallback API key |
|
|
277
|
+
|
|
278
|
+
## Requirements
|
|
279
|
+
|
|
280
|
+
- Node.js >= 20
|
|
281
|
+
- Linux with mount namespace support (`unshare`, `nsenter` from util-linux)
|
|
282
|
+
- `CAP_SYS_ADMIN` capability (or root)
|
|
283
|
+
- `claude` CLI installed and in `$PATH`
|
|
284
|
+
|
|
285
|
+
## The idea
|
|
286
|
+
|
|
287
|
+
Related in spirit to Vannevar Bush's Memex (1945) — a personal, curated knowledge store with associative trails between documents. The part Bush couldn't solve was who does the maintenance. The LLM handles that.
|
|
288
|
+
|
|
289
|
+
Most people's experience with LLMs and documents is RAG: retrieve chunks, generate answers, forget everything. Nothing compounds. Memex is different — the LLM incrementally builds and maintains a persistent wiki. The cross-references are already there. The contradictions have been flagged. The synthesis reflects everything ingested. Every source makes the whole richer.
|
|
290
|
+
|
|
291
|
+
You never write the wiki yourself. You source, explore, and ask questions. The LLM does the summarizing, cross-referencing, filing, and bookkeeping that makes a knowledge base actually useful over time.
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { ApiResponse, Wiki, WikiConfig, QueueJob, JobType, AuditEntry } from '../lib/types.js';
|
|
2
|
+
export declare class MemexClient {
|
|
3
|
+
private socketPath;
|
|
4
|
+
constructor(socketPath?: string);
|
|
5
|
+
request<T = unknown>(method: string, path: string, body?: unknown): Promise<ApiResponse<T>>;
|
|
6
|
+
/**
|
|
7
|
+
* Make a request and stream the raw response body chunks.
|
|
8
|
+
* Used for login flow where we stream CLI output.
|
|
9
|
+
*/
|
|
10
|
+
stream(method: string, path: string, body?: unknown): Promise<AsyncIterable<string>>;
|
|
11
|
+
createWiki(id: string, name?: string): Promise<ApiResponse<Wiki>>;
|
|
12
|
+
listWikis(): Promise<ApiResponse<Wiki[]>>;
|
|
13
|
+
getWiki(id: string): Promise<ApiResponse<Wiki & {
|
|
14
|
+
pending_jobs: number;
|
|
15
|
+
}>>;
|
|
16
|
+
destroyWiki(id: string, keepData?: boolean): Promise<ApiResponse<unknown>>;
|
|
17
|
+
updateConfig(wikiId: string, config: WikiConfig): Promise<ApiResponse<Wiki>>;
|
|
18
|
+
chownWiki(wikiId: string, uid: number): Promise<ApiResponse<Wiki>>;
|
|
19
|
+
setApiKey(wikiId: string, key: string): Promise<ApiResponse<unknown>>;
|
|
20
|
+
setCredentials(wikiId: string, credentials: string): Promise<ApiResponse<unknown>>;
|
|
21
|
+
submitJob(wikiId: string, type: JobType, payload: object, wait?: boolean): Promise<ApiResponse<QueueJob>>;
|
|
22
|
+
getJob(wikiId: string, jobId: number): Promise<ApiResponse<QueueJob>>;
|
|
23
|
+
listJobs(wikiId: string): Promise<ApiResponse<QueueJob[]>>;
|
|
24
|
+
getAuditLog(wikiId: string, limit?: number): Promise<ApiResponse<AuditEntry[]>>;
|
|
25
|
+
/**
|
|
26
|
+
* Upload a local file to the daemon for ingestion into a wiki's raw/ directory.
|
|
27
|
+
*/
|
|
28
|
+
uploadFile(wikiId: string, localPath: string): Promise<ApiResponse<{
|
|
29
|
+
filename: string;
|
|
30
|
+
}>>;
|
|
31
|
+
/**
|
|
32
|
+
* Submit a job and poll until it completes.
|
|
33
|
+
*/
|
|
34
|
+
waitForJob(wikiId: string, jobId: number, onPoll?: (job: QueueJob) => void): Promise<QueueJob>;
|
|
35
|
+
}
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import { request as httpRequest } from 'node:http';
|
|
2
|
+
import { readFileSync } from 'node:fs';
|
|
3
|
+
import { basename } from 'node:path';
|
|
4
|
+
import { SOCKET_PATH, JOB_POLL_INTERVAL_MS } from '../lib/constants.js';
|
|
5
|
+
import { DaemonNotRunningError } from '../lib/errors.js';
|
|
6
|
+
export class MemexClient {
|
|
7
|
+
socketPath;
|
|
8
|
+
constructor(socketPath = SOCKET_PATH) {
|
|
9
|
+
this.socketPath = socketPath;
|
|
10
|
+
}
|
|
11
|
+
// ── Generic request ────────────────────────────────────────────────────
|
|
12
|
+
async request(method, path, body) {
|
|
13
|
+
return new Promise((resolve, reject) => {
|
|
14
|
+
const opts = {
|
|
15
|
+
socketPath: this.socketPath,
|
|
16
|
+
method,
|
|
17
|
+
path,
|
|
18
|
+
headers: { 'Content-Type': 'application/json' },
|
|
19
|
+
};
|
|
20
|
+
const req = httpRequest(opts, (res) => {
|
|
21
|
+
const chunks = [];
|
|
22
|
+
res.on('data', (chunk) => chunks.push(chunk));
|
|
23
|
+
res.on('end', () => {
|
|
24
|
+
const raw = Buffer.concat(chunks).toString('utf-8');
|
|
25
|
+
try {
|
|
26
|
+
resolve(JSON.parse(raw));
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
resolve({ ok: false, error: raw });
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
req.on('error', (err) => {
|
|
34
|
+
if (err.code === 'ECONNREFUSED' || err.code === 'ENOENT') {
|
|
35
|
+
reject(new DaemonNotRunningError());
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
reject(err);
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
if (body !== undefined) {
|
|
42
|
+
req.write(JSON.stringify(body));
|
|
43
|
+
}
|
|
44
|
+
req.end();
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Make a request and stream the raw response body chunks.
|
|
49
|
+
* Used for login flow where we stream CLI output.
|
|
50
|
+
*/
|
|
51
|
+
async stream(method, path, body) {
|
|
52
|
+
return new Promise((resolve, reject) => {
|
|
53
|
+
const opts = {
|
|
54
|
+
socketPath: this.socketPath,
|
|
55
|
+
method,
|
|
56
|
+
path,
|
|
57
|
+
headers: { 'Content-Type': 'application/json' },
|
|
58
|
+
};
|
|
59
|
+
const req = httpRequest(opts, (res) => {
|
|
60
|
+
// Track response errors so the async iterator can propagate them
|
|
61
|
+
let responseError = null;
|
|
62
|
+
res.on('error', (err) => { responseError = err; });
|
|
63
|
+
const iterable = {
|
|
64
|
+
[Symbol.asyncIterator]() {
|
|
65
|
+
return {
|
|
66
|
+
next() {
|
|
67
|
+
return new Promise((resolveNext, rejectNext) => {
|
|
68
|
+
if (responseError) {
|
|
69
|
+
rejectNext(responseError);
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
const onData = (chunk) => {
|
|
73
|
+
res.removeListener('end', onEnd);
|
|
74
|
+
res.removeListener('error', onError);
|
|
75
|
+
resolveNext({ value: chunk.toString('utf-8'), done: false });
|
|
76
|
+
};
|
|
77
|
+
const onEnd = () => {
|
|
78
|
+
res.removeListener('data', onData);
|
|
79
|
+
res.removeListener('error', onError);
|
|
80
|
+
resolveNext({ value: '', done: true });
|
|
81
|
+
};
|
|
82
|
+
const onError = (err) => {
|
|
83
|
+
res.removeListener('data', onData);
|
|
84
|
+
res.removeListener('end', onEnd);
|
|
85
|
+
rejectNext(err);
|
|
86
|
+
};
|
|
87
|
+
res.once('data', onData);
|
|
88
|
+
res.once('end', onEnd);
|
|
89
|
+
res.once('error', onError);
|
|
90
|
+
});
|
|
91
|
+
},
|
|
92
|
+
};
|
|
93
|
+
},
|
|
94
|
+
};
|
|
95
|
+
resolve(iterable);
|
|
96
|
+
});
|
|
97
|
+
req.on('error', (err) => {
|
|
98
|
+
if (err.code === 'ECONNREFUSED' || err.code === 'ENOENT') {
|
|
99
|
+
reject(new DaemonNotRunningError());
|
|
100
|
+
}
|
|
101
|
+
else {
|
|
102
|
+
reject(err);
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
if (body !== undefined) {
|
|
106
|
+
req.write(JSON.stringify(body));
|
|
107
|
+
}
|
|
108
|
+
req.end();
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
// ── Convenience methods ────────────────────────────────────────────────
|
|
112
|
+
createWiki(id, name) {
|
|
113
|
+
return this.request('POST', '/wikis', { id, name });
|
|
114
|
+
}
|
|
115
|
+
listWikis() {
|
|
116
|
+
return this.request('GET', '/wikis');
|
|
117
|
+
}
|
|
118
|
+
getWiki(id) {
|
|
119
|
+
return this.request('GET', `/wikis/${id}`);
|
|
120
|
+
}
|
|
121
|
+
destroyWiki(id, keepData = false) {
|
|
122
|
+
return this.request('DELETE', `/wikis/${id}`, { keepData });
|
|
123
|
+
}
|
|
124
|
+
updateConfig(wikiId, config) {
|
|
125
|
+
return this.request('PUT', `/wikis/${wikiId}/config`, config);
|
|
126
|
+
}
|
|
127
|
+
chownWiki(wikiId, uid) {
|
|
128
|
+
return this.request('POST', `/wikis/${wikiId}/chown`, { uid });
|
|
129
|
+
}
|
|
130
|
+
setApiKey(wikiId, key) {
|
|
131
|
+
return this.request('POST', `/wikis/${wikiId}/api-key`, { key });
|
|
132
|
+
}
|
|
133
|
+
setCredentials(wikiId, credentials) {
|
|
134
|
+
return this.request('POST', `/wikis/${wikiId}/credentials`, { credentials });
|
|
135
|
+
}
|
|
136
|
+
submitJob(wikiId, type, payload, wait = false) {
|
|
137
|
+
const path = `/wikis/${wikiId}/jobs` + (wait ? '?wait=true' : '');
|
|
138
|
+
return this.request('POST', path, { type, payload });
|
|
139
|
+
}
|
|
140
|
+
getJob(wikiId, jobId) {
|
|
141
|
+
return this.request('GET', `/wikis/${wikiId}/jobs/${jobId}`);
|
|
142
|
+
}
|
|
143
|
+
listJobs(wikiId) {
|
|
144
|
+
return this.request('GET', `/wikis/${wikiId}/jobs`);
|
|
145
|
+
}
|
|
146
|
+
getAuditLog(wikiId, limit) {
|
|
147
|
+
const path = `/wikis/${wikiId}/logs` + (limit ? `?limit=${limit}` : '');
|
|
148
|
+
return this.request('GET', path);
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Upload a local file to the daemon for ingestion into a wiki's raw/ directory.
|
|
152
|
+
*/
|
|
153
|
+
async uploadFile(wikiId, localPath) {
|
|
154
|
+
const content = readFileSync(localPath);
|
|
155
|
+
const filename = basename(localPath);
|
|
156
|
+
return this.request('POST', `/wikis/${wikiId}/ingest-file`, {
|
|
157
|
+
filename,
|
|
158
|
+
content: content.toString('base64'),
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Submit a job and poll until it completes.
|
|
163
|
+
*/
|
|
164
|
+
async waitForJob(wikiId, jobId, onPoll) {
|
|
165
|
+
while (true) {
|
|
166
|
+
const resp = await this.getJob(wikiId, jobId);
|
|
167
|
+
if (!resp.ok || !resp.data) {
|
|
168
|
+
throw new Error(resp.error ?? 'Failed to get job status');
|
|
169
|
+
}
|
|
170
|
+
const job = resp.data;
|
|
171
|
+
if (onPoll)
|
|
172
|
+
onPoll(job);
|
|
173
|
+
if (job.status === 'completed' || job.status === 'failed') {
|
|
174
|
+
return job;
|
|
175
|
+
}
|
|
176
|
+
await sleep(JOB_POLL_INTERVAL_MS);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
function sleep(ms) {
|
|
181
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
182
|
+
}
|
|
183
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/cli/client.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import { MemexClient } from '../client.js';
|
|
3
|
+
export const chownCommand = new Command('chown')
|
|
4
|
+
.description('Transfer wiki ownership to another user (by UID)')
|
|
5
|
+
.argument('<wikiId>', 'Wiki identifier')
|
|
6
|
+
.argument('<uid>', 'New owner UID (numeric)')
|
|
7
|
+
.action(async (wikiId, uidStr) => {
|
|
8
|
+
const uid = Number(uidStr);
|
|
9
|
+
if (!Number.isInteger(uid) || uid < 0) {
|
|
10
|
+
console.error(`Error: uid must be a non-negative integer, got '${uidStr}'`);
|
|
11
|
+
process.exit(1);
|
|
12
|
+
}
|
|
13
|
+
const client = new MemexClient();
|
|
14
|
+
const resp = await client.chownWiki(wikiId, uid);
|
|
15
|
+
if (!resp.ok) {
|
|
16
|
+
console.error(`Error: ${resp.error}`);
|
|
17
|
+
process.exit(1);
|
|
18
|
+
}
|
|
19
|
+
const wiki = resp.data;
|
|
20
|
+
console.log(`Transferred wiki '${wiki.id}' to uid ${wiki.owner_uid}`);
|
|
21
|
+
});
|
|
22
|
+
//# sourceMappingURL=chown.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chown.js","sourceRoot":"","sources":["../../../src/cli/commands/chown.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAG3C,MAAM,CAAC,MAAM,YAAY,GAAG,IAAI,OAAO,CAAC,OAAO,CAAC;KAC7C,WAAW,CAAC,kDAAkD,CAAC;KAC/D,QAAQ,CAAC,UAAU,EAAE,iBAAiB,CAAC;KACvC,QAAQ,CAAC,OAAO,EAAE,yBAAyB,CAAC;KAC5C,MAAM,CAAC,KAAK,EAAE,MAAc,EAAE,MAAc,EAAE,EAAE;IAC/C,MAAM,GAAG,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC;IAC3B,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,GAAG,GAAG,CAAC,EAAE,CAAC;QACtC,OAAO,CAAC,KAAK,CAAC,mDAAmD,MAAM,GAAG,CAAC,CAAC;QAC5E,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,MAAM,MAAM,GAAG,IAAI,WAAW,EAAE,CAAC;IACjC,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,SAAS,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAEjD,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC;QACb,OAAO,CAAC,KAAK,CAAC,UAAU,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC;QACtC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,MAAM,IAAI,GAAG,IAAI,CAAC,IAAY,CAAC;IAC/B,OAAO,CAAC,GAAG,CAAC,qBAAqB,IAAI,CAAC,EAAE,YAAY,IAAI,CAAC,SAAS,EAAE,CAAC,CAAC;AACxE,CAAC,CAAC,CAAC"}
|