wiki-viewer 1.0.0 → 1.0.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/.next/standalone/README.md +328 -99
- package/.next/standalone/agents/bootstrap-prompt.md +1 -0
- package/.next/standalone/agents/wiki-viewer-skill/SKILL.md +236 -0
- package/.next/standalone/bin/wiki-viewer.js +57 -4
- package/.next/standalone/docs/agent-collab-plan.md +1615 -0
- package/.next/standalone/docs/agent-collab-v2-plan.md +771 -0
- package/.next/standalone/package.json +19 -2
- package/.next/standalone/pnpm-lock.yaml +1368 -325
- package/.next/standalone/pnpm-workspace.yaml +7 -1
- package/.next/standalone/src/app/api/agent/activity/route.ts +58 -0
- package/.next/standalone/src/app/api/agent/admin/agents/[agentId]/revoke/route.ts +40 -0
- package/.next/standalone/src/app/api/agent/admin/agents/route.ts +33 -0
- package/.next/standalone/src/app/api/agent/admin/registrations/[regId]/approve/route.ts +83 -0
- package/.next/standalone/src/app/api/agent/admin/registrations/[regId]/deny/route.ts +36 -0
- package/.next/standalone/src/app/api/agent/admin/registrations/route.ts +27 -0
- package/.next/standalone/src/app/api/agent/events/[...path]/route.ts +136 -0
- package/.next/standalone/src/app/api/agent/files/[...path]/route.ts +202 -0
- package/.next/standalone/src/app/api/agent/internal/span/route.ts +117 -0
- package/.next/standalone/src/app/api/agent/register/[regId]/route.ts +50 -0
- package/.next/standalone/src/app/api/agent/register/route.ts +110 -0
- package/.next/standalone/src/app/api/agent/settings/route.ts +30 -0
- package/.next/standalone/src/app/api/agent/settings/token/regenerate/route.ts +20 -0
- package/.next/standalone/src/app/api/agent/sidecar/[...path]/route.ts +49 -0
- package/.next/standalone/src/app/api/agents/install/route.ts +83 -0
- package/.next/standalone/src/app/api/agents/skill/route.ts +20 -0
- package/.next/standalone/src/app/api/agents/skill.tar.gz/route.ts +87 -0
- package/.next/standalone/src/app/api/auth/[...all]/route.ts +7 -0
- package/.next/standalone/src/app/api/owner/init/route.ts +14 -0
- package/.next/standalone/src/app/api/system/auth-settings/route.ts +85 -0
- package/.next/standalone/src/app/api/system/browse/route.ts +4 -0
- package/.next/standalone/src/app/api/system/clear-root/route.ts +8 -1
- package/.next/standalone/src/app/api/system/config/route.ts +5 -1
- package/.next/standalone/src/app/api/system/pins/route.ts +7 -0
- package/.next/standalone/src/app/api/system/reveal/route.ts +7 -0
- package/.next/standalone/src/app/api/system/root-status/route.ts +5 -1
- package/.next/standalone/src/app/api/system/set-root/route.ts +7 -0
- package/.next/standalone/src/app/api/wiki/app/route.ts +15 -0
- package/.next/standalone/src/app/api/wiki/content/route.ts +110 -11
- package/.next/standalone/src/app/api/wiki/folder/route.ts +7 -0
- package/.next/standalone/src/app/api/wiki/move/route.ts +7 -0
- package/.next/standalone/src/app/api/wiki/new-file/route.ts +55 -0
- package/.next/standalone/src/app/api/wiki/page/route.ts +7 -0
- package/.next/standalone/src/app/api/wiki/route.ts +10 -0
- package/.next/standalone/src/app/api/wiki/slugs/route.ts +5 -1
- package/.next/standalone/src/app/api/wiki/upload/route.ts +7 -0
- package/.next/standalone/src/app/api/wiki/watch/route.ts +6 -1
- package/.next/standalone/src/app/globals.css +70 -0
- package/.next/standalone/src/app/page.tsx +461 -217
- package/.next/standalone/src/app/signin/page.tsx +217 -0
- package/.next/standalone/src/components/ai-panel/activity-row.tsx +64 -0
- package/.next/standalone/src/components/ai-panel/ai-panel.tsx +322 -0
- package/.next/standalone/src/components/ai-panel/token-section.tsx +312 -0
- package/.next/standalone/src/components/auth-settings-sheet.tsx +209 -0
- package/.next/standalone/src/components/editor/bubble-menu.tsx +41 -2
- package/.next/standalone/src/components/editor/comment-pip.tsx +56 -0
- package/.next/standalone/src/components/editor/comment-thread.tsx +252 -0
- package/.next/standalone/src/components/editor/editor.tsx +513 -10
- package/.next/standalone/src/components/editor/extensions/proof-span.ts +60 -0
- package/.next/standalone/src/components/editor/extensions.ts +2 -0
- package/.next/standalone/src/components/editor/proof-span-popover.tsx +161 -0
- package/.next/standalone/src/components/editor/suggest-edit-popover.tsx +228 -0
- package/.next/standalone/src/components/editor/suggestion-card.tsx +201 -0
- package/.next/standalone/src/components/view-width-toggle.tsx +47 -0
- package/.next/standalone/src/components/wiki/markdown-preview.tsx +120 -0
- package/.next/standalone/src/lib/auth/allowlist.ts +50 -0
- package/.next/standalone/src/lib/auth/client.ts +4 -0
- package/.next/standalone/src/lib/auth/csrf.ts +68 -0
- package/.next/standalone/src/lib/auth/server.ts +164 -0
- package/.next/standalone/src/lib/config.ts +4 -0
- package/.next/standalone/src/lib/markdown/parse-frontmatter.ts +20 -0
- package/.next/standalone/src/lib/markdown/sanitize-schema.ts +106 -0
- package/.next/standalone/src/lib/markdown/to-html.ts +46 -8
- package/.next/standalone/src/lib/markdown/to-markdown.ts +14 -0
- package/.next/standalone/src/lib/proof/activity-shared.ts +31 -0
- package/.next/standalone/src/lib/proof/activity.ts +74 -0
- package/.next/standalone/src/lib/proof/auth.ts +132 -0
- package/.next/standalone/src/lib/proof/block-refs.ts +133 -0
- package/.next/standalone/src/lib/proof/blocks.ts +73 -0
- package/.next/standalone/src/lib/proof/client-auth.ts +23 -0
- package/.next/standalone/src/lib/proof/event-bus.ts +47 -0
- package/.next/standalone/src/lib/proof/file-lock.ts +38 -0
- package/.next/standalone/src/lib/proof/glob.ts +51 -0
- package/.next/standalone/src/lib/proof/idempotency.ts +32 -0
- package/.next/standalone/src/lib/proof/mutex.ts +32 -0
- package/.next/standalone/src/lib/proof/ops-applier.ts +678 -0
- package/.next/standalone/src/lib/proof/owner-auth.ts +2 -0
- package/.next/standalone/src/lib/proof/pending.ts +97 -0
- package/.next/standalone/src/lib/proof/proof-span.ts +141 -0
- package/.next/standalone/src/lib/proof/rate-limit.ts +53 -0
- package/.next/standalone/src/lib/proof/register-rate-limit.ts +53 -0
- package/.next/standalone/src/lib/proof/registry.ts +190 -0
- package/.next/standalone/src/lib/proof/sidecar.ts +66 -0
- package/.next/standalone/src/lib/proof/suggest-capture.ts +69 -0
- package/.next/standalone/src/lib/proof/types.ts +170 -0
- package/.next/standalone/src/lib/proof-config.ts +14 -0
- package/.next/standalone/src/middleware.ts +38 -0
- package/.next/standalone/src/stores/ai-panel-store.ts +58 -3
- package/.next/standalone/src/stores/editor-store.ts +115 -9
- package/.next/standalone/src/stores/proof-store.ts +123 -0
- package/.next/standalone/src/stores/view-width-store.ts +62 -0
- package/.next/standalone/src/tests/proof/activity-aggregator.test.ts +146 -0
- package/.next/standalone/src/tests/proof/agent-ownership.test.ts +140 -0
- package/.next/standalone/src/tests/proof/agents-install.test.ts +57 -0
- package/.next/standalone/src/tests/proof/better-auth.test.ts +68 -0
- package/.next/standalone/src/tests/proof/block-refs.test.ts +108 -0
- package/.next/standalone/src/tests/proof/blocks.test.ts +92 -0
- package/.next/standalone/src/tests/proof/comments-ops.test.ts +177 -0
- package/.next/standalone/src/tests/proof/editor-roundtrip.test.ts +85 -0
- package/.next/standalone/src/tests/proof/external-edit.test.ts +58 -0
- package/.next/standalone/src/tests/proof/file-lock.test.ts +80 -0
- package/.next/standalone/src/tests/proof/glob.test.ts +82 -0
- package/.next/standalone/src/tests/proof/helpers/auth-session.ts +27 -0
- package/.next/standalone/src/tests/proof/markdown-to-html.test.ts +46 -0
- package/.next/standalone/src/tests/proof/ops-applier.test.ts +368 -0
- package/.next/standalone/src/tests/proof/owner-init.test.ts +2 -0
- package/.next/standalone/src/tests/proof/parse-frontmatter.test.ts +60 -0
- package/.next/standalone/src/tests/proof/proof-span.test.ts +98 -0
- package/.next/standalone/src/tests/proof/rate-limit.test.ts +54 -0
- package/.next/standalone/src/tests/proof/registration-flow.test.ts +330 -0
- package/.next/standalone/src/tests/proof/registry.test.ts +169 -0
- package/.next/standalone/src/tests/proof/routes.test.ts +516 -0
- package/.next/standalone/src/tests/proof/sidecar-trim.test.ts +58 -0
- package/.next/standalone/src/tests/proof/suggestion-ops.test.ts +280 -0
- package/.next/standalone/src/tests/proof/wiki-content-put.test.ts +140 -0
- package/.next/standalone/src/tests/proof/wiki-routes-auth.test.ts +208 -0
- package/README.md +328 -99
- package/bin/wiki-viewer.js +57 -4
- package/package.json +19 -2
|
@@ -1,15 +1,13 @@
|
|
|
1
1
|
<div align="center">
|
|
2
2
|
<img src="public/logo.svg" width="80" height="80" alt="wiki-viewer logo" />
|
|
3
3
|
<h1>wiki-viewer</h1>
|
|
4
|
-
<p><strong>Browse, read, and edit your local files from a clean web UI.</strong></p>
|
|
4
|
+
<p><strong>Browse, read, and edit your local files from a clean web UI. Now with multi-user auth and an HTTP API for AI agents.</strong></p>
|
|
5
5
|
<p>
|
|
6
6
|
Markdown · PDF · Office docs · Notebooks · Images · Code · and more
|
|
7
7
|
</p>
|
|
8
8
|
|
|
9
9
|
<p>
|
|
10
|
-
|
|
11
|
-
<!-- <a href="https://www.npmjs.com/package/wiki-viewer"><img src="https://img.shields.io/npm/v/wiki-viewer" alt="npm version" /></a> -->
|
|
12
|
-
<img src="https://img.shields.io/badge/npm-not%20yet%20published-lightgrey" alt="npm not yet published" />
|
|
10
|
+
<a href="https://www.npmjs.com/package/wiki-viewer"><img src="https://img.shields.io/npm/v/wiki-viewer" alt="npm version" /></a>
|
|
13
11
|
<img src="https://img.shields.io/badge/node-%3E%3D18-brightgreen" alt="Node.js ≥18" />
|
|
14
12
|
<img src="https://img.shields.io/badge/license-MIT-blue" alt="MIT license" />
|
|
15
13
|
</p>
|
|
@@ -19,9 +17,15 @@
|
|
|
19
17
|
|
|
20
18
|
## What is it?
|
|
21
19
|
|
|
22
|
-
**wiki-viewer** is a
|
|
20
|
+
**wiki-viewer** is a local-or-remote file browser and editor you run from your terminal. It starts a small web server and lets you navigate, read, and edit any directory on your machine.
|
|
23
21
|
|
|
24
|
-
|
|
22
|
+
Originally a zero-config single-user tool, it now supports:
|
|
23
|
+
|
|
24
|
+
- Multi-user sign-in (Google OAuth or email + password) for teams of 3 to 10 sharing a VPS.
|
|
25
|
+
- An HTTP collaboration API that lets AI agents read and edit Markdown files alongside you, with provenance marks, comments, suggestions, and revision-checked mutations.
|
|
26
|
+
- Per-agent registration and scoped tokens. No shared bearer secret.
|
|
27
|
+
|
|
28
|
+
Single-user, no-auth mode still works. Auth turns on automatically once anyone signs up.
|
|
25
29
|
|
|
26
30
|
---
|
|
27
31
|
|
|
@@ -34,14 +38,14 @@ No cloud. No accounts. No syncing. Your files stay on your machine.
|
|
|
34
38
|
| **File ops** | Upload files, create folders, delete, drag-to-move |
|
|
35
39
|
| **Wiki links** | `[[page-name]]` links between Markdown files |
|
|
36
40
|
| **Dark mode** | System-aware, with manual toggle |
|
|
37
|
-
| **
|
|
41
|
+
| **Auth** | Google OAuth and email + password via [Better Auth](https://better-auth.com). Email allowlist. SQLite-backed sessions. |
|
|
42
|
+
| **AI agents** | Per-agent HTTP API. Trust On First Use registration. Comments, suggestions, inline provenance marks (`<proof-span>`). Block-level revision check. Idempotency keys. Per-IP rate limiting. |
|
|
43
|
+
| **HTTPS** | Required for remote access. Self-signed cert (OpenSSL), trusted local cert (mkcert), or your own TLS in front of plain HTTP. |
|
|
38
44
|
|
|
39
45
|
---
|
|
40
46
|
|
|
41
47
|
## Quick start
|
|
42
48
|
|
|
43
|
-
> **Note:** The npm package is not yet published. Use the [dev setup](#-dev-setup) below to run locally from source. The `npx` commands below will work once the package is on npm.
|
|
44
|
-
|
|
45
49
|
```bash
|
|
46
50
|
# Point it at a directory
|
|
47
51
|
npx wiki-viewer ~/notes
|
|
@@ -53,11 +57,15 @@ npx wiki-viewer
|
|
|
53
57
|
npx wiki-viewer ~/notes --https
|
|
54
58
|
```
|
|
55
59
|
|
|
56
|
-
|
|
60
|
+
Open **http://localhost:3000** (or **https://localhost:3000** with `--https`).
|
|
57
61
|
|
|
58
|
-
|
|
62
|
+
To switch the served directory later, click **Change** at the bottom of the sidebar and pick a new folder. No restart needed.
|
|
59
63
|
|
|
60
|
-
|
|
64
|
+
On first run with no users in the database, the app works in single-user mode and any visitor on `localhost` can sign up. Set `AUTH_ALLOWED_DOMAIN` or `AUTH_ALLOWED_EMAILS` before exposing the server to anyone else.
|
|
65
|
+
|
|
66
|
+
> ⚠️ **Running on a remote host?** The app must be accessed over **HTTPS**. Browsers gate several APIs (service workers, PDF.js, secure-context features) behind HTTPS. Plain HTTP only works on `localhost`.
|
|
67
|
+
|
|
68
|
+
### CLI options
|
|
61
69
|
|
|
62
70
|
```
|
|
63
71
|
wiki-viewer [directory] [options]
|
|
@@ -67,11 +75,11 @@ wiki-viewer [directory] [options]
|
|
|
67
75
|
Options:
|
|
68
76
|
-p, --port <port> Port to listen on (default: 3000)
|
|
69
77
|
-H, --host <host> Host to bind to (default: localhost)
|
|
70
|
-
--https Enable HTTPS (self-signed cert, required
|
|
78
|
+
--https Enable HTTPS (self-signed cert, required on remote)
|
|
71
79
|
-h, --help Show this help message
|
|
72
80
|
```
|
|
73
81
|
|
|
74
|
-
|
|
82
|
+
Examples:
|
|
75
83
|
|
|
76
84
|
```bash
|
|
77
85
|
# Custom port
|
|
@@ -86,116 +94,321 @@ npx wiki-viewer ~/notes --https -p 8443
|
|
|
86
94
|
|
|
87
95
|
---
|
|
88
96
|
|
|
89
|
-
##
|
|
97
|
+
## Auth and multi-user mode
|
|
90
98
|
|
|
91
|
-
|
|
99
|
+
wiki-viewer uses [Better Auth](https://better-auth.com) with SQLite. State lives at `~/.wiki-viewer/auth.db` (WAL mode) and `~/.wiki-viewer/auth.secret` (chmod 0600). Both are created on first start. Set `BETTER_AUTH_SECRET` in the environment to override the file-stored secret.
|
|
92
100
|
|
|
93
|
-
|
|
101
|
+
Sign-in providers:
|
|
94
102
|
|
|
95
|
-
- **
|
|
96
|
-
- **
|
|
97
|
-
```bash
|
|
98
|
-
npm install -g pnpm
|
|
99
|
-
```
|
|
103
|
+
- **Google OAuth.** Enabled when both `GOOGLE_CLIENT_ID` and `GOOGLE_CLIENT_SECRET` are set.
|
|
104
|
+
- **Email + password.** Always available. No verification email is sent by default.
|
|
100
105
|
|
|
101
|
-
###
|
|
106
|
+
### Email allowlist
|
|
107
|
+
|
|
108
|
+
By default any email can sign up. Lock it down before exposing the server.
|
|
109
|
+
|
|
110
|
+
There are two ways to set the allowlist. The settings sheet is the easy path; env vars are the fallback for headless or scripted deploys.
|
|
111
|
+
|
|
112
|
+
**From the UI (recommended).** Click the gear icon in the sidebar toolbar to open Signup allowlist. Enter allowed emails and domains, one per line or comma-separated, then save. Changes are stored in `~/.wiki-viewer/config.json` and apply on the next signup with no restart. Clearing both lists reverts to the environment variables below.
|
|
113
|
+
|
|
114
|
+
**From the environment.** Set either or both before starting the server:
|
|
102
115
|
|
|
103
116
|
```bash
|
|
104
|
-
|
|
105
|
-
|
|
117
|
+
# Allow specific accounts
|
|
118
|
+
export AUTH_ALLOWED_EMAILS="alice@team.com,bob@team.com"
|
|
119
|
+
|
|
120
|
+
# Or whole domains (csv)
|
|
121
|
+
export AUTH_ALLOWED_DOMAIN="team.com,partner.org"
|
|
106
122
|
```
|
|
107
123
|
|
|
108
|
-
|
|
124
|
+
**Precedence.** If the UI allowlist in `config.json` is non-empty, it wins and the env vars are ignored. If it is empty, the env vars are used. If neither is set, any email can sign up. When both an email and a domain list apply, either match grants access.
|
|
125
|
+
|
|
126
|
+
### Production guard
|
|
127
|
+
|
|
128
|
+
In production, the server refuses to boot unless `BETTER_AUTH_URL` is set to an `https://` origin. This prevents accidentally serving auth cookies over plain HTTP.
|
|
109
129
|
|
|
110
130
|
```bash
|
|
111
|
-
|
|
131
|
+
export BETTER_AUTH_URL="https://wiki.team.com"
|
|
112
132
|
```
|
|
113
133
|
|
|
114
|
-
|
|
134
|
+
For development or local smoke tests, set `WIKI_ALLOW_INSECURE=1` to bypass the guard.
|
|
135
|
+
|
|
136
|
+
### CSRF and trusted origins
|
|
137
|
+
|
|
138
|
+
All state-changing routes (`POST` / `PUT` / `DELETE` / `PATCH`) under `/api/wiki/*` and `/api/system/*` check the request's `Origin` header against an allowlist. Cross-origin requests with a session cookie are rejected with `403 FORBIDDEN`.
|
|
139
|
+
|
|
140
|
+
By default the allowlist includes `localhost` and `127.0.0.1`. Add hostnames you actually browse from:
|
|
115
141
|
|
|
116
142
|
```bash
|
|
117
|
-
|
|
118
|
-
|
|
143
|
+
export WIKI_OWNER_HOSTS="wiki.team.com,office.lan"
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
Do not place wiki-viewer behind a reverse proxy that rewrites `Host`. The Origin check assumes the browser-visible hostname matches what wiki-viewer sees. Bind to loopback and front with a TLS-terminating proxy that preserves the original `Host` (nginx `proxy_set_header Host $host;` or Caddy default).
|
|
119
147
|
|
|
120
|
-
|
|
121
|
-
pnpm dev
|
|
148
|
+
### Rate limits
|
|
122
149
|
|
|
123
|
-
|
|
124
|
-
|
|
150
|
+
| Endpoint | Default |
|
|
151
|
+
| ----------------- | ------------------------- |
|
|
152
|
+
| `/sign-in/email` | 20 requests / 60 seconds |
|
|
153
|
+
| `/sign-up/email` | 10 requests / 60 seconds |
|
|
154
|
+
| Other auth routes | 100 requests / 60 seconds |
|
|
155
|
+
|
|
156
|
+
Rate limiting is disabled in development (`NODE_ENV !== "production"`).
|
|
157
|
+
|
|
158
|
+
### Recovering a locked-out account
|
|
159
|
+
|
|
160
|
+
The rate limiter stores counts in memory. Restart the server to clear it.
|
|
161
|
+
|
|
162
|
+
To delete and re-create a user from scratch:
|
|
163
|
+
|
|
164
|
+
```bash
|
|
165
|
+
sqlite3 ~/.wiki-viewer/auth.db "DELETE FROM user WHERE email='you@team.com';"
|
|
166
|
+
sqlite3 ~/.wiki-viewer/auth.db "DELETE FROM account WHERE userId NOT IN (SELECT id FROM user);"
|
|
167
|
+
sqlite3 ~/.wiki-viewer/auth.db "DELETE FROM session WHERE userId NOT IN (SELECT id FROM user);"
|
|
125
168
|
```
|
|
126
169
|
|
|
127
|
-
|
|
170
|
+
Then sign up again.
|
|
171
|
+
|
|
172
|
+
### Editor save conflicts
|
|
173
|
+
|
|
174
|
+
The Markdown editor now sends a `baseRevision` with every save. If another user (or an agent) modified the file in the meantime, the server returns `409 STALE_REVISION` and the editor silently reloads the new content. Your in-progress edits in that tab are lost. Reload before making large changes if you know someone else is also editing.
|
|
175
|
+
|
|
176
|
+
---
|
|
177
|
+
|
|
178
|
+
## Working with AI agents
|
|
128
179
|
|
|
129
|
-
|
|
180
|
+
wiki-viewer exposes an HTTP collaboration protocol so agents (Claude, Cursor, ChatGPT desktop, custom scripts) can read and edit Markdown files. Every AI-authored insert is wrapped in an inline `<proof-span>` mark so the human reviewer can see, accept, or revert each change.
|
|
130
181
|
|
|
131
|
-
|
|
182
|
+
The protocol is intentionally API-compatible in spirit with [Proof SDK](https://github.com/EveryInc/proof-sdk).
|
|
132
183
|
|
|
133
|
-
|
|
134
|
-
| ---------------- | ---------------------------------------- |
|
|
135
|
-
| `pnpm dev` | Start Next.js development server |
|
|
136
|
-
| `pnpm dev:https` | Start dev server with experimental HTTPS |
|
|
137
|
-
| `pnpm build` | Build production bundle |
|
|
138
|
-
| `pnpm start` | Start production server (after build) |
|
|
139
|
-
| `pnpm wiki` | Run the CLI entry point (after build) |
|
|
184
|
+
### Trust On First Use registration
|
|
140
185
|
|
|
141
|
-
|
|
186
|
+
Each agent gets its own bearer token tied to a stable identity. No shared secret.
|
|
142
187
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
188
|
+
1. Agent calls `POST /api/agent/register` with `id`, `displayName`, and requested `scope`.
|
|
189
|
+
2. Server returns `registrationId` and `pollUrl`. The id itself is the pickup secret.
|
|
190
|
+
3. Owner opens the AI Panel, sees the pending request, clicks **Approve**.
|
|
191
|
+
4. Agent polls and receives a one-shot `token`. Pickup deletes the token from the server.
|
|
192
|
+
5. Agent sends `Authorization: Bearer <token>` and `X-Agent-Id: <id>` on every later request.
|
|
193
|
+
|
|
194
|
+
The registry lives in `~/.wiki-viewer/agents.json`. Only SHA-256 hashes of tokens are stored.
|
|
195
|
+
|
|
196
|
+
### Distribute the agent skill
|
|
197
|
+
|
|
198
|
+
The running server exposes itself as an installable [Agent Skill](https://github.com/anthropics/skills):
|
|
199
|
+
|
|
200
|
+
```bash
|
|
201
|
+
# Claude Code, Codex, Cursor, OpenCode
|
|
202
|
+
npx skills add anh-chu/wiki-viewer/agents/wiki-viewer-skill
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
For any chat agent, paste the bootstrap prompt from `<your-server>/agents/install` (also visible in the AI Panel). The agent fetches `/api/agents/install` and learns the full op vocabulary at runtime.
|
|
206
|
+
|
|
207
|
+
### Op vocabulary
|
|
208
|
+
|
|
209
|
+
Block-level edits (revision-checked, idempotent):
|
|
210
|
+
|
|
211
|
+
```json
|
|
212
|
+
{ "type": "block.replace", "ref": "b7f2c1", "markdown": "New content." }
|
|
213
|
+
{ "type": "block.insertAfter", "ref": "b7f2c1", "markdown": "..." }
|
|
214
|
+
{ "type": "block.insertBefore", "ref": "b7f2c1", "markdown": "..." }
|
|
215
|
+
{ "type": "block.delete", "ref": "b7f2c1" }
|
|
216
|
+
{ "type": "block.append", "markdown": "..." }
|
|
217
|
+
{ "type": "block.prepend", "markdown": "..." }
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
Comments and suggestions:
|
|
221
|
+
|
|
222
|
+
```json
|
|
223
|
+
{ "type": "comment.add", "ref": "b7f2c1", "text": "Why end of June?" }
|
|
224
|
+
{ "type": "comment.reply", "commentId": "c4a1", "text": "API freeze." }
|
|
225
|
+
{ "type": "comment.resolve", "commentId": "c4a1" }
|
|
226
|
+
{ "type": "suggestion.add", "ref": "b7f2c1", "kind": "replace", "markdown": "...",
|
|
227
|
+
"basis": "described", "basisDetail": "user asked" }
|
|
228
|
+
{ "type": "suggestion.accept", "suggestionId": "s3b2" }
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
See [`docs/agent-collab-plan.md`](docs/agent-collab-plan.md) for the full spec: snapshot shape, event log, suggestion lifecycle, provenance attribute rules, and edge cases.
|
|
232
|
+
|
|
233
|
+
### Key routes
|
|
234
|
+
|
|
235
|
+
Anonymous:
|
|
236
|
+
|
|
237
|
+
| Method | Path | Description |
|
|
238
|
+
| ------ | ----------------------------- | ----------------------------------------------- |
|
|
239
|
+
| `POST` | `/api/agent/register` | Request registration. Returns `registrationId`. |
|
|
240
|
+
| `GET` | `/api/agent/register/<regId>` | Poll status. Returns token once approved. |
|
|
241
|
+
| `GET` | `/api/agents/install` | Discovery JSON for agents. |
|
|
242
|
+
| `GET` | `/api/agents/skill` | Raw SKILL.md. |
|
|
243
|
+
| `GET` | `/api/agents/skill.tar.gz` | Skill as gzip tarball. |
|
|
244
|
+
|
|
245
|
+
Owner-only (session cookie):
|
|
246
|
+
|
|
247
|
+
| Method | Path | Description |
|
|
248
|
+
| ------ | ------------------------------------------------ | --------------------------- |
|
|
249
|
+
| `GET` | `/api/agent/admin/registrations` | List pending registrations. |
|
|
250
|
+
| `POST` | `/api/agent/admin/registrations/<regId>/approve` | Approve, mint token. |
|
|
251
|
+
| `POST` | `/api/agent/admin/registrations/<regId>/deny` | Deny. |
|
|
252
|
+
| `GET` | `/api/agent/admin/agents` | List your approved agents. |
|
|
253
|
+
| `POST` | `/api/agent/admin/agents/<id>/revoke` | Revoke an agent. |
|
|
254
|
+
|
|
255
|
+
Agent routes (bearer + `X-Agent-Id`, scope-checked):
|
|
256
|
+
|
|
257
|
+
| Method | Path | Required scope |
|
|
258
|
+
| ------ | ---------------------------------------- | --------------------- |
|
|
259
|
+
| `GET` | `/api/agent/files/<path.md>` | `read` + path match |
|
|
260
|
+
| `POST` | `/api/agent/files/<path.md>` | `mutate` + path match |
|
|
261
|
+
| `GET` | `/api/agent/events/<path.md>?after=<id>` | `read` + path match |
|
|
262
|
+
| `POST` | `/api/agent/events/<path.md>` | `read` + path match |
|
|
263
|
+
| `GET` | `/api/agent/sidecar/<path.md>` | `read` + path match |
|
|
264
|
+
| `GET` | `/api/agent/settings` | `read` |
|
|
265
|
+
| `GET` | `/api/agent/activity` | `read` |
|
|
266
|
+
|
|
267
|
+
### Full curl trace
|
|
268
|
+
|
|
269
|
+
```bash
|
|
270
|
+
# 1. Agent registers
|
|
271
|
+
curl -s -X POST https://wiki.team.com/api/agent/register \
|
|
272
|
+
-H "Content-Type: application/json" \
|
|
273
|
+
-d '{"id":"ai:claude","displayName":"Claude",
|
|
274
|
+
"scope":{"paths":["**/*"],"ops":["read","mutate"]}}'
|
|
275
|
+
# -> { "registrationId":"reg_abc","pollUrl":"/api/agent/register/reg_abc","status":"pending" }
|
|
276
|
+
|
|
277
|
+
# 2. Owner approves in the AI Panel.
|
|
278
|
+
|
|
279
|
+
# 3. Agent picks up the token (one shot, 410 on replay)
|
|
280
|
+
TOKEN=$(curl -s https://wiki.team.com/api/agent/register/reg_abc | jq -r .token)
|
|
281
|
+
|
|
282
|
+
# 4. Read a file
|
|
283
|
+
curl -s \
|
|
284
|
+
-H "Authorization: Bearer $TOKEN" \
|
|
285
|
+
-H "X-Agent-Id: ai:claude" \
|
|
286
|
+
https://wiki.team.com/api/agent/files/notes.md | jq
|
|
287
|
+
|
|
288
|
+
# 5. Mutate. `by` must equal `X-Agent-Id`. Idempotency-Key is required.
|
|
289
|
+
curl -s -X POST \
|
|
290
|
+
-H "Authorization: Bearer $TOKEN" \
|
|
291
|
+
-H "X-Agent-Id: ai:claude" \
|
|
292
|
+
-H "Content-Type: application/json" \
|
|
293
|
+
-H "Idempotency-Key: req-$(cat /proc/sys/kernel/random/uuid)" \
|
|
294
|
+
-d '{"baseRevision":7,"by":"ai:claude",
|
|
295
|
+
"ops":[{"type":"block.append","markdown":"From your agent."}]}' \
|
|
296
|
+
https://wiki.team.com/api/agent/files/notes.md
|
|
297
|
+
|
|
298
|
+
# 6. Poll events to see human comments, suggestion accepts, external edits
|
|
299
|
+
curl -s -H "Authorization: Bearer $TOKEN" -H "X-Agent-Id: ai:claude" \
|
|
300
|
+
"https://wiki.team.com/api/agent/events/notes.md?after=0" | jq
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
Response codes to handle:
|
|
304
|
+
|
|
305
|
+
- `401 UNAUTHORIZED` — bad token or `X-Agent-Id`.
|
|
306
|
+
- `403 FORBIDDEN` — out of scope or `by` mismatches identity.
|
|
307
|
+
- `409 STALE_REVISION` — refetch and retry. Response includes a fresh snapshot.
|
|
308
|
+
- `409 BLOCK_NOT_FOUND` — the ref no longer exists.
|
|
309
|
+
- `409 IDEMPOTENCY_KEY_REUSED` — same key, different body.
|
|
310
|
+
- `429 RATE_LIMITED` — honor `Retry-After`. Default 60 ops/minute per agent.
|
|
311
|
+
|
|
312
|
+
### Agent rate limit override
|
|
313
|
+
|
|
314
|
+
```bash
|
|
315
|
+
export AGENT_RATE_LIMIT=120
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
Note: `AGENT_BEARER_TOKEN` (legacy single-secret mode) does nothing now. The server logs a one-time warning if it is set. Remove it.
|
|
148
319
|
|
|
149
320
|
---
|
|
150
321
|
|
|
151
|
-
##
|
|
322
|
+
## Dev setup
|
|
152
323
|
|
|
153
|
-
|
|
324
|
+
### Prerequisites
|
|
154
325
|
|
|
155
|
-
|
|
326
|
+
- **Node.js** ≥ 18
|
|
327
|
+
- **pnpm** — `npm install -g pnpm`
|
|
328
|
+
|
|
329
|
+
### Run from source
|
|
156
330
|
|
|
157
331
|
```bash
|
|
158
|
-
# 1. Clone & install
|
|
159
332
|
git clone https://github.com/anh-chu/wiki-viewer.git
|
|
160
333
|
cd wiki-viewer
|
|
161
334
|
pnpm install
|
|
335
|
+
ROOT_DIR=~/notes pnpm dev
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
The dev server supports hot reload.
|
|
339
|
+
|
|
340
|
+
### Scripts
|
|
341
|
+
|
|
342
|
+
| Command | Description |
|
|
343
|
+
| ---------------- | -------------------------------------- |
|
|
344
|
+
| `pnpm dev` | Next.js development server |
|
|
345
|
+
| `pnpm dev:https` | Dev server with experimental HTTPS |
|
|
346
|
+
| `pnpm build` | Production build |
|
|
347
|
+
| `pnpm start` | Production server (after `build`) |
|
|
348
|
+
| `pnpm wiki` | CLI entry point (after `build`) |
|
|
349
|
+
| `pnpm test` | Run the proof + auth test suite (180+) |
|
|
350
|
+
|
|
351
|
+
### All environment variables
|
|
352
|
+
|
|
353
|
+
| Variable | Description | Default |
|
|
354
|
+
| ---------------------- | --------------------------------------------------------------------------------- | --------------------- |
|
|
355
|
+
| `ROOT_DIR` | Directory to serve | `~/wiki-viewer-files` |
|
|
356
|
+
| `PORT` | Port to listen on | `3000` |
|
|
357
|
+
| `HOSTNAME` | Host / interface to bind | `localhost` |
|
|
358
|
+
| `BETTER_AUTH_URL` | Public origin (required in production, must be `https://`) | unset |
|
|
359
|
+
| `BETTER_AUTH_SECRET` | Override for the auto-generated session signing secret | file-stored |
|
|
360
|
+
| `GOOGLE_CLIENT_ID` | Enable Google OAuth button | unset |
|
|
361
|
+
| `GOOGLE_CLIENT_SECRET` | Enable Google OAuth button | unset |
|
|
362
|
+
| `AUTH_ALLOWED_EMAILS` | csv: only these emails can sign up (overridden by UI allowlist if set) | unset (open) |
|
|
363
|
+
| `AUTH_ALLOWED_DOMAIN` | csv: only emails on these domains can sign up (overridden by UI allowlist if set) | unset (open) |
|
|
364
|
+
| `WIKI_OWNER_HOSTS` | csv: extra hostnames trusted for CSRF Origin check | `localhost,127.0.0.1` |
|
|
365
|
+
| `WIKI_ALLOW_INSECURE` | Set to `1` to bypass the prod-https guard (dev / CI only) | unset |
|
|
366
|
+
| `AGENT_RATE_LIMIT` | Max mutation ops per minute per agent identity | `60` |
|
|
367
|
+
|
|
368
|
+
---
|
|
369
|
+
|
|
370
|
+
## Self-hosted deployment
|
|
371
|
+
|
|
372
|
+
### Option A — Build and run directly
|
|
162
373
|
|
|
163
|
-
|
|
374
|
+
```bash
|
|
375
|
+
git clone https://github.com/anh-chu/wiki-viewer.git
|
|
376
|
+
cd wiki-viewer
|
|
377
|
+
pnpm install
|
|
164
378
|
pnpm build
|
|
165
379
|
|
|
166
|
-
#
|
|
380
|
+
# Static assets into the standalone output
|
|
167
381
|
cp -r .next/static .next/standalone/.next/static
|
|
168
382
|
cp -r public .next/standalone/public
|
|
169
383
|
|
|
170
|
-
#
|
|
171
|
-
ROOT_DIR=/
|
|
384
|
+
# Start
|
|
385
|
+
ROOT_DIR=/srv/notes BETTER_AUTH_URL=https://wiki.team.com \
|
|
386
|
+
AUTH_ALLOWED_DOMAIN=team.com \
|
|
387
|
+
node .next/standalone/server.js
|
|
172
388
|
```
|
|
173
389
|
|
|
174
|
-
Or use the CLI wrapper
|
|
390
|
+
Or use the CLI wrapper, which handles the static-asset copy:
|
|
175
391
|
|
|
176
392
|
```bash
|
|
177
|
-
node bin/wiki-viewer.js /
|
|
393
|
+
node bin/wiki-viewer.js /srv/notes
|
|
178
394
|
```
|
|
179
395
|
|
|
180
|
-
### Option B —
|
|
396
|
+
### Option B — PM2
|
|
181
397
|
|
|
182
398
|
```bash
|
|
183
399
|
npm install -g pm2
|
|
184
400
|
|
|
185
|
-
# Start and name the process
|
|
186
401
|
pm2 start bin/wiki-viewer.js \
|
|
187
402
|
--name wiki-viewer \
|
|
188
|
-
--
|
|
189
|
-
-- /path/to/your/files --port 3000
|
|
403
|
+
-- /srv/notes --port 3000
|
|
190
404
|
|
|
191
|
-
# Save so it auto-starts on reboot
|
|
192
405
|
pm2 save
|
|
193
|
-
pm2 startup
|
|
406
|
+
pm2 startup # follow printed instructions
|
|
194
407
|
```
|
|
195
408
|
|
|
196
|
-
### Option C — systemd
|
|
409
|
+
### Option C — systemd
|
|
197
410
|
|
|
198
|
-
|
|
411
|
+
`/etc/systemd/system/wiki-viewer.service`:
|
|
199
412
|
|
|
200
413
|
```ini
|
|
201
414
|
[Unit]
|
|
@@ -206,44 +419,44 @@ After=network.target
|
|
|
206
419
|
Type=simple
|
|
207
420
|
User=YOUR_USER
|
|
208
421
|
WorkingDirectory=/home/YOUR_USER/wiki-viewer
|
|
209
|
-
ExecStart=/usr/bin/node bin/wiki-viewer.js /
|
|
422
|
+
ExecStart=/usr/bin/node bin/wiki-viewer.js /srv/notes --port 3000 --host 0.0.0.0
|
|
210
423
|
Restart=on-failure
|
|
211
424
|
Environment=NODE_ENV=production
|
|
425
|
+
Environment=BETTER_AUTH_URL=https://wiki.team.com
|
|
426
|
+
Environment=AUTH_ALLOWED_DOMAIN=team.com
|
|
427
|
+
Environment=WIKI_OWNER_HOSTS=wiki.team.com
|
|
212
428
|
|
|
213
429
|
[Install]
|
|
214
430
|
WantedBy=multi-user.target
|
|
215
431
|
```
|
|
216
432
|
|
|
217
|
-
Then enable and start it:
|
|
218
|
-
|
|
219
433
|
```bash
|
|
220
434
|
sudo systemctl daemon-reload
|
|
221
435
|
sudo systemctl enable wiki-viewer
|
|
222
436
|
sudo systemctl start wiki-viewer
|
|
223
|
-
sudo
|
|
437
|
+
sudo journalctl -u wiki-viewer -f
|
|
224
438
|
```
|
|
225
439
|
|
|
226
|
-
### HTTPS
|
|
440
|
+
### HTTPS
|
|
227
441
|
|
|
228
|
-
|
|
442
|
+
`--https` runs an HTTPS reverse proxy in front of the internal HTTP server. mkcert is used when available, otherwise OpenSSL self-signed.
|
|
229
443
|
|
|
230
|
-
|
|
444
|
+
For real deployments, run wiki-viewer on plain HTTP behind nginx or Caddy with a Let's Encrypt cert. Configure the proxy to:
|
|
231
445
|
|
|
232
|
-
|
|
233
|
-
|
|
446
|
+
- Terminate TLS.
|
|
447
|
+
- Preserve the original `Host` header (`proxy_set_header Host $host;` in nginx; Caddy does this by default).
|
|
448
|
+
- Forward all paths.
|
|
234
449
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
**Certificate generation** (automatic, stored at `~/.wiki-viewer/certs/`):
|
|
238
|
-
|
|
239
|
-
- If **mkcert** is installed → locally-trusted cert (no browser warning)
|
|
240
|
-
- Otherwise → OpenSSL self-signed cert (browser warns once — click through)
|
|
241
|
-
|
|
242
|
-
```bash
|
|
243
|
-
node bin/wiki-viewer.js /path/to/your/files --https --port 443 --host 0.0.0.0
|
|
244
|
-
```
|
|
450
|
+
### Production deployment checklist
|
|
245
451
|
|
|
246
|
-
|
|
452
|
+
- [ ] `BETTER_AUTH_URL=https://your-domain` exported in the service environment.
|
|
453
|
+
- [ ] `AUTH_ALLOWED_DOMAIN` or `AUTH_ALLOWED_EMAILS` set before opening the server to anyone.
|
|
454
|
+
- [ ] `WIKI_OWNER_HOSTS` includes every hostname your users browse from.
|
|
455
|
+
- [ ] TLS handled by your reverse proxy or `--https`. Plain HTTP rejects in prod.
|
|
456
|
+
- [ ] `~/.wiki-viewer/` on local disk only. SQLite WAL is not safe on NFS or shared between replicas.
|
|
457
|
+
- [ ] Single host. If you cluster, you also need a shared lock service. Out of scope today.
|
|
458
|
+
- [ ] OAuth redirect URI registered with Google: `https://your-domain/api/auth/callback/google`.
|
|
459
|
+
- [ ] If you ran an older version: legacy agents in `agents.json` without `ownerUserId` are visible to and revocable by any signed-in user. Either edit the file to add `"ownerUserId": "<your user id>"` to each, or revoke and re-register them.
|
|
247
460
|
|
|
248
461
|
---
|
|
249
462
|
|
|
@@ -251,16 +464,32 @@ node bin/wiki-viewer.js /path/to/your/files --https --port 443 --host 0.0.0.0
|
|
|
251
464
|
|
|
252
465
|
```
|
|
253
466
|
wiki-viewer/
|
|
467
|
+
├── agents/ Installable Agent Skill + bootstrap prompt
|
|
468
|
+
│ ├── wiki-viewer-skill/ SKILL.md and assets
|
|
469
|
+
│ └── bootstrap-prompt.md One-paragraph prompt for any chat agent
|
|
254
470
|
├── bin/
|
|
255
|
-
│ └── wiki-viewer.js
|
|
471
|
+
│ └── wiki-viewer.js CLI entry point
|
|
472
|
+
├── docs/
|
|
473
|
+
│ ├── agent-collab-plan.md Full v1 spec for the agent HTTP protocol
|
|
474
|
+
│ └── agent-collab-v2-plan.md Reference for a future Yjs / multi-tenant pivot
|
|
256
475
|
├── src/
|
|
257
|
-
│ ├── app/
|
|
258
|
-
│ ├──
|
|
259
|
-
│ ├──
|
|
260
|
-
│ ├──
|
|
261
|
-
│
|
|
476
|
+
│ ├── app/
|
|
477
|
+
│ │ ├── api/agent/ Agent HTTP API
|
|
478
|
+
│ │ ├── api/agents/ Public install endpoints
|
|
479
|
+
│ │ ├── api/auth/ Better Auth handler
|
|
480
|
+
│ │ ├── api/wiki/ File browser API (session-gated)
|
|
481
|
+
│ │ ├── api/system/ System config API (session-gated)
|
|
482
|
+
│ │ └── signin/ Sign-in page
|
|
483
|
+
│ ├── components/
|
|
484
|
+
│ │ ├── editor/ TipTap editor + proof-span + comment-pip + suggestion-card
|
|
485
|
+
│ │ └── ai-panel/ Right-side AI panel (agents, activity, install)
|
|
486
|
+
│ ├── lib/
|
|
487
|
+
│ │ ├── auth/ Better Auth server + client + allowlist + CSRF
|
|
488
|
+
│ │ └── proof/ Agent protocol core (ops-applier, registry, file-lock)
|
|
489
|
+
│ ├── stores/ Zustand state
|
|
490
|
+
│ ├── tests/proof/ Node test runner suite (180+ tests)
|
|
491
|
+
│ └── middleware.ts Cookie-presence gate for UI routes
|
|
262
492
|
├── public/
|
|
263
|
-
│ └── logo.svg
|
|
264
493
|
├── next.config.ts
|
|
265
494
|
└── package.json
|
|
266
495
|
```
|
|
@@ -269,10 +498,10 @@ wiki-viewer/
|
|
|
269
498
|
|
|
270
499
|
## Contributing
|
|
271
500
|
|
|
272
|
-
1. Fork
|
|
273
|
-
2.
|
|
274
|
-
3.
|
|
275
|
-
4. Open a
|
|
501
|
+
1. Fork and branch: `git checkout -b my-feature`
|
|
502
|
+
2. `pnpm install && pnpm dev`
|
|
503
|
+
3. Run tests: `pnpm test`
|
|
504
|
+
4. Open a PR.
|
|
276
505
|
|
|
277
506
|
Bug reports and feature requests welcome via [GitHub Issues](https://github.com/anh-chu/wiki-viewer/issues).
|
|
278
507
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
You are connecting to a running wiki-viewer instance at $WIKI_URL (e.g. `http://localhost:3000`). Fetch `$WIKI_URL/api/agents/install`and follow the instructions there to register, wait for human approval, then read and edit markdown files via its HTTP API. Set`basis`and`basisDetail`on every content op so the human can see where your changes came from. Prefer`suggestion.add` over direct block ops unless told otherwise.
|