@openclaw/google-meet 2026.7.2-beta.7 → 2026.8.1-beta.2
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/chrome-create-PKAyxCiL.js +325 -0
- package/dist/{cli-DCcRyaMf.js → cli-o7RWVqDb.js} +166 -224
- package/dist/{config-BcaS1BDr.js → config-wBK8loBb.js} +97 -221
- package/dist/{create-af1MDlus.js → create-UHeaRzsH.js} +3 -14
- package/dist/google-meet-platform-adapter-C3J-iFRE.js +791 -0
- package/dist/google-meet-platform-constants-Bs5iAg3E.js +25 -0
- package/dist/index.js +185 -2639
- package/dist/meet-url-BP2Jlvjs.js +17 -0
- package/dist/node-host-BXtXbsMa.js +49 -0
- package/dist/node-invoke-policy-B0gOlLJR.js +24 -0
- package/dist/{oauth-DOfMKlGp.js → oauth-9sUJ68Mw.js} +3 -14
- package/dist/{chrome-create-DMKruRFW.js → plugin-helpers-CfeT1SYY.js} +335 -355
- package/dist/plugin-registration-rSOssQmm.js +141 -0
- package/dist/runtime-B8d1beHo.js +1308 -0
- package/openclaw.plugin.json +8 -47
- package/package.json +4 -4
|
@@ -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 };
|