9remote 3.0.32 → 3.0.34
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/cli.cjs +169 -127
- package/dist/extension/background.js +225 -0
- package/dist/extension/icons/icon128.png +0 -0
- package/dist/extension/icons/icon16.png +0 -0
- package/dist/extension/icons/icon48.png +0 -0
- package/dist/extension/manifest.json +11 -0
- package/dist/extension/popup.html +58 -0
- package/dist/extension/popup.js +23 -0
- package/dist/ptyDaemon.cjs +9 -9
- package/dist/server.cjs +400 -288
- package/dist/ui/assets/index-DbuftNJF.js +8 -0
- package/dist/ui/index.html +1 -1
- package/dist/web/404.html +1 -1
- package/dist/web/__next.__PAGE__.txt +2 -2
- package/dist/web/__next._full.txt +8 -8
- package/dist/web/__next._head.txt +1 -1
- package/dist/web/__next._index.txt +7 -7
- package/dist/web/__next._tree.txt +2 -2
- package/dist/web/_next/static/chunks/1750.09ca4eade7a6e5d3.js +1 -0
- package/dist/web/_next/static/chunks/{4300-fb55814d743e1313.js → 4300-7d6c9b3328075562.js} +1 -1
- package/dist/web/_next/static/chunks/5788-d4b8dc2ab68396b6.js +1 -0
- package/dist/web/_next/static/chunks/{6552-f908d82b4c4161ed.js → 6552-cebe84e4399a184f.js} +1 -1
- package/dist/web/_next/static/chunks/6739.22a05398bfd34922.js +1 -0
- package/dist/web/_next/static/chunks/8207-45554e2570bc59e6.js +1 -0
- package/dist/web/_next/static/chunks/8762-cb470d05070f5ead.js +1 -0
- package/dist/web/_next/static/chunks/8922-8660f73e9cd42406.js +1 -0
- package/dist/web/_next/static/chunks/{9871-9cd97efc98617c33.js → 9871-c277aedb0437cb82.js} +1 -1
- package/dist/web/_next/static/chunks/app/admin/access/{page-3a49e3c597eea658.js → page-65febd13b3d1fc91.js} +1 -1
- package/dist/web/_next/static/chunks/app/admin/login/page-8ff8150184b7ccaa.js +1 -0
- package/dist/web/_next/static/chunks/app/admin/ota/{page-406ff48a5882efa4.js → page-d8766baa3680522d.js} +1 -1
- package/dist/web/_next/static/chunks/app/admin/{page-434461c255fd088a.js → page-6237b1487c05f33e.js} +1 -1
- package/dist/web/_next/static/chunks/app/admin/turn/{page-dd7552f387e08b36.js → page-6a6b695b67d59451.js} +1 -1
- package/dist/web/_next/static/chunks/app/dev/question/page-bdedfcfce7a3c516.js +1 -0
- package/dist/web/_next/static/chunks/app/layout-a0e78c8427f97e4f.js +1 -0
- package/dist/web/_next/static/chunks/app/login/{page-80cc1994a494dfed.js → page-edc7457ddecf9262.js} +1 -1
- package/dist/web/_next/static/chunks/app/{page-f416e79015c32480.js → page-b76772608a876c23.js} +2 -2
- package/dist/web/_next/static/chunks/app/workspace/layout-0fd9ea03182e9c89.js +15 -0
- package/dist/web/_next/static/chunks/{webpack-b56d3361b185b0cb.js → webpack-f3500fd1fae71c88.js} +1 -1
- package/dist/web/_next/static/css/1b307dedbb687210.css +1 -0
- package/dist/web/_not-found/__next._full.txt +7 -7
- package/dist/web/_not-found/__next._head.txt +1 -1
- package/dist/web/_not-found/__next._index.txt +7 -7
- package/dist/web/_not-found/__next._not-found.__PAGE__.txt +1 -1
- package/dist/web/_not-found/__next._not-found.txt +1 -1
- package/dist/web/_not-found/__next._tree.txt +2 -2
- package/dist/web/_not-found.html +1 -1
- package/dist/web/_not-found.txt +7 -7
- package/dist/web/admin/__next._full.txt +8 -8
- package/dist/web/admin/__next._head.txt +1 -1
- package/dist/web/admin/__next._index.txt +7 -7
- package/dist/web/admin/__next._tree.txt +2 -2
- package/dist/web/admin/__next.admin.__PAGE__.txt +2 -2
- package/dist/web/admin/__next.admin.txt +1 -1
- package/dist/web/admin/access/__next._full.txt +8 -8
- package/dist/web/admin/access/__next._head.txt +1 -1
- package/dist/web/admin/access/__next._index.txt +7 -7
- package/dist/web/admin/access/__next._tree.txt +2 -2
- package/dist/web/admin/access/__next.admin.access.__PAGE__.txt +2 -2
- package/dist/web/admin/access/__next.admin.access.txt +1 -1
- package/dist/web/admin/access/__next.admin.txt +1 -1
- package/dist/web/admin/access.html +1 -1
- package/dist/web/admin/access.txt +8 -8
- package/dist/web/admin/login/__next._full.txt +8 -8
- package/dist/web/admin/login/__next._head.txt +1 -1
- package/dist/web/admin/login/__next._index.txt +7 -7
- package/dist/web/admin/login/__next._tree.txt +2 -2
- package/dist/web/admin/login/__next.admin.login.__PAGE__.txt +2 -2
- package/dist/web/admin/login/__next.admin.login.txt +1 -1
- package/dist/web/admin/login/__next.admin.txt +1 -1
- package/dist/web/admin/login.html +1 -1
- package/dist/web/admin/login.txt +8 -8
- package/dist/web/admin/ota/__next._full.txt +8 -8
- package/dist/web/admin/ota/__next._head.txt +1 -1
- package/dist/web/admin/ota/__next._index.txt +7 -7
- package/dist/web/admin/ota/__next._tree.txt +2 -2
- package/dist/web/admin/ota/__next.admin.ota.__PAGE__.txt +2 -2
- package/dist/web/admin/ota/__next.admin.ota.txt +1 -1
- package/dist/web/admin/ota/__next.admin.txt +1 -1
- package/dist/web/admin/ota.html +1 -1
- package/dist/web/admin/ota.txt +8 -8
- package/dist/web/admin/turn/__next._full.txt +8 -8
- package/dist/web/admin/turn/__next._head.txt +1 -1
- package/dist/web/admin/turn/__next._index.txt +7 -7
- package/dist/web/admin/turn/__next._tree.txt +2 -2
- package/dist/web/admin/turn/__next.admin.turn.__PAGE__.txt +2 -2
- package/dist/web/admin/turn/__next.admin.turn.txt +1 -1
- package/dist/web/admin/turn/__next.admin.txt +1 -1
- package/dist/web/admin/turn.html +1 -1
- package/dist/web/admin/turn.txt +8 -8
- package/dist/web/admin.html +1 -1
- package/dist/web/admin.txt +8 -8
- package/dist/web/dev/question/__next._full.txt +8 -8
- package/dist/web/dev/question/__next._head.txt +1 -1
- package/dist/web/dev/question/__next._index.txt +7 -7
- package/dist/web/dev/question/__next._tree.txt +2 -2
- package/dist/web/dev/question/__next.dev.question.__PAGE__.txt +2 -2
- package/dist/web/dev/question/__next.dev.question.txt +1 -1
- package/dist/web/dev/question/__next.dev.txt +1 -1
- package/dist/web/dev/question.html +1 -1
- package/dist/web/dev/question.txt +8 -8
- package/dist/web/download/__next._full.txt +8 -8
- package/dist/web/download/__next._head.txt +1 -1
- package/dist/web/download/__next._index.txt +7 -7
- package/dist/web/download/__next._tree.txt +2 -2
- package/dist/web/download/__next.download.__PAGE__.txt +2 -2
- package/dist/web/download/__next.download.txt +1 -1
- package/dist/web/download.html +1 -1
- package/dist/web/download.txt +8 -8
- package/dist/web/index.html +1 -1
- package/dist/web/index.txt +8 -8
- package/dist/web/login/__next._full.txt +8 -8
- package/dist/web/login/__next._head.txt +1 -1
- package/dist/web/login/__next._index.txt +7 -7
- package/dist/web/login/__next._tree.txt +2 -2
- package/dist/web/login/__next.login.__PAGE__.txt +2 -2
- package/dist/web/login/__next.login.txt +1 -1
- package/dist/web/login.html +1 -1
- package/dist/web/login.txt +8 -8
- package/dist/web/sw.js +2 -8
- package/dist/web/workspace/__next._full.txt +8 -8
- package/dist/web/workspace/__next._head.txt +1 -1
- package/dist/web/workspace/__next._index.txt +7 -7
- package/dist/web/workspace/__next._tree.txt +2 -2
- package/dist/web/workspace/__next.workspace.__PAGE__.txt +1 -1
- package/dist/web/workspace/__next.workspace.txt +2 -2
- package/dist/web/workspace/browse/__next._full.txt +8 -8
- package/dist/web/workspace/browse/__next._head.txt +1 -1
- package/dist/web/workspace/browse/__next._index.txt +7 -7
- package/dist/web/workspace/browse/__next._tree.txt +2 -2
- package/dist/web/workspace/browse/__next.workspace.browse.__PAGE__.txt +1 -1
- package/dist/web/workspace/browse/__next.workspace.browse.txt +1 -1
- package/dist/web/workspace/browse/__next.workspace.txt +2 -2
- package/dist/web/workspace/browse.html +1 -1
- package/dist/web/workspace/browse.txt +8 -8
- package/dist/web/workspace/editor/__next._full.txt +8 -8
- package/dist/web/workspace/editor/__next._head.txt +1 -1
- package/dist/web/workspace/editor/__next._index.txt +7 -7
- package/dist/web/workspace/editor/__next._tree.txt +2 -2
- package/dist/web/workspace/editor/__next.workspace.editor.__PAGE__.txt +1 -1
- package/dist/web/workspace/editor/__next.workspace.editor.txt +1 -1
- package/dist/web/workspace/editor/__next.workspace.txt +2 -2
- package/dist/web/workspace/editor.html +1 -1
- package/dist/web/workspace/editor.txt +8 -8
- package/dist/web/workspace/files/__next._full.txt +8 -8
- package/dist/web/workspace/files/__next._head.txt +1 -1
- package/dist/web/workspace/files/__next._index.txt +7 -7
- package/dist/web/workspace/files/__next._tree.txt +2 -2
- package/dist/web/workspace/files/__next.workspace.files.__PAGE__.txt +1 -1
- package/dist/web/workspace/files/__next.workspace.files.txt +1 -1
- package/dist/web/workspace/files/__next.workspace.txt +2 -2
- package/dist/web/workspace/files.html +1 -1
- package/dist/web/workspace/files.txt +8 -8
- package/dist/web/workspace/git/__next._full.txt +8 -8
- package/dist/web/workspace/git/__next._head.txt +1 -1
- package/dist/web/workspace/git/__next._index.txt +7 -7
- package/dist/web/workspace/git/__next._tree.txt +2 -2
- package/dist/web/workspace/git/__next.workspace.git.__PAGE__.txt +1 -1
- package/dist/web/workspace/git/__next.workspace.git.txt +1 -1
- package/dist/web/workspace/git/__next.workspace.txt +2 -2
- package/dist/web/workspace/git.html +1 -1
- package/dist/web/workspace/git.txt +8 -8
- package/dist/web/workspace/mobile/__next._full.txt +8 -8
- package/dist/web/workspace/mobile/__next._head.txt +1 -1
- package/dist/web/workspace/mobile/__next._index.txt +7 -7
- package/dist/web/workspace/mobile/__next._tree.txt +2 -2
- package/dist/web/workspace/mobile/__next.workspace.mobile.__PAGE__.txt +1 -1
- package/dist/web/workspace/mobile/__next.workspace.mobile.txt +1 -1
- package/dist/web/workspace/mobile/__next.workspace.txt +2 -2
- package/dist/web/workspace/mobile.html +1 -1
- package/dist/web/workspace/mobile.txt +8 -8
- package/dist/web/workspace/pair/__next._full.txt +8 -8
- package/dist/web/workspace/pair/__next._head.txt +1 -1
- package/dist/web/workspace/pair/__next._index.txt +7 -7
- package/dist/web/workspace/pair/__next._tree.txt +2 -2
- package/dist/web/workspace/pair/__next.workspace.pair.__PAGE__.txt +1 -1
- package/dist/web/workspace/pair/__next.workspace.pair.txt +1 -1
- package/dist/web/workspace/pair/__next.workspace.txt +2 -2
- package/dist/web/workspace/pair.html +1 -1
- package/dist/web/workspace/pair.txt +8 -8
- package/dist/web/workspace/remote/__next._full.txt +8 -8
- package/dist/web/workspace/remote/__next._head.txt +1 -1
- package/dist/web/workspace/remote/__next._index.txt +7 -7
- package/dist/web/workspace/remote/__next._tree.txt +2 -2
- package/dist/web/workspace/remote/__next.workspace.remote.__PAGE__.txt +1 -1
- package/dist/web/workspace/remote/__next.workspace.remote.txt +1 -1
- package/dist/web/workspace/remote/__next.workspace.txt +2 -2
- package/dist/web/workspace/remote.html +1 -1
- package/dist/web/workspace/remote.txt +8 -8
- package/dist/web/workspace/terminal/default/__next._full.txt +8 -8
- package/dist/web/workspace/terminal/default/__next._head.txt +1 -1
- package/dist/web/workspace/terminal/default/__next._index.txt +7 -7
- package/dist/web/workspace/terminal/default/__next._tree.txt +2 -2
- package/dist/web/workspace/terminal/default/__next.workspace.terminal.$d$sessionId.__PAGE__.txt +1 -1
- package/dist/web/workspace/terminal/default/__next.workspace.terminal.$d$sessionId.txt +1 -1
- package/dist/web/workspace/terminal/default/__next.workspace.terminal.txt +1 -1
- package/dist/web/workspace/terminal/default/__next.workspace.txt +2 -2
- package/dist/web/workspace/terminal/default.html +1 -1
- package/dist/web/workspace/terminal/default.txt +8 -8
- package/dist/web/workspace/welcome/__next._full.txt +8 -8
- package/dist/web/workspace/welcome/__next._head.txt +1 -1
- package/dist/web/workspace/welcome/__next._index.txt +7 -7
- package/dist/web/workspace/welcome/__next._tree.txt +2 -2
- package/dist/web/workspace/welcome/__next.workspace.txt +2 -2
- package/dist/web/workspace/welcome/__next.workspace.welcome.__PAGE__.txt +1 -1
- package/dist/web/workspace/welcome/__next.workspace.welcome.txt +1 -1
- package/dist/web/workspace/welcome.html +1 -1
- package/dist/web/workspace/welcome.txt +8 -8
- package/dist/web/workspace/workspaces/__next._full.txt +8 -8
- package/dist/web/workspace/workspaces/__next._head.txt +1 -1
- package/dist/web/workspace/workspaces/__next._index.txt +7 -7
- package/dist/web/workspace/workspaces/__next._tree.txt +2 -2
- package/dist/web/workspace/workspaces/__next.workspace.txt +2 -2
- package/dist/web/workspace/workspaces/__next.workspace.workspaces.__PAGE__.txt +1 -1
- package/dist/web/workspace/workspaces/__next.workspace.workspaces.txt +1 -1
- package/dist/web/workspace/workspaces.html +1 -1
- package/dist/web/workspace/workspaces.txt +8 -8
- package/dist/web/workspace.html +1 -1
- package/dist/web/workspace.txt +8 -8
- package/package.json +5 -2
- package/dist/ui/assets/index--4F7rf4t.js +0 -8
- package/dist/web/_next/static/chunks/1750.2cbe0f722b879a3b.js +0 -1
- package/dist/web/_next/static/chunks/5788-d574869f0879e799.js +0 -1
- package/dist/web/_next/static/chunks/6669-178477b6a852b251.js +0 -1
- package/dist/web/_next/static/chunks/6739.82570eba0a02cb26.js +0 -1
- package/dist/web/_next/static/chunks/8762-62e9a4a001d3e204.js +0 -1
- package/dist/web/_next/static/chunks/8922-6961d48ca52e1c93.js +0 -1
- package/dist/web/_next/static/chunks/app/admin/login/page-c7bdfdfbcc763df4.js +0 -1
- package/dist/web/_next/static/chunks/app/dev/question/page-ab488886d05baea5.js +0 -1
- package/dist/web/_next/static/chunks/app/layout-444afebe5b95afff.js +0 -1
- package/dist/web/_next/static/chunks/app/workspace/layout-a1024034aa67994d.js +0 -15
- package/dist/web/_next/static/css/630ce3b502aeea55.css +0 -1
- /package/dist/web/_next/static/{nx78GyCTuW2V1ns5cZIT_ → DdbafQkcX_ZaKRMjMeNKp}/_buildManifest.js +0 -0
- /package/dist/web/_next/static/{nx78GyCTuW2V1ns5cZIT_ → DdbafQkcX_ZaKRMjMeNKp}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
// 9remote browser bridge — a dumb CDP pipe. All business logic (snapshot,
|
|
2
|
+
// click gates, tab discipline, settle waits) lives in the host engine; this
|
|
3
|
+
// worker only forwards CDP verbs to chrome.debugger and shims the browser-level
|
|
4
|
+
// tab verbs the engine speaks over the wire. It should almost never change.
|
|
5
|
+
// Deliberately NOT an ES module (no import): the host bakes the relay URL into
|
|
6
|
+
// this placeholder at install time, so no "type": "module" / SW-cache class of bugs.
|
|
7
|
+
const RELAY_URL = "__RELAY_URL__";
|
|
8
|
+
|
|
9
|
+
let ws = null;
|
|
10
|
+
let paused = false; // user's kill switch (popup) — survives Chrome restarts
|
|
11
|
+
const attached = new Set(); // target ids (strings — chrome.debugger's form) with a live attach
|
|
12
|
+
const agentTabs = new Set(); // tabs the bridge opened or drives — never "the user's focus"
|
|
13
|
+
let activeTabId = null; // tab Chrome currently shows
|
|
14
|
+
let prevActiveTabId = null; // tab shown before the current one — the focus restore target
|
|
15
|
+
// Idle soft-detach: Chrome closes the "started debugging" banner ~5s after the
|
|
16
|
+
// debugger detaches, so an idle agent gets out of the user's face. The next
|
|
17
|
+
// command re-attaches transparently — the engine never notices.
|
|
18
|
+
const idleDetached = new Set(); // tabs WE detached while idle
|
|
19
|
+
let lastCallAt = 0;
|
|
20
|
+
const IDLE_DETACH_MS = 10000;
|
|
21
|
+
const IDLE_SWEEP_MS = 3000;
|
|
22
|
+
|
|
23
|
+
function send(frame) {
|
|
24
|
+
try { ws?.send(JSON.stringify(frame)); } catch { /* relay down — close handler reconnects */ }
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
async function detachQuiet(tid, idle = false) {
|
|
28
|
+
attached.delete(tid);
|
|
29
|
+
if (!idle) idleDetached.delete(tid);
|
|
30
|
+
try { await chrome.debugger.detach({ targetId: tid }); } catch { /* already gone */ }
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Restore what the user was watching when a popup stole the focus. Only called
|
|
34
|
+
// at the adoption moment — a user DELIBERATELY clicking an agent tab to watch
|
|
35
|
+
// must stick, so activation itself never triggers a snapback.
|
|
36
|
+
// Chrome rejects tab edits mid-drag (and once spuriously per session) — the
|
|
37
|
+
// rejection MUST be caught or it surfaces as an uncaught error; a short retry
|
|
38
|
+
// covers the transient case.
|
|
39
|
+
function snapBackFocus(tid) {
|
|
40
|
+
if (String(activeTabId) !== tid) return; // nothing was stolen — leave the user alone
|
|
41
|
+
if (prevActiveTabId == null || String(prevActiveTabId) === tid) return;
|
|
42
|
+
const focusBack = () => chrome.tabs.update(prevActiveTabId, { active: true }).catch(() => {});
|
|
43
|
+
focusBack();
|
|
44
|
+
setTimeout(focusBack, 250);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Browser-level verbs the engine needs but chrome.debugger does not expose —
|
|
48
|
+
// routed to chrome.tabs / chrome.debugger equivalents. Everything else rides
|
|
49
|
+
// sendCommand untouched, so engine changes never require an extension update.
|
|
50
|
+
const SHIM = {
|
|
51
|
+
"Target.getTargets": async () => {
|
|
52
|
+
const targets = await chrome.debugger.getTargets();
|
|
53
|
+
return { targetInfos: targets.map((t) => ({ targetId: String(t.id), type: t.type, title: t.title, url: t.url })) };
|
|
54
|
+
},
|
|
55
|
+
"Target.createTarget": async ({ url }) => {
|
|
56
|
+
// Always background: agent tabs never steal the user's focus (focus
|
|
57
|
+
// emulation on the page keeps them rendering anyway).
|
|
58
|
+
const tab = await chrome.tabs.create({ url: url || "about:blank", active: false });
|
|
59
|
+
agentTabs.add(String(tab.id));
|
|
60
|
+
return { targetId: String(tab.id) };
|
|
61
|
+
},
|
|
62
|
+
"Target.closeTarget": async ({ targetId }) => {
|
|
63
|
+
await chrome.tabs.remove(Number(targetId));
|
|
64
|
+
return {};
|
|
65
|
+
},
|
|
66
|
+
"Target.attachToTarget": async ({ targetId }) => {
|
|
67
|
+
// chrome.debugger debuggee ids are STRINGS; chrome.tabs takes numbers —
|
|
68
|
+
// translate at each boundary and keep strings everywhere inside.
|
|
69
|
+
const tid = String(targetId);
|
|
70
|
+
if (attached.has(tid)) return { sessionId: tid }; // session heal re-attaches its own target
|
|
71
|
+
// One attached page at a time — mirrors engine usage, avoids stacked infobars.
|
|
72
|
+
for (const prev of [...attached]) if (prev !== tid) detachQuiet(prev);
|
|
73
|
+
await chrome.debugger.attach({ targetId: tid }, "1.3");
|
|
74
|
+
attached.add(tid);
|
|
75
|
+
// A popup our click spawned takes the focus Chrome gives it — hand it back
|
|
76
|
+
// to the tab the user was actually reading. The agent drives in background.
|
|
77
|
+
agentTabs.add(tid);
|
|
78
|
+
snapBackFocus(tid);
|
|
79
|
+
return { sessionId: tid };
|
|
80
|
+
},
|
|
81
|
+
"Target.detachFromTarget": async ({ sessionId }) => {
|
|
82
|
+
await detachQuiet(String(sessionId));
|
|
83
|
+
return {};
|
|
84
|
+
},
|
|
85
|
+
// OOPIF child sessions are not reachable via chrome.debugger — accept and
|
|
86
|
+
// ignore so the engine's setup() does not fail (frames just stay unobserved).
|
|
87
|
+
"Target.setAutoAttach": async () => ({}),
|
|
88
|
+
// Browser-level discovery is synthesized from tabs.onCreated below.
|
|
89
|
+
"Target.setDiscoverTargets": async () => ({}),
|
|
90
|
+
// The engine uses it to surface its tab; for the user's real browser that
|
|
91
|
+
// IS the focus steal we are suppressing.
|
|
92
|
+
"Page.bringToFront": async () => ({})
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
let inputAt = 0; // last Input.* dispatch — gates popup adoption below
|
|
96
|
+
|
|
97
|
+
async function dispatch(method, params, targetId) {
|
|
98
|
+
if (SHIM[method]) return SHIM[method](params || {});
|
|
99
|
+
if (targetId == null) throw new Error(`${method} is not available via the extension`);
|
|
100
|
+
if (method.startsWith("Input.")) inputAt = Date.now();
|
|
101
|
+
const tid = String(targetId);
|
|
102
|
+
// Not attached (idle soft-detach, or this worker restarted and lost its set):
|
|
103
|
+
// try to (re)attach first. Only a missing tab masquerades as "No target";
|
|
104
|
+
// other failures (e.g. DevTools open on the tab) must surface as themselves.
|
|
105
|
+
if (!attached.has(tid)) {
|
|
106
|
+
idleDetached.delete(tid);
|
|
107
|
+
try {
|
|
108
|
+
await chrome.debugger.attach({ targetId: tid }, "1.3");
|
|
109
|
+
attached.add(tid);
|
|
110
|
+
} catch (e) {
|
|
111
|
+
throw /no target|not found|cannot access/i.test(e?.message || "")
|
|
112
|
+
? new Error(`No target with given id: ${targetId}`)
|
|
113
|
+
: e;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
try {
|
|
117
|
+
return await chrome.debugger.sendCommand({ targetId: tid }, method, params || {});
|
|
118
|
+
} catch (e) {
|
|
119
|
+
// A detached/closed target must look dead to the host's DEAD_SESSION heal.
|
|
120
|
+
if (!attached.has(tid)) throw new Error(`No target with given id: ${targetId}`);
|
|
121
|
+
throw e;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
chrome.debugger.onEvent.addListener((source, method, params) => {
|
|
126
|
+
send({ t: "event", method, params, sessionId: String(source.targetId) });
|
|
127
|
+
});
|
|
128
|
+
// Synthesize Target.targetCreated the engine's popup adoption listens for —
|
|
129
|
+
// chrome.debugger never surfaces browser-level target events.
|
|
130
|
+
const POPUP_WINDOW_MS = 1500; // born within this after our Input dispatch = the agent's click
|
|
131
|
+
chrome.tabs.onCreated.addListener((tab) => {
|
|
132
|
+
// Only a tab born right after an agent click counts as the agent's popup —
|
|
133
|
+
// a tab the USER opens from an agent-driven tab (cmd/middle-click) is theirs;
|
|
134
|
+
// tagging it would drag the session onto the user's tab.
|
|
135
|
+
const agentPopup = tab.openerTabId != null && agentTabs.has(String(tab.openerTabId)) &&
|
|
136
|
+
Date.now() - inputAt < POPUP_WINDOW_MS;
|
|
137
|
+
if (agentPopup) agentTabs.add(String(tab.id));
|
|
138
|
+
send({ t: "event", method: "Target.targetCreated", params: { targetInfo: {
|
|
139
|
+
targetId: String(tab.id), type: "page",
|
|
140
|
+
url: tab.url || tab.pendingUrl || "",
|
|
141
|
+
...((tab.openerTabId != null && agentPopup) ? { openerId: String(tab.openerTabId) } : {})
|
|
142
|
+
} } });
|
|
143
|
+
});
|
|
144
|
+
// Pure tracking: which tab Chrome shows, and which one came before it. The
|
|
145
|
+
// user clicking ANY tab (agent tabs included, to watch it work) just updates
|
|
146
|
+
// the trackers — focus is only ever restored at the adoption moment above.
|
|
147
|
+
chrome.tabs.onActivated.addListener(({ tabId }) => {
|
|
148
|
+
if (activeTabId !== tabId) { prevActiveTabId = activeTabId; activeTabId = tabId; }
|
|
149
|
+
});
|
|
150
|
+
// A closed tab is gone for good — never re-attach onto its id.
|
|
151
|
+
chrome.tabs.onRemoved.addListener((tabId) => {
|
|
152
|
+
attached.delete(String(tabId));
|
|
153
|
+
idleDetached.delete(String(tabId));
|
|
154
|
+
});
|
|
155
|
+
// The idle sweep behind the soft-detach (see idleDetached above).
|
|
156
|
+
setInterval(() => {
|
|
157
|
+
if (!attached.size || Date.now() - lastCallAt < IDLE_DETACH_MS) return;
|
|
158
|
+
for (const tid of [...attached]) {
|
|
159
|
+
idleDetached.add(tid);
|
|
160
|
+
detachQuiet(tid, true);
|
|
161
|
+
}
|
|
162
|
+
}, IDLE_SWEEP_MS);
|
|
163
|
+
// "Cancel" on the debug infobar, DevTools open, or tab close — surface it the
|
|
164
|
+
// way CDP would so the engine drops and rebuilds the session instead of wedging.
|
|
165
|
+
chrome.debugger.onDetach.addListener((source) => {
|
|
166
|
+
attached.delete(source.targetId);
|
|
167
|
+
idleDetached.delete(String(source.targetId)); // user cancelled — stay detached
|
|
168
|
+
send({ t: "event", method: "Target.detachedFromTarget", params: {
|
|
169
|
+
sessionId: String(source.targetId), targetId: String(source.targetId)
|
|
170
|
+
} });
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
// The user's kill switch: pause = drop debugger + relay, hard. Nothing rides
|
|
174
|
+
// through; the host simply sees the extension go offline.
|
|
175
|
+
async function setPaused(next) {
|
|
176
|
+
paused = next;
|
|
177
|
+
try { await chrome.storage.local.set({ paused: next }); } catch { /* non-fatal */ }
|
|
178
|
+
if (next) {
|
|
179
|
+
for (const tid of [...attached]) detachQuiet(tid);
|
|
180
|
+
try { ws?.close(); } catch { /* already closed */ }
|
|
181
|
+
ws = null;
|
|
182
|
+
} else if (!ws) {
|
|
183
|
+
connect();
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// Popup surface — status + the pause toggle.
|
|
188
|
+
chrome.runtime.onMessage.addListener((msg, _sender, sendResponse) => {
|
|
189
|
+
if (msg?.t === "status") {
|
|
190
|
+
let host = "";
|
|
191
|
+
try { host = new URL(RELAY_URL).host; } catch { /* keep empty */ }
|
|
192
|
+
sendResponse({
|
|
193
|
+
connected: ws?.readyState === WebSocket.OPEN, host,
|
|
194
|
+
version: chrome.runtime.getManifest().version, paused
|
|
195
|
+
});
|
|
196
|
+
return false;
|
|
197
|
+
}
|
|
198
|
+
if (msg?.t === "setPaused") {
|
|
199
|
+
setPaused(Boolean(msg.paused)).then(() => sendResponse({ ok: true }));
|
|
200
|
+
return true;
|
|
201
|
+
}
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
function connect() {
|
|
205
|
+
if (paused) return; // never dial out while the kill switch is on
|
|
206
|
+
ws = new WebSocket(RELAY_URL);
|
|
207
|
+
ws.onopen = () => send({ t: "hello", proto: 1 });
|
|
208
|
+
ws.onmessage = async (ev) => {
|
|
209
|
+
let m;
|
|
210
|
+
try { m = JSON.parse(ev.data); } catch { return; }
|
|
211
|
+
if (m.t === "ping") return send({ t: "pong" });
|
|
212
|
+
if (m.t !== "call") return;
|
|
213
|
+
lastCallAt = Date.now();
|
|
214
|
+
try { send({ t: "result", id: m.id, result: await dispatch(m.method, m.params, m.targetId) }); }
|
|
215
|
+
catch (e) { send({ t: "result", id: m.id, error: e?.message || String(e) }); }
|
|
216
|
+
};
|
|
217
|
+
const retry = () => { ws = null; if (!paused) setTimeout(connect, 2000); }; // host down: retry quietly
|
|
218
|
+
ws.onclose = retry;
|
|
219
|
+
ws.onerror = () => { try { ws?.close(); } catch { /* retry via onclose */ } };
|
|
220
|
+
}
|
|
221
|
+
// Boot: honor a persisted pause before the first connection attempt.
|
|
222
|
+
chrome.storage.local.get(["paused"]).then((saved) => {
|
|
223
|
+
paused = Boolean(saved?.paused);
|
|
224
|
+
if (!paused) connect();
|
|
225
|
+
}).catch(() => connect());
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
{
|
|
2
|
+
"manifest_version": 3,
|
|
3
|
+
"name": "9remote Browser Bridge",
|
|
4
|
+
"version": "1.0.0",
|
|
5
|
+
"description": "Lets the 9remote host drive this browser. All logic lives on the host — this is only a CDP pipe.",
|
|
6
|
+
"permissions": ["debugger", "tabs", "storage"],
|
|
7
|
+
"background": { "service_worker": "background.js" },
|
|
8
|
+
"action": { "default_popup": "popup.html" },
|
|
9
|
+
"icons": { "16": "icons/icon16.png", "48": "icons/icon48.png", "128": "icons/icon128.png" },
|
|
10
|
+
"minimum_chrome_version": "116"
|
|
11
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html>
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<style>
|
|
6
|
+
/* Web light theme (:root.light in globals.css), mirrored — no Tailwind here. */
|
|
7
|
+
:root {
|
|
8
|
+
--bg: #FCFBF9; --surface: #ffffff; --surface-2: #f4f4f5; --surface-3: #e7e7e9;
|
|
9
|
+
--border: #e5e7eb; --border-subtle: #e9e9ec;
|
|
10
|
+
--text: #0a0a0a; --muted: #6B7280; --subtle: #9CA3AF;
|
|
11
|
+
--brand: #FF570A; --ok: #10B981; --warn: #B45309;
|
|
12
|
+
}
|
|
13
|
+
* { box-sizing: border-box; }
|
|
14
|
+
body { width: 264px; margin: 0; font: 13px -apple-system, "Segoe UI", sans-serif; background: var(--bg); color: var(--text); }
|
|
15
|
+
.head { display: flex; align-items: center; gap: 10px; padding: 14px 14px 10px; }
|
|
16
|
+
.head img { width: 30px; height: 30px; border-radius: 7px; }
|
|
17
|
+
.head .name { font-weight: 600; font-size: 14px; }
|
|
18
|
+
.head .ver { color: var(--subtle); font-size: 11px; margin-left: auto; }
|
|
19
|
+
.status { display: flex; align-items: center; gap: 8px; padding: 0 14px 12px; font-size: 12.5px; }
|
|
20
|
+
.dot { width: 8px; height: 8px; border-radius: 50%; flex-shrink: 0; }
|
|
21
|
+
.dot.on { background: var(--ok); box-shadow: 0 0 6px #10b98166; }
|
|
22
|
+
.dot.off { background: var(--warn); }
|
|
23
|
+
.dot.cut { background: var(--surface-3); }
|
|
24
|
+
.status .host { color: var(--muted); margin-left: auto; font-size: 11px; }
|
|
25
|
+
.section { border-top: 1px solid var(--border-subtle); padding: 10px 14px; }
|
|
26
|
+
.row { display: flex; align-items: center; justify-content: space-between; }
|
|
27
|
+
.row .what { font-size: 12.5px; }
|
|
28
|
+
.row .hint { color: var(--muted); font-size: 10.5px; margin-top: 2px; }
|
|
29
|
+
/* Switch — neutral on-state (no brand color) */
|
|
30
|
+
.switch { position: relative; width: 36px; height: 20px; flex-shrink: 0; cursor: pointer; }
|
|
31
|
+
.switch input { display: none; }
|
|
32
|
+
.slider { position: absolute; inset: 0; background: var(--surface-2); border: 1px solid var(--border); border-radius: 999px; transition: background .15s; }
|
|
33
|
+
.slider::after { content: ""; position: absolute; top: 2px; left: 2px; width: 14px; height: 14px; border-radius: 50%; background: #fff; box-shadow: 0 1px 2px rgba(0,0,0,.18); transition: transform .15s; }
|
|
34
|
+
.switch input:checked + .slider { background: #0a0a0a; border-color: #0a0a0a; }
|
|
35
|
+
.switch input:checked + .slider::after { transform: translateX(16px); }
|
|
36
|
+
</style>
|
|
37
|
+
</head>
|
|
38
|
+
<body>
|
|
39
|
+
<div class="head">
|
|
40
|
+
<img src="icons/icon48.png" alt="">
|
|
41
|
+
<span class="name">9remote Bridge</span>
|
|
42
|
+
<span class="ver" id="ver"></span>
|
|
43
|
+
</div>
|
|
44
|
+
<div class="status">
|
|
45
|
+
<span class="dot off" id="dot"></span>
|
|
46
|
+
<span id="label">Checking…</span>
|
|
47
|
+
<span class="host" id="host"></span>
|
|
48
|
+
</div>
|
|
49
|
+
<div class="section row">
|
|
50
|
+
<div>
|
|
51
|
+
<div class="what">Bridge</div>
|
|
52
|
+
<div class="hint" id="pauseHint">Host may drive this browser</div>
|
|
53
|
+
</div>
|
|
54
|
+
<label class="switch"><input type="checkbox" id="bridge" checked><span class="slider"></span></label>
|
|
55
|
+
</div>
|
|
56
|
+
<script src="popup.js"></script>
|
|
57
|
+
</body>
|
|
58
|
+
</html>
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
// Popup = connection status + the one control that matters: cutting host
|
|
2
|
+
// access. Everything actionable lives in the 9remote web UI.
|
|
3
|
+
const $ = (id) => document.getElementById(id);
|
|
4
|
+
const ask = (msg) => new Promise((r) => chrome.runtime.sendMessage(msg, r));
|
|
5
|
+
|
|
6
|
+
async function render() {
|
|
7
|
+
const s = await ask({ t: "status" });
|
|
8
|
+
if (!s) return;
|
|
9
|
+
$("ver").textContent = "v" + (s.version || "");
|
|
10
|
+
$("host").textContent = s.host || "";
|
|
11
|
+
const paused = Boolean(s.paused);
|
|
12
|
+
$("dot").className = "dot " + (paused ? "cut" : s.connected ? "on" : "off");
|
|
13
|
+
$("label").textContent = paused ? "Bridge paused" : s.connected ? "Connected" : "Reconnecting…";
|
|
14
|
+
$("pauseHint").textContent = paused ? "Blocked until you turn it back on" : "Host may drive this browser";
|
|
15
|
+
$("bridge").checked = !paused;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
$("bridge").addEventListener("change", async (e) => {
|
|
19
|
+
await ask({ t: "setPaused", paused: !e.target.checked });
|
|
20
|
+
render();
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
render();
|
package/dist/ptyDaemon.cjs
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
const __importMetaUrl = require('url').pathToFileURL(__filename).href;
|
|
3
|
-
var pe=Object.create;var
|
|
4
|
-
`;try{
|
|
5
|
-
`,
|
|
6
|
-
`,
|
|
7
|
-
`,
|
|
8
|
-
`,
|
|
3
|
+
var pe=Object.create;var X=Object.defineProperty;var fe=Object.getOwnPropertyDescriptor;var de=Object.getOwnPropertyNames;var he=Object.getPrototypeOf,Se=Object.prototype.hasOwnProperty;var me=(e,t,r,s)=>{if(t&&typeof t=="object"||typeof t=="function")for(let n of de(t))!Se.call(e,n)&&n!==r&&X(e,n,{get:()=>t[n],enumerable:!(s=fe(t,n))||s.enumerable});return e};var C=(e,t,r)=>(r=e!=null?pe(he(e)):{},me(t||!e||!e.__esModule?X(r,"default",{value:e,enumerable:!0}):r,e));var ce=C(require("net"),1),d=C(require("fs"),1),R=C(require("path"),1),N=C(require("os"),1),q=require("child_process"),ie=C(require("node-pty"),1);var B="73";function ge(){return`function prompt { $p = $PWD.Path -replace '\\\\','/'; "$([char]27)]7;file://$([System.Net.Dns]::GetHostName())$p$([char]27)\\PS $($PWD.Path)> " }`}function K(e){return e.id==="powershell"||e.id==="pwsh"?[...e.args,"-NoExit","-Command",ge()]:e.args}var Y={win32:[{id:"cmd",label:"Command Prompt",path:"cmd.exe",args:[]},{id:"powershell",label:"PowerShell",path:"powershell.exe",args:["-NoLogo"]},{id:"pwsh",label:"PowerShell Core",path:"pwsh.exe",args:["-NoLogo"]}],unix:[{id:"bash",label:"Bash",path:"/bin/bash",args:["-l"]},{id:"zsh",label:"Zsh",path:"/bin/zsh",args:["-l"]},{id:"sh",label:"Sh",path:"/bin/sh",args:["-l"]}]};function V(e){if(process.platform==="win32"){let s=e?Y.win32.find(o=>o.id===e):null;if(s)return s;let n=process.env.COMSPEC;return n?{id:"cmd",label:"Command Prompt",path:n,args:[]}:Y.win32[0]}let t=process.env.SHELL||"/bin/bash",r=t.split("/").pop();return{id:r,label:r,path:t,args:["-l"]}}var Xe=45*1024,Ye={CHUNK_BYTES:32*1024,HEAD_BYTES:512*1024,HEAD_LINES:60,TITLE_MAX:120,SCAN_FILE_CAP:200,SCAN_DEPTH:4,PER_AGENT_LIMIT:50,DEFAULT_LIMIT:100,CACHE_TTL_MS:30*1e3,SEARCH_MIN_CHARS:2,SEARCH_QUERY_MAX:200,SEARCH_LIMIT:50,SEARCH_SNIPPET_MAX:200,SEARCH_SNIPPET_PAD:120,SEARCH_TOTAL_BYTES:64*1024*1024,SEARCH_FILE_BYTES:32*1024*1024,SEARCH_TIME_BUDGET_MS:700};var k={"terminal.ping":{reply:"pong",coalesce:null},"terminal.listSessions":{reply:"sessionList",coalesce:null},"terminal.createSession":{reply:"createResult",coalesce:null},"terminal.joinSession":{reply:"joinResult",coalesce:null},"terminal.requestHistory":{reply:"historyResult",coalesce:null},"terminal.input":{reply:null,coalesce:null},"terminal.resize":{reply:null,coalesce:"resize"},"terminal.deleteSession":{reply:"deleteResult",coalesce:null},"terminal.getCwd":{reply:"cwdResult",coalesce:null},"proc.start":{reply:"procStartResult",coalesce:null},"proc.attach":{reply:"procAttachResult",coalesce:null},"proc.lines":{reply:"procLinesResult",coalesce:null},"proc.write":{reply:"procWriteResult",coalesce:null},"proc.endInput":{reply:"procEndInputResult",coalesce:null},"proc.signal":{reply:"procSignalResult",coalesce:null},"proc.stop":{reply:"procStopResult",coalesce:null},"proc.list":{reply:"procListResult",coalesce:null},"kv.set":{reply:"kvSetResult",coalesce:null},"kv.get":{reply:"kvGetResult",coalesce:null},"kv.del":{reply:"kvDelResult",coalesce:null}},ye={ping:"terminal.ping",listSessions:"terminal.listSessions",createSession:"terminal.createSession",joinSession:"terminal.joinSession",requestHistory:"terminal.requestHistory",input:"terminal.input",resize:"terminal.resize",deleteSession:"terminal.deleteSession",getCwd:"terminal.getCwd",procStart:"proc.start",procAttach:"proc.attach",procLines:"proc.lines",procWrite:"proc.write",procSignal:"proc.signal",procStop:"proc.stop",procList:"proc.list",kvSet:"kv.set",kvGet:"kv.get",kvDel:"kv.del"};function J(e){return k[e]?e:ye[e]||null}var Ee=5e3,Q=Symbol("handler-timeout");function ee({deps:e={},send:t}={}){let r=new Map,s=new Map,n=[],o=!1,c=new Map,a=new Map;function i(p,u){for(let[_,E]of Object.entries(u)){let I=`${p}.${_}`;r.set(I,E),s.set(I,k[I]||{reply:null,coalesce:null})}}function h(){let p=Object.keys(k).filter(u=>!r.has(u));if(p.length)throw new Error(`Routes without a handler: ${p.join(", ")}`)}let l=p=>p.sessionId||p.procId||"";function S({client:p,message:u}){let _=u?.type,E=J(_)||_;if(!E||!r.has(E)){t(p,{type:"error",error:`Unknown route: ${_}`,requestId:u?.requestId});return}let I=s.get(E)||{reply:null,coalesce:null},z=`${E}:${u.sessionId??""}`,b=l(u);if(b&&c.has(b)){t(p,{type:I.reply||"error",success:!1,error:"Previous request on this session is still running",requestId:u.requestId});return}if(I?.coalesce){let P=a.get(z);if(P){P.message=u;return}let $={client:p,message:u,key:E,spec:I};a.set(z,$),n.push($)}else n.push({client:p,message:u,key:E,spec:I});f()}async function f(){if(!o){o=!0,await Promise.resolve();try{for(;n.length;){let p=n.shift();p.spec?.coalesce&&a.delete(`${p.key}:${p.message.sessionId??""}`),await y(p)}}finally{o=!1,n.length&&f()}}}async function y({client:p,message:u,key:_,spec:E}){let I=r.get(_),z=`${_}:${u.sessionId??""}`,b=x=>{if(!p)return;let M=!!x?.error;!E?.reply&&!M||t(p,{type:E?.reply||"error",...x,requestId:u.requestId})},P,$=new Promise(x=>{P=setTimeout(()=>x(Q),u.timeout||Ee),P.unref?.()}),W=Promise.resolve().then(()=>I(u,e,{client:p,answer:b}));try{let x=await Promise.race([W,$]);if(x===Q){let M=l(u);M&&(c.set(M,!0),W.catch(()=>{}).then(()=>c.delete(M))),b({success:!1,error:"Handler timeout"});return}b(x||{})}catch(x){b({success:!1,error:x?.message||String(x)})}finally{clearTimeout(P)}}return{register:i,assertComplete:h,enqueue:S}}function te(e=500){let t=new Map;return{set(r,s){return typeof r!="string"||!r?!1:(t.delete(r),t.set(r,structuredClone(s??null)),t.size>e&&t.delete(t.keys().next().value),!0)},get(r){return t.has(r)?structuredClone(t.get(r)):null},del(r){return t.delete(r)},get size(){return t.size}}}var re={set:(e,t)=>({success:t.kv.set(e.key,e.value)}),get:(e,t)=>({success:!0,value:t.kv.get(e.key)}),del:(e,t)=>({success:t.kv.del(e.key)})};function A(e){return e?.length?e.reduce((t,r)=>t+r.length,0):0}function U(e,t){if(!e?.length||t<=0)return Buffer.alloc(0);let r=t,s=[];for(let o=e.length-1;o>=0&&r>0;o--){let c=e[o];c.length<=r?(s.push(c),r-=c.length):(s.push(c.subarray(c.length-r)),r=0)}s.reverse();let n=Buffer.concat(s);if(n.length>1&&n[0]!==27){let o=Math.min(n.length,512);for(let c=1;c<o;c++)if(n[c]===27)return n.subarray(c)}return n}function Ie(e,t,r=512){if(t<=0||!e?.length)return t;let s=0;for(let c of e)s+=c.length;let n=s,o=r;for(let c=e.length-1;c>=0&&o>0;c--){let a=e[c];if(a.length===0){n-=0;continue}let i=n-a.length;if(i>=t){n=i;continue}let h=Math.min(a.length,t-i);for(let l=h-1;l>=0&&o>0;l--){if(a[l]===27)return i+l;o--}n=i}return t}function se(e,t,r){if(!e?.length||r<=0)return{prefix:Buffer.alloc(0),trimmed:0,extra:0};let s=A(e),n=s-Math.max(0,Math.min(t,s)),o=n-r;if(o<0&&(r+=o,o=0),r<=0)return{prefix:Buffer.alloc(0),trimmed:0,extra:0};let c=0;if(o>0){let h=Ie(e,o);h<o&&(c=o-h,o=h,r+=c)}let a=0,i=[];for(let h=0;h<e.length&&r>0;h++){let l=e[h],S=a+l.length;if(S<=o){a=S;continue}if(a>=n)break;let f=Math.max(0,o-a),y=Math.min(r,l.length-f);i.push(l.subarray(f,f+y)),r-=y,a=S}return{prefix:Buffer.concat(i),trimmed:0,extra:c}}var D=process.env.NREMOTE_HOME||R.default.join(N.default.homedir(),".9remote"),v=process.platform==="win32"?"\\\\.\\pipe\\9remote-pty":R.default.join(D,"pty-daemon.sock"),F=R.default.join(D,"pids","ptyDaemon.pid"),g=new Map,H=new Set,ne=2*1024*1024,xe=256*1024,Re=256*1024,_e=5*1024*1024,Te=["1049","1047","1000","1002","1003","1006","1015","1005"],be=/\x1b\[\?([0-9;]+)([hl])/g;function we(e,t){typeof t!="string"&&(t=String(t));for(let r of t.matchAll(be)){let s=r[2]==="h";for(let n of r[1].split(";"))e[s?"add":"delete"](`?${n}`)}}function Pe(e){let t=Te.filter(r=>e.has(`?${r}`));return t.length?`\x1B[?${t.join(";")}h`:""}var j=R.default.join(D,"logs"),O=R.default.join(j,"daemon.log");try{d.default.existsSync(j)||d.default.mkdirSync(j,{recursive:!0})}catch{}function ve(){try{if(d.default.existsSync(O)&&d.default.statSync(O).size>_e){let r=d.default.readFileSync(O,"utf8").slice(-1024*1024);d.default.writeFileSync(O,r)}}catch{}}function w(e,t=null){ve();let s=`[${new Date().toISOString()}] ERROR: ${e}`;t&&(s+=` - ${t.message||t}`),s+=`
|
|
4
|
+
`;try{d.default.appendFileSync(O,s)}catch{}console.error(s.trim())}function le(){return process.env.CODESPACES==="true"?process.env.CODESPACE_VSCODE_FOLDER||"/workspaces":process.env.HOME||process.env.USERPROFILE||N.default.homedir()}function Me(e){let t={...process.env,TERM:"xterm-256color",COLORTERM:"truecolor",LANG:process.env.LANG||"en_US.UTF-8"};delete t.PORT,delete t.NODE_ENV;let r=e.includes("zsh"),s=e.includes("bash"),n=null;if(r){n=d.default.mkdtempSync(R.default.join(N.default.tmpdir(),"9remote-zsh-"));let o=R.default.join(n,".zshrc"),c=t.HOME||N.default.homedir(),a="";a+=`[ -f "${c}/.zprofile" ] && source "${c}/.zprofile"
|
|
5
|
+
`,a+=`[ -f "${c}/.zshrc" ] && source "${c}/.zshrc"
|
|
6
|
+
`,a+=`[ -f "${c}/.zlogin" ] && source "${c}/.zlogin"
|
|
7
|
+
`,a+='NineRemoteOsc7() { print -Pn "\\e]7;file://%m${PWD}\\e\\\\" }\n',a+=`precmd_functions+=( NineRemoteOsc7 )
|
|
8
|
+
`,d.default.writeFileSync(o,a),t.ZDOTDIR=n}else if(s){let o=t.PROMPT_COMMAND||"";t.PROMPT_COMMAND=`printf "\\e]7;file://%s\\a" "\${HOSTNAME}\${PWD}"${o?`; ${o}`:""}`}return/cmd\.exe$/i.test(e)&&(t.PROMPT=`$E]7;file://${process.env.COMPUTERNAME||""}/$P$E\\$G$S`),{env:t,zdotDir:n}}function T(e){let t=JSON.stringify(e)+`
|
|
9
9
|
`;for(let r of H)try{r.write(t)}catch{}}function oe(e,t){try{e.write(JSON.stringify(t)+`
|
|
10
|
-
`)}catch{}}var m=new Map,
|
|
11
|
-
`);
|
|
12
|
-
`);
|
|
10
|
+
`)}catch{}}var m=new Map,Ce=0,Ae=512*1024,Oe=256*1024,Ne=3e3,De=3600*1e3;function ae(e,t=0){let r=typeof t=="object"&&t!==null?t.from??0:Number(t)||0,s=[];for(let n of e.lines)n.n>r&&s.push({n:n.n,enc:"b64",data:n.data.toString("base64")});return s}function Le(e,{bin:t,args:r=[],cwd:s,env:n,windowsVerbatimArguments:o}={}){if(!e)return{success:!1,error:"Missing procId"};if(!t)return{success:!1,error:"Missing bin"};let c=m.get(e);if(c&&!c.exited)return{success:!1,error:"Process already running"};let a=(0,q.spawn)(t,r,{cwd:s&&d.default.existsSync(s)?s:le(),stdio:["pipe","pipe","pipe"],env:{...process.env,...n||{}},detached:process.platform!=="win32",windowsVerbatimArguments:o===!0}),i={id:e,epoch:++Ce,child:a,cwd:s,lines:[],lineCount:0,bytes:0,exited:!1,exitCode:null,tail:""};m.set(e,i);let h=S=>{let f=Buffer.from(S,"utf8");for(i.lineCount++,i.lines.push({n:i.lineCount,data:f}),i.bytes+=f.length;i.bytes>Ae&&i.lines.length>1;)i.bytes-=i.lines[0].data.length,i.lines.shift();T({type:"procLine",procId:e,epoch:i.epoch,n:i.lineCount,enc:"b64",data:f.toString("base64")})},l=S=>{S.on("data",f=>{if(i.tail+=f.toString("utf8"),i.tail.length>Oe){h(i.tail),i.tail="";return}let y=i.tail.split(`
|
|
11
|
+
`);i.tail=y.pop()||"";for(let p of y)h(p)})};return l(a.stdout),l(a.stderr),a.on("error",S=>{i.exited=!0,i.exitedAt=Date.now(),T({type:"procExit",procId:e,epoch:i.epoch,code:null,error:S.message})}),a.on("close",(S,f)=>{i.tail&&(h(i.tail),i.tail=""),i.exited=!0,i.exitedAt=Date.now(),i.exitCode=S,i.child=null,T({type:"procExit",procId:e,epoch:i.epoch,code:S,signal:f||null})}),{success:!0,procId:e,epoch:i.epoch,pid:a.pid}}function $e(e,t=0){typeof t=="object"&&t!==null&&(t=t.from??0),t=Number(t)||0;let r=m.get(e);return r?{success:!0,alive:!r.exited,epoch:r.epoch,lines:ae(r,t),total:r.lineCount,oldest:r.lines[0]?.n??r.lineCount+1,exitCode:r.exitCode}:{success:!1,error:"Process not found"}}function ke(e,t,r="b64"){let s=m.get(e);return s?.child?.stdin?.writable?(s.child.stdin.write(r==="b64"?Buffer.from(t,"base64"):t),{success:!0}):{success:!1,error:"Process not writable"}}function He(e){let t=m.get(e);if(!t?.child?.stdin?.writable)return{success:!1,error:"Process not writable"};try{t.child.stdin.end()}catch(r){return{success:!1,error:r.message}}return{success:!0}}function ze(e,t="SIGINT"){let r=m.get(e);if(!r?.child)return{success:!1,error:"Process not running"};try{r.child.kill(t)}catch(s){return{success:!1,error:s.message}}return{success:!0}}function G(e,t){if(e?.pid)try{process.platform==="win32"?(0,q.spawn)("taskkill",["/pid",String(e.pid),"/T","/F"]).on("error",()=>{}):process.kill(-e.pid,t)}catch{}}function Be(e){let t=m.get(e);if(!t)return{success:!1,error:"Process not found"};let r=t.child;return r&&(G(r,"SIGINT"),setTimeout(()=>G(r,"SIGKILL"),Ne).unref?.()),m.delete(e),{success:!0}}setInterval(()=>{let e=Date.now();for(let[t,r]of m)r.exited&&r.exitedAt&&e-r.exitedAt>=De&&m.delete(t)},600*1e3).unref?.();function Ue(e,t,r=80,s=24,n=null,o=null){if(g.has(e))return{success:!1,error:"Session already exists"};let c=V(n);(!o||!d.default.existsSync(o))&&(o=le());try{let{env:a,zdotDir:i}=Me(c.path);a.NINE_REMOTE_SESSION_ID=e;let h=ie.default.spawn(c.path,K(c),{name:"xterm-256color",cols:r,rows:s,cwd:o,env:a,useConpty:process.platform==="win32"}),l={pty:h,buffer:[],modes:new Set,name:t,createdAt:Date.now(),cwd:o,shellId:c.id,shellLabel:c.label,zdotDir:i,pending:null,flushScheduled:!1,procTimer:null,foregroundProcess:Z({pty:h})||c.id},S=()=>{l.flushScheduled=!1;let f=l.pending;f&&(l.pending=null,T({type:"output",sessionId:e,enc:"b64",data:Buffer.from(f).toString("base64")}))};return h.onData(f=>{l.buffer.push(Buffer.from(f,"utf-8")),we(l.modes,f);let y=f.match(/\x1b\]7;file:\/\/[^/]*([^\x07\x1b]*)/);if(y){let u;try{u=decodeURIComponent(y[1])}catch{u=y[1]}process.platform==="win32"&&/^\/[a-zA-Z]:[\\/]/.test(u)&&(u=u.slice(1)),u&&u!==l.cwd&&(l.cwd=u,T({type:"cwdChange",sessionId:e,cwd:u}))}A(l.buffer)>ne&&(l.buffer=[U(l.buffer,ne)]),l.pending=l.pending===null?f:l.pending+f,l.flushScheduled||(l.flushScheduled=!0,setImmediate(S)),l.procTimer&&clearTimeout(l.procTimer),l.procTimer=setTimeout(()=>{l.procTimer=null,ue(l,e)},250)}),h.onExit(()=>{l.procTimer&&clearTimeout(l.procTimer),g.delete(e),l.zdotDir&&d.default.rm(l.zdotDir,{recursive:!0,force:!0},()=>{}),T({type:"sessionClosed",sessionId:e})}),g.set(e,l),{success:!0,sessionId:e,cwd:o,shellId:c.id,shellLabel:c.label}}catch(a){return w("Failed to create session",a),{success:!1,error:a.message}}}function Z(e){if(!e?.pty)return null;try{let t=e.pty.process;return t?R.default.basename(t):null}catch{return null}}function ue(e,t){if(!e?.pty)return;let r=Z(e);if(r&&r!==e.foregroundProcess){let s=e.foregroundProcess;e.foregroundProcess=r,s!==null&&T({type:"processChange",sessionId:t,process:r,prevProcess:s})}}var Fe=te(500),L=ee({deps:{sessions:g,procs:m,send:oe,broadcast:T,DAEMON_VERSION:B,kv:Fe},send:oe}),je={ping:()=>({version:B}),listSessions:()=>({sessions:Array.from(g.entries()).map(([e,t])=>({id:e,name:t.name,createdAt:t.createdAt,shellId:t.shellId,shellLabel:t.shellLabel,cwd:t.cwd,foregroundProcess:t.foregroundProcess||Z(t)}))}),createSession:e=>Ue(e.sessionId||`session-${Date.now()}`,e.name,e.cols,e.rows,e.shellId,e.cwd),joinSession:(e,{send:t})=>{let r=g.get(e.sessionId);if(!r)return{success:!1,error:"Session not found"};let s=e.client,n=Pe(r.modes);n&&t(s,{type:"output",sessionId:e.sessionId,enc:"b64",replay:!0,data:Buffer.from(n).toString("base64")});let o=U(r.buffer,xe);return o&&o.length&&t(s,{type:"output",sessionId:e.sessionId,enc:"b64",replay:!0,data:o.toString("base64")}),{success:!0,name:r.name,cwd:r.cwd,shellId:r.shellId,shellLabel:r.shellLabel,total:A(r.buffer),replaySize:o?o.length:0}},requestHistory:e=>{let t=g.get(e.sessionId);if(!t)return{success:!1,error:"Session not found"};let r=A(t.buffer),s=Math.max(0,Math.min(e.have||0,r)),n=r-s,o=Math.min(Re,n),{prefix:c,extra:a=0}=o>0?se(t.buffer,s,o):{prefix:Buffer.alloc(0),extra:0};return{success:!0,sessionId:e.sessionId,enc:"b64",prefix:c&&c.length?c.toString("base64"):"",prefixLen:c?c.length:0,total:r,remaining:Math.max(0,n-o)}},input:e=>{let t=g.get(e.sessionId);return t?.pty?(t.pty.write(e.data),t.procTimer&&clearTimeout(t.procTimer),t.procTimer=setTimeout(()=>{t.procTimer=null,ue(t,e.sessionId)},300),{}):{}},resize:e=>{let t=g.get(e.sessionId);if(!t?.pty)return{};try{t.pty.resize(e.cols,e.rows)}catch{}return{}},deleteSession:(e,{broadcast:t})=>{let r=g.get(e.sessionId);return r?(r.pty&&r.pty.kill(),g.delete(e.sessionId),t({type:"sessionClosed",sessionId:e.sessionId}),{success:!0}):{success:!1,error:"Session not found"}},getCwd:e=>({cwd:g.get(e.sessionId)?.cwd||null})},Ge={start:e=>Le(e.procId,e),attach:e=>$e(e.procId,e.from),lines:e=>{let t=m.get(e.procId);return t?{success:!0,epoch:t.epoch,lines:ae(t,e.from||0),total:t.lineCount,oldest:t.lines[0]?.n??t.lineCount+1}:{success:!1,error:"Process not found"}},write:e=>ke(e.procId,e.data,e.enc),endInput:e=>He(e.procId),signal:e=>ze(e.procId,e.signal),stop:e=>Be(e.procId),list:()=>({success:!0,procs:Array.from(m.values()).map(e=>({procId:e.id,alive:!e.exited,total:e.lineCount,cwd:e.cwd}))})};L.register("terminal",je);L.register("proc",Ge);L.register("kv",re);L.assertComplete();function qe(e,t){L.enqueue({client:e,message:{...t,client:e}})}function Ze(){if(!d.default.existsSync(D))try{d.default.mkdirSync(D,{recursive:!0})}catch(r){w("Failed to create socket directory",r),process.exit(1)}if(process.platform!=="win32"&&d.default.existsSync(v))try{d.default.unlinkSync(v)}catch(r){w("Failed to remove stale socket",r),process.exit(1)}let e=ce.default.createServer(r=>{H.add(r);let s="";r.on("data",n=>{s+=n.toString();let o=s.split(`
|
|
12
|
+
`);s=o.pop()||"";for(let c of o)if(c.trim())try{let a=JSON.parse(c);qe(r,a)}catch{w("Invalid message",c)}}),r.on("close",()=>{H.delete(r)}),r.on("error",n=>{w("Client error",n),H.delete(r)})});e.on("error",r=>{w("Server error",r),r.code==="EADDRINUSE"&&w("Socket already in use, exiting"),process.exit(1)}),e.listen(v,()=>{if(process.platform!=="win32")try{d.default.chmodSync(v,384)}catch{}});try{d.default.mkdirSync(R.default.dirname(F),{recursive:!0}),d.default.writeFileSync(F,String(process.pid),{mode:384})}catch{}let t=()=>{for(let[,r]of g)if(r.procTimer&&clearTimeout(r.procTimer),r.pty)try{r.pty.kill()}catch{}for(let[,r]of m)G(r.child,"SIGINT");try{e.close()}catch{}if(process.platform!=="win32"&&d.default.existsSync(v))try{d.default.unlinkSync(v)}catch{}try{d.default.unlinkSync(F)}catch{}process.exit(0)};process.on("SIGTERM",t),process.on("SIGINT",t)}Ze();
|