relic-mcp 0.14.3 → 0.14.4
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 +4 -3
- package/dist/cover/cover.js +9 -2
- package/dist/cover/styles.css +30 -3
- package/dist/relic-mcp.js +113 -4
- package/mcp-servers.json +2 -2
- package/package.json +1 -1
- package/prebuilds/darwin-arm64/build-info.json +1 -1
- package/prebuilds/darwin-x64/build-info.json +1 -1
- package/prebuilds/linux-arm64-gnu/build-info.json +1 -1
- package/prebuilds/linux-x64-gnu/build-info.json +1 -1
- package/src/cover/inputs.ts +25 -1
- package/src/publish.ts +11 -1
- package/src/republish.ts +7 -2
|
@@ -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.14.
|
|
13
|
+
"version": "0.14.4",
|
|
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.14.
|
|
24
|
+
"version": "0.14.4"
|
|
25
25
|
}
|
|
26
26
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "relic",
|
|
3
|
-
"version": "0.14.
|
|
3
|
+
"version": "0.14.4",
|
|
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
|
@@ -32,9 +32,9 @@ For any client that takes a JSON config:
|
|
|
32
32
|
3. Encrypts locally with AES-128-GCM under RFC 8188 `aes128gcm` framing.
|
|
33
33
|
4. Uploads **only ciphertext**, straight to object storage under a signed URL.
|
|
34
34
|
It does not pass through the Relic service.
|
|
35
|
-
5. Tells the service four things: a coarse renderer class from
|
|
36
|
-
list (`markdown`, `code`, `html`, `jsx`, `image`, `pdf`, `
|
|
37
|
-
`binary`, `otio`), this client's name, the ciphertext's byte length, and the
|
|
35
|
+
5. Tells the service four things: a coarse renderer class from an eleven-value
|
|
36
|
+
list (`markdown`, `code`, `html`, `jsx`, `image`, `pdf`, `model`, `media`,
|
|
37
|
+
`archive`, `binary`, `otio`), this client's name, the ciphertext's byte length, and the
|
|
38
38
|
title, which defaults to the filename and is stored in the clear. By default,
|
|
39
39
|
it also renders a 1200x630 cover image of the file's first screen in local
|
|
40
40
|
headless Chrome and uploads it unencrypted via `POST /api/relics/{id}/cover`
|
|
@@ -42,6 +42,7 @@ For any client that takes a JSON config:
|
|
|
42
42
|
timeline relics (`otio`) skip unencrypted first-screen media rendering by
|
|
43
43
|
default, using a generic card carrying the title and format label to protect
|
|
44
44
|
private timeline cuts and media from leaking into unencrypted link previews.
|
|
45
|
+
STL and OBJ models also use a generic card instead of a geometry preview.
|
|
45
46
|
Not the mimetype, not the contents.
|
|
46
47
|
6. Records the relic's id, key, and publish token locally, in a 0600 file
|
|
47
48
|
under your user config directory, so the relic can be republished from
|
package/dist/cover/cover.js
CHANGED
|
@@ -1300,8 +1300,15 @@ function renderMarkdown(source) {
|
|
|
1300
1300
|
flushParagraph();
|
|
1301
1301
|
const ordered = /^\s*\d+\./.test(line);
|
|
1302
1302
|
const items = [];
|
|
1303
|
-
while (index < lines.length
|
|
1304
|
-
|
|
1303
|
+
while (index < lines.length) {
|
|
1304
|
+
const current = lines[index];
|
|
1305
|
+
if (/^\s*([-*+]|\d+\.)\s+/.test(current)) {
|
|
1306
|
+
items.push(current.replace(/^\s*([-*+]|\d+\.)\s+/, ""));
|
|
1307
|
+
} else if (/^\s+\S/.test(current) && !/^\s*(>|```)/.test(current)) {
|
|
1308
|
+
items[items.length - 1] += ` ${current.trim()}`;
|
|
1309
|
+
} else {
|
|
1310
|
+
break;
|
|
1311
|
+
}
|
|
1305
1312
|
index++;
|
|
1306
1313
|
}
|
|
1307
1314
|
const tag = ordered ? "ol" : "ul";
|
package/dist/cover/styles.css
CHANGED
|
@@ -4786,7 +4786,16 @@ canvas.relic-page {
|
|
|
4786
4786
|
.stage-otio > .notice { flex: none; }
|
|
4787
4787
|
.otio-view { --otio-label-width: 174px; display: flex; flex-direction: column; flex: 1 1 0; min-height: 0; min-width: 0; width: 100%; }
|
|
4788
4788
|
.otio-loading { margin: 1rem; font: .8rem var(--mono); color: var(--ink-soft); }
|
|
4789
|
-
.otio-monitor-row { display: grid; grid-template-columns: minmax(0, 1fr) 244px; grid-template-rows: minmax(0, 1fr); flex: 1 1 0; min-height:
|
|
4789
|
+
.otio-monitor-row { display: grid; grid-template-columns: minmax(0, 1fr) 244px; grid-template-rows: minmax(0, 1fr); flex: 1 1 0; min-height: 0; border-bottom: 1px solid var(--rule-strong); }
|
|
4790
|
+
.otio-resize-handle { position: relative; flex: 0 0 16px; min-width: 0; cursor: row-resize; touch-action: none; user-select: none; background: var(--ground); border-bottom: 1px solid var(--rule-strong); }
|
|
4791
|
+
.otio-resize-handle::before { content: ""; position: absolute; top: 50%; left: 50%; width: 36px; height: 4px; transform: translate(-50%, -50%); border-block: 1px solid var(--ink-soft); }
|
|
4792
|
+
.otio-resize-handle:hover::before, .otio-resize-handle:focus-visible::before, .otio-view.is-resizing .otio-resize-handle::before { border-color: var(--patina); }
|
|
4793
|
+
.otio-resize-handle:focus-visible { outline: 2px solid var(--patina); outline-offset: -2px; }
|
|
4794
|
+
.otio-resize-handle[aria-disabled="true"] { cursor: default; }
|
|
4795
|
+
.otio-view.is-resizing, .otio-view.is-resizing * { cursor: row-resize !important; user-select: none !important; }
|
|
4796
|
+
@media (pointer: coarse) {
|
|
4797
|
+
.otio-resize-handle { flex-basis: 24px; }
|
|
4798
|
+
}
|
|
4790
4799
|
.otio-preview { position: relative; background: #080a0b; min-width: 0; min-height: 0; display: grid; place-items: center; overflow: hidden; }
|
|
4791
4800
|
.otio-canvas { display: block; width: 100%; height: 100%; min-width: 0; min-height: 0; object-fit: contain; }
|
|
4792
4801
|
.otio-preview-scope { position: absolute; top: .7rem; left: .7rem; max-width: calc(100% - 1.4rem); padding: .2rem .35rem; color: #e6e9e7; background: #080a0bd9; font: .6rem/1.5 var(--mono); }
|
|
@@ -4856,6 +4865,10 @@ canvas.relic-page {
|
|
|
4856
4865
|
.otio-transport { display: flex; flex-wrap: wrap; align-items: center; gap: .5rem; min-height: 46px; padding: .55rem .75rem; border-bottom: 1px solid var(--rule-strong); background: var(--surface); }
|
|
4857
4866
|
.otio-transport button { padding: .3rem .45rem; border: 1px solid var(--rule-strong); background: transparent; color: var(--ink); font: .65rem var(--sans); cursor: pointer; }
|
|
4858
4867
|
.otio-transport .otio-play { background: var(--patina); border-color: var(--patina); color: var(--surface); min-width: 44px; }
|
|
4868
|
+
.otio-focus-context { display: flex; align-items: center; gap: .35rem .65rem; flex: 1 0 100%; min-width: 0; padding-bottom: .35rem; border-bottom: 1px solid var(--rule); }
|
|
4869
|
+
.otio-focus-title { flex: 1 1 0; min-width: 0; overflow-wrap: anywhere; color: var(--patina-ink); font: 600 .68rem/1.5 var(--sans); }
|
|
4870
|
+
.otio-transport .otio-back-composition { flex: none; color: var(--patina-ink); }
|
|
4871
|
+
.otio-frame-icon { display: none; width: 14px; height: 14px; fill: none; stroke: currentColor; stroke-width: 1.5; }
|
|
4859
4872
|
.otio-time { font: .7rem var(--mono); font-variant-numeric: tabular-nums; }
|
|
4860
4873
|
.otio-seek { min-width: 65px; width: 100px; flex: 1; accent-color: var(--patina); cursor: pointer; }
|
|
4861
4874
|
.otio-transport .otio-inspector-toggle { display: none; }
|
|
@@ -4867,6 +4880,7 @@ canvas.relic-page {
|
|
|
4867
4880
|
.otio-mode[aria-pressed="true"] { border-color: var(--patina); background: var(--diff-added); color: var(--patina-ink); }
|
|
4868
4881
|
.otio-track-select, .otio-timeline-select { max-width: 190px; min-width: 0; border: 1px solid var(--rule-strong); background: var(--surface); color: var(--ink); padding: .28rem; padding-inline-end: 1.5rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font: .62rem var(--sans); }
|
|
4869
4882
|
.otio-timeline-scroll { flex: 0 1 auto; min-height: 0; max-height: min(240px, 30%); max-width: 100%; overflow: auto; overscroll-behavior: contain; scroll-padding-inline-start: var(--otio-label-width); }
|
|
4883
|
+
.otio-view.is-resized .otio-timeline-scroll { flex: 0 0 auto; max-height: none; }
|
|
4870
4884
|
.otio-track-grid { position: relative; min-width: 760px; }
|
|
4871
4885
|
.otio-ruler, .otio-track-row { display: grid; grid-template-columns: var(--otio-label-width) minmax(0, 1fr); border-bottom: 1px solid var(--rule); }
|
|
4872
4886
|
.otio-ruler-label { position: sticky; z-index: 7; left: 0; padding: .35rem .6rem; border-right: 1px solid var(--rule-strong); background: var(--surface); color: var(--ink-soft); font: .6rem var(--mono); }
|
|
@@ -4880,7 +4894,8 @@ canvas.relic-page {
|
|
|
4880
4894
|
.otio-track-label { position: sticky; left: 0; z-index: 6; display: flex; align-items: center; gap: .35rem; min-width: 0; padding: .4rem .45rem; border-right: 1px solid var(--rule-strong); background: var(--surface); }
|
|
4881
4895
|
.otio-track-code { flex: 0 0 auto; color: var(--ink-soft); font: .6rem var(--mono); }
|
|
4882
4896
|
.otio-track-name { flex: 1; min-width: 0; line-height: 1.35; }
|
|
4883
|
-
.otio-track-
|
|
4897
|
+
.otio-track-activate { display: block; width: 100%; padding: 0; border: 0; background: transparent; color: var(--ink); text-align: left; font: 500 .65rem/1.35 var(--sans); overflow-wrap: anywhere; cursor: pointer; }
|
|
4898
|
+
.otio-track-activate[aria-pressed="true"] { color: var(--patina-ink); text-decoration: underline; text-underline-offset: .2em; }
|
|
4884
4899
|
.otio-track-name small { display: block; color: var(--ink-soft); font: .48rem var(--mono); overflow-wrap: anywhere; }
|
|
4885
4900
|
.otio-track-row:not([data-depth="0"]) .otio-track-label { padding-left: calc(.45rem + min(var(--otio-depth, 0), 4) * .35rem); background: var(--ground); }
|
|
4886
4901
|
.otio-track-toggle, .otio-disclosure { flex: 0 0 auto; border: 0; background: transparent; color: var(--ink-soft); padding: .2rem; font: .53rem var(--sans); cursor: pointer; }
|
|
@@ -4903,6 +4918,9 @@ canvas.relic-page {
|
|
|
4903
4918
|
.otio-item.has-diagnostic { border-color: var(--oxide); color: var(--oxide); }
|
|
4904
4919
|
.otio-item.is-unplaced, .otio-item.is-trimmed-out { position: relative; display: block; left: auto; top: auto; bottom: auto; width: auto; margin: .2rem; border-style: dashed; color: var(--oxide); white-space: normal; }
|
|
4905
4920
|
.otio-item.is-trimmed-out { color: var(--ink-soft); opacity: .6; }
|
|
4921
|
+
.otio-item[aria-pressed="true"] { border-color: var(--patina); box-shadow: inset 0 0 0 1px var(--patina); background-color: var(--diff-added); color: var(--patina-ink); opacity: 1; }
|
|
4922
|
+
.otio-item[aria-pressed="true"][data-enabled="false"] { border-style: dashed; }
|
|
4923
|
+
.otio-focus-status { font: .75rem/1.6 var(--mono); color: #e6e9e7; }
|
|
4906
4924
|
.otio-timeline-marker { position: absolute; top: 0; height: 5px; min-width: 7px; z-index: 4; padding: 0; border: 1px solid var(--ink); border-radius: 0; background: var(--otio-marker-color, var(--patina)); cursor: pointer; }
|
|
4907
4925
|
.otio-timeline-marker.is-point { top: 1px; width: 7px; height: 7px; z-index: 5; transform: translateX(-50%) rotate(45deg); }
|
|
4908
4926
|
.otio-selection { position: absolute; top: 1px; bottom: 1px; z-index: 3; min-width: 4px; border: 2px dashed var(--patina); background: color-mix(in srgb, var(--patina) 22%, transparent); pointer-events: none; }
|
|
@@ -4965,6 +4983,10 @@ canvas.relic-page {
|
|
|
4965
4983
|
padding-top: 0.15rem;
|
|
4966
4984
|
background: var(--surface);
|
|
4967
4985
|
}
|
|
4986
|
+
.popover-card.is-otio-composer.is-focused-otio-composer { padding: .2rem .4rem; }
|
|
4987
|
+
.popover-card.is-otio-composer.is-focused-otio-composer .popover-bounded-body { min-height: 0; }
|
|
4988
|
+
.popover-card.is-otio-composer.is-focused-otio-composer .popover-field { flex: 0 1 3rem; height: 3rem; min-height: calc(1.3em + .4rem + 2px); padding: .2rem .3rem; font-size: .85rem; line-height: 1.3; }
|
|
4989
|
+
.popover-card.is-otio-composer.is-focused-otio-composer .popover-tools { margin-top: .15rem; padding-top: .15rem; }
|
|
4968
4990
|
@media (max-width: 64rem) {
|
|
4969
4991
|
.otio-monitor-row { grid-template-columns: minmax(0, 1fr) 210px; }
|
|
4970
4992
|
.otio-selection-tools { width: 100%; }
|
|
@@ -4978,8 +5000,13 @@ canvas.relic-page {
|
|
|
4978
5000
|
.otio-transport .otio-inspector-toggle { display: block; }
|
|
4979
5001
|
.otio-transport { gap: .4rem; padding: .5rem; }
|
|
4980
5002
|
.otio-seek { flex-basis: 100%; order: 2; }
|
|
5003
|
+
.otio-transport .otio-previous-frame, .otio-transport .otio-next-frame { display: inline-grid; place-items: center; width: 32px; min-height: 28px; padding: .25rem; font-size: 0; }
|
|
5004
|
+
.otio-frame-icon { display: block; }
|
|
5005
|
+
.otio-focus-context { gap: .25rem .5rem; }
|
|
5006
|
+
.otio-focus-title { flex-basis: 8rem; font-size: .63rem; }
|
|
4981
5007
|
.otio-timeline-heading { padding: .65rem .5rem; }
|
|
4982
|
-
.otio-track-select { max-width: 160px; }
|
|
5008
|
+
.otio-track-select { flex: 1 1 7rem; width: 7rem; max-width: 160px; }
|
|
5009
|
+
.otio-view.is-element-focused .otio-selection-label { display: none; }
|
|
4983
5010
|
.otio-preview-scope { font-size: .5rem; }
|
|
4984
5011
|
.otio-preview-status { padding: 0 .5rem; overscroll-behavior: contain; }
|
|
4985
5012
|
.otio-preview:has(> .otio-preview-status:not([hidden])) > .otio-preview-scope { margin-top: .35rem; }
|
package/dist/relic-mcp.js
CHANGED
|
@@ -26495,6 +26495,69 @@ function generateRelicId() {
|
|
|
26495
26495
|
// ../relic-format/src/index.ts
|
|
26496
26496
|
init_mnemonic();
|
|
26497
26497
|
|
|
26498
|
+
// ../relic-format/src/model.ts
|
|
26499
|
+
function modelFormatFromFilename(filename) {
|
|
26500
|
+
const dot = filename.lastIndexOf(".");
|
|
26501
|
+
if (dot <= filename.lastIndexOf("/") || dot < 0)
|
|
26502
|
+
return;
|
|
26503
|
+
const ext = filename.slice(dot + 1).toLowerCase();
|
|
26504
|
+
return ext === "stl" || ext === "obj" ? ext : undefined;
|
|
26505
|
+
}
|
|
26506
|
+
function modelFormatFromMime(mimetype) {
|
|
26507
|
+
const mime = mimetype.toLowerCase().split(";")[0]?.trim();
|
|
26508
|
+
if (mime === "model/stl")
|
|
26509
|
+
return "stl";
|
|
26510
|
+
if (mime === "model/obj")
|
|
26511
|
+
return "obj";
|
|
26512
|
+
return;
|
|
26513
|
+
}
|
|
26514
|
+
function sniffModelFormat(content) {
|
|
26515
|
+
if (content.length >= 84) {
|
|
26516
|
+
const count = new DataView(content.buffer, content.byteOffset, content.byteLength).getUint32(80, true);
|
|
26517
|
+
if (count > 0 && 84 + count * 50 === content.length)
|
|
26518
|
+
return "stl";
|
|
26519
|
+
}
|
|
26520
|
+
const decoder = new TextDecoder("utf-8", { fatal: true });
|
|
26521
|
+
let pending = "";
|
|
26522
|
+
let hasSolid = false;
|
|
26523
|
+
let hasFacet = false;
|
|
26524
|
+
let hasNormal = false;
|
|
26525
|
+
let hasVertex = false;
|
|
26526
|
+
let hasFace = false;
|
|
26527
|
+
try {
|
|
26528
|
+
for (let offset = 0;offset < content.length; offset += 65536) {
|
|
26529
|
+
const end = Math.min(content.length, offset + 65536);
|
|
26530
|
+
pending += decoder.decode(content.subarray(offset, end), {
|
|
26531
|
+
stream: end < content.length
|
|
26532
|
+
});
|
|
26533
|
+
const lines = pending.split(/[\r\n]/);
|
|
26534
|
+
pending = lines.pop() ?? "";
|
|
26535
|
+
if (pending.length > 65536)
|
|
26536
|
+
pending = "";
|
|
26537
|
+
if (end === content.length)
|
|
26538
|
+
lines.push(pending);
|
|
26539
|
+
for (const line of lines) {
|
|
26540
|
+
if (/^\s*solid(?:\s|$)/i.test(line))
|
|
26541
|
+
hasSolid = true;
|
|
26542
|
+
if (/\bfacet\b/i.test(line))
|
|
26543
|
+
hasFacet = true;
|
|
26544
|
+
if (/\bnormal\b/i.test(line))
|
|
26545
|
+
hasNormal = true;
|
|
26546
|
+
if (/^\s*v\s+[-+\d.]/.test(line))
|
|
26547
|
+
hasVertex = true;
|
|
26548
|
+
if (/^\s*f\s+\S+\s+\S+\s+\S+/.test(line))
|
|
26549
|
+
hasFace = true;
|
|
26550
|
+
if (hasSolid && hasFacet && hasNormal)
|
|
26551
|
+
return "stl";
|
|
26552
|
+
if (hasVertex && hasFace)
|
|
26553
|
+
return "obj";
|
|
26554
|
+
}
|
|
26555
|
+
}
|
|
26556
|
+
} catch {
|
|
26557
|
+
return;
|
|
26558
|
+
}
|
|
26559
|
+
return;
|
|
26560
|
+
}
|
|
26498
26561
|
// ../relic-format/src/otio-json.ts
|
|
26499
26562
|
class OtioRawNumber {
|
|
26500
26563
|
raw;
|
|
@@ -29562,6 +29625,7 @@ function privilegeTier(cls) {
|
|
|
29562
29625
|
return 3;
|
|
29563
29626
|
case "image":
|
|
29564
29627
|
case "pdf":
|
|
29628
|
+
case "model":
|
|
29565
29629
|
return 2;
|
|
29566
29630
|
case "markdown":
|
|
29567
29631
|
case "code":
|
|
@@ -29583,6 +29647,11 @@ function deriveRendererClass(content, filename) {
|
|
|
29583
29647
|
}
|
|
29584
29648
|
}
|
|
29585
29649
|
const extension = extensionOf(filename);
|
|
29650
|
+
const candidate = sniffModelFormat(content);
|
|
29651
|
+
const declaredModel = modelFormatFromFilename(filename);
|
|
29652
|
+
if (candidate && (declaredModel === candidate || !declaredModel && (extension === undefined || EXTENSIONS[extension] === undefined)) && sniffContentClass(content) !== "html") {
|
|
29653
|
+
return "model";
|
|
29654
|
+
}
|
|
29586
29655
|
if (extension !== undefined) {
|
|
29587
29656
|
const fromExtension = EXTENSIONS[extension];
|
|
29588
29657
|
if (fromExtension !== undefined) {
|
|
@@ -29610,6 +29679,7 @@ function leastPrivileged(first, second) {
|
|
|
29610
29679
|
"jsx",
|
|
29611
29680
|
"image",
|
|
29612
29681
|
"pdf",
|
|
29682
|
+
"model",
|
|
29613
29683
|
"markdown",
|
|
29614
29684
|
"code",
|
|
29615
29685
|
"media",
|
|
@@ -30294,6 +30364,23 @@ var MAX_RENDER_BYTES = 100 * 1024 * 1024;
|
|
|
30294
30364
|
function routeFor(filename, declaredMimetype, content) {
|
|
30295
30365
|
const declared = classFromMimetype(declaredMimetype, filename);
|
|
30296
30366
|
let sniffed = sniffContentClass(content);
|
|
30367
|
+
const filenameModel = modelFormatFromFilename(filename);
|
|
30368
|
+
const mimeModel = modelFormatFromMime(declaredMimetype);
|
|
30369
|
+
const mime = declaredMimetype.toLowerCase().split(";")[0]?.trim() ?? "";
|
|
30370
|
+
const genericMime = mime === "" || mime === "application/octet-stream" || mime === "text/plain";
|
|
30371
|
+
const candidate = sniffed === "code" || sniffed === "binary" ? sniffModelFormat(content) : undefined;
|
|
30372
|
+
const declaredModel = filenameModel ?? mimeModel;
|
|
30373
|
+
if (declaredModel || candidate) {
|
|
30374
|
+
const namedClass = deriveRendererClass(new Uint8Array([0]), filename);
|
|
30375
|
+
const conflicting = filenameModel && mimeModel && filenameModel !== mimeModel || declaredModel && candidate && declaredModel !== candidate || filenameModel && !genericMime && !mimeModel || candidate && !filenameModel && namedClass !== "binary" || candidate && !declaredModel && !genericMime;
|
|
30376
|
+
if (candidate && !conflicting && (!declaredModel || candidate === declaredModel)) {
|
|
30377
|
+
return { route: "model", notice: undefined };
|
|
30378
|
+
}
|
|
30379
|
+
return {
|
|
30380
|
+
route: "download",
|
|
30381
|
+
notice: "The file name or type does not match its contents. Download it to inspect it."
|
|
30382
|
+
};
|
|
30383
|
+
}
|
|
30297
30384
|
if (declared === "jsx" && sniffed === "code") {
|
|
30298
30385
|
const source = new TextDecoder("utf-8", { fatal: false }).decode(content);
|
|
30299
30386
|
if (isComponentSource(source, filename))
|
|
@@ -30320,6 +30407,8 @@ function routeForClass(cls) {
|
|
|
30320
30407
|
return "media";
|
|
30321
30408
|
case "otio":
|
|
30322
30409
|
return "otio";
|
|
30410
|
+
case "model":
|
|
30411
|
+
return "model";
|
|
30323
30412
|
case "html":
|
|
30324
30413
|
return "sandboxed-html";
|
|
30325
30414
|
case "jsx":
|
|
@@ -30340,6 +30429,8 @@ function classFromMimetype(mimetype, filename) {
|
|
|
30340
30429
|
return "html";
|
|
30341
30430
|
if (type === "text/jsx" || type === "text/tsx")
|
|
30342
30431
|
return "jsx";
|
|
30432
|
+
if (type === "model/stl" || type === "model/obj")
|
|
30433
|
+
return "model";
|
|
30343
30434
|
if (type.startsWith("image/"))
|
|
30344
30435
|
return "image";
|
|
30345
30436
|
if (type.startsWith("video/") || type.startsWith("audio/"))
|
|
@@ -30376,6 +30467,8 @@ function describe(cls) {
|
|
|
30376
30467
|
return "audio or video";
|
|
30377
30468
|
case "otio":
|
|
30378
30469
|
return "an OpenTimelineIO timeline";
|
|
30470
|
+
case "model":
|
|
30471
|
+
return "a 3D model";
|
|
30379
30472
|
case "archive":
|
|
30380
30473
|
return "an archive";
|
|
30381
30474
|
default:
|
|
@@ -31274,6 +31367,7 @@ async function publish(input, deps) {
|
|
|
31274
31367
|
});
|
|
31275
31368
|
}
|
|
31276
31369
|
const filename = input.filename ?? source.basename;
|
|
31370
|
+
const coverFilename = modelFormatFromFilename(source.basename) === undefined ? filename : source.basename;
|
|
31277
31371
|
const normalizedTitle = normalizeTitle(input.title ?? filename);
|
|
31278
31372
|
const rendererClass = deriveRendererClass(source.bytes, filename);
|
|
31279
31373
|
const digest = await contentDigest({
|
|
@@ -31293,7 +31387,7 @@ async function publish(input, deps) {
|
|
|
31293
31387
|
try {
|
|
31294
31388
|
coverOutcome = await deps.renderCover({
|
|
31295
31389
|
bytes: source.bytes,
|
|
31296
|
-
filename,
|
|
31390
|
+
filename: coverFilename,
|
|
31297
31391
|
mimetype: guessMimetype(filename, rendererClass),
|
|
31298
31392
|
rendererClass
|
|
31299
31393
|
});
|
|
@@ -31595,12 +31689,16 @@ var CLASS_FALLBACK = {
|
|
|
31595
31689
|
pdf: "application/pdf",
|
|
31596
31690
|
image: "application/octet-stream",
|
|
31597
31691
|
media: "application/octet-stream",
|
|
31692
|
+
model: "application/octet-stream",
|
|
31598
31693
|
otio: "application/x-otioz",
|
|
31599
31694
|
archive: "application/octet-stream",
|
|
31600
31695
|
binary: "application/octet-stream"
|
|
31601
31696
|
};
|
|
31602
31697
|
function guessMimetype(filename, rendererClass) {
|
|
31603
31698
|
const base = filename.slice(filename.lastIndexOf("/") + 1).toLowerCase();
|
|
31699
|
+
const modelFormat = modelFormatFromFilename(base);
|
|
31700
|
+
if (modelFormat !== undefined)
|
|
31701
|
+
return `model/${modelFormat}`;
|
|
31604
31702
|
const dot = base.lastIndexOf(".");
|
|
31605
31703
|
if (dot > 0 && dot < base.length - 1) {
|
|
31606
31704
|
const found = MIMETYPES[base.slice(dot + 1)];
|
|
@@ -34523,6 +34621,16 @@ async function extractMediaCover(bytes, filename, mimetype, ffmpegPath, totalTim
|
|
|
34523
34621
|
// src/cover/inputs.ts
|
|
34524
34622
|
var TEXT_PREFIX_BYTES = 1048576;
|
|
34525
34623
|
async function planCover(source, options) {
|
|
34624
|
+
if (source.rendererClass === "model" || modelFormatFromFilename(source.filename) !== undefined || modelFormatFromMime(source.mimetype) !== undefined || sniffModelFormat(source.bytes) !== undefined) {
|
|
34625
|
+
return {
|
|
34626
|
+
kind: "skip",
|
|
34627
|
+
outcome: {
|
|
34628
|
+
status: "skipped",
|
|
34629
|
+
reason: "no_cover_for_class",
|
|
34630
|
+
detail: "Generic cover used for 3D model (public title and format label only)"
|
|
34631
|
+
}
|
|
34632
|
+
};
|
|
34633
|
+
}
|
|
34526
34634
|
if (source.bytes.length === 0 && (source.rendererClass === "markdown" || source.rendererClass === "code")) {
|
|
34527
34635
|
return {
|
|
34528
34636
|
kind: "skip",
|
|
@@ -35479,6 +35587,7 @@ async function republish(input, deps) {
|
|
|
35479
35587
|
}
|
|
35480
35588
|
const source = await readSource(input.path, deps.files);
|
|
35481
35589
|
const filename = input.filename ?? source.basename;
|
|
35590
|
+
const coverFilename = modelFormatFromFilename(source.basename) === undefined ? filename : source.basename;
|
|
35482
35591
|
const normalizedTitle = normalizeTitle(input.title ?? filename);
|
|
35483
35592
|
const rendererClass = deriveRendererClass(source.bytes, filename);
|
|
35484
35593
|
const digest = await contentDigest({
|
|
@@ -35509,7 +35618,7 @@ async function republish(input, deps) {
|
|
|
35509
35618
|
}
|
|
35510
35619
|
const outcome = await deps.renderCover({
|
|
35511
35620
|
bytes: source.bytes,
|
|
35512
|
-
filename,
|
|
35621
|
+
filename: coverFilename,
|
|
35513
35622
|
mimetype: guessMimetype(filename, rendererClass),
|
|
35514
35623
|
rendererClass
|
|
35515
35624
|
});
|
|
@@ -35539,7 +35648,7 @@ async function republish(input, deps) {
|
|
|
35539
35648
|
try {
|
|
35540
35649
|
coverOutcome = await deps.renderCover({
|
|
35541
35650
|
bytes: source.bytes,
|
|
35542
|
-
filename,
|
|
35651
|
+
filename: coverFilename,
|
|
35543
35652
|
mimetype: guessMimetype(filename, rendererClass),
|
|
35544
35653
|
rendererClass
|
|
35545
35654
|
});
|
|
@@ -36993,7 +37102,7 @@ var DESCRIBE_TOOL_DEFINITION = {
|
|
|
36993
37102
|
additionalProperties: false
|
|
36994
37103
|
}
|
|
36995
37104
|
};
|
|
36996
|
-
var SERVER_VERSION = "0.14.
|
|
37105
|
+
var SERVER_VERSION = "0.14.4";
|
|
36997
37106
|
var SERVER_INFO = {
|
|
36998
37107
|
name: "relic",
|
|
36999
37108
|
title: "Relic",
|
package/mcp-servers.json
CHANGED
package/package.json
CHANGED
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
},
|
|
20
20
|
"nodeApiVersion": 8,
|
|
21
21
|
"schemaVersion": 3,
|
|
22
|
-
"sourceSha256": "
|
|
22
|
+
"sourceSha256": "d9c7ba4de9fdca6b2652ff25d58aa1aa2afe38312af26376561ba1543ff5fad0",
|
|
23
23
|
"target": "darwin-arm64",
|
|
24
24
|
"toolchain": {
|
|
25
25
|
"binaryDeploymentTarget": "15.0",
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
},
|
|
20
20
|
"nodeApiVersion": 8,
|
|
21
21
|
"schemaVersion": 3,
|
|
22
|
-
"sourceSha256": "
|
|
22
|
+
"sourceSha256": "d9c7ba4de9fdca6b2652ff25d58aa1aa2afe38312af26376561ba1543ff5fad0",
|
|
23
23
|
"target": "darwin-x64",
|
|
24
24
|
"toolchain": {
|
|
25
25
|
"binaryDeploymentTarget": "15.0",
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
},
|
|
21
21
|
"nodeApiVersion": 8,
|
|
22
22
|
"schemaVersion": 3,
|
|
23
|
-
"sourceSha256": "
|
|
23
|
+
"sourceSha256": "d9c7ba4de9fdca6b2652ff25d58aa1aa2afe38312af26376561ba1543ff5fad0",
|
|
24
24
|
"target": "linux-arm64-gnu",
|
|
25
25
|
"toolchain": {
|
|
26
26
|
"compilerPath": "/opt/rh/gcc-toolset-14/root/usr/bin/gcc",
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
},
|
|
21
21
|
"nodeApiVersion": 8,
|
|
22
22
|
"schemaVersion": 3,
|
|
23
|
-
"sourceSha256": "
|
|
23
|
+
"sourceSha256": "d9c7ba4de9fdca6b2652ff25d58aa1aa2afe38312af26376561ba1543ff5fad0",
|
|
24
24
|
"target": "linux-x64-gnu",
|
|
25
25
|
"toolchain": {
|
|
26
26
|
"compilerPath": "/opt/rh/gcc-toolset-14/root/usr/bin/gcc",
|
package/src/cover/inputs.ts
CHANGED
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
modelFormatFromFilename,
|
|
3
|
+
modelFormatFromMime,
|
|
4
|
+
sniffContentClass,
|
|
5
|
+
sniffModelFormat,
|
|
6
|
+
} from '@relic/format';
|
|
2
7
|
import {
|
|
3
8
|
COVER_INPUT_PART_BYTES,
|
|
4
9
|
type CoverOutcome,
|
|
@@ -31,6 +36,25 @@ export async function planCover(
|
|
|
31
36
|
source: CoverSource,
|
|
32
37
|
options?: PlanCoverOptions
|
|
33
38
|
): Promise<CoverPlan> {
|
|
39
|
+
// Covers are public. Never pass declared or byte-recognized model content
|
|
40
|
+
// to a text, image, or media renderer, even when declarations disagree.
|
|
41
|
+
if (
|
|
42
|
+
source.rendererClass === 'model' ||
|
|
43
|
+
modelFormatFromFilename(source.filename) !== undefined ||
|
|
44
|
+
modelFormatFromMime(source.mimetype) !== undefined ||
|
|
45
|
+
sniffModelFormat(source.bytes) !== undefined
|
|
46
|
+
) {
|
|
47
|
+
return {
|
|
48
|
+
kind: 'skip',
|
|
49
|
+
outcome: {
|
|
50
|
+
status: 'skipped',
|
|
51
|
+
reason: 'no_cover_for_class',
|
|
52
|
+
detail:
|
|
53
|
+
'Generic cover used for 3D model (public title and format label only)',
|
|
54
|
+
},
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
34
58
|
if (
|
|
35
59
|
source.bytes.length === 0 &&
|
|
36
60
|
(source.rendererClass === 'markdown' || source.rendererClass === 'code')
|
package/src/publish.ts
CHANGED
|
@@ -21,6 +21,7 @@ import {
|
|
|
21
21
|
generateKey,
|
|
22
22
|
generateRelicId,
|
|
23
23
|
indexOtioZip,
|
|
24
|
+
modelFormatFromFilename,
|
|
24
25
|
normalizeTitle,
|
|
25
26
|
PLAINTEXT_CAP_BYTES,
|
|
26
27
|
parseOtioJson,
|
|
@@ -378,6 +379,12 @@ export async function publish(
|
|
|
378
379
|
}
|
|
379
380
|
|
|
380
381
|
const filename = input.filename ?? source.basename;
|
|
382
|
+
// A display-name override must not hide the source's model extension from
|
|
383
|
+
// the public-cover guard.
|
|
384
|
+
const coverFilename =
|
|
385
|
+
modelFormatFromFilename(source.basename) === undefined
|
|
386
|
+
? filename
|
|
387
|
+
: source.basename;
|
|
381
388
|
const normalizedTitle = normalizeTitle(input.title ?? filename);
|
|
382
389
|
// The class is derived from bytes this process already holds, never taken as
|
|
383
390
|
// a tool input. Exposing it as a parameter would make the taxonomy
|
|
@@ -404,7 +411,7 @@ export async function publish(
|
|
|
404
411
|
try {
|
|
405
412
|
coverOutcome = await deps.renderCover({
|
|
406
413
|
bytes: source.bytes,
|
|
407
|
-
filename,
|
|
414
|
+
filename: coverFilename,
|
|
408
415
|
mimetype: guessMimetype(filename, rendererClass),
|
|
409
416
|
rendererClass,
|
|
410
417
|
});
|
|
@@ -977,6 +984,7 @@ const CLASS_FALLBACK: Readonly<Record<RendererClass, string>> = {
|
|
|
977
984
|
pdf: 'application/pdf',
|
|
978
985
|
image: 'application/octet-stream',
|
|
979
986
|
media: 'application/octet-stream',
|
|
987
|
+
model: 'application/octet-stream',
|
|
980
988
|
otio: 'application/x-otioz',
|
|
981
989
|
archive: 'application/octet-stream',
|
|
982
990
|
binary: 'application/octet-stream',
|
|
@@ -994,6 +1002,8 @@ export function guessMimetype(
|
|
|
994
1002
|
rendererClass: RendererClass
|
|
995
1003
|
): string {
|
|
996
1004
|
const base = filename.slice(filename.lastIndexOf('/') + 1).toLowerCase();
|
|
1005
|
+
const modelFormat = modelFormatFromFilename(base);
|
|
1006
|
+
if (modelFormat !== undefined) return `model/${modelFormat}`;
|
|
997
1007
|
const dot = base.lastIndexOf('.');
|
|
998
1008
|
if (dot > 0 && dot < base.length - 1) {
|
|
999
1009
|
const found = MIMETYPES[base.slice(dot + 1)];
|
package/src/republish.ts
CHANGED
|
@@ -16,6 +16,7 @@ import {
|
|
|
16
16
|
deriveShareProof,
|
|
17
17
|
encryptRelic,
|
|
18
18
|
isValidRelicId,
|
|
19
|
+
modelFormatFromFilename,
|
|
19
20
|
normalizeTitle,
|
|
20
21
|
type RendererClass,
|
|
21
22
|
shareVerifierFor,
|
|
@@ -214,6 +215,10 @@ export async function republish(
|
|
|
214
215
|
// still open, so naming the duplicate first names the cause.
|
|
215
216
|
const source = await readSource(input.path, deps.files);
|
|
216
217
|
const filename = input.filename ?? source.basename;
|
|
218
|
+
const coverFilename =
|
|
219
|
+
modelFormatFromFilename(source.basename) === undefined
|
|
220
|
+
? filename
|
|
221
|
+
: source.basename;
|
|
217
222
|
const normalizedTitle = normalizeTitle(input.title ?? filename);
|
|
218
223
|
// Same rule as a first publish: the class comes from bytes in hand, never
|
|
219
224
|
// from a tool input, so the taxonomy stays machine-attested.
|
|
@@ -265,7 +270,7 @@ export async function republish(
|
|
|
265
270
|
// same && requested && !previous -> render first
|
|
266
271
|
const outcome = await deps.renderCover({
|
|
267
272
|
bytes: source.bytes,
|
|
268
|
-
filename,
|
|
273
|
+
filename: coverFilename,
|
|
269
274
|
mimetype: guessMimetype(filename, rendererClass),
|
|
270
275
|
rendererClass,
|
|
271
276
|
});
|
|
@@ -305,7 +310,7 @@ export async function republish(
|
|
|
305
310
|
try {
|
|
306
311
|
coverOutcome = await deps.renderCover({
|
|
307
312
|
bytes: source.bytes,
|
|
308
|
-
filename,
|
|
313
|
+
filename: coverFilename,
|
|
309
314
|
mimetype: guessMimetype(filename, rendererClass),
|
|
310
315
|
rendererClass,
|
|
311
316
|
});
|