@openclaw/google-meet 2026.6.11 → 2026.7.1-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,7 @@
1
1
  import { t as googleApiError } from "./google-api-errors-CDUYkIcK.js";
2
2
  import { uniqueStrings } from "openclaw/plugin-sdk/string-coerce-runtime";
3
3
  import { fetchWithSsrFGuard } from "openclaw/plugin-sdk/ssrf-runtime";
4
+ import { readProviderTextResponse } from "openclaw/plugin-sdk/provider-http";
4
5
  import { addTimerTimeoutGraceMs } from "openclaw/plugin-sdk/number-runtime";
5
6
  import { sleep } from "openclaw/plugin-sdk/runtime-env";
6
7
  //#region extensions/google-meet/src/drive.ts
@@ -40,7 +41,7 @@ async function exportGoogleDriveDocumentText(params) {
40
41
  prefix: "Google Drive files.export",
41
42
  scopes: [GOOGLE_DRIVE_MEET_SCOPE]
42
43
  });
43
- return await response.text();
44
+ return await readProviderTextResponse(response, "Google Drive files.export");
44
45
  } finally {
45
46
  await release();
46
47
  }
@@ -597,6 +598,15 @@ function buildGoogleMeetPreflightReport(params) {
597
598
  }
598
599
  //#endregion
599
600
  //#region extensions/google-meet/src/transports/chrome-browser-proxy.ts
601
+ function forceMeetEnglishUi(url) {
602
+ try {
603
+ const parsed = new URL(url);
604
+ parsed.searchParams.set("hl", "en");
605
+ return parsed.toString();
606
+ } catch {
607
+ return url;
608
+ }
609
+ }
600
610
  function normalizeMeetUrlForReuse(url) {
601
611
  if (!url) return;
602
612
  try {
@@ -698,7 +708,8 @@ async function callBrowserProxyOnNode(params) {
698
708
  body: params.body,
699
709
  timeoutMs: params.timeoutMs
700
710
  },
701
- timeoutMs: addTimerTimeoutGraceMs(params.timeoutMs) ?? 1
711
+ timeoutMs: addTimerTimeoutGraceMs(params.timeoutMs) ?? 1,
712
+ scopes: ["operator.admin"]
702
713
  }));
703
714
  }
704
715
  function asBrowserTabs(result) {
@@ -817,7 +828,9 @@ const CREATE_MEET_FROM_BROWSER_SCRIPT = `async () => {
817
828
  }
818
829
  const href = current();
819
830
  if (meetUrlPattern.test(href)) {
820
- return { meetingUri: href, browserUrl: href, browserTitle: document.title, notes };
831
+ // The /new redirect keeps the hl=en param we open with; strip query/hash so the
832
+ // meeting link handed to users stays canonical instead of forcing English on them.
833
+ return { meetingUri: href.split(/[?#]/)[0], browserUrl: href, browserTitle: document.title, notes };
821
834
  }
822
835
  const pageText = text(document.body);
823
836
  if (clickButton(/\\buse microphone\\b/i, "Accepted Meet microphone prompt with browser automation.")) {
@@ -896,7 +909,7 @@ async function createMeetWithBrowserProxyOnNode(params) {
896
909
  nodeId,
897
910
  method: "POST",
898
911
  path: "/tabs/open",
899
- body: { url: GOOGLE_MEET_NEW_URL },
912
+ body: { url: forceMeetEnglishUi(GOOGLE_MEET_NEW_URL) },
900
913
  timeoutMs: stepTimeoutMs
901
914
  }));
902
915
  const targetId = tab?.targetId;
@@ -950,4 +963,4 @@ async function createMeetWithBrowserProxyOnNode(params) {
950
963
  throw new Error(lastResult?.manualAction ?? `Google Meet did not return a meeting URL from the browser create flow before timeout.${lastError ? ` Last browser automation error: ${formatBrowserAutomationError(lastError)}` : ""}`);
951
964
  }
952
965
  //#endregion
953
- export { isSameMeetUrlForReuse as a, resolveChromeNode as c, createGoogleMeetSpace as d, endGoogleMeetActiveConference as f, fetchLatestGoogleMeetConferenceRecord as g, fetchGoogleMeetSpace as h, callBrowserProxyOnNode as i, resolveChromeNodeInfo as l, fetchGoogleMeetAttendance as m, isGoogleMeetBrowserManualActionError as n, normalizeMeetUrlForReuse as o, fetchGoogleMeetArtifacts as p, asBrowserTabs as r, readBrowserTab as s, createMeetWithBrowserProxyOnNode as t, buildGoogleMeetPreflightReport as u };
966
+ export { fetchLatestGoogleMeetConferenceRecord as _, forceMeetEnglishUi as a, readBrowserTab as c, buildGoogleMeetPreflightReport as d, createGoogleMeetSpace as f, fetchGoogleMeetSpace as g, fetchGoogleMeetAttendance as h, callBrowserProxyOnNode as i, resolveChromeNode as l, fetchGoogleMeetArtifacts as m, isGoogleMeetBrowserManualActionError as n, isSameMeetUrlForReuse as o, endGoogleMeetActiveConference as p, asBrowserTabs as r, normalizeMeetUrlForReuse as s, createMeetWithBrowserProxyOnNode as t, resolveChromeNodeInfo as u };
@@ -1,7 +1,7 @@
1
1
  import { a as buildGoogleMeetCalendarDayWindow, i as resolveGoogleMeetGatewayOperationTimeoutMs, o as findGoogleMeetCalendarEvent, s as listGoogleMeetCalendarEvents } from "./config-D_YKxh0m.js";
2
- import { d as createGoogleMeetSpace, f as endGoogleMeetActiveConference, g as fetchLatestGoogleMeetConferenceRecord, h as fetchGoogleMeetSpace, m as fetchGoogleMeetAttendance, p as fetchGoogleMeetArtifacts, u as buildGoogleMeetPreflightReport } from "./chrome-create-DWEeDc7N.js";
3
- import { buildGoogleMeetAuthUrl, createGoogleMeetOAuthState, createGoogleMeetPkce, exchangeGoogleMeetAuthCode, resolveGoogleMeetAccessToken, waitForGoogleMeetAuthCode } from "./oauth-BzOU5Ztq.js";
4
- import { hasCreateSpaceConfigInput, resolveCreateSpaceConfig } from "./create-C7GxW7BL.js";
2
+ import { _ as fetchLatestGoogleMeetConferenceRecord, d as buildGoogleMeetPreflightReport, f as createGoogleMeetSpace, g as fetchGoogleMeetSpace, h as fetchGoogleMeetAttendance, m as fetchGoogleMeetArtifacts, p as endGoogleMeetActiveConference } from "./chrome-create-C1akZT_q.js";
3
+ import { buildGoogleMeetAuthUrl, createGoogleMeetOAuthState, createGoogleMeetPkce, exchangeGoogleMeetAuthCode, resolveGoogleMeetAccessToken, waitForGoogleMeetAuthCode } from "./oauth-DK1mwF2i.js";
4
+ import { hasCreateSpaceConfigInput, resolveCreateSpaceConfig } from "./create-CHlW2v6-.js";
5
5
  import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
6
6
  import { callGatewayFromCli } from "openclaw/plugin-sdk/gateway-runtime";
7
7
  import { clampTimerTimeoutMs, parseStrictPositiveInteger } from "openclaw/plugin-sdk/number-runtime";
@@ -1,5 +1,5 @@
1
- import { d as createGoogleMeetSpace, t as createMeetWithBrowserProxyOnNode } from "./chrome-create-DWEeDc7N.js";
2
- import { resolveGoogleMeetAccessToken } from "./oauth-BzOU5Ztq.js";
1
+ import { f as createGoogleMeetSpace, t as createMeetWithBrowserProxyOnNode } from "./chrome-create-C1akZT_q.js";
2
+ import { resolveGoogleMeetAccessToken } from "./oauth-DK1mwF2i.js";
3
3
  import { normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
4
4
  //#region extensions/google-meet/src/create.ts
5
5
  function normalizeTransport(value) {
package/dist/index.js CHANGED
@@ -1,8 +1,9 @@
1
1
  import { a as buildGoogleMeetCalendarDayWindow, 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-D_YKxh0m.js";
2
- import { a as isSameMeetUrlForReuse, c as resolveChromeNode, f as endGoogleMeetActiveConference, g as fetchLatestGoogleMeetConferenceRecord, h as fetchGoogleMeetSpace, i as callBrowserProxyOnNode, l as resolveChromeNodeInfo, m as fetchGoogleMeetAttendance, n as isGoogleMeetBrowserManualActionError, o as normalizeMeetUrlForReuse, p as fetchGoogleMeetArtifacts, r as asBrowserTabs, s as readBrowserTab, t as createMeetWithBrowserProxyOnNode, u as buildGoogleMeetPreflightReport } from "./chrome-create-DWEeDc7N.js";
2
+ import { _ as fetchLatestGoogleMeetConferenceRecord, a as forceMeetEnglishUi, c as readBrowserTab, d as buildGoogleMeetPreflightReport, g as fetchGoogleMeetSpace, h as fetchGoogleMeetAttendance, i as callBrowserProxyOnNode, l as resolveChromeNode, m as fetchGoogleMeetArtifacts, n as isGoogleMeetBrowserManualActionError, o as isSameMeetUrlForReuse, p as endGoogleMeetActiveConference, r as asBrowserTabs, s as normalizeMeetUrlForReuse, t as createMeetWithBrowserProxyOnNode, u as resolveChromeNodeInfo } from "./chrome-create-C1akZT_q.js";
3
3
  import { optionalPositiveIntegerSchema, readPositiveIntegerParam } from "openclaw/plugin-sdk/channel-actions";
4
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";
6
7
  import { definePluginEntry } from "openclaw/plugin-sdk/plugin-entry";
7
8
  import { asRecord, normalizeOptionalString, uniqueStrings } from "openclaw/plugin-sdk/string-coerce-runtime";
8
9
  import { Type } from "typebox";
@@ -31,10 +32,10 @@ function readStringArray(value) {
31
32
  const result = value.filter((entry) => typeof entry === "string" && entry.length > 0);
32
33
  return result.length > 0 ? result : void 0;
33
34
  }
34
- function asRecord$2(value) {
35
+ function asRecord$3(value) {
35
36
  return value && typeof value === "object" && !Array.isArray(value) ? value : {};
36
37
  }
37
- function readString$1(value) {
38
+ function readString$2(value) {
38
39
  return typeof value === "string" && value.length > 0 ? value : void 0;
39
40
  }
40
41
  function formatErrorMessage$1(error) {
@@ -155,7 +156,7 @@ function terminateChild(child) {
155
156
  }, 2e3).unref?.();
156
157
  }
157
158
  async function pullAudio(params) {
158
- const bridgeId = readString$1(params.bridgeId);
159
+ const bridgeId = readString$2(params.bridgeId);
159
160
  if (!bridgeId) throw new Error("bridgeId required");
160
161
  const session = sessions.get(bridgeId);
161
162
  if (!session) throw new Error(`unknown bridgeId: ${bridgeId}`);
@@ -171,8 +172,8 @@ async function pullAudio(params) {
171
172
  };
172
173
  }
173
174
  function pushAudio(params) {
174
- const bridgeId = readString$1(params.bridgeId);
175
- const base64 = readString$1(params.base64);
175
+ const bridgeId = readString$2(params.bridgeId);
176
+ const base64 = readString$2(params.base64);
176
177
  if (!bridgeId || !base64) throw new Error("bridgeId and base64 required");
177
178
  const session = sessions.get(bridgeId);
178
179
  if (!session || session.closed) throw new Error(`bridge is not open: ${bridgeId}`);
@@ -191,7 +192,7 @@ function pushAudio(params) {
191
192
  };
192
193
  }
193
194
  function clearAudio(params) {
194
- const bridgeId = readString$1(params.bridgeId);
195
+ const bridgeId = readString$2(params.bridgeId);
195
196
  if (!bridgeId) throw new Error("bridgeId required");
196
197
  const session = sessions.get(bridgeId);
197
198
  if (!session || session.closed) throw new Error(`bridge is not open: ${bridgeId}`);
@@ -209,10 +210,10 @@ function clearAudio(params) {
209
210
  };
210
211
  }
211
212
  function startChrome(params) {
212
- const url = readString$1(params.url);
213
+ const url = readString$2(params.url);
213
214
  if (!url) throw new Error("url required");
214
215
  const timeoutMs = readNumber(params.joinTimeoutMs, 3e4);
215
- const mode = readString$1(params.mode);
216
+ const mode = readString$2(params.mode);
216
217
  let bridgeId;
217
218
  let audioBridge;
218
219
  if (mode === "agent" || mode === "bidi" || mode === "realtime") {
@@ -242,11 +243,11 @@ function startChrome(params) {
242
243
  const argv = [
243
244
  "open",
244
245
  "-a",
245
- "Google Chrome"
246
+ "Google Chrome",
247
+ url
246
248
  ];
247
- const browserProfile = readString$1(params.browserProfile);
249
+ const browserProfile = readString$2(params.browserProfile);
248
250
  if (browserProfile) argv.push("--args", `--profile-directory=${browserProfile}`);
249
- argv.push(url);
250
251
  const result = runCommandWithTimeout(argv, timeoutMs);
251
252
  if (result.code !== 0) {
252
253
  if (bridgeId) {
@@ -268,7 +269,7 @@ function startChrome(params) {
268
269
  };
269
270
  }
270
271
  function bridgeStatus(params) {
271
- const bridgeId = readString$1(params.bridgeId);
272
+ const bridgeId = readString$2(params.bridgeId);
272
273
  const session = bridgeId ? sessions.get(bridgeId) : void 0;
273
274
  return { bridge: session ? {
274
275
  bridgeId,
@@ -311,15 +312,15 @@ function summarizeSession(session) {
311
312
  };
312
313
  }
313
314
  function listSessions(params) {
314
- const urlKey = normalizeMeetKey(readString$1(params.url));
315
- const mode = readString$1(params.mode);
315
+ const urlKey = normalizeMeetKey(readString$2(params.url));
316
+ const mode = readString$2(params.mode);
316
317
  return { bridges: [...sessions.values()].filter((session) => !session.closed).filter((session) => !urlKey || normalizeMeetKey(session.url) === urlKey).filter((session) => !mode || session.mode === mode).map(summarizeSession) };
317
318
  }
318
319
  function stopSessionsByUrl(params) {
319
- const urlKey = normalizeMeetKey(readString$1(params.url));
320
+ const urlKey = normalizeMeetKey(readString$2(params.url));
320
321
  if (!urlKey) throw new Error("url required");
321
- const mode = readString$1(params.mode);
322
- const exceptBridgeId = readString$1(params.exceptBridgeId);
322
+ const mode = readString$2(params.mode);
323
+ const exceptBridgeId = readString$2(params.exceptBridgeId);
323
324
  let stopped = 0;
324
325
  for (const [bridgeId, session] of sessions) {
325
326
  if (exceptBridgeId && bridgeId === exceptBridgeId) continue;
@@ -336,7 +337,7 @@ function stopSessionsByUrl(params) {
336
337
  };
337
338
  }
338
339
  function stopChrome(params) {
339
- const bridgeId = readString$1(params.bridgeId);
340
+ const bridgeId = readString$2(params.bridgeId);
340
341
  if (!bridgeId) return {
341
342
  ok: true,
342
343
  stopped: false
@@ -360,8 +361,8 @@ async function handleGoogleMeetNodeHostCommand(paramsJSON) {
360
361
  } catch {
361
362
  throw new Error("Google Meet node host received malformed params JSON.");
362
363
  }
363
- const params = asRecord$2(raw);
364
- const action = readString$1(params.action);
364
+ const params = asRecord$3(raw);
365
+ const action = readString$2(params.action);
365
366
  let result;
366
367
  switch (action) {
367
368
  case "setup":
@@ -1575,10 +1576,10 @@ async function startCommandRealtimeAudioBridge(params) {
1575
1576
  }
1576
1577
  //#endregion
1577
1578
  //#region extensions/google-meet/src/realtime-node.ts
1578
- function asRecord$1(value) {
1579
+ function asRecord$2(value) {
1579
1580
  return value && typeof value === "object" && !Array.isArray(value) ? value : {};
1580
1581
  }
1581
- function readString(value) {
1582
+ function readString$1(value) {
1582
1583
  return typeof value === "string" && value.trim() ? value : void 0;
1583
1584
  }
1584
1585
  function normalizeGoogleMeetTtsPromptText(text) {
@@ -1609,10 +1610,10 @@ function startGoogleMeetNodeAudioInputLoop(params) {
1609
1610
  },
1610
1611
  timeoutMs: 2e3
1611
1612
  });
1612
- const result = asRecord$1(asRecord$1(raw).payload ?? raw);
1613
+ const result = asRecord$2(asRecord$2(raw).payload ?? raw);
1613
1614
  consecutiveInputErrors = 0;
1614
1615
  lastInputError = void 0;
1615
- const base64 = readString(result.base64);
1616
+ const base64 = readString$1(result.base64);
1616
1617
  if (base64) {
1617
1618
  const audio = Buffer.from(base64, "base64");
1618
1619
  if (params.isInputSuppressed()) {
@@ -2389,7 +2390,7 @@ function meetStatusScript(params) {
2389
2390
  notes.push("Muted Meet microphone for observe-only mode.");
2390
2391
  }
2391
2392
  const join = !readOnly && ${JSON.stringify(params.autoJoin)}
2392
- ? findButton(/join now|ask to join/i)
2393
+ ? findButton(/join now|ask to join|join here too/i)
2393
2394
  : null;
2394
2395
  if (join) join.click();
2395
2396
  const microphoneChoice = findButton(/\\buse microphone\\b/i);
@@ -2623,7 +2624,7 @@ async function openMeetWithBrowserRequest(params) {
2623
2624
  tab = readBrowserTab(await params.callBrowser({
2624
2625
  method: "POST",
2625
2626
  path: "/tabs/open",
2626
- body: { url: params.url },
2627
+ body: { url: forceMeetEnglishUi(params.url) },
2627
2628
  timeoutMs
2628
2629
  }));
2629
2630
  targetId = tab?.targetId;
@@ -3721,17 +3722,154 @@ var GoogleMeetRuntime = class {
3721
3722
  }
3722
3723
  };
3723
3724
  //#endregion
3725
+ //#region extensions/google-meet/src/node-invoke-policy.ts
3726
+ const GOOGLE_MEET_CHROME_NODE_COMMAND = "googlemeet.chrome";
3727
+ const START_MODES = new Set([
3728
+ "agent",
3729
+ "bidi",
3730
+ "realtime",
3731
+ "transcribe"
3732
+ ]);
3733
+ function asRecord$1(value) {
3734
+ return value && typeof value === "object" && !Array.isArray(value) ? value : {};
3735
+ }
3736
+ function readString(value) {
3737
+ return typeof value === "string" && value.length > 0 ? value : void 0;
3738
+ }
3739
+ function readPositiveNumber(value) {
3740
+ return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : void 0;
3741
+ }
3742
+ function copyCommand(command) {
3743
+ return command && command.length > 0 ? [...command] : void 0;
3744
+ }
3745
+ function denied(message, code = "GOOGLE_MEET_NODE_POLICY_DENIED") {
3746
+ return {
3747
+ ok: false,
3748
+ code,
3749
+ message
3750
+ };
3751
+ }
3752
+ function approved(params) {
3753
+ return {
3754
+ approved: true,
3755
+ params
3756
+ };
3757
+ }
3758
+ function buildStartParams(params, config) {
3759
+ let url;
3760
+ try {
3761
+ url = normalizeMeetUrl(params.url);
3762
+ } catch (error) {
3763
+ return {
3764
+ approved: false,
3765
+ result: denied(error instanceof Error ? error.message : "googlemeet.chrome start requires url")
3766
+ };
3767
+ }
3768
+ const mode = readString(params.mode);
3769
+ if (mode && !START_MODES.has(mode)) return {
3770
+ approved: false,
3771
+ result: denied(`googlemeet.chrome start mode is unsupported: ${mode}`)
3772
+ };
3773
+ const startParams = {
3774
+ action: "start",
3775
+ url,
3776
+ launch: params.launch === false ? false : config.chrome.launch,
3777
+ browserProfile: config.chrome.browserProfile,
3778
+ joinTimeoutMs: config.chrome.joinTimeoutMs
3779
+ };
3780
+ if (mode) startParams.mode = mode;
3781
+ const audioInputCommand = copyCommand(config.chrome.audioInputCommand);
3782
+ if (audioInputCommand) startParams.audioInputCommand = audioInputCommand;
3783
+ const audioOutputCommand = copyCommand(config.chrome.audioOutputCommand);
3784
+ if (audioOutputCommand) startParams.audioOutputCommand = audioOutputCommand;
3785
+ const audioBridgeCommand = copyCommand(config.chrome.audioBridgeCommand);
3786
+ if (audioBridgeCommand) startParams.audioBridgeCommand = audioBridgeCommand;
3787
+ const audioBridgeHealthCommand = copyCommand(config.chrome.audioBridgeHealthCommand);
3788
+ if (audioBridgeHealthCommand) startParams.audioBridgeHealthCommand = audioBridgeHealthCommand;
3789
+ return approved(startParams);
3790
+ }
3791
+ function buildForwardParams(params) {
3792
+ const action = readString(params.action);
3793
+ switch (action) {
3794
+ case "setup": return { action };
3795
+ case "status": {
3796
+ const bridgeId = readString(params.bridgeId);
3797
+ return bridgeId ? {
3798
+ action,
3799
+ bridgeId
3800
+ } : { action };
3801
+ }
3802
+ case "list": {
3803
+ const forwarded = { action };
3804
+ const url = readString(params.url);
3805
+ const mode = readString(params.mode);
3806
+ if (url) forwarded.url = url;
3807
+ if (mode) forwarded.mode = mode;
3808
+ return forwarded;
3809
+ }
3810
+ case "stopByUrl": {
3811
+ const forwarded = { action };
3812
+ const url = readString(params.url);
3813
+ const mode = readString(params.mode);
3814
+ const exceptBridgeId = readString(params.exceptBridgeId);
3815
+ if (url) forwarded.url = url;
3816
+ if (mode) forwarded.mode = mode;
3817
+ if (exceptBridgeId) forwarded.exceptBridgeId = exceptBridgeId;
3818
+ return forwarded;
3819
+ }
3820
+ case "pullAudio": {
3821
+ const forwarded = { action };
3822
+ const bridgeId = readString(params.bridgeId);
3823
+ const timeoutMs = readPositiveNumber(params.timeoutMs);
3824
+ if (bridgeId) forwarded.bridgeId = bridgeId;
3825
+ if (timeoutMs) forwarded.timeoutMs = timeoutMs;
3826
+ return forwarded;
3827
+ }
3828
+ case "pushAudio": {
3829
+ const forwarded = { action };
3830
+ const bridgeId = readString(params.bridgeId);
3831
+ const base64 = readString(params.base64);
3832
+ if (bridgeId) forwarded.bridgeId = bridgeId;
3833
+ if (base64) forwarded.base64 = base64;
3834
+ return forwarded;
3835
+ }
3836
+ case "clearAudio":
3837
+ case "stop": {
3838
+ const bridgeId = readString(params.bridgeId);
3839
+ return bridgeId ? {
3840
+ action,
3841
+ bridgeId
3842
+ } : { action };
3843
+ }
3844
+ default: return null;
3845
+ }
3846
+ }
3847
+ function createGoogleMeetChromeNodeInvokePolicy(config) {
3848
+ return {
3849
+ commands: [GOOGLE_MEET_CHROME_NODE_COMMAND],
3850
+ dangerous: true,
3851
+ async handle(ctx) {
3852
+ if (ctx.command !== "googlemeet.chrome") return denied(`unsupported Google Meet node command: ${ctx.command}`);
3853
+ const params = asRecord$1(ctx.params);
3854
+ const action = readString(params.action);
3855
+ let decision;
3856
+ if (action === "start") decision = buildStartParams(params, config);
3857
+ else {
3858
+ const forwardParams = buildForwardParams(params);
3859
+ decision = forwardParams ? approved(forwardParams) : {
3860
+ approved: false,
3861
+ result: denied("unsupported googlemeet.chrome action")
3862
+ };
3863
+ }
3864
+ if (!decision.approved) return decision.result;
3865
+ return await ctx.invokeNode({ params: decision.params });
3866
+ }
3867
+ };
3868
+ }
3869
+ //#endregion
3724
3870
  //#region extensions/google-meet/index.ts
3725
- let googleMeetCreateModulePromise = null;
3726
- let googleMeetCliModulePromise = null;
3727
- const loadGoogleMeetCreateModule = async () => {
3728
- googleMeetCreateModulePromise ??= import("./create-C7GxW7BL.js");
3729
- return await googleMeetCreateModulePromise;
3730
- };
3731
- const loadGoogleMeetCliModule = async () => {
3732
- googleMeetCliModulePromise ??= import("./cli-BnEq0tBJ.js");
3733
- return await googleMeetCliModulePromise;
3734
- };
3871
+ const loadGoogleMeetCreateModule = createLazyRuntimeModule(() => import("./create-CHlW2v6-.js"));
3872
+ const loadGoogleMeetCliModule = createLazyRuntimeModule(() => import("./cli-BXICNPAs.js"));
3735
3873
  const googleMeetConfigSchema = {
3736
3874
  parse(value) {
3737
3875
  return resolveGoogleMeetConfig(value);
@@ -4102,7 +4240,7 @@ async function createAndJoinMeetFromParams(params) {
4102
4240
  return (await loadGoogleMeetCreateModule()).createAndJoinMeetFromParams(params);
4103
4241
  }
4104
4242
  async function resolveGoogleMeetTokenFromParams(config, raw) {
4105
- const { resolveGoogleMeetAccessToken } = await import("./oauth-BzOU5Ztq.js");
4243
+ const { resolveGoogleMeetAccessToken } = await import("./oauth-DK1mwF2i.js");
4106
4244
  return resolveGoogleMeetAccessToken({
4107
4245
  clientId: normalizeOptionalString(raw.clientId) ?? config.oauth.clientId,
4108
4246
  clientSecret: normalizeOptionalString(raw.clientSecret) ?? config.oauth.clientSecret,
@@ -4602,10 +4740,12 @@ var google_meet_default = definePluginEntry({
4602
4740
  }
4603
4741
  }), { name: "google_meet" });
4604
4742
  api.registerNodeHostCommand({
4605
- command: "googlemeet.chrome",
4743
+ command: GOOGLE_MEET_CHROME_NODE_COMMAND,
4606
4744
  cap: "google-meet",
4745
+ dangerous: true,
4607
4746
  handle: handleGoogleMeetNodeHostCommand
4608
4747
  });
4748
+ api.registerNodeInvokePolicy(createGoogleMeetChromeNodeInvokePolicy(config));
4609
4749
  api.registerCli(async ({ program }) => {
4610
4750
  const { registerGoogleMeetCli } = await loadGoogleMeetCliModule();
4611
4751
  registerGoogleMeetCli({
@@ -124,25 +124,42 @@ function createGoogleMeetPkce() {
124
124
  function createGoogleMeetOAuthState() {
125
125
  return generateOAuthState();
126
126
  }
127
+ function isLocalCallbackListenerError(error) {
128
+ if (!(error instanceof Error)) return false;
129
+ return error.message.includes("EADDRINUSE") || error.message.includes("port") || error.message.includes("listen");
130
+ }
131
+ async function readManualGoogleMeetAuthCode(params) {
132
+ const parsed = parseOAuthCallbackInput(await params.promptInput("Paste the full redirect URL here: "), {
133
+ missingState: "Missing 'state' parameter. Paste the full redirect URL.",
134
+ invalidInput: "Paste the full redirect URL, not just the code."
135
+ });
136
+ if ("error" in parsed) throw new Error(parsed.error);
137
+ if (parsed.state !== params.state) throw new Error("OAuth state mismatch - please try again");
138
+ return parsed.code;
139
+ }
127
140
  async function waitForGoogleMeetAuthCode(params) {
128
141
  params.writeLine(`Open this URL in your browser:\n\n${params.authUrl}\n`);
129
- if (params.manual) {
130
- const parsed = parseOAuthCallbackInput(await params.promptInput("Paste the full redirect URL here: "), {
131
- missingState: "Missing 'state' parameter. Paste the full redirect URL.",
132
- invalidInput: "Paste the full redirect URL, not just the code."
142
+ if (params.manual) return await readManualGoogleMeetAuthCode({
143
+ state: params.state,
144
+ promptInput: params.promptInput
145
+ });
146
+ try {
147
+ return (await waitForLocalOAuthCallback({
148
+ expectedState: params.state,
149
+ timeoutMs: params.timeoutMs,
150
+ port: 8085,
151
+ callbackPath: "/oauth2callback",
152
+ redirectUri: GOOGLE_MEET_REDIRECT_URI,
153
+ successTitle: "Google Meet OAuth complete"
154
+ })).code;
155
+ } catch (error) {
156
+ if (!isLocalCallbackListenerError(error)) throw error;
157
+ params.writeLine("Local callback server failed. Switching to manual mode...");
158
+ return await readManualGoogleMeetAuthCode({
159
+ state: params.state,
160
+ promptInput: params.promptInput
133
161
  });
134
- if ("error" in parsed) throw new Error(parsed.error);
135
- if (parsed.state !== params.state) throw new Error("OAuth state mismatch - please try again");
136
- return parsed.code;
137
162
  }
138
- return (await waitForLocalOAuthCallback({
139
- expectedState: params.state,
140
- timeoutMs: params.timeoutMs,
141
- port: 8085,
142
- callbackPath: "/oauth2callback",
143
- redirectUri: GOOGLE_MEET_REDIRECT_URI,
144
- successTitle: "Google Meet OAuth complete"
145
- })).code;
146
163
  }
147
164
  //#endregion
148
165
  export { buildGoogleMeetAuthUrl, createGoogleMeetOAuthState, createGoogleMeetPkce, exchangeGoogleMeetAuthCode, resolveGoogleMeetAccessToken, waitForGoogleMeetAuthCode };
@@ -1,18 +1,18 @@
1
1
  {
2
2
  "name": "@openclaw/google-meet",
3
- "version": "2026.6.11",
3
+ "version": "2026.7.1-beta.2",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@openclaw/google-meet",
9
- "version": "2026.6.11",
9
+ "version": "2026.7.1-beta.2",
10
10
  "dependencies": {
11
11
  "commander": "14.0.3",
12
12
  "typebox": "1.1.39"
13
13
  },
14
14
  "peerDependencies": {
15
- "openclaw": ">=2026.6.11"
15
+ "openclaw": ">=2026.7.1-beta.2"
16
16
  },
17
17
  "peerDependenciesMeta": {
18
18
  "openclaw": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openclaw/google-meet",
3
- "version": "2026.6.11",
3
+ "version": "2026.7.1-beta.2",
4
4
  "description": "OpenClaw Google Meet participant plugin for joining calls through Chrome or Twilio transports.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -12,7 +12,7 @@
12
12
  "typebox": "1.1.39"
13
13
  },
14
14
  "peerDependencies": {
15
- "openclaw": ">=2026.6.11"
15
+ "openclaw": ">=2026.7.1-beta.2"
16
16
  },
17
17
  "peerDependenciesMeta": {
18
18
  "openclaw": {
@@ -29,10 +29,10 @@
29
29
  "minHostVersion": ">=2026.4.20"
30
30
  },
31
31
  "compat": {
32
- "pluginApi": ">=2026.6.11"
32
+ "pluginApi": ">=2026.7.1-beta.2"
33
33
  },
34
34
  "build": {
35
- "openclawVersion": "2026.6.11"
35
+ "openclawVersion": "2026.7.1-beta.2"
36
36
  },
37
37
  "release": {
38
38
  "publishToClawHub": true,