@openclaw/google-meet 2026.7.2-beta.4 → 2026.7.2-beta.5
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-DkWXFsK2.js → chrome-create-DMKruRFW.js} +18 -17
- package/dist/{cli-CksOXL4n.js → cli-GeuAaEIy.js} +6 -6
- package/dist/cli-metadata.js +13 -0
- package/dist/cli-output-mode-BwH_6tH8.js +28 -0
- package/dist/{create-CNAkrEju.js → create-af1MDlus.js} +1 -1
- package/dist/index.js +68 -264
- package/package.json +4 -5
- package/npm-shrinkwrap.json +0 -157
|
@@ -687,8 +687,7 @@ const GOOGLE_MEET_BROWSER_NAVIGATION_RETRY_MS = 1e3;
|
|
|
687
687
|
const GOOGLE_MEET_BROWSER_POLL_MS = 500;
|
|
688
688
|
var GoogleMeetBrowserManualActionError = class extends Error {
|
|
689
689
|
constructor(payload) {
|
|
690
|
-
|
|
691
|
-
super(`${prefix}${payload.manualActionMessage}`);
|
|
690
|
+
super(`${payload.manualAction.reason}: ${payload.manualAction.message}`);
|
|
692
691
|
this.name = "GoogleMeetBrowserManualActionError";
|
|
693
692
|
this.payload = {
|
|
694
693
|
source: "browser",
|
|
@@ -738,6 +737,14 @@ async function focusBrowserTab(params) {
|
|
|
738
737
|
function readStringArray(value) {
|
|
739
738
|
return Array.isArray(value) ? value.filter((entry) => typeof entry === "string") : void 0;
|
|
740
739
|
}
|
|
740
|
+
function readBrowserManualAction(value) {
|
|
741
|
+
if (!value || typeof value !== "object") return;
|
|
742
|
+
const action = value;
|
|
743
|
+
return typeof action.reason === "string" && typeof action.message === "string" ? {
|
|
744
|
+
reason: action.reason,
|
|
745
|
+
message: action.message
|
|
746
|
+
} : void 0;
|
|
747
|
+
}
|
|
741
748
|
function readBrowserCreateResult(result) {
|
|
742
749
|
const record = result && typeof result === "object" ? result : {};
|
|
743
750
|
const nested = record.result && typeof record.result === "object" ? record.result : record;
|
|
@@ -745,8 +752,7 @@ function readBrowserCreateResult(result) {
|
|
|
745
752
|
meetingUri: typeof nested.meetingUri === "string" ? nested.meetingUri : void 0,
|
|
746
753
|
browserUrl: typeof nested.browserUrl === "string" ? nested.browserUrl : void 0,
|
|
747
754
|
browserTitle: typeof nested.browserTitle === "string" ? nested.browserTitle : void 0,
|
|
748
|
-
manualAction:
|
|
749
|
-
manualActionReason: typeof nested.manualActionReason === "string" ? nested.manualActionReason : void 0,
|
|
755
|
+
manualAction: readBrowserManualAction(nested.manualAction),
|
|
750
756
|
notes: readStringArray(nested.notes),
|
|
751
757
|
retryAfterMs: typeof nested.retryAfterMs === "number" && Number.isFinite(nested.retryAfterMs) ? nested.retryAfterMs : void 0
|
|
752
758
|
};
|
|
@@ -755,6 +761,7 @@ const CREATE_MEET_FROM_BROWSER_SCRIPT = `async () => {
|
|
|
755
761
|
const meetUrlPattern = /^https:\\/\\/meet\\.google\\.com\\/[a-z]{3}-[a-z]{4}-[a-z]{3}(?:$|[/?#])/i;
|
|
756
762
|
const text = (node) => (node?.innerText || node?.textContent || "").trim();
|
|
757
763
|
const current = () => location.href;
|
|
764
|
+
const manualActionFor = (reason, message) => ({ reason, message });
|
|
758
765
|
const notes = [];
|
|
759
766
|
const findButton = (pattern) =>
|
|
760
767
|
[...document.querySelectorAll("button")].find((button) => {
|
|
@@ -778,8 +785,7 @@ const CREATE_MEET_FROM_BROWSER_SCRIPT = `async () => {
|
|
|
778
785
|
};
|
|
779
786
|
if (!current().startsWith("https://meet.google.com/")) {
|
|
780
787
|
return {
|
|
781
|
-
|
|
782
|
-
manualAction: "Sign in to Google in the OpenClaw browser profile, then retry meeting creation.",
|
|
788
|
+
manualAction: manualActionFor("google-login-required", "Sign in to Google in the OpenClaw browser profile, then retry meeting creation."),
|
|
783
789
|
browserUrl: current(),
|
|
784
790
|
browserTitle: document.title,
|
|
785
791
|
notes,
|
|
@@ -805,8 +811,7 @@ const CREATE_MEET_FROM_BROWSER_SCRIPT = `async () => {
|
|
|
805
811
|
}
|
|
806
812
|
if (/do you want people to hear you in the meeting/i.test(pageText)) {
|
|
807
813
|
return {
|
|
808
|
-
|
|
809
|
-
manualAction: "Meet is showing the microphone choice. Click Use microphone in the OpenClaw browser profile, then retry meeting creation.",
|
|
814
|
+
manualAction: manualActionFor("meet-audio-choice-required", "Meet is showing the microphone choice. Click Use microphone in the OpenClaw browser profile, then retry meeting creation."),
|
|
810
815
|
browserUrl: href,
|
|
811
816
|
browserTitle: document.title,
|
|
812
817
|
notes,
|
|
@@ -814,8 +819,7 @@ const CREATE_MEET_FROM_BROWSER_SCRIPT = `async () => {
|
|
|
814
819
|
}
|
|
815
820
|
if (/allow.*(microphone|camera)|blocked.*(microphone|camera)|permission.*(microphone|camera)/i.test(pageText)) {
|
|
816
821
|
return {
|
|
817
|
-
|
|
818
|
-
manualAction: "Allow microphone/camera permissions for Meet in the OpenClaw browser profile, then retry meeting creation.",
|
|
822
|
+
manualAction: manualActionFor("meet-permission-required", "Allow microphone/camera permissions for Meet in the OpenClaw browser profile, then retry meeting creation."),
|
|
819
823
|
browserUrl: href,
|
|
820
824
|
browserTitle: document.title,
|
|
821
825
|
notes,
|
|
@@ -823,7 +827,7 @@ const CREATE_MEET_FROM_BROWSER_SCRIPT = `async () => {
|
|
|
823
827
|
}
|
|
824
828
|
if (/couldn't create|unable to create/i.test(pageText)) {
|
|
825
829
|
return {
|
|
826
|
-
manualAction: "Resolve the Google Meet page prompt in the OpenClaw browser profile, then retry meeting creation.",
|
|
830
|
+
manualAction: manualActionFor("browser-control-unavailable", "Resolve the Google Meet page prompt in the OpenClaw browser profile, then retry meeting creation."),
|
|
827
831
|
browserUrl: href,
|
|
828
832
|
browserTitle: document.title,
|
|
829
833
|
notes,
|
|
@@ -831,8 +835,7 @@ const CREATE_MEET_FROM_BROWSER_SCRIPT = `async () => {
|
|
|
831
835
|
}
|
|
832
836
|
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)) {
|
|
833
837
|
return {
|
|
834
|
-
|
|
835
|
-
manualAction: "Sign in to Google in the OpenClaw browser profile, then retry meeting creation.",
|
|
838
|
+
manualAction: manualActionFor("google-login-required", "Sign in to Google in the OpenClaw browser profile, then retry meeting creation."),
|
|
836
839
|
browserUrl: href,
|
|
837
840
|
browserTitle: document.title,
|
|
838
841
|
notes,
|
|
@@ -921,9 +924,7 @@ async function createMeetWithBrowserProxyOnNode(params) {
|
|
|
921
924
|
notes: [...notes]
|
|
922
925
|
};
|
|
923
926
|
if (result.manualAction) throw new GoogleMeetBrowserManualActionError({
|
|
924
|
-
|
|
925
|
-
manualActionReason: result.manualActionReason,
|
|
926
|
-
manualActionMessage: result.manualAction,
|
|
927
|
+
manualAction: result.manualAction,
|
|
927
928
|
browser: {
|
|
928
929
|
nodeId,
|
|
929
930
|
targetId,
|
|
@@ -938,7 +939,7 @@ async function createMeetWithBrowserProxyOnNode(params) {
|
|
|
938
939
|
if (!isBrowserNavigationInterruption(error)) throw error;
|
|
939
940
|
await sleep(GOOGLE_MEET_BROWSER_NAVIGATION_RETRY_MS);
|
|
940
941
|
}
|
|
941
|
-
throw new Error(lastResult?.manualAction ?? `Google Meet did not return a meeting URL from the browser create flow before timeout.${lastError ? ` Last browser automation error: ${formatBrowserAutomationError(lastError)}` : ""}`);
|
|
942
|
+
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)}` : ""}`);
|
|
942
943
|
}
|
|
943
944
|
//#endregion
|
|
944
945
|
export { endGoogleMeetActiveConference as _, isRecoverableMeetTab as a, readMeetAuthUser as c, resolveChromeNodeInfo as d, GOOGLE_MEET_NODE_COMMAND as f, createGoogleMeetSpace as g, fetchGoogleMeetAttendance as h, isEnglishMeetTab as i, callBrowserProxyOnNode as l, fetchGoogleMeetArtifacts as m, isGoogleMeetBrowserManualActionError as n, isSameMeetUrlForReuse as o, buildGoogleMeetPreflightReport as p, forceMeetEnglishUi as r, normalizeMeetUrlForReuse as s, createMeetWithBrowserProxyOnNode as t, resolveChromeNode as u, fetchGoogleMeetSpace as v, fetchLatestGoogleMeetConferenceRecord as y };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { n as hasCreateSpaceConfigInput, r as resolveCreateSpaceConfig } from "./create-
|
|
1
|
+
import { n as hasCreateSpaceConfigInput, r as resolveCreateSpaceConfig } from "./create-af1MDlus.js";
|
|
2
2
|
import { a as buildGoogleMeetCalendarDayWindow, i as resolveGoogleMeetGatewayOperationTimeoutMs, o as findGoogleMeetCalendarEvent, s as listGoogleMeetCalendarEvents } from "./config-BcaS1BDr.js";
|
|
3
|
-
import { _ as endGoogleMeetActiveConference, g as createGoogleMeetSpace, h as fetchGoogleMeetAttendance, m as fetchGoogleMeetArtifacts, p as buildGoogleMeetPreflightReport, v as fetchGoogleMeetSpace, y as fetchLatestGoogleMeetConferenceRecord } from "./chrome-create-
|
|
3
|
+
import { _ as endGoogleMeetActiveConference, g as createGoogleMeetSpace, h as fetchGoogleMeetAttendance, m as fetchGoogleMeetArtifacts, p as buildGoogleMeetPreflightReport, v as fetchGoogleMeetSpace, y as fetchLatestGoogleMeetConferenceRecord } from "./chrome-create-DMKruRFW.js";
|
|
4
4
|
import { buildGoogleMeetAuthUrl, createGoogleMeetOAuthState, createGoogleMeetPkce, exchangeGoogleMeetAuthCode, resolveGoogleMeetAccessToken, waitForGoogleMeetAuthCode } from "./oauth-DOfMKlGp.js";
|
|
5
5
|
import { callGatewayFromCli } from "openclaw/plugin-sdk/gateway-runtime";
|
|
6
6
|
import { clampTimerTimeoutMs, parseStrictNonNegativeInteger, parseStrictPositiveInteger } from "openclaw/plugin-sdk/number-runtime";
|
|
@@ -143,10 +143,10 @@ function writeDoctorStatus(status) {
|
|
|
143
143
|
writeStdoutLine("captioning: %s", formatBoolean(health?.captioning));
|
|
144
144
|
writeStdoutLine("transcript lines: %s", health?.transcriptLines ?? 0);
|
|
145
145
|
writeStdoutLine("last caption: %s", formatOptional(health?.lastCaptionAt));
|
|
146
|
-
writeStdoutLine("manual action: %s", formatBoolean(health?.
|
|
147
|
-
if (health?.
|
|
148
|
-
writeStdoutLine("manual reason: %s", formatOptional(health.
|
|
149
|
-
writeStdoutLine("manual message: %s", formatOptional(health.
|
|
146
|
+
writeStdoutLine("manual action: %s", formatBoolean(Boolean(health?.manualAction)));
|
|
147
|
+
if (health?.manualAction) {
|
|
148
|
+
writeStdoutLine("manual reason: %s", formatOptional(health.manualAction.reason));
|
|
149
|
+
writeStdoutLine("manual message: %s", formatOptional(health.manualAction.message));
|
|
150
150
|
}
|
|
151
151
|
writeStdoutLine("speech ready: %s", formatBoolean(health?.speechReady));
|
|
152
152
|
if (health?.speechReady === false) {
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { t as GOOGLE_MEET_CLI_DESCRIPTOR } from "./cli-output-mode-BwH_6tH8.js";
|
|
2
|
+
import { definePluginEntry } from "openclaw/plugin-sdk/plugin-entry";
|
|
3
|
+
//#region extensions/google-meet/cli-metadata.ts
|
|
4
|
+
var cli_metadata_default = definePluginEntry({
|
|
5
|
+
id: "google-meet",
|
|
6
|
+
name: "Google Meet",
|
|
7
|
+
description: "Google Meet CLI metadata",
|
|
8
|
+
register(api) {
|
|
9
|
+
api.registerCli(() => {}, { descriptors: [GOOGLE_MEET_CLI_DESCRIPTOR] });
|
|
10
|
+
}
|
|
11
|
+
});
|
|
12
|
+
//#endregion
|
|
13
|
+
export { cli_metadata_default as default };
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { getRootOptionAwareCommandPath } from "openclaw/plugin-sdk/cli-argv";
|
|
2
|
+
//#region extensions/google-meet/src/cli-output-mode.ts
|
|
3
|
+
const DEFAULT_JSON_COMMANDS = /* @__PURE__ */ new Set([
|
|
4
|
+
"join",
|
|
5
|
+
"status",
|
|
6
|
+
"test-listen",
|
|
7
|
+
"test-speech"
|
|
8
|
+
]);
|
|
9
|
+
function hasOption(argv, flag) {
|
|
10
|
+
for (const arg of argv.slice(2)) {
|
|
11
|
+
if (arg === "--") return false;
|
|
12
|
+
if (arg === flag || arg.startsWith(`${flag}=`)) return true;
|
|
13
|
+
}
|
|
14
|
+
return false;
|
|
15
|
+
}
|
|
16
|
+
/** Runtime probe commands emit JSON without requiring the shared `--json` option. */
|
|
17
|
+
function isGoogleMeetMachineOutput(params) {
|
|
18
|
+
const [, command] = getRootOptionAwareCommandPath(params.argv, 2);
|
|
19
|
+
return DEFAULT_JSON_COMMANDS.has(command ?? "") || command === "export" && hasOption(params.argv, "--dry-run");
|
|
20
|
+
}
|
|
21
|
+
const GOOGLE_MEET_CLI_DESCRIPTOR = {
|
|
22
|
+
name: "googlemeet",
|
|
23
|
+
description: "Join and manage Google Meet calls",
|
|
24
|
+
hasSubcommands: true,
|
|
25
|
+
machineOutput: isGoogleMeetMachineOutput
|
|
26
|
+
};
|
|
27
|
+
//#endregion
|
|
28
|
+
export { GOOGLE_MEET_CLI_DESCRIPTOR as t };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { g as createGoogleMeetSpace, t as createMeetWithBrowserProxyOnNode } from "./chrome-create-
|
|
1
|
+
import { g as createGoogleMeetSpace, t as createMeetWithBrowserProxyOnNode } from "./chrome-create-DMKruRFW.js";
|
|
2
2
|
import { resolveGoogleMeetAccessToken } from "./oauth-DOfMKlGp.js";
|
|
3
3
|
import { normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
4
4
|
//#region \0rolldown/runtime.js
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { a as buildGoogleMeetCalendarDayWindow, c as normalizeMeetUrl, i as resolveGoogleMeetGatewayOperationTimeoutMs, n as DEFAULT_GOOGLE_MEET_AUDIO_OUTPUT_COMMAND, o as findGoogleMeetCalendarEvent, r as resolveGoogleMeetConfig, s as listGoogleMeetCalendarEvents, t as DEFAULT_GOOGLE_MEET_AUDIO_INPUT_COMMAND } from "./config-BcaS1BDr.js";
|
|
2
|
-
import {
|
|
2
|
+
import { t as GOOGLE_MEET_CLI_DESCRIPTOR } from "./cli-output-mode-BwH_6tH8.js";
|
|
3
|
+
import { _ as endGoogleMeetActiveConference, a as isRecoverableMeetTab, c as readMeetAuthUser, d as resolveChromeNodeInfo, f as GOOGLE_MEET_NODE_COMMAND, h as fetchGoogleMeetAttendance, i as isEnglishMeetTab, l as callBrowserProxyOnNode, m as fetchGoogleMeetArtifacts, n as isGoogleMeetBrowserManualActionError, o as isSameMeetUrlForReuse, p as buildGoogleMeetPreflightReport, r as forceMeetEnglishUi, s as normalizeMeetUrlForReuse, t as createMeetWithBrowserProxyOnNode, u as resolveChromeNode, v as fetchGoogleMeetSpace, y as fetchLatestGoogleMeetConferenceRecord } from "./chrome-create-DMKruRFW.js";
|
|
3
4
|
import { optionalPositiveIntegerSchema, readPositiveIntegerParam } from "openclaw/plugin-sdk/channel-actions";
|
|
4
5
|
import { ErrorCodes, GatewayClient, callGatewayFromCli, errorShape, startGatewayClientWhenEventLoopReady } from "openclaw/plugin-sdk/gateway-runtime";
|
|
5
6
|
import { definePluginEntry } from "openclaw/plugin-sdk/plugin-entry";
|
|
@@ -9,10 +10,9 @@ import { jsonResult } from "openclaw/plugin-sdk/tool-results";
|
|
|
9
10
|
import { createMeetingTranscriptSourceProvider, resolveTranscriptsConfig } from "openclaw/plugin-sdk/transcripts";
|
|
10
11
|
import { isBlockedHostnameOrIp } from "openclaw/plugin-sdk/ssrf-runtime";
|
|
11
12
|
import { spawnSync } from "node:child_process";
|
|
12
|
-
import { MeetingSessionRuntime, addMeetingSetupCheck, createLocalMeetingRealtimeAudioTransport, createMeetingBrowserNodeInvokePolicy, createMeetingNodeHost, createMeetingRealtimeEngineBindings, createMeetingSession, createMeetingSetupStatus, createMeetingVoiceCallGateway, createNodeMeetingRealtimeAudioTransport, endMeetingVoiceCallGatewayCall, getMeetingVoiceCallGatewayCall, isMeetingVoiceCallMissingError, joinMeetingViaVoiceCallGateway, leaveMeetingWithBrowser, openMeetingWithBrowser, readMeetingTranscriptWithBrowser, recoverMeetingBrowserTab, resolveLocalMeetingBrowserRequest, speakMeetingViaVoiceCallGateway, startMeetingAgentRealtimeEngine, startMeetingRealtimeEngine } from "openclaw/plugin-sdk/meeting-runtime";
|
|
13
|
+
import { MeetingPlatformAdapter, MeetingSessionRuntime, addMeetingSetupCheck, createLocalMeetingRealtimeAudioTransport, createMeetingBrowserNodeInvokePolicy, createMeetingNodeHost, createMeetingRealtimeEngineBindings, createMeetingSession, createMeetingSetupStatus, createMeetingVoiceCallGateway, createNodeMeetingRealtimeAudioTransport, endMeetingVoiceCallGatewayCall, getMeetingVoiceCallGatewayCall, isMeetingVoiceCallMissingError, joinMeetingViaVoiceCallGateway, leaveMeetingWithBrowser, openMeetingWithBrowser, readMeetingTranscriptWithBrowser, recoverMeetingBrowserTab, resolveLocalMeetingBrowserRequest, speakMeetingViaVoiceCallGateway, startMeetingAgentRealtimeEngine, startMeetingRealtimeEngine } from "openclaw/plugin-sdk/meeting-runtime";
|
|
13
14
|
import { addTimerTimeoutGraceMs } from "openclaw/plugin-sdk/number-runtime";
|
|
14
15
|
import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
|
|
15
|
-
import { sleep } from "openclaw/plugin-sdk/runtime-env";
|
|
16
16
|
import { createLazyRuntimeModule } from "openclaw/plugin-sdk/lazy-runtime";
|
|
17
17
|
import { resolveDefaultAgentId } from "openclaw/plugin-sdk/agent-runtime";
|
|
18
18
|
import fs from "node:fs";
|
|
@@ -33,6 +33,7 @@ const GOOGLE_MEET_CAPTION_SETTLE_MS = 1e3;
|
|
|
33
33
|
function meetStatusScript(params) {
|
|
34
34
|
return `async () => {
|
|
35
35
|
const text = (node) => (node?.innerText || node?.textContent || "").trim();
|
|
36
|
+
const manualActionFor = (reason, message) => ({ reason, message });
|
|
36
37
|
const allowMicrophone = ${JSON.stringify(params.allowMicrophone)};
|
|
37
38
|
const captionSessionId = ${JSON.stringify(params.captionSessionId)};
|
|
38
39
|
const captureCaptions = ${JSON.stringify(params.captureCaptions)};
|
|
@@ -331,27 +332,17 @@ function meetStatusScript(params) {
|
|
|
331
332
|
const leaveReason = !inCall && /you left the meeting|you.?ve left the meeting|removed from the meeting|you were removed|call ended|meeting ended/i.test(pageText)
|
|
332
333
|
? pageText.match(/you left the meeting|you.?ve left the meeting|removed from the meeting|you were removed|call ended|meeting ended/i)?.[0]
|
|
333
334
|
: undefined;
|
|
334
|
-
let
|
|
335
|
-
let manualActionMessage;
|
|
335
|
+
let manualAction;
|
|
336
336
|
if (!inCall && (host === "accounts.google.com" || /use your google account|to continue to google meet|choose an account|sign in to (join|continue)/i.test(pageText))) {
|
|
337
|
-
|
|
338
|
-
manualActionMessage = "Sign in to Google in the OpenClaw browser profile, then retry the Meet join.";
|
|
337
|
+
manualAction = manualActionFor("google-login-required", "Sign in to Google in the OpenClaw browser profile, then retry the Meet join.");
|
|
339
338
|
} else if (!inCall && joinElsewhere) {
|
|
340
|
-
|
|
341
|
-
manualActionMessage = "Meet is already active in another tab or device. Leave that session or reuse an English-pinned tab before retrying.";
|
|
339
|
+
manualAction = manualActionFor("meet-session-conflict", "Meet is already active in another tab or device. Leave that session or reuse an English-pinned tab before retrying.");
|
|
342
340
|
} else if (!inCall && /asking to be let in|you.?ll join when someone lets you in|waiting to be let in|ask to join/i.test(pageText)) {
|
|
343
|
-
|
|
344
|
-
manualActionMessage = "Admit the OpenClaw browser participant in Google Meet, then retry speech.";
|
|
341
|
+
manualAction = manualActionFor("meet-admission-required", "Admit the OpenClaw browser participant in Google Meet, then retry speech.");
|
|
345
342
|
} else if (permissionNeeded) {
|
|
346
|
-
|
|
347
|
-
manualActionMessage = allowMicrophone
|
|
348
|
-
? "Allow microphone/camera/speaker permissions for Meet in the OpenClaw browser profile, then retry."
|
|
349
|
-
: "Join without microphone/camera permissions in the OpenClaw browser profile, then retry.";
|
|
343
|
+
manualAction = manualActionFor("meet-permission-required", allowMicrophone ? "Allow microphone/camera/speaker permissions for Meet in the OpenClaw browser profile, then retry." : "Join without microphone/camera permissions in the OpenClaw browser profile, then retry.");
|
|
350
344
|
} else if (!inCall && (allowMicrophone ? !microphoneChoice : !noMicrophoneChoice) && /do you want people to hear you in the meeting/i.test(pageText)) {
|
|
351
|
-
|
|
352
|
-
manualActionMessage = allowMicrophone
|
|
353
|
-
? "Meet is showing the microphone choice. Click Use microphone in the OpenClaw browser profile, then retry."
|
|
354
|
-
: "Meet is showing the microphone choice. Choose the no-microphone option in the OpenClaw browser profile, then retry.";
|
|
345
|
+
manualAction = manualActionFor("meet-audio-choice-required", allowMicrophone ? "Meet is showing the microphone choice. Click Use microphone in the OpenClaw browser profile, then retry." : "Meet is showing the microphone choice. Choose the no-microphone option in the OpenClaw browser profile, then retry.");
|
|
355
346
|
}
|
|
356
347
|
return JSON.stringify({
|
|
357
348
|
clickedJoin: Boolean(join),
|
|
@@ -370,9 +361,7 @@ function meetStatusScript(params) {
|
|
|
370
361
|
audioOutputRouted,
|
|
371
362
|
audioOutputDeviceLabel,
|
|
372
363
|
audioOutputRouteError,
|
|
373
|
-
|
|
374
|
-
manualActionReason,
|
|
375
|
-
manualActionMessage,
|
|
364
|
+
manualAction,
|
|
376
365
|
title: document.title,
|
|
377
366
|
url: pageUrl,
|
|
378
367
|
notes
|
|
@@ -519,42 +508,6 @@ function prefixDtmfWait(sequence, delayMs) {
|
|
|
519
508
|
}
|
|
520
509
|
//#endregion
|
|
521
510
|
//#region extensions/google-meet/src/transports/google-meet-platform-adapter.ts
|
|
522
|
-
function isGoogleMeetTalkBackMode$1(mode) {
|
|
523
|
-
return mode === "agent" || mode === "bidi";
|
|
524
|
-
}
|
|
525
|
-
function parseMeetBrowserStatus(result) {
|
|
526
|
-
const raw = (result && typeof result === "object" ? result : {}).result;
|
|
527
|
-
if (typeof raw !== "string" || !raw.trim()) return;
|
|
528
|
-
let parsed;
|
|
529
|
-
try {
|
|
530
|
-
parsed = JSON.parse(raw);
|
|
531
|
-
} catch {
|
|
532
|
-
throw new Error("Google Meet browser status JSON is malformed.");
|
|
533
|
-
}
|
|
534
|
-
return {
|
|
535
|
-
inCall: parsed.inCall,
|
|
536
|
-
micMuted: parsed.micMuted,
|
|
537
|
-
lobbyWaiting: parsed.lobbyWaiting,
|
|
538
|
-
leaveReason: parsed.leaveReason,
|
|
539
|
-
captioning: parsed.captioning,
|
|
540
|
-
captionsEnabledAttempted: parsed.captionsEnabledAttempted,
|
|
541
|
-
transcriptLines: parsed.transcriptLines,
|
|
542
|
-
lastCaptionAt: parsed.lastCaptionAt,
|
|
543
|
-
lastCaptionSpeaker: parsed.lastCaptionSpeaker,
|
|
544
|
-
lastCaptionText: parsed.lastCaptionText,
|
|
545
|
-
recentTranscript: parsed.recentTranscript,
|
|
546
|
-
audioOutputRouted: parsed.audioOutputRouted,
|
|
547
|
-
audioOutputDeviceLabel: parsed.audioOutputDeviceLabel,
|
|
548
|
-
audioOutputRouteError: parsed.audioOutputRouteError,
|
|
549
|
-
manualActionRequired: parsed.manualActionRequired,
|
|
550
|
-
manualActionReason: parsed.manualActionReason,
|
|
551
|
-
manualActionMessage: parsed.manualActionMessage,
|
|
552
|
-
browserUrl: parsed.url,
|
|
553
|
-
browserTitle: parsed.title,
|
|
554
|
-
status: "browser-control",
|
|
555
|
-
notes: Array.isArray(parsed.notes) ? parsed.notes.filter((note) => typeof note === "string") : void 0
|
|
556
|
-
};
|
|
557
|
-
}
|
|
558
511
|
function parsePermissionGrantNotes(result) {
|
|
559
512
|
const record = result && typeof result === "object" ? result : {};
|
|
560
513
|
const unsupportedPermissions = Array.isArray(record.unsupportedPermissions) ? record.unsupportedPermissions.filter((value) => typeof value === "string") : [];
|
|
@@ -562,63 +515,19 @@ function parsePermissionGrantNotes(result) {
|
|
|
562
515
|
if (unsupportedPermissions.includes("speakerSelection")) notes.push("Chrome did not accept the optional Meet speaker-selection permission.");
|
|
563
516
|
return notes;
|
|
564
517
|
}
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
}
|
|
577
|
-
if (!parsed || typeof parsed !== "object") throw new Error("Google Meet transcript payload is invalid.");
|
|
578
|
-
const payload = parsed;
|
|
579
|
-
const droppedLines = typeof payload.droppedLines === "number" && Number.isSafeInteger(payload.droppedLines) ? Math.max(0, payload.droppedLines) : 0;
|
|
580
|
-
const lines = Array.isArray(payload.lines) ? payload.lines.flatMap((value) => {
|
|
581
|
-
if (!value || typeof value !== "object") return [];
|
|
582
|
-
const line = value;
|
|
583
|
-
if (typeof line.text !== "string" || !line.text.trim()) return [];
|
|
584
|
-
return [{
|
|
585
|
-
...typeof line.at === "string" ? { at: line.at } : {},
|
|
586
|
-
...typeof line.speaker === "string" ? { speaker: line.speaker } : {},
|
|
587
|
-
text: line.text
|
|
588
|
-
}];
|
|
589
|
-
}) : [];
|
|
590
|
-
return {
|
|
591
|
-
droppedLines,
|
|
592
|
-
...typeof payload.epoch === "string" ? { epoch: payload.epoch } : {},
|
|
593
|
-
lines,
|
|
594
|
-
...typeof payload.urlMatched === "boolean" ? { urlMatched: payload.urlMatched } : {},
|
|
595
|
-
...typeof payload.sessionMatched === "boolean" ? { sessionMatched: payload.sessionMatched } : {}
|
|
596
|
-
};
|
|
597
|
-
}
|
|
598
|
-
function parseMeetLeaveResult(result) {
|
|
599
|
-
const raw = (result && typeof result === "object" ? result : {}).result;
|
|
600
|
-
if (typeof raw !== "string" || !raw.trim()) return { departed: false };
|
|
601
|
-
try {
|
|
602
|
-
const parsed = JSON.parse(raw);
|
|
603
|
-
const leaveAction = parsed.leaveAction === "leave" || parsed.leaveAction === "confirm" ? parsed.leaveAction : void 0;
|
|
604
|
-
return {
|
|
605
|
-
departed: parsed.departed === true,
|
|
606
|
-
...leaveAction ? { leaveAction } : {},
|
|
607
|
-
...typeof parsed.urlMatched === "boolean" ? { urlMatched: parsed.urlMatched } : {}
|
|
608
|
-
};
|
|
609
|
-
} catch {
|
|
610
|
-
return { departed: false };
|
|
611
|
-
}
|
|
612
|
-
}
|
|
613
|
-
function classifyMeetManualAction(health) {
|
|
614
|
-
if (!health.manualActionRequired || !health.manualActionReason || !health.manualActionMessage) return;
|
|
615
|
-
return {
|
|
616
|
-
category: health.manualActionReason === "google-login-required" ? "login-required" : health.manualActionReason === "meet-admission-required" ? "admission-required" : health.manualActionReason === "meet-permission-required" ? "permission-required" : health.manualActionReason === "meet-audio-choice-required" ? "audio-choice-required" : health.manualActionReason === "meet-locale-required" ? "locale-required" : health.manualActionReason === "meet-session-conflict" ? "session-conflict" : health.manualActionReason === "browser-control-unavailable" ? "browser-control-unavailable" : "custom",
|
|
617
|
-
reason: health.manualActionReason,
|
|
618
|
-
message: health.manualActionMessage
|
|
619
|
-
};
|
|
518
|
+
const manualActionCategories = /* @__PURE__ */ new Map([
|
|
519
|
+
["browser-control-unavailable", "browser-control-unavailable"],
|
|
520
|
+
["google-login-required", "login-required"],
|
|
521
|
+
["meet-admission-required", "admission-required"],
|
|
522
|
+
["meet-audio-choice-required", "audio-choice-required"],
|
|
523
|
+
["meet-locale-required", "locale-required"],
|
|
524
|
+
["meet-permission-required", "permission-required"],
|
|
525
|
+
["meet-session-conflict", "session-conflict"]
|
|
526
|
+
]);
|
|
527
|
+
function classifyManualActionReason(reason) {
|
|
528
|
+
return manualActionCategories.get(reason) ?? "custom";
|
|
620
529
|
}
|
|
621
|
-
const GOOGLE_MEET_PLATFORM_ADAPTER = {
|
|
530
|
+
const GOOGLE_MEET_PLATFORM_ADAPTER = MeetingPlatformAdapter.create({
|
|
622
531
|
id: "google-meet",
|
|
623
532
|
displayName: "Google Meet",
|
|
624
533
|
browserLabel: "Meet",
|
|
@@ -661,28 +570,24 @@ const GOOGLE_MEET_PLATFORM_ADAPTER = {
|
|
|
661
570
|
}
|
|
662
571
|
},
|
|
663
572
|
browser: {
|
|
664
|
-
allowsMicrophone:
|
|
573
|
+
allowsMicrophone: MeetingPlatformAdapter.isTalkBackMode,
|
|
665
574
|
buildStatusJoinScript: (params) => meetStatusScript({
|
|
666
|
-
allowMicrophone:
|
|
575
|
+
allowMicrophone: MeetingPlatformAdapter.isTalkBackMode(params.mode),
|
|
667
576
|
autoJoin: params.autoJoin,
|
|
668
577
|
captionSessionId: params.meetingSessionId || void 0,
|
|
669
578
|
captureCaptions: params.captureCaptions,
|
|
670
579
|
guestName: params.guestName,
|
|
671
580
|
readOnly: params.readOnly
|
|
672
581
|
}),
|
|
673
|
-
parseStatus: parseMeetBrowserStatus,
|
|
674
|
-
classifyManualAction: classifyMeetManualAction,
|
|
675
582
|
browserControlUnavailable: () => ({
|
|
676
583
|
category: "browser-control-unavailable",
|
|
677
584
|
reason: "browser-control-unavailable",
|
|
678
585
|
message: "Open the OpenClaw browser profile, finish Google Meet login, admission, or permission prompts, then retry."
|
|
679
586
|
}),
|
|
680
587
|
buildLeaveScript: meetLeaveScript,
|
|
681
|
-
parseLeaveResult: parseMeetLeaveResult,
|
|
682
588
|
captions: {
|
|
683
589
|
enabled: (_mode) => true,
|
|
684
|
-
buildTranscriptScript: ({ finalize, meetingSessionId, meetingUrl }) => meetTranscriptScript(meetingUrl, meetingSessionId, finalize)
|
|
685
|
-
parseTranscript: parseMeetTranscriptSnapshot
|
|
590
|
+
buildTranscriptScript: ({ finalize, meetingSessionId, meetingUrl }) => meetTranscriptScript(meetingUrl, meetingSessionId, finalize)
|
|
686
591
|
},
|
|
687
592
|
permissions: ({ allowMicrophone }) => allowMicrophone ? {
|
|
688
593
|
origin: "https://meet.google.com",
|
|
@@ -708,8 +613,16 @@ const GOOGLE_MEET_PLATFORM_ADAPTER = {
|
|
|
708
613
|
pin,
|
|
709
614
|
dtmfSequence: params.dtmfSequence || params.defaultDtmfSequence ? rawDtmfSequence : prefixDtmfWait(rawDtmfSequence, params.dtmfDelayMs)
|
|
710
615
|
};
|
|
711
|
-
} }
|
|
712
|
-
|
|
616
|
+
} },
|
|
617
|
+
parsing: {
|
|
618
|
+
classifyManualActionReason,
|
|
619
|
+
displayName: "Meet",
|
|
620
|
+
invalidTranscriptMessage: "Google Meet transcript payload is invalid.",
|
|
621
|
+
malformedStatusMessage: "Google Meet browser status JSON is malformed.",
|
|
622
|
+
malformedTranscriptMessage: "Google Meet transcript JSON is malformed.",
|
|
623
|
+
statusFields: (parsed) => ({ leaveReason: typeof parsed.leaveReason === "string" ? parsed.leaveReason : void 0 })
|
|
624
|
+
}
|
|
625
|
+
});
|
|
713
626
|
//#endregion
|
|
714
627
|
//#region extensions/google-meet/src/node-host.ts
|
|
715
628
|
function assertBlackHoleAvailable(timeoutMs) {
|
|
@@ -779,131 +692,30 @@ function createGoogleMeetChromeNodeInvokePolicy(config) {
|
|
|
779
692
|
}
|
|
780
693
|
//#endregion
|
|
781
694
|
//#region extensions/google-meet/src/runtime-probes.ts
|
|
782
|
-
function resolveMode$1(request, config) {
|
|
783
|
-
return request.mode === "realtime" ? "agent" : request.mode ?? config.defaultMode;
|
|
784
|
-
}
|
|
785
695
|
function resolveProbeTimeoutMs(input, fallback) {
|
|
786
696
|
if (input === void 0) return Math.min(Math.max(fallback, 1), 12e4);
|
|
787
697
|
if (!Number.isFinite(input) || input <= 0) throw new Error("timeoutMs must be a positive number");
|
|
788
698
|
return Math.min(Math.trunc(input), 12e4);
|
|
789
699
|
}
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
...resolved,
|
|
810
|
-
message: request.message ?? "Say exactly: Google Meet speech test complete."
|
|
811
|
-
});
|
|
812
|
-
const baseline = existing?.id === result.session.id ? existingBaseline : {
|
|
813
|
-
outputBytes: 0,
|
|
814
|
-
outputGeneration: 0
|
|
815
|
-
};
|
|
816
|
-
let health = result.session.chrome?.health;
|
|
817
|
-
const verified = () => (health?.lastOutputBytes ?? 0) > baseline.outputBytes && (health?.outputGeneration ?? 0) > baseline.outputGeneration && health?.verifiedOutputGeneration === health?.outputGeneration;
|
|
818
|
-
const shouldWait = result.spoken === true && health?.manualActionRequired !== true && context.hasHealthHandle(result.session.id);
|
|
819
|
-
if (shouldWait && !verified()) {
|
|
820
|
-
const deadline = Date.now() + Math.min(context.config.chrome.joinTimeoutMs, 5e3);
|
|
821
|
-
while (Date.now() < deadline) {
|
|
822
|
-
await sleep(100);
|
|
823
|
-
context.refreshHealth(result.session.id);
|
|
824
|
-
health = result.session.chrome?.health;
|
|
825
|
-
if (verified()) break;
|
|
826
|
-
}
|
|
827
|
-
}
|
|
828
|
-
const speechOutputVerified = verified();
|
|
829
|
-
return {
|
|
830
|
-
createdSession: !before.has(result.session.id),
|
|
831
|
-
inCall: health?.inCall,
|
|
832
|
-
manualActionRequired: health?.manualActionRequired,
|
|
833
|
-
manualActionReason: health?.manualActionReason,
|
|
834
|
-
manualActionMessage: health?.manualActionMessage,
|
|
835
|
-
spoken: result.spoken ?? false,
|
|
836
|
-
speechOutputVerified,
|
|
837
|
-
speechOutputTimedOut: shouldWait && !speechOutputVerified,
|
|
838
|
-
speechReady: health?.speechReady,
|
|
839
|
-
speechBlockedReason: health?.speechBlockedReason,
|
|
840
|
-
speechBlockedMessage: health?.speechBlockedMessage,
|
|
841
|
-
audioOutputActive: health?.audioOutputActive,
|
|
842
|
-
lastOutputBytes: health?.lastOutputBytes,
|
|
843
|
-
outputLoopbackSignalBytes: health?.outputLoopbackSignalBytes,
|
|
844
|
-
lastOutputLoopbackAt: health?.lastOutputLoopbackAt,
|
|
845
|
-
lastOutputLoopbackCorrelation: health?.lastOutputLoopbackCorrelation,
|
|
846
|
-
lastOutputLoopbackRms: health?.lastOutputLoopbackRms,
|
|
847
|
-
lastOutputLoopbackPeak: health?.lastOutputLoopbackPeak,
|
|
848
|
-
outputGeneration: health?.outputGeneration,
|
|
849
|
-
verifiedOutputGeneration: health?.verifiedOutputGeneration,
|
|
850
|
-
session: result.session
|
|
851
|
-
};
|
|
852
|
-
}
|
|
853
|
-
async function testGoogleMeetListening(context, request) {
|
|
854
|
-
const requestedMode = request.mode ? resolveMode$1(request, context.config) : void 0;
|
|
855
|
-
if (requestedMode === "agent" || requestedMode === "bidi") throw new Error("test_listen requires mode: transcribe; use test_speech for talk-back sessions.");
|
|
856
|
-
const resolved = {
|
|
857
|
-
url: normalizeMeetUrl(request.url),
|
|
858
|
-
transport: request.transport ?? context.config.defaultTransport,
|
|
859
|
-
mode: "transcribe",
|
|
860
|
-
agentId: context.resolveAgentId(request)
|
|
861
|
-
};
|
|
862
|
-
if (resolved.transport === "twilio") throw new Error("test_listen supports chrome or chrome-node transports");
|
|
863
|
-
const beforeSessions = context.list();
|
|
864
|
-
const before = new Set(beforeSessions.map((session) => session.id));
|
|
865
|
-
const existing = beforeSessions.find((session) => context.isReusable(session, resolved));
|
|
866
|
-
const start = {
|
|
867
|
-
lines: existing?.chrome?.health?.transcriptLines ?? 0,
|
|
868
|
-
at: existing?.chrome?.health?.lastCaptionAt,
|
|
869
|
-
text: existing?.chrome?.health?.lastCaptionText
|
|
870
|
-
};
|
|
871
|
-
const result = await context.join({
|
|
872
|
-
...request,
|
|
873
|
-
...resolved,
|
|
874
|
-
message: void 0
|
|
875
|
-
});
|
|
876
|
-
let health = result.session.chrome?.health;
|
|
877
|
-
const advanced = () => (health?.transcriptLines ?? 0) > (existing?.id === result.session.id ? start.lines : 0) || Boolean(health?.lastCaptionAt && health.lastCaptionAt !== start.at) || Boolean(health?.lastCaptionText && health.lastCaptionText !== start.text);
|
|
878
|
-
const shouldWait = health?.manualActionRequired !== true && Boolean((result.session.transport === "chrome" || result.session.transport === "chrome-node") && result.session.chrome?.launched);
|
|
879
|
-
if (shouldWait && !advanced()) {
|
|
880
|
-
const deadline = Date.now() + resolveProbeTimeoutMs(request.timeoutMs, context.config.chrome.joinTimeoutMs);
|
|
881
|
-
while (Date.now() < deadline) {
|
|
882
|
-
await sleep(250);
|
|
883
|
-
await context.refreshCaptionHealth(result.session);
|
|
884
|
-
health = result.session.chrome?.health;
|
|
885
|
-
if (health?.manualActionRequired || advanced()) break;
|
|
886
|
-
}
|
|
887
|
-
}
|
|
888
|
-
const listenVerified = advanced();
|
|
889
|
-
return {
|
|
890
|
-
createdSession: !before.has(result.session.id),
|
|
891
|
-
inCall: health?.inCall,
|
|
892
|
-
manualActionRequired: health?.manualActionRequired,
|
|
893
|
-
manualActionReason: health?.manualActionReason,
|
|
894
|
-
manualActionMessage: health?.manualActionMessage,
|
|
895
|
-
listenVerified,
|
|
896
|
-
listenTimedOut: shouldWait && !listenVerified && health?.manualActionRequired !== true,
|
|
897
|
-
captioning: health?.captioning,
|
|
898
|
-
captionsEnabledAttempted: health?.captionsEnabledAttempted,
|
|
899
|
-
transcriptLines: health?.transcriptLines,
|
|
900
|
-
lastCaptionAt: health?.lastCaptionAt,
|
|
901
|
-
lastCaptionSpeaker: health?.lastCaptionSpeaker,
|
|
902
|
-
lastCaptionText: health?.lastCaptionText,
|
|
903
|
-
recentTranscript: health?.recentTranscript,
|
|
904
|
-
session: result.session
|
|
905
|
-
};
|
|
906
|
-
}
|
|
700
|
+
const probes = MeetingPlatformAdapter.createRuntimeProbes({
|
|
701
|
+
defaultSpeechMessage: "Say exactly: Google Meet speech test complete.",
|
|
702
|
+
invalidRequest: (message) => new Error(message),
|
|
703
|
+
resolveTimeoutMs: resolveProbeTimeoutMs,
|
|
704
|
+
shouldWaitForListening: (session) => Boolean((session.transport === "chrome" || session.transport === "chrome-node") && session.chrome?.launched),
|
|
705
|
+
talkBackMode: MeetingPlatformAdapter.isTalkBackMode,
|
|
706
|
+
normalizeUrl: normalizeMeetUrl,
|
|
707
|
+
resolveRequestMode: (mode) => mode === "realtime" ? "agent" : mode,
|
|
708
|
+
defaultTransport: (config) => config.defaultTransport,
|
|
709
|
+
validateListeningTransport: (transport) => {
|
|
710
|
+
if (transport === "twilio") throw new Error("test_listen supports chrome or chrome-node transports");
|
|
711
|
+
},
|
|
712
|
+
resolveSpeechTimeoutMs: (_request, config) => Math.min(config.chrome.joinTimeoutMs, 5e3),
|
|
713
|
+
refreshCaptionHealth: async (context, session) => await context.refreshCaptionHealth(session),
|
|
714
|
+
speechModeError: "test_speech requires mode: agent or bidi; use join mode: transcribe for observe-only sessions.",
|
|
715
|
+
listeningModeError: "test_listen requires mode: transcribe; use test_speech for talk-back sessions."
|
|
716
|
+
});
|
|
717
|
+
const testGoogleMeetListening = probes.testListening;
|
|
718
|
+
const testGoogleMeetSpeech = probes.testSpeech;
|
|
907
719
|
//#endregion
|
|
908
720
|
//#region extensions/google-meet/src/runtime-session.ts
|
|
909
721
|
function resolveTransport(input, config) {
|
|
@@ -980,7 +792,7 @@ function getGoogleMeetSetupStatus(config, options) {
|
|
|
980
792
|
const fullConfig = asRecord(options?.fullConfig);
|
|
981
793
|
const mode = options?.mode ?? config.defaultMode;
|
|
982
794
|
const transport = options?.transport ?? config.defaultTransport;
|
|
983
|
-
const needsChromeRealtimeAudio = (mode
|
|
795
|
+
const needsChromeRealtimeAudio = MeetingPlatformAdapter.isTalkBackMode(mode) && (transport === "chrome" || transport === "chrome-node");
|
|
984
796
|
const pluginEntries = asRecord(asRecord(fullConfig.plugins).entries);
|
|
985
797
|
const pluginAllow = asRecord(fullConfig.plugins).allow;
|
|
986
798
|
const voiceCallEntry = asRecord(pluginEntries["voice-call"]);
|
|
@@ -1091,7 +903,7 @@ async function assertBlackHole2chAvailable(params) {
|
|
|
1091
903
|
}
|
|
1092
904
|
async function launchChromeMeet(params) {
|
|
1093
905
|
const checkRealtimeAudioPrerequisites = async () => {
|
|
1094
|
-
if (!
|
|
906
|
+
if (!MeetingPlatformAdapter.isTalkBackMode(params.mode)) return;
|
|
1095
907
|
await assertBlackHole2chAvailable({
|
|
1096
908
|
runtime: params.runtime,
|
|
1097
909
|
timeoutMs: Math.min(params.config.chrome.joinTimeoutMs, 1e4)
|
|
@@ -1102,7 +914,7 @@ async function launchChromeMeet(params) {
|
|
|
1102
914
|
}
|
|
1103
915
|
};
|
|
1104
916
|
const startRealtimeAudioBridge = async () => {
|
|
1105
|
-
if (!
|
|
917
|
+
if (!MeetingPlatformAdapter.isTalkBackMode(params.mode)) return;
|
|
1106
918
|
if (params.config.chrome.audioBridgeCommand) {
|
|
1107
919
|
if (params.mode === "agent") throw new Error("Chrome agent mode requires chrome.audioInputCommand and chrome.audioOutputCommand so OpenClaw can run STT and regular TTS directly.");
|
|
1108
920
|
const bridge = await params.runtime.system.runCommandWithTimeout(params.config.chrome.audioBridgeCommand, { timeoutMs: params.config.chrome.joinTimeoutMs });
|
|
@@ -1174,7 +986,7 @@ async function launchChromeMeet(params) {
|
|
|
1174
986
|
url: params.url
|
|
1175
987
|
}
|
|
1176
988
|
});
|
|
1177
|
-
const audioBridge =
|
|
989
|
+
const audioBridge = MeetingPlatformAdapter.isTalkBackMode(params.mode) && result.browser?.inCall === true && result.browser.micMuted === false && result.browser.manualAction === void 0 ? await startRealtimeAudioBridge() : void 0;
|
|
1178
990
|
return {
|
|
1179
991
|
...result,
|
|
1180
992
|
audioBridge
|
|
@@ -1346,7 +1158,7 @@ async function launchChromeMeetOnNode(params) {
|
|
|
1346
1158
|
meetingSessionId: params.meetingSessionId,
|
|
1347
1159
|
url: params.url
|
|
1348
1160
|
});
|
|
1349
|
-
if (params.config.chrome.launch &&
|
|
1161
|
+
if (params.config.chrome.launch && MeetingPlatformAdapter.isTalkBackMode(params.mode) && (browserControl.browser?.inCall !== true || browserControl.browser.micMuted !== false || browserControl.browser.manualAction)) return {
|
|
1350
1162
|
nodeId,
|
|
1351
1163
|
launched: browserControl.launched,
|
|
1352
1164
|
browser: browserControl.browser,
|
|
@@ -1636,7 +1448,6 @@ var GoogleMeetRuntime = class {
|
|
|
1636
1448
|
speech: {
|
|
1637
1449
|
audioBridgeUnavailable: "Realtime speech requires an active Chrome audio bridge.",
|
|
1638
1450
|
browserUnverified: "Google Meet browser state has not been verified yet.",
|
|
1639
|
-
manualActionFallback: "Resolve the Google Meet browser prompt before asking OpenClaw to speak.",
|
|
1640
1451
|
microphoneMuted: "Turn on the OpenClaw Google Meet microphone before asking OpenClaw to speak.",
|
|
1641
1452
|
microphoneMutedReason: "meet-microphone-muted",
|
|
1642
1453
|
notInCall: "Google Meet has not reported that the browser participant is in the call.",
|
|
@@ -1659,7 +1470,7 @@ var GoogleMeetRuntime = class {
|
|
|
1659
1470
|
}),
|
|
1660
1471
|
resolveSpeechInstructions: (request) => request.message ?? params.config.realtime.introMessage,
|
|
1661
1472
|
isBrowserTransport,
|
|
1662
|
-
isTalkBackMode:
|
|
1473
|
+
isTalkBackMode: (mode) => MeetingPlatformAdapter.isTalkBackMode(mode),
|
|
1663
1474
|
isTranscribeMode: (mode) => mode === "transcribe",
|
|
1664
1475
|
sameMeetingUrl: (left, right) => adapter.urls.isSameMeeting(left, right),
|
|
1665
1476
|
normalizeMeetingUrlForReuse: (url) => adapter.urls.normalizeForReuse(url),
|
|
@@ -1809,7 +1620,7 @@ var GoogleMeetRuntime = class {
|
|
|
1809
1620
|
};
|
|
1810
1621
|
const handles = this.#attachChromeAudioBridge(session, result.audioBridge);
|
|
1811
1622
|
if (handles) context.attachRuntimeHandles(session, handles);
|
|
1812
|
-
session.notes.push(result.audioBridge ? session.transport === "chrome-node" ? "Chrome node transport joins as the signed-in Google profile on the selected node and routes realtime audio through the node bridge." : "Chrome transport joins as the signed-in Google profile and routes realtime audio through the configured bridge." :
|
|
1623
|
+
session.notes.push(result.audioBridge ? session.transport === "chrome-node" ? "Chrome node transport joins as the signed-in Google profile on the selected node and routes realtime audio through the node bridge." : "Chrome transport joins as the signed-in Google profile and routes realtime audio through the configured bridge." : MeetingPlatformAdapter.isTalkBackMode(session.mode) ? "Chrome transport joins as the signed-in Google profile and expects BlackHole 2ch audio routing." : "Chrome transport joins as the signed-in Google profile without starting the realtime audio bridge.");
|
|
1813
1624
|
this.#sessions.refreshSpeechReadiness(session);
|
|
1814
1625
|
return {};
|
|
1815
1626
|
}
|
|
@@ -1835,7 +1646,7 @@ var GoogleMeetRuntime = class {
|
|
|
1835
1646
|
...request.requesterSessionKey ? { requesterSessionKey: request.requesterSessionKey } : {},
|
|
1836
1647
|
agentId: delegatedAgentId,
|
|
1837
1648
|
sessionKey: delegatedAgentId ? `agent:${delegatedAgentId}:google-meet:${session.id}` : `voice:google-meet:${session.id}`,
|
|
1838
|
-
message:
|
|
1649
|
+
message: MeetingPlatformAdapter.isTalkBackMode(session.mode) ? request.message ?? this.params.config.voiceCall.introMessage ?? this.params.config.realtime.introMessage : void 0
|
|
1839
1650
|
}) : void 0;
|
|
1840
1651
|
session.twilio = {
|
|
1841
1652
|
dialInNumber,
|
|
@@ -1867,7 +1678,7 @@ var GoogleMeetRuntime = class {
|
|
|
1867
1678
|
} : void 0;
|
|
1868
1679
|
}
|
|
1869
1680
|
async #ensureChromeRealtimeBridge(session) {
|
|
1870
|
-
if (!
|
|
1681
|
+
if (!MeetingPlatformAdapter.isTalkBackMode(session.mode) || !isBrowserTransport(session.transport) || session.state !== "active" || !session.chrome || session.chrome.audioBridge || session.chrome.health?.inCall !== true || session.chrome.health.micMuted !== false || session.chrome.health.manualAction) return;
|
|
1871
1682
|
const config = withSessionAgentConfig(this.params.config, session.agentId);
|
|
1872
1683
|
const recoveryConfig = {
|
|
1873
1684
|
...config,
|
|
@@ -2059,8 +1870,8 @@ var GoogleMeetRuntime = class {
|
|
|
2059
1870
|
};
|
|
2060
1871
|
//#endregion
|
|
2061
1872
|
//#region extensions/google-meet/src/plugin-helpers.ts
|
|
2062
|
-
const loadGoogleMeetCreateModule = createLazyRuntimeModule(() => import("./create-
|
|
2063
|
-
const loadGoogleMeetCliModule = createLazyRuntimeModule(() => import("./cli-
|
|
1873
|
+
const loadGoogleMeetCreateModule = createLazyRuntimeModule(() => import("./create-af1MDlus.js").then((n) => n.t));
|
|
1874
|
+
const loadGoogleMeetCliModule = createLazyRuntimeModule(() => import("./cli-GeuAaEIy.js"));
|
|
2064
1875
|
function asParamRecord(params) {
|
|
2065
1876
|
return params && typeof params === "object" && !Array.isArray(params) ? params : {};
|
|
2066
1877
|
}
|
|
@@ -2071,9 +1882,6 @@ function normalizeMode(value) {
|
|
|
2071
1882
|
if (value === "realtime") return "agent";
|
|
2072
1883
|
return value === "agent" || value === "bidi" || value === "transcribe" ? value : void 0;
|
|
2073
1884
|
}
|
|
2074
|
-
function isGoogleMeetTalkBackMode(mode) {
|
|
2075
|
-
return mode === "agent" || mode === "bidi";
|
|
2076
|
-
}
|
|
2077
1885
|
function resolveMeetingInput(config, value) {
|
|
2078
1886
|
const meeting = normalizeOptionalString(value) ?? config.defaults.meeting;
|
|
2079
1887
|
if (!meeting) throw new Error("Meeting input is required");
|
|
@@ -2112,7 +1920,7 @@ function isGoogleMeetAgentToolActionUnsupportedOnHost(params) {
|
|
|
2112
1920
|
if (action !== "join" && action !== "test_speech" && !(action === "create" && shouldJoinCreatedMeet(params.raw))) return false;
|
|
2113
1921
|
const transport = normalizeTransport(params.raw.transport) ?? params.config.defaultTransport;
|
|
2114
1922
|
const mode = action === "test_speech" ? "agent" : normalizeMode(params.raw.mode) ?? params.config.defaultMode;
|
|
2115
|
-
return transport === "chrome" &&
|
|
1923
|
+
return transport === "chrome" && MeetingPlatformAdapter.isTalkBackMode(mode);
|
|
2116
1924
|
}
|
|
2117
1925
|
function assertGoogleMeetAgentToolActionSupported(params) {
|
|
2118
1926
|
if (!isGoogleMeetAgentToolActionUnsupportedOnHost(params)) return;
|
|
@@ -3016,11 +2824,7 @@ var google_meet_default = definePluginEntry({
|
|
|
3016
2824
|
});
|
|
3017
2825
|
}, {
|
|
3018
2826
|
commands: ["googlemeet"],
|
|
3019
|
-
descriptors: [
|
|
3020
|
-
name: "googlemeet",
|
|
3021
|
-
description: "Join and manage Google Meet calls",
|
|
3022
|
-
hasSubcommands: true
|
|
3023
|
-
}]
|
|
2827
|
+
descriptors: [GOOGLE_MEET_CLI_DESCRIPTOR]
|
|
3024
2828
|
});
|
|
3025
2829
|
}
|
|
3026
2830
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openclaw/google-meet",
|
|
3
|
-
"version": "2026.7.2-beta.
|
|
3
|
+
"version": "2026.7.2-beta.5",
|
|
4
4
|
"description": "OpenClaw Google Meet participant plugin for joining calls through Chrome or Twilio transports.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
"typebox": "1.3.6"
|
|
14
14
|
},
|
|
15
15
|
"peerDependencies": {
|
|
16
|
-
"openclaw": ">=2026.7.2-beta.
|
|
16
|
+
"openclaw": ">=2026.7.2-beta.5"
|
|
17
17
|
},
|
|
18
18
|
"peerDependenciesMeta": {
|
|
19
19
|
"openclaw": {
|
|
@@ -30,10 +30,10 @@
|
|
|
30
30
|
"minHostVersion": ">=2026.4.20"
|
|
31
31
|
},
|
|
32
32
|
"compat": {
|
|
33
|
-
"pluginApi": ">=2026.7.2-beta.
|
|
33
|
+
"pluginApi": ">=2026.7.2-beta.5"
|
|
34
34
|
},
|
|
35
35
|
"build": {
|
|
36
|
-
"openclawVersion": "2026.7.2-beta.
|
|
36
|
+
"openclawVersion": "2026.7.2-beta.5"
|
|
37
37
|
},
|
|
38
38
|
"release": {
|
|
39
39
|
"publishToClawHub": true,
|
|
@@ -46,7 +46,6 @@
|
|
|
46
46
|
"files": [
|
|
47
47
|
"dist/**",
|
|
48
48
|
"openclaw.plugin.json",
|
|
49
|
-
"npm-shrinkwrap.json",
|
|
50
49
|
"README.md"
|
|
51
50
|
],
|
|
52
51
|
"bundledDependencies": [
|
package/npm-shrinkwrap.json
DELETED
|
@@ -1,157 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@openclaw/google-meet",
|
|
3
|
-
"version": "2026.7.2-beta.4",
|
|
4
|
-
"lockfileVersion": 3,
|
|
5
|
-
"requires": true,
|
|
6
|
-
"packages": {
|
|
7
|
-
"": {
|
|
8
|
-
"name": "@openclaw/google-meet",
|
|
9
|
-
"version": "2026.7.2-beta.4",
|
|
10
|
-
"dependencies": {
|
|
11
|
-
"jszip": "3.10.1",
|
|
12
|
-
"pretty-ms": "9.3.0",
|
|
13
|
-
"typebox": "1.3.6"
|
|
14
|
-
},
|
|
15
|
-
"peerDependencies": {
|
|
16
|
-
"openclaw": ">=2026.7.2-beta.4"
|
|
17
|
-
},
|
|
18
|
-
"peerDependenciesMeta": {
|
|
19
|
-
"openclaw": {
|
|
20
|
-
"optional": true
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
},
|
|
24
|
-
"node_modules/core-util-is": {
|
|
25
|
-
"version": "1.0.3",
|
|
26
|
-
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz",
|
|
27
|
-
"integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==",
|
|
28
|
-
"license": "MIT"
|
|
29
|
-
},
|
|
30
|
-
"node_modules/immediate": {
|
|
31
|
-
"version": "3.0.6",
|
|
32
|
-
"resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz",
|
|
33
|
-
"integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==",
|
|
34
|
-
"license": "MIT"
|
|
35
|
-
},
|
|
36
|
-
"node_modules/inherits": {
|
|
37
|
-
"version": "2.0.4",
|
|
38
|
-
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
|
|
39
|
-
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
|
|
40
|
-
"license": "ISC"
|
|
41
|
-
},
|
|
42
|
-
"node_modules/isarray": {
|
|
43
|
-
"version": "1.0.0",
|
|
44
|
-
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
|
|
45
|
-
"integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==",
|
|
46
|
-
"license": "MIT"
|
|
47
|
-
},
|
|
48
|
-
"node_modules/jszip": {
|
|
49
|
-
"version": "3.10.1",
|
|
50
|
-
"resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz",
|
|
51
|
-
"integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==",
|
|
52
|
-
"license": "(MIT OR GPL-3.0-or-later)",
|
|
53
|
-
"dependencies": {
|
|
54
|
-
"lie": "~3.3.0",
|
|
55
|
-
"pako": "~1.0.2",
|
|
56
|
-
"readable-stream": "~2.3.6",
|
|
57
|
-
"setimmediate": "^1.0.5"
|
|
58
|
-
}
|
|
59
|
-
},
|
|
60
|
-
"node_modules/lie": {
|
|
61
|
-
"version": "3.3.0",
|
|
62
|
-
"resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz",
|
|
63
|
-
"integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==",
|
|
64
|
-
"license": "MIT",
|
|
65
|
-
"dependencies": {
|
|
66
|
-
"immediate": "~3.0.5"
|
|
67
|
-
}
|
|
68
|
-
},
|
|
69
|
-
"node_modules/pako": {
|
|
70
|
-
"version": "1.0.11",
|
|
71
|
-
"resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz",
|
|
72
|
-
"integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==",
|
|
73
|
-
"license": "(MIT AND Zlib)"
|
|
74
|
-
},
|
|
75
|
-
"node_modules/parse-ms": {
|
|
76
|
-
"version": "4.0.0",
|
|
77
|
-
"resolved": "https://registry.npmjs.org/parse-ms/-/parse-ms-4.0.0.tgz",
|
|
78
|
-
"integrity": "sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw==",
|
|
79
|
-
"license": "MIT",
|
|
80
|
-
"engines": {
|
|
81
|
-
"node": ">=18"
|
|
82
|
-
},
|
|
83
|
-
"funding": {
|
|
84
|
-
"url": "https://github.com/sponsors/sindresorhus"
|
|
85
|
-
}
|
|
86
|
-
},
|
|
87
|
-
"node_modules/pretty-ms": {
|
|
88
|
-
"version": "9.3.0",
|
|
89
|
-
"resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-9.3.0.tgz",
|
|
90
|
-
"integrity": "sha512-gjVS5hOP+M3wMm5nmNOucbIrqudzs9v/57bWRHQWLYklXqoXKrVfYW2W9+glfGsqtPgpiz5WwyEEB+ksXIx3gQ==",
|
|
91
|
-
"license": "MIT",
|
|
92
|
-
"dependencies": {
|
|
93
|
-
"parse-ms": "^4.0.0"
|
|
94
|
-
},
|
|
95
|
-
"engines": {
|
|
96
|
-
"node": ">=18"
|
|
97
|
-
},
|
|
98
|
-
"funding": {
|
|
99
|
-
"url": "https://github.com/sponsors/sindresorhus"
|
|
100
|
-
}
|
|
101
|
-
},
|
|
102
|
-
"node_modules/process-nextick-args": {
|
|
103
|
-
"version": "2.0.1",
|
|
104
|
-
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
|
|
105
|
-
"integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
|
|
106
|
-
"license": "MIT"
|
|
107
|
-
},
|
|
108
|
-
"node_modules/readable-stream": {
|
|
109
|
-
"version": "2.3.8",
|
|
110
|
-
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz",
|
|
111
|
-
"integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==",
|
|
112
|
-
"license": "MIT",
|
|
113
|
-
"dependencies": {
|
|
114
|
-
"core-util-is": "~1.0.0",
|
|
115
|
-
"inherits": "~2.0.3",
|
|
116
|
-
"isarray": "~1.0.0",
|
|
117
|
-
"process-nextick-args": "~2.0.0",
|
|
118
|
-
"safe-buffer": "~5.1.1",
|
|
119
|
-
"string_decoder": "~1.1.1",
|
|
120
|
-
"util-deprecate": "~1.0.1"
|
|
121
|
-
}
|
|
122
|
-
},
|
|
123
|
-
"node_modules/safe-buffer": {
|
|
124
|
-
"version": "5.1.2",
|
|
125
|
-
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
|
|
126
|
-
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
|
|
127
|
-
"license": "MIT"
|
|
128
|
-
},
|
|
129
|
-
"node_modules/setimmediate": {
|
|
130
|
-
"version": "1.0.5",
|
|
131
|
-
"resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz",
|
|
132
|
-
"integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==",
|
|
133
|
-
"license": "MIT"
|
|
134
|
-
},
|
|
135
|
-
"node_modules/string_decoder": {
|
|
136
|
-
"version": "1.1.1",
|
|
137
|
-
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
|
|
138
|
-
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
|
|
139
|
-
"license": "MIT",
|
|
140
|
-
"dependencies": {
|
|
141
|
-
"safe-buffer": "~5.1.0"
|
|
142
|
-
}
|
|
143
|
-
},
|
|
144
|
-
"node_modules/typebox": {
|
|
145
|
-
"version": "1.3.6",
|
|
146
|
-
"resolved": "https://registry.npmjs.org/typebox/-/typebox-1.3.6.tgz",
|
|
147
|
-
"integrity": "sha512-Sc8RA0NCMEFmApHNU9ZMzqcpQj46She44J8ffpLM/bdhLNUZKq7DJumcLcsFx1gRmDfQPgCgOmFFJ7rcnfWNyA==",
|
|
148
|
-
"license": "MIT"
|
|
149
|
-
},
|
|
150
|
-
"node_modules/util-deprecate": {
|
|
151
|
-
"version": "1.0.2",
|
|
152
|
-
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
|
153
|
-
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
|
|
154
|
-
"license": "MIT"
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
}
|