@omercnet/paseo-shared-browser 1.1.0-next.196.1 → 1.1.0-next.204.1
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/README.md +9 -5
- package/client/sidebar.tsx +134 -0
- package/index.client.tsx +7 -1
- package/index.server.ts +5 -1
- package/package.json +4 -4
- package/paseo-plugin.json +1 -1
package/README.md
CHANGED
|
@@ -93,15 +93,19 @@ Before upgrading the plugin on Windows, close active Shared Browser sessions and
|
|
|
93
93
|
daemon; Windows does not allow the installer to replace runtime executables that are still running.
|
|
94
94
|
|
|
95
95
|
Open a workspace, search the Command Center for **Open Shared Browser**, or tap the **Shared
|
|
96
|
-
Browser** composer pill while a workspace session is open.
|
|
96
|
+
Browser** composer pill while a workspace session is open. On Paseo 0.11 or newer, a **Shared
|
|
97
|
+
Browser (n)** row in the sidebar footer appears while browser sessions are open; it lists them and
|
|
98
|
+
jumps to the chosen workspace's browser panel.
|
|
97
99
|
|
|
98
100
|
## Agent MCP access
|
|
99
101
|
|
|
100
102
|
The plugin automatically injects its stdio MCP adapter only when a new, non-internal agent is
|
|
101
103
|
created with a provider that accepts external MCP servers. Agents that already exist, resumed
|
|
102
|
-
sessions, imported sessions, and Paseo's internal agents are not modified. Paseo's OMP
|
|
103
|
-
|
|
104
|
-
|
|
104
|
+
sessions, imported sessions, and Paseo's internal agents are not modified. Paseo's built-in OMP
|
|
105
|
+
provider accepts session MCP servers from Paseo 0.11, so new OMP agents receive the adapter there.
|
|
106
|
+
On Paseo 0.9 and 0.10 the built-in OMP adapter rejects external MCP servers, so OMP agents are left
|
|
107
|
+
unchanged. Pi agents continue to receive the adapter, but they require Pi's optional MCP support to
|
|
108
|
+
launch it.
|
|
105
109
|
|
|
106
110
|
The injected MCP server exposes exactly these tools: `shared_browser_status`,
|
|
107
111
|
`shared_browser_capture`, `shared_browser_acquire_control`, `shared_browser_release_control`,
|
|
@@ -180,5 +184,5 @@ persistence, and archive teardown.
|
|
|
180
184
|
Release Please maintains the version, changelog, component tag, and GitHub release from
|
|
181
185
|
Conventional Commits in the monorepo.
|
|
182
186
|
|
|
183
|
-
Both the Paseo daemon and app must satisfy `^0.9.0 || ^0.10.0`. The client surface uses React Native primitives
|
|
187
|
+
Both the Paseo daemon and app must satisfy `^0.9.0 || ^0.10.0 || ^0.11.0`. The client surface uses React Native primitives
|
|
184
188
|
and works in desktop, web, iOS, and Android Paseo clients.
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type PluginClientContext,
|
|
3
|
+
type PluginPopoverProps,
|
|
4
|
+
type PluginSidebarItemProps,
|
|
5
|
+
useRpc,
|
|
6
|
+
useWorkspace,
|
|
7
|
+
} from "@getpaseo/plugin/client";
|
|
8
|
+
import { SidebarRow } from "@getpaseo/plugin/client/ui";
|
|
9
|
+
import { useQuery } from "@tanstack/react-query";
|
|
10
|
+
import { useMemo } from "react";
|
|
11
|
+
import { Pressable, StyleSheet, Text, View } from "react-native";
|
|
12
|
+
import { listOpenBrowserWorkspacesRpc } from "../shared/browser";
|
|
13
|
+
|
|
14
|
+
const PANEL_ID = "shared-browser";
|
|
15
|
+
const PRESENCE_POLL_MS = 5_000;
|
|
16
|
+
const TOUCH_TARGET = 44;
|
|
17
|
+
const ROW_HEIGHT = 36;
|
|
18
|
+
const RADIUS = 8;
|
|
19
|
+
|
|
20
|
+
function useOpenBrowserWorkspaceIds(): readonly string[] {
|
|
21
|
+
const listOpen = useRpc(listOpenBrowserWorkspacesRpc);
|
|
22
|
+
const query = useQuery({
|
|
23
|
+
queryKey: ["shared-browser", "presence"],
|
|
24
|
+
queryFn: async () => (await listOpen({})).workspaceIds,
|
|
25
|
+
refetchInterval: PRESENCE_POLL_MS,
|
|
26
|
+
retry: false,
|
|
27
|
+
});
|
|
28
|
+
return query.data ?? [];
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function SessionRow({
|
|
32
|
+
workspaceId,
|
|
33
|
+
theme,
|
|
34
|
+
compact,
|
|
35
|
+
onPress,
|
|
36
|
+
}: {
|
|
37
|
+
workspaceId: string;
|
|
38
|
+
theme: PluginPopoverProps["theme"];
|
|
39
|
+
compact: boolean;
|
|
40
|
+
onPress(): void;
|
|
41
|
+
}) {
|
|
42
|
+
const name = useWorkspace(workspaceId, (workspace) => workspace.title ?? workspace.name);
|
|
43
|
+
const styles = useMemo(
|
|
44
|
+
() =>
|
|
45
|
+
StyleSheet.create({
|
|
46
|
+
row: {
|
|
47
|
+
minHeight: compact ? TOUCH_TARGET : ROW_HEIGHT,
|
|
48
|
+
justifyContent: "center",
|
|
49
|
+
paddingHorizontal: 12,
|
|
50
|
+
borderRadius: RADIUS,
|
|
51
|
+
},
|
|
52
|
+
label: { color: theme.colors.foreground, fontSize: 14 },
|
|
53
|
+
}),
|
|
54
|
+
[theme, compact],
|
|
55
|
+
);
|
|
56
|
+
// A workspace this client has not loaded cannot host the panel; openPanel would throw.
|
|
57
|
+
if (name === null) return null;
|
|
58
|
+
return (
|
|
59
|
+
<Pressable
|
|
60
|
+
accessibilityRole="button"
|
|
61
|
+
accessibilityLabel={`Open Shared Browser for ${name}`}
|
|
62
|
+
onPress={onPress}
|
|
63
|
+
style={({ pressed }) => [
|
|
64
|
+
styles.row,
|
|
65
|
+
pressed ? { backgroundColor: theme.colors.surface1 } : null,
|
|
66
|
+
]}
|
|
67
|
+
>
|
|
68
|
+
<Text numberOfLines={1} style={styles.label}>
|
|
69
|
+
{name}
|
|
70
|
+
</Text>
|
|
71
|
+
</Pressable>
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function createSessionsPopover(client: PluginClientContext) {
|
|
76
|
+
return function SessionsPopover({ theme, layout, close }: PluginPopoverProps) {
|
|
77
|
+
const workspaceIds = useOpenBrowserWorkspaceIds();
|
|
78
|
+
const styles = useMemo(
|
|
79
|
+
() =>
|
|
80
|
+
StyleSheet.create({
|
|
81
|
+
container: { padding: layout.compact ? 16 : 8, gap: 4, minWidth: 220 },
|
|
82
|
+
title: {
|
|
83
|
+
color: theme.colors.foregroundMuted,
|
|
84
|
+
fontSize: 12,
|
|
85
|
+
paddingHorizontal: 12,
|
|
86
|
+
paddingBottom: 4,
|
|
87
|
+
},
|
|
88
|
+
}),
|
|
89
|
+
[theme, layout.compact],
|
|
90
|
+
);
|
|
91
|
+
return (
|
|
92
|
+
<View style={styles.container}>
|
|
93
|
+
<Text style={styles.title}>Open browser sessions</Text>
|
|
94
|
+
{workspaceIds.map((workspaceId) => (
|
|
95
|
+
<SessionRow
|
|
96
|
+
key={workspaceId}
|
|
97
|
+
workspaceId={workspaceId}
|
|
98
|
+
theme={theme}
|
|
99
|
+
compact={layout.compact}
|
|
100
|
+
onPress={() => {
|
|
101
|
+
close();
|
|
102
|
+
client.openPanel(PANEL_ID, { workspaceId });
|
|
103
|
+
}}
|
|
104
|
+
/>
|
|
105
|
+
))}
|
|
106
|
+
</View>
|
|
107
|
+
);
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function createSidebarItem(client: PluginClientContext) {
|
|
112
|
+
const SessionsPopover = createSessionsPopover(client);
|
|
113
|
+
return function SharedBrowserSidebarItem({ openPopover }: PluginSidebarItemProps) {
|
|
114
|
+
const count = useOpenBrowserWorkspaceIds().length;
|
|
115
|
+
if (count === 0) return null;
|
|
116
|
+
return (
|
|
117
|
+
<SidebarRow
|
|
118
|
+
icon="PanelsTopLeft"
|
|
119
|
+
label={`Shared Browser (${count})`}
|
|
120
|
+
onPress={() => openPopover(SessionsPopover)}
|
|
121
|
+
/>
|
|
122
|
+
);
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/** Sidebar footer row listing open sessions. Hosts before 0.11 lack the API and get no row. */
|
|
127
|
+
export function contributeSharedBrowserSidebar(client: PluginClientContext): () => void {
|
|
128
|
+
if (typeof client.addSidebarFooterItem !== "function") return () => {};
|
|
129
|
+
return client.addSidebarFooterItem({
|
|
130
|
+
id: "shared-browser-sessions",
|
|
131
|
+
title: "Shared Browser",
|
|
132
|
+
Component: createSidebarItem(client),
|
|
133
|
+
});
|
|
134
|
+
}
|
package/index.client.tsx
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { PluginClientContext } from "@getpaseo/plugin/client";
|
|
2
2
|
import { contributeSharedBrowserClient, SharedBrowserPanel } from "./client/browser";
|
|
3
|
+
import { contributeSharedBrowserSidebar } from "./client/sidebar";
|
|
3
4
|
|
|
4
5
|
export default function contribute(client: PluginClientContext) {
|
|
5
6
|
client.addWorkspacePanel({
|
|
@@ -31,5 +32,10 @@ export default function contribute(client: PluginClientContext) {
|
|
|
31
32
|
},
|
|
32
33
|
});
|
|
33
34
|
|
|
34
|
-
|
|
35
|
+
const removeSidebar = contributeSharedBrowserSidebar(client);
|
|
36
|
+
const cleanup = contributeSharedBrowserClient(client);
|
|
37
|
+
return () => {
|
|
38
|
+
removeSidebar();
|
|
39
|
+
return cleanup();
|
|
40
|
+
};
|
|
35
41
|
}
|
package/index.server.ts
CHANGED
|
@@ -77,8 +77,12 @@ export default function contribute(server: PluginServerContext) {
|
|
|
77
77
|
server.handle(resizeBrowserRpc, handleResizeBrowser);
|
|
78
78
|
server.handle(applyDevicePresetRpc, handleApplyDevicePreset);
|
|
79
79
|
server.handle(sendBrowserInputRpc, handleSendBrowserInput);
|
|
80
|
+
// OMP accepts session MCP servers from Paseo 0.11.0-beta.1, the release that added
|
|
81
|
+
// registerUsageSource; 0.9/0.10 reject agent creation when an OMP config carries MCP servers.
|
|
82
|
+
const ompAcceptsMcp = typeof server.registerUsageSource === "function";
|
|
80
83
|
server.before("agent.create", async ({ request }) => {
|
|
81
|
-
if (request.config.internal
|
|
84
|
+
if (request.config.internal) return request;
|
|
85
|
+
if (request.config.provider === "omp" && !ompAcceptsMcp) return request;
|
|
82
86
|
const ticket = randomBytes(32).toString("base64url");
|
|
83
87
|
if (!(await issueTicketWithinDeadline(ticket))) return request;
|
|
84
88
|
return {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@omercnet/paseo-shared-browser",
|
|
3
|
-
"version": "1.1.0-next.
|
|
3
|
+
"version": "1.1.0-next.204.1",
|
|
4
4
|
"description": "One real Chromium session per Paseo workspace, shared live across every connected client.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"browser",
|
|
@@ -43,12 +43,12 @@
|
|
|
43
43
|
"check:write": "biome check --write .",
|
|
44
44
|
"typecheck": "tsc --noEmit",
|
|
45
45
|
"prepare:runtime": "node scripts/prepare-runtime.mjs",
|
|
46
|
-
"test": "vitest run tests/contracts.test.ts tests/lease.test.ts tests/runtime-lifecycle.test.ts tests/agent-access.test.ts tests/composer-pills.test.ts",
|
|
47
|
-
"test:unit": "vitest run tests/contracts.test.ts tests/lease.test.ts tests/runtime-lifecycle.test.ts tests/agent-access.test.ts tests/composer-pills.test.ts",
|
|
46
|
+
"test": "vitest run tests/contracts.test.ts tests/lease.test.ts tests/runtime-lifecycle.test.ts tests/agent-access.test.ts tests/composer-pills.test.ts tests/sidebar.test.ts",
|
|
47
|
+
"test:unit": "vitest run tests/contracts.test.ts tests/lease.test.ts tests/runtime-lifecycle.test.ts tests/agent-access.test.ts tests/composer-pills.test.ts tests/sidebar.test.ts",
|
|
48
48
|
"test:smoke": "vitest run tests/browser.smoke.test.ts --testTimeout=30000"
|
|
49
49
|
},
|
|
50
50
|
"dependencies": {
|
|
51
|
-
"@getpaseo/plugin": "0.
|
|
51
|
+
"@getpaseo/plugin": "0.11.0-beta.3",
|
|
52
52
|
"@modelcontextprotocol/sdk": "1.30.0",
|
|
53
53
|
"agent-browser": "0.37.1",
|
|
54
54
|
"esbuild": "0.28.2",
|
package/paseo-plugin.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"id": "shared-browser",
|
|
3
3
|
"description": "One real Chromium session per Paseo workspace, shared live across every connected client.",
|
|
4
|
-
"requirements": { "paseo": "^0.9.0 || ^0.10.0" },
|
|
4
|
+
"requirements": { "paseo": "^0.9.0 || ^0.10.0 || ^0.11.0" },
|
|
5
5
|
"build": [["node", "scripts/prepare-dependencies.mjs"], ["npm", "run", "prepare:runtime"]]
|
|
6
6
|
}
|