gazesight 0.5.1 → 0.5.3
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/dist/chrome/background.js +1 -1
- package/dist/chrome/manifest.json +2 -2
- package/dist/chrome/popup.html +2 -2
- package/dist/chrome/popup.js +1 -1
- package/dist/cli/index.js +55 -14
- package/dist/firefox/background.js +21 -21
- package/dist/firefox/manifest.json +90 -90
- package/dist/firefox/popup.html +2 -2
- package/dist/firefox/popup.js +1 -1
- package/dist/gazesight-firefox.xpi +0 -0
- package/dist/mcp/index.js +100 -49
- package/package.json +141 -141
|
@@ -1,90 +1,90 @@
|
|
|
1
|
-
{
|
|
2
|
-
"manifest_version": 2,
|
|
3
|
-
"name": "GazeSight",
|
|
4
|
-
"version": "0.5.
|
|
5
|
-
"description": "Give coding agents visual access to the Firefox tab you're already using.",
|
|
6
|
-
"icons": {
|
|
7
|
-
"16": "icons/icon-16.png",
|
|
8
|
-
"32": "icons/icon-32.png",
|
|
9
|
-
"48": "icons/icon-48.png",
|
|
10
|
-
"64": "icons/icon-64.png",
|
|
11
|
-
"96": "icons/icon-96.png",
|
|
12
|
-
"128": "icons/icon-128.png"
|
|
13
|
-
},
|
|
14
|
-
"browser_specific_settings": {
|
|
15
|
-
"gecko": {
|
|
16
|
-
"id": "extension@gazesight.dev",
|
|
17
|
-
"strict_min_version": "142.0",
|
|
18
|
-
"data_collection_permissions": {
|
|
19
|
-
"required": [
|
|
20
|
-
"websiteContent",
|
|
21
|
-
"browsingActivity"
|
|
22
|
-
]
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
},
|
|
26
|
-
"permissions": [
|
|
27
|
-
"activeTab",
|
|
28
|
-
"tabs",
|
|
29
|
-
"storage",
|
|
30
|
-
"scripting",
|
|
31
|
-
"webRequest",
|
|
32
|
-
"http://localhost/*",
|
|
33
|
-
"https://localhost/*",
|
|
34
|
-
"http://127.0.0.1/*",
|
|
35
|
-
"https://127.0.0.1/*",
|
|
36
|
-
"http://[::1]/*",
|
|
37
|
-
"https://[::1]/*"
|
|
38
|
-
],
|
|
39
|
-
"optional_permissions": [
|
|
40
|
-
"<all_urls>",
|
|
41
|
-
"downloads"
|
|
42
|
-
],
|
|
43
|
-
"background": {
|
|
44
|
-
"scripts": [
|
|
45
|
-
"background.js"
|
|
46
|
-
],
|
|
47
|
-
"persistent": true
|
|
48
|
-
},
|
|
49
|
-
"browser_action": {
|
|
50
|
-
"default_title": "GazeSight — target and connection",
|
|
51
|
-
"default_popup": "popup.html",
|
|
52
|
-
"default_icon": {
|
|
53
|
-
"16": "icons/icon-16.png",
|
|
54
|
-
"32": "icons/icon-32.png",
|
|
55
|
-
"48": "icons/icon-48.png",
|
|
56
|
-
"64": "icons/icon-64.png",
|
|
57
|
-
"96": "icons/icon-96.png",
|
|
58
|
-
"128": "icons/icon-128.png"
|
|
59
|
-
}
|
|
60
|
-
},
|
|
61
|
-
"commands": {
|
|
62
|
-
"gazesight-select-target": {
|
|
63
|
-
"suggested_key": {
|
|
64
|
-
"default": "Ctrl+Shift+Y"
|
|
65
|
-
},
|
|
66
|
-
"description": "Select or reauthorize this tab with GazeSight"
|
|
67
|
-
}
|
|
68
|
-
},
|
|
69
|
-
"options_ui": {
|
|
70
|
-
"page": "options.html",
|
|
71
|
-
"open_in_tab": true
|
|
72
|
-
},
|
|
73
|
-
"content_security_policy": "script-src 'self'; object-src 'none'; connect-src ws://127.0.0.1:* http://127.0.0.1:* data:",
|
|
74
|
-
"content_scripts": [
|
|
75
|
-
{
|
|
76
|
-
"matches": [
|
|
77
|
-
"http://localhost/*",
|
|
78
|
-
"https://localhost/*",
|
|
79
|
-
"http://127.0.0.1/*",
|
|
80
|
-
"https://127.0.0.1/*",
|
|
81
|
-
"http://[::1]/*",
|
|
82
|
-
"https://[::1]/*"
|
|
83
|
-
],
|
|
84
|
-
"js": [
|
|
85
|
-
"content.js"
|
|
86
|
-
],
|
|
87
|
-
"run_at": "document_start"
|
|
88
|
-
}
|
|
89
|
-
]
|
|
90
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"manifest_version": 2,
|
|
3
|
+
"name": "GazeSight",
|
|
4
|
+
"version": "0.5.3",
|
|
5
|
+
"description": "Give coding agents visual access to the Firefox tab you're already using.",
|
|
6
|
+
"icons": {
|
|
7
|
+
"16": "icons/icon-16.png",
|
|
8
|
+
"32": "icons/icon-32.png",
|
|
9
|
+
"48": "icons/icon-48.png",
|
|
10
|
+
"64": "icons/icon-64.png",
|
|
11
|
+
"96": "icons/icon-96.png",
|
|
12
|
+
"128": "icons/icon-128.png"
|
|
13
|
+
},
|
|
14
|
+
"browser_specific_settings": {
|
|
15
|
+
"gecko": {
|
|
16
|
+
"id": "extension@gazesight.dev",
|
|
17
|
+
"strict_min_version": "142.0",
|
|
18
|
+
"data_collection_permissions": {
|
|
19
|
+
"required": [
|
|
20
|
+
"websiteContent",
|
|
21
|
+
"browsingActivity"
|
|
22
|
+
]
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
"permissions": [
|
|
27
|
+
"activeTab",
|
|
28
|
+
"tabs",
|
|
29
|
+
"storage",
|
|
30
|
+
"scripting",
|
|
31
|
+
"webRequest",
|
|
32
|
+
"http://localhost/*",
|
|
33
|
+
"https://localhost/*",
|
|
34
|
+
"http://127.0.0.1/*",
|
|
35
|
+
"https://127.0.0.1/*",
|
|
36
|
+
"http://[::1]/*",
|
|
37
|
+
"https://[::1]/*"
|
|
38
|
+
],
|
|
39
|
+
"optional_permissions": [
|
|
40
|
+
"<all_urls>",
|
|
41
|
+
"downloads"
|
|
42
|
+
],
|
|
43
|
+
"background": {
|
|
44
|
+
"scripts": [
|
|
45
|
+
"background.js"
|
|
46
|
+
],
|
|
47
|
+
"persistent": true
|
|
48
|
+
},
|
|
49
|
+
"browser_action": {
|
|
50
|
+
"default_title": "GazeSight — target and connection",
|
|
51
|
+
"default_popup": "popup.html",
|
|
52
|
+
"default_icon": {
|
|
53
|
+
"16": "icons/icon-16.png",
|
|
54
|
+
"32": "icons/icon-32.png",
|
|
55
|
+
"48": "icons/icon-48.png",
|
|
56
|
+
"64": "icons/icon-64.png",
|
|
57
|
+
"96": "icons/icon-96.png",
|
|
58
|
+
"128": "icons/icon-128.png"
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
"commands": {
|
|
62
|
+
"gazesight-select-target": {
|
|
63
|
+
"suggested_key": {
|
|
64
|
+
"default": "Ctrl+Shift+Y"
|
|
65
|
+
},
|
|
66
|
+
"description": "Select or reauthorize this tab with GazeSight"
|
|
67
|
+
}
|
|
68
|
+
},
|
|
69
|
+
"options_ui": {
|
|
70
|
+
"page": "options.html",
|
|
71
|
+
"open_in_tab": true
|
|
72
|
+
},
|
|
73
|
+
"content_security_policy": "script-src 'self'; object-src 'none'; connect-src ws://127.0.0.1:* http://127.0.0.1:* data:",
|
|
74
|
+
"content_scripts": [
|
|
75
|
+
{
|
|
76
|
+
"matches": [
|
|
77
|
+
"http://localhost/*",
|
|
78
|
+
"https://localhost/*",
|
|
79
|
+
"http://127.0.0.1/*",
|
|
80
|
+
"https://127.0.0.1/*",
|
|
81
|
+
"http://[::1]/*",
|
|
82
|
+
"https://[::1]/*"
|
|
83
|
+
],
|
|
84
|
+
"js": [
|
|
85
|
+
"content.js"
|
|
86
|
+
],
|
|
87
|
+
"run_at": "document_start"
|
|
88
|
+
}
|
|
89
|
+
]
|
|
90
|
+
}
|
package/dist/firefox/popup.html
CHANGED
|
@@ -19,8 +19,8 @@
|
|
|
19
19
|
<div class="paper">
|
|
20
20
|
<main>
|
|
21
21
|
<section id="pairing-connect" class="pairing-connect" hidden>
|
|
22
|
-
<strong>Connect your browser</strong>
|
|
23
|
-
<p id="pairing-message">
|
|
22
|
+
<strong id="pairing-title">Connect your browser</strong>
|
|
23
|
+
<p id="pairing-message">Click Connect to GazeSight and approve the matching code in your terminal.</p>
|
|
24
24
|
<p id="pairing-code" class="pairing-code" hidden></p>
|
|
25
25
|
<button id="connect" class="primary connect">Connect to GazeSight</button>
|
|
26
26
|
</section>
|
package/dist/firefox/popup.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
var r="pendingPairing";function k(){return[...crypto.getRandomValues(new Uint8Array(32))].map(t=>t.toString(16).padStart(2,"0")).join("")}function E(){return location.protocol==="moz-extension:"?"firefox":"chrome"}async function m(e,t,o,i,a=2500){return fetch(`http://127.0.0.1:${e}/gazesight/pairing/${t}`,{method:"POST",headers:{"Content-Type":"application/json",...i?{Authorization:`Bearer ${i}`}:{}},body:JSON.stringify(o),signal:AbortSignal.timeout(a)})}async function w(e,t){let{requestId:o,proof:i,code:a,expiresAt:l,port:b}=t;for(;;){if(Date.now()>=l)return await browser.storage.local.remove(r),e("expired",a),!1;e("waiting_approval",a);let g;try{g=await m(b,"status",{requestId:o,secret:i})}catch{await new Promise(p=>setTimeout(p,800));continue}let
|
|
1
|
+
var r="pendingPairing";function k(){return[...crypto.getRandomValues(new Uint8Array(32))].map(t=>t.toString(16).padStart(2,"0")).join("")}function E(){return location.protocol==="moz-extension:"?"firefox":"chrome"}async function m(e,t,o,i,a=2500){return fetch(`http://127.0.0.1:${e}/gazesight/pairing/${t}`,{method:"POST",headers:{"Content-Type":"application/json",...i?{Authorization:`Bearer ${i}`}:{}},body:JSON.stringify(o),signal:AbortSignal.timeout(a)})}async function w(e,t){let{requestId:o,proof:i,code:a,expiresAt:l,port:b}=t;for(;;){if(Date.now()>=l)return await browser.storage.local.remove(r),e("expired",a),!1;e("waiting_approval",a);let g;try{g=await m(b,"status",{requestId:o,secret:i})}catch{await new Promise(p=>setTimeout(p,800));continue}let c=await g.json();if(c.state==="approved"&&c.credential&&c.pairingId)return await browser.storage.local.set({token:c.credential,pairingId:c.pairingId,port:b,pairingState:"connected"}),await browser.storage.local.remove(r),e("connected"),!0;if(c.state==="rejected")return await browser.storage.local.remove(r),e("reconnect_required",a),!1;if(c.state==="expired"||g.status===410)return await browser.storage.local.remove(r),e("expired",a),!1;await new Promise(p=>setTimeout(p,800))}}async function h(e,t=32147){e("searching");let o=k(),i;try{i=await m(t,"request",{browser:E(),extensionId:browser.runtime.id,secret:o},void 0,6e4)}catch{return e("unavailable"),!1}if(!i.ok)return e("unavailable"),!1;let a=await i.json(),l={requestId:a.requestId,proof:o,code:a.code,expiresAt:a.expiresAt,port:t};return await browser.storage.local.set({[r]:l}),e("pending",a.code),w(e,l)}async function f(e){let o=(await browser.storage.local.get(r))[r];if(o){if(Date.now()>=o.expiresAt){await browser.storage.local.remove(r);return}return w(e,o)}}function n(e,t){document.getElementById(e).textContent=t}var u=!1,y;function v(e,t=!1){clearTimeout(y),n("feedback",e),t&&(y=setTimeout(()=>n("feedback",""),3500))}async function d(e){let t=await browser.runtime.sendMessage({type:"gazesight-ui",action:e});if(!t?.ok)throw new Error(t?.error??"GazeSight unavailable. Open settings to check pairing.");return t.value}async function s(){let e=await d("status");document.body.dataset.connected=String(e.connected),n("connection",e.connected?"Connected":"Disconnected");let t=await browser.storage.local.get(["token","port"]);document.getElementById("pairing-connect").hidden=e.connected,n("pairing-title",typeof t.token=="string"?"Reconnect or re-pair":"Connect your browser"),document.body.dataset.background=e.backgroundCapture.enabled?"enabled":"disabled",n("background-state",e.backgroundCapture.enabled?"On":"Off"),n("background-toggle",e.backgroundCapture.enabled?"Disable":"Enable"),document.getElementById("background-toggle").setAttribute("aria-pressed",String(e.backgroundCapture.enabled)),n("background-description",e.backgroundCapture.enabled?"GazeSight can capture this target while you work elsewhere.":e.backgroundCapture.permissionGranted?"Background mode is off. Firefox still reports the optional permission; remove it in Add-ons Manager if needed.":e.backgroundMode==="permission_denied"?"Permission was not granted. Standard foreground capture remains available.":"Allow GazeSight to keep capturing the selected development tab while you work in another tab or application."),document.body.dataset.continuous=e.continuousAuthorization.enabled?"enabled":"disabled",n("continuous-state",e.continuousAuthorization.enabled?"On":"Off"),n("continuous-toggle",e.continuousAuthorization.enabled?"Disable":"Enable"),document.getElementById("continuous-toggle").setAttribute("aria-pressed",String(e.continuousAuthorization.enabled)),n("continuous-description",e.continuousAuthorization.enabled?e.continuousAuthorization.authorizedOrigin?`Staying connected to ${e.continuousAuthorization.authorizedOrigin} across reloads, navigation and previews.`:"Keeps GazeSight connected across reloads, navigation and previews once you select a tab.":"Keep GazeSight connected to this development tab across reloads, navigation and responsive previews, so you don't have to reauthorize it every time."),n("pairing",e.paired?"Paired":"Not paired"),n("target",e.targetState==="authorized"?"Authorized":e.targetState==="none"?"None":"Reauthorize"),n("url",e.url??"No target selected"),n("viewport",e.viewport?`${e.viewport.width} \xD7 ${e.viewport.height} CSS \xB7 DPR ${e.viewport.devicePixelRatio}`:"Viewport unavailable"),n("select-label",e.targetState==="reauthorization_required"?"Reauthorize this tab":"Use this tab"),n("last-capture",e.lastCapture?`${e.lastCapture.mode} \xB7 ${e.lastCapture.width} \xD7 ${e.lastCapture.height} \xB7 ${new Date(e.lastCapture.time).toLocaleTimeString()}`:"None yet"),n("last-error",e.lastError?`${e.lastError.code}: ${e.lastError.message}`:"None");for(let o of["fast","stable"])document.getElementById(o).disabled=u||!e.connected||e.targetState!=="authorized";document.getElementById("disconnect").disabled=u||e.targetState==="none"}var I={not_connected:"Not connected. Choose Connect and approve the matching code in your terminal.",searching:"Searching for local GazeSight\u2026",pending:"Pairing request pending.",waiting_approval:"Check that this code matches the one in your terminal.",connected:"Connected.",unavailable:"Connection unavailable. Run gazesight pair and try again.",expired:"Pairing expired. Try again.",reconnect_required:"Reconnect required."};function S(e,t){let o=document.getElementById("pairing-code");n("pairing-message",I[e]),o.hidden=!t,o.textContent=t?`Verification code: ${t}`:""}document.getElementById("connect").addEventListener("click",async()=>{let e=document.getElementById("connect");e.disabled=!0;let t=await browser.storage.local.get(["port"]);await h(S,Number(t.port??32147)),e.disabled=!1,await s()});(async()=>typeof(await browser.storage.local.get(["token"])).token!="string"&&await f(S)&&await s())();for(let e of["select","disconnect","fast","stable","diagnostics"])document.getElementById(e).addEventListener("click",async()=>{if(!u){u=!0,document.querySelectorAll("button").forEach(t=>t.disabled=!0),n("feedback",e==="fast"||e==="stable"?"Capturing\u2026":"Working\u2026");try{if(e==="fast"&&!await browser.permissions.contains({permissions:["downloads"]})){await browser.windows.create({url:browser.runtime.getURL("permission.html?action=download"),type:"popup",focused:!0,width:600,height:700}),window.close();return}let t=await d(e);e==="diagnostics"&&(document.getElementById("diagnostics-panel").hidden=!1,n("diagnostics-output",JSON.stringify(t,null,2))),v(e==="disconnect"?"Target disconnected.":e==="select"?"Target selected.":e==="diagnostics"?"Diagnostics updated.":e==="fast"?`\u2713 Saved to Downloads/${t.filename}`:"Capture completed.",e==="fast")}catch(t){v(t instanceof Error?t.message:"Operation failed.")}finally{u=!1,document.querySelectorAll("button").forEach(t=>t.disabled=!1),await s().catch(t=>n("feedback",String(t)))}}});document.getElementById("copy-url").addEventListener("click",async()=>{let e=document.getElementById("url").textContent;if(!(!e||e==="No target selected"))try{await navigator.clipboard.writeText(e),n("feedback","URL copied.")}catch{n("feedback","Unable to copy URL. Select and copy it manually.")}});document.getElementById("background-toggle").addEventListener("click",async()=>{if((await d("status")).backgroundCapture.enabled){await d("background-disable"),await s();return}await browser.windows.create({url:browser.runtime.getURL("permission.html?action=background"),type:"popup",focused:!0,width:600,height:700}),window.close()});document.getElementById("continuous-toggle").addEventListener("click",async()=>{let e=await d("status");if(e.continuousAuthorization.enabled){await d("continuous-disable"),await s();return}if(e.continuousAuthorization.permissionGranted){await d("continuous-enable"),await s();return}await browser.windows.create({url:browser.runtime.getURL("permission.html?action=continuous"),type:"popup",focused:!0,width:600,height:700}),window.close()});browser.commands.getAll().then(e=>{let t=e.find(o=>o.name==="gazesight-select-target")?.shortcut;t&&n("shortcut",t)}).catch(()=>{});document.getElementById("settings").addEventListener("click",()=>void browser.runtime.openOptionsPage());s().catch(e=>n("feedback",String(e)));var P=setInterval(()=>{u||s().catch(e=>n("feedback",String(e)))},2e3);window.addEventListener("pagehide",()=>clearInterval(P),{once:!0});
|
|
Binary file
|
package/dist/mcp/index.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// apps/mcp-server/src/index.ts
|
|
4
|
+
import { spawn } from "node:child_process";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
4
6
|
import { StdioServerTransport as StdioServerTransport2 } from "@modelcontextprotocol/server/stdio";
|
|
5
7
|
|
|
6
8
|
// apps/mcp-server/src/config.ts
|
|
@@ -119,7 +121,7 @@ function loadConfig(env = process.env) {
|
|
|
119
121
|
}
|
|
120
122
|
|
|
121
123
|
// packages/protocol/src/release.ts
|
|
122
|
-
var VERSION = "0.5.
|
|
124
|
+
var VERSION = "0.5.3";
|
|
123
125
|
|
|
124
126
|
// apps/mcp-server/src/server.ts
|
|
125
127
|
import { McpServer } from "@modelcontextprotocol/server";
|
|
@@ -738,7 +740,7 @@ async function runDiff(baseline, current, threshold) {
|
|
|
738
740
|
);
|
|
739
741
|
let timer;
|
|
740
742
|
try {
|
|
741
|
-
return await new Promise((
|
|
743
|
+
return await new Promise((resolve2, reject) => {
|
|
742
744
|
timer = setTimeout(
|
|
743
745
|
() => reject(
|
|
744
746
|
new GSError(
|
|
@@ -750,7 +752,7 @@ async function runDiff(baseline, current, threshold) {
|
|
|
750
752
|
);
|
|
751
753
|
worker.once(
|
|
752
754
|
"message",
|
|
753
|
-
(result) => result.ok ?
|
|
755
|
+
(result) => result.ok ? resolve2(result.value) : reject(new GSError(result.code, "Local PNG diff failed."))
|
|
754
756
|
);
|
|
755
757
|
worker.once(
|
|
756
758
|
"error",
|
|
@@ -1486,7 +1488,7 @@ var RequestRouter = class {
|
|
|
1486
1488
|
type,
|
|
1487
1489
|
payload
|
|
1488
1490
|
});
|
|
1489
|
-
return new Promise((
|
|
1491
|
+
return new Promise((resolve2, reject) => {
|
|
1490
1492
|
const timer = setTimeout(() => {
|
|
1491
1493
|
this.pending.delete(request.id);
|
|
1492
1494
|
reject(
|
|
@@ -1496,7 +1498,7 @@ var RequestRouter = class {
|
|
|
1496
1498
|
)
|
|
1497
1499
|
);
|
|
1498
1500
|
}, this.timeoutMs);
|
|
1499
|
-
this.pending.set(request.id, { resolve, reject, timer });
|
|
1501
|
+
this.pending.set(request.id, { resolve: resolve2, reject, timer });
|
|
1500
1502
|
try {
|
|
1501
1503
|
this.send(request);
|
|
1502
1504
|
} catch (error) {
|
|
@@ -1718,7 +1720,7 @@ var PairingService = class {
|
|
|
1718
1720
|
}
|
|
1719
1721
|
listPending() {
|
|
1720
1722
|
this.expire();
|
|
1721
|
-
return [...this.pending.values()].filter((item) => item.state === "pending").map(({ id, browser, extensionId, code, createdAt, expiresAt }) => ({ id, browser, extensionId, code, createdAt, expiresAt }));
|
|
1723
|
+
return [...this.pending.values()].filter((item) => item.state === "pending").map(({ id, browser, extensionId, code, createdAt, expiresAt }) => ({ id, browser, extensionId, code, createdAt, expiresAt })).sort((a, b) => b.createdAt - a.createdAt);
|
|
1722
1724
|
}
|
|
1723
1725
|
listPairings() {
|
|
1724
1726
|
return this.pairings.map(({ id, browser, extensionId, origin, createdAt, revokedAt }) => ({ id, browser, extensionId, origin, createdAt, ...revokedAt ? { revokedAt } : {} }));
|
|
@@ -2102,7 +2104,7 @@ var Bridge = class {
|
|
|
2102
2104
|
async ready() {
|
|
2103
2105
|
if (this.listenError) throw this.listenError;
|
|
2104
2106
|
if (this.server.address()) return;
|
|
2105
|
-
await new Promise((
|
|
2107
|
+
await new Promise((resolve2, reject) => {
|
|
2106
2108
|
const cleanup = () => {
|
|
2107
2109
|
this.server.off("listening", onListening);
|
|
2108
2110
|
this.server.off("error", onError);
|
|
@@ -2110,7 +2112,7 @@ var Bridge = class {
|
|
|
2110
2112
|
};
|
|
2111
2113
|
const onListening = () => {
|
|
2112
2114
|
cleanup();
|
|
2113
|
-
|
|
2115
|
+
resolve2();
|
|
2114
2116
|
};
|
|
2115
2117
|
const onError = () => {
|
|
2116
2118
|
cleanup();
|
|
@@ -2129,9 +2131,9 @@ var Bridge = class {
|
|
|
2129
2131
|
this.wiring.clear();
|
|
2130
2132
|
for (const client of this.server.clients) client.terminate();
|
|
2131
2133
|
if (this.http.listening)
|
|
2132
|
-
await new Promise((
|
|
2134
|
+
await new Promise((resolve2) => this.http.close(() => resolve2()));
|
|
2133
2135
|
else if (this.server.address())
|
|
2134
|
-
await new Promise((
|
|
2136
|
+
await new Promise((resolve2) => this.server.close(() => resolve2()));
|
|
2135
2137
|
})();
|
|
2136
2138
|
return this.closePromise;
|
|
2137
2139
|
}
|
|
@@ -2260,14 +2262,17 @@ import { EventEmitter } from "node:events";
|
|
|
2260
2262
|
// apps/mcp-server/src/runtime-ipc/protocol.ts
|
|
2261
2263
|
import { createHash as createHash2, randomBytes as randomBytes2 } from "node:crypto";
|
|
2262
2264
|
import { tmpdir } from "node:os";
|
|
2263
|
-
import { basename, dirname as dirname2, join as join3 } from "node:path";
|
|
2265
|
+
import { basename, dirname as dirname2, join as join3, resolve } from "node:path";
|
|
2264
2266
|
var RUNTIME_PROTOCOL_VERSION = 1;
|
|
2265
2267
|
var MAX_FRAME_BYTES = 32 * 1024 * 1024;
|
|
2266
2268
|
var MAX_PENDING_REQUESTS = 16;
|
|
2267
2269
|
var REQUEST_TIMEOUT_MS = 3e5;
|
|
2268
2270
|
var HANDSHAKE_TIMEOUT_MS = 5e3;
|
|
2269
2271
|
function runtimeManifestPath(configPath) {
|
|
2270
|
-
|
|
2272
|
+
if (basename(configPath).toLowerCase() === "config.json")
|
|
2273
|
+
return join3(dirname2(configPath), "runtime-ipc.json");
|
|
2274
|
+
const identity = createHash2("sha256").update(resolve(configPath).toLowerCase()).digest("hex").slice(0, 16);
|
|
2275
|
+
return join3(dirname2(configPath), `runtime-ipc-${identity}.json`);
|
|
2271
2276
|
}
|
|
2272
2277
|
function runtimeEndpoint(platform, userId, home, env = process.env, temporaryDirectory = tmpdir()) {
|
|
2273
2278
|
const runtimeIdentity = randomBytes2(24).toString("hex");
|
|
@@ -2406,9 +2411,9 @@ var JsonLineChannel = class extends EventEmitter {
|
|
|
2406
2411
|
async nextFrame(timeoutMs) {
|
|
2407
2412
|
if (this.frames.length) return this.frames.shift();
|
|
2408
2413
|
if (this.closed) throw new Error("GS_RUNTIME_UNAVAILABLE");
|
|
2409
|
-
return new Promise((
|
|
2414
|
+
return new Promise((resolve2, reject) => {
|
|
2410
2415
|
const waiter = {
|
|
2411
|
-
resolve,
|
|
2416
|
+
resolve: resolve2,
|
|
2412
2417
|
reject,
|
|
2413
2418
|
timer: setTimeout(() => {
|
|
2414
2419
|
const index = this.waiters.indexOf(waiter);
|
|
@@ -2471,8 +2476,8 @@ var JsonLineChannel = class extends EventEmitter {
|
|
|
2471
2476
|
throw new Error("GS_RUNTIME_INVALID_MESSAGE");
|
|
2472
2477
|
}
|
|
2473
2478
|
if (!this.socket.write(data))
|
|
2474
|
-
await new Promise((
|
|
2475
|
-
this.socket.once("drain",
|
|
2479
|
+
await new Promise((resolve2, reject) => {
|
|
2480
|
+
this.socket.once("drain", resolve2);
|
|
2476
2481
|
this.socket.once("error", reject);
|
|
2477
2482
|
});
|
|
2478
2483
|
}
|
|
@@ -2547,20 +2552,20 @@ function safeToRemove(path) {
|
|
|
2547
2552
|
).catch(() => false);
|
|
2548
2553
|
}
|
|
2549
2554
|
function endpointIsLive(endpoint) {
|
|
2550
|
-
return new Promise((
|
|
2555
|
+
return new Promise((resolve2) => {
|
|
2551
2556
|
const socket = connectSocket(endpoint);
|
|
2552
2557
|
const timer = setTimeout(() => {
|
|
2553
2558
|
socket.destroy();
|
|
2554
|
-
|
|
2559
|
+
resolve2(false);
|
|
2555
2560
|
}, 250);
|
|
2556
2561
|
socket.once("connect", () => {
|
|
2557
2562
|
clearTimeout(timer);
|
|
2558
2563
|
socket.destroy();
|
|
2559
|
-
|
|
2564
|
+
resolve2(true);
|
|
2560
2565
|
});
|
|
2561
2566
|
socket.once("error", () => {
|
|
2562
2567
|
clearTimeout(timer);
|
|
2563
|
-
|
|
2568
|
+
resolve2(false);
|
|
2564
2569
|
});
|
|
2565
2570
|
});
|
|
2566
2571
|
}
|
|
@@ -2650,12 +2655,12 @@ var RuntimeIpcServer = class {
|
|
|
2650
2655
|
}
|
|
2651
2656
|
this.server = createServer3((socket) => this.accept(socket));
|
|
2652
2657
|
this.server.maxConnections = 32;
|
|
2653
|
-
await new Promise((
|
|
2658
|
+
await new Promise((resolve2, reject) => {
|
|
2654
2659
|
const onError = (error) => reject(error);
|
|
2655
2660
|
this.server.once("error", onError);
|
|
2656
2661
|
this.server.listen(this.endpointInfo.endpoint, () => {
|
|
2657
2662
|
this.server.off("error", onError);
|
|
2658
|
-
|
|
2663
|
+
resolve2();
|
|
2659
2664
|
});
|
|
2660
2665
|
});
|
|
2661
2666
|
if (this.platform !== "win32")
|
|
@@ -2762,7 +2767,7 @@ var RuntimeIpcServer = class {
|
|
|
2762
2767
|
this.hosts.clear();
|
|
2763
2768
|
this.runtime.setMcpClientCount(0);
|
|
2764
2769
|
if (this.server)
|
|
2765
|
-
await new Promise((
|
|
2770
|
+
await new Promise((resolve2) => this.server.close(() => resolve2()));
|
|
2766
2771
|
if (await safeToRemove(this.manifestPath)) {
|
|
2767
2772
|
try {
|
|
2768
2773
|
const value = JSON.parse(await readFile2(this.manifestPath, "utf8"));
|
|
@@ -2833,7 +2838,7 @@ async function readManifest(path) {
|
|
|
2833
2838
|
}
|
|
2834
2839
|
}
|
|
2835
2840
|
function connect(endpoint) {
|
|
2836
|
-
return new Promise((
|
|
2841
|
+
return new Promise((resolve2, reject) => {
|
|
2837
2842
|
const socket = connectSocket2(endpoint);
|
|
2838
2843
|
const timer = setTimeout(() => {
|
|
2839
2844
|
socket.destroy();
|
|
@@ -2841,7 +2846,7 @@ function connect(endpoint) {
|
|
|
2841
2846
|
}, HANDSHAKE_TIMEOUT_MS);
|
|
2842
2847
|
socket.once("connect", () => {
|
|
2843
2848
|
clearTimeout(timer);
|
|
2844
|
-
|
|
2849
|
+
resolve2(socket);
|
|
2845
2850
|
});
|
|
2846
2851
|
socket.once("error", () => {
|
|
2847
2852
|
clearTimeout(timer);
|
|
@@ -2988,7 +2993,8 @@ try {
|
|
|
2988
2993
|
await runDesktopAdapter(
|
|
2989
2994
|
process.env.GAZESIGHT_CONFIG_FILE ?? defaultConfigPath()
|
|
2990
2995
|
);
|
|
2991
|
-
} else if (args.includes("--desktop-runtime")) {
|
|
2996
|
+
} else if (args.includes("--desktop-runtime") || args.includes("--auto-runtime")) {
|
|
2997
|
+
const auto = args.includes("--auto-runtime");
|
|
2992
2998
|
const config = loadConfig();
|
|
2993
2999
|
const runtime = createRuntime(config);
|
|
2994
3000
|
const ipc = new RuntimeIpcServer(
|
|
@@ -3006,40 +3012,85 @@ try {
|
|
|
3006
3012
|
}
|
|
3007
3013
|
console.error(`INFO bridge listening 127.0.0.1:${config.port}`);
|
|
3008
3014
|
let closing = false;
|
|
3015
|
+
let idleTimer;
|
|
3009
3016
|
const close = async () => {
|
|
3010
3017
|
if (closing) return;
|
|
3011
3018
|
closing = true;
|
|
3019
|
+
if (idleTimer) clearInterval(idleTimer);
|
|
3012
3020
|
await ipc.stop();
|
|
3013
3021
|
await runtime.stop();
|
|
3014
3022
|
};
|
|
3015
3023
|
process.once("SIGINT", () => void close());
|
|
3016
3024
|
process.once("SIGTERM", () => void close());
|
|
3017
|
-
|
|
3025
|
+
if (auto) {
|
|
3026
|
+
let idleSince = Date.now();
|
|
3027
|
+
idleTimer = setInterval(() => {
|
|
3028
|
+
if (ipc.connectedClients > 0) idleSince = Date.now();
|
|
3029
|
+
else if (Date.now() - idleSince > 3e4) void close();
|
|
3030
|
+
}, 1e3);
|
|
3031
|
+
} else process.stdin.once("end", () => void close());
|
|
3018
3032
|
} else {
|
|
3019
3033
|
const config = loadConfig();
|
|
3020
|
-
|
|
3021
|
-
|
|
3022
|
-
|
|
3023
|
-
|
|
3024
|
-
|
|
3025
|
-
|
|
3026
|
-
|
|
3027
|
-
|
|
3028
|
-
|
|
3029
|
-
|
|
3030
|
-
|
|
3034
|
+
if (!config.configPath) {
|
|
3035
|
+
const runtime = createRuntime(config);
|
|
3036
|
+
let closeHost = async () => {
|
|
3037
|
+
};
|
|
3038
|
+
try {
|
|
3039
|
+
const session = await runtime.connectMcpHost();
|
|
3040
|
+
closeHost = session.close;
|
|
3041
|
+
await session.host.connect(new StdioServerTransport2());
|
|
3042
|
+
} catch (error) {
|
|
3043
|
+
await runtime.stop();
|
|
3044
|
+
await closeHost();
|
|
3045
|
+
throw error;
|
|
3046
|
+
}
|
|
3047
|
+
console.error(`INFO bridge listening 127.0.0.1:${config.port}`);
|
|
3048
|
+
let closing = false;
|
|
3049
|
+
const close = async () => {
|
|
3050
|
+
if (closing) return;
|
|
3051
|
+
closing = true;
|
|
3052
|
+
await runtime.stop();
|
|
3053
|
+
await closeHost();
|
|
3054
|
+
};
|
|
3055
|
+
process.once("SIGINT", () => void close());
|
|
3056
|
+
process.once("SIGTERM", () => void close());
|
|
3057
|
+
process.stdin.once("end", () => void close());
|
|
3058
|
+
} else {
|
|
3059
|
+
const configPath = config.configPath;
|
|
3060
|
+
const attach = async () => {
|
|
3061
|
+
try {
|
|
3062
|
+
await runDesktopAdapter(configPath);
|
|
3063
|
+
return true;
|
|
3064
|
+
} catch (error) {
|
|
3065
|
+
if (error instanceof Error && error.message === "GS_DESKTOP_RUNTIME_NOT_FOUND")
|
|
3066
|
+
return false;
|
|
3067
|
+
throw error;
|
|
3068
|
+
}
|
|
3069
|
+
};
|
|
3070
|
+
if (!await attach()) {
|
|
3071
|
+
const child = spawn(
|
|
3072
|
+
process.execPath,
|
|
3073
|
+
[fileURLToPath(import.meta.url), "--auto-runtime"],
|
|
3074
|
+
{
|
|
3075
|
+
detached: true,
|
|
3076
|
+
windowsHide: true,
|
|
3077
|
+
stdio: "ignore",
|
|
3078
|
+
env: { ...process.env, GAZESIGHT_CONFIG_FILE: configPath }
|
|
3079
|
+
}
|
|
3080
|
+
);
|
|
3081
|
+
await new Promise((resolve2, reject) => {
|
|
3082
|
+
child.once("spawn", resolve2);
|
|
3083
|
+
child.once("error", reject);
|
|
3084
|
+
});
|
|
3085
|
+
child.unref();
|
|
3086
|
+
let attached = false;
|
|
3087
|
+
for (let attempt = 0; attempt < 50 && !attached; attempt++) {
|
|
3088
|
+
await new Promise((resolve2) => setTimeout(resolve2, 100));
|
|
3089
|
+
attached = await attach();
|
|
3090
|
+
}
|
|
3091
|
+
if (!attached) throw new Error("GS_RUNTIME_UNAVAILABLE");
|
|
3092
|
+
}
|
|
3031
3093
|
}
|
|
3032
|
-
console.error(`INFO bridge listening 127.0.0.1:${config.port}`);
|
|
3033
|
-
let closing = false;
|
|
3034
|
-
const close = async () => {
|
|
3035
|
-
if (closing) return;
|
|
3036
|
-
closing = true;
|
|
3037
|
-
await runtime.stop();
|
|
3038
|
-
await closeHost();
|
|
3039
|
-
};
|
|
3040
|
-
process.once("SIGINT", () => void close());
|
|
3041
|
-
process.once("SIGTERM", () => void close());
|
|
3042
|
-
process.stdin.once("end", () => void close());
|
|
3043
3094
|
}
|
|
3044
3095
|
} catch (error) {
|
|
3045
3096
|
const code = error instanceof Error ? error.message : "";
|