open-edison 0.1.26__py3-none-any.whl → 0.1.30__py3-none-any.whl
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.
- {open_edison-0.1.26.dist-info → open_edison-0.1.30.dist-info}/METADATA +19 -16
- open_edison-0.1.30.dist-info/RECORD +21 -0
- src/cli.py +6 -0
- src/config.py +16 -28
- src/frontend_dist/assets/index-DOR5YaNc.js +51 -0
- src/frontend_dist/assets/index-o6_8mdM8.css +1 -0
- src/frontend_dist/index.html +21 -0
- src/frontend_dist/sw.js +71 -0
- src/middleware/session_tracking.py +40 -8
- src/permissions.py +3 -14
- src/server.py +27 -12
- src/single_user_mcp.py +12 -2
- open_edison-0.1.26.dist-info/RECORD +0 -17
- {open_edison-0.1.26.dist-info → open_edison-0.1.30.dist-info}/WHEEL +0 -0
- {open_edison-0.1.26.dist-info → open_edison-0.1.30.dist-info}/entry_points.txt +0 -0
- {open_edison-0.1.26.dist-info → open_edison-0.1.30.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1 @@
|
|
1
|
+
*,:before,:after{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }*,:before,:after{box-sizing:border-box;border-width:0;border-style:solid;border-color:#e5e7eb}:before,:after{--tw-content: ""}html,:host{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji",Segoe UI Symbol,"Noto Color Emoji";font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dl,dd,h1,h2,h3,h4,h5,h6,hr,figure,p,pre{margin:0}fieldset{margin:0;padding:0}legend{padding:0}ol,ul,menu{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}button,[role=button]{cursor:pointer}:disabled{cursor:default}img,svg,video,canvas,audio,iframe,embed,object{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]:where(:not([hidden=until-found])){display:none}.\!container{width:100%!important}.container{width:100%}@media (min-width: 640px){.\!container{max-width:640px!important}.container{max-width:640px}}@media (min-width: 768px){.\!container{max-width:768px!important}.container{max-width:768px}}@media (min-width: 1024px){.\!container{max-width:1024px!important}.container{max-width:1024px}}@media (min-width: 1280px){.\!container{max-width:1280px!important}.container{max-width:1280px}}@media (min-width: 1536px){.\!container{max-width:1536px!important}.container{max-width:1536px}}.fixed{position:fixed}.relative{position:relative}.bottom-4{bottom:1rem}.right-4{right:1rem}.z-50{z-index:50}.m-0{margin:0}.mx-auto{margin-left:auto;margin-right:auto}.mb-1{margin-bottom:.25rem}.mb-2{margin-bottom:.5rem}.mb-3{margin-bottom:.75rem}.ml-2{margin-left:.5rem}.ml-3{margin-left:.75rem}.ml-auto{margin-left:auto}.mr-2{margin-right:.5rem}.mt-0\.5{margin-top:.125rem}.mt-1{margin-top:.25rem}.mt-2{margin-top:.5rem}.mt-3{margin-top:.75rem}.mt-4{margin-top:1rem}.inline-block{display:inline-block}.inline{display:inline}.flex{display:flex}.inline-flex{display:inline-flex}.table{display:table}.grid{display:grid}.hidden{display:none}.h-2{height:.5rem}.h-3{height:.75rem}.h-5{height:1.25rem}.h-6{height:1.5rem}.h-\[580px\]{height:580px}.w-10{width:2.5rem}.w-2{width:.5rem}.w-3{width:.75rem}.w-4{width:1rem}.w-5{width:1.25rem}.w-\[260px\]{width:260px}.w-full{width:100%}.min-w-\[240px\]{min-width:240px}.max-w-\[1400px\]{max-width:1400px}.max-w-\[260px\]{max-width:260px}.max-w-sm{max-width:24rem}.border-collapse{border-collapse:collapse}.translate-x-1{--tw-translate-x: .25rem;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.translate-x-5{--tw-translate-x: 1.25rem;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.transform{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}@keyframes pulse{50%{opacity:.5}}.animate-pulse{animation:pulse 2s cubic-bezier(.4,0,.6,1) infinite}.cursor-pointer{cursor:pointer}.select-none{-webkit-user-select:none;-moz-user-select:none;user-select:none}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-start{align-items:flex-start}.items-end{align-items:flex-end}.items-center{align-items:center}.justify-between{justify-content:space-between}.gap-1{gap:.25rem}.gap-2{gap:.5rem}.gap-3{gap:.75rem}.space-y-1>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.25rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.25rem * var(--tw-space-y-reverse))}.space-y-2>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.5rem * var(--tw-space-y-reverse))}.space-y-3>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.75rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.75rem * var(--tw-space-y-reverse))}.space-y-4>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(1rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1rem * var(--tw-space-y-reverse))}.overflow-hidden{overflow:hidden}.overflow-y-auto{overflow-y:auto}.truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.whitespace-nowrap{white-space:nowrap}.whitespace-pre-wrap{white-space:pre-wrap}.rounded{border-radius:.25rem}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:.5rem}.rounded-t{border-top-left-radius:.25rem;border-top-right-radius:.25rem}.border{border-width:1px}.border-b{border-bottom-width:1px}.border-r{border-right-width:1px}.border-amber-400\/30{border-color:#fbbf244d}.border-app-accent{border-color:var(--accent)}.border-app-border{border-color:var(--border)}.border-blue-400\/30{border-color:#60a5fa4d}.border-rose-400\/30{border-color:#fb71854d}.bg-amber-400{--tw-bg-opacity: 1;background-color:rgb(251 191 36 / var(--tw-bg-opacity, 1))}.bg-app-accent{background-color:var(--accent)}.bg-app-bg{background-color:var(--bg)}.bg-app-border{background-color:var(--border)}.bg-blue-400{--tw-bg-opacity: 1;background-color:rgb(96 165 250 / var(--tw-bg-opacity, 1))}.bg-blue-500{--tw-bg-opacity: 1;background-color:rgb(59 130 246 / var(--tw-bg-opacity, 1))}.bg-green-500{--tw-bg-opacity: 1;background-color:rgb(34 197 94 / var(--tw-bg-opacity, 1))}.bg-red-500{--tw-bg-opacity: 1;background-color:rgb(239 68 68 / var(--tw-bg-opacity, 1))}.bg-rose-400{--tw-bg-opacity: 1;background-color:rgb(251 113 133 / var(--tw-bg-opacity, 1))}.bg-white{--tw-bg-opacity: 1;background-color:rgb(255 255 255 / var(--tw-bg-opacity, 1))}.bg-yellow-500{--tw-bg-opacity: 1;background-color:rgb(234 179 8 / var(--tw-bg-opacity, 1))}.p-2{padding:.5rem}.p-3{padding:.75rem}.p-4{padding:1rem}.p-6{padding:1.5rem}.\!px-2{padding-left:.5rem!important;padding-right:.5rem!important}.\!px-3{padding-left:.75rem!important;padding-right:.75rem!important}.\!py-1\.5{padding-top:.375rem!important;padding-bottom:.375rem!important}.\!py-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.px-2{padding-left:.5rem;padding-right:.5rem}.px-3{padding-left:.75rem;padding-right:.75rem}.py-0\.5{padding-top:.125rem;padding-bottom:.125rem}.py-1{padding-top:.25rem;padding-bottom:.25rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.py-3{padding-top:.75rem;padding-bottom:.75rem}.pb-2{padding-bottom:.5rem}.text-left{text-align:left}.text-center{text-align:center}.align-bottom{vertical-align:bottom}.font-mono{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.text-2xl{font-size:1.5rem;line-height:2rem}.text-\[10px\]{font-size:10px}.text-lg{font-size:1.125rem;line-height:1.75rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xl{font-size:1.25rem;line-height:1.75rem}.text-xs{font-size:.75rem;line-height:1rem}.font-bold{font-weight:700}.font-medium{font-weight:500}.font-semibold{font-weight:600}.text-amber-400{--tw-text-opacity: 1;color:rgb(251 191 36 / var(--tw-text-opacity, 1))}.text-app-accent{color:var(--accent)}.text-app-muted{color:var(--muted)}.text-app-text{color:var(--text)}.text-blue-400{--tw-text-opacity: 1;color:rgb(96 165 250 / var(--tw-text-opacity, 1))}.text-blue-500{--tw-text-opacity: 1;color:rgb(59 130 246 / var(--tw-text-opacity, 1))}.text-gray-400{--tw-text-opacity: 1;color:rgb(156 163 175 / var(--tw-text-opacity, 1))}.text-green-400{--tw-text-opacity: 1;color:rgb(74 222 128 / var(--tw-text-opacity, 1))}.text-green-500{--tw-text-opacity: 1;color:rgb(34 197 94 / var(--tw-text-opacity, 1))}.text-red-400{--tw-text-opacity: 1;color:rgb(248 113 113 / var(--tw-text-opacity, 1))}.text-red-500{--tw-text-opacity: 1;color:rgb(239 68 68 / var(--tw-text-opacity, 1))}.text-rose-400{--tw-text-opacity: 1;color:rgb(251 113 133 / var(--tw-text-opacity, 1))}.text-white{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity, 1))}.text-yellow-500{--tw-text-opacity: 1;color:rgb(234 179 8 / var(--tw-text-opacity, 1))}.accent-blue-500{accent-color:#3b82f6}.shadow{--tw-shadow: 0 1px 3px 0 rgb(0 0 0 / .1), 0 1px 2px -1px rgb(0 0 0 / .1);--tw-shadow-colored: 0 1px 3px 0 var(--tw-shadow-color), 0 1px 2px -1px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-lg{--tw-shadow: 0 10px 15px -3px rgb(0 0 0 / .1), 0 4px 6px -4px rgb(0 0 0 / .1);--tw-shadow-colored: 0 10px 15px -3px var(--tw-shadow-color), 0 4px 6px -4px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.transition-all{transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-colors{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-transform{transition-property:transform;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.duration-300{transition-duration:.3s}:root{--bg: #0b0c10;--card: #111318;--border: #1f2430;--text: #e6e6e6;--muted: #a0a7b4;--accent: #7c3aed;--success: #10b981;--warning: #f59e0b;--danger: #ef4444}[data-theme=dark]{--bg: #0b0c10;--card: #111318;--border: #1f2430;--text: #e6e6e6;--muted: #a0a7b4}[data-theme=light]{--bg: #f8fafc;--card: #ffffff;--border: #e5e7eb;--text: #0f172a;--muted: #475569}@media (prefers-color-scheme: light){:root{--bg: #f8fafc;--card: #ffffff;--border: #e5e7eb;--text: #0f172a;--muted: #475569}}html,body,#root{height:100%}body{margin:0;background:var(--bg);color:var(--text)}.container{margin:0 auto;padding:24px;max-width:1100px}.grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(280px,1fr));gap:16px}.card{background:var(--card);border:1px solid var(--border);border-radius:12px;padding:16px;box-shadow:0 1px 2px #0000000a,0 2px 12px #00000014}.stat{display:flex;align-items:center;gap:12px}.badge{display:inline-block;font-size:12px;padding:2px 8px;border-radius:999px;border:1px solid var(--border);background:#7c3aed14;color:var(--text)}.table{width:100%;border-collapse:collapse}.table th,.table td{border-bottom:1px solid var(--border);padding:8px 4px;text-align:left}.muted{color:var(--muted)}.accent{color:var(--accent)}.success{color:var(--success)}.warning{color:var(--warning)}.danger{color:var(--danger)}.toolbar{display:flex;align-items:center;justify-content:space-between;gap:12px}.button{border:1px solid var(--border);background:var(--card);color:var(--text);padding:6px 10px;border-radius:8px;cursor:pointer}.button:hover{filter:brightness(1.05)}.hover\:text-gray-200:hover{--tw-text-opacity: 1;color:rgb(229 231 235 / var(--tw-text-opacity, 1))}.focus\:outline-none:focus{outline:2px solid transparent;outline-offset:2px}.focus-visible\:ring-2:focus-visible{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.focus-visible\:ring-blue-400:focus-visible{--tw-ring-opacity: 1;--tw-ring-color: rgb(96 165 250 / var(--tw-ring-opacity, 1))}@media (min-width: 640px){.sm\:flex{display:flex}.sm\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.sm\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.sm\:flex-row{flex-direction:row}.sm\:items-end{align-items:flex-end}}@media (min-width: 1024px){.lg\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.lg\:grid-cols-\[220px_1fr\]{grid-template-columns:220px 1fr}}
|
@@ -0,0 +1,21 @@
|
|
1
|
+
<!doctype html>
|
2
|
+
<html lang="en">
|
3
|
+
|
4
|
+
<head>
|
5
|
+
<meta charset="UTF-8" />
|
6
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
7
|
+
<title>Open Edison Sessions</title>
|
8
|
+
<script>
|
9
|
+
// Prevents FOUC by setting theme asap
|
10
|
+
const prefersLight = window.matchMedia && window.matchMedia('(prefers-color-scheme: light)').matches;
|
11
|
+
document.documentElement.setAttribute('data-theme', prefersLight ? 'light' : 'dark');
|
12
|
+
</script>
|
13
|
+
<script type="module" crossorigin src="/assets/index-DOR5YaNc.js"></script>
|
14
|
+
<link rel="stylesheet" crossorigin href="/assets/index-o6_8mdM8.css">
|
15
|
+
</head>
|
16
|
+
|
17
|
+
<body>
|
18
|
+
<div id="root"></div>
|
19
|
+
</body>
|
20
|
+
|
21
|
+
</html>
|
src/frontend_dist/sw.js
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
// Minimal service worker for actionable notifications
|
2
|
+
|
3
|
+
self.addEventListener('install', (event) => {
|
4
|
+
self.skipWaiting();
|
5
|
+
});
|
6
|
+
|
7
|
+
self.addEventListener('activate', (event) => {
|
8
|
+
event.waitUntil(self.clients.claim());
|
9
|
+
});
|
10
|
+
|
11
|
+
// Receive messages from pages to show notifications with actions
|
12
|
+
self.addEventListener('message', (event) => {
|
13
|
+
try {
|
14
|
+
const data = event.data || {};
|
15
|
+
if (data && data.type === 'SHOW_MCP_BLOCK_NOTIFICATION') {
|
16
|
+
const title = data.title || 'Action required';
|
17
|
+
const body = data.body || 'Approve or deny the request';
|
18
|
+
const payload = data.data || {};
|
19
|
+
event.waitUntil(
|
20
|
+
self.registration.showNotification(title, {
|
21
|
+
body,
|
22
|
+
requireInteraction: true,
|
23
|
+
data: payload,
|
24
|
+
actions: [
|
25
|
+
{ action: 'approve', title: 'Approve' },
|
26
|
+
{ action: 'deny', title: 'Deny' }
|
27
|
+
]
|
28
|
+
})
|
29
|
+
);
|
30
|
+
}
|
31
|
+
} catch (e) {
|
32
|
+
// swallow
|
33
|
+
}
|
34
|
+
});
|
35
|
+
|
36
|
+
// Handle action button clicks and generic clicks
|
37
|
+
self.addEventListener('notificationclick', (event) => {
|
38
|
+
try {
|
39
|
+
const payload = (event.notification && event.notification.data) || {};
|
40
|
+
const action = event.action;
|
41
|
+
event.notification.close();
|
42
|
+
|
43
|
+
if (action === 'approve') {
|
44
|
+
const body = {
|
45
|
+
session_id: payload.sessionId,
|
46
|
+
kind: payload.kind,
|
47
|
+
name: payload.name
|
48
|
+
};
|
49
|
+
event.waitUntil(
|
50
|
+
fetch('/api/approve', {
|
51
|
+
method: 'POST',
|
52
|
+
headers: { 'Content-Type': 'application/json' },
|
53
|
+
body: JSON.stringify(body)
|
54
|
+
}).catch(() => { })
|
55
|
+
);
|
56
|
+
return;
|
57
|
+
}
|
58
|
+
|
59
|
+
if (action === 'deny') {
|
60
|
+
// No-op; could notify page if desired
|
61
|
+
return;
|
62
|
+
}
|
63
|
+
|
64
|
+
// Generic click: open dashboard as a safe fallback
|
65
|
+
event.waitUntil(self.clients.openWindow('/dashboard').catch(() => { }));
|
66
|
+
} catch (e) {
|
67
|
+
// swallow
|
68
|
+
}
|
69
|
+
});
|
70
|
+
|
71
|
+
|
@@ -233,7 +233,11 @@ class SessionTrackingMiddleware(Middleware):
|
|
233
233
|
# Get or create session stats
|
234
234
|
_, _session_id = self._get_or_create_session_stats(context)
|
235
235
|
|
236
|
-
|
236
|
+
try:
|
237
|
+
return await call_next(context) # type: ignore
|
238
|
+
except Exception:
|
239
|
+
log.exception("MCP request handling failed")
|
240
|
+
raise
|
237
241
|
|
238
242
|
# Hooks for Tools
|
239
243
|
async def on_list_tools( # noqa
|
@@ -243,7 +247,11 @@ class SessionTrackingMiddleware(Middleware):
|
|
243
247
|
) -> Any:
|
244
248
|
log.debug("🔍 on_list_tools")
|
245
249
|
# Get the original response
|
246
|
-
|
250
|
+
try:
|
251
|
+
response = await call_next(context)
|
252
|
+
except Exception:
|
253
|
+
log.exception("MCP list_tools failed")
|
254
|
+
raise
|
247
255
|
log.trace(f"🔍 on_list_tools response: {response}")
|
248
256
|
|
249
257
|
session_id = current_session_id_ctxvar.get()
|
@@ -368,7 +376,11 @@ class SessionTrackingMiddleware(Middleware):
|
|
368
376
|
"""Process resource access and track security implications."""
|
369
377
|
log.trace("🔍 on_list_resources")
|
370
378
|
# Get the original response
|
371
|
-
|
379
|
+
try:
|
380
|
+
response = await call_next(context)
|
381
|
+
except Exception:
|
382
|
+
log.exception("MCP list_resources failed")
|
383
|
+
raise
|
372
384
|
log.trace(f"🔍 on_list_resources response: {response}")
|
373
385
|
|
374
386
|
session_id = current_session_id_ctxvar.get()
|
@@ -415,7 +427,11 @@ class SessionTrackingMiddleware(Middleware):
|
|
415
427
|
session_id = current_session_id_ctxvar.get()
|
416
428
|
if session_id is None:
|
417
429
|
log.warning("No session ID found for resource access tracking")
|
418
|
-
|
430
|
+
try:
|
431
|
+
return await call_next(context)
|
432
|
+
except Exception:
|
433
|
+
log.exception("MCP read_resource failed")
|
434
|
+
raise
|
419
435
|
|
420
436
|
session = get_session_from_db(session_id)
|
421
437
|
log.trace(f"Adding resource access to session {session_id}")
|
@@ -457,7 +473,11 @@ class SessionTrackingMiddleware(Middleware):
|
|
457
473
|
db_session.commit()
|
458
474
|
|
459
475
|
log.trace(f"Resource access {resource_name} added to session {session_id}")
|
460
|
-
|
476
|
+
try:
|
477
|
+
return await call_next(context)
|
478
|
+
except Exception:
|
479
|
+
log.exception("MCP read_resource failed")
|
480
|
+
raise
|
461
481
|
|
462
482
|
# Hooks for Prompts
|
463
483
|
async def on_list_prompts( # noqa
|
@@ -468,7 +488,11 @@ class SessionTrackingMiddleware(Middleware):
|
|
468
488
|
"""Process resource access and track security implications."""
|
469
489
|
log.debug("🔍 on_list_prompts")
|
470
490
|
# Get the original response
|
471
|
-
|
491
|
+
try:
|
492
|
+
response = await call_next(context)
|
493
|
+
except Exception:
|
494
|
+
log.exception("MCP list_prompts failed")
|
495
|
+
raise
|
472
496
|
log.debug(f"🔍 on_list_prompts response: {response}")
|
473
497
|
|
474
498
|
session_id = current_session_id_ctxvar.get()
|
@@ -515,7 +539,11 @@ class SessionTrackingMiddleware(Middleware):
|
|
515
539
|
session_id = current_session_id_ctxvar.get()
|
516
540
|
if session_id is None:
|
517
541
|
log.warning("No session ID found for prompt access tracking")
|
518
|
-
|
542
|
+
try:
|
543
|
+
return await call_next(context)
|
544
|
+
except Exception:
|
545
|
+
log.exception("MCP get_prompt failed")
|
546
|
+
raise
|
519
547
|
|
520
548
|
session = get_session_from_db(session_id)
|
521
549
|
log.trace(f"Adding prompt access to session {session_id}")
|
@@ -554,4 +582,8 @@ class SessionTrackingMiddleware(Middleware):
|
|
554
582
|
db_session.commit()
|
555
583
|
|
556
584
|
log.trace(f"Prompt access {prompt_name} added to session {session_id}")
|
557
|
-
|
585
|
+
try:
|
586
|
+
return await call_next(context)
|
587
|
+
except Exception:
|
588
|
+
log.exception("MCP get_prompt failed")
|
589
|
+
raise
|
src/permissions.py
CHANGED
@@ -14,22 +14,8 @@ from loguru import logger as log
|
|
14
14
|
|
15
15
|
from src.config import Config, get_config_dir
|
16
16
|
|
17
|
-
# Detect repository root (same logic as in src.config)
|
18
|
-
_ROOT_DIR = Path(__file__).parent.parent
|
19
|
-
|
20
17
|
|
21
18
|
def _default_permissions_dir() -> Path:
|
22
|
-
"""Resolve default permissions directory.
|
23
|
-
|
24
|
-
In development (repo checkout with pyproject.toml), prefer repository root so
|
25
|
-
we use repo-local tool/resource/prompt permissions JSON files. Otherwise fall
|
26
|
-
back to the standard user config directory.
|
27
|
-
"""
|
28
|
-
try:
|
29
|
-
if (_ROOT_DIR / "pyproject.toml").exists():
|
30
|
-
return _ROOT_DIR
|
31
|
-
except Exception:
|
32
|
-
pass
|
33
19
|
return get_config_dir()
|
34
20
|
|
35
21
|
|
@@ -76,6 +62,9 @@ class PromptPermission:
|
|
76
62
|
write_operation: bool = False
|
77
63
|
read_private_data: bool = False
|
78
64
|
read_untrusted_public_data: bool = False
|
65
|
+
# Optional metadata fields (ignored by enforcement but accepted from JSON)
|
66
|
+
description: str | None = None
|
67
|
+
acl: str = "PUBLIC"
|
79
68
|
|
80
69
|
|
81
70
|
@dataclass
|
src/server.py
CHANGED
@@ -30,7 +30,7 @@ from loguru import logger as log
|
|
30
30
|
from pydantic import BaseModel, Field
|
31
31
|
|
32
32
|
from src import events
|
33
|
-
from src.config import Config, MCPServerConfig
|
33
|
+
from src.config import Config, MCPServerConfig, load_json_file
|
34
34
|
from src.config import get_config_dir as _get_cfg_dir # type: ignore[attr-defined]
|
35
35
|
from src.middleware.session_tracking import (
|
36
36
|
MCPSessionModel,
|
@@ -86,10 +86,16 @@ class OpenEdisonProxy:
|
|
86
86
|
# If packaged frontend assets exist, mount at /dashboard
|
87
87
|
try:
|
88
88
|
# Prefer packaged assets under src/frontend_dist
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
89
|
+
primary_candidate = Path(__file__).parent / "frontend_dist"
|
90
|
+
secondary_candidate = Path(__file__).parent.parent / "frontend_dist"
|
91
|
+
log.trace(
|
92
|
+
"Checking dashboard assets candidates: primary={}, exists={}, secondary={}, exists={}",
|
93
|
+
primary_candidate,
|
94
|
+
primary_candidate.exists(),
|
95
|
+
secondary_candidate,
|
96
|
+
secondary_candidate.exists(),
|
97
|
+
)
|
98
|
+
static_dir = primary_candidate if primary_candidate.exists() else secondary_candidate
|
93
99
|
if static_dir.exists():
|
94
100
|
app.mount(
|
95
101
|
"/dashboard",
|
@@ -121,9 +127,18 @@ class OpenEdisonProxy:
|
|
121
127
|
app.add_api_route("/favicon.ico", _favicon, methods=["GET"]) # type: ignore[arg-type]
|
122
128
|
log.info(f"📊 Dashboard static assets mounted at /dashboard from {static_dir}")
|
123
129
|
else:
|
124
|
-
|
130
|
+
# Emit a more detailed diagnostic and fail fast when assets are not found
|
131
|
+
cwd = Path.cwd()
|
132
|
+
msg = (
|
133
|
+
"Packaged dashboard assets not found. Expected at one of: "
|
134
|
+
f"{primary_candidate} or {secondary_candidate}. "
|
135
|
+
f"cwd={cwd}, __file__={Path(__file__).resolve()}"
|
136
|
+
)
|
137
|
+
log.error(msg)
|
138
|
+
raise RuntimeError(msg)
|
125
139
|
except Exception as mount_err: # noqa: BLE001
|
126
|
-
log.
|
140
|
+
log.error(f"Failed to mount dashboard static assets: {mount_err}")
|
141
|
+
raise
|
127
142
|
|
128
143
|
# Special-case: serve SQLite db and config JSONs for dashboard (prod replacement for Vite @fs)
|
129
144
|
def _resolve_db_path() -> Path:
|
@@ -250,6 +265,11 @@ class OpenEdisonProxy:
|
|
250
265
|
_ = json.loads(content or "{}")
|
251
266
|
target.write_text(content or "{}", encoding="utf-8")
|
252
267
|
log.debug(f"Saved JSON config to {target}")
|
268
|
+
|
269
|
+
# Clear cache for the config file, if it was config.json
|
270
|
+
if name == "config.json":
|
271
|
+
load_json_file.cache_clear()
|
272
|
+
|
253
273
|
return {"status": "ok"}
|
254
274
|
except Exception as e: # noqa: BLE001
|
255
275
|
raise HTTPException(status_code=400, detail=f"Save failed: {e}") from e
|
@@ -538,11 +558,6 @@ class OpenEdisonProxy:
|
|
538
558
|
try:
|
539
559
|
log.info("🔄 Reinitializing MCP servers via API endpoint")
|
540
560
|
|
541
|
-
# Create a completely new SingleUserMCP instance to ensure clean state
|
542
|
-
old_mcp = self.single_user_mcp
|
543
|
-
self.single_user_mcp = SingleUserMCP()
|
544
|
-
del old_mcp
|
545
|
-
|
546
561
|
# Initialize the new instance with fresh config
|
547
562
|
await self.single_user_mcp.initialize()
|
548
563
|
|
src/single_user_mcp.py
CHANGED
@@ -49,7 +49,8 @@ class SingleUserMCP(FastMCP[Any]):
|
|
49
49
|
"""
|
50
50
|
|
51
51
|
def __init__(self):
|
52
|
-
|
52
|
+
# Disable error masking so upstream error details are preserved in responses
|
53
|
+
super().__init__(name="open-edison-single-user", mask_error_details=False)
|
53
54
|
|
54
55
|
# Add session tracking middleware for data access monitoring
|
55
56
|
self.add_middleware(SessionTrackingMiddleware())
|
@@ -288,6 +289,10 @@ class SingleUserMCP(FastMCP[Any]):
|
|
288
289
|
f"Found {len(enabled_servers)} enabled servers: {[s.name for s in enabled_servers]}"
|
289
290
|
)
|
290
291
|
|
292
|
+
# Unmount all servers
|
293
|
+
for server_name in list(mounted_servers.keys()):
|
294
|
+
await self.unmount(server_name)
|
295
|
+
|
291
296
|
# Create composite proxy for all real servers
|
292
297
|
success = await self.create_composite_proxy(enabled_servers)
|
293
298
|
if not success:
|
@@ -296,6 +301,11 @@ class SingleUserMCP(FastMCP[Any]):
|
|
296
301
|
|
297
302
|
log.info("✅ Single User MCP server initialized with composite proxy")
|
298
303
|
|
304
|
+
# Invalidate and warm lists to ensure reload
|
305
|
+
_ = await self._tool_manager.list_tools()
|
306
|
+
_ = await self._resource_manager.list_resources()
|
307
|
+
_ = await self._prompt_manager.list_prompts()
|
308
|
+
|
299
309
|
def _calculate_risk_level(self, trifecta: dict[str, bool]) -> str:
|
300
310
|
"""
|
301
311
|
Calculate a human-readable risk level based on trifecta flags.
|
@@ -386,7 +396,7 @@ class SingleUserMCP(FastMCP[Any]):
|
|
386
396
|
"""
|
387
397
|
tool_list = await self._tool_manager.list_tools()
|
388
398
|
available_tools: list[str] = []
|
389
|
-
log.
|
399
|
+
log.trace(f"Raw tool list: {tool_list}")
|
390
400
|
perms = Permissions()
|
391
401
|
for tool in tool_list:
|
392
402
|
# Use the prefixed key (e.g., "filesystem_read_file") to match flattened permissions
|
@@ -1,17 +0,0 @@
|
|
1
|
-
src/__init__.py,sha256=QWeZdjAm2D2B0eWhd8m2-DPpWvIP26KcNJxwEoU1oEQ,254
|
2
|
-
src/__main__.py,sha256=kQsaVyzRa_ESC57JpKDSQJAHExuXme0rM5beJsYxFeA,161
|
3
|
-
src/cli.py,sha256=_F1xtUU2h4snWUHf1NptRWGQaD2OSIhEPGLh9Rzmtis,10032
|
4
|
-
src/config.py,sha256=jZYX4q09hg2VlLCq7FIKa_bL7NpNNMStYMQyEMZPrDg,9910
|
5
|
-
src/events.py,sha256=rBH7rnaSWZ7GIC8zyBTwpcvIKWmKYCki-DNGgJhxPow,5001
|
6
|
-
src/oauth_manager.py,sha256=qcQa5BDRZr4bjqiXNflCnrXOh9mo9JVjvP2Caseg2Uc,9943
|
7
|
-
src/permissions.py,sha256=kIbLPtaJAwV5CF-YECLhU7HEF704LdQCI2xIh-TCu4I,10834
|
8
|
-
src/server.py,sha256=Gg2DnnvZr59e0PGtRXVD94fxVxtiNnizlIrVkeLGIok,44504
|
9
|
-
src/single_user_mcp.py,sha256=bbWbuuBxjL0DJY9kAHtHt8jwQNePXPa4cD4gnbD9XmE,16897
|
10
|
-
src/telemetry.py,sha256=-RZPIjpI53zbsKmp-63REeZ1JirWHV5WvpSRa2nqZEk,11321
|
11
|
-
src/middleware/data_access_tracker.py,sha256=bArBffWgYmvxOx9z_pgXQhogvnWQcc1m6WvEblDD4gw,15039
|
12
|
-
src/middleware/session_tracking.py,sha256=p3UMruIe5RBYnfAzmSaHMOeN3xKN-9EiCSd7nAGjgrw,22138
|
13
|
-
open_edison-0.1.26.dist-info/METADATA,sha256=Tfw-vOv_KlIvUBMcU5DjwHNiksFxJejJBurC0dp_b4Q,11685
|
14
|
-
open_edison-0.1.26.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
15
|
-
open_edison-0.1.26.dist-info/entry_points.txt,sha256=qNAkJcnoTXRhj8J--3PDmXz_TQKdB8H_0C9wiCtDIyA,72
|
16
|
-
open_edison-0.1.26.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
|
17
|
-
open_edison-0.1.26.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|