skykoi 2026.3.278 → 2026.3.280

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 (174) hide show
  1. package/assets/chrome-extension/background.js +212 -210
  2. package/assets/chrome-extension/options.html +31 -11
  3. package/assets/chrome-extension/options.js +34 -34
  4. package/dist/{acp-cli-By3Qrc0T.js → acp-cli-B6Y_fz5U.js} +6 -6
  5. package/dist/{archive-D-nIFDvI.js → archive-BpTDgDTy.js} +1 -1
  6. package/dist/{audit-ZCofKzBh.js → audit-D1fpejRg.js} +8 -8
  7. package/dist/{auth-BzM5UpTJ.js → auth-D0z_AJK6.js} +1 -1
  8. package/dist/{auth-health-BC19VMJe.js → auth-health-CEfawBgz.js} +1 -1
  9. package/dist/build-info.json +3 -3
  10. package/dist/{call-Bos6YLrN.js → call-DCuOjRUG.js} +3 -3
  11. package/dist/canvas-host/a2ui/.bundle.hash +1 -1
  12. package/dist/canvas-host/a2ui/a2ui.bundle.js +6 -4
  13. package/dist/{channel-options-BRwi2S1q.js → channel-options-CqprncCD.js} +2 -2
  14. package/dist/{channel-summary-BB3bwKMa.js → channel-summary-CQFJJfiX.js} +4 -4
  15. package/dist/{channels-cli-BP9FkXtR.js → channels-cli-BSoRJh9j.js} +28 -28
  16. package/dist/cli/daemon-cli.js +1 -1
  17. package/dist/{cli-DWW95jJe.js → cli-Ce2MWW7B.js} +22 -22
  18. package/dist/{completion-cli-Bk5t5c97.js → completion-cli-BWSlYCwr.js} +1 -1
  19. package/dist/{config-B1La6DxS.js → config-DFSuRu_s.js} +3 -3
  20. package/dist/{config-guard-BLj9NwR0.js → config-guard-DaFM3Eof.js} +20 -20
  21. package/dist/{configure-Ci7m67M0.js → configure-tON2C9Zo.js} +7 -7
  22. package/dist/{control-service-D30sRnYY.js → control-service-B1LQv-jp.js} +2 -2
  23. package/dist/control-ui/assets/index-C7Jts3SS.js.map +1 -1
  24. package/dist/{cron-cli-_UjEtRij.js → cron-cli-CIsurmrl.js} +6 -6
  25. package/dist/{daemon-cli-P1M4WfxO.js → daemon-cli-BxVVzYet.js} +9 -9
  26. package/dist/{daemon-runtime-Ct4U4ixT.js → daemon-runtime-DUhZ0AE3.js} +1 -1
  27. package/dist/{deliver-B-J5hhwR.js → deliver-QH8nLFB5.js} +7 -7
  28. package/dist/{deps-OwHcOs6U.js → deps-CQov5sWA.js} +2 -2
  29. package/dist/{devices-cli-C9pNVqZ7.js → devices-cli-Bn9pQ5ky.js} +5 -5
  30. package/dist/{directory-cli-BPMP0Zk7.js → directory-cli-GOnzyOoc.js} +3 -3
  31. package/dist/{dispatcher-QEMerbmH.js → dispatcher-SB6YDznX.js} +1 -1
  32. package/dist/{dist-BfJYv-iR.js → dist-9wkUrskD.js} +2 -2
  33. package/dist/{dist-2N9GeOCZ.js → dist-CLm8TPvp.js} +3 -3
  34. package/dist/{dns-cli-CVVLxo5W.js → dns-cli-CyvmFPia.js} +3 -3
  35. package/dist/{docs-cli-DnVsm2sD.js → docs-cli-nLD4utdW.js} +3 -3
  36. package/dist/{doctor-C26BWayN.js → doctor-4O-p5ipj.js} +16 -16
  37. package/dist/entry.js +1 -1
  38. package/dist/{exec-approvals-cli-DsmzGEdE.js → exec-approvals-cli-BNElx2Vq.js} +7 -7
  39. package/dist/extension-api.js +22 -22
  40. package/dist/{gateway-cli-CCLbtMBI.js → gateway-cli-CjwJynIz.js} +50 -50
  41. package/dist/{gateway-rpc-CxxLj5l6.js → gateway-rpc-C630PmXq.js} +1 -1
  42. package/dist/{github-copilot-auth-ae1ycEGH.js → github-copilot-auth-CfebkeVF.js} +4 -4
  43. package/dist/{gmail-setup-utils-C47vy-M_.js → gmail-setup-utils-DcuIAwKK.js} +1 -1
  44. package/dist/{health-format-DevJEyuH.js → health-format-C-5YUjyN.js} +6 -6
  45. package/dist/{hooks-cli-Bk-8PCu1.js → hooks-cli-DHv47L8h.js} +25 -25
  46. package/dist/{hooks-status-ONt2WSFD.js → hooks-status-BI-Kg4Yg.js} +1 -1
  47. package/dist/{image-Db_oz8I6.js → image-tWMcAovq.js} +5 -5
  48. package/dist/index.js +50 -50
  49. package/dist/{installs-m9gMtTy1.js → installs-Bg5c6KoO.js} +1 -1
  50. package/dist/{koi-BenZ-HcC.js → koi-ktvnaIfS.js} +8 -8
  51. package/dist/{login-qr-BUNnQgv9.js → login-qr-DKqrrAZR.js} +1 -1
  52. package/dist/{logs-cli-DjmYGwDw.js → logs-cli-DmAmSxEQ.js} +6 -6
  53. package/dist/{manager-CofU7sxR.js → manager-BjhAM06t.js} +1 -1
  54. package/dist/{model-selection-BdujDB_E.js → model-selection-mCem7UcZ.js} +23 -8
  55. package/dist/{models-cli-6PPNkmAm.js → models-cli-BizaZ067.js} +24 -24
  56. package/dist/{net-DG4ItObd.js → net-dAXg-DbE.js} +2 -2
  57. package/dist/{node-cli-C_clt6lE.js → node-cli-B0bHqkX-.js} +11 -11
  58. package/dist/{nodes-cli-BzVkLBFe.js → nodes-cli-DEPAIC6K.js} +6 -6
  59. package/dist/{onboard-channels-M4xOz6Tx.js → onboard-channels-DvwX0NAI.js} +3 -3
  60. package/dist/{onboard-skills-DfOGA4RH.js → onboard-skills-B1A80W7q.js} +8 -8
  61. package/dist/{onboarding-BMf2ucWp.js → onboarding-1427o2ED.js} +18 -17
  62. package/dist/{pairing-cli-Zm24Bwa3.js → pairing-cli-69QjJ5Xa.js} +3 -3
  63. package/dist/{pi-embedded-helpers-BR1OjovY.js → pi-embedded-helpers-CCBbyQJT.js} +1 -1
  64. package/dist/{pi-model-discovery-Cbn1GZwb.js → pi-model-discovery-C2vnlBAx.js} +1 -1
  65. package/dist/{pi-tools.policy-BTylCL58.js → pi-tools.policy-CeUHuvkF.js} +1 -1
  66. package/dist/{plugin-auto-enable-Bx_BHwcy.js → plugin-auto-enable-DPugTU6E.js} +1 -1
  67. package/dist/{plugin-registry-DkvBeUxp.js → plugin-registry-CW8wr4Y-.js} +2 -2
  68. package/dist/plugin-sdk/browser/config.d.ts +1 -0
  69. package/dist/plugin-sdk/compat/legacy-names.d.ts +1 -0
  70. package/dist/plugin-sdk/index.js +29 -14
  71. package/dist/{plugins-cli-DCjlrnJy.js → plugins-cli-CucfnMF8.js} +26 -26
  72. package/dist/{program-C3SbZ8xp.js → program-qtsOE9Wj.js} +6 -6
  73. package/dist/{register.subclis-BJPL11BX.js → register.subclis-Dab8Usr4.js} +28 -28
  74. package/dist/{reply-CQKbNkW1.js → reply-DbSDQiIc.js} +87 -37
  75. package/dist/{routes-vNI4VWn-.js → routes-DTCo5sqi.js} +17 -14
  76. package/dist/{rpc-DykocUdv.js → rpc-BPzU2GEK.js} +1 -1
  77. package/dist/{run-main-BUlzecP7.js → run-main-CuRcoDQM.js} +52 -52
  78. package/dist/{runner-DRI8-OuM.js → runner-BH0V-ihf.js} +5 -5
  79. package/dist/{sandbox-COQl76qj.js → sandbox-C18TfibJ.js} +5 -5
  80. package/dist/{sandbox-cli-D_WC6tOL.js → sandbox-cli-B3MrFcpb.js} +9 -9
  81. package/dist/{security-cli-Bg751BmY.js → security-cli-COu1DzpE.js} +14 -14
  82. package/dist/{server-context-BG4YK6Ll.js → server-context-By4m-ZR-.js} +14 -5
  83. package/dist/{server-node-events-B-RuKYD2.js → server-node-events-DtB7jTdC.js} +24 -24
  84. package/dist/{service-audit-DMmKqL8q.js → service-audit-DAp2zv_8.js} +1 -1
  85. package/dist/{sessions-LG_t6M1u.js → sessions-BG9-1nv9.js} +2 -2
  86. package/dist/{shared-ih2ZCRlQ.js → shared-DGIWPJP2.js} +1 -1
  87. package/dist/{skills-C0SvpGTU.js → skills-DgBgB325.js} +1 -1
  88. package/dist/{skills-cli-COUXiho3.js → skills-cli-EHDty6AK.js} +6 -6
  89. package/dist/{skills-status-PpCs1M-j.js → skills-status-QmdDW96R.js} +2 -2
  90. package/dist/{status-CSccNDO5.js → status-DlYCO3S-.js} +2 -2
  91. package/dist/{system-cli-BwHARoov.js → system-cli-CpjU8A_6.js} +6 -6
  92. package/dist/{tui-cli-Bzwfs5KA.js → tui-cli-jI_TGgaX.js} +14 -14
  93. package/dist/{tui-bwRb_Ht8.js → tui-ltTLDIiD.js} +6 -6
  94. package/dist/{update-DYs42iFB.js → update-D5nDnc-5.js} +1 -1
  95. package/dist/{update-cli-7_iySREb.js → update-cli-BgiWzE6D.js} +39 -39
  96. package/dist/{update-runner-CTtv4PVN.js → update-runner-DhEGhTtX.js} +5 -5
  97. package/dist/{webhooks-cli-BuHqy_S0.js → webhooks-cli-DM7fc19r.js} +6 -6
  98. package/docs/automation/cron-vs-heartbeat.md +1 -1
  99. package/docs/channels/feishu.md +2 -2
  100. package/docs/channels/msteams.md +7 -7
  101. package/docs/channels/troubleshooting.md +15 -15
  102. package/docs/concepts/typebox.md +8 -8
  103. package/docs/gateway/configuration.md +2 -2
  104. package/docs/gateway/remote-gateway-readme.md +1 -1
  105. package/docs/help/faq.md +8 -8
  106. package/docs/index.md +1 -1
  107. package/docs/install/fly.md +7 -7
  108. package/docs/install/gcp.md +12 -12
  109. package/docs/install/hetzner.md +12 -12
  110. package/docs/install/installer.md +37 -37
  111. package/docs/install/macos-vm.md +4 -4
  112. package/docs/pi.md +12 -12
  113. package/docs/prose.md +1 -1
  114. package/docs/reference/templates/AGENTS.md +26 -1
  115. package/docs/reference/templates/BOOTSTRAP.md +3 -0
  116. package/docs/reference/templates/HEARTBEAT.md +12 -3
  117. package/docs/reference/templates/KOIS.md +26 -1
  118. package/docs/reference/templates/SOUL.md +1 -0
  119. package/docs/zh-CN/channels/feishu.md +3 -3
  120. package/docs/zh-CN/channels/msteams.md +7 -7
  121. package/docs/zh-CN/concepts/typebox.md +8 -8
  122. package/docs/zh-CN/gateway/security/index.md +1 -1
  123. package/docs/zh-CN/gateway/troubleshooting.md +8 -8
  124. package/docs/zh-CN/help/faq.md +8 -8
  125. package/docs/zh-CN/index.md +1 -1
  126. package/docs/zh-CN/install/fly.md +7 -7
  127. package/docs/zh-CN/install/gcp.md +7 -7
  128. package/docs/zh-CN/install/hetzner.md +7 -7
  129. package/docs/zh-CN/install/macos-vm.md +4 -4
  130. package/docs/zh-CN/pi.md +11 -11
  131. package/docs/zh-CN/prose.md +1 -1
  132. package/extensions/bluebubbles/src/monitor.ts +1 -4
  133. package/extensions/diagnostics-otel/src/service.test.ts +3 -9
  134. package/extensions/google-gemini-cli-auth/oauth.ts +1 -4
  135. package/extensions/matrix/CHANGELOG.md +1 -0
  136. package/extensions/matrix/src/actions.ts +1 -1
  137. package/extensions/matrix/src/channel.ts +3 -3
  138. package/extensions/matrix/src/directory-live.ts +1 -1
  139. package/extensions/matrix/src/matrix/accounts.ts +1 -1
  140. package/extensions/matrix/src/matrix/client/config.ts +2 -2
  141. package/extensions/matrix/src/matrix/client.test.ts +1 -1
  142. package/extensions/matrix/src/matrix/monitor/location.ts +1 -5
  143. package/extensions/matrix/src/matrix/probe.ts +2 -1
  144. package/extensions/matrix/src/outbound.ts +3 -3
  145. package/extensions/msteams/CHANGELOG.md +1 -0
  146. package/extensions/msteams/package.json +2 -2
  147. package/extensions/nostr/CHANGELOG.md +1 -0
  148. package/extensions/open-prose/skills/prose/SKILL.md +8 -8
  149. package/extensions/open-prose/skills/prose/alt-borges.md +1 -1
  150. package/extensions/open-prose/skills/prose/alts/arabian-nights.md +2 -2
  151. package/extensions/open-prose/skills/prose/alts/borges.md +1 -1
  152. package/extensions/open-prose/skills/prose/alts/folk.md +1 -1
  153. package/extensions/open-prose/skills/prose/alts/homer.md +1 -1
  154. package/extensions/open-prose/skills/prose/alts/kafka.md +2 -2
  155. package/extensions/open-prose/skills/prose/compiler.md +52 -52
  156. package/extensions/open-prose/skills/prose/examples/README.md +8 -8
  157. package/extensions/open-prose/skills/prose/examples/roadmap/README.md +1 -1
  158. package/extensions/open-prose/skills/prose/help.md +2 -2
  159. package/extensions/open-prose/skills/prose/primitives/session.md +3 -3
  160. package/extensions/open-prose/skills/prose/prose.md +10 -10
  161. package/extensions/open-prose/skills/prose/state/filesystem.md +6 -6
  162. package/extensions/open-prose/skills/prose/state/in-context.md +1 -1
  163. package/extensions/open-prose/skills/prose/state/postgres.md +9 -9
  164. package/extensions/open-prose/skills/prose/state/sqlite.md +9 -9
  165. package/extensions/twitch/CHANGELOG.md +1 -0
  166. package/extensions/twitch/src/onboarding.ts +1 -4
  167. package/extensions/voice-call/CHANGELOG.md +1 -0
  168. package/extensions/voice-call/src/core-bridge.ts +1 -5
  169. package/extensions/zalo/CHANGELOG.md +1 -0
  170. package/extensions/zalo/src/accounts.ts +1 -4
  171. package/extensions/zalouser/CHANGELOG.md +1 -0
  172. package/package.json +1 -1
  173. package/skills/chrome-e2e/SKILL.md +23 -17
  174. package/skills/coding-koi/SKILL.md +8 -10
@@ -1,438 +1,440 @@
1
- const DEFAULT_PORT = 18792
1
+ const DEFAULT_PORT = 18792;
2
2
 
3
3
  const BADGE = {
4
- on: { text: 'ON', color: '#FF5A36' },
5
- off: { text: '', color: '#000000' },
6
- connecting: { text: '', color: '#F59E0B' },
7
- error: { text: '!', color: '#B91C1C' },
8
- }
4
+ on: { text: "ON", color: "#FF5A36" },
5
+ off: { text: "", color: "#000000" },
6
+ connecting: { text: "", color: "#F59E0B" },
7
+ error: { text: "!", color: "#B91C1C" },
8
+ };
9
9
 
10
10
  /** @type {WebSocket|null} */
11
- let relayWs = null
11
+ let relayWs = null;
12
12
  /** @type {Promise<void>|null} */
13
- let relayConnectPromise = null
13
+ let relayConnectPromise = null;
14
14
 
15
- let debuggerListenersInstalled = false
15
+ let debuggerListenersInstalled = false;
16
16
 
17
- let nextSession = 1
17
+ let nextSession = 1;
18
18
 
19
19
  /** @type {Map<number, {state:'connecting'|'connected', sessionId?:string, targetId?:string, attachOrder?:number}>} */
20
- const tabs = new Map()
20
+ const tabs = new Map();
21
21
  /** @type {Map<string, number>} */
22
- const tabBySession = new Map()
22
+ const tabBySession = new Map();
23
23
  /** @type {Map<string, number>} */
24
- const childSessionToTab = new Map()
24
+ const childSessionToTab = new Map();
25
25
 
26
26
  /** @type {Map<number, {resolve:(v:any)=>void, reject:(e:Error)=>void}>} */
27
- const pending = new Map()
27
+ const pending = new Map();
28
28
 
29
29
  function nowStack() {
30
30
  try {
31
- return new Error().stack || ''
31
+ return new Error().stack || "";
32
32
  } catch {
33
- return ''
33
+ return "";
34
34
  }
35
35
  }
36
36
 
37
37
  async function getRelayPort() {
38
- const stored = await chrome.storage.local.get(['relayPort'])
39
- const raw = stored.relayPort
40
- const n = Number.parseInt(String(raw || ''), 10)
41
- if (!Number.isFinite(n) || n <= 0 || n > 65535) return DEFAULT_PORT
42
- return n
38
+ const stored = await chrome.storage.local.get(["relayPort"]);
39
+ const raw = stored.relayPort;
40
+ const n = Number.parseInt(String(raw || ""), 10);
41
+ if (!Number.isFinite(n) || n <= 0 || n > 65535) return DEFAULT_PORT;
42
+ return n;
43
43
  }
44
44
 
45
45
  function setBadge(tabId, kind) {
46
- const cfg = BADGE[kind]
47
- void chrome.action.setBadgeText({ tabId, text: cfg.text })
48
- void chrome.action.setBadgeBackgroundColor({ tabId, color: cfg.color })
49
- void chrome.action.setBadgeTextColor({ tabId, color: '#FFFFFF' }).catch(() => {})
46
+ const cfg = BADGE[kind];
47
+ void chrome.action.setBadgeText({ tabId, text: cfg.text });
48
+ void chrome.action.setBadgeBackgroundColor({ tabId, color: cfg.color });
49
+ void chrome.action.setBadgeTextColor({ tabId, color: "#FFFFFF" }).catch(() => {});
50
50
  }
51
51
 
52
52
  async function ensureRelayConnection() {
53
- if (relayWs && relayWs.readyState === WebSocket.OPEN) return
54
- if (relayConnectPromise) return await relayConnectPromise
53
+ if (relayWs && relayWs.readyState === WebSocket.OPEN) return;
54
+ if (relayConnectPromise) return await relayConnectPromise;
55
55
 
56
56
  relayConnectPromise = (async () => {
57
- const port = await getRelayPort()
58
- const httpBase = `http://127.0.0.1:${port}`
59
- const wsUrl = `ws://127.0.0.1:${port}/extension`
57
+ const port = await getRelayPort();
58
+ const httpBase = `http://127.0.0.1:${port}`;
59
+ const wsUrl = `ws://127.0.0.1:${port}/extension`;
60
60
 
61
61
  // Fast preflight: is the relay server up?
62
62
  try {
63
- await fetch(`${httpBase}/`, { method: 'HEAD', signal: AbortSignal.timeout(2000) })
63
+ await fetch(`${httpBase}/`, { method: "HEAD", signal: AbortSignal.timeout(2000) });
64
64
  } catch (err) {
65
- throw new Error(`Relay server not reachable at ${httpBase} (${String(err)})`)
65
+ throw new Error(`Relay server not reachable at ${httpBase} (${String(err)})`);
66
66
  }
67
67
 
68
- const ws = new WebSocket(wsUrl)
69
- relayWs = ws
68
+ const ws = new WebSocket(wsUrl);
69
+ relayWs = ws;
70
70
 
71
71
  await new Promise((resolve, reject) => {
72
- const t = setTimeout(() => reject(new Error('WebSocket connect timeout')), 5000)
72
+ const t = setTimeout(() => reject(new Error("WebSocket connect timeout")), 5000);
73
73
  ws.onopen = () => {
74
- clearTimeout(t)
75
- resolve()
76
- }
74
+ clearTimeout(t);
75
+ resolve();
76
+ };
77
77
  ws.onerror = () => {
78
- clearTimeout(t)
79
- reject(new Error('WebSocket connect failed'))
80
- }
78
+ clearTimeout(t);
79
+ reject(new Error("WebSocket connect failed"));
80
+ };
81
81
  ws.onclose = (ev) => {
82
- clearTimeout(t)
83
- reject(new Error(`WebSocket closed (${ev.code} ${ev.reason || 'no reason'})`))
84
- }
85
- })
82
+ clearTimeout(t);
83
+ reject(new Error(`WebSocket closed (${ev.code} ${ev.reason || "no reason"})`));
84
+ };
85
+ });
86
86
 
87
- ws.onmessage = (event) => void onRelayMessage(String(event.data || ''))
88
- ws.onclose = () => onRelayClosed('closed')
89
- ws.onerror = () => onRelayClosed('error')
87
+ ws.onmessage = (event) => void onRelayMessage(String(event.data || ""));
88
+ ws.onclose = () => onRelayClosed("closed");
89
+ ws.onerror = () => onRelayClosed("error");
90
90
 
91
91
  if (!debuggerListenersInstalled) {
92
- debuggerListenersInstalled = true
93
- chrome.debugger.onEvent.addListener(onDebuggerEvent)
94
- chrome.debugger.onDetach.addListener(onDebuggerDetach)
92
+ debuggerListenersInstalled = true;
93
+ chrome.debugger.onEvent.addListener(onDebuggerEvent);
94
+ chrome.debugger.onDetach.addListener(onDebuggerDetach);
95
95
  }
96
- })()
96
+ })();
97
97
 
98
98
  try {
99
- await relayConnectPromise
99
+ await relayConnectPromise;
100
100
  } finally {
101
- relayConnectPromise = null
101
+ relayConnectPromise = null;
102
102
  }
103
103
  }
104
104
 
105
105
  function onRelayClosed(reason) {
106
- relayWs = null
106
+ relayWs = null;
107
107
  for (const [id, p] of pending.entries()) {
108
- pending.delete(id)
109
- p.reject(new Error(`Relay disconnected (${reason})`))
108
+ pending.delete(id);
109
+ p.reject(new Error(`Relay disconnected (${reason})`));
110
110
  }
111
111
 
112
112
  for (const tabId of tabs.keys()) {
113
- void chrome.debugger.detach({ tabId }).catch(() => {})
114
- setBadge(tabId, 'connecting')
113
+ void chrome.debugger.detach({ tabId }).catch(() => {});
114
+ setBadge(tabId, "connecting");
115
115
  void chrome.action.setTitle({
116
116
  tabId,
117
- title: 'SKYKOI Browser Relay: disconnected (click to re-attach)',
118
- })
117
+ title: "SKYKOI Browser Relay: disconnected (click to re-attach)",
118
+ });
119
119
  }
120
- tabs.clear()
121
- tabBySession.clear()
122
- childSessionToTab.clear()
120
+ tabs.clear();
121
+ tabBySession.clear();
122
+ childSessionToTab.clear();
123
123
  }
124
124
 
125
125
  function sendToRelay(payload) {
126
- const ws = relayWs
126
+ const ws = relayWs;
127
127
  if (!ws || ws.readyState !== WebSocket.OPEN) {
128
- throw new Error('Relay not connected')
128
+ throw new Error("Relay not connected");
129
129
  }
130
- ws.send(JSON.stringify(payload))
130
+ ws.send(JSON.stringify(payload));
131
131
  }
132
132
 
133
133
  async function maybeOpenHelpOnce() {
134
134
  try {
135
- const stored = await chrome.storage.local.get(['helpOnErrorShown'])
136
- if (stored.helpOnErrorShown === true) return
137
- await chrome.storage.local.set({ helpOnErrorShown: true })
138
- await chrome.runtime.openOptionsPage()
135
+ const stored = await chrome.storage.local.get(["helpOnErrorShown"]);
136
+ if (stored.helpOnErrorShown === true) return;
137
+ await chrome.storage.local.set({ helpOnErrorShown: true });
138
+ await chrome.runtime.openOptionsPage();
139
139
  } catch {
140
140
  // ignore
141
141
  }
142
142
  }
143
143
 
144
144
  function requestFromRelay(command) {
145
- const id = command.id
145
+ const id = command.id;
146
146
  return new Promise((resolve, reject) => {
147
- pending.set(id, { resolve, reject })
147
+ pending.set(id, { resolve, reject });
148
148
  try {
149
- sendToRelay(command)
149
+ sendToRelay(command);
150
150
  } catch (err) {
151
- pending.delete(id)
152
- reject(err instanceof Error ? err : new Error(String(err)))
151
+ pending.delete(id);
152
+ reject(err instanceof Error ? err : new Error(String(err)));
153
153
  }
154
- })
154
+ });
155
155
  }
156
156
 
157
157
  async function onRelayMessage(text) {
158
158
  /** @type {any} */
159
- let msg
159
+ let msg;
160
160
  try {
161
- msg = JSON.parse(text)
161
+ msg = JSON.parse(text);
162
162
  } catch {
163
- return
163
+ return;
164
164
  }
165
165
 
166
- if (msg && msg.method === 'ping') {
166
+ if (msg && msg.method === "ping") {
167
167
  try {
168
- sendToRelay({ method: 'pong' })
168
+ sendToRelay({ method: "pong" });
169
169
  } catch {
170
170
  // ignore
171
171
  }
172
- return
172
+ return;
173
173
  }
174
174
 
175
- if (msg && typeof msg.id === 'number' && (msg.result !== undefined || msg.error !== undefined)) {
176
- const p = pending.get(msg.id)
177
- if (!p) return
178
- pending.delete(msg.id)
179
- if (msg.error) p.reject(new Error(String(msg.error)))
180
- else p.resolve(msg.result)
181
- return
175
+ if (msg && typeof msg.id === "number" && (msg.result !== undefined || msg.error !== undefined)) {
176
+ const p = pending.get(msg.id);
177
+ if (!p) return;
178
+ pending.delete(msg.id);
179
+ if (msg.error) p.reject(new Error(String(msg.error)));
180
+ else p.resolve(msg.result);
181
+ return;
182
182
  }
183
183
 
184
- if (msg && typeof msg.id === 'number' && msg.method === 'forwardCDPCommand') {
184
+ if (msg && typeof msg.id === "number" && msg.method === "forwardCDPCommand") {
185
185
  try {
186
- const result = await handleForwardCdpCommand(msg)
187
- sendToRelay({ id: msg.id, result })
186
+ const result = await handleForwardCdpCommand(msg);
187
+ sendToRelay({ id: msg.id, result });
188
188
  } catch (err) {
189
- sendToRelay({ id: msg.id, error: err instanceof Error ? err.message : String(err) })
189
+ sendToRelay({ id: msg.id, error: err instanceof Error ? err.message : String(err) });
190
190
  }
191
191
  }
192
192
  }
193
193
 
194
194
  function getTabBySessionId(sessionId) {
195
- const direct = tabBySession.get(sessionId)
196
- if (direct) return { tabId: direct, kind: 'main' }
197
- const child = childSessionToTab.get(sessionId)
198
- if (child) return { tabId: child, kind: 'child' }
199
- return null
195
+ const direct = tabBySession.get(sessionId);
196
+ if (direct) return { tabId: direct, kind: "main" };
197
+ const child = childSessionToTab.get(sessionId);
198
+ if (child) return { tabId: child, kind: "child" };
199
+ return null;
200
200
  }
201
201
 
202
202
  function getTabByTargetId(targetId) {
203
203
  for (const [tabId, tab] of tabs.entries()) {
204
- if (tab.targetId === targetId) return tabId
204
+ if (tab.targetId === targetId) return tabId;
205
205
  }
206
- return null
206
+ return null;
207
207
  }
208
208
 
209
209
  async function attachTab(tabId, opts = {}) {
210
- const debuggee = { tabId }
211
- await chrome.debugger.attach(debuggee, '1.3')
212
- await chrome.debugger.sendCommand(debuggee, 'Page.enable').catch(() => {})
213
-
214
- const info = /** @type {any} */ (await chrome.debugger.sendCommand(debuggee, 'Target.getTargetInfo'))
215
- const targetInfo = info?.targetInfo
216
- const targetId = String(targetInfo?.targetId || '').trim()
210
+ const debuggee = { tabId };
211
+ await chrome.debugger.attach(debuggee, "1.3");
212
+ await chrome.debugger.sendCommand(debuggee, "Page.enable").catch(() => {});
213
+
214
+ const info = /** @type {any} */ (
215
+ await chrome.debugger.sendCommand(debuggee, "Target.getTargetInfo")
216
+ );
217
+ const targetInfo = info?.targetInfo;
218
+ const targetId = String(targetInfo?.targetId || "").trim();
217
219
  if (!targetId) {
218
- throw new Error('Target.getTargetInfo returned no targetId')
220
+ throw new Error("Target.getTargetInfo returned no targetId");
219
221
  }
220
222
 
221
- const sessionId = `cb-tab-${nextSession++}`
222
- const attachOrder = nextSession
223
+ const sessionId = `cb-tab-${nextSession++}`;
224
+ const attachOrder = nextSession;
223
225
 
224
- tabs.set(tabId, { state: 'connected', sessionId, targetId, attachOrder })
225
- tabBySession.set(sessionId, tabId)
226
+ tabs.set(tabId, { state: "connected", sessionId, targetId, attachOrder });
227
+ tabBySession.set(sessionId, tabId);
226
228
  void chrome.action.setTitle({
227
229
  tabId,
228
- title: 'SKYKOI Browser Relay: attached (click to detach)',
229
- })
230
+ title: "SKYKOI Browser Relay: attached (click to detach)",
231
+ });
230
232
 
231
233
  if (!opts.skipAttachedEvent) {
232
234
  sendToRelay({
233
- method: 'forwardCDPEvent',
235
+ method: "forwardCDPEvent",
234
236
  params: {
235
- method: 'Target.attachedToTarget',
237
+ method: "Target.attachedToTarget",
236
238
  params: {
237
239
  sessionId,
238
240
  targetInfo: { ...targetInfo, attached: true },
239
241
  waitingForDebugger: false,
240
242
  },
241
243
  },
242
- })
244
+ });
243
245
  }
244
246
 
245
- setBadge(tabId, 'on')
246
- return { sessionId, targetId }
247
+ setBadge(tabId, "on");
248
+ return { sessionId, targetId };
247
249
  }
248
250
 
249
251
  async function detachTab(tabId, reason) {
250
- const tab = tabs.get(tabId)
252
+ const tab = tabs.get(tabId);
251
253
  if (tab?.sessionId && tab?.targetId) {
252
254
  try {
253
255
  sendToRelay({
254
- method: 'forwardCDPEvent',
256
+ method: "forwardCDPEvent",
255
257
  params: {
256
- method: 'Target.detachedFromTarget',
258
+ method: "Target.detachedFromTarget",
257
259
  params: { sessionId: tab.sessionId, targetId: tab.targetId, reason },
258
260
  },
259
- })
261
+ });
260
262
  } catch {
261
263
  // ignore
262
264
  }
263
265
  }
264
266
 
265
- if (tab?.sessionId) tabBySession.delete(tab.sessionId)
266
- tabs.delete(tabId)
267
+ if (tab?.sessionId) tabBySession.delete(tab.sessionId);
268
+ tabs.delete(tabId);
267
269
 
268
270
  for (const [childSessionId, parentTabId] of childSessionToTab.entries()) {
269
- if (parentTabId === tabId) childSessionToTab.delete(childSessionId)
271
+ if (parentTabId === tabId) childSessionToTab.delete(childSessionId);
270
272
  }
271
273
 
272
274
  try {
273
- await chrome.debugger.detach({ tabId })
275
+ await chrome.debugger.detach({ tabId });
274
276
  } catch {
275
277
  // ignore
276
278
  }
277
279
 
278
- setBadge(tabId, 'off')
280
+ setBadge(tabId, "off");
279
281
  void chrome.action.setTitle({
280
282
  tabId,
281
- title: 'SKYKOI Browser Relay (click to attach/detach)',
282
- })
283
+ title: "SKYKOI Browser Relay (click to attach/detach)",
284
+ });
283
285
  }
284
286
 
285
287
  async function connectOrToggleForActiveTab() {
286
- const [active] = await chrome.tabs.query({ active: true, currentWindow: true })
287
- const tabId = active?.id
288
- if (!tabId) return
289
-
290
- const existing = tabs.get(tabId)
291
- if (existing?.state === 'connected') {
292
- await detachTab(tabId, 'toggle')
293
- return
288
+ const [active] = await chrome.tabs.query({ active: true, currentWindow: true });
289
+ const tabId = active?.id;
290
+ if (!tabId) return;
291
+
292
+ const existing = tabs.get(tabId);
293
+ if (existing?.state === "connected") {
294
+ await detachTab(tabId, "toggle");
295
+ return;
294
296
  }
295
297
 
296
- tabs.set(tabId, { state: 'connecting' })
297
- setBadge(tabId, 'connecting')
298
+ tabs.set(tabId, { state: "connecting" });
299
+ setBadge(tabId, "connecting");
298
300
  void chrome.action.setTitle({
299
301
  tabId,
300
- title: 'SKYKOI Browser Relay: connecting to local relay…',
301
- })
302
+ title: "SKYKOI Browser Relay: connecting to local relay…",
303
+ });
302
304
 
303
305
  try {
304
- await ensureRelayConnection()
305
- await attachTab(tabId)
306
+ await ensureRelayConnection();
307
+ await attachTab(tabId);
306
308
  } catch (err) {
307
- tabs.delete(tabId)
308
- setBadge(tabId, 'error')
309
+ tabs.delete(tabId);
310
+ setBadge(tabId, "error");
309
311
  void chrome.action.setTitle({
310
312
  tabId,
311
- title: 'SKYKOI Browser Relay: relay not running (open options for setup)',
312
- })
313
- void maybeOpenHelpOnce()
313
+ title: "SKYKOI Browser Relay: relay not running (open options for setup)",
314
+ });
315
+ void maybeOpenHelpOnce();
314
316
  // Extra breadcrumbs in chrome://extensions service worker logs.
315
- const message = err instanceof Error ? err.message : String(err)
316
- console.warn('attach failed', message, nowStack())
317
+ const message = err instanceof Error ? err.message : String(err);
318
+ console.warn("attach failed", message, nowStack());
317
319
  }
318
320
  }
319
321
 
320
322
  async function handleForwardCdpCommand(msg) {
321
- const method = String(msg?.params?.method || '').trim()
322
- const params = msg?.params?.params || undefined
323
- const sessionId = typeof msg?.params?.sessionId === 'string' ? msg.params.sessionId : undefined
323
+ const method = String(msg?.params?.method || "").trim();
324
+ const params = msg?.params?.params || undefined;
325
+ const sessionId = typeof msg?.params?.sessionId === "string" ? msg.params.sessionId : undefined;
324
326
 
325
327
  // Map command to tab
326
- const bySession = sessionId ? getTabBySessionId(sessionId) : null
327
- const targetId = typeof params?.targetId === 'string' ? params.targetId : undefined
328
+ const bySession = sessionId ? getTabBySessionId(sessionId) : null;
329
+ const targetId = typeof params?.targetId === "string" ? params.targetId : undefined;
328
330
  const tabId =
329
331
  bySession?.tabId ||
330
332
  (targetId ? getTabByTargetId(targetId) : null) ||
331
333
  (() => {
332
334
  // No sessionId: pick the first connected tab (stable-ish).
333
335
  for (const [id, tab] of tabs.entries()) {
334
- if (tab.state === 'connected') return id
336
+ if (tab.state === "connected") return id;
335
337
  }
336
- return null
337
- })()
338
+ return null;
339
+ })();
338
340
 
339
- if (!tabId) throw new Error(`No attached tab for method ${method}`)
341
+ if (!tabId) throw new Error(`No attached tab for method ${method}`);
340
342
 
341
343
  /** @type {chrome.debugger.DebuggerSession} */
342
- const debuggee = { tabId }
344
+ const debuggee = { tabId };
343
345
 
344
- if (method === 'Runtime.enable') {
346
+ if (method === "Runtime.enable") {
345
347
  try {
346
- await chrome.debugger.sendCommand(debuggee, 'Runtime.disable')
347
- await new Promise((r) => setTimeout(r, 50))
348
+ await chrome.debugger.sendCommand(debuggee, "Runtime.disable");
349
+ await new Promise((r) => setTimeout(r, 50));
348
350
  } catch {
349
351
  // ignore
350
352
  }
351
- return await chrome.debugger.sendCommand(debuggee, 'Runtime.enable', params)
353
+ return await chrome.debugger.sendCommand(debuggee, "Runtime.enable", params);
352
354
  }
353
355
 
354
- if (method === 'Target.createTarget') {
355
- const url = typeof params?.url === 'string' ? params.url : 'about:blank'
356
- const tab = await chrome.tabs.create({ url, active: false })
357
- if (!tab.id) throw new Error('Failed to create tab')
358
- await new Promise((r) => setTimeout(r, 100))
359
- const attached = await attachTab(tab.id)
360
- return { targetId: attached.targetId }
356
+ if (method === "Target.createTarget") {
357
+ const url = typeof params?.url === "string" ? params.url : "about:blank";
358
+ const tab = await chrome.tabs.create({ url, active: false });
359
+ if (!tab.id) throw new Error("Failed to create tab");
360
+ await new Promise((r) => setTimeout(r, 100));
361
+ const attached = await attachTab(tab.id);
362
+ return { targetId: attached.targetId };
361
363
  }
362
364
 
363
- if (method === 'Target.closeTarget') {
364
- const target = typeof params?.targetId === 'string' ? params.targetId : ''
365
- const toClose = target ? getTabByTargetId(target) : tabId
366
- if (!toClose) return { success: false }
365
+ if (method === "Target.closeTarget") {
366
+ const target = typeof params?.targetId === "string" ? params.targetId : "";
367
+ const toClose = target ? getTabByTargetId(target) : tabId;
368
+ if (!toClose) return { success: false };
367
369
  try {
368
- await chrome.tabs.remove(toClose)
370
+ await chrome.tabs.remove(toClose);
369
371
  } catch {
370
- return { success: false }
372
+ return { success: false };
371
373
  }
372
- return { success: true }
374
+ return { success: true };
373
375
  }
374
376
 
375
- if (method === 'Target.activateTarget') {
376
- const target = typeof params?.targetId === 'string' ? params.targetId : ''
377
- const toActivate = target ? getTabByTargetId(target) : tabId
378
- if (!toActivate) return {}
379
- const tab = await chrome.tabs.get(toActivate).catch(() => null)
380
- if (!tab) return {}
377
+ if (method === "Target.activateTarget") {
378
+ const target = typeof params?.targetId === "string" ? params.targetId : "";
379
+ const toActivate = target ? getTabByTargetId(target) : tabId;
380
+ if (!toActivate) return {};
381
+ const tab = await chrome.tabs.get(toActivate).catch(() => null);
382
+ if (!tab) return {};
381
383
  if (tab.windowId) {
382
- await chrome.windows.update(tab.windowId, { focused: true }).catch(() => {})
384
+ await chrome.windows.update(tab.windowId, { focused: true }).catch(() => {});
383
385
  }
384
- await chrome.tabs.update(toActivate, { active: true }).catch(() => {})
385
- return {}
386
+ await chrome.tabs.update(toActivate, { active: true }).catch(() => {});
387
+ return {};
386
388
  }
387
389
 
388
- const tabState = tabs.get(tabId)
389
- const mainSessionId = tabState?.sessionId
390
+ const tabState = tabs.get(tabId);
391
+ const mainSessionId = tabState?.sessionId;
390
392
  const debuggerSession =
391
393
  sessionId && mainSessionId && sessionId !== mainSessionId
392
394
  ? { ...debuggee, sessionId }
393
- : debuggee
395
+ : debuggee;
394
396
 
395
- return await chrome.debugger.sendCommand(debuggerSession, method, params)
397
+ return await chrome.debugger.sendCommand(debuggerSession, method, params);
396
398
  }
397
399
 
398
400
  function onDebuggerEvent(source, method, params) {
399
- const tabId = source.tabId
400
- if (!tabId) return
401
- const tab = tabs.get(tabId)
402
- if (!tab?.sessionId) return
401
+ const tabId = source.tabId;
402
+ if (!tabId) return;
403
+ const tab = tabs.get(tabId);
404
+ if (!tab?.sessionId) return;
403
405
 
404
- if (method === 'Target.attachedToTarget' && params?.sessionId) {
405
- childSessionToTab.set(String(params.sessionId), tabId)
406
+ if (method === "Target.attachedToTarget" && params?.sessionId) {
407
+ childSessionToTab.set(String(params.sessionId), tabId);
406
408
  }
407
409
 
408
- if (method === 'Target.detachedFromTarget' && params?.sessionId) {
409
- childSessionToTab.delete(String(params.sessionId))
410
+ if (method === "Target.detachedFromTarget" && params?.sessionId) {
411
+ childSessionToTab.delete(String(params.sessionId));
410
412
  }
411
413
 
412
414
  try {
413
415
  sendToRelay({
414
- method: 'forwardCDPEvent',
416
+ method: "forwardCDPEvent",
415
417
  params: {
416
418
  sessionId: source.sessionId || tab.sessionId,
417
419
  method,
418
420
  params,
419
421
  },
420
- })
422
+ });
421
423
  } catch {
422
424
  // ignore
423
425
  }
424
426
  }
425
427
 
426
428
  function onDebuggerDetach(source, reason) {
427
- const tabId = source.tabId
428
- if (!tabId) return
429
- if (!tabs.has(tabId)) return
430
- void detachTab(tabId, reason)
429
+ const tabId = source.tabId;
430
+ if (!tabId) return;
431
+ if (!tabs.has(tabId)) return;
432
+ void detachTab(tabId, reason);
431
433
  }
432
434
 
433
- chrome.action.onClicked.addListener(() => void connectOrToggleForActiveTab())
435
+ chrome.action.onClicked.addListener(() => void connectOrToggleForActiveTab());
434
436
 
435
437
  chrome.runtime.onInstalled.addListener(() => {
436
438
  // Useful: first-time instructions.
437
- void chrome.runtime.openOptionsPage()
438
- })
439
+ void chrome.runtime.openOptionsPage();
440
+ });