@pharaoh-so/mcp 0.3.46 → 0.3.49
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/.mcp.json +3 -1
- package/CHANGELOG.md +23 -0
- package/README.md +29 -2
- package/dist/backend.d.ts +6 -2
- package/dist/backend.js +43 -4
- package/dist/credentials.d.ts +9 -0
- package/dist/credentials.js +15 -6
- package/dist/index.js +17 -2
- package/dist/inspect.js +2 -0
- package/dist/install-skills.d.ts +63 -10
- package/dist/install-skills.js +162 -59
- package/dist/proxy-server.d.ts +4 -0
- package/dist/proxy-server.js +45 -3
- package/dist/setup.d.ts +13 -4
- package/dist/setup.js +19 -19
- package/dist/trace-resource.d.ts +3 -0
- package/dist/trace-resource.js +41 -0
- package/dist/version.d.ts +30 -0
- package/dist/version.js +66 -4
- package/inspect-tools.json +35 -10
- package/package.json +72 -69
- package/skills/pharaoh/SKILL.md +1 -1
- package/trace-app.html +296 -0
package/trace-app.html
ADDED
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width,initial-scale=1">
|
|
6
|
+
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; script-src 'unsafe-inline'; style-src 'unsafe-inline'; connect-src 'none'; img-src 'none'; font-src 'none'; object-src 'none'; frame-src 'none'; base-uri 'none'; form-action 'none'">
|
|
7
|
+
<title>Pharaoh Trace</title>
|
|
8
|
+
<style>
|
|
9
|
+
:root { color-scheme:dark; --bg:#171813; --text:#e5e6d8; --muted:#aaa995; --gold:#d4ad51; --yellow:#edc54c; --rule:#414236; --hover:#22251c; --error:#e3a18d; font:400 12px/1.55 ui-monospace,SFMono-Regular,Menlo,Consolas,"Liberation Mono",monospace; background:var(--bg); color:var(--text); }
|
|
10
|
+
:root[data-theme="light"] { color-scheme:light; --bg:#faf8f1; --text:#292b20; --muted:#656553; --gold:#866315; --yellow:#926700; --rule:#d8d4c5; --hover:#efede2; --error:#a13929; }
|
|
11
|
+
* { box-sizing:border-box; }
|
|
12
|
+
body { margin:0; min-width:0; }
|
|
13
|
+
#trace { border-left:2px solid var(--gold); padding:14px 18px 12px; min-width:0; }
|
|
14
|
+
header { display:flex; flex-wrap:wrap; justify-content:space-between; align-items:baseline; gap:3px 14px; color:var(--muted); font-size:10px; }
|
|
15
|
+
#tool { overflow-wrap:anywhere; min-width:0; }
|
|
16
|
+
#status { white-space:nowrap; }
|
|
17
|
+
#status[data-state="ok"] { color:#4ade80; }
|
|
18
|
+
#status[data-state="pending"],.filled { color:var(--yellow); }
|
|
19
|
+
#status[data-state="error"] { color:var(--error); }
|
|
20
|
+
#trace[data-pending="true"] #status { animation:pending 1.4s ease-in-out infinite alternate; }
|
|
21
|
+
@keyframes pending { from { opacity:.55; } to { opacity:1; } }
|
|
22
|
+
#repo { font-size:14px; margin:9px 0 0; overflow-wrap:anywhere; }
|
|
23
|
+
#provenance { color:var(--muted); font-size:10px; overflow-wrap:anywhere; margin-top:2px; }
|
|
24
|
+
h1 { font-size:14px; line-height:1.5; font-weight:500; margin:13px 0 0; overflow-wrap:anywhere; }
|
|
25
|
+
#summary { margin:4px 0 0; color:var(--muted); overflow-wrap:anywhere; }
|
|
26
|
+
ul { list-style:none; padding:0; margin:8px 0 0; min-width:0; }
|
|
27
|
+
li { min-width:0; }
|
|
28
|
+
summary,.row { display:grid; grid-template-columns:18px minmax(0,1fr) auto; gap:6px; align-items:baseline; padding:6px 0; min-width:0; }
|
|
29
|
+
summary { list-style:none; border-bottom:1px solid var(--rule); cursor:pointer; }
|
|
30
|
+
summary::-webkit-details-marker { display:none; }
|
|
31
|
+
summary:hover { background:var(--hover); }
|
|
32
|
+
summary:focus-visible { outline:2px solid var(--gold); outline-offset:2px; }
|
|
33
|
+
.branch { color:var(--gold); white-space:pre; }
|
|
34
|
+
details>summary .branch::after { content:"+"; }
|
|
35
|
+
details[open]>summary .branch::after { content:"−"; }
|
|
36
|
+
.label,.detail,.badge { overflow-wrap:anywhere; white-space:pre-wrap; min-width:0; unicode-bidi:plaintext; }
|
|
37
|
+
details[open]>summary .label { color:var(--gold); }
|
|
38
|
+
.detail { display:block; color:var(--muted); font-size:10px; margin-top:2px; }
|
|
39
|
+
.badge { color:var(--muted); font-size:10px; text-align:right; }
|
|
40
|
+
.badge[data-state="ok"] { color:#4ade80; }
|
|
41
|
+
.badge[data-state="warning"],.badge[data-state="pending"] { color:var(--yellow); }
|
|
42
|
+
.badge[data-state="error"] { color:var(--error); }
|
|
43
|
+
.children { margin:0 0 6px 17px; border-left:1px solid var(--rule); padding-left:9px; }
|
|
44
|
+
.meter { display:block; color:var(--muted); font-size:11px; margin:4px 0; overflow-wrap:anywhere; }
|
|
45
|
+
#notes { border-top:1px solid var(--rule); margin-top:10px; padding-top:8px; color:var(--muted); font-size:10px; }
|
|
46
|
+
#notes p { margin:3px 0; overflow-wrap:anywhere; white-space:pre-wrap; }
|
|
47
|
+
#fallback { font:inherit; font-size:11px; margin:10px 0 0; white-space:pre-wrap; overflow-wrap:anywhere; unicode-bidi:plaintext; }
|
|
48
|
+
[hidden] { display:none; }
|
|
49
|
+
@media(max-width:400px) { #trace { padding:12px 10px 10px; } summary,.row { grid-template-columns:15px minmax(0,1fr); gap:4px; } .badge { grid-column:2; text-align:left; } .children { margin-left:9px; padding-left:7px; } }
|
|
50
|
+
@media(prefers-reduced-motion:reduce) { #trace[data-pending="true"] #status { animation:none; } }
|
|
51
|
+
</style>
|
|
52
|
+
</head>
|
|
53
|
+
<body>
|
|
54
|
+
<main id="trace" data-pending="false">
|
|
55
|
+
<header><span id="tool">Pharaoh result</span><span id="status" role="status" aria-live="polite" aria-atomic="true">waiting</span></header>
|
|
56
|
+
<div id="repo" hidden></div>
|
|
57
|
+
<div id="provenance" hidden></div>
|
|
58
|
+
<h1 id="title">Waiting for a tool result</h1>
|
|
59
|
+
<p id="summary" hidden></p>
|
|
60
|
+
<ul id="tree" aria-label="Result evidence" hidden></ul>
|
|
61
|
+
<pre id="fallback">The tool's text response remains available in chat.</pre>
|
|
62
|
+
<div id="notes" hidden></div>
|
|
63
|
+
</main>
|
|
64
|
+
<script>
|
|
65
|
+
(() => {
|
|
66
|
+
"use strict";
|
|
67
|
+
const META_KEY = "so.pharaoh/trace";
|
|
68
|
+
const PROTOCOL_VERSION = "2026-01-26";
|
|
69
|
+
// Kept in parity with TRACE_LIMITS in trace-contract.ts by the resource test.
|
|
70
|
+
const LIMITS = { bytes:65536, nodes:120, depth:6, label:512, detail:2048, badge:128, summary:1024, notes:16, note:1024, repo:256 };
|
|
71
|
+
const MAX_TEXT = 32768;
|
|
72
|
+
const root = document.getElementById("trace");
|
|
73
|
+
const fields = Object.fromEntries(["tool","status","repo","provenance","title","summary","tree","fallback","notes"].map(id => [id,document.getElementById(id)]));
|
|
74
|
+
const initId = "pharaoh-trace-initialize";
|
|
75
|
+
let ready = false;
|
|
76
|
+
let closed = false;
|
|
77
|
+
let cancelled = false;
|
|
78
|
+
let resizeFrame = 0;
|
|
79
|
+
let lastHeight = 0;
|
|
80
|
+
let lastWidth = 0;
|
|
81
|
+
const record = value => value !== null && typeof value === "object" && !Array.isArray(value);
|
|
82
|
+
const string = (value,max) => typeof value === "string" && value.length <= max;
|
|
83
|
+
const send = message => { if (!closed) window.parent.postMessage({jsonrpc:"2.0",...message},"*"); };
|
|
84
|
+
function status(label,state) {
|
|
85
|
+
fields.status.textContent = label;
|
|
86
|
+
fields.status.dataset.state = state;
|
|
87
|
+
root.dataset.pending = String(state === "pending");
|
|
88
|
+
}
|
|
89
|
+
function display(field,text) {
|
|
90
|
+
fields[field].textContent = text || "";
|
|
91
|
+
fields[field].hidden = !text;
|
|
92
|
+
}
|
|
93
|
+
function notes(items) {
|
|
94
|
+
fields.notes.replaceChildren();
|
|
95
|
+
for (const text of items) {
|
|
96
|
+
const paragraph = document.createElement("p");
|
|
97
|
+
paragraph.textContent = text;
|
|
98
|
+
fields.notes.append(paragraph);
|
|
99
|
+
}
|
|
100
|
+
fields.notes.hidden = items.length === 0;
|
|
101
|
+
}
|
|
102
|
+
function clearResult() {
|
|
103
|
+
fields.tree.replaceChildren();
|
|
104
|
+
fields.tree.hidden = true;
|
|
105
|
+
display("summary","");
|
|
106
|
+
display("provenance","");
|
|
107
|
+
notes([]);
|
|
108
|
+
}
|
|
109
|
+
function fallback(title,text,notice,state="neutral") {
|
|
110
|
+
clearResult();
|
|
111
|
+
fields.title.textContent = title;
|
|
112
|
+
display("fallback",text);
|
|
113
|
+
status(state === "error" ? "[!] error" : state === "cancelled" ? "[||] cancelled" : "response received",state);
|
|
114
|
+
if (notice) notes([notice]);
|
|
115
|
+
}
|
|
116
|
+
function readableText(result) {
|
|
117
|
+
if (!record(result) || !Array.isArray(result.content)) return {text:"No readable text was returned. Read the tool response in chat.",omitted:false};
|
|
118
|
+
let text = "";
|
|
119
|
+
let omitted = result.content.length > 64;
|
|
120
|
+
for (const block of result.content.slice(0,64)) {
|
|
121
|
+
if (!record(block) || block.type !== "text" || typeof block.text !== "string") continue;
|
|
122
|
+
const remaining = MAX_TEXT - text.length;
|
|
123
|
+
const separator = text ? 2 : 0;
|
|
124
|
+
if (remaining <= separator) { omitted = true; break; }
|
|
125
|
+
const part = block.text.slice(0,remaining - separator);
|
|
126
|
+
if (text) text += "\n\n";
|
|
127
|
+
text += part;
|
|
128
|
+
if (part.length < block.text.length) { omitted = true; break; }
|
|
129
|
+
}
|
|
130
|
+
return {text:text || "No readable text was returned. Read the tool response in chat.",omitted};
|
|
131
|
+
}
|
|
132
|
+
function validView(view) {
|
|
133
|
+
try {
|
|
134
|
+
if (!record(view) || view.version !== 1 || !string(view.title,LIMITS.label) || !view.title.trim() || !Array.isArray(view.nodes)) return false;
|
|
135
|
+
if (new TextEncoder().encode(JSON.stringify(view)).byteLength > LIMITS.bytes) return false;
|
|
136
|
+
if (view.repo !== undefined && !string(view.repo,LIMITS.repo)) return false;
|
|
137
|
+
if (view.summary !== undefined && !string(view.summary,LIMITS.summary)) return false;
|
|
138
|
+
if (view.notes !== undefined && (!Array.isArray(view.notes) || view.notes.length > LIMITS.notes || !view.notes.every(note => string(note,LIMITS.note)))) return false;
|
|
139
|
+
if (view.freshness !== undefined) {
|
|
140
|
+
const value = view.freshness;
|
|
141
|
+
if (!record(value) || !string(value.status,80) || !(value.mappedCommit === null || string(value.mappedCommit,160)) || !(value.lastAnalyzed === null || string(value.lastAnalyzed,160))) return false;
|
|
142
|
+
}
|
|
143
|
+
let count = 0;
|
|
144
|
+
const visit = (node,depth) => {
|
|
145
|
+
if (++count > LIMITS.nodes || depth > LIMITS.depth || !record(node) || !string(node.label,LIMITS.label) || !node.label.trim()) return false;
|
|
146
|
+
if (node.detail !== undefined && !string(node.detail,LIMITS.detail)) return false;
|
|
147
|
+
if (node.badge !== undefined && !string(node.badge,LIMITS.badge)) return false;
|
|
148
|
+
if (node.status !== undefined && !["ok","pending","warning","error"].includes(node.status)) return false;
|
|
149
|
+
if (node.meter !== undefined && (!record(node.meter) || !Number.isFinite(node.meter.value) || !Number.isFinite(node.meter.total) || node.meter.total <= 0 || node.meter.value < 0 || node.meter.value > node.meter.total)) return false;
|
|
150
|
+
return node.children === undefined || (Array.isArray(node.children) && node.children.every(child => visit(child,depth + 1)));
|
|
151
|
+
};
|
|
152
|
+
return view.nodes.every(node => visit(node,1));
|
|
153
|
+
} catch { return false; }
|
|
154
|
+
}
|
|
155
|
+
function element(tag,className,text) {
|
|
156
|
+
const node = document.createElement(tag);
|
|
157
|
+
if (className) node.className = className;
|
|
158
|
+
if (text !== undefined) node.textContent = text;
|
|
159
|
+
return node;
|
|
160
|
+
}
|
|
161
|
+
function renderNode(node,last,open=false) {
|
|
162
|
+
const item = element("li");
|
|
163
|
+
const group = Array.isArray(node.children) && node.children.length > 0;
|
|
164
|
+
const container = group ? element("details") : item;
|
|
165
|
+
if (group && open) container.open = true;
|
|
166
|
+
const row = element(group ? "summary" : "div",group ? "" : "row");
|
|
167
|
+
const branch = element("span","branch",group ? "" : last ? "└─" : "├─");
|
|
168
|
+
branch.setAttribute("aria-hidden","true");
|
|
169
|
+
const label = element("span","label",node.label);
|
|
170
|
+
if (node.detail) label.append(element("span","detail",node.detail));
|
|
171
|
+
const symbol = {ok:"[ok]",pending:"[...]",warning:"[!]",error:"[!]"}[node.status] || "";
|
|
172
|
+
const badge = element("span","badge",[symbol,node.badge].filter(Boolean).join(" "));
|
|
173
|
+
if (node.status) badge.dataset.state = node.status;
|
|
174
|
+
row.append(branch,label,badge);
|
|
175
|
+
container.append(row);
|
|
176
|
+
if (node.meter) {
|
|
177
|
+
const filled = Math.round(node.meter.value / node.meter.total * 12);
|
|
178
|
+
const meter = element("span","meter");
|
|
179
|
+
meter.append("[",element("span","filled","#".repeat(filled)),`${".".repeat(12-filled)}] ${node.meter.value} / ${node.meter.total}`);
|
|
180
|
+
label.append(meter);
|
|
181
|
+
}
|
|
182
|
+
if (group) {
|
|
183
|
+
const children = element("ul","children");
|
|
184
|
+
node.children.forEach((child,index) => { children.append(renderNode(child,index === node.children.length-1)); });
|
|
185
|
+
container.append(children);
|
|
186
|
+
item.append(container);
|
|
187
|
+
}
|
|
188
|
+
return item;
|
|
189
|
+
}
|
|
190
|
+
function renderView(view) {
|
|
191
|
+
clearResult();
|
|
192
|
+
fields.title.textContent = view.title;
|
|
193
|
+
display("repo",view.repo);
|
|
194
|
+
display("summary",view.summary);
|
|
195
|
+
display("fallback","");
|
|
196
|
+
if (view.freshness) {
|
|
197
|
+
const freshness = view.freshness;
|
|
198
|
+
display("provenance",[freshness.mappedCommit ? `mapped ${freshness.mappedCommit}` : "mapped commit unknown",freshness.lastAnalyzed ? `as of ${freshness.lastAnalyzed}` : "map time unknown",`status: ${freshness.status}`].join(" · "));
|
|
199
|
+
}
|
|
200
|
+
const firstGroup = view.nodes.findIndex(node => node.children?.length);
|
|
201
|
+
view.nodes.forEach((node,index) => { fields.tree.append(renderNode(node,index === view.nodes.length-1,index === firstGroup)); });
|
|
202
|
+
fields.tree.hidden = view.nodes.length === 0;
|
|
203
|
+
notes(view.notes || []);
|
|
204
|
+
status("[ok] result","ok");
|
|
205
|
+
}
|
|
206
|
+
function input(params) {
|
|
207
|
+
cancelled = false;
|
|
208
|
+
clearResult();
|
|
209
|
+
display("fallback","Waiting for the tool response.");
|
|
210
|
+
fields.title.textContent = "Request in progress";
|
|
211
|
+
display("repo",record(params.arguments) && string(params.arguments.repo,LIMITS.repo) ? params.arguments.repo : "");
|
|
212
|
+
status("[>] working","pending");
|
|
213
|
+
}
|
|
214
|
+
function result(params) {
|
|
215
|
+
if (cancelled) return;
|
|
216
|
+
const text = readableText(params);
|
|
217
|
+
const omission = text.omitted ? "Text shortened for this view. The full tool response remains in chat." : "";
|
|
218
|
+
if (params.isError === true) {
|
|
219
|
+
fallback("Request failed",text.text,omission,"error");
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
const view = record(params._meta) ? params._meta[META_KEY] : undefined;
|
|
223
|
+
if (validView(view)) renderView(view);
|
|
224
|
+
else fallback("Tool response",text.text,[view === undefined ? "" : "Trace metadata could not be displayed; showing the tool's text response.",omission].filter(Boolean).join(" "));
|
|
225
|
+
}
|
|
226
|
+
function context(params) {
|
|
227
|
+
if (params.theme === "dark" || params.theme === "light") document.documentElement.dataset.theme = params.theme;
|
|
228
|
+
const name = params.toolInfo?.tool?.name;
|
|
229
|
+
if (string(name,128)) fields.tool.textContent = name;
|
|
230
|
+
}
|
|
231
|
+
function size() {
|
|
232
|
+
if (!ready || closed || resizeFrame) return;
|
|
233
|
+
resizeFrame = requestAnimationFrame(() => {
|
|
234
|
+
resizeFrame = 0;
|
|
235
|
+
const height = Math.ceil(root.getBoundingClientRect().height);
|
|
236
|
+
const width = Math.ceil(document.documentElement.clientWidth);
|
|
237
|
+
if (height !== lastHeight || width !== lastWidth) {
|
|
238
|
+
lastHeight = height;
|
|
239
|
+
lastWidth = width;
|
|
240
|
+
send({method:"ui/notifications/size-changed",params:{height,width}});
|
|
241
|
+
}
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
const observer = typeof ResizeObserver === "function" ? new ResizeObserver(size) : null;
|
|
245
|
+
observer?.observe(root);
|
|
246
|
+
root.addEventListener("toggle",size,true);
|
|
247
|
+
window.addEventListener("resize",size);
|
|
248
|
+
function receive(event) {
|
|
249
|
+
if (closed || event.source !== window.parent || !record(event.data) || event.data.jsonrpc !== "2.0") return;
|
|
250
|
+
const message = event.data;
|
|
251
|
+
if (message.id === initId && message.method === undefined) {
|
|
252
|
+
if (ready) return;
|
|
253
|
+
if (!record(message.result) || message.result.protocolVersion !== PROTOCOL_VERSION || message.error) {
|
|
254
|
+
clearTimeout(initTimeout);
|
|
255
|
+
fallback("Text response available","Read the tool response in chat.","This host could not initialize Trace.");
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
258
|
+
ready = true;
|
|
259
|
+
clearTimeout(initTimeout);
|
|
260
|
+
if (record(message.result.hostContext)) context(message.result.hostContext);
|
|
261
|
+
send({method:"ui/notifications/initialized",params:{}});
|
|
262
|
+
size();
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
265
|
+
const request = typeof message.id === "string" || typeof message.id === "number";
|
|
266
|
+
if (request) {
|
|
267
|
+
if (message.method === "ping") send({id:message.id,result:{}});
|
|
268
|
+
else if (message.method === "ui/resource-teardown") {
|
|
269
|
+
send({id:message.id,result:{}});
|
|
270
|
+
closed = true;
|
|
271
|
+
clearTimeout(initTimeout);
|
|
272
|
+
cancelAnimationFrame(resizeFrame);
|
|
273
|
+
observer?.disconnect();
|
|
274
|
+
root.dataset.pending = "false";
|
|
275
|
+
window.removeEventListener("message",receive);
|
|
276
|
+
} else send({id:message.id,error:{code:-32601,message:"Unsupported request"}});
|
|
277
|
+
return;
|
|
278
|
+
}
|
|
279
|
+
if (!ready || !record(message.params)) return;
|
|
280
|
+
if (message.method === "ui/notifications/tool-input") input(message.params);
|
|
281
|
+
else if (message.method === "ui/notifications/tool-result") result(message.params);
|
|
282
|
+
else if (message.method === "ui/notifications/tool-cancelled") {
|
|
283
|
+
cancelled = true;
|
|
284
|
+
fallback("Request cancelled",string(message.params.reason,LIMITS.detail) ? message.params.reason : "The host cancelled this tool request.","","cancelled");
|
|
285
|
+
} else if (message.method === "ui/notifications/host-context-changed") context(message.params);
|
|
286
|
+
size();
|
|
287
|
+
}
|
|
288
|
+
window.addEventListener("message",receive);
|
|
289
|
+
const initTimeout = setTimeout(() => {
|
|
290
|
+
if (!ready && !closed) fallback("Text response available","Read the tool response in chat.","Trace has not received a host connection.");
|
|
291
|
+
},10000);
|
|
292
|
+
send({id:initId,method:"ui/initialize",params:{appInfo:{name:"Pharaoh Trace",version:"1.1.0"},appCapabilities:{availableDisplayModes:["inline"]},protocolVersion:PROTOCOL_VERSION}});
|
|
293
|
+
})();
|
|
294
|
+
</script>
|
|
295
|
+
</body>
|
|
296
|
+
</html>
|