gazesight 0.3.5

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.
Files changed (52) hide show
  1. package/LICENSE +37 -0
  2. package/README.md +102 -0
  3. package/dist/chrome/background-capture-offscreen.html +10 -0
  4. package/dist/chrome/background-capture-offscreen.js +1 -0
  5. package/dist/chrome/background.js +62 -0
  6. package/dist/chrome/content.js +62 -0
  7. package/dist/chrome/diagnostics-page.js +1 -0
  8. package/dist/chrome/fullpage-stitch-offscreen.html +10 -0
  9. package/dist/chrome/fullpage-stitch-offscreen.js +1 -0
  10. package/dist/chrome/icons/icon-128.png +0 -0
  11. package/dist/chrome/icons/icon-16.png +0 -0
  12. package/dist/chrome/icons/icon-32.png +0 -0
  13. package/dist/chrome/icons/icon-48.png +0 -0
  14. package/dist/chrome/icons/icon-64.png +0 -0
  15. package/dist/chrome/icons/icon-96.png +0 -0
  16. package/dist/chrome/logo_sans_background.png +0 -0
  17. package/dist/chrome/manifest.json +70 -0
  18. package/dist/chrome/options.css +66 -0
  19. package/dist/chrome/options.html +76 -0
  20. package/dist/chrome/options.js +82 -0
  21. package/dist/chrome/permission.css +13 -0
  22. package/dist/chrome/permission.html +15 -0
  23. package/dist/chrome/permission.js +1 -0
  24. package/dist/chrome/popup.css +446 -0
  25. package/dist/chrome/popup.html +191 -0
  26. package/dist/chrome/popup.js +1 -0
  27. package/dist/cli/index.js +320 -0
  28. package/dist/firefox/background.js +62 -0
  29. package/dist/firefox/content.js +62 -0
  30. package/dist/firefox/diagnostics-page.js +1 -0
  31. package/dist/firefox/icons/icon-128.png +0 -0
  32. package/dist/firefox/icons/icon-16.png +0 -0
  33. package/dist/firefox/icons/icon-32.png +0 -0
  34. package/dist/firefox/icons/icon-48.png +0 -0
  35. package/dist/firefox/icons/icon-64.png +0 -0
  36. package/dist/firefox/icons/icon-96.png +0 -0
  37. package/dist/firefox/logo_sans_background.png +0 -0
  38. package/dist/firefox/manifest.json +90 -0
  39. package/dist/firefox/options.css +66 -0
  40. package/dist/firefox/options.html +76 -0
  41. package/dist/firefox/options.js +82 -0
  42. package/dist/firefox/permission.css +13 -0
  43. package/dist/firefox/permission.html +15 -0
  44. package/dist/firefox/permission.js +1 -0
  45. package/dist/firefox/popup.css +446 -0
  46. package/dist/firefox/popup.html +191 -0
  47. package/dist/firefox/popup.js +1 -0
  48. package/dist/gazesight-firefox.xpi +0 -0
  49. package/dist/mcp/index.js +1428 -0
  50. package/dist/worker/visual-diff.js +642 -0
  51. package/docs/preview-matrix.csv +6 -0
  52. package/package.json +130 -0
@@ -0,0 +1 @@
1
+ var W=Object.create;var $=Object.defineProperty;var H=Object.getOwnPropertyDescriptor;var Z=Object.getOwnPropertyNames;var K=Object.getPrototypeOf,V=Object.prototype.hasOwnProperty;var J=(e,t)=>()=>(t||e((t={exports:{}}).exports,t),t.exports);var Q=(e,t,l,b)=>{if(t&&typeof t=="object"||typeof t=="function")for(let x of Z(t))!V.call(e,x)&&x!==l&&$(e,x,{get:()=>t[x],enumerable:!(b=H(t,x))||b.enumerable});return e};var X=(e,t,l)=>(l=e!=null?W(K(e)):{},Q(t||!e||!e.__esModule?$(l,"default",{value:e,enumerable:!0}):l,e));var O=J((P,N)=>{(function(e,t){if(typeof define=="function"&&define.amd)define("webextension-polyfill",["module"],t);else if(typeof P<"u")t(N);else{var l={exports:{}};t(l),e.browser=l.exports}})(typeof globalThis<"u"?globalThis:typeof self<"u"?self:P,function(e){"use strict";if(!(globalThis.chrome&&globalThis.chrome.runtime&&globalThis.chrome.runtime.id))throw new Error("This script should only be loaded in a browser extension.");if(globalThis.browser&&globalThis.browser.runtime&&globalThis.browser.runtime.id)e.exports=globalThis.browser;else{let t="The message port closed before a response was received.",l=b=>{let x={alarms:{clear:{minArgs:0,maxArgs:1},clearAll:{minArgs:0,maxArgs:0},get:{minArgs:0,maxArgs:1},getAll:{minArgs:0,maxArgs:0}},bookmarks:{create:{minArgs:1,maxArgs:1},get:{minArgs:1,maxArgs:1},getChildren:{minArgs:1,maxArgs:1},getRecent:{minArgs:1,maxArgs:1},getSubTree:{minArgs:1,maxArgs:1},getTree:{minArgs:0,maxArgs:0},move:{minArgs:2,maxArgs:2},remove:{minArgs:1,maxArgs:1},removeTree:{minArgs:1,maxArgs:1},search:{minArgs:1,maxArgs:1},update:{minArgs:2,maxArgs:2}},browserAction:{disable:{minArgs:0,maxArgs:1,fallbackToNoCallback:!0},enable:{minArgs:0,maxArgs:1,fallbackToNoCallback:!0},getBadgeBackgroundColor:{minArgs:1,maxArgs:1},getBadgeText:{minArgs:1,maxArgs:1},getPopup:{minArgs:1,maxArgs:1},getTitle:{minArgs:1,maxArgs:1},openPopup:{minArgs:0,maxArgs:0},setBadgeBackgroundColor:{minArgs:1,maxArgs:1,fallbackToNoCallback:!0},setBadgeText:{minArgs:1,maxArgs:1,fallbackToNoCallback:!0},setIcon:{minArgs:1,maxArgs:1},setPopup:{minArgs:1,maxArgs:1,fallbackToNoCallback:!0},setTitle:{minArgs:1,maxArgs:1,fallbackToNoCallback:!0}},browsingData:{remove:{minArgs:2,maxArgs:2},removeCache:{minArgs:1,maxArgs:1},removeCookies:{minArgs:1,maxArgs:1},removeDownloads:{minArgs:1,maxArgs:1},removeFormData:{minArgs:1,maxArgs:1},removeHistory:{minArgs:1,maxArgs:1},removeLocalStorage:{minArgs:1,maxArgs:1},removePasswords:{minArgs:1,maxArgs:1},removePluginData:{minArgs:1,maxArgs:1},settings:{minArgs:0,maxArgs:0}},commands:{getAll:{minArgs:0,maxArgs:0}},contextMenus:{remove:{minArgs:1,maxArgs:1},removeAll:{minArgs:0,maxArgs:0},update:{minArgs:2,maxArgs:2}},cookies:{get:{minArgs:1,maxArgs:1},getAll:{minArgs:1,maxArgs:1},getAllCookieStores:{minArgs:0,maxArgs:0},remove:{minArgs:1,maxArgs:1},set:{minArgs:1,maxArgs:1}},devtools:{inspectedWindow:{eval:{minArgs:1,maxArgs:2,singleCallbackArg:!1}},panels:{create:{minArgs:3,maxArgs:3,singleCallbackArg:!0},elements:{createSidebarPane:{minArgs:1,maxArgs:1}}}},downloads:{cancel:{minArgs:1,maxArgs:1},download:{minArgs:1,maxArgs:1},erase:{minArgs:1,maxArgs:1},getFileIcon:{minArgs:1,maxArgs:2},open:{minArgs:1,maxArgs:1,fallbackToNoCallback:!0},pause:{minArgs:1,maxArgs:1},removeFile:{minArgs:1,maxArgs:1},resume:{minArgs:1,maxArgs:1},search:{minArgs:1,maxArgs:1},show:{minArgs:1,maxArgs:1,fallbackToNoCallback:!0}},extension:{isAllowedFileSchemeAccess:{minArgs:0,maxArgs:0},isAllowedIncognitoAccess:{minArgs:0,maxArgs:0}},history:{addUrl:{minArgs:1,maxArgs:1},deleteAll:{minArgs:0,maxArgs:0},deleteRange:{minArgs:1,maxArgs:1},deleteUrl:{minArgs:1,maxArgs:1},getVisits:{minArgs:1,maxArgs:1},search:{minArgs:1,maxArgs:1}},i18n:{detectLanguage:{minArgs:1,maxArgs:1},getAcceptLanguages:{minArgs:0,maxArgs:0}},identity:{launchWebAuthFlow:{minArgs:1,maxArgs:1}},idle:{queryState:{minArgs:1,maxArgs:1}},management:{get:{minArgs:1,maxArgs:1},getAll:{minArgs:0,maxArgs:0},getSelf:{minArgs:0,maxArgs:0},setEnabled:{minArgs:2,maxArgs:2},uninstallSelf:{minArgs:0,maxArgs:1}},notifications:{clear:{minArgs:1,maxArgs:1},create:{minArgs:1,maxArgs:2},getAll:{minArgs:0,maxArgs:0},getPermissionLevel:{minArgs:0,maxArgs:0},update:{minArgs:2,maxArgs:2}},pageAction:{getPopup:{minArgs:1,maxArgs:1},getTitle:{minArgs:1,maxArgs:1},hide:{minArgs:1,maxArgs:1,fallbackToNoCallback:!0},setIcon:{minArgs:1,maxArgs:1},setPopup:{minArgs:1,maxArgs:1,fallbackToNoCallback:!0},setTitle:{minArgs:1,maxArgs:1,fallbackToNoCallback:!0},show:{minArgs:1,maxArgs:1,fallbackToNoCallback:!0}},permissions:{contains:{minArgs:1,maxArgs:1},getAll:{minArgs:0,maxArgs:0},remove:{minArgs:1,maxArgs:1},request:{minArgs:1,maxArgs:1}},runtime:{getBackgroundPage:{minArgs:0,maxArgs:0},getPlatformInfo:{minArgs:0,maxArgs:0},openOptionsPage:{minArgs:0,maxArgs:0},requestUpdateCheck:{minArgs:0,maxArgs:0},sendMessage:{minArgs:1,maxArgs:3},sendNativeMessage:{minArgs:2,maxArgs:2},setUninstallURL:{minArgs:1,maxArgs:1}},sessions:{getDevices:{minArgs:0,maxArgs:1},getRecentlyClosed:{minArgs:0,maxArgs:1},restore:{minArgs:0,maxArgs:1}},storage:{local:{clear:{minArgs:0,maxArgs:0},get:{minArgs:0,maxArgs:1},getBytesInUse:{minArgs:0,maxArgs:1},remove:{minArgs:1,maxArgs:1},set:{minArgs:1,maxArgs:1}},managed:{get:{minArgs:0,maxArgs:1},getBytesInUse:{minArgs:0,maxArgs:1}},sync:{clear:{minArgs:0,maxArgs:0},get:{minArgs:0,maxArgs:1},getBytesInUse:{minArgs:0,maxArgs:1},remove:{minArgs:1,maxArgs:1},set:{minArgs:1,maxArgs:1}}},tabs:{captureVisibleTab:{minArgs:0,maxArgs:2},create:{minArgs:1,maxArgs:1},detectLanguage:{minArgs:0,maxArgs:1},discard:{minArgs:0,maxArgs:1},duplicate:{minArgs:1,maxArgs:1},executeScript:{minArgs:1,maxArgs:2},get:{minArgs:1,maxArgs:1},getCurrent:{minArgs:0,maxArgs:0},getZoom:{minArgs:0,maxArgs:1},getZoomSettings:{minArgs:0,maxArgs:1},goBack:{minArgs:0,maxArgs:1},goForward:{minArgs:0,maxArgs:1},highlight:{minArgs:1,maxArgs:1},insertCSS:{minArgs:1,maxArgs:2},move:{minArgs:2,maxArgs:2},query:{minArgs:1,maxArgs:1},reload:{minArgs:0,maxArgs:2},remove:{minArgs:1,maxArgs:1},removeCSS:{minArgs:1,maxArgs:2},sendMessage:{minArgs:2,maxArgs:3},setZoom:{minArgs:1,maxArgs:2},setZoomSettings:{minArgs:1,maxArgs:2},update:{minArgs:1,maxArgs:2}},topSites:{get:{minArgs:0,maxArgs:0}},webNavigation:{getAllFrames:{minArgs:1,maxArgs:1},getFrame:{minArgs:1,maxArgs:1}},webRequest:{handlerBehaviorChanged:{minArgs:0,maxArgs:0}},windows:{create:{minArgs:0,maxArgs:1},get:{minArgs:1,maxArgs:2},getAll:{minArgs:0,maxArgs:1},getCurrent:{minArgs:0,maxArgs:1},getLastFocused:{minArgs:0,maxArgs:1},remove:{minArgs:1,maxArgs:1},update:{minArgs:2,maxArgs:2}}};if(Object.keys(x).length===0)throw new Error("api-metadata.json has not been included in browser-polyfill");class z extends WeakMap{constructor(s,a=void 0){super(a),this.createItem=s}get(s){return this.has(s)||this.set(s,this.createItem(s)),super.get(s)}}let q=r=>r&&typeof r=="object"&&typeof r.then=="function",B=(r,s)=>(...a)=>{b.runtime.lastError?r.reject(new Error(b.runtime.lastError.message)):s.singleCallbackArg||a.length<=1&&s.singleCallbackArg!==!1?r.resolve(a[0]):r.resolve(a)},y=r=>r==1?"argument":"arguments",U=(r,s)=>function(i,...m){if(m.length<s.minArgs)throw new Error(`Expected at least ${s.minArgs} ${y(s.minArgs)} for ${r}(), got ${m.length}`);if(m.length>s.maxArgs)throw new Error(`Expected at most ${s.maxArgs} ${y(s.maxArgs)} for ${r}(), got ${m.length}`);return new Promise((c,A)=>{if(s.fallbackToNoCallback)try{i[r](...m,B({resolve:c,reject:A},s))}catch(n){console.warn(`${r} API method doesn't seem to support the callback parameter, falling back to call it without a callback: `,n),i[r](...m),s.fallbackToNoCallback=!1,s.noCallback=!0,c()}else s.noCallback?(i[r](...m),c()):i[r](...m,B({resolve:c,reject:A},s))})},R=(r,s,a)=>new Proxy(s,{apply(i,m,c){return a.call(m,r,...c)}}),v=Function.call.bind(Object.prototype.hasOwnProperty),C=(r,s={},a={})=>{let i=Object.create(null),m={has(A,n){return n in r||n in i},get(A,n,u){if(n in i)return i[n];if(!(n in r))return;let g=r[n];if(typeof g=="function")if(typeof s[n]=="function")g=R(r,r[n],s[n]);else if(v(a,n)){let h=U(n,a[n]);g=R(r,r[n],h)}else g=g.bind(r);else if(typeof g=="object"&&g!==null&&(v(s,n)||v(a,n)))g=C(g,s[n],a[n]);else if(v(a,"*"))g=C(g,s[n],a["*"]);else return Object.defineProperty(i,n,{configurable:!0,enumerable:!0,get(){return r[n]},set(h){r[n]=h}}),g;return i[n]=g,g},set(A,n,u,g){return n in i?i[n]=u:r[n]=u,!0},defineProperty(A,n,u){return Reflect.defineProperty(i,n,u)},deleteProperty(A,n){return Reflect.deleteProperty(i,n)}},c=Object.create(r);return new Proxy(c,m)},E=r=>({addListener(s,a,...i){s.addListener(r.get(a),...i)},hasListener(s,a){return s.hasListener(r.get(a))},removeListener(s,a){s.removeListener(r.get(a))}}),D=new z(r=>typeof r!="function"?r:function(a){let i=C(a,{},{getContent:{minArgs:0,maxArgs:0}});r(i)}),L=new z(r=>typeof r!="function"?r:function(a,i,m){let c=!1,A,n=new Promise(k=>{A=function(d){c=!0,k(d)}}),u;try{u=r(a,i,A)}catch(k){u=Promise.reject(k)}let g=u!==!0&&q(u);if(u!==!0&&!g&&!c)return!1;let h=k=>{k.then(d=>{m(d)},d=>{let T;d&&(d instanceof Error||typeof d.message=="string")?T=d.message:T="An unexpected error occurred",m({__mozWebExtensionPolyfillReject__:!0,message:T})}).catch(d=>{console.error("Failed to send onMessage rejected reply",d)})};return h(g?u:n),!0}),j=({reject:r,resolve:s},a)=>{b.runtime.lastError?b.runtime.lastError.message===t?s():r(new Error(b.runtime.lastError.message)):a&&a.__mozWebExtensionPolyfillReject__?r(new Error(a.message)):s(a)},M=(r,s,a,...i)=>{if(i.length<s.minArgs)throw new Error(`Expected at least ${s.minArgs} ${y(s.minArgs)} for ${r}(), got ${i.length}`);if(i.length>s.maxArgs)throw new Error(`Expected at most ${s.maxArgs} ${y(s.maxArgs)} for ${r}(), got ${i.length}`);return new Promise((m,c)=>{let A=j.bind(null,{resolve:m,reject:c});i.push(A),a.sendMessage(...i)})},G={devtools:{network:{onRequestFinished:E(D)}},runtime:{onMessage:E(L),onMessageExternal:E(L),sendMessage:M.bind(null,"sendMessage",{minArgs:1,maxArgs:3})},tabs:{sendMessage:M.bind(null,"sendMessage",{minArgs:2,maxArgs:3})}},S={clear:{minArgs:1,maxArgs:1},get:{minArgs:1,maxArgs:1},set:{minArgs:1,maxArgs:1}};return x.privacy={network:{"*":S},services:{"*":S},websites:{"*":S}},C(b,G,x)};e.exports=l(chrome)}})});var I=X(O(),1);globalThis.browser=I.default;function o(e,t){document.getElementById(e).textContent=t}var p=!1,_;function F(e,t=!1){clearTimeout(_),o("feedback",e),t&&(_=setTimeout(()=>o("feedback",""),3500))}async function f(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 w(){let e=await f("status");document.body.dataset.connected=String(e.connected),o("connection",e.connected?"Connected":"Disconnected"),document.body.dataset.background=e.backgroundCapture.enabled?"enabled":"disabled",o("background-state",e.backgroundCapture.enabled?"On":"Off"),o("background-toggle",e.backgroundCapture.enabled?"Disable":"Enable"),document.getElementById("background-toggle").setAttribute("aria-pressed",String(e.backgroundCapture.enabled)),o("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",o("continuous-state",e.continuousAuthorization.enabled?"On":"Off"),o("continuous-toggle",e.continuousAuthorization.enabled?"Disable":"Enable"),document.getElementById("continuous-toggle").setAttribute("aria-pressed",String(e.continuousAuthorization.enabled)),o("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."),o("pairing",e.paired?"Paired":"Not paired"),o("target",e.targetState==="authorized"?"Authorized":e.targetState==="none"?"None":"Reauthorize"),o("url",e.url??"No target selected"),o("viewport",e.viewport?`${e.viewport.width} \xD7 ${e.viewport.height} CSS \xB7 DPR ${e.viewport.devicePixelRatio}`:"Viewport unavailable"),o("select-label",e.targetState==="reauthorization_required"?"Reauthorize this tab":"Use this tab"),o("last-capture",e.lastCapture?`${e.lastCapture.mode} \xB7 ${e.lastCapture.width} \xD7 ${e.lastCapture.height} \xB7 ${new Date(e.lastCapture.time).toLocaleTimeString()}`:"None yet"),o("last-error",e.lastError?`${e.lastError.code}: ${e.lastError.message}`:"None");for(let t of["fast","stable"])document.getElementById(t).disabled=p||!e.connected||e.targetState!=="authorized";document.getElementById("disconnect").disabled=p||e.targetState==="none"}for(let e of["select","disconnect","fast","stable","diagnostics"])document.getElementById(e).addEventListener("click",async()=>{if(!p){p=!0,document.querySelectorAll("button").forEach(t=>t.disabled=!0),o("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 f(e);e==="diagnostics"&&(document.getElementById("diagnostics-panel").hidden=!1,o("diagnostics-output",JSON.stringify(t,null,2))),F(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){F(t instanceof Error?t.message:"Operation failed.")}finally{p=!1,document.querySelectorAll("button").forEach(t=>t.disabled=!1),await w().catch(t=>o("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),o("feedback","URL copied.")}catch{o("feedback","Unable to copy URL. Select and copy it manually.")}});document.getElementById("background-toggle").addEventListener("click",async()=>{if((await f("status")).backgroundCapture.enabled){await f("background-disable"),await w();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 f("status");if(e.continuousAuthorization.enabled){await f("continuous-disable"),await w();return}if(e.continuousAuthorization.permissionGranted){await f("continuous-enable"),await w();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(l=>l.name==="gazesight-select-target")?.shortcut;t&&o("shortcut",t)}).catch(()=>{});document.getElementById("settings").addEventListener("click",()=>void browser.runtime.openOptionsPage());w().catch(e=>o("feedback",String(e)));var Y=setInterval(()=>{p||w().catch(e=>o("feedback",String(e)))},2e3);window.addEventListener("pagehide",()=>clearInterval(Y),{once:!0});
@@ -0,0 +1,320 @@
1
+ #!/usr/bin/env node
2
+
3
+ // apps/cli/src/index.ts
4
+ import { existsSync } from "node:fs";
5
+ import { spawnSync } from "node:child_process";
6
+ import { createInterface } from "node:readline/promises";
7
+ import { resolve, dirname as dirname2, join as join2, delimiter } from "node:path";
8
+ import { fileURLToPath, pathToFileURL } from "node:url";
9
+
10
+ // apps/cli/src/config.ts
11
+ import { randomBytes } from "node:crypto";
12
+ import { readFileSync, writeFileSync, mkdirSync } from "node:fs";
13
+ import { dirname, join } from "node:path";
14
+ import { homedir } from "node:os";
15
+
16
+ // apps/cli/src/config-browser.ts
17
+ import { z } from "zod";
18
+ var agentGuidanceSchema = z.strictObject({
19
+ enabled: z.boolean().default(true),
20
+ visualFidelity: z.boolean().default(true)
21
+ });
22
+ var localConfigSchema = z.strictObject({
23
+ token: z.string().regex(/^[a-f0-9]{64}$/),
24
+ port: z.number().int().min(1024).max(65535),
25
+ allowedOrigins: z.array(z.url()).default([]),
26
+ agentGuidance: agentGuidanceSchema.default({
27
+ enabled: true,
28
+ visualFidelity: true
29
+ })
30
+ });
31
+
32
+ // apps/cli/src/config.ts
33
+ function configPathFor(platform, home, env) {
34
+ if (platform === "win32")
35
+ return join(
36
+ env.APPDATA ?? join(home, "AppData", "Roaming"),
37
+ "GazeSight",
38
+ "config.json"
39
+ );
40
+ if (platform === "darwin")
41
+ return join(
42
+ home,
43
+ "Library",
44
+ "Application Support",
45
+ "GazeSight",
46
+ "config.json"
47
+ );
48
+ return join(
49
+ env.XDG_CONFIG_HOME ?? join(home, ".config"),
50
+ "gazesight",
51
+ "config.json"
52
+ );
53
+ }
54
+ var defaultConfigPath = () => configPathFor(process.platform, homedir(), process.env);
55
+ function readLocalConfig(path) {
56
+ try {
57
+ return localConfigSchema.parse(JSON.parse(readFileSync(path, "utf8")));
58
+ } catch {
59
+ throw new Error(
60
+ "Invalid or missing GazeSight configuration. Run gazesight setup; configuration values are redacted."
61
+ );
62
+ }
63
+ }
64
+ function setupConfig(path, port) {
65
+ const config = localConfigSchema.parse({
66
+ token: randomBytes(32).toString("hex"),
67
+ port
68
+ });
69
+ mkdirSync(dirname(path), { recursive: true });
70
+ try {
71
+ writeFileSync(path, JSON.stringify(config, null, 2), {
72
+ mode: 384,
73
+ flag: "wx"
74
+ });
75
+ } catch (error) {
76
+ if (error.code === "EEXIST")
77
+ return readLocalConfig(path);
78
+ throw new Error(
79
+ "Cannot create the private GazeSight configuration directory."
80
+ );
81
+ }
82
+ return config;
83
+ }
84
+
85
+ // packages/protocol/src/release.ts
86
+ var VERSION = "0.3.5";
87
+
88
+ // apps/cli/src/doctor.ts
89
+ import { z as z2 } from "zod";
90
+ function diagnosticRows(status, reachable = false, authenticated = false) {
91
+ return [
92
+ {
93
+ name: "MCP server",
94
+ pass: reachable && authenticated,
95
+ action: "Start your configured GazeSight MCP server."
96
+ },
97
+ {
98
+ name: "bridge port",
99
+ pass: reachable,
100
+ action: "Start GazeSight on the configured loopback port; check for a port conflict."
101
+ },
102
+ {
103
+ name: "extension connected",
104
+ pass: !!status?.connected,
105
+ action: "Load the Firefox or Chrome extension and import your local config in its extension options."
106
+ },
107
+ {
108
+ name: "pairing",
109
+ pass: authenticated && !!status?.paired,
110
+ action: "Import the same local configuration into Firefox options; verify port and pairing."
111
+ },
112
+ {
113
+ name: "target selected",
114
+ pass: !!status && status.targetState !== "none",
115
+ action: "Open your local preview in Firefox and click the GazeSight icon."
116
+ },
117
+ {
118
+ name: "capture permission",
119
+ pass: status?.targetState === "authorized",
120
+ action: "Open the target tab and click the GazeSight icon once to reauthorize this document."
121
+ },
122
+ {
123
+ name: "Background capture",
124
+ pass: true,
125
+ status: status?.backgroundCapture?.enabled && status.backgroundCapture.permissionGranted ? "ENABLED" : status?.backgroundCapture?.enabled ? "WARN \u2014 permission missing; disable and enable the mode again" : "DISABLED",
126
+ action: "Background capture is optional."
127
+ }
128
+ ];
129
+ }
130
+ async function probe(port, token) {
131
+ try {
132
+ const response = await fetch(`http://127.0.0.1:${port}/gazesight/status`, {
133
+ headers: { Authorization: `Bearer ${token}` },
134
+ signal: AbortSignal.timeout(1500)
135
+ });
136
+ if (!response.ok || response.headers.get("X-GazeSight-Version") !== VERSION)
137
+ return { reachable: true, authenticated: false };
138
+ const status = statusSchema.parse(await response.json());
139
+ return { reachable: true, authenticated: true, status };
140
+ } catch {
141
+ return { reachable: false, authenticated: false };
142
+ }
143
+ }
144
+ var statusSchema = z2.object({
145
+ connected: z2.boolean(),
146
+ paired: z2.boolean(),
147
+ targetState: z2.enum(["none", "authorized", "reauthorization_required"]),
148
+ backgroundCapture: z2.object({ enabled: z2.boolean(), permissionGranted: z2.boolean() }).optional()
149
+ });
150
+
151
+ // apps/cli/src/index.ts
152
+ var root = resolve(dirname2(fileURLToPath(import.meta.url)), "..");
153
+ var args = process.argv.slice(2);
154
+ function option(name) {
155
+ const i = args.indexOf(name);
156
+ if (i < 0) return void 0;
157
+ if (!args[i + 1] || args[i + 1].startsWith("--"))
158
+ throw new Error("Missing option value.");
159
+ return args[i + 1];
160
+ }
161
+ function detectOnPath(binary) {
162
+ for (const directory of (process.env.PATH ?? "").split(delimiter))
163
+ for (const name of [binary, `${binary}.exe`, `${binary}.cmd`]) {
164
+ const candidate = join2(directory, name);
165
+ if (existsSync(candidate)) return candidate;
166
+ }
167
+ return void 0;
168
+ }
169
+ async function promptClientChoice() {
170
+ if (!process.stdin.isTTY) return ["claude", "codex"];
171
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
172
+ try {
173
+ const answer = (await rl.question(
174
+ "Configure MCP for:\n 1) Claude Code\n 2) Codex\n 3) Both\nEnter choice [1-3] (default 3): "
175
+ )).trim();
176
+ if (answer === "1") return ["claude"];
177
+ if (answer === "2") return ["codex"];
178
+ return ["claude", "codex"];
179
+ } finally {
180
+ rl.close();
181
+ }
182
+ }
183
+ function configureAgent(agent, binaryPath, serverArgs) {
184
+ if (agent === "claude") {
185
+ const existing = spawnSync(binaryPath, ["mcp", "get", "gazesight"], {
186
+ encoding: "utf8"
187
+ });
188
+ if (existing.status === 0)
189
+ return { ok: true, message: "Already configured (unchanged)." };
190
+ const add2 = spawnSync(
191
+ binaryPath,
192
+ ["mcp", "add", "gazesight", "--scope", "user", "--", ...serverArgs],
193
+ { encoding: "utf8" }
194
+ );
195
+ if (add2.status === 0) return { ok: true, message: "Configured." };
196
+ return {
197
+ ok: false,
198
+ message: `Failed: ${(add2.stderr || add2.stdout || "unknown error").trim().split("\n")[0]}`
199
+ };
200
+ }
201
+ const list = spawnSync(binaryPath, ["mcp", "list"], { encoding: "utf8" });
202
+ if (list.status === 0 && list.stdout.includes("gazesight"))
203
+ return { ok: true, message: "Already configured (unchanged)." };
204
+ const add = spawnSync(binaryPath, ["mcp", "add", "gazesight", "--", ...serverArgs], {
205
+ encoding: "utf8"
206
+ });
207
+ if (add.status === 0) return { ok: true, message: "Configured." };
208
+ return {
209
+ ok: false,
210
+ message: `Failed: ${(add.stderr || add.stdout || "unknown error").trim().split("\n")[0]}`
211
+ };
212
+ }
213
+ try {
214
+ const configPath = resolve(
215
+ option("--config") ?? process.env.GAZESIGHT_CONFIG_FILE ?? defaultConfigPath()
216
+ );
217
+ if (args[0] === "mcp") {
218
+ process.env.GAZESIGHT_CONFIG_FILE = configPath;
219
+ await import(pathToFileURL(join2(root, "mcp/index.js")).href);
220
+ } else if (args.includes("--version")) console.log(VERSION);
221
+ else if (!args[0] || args.includes("--help"))
222
+ console.log(
223
+ `GazeSight ${VERSION}
224
+ gazesight setup [--config PATH] [--port PORT] [--client claude|codex|both]
225
+ gazesight doctor [--config PATH]
226
+ gazesight mcp [--config PATH]
227
+ No browser is launched. Secrets are never printed.`
228
+ );
229
+ else {
230
+ if (!["setup", "doctor"].includes(args[0]))
231
+ throw new Error("Unknown command.");
232
+ if (args.slice(1).some(
233
+ (value, i) => i % 2 === 0 && !["--config", "--port", "--client"].includes(value)
234
+ ))
235
+ throw new Error("Unknown option.");
236
+ const clientOption = option("--client");
237
+ if (clientOption && !["claude", "codex", "both"].includes(clientOption))
238
+ throw new Error("--client must be claude, codex, or both.");
239
+ const node = Number(process.versions.node.split(".")[0]) >= 20;
240
+ const build = existsSync(join2(root, "mcp/index.js")) && existsSync(join2(root, "firefox/manifest.json"));
241
+ console.log(
242
+ `Node: ${node ? "PASS" : "FAIL"}
243
+ GazeSight build: ${build ? "PASS" : "FAIL"}`
244
+ );
245
+ if (!node || !build)
246
+ throw new Error(
247
+ "Reinstall a complete GazeSight package; Node >=20 required."
248
+ );
249
+ const config = args[0] === "setup" ? setupConfig(configPath, Number(option("--port") ?? 32147)) : readLocalConfig(configPath);
250
+ console.log(
251
+ `Local configuration: ${configPath}
252
+ Bridge: 127.0.0.1:${config.port}
253
+ Agent guidance: ${config.agentGuidance.enabled ? "Enabled" : "Disabled"}${config.agentGuidance.enabled ? ` (Visual Fidelity: ${config.agentGuidance.visualFidelity ? "Enabled" : "Disabled"})` : ""}`
254
+ );
255
+ if (args[0] === "setup") {
256
+ const explicit = Boolean(clientOption) || Boolean(process.stdin.isTTY);
257
+ const chosen = !explicit ? [] : clientOption === "claude" ? ["claude"] : clientOption === "codex" ? ["codex"] : clientOption === "both" ? ["claude", "codex"] : await promptClientChoice();
258
+ const serverArgs = [
259
+ process.execPath,
260
+ fileURLToPath(import.meta.url),
261
+ "mcp",
262
+ "--config",
263
+ configPath
264
+ ];
265
+ for (const agent of chosen) {
266
+ const label = agent === "claude" ? "Claude Code" : "Codex";
267
+ const binaryPath = detectOnPath(agent);
268
+ if (!binaryPath) {
269
+ console.log(`${label}: not detected on PATH \u2014 configure manually below.`);
270
+ continue;
271
+ }
272
+ const result2 = configureAgent(agent, binaryPath, serverArgs);
273
+ console.log(`${label}: ${result2.ok ? "PASS" : "FAIL"} \u2014 ${result2.message}`);
274
+ }
275
+ const undetected = explicit ? chosen.filter((agent) => !detectOnPath(agent)) : ["claude", "codex"];
276
+ if (undetected.length) {
277
+ const server = { command: process.execPath, args: serverArgs.slice(1) };
278
+ if (undetected.includes("claude")) {
279
+ console.log("Manual Claude Code .mcp.json (merge into ~/.claude.json):");
280
+ console.log(
281
+ JSON.stringify({ mcpServers: { gazesight: server } }, null, 2)
282
+ );
283
+ }
284
+ if (undetected.includes("codex")) {
285
+ console.log("Manual Codex config.toml (merge into ~/.codex/config.toml):");
286
+ console.log(
287
+ `[mcp_servers.gazesight]
288
+ command = ${JSON.stringify(server.command)}
289
+ args = ${JSON.stringify(server.args)}`
290
+ );
291
+ }
292
+ }
293
+ console.log(
294
+ `Pairing: Firefox options > Import local configuration > ${configPath} > Enregistrer. The token stays in the private file.
295
+ Chrome (manual install, not yet on the Chrome Web Store): open chrome://extensions, enable Developer mode, "Load unpacked", select ${join2(root, "chrome")}, then Options > Import local configuration.
296
+ Development only (Firefox): about:debugging > Load Temporary Add-on > ${join2(root, "firefox/manifest.json")}
297
+ Unsigned XPI: ${join2(root, "gazesight-firefox.xpi")}
298
+ External Firefox Release/Beta installation requires the separately supplied Mozilla-signed XPI.
299
+ Start your coding agent, open your local app in your browser, click GazeSight, then run gazesight doctor.`
300
+ );
301
+ }
302
+ const result = await probe(config.port, config.token);
303
+ const rows = diagnosticRows(
304
+ result.status,
305
+ result.reachable,
306
+ result.authenticated
307
+ );
308
+ for (const row of rows)
309
+ console.log(
310
+ "status" in row ? `${row.name}: ${row.status}` : `${row.name}: ${row.pass ? "PASS" : `FAIL \u2014 ${row.action}`}`
311
+ );
312
+ if (args[0] === "doctor" && rows.some((row) => !row.pass))
313
+ process.exitCode = 1;
314
+ }
315
+ } catch {
316
+ console.error(
317
+ "FAIL \u2014 Check the command/options, Node >=20, installed package, private configuration and port 1024\u201365535. Run gazesight --help. Secrets are redacted."
318
+ );
319
+ process.exitCode = 1;
320
+ }