relic-mcp 0.9.0 → 0.11.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/.claude-plugin/marketplace.json +2 -2
- package/.claude-plugin/plugin.json +1 -1
- package/README.md +35 -8
- package/dist/cover/cover.css +273 -0
- package/dist/cover/cover.js +45121 -0
- package/dist/cover/frame.html +12812 -0
- package/dist/cover/index.html +14 -0
- package/dist/cover/pdf.worker.mjs +58272 -0
- package/dist/cover/styles.css +3671 -0
- package/dist/relic-mcp.js +28550 -3353
- package/package.json +1 -1
- package/skills/relic/SKILL.md +15 -4
- package/src/cover/archive.ts +418 -0
- package/src/cover/assets.ts +82 -0
- package/src/cover/cdp.ts +360 -0
- package/src/cover/chrome.ts +591 -0
- package/src/cover/inputs.ts +285 -0
- package/src/cover/media.ts +151 -0
- package/src/cover/raster.ts +182 -0
- package/src/cover/render.ts +329 -0
- package/src/cover/upload.ts +146 -0
- package/src/cover/virtual-origin.ts +108 -0
- package/src/index.ts +11 -0
- package/src/publish.ts +119 -0
- package/src/republish.ts +166 -7
- package/src/resolve-anchor.ts +185 -44
- package/src/server.ts +118 -7
- package/src/state.ts +36 -1
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
{
|
|
11
11
|
"name": "relic",
|
|
12
12
|
"description": "Publish a file from your machine as an encrypted, shareable link. The agent encrypts locally, uploads only ciphertext, and hands back a URL whose fragment holds the key, so the service stores something it cannot read.",
|
|
13
|
-
"version": "0.
|
|
13
|
+
"version": "0.11.0",
|
|
14
14
|
"source": "./",
|
|
15
15
|
"author": {
|
|
16
16
|
"name": "The Bushido Collective",
|
|
@@ -21,6 +21,6 @@
|
|
|
21
21
|
}
|
|
22
22
|
],
|
|
23
23
|
"metadata": {
|
|
24
|
-
"version": "0.
|
|
24
|
+
"version": "0.11.0"
|
|
25
25
|
}
|
|
26
26
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "relic",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.11.0",
|
|
4
4
|
"description": "Publish a file from your machine as an encrypted, shareable link. The agent encrypts locally, uploads only ciphertext, and hands back a URL whose fragment holds the key, so the service stores something it cannot read.",
|
|
5
5
|
"mcpServers": "./mcp-servers.json",
|
|
6
6
|
"author": {
|
package/README.md
CHANGED
|
@@ -31,9 +31,12 @@ For any client that takes a JSON config:
|
|
|
31
31
|
4. Uploads **only ciphertext**, straight to object storage under a signed URL.
|
|
32
32
|
It does not pass through the Relic service.
|
|
33
33
|
5. Tells the service four things: a coarse renderer class from an eight-value
|
|
34
|
-
list, this client's name, the ciphertext's byte length, and the title,
|
|
35
|
-
|
|
36
|
-
|
|
34
|
+
list, this client's name, the ciphertext's byte length, and the title, which
|
|
35
|
+
defaults to the filename and is stored in the clear. By default, it also
|
|
36
|
+
renders a 1200x630 cover image of the file's first screen in local headless
|
|
37
|
+
Chrome and uploads it unencrypted via `POST /api/relics/{id}/cover` for link
|
|
38
|
+
preview cards (pass `cover: false` to decline). Not the mimetype, not the
|
|
39
|
+
contents.
|
|
37
40
|
6. Records the relic's id, key, and publish token locally, in a 0600 file
|
|
38
41
|
under your user config directory, so the relic can be republished from
|
|
39
42
|
this machine later. See [Republishing](#republishing). The service never
|
|
@@ -50,7 +53,10 @@ because handing you a usable link is the point. **So the key enters your
|
|
|
50
53
|
model's context and your session transcript on every publish.** Zero-knowledge
|
|
51
54
|
holds against the Relic operator. It does not hold against your model provider
|
|
52
55
|
or whoever stores your transcripts. That is structural, not a defect awaiting
|
|
53
|
-
a fix.
|
|
56
|
+
a fix. The cover is plaintext pixels of the first screen, stored server-side
|
|
57
|
+
and served to anyone holding the link without its fragment, including unfurl
|
|
58
|
+
services and their caches; `cover: false` declines; republishing with it clears;
|
|
59
|
+
takedown deletes it.
|
|
54
60
|
|
|
55
61
|
## Republishing
|
|
56
62
|
|
|
@@ -143,18 +149,37 @@ fetched from the network and executed.
|
|
|
143
149
|
Because a local client is otherwise opaque, it ships as **readable source
|
|
144
150
|
rather than a compiled binary**. `dist/relic-mcp.js` is a single unminified
|
|
145
151
|
file and it is exactly what runs; the TypeScript it was built from is in the
|
|
146
|
-
same package.
|
|
147
|
-
|
|
152
|
+
same package. The cover rendering page assets in `dist/cover/` contain the
|
|
153
|
+
unminified cover bundle built from `@relic/viewer`, with vendored React, pdfjs,
|
|
154
|
+
and sucrase for offline document rendering in local Chrome. Releases carry npm
|
|
155
|
+
provenance, a cryptographic attestation binding the tarball to a specific commit
|
|
156
|
+
and workflow.
|
|
157
|
+
|
|
158
|
+
### Cover rendering and local Chrome requirement
|
|
159
|
+
|
|
160
|
+
By default, publishing or republishing renders a 1200x630 cover image in an
|
|
161
|
+
installed headless browser (Google Chrome, Chromium, or Microsoft Edge) over
|
|
162
|
+
Chrome DevTools Protocol (CDP) pipes. No browser is downloaded by this package.
|
|
163
|
+
If no browser is installed or if `RELIC_CHROME_PATH=none`, publishing still
|
|
164
|
+
succeeds and reports the cover as skipped with the reason, falling back to
|
|
165
|
+
Relic's generic preview card.
|
|
166
|
+
|
|
167
|
+
The cover renderer bundle lives in `dist/cover/`, served to Chrome over a
|
|
168
|
+
virtual origin (`https://cover.relic.invalid`) with strict network containment
|
|
169
|
+
flags (`--host-resolver-rules=MAP * ~NOTFOUND`,
|
|
170
|
+
`--proxy-server=http://proxy.relic.invalid:1`,
|
|
171
|
+
`--webrtc-ip-handling-policy=disable_non_proxied_udp`, `--disable-quic`) so no
|
|
172
|
+
relic plaintext leaves the machine during rendering.
|
|
148
173
|
|
|
149
174
|
## Tools
|
|
150
175
|
|
|
151
176
|
| Tool | Input | Notes |
|
|
152
177
|
|---|---|---|
|
|
153
|
-
| `relic_publish` | `path`, optional `filename`, `title`, `ttl_days` | A filesystem path. Inline content is deliberately not accepted, so the plaintext never joins the key in your transcript. A relic carries a plaintext title defaulting to the filename and stored by the service in the clear for link previews; pass `""` to publish without one. A relic is kept until it is deleted; `ttl_days` (an integer, 1 to 3650) gives it a lifetime. Reports the relic as version 1. |
|
|
178
|
+
| `relic_publish` | `path`, optional `filename`, `title`, `cover`, `ttl_days` | A filesystem path. Inline content is deliberately not accepted, so the plaintext never joins the key in your transcript. A relic carries a plaintext title defaulting to the filename and stored by the service in the clear for link previews; pass `""` to publish without one. By default, renders a 1200x630 cover image of the first screen in local headless Chrome and uploads it unencrypted for link preview cards; pass `cover: false` to decline. A relic is kept until it is deleted; `ttl_days` (an integer, 1 to 3650) gives it a lifetime. Reports the relic as version 1. |
|
|
154
179
|
| `relic_lookup_source` | `path` | Reads local state to find whether this machine already published that file, and returns the exact `relic_republish` call. Calls no server. |
|
|
155
180
|
| `relic_list` | optional `limit`, `include_expired`, `verify`, `refresh` | Every relic this machine published, newest first. The only tool that enumerates: lookup answers only from the file a relic came from. A name this machine never recorded is recovered by decrypting the relic's own envelope in one 64 KB range request, or comes back `null` with the reason. A relic the service will not serve is listed with which of removed, expired, exhausted, or unreachable it is. Each row carries the share URL, fragment included, so each row is a credential. Reaching the service spends one of a relic's finite opens; recovered names are cached, so a repeat listing spends none. |
|
|
156
181
|
| `relic_show` | `relic_id`, optional `include_content` | One relic in detail, including how many versions the service holds against how many this machine recorded, and with `include_content` the current decrypted content. Read it before republishing anything you did not just write: republish replaces what the link serves, so without the read-back the edit is blind. Content that is not valid UTF-8 is reported as size and type rather than mangled text. |
|
|
157
|
-
| `relic_republish` | `relic_id`, `path`, optional `filename`, `title`, `ttl_days` | Publishes a new version under the same key, so the share URL is unchanged. Replaces the plaintext title (defaults to the new filename; pass `""` to remove it). Works only on the machine holding that relic's key and publish token; a taken-down relic can never be revived. |
|
|
182
|
+
| `relic_republish` | `relic_id`, `path`, optional `filename`, `title`, `cover`, `ttl_days` | Publishes a new version under the same key, so the share URL is unchanged. Replaces the plaintext title (defaults to the new filename; pass `""` to remove it). Replaces the cover image with a newly rendered cover, or clears it if `cover: false` is passed. Works only on the machine holding that relic's key and publish token; a taken-down relic can never be revived. |
|
|
158
183
|
| `relic_read_comments` | `relic_id` | Returns the relic's comments oldest first, decrypted locally, with the author and a count of any that would not decrypt. Works only on the machine that published. |
|
|
159
184
|
| `relic_comment` | `relic_id`, `body`, optional `display_name` | Leaves a comment, encrypted on this machine, authorized by the publish token and attributed as `publisher`. Body caps at 4096 bytes of UTF-8. |
|
|
160
185
|
| `relic_describe_client` | none | Explains the encryption path. Reads nothing, sends nothing. |
|
|
@@ -171,6 +196,8 @@ binding the tarball to a specific commit and workflow.
|
|
|
171
196
|
| `RELIC_MCP_HTTP` | `1` to serve Streamable HTTP instead of stdio. |
|
|
172
197
|
| `RELIC_MCP_PORT`, `RELIC_MCP_HOST` | HTTP bind. Defaults to `127.0.0.1:7333`. |
|
|
173
198
|
| `RELIC_MCP_ALLOWED_ORIGINS` | Comma-separated `Origin` allowlist for HTTP. |
|
|
199
|
+
| `RELIC_CHROME_PATH` | Explicit path to Chrome, Chromium, or Edge binary for cover rendering. Set to `none` or `disabled` to skip covers. |
|
|
200
|
+
| `RELIC_FFMPEG_PATH` | Path to ffmpeg binary for generating video frame and audio waveform/artwork cover images. |
|
|
174
201
|
|
|
175
202
|
## Protocol
|
|
176
203
|
|
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
/* Cover layout and style overrides for Open Graph preview cards. */
|
|
2
|
+
|
|
3
|
+
html, body {
|
|
4
|
+
margin: 0;
|
|
5
|
+
padding: 0;
|
|
6
|
+
width: 600px;
|
|
7
|
+
height: 315px;
|
|
8
|
+
overflow: hidden;
|
|
9
|
+
background: var(--ground);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
.cv {
|
|
13
|
+
--ground: #f1f2f0;
|
|
14
|
+
--surface: #fbfbfa;
|
|
15
|
+
--ink: #16191a;
|
|
16
|
+
--ink-soft: #5a6163;
|
|
17
|
+
--rule: #d2d5d2;
|
|
18
|
+
--rule-strong: #b3b8b4;
|
|
19
|
+
--patina: #1f6b64;
|
|
20
|
+
--patina-ink: #0f3b37;
|
|
21
|
+
--oxide: #8c4a2f;
|
|
22
|
+
|
|
23
|
+
position: relative;
|
|
24
|
+
display: flex;
|
|
25
|
+
flex-direction: column;
|
|
26
|
+
width: 600px;
|
|
27
|
+
height: 315px;
|
|
28
|
+
overflow: hidden;
|
|
29
|
+
background: var(--ground);
|
|
30
|
+
color: var(--ink);
|
|
31
|
+
font-family: var(--sans);
|
|
32
|
+
font-size: 16px;
|
|
33
|
+
line-height: 1.55;
|
|
34
|
+
color-scheme: light;
|
|
35
|
+
-webkit-text-size-adjust: 100%;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
.cv *, .cv *::before, .cv *::after {
|
|
39
|
+
box-sizing: border-box;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
.cv, .cv * {
|
|
43
|
+
scrollbar-width: none;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/* The reading surface */
|
|
47
|
+
.cv .stage {
|
|
48
|
+
padding: 2rem 1.25rem 5rem;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
.cv .doc {
|
|
52
|
+
max-width: 46rem;
|
|
53
|
+
margin: 0 auto;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/* Code covers at 15 px override */
|
|
57
|
+
.cv .code {
|
|
58
|
+
font-size: 15px;
|
|
59
|
+
line-height: 24px;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/* Sandboxed html and jsx fill the surface */
|
|
63
|
+
.cv .stage-sandboxed-html,
|
|
64
|
+
.cv .stage-sandboxed-jsx {
|
|
65
|
+
display: flex;
|
|
66
|
+
flex-direction: column;
|
|
67
|
+
height: 100%;
|
|
68
|
+
padding: 0;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
.cv .stage-sandboxed-html .doc,
|
|
72
|
+
.cv .stage-sandboxed-jsx .doc {
|
|
73
|
+
flex: 1 1 auto;
|
|
74
|
+
min-height: 0;
|
|
75
|
+
margin: 0;
|
|
76
|
+
max-width: none;
|
|
77
|
+
height: 100%;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
.cv .usercontent-frame {
|
|
81
|
+
display: block;
|
|
82
|
+
width: 100%;
|
|
83
|
+
height: 100%;
|
|
84
|
+
border: 0;
|
|
85
|
+
background: #fff;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/* Picture kinds fill the canvas */
|
|
89
|
+
.cv-picture img {
|
|
90
|
+
display: block;
|
|
91
|
+
width: 600px;
|
|
92
|
+
height: 315px;
|
|
93
|
+
object-fit: cover;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
.cv-image img {
|
|
97
|
+
object-position: 50% 0%;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
.cv-video img {
|
|
101
|
+
object-position: 50% 50%;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/* PDF */
|
|
105
|
+
.cv-pdf {
|
|
106
|
+
background: #fff;
|
|
107
|
+
position: relative;
|
|
108
|
+
width: 600px;
|
|
109
|
+
height: 315px;
|
|
110
|
+
overflow: hidden;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/* Fallback card */
|
|
114
|
+
.cv-card img {
|
|
115
|
+
display: block;
|
|
116
|
+
width: 600px;
|
|
117
|
+
height: 315px;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/* Archive listing layout */
|
|
121
|
+
.cv-archive .stage {
|
|
122
|
+
height: 100%;
|
|
123
|
+
padding: 2rem 1.25rem 0;
|
|
124
|
+
display: flex;
|
|
125
|
+
flex-direction: column;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
.ar {
|
|
129
|
+
--gutter: 3.25rem;
|
|
130
|
+
--row: 24px;
|
|
131
|
+
flex: 1 1 auto;
|
|
132
|
+
min-height: 0;
|
|
133
|
+
margin: 0;
|
|
134
|
+
padding: 0.25rem 0;
|
|
135
|
+
list-style: none;
|
|
136
|
+
font-family: var(--mono);
|
|
137
|
+
font-size: 15px;
|
|
138
|
+
line-height: 24px;
|
|
139
|
+
border: 1px solid var(--rule);
|
|
140
|
+
border-bottom: 0;
|
|
141
|
+
background:
|
|
142
|
+
linear-gradient(to right,
|
|
143
|
+
var(--ground) 0 var(--gutter),
|
|
144
|
+
var(--rule) var(--gutter) calc(var(--gutter) + 1px),
|
|
145
|
+
var(--surface) calc(var(--gutter) + 1px));
|
|
146
|
+
overflow: hidden;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
.ar-row {
|
|
150
|
+
display: grid;
|
|
151
|
+
grid-template-columns: var(--gutter) minmax(0, 1fr) auto;
|
|
152
|
+
height: var(--row);
|
|
153
|
+
align-items: center;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
.ar-num {
|
|
157
|
+
padding-right: 0.75rem;
|
|
158
|
+
text-align: right;
|
|
159
|
+
color: var(--ink-soft);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
.ar-path {
|
|
163
|
+
--depth: 0;
|
|
164
|
+
height: 100%;
|
|
165
|
+
display: flex;
|
|
166
|
+
align-items: center;
|
|
167
|
+
min-width: 0;
|
|
168
|
+
padding-left: calc(0.85rem + var(--depth) * 2ch);
|
|
169
|
+
white-space: nowrap;
|
|
170
|
+
background-image: repeating-linear-gradient(to right,
|
|
171
|
+
transparent 0 0.45ch,
|
|
172
|
+
var(--rule-strong) 0.45ch calc(0.45ch + 1px),
|
|
173
|
+
transparent calc(0.45ch + 1px) 2ch);
|
|
174
|
+
background-size: calc(var(--depth) * 2ch) 100%;
|
|
175
|
+
background-position: 0.85rem 0;
|
|
176
|
+
background-repeat: no-repeat;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
.ar-dir {
|
|
180
|
+
flex: none;
|
|
181
|
+
color: var(--ink-soft);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
.ar-base {
|
|
185
|
+
flex: 0 1 auto;
|
|
186
|
+
min-width: 0;
|
|
187
|
+
overflow: hidden;
|
|
188
|
+
text-overflow: ellipsis;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
.ar-lead {
|
|
192
|
+
flex: 1 1 0;
|
|
193
|
+
min-width: 1.5ch;
|
|
194
|
+
align-self: stretch;
|
|
195
|
+
margin-left: 0.75ch;
|
|
196
|
+
background: linear-gradient(to right, var(--rule-strong) 1px, transparent 1px)
|
|
197
|
+
repeat-x 0 calc(50% + 0.3em) / 5px 1px;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
.ar-row.is-dir .ar-base {
|
|
201
|
+
color: var(--patina-ink);
|
|
202
|
+
font-weight: 600;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
.ar-row.is-dir .ar-lead,
|
|
206
|
+
.ar-row.is-more .ar-lead {
|
|
207
|
+
display: none;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
.ar-size {
|
|
211
|
+
padding: 0 0.9rem 0 0.75ch;
|
|
212
|
+
text-align: right;
|
|
213
|
+
color: var(--ink-soft);
|
|
214
|
+
font-variant-numeric: tabular-nums;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
.ar-row.is-more .ar-path {
|
|
218
|
+
color: var(--ink-soft);
|
|
219
|
+
font-style: italic;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/* Audio artwork layout */
|
|
223
|
+
.cv-artwork .stage {
|
|
224
|
+
height: 100%;
|
|
225
|
+
padding: 1.25rem;
|
|
226
|
+
display: flex;
|
|
227
|
+
align-items: center;
|
|
228
|
+
justify-content: center;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/* Sized whole into the largest square the stage leaves (fitArtwork). */
|
|
232
|
+
.cv-artwork img {
|
|
233
|
+
display: block;
|
|
234
|
+
flex: none;
|
|
235
|
+
border: 1px solid var(--rule);
|
|
236
|
+
background: var(--surface);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/* Audio waveform layout */
|
|
240
|
+
.cv-waveform .stage {
|
|
241
|
+
height: 100%;
|
|
242
|
+
padding: 2rem 1.25rem;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
.wf {
|
|
246
|
+
position: relative;
|
|
247
|
+
height: 100%;
|
|
248
|
+
background: var(--surface);
|
|
249
|
+
border: 1px solid var(--rule);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
.wf-axis {
|
|
253
|
+
position: absolute;
|
|
254
|
+
left: 1.25rem;
|
|
255
|
+
right: 1.25rem;
|
|
256
|
+
top: 50%;
|
|
257
|
+
height: 1px;
|
|
258
|
+
background: var(--rule-strong);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
.wf-wave {
|
|
262
|
+
position: absolute;
|
|
263
|
+
inset: 1.5rem 1.25rem;
|
|
264
|
+
background: var(--patina-ink);
|
|
265
|
+
-webkit-mask-image: var(--wf-src);
|
|
266
|
+
mask-image: var(--wf-src);
|
|
267
|
+
-webkit-mask-repeat: no-repeat;
|
|
268
|
+
mask-repeat: no-repeat;
|
|
269
|
+
-webkit-mask-size: 100% var(--wf-h, 100%);
|
|
270
|
+
mask-size: 100% var(--wf-h, 100%);
|
|
271
|
+
-webkit-mask-position: 0 var(--wf-y, 50%);
|
|
272
|
+
mask-position: 0 var(--wf-y, 50%);
|
|
273
|
+
}
|