@openclaw/google-meet 2026.7.2-beta.3 → 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-CBdUFMBH.js → chrome-create-DMKruRFW.js} +50 -47
- package/dist/{cli-CxRkKrq1.js → cli-GeuAaEIy.js} +664 -603
- package/dist/cli-metadata.js +13 -0
- package/dist/cli-output-mode-BwH_6tH8.js +28 -0
- package/dist/{create-DK5lNDEp.js → create-af1MDlus.js} +20 -2
- package/dist/index.js +545 -753
- package/node_modules/typebox/build/compile/validator.d.mts +1 -4
- package/node_modules/typebox/build/guard/guard.d.mts +1 -1
- package/node_modules/typebox/build/guard/guard.mjs +5 -2
- package/node_modules/typebox/build/guard/string.mjs +16 -4
- package/node_modules/typebox/build/schema/engine/_functions.mjs +34 -15
- package/node_modules/typebox/build/type/action/readonly_object.d.mts +5 -0
- package/node_modules/typebox/build/type/action/readonly_object.mjs +8 -0
- package/node_modules/typebox/build/typebox.d.mts +1 -1
- package/node_modules/typebox/build/typebox.mjs +1 -1
- package/node_modules/typebox/package.json +1 -1
- package/node_modules/typebox/readme.md +29 -32
- package/openclaw.plugin.json +3 -2
- package/package.json +5 -6
- package/npm-shrinkwrap.json +0 -157
package/dist/index.js
CHANGED
|
@@ -1,23 +1,24 @@
|
|
|
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
|
-
import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
|
|
5
5
|
import { ErrorCodes, GatewayClient, callGatewayFromCli, errorShape, startGatewayClientWhenEventLoopReady } from "openclaw/plugin-sdk/gateway-runtime";
|
|
6
|
-
import { createLazyRuntimeModule } from "openclaw/plugin-sdk/lazy-runtime";
|
|
7
6
|
import { definePluginEntry } from "openclaw/plugin-sdk/plugin-entry";
|
|
8
7
|
import { normalizeAgentId, parseAgentSessionKey } from "openclaw/plugin-sdk/routing";
|
|
9
8
|
import { asRecord, normalizeOptionalString, uniqueStrings } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
10
9
|
import { jsonResult } from "openclaw/plugin-sdk/tool-results";
|
|
11
|
-
import {
|
|
10
|
+
import { createMeetingTranscriptSourceProvider, resolveTranscriptsConfig } from "openclaw/plugin-sdk/transcripts";
|
|
12
11
|
import { isBlockedHostnameOrIp } from "openclaw/plugin-sdk/ssrf-runtime";
|
|
13
|
-
import { MeetingSessionRuntime, addMeetingSetupCheck, consultMeetingAgent, createLocalMeetingRealtimeAudioTransport, createMeetingBrowserNodeInvokePolicy, createMeetingNodeHost, createMeetingSetupStatus, createMeetingVoiceCallGateway, createNodeMeetingRealtimeAudioTransport, endMeetingVoiceCallGatewayCall, getMeetingVoiceCallGatewayCall, handleMeetingRealtimeConsultToolCall, isMeetingVoiceCallMissingError, joinMeetingViaVoiceCallGateway, leaveMeetingWithBrowser, openMeetingWithBrowser, readMeetingTranscriptWithBrowser, recoverMeetingBrowserTab, resolveLocalMeetingBrowserRequest, resolveMeetingRealtimeTools, speakMeetingViaVoiceCallGateway, startMeetingAgentRealtimeEngine, startMeetingRealtimeEngine } from "openclaw/plugin-sdk/meeting-runtime";
|
|
14
|
-
import { addTimerTimeoutGraceMs } from "openclaw/plugin-sdk/number-runtime";
|
|
15
12
|
import { spawnSync } from "node:child_process";
|
|
16
|
-
import {
|
|
17
|
-
import {
|
|
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";
|
|
14
|
+
import { addTimerTimeoutGraceMs } from "openclaw/plugin-sdk/number-runtime";
|
|
15
|
+
import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
|
|
16
|
+
import { createLazyRuntimeModule } from "openclaw/plugin-sdk/lazy-runtime";
|
|
17
|
+
import { resolveDefaultAgentId } from "openclaw/plugin-sdk/agent-runtime";
|
|
18
18
|
import fs from "node:fs";
|
|
19
19
|
import os from "node:os";
|
|
20
20
|
import path from "node:path";
|
|
21
|
+
import { Type } from "typebox";
|
|
21
22
|
//#region extensions/google-meet/src/transports/chrome-audio-device.ts
|
|
22
23
|
const GOOGLE_MEET_SYSTEM_PROFILER_COMMAND = "/usr/sbin/system_profiler";
|
|
23
24
|
function outputMentionsBlackHole2ch(output) {
|
|
@@ -32,6 +33,7 @@ const GOOGLE_MEET_CAPTION_SETTLE_MS = 1e3;
|
|
|
32
33
|
function meetStatusScript(params) {
|
|
33
34
|
return `async () => {
|
|
34
35
|
const text = (node) => (node?.innerText || node?.textContent || "").trim();
|
|
36
|
+
const manualActionFor = (reason, message) => ({ reason, message });
|
|
35
37
|
const allowMicrophone = ${JSON.stringify(params.allowMicrophone)};
|
|
36
38
|
const captionSessionId = ${JSON.stringify(params.captionSessionId)};
|
|
37
39
|
const captureCaptions = ${JSON.stringify(params.captureCaptions)};
|
|
@@ -330,27 +332,17 @@ function meetStatusScript(params) {
|
|
|
330
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)
|
|
331
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]
|
|
332
334
|
: undefined;
|
|
333
|
-
let
|
|
334
|
-
let manualActionMessage;
|
|
335
|
+
let manualAction;
|
|
335
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))) {
|
|
336
|
-
|
|
337
|
-
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.");
|
|
338
338
|
} else if (!inCall && joinElsewhere) {
|
|
339
|
-
|
|
340
|
-
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.");
|
|
341
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)) {
|
|
342
|
-
|
|
343
|
-
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.");
|
|
344
342
|
} else if (permissionNeeded) {
|
|
345
|
-
|
|
346
|
-
manualActionMessage = allowMicrophone
|
|
347
|
-
? "Allow microphone/camera/speaker permissions for Meet in the OpenClaw browser profile, then retry."
|
|
348
|
-
: "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.");
|
|
349
344
|
} else if (!inCall && (allowMicrophone ? !microphoneChoice : !noMicrophoneChoice) && /do you want people to hear you in the meeting/i.test(pageText)) {
|
|
350
|
-
|
|
351
|
-
manualActionMessage = allowMicrophone
|
|
352
|
-
? "Meet is showing the microphone choice. Click Use microphone in the OpenClaw browser profile, then retry."
|
|
353
|
-
: "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.");
|
|
354
346
|
}
|
|
355
347
|
return JSON.stringify({
|
|
356
348
|
clickedJoin: Boolean(join),
|
|
@@ -369,9 +361,7 @@ function meetStatusScript(params) {
|
|
|
369
361
|
audioOutputRouted,
|
|
370
362
|
audioOutputDeviceLabel,
|
|
371
363
|
audioOutputRouteError,
|
|
372
|
-
|
|
373
|
-
manualActionReason,
|
|
374
|
-
manualActionMessage,
|
|
364
|
+
manualAction,
|
|
375
365
|
title: document.title,
|
|
376
366
|
url: pageUrl,
|
|
377
367
|
notes
|
|
@@ -518,42 +508,6 @@ function prefixDtmfWait(sequence, delayMs) {
|
|
|
518
508
|
}
|
|
519
509
|
//#endregion
|
|
520
510
|
//#region extensions/google-meet/src/transports/google-meet-platform-adapter.ts
|
|
521
|
-
function isGoogleMeetTalkBackMode$2(mode) {
|
|
522
|
-
return mode === "agent" || mode === "bidi";
|
|
523
|
-
}
|
|
524
|
-
function parseMeetBrowserStatus(result) {
|
|
525
|
-
const raw = (result && typeof result === "object" ? result : {}).result;
|
|
526
|
-
if (typeof raw !== "string" || !raw.trim()) return;
|
|
527
|
-
let parsed;
|
|
528
|
-
try {
|
|
529
|
-
parsed = JSON.parse(raw);
|
|
530
|
-
} catch {
|
|
531
|
-
throw new Error("Google Meet browser status JSON is malformed.");
|
|
532
|
-
}
|
|
533
|
-
return {
|
|
534
|
-
inCall: parsed.inCall,
|
|
535
|
-
micMuted: parsed.micMuted,
|
|
536
|
-
lobbyWaiting: parsed.lobbyWaiting,
|
|
537
|
-
leaveReason: parsed.leaveReason,
|
|
538
|
-
captioning: parsed.captioning,
|
|
539
|
-
captionsEnabledAttempted: parsed.captionsEnabledAttempted,
|
|
540
|
-
transcriptLines: parsed.transcriptLines,
|
|
541
|
-
lastCaptionAt: parsed.lastCaptionAt,
|
|
542
|
-
lastCaptionSpeaker: parsed.lastCaptionSpeaker,
|
|
543
|
-
lastCaptionText: parsed.lastCaptionText,
|
|
544
|
-
recentTranscript: parsed.recentTranscript,
|
|
545
|
-
audioOutputRouted: parsed.audioOutputRouted,
|
|
546
|
-
audioOutputDeviceLabel: parsed.audioOutputDeviceLabel,
|
|
547
|
-
audioOutputRouteError: parsed.audioOutputRouteError,
|
|
548
|
-
manualActionRequired: parsed.manualActionRequired,
|
|
549
|
-
manualActionReason: parsed.manualActionReason,
|
|
550
|
-
manualActionMessage: parsed.manualActionMessage,
|
|
551
|
-
browserUrl: parsed.url,
|
|
552
|
-
browserTitle: parsed.title,
|
|
553
|
-
status: "browser-control",
|
|
554
|
-
notes: Array.isArray(parsed.notes) ? parsed.notes.filter((note) => typeof note === "string") : void 0
|
|
555
|
-
};
|
|
556
|
-
}
|
|
557
511
|
function parsePermissionGrantNotes(result) {
|
|
558
512
|
const record = result && typeof result === "object" ? result : {};
|
|
559
513
|
const unsupportedPermissions = Array.isArray(record.unsupportedPermissions) ? record.unsupportedPermissions.filter((value) => typeof value === "string") : [];
|
|
@@ -561,67 +515,41 @@ function parsePermissionGrantNotes(result) {
|
|
|
561
515
|
if (unsupportedPermissions.includes("speakerSelection")) notes.push("Chrome did not accept the optional Meet speaker-selection permission.");
|
|
562
516
|
return notes;
|
|
563
517
|
}
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
}
|
|
576
|
-
if (!parsed || typeof parsed !== "object") throw new Error("Google Meet transcript payload is invalid.");
|
|
577
|
-
const payload = parsed;
|
|
578
|
-
const droppedLines = typeof payload.droppedLines === "number" && Number.isSafeInteger(payload.droppedLines) ? Math.max(0, payload.droppedLines) : 0;
|
|
579
|
-
const lines = Array.isArray(payload.lines) ? payload.lines.flatMap((value) => {
|
|
580
|
-
if (!value || typeof value !== "object") return [];
|
|
581
|
-
const line = value;
|
|
582
|
-
if (typeof line.text !== "string" || !line.text.trim()) return [];
|
|
583
|
-
return [{
|
|
584
|
-
...typeof line.at === "string" ? { at: line.at } : {},
|
|
585
|
-
...typeof line.speaker === "string" ? { speaker: line.speaker } : {},
|
|
586
|
-
text: line.text
|
|
587
|
-
}];
|
|
588
|
-
}) : [];
|
|
589
|
-
return {
|
|
590
|
-
droppedLines,
|
|
591
|
-
...typeof payload.epoch === "string" ? { epoch: payload.epoch } : {},
|
|
592
|
-
lines,
|
|
593
|
-
...typeof payload.urlMatched === "boolean" ? { urlMatched: payload.urlMatched } : {},
|
|
594
|
-
...typeof payload.sessionMatched === "boolean" ? { sessionMatched: payload.sessionMatched } : {}
|
|
595
|
-
};
|
|
596
|
-
}
|
|
597
|
-
function parseMeetLeaveResult(result) {
|
|
598
|
-
const raw = (result && typeof result === "object" ? result : {}).result;
|
|
599
|
-
if (typeof raw !== "string" || !raw.trim()) return { departed: false };
|
|
600
|
-
try {
|
|
601
|
-
const parsed = JSON.parse(raw);
|
|
602
|
-
const leaveAction = parsed.leaveAction === "leave" || parsed.leaveAction === "confirm" ? parsed.leaveAction : void 0;
|
|
603
|
-
return {
|
|
604
|
-
departed: parsed.departed === true,
|
|
605
|
-
...leaveAction ? { leaveAction } : {},
|
|
606
|
-
...typeof parsed.urlMatched === "boolean" ? { urlMatched: parsed.urlMatched } : {}
|
|
607
|
-
};
|
|
608
|
-
} catch {
|
|
609
|
-
return { departed: false };
|
|
610
|
-
}
|
|
611
|
-
}
|
|
612
|
-
function classifyMeetManualAction(health) {
|
|
613
|
-
if (!health.manualActionRequired || !health.manualActionReason || !health.manualActionMessage) return;
|
|
614
|
-
return {
|
|
615
|
-
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",
|
|
616
|
-
reason: health.manualActionReason,
|
|
617
|
-
message: health.manualActionMessage
|
|
618
|
-
};
|
|
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";
|
|
619
529
|
}
|
|
620
|
-
const GOOGLE_MEET_PLATFORM_ADAPTER = {
|
|
530
|
+
const GOOGLE_MEET_PLATFORM_ADAPTER = MeetingPlatformAdapter.create({
|
|
621
531
|
id: "google-meet",
|
|
622
532
|
displayName: "Google Meet",
|
|
623
533
|
browserLabel: "Meet",
|
|
624
534
|
logScope: "[google-meet]",
|
|
535
|
+
agentConsult: {
|
|
536
|
+
surface: "a private Google Meet",
|
|
537
|
+
userLabel: "Participant",
|
|
538
|
+
assistantLabel: "Agent",
|
|
539
|
+
questionSourceLabel: "participant",
|
|
540
|
+
workingResponseLabel: "participant",
|
|
541
|
+
extraSystemPrompt: [
|
|
542
|
+
"You are a behind-the-scenes consultant for a live meeting voice agent.",
|
|
543
|
+
"Prioritize a fast, speakable answer over exhaustive investigation.",
|
|
544
|
+
"For tool-backed status checks, prefer one or two bounded read-only queries before answering.",
|
|
545
|
+
"Do not print secret values or dump environment variables; only check whether required configuration is present.",
|
|
546
|
+
"Be accurate, brief, and speakable."
|
|
547
|
+
].join(" ")
|
|
548
|
+
},
|
|
549
|
+
session: {
|
|
550
|
+
idPrefix: "meet",
|
|
551
|
+
participantIdentity: (transport) => transport === "twilio" ? "Twilio phone participant" : transport === "chrome-node" ? "signed-in Google Chrome profile on a paired node" : "signed-in Google Chrome profile"
|
|
552
|
+
},
|
|
625
553
|
nodeCommandName: GOOGLE_MEET_NODE_COMMAND,
|
|
626
554
|
nodeConfigPath: "plugins.entries.google-meet.config.chromeNode.node",
|
|
627
555
|
urls: {
|
|
@@ -642,28 +570,24 @@ const GOOGLE_MEET_PLATFORM_ADAPTER = {
|
|
|
642
570
|
}
|
|
643
571
|
},
|
|
644
572
|
browser: {
|
|
645
|
-
allowsMicrophone:
|
|
573
|
+
allowsMicrophone: MeetingPlatformAdapter.isTalkBackMode,
|
|
646
574
|
buildStatusJoinScript: (params) => meetStatusScript({
|
|
647
|
-
allowMicrophone:
|
|
575
|
+
allowMicrophone: MeetingPlatformAdapter.isTalkBackMode(params.mode),
|
|
648
576
|
autoJoin: params.autoJoin,
|
|
649
577
|
captionSessionId: params.meetingSessionId || void 0,
|
|
650
578
|
captureCaptions: params.captureCaptions,
|
|
651
579
|
guestName: params.guestName,
|
|
652
580
|
readOnly: params.readOnly
|
|
653
581
|
}),
|
|
654
|
-
parseStatus: parseMeetBrowserStatus,
|
|
655
|
-
classifyManualAction: classifyMeetManualAction,
|
|
656
582
|
browserControlUnavailable: () => ({
|
|
657
583
|
category: "browser-control-unavailable",
|
|
658
584
|
reason: "browser-control-unavailable",
|
|
659
585
|
message: "Open the OpenClaw browser profile, finish Google Meet login, admission, or permission prompts, then retry."
|
|
660
586
|
}),
|
|
661
587
|
buildLeaveScript: meetLeaveScript,
|
|
662
|
-
parseLeaveResult: parseMeetLeaveResult,
|
|
663
588
|
captions: {
|
|
664
|
-
enabled: (
|
|
665
|
-
buildTranscriptScript: ({ finalize, meetingSessionId, meetingUrl }) => meetTranscriptScript(meetingUrl, meetingSessionId, finalize)
|
|
666
|
-
parseTranscript: parseMeetTranscriptSnapshot
|
|
589
|
+
enabled: (_mode) => true,
|
|
590
|
+
buildTranscriptScript: ({ finalize, meetingSessionId, meetingUrl }) => meetTranscriptScript(meetingUrl, meetingSessionId, finalize)
|
|
667
591
|
},
|
|
668
592
|
permissions: ({ allowMicrophone }) => allowMicrophone ? {
|
|
669
593
|
origin: "https://meet.google.com",
|
|
@@ -689,8 +613,16 @@ const GOOGLE_MEET_PLATFORM_ADAPTER = {
|
|
|
689
613
|
pin,
|
|
690
614
|
dtmfSequence: params.dtmfSequence || params.defaultDtmfSequence ? rawDtmfSequence : prefixDtmfWait(rawDtmfSequence, params.dtmfDelayMs)
|
|
691
615
|
};
|
|
692
|
-
} }
|
|
693
|
-
|
|
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
|
+
});
|
|
694
626
|
//#endregion
|
|
695
627
|
//#region extensions/google-meet/src/node-host.ts
|
|
696
628
|
function assertBlackHoleAvailable(timeoutMs) {
|
|
@@ -760,139 +692,53 @@ function createGoogleMeetChromeNodeInvokePolicy(config) {
|
|
|
760
692
|
}
|
|
761
693
|
//#endregion
|
|
762
694
|
//#region extensions/google-meet/src/runtime-probes.ts
|
|
763
|
-
function resolveMode$1(request, config) {
|
|
764
|
-
return request.mode === "realtime" ? "agent" : request.mode ?? config.defaultMode;
|
|
765
|
-
}
|
|
766
695
|
function resolveProbeTimeoutMs(input, fallback) {
|
|
767
696
|
if (input === void 0) return Math.min(Math.max(fallback, 1), 12e4);
|
|
768
697
|
if (!Number.isFinite(input) || input <= 0) throw new Error("timeoutMs must be a positive number");
|
|
769
698
|
return Math.min(Math.trunc(input), 12e4);
|
|
770
699
|
}
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
const startOutputBytes = existing?.id === result.session.id ? existingOutputBytes : 0;
|
|
791
|
-
let health = result.session.chrome?.health;
|
|
792
|
-
const shouldWait = result.spoken === true && health?.manualActionRequired !== true && context.hasHealthHandle(result.session.id);
|
|
793
|
-
if (shouldWait && (health?.lastOutputBytes ?? 0) <= startOutputBytes) {
|
|
794
|
-
const deadline = Date.now() + Math.min(context.config.chrome.joinTimeoutMs, 5e3);
|
|
795
|
-
while (Date.now() < deadline) {
|
|
796
|
-
await sleep(100);
|
|
797
|
-
context.refreshHealth(result.session.id);
|
|
798
|
-
health = result.session.chrome?.health;
|
|
799
|
-
if ((health?.lastOutputBytes ?? 0) > startOutputBytes) break;
|
|
800
|
-
}
|
|
801
|
-
}
|
|
802
|
-
const speechOutputVerified = (health?.lastOutputBytes ?? 0) > startOutputBytes;
|
|
803
|
-
return {
|
|
804
|
-
createdSession: !before.has(result.session.id),
|
|
805
|
-
inCall: health?.inCall,
|
|
806
|
-
manualActionRequired: health?.manualActionRequired,
|
|
807
|
-
manualActionReason: health?.manualActionReason,
|
|
808
|
-
manualActionMessage: health?.manualActionMessage,
|
|
809
|
-
spoken: result.spoken ?? false,
|
|
810
|
-
speechOutputVerified,
|
|
811
|
-
speechOutputTimedOut: shouldWait && !speechOutputVerified,
|
|
812
|
-
speechReady: health?.speechReady,
|
|
813
|
-
speechBlockedReason: health?.speechBlockedReason,
|
|
814
|
-
speechBlockedMessage: health?.speechBlockedMessage,
|
|
815
|
-
audioOutputActive: health?.audioOutputActive,
|
|
816
|
-
lastOutputBytes: health?.lastOutputBytes,
|
|
817
|
-
session: result.session
|
|
818
|
-
};
|
|
819
|
-
}
|
|
820
|
-
async function testGoogleMeetListening(context, request) {
|
|
821
|
-
const requestedMode = request.mode ? resolveMode$1(request, context.config) : void 0;
|
|
822
|
-
if (requestedMode === "agent" || requestedMode === "bidi") throw new Error("test_listen requires mode: transcribe; use test_speech for talk-back sessions.");
|
|
823
|
-
const resolved = {
|
|
824
|
-
url: normalizeMeetUrl(request.url),
|
|
825
|
-
transport: request.transport ?? context.config.defaultTransport,
|
|
826
|
-
mode: "transcribe",
|
|
827
|
-
agentId: context.resolveAgentId(request)
|
|
828
|
-
};
|
|
829
|
-
if (resolved.transport === "twilio") throw new Error("test_listen supports chrome or chrome-node transports");
|
|
830
|
-
const beforeSessions = context.list();
|
|
831
|
-
const before = new Set(beforeSessions.map((session) => session.id));
|
|
832
|
-
const existing = beforeSessions.find((session) => context.isReusable(session, resolved));
|
|
833
|
-
const start = {
|
|
834
|
-
lines: existing?.chrome?.health?.transcriptLines ?? 0,
|
|
835
|
-
at: existing?.chrome?.health?.lastCaptionAt,
|
|
836
|
-
text: existing?.chrome?.health?.lastCaptionText
|
|
837
|
-
};
|
|
838
|
-
const result = await context.join({
|
|
839
|
-
...request,
|
|
840
|
-
...resolved,
|
|
841
|
-
message: void 0
|
|
842
|
-
});
|
|
843
|
-
let health = result.session.chrome?.health;
|
|
844
|
-
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);
|
|
845
|
-
const shouldWait = health?.manualActionRequired !== true && Boolean((result.session.transport === "chrome" || result.session.transport === "chrome-node") && result.session.chrome?.launched);
|
|
846
|
-
if (shouldWait && !advanced()) {
|
|
847
|
-
const deadline = Date.now() + resolveProbeTimeoutMs(request.timeoutMs, context.config.chrome.joinTimeoutMs);
|
|
848
|
-
while (Date.now() < deadline) {
|
|
849
|
-
await sleep(250);
|
|
850
|
-
await context.refreshCaptionHealth(result.session);
|
|
851
|
-
health = result.session.chrome?.health;
|
|
852
|
-
if (health?.manualActionRequired || advanced()) break;
|
|
853
|
-
}
|
|
854
|
-
}
|
|
855
|
-
const listenVerified = advanced();
|
|
856
|
-
return {
|
|
857
|
-
createdSession: !before.has(result.session.id),
|
|
858
|
-
inCall: health?.inCall,
|
|
859
|
-
manualActionRequired: health?.manualActionRequired,
|
|
860
|
-
manualActionReason: health?.manualActionReason,
|
|
861
|
-
manualActionMessage: health?.manualActionMessage,
|
|
862
|
-
listenVerified,
|
|
863
|
-
listenTimedOut: shouldWait && !listenVerified && health?.manualActionRequired !== true,
|
|
864
|
-
captioning: health?.captioning,
|
|
865
|
-
captionsEnabledAttempted: health?.captionsEnabledAttempted,
|
|
866
|
-
transcriptLines: health?.transcriptLines,
|
|
867
|
-
lastCaptionAt: health?.lastCaptionAt,
|
|
868
|
-
lastCaptionSpeaker: health?.lastCaptionSpeaker,
|
|
869
|
-
lastCaptionText: health?.lastCaptionText,
|
|
870
|
-
recentTranscript: health?.recentTranscript,
|
|
871
|
-
session: result.session
|
|
872
|
-
};
|
|
873
|
-
}
|
|
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;
|
|
874
719
|
//#endregion
|
|
875
720
|
//#region extensions/google-meet/src/runtime-session.ts
|
|
876
|
-
function
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
721
|
+
function resolveTransport(input, config) {
|
|
722
|
+
return input ?? config.defaultTransport;
|
|
723
|
+
}
|
|
724
|
+
function resolveMode(input, config) {
|
|
725
|
+
return input === "realtime" ? "agent" : input ?? config.defaultMode;
|
|
726
|
+
}
|
|
727
|
+
function withSessionAgentConfig(config, agentId) {
|
|
728
|
+
return config.realtime.agentId === agentId ? config : {
|
|
729
|
+
...config,
|
|
885
730
|
realtime: {
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
model: resolved.mode === "bidi" ? config.realtime.model : void 0,
|
|
890
|
-
transcriptionProvider: resolved.mode === "agent" ? config.realtime.transcriptionProvider ?? config.realtime.provider : void 0,
|
|
891
|
-
toolPolicy: config.realtime.toolPolicy
|
|
892
|
-
},
|
|
893
|
-
notes: []
|
|
731
|
+
...config.realtime,
|
|
732
|
+
agentId
|
|
733
|
+
}
|
|
894
734
|
};
|
|
895
735
|
}
|
|
736
|
+
function isBrowserTransport(transport) {
|
|
737
|
+
return transport === "chrome" || transport === "chrome-node";
|
|
738
|
+
}
|
|
739
|
+
function noteSession(session, note) {
|
|
740
|
+
session.notes = [...session.notes.filter((item) => item !== note), note];
|
|
741
|
+
}
|
|
896
742
|
//#endregion
|
|
897
743
|
//#region extensions/google-meet/src/setup.ts
|
|
898
744
|
function resolveUserPath(input) {
|
|
@@ -946,7 +792,7 @@ function getGoogleMeetSetupStatus(config, options) {
|
|
|
946
792
|
const fullConfig = asRecord(options?.fullConfig);
|
|
947
793
|
const mode = options?.mode ?? config.defaultMode;
|
|
948
794
|
const transport = options?.transport ?? config.defaultTransport;
|
|
949
|
-
const needsChromeRealtimeAudio = (mode
|
|
795
|
+
const needsChromeRealtimeAudio = MeetingPlatformAdapter.isTalkBackMode(mode) && (transport === "chrome" || transport === "chrome-node");
|
|
950
796
|
const pluginEntries = asRecord(asRecord(fullConfig.plugins).entries);
|
|
951
797
|
const pluginAllow = asRecord(fullConfig.plugins).allow;
|
|
952
798
|
const voiceCallEntry = asRecord(pluginEntries["voice-call"]);
|
|
@@ -1035,84 +881,9 @@ function addGoogleMeetSetupCheck(status, check) {
|
|
|
1035
881
|
return addMeetingSetupCheck(status, check);
|
|
1036
882
|
}
|
|
1037
883
|
//#endregion
|
|
1038
|
-
//#region extensions/google-meet/src/agent-consult.ts
|
|
1039
|
-
const GOOGLE_MEET_CONSULT_SURFACE = {
|
|
1040
|
-
id: "google-meet",
|
|
1041
|
-
provider: "google-meet",
|
|
1042
|
-
lane: "google-meet",
|
|
1043
|
-
surface: "a private Google Meet",
|
|
1044
|
-
userLabel: "Participant",
|
|
1045
|
-
assistantLabel: "Agent",
|
|
1046
|
-
questionSourceLabel: "participant",
|
|
1047
|
-
workingResponseLabel: "participant",
|
|
1048
|
-
extraSystemPrompt: [
|
|
1049
|
-
"You are a behind-the-scenes consultant for a live meeting voice agent.",
|
|
1050
|
-
"Prioritize a fast, speakable answer over exhaustive investigation.",
|
|
1051
|
-
"For tool-backed status checks, prefer one or two bounded read-only queries before answering.",
|
|
1052
|
-
"Do not print secret values or dump environment variables; only check whether required configuration is present.",
|
|
1053
|
-
"Be accurate, brief, and speakable."
|
|
1054
|
-
].join(" ")
|
|
1055
|
-
};
|
|
1056
|
-
function resolveGoogleMeetRealtimeTools(policy) {
|
|
1057
|
-
return resolveMeetingRealtimeTools(policy);
|
|
1058
|
-
}
|
|
1059
|
-
async function consultOpenClawAgentForGoogleMeet(params) {
|
|
1060
|
-
return await consultMeetingAgent({
|
|
1061
|
-
surface: GOOGLE_MEET_CONSULT_SURFACE,
|
|
1062
|
-
config: params.fullConfig,
|
|
1063
|
-
runtime: params.runtime,
|
|
1064
|
-
logger: params.logger,
|
|
1065
|
-
agentId: params.config.realtime.agentId,
|
|
1066
|
-
toolPolicy: params.config.realtime.toolPolicy,
|
|
1067
|
-
meetingSessionId: params.meetingSessionId,
|
|
1068
|
-
requesterSessionKey: params.requesterSessionKey,
|
|
1069
|
-
args: params.args,
|
|
1070
|
-
transcript: params.transcript
|
|
1071
|
-
});
|
|
1072
|
-
}
|
|
1073
|
-
async function handleGoogleMeetRealtimeConsultToolCall(params) {
|
|
1074
|
-
await handleMeetingRealtimeConsultToolCall({
|
|
1075
|
-
surface: GOOGLE_MEET_CONSULT_SURFACE,
|
|
1076
|
-
strategy: params.strategy,
|
|
1077
|
-
session: params.session,
|
|
1078
|
-
event: params.event,
|
|
1079
|
-
config: params.fullConfig,
|
|
1080
|
-
runtime: params.runtime,
|
|
1081
|
-
logger: params.logger,
|
|
1082
|
-
agentId: params.config.realtime.agentId,
|
|
1083
|
-
toolPolicy: params.config.realtime.toolPolicy,
|
|
1084
|
-
meetingSessionId: params.meetingSessionId,
|
|
1085
|
-
requesterSessionKey: params.requesterSessionKey,
|
|
1086
|
-
transcript: params.transcript,
|
|
1087
|
-
onTalkEvent: params.onTalkEvent
|
|
1088
|
-
});
|
|
1089
|
-
}
|
|
1090
|
-
//#endregion
|
|
1091
884
|
//#region extensions/google-meet/src/transports/chrome.ts
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
logScope: GOOGLE_MEET_PLATFORM_ADAPTER.logScope,
|
|
1095
|
-
sessionIdPrefix: GOOGLE_MEET_PLATFORM_ADAPTER.id
|
|
1096
|
-
};
|
|
1097
|
-
function createGoogleMeetRealtimeEngineBindings(params) {
|
|
1098
|
-
return {
|
|
1099
|
-
platform: GOOGLE_MEET_RUNTIME_PLATFORM,
|
|
1100
|
-
consultAgent: (consult) => consultOpenClawAgentForGoogleMeet({
|
|
1101
|
-
config: params.config,
|
|
1102
|
-
fullConfig: params.fullConfig,
|
|
1103
|
-
runtime: params.runtime,
|
|
1104
|
-
logger: params.logger,
|
|
1105
|
-
...consult
|
|
1106
|
-
}),
|
|
1107
|
-
tools: resolveGoogleMeetRealtimeTools(params.config.realtime.toolPolicy),
|
|
1108
|
-
handleToolCall: (call) => handleGoogleMeetRealtimeConsultToolCall({
|
|
1109
|
-
config: params.config,
|
|
1110
|
-
fullConfig: params.fullConfig,
|
|
1111
|
-
runtime: params.runtime,
|
|
1112
|
-
logger: params.logger,
|
|
1113
|
-
...call
|
|
1114
|
-
})
|
|
1115
|
-
};
|
|
885
|
+
function shouldCaptureCaptions(mode, fullConfig) {
|
|
886
|
+
return mode === "transcribe" || !fullConfig || resolveTranscriptsConfig(fullConfig.transcripts).enabled;
|
|
1116
887
|
}
|
|
1117
888
|
async function assertBlackHole2chAvailable(params) {
|
|
1118
889
|
if (process.platform !== "darwin") throw new Error("Chrome Meet transport with blackhole-2ch audio is currently macOS-only");
|
|
@@ -1132,7 +903,7 @@ async function assertBlackHole2chAvailable(params) {
|
|
|
1132
903
|
}
|
|
1133
904
|
async function launchChromeMeet(params) {
|
|
1134
905
|
const checkRealtimeAudioPrerequisites = async () => {
|
|
1135
|
-
if (!
|
|
906
|
+
if (!MeetingPlatformAdapter.isTalkBackMode(params.mode)) return;
|
|
1136
907
|
await assertBlackHole2chAvailable({
|
|
1137
908
|
runtime: params.runtime,
|
|
1138
909
|
timeoutMs: Math.min(params.config.chrome.joinTimeoutMs, 1e4)
|
|
@@ -1143,7 +914,7 @@ async function launchChromeMeet(params) {
|
|
|
1143
914
|
}
|
|
1144
915
|
};
|
|
1145
916
|
const startRealtimeAudioBridge = async () => {
|
|
1146
|
-
if (!
|
|
917
|
+
if (!MeetingPlatformAdapter.isTalkBackMode(params.mode)) return;
|
|
1147
918
|
if (params.config.chrome.audioBridgeCommand) {
|
|
1148
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.");
|
|
1149
920
|
const bridge = await params.runtime.system.runCommandWithTimeout(params.config.chrome.audioBridgeCommand, { timeoutMs: params.config.chrome.joinTimeoutMs });
|
|
@@ -1154,14 +925,18 @@ async function launchChromeMeet(params) {
|
|
|
1154
925
|
const transport = createLocalMeetingRealtimeAudioTransport({
|
|
1155
926
|
inputCommand: params.config.chrome.audioInputCommand,
|
|
1156
927
|
outputCommand: params.config.chrome.audioOutputCommand,
|
|
928
|
+
audioFormat: params.config.chrome.audioFormat,
|
|
1157
929
|
bargeInInputCommand: params.config.chrome.bargeInInputCommand,
|
|
1158
930
|
bargeInRmsThreshold: params.config.chrome.bargeInRmsThreshold,
|
|
1159
931
|
bargeInPeakThreshold: params.config.chrome.bargeInPeakThreshold,
|
|
1160
932
|
bargeInCooldownMs: params.config.chrome.bargeInCooldownMs,
|
|
1161
933
|
logger: params.logger,
|
|
1162
|
-
logScope:
|
|
934
|
+
logScope: GOOGLE_MEET_PLATFORM_ADAPTER.logScope
|
|
935
|
+
});
|
|
936
|
+
const bindings = createMeetingRealtimeEngineBindings({
|
|
937
|
+
platform: GOOGLE_MEET_PLATFORM_ADAPTER,
|
|
938
|
+
...params
|
|
1163
939
|
});
|
|
1164
|
-
const bindings = createGoogleMeetRealtimeEngineBindings(params);
|
|
1165
940
|
const engine = params.mode === "agent" ? await startMeetingAgentRealtimeEngine({
|
|
1166
941
|
config: params.config,
|
|
1167
942
|
fullConfig: params.fullConfig,
|
|
@@ -1205,12 +980,13 @@ async function launchChromeMeet(params) {
|
|
|
1205
980
|
callBrowser: await resolveLocalMeetingBrowserRequest(params.runtime),
|
|
1206
981
|
config: params.config.chrome,
|
|
1207
982
|
session: {
|
|
983
|
+
captureCaptions: shouldCaptureCaptions(params.mode, params.fullConfig),
|
|
1208
984
|
meetingSessionId: params.meetingSessionId,
|
|
1209
985
|
mode: params.mode,
|
|
1210
986
|
url: params.url
|
|
1211
987
|
}
|
|
1212
988
|
});
|
|
1213
|
-
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;
|
|
1214
990
|
return {
|
|
1215
991
|
...result,
|
|
1216
992
|
audioBridge
|
|
@@ -1226,6 +1002,7 @@ async function leaveChromeMeet(params) {
|
|
|
1226
1002
|
adapter: GOOGLE_MEET_PLATFORM_ADAPTER,
|
|
1227
1003
|
callBrowser: await resolveLocalMeetingBrowserRequest(params.runtime),
|
|
1228
1004
|
launch: params.config.chrome.launch,
|
|
1005
|
+
meetingSessionId: params.meetingSessionId,
|
|
1229
1006
|
meetingUrl: params.meetingUrl,
|
|
1230
1007
|
tab: params.tab,
|
|
1231
1008
|
timeoutMs: params.config.chrome.joinTimeoutMs
|
|
@@ -1281,6 +1058,7 @@ async function leaveChromeMeetOnNode(params) {
|
|
|
1281
1058
|
timeoutMs: request.timeoutMs
|
|
1282
1059
|
}),
|
|
1283
1060
|
launch: params.config.chrome.launch,
|
|
1061
|
+
meetingSessionId: params.meetingSessionId,
|
|
1284
1062
|
meetingUrl: params.meetingUrl,
|
|
1285
1063
|
tab: params.tab,
|
|
1286
1064
|
timeoutMs: params.config.chrome.joinTimeoutMs
|
|
@@ -1299,6 +1077,7 @@ async function openMeetWithBrowserProxy(params) {
|
|
|
1299
1077
|
}),
|
|
1300
1078
|
config: params.config.chrome,
|
|
1301
1079
|
session: {
|
|
1080
|
+
captureCaptions: params.captureCaptions,
|
|
1302
1081
|
mode: params.mode,
|
|
1303
1082
|
meetingSessionId: params.meetingSessionId,
|
|
1304
1083
|
url: params.url
|
|
@@ -1311,6 +1090,7 @@ async function recoverCurrentMeetTab(params) {
|
|
|
1311
1090
|
...await recoverMeetingBrowserTab({
|
|
1312
1091
|
adapter: GOOGLE_MEET_PLATFORM_ADAPTER,
|
|
1313
1092
|
callBrowser: await resolveLocalMeetingBrowserRequest(params.runtime),
|
|
1093
|
+
captureCaptions: shouldCaptureCaptions(params.mode ?? "bidi", params.fullConfig),
|
|
1314
1094
|
config: params.config.chrome,
|
|
1315
1095
|
locationLabel: "in local Chrome",
|
|
1316
1096
|
mode: params.mode ?? "bidi",
|
|
@@ -1339,6 +1119,7 @@ async function recoverCurrentMeetTabOnNode(params) {
|
|
|
1339
1119
|
body: request.body,
|
|
1340
1120
|
timeoutMs: request.timeoutMs
|
|
1341
1121
|
}),
|
|
1122
|
+
captureCaptions: shouldCaptureCaptions(params.mode ?? "bidi", params.fullConfig),
|
|
1342
1123
|
config: params.config.chrome,
|
|
1343
1124
|
locationLabel: "on the selected Chrome node",
|
|
1344
1125
|
mode: params.mode ?? "bidi",
|
|
@@ -1372,11 +1153,12 @@ async function launchChromeMeetOnNode(params) {
|
|
|
1372
1153
|
runtime: params.runtime,
|
|
1373
1154
|
nodeId,
|
|
1374
1155
|
config: params.config,
|
|
1156
|
+
captureCaptions: shouldCaptureCaptions(params.mode, params.fullConfig),
|
|
1375
1157
|
mode: params.mode,
|
|
1376
1158
|
meetingSessionId: params.meetingSessionId,
|
|
1377
1159
|
url: params.url
|
|
1378
1160
|
});
|
|
1379
|
-
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 {
|
|
1380
1162
|
nodeId,
|
|
1381
1163
|
launched: browserControl.launched,
|
|
1382
1164
|
browser: browserControl.browser,
|
|
@@ -1405,12 +1187,16 @@ async function launchChromeMeetOnNode(params) {
|
|
|
1405
1187
|
runtime: params.runtime,
|
|
1406
1188
|
nodeId,
|
|
1407
1189
|
bridgeId: result.bridgeId,
|
|
1190
|
+
audioFormat: params.config.chrome.audioFormat,
|
|
1408
1191
|
logger: params.logger,
|
|
1409
1192
|
commandName: GOOGLE_MEET_NODE_COMMAND,
|
|
1410
|
-
logScope:
|
|
1193
|
+
logScope: GOOGLE_MEET_PLATFORM_ADAPTER.logScope,
|
|
1411
1194
|
logPrefix: params.mode === "agent" ? "node agent" : "node"
|
|
1412
1195
|
});
|
|
1413
|
-
const bindings =
|
|
1196
|
+
const bindings = createMeetingRealtimeEngineBindings({
|
|
1197
|
+
platform: GOOGLE_MEET_PLATFORM_ADAPTER,
|
|
1198
|
+
...params
|
|
1199
|
+
});
|
|
1414
1200
|
const engine = params.mode === "agent" ? await startMeetingAgentRealtimeEngine({
|
|
1415
1201
|
config: params.config,
|
|
1416
1202
|
fullConfig: params.fullConfig,
|
|
@@ -1627,45 +1413,18 @@ async function speakMeetViaVoiceCallGateway(params) {
|
|
|
1627
1413
|
}
|
|
1628
1414
|
//#endregion
|
|
1629
1415
|
//#region extensions/google-meet/src/runtime.ts
|
|
1630
|
-
|
|
1631
|
-
return (/* @__PURE__ */ new Date()).toISOString();
|
|
1632
|
-
}
|
|
1633
|
-
function buildTwilioVoiceCallSessionKey(meetingSessionId) {
|
|
1634
|
-
return `voice:google-meet:${meetingSessionId}`;
|
|
1635
|
-
}
|
|
1636
|
-
function resolveTransport(input, config) {
|
|
1637
|
-
return input ?? config.defaultTransport;
|
|
1638
|
-
}
|
|
1639
|
-
function resolveMode(input, config) {
|
|
1640
|
-
return input === "realtime" ? "agent" : input ?? config.defaultMode;
|
|
1641
|
-
}
|
|
1642
|
-
function resolveSessionAgentId(request, config) {
|
|
1643
|
-
return normalizeAgentId(request.agentId ?? config.realtime.agentId);
|
|
1644
|
-
}
|
|
1645
|
-
function withSessionAgentConfig(config, agentId) {
|
|
1646
|
-
return config.realtime.agentId === agentId ? config : {
|
|
1647
|
-
...config,
|
|
1648
|
-
realtime: {
|
|
1649
|
-
...config.realtime,
|
|
1650
|
-
agentId
|
|
1651
|
-
}
|
|
1652
|
-
};
|
|
1653
|
-
}
|
|
1654
|
-
function isGoogleMeetTalkBackMode$1(mode) {
|
|
1655
|
-
return mode === "agent" || mode === "bidi";
|
|
1656
|
-
}
|
|
1657
|
-
function isBrowserTransport(transport) {
|
|
1658
|
-
return transport === "chrome" || transport === "chrome-node";
|
|
1659
|
-
}
|
|
1660
|
-
function noteSession(session, note) {
|
|
1661
|
-
session.notes = [...session.notes.filter((item) => item !== note), note];
|
|
1662
|
-
}
|
|
1416
|
+
const nowIso = () => (/* @__PURE__ */ new Date()).toISOString();
|
|
1663
1417
|
var GoogleMeetRuntime = class {
|
|
1664
|
-
#createdBrowserTabs
|
|
1418
|
+
#createdBrowserTabs;
|
|
1419
|
+
#agentId;
|
|
1665
1420
|
#voiceCallGateway;
|
|
1666
1421
|
#sessions;
|
|
1667
1422
|
constructor(params) {
|
|
1668
1423
|
this.params = params;
|
|
1424
|
+
this.#createdBrowserTabs = /* @__PURE__ */ new Map();
|
|
1425
|
+
this.transcriptSourceRuntime = () => this.#sessions;
|
|
1426
|
+
const adapter = GOOGLE_MEET_PLATFORM_ADAPTER;
|
|
1427
|
+
this.#agentId = resolveDefaultAgentId(params.fullConfig);
|
|
1669
1428
|
this.#voiceCallGateway = createVoiceCallGateway(params);
|
|
1670
1429
|
this.#sessions = new MeetingSessionRuntime({
|
|
1671
1430
|
logger: params.logger,
|
|
@@ -1689,7 +1448,6 @@ var GoogleMeetRuntime = class {
|
|
|
1689
1448
|
speech: {
|
|
1690
1449
|
audioBridgeUnavailable: "Realtime speech requires an active Chrome audio bridge.",
|
|
1691
1450
|
browserUnverified: "Google Meet browser state has not been verified yet.",
|
|
1692
|
-
manualActionFallback: "Resolve the Google Meet browser prompt before asking OpenClaw to speak.",
|
|
1693
1451
|
microphoneMuted: "Turn on the OpenClaw Google Meet microphone before asking OpenClaw to speak.",
|
|
1694
1452
|
microphoneMutedReason: "meet-microphone-muted",
|
|
1695
1453
|
notInCall: "Google Meet has not reported that the browser participant is in the call.",
|
|
@@ -1699,22 +1457,23 @@ var GoogleMeetRuntime = class {
|
|
|
1699
1457
|
}
|
|
1700
1458
|
},
|
|
1701
1459
|
resolveJoin: (request) => ({
|
|
1702
|
-
url:
|
|
1460
|
+
url: adapter.urls.validateAndNormalize(request.url),
|
|
1703
1461
|
transport: resolveTransport(request.transport, params.config),
|
|
1704
1462
|
mode: resolveMode(request.mode, params.config),
|
|
1705
|
-
agentId:
|
|
1463
|
+
agentId: normalizeAgentId(request.agentId ?? params.config.realtime.agentId ?? this.#agentId)
|
|
1706
1464
|
}),
|
|
1707
|
-
createSession: ({
|
|
1465
|
+
createSession: ({ resolved, createdAt }) => createMeetingSession({
|
|
1466
|
+
platform: adapter,
|
|
1708
1467
|
config: params.config,
|
|
1709
1468
|
resolved,
|
|
1710
1469
|
createdAt
|
|
1711
1470
|
}),
|
|
1712
1471
|
resolveSpeechInstructions: (request) => request.message ?? params.config.realtime.introMessage,
|
|
1713
1472
|
isBrowserTransport,
|
|
1714
|
-
isTalkBackMode:
|
|
1473
|
+
isTalkBackMode: (mode) => MeetingPlatformAdapter.isTalkBackMode(mode),
|
|
1715
1474
|
isTranscribeMode: (mode) => mode === "transcribe",
|
|
1716
|
-
sameMeetingUrl: (left, right) =>
|
|
1717
|
-
normalizeMeetingUrlForReuse: (url) =>
|
|
1475
|
+
sameMeetingUrl: (left, right) => adapter.urls.isSameMeeting(left, right),
|
|
1476
|
+
normalizeMeetingUrlForReuse: (url) => adapter.urls.normalizeForReuse(url),
|
|
1718
1477
|
getBrowser: (session) => session.chrome ? {
|
|
1719
1478
|
launched: session.chrome.launched,
|
|
1720
1479
|
nodeId: session.chrome.nodeId,
|
|
@@ -1732,12 +1491,17 @@ var GoogleMeetRuntime = class {
|
|
|
1732
1491
|
releaseBrowserTab: async (session) => await this.#releaseBrowserTab(session),
|
|
1733
1492
|
refreshBrowserHealth: async (session, options) => await this.#refreshBrowserHealth(session, options),
|
|
1734
1493
|
refreshStatus: async (session) => await this.#refreshStatus(session),
|
|
1735
|
-
refreshReusableSession: async (session) => {
|
|
1494
|
+
refreshReusableSession: async (session, _request, _resolved) => {
|
|
1736
1495
|
if (session.transport === "twilio") await this.#refreshTwilioVoiceCallStatus(session);
|
|
1737
1496
|
},
|
|
1738
1497
|
ensureRealtimeBridge: async (session) => await this.#ensureChromeRealtimeBridge(session),
|
|
1739
1498
|
captureTranscript: async (session, options) => await this.#captureTranscript(session, options),
|
|
1740
|
-
speakViaTransport: async (session, instructions) => await this.#speakViaTransport(session, instructions)
|
|
1499
|
+
speakViaTransport: async (session, instructions) => await this.#speakViaTransport(session, instructions),
|
|
1500
|
+
durableTranscripts: {
|
|
1501
|
+
config: params.fullConfig.transcripts,
|
|
1502
|
+
providerId: "google-meet",
|
|
1503
|
+
providerName: "Google Meet"
|
|
1504
|
+
}
|
|
1741
1505
|
});
|
|
1742
1506
|
}
|
|
1743
1507
|
list() {
|
|
@@ -1772,10 +1536,12 @@ var GoogleMeetRuntime = class {
|
|
|
1772
1536
|
return transport === "chrome-node" ? await recoverCurrentMeetTabOnNode({
|
|
1773
1537
|
runtime: this.params.runtime,
|
|
1774
1538
|
config: this.params.config,
|
|
1539
|
+
fullConfig: this.params.fullConfig,
|
|
1775
1540
|
url
|
|
1776
1541
|
}) : await recoverCurrentMeetTab({
|
|
1777
1542
|
runtime: this.params.runtime,
|
|
1778
1543
|
config: this.params.config,
|
|
1544
|
+
fullConfig: this.params.fullConfig,
|
|
1779
1545
|
url
|
|
1780
1546
|
});
|
|
1781
1547
|
}
|
|
@@ -1797,7 +1563,7 @@ var GoogleMeetRuntime = class {
|
|
|
1797
1563
|
#probeContext() {
|
|
1798
1564
|
return {
|
|
1799
1565
|
config: this.params.config,
|
|
1800
|
-
resolveAgentId: (request) =>
|
|
1566
|
+
resolveAgentId: (request) => normalizeAgentId(request.agentId ?? this.params.config.realtime.agentId ?? this.#agentId),
|
|
1801
1567
|
list: () => this.list(),
|
|
1802
1568
|
join: async (request) => await this.join(request),
|
|
1803
1569
|
isReusable: (session, resolved) => this.#sessions.isReusableSession(session, resolved),
|
|
@@ -1854,7 +1620,7 @@ var GoogleMeetRuntime = class {
|
|
|
1854
1620
|
};
|
|
1855
1621
|
const handles = this.#attachChromeAudioBridge(session, result.audioBridge);
|
|
1856
1622
|
if (handles) context.attachRuntimeHandles(session, handles);
|
|
1857
|
-
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.");
|
|
1858
1624
|
this.#sessions.refreshSpeechReadiness(session);
|
|
1859
1625
|
return {};
|
|
1860
1626
|
}
|
|
@@ -1879,8 +1645,8 @@ var GoogleMeetRuntime = class {
|
|
|
1879
1645
|
logger: this.params.logger,
|
|
1880
1646
|
...request.requesterSessionKey ? { requesterSessionKey: request.requesterSessionKey } : {},
|
|
1881
1647
|
agentId: delegatedAgentId,
|
|
1882
|
-
sessionKey: delegatedAgentId ? `agent:${delegatedAgentId}:google-meet:${session.id}` :
|
|
1883
|
-
message:
|
|
1648
|
+
sessionKey: delegatedAgentId ? `agent:${delegatedAgentId}:google-meet:${session.id}` : `voice:google-meet:${session.id}`,
|
|
1649
|
+
message: MeetingPlatformAdapter.isTalkBackMode(session.mode) ? request.message ?? this.params.config.voiceCall.introMessage ?? this.params.config.realtime.introMessage : void 0
|
|
1884
1650
|
}) : void 0;
|
|
1885
1651
|
session.twilio = {
|
|
1886
1652
|
dialInNumber,
|
|
@@ -1912,7 +1678,7 @@ var GoogleMeetRuntime = class {
|
|
|
1912
1678
|
} : void 0;
|
|
1913
1679
|
}
|
|
1914
1680
|
async #ensureChromeRealtimeBridge(session) {
|
|
1915
|
-
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;
|
|
1916
1682
|
const config = withSessionAgentConfig(this.params.config, session.agentId);
|
|
1917
1683
|
const recoveryConfig = {
|
|
1918
1684
|
...config,
|
|
@@ -1950,6 +1716,7 @@ var GoogleMeetRuntime = class {
|
|
|
1950
1716
|
const result = session.transport === "chrome-node" ? await recoverCurrentMeetTabOnNode({
|
|
1951
1717
|
runtime: this.params.runtime,
|
|
1952
1718
|
config: this.params.config,
|
|
1719
|
+
fullConfig: this.params.fullConfig,
|
|
1953
1720
|
mode: session.mode,
|
|
1954
1721
|
readOnly: options.readOnly,
|
|
1955
1722
|
trackedMeetingUrl: session.url,
|
|
@@ -1958,6 +1725,7 @@ var GoogleMeetRuntime = class {
|
|
|
1958
1725
|
}) : await recoverCurrentMeetTab({
|
|
1959
1726
|
runtime: this.params.runtime,
|
|
1960
1727
|
config: this.params.config,
|
|
1728
|
+
fullConfig: this.params.fullConfig,
|
|
1961
1729
|
mode: session.mode,
|
|
1962
1730
|
readOnly: options.readOnly,
|
|
1963
1731
|
trackedMeetingUrl: session.url,
|
|
@@ -2068,11 +1836,13 @@ var GoogleMeetRuntime = class {
|
|
|
2068
1836
|
runtime: this.params.runtime,
|
|
2069
1837
|
nodeId: session.chrome?.nodeId,
|
|
2070
1838
|
config: this.params.config,
|
|
1839
|
+
meetingSessionId: session.id,
|
|
2071
1840
|
meetingUrl: session.url,
|
|
2072
1841
|
tab
|
|
2073
1842
|
}) : await leaveChromeMeet({
|
|
2074
1843
|
runtime: this.params.runtime,
|
|
2075
1844
|
config: this.params.config,
|
|
1845
|
+
meetingSessionId: session.id,
|
|
2076
1846
|
meetingUrl: session.url,
|
|
2077
1847
|
tab
|
|
2078
1848
|
});
|
|
@@ -2082,107 +1852,367 @@ var GoogleMeetRuntime = class {
|
|
|
2082
1852
|
noteSession(session, `Browser control could not leave the Meet tab: ${formatErrorMessage(error)}`);
|
|
2083
1853
|
left = false;
|
|
2084
1854
|
}
|
|
2085
|
-
if (session.chrome && left)
|
|
1855
|
+
if (session.chrome && left) {
|
|
1856
|
+
session.chrome.browserTab = void 0;
|
|
1857
|
+
if (session.chrome.health) session.chrome.health = {
|
|
1858
|
+
...session.chrome.health,
|
|
1859
|
+
captioning: false,
|
|
1860
|
+
audioOutputRouted: false,
|
|
1861
|
+
providerConnected: false,
|
|
1862
|
+
realtimeReady: false,
|
|
1863
|
+
audioInputActive: false,
|
|
1864
|
+
audioOutputActive: false
|
|
1865
|
+
};
|
|
1866
|
+
}
|
|
2086
1867
|
session.browserLeft = left;
|
|
2087
1868
|
return left;
|
|
2088
1869
|
}
|
|
2089
1870
|
};
|
|
2090
1871
|
//#endregion
|
|
2091
|
-
//#region extensions/google-meet/
|
|
2092
|
-
const loadGoogleMeetCreateModule = createLazyRuntimeModule(() => import("./create-
|
|
2093
|
-
const loadGoogleMeetCliModule = createLazyRuntimeModule(() => import("./cli-
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
1872
|
+
//#region extensions/google-meet/src/plugin-helpers.ts
|
|
1873
|
+
const loadGoogleMeetCreateModule = createLazyRuntimeModule(() => import("./create-af1MDlus.js").then((n) => n.t));
|
|
1874
|
+
const loadGoogleMeetCliModule = createLazyRuntimeModule(() => import("./cli-GeuAaEIy.js"));
|
|
1875
|
+
function asParamRecord(params) {
|
|
1876
|
+
return params && typeof params === "object" && !Array.isArray(params) ? params : {};
|
|
1877
|
+
}
|
|
1878
|
+
function normalizeTransport(value) {
|
|
1879
|
+
return value === "chrome" || value === "chrome-node" || value === "twilio" ? value : void 0;
|
|
1880
|
+
}
|
|
1881
|
+
function normalizeMode(value) {
|
|
1882
|
+
if (value === "realtime") return "agent";
|
|
1883
|
+
return value === "agent" || value === "bidi" || value === "transcribe" ? value : void 0;
|
|
1884
|
+
}
|
|
1885
|
+
function resolveMeetingInput(config, value) {
|
|
1886
|
+
const meeting = normalizeOptionalString(value) ?? config.defaults.meeting;
|
|
1887
|
+
if (!meeting) throw new Error("Meeting input is required");
|
|
1888
|
+
return meeting;
|
|
1889
|
+
}
|
|
1890
|
+
function shouldJoinCreatedMeet(raw) {
|
|
1891
|
+
return raw.join !== false && raw.join !== "false";
|
|
1892
|
+
}
|
|
1893
|
+
const googleMeetToolDeps = {
|
|
1894
|
+
callGatewayFromCli,
|
|
1895
|
+
platform: () => process.platform
|
|
1896
|
+
};
|
|
1897
|
+
const testing = {
|
|
1898
|
+
setCallGatewayFromCliForTests(next) {
|
|
1899
|
+
googleMeetToolDeps.callGatewayFromCli = next ?? callGatewayFromCli;
|
|
2097
1900
|
},
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
}
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
|
|
2166
|
-
|
|
2167
|
-
|
|
2168
|
-
|
|
2169
|
-
|
|
2170
|
-
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
}
|
|
1901
|
+
setPlatformForTests(next) {
|
|
1902
|
+
googleMeetToolDeps.platform = next ?? (() => process.platform);
|
|
1903
|
+
},
|
|
1904
|
+
isGoogleMeetAgentToolActionUnsupportedOnHost,
|
|
1905
|
+
resolveGoogleMeetGatewayOperationTimeoutMs
|
|
1906
|
+
};
|
|
1907
|
+
function googleMeetGatewayMethodForToolAction(action) {
|
|
1908
|
+
switch (action) {
|
|
1909
|
+
case "recover_current_tab": return "googlemeet.recoverCurrentTab";
|
|
1910
|
+
case "setup_status": return "googlemeet.setup";
|
|
1911
|
+
case "test_speech": return "googlemeet.testSpeech";
|
|
1912
|
+
case "test_listen": return "googlemeet.testListen";
|
|
1913
|
+
case "end_active_conference": return "googlemeet.endActiveConference";
|
|
1914
|
+
default: return `googlemeet.${action}`;
|
|
1915
|
+
}
|
|
1916
|
+
}
|
|
1917
|
+
function isGoogleMeetAgentToolActionUnsupportedOnHost(params) {
|
|
1918
|
+
if ((params.platform ?? googleMeetToolDeps.platform()) === "darwin") return false;
|
|
1919
|
+
const action = params.raw.action;
|
|
1920
|
+
if (action !== "join" && action !== "test_speech" && !(action === "create" && shouldJoinCreatedMeet(params.raw))) return false;
|
|
1921
|
+
const transport = normalizeTransport(params.raw.transport) ?? params.config.defaultTransport;
|
|
1922
|
+
const mode = action === "test_speech" ? "agent" : normalizeMode(params.raw.mode) ?? params.config.defaultMode;
|
|
1923
|
+
return transport === "chrome" && MeetingPlatformAdapter.isTalkBackMode(mode);
|
|
1924
|
+
}
|
|
1925
|
+
function assertGoogleMeetAgentToolActionSupported(params) {
|
|
1926
|
+
if (!isGoogleMeetAgentToolActionUnsupportedOnHost(params)) return;
|
|
1927
|
+
throw new Error("Google Meet local Chrome talk-back audio is macOS-only. On this host, use mode: transcribe, transport: twilio, or transport: chrome-node backed by a macOS node.");
|
|
1928
|
+
}
|
|
1929
|
+
function readGatewayErrorDetails(err) {
|
|
1930
|
+
if (!err || typeof err !== "object" || !("details" in err)) return;
|
|
1931
|
+
return err.details;
|
|
1932
|
+
}
|
|
1933
|
+
async function callGoogleMeetGatewayFromTool(params) {
|
|
1934
|
+
try {
|
|
1935
|
+
if (params.runtime) return await params.runtime.gateway.request(googleMeetGatewayMethodForToolAction(params.action), params.raw, {
|
|
1936
|
+
timeoutMs: resolveGoogleMeetGatewayOperationTimeoutMs(params.config),
|
|
1937
|
+
scopes: ["operator.admin"]
|
|
1938
|
+
});
|
|
1939
|
+
return await googleMeetToolDeps.callGatewayFromCli(googleMeetGatewayMethodForToolAction(params.action), {
|
|
1940
|
+
json: true,
|
|
1941
|
+
timeout: String(resolveGoogleMeetGatewayOperationTimeoutMs(params.config))
|
|
1942
|
+
}, params.raw, {
|
|
1943
|
+
progress: false,
|
|
1944
|
+
scopes: ["operator.admin"]
|
|
1945
|
+
});
|
|
1946
|
+
} catch (err) {
|
|
1947
|
+
const details = readGatewayErrorDetails(err);
|
|
1948
|
+
if (details && typeof details === "object") return details;
|
|
1949
|
+
throw err;
|
|
1950
|
+
}
|
|
1951
|
+
}
|
|
1952
|
+
function keepTrustedToolAgentId(raw, client) {
|
|
1953
|
+
const { agentId: rawAgentId, ...rest } = raw;
|
|
1954
|
+
if (client?.internal?.pluginRuntimeOwnerId !== "google-meet") return rest;
|
|
1955
|
+
const agentId = normalizeOptionalString(rawAgentId);
|
|
1956
|
+
return agentId ? {
|
|
1957
|
+
...rest,
|
|
1958
|
+
agentId
|
|
1959
|
+
} : rest;
|
|
1960
|
+
}
|
|
1961
|
+
async function createMeetFromParams(params) {
|
|
1962
|
+
return (await loadGoogleMeetCreateModule()).createMeetFromParams(params);
|
|
1963
|
+
}
|
|
1964
|
+
async function createAndJoinMeetFromParams(params) {
|
|
1965
|
+
return (await loadGoogleMeetCreateModule()).createAndJoinMeetFromParams(params);
|
|
1966
|
+
}
|
|
1967
|
+
async function resolveGoogleMeetTokenFromParams(config, raw) {
|
|
1968
|
+
const { resolveGoogleMeetAccessToken } = await import("./oauth-DOfMKlGp.js");
|
|
1969
|
+
return resolveGoogleMeetAccessToken({
|
|
1970
|
+
clientId: normalizeOptionalString(raw.clientId) ?? config.oauth.clientId,
|
|
1971
|
+
clientSecret: normalizeOptionalString(raw.clientSecret) ?? config.oauth.clientSecret,
|
|
1972
|
+
refreshToken: normalizeOptionalString(raw.refreshToken) ?? config.oauth.refreshToken,
|
|
1973
|
+
accessToken: normalizeOptionalString(raw.accessToken) ?? config.oauth.accessToken,
|
|
1974
|
+
expiresAt: typeof raw.expiresAt === "number" ? raw.expiresAt : config.oauth.expiresAt
|
|
1975
|
+
});
|
|
1976
|
+
}
|
|
1977
|
+
function wantsCalendarLookup(raw) {
|
|
1978
|
+
return raw.today === true || Boolean(normalizeOptionalString(raw.event));
|
|
1979
|
+
}
|
|
1980
|
+
async function resolveMeetingFromParams(params) {
|
|
1981
|
+
if (wantsCalendarLookup(params.raw)) {
|
|
1982
|
+
const window = params.raw.today === true ? buildGoogleMeetCalendarDayWindow() : {};
|
|
1983
|
+
const calendarEvent = await findGoogleMeetCalendarEvent({
|
|
1984
|
+
accessToken: params.accessToken,
|
|
1985
|
+
calendarId: normalizeOptionalString(params.raw.calendarId),
|
|
1986
|
+
eventQuery: normalizeOptionalString(params.raw.event),
|
|
1987
|
+
...window
|
|
1988
|
+
});
|
|
1989
|
+
return {
|
|
1990
|
+
meeting: calendarEvent.meetingUri,
|
|
1991
|
+
calendarEvent
|
|
1992
|
+
};
|
|
1993
|
+
}
|
|
1994
|
+
return { meeting: resolveMeetingInput(params.config, params.raw.meeting) };
|
|
1995
|
+
}
|
|
1996
|
+
async function resolveSpaceFromParams(config, raw) {
|
|
1997
|
+
const token = await resolveGoogleMeetTokenFromParams(config, raw);
|
|
1998
|
+
const { meeting, calendarEvent } = await resolveMeetingFromParams({
|
|
1999
|
+
config,
|
|
2000
|
+
raw,
|
|
2001
|
+
accessToken: token.accessToken
|
|
2002
|
+
});
|
|
2003
|
+
return {
|
|
2004
|
+
meeting,
|
|
2005
|
+
token,
|
|
2006
|
+
space: await fetchGoogleMeetSpace({
|
|
2007
|
+
accessToken: token.accessToken,
|
|
2008
|
+
meeting
|
|
2009
|
+
}),
|
|
2010
|
+
calendarEvent
|
|
2011
|
+
};
|
|
2012
|
+
}
|
|
2013
|
+
async function resolveArtifactQueryFromParams(config, raw) {
|
|
2014
|
+
const meeting = normalizeOptionalString(raw.meeting) ?? config.defaults.meeting;
|
|
2015
|
+
const conferenceRecord = normalizeOptionalString(raw.conferenceRecord);
|
|
2016
|
+
const token = await resolveGoogleMeetTokenFromParams(config, raw);
|
|
2017
|
+
const resolvedMeeting = conferenceRecord ? { meeting } : wantsCalendarLookup(raw) ? await resolveMeetingFromParams({
|
|
2018
|
+
config,
|
|
2019
|
+
raw,
|
|
2020
|
+
accessToken: token.accessToken
|
|
2021
|
+
}) : { meeting };
|
|
2022
|
+
if (!resolvedMeeting.meeting && !conferenceRecord) throw new Error("Meeting input, calendar lookup, or conferenceRecord required");
|
|
2023
|
+
return {
|
|
2024
|
+
token,
|
|
2025
|
+
meeting: resolvedMeeting.meeting,
|
|
2026
|
+
calendarEvent: resolvedMeeting.calendarEvent,
|
|
2027
|
+
conferenceRecord,
|
|
2028
|
+
pageSize: readPositiveIntegerParam(raw, "pageSize"),
|
|
2029
|
+
includeTranscriptEntries: raw.includeTranscriptEntries !== false,
|
|
2030
|
+
includeDocumentBodies: raw.includeDocumentBodies === true,
|
|
2031
|
+
allConferenceRecords: raw.includeAllConferenceRecords === true,
|
|
2032
|
+
mergeDuplicateParticipants: raw.mergeDuplicateParticipants !== false,
|
|
2033
|
+
lateAfterMinutes: readPositiveIntegerParam(raw, "lateAfterMinutes"),
|
|
2034
|
+
earlyBeforeMinutes: readPositiveIntegerParam(raw, "earlyBeforeMinutes")
|
|
2035
|
+
};
|
|
2036
|
+
}
|
|
2037
|
+
async function exportGoogleMeetBundleFromParams(config, raw) {
|
|
2038
|
+
const resolved = await resolveArtifactQueryFromParams(config, raw);
|
|
2039
|
+
const [artifacts, attendance] = await Promise.all([fetchGoogleMeetArtifacts({
|
|
2040
|
+
accessToken: resolved.token.accessToken,
|
|
2041
|
+
meeting: resolved.meeting,
|
|
2042
|
+
conferenceRecord: resolved.conferenceRecord,
|
|
2043
|
+
pageSize: resolved.pageSize,
|
|
2044
|
+
includeTranscriptEntries: resolved.includeTranscriptEntries,
|
|
2045
|
+
includeDocumentBodies: resolved.includeDocumentBodies,
|
|
2046
|
+
allConferenceRecords: resolved.allConferenceRecords
|
|
2047
|
+
}), fetchGoogleMeetAttendance({
|
|
2048
|
+
accessToken: resolved.token.accessToken,
|
|
2049
|
+
meeting: resolved.meeting,
|
|
2050
|
+
conferenceRecord: resolved.conferenceRecord,
|
|
2051
|
+
pageSize: resolved.pageSize,
|
|
2052
|
+
allConferenceRecords: resolved.allConferenceRecords,
|
|
2053
|
+
mergeDuplicateParticipants: resolved.mergeDuplicateParticipants,
|
|
2054
|
+
lateAfterMinutes: resolved.lateAfterMinutes,
|
|
2055
|
+
earlyBeforeMinutes: resolved.earlyBeforeMinutes
|
|
2056
|
+
})]);
|
|
2057
|
+
const { buildGoogleMeetExportManifest, googleMeetExportFileNames, writeMeetExportBundle } = await loadGoogleMeetCliModule();
|
|
2058
|
+
const calendarId = normalizeOptionalString(raw.calendarId);
|
|
2059
|
+
const request = {
|
|
2060
|
+
...resolved.meeting ? { meeting: resolved.meeting } : {},
|
|
2061
|
+
...resolved.conferenceRecord ? { conferenceRecord: resolved.conferenceRecord } : {},
|
|
2062
|
+
...resolved.calendarEvent?.event.id ? { calendarEventId: resolved.calendarEvent.event.id } : {},
|
|
2063
|
+
...resolved.calendarEvent?.event.summary ? { calendarEventSummary: resolved.calendarEvent.event.summary } : {},
|
|
2064
|
+
...calendarId ? { calendarId } : {},
|
|
2065
|
+
...resolved.pageSize !== void 0 ? { pageSize: resolved.pageSize } : {},
|
|
2066
|
+
includeTranscriptEntries: resolved.includeTranscriptEntries,
|
|
2067
|
+
includeDocumentBodies: resolved.includeDocumentBodies,
|
|
2068
|
+
allConferenceRecords: resolved.allConferenceRecords,
|
|
2069
|
+
mergeDuplicateParticipants: resolved.mergeDuplicateParticipants,
|
|
2070
|
+
...resolved.lateAfterMinutes !== void 0 ? { lateAfterMinutes: resolved.lateAfterMinutes } : {},
|
|
2071
|
+
...resolved.earlyBeforeMinutes !== void 0 ? { earlyBeforeMinutes: resolved.earlyBeforeMinutes } : {}
|
|
2072
|
+
};
|
|
2073
|
+
const tokenSource = resolved.token.refreshed ? "refresh-token" : "cached-access-token";
|
|
2074
|
+
if (raw.dryRun === true) return {
|
|
2075
|
+
dryRun: true,
|
|
2076
|
+
manifest: buildGoogleMeetExportManifest({
|
|
2077
|
+
artifacts,
|
|
2078
|
+
attendance,
|
|
2079
|
+
files: googleMeetExportFileNames(),
|
|
2080
|
+
request,
|
|
2081
|
+
tokenSource,
|
|
2082
|
+
...resolved.calendarEvent ? { calendarEvent: resolved.calendarEvent } : {}
|
|
2083
|
+
}),
|
|
2084
|
+
...resolved.calendarEvent ? { calendarEvent: resolved.calendarEvent } : {},
|
|
2085
|
+
tokenSource
|
|
2086
|
+
};
|
|
2087
|
+
const outputDir = normalizeOptionalString(raw.outputDir) ?? normalizeOptionalString(raw.output);
|
|
2088
|
+
return {
|
|
2089
|
+
...await writeMeetExportBundle({
|
|
2090
|
+
...outputDir ? { outputDir } : {},
|
|
2091
|
+
artifacts,
|
|
2092
|
+
attendance,
|
|
2093
|
+
zip: raw.zip === true,
|
|
2094
|
+
request,
|
|
2095
|
+
tokenSource,
|
|
2096
|
+
...resolved.calendarEvent ? { calendarEvent: resolved.calendarEvent } : {}
|
|
2097
|
+
}),
|
|
2098
|
+
...resolved.calendarEvent ? { calendarEvent: resolved.calendarEvent } : {},
|
|
2099
|
+
tokenSource
|
|
2100
|
+
};
|
|
2101
|
+
}
|
|
2102
|
+
function createGoogleMeetRuntimeAccessor(params) {
|
|
2103
|
+
let runtime = null;
|
|
2104
|
+
return async () => {
|
|
2105
|
+
if (!params.config.enabled) throw new Error("Google Meet plugin disabled in plugin config");
|
|
2106
|
+
if (!runtime) runtime = new GoogleMeetRuntime({
|
|
2107
|
+
config: params.config,
|
|
2108
|
+
fullConfig: params.api.config,
|
|
2109
|
+
runtime: params.api.runtime,
|
|
2110
|
+
logger: params.api.logger
|
|
2111
|
+
});
|
|
2112
|
+
return runtime;
|
|
2113
|
+
};
|
|
2114
|
+
}
|
|
2115
|
+
function formatGoogleMeetGatewayError(err) {
|
|
2116
|
+
return isGoogleMeetBrowserManualActionError(err) ? err.payload : { error: formatErrorMessage(err) };
|
|
2117
|
+
}
|
|
2118
|
+
function sendGoogleMeetGatewayError(respond, err, code = ErrorCodes.UNAVAILABLE) {
|
|
2119
|
+
const payload = formatGoogleMeetGatewayError(err);
|
|
2120
|
+
respond(false, payload, errorShape(code, typeof payload.error === "string" ? payload.error : "Google Meet request failed", { details: payload }));
|
|
2121
|
+
}
|
|
2122
|
+
//#endregion
|
|
2123
|
+
//#region extensions/google-meet/src/plugin-schema.ts
|
|
2124
|
+
const googleMeetConfigSchema = {
|
|
2125
|
+
parse(value) {
|
|
2126
|
+
return resolveGoogleMeetConfig(value);
|
|
2127
|
+
},
|
|
2128
|
+
uiHints: {
|
|
2129
|
+
"defaults.meeting": {
|
|
2130
|
+
label: "Default Meeting",
|
|
2131
|
+
help: "Meet URL, meeting code, or spaces/{id} used when CLI commands omit a meeting."
|
|
2132
|
+
},
|
|
2133
|
+
"preview.enrollmentAcknowledged": {
|
|
2134
|
+
label: "Preview Acknowledged",
|
|
2135
|
+
help: "Confirms you understand the Google Meet Media API is still Developer Preview.",
|
|
2136
|
+
advanced: true
|
|
2137
|
+
},
|
|
2138
|
+
defaultTransport: {
|
|
2139
|
+
label: "Default Transport",
|
|
2140
|
+
help: "Chrome uses a signed-in browser profile. Chrome-node runs Chrome on a paired node. Twilio uses Meet dial-in numbers."
|
|
2141
|
+
},
|
|
2142
|
+
defaultMode: {
|
|
2143
|
+
label: "Default Mode",
|
|
2144
|
+
help: "Agent uses realtime transcription plus regular OpenClaw TTS. Bidi uses the realtime voice model directly. Transcribe observes only."
|
|
2145
|
+
},
|
|
2146
|
+
"chrome.audioBackend": {
|
|
2147
|
+
label: "Chrome Audio Backend",
|
|
2148
|
+
help: "BlackHole 2ch is required for local duplex audio routing."
|
|
2149
|
+
},
|
|
2150
|
+
"chrome.launch": { label: "Launch Chrome" },
|
|
2151
|
+
"chrome.browserProfile": {
|
|
2152
|
+
label: "Chrome Profile",
|
|
2153
|
+
advanced: true
|
|
2154
|
+
},
|
|
2155
|
+
"chrome.guestName": {
|
|
2156
|
+
label: "Guest Name",
|
|
2157
|
+
help: "Used when Chrome lands on the signed-out Meet guest-name screen."
|
|
2158
|
+
},
|
|
2159
|
+
"chrome.reuseExistingTab": {
|
|
2160
|
+
label: "Reuse Existing Meet Tab",
|
|
2161
|
+
help: "Avoids opening duplicate tabs for the same Meet URL."
|
|
2162
|
+
},
|
|
2163
|
+
"chrome.autoJoin": {
|
|
2164
|
+
label: "Auto Join Guest Screen",
|
|
2165
|
+
help: "Best-effort guest-name fill and Join Now click through OpenClaw browser automation."
|
|
2166
|
+
},
|
|
2167
|
+
"chrome.waitForInCallMs": {
|
|
2168
|
+
label: "Wait For In-Call (ms)",
|
|
2169
|
+
help: "Waits for Chrome to report that the Meet tab is in-call before the realtime intro speaks.",
|
|
2170
|
+
advanced: true
|
|
2171
|
+
},
|
|
2172
|
+
"chrome.audioFormat": {
|
|
2173
|
+
label: "Audio Format",
|
|
2174
|
+
help: "Command-pair audio format. PCM16 24 kHz is the default Chrome/Meet path; G.711 mu-law 8 kHz remains available for legacy command pairs.",
|
|
2175
|
+
advanced: true
|
|
2176
|
+
},
|
|
2177
|
+
"chrome.audioBufferBytes": {
|
|
2178
|
+
label: "Audio Buffer Bytes",
|
|
2179
|
+
help: "SoX processing buffer for generated Chrome command-pair audio commands. Lower values reduce latency but may underrun on busy hosts.",
|
|
2180
|
+
advanced: true
|
|
2181
|
+
},
|
|
2182
|
+
"chrome.audioInputCommand": {
|
|
2183
|
+
label: "Audio Input Command",
|
|
2184
|
+
help: "Command that writes meeting audio to stdout in chrome.audioFormat.",
|
|
2185
|
+
advanced: true
|
|
2186
|
+
},
|
|
2187
|
+
"chrome.audioOutputCommand": {
|
|
2188
|
+
label: "Audio Output Command",
|
|
2189
|
+
help: "Command that reads assistant audio from stdin in chrome.audioFormat.",
|
|
2190
|
+
advanced: true
|
|
2191
|
+
},
|
|
2192
|
+
"chrome.bargeInInputCommand": {
|
|
2193
|
+
label: "Barge-In Input Command",
|
|
2194
|
+
help: "Optional Gateway-hosted microphone command that writes signed 16-bit little-endian mono PCM for human interruption detection while assistant playback is active.",
|
|
2195
|
+
advanced: true
|
|
2196
|
+
},
|
|
2197
|
+
"chrome.bargeInRmsThreshold": {
|
|
2198
|
+
label: "Barge-In RMS Threshold",
|
|
2199
|
+
help: "RMS level on chrome.bargeInInputCommand that counts as a human interruption.",
|
|
2200
|
+
advanced: true
|
|
2201
|
+
},
|
|
2202
|
+
"chrome.bargeInPeakThreshold": {
|
|
2203
|
+
label: "Barge-In Peak Threshold",
|
|
2204
|
+
help: "Peak level on chrome.bargeInInputCommand that counts as a human interruption.",
|
|
2205
|
+
advanced: true
|
|
2206
|
+
},
|
|
2207
|
+
"chrome.bargeInCooldownMs": {
|
|
2208
|
+
label: "Barge-In Cooldown (ms)",
|
|
2209
|
+
help: "Minimum delay between repeated barge-in clears.",
|
|
2210
|
+
advanced: true
|
|
2211
|
+
},
|
|
2212
|
+
"chrome.audioBridgeCommand": {
|
|
2213
|
+
label: "Audio Bridge Command",
|
|
2214
|
+
advanced: true
|
|
2215
|
+
},
|
|
2186
2216
|
"chrome.audioBridgeHealthCommand": {
|
|
2187
2217
|
label: "Audio Bridge Health Command",
|
|
2188
2218
|
advanced: true
|
|
@@ -2377,236 +2407,8 @@ const GoogleMeetToolSchema = Type.Object({
|
|
|
2377
2407
|
clientSecret: Type.Optional(Type.String({ description: "OAuth client secret override" })),
|
|
2378
2408
|
expiresAt: Type.Optional(Type.Number({ description: "Cached access token expiry ms" }))
|
|
2379
2409
|
});
|
|
2380
|
-
|
|
2381
|
-
|
|
2382
|
-
}
|
|
2383
|
-
function normalizeTransport(value) {
|
|
2384
|
-
return value === "chrome" || value === "chrome-node" || value === "twilio" ? value : void 0;
|
|
2385
|
-
}
|
|
2386
|
-
function normalizeMode(value) {
|
|
2387
|
-
if (value === "realtime") return "agent";
|
|
2388
|
-
return value === "agent" || value === "bidi" || value === "transcribe" ? value : void 0;
|
|
2389
|
-
}
|
|
2390
|
-
function isGoogleMeetTalkBackMode(mode) {
|
|
2391
|
-
return mode === "agent" || mode === "bidi";
|
|
2392
|
-
}
|
|
2393
|
-
function resolveMeetingInput(config, value) {
|
|
2394
|
-
const meeting = normalizeOptionalString(value) ?? config.defaults.meeting;
|
|
2395
|
-
if (!meeting) throw new Error("Meeting input is required");
|
|
2396
|
-
return meeting;
|
|
2397
|
-
}
|
|
2398
|
-
function shouldJoinCreatedMeet(raw) {
|
|
2399
|
-
return raw.join !== false && raw.join !== "false";
|
|
2400
|
-
}
|
|
2401
|
-
const googleMeetToolDeps = {
|
|
2402
|
-
callGatewayFromCli,
|
|
2403
|
-
platform: () => process.platform
|
|
2404
|
-
};
|
|
2405
|
-
const testing = {
|
|
2406
|
-
setCallGatewayFromCliForTests(next) {
|
|
2407
|
-
googleMeetToolDeps.callGatewayFromCli = next ?? callGatewayFromCli;
|
|
2408
|
-
},
|
|
2409
|
-
setPlatformForTests(next) {
|
|
2410
|
-
googleMeetToolDeps.platform = next ?? (() => process.platform);
|
|
2411
|
-
},
|
|
2412
|
-
isGoogleMeetAgentToolActionUnsupportedOnHost,
|
|
2413
|
-
resolveGoogleMeetGatewayOperationTimeoutMs
|
|
2414
|
-
};
|
|
2415
|
-
function googleMeetGatewayMethodForToolAction(action) {
|
|
2416
|
-
switch (action) {
|
|
2417
|
-
case "recover_current_tab": return "googlemeet.recoverCurrentTab";
|
|
2418
|
-
case "setup_status": return "googlemeet.setup";
|
|
2419
|
-
case "test_speech": return "googlemeet.testSpeech";
|
|
2420
|
-
case "test_listen": return "googlemeet.testListen";
|
|
2421
|
-
case "end_active_conference": return "googlemeet.endActiveConference";
|
|
2422
|
-
default: return `googlemeet.${action}`;
|
|
2423
|
-
}
|
|
2424
|
-
}
|
|
2425
|
-
function isGoogleMeetAgentToolActionUnsupportedOnHost(params) {
|
|
2426
|
-
if ((params.platform ?? googleMeetToolDeps.platform()) === "darwin") return false;
|
|
2427
|
-
const action = params.raw.action;
|
|
2428
|
-
if (action !== "join" && action !== "test_speech" && !(action === "create" && shouldJoinCreatedMeet(params.raw))) return false;
|
|
2429
|
-
const transport = normalizeTransport(params.raw.transport) ?? params.config.defaultTransport;
|
|
2430
|
-
const mode = action === "test_speech" ? "agent" : normalizeMode(params.raw.mode) ?? params.config.defaultMode;
|
|
2431
|
-
return transport === "chrome" && isGoogleMeetTalkBackMode(mode);
|
|
2432
|
-
}
|
|
2433
|
-
function assertGoogleMeetAgentToolActionSupported(params) {
|
|
2434
|
-
if (!isGoogleMeetAgentToolActionUnsupportedOnHost(params)) return;
|
|
2435
|
-
throw new Error("Google Meet local Chrome talk-back audio is macOS-only. On this host, use mode: transcribe, transport: twilio, or transport: chrome-node backed by a macOS node.");
|
|
2436
|
-
}
|
|
2437
|
-
function readGatewayErrorDetails(err) {
|
|
2438
|
-
if (!err || typeof err !== "object" || !("details" in err)) return;
|
|
2439
|
-
return err.details;
|
|
2440
|
-
}
|
|
2441
|
-
async function callGoogleMeetGatewayFromTool(params) {
|
|
2442
|
-
try {
|
|
2443
|
-
if (params.runtime) return await params.runtime.gateway.request(googleMeetGatewayMethodForToolAction(params.action), params.raw, {
|
|
2444
|
-
timeoutMs: resolveGoogleMeetGatewayOperationTimeoutMs(params.config),
|
|
2445
|
-
scopes: ["operator.admin"]
|
|
2446
|
-
});
|
|
2447
|
-
return await googleMeetToolDeps.callGatewayFromCli(googleMeetGatewayMethodForToolAction(params.action), {
|
|
2448
|
-
json: true,
|
|
2449
|
-
timeout: String(resolveGoogleMeetGatewayOperationTimeoutMs(params.config))
|
|
2450
|
-
}, params.raw, {
|
|
2451
|
-
progress: false,
|
|
2452
|
-
scopes: ["operator.admin"]
|
|
2453
|
-
});
|
|
2454
|
-
} catch (err) {
|
|
2455
|
-
const details = readGatewayErrorDetails(err);
|
|
2456
|
-
if (details && typeof details === "object") return details;
|
|
2457
|
-
throw err;
|
|
2458
|
-
}
|
|
2459
|
-
}
|
|
2460
|
-
function keepTrustedToolAgentId(raw, client) {
|
|
2461
|
-
const { agentId: rawAgentId, ...rest } = raw;
|
|
2462
|
-
if (client?.internal?.pluginRuntimeOwnerId !== "google-meet") return rest;
|
|
2463
|
-
const agentId = normalizeOptionalString(rawAgentId);
|
|
2464
|
-
return agentId ? {
|
|
2465
|
-
...rest,
|
|
2466
|
-
agentId
|
|
2467
|
-
} : rest;
|
|
2468
|
-
}
|
|
2469
|
-
async function createMeetFromParams(params) {
|
|
2470
|
-
return (await loadGoogleMeetCreateModule()).createMeetFromParams(params);
|
|
2471
|
-
}
|
|
2472
|
-
async function createAndJoinMeetFromParams(params) {
|
|
2473
|
-
return (await loadGoogleMeetCreateModule()).createAndJoinMeetFromParams(params);
|
|
2474
|
-
}
|
|
2475
|
-
async function resolveGoogleMeetTokenFromParams(config, raw) {
|
|
2476
|
-
const { resolveGoogleMeetAccessToken } = await import("./oauth-DOfMKlGp.js");
|
|
2477
|
-
return resolveGoogleMeetAccessToken({
|
|
2478
|
-
clientId: normalizeOptionalString(raw.clientId) ?? config.oauth.clientId,
|
|
2479
|
-
clientSecret: normalizeOptionalString(raw.clientSecret) ?? config.oauth.clientSecret,
|
|
2480
|
-
refreshToken: normalizeOptionalString(raw.refreshToken) ?? config.oauth.refreshToken,
|
|
2481
|
-
accessToken: normalizeOptionalString(raw.accessToken) ?? config.oauth.accessToken,
|
|
2482
|
-
expiresAt: typeof raw.expiresAt === "number" ? raw.expiresAt : config.oauth.expiresAt
|
|
2483
|
-
});
|
|
2484
|
-
}
|
|
2485
|
-
function wantsCalendarLookup(raw) {
|
|
2486
|
-
return raw.today === true || Boolean(normalizeOptionalString(raw.event));
|
|
2487
|
-
}
|
|
2488
|
-
async function resolveMeetingFromParams(params) {
|
|
2489
|
-
if (wantsCalendarLookup(params.raw)) {
|
|
2490
|
-
const window = params.raw.today === true ? buildGoogleMeetCalendarDayWindow() : {};
|
|
2491
|
-
const calendarEvent = await findGoogleMeetCalendarEvent({
|
|
2492
|
-
accessToken: params.accessToken,
|
|
2493
|
-
calendarId: normalizeOptionalString(params.raw.calendarId),
|
|
2494
|
-
eventQuery: normalizeOptionalString(params.raw.event),
|
|
2495
|
-
...window
|
|
2496
|
-
});
|
|
2497
|
-
return {
|
|
2498
|
-
meeting: calendarEvent.meetingUri,
|
|
2499
|
-
calendarEvent
|
|
2500
|
-
};
|
|
2501
|
-
}
|
|
2502
|
-
return { meeting: resolveMeetingInput(params.config, params.raw.meeting) };
|
|
2503
|
-
}
|
|
2504
|
-
async function resolveSpaceFromParams(config, raw) {
|
|
2505
|
-
const token = await resolveGoogleMeetTokenFromParams(config, raw);
|
|
2506
|
-
const { meeting, calendarEvent } = await resolveMeetingFromParams({
|
|
2507
|
-
config,
|
|
2508
|
-
raw,
|
|
2509
|
-
accessToken: token.accessToken
|
|
2510
|
-
});
|
|
2511
|
-
return {
|
|
2512
|
-
meeting,
|
|
2513
|
-
token,
|
|
2514
|
-
space: await fetchGoogleMeetSpace({
|
|
2515
|
-
accessToken: token.accessToken,
|
|
2516
|
-
meeting
|
|
2517
|
-
}),
|
|
2518
|
-
calendarEvent
|
|
2519
|
-
};
|
|
2520
|
-
}
|
|
2521
|
-
async function resolveArtifactQueryFromParams(config, raw) {
|
|
2522
|
-
const meeting = normalizeOptionalString(raw.meeting) ?? config.defaults.meeting;
|
|
2523
|
-
const conferenceRecord = normalizeOptionalString(raw.conferenceRecord);
|
|
2524
|
-
const token = await resolveGoogleMeetTokenFromParams(config, raw);
|
|
2525
|
-
const resolvedMeeting = conferenceRecord ? { meeting } : wantsCalendarLookup(raw) ? await resolveMeetingFromParams({
|
|
2526
|
-
config,
|
|
2527
|
-
raw,
|
|
2528
|
-
accessToken: token.accessToken
|
|
2529
|
-
}) : { meeting };
|
|
2530
|
-
if (!resolvedMeeting.meeting && !conferenceRecord) throw new Error("Meeting input, calendar lookup, or conferenceRecord required");
|
|
2531
|
-
return {
|
|
2532
|
-
token,
|
|
2533
|
-
meeting: resolvedMeeting.meeting,
|
|
2534
|
-
calendarEvent: resolvedMeeting.calendarEvent,
|
|
2535
|
-
conferenceRecord,
|
|
2536
|
-
pageSize: readPositiveIntegerParam(raw, "pageSize"),
|
|
2537
|
-
includeTranscriptEntries: raw.includeTranscriptEntries !== false,
|
|
2538
|
-
includeDocumentBodies: raw.includeDocumentBodies === true,
|
|
2539
|
-
allConferenceRecords: raw.includeAllConferenceRecords === true,
|
|
2540
|
-
mergeDuplicateParticipants: raw.mergeDuplicateParticipants !== false,
|
|
2541
|
-
lateAfterMinutes: readPositiveIntegerParam(raw, "lateAfterMinutes"),
|
|
2542
|
-
earlyBeforeMinutes: readPositiveIntegerParam(raw, "earlyBeforeMinutes")
|
|
2543
|
-
};
|
|
2544
|
-
}
|
|
2545
|
-
async function exportGoogleMeetBundleFromParams(config, raw) {
|
|
2546
|
-
const resolved = await resolveArtifactQueryFromParams(config, raw);
|
|
2547
|
-
const [artifacts, attendance] = await Promise.all([fetchGoogleMeetArtifacts({
|
|
2548
|
-
accessToken: resolved.token.accessToken,
|
|
2549
|
-
meeting: resolved.meeting,
|
|
2550
|
-
conferenceRecord: resolved.conferenceRecord,
|
|
2551
|
-
pageSize: resolved.pageSize,
|
|
2552
|
-
includeTranscriptEntries: resolved.includeTranscriptEntries,
|
|
2553
|
-
includeDocumentBodies: resolved.includeDocumentBodies,
|
|
2554
|
-
allConferenceRecords: resolved.allConferenceRecords
|
|
2555
|
-
}), fetchGoogleMeetAttendance({
|
|
2556
|
-
accessToken: resolved.token.accessToken,
|
|
2557
|
-
meeting: resolved.meeting,
|
|
2558
|
-
conferenceRecord: resolved.conferenceRecord,
|
|
2559
|
-
pageSize: resolved.pageSize,
|
|
2560
|
-
allConferenceRecords: resolved.allConferenceRecords,
|
|
2561
|
-
mergeDuplicateParticipants: resolved.mergeDuplicateParticipants,
|
|
2562
|
-
lateAfterMinutes: resolved.lateAfterMinutes,
|
|
2563
|
-
earlyBeforeMinutes: resolved.earlyBeforeMinutes
|
|
2564
|
-
})]);
|
|
2565
|
-
const { buildGoogleMeetExportManifest, googleMeetExportFileNames, writeMeetExportBundle } = await loadGoogleMeetCliModule();
|
|
2566
|
-
const calendarId = normalizeOptionalString(raw.calendarId);
|
|
2567
|
-
const request = {
|
|
2568
|
-
...resolved.meeting ? { meeting: resolved.meeting } : {},
|
|
2569
|
-
...resolved.conferenceRecord ? { conferenceRecord: resolved.conferenceRecord } : {},
|
|
2570
|
-
...resolved.calendarEvent?.event.id ? { calendarEventId: resolved.calendarEvent.event.id } : {},
|
|
2571
|
-
...resolved.calendarEvent?.event.summary ? { calendarEventSummary: resolved.calendarEvent.event.summary } : {},
|
|
2572
|
-
...calendarId ? { calendarId } : {},
|
|
2573
|
-
...resolved.pageSize !== void 0 ? { pageSize: resolved.pageSize } : {},
|
|
2574
|
-
includeTranscriptEntries: resolved.includeTranscriptEntries,
|
|
2575
|
-
includeDocumentBodies: resolved.includeDocumentBodies,
|
|
2576
|
-
allConferenceRecords: resolved.allConferenceRecords,
|
|
2577
|
-
mergeDuplicateParticipants: resolved.mergeDuplicateParticipants,
|
|
2578
|
-
...resolved.lateAfterMinutes !== void 0 ? { lateAfterMinutes: resolved.lateAfterMinutes } : {},
|
|
2579
|
-
...resolved.earlyBeforeMinutes !== void 0 ? { earlyBeforeMinutes: resolved.earlyBeforeMinutes } : {}
|
|
2580
|
-
};
|
|
2581
|
-
const tokenSource = resolved.token.refreshed ? "refresh-token" : "cached-access-token";
|
|
2582
|
-
if (raw.dryRun === true) return {
|
|
2583
|
-
dryRun: true,
|
|
2584
|
-
manifest: buildGoogleMeetExportManifest({
|
|
2585
|
-
artifacts,
|
|
2586
|
-
attendance,
|
|
2587
|
-
files: googleMeetExportFileNames(),
|
|
2588
|
-
request,
|
|
2589
|
-
tokenSource,
|
|
2590
|
-
...resolved.calendarEvent ? { calendarEvent: resolved.calendarEvent } : {}
|
|
2591
|
-
}),
|
|
2592
|
-
...resolved.calendarEvent ? { calendarEvent: resolved.calendarEvent } : {},
|
|
2593
|
-
tokenSource
|
|
2594
|
-
};
|
|
2595
|
-
const outputDir = normalizeOptionalString(raw.outputDir) ?? normalizeOptionalString(raw.output);
|
|
2596
|
-
return {
|
|
2597
|
-
...await writeMeetExportBundle({
|
|
2598
|
-
...outputDir ? { outputDir } : {},
|
|
2599
|
-
artifacts,
|
|
2600
|
-
attendance,
|
|
2601
|
-
zip: raw.zip === true,
|
|
2602
|
-
request,
|
|
2603
|
-
tokenSource,
|
|
2604
|
-
...resolved.calendarEvent ? { calendarEvent: resolved.calendarEvent } : {}
|
|
2605
|
-
}),
|
|
2606
|
-
...resolved.calendarEvent ? { calendarEvent: resolved.calendarEvent } : {},
|
|
2607
|
-
tokenSource
|
|
2608
|
-
};
|
|
2609
|
-
}
|
|
2410
|
+
//#endregion
|
|
2411
|
+
//#region extensions/google-meet/index.ts
|
|
2610
2412
|
var google_meet_default = definePluginEntry({
|
|
2611
2413
|
id: "google-meet",
|
|
2612
2414
|
name: "Google Meet",
|
|
@@ -2614,22 +2416,16 @@ var google_meet_default = definePluginEntry({
|
|
|
2614
2416
|
configSchema: googleMeetConfigSchema,
|
|
2615
2417
|
register(api) {
|
|
2616
2418
|
const config = googleMeetConfigSchema.parse(api.pluginConfig);
|
|
2617
|
-
|
|
2618
|
-
|
|
2619
|
-
|
|
2620
|
-
|
|
2621
|
-
|
|
2622
|
-
|
|
2623
|
-
|
|
2624
|
-
|
|
2625
|
-
|
|
2626
|
-
|
|
2627
|
-
};
|
|
2628
|
-
const formatGatewayError = (err) => isGoogleMeetBrowserManualActionError(err) ? err.payload : { error: formatErrorMessage(err) };
|
|
2629
|
-
const sendError = (respond, err, code = ErrorCodes.UNAVAILABLE) => {
|
|
2630
|
-
const payload = formatGatewayError(err);
|
|
2631
|
-
respond(false, payload, errorShape(code, typeof payload.error === "string" ? payload.error : "Google Meet request failed", { details: payload }));
|
|
2632
|
-
};
|
|
2419
|
+
const ensureRuntime = createGoogleMeetRuntimeAccessor({
|
|
2420
|
+
api,
|
|
2421
|
+
config
|
|
2422
|
+
});
|
|
2423
|
+
api.registerTranscriptSourceProvider(createMeetingTranscriptSourceProvider({
|
|
2424
|
+
id: "google-meet",
|
|
2425
|
+
aliases: ["googlemeet", "meet"],
|
|
2426
|
+
name: "Google Meet",
|
|
2427
|
+
runtime: async () => (await ensureRuntime()).transcriptSourceRuntime()
|
|
2428
|
+
}));
|
|
2633
2429
|
api.registerGatewayMethod("googlemeet.join", async ({ params, client, respond }) => {
|
|
2634
2430
|
try {
|
|
2635
2431
|
const trustedParams = keepTrustedToolAgentId(asParamRecord(params), client);
|
|
@@ -2645,7 +2441,7 @@ var google_meet_default = definePluginEntry({
|
|
|
2645
2441
|
agentId: normalizeOptionalString(trustedParams.agentId)
|
|
2646
2442
|
}));
|
|
2647
2443
|
} catch (err) {
|
|
2648
|
-
|
|
2444
|
+
sendGoogleMeetGatewayError(respond, err);
|
|
2649
2445
|
}
|
|
2650
2446
|
});
|
|
2651
2447
|
api.registerGatewayMethod("googlemeet.create", async ({ params, client, respond }) => {
|
|
@@ -2662,31 +2458,31 @@ var google_meet_default = definePluginEntry({
|
|
|
2662
2458
|
raw
|
|
2663
2459
|
}));
|
|
2664
2460
|
} catch (err) {
|
|
2665
|
-
|
|
2461
|
+
sendGoogleMeetGatewayError(respond, err);
|
|
2666
2462
|
}
|
|
2667
2463
|
});
|
|
2668
2464
|
api.registerGatewayMethod("googlemeet.status", async ({ params, respond }) => {
|
|
2669
2465
|
try {
|
|
2670
2466
|
respond(true, await (await ensureRuntime()).status(normalizeOptionalString(params?.sessionId)));
|
|
2671
2467
|
} catch (err) {
|
|
2672
|
-
|
|
2468
|
+
sendGoogleMeetGatewayError(respond, err);
|
|
2673
2469
|
}
|
|
2674
2470
|
});
|
|
2675
2471
|
api.registerGatewayMethod("googlemeet.transcript", async ({ params, respond }) => {
|
|
2676
2472
|
try {
|
|
2677
2473
|
const sessionId = normalizeOptionalString(params?.sessionId);
|
|
2678
2474
|
if (!sessionId) {
|
|
2679
|
-
|
|
2475
|
+
sendGoogleMeetGatewayError(respond, /* @__PURE__ */ new Error("sessionId required"), ErrorCodes.INVALID_REQUEST);
|
|
2680
2476
|
return;
|
|
2681
2477
|
}
|
|
2682
2478
|
const sinceIndex = params?.sinceIndex;
|
|
2683
2479
|
if (sinceIndex !== void 0 && (typeof sinceIndex !== "number" || !Number.isSafeInteger(sinceIndex) || sinceIndex < 0)) {
|
|
2684
|
-
|
|
2480
|
+
sendGoogleMeetGatewayError(respond, /* @__PURE__ */ new Error("sinceIndex must be a non-negative safe integer"), ErrorCodes.INVALID_REQUEST);
|
|
2685
2481
|
return;
|
|
2686
2482
|
}
|
|
2687
2483
|
respond(true, await (await ensureRuntime()).transcript(sessionId, sinceIndex === void 0 ? {} : { sinceIndex }));
|
|
2688
2484
|
} catch (err) {
|
|
2689
|
-
|
|
2485
|
+
sendGoogleMeetGatewayError(respond, err);
|
|
2690
2486
|
}
|
|
2691
2487
|
});
|
|
2692
2488
|
api.registerGatewayMethod("googlemeet.recoverCurrentTab", async ({ params, respond }) => {
|
|
@@ -2696,7 +2492,7 @@ var google_meet_default = definePluginEntry({
|
|
|
2696
2492
|
transport: normalizeTransport(params?.transport)
|
|
2697
2493
|
}));
|
|
2698
2494
|
} catch (err) {
|
|
2699
|
-
|
|
2495
|
+
sendGoogleMeetGatewayError(respond, err);
|
|
2700
2496
|
}
|
|
2701
2497
|
});
|
|
2702
2498
|
api.registerGatewayMethod("googlemeet.setup", async ({ params, respond }) => {
|
|
@@ -2707,7 +2503,7 @@ var google_meet_default = definePluginEntry({
|
|
|
2707
2503
|
dialInNumber: normalizeOptionalString(params?.dialInNumber)
|
|
2708
2504
|
}));
|
|
2709
2505
|
} catch (err) {
|
|
2710
|
-
|
|
2506
|
+
sendGoogleMeetGatewayError(respond, err);
|
|
2711
2507
|
}
|
|
2712
2508
|
});
|
|
2713
2509
|
api.registerGatewayMethod("googlemeet.latest", async ({ params, respond }) => {
|
|
@@ -2727,7 +2523,7 @@ var google_meet_default = definePluginEntry({
|
|
|
2727
2523
|
...resolved.calendarEvent ? { calendarEvent: resolved.calendarEvent } : {}
|
|
2728
2524
|
});
|
|
2729
2525
|
} catch (err) {
|
|
2730
|
-
|
|
2526
|
+
sendGoogleMeetGatewayError(respond, err);
|
|
2731
2527
|
}
|
|
2732
2528
|
});
|
|
2733
2529
|
api.registerGatewayMethod("googlemeet.calendarEvents", async ({ params, respond }) => {
|
|
@@ -2742,7 +2538,7 @@ var google_meet_default = definePluginEntry({
|
|
|
2742
2538
|
...window
|
|
2743
2539
|
}));
|
|
2744
2540
|
} catch (err) {
|
|
2745
|
-
|
|
2541
|
+
sendGoogleMeetGatewayError(respond, err);
|
|
2746
2542
|
}
|
|
2747
2543
|
});
|
|
2748
2544
|
api.registerGatewayMethod("googlemeet.artifacts", async ({ params, respond }) => {
|
|
@@ -2759,7 +2555,7 @@ var google_meet_default = definePluginEntry({
|
|
|
2759
2555
|
allConferenceRecords: resolved.allConferenceRecords
|
|
2760
2556
|
}));
|
|
2761
2557
|
} catch (err) {
|
|
2762
|
-
|
|
2558
|
+
sendGoogleMeetGatewayError(respond, err);
|
|
2763
2559
|
}
|
|
2764
2560
|
});
|
|
2765
2561
|
api.registerGatewayMethod("googlemeet.attendance", async ({ params, respond }) => {
|
|
@@ -2777,26 +2573,26 @@ var google_meet_default = definePluginEntry({
|
|
|
2777
2573
|
earlyBeforeMinutes: resolved.earlyBeforeMinutes
|
|
2778
2574
|
}));
|
|
2779
2575
|
} catch (err) {
|
|
2780
|
-
|
|
2576
|
+
sendGoogleMeetGatewayError(respond, err);
|
|
2781
2577
|
}
|
|
2782
2578
|
});
|
|
2783
2579
|
api.registerGatewayMethod("googlemeet.export", async ({ params, respond }) => {
|
|
2784
2580
|
try {
|
|
2785
2581
|
respond(true, await exportGoogleMeetBundleFromParams(config, asParamRecord(params)));
|
|
2786
2582
|
} catch (err) {
|
|
2787
|
-
|
|
2583
|
+
sendGoogleMeetGatewayError(respond, err);
|
|
2788
2584
|
}
|
|
2789
2585
|
});
|
|
2790
2586
|
api.registerGatewayMethod("googlemeet.leave", async ({ params, respond }) => {
|
|
2791
2587
|
try {
|
|
2792
2588
|
const sessionId = normalizeOptionalString(params?.sessionId);
|
|
2793
2589
|
if (!sessionId) {
|
|
2794
|
-
|
|
2590
|
+
sendGoogleMeetGatewayError(respond, /* @__PURE__ */ new Error("sessionId required"), ErrorCodes.INVALID_REQUEST);
|
|
2795
2591
|
return;
|
|
2796
2592
|
}
|
|
2797
2593
|
respond(true, await (await ensureRuntime()).leave(sessionId));
|
|
2798
2594
|
} catch (err) {
|
|
2799
|
-
|
|
2595
|
+
sendGoogleMeetGatewayError(respond, err);
|
|
2800
2596
|
}
|
|
2801
2597
|
});
|
|
2802
2598
|
api.registerGatewayMethod("googlemeet.endActiveConference", async ({ params, respond }) => {
|
|
@@ -2807,19 +2603,19 @@ var google_meet_default = definePluginEntry({
|
|
|
2807
2603
|
meeting: resolveMeetingInput(config, raw.meeting)
|
|
2808
2604
|
}));
|
|
2809
2605
|
} catch (err) {
|
|
2810
|
-
|
|
2606
|
+
sendGoogleMeetGatewayError(respond, err);
|
|
2811
2607
|
}
|
|
2812
2608
|
});
|
|
2813
2609
|
api.registerGatewayMethod("googlemeet.speak", async ({ params, respond }) => {
|
|
2814
2610
|
try {
|
|
2815
2611
|
const sessionId = normalizeOptionalString(params?.sessionId);
|
|
2816
2612
|
if (!sessionId) {
|
|
2817
|
-
|
|
2613
|
+
sendGoogleMeetGatewayError(respond, /* @__PURE__ */ new Error("sessionId required"), ErrorCodes.INVALID_REQUEST);
|
|
2818
2614
|
return;
|
|
2819
2615
|
}
|
|
2820
2616
|
respond(true, await (await ensureRuntime()).speak(sessionId, normalizeOptionalString(params?.message)));
|
|
2821
2617
|
} catch (err) {
|
|
2822
|
-
|
|
2618
|
+
sendGoogleMeetGatewayError(respond, err);
|
|
2823
2619
|
}
|
|
2824
2620
|
});
|
|
2825
2621
|
api.registerGatewayMethod("googlemeet.testSpeech", async ({ params, client, respond }) => {
|
|
@@ -2837,7 +2633,7 @@ var google_meet_default = definePluginEntry({
|
|
|
2837
2633
|
agentId: normalizeOptionalString(trustedParams.agentId)
|
|
2838
2634
|
}));
|
|
2839
2635
|
} catch (err) {
|
|
2840
|
-
|
|
2636
|
+
sendGoogleMeetGatewayError(respond, err);
|
|
2841
2637
|
}
|
|
2842
2638
|
});
|
|
2843
2639
|
api.registerGatewayMethod("googlemeet.testListen", async ({ params, client, respond }) => {
|
|
@@ -2851,7 +2647,7 @@ var google_meet_default = definePluginEntry({
|
|
|
2851
2647
|
timeoutMs: readPositiveIntegerParam(trustedParams, "timeoutMs")
|
|
2852
2648
|
}));
|
|
2853
2649
|
} catch (err) {
|
|
2854
|
-
|
|
2650
|
+
sendGoogleMeetGatewayError(respond, err);
|
|
2855
2651
|
}
|
|
2856
2652
|
});
|
|
2857
2653
|
api.registerTool((toolContext) => ({
|
|
@@ -3008,7 +2804,7 @@ var google_meet_default = definePluginEntry({
|
|
|
3008
2804
|
default: throw new Error("unknown google_meet action");
|
|
3009
2805
|
}
|
|
3010
2806
|
} catch (err) {
|
|
3011
|
-
return jsonResult(
|
|
2807
|
+
return jsonResult(formatGoogleMeetGatewayError(err));
|
|
3012
2808
|
}
|
|
3013
2809
|
}
|
|
3014
2810
|
}), { name: "google_meet" });
|
|
@@ -3028,11 +2824,7 @@ var google_meet_default = definePluginEntry({
|
|
|
3028
2824
|
});
|
|
3029
2825
|
}, {
|
|
3030
2826
|
commands: ["googlemeet"],
|
|
3031
|
-
descriptors: [
|
|
3032
|
-
name: "googlemeet",
|
|
3033
|
-
description: "Join and manage Google Meet calls",
|
|
3034
|
-
hasSubcommands: true
|
|
3035
|
-
}]
|
|
2827
|
+
descriptors: [GOOGLE_MEET_CLI_DESCRIPTOR]
|
|
3036
2828
|
});
|
|
3037
2829
|
}
|
|
3038
2830
|
});
|