@openclaw/google-meet 2026.7.2-beta.7 → 2026.8.1-beta.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,325 @@
1
+ import { r as GoogleMeetBrowserManualActionError, t as GOOGLE_MEET_BROWSER_NODE_ADAPTER } from "./google-meet-platform-constants-Bs5iAg3E.js";
2
+ import { sleep } from "openclaw/plugin-sdk/runtime-env";
3
+ import { asMeetingBrowserTabs, callMeetingBrowserProxyOnNode, readMeetingBrowserTab, resolveMeetingBrowserNode, resolveMeetingBrowserNodeInfo } from "openclaw/plugin-sdk/meeting-runtime";
4
+ //#region extensions/google-meet/src/transports/chrome-browser-proxy.ts
5
+ async function resolveChromeNodeInfo(params) {
6
+ return await resolveMeetingBrowserNodeInfo({
7
+ ...params,
8
+ adapter: GOOGLE_MEET_BROWSER_NODE_ADAPTER
9
+ });
10
+ }
11
+ async function resolveChromeNode(params) {
12
+ return await resolveMeetingBrowserNode({
13
+ ...params,
14
+ adapter: GOOGLE_MEET_BROWSER_NODE_ADAPTER
15
+ });
16
+ }
17
+ async function callBrowserProxyOnNode(params) {
18
+ return await callMeetingBrowserProxyOnNode({
19
+ ...params,
20
+ adapter: GOOGLE_MEET_BROWSER_NODE_ADAPTER
21
+ });
22
+ }
23
+ const asBrowserTabs = asMeetingBrowserTabs;
24
+ const readBrowserTab = readMeetingBrowserTab;
25
+ //#endregion
26
+ //#region extensions/google-meet/src/transports/google-meet-urls.ts
27
+ function forceMeetEnglishUi(url) {
28
+ try {
29
+ const parsed = new URL(url);
30
+ parsed.searchParams.set("hl", "en");
31
+ return parsed.toString();
32
+ } catch {
33
+ return url;
34
+ }
35
+ }
36
+ function normalizeMeetUrlForReuse(url) {
37
+ if (!url) return;
38
+ try {
39
+ const parsed = new URL(url);
40
+ if (parsed.protocol !== "https:" || parsed.hostname.toLowerCase() !== "meet.google.com") return;
41
+ const match = parsed.pathname.match(/^\/([a-z]{3}-[a-z]{4}-[a-z]{3})(?:\/)?$/i);
42
+ if (!match?.[1]) return;
43
+ return `https://meet.google.com/${match[1].toLowerCase()}`;
44
+ } catch {
45
+ return;
46
+ }
47
+ }
48
+ function isSameMeetUrlForReuse(a, b) {
49
+ const normalizedA = normalizeMeetUrlForReuse(a);
50
+ const normalizedB = normalizeMeetUrlForReuse(b);
51
+ return Boolean(normalizedA && normalizedB && normalizedA === normalizedB);
52
+ }
53
+ function isEnglishMeetTab(url) {
54
+ if (!url) return false;
55
+ try {
56
+ const parsed = new URL(url);
57
+ return parsed.protocol === "https:" && parsed.hostname.toLowerCase() === "meet.google.com" && parsed.searchParams.get("hl")?.toLowerCase() === "en";
58
+ } catch {
59
+ return false;
60
+ }
61
+ }
62
+ function readMeetAuthUser(url) {
63
+ if (!url) return;
64
+ try {
65
+ return new URL(url).searchParams.get("authuser") ?? void 0;
66
+ } catch {
67
+ return;
68
+ }
69
+ }
70
+ function isRecoverableMeetTab(tab, url) {
71
+ if (url) return isSameMeetUrlForReuse(tab.url, url);
72
+ if (normalizeMeetUrlForReuse(tab.url)) return true;
73
+ return (tab.url ?? "").startsWith("https://accounts.google.com/") && /sign in|google accounts|meet/i.test(tab.title ?? "");
74
+ }
75
+ //#endregion
76
+ //#region extensions/google-meet/src/transports/chrome-create.ts
77
+ const GOOGLE_MEET_NEW_URL = "https://meet.google.com/new";
78
+ const GOOGLE_MEET_BROWSER_CREATE_TIMEOUT_MS = 6e4;
79
+ const GOOGLE_MEET_BROWSER_STEP_TIMEOUT_MS = 1e4;
80
+ const GOOGLE_MEET_BROWSER_NAVIGATION_RETRY_MS = 1e3;
81
+ const GOOGLE_MEET_BROWSER_POLL_MS = 500;
82
+ function formatBrowserAutomationError(error) {
83
+ if (error instanceof Error) return error.message;
84
+ try {
85
+ return JSON.stringify(error);
86
+ } catch {
87
+ return "unknown error";
88
+ }
89
+ }
90
+ function isBrowserNavigationInterruption(error) {
91
+ return /execution context was destroyed|navigation|target closed/i.test(formatBrowserAutomationError(error));
92
+ }
93
+ function isGoogleMeetCreateTab(tab) {
94
+ const url = tab.url ?? "";
95
+ if (/^https:\/\/meet\.google\.com\/(?:new|[a-z]{3}-[a-z]{4}-[a-z]{3})(?:$|[/?#])/i.test(url)) return true;
96
+ return url.startsWith("https://accounts.google.com/") && /sign in|google accounts|meet/i.test(tab.title ?? "");
97
+ }
98
+ async function findGoogleMeetCreateTab(params) {
99
+ return asBrowserTabs(await callBrowserProxyOnNode({
100
+ runtime: params.runtime,
101
+ nodeId: params.nodeId,
102
+ method: "GET",
103
+ path: "/tabs",
104
+ timeoutMs: params.timeoutMs
105
+ })).find(isGoogleMeetCreateTab);
106
+ }
107
+ async function focusBrowserTab(params) {
108
+ await callBrowserProxyOnNode({
109
+ runtime: params.runtime,
110
+ nodeId: params.nodeId,
111
+ method: "POST",
112
+ path: "/tabs/focus",
113
+ body: { targetId: params.targetId },
114
+ timeoutMs: params.timeoutMs
115
+ });
116
+ }
117
+ function readStringArray(value) {
118
+ return Array.isArray(value) ? value.filter((entry) => typeof entry === "string") : void 0;
119
+ }
120
+ function readBrowserManualAction(value) {
121
+ if (!value || typeof value !== "object") return;
122
+ const action = value;
123
+ return typeof action.reason === "string" && typeof action.message === "string" ? {
124
+ reason: action.reason,
125
+ message: action.message
126
+ } : void 0;
127
+ }
128
+ function readBrowserCreateResult(result) {
129
+ const record = result && typeof result === "object" ? result : {};
130
+ const nested = record.result && typeof record.result === "object" ? record.result : record;
131
+ return {
132
+ meetingUri: typeof nested.meetingUri === "string" ? nested.meetingUri : void 0,
133
+ browserUrl: typeof nested.browserUrl === "string" ? nested.browserUrl : void 0,
134
+ browserTitle: typeof nested.browserTitle === "string" ? nested.browserTitle : void 0,
135
+ manualAction: readBrowserManualAction(nested.manualAction),
136
+ notes: readStringArray(nested.notes),
137
+ retryAfterMs: typeof nested.retryAfterMs === "number" && Number.isFinite(nested.retryAfterMs) ? nested.retryAfterMs : void 0
138
+ };
139
+ }
140
+ const CREATE_MEET_FROM_BROWSER_SCRIPT = `async () => {
141
+ const meetUrlPattern = /^https:\\/\\/meet\\.google\\.com\\/[a-z]{3}-[a-z]{4}-[a-z]{3}(?:$|[/?#])/i;
142
+ const text = (node) => (node?.innerText || node?.textContent || "").trim();
143
+ const current = () => location.href;
144
+ const manualActionFor = (reason, message) => ({ reason, message });
145
+ const notes = [];
146
+ const findButton = (pattern) =>
147
+ [...document.querySelectorAll("button")].find((button) => {
148
+ const label = [
149
+ button.getAttribute("aria-label"),
150
+ button.getAttribute("data-tooltip"),
151
+ text(button),
152
+ ]
153
+ .filter(Boolean)
154
+ .join(" ");
155
+ return pattern.test(label) && !button.disabled;
156
+ });
157
+ const clickButton = (pattern, note) => {
158
+ const button = findButton(pattern);
159
+ if (!button) {
160
+ return false;
161
+ }
162
+ button.click();
163
+ notes.push(note);
164
+ return true;
165
+ };
166
+ if (!current().startsWith("https://meet.google.com/")) {
167
+ return {
168
+ manualAction: manualActionFor("google-login-required", "Sign in to Google in the OpenClaw browser profile, then retry meeting creation."),
169
+ browserUrl: current(),
170
+ browserTitle: document.title,
171
+ notes,
172
+ };
173
+ }
174
+ const href = current();
175
+ if (meetUrlPattern.test(href)) {
176
+ // The /new redirect keeps the hl=en param we open with; strip query/hash so the
177
+ // meeting link handed to users stays canonical instead of forcing English on them.
178
+ return { meetingUri: href.split(/[?#]/)[0], browserUrl: href, browserTitle: document.title, notes };
179
+ }
180
+ const pageText = text(document.body);
181
+ if (clickButton(/\\buse microphone\\b/i, "Accepted Meet microphone prompt with browser automation.")) {
182
+ return { browserUrl: href, browserTitle: document.title, notes, retryAfterMs: 1000 };
183
+ }
184
+ if (
185
+ clickButton(
186
+ /continue without microphone/i,
187
+ "Continued through Meet microphone prompt with browser automation.",
188
+ )
189
+ ) {
190
+ return { browserUrl: href, browserTitle: document.title, notes, retryAfterMs: 1000 };
191
+ }
192
+ if (/do you want people to hear you in the meeting/i.test(pageText)) {
193
+ return {
194
+ manualAction: manualActionFor("meet-audio-choice-required", "Meet is showing the microphone choice. Click Use microphone in the OpenClaw browser profile, then retry meeting creation."),
195
+ browserUrl: href,
196
+ browserTitle: document.title,
197
+ notes,
198
+ };
199
+ }
200
+ if (/allow.*(microphone|camera)|blocked.*(microphone|camera)|permission.*(microphone|camera)/i.test(pageText)) {
201
+ return {
202
+ manualAction: manualActionFor("meet-permission-required", "Allow microphone/camera permissions for Meet in the OpenClaw browser profile, then retry meeting creation."),
203
+ browserUrl: href,
204
+ browserTitle: document.title,
205
+ notes,
206
+ };
207
+ }
208
+ if (/couldn't create|unable to create/i.test(pageText)) {
209
+ return {
210
+ manualAction: manualActionFor("browser-control-unavailable", "Resolve the Google Meet page prompt in the OpenClaw browser profile, then retry meeting creation."),
211
+ browserUrl: href,
212
+ browserTitle: document.title,
213
+ notes,
214
+ };
215
+ }
216
+ if (location.hostname.toLowerCase() === "accounts.google.com" || /use your google account|to continue to google meet|choose an account|sign in to (join|continue)/i.test(pageText)) {
217
+ return {
218
+ manualAction: manualActionFor("google-login-required", "Sign in to Google in the OpenClaw browser profile, then retry meeting creation."),
219
+ browserUrl: href,
220
+ browserTitle: document.title,
221
+ notes,
222
+ };
223
+ }
224
+ return {
225
+ retryAfterMs: 500,
226
+ browserUrl: current(),
227
+ browserTitle: document.title,
228
+ notes,
229
+ };
230
+ }`;
231
+ async function createMeetWithBrowserProxyOnNode(params) {
232
+ const nodeId = await resolveChromeNode({
233
+ runtime: params.runtime,
234
+ requestedNode: params.config.chromeNode.node
235
+ });
236
+ const timeoutMs = Math.max(GOOGLE_MEET_BROWSER_CREATE_TIMEOUT_MS, params.config.chrome.joinTimeoutMs);
237
+ const stepTimeoutMs = Math.min(timeoutMs, GOOGLE_MEET_BROWSER_STEP_TIMEOUT_MS);
238
+ let openedByPlugin = false;
239
+ let tab = await findGoogleMeetCreateTab({
240
+ runtime: params.runtime,
241
+ nodeId,
242
+ timeoutMs: stepTimeoutMs
243
+ });
244
+ if (tab?.targetId) {
245
+ await focusBrowserTab({
246
+ runtime: params.runtime,
247
+ nodeId,
248
+ targetId: tab.targetId,
249
+ timeoutMs: stepTimeoutMs
250
+ });
251
+ const reusedUrl = tab.url ?? "";
252
+ const englishUrl = (/^https:\/\/meet\.google\.com\/new(?:$|[/?#])/i.test(reusedUrl) || reusedUrl.startsWith("https://accounts.google.com/")) && reusedUrl ? forceMeetEnglishUi(reusedUrl) : void 0;
253
+ if (englishUrl && englishUrl !== reusedUrl) tab = readBrowserTab(await callBrowserProxyOnNode({
254
+ runtime: params.runtime,
255
+ nodeId,
256
+ method: "POST",
257
+ path: "/navigate",
258
+ body: {
259
+ targetId: tab.targetId,
260
+ url: englishUrl
261
+ },
262
+ timeoutMs: stepTimeoutMs
263
+ })) ?? tab;
264
+ } else {
265
+ tab = readBrowserTab(await callBrowserProxyOnNode({
266
+ runtime: params.runtime,
267
+ nodeId,
268
+ method: "POST",
269
+ path: "/tabs/open",
270
+ body: { url: forceMeetEnglishUi(GOOGLE_MEET_NEW_URL) },
271
+ timeoutMs: stepTimeoutMs
272
+ }));
273
+ openedByPlugin = Boolean(tab?.targetId);
274
+ }
275
+ const targetId = tab?.targetId;
276
+ if (!targetId) throw new Error("Browser fallback opened Google Meet but did not return a targetId.");
277
+ const notes = /* @__PURE__ */ new Set();
278
+ let lastResult;
279
+ let lastError;
280
+ const deadline = Date.now() + timeoutMs;
281
+ while (Date.now() <= deadline) try {
282
+ const result = readBrowserCreateResult(await callBrowserProxyOnNode({
283
+ runtime: params.runtime,
284
+ nodeId,
285
+ method: "POST",
286
+ path: "/act",
287
+ body: {
288
+ kind: "evaluate",
289
+ targetId,
290
+ fn: CREATE_MEET_FROM_BROWSER_SCRIPT
291
+ },
292
+ timeoutMs: stepTimeoutMs
293
+ }));
294
+ lastResult = result;
295
+ for (const note of result.notes ?? []) notes.add(note);
296
+ if (result.meetingUri) return {
297
+ source: "browser",
298
+ nodeId,
299
+ targetId,
300
+ openedByPlugin,
301
+ meetingUri: result.meetingUri,
302
+ browserUrl: result.browserUrl,
303
+ browserTitle: result.browserTitle,
304
+ notes: [...notes]
305
+ };
306
+ if (result.manualAction) throw new GoogleMeetBrowserManualActionError({
307
+ manualAction: result.manualAction,
308
+ browser: {
309
+ nodeId,
310
+ targetId,
311
+ browserUrl: result.browserUrl,
312
+ browserTitle: result.browserTitle,
313
+ notes: [...notes]
314
+ }
315
+ });
316
+ await sleep(result.retryAfterMs ?? GOOGLE_MEET_BROWSER_POLL_MS);
317
+ } catch (error) {
318
+ lastError = error;
319
+ if (!isBrowserNavigationInterruption(error)) throw error;
320
+ await sleep(GOOGLE_MEET_BROWSER_NAVIGATION_RETRY_MS);
321
+ }
322
+ throw new Error(lastResult?.manualAction?.message ?? `Google Meet did not return a meeting URL from the browser create flow before timeout.${lastError ? ` Last browser automation error: ${formatBrowserAutomationError(lastError)}` : ""}`);
323
+ }
324
+ //#endregion
325
+ export { isSameMeetUrlForReuse as a, callBrowserProxyOnNode as c, isRecoverableMeetTab as i, resolveChromeNode as l, forceMeetEnglishUi as n, normalizeMeetUrlForReuse as o, isEnglishMeetTab as r, readMeetAuthUser as s, createMeetWithBrowserProxyOnNode as t, resolveChromeNodeInfo as u };