@openclaw/google-meet 2026.10.1 → 2026.10.5-beta.1

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.
@@ -24,25 +24,17 @@ async function callBrowserProxyOnNode(params) {
24
24
  //#endregion
25
25
  //#region extensions/google-meet/src/transports/google-meet-urls.ts
26
26
  function forceMeetEnglishUi(url) {
27
- try {
28
- const parsed = new URL(url);
29
- parsed.searchParams.set("hl", "en");
30
- return parsed.toString();
31
- } catch {
32
- return url;
33
- }
27
+ const parsed = URL.parse(url);
28
+ if (!parsed) return url;
29
+ parsed.searchParams.set("hl", "en");
30
+ return parsed.toString();
34
31
  }
35
32
  function normalizeMeetUrlForReuse(url) {
36
- if (!url) return;
37
- try {
38
- const parsed = new URL(url);
39
- if (parsed.protocol !== "https:" || parsed.hostname.toLowerCase() !== "meet.google.com") return;
40
- const match = parsed.pathname.match(/^\/([a-z]{3}-[a-z]{4}-[a-z]{3})(?:\/)?$/i);
41
- if (!match?.[1]) return;
42
- return `https://meet.google.com/${match[1].toLowerCase()}`;
43
- } catch {
44
- return;
45
- }
33
+ const parsed = URL.parse(url ?? "");
34
+ if (parsed?.protocol !== "https:" || parsed.hostname.toLowerCase() !== "meet.google.com") return;
35
+ const match = parsed.pathname.match(/^\/([a-z]{3}-[a-z]{4}-[a-z]{3})(?:\/)?$/i);
36
+ if (!match?.[1]) return;
37
+ return `https://meet.google.com/${match[1].toLowerCase()}`;
46
38
  }
47
39
  function isSameMeetUrlForReuse(a, b) {
48
40
  const normalizedA = normalizeMeetUrlForReuse(a);
@@ -50,21 +42,11 @@ function isSameMeetUrlForReuse(a, b) {
50
42
  return Boolean(normalizedA && normalizedB && normalizedA === normalizedB);
51
43
  }
52
44
  function isEnglishMeetTab(url) {
53
- if (!url) return false;
54
- try {
55
- const parsed = new URL(url);
56
- return parsed.protocol === "https:" && parsed.hostname.toLowerCase() === "meet.google.com" && parsed.searchParams.get("hl")?.toLowerCase() === "en";
57
- } catch {
58
- return false;
59
- }
45
+ const parsed = URL.parse(url ?? "");
46
+ return parsed?.protocol === "https:" && parsed.hostname.toLowerCase() === "meet.google.com" && parsed.searchParams.get("hl")?.toLowerCase() === "en";
60
47
  }
61
48
  function readMeetAuthUser(url) {
62
- if (!url) return;
63
- try {
64
- return new URL(url).searchParams.get("authuser") ?? void 0;
65
- } catch {
66
- return;
67
- }
49
+ return URL.parse(url ?? "")?.searchParams.get("authuser") ?? void 0;
68
50
  }
69
51
  function isRecoverableMeetTab(tab, url) {
70
52
  if (url) return isSameMeetUrlForReuse(tab.url, url);
@@ -189,44 +171,38 @@ async function createMeetWithBrowserProxyOnNode(params) {
189
171
  });
190
172
  const timeoutMs = Math.max(GOOGLE_MEET_BROWSER_CREATE_TIMEOUT_MS, params.config.chrome.joinTimeoutMs);
191
173
  const stepTimeoutMs = Math.min(timeoutMs, GOOGLE_MEET_BROWSER_STEP_TIMEOUT_MS);
192
- let openedByPlugin = false;
193
- let tab = asMeetingBrowserTabs(await callBrowserProxyOnNode({
174
+ const callBrowser = (request) => callBrowserProxyOnNode({
175
+ ...request,
194
176
  runtime: params.runtime,
195
177
  nodeId,
196
- method: "GET",
197
- path: "/tabs",
198
178
  timeoutMs: stepTimeoutMs
179
+ });
180
+ let openedByPlugin = false;
181
+ let tab = asMeetingBrowserTabs(await callBrowser({
182
+ method: "GET",
183
+ path: "/tabs"
199
184
  })).find(isGoogleMeetCreateTab);
200
185
  if (tab?.targetId) {
201
- await callBrowserProxyOnNode({
202
- runtime: params.runtime,
203
- nodeId,
186
+ await callBrowser({
204
187
  method: "POST",
205
188
  path: "/tabs/focus",
206
- body: { targetId: tab.targetId },
207
- timeoutMs: stepTimeoutMs
189
+ body: { targetId: tab.targetId }
208
190
  });
209
191
  const reusedUrl = tab.url ?? "";
210
192
  const englishUrl = (/^https:\/\/meet\.google\.com\/new(?:$|[/?#])/i.test(reusedUrl) || reusedUrl.startsWith("https://accounts.google.com/")) && reusedUrl ? forceMeetEnglishUi(reusedUrl) : void 0;
211
- if (englishUrl && englishUrl !== reusedUrl) tab = readMeetingBrowserTab(await callBrowserProxyOnNode({
212
- runtime: params.runtime,
213
- nodeId,
193
+ if (englishUrl && englishUrl !== reusedUrl) tab = readMeetingBrowserTab(await callBrowser({
214
194
  method: "POST",
215
195
  path: "/navigate",
216
196
  body: {
217
197
  targetId: tab.targetId,
218
198
  url: englishUrl
219
- },
220
- timeoutMs: stepTimeoutMs
199
+ }
221
200
  })) ?? tab;
222
201
  } else {
223
- tab = readMeetingBrowserTab(await callBrowserProxyOnNode({
224
- runtime: params.runtime,
225
- nodeId,
202
+ tab = readMeetingBrowserTab(await callBrowser({
226
203
  method: "POST",
227
204
  path: "/tabs/open",
228
- body: { url: forceMeetEnglishUi(GOOGLE_MEET_NEW_URL) },
229
- timeoutMs: stepTimeoutMs
205
+ body: { url: forceMeetEnglishUi(GOOGLE_MEET_NEW_URL) }
230
206
  }));
231
207
  openedByPlugin = Boolean(tab?.targetId);
232
208
  }
@@ -237,17 +213,14 @@ async function createMeetWithBrowserProxyOnNode(params) {
237
213
  let lastError;
238
214
  const deadline = Date.now() + timeoutMs;
239
215
  while (Date.now() <= deadline) try {
240
- const result = readBrowserCreateResult(await callBrowserProxyOnNode({
241
- runtime: params.runtime,
242
- nodeId,
216
+ const result = readBrowserCreateResult(await callBrowser({
243
217
  method: "POST",
244
218
  path: "/act",
245
219
  body: {
246
220
  kind: "evaluate",
247
221
  targetId,
248
222
  fn: CREATE_MEET_FROM_BROWSER_SCRIPT
249
- },
250
- timeoutMs: stepTimeoutMs
223
+ }
251
224
  }));
252
225
  lastResult = result;
253
226
  for (const note of result.notes ?? []) notes.add(note);
@@ -1,4 +1,4 @@
1
- import { g as __exportAll } from "./plugin-helpers-D1OyyzEZ.mjs";
1
+ import { g as __exportAll } from "./plugin-helpers-BhofFtQ0.mjs";
2
2
  import { clampTimerTimeoutMs, parseStrictPositiveInteger } from "openclaw/plugin-sdk/number-runtime";
3
3
  import fsp, { stat } from "node:fs/promises";
4
4
  import path from "node:path";
@@ -6,7 +6,7 @@ import JSZip from "jszip";
6
6
  import { replaceFileAtomic, writeExternalFileWithinRoot } from "openclaw/plugin-sdk/security-runtime";
7
7
  import { createInterface } from "node:readline/promises";
8
8
  import { format } from "node:util";
9
- import { isGatewayClientRequestError, isGatewayTransportError } from "openclaw/plugin-sdk/gateway-runtime";
9
+ import { callGatewayFromCli, isGatewayClientRequestError, isGatewayTransportError } from "openclaw/plugin-sdk/gateway-runtime";
10
10
  import { formatDurationCompact } from "openclaw/plugin-sdk/time-runtime";
11
11
  //#region extensions/google-meet/src/cli-shared.ts
12
12
  const GOOGLE_MEET_GATEWAY_DEFAULT_TIMEOUT_MS = 5e3;
@@ -116,7 +116,7 @@ async function callGoogleMeetGateway(params) {
116
116
  const timeoutMs = resolveGoogleMeetGatewayTimeoutMs(params.timeoutMs);
117
117
  return {
118
118
  ok: true,
119
- payload: await params.callGateway(params.method, {
119
+ payload: await callGatewayFromCli(params.method, {
120
120
  json: true,
121
121
  timeout: String(timeoutMs)
122
122
  }, params.payload, { progress: false })
@@ -270,9 +270,9 @@ function appendArtifactSummary(lines, entry) {
270
270
  if (smartNote.documentTextError) lines.push(`- smart note document body warning: ${smartNote.documentTextError}`);
271
271
  }
272
272
  }
273
- function renderAttendance(result, format) {
273
+ function createReport(title, result, format) {
274
274
  const markdown = format === "markdown";
275
- const lines = markdown ? ["# Google Meet Attendance"] : [];
275
+ const lines = markdown ? [`# Google Meet ${title}`] : [];
276
276
  const field = (label, value) => {
277
277
  lines.push(`${markdown ? label : label.toLowerCase()}: ${value}`);
278
278
  };
@@ -280,6 +280,14 @@ function renderAttendance(result, format) {
280
280
  if (result.space) field("Space", result.space.name);
281
281
  if (markdown) lines.push("");
282
282
  field("Conference records", result.conferenceRecords.length);
283
+ return {
284
+ markdown,
285
+ lines,
286
+ field
287
+ };
288
+ }
289
+ function renderAttendance(result, format) {
290
+ const { markdown, lines, field } = createReport("Attendance", result, format);
283
291
  field("Attendance rows", result.attendance.length);
284
292
  for (const row of result.attendance) {
285
293
  const identity = row.displayName || row.user || row.participant;
@@ -334,15 +342,7 @@ function appendArtifactDocuments(lines, title, documents) {
334
342
  }
335
343
  }
336
344
  function renderArtifacts(result, format) {
337
- const markdown = format === "markdown";
338
- const lines = markdown ? ["# Google Meet Artifacts"] : [];
339
- const field = (label, value) => {
340
- lines.push(`${markdown ? label : label.toLowerCase()}: ${value}`);
341
- };
342
- if (result.input) field("Input", result.input);
343
- if (result.space) field("Space", result.space.name);
344
- if (markdown) lines.push("");
345
- field("Conference records", result.conferenceRecords.length);
345
+ const { markdown, lines, field } = createReport("Artifacts", result, format);
346
346
  for (const entry of result.artifacts) {
347
347
  lines.push("", `${markdown ? "##" : "record:"} ${entry.conferenceRecord.name}`);
348
348
  field("Started", formatOptional(entry.conferenceRecord.startTime));
@@ -495,8 +495,23 @@ function collectGoogleMeetArtifactWarnings(result) {
495
495
  return warnings;
496
496
  }
497
497
  function buildGoogleMeetExportManifest(params) {
498
- const transcriptEntryCount = params.artifacts.artifacts.reduce((count, entry) => count + entry.transcriptEntries.reduce((entryCount, transcript) => entryCount + transcript.entries.length, 0), 0);
499
498
  const warnings = collectGoogleMeetArtifactWarnings(params.artifacts);
499
+ const counts = {
500
+ conferenceRecords: params.artifacts.conferenceRecords.length,
501
+ artifacts: params.artifacts.artifacts.length,
502
+ attendanceRows: params.attendance.attendance.length,
503
+ recordings: 0,
504
+ transcripts: 0,
505
+ transcriptEntries: 0,
506
+ smartNotes: 0,
507
+ warnings: warnings.length
508
+ };
509
+ for (const entry of params.artifacts.artifacts) {
510
+ counts.recordings += entry.recordings.length;
511
+ counts.transcripts += entry.transcripts.length;
512
+ counts.smartNotes += entry.smartNotes.length;
513
+ for (const transcript of entry.transcriptEntries) counts.transcriptEntries += transcript.entries.length;
514
+ }
500
515
  return {
501
516
  generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
502
517
  ...params.request ? { request: params.request } : {},
@@ -506,16 +521,7 @@ function buildGoogleMeetExportManifest(params) {
506
521
  ...params.artifacts.input ? { artifacts: params.artifacts.input } : {},
507
522
  ...params.attendance.input ? { attendance: params.attendance.input } : {}
508
523
  },
509
- counts: {
510
- conferenceRecords: params.artifacts.conferenceRecords.length,
511
- artifacts: params.artifacts.artifacts.length,
512
- attendanceRows: params.attendance.attendance.length,
513
- recordings: params.artifacts.artifacts.reduce((count, entry) => count + entry.recordings.length, 0),
514
- transcripts: params.artifacts.artifacts.reduce((count, entry) => count + entry.transcripts.length, 0),
515
- transcriptEntries: transcriptEntryCount,
516
- smartNotes: params.artifacts.artifacts.reduce((count, entry) => count + entry.smartNotes.length, 0),
517
- warnings: warnings.length
518
- },
524
+ counts,
519
525
  conferenceRecords: params.artifacts.conferenceRecords.map((record) => record.name),
520
526
  files: params.files,
521
527
  ...params.zipFile ? { zipFile: params.zipFile } : {},
@@ -1,10 +1,9 @@
1
- import { a as resolveArtifactQueryFromParams, c as resolveSpaceFromParams, d as endGoogleMeetActiveConference, f as fetchGoogleMeetSpace, h as listGoogleMeetCalendarEvents, l as buildGoogleMeetPreflightReport, m as buildGoogleMeetCalendarDayWindow, n as fetchResolvedGoogleMeetArtifacts, o as resolveGoogleMeetTokenFromParams, p as fetchLatestGoogleMeetConferenceRecord, r as fetchResolvedGoogleMeetAttendance, s as resolveMeetingFromParams, t as buildGoogleMeetExportRequest, u as createGoogleMeetSpace } from "./plugin-helpers-D1OyyzEZ.mjs";
1
+ import { a as resolveArtifactQueryFromParams, c as resolveSpaceFromParams, d as endGoogleMeetActiveConference, f as fetchGoogleMeetSpace, h as listGoogleMeetCalendarEvents, l as buildGoogleMeetPreflightReport, m as buildGoogleMeetCalendarDayWindow, n as fetchResolvedGoogleMeetArtifacts, o as resolveGoogleMeetTokenFromParams, p as fetchLatestGoogleMeetConferenceRecord, r as fetchResolvedGoogleMeetAttendance, s as resolveMeetingFromParams, t as buildGoogleMeetExportRequest, u as createGoogleMeetSpace } from "./plugin-helpers-BhofFtQ0.mjs";
2
2
  import { i as resolveGoogleMeetGatewayOperationTimeoutMs } from "./config-BXGAMntj.mjs";
3
- import { C as writeStdoutJson, S as writeSetupStatus, _ as resolveGoogleMeetOAuthCallbackTimeoutMs, a as renderAttendanceCsv, b as writeLeaveResult, c as callGoogleMeetGateway, d as parseGoogleMeetTransport, f as parseOptionalNumber, g as resolveCliJoinRequest, h as promptInput, i as renderAttendance, l as parseGoogleMeetBrowserTransport, m as parsePositiveNumber, n as exportGoogleMeetBundle, o as writeCalendarEventsSummary, p as parsePositiveIntegerOption, r as renderArtifacts, s as writeLatestConferenceRecordSummary, u as parseGoogleMeetMode, v as writeCliOutput, w as writeStdoutLine, x as writeRecoverCurrentTabResult, y as writeDoctorStatus } from "./cli-export-BOebaVFW.mjs";
4
- import { buildGoogleMeetAuthUrl, exchangeGoogleMeetAuthCode, resolveGoogleMeetAccessToken, waitForGoogleMeetAuthCode } from "./oauth-XgdPFWEJ.mjs";
5
- import { n as hasCreateSpaceConfigInput, r as resolveCreateSpaceConfig } from "./create-DyYbmN11.mjs";
3
+ import { C as writeStdoutJson, S as writeSetupStatus, _ as resolveGoogleMeetOAuthCallbackTimeoutMs, a as renderAttendanceCsv, b as writeLeaveResult, c as callGoogleMeetGateway, d as parseGoogleMeetTransport, f as parseOptionalNumber, g as resolveCliJoinRequest, h as promptInput, i as renderAttendance, l as parseGoogleMeetBrowserTransport, m as parsePositiveNumber, n as exportGoogleMeetBundle, o as writeCalendarEventsSummary, p as parsePositiveIntegerOption, r as renderArtifacts, s as writeLatestConferenceRecordSummary, u as parseGoogleMeetMode, v as writeCliOutput, w as writeStdoutLine, x as writeRecoverCurrentTabResult, y as writeDoctorStatus } from "./cli-export-DDUSzhh0.mjs";
4
+ import { buildGoogleMeetAuthUrl, exchangeGoogleMeetAuthCode, resolveGoogleMeetAccessToken, waitForGoogleMeetAuthCode } from "./oauth-pXa-YQ9H.mjs";
5
+ import { i as hasGoogleMeetOAuth, r as hasCreateSpaceConfigInput, t as createSpaceFromParams } from "./create-0pfsnISA.mjs";
6
6
  import { parseStrictNonNegativeInteger } from "openclaw/plugin-sdk/number-runtime";
7
- import { callGatewayFromCli } from "openclaw/plugin-sdk/gateway-runtime";
8
7
  import { generateHexPkceVerifierChallenge } from "openclaw/plugin-sdk/provider-auth";
9
8
  import { generateOAuthState } from "openclaw/plugin-sdk/provider-auth-runtime";
10
9
  //#region extensions/google-meet/src/cli-command-context.ts
@@ -20,9 +19,21 @@ function addGoogleMeetCalendarOptions(command) {
20
19
  function addGoogleMeetArtifactOptions(command) {
21
20
  return addGoogleMeetOAuthOptions(addGoogleMeetCalendarOptions(addGoogleMeetMeetingOption(command).option("--conference-record <name>", "Conference record name or id"))).option("--page-size <n>", "Max resources per Meet API page").option("--all-conference-records", "Fetch every conference record for --meeting");
22
21
  }
22
+ function resolveCliMeetingInput(config, value) {
23
+ const meeting = value?.trim() || config.defaults.meeting;
24
+ if (!meeting) throw new Error("Meeting input is required. Pass a URL/meeting code or configure defaults.meeting.");
25
+ return meeting;
26
+ }
27
+ function resolveCliParams(options) {
28
+ const { calendar, expiresAt, ...raw } = options;
29
+ return {
30
+ ...raw,
31
+ calendarId: calendar,
32
+ expiresAt: parseOptionalNumber(expiresAt)
33
+ };
34
+ }
23
35
  async function callGoogleMeetRuntime(context, method, payload, local, timeoutMs) {
24
36
  const delegated = await callGoogleMeetGateway({
25
- callGateway: context.callGateway,
26
37
  method,
27
38
  payload,
28
39
  timeoutMs
@@ -32,7 +43,22 @@ async function callGoogleMeetRuntime(context, method, payload, local, timeoutMs)
32
43
  //#endregion
33
44
  //#region extensions/google-meet/src/cli-artifact-commands.ts
34
45
  async function resolveCliArtifactQuery(context, options) {
35
- const { lateAfterMinutes, earlyBeforeMinutes, ...raw } = context.resolveCliArtifactParams(options);
46
+ const meeting = options.meeting?.trim() || context.config.defaults.meeting;
47
+ const conferenceRecord = options.conferenceRecord?.trim();
48
+ if (!meeting && !conferenceRecord && !(options.today || options.event?.trim())) throw new Error("Meeting input or conference record is required. Pass --meeting, --today, --event, --conference-record, or configure defaults.meeting.");
49
+ const { lateAfterMinutes: late, earlyBeforeMinutes: early, ...queryOptions } = options;
50
+ const raw = {
51
+ ...resolveCliParams(queryOptions),
52
+ meeting,
53
+ conferenceRecord,
54
+ pageSize: parsePositiveIntegerOption(options.pageSize, "page-size"),
55
+ includeTranscriptEntries: options.transcriptEntries,
56
+ includeAllConferenceRecords: options.allConferenceRecords,
57
+ includeDocumentBodies: options.includeDocBodies,
58
+ mergeDuplicateParticipants: options.mergeDuplicates
59
+ };
60
+ const lateAfterMinutes = parseOptionalNumber(late);
61
+ const earlyBeforeMinutes = parseOptionalNumber(early);
36
62
  return {
37
63
  ...await resolveArtifactQueryFromParams(context.config, raw),
38
64
  lateAfterMinutes,
@@ -42,36 +68,28 @@ async function resolveCliArtifactQuery(context, options) {
42
68
  function resolveTokenSource(refreshed) {
43
69
  return refreshed ? "refresh-token" : "cached-access-token";
44
70
  }
71
+ async function writeArtifactOutput(options, result, refreshed, render, csv) {
72
+ const tokenSource = resolveTokenSource(refreshed);
73
+ let text;
74
+ if (options.json) text = JSON.stringify({
75
+ ...result,
76
+ tokenSource
77
+ }, null, 2);
78
+ else if (options.format === "markdown") text = render(result, "markdown");
79
+ else if (options.format === "csv" && csv) text = csv(result);
80
+ else if (!options.format || options.format === "summary") text = `${render(result, "summary")}token source: ${tokenSource}\n`;
81
+ else throw new Error(csv ? "Unsupported format. Expected summary, markdown, or csv." : "Unsupported format. Expected summary or markdown.");
82
+ await writeCliOutput(options, text);
83
+ }
45
84
  function registerGoogleMeetArtifactCommands(context) {
46
85
  const { root } = context;
47
86
  addGoogleMeetArtifactOptions(root.command("artifacts").description("List Meet conference records and available participant/artifact metadata")).option("--no-transcript-entries", "Skip structured transcript entry lookup").option("--include-doc-bodies", "Export linked transcript and smart-note Google Docs text").option("--format <format>", "Output format: summary or markdown", "summary").option("--output <path>", "Write output to a file instead of stdout").option("--json", "Print JSON output", false).action(async (options) => {
48
87
  const resolved = await resolveCliArtifactQuery(context, options);
49
- const result = await fetchResolvedGoogleMeetArtifacts(resolved);
50
- const tokenSource = resolveTokenSource(resolved.token.refreshed);
51
- let text;
52
- if (options.json) text = JSON.stringify({
53
- ...result,
54
- tokenSource
55
- }, null, 2);
56
- else if (options.format === "markdown") text = renderArtifacts(result, "markdown");
57
- else if (!options.format || options.format === "summary") text = `${renderArtifacts(result, "summary")}token source: ${tokenSource}\n`;
58
- else throw new Error("Unsupported format. Expected summary or markdown.");
59
- await writeCliOutput(options, text);
88
+ await writeArtifactOutput(options, await fetchResolvedGoogleMeetArtifacts(resolved), resolved.token.refreshed, renderArtifacts);
60
89
  });
61
90
  addGoogleMeetArtifactOptions(root.command("attendance").description("List Meet participants and participant sessions")).option("--no-merge-duplicates", "Keep duplicate participant resources as separate rows").option("--late-after-minutes <n>", "Mark participants late after this many minutes", "5").option("--early-before-minutes <n>", "Mark early leavers before this many minutes", "5").option("--format <format>", "Output format: summary, markdown, or csv", "summary").option("--output <path>", "Write output to a file instead of stdout").option("--json", "Print JSON output", false).action(async (options) => {
62
91
  const resolved = await resolveCliArtifactQuery(context, options);
63
- const result = await fetchResolvedGoogleMeetAttendance(resolved);
64
- const tokenSource = resolveTokenSource(resolved.token.refreshed);
65
- let text;
66
- if (options.json) text = JSON.stringify({
67
- ...result,
68
- tokenSource
69
- }, null, 2);
70
- else if (options.format === "markdown") text = renderAttendance(result, "markdown");
71
- else if (options.format === "csv") text = renderAttendanceCsv(result);
72
- else if (!options.format || options.format === "summary") text = `${renderAttendance(result, "summary")}token source: ${tokenSource}\n`;
73
- else throw new Error("Unsupported format. Expected summary, markdown, or csv.");
74
- await writeCliOutput(options, text);
92
+ await writeArtifactOutput(options, await fetchResolvedGoogleMeetAttendance(resolved), resolved.token.refreshed, renderAttendance, renderAttendanceCsv);
75
93
  });
76
94
  addGoogleMeetArtifactOptions(root.command("export").description("Write Meet artifacts, attendance, transcript, and raw JSON into a folder")).option("--no-transcript-entries", "Skip structured transcript entry lookup").option("--include-doc-bodies", "Export linked transcript and smart-note Google Docs text").option("--no-merge-duplicates", "Keep duplicate participant resources as separate rows").option("--late-after-minutes <n>", "Mark participants late after this many minutes", "5").option("--early-before-minutes <n>", "Mark early leavers before this many minutes", "5").option("--output <dir>", "Output directory").option("--zip", "Also write a portable .zip archive").option("--dry-run", "Fetch export data and print the manifest without writing files", false).option("--json", "Print JSON output", false).action(async (options) => {
77
95
  const resolved = await resolveCliArtifactQuery(context, options);
@@ -227,11 +245,10 @@ function registerGoogleMeetDoctorCommand(context) {
227
245
  //#endregion
228
246
  //#region extensions/google-meet/src/cli-runtime-commands.ts
229
247
  function registerGoogleMeetProbeCommands(context) {
230
- const { root, callGateway, operationTimeoutMs, resolveMeetingInput } = context;
248
+ const { root, operationTimeoutMs } = context;
231
249
  root.command("join").argument("[url]", "Explicit https://meet.google.com/... URL").option("--transport <transport>", "Transport: chrome, chrome-node, or twilio").option("--mode <mode>", "Mode: agent, bidi, or transcribe").option("--message <text>", "Realtime speech to trigger after join").option("--dial-in-number <phone>", "Meet dial-in number for Twilio transport").option("--pin <pin>", "Meet phone PIN; # is appended if omitted").option("--dtmf-sequence <sequence>", "Explicit Twilio DTMF sequence").action(async (url, options) => {
232
- const payload = resolveCliJoinRequest(resolveMeetingInput(context.config, url), options);
250
+ const payload = resolveCliJoinRequest(resolveCliMeetingInput(context.config, url), options);
233
251
  const delegated = await callGoogleMeetGateway({
234
- callGateway,
235
252
  method: "googlemeet.join",
236
253
  payload,
237
254
  timeoutMs: operationTimeoutMs
@@ -246,7 +263,7 @@ function registerGoogleMeetProbeCommands(context) {
246
263
  });
247
264
  root.command("test-speech").argument("[url]", "Explicit https://meet.google.com/... URL").option("--transport <transport>", "Transport: chrome, chrome-node, or twilio").option("--mode <mode>", "Mode: agent, bidi, or transcribe").option("--message <text>", "Realtime speech to trigger", "Say exactly: Google Meet speech test complete.").action(async (url, options) => {
248
265
  const payload = {
249
- url: resolveMeetingInput(context.config, url),
266
+ url: resolveCliMeetingInput(context.config, url),
250
267
  transport: parseGoogleMeetTransport(options.transport),
251
268
  mode: parseGoogleMeetMode(options.mode),
252
269
  message: options.message
@@ -255,7 +272,7 @@ function registerGoogleMeetProbeCommands(context) {
255
272
  });
256
273
  root.command("test-listen").argument("[url]", "Explicit https://meet.google.com/... URL").option("--transport <transport>", "Transport: chrome or chrome-node").option("--timeout-ms <ms>", "How long to wait for fresh captions/transcript movement").action(async (url, options) => {
257
274
  const payload = {
258
- url: resolveMeetingInput(context.config, url),
275
+ url: resolveCliMeetingInput(context.config, url),
259
276
  transport: parseGoogleMeetBrowserTransport(options.transport),
260
277
  timeoutMs: parsePositiveNumber(options.timeoutMs, "timeout-ms")
261
278
  };
@@ -339,11 +356,10 @@ function writeGoogleMeetCreateOutput(payload, json) {
339
356
  writeStdoutLine(joinedSessionId ? "joined: %s" : "joined: no (run `openclaw googlemeet join %s`)", joinedSessionId ?? payload.meetingUri);
340
357
  }
341
358
  function registerGoogleMeetCreateCommands(context) {
342
- const { root, callGateway, operationTimeoutMs, hasCreateOAuth, resolveMeetingInput, resolveCliParams } = context;
359
+ const { root, operationTimeoutMs } = context;
343
360
  addGoogleMeetOAuthOptions(root.command("create").description("Create a new Google Meet space and print its meeting URL")).option("--access-type <type>", "Google Meet SpaceConfig accessType for API create: OPEN, TRUSTED, or RESTRICTED").option("--entry-point-access <type>", "Google Meet SpaceConfig entryPointAccess for API create: ALL or CREATOR_APP_ONLY").option("--no-join", "Only create the meeting URL; do not join it").option("--transport <transport>", "Join transport: chrome, chrome-node, or twilio").option("--mode <mode>", "Join mode: agent, bidi, or transcribe").option("--message <text>", "Realtime speech to trigger after join").option("--dial-in-number <phone>", "Meet dial-in number for Twilio transport").option("--pin <pin>", "Meet phone PIN; # is appended if omitted").option("--dtmf-sequence <sequence>", "Explicit Twilio DTMF sequence").option("--json", "Print JSON output", false).action(async (options) => {
344
361
  if (options.join !== false) {
345
362
  const delegated = await callGoogleMeetGateway({
346
- callGateway,
347
363
  method: "googlemeet.create",
348
364
  payload: { ...options },
349
365
  timeoutMs: operationTimeoutMs
@@ -354,43 +370,43 @@ function registerGoogleMeetCreateCommands(context) {
354
370
  return;
355
371
  }
356
372
  }
357
- if (!hasCreateOAuth(context.config, options)) {
373
+ let runtime;
374
+ let created;
375
+ if (!hasGoogleMeetOAuth(context.config, options)) {
358
376
  if (hasCreateSpaceConfigInput(options)) throw new Error("Google Meet access policy options require OAuth/API room creation. Configure Google Meet OAuth or remove --access-type/--entry-point-access.");
359
- const rt = await context.ensureRuntime();
360
- const result = await rt.createViaBrowser();
361
- const join = options.join !== false ? await rt.join(resolveCliJoinRequest(result.meetingUri, options)) : void 0;
362
- writeGoogleMeetCreateOutput({
377
+ runtime = await context.ensureRuntime();
378
+ const result = await runtime.createViaBrowser();
379
+ created = {
363
380
  source: result.source,
364
381
  meetingUri: result.meetingUri,
365
- joined: Boolean(join),
366
- ...join ? { join } : {},
367
382
  browser: {
368
383
  nodeId: result.nodeId,
369
384
  targetId: result.targetId,
370
385
  browserUrl: result.browserUrl,
371
386
  browserTitle: result.browserTitle
372
387
  }
373
- }, options.json);
374
- return;
388
+ };
389
+ } else {
390
+ const { token, source: _source, ...result } = await createSpaceFromParams(context.config, resolveCliParams(options));
391
+ created = {
392
+ ...result,
393
+ tokenSource: token.refreshed ? "refresh-token" : "cached-access-token"
394
+ };
375
395
  }
376
- const token = await resolveGoogleMeetTokenFromParams(context.config, resolveCliParams(options));
377
- const result = await createGoogleMeetSpace({
378
- accessToken: token.accessToken,
379
- config: resolveCreateSpaceConfig(options)
380
- });
381
- const join = options.join !== false ? await (await context.ensureRuntime()).join(resolveCliJoinRequest(result.meetingUri, options)) : void 0;
396
+ const join = options.join !== false ? await (runtime ?? await context.ensureRuntime()).join(resolveCliJoinRequest(created.meetingUri, options)) : void 0;
397
+ const { browser, ...result } = created;
382
398
  writeGoogleMeetCreateOutput({
383
399
  ...result,
384
- tokenSource: token.refreshed ? "refresh-token" : "cached-access-token",
385
400
  joined: Boolean(join),
386
- ...join ? { join } : {}
401
+ ...join ? { join } : {},
402
+ ...browser ? { browser } : {}
387
403
  }, options.json);
388
404
  });
389
405
  addGoogleMeetOAuthOptions(root.command("end-active-conference").description("End the active conference for a Google Meet space").argument("[meeting]", "Meet URL, meeting code, or spaces/{id}")).option("--json", "Print JSON output", false).action(async (meeting, options) => {
390
406
  const token = await resolveGoogleMeetTokenFromParams(context.config, resolveCliParams(options));
391
407
  const result = await endGoogleMeetActiveConference({
392
408
  accessToken: token.accessToken,
393
- meeting: resolveMeetingInput(context.config, meeting ?? options.meeting)
409
+ meeting: resolveCliMeetingInput(context.config, meeting ?? options.meeting)
394
410
  });
395
411
  if (options.json) {
396
412
  writeStdoutJson({
@@ -405,9 +421,9 @@ function registerGoogleMeetCreateCommands(context) {
405
421
  });
406
422
  }
407
423
  function registerGoogleMeetApiCommands(context) {
408
- const { root, resolveCliParams, resolveMeetingInput } = context;
424
+ const { root } = context;
409
425
  addGoogleMeetOAuthOptions(addGoogleMeetMeetingOption(root.command("resolve-space").description("Resolve a Meet URL, meeting code, or spaces/{id} to its canonical space"))).option("--json", "Print JSON output", false).action(async (options) => {
410
- const meeting = resolveMeetingInput(context.config, options.meeting);
426
+ const meeting = resolveCliMeetingInput(context.config, options.meeting);
411
427
  const { space, token } = await resolveSpaceFromParams(context.config, {
412
428
  ...resolveCliParams(options),
413
429
  meeting
@@ -424,7 +440,7 @@ function registerGoogleMeetApiCommands(context) {
424
440
  writeStdoutLine("token source: %s", token.refreshed ? "refresh-token" : "cached-access-token");
425
441
  });
426
442
  addGoogleMeetOAuthOptions(addGoogleMeetMeetingOption(root.command("preflight").description("Validate OAuth + meeting resolution prerequisites for Meet media work"))).option("--json", "Print JSON output", false).action(async (options) => {
427
- const meeting = resolveMeetingInput(context.config, options.meeting);
443
+ const meeting = resolveCliMeetingInput(context.config, options.meeting);
428
444
  const { space, token } = await resolveSpaceFromParams(context.config, {
429
445
  ...resolveCliParams(options),
430
446
  meeting
@@ -508,44 +524,7 @@ function registerGoogleMeetApiCommands(context) {
508
524
  }
509
525
  //#endregion
510
526
  //#region extensions/google-meet/src/cli.ts
511
- function resolveMeetingInput(config, value) {
512
- const meeting = value?.trim() || config.defaults.meeting;
513
- if (!meeting) throw new Error("Meeting input is required. Pass a URL/meeting code or configure defaults.meeting.");
514
- return meeting;
515
- }
516
- function hasCalendarLookupOptions(options) {
517
- return Boolean(options.today || options.event?.trim());
518
- }
519
- function resolveCliParams(options) {
520
- const { calendar, expiresAt, ...raw } = options;
521
- return {
522
- ...raw,
523
- calendarId: calendar,
524
- expiresAt: parseOptionalNumber(expiresAt)
525
- };
526
- }
527
- function resolveCliArtifactParams(config, options) {
528
- const meeting = options.meeting?.trim() || config.defaults.meeting;
529
- const conferenceRecord = options.conferenceRecord?.trim();
530
- if (!meeting && !conferenceRecord && !hasCalendarLookupOptions(options)) throw new Error("Meeting input or conference record is required. Pass --meeting, --today, --event, --conference-record, or configure defaults.meeting.");
531
- return {
532
- ...resolveCliParams(options),
533
- meeting,
534
- conferenceRecord,
535
- pageSize: parsePositiveIntegerOption(options.pageSize, "page-size"),
536
- includeTranscriptEntries: options.transcriptEntries,
537
- includeAllConferenceRecords: options.allConferenceRecords,
538
- includeDocumentBodies: options.includeDocBodies,
539
- mergeDuplicateParticipants: options.mergeDuplicates,
540
- lateAfterMinutes: parseOptionalNumber(options.lateAfterMinutes),
541
- earlyBeforeMinutes: parseOptionalNumber(options.earlyBeforeMinutes)
542
- };
543
- }
544
- function hasCreateOAuth(config, options) {
545
- return Boolean(options.accessToken?.trim() || options.refreshToken?.trim() || config.oauth.accessToken || config.oauth.refreshToken);
546
- }
547
527
  function registerGoogleMeetCli(params) {
548
- const callGateway = params.callGatewayFromCli ?? callGatewayFromCli;
549
528
  const operationTimeoutMs = resolveGoogleMeetGatewayOperationTimeoutMs(params.config);
550
529
  const root = params.program.command("googlemeet").description("Google Meet participant utilities").addHelpText("after", () => `\nDocs: https://docs.openclaw.ai/plugins/google-meet\n`);
551
530
  root.command("auth").description("Google Meet OAuth helpers").command("login").description("Run a PKCE OAuth flow and print refresh-token JSON to store in plugin config").option("--client-id <id>", "OAuth client id override").option("--client-secret <secret>", "OAuth client secret override").option("--manual", "Use copy/paste callback flow instead of localhost callback").option("--json", "Print the token payload as JSON", false).option("--timeout-sec <n>", "Local callback timeout in seconds", "300").action(async (options) => {
@@ -592,12 +571,7 @@ function registerGoogleMeetCli(params) {
592
571
  root,
593
572
  config: params.config,
594
573
  ensureRuntime: params.ensureRuntime,
595
- callGateway,
596
- operationTimeoutMs,
597
- resolveMeetingInput,
598
- resolveCliParams,
599
- resolveCliArtifactParams: (options) => resolveCliArtifactParams(params.config, options),
600
- hasCreateOAuth
574
+ operationTimeoutMs
601
575
  };
602
576
  registerGoogleMeetCreateCommands(context);
603
577
  registerGoogleMeetProbeCommands(context);
@@ -1,12 +1,14 @@
1
- import { g as __exportAll, o as resolveGoogleMeetTokenFromParams, u as createGoogleMeetSpace } from "./plugin-helpers-D1OyyzEZ.mjs";
2
- import { d as normalizeMode, f as normalizeTransport } from "./plugin-registration-BP83dqMw.mjs";
3
- import { t as createMeetWithBrowserProxyOnNode } from "./chrome-create-Bh1L33pr.mjs";
1
+ import { g as __exportAll, o as resolveGoogleMeetTokenFromParams, u as createGoogleMeetSpace } from "./plugin-helpers-BhofFtQ0.mjs";
2
+ import { d as normalizeMode, f as normalizeTransport } from "./plugin-registration-B_Ohay99.mjs";
3
+ import { t as createMeetWithBrowserProxyOnNode } from "./chrome-create-CW_QQzuj.mjs";
4
4
  import { normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
5
5
  //#region extensions/google-meet/src/create.ts
6
6
  var create_exports = /* @__PURE__ */ __exportAll({
7
7
  createAndJoinMeetFromParams: () => createAndJoinMeetFromParams,
8
8
  createMeetFromParams: () => createMeetFromParams,
9
+ createSpaceFromParams: () => createSpaceFromParams,
9
10
  hasCreateSpaceConfigInput: () => hasCreateSpaceConfigInput,
11
+ hasGoogleMeetOAuth: () => hasGoogleMeetOAuth,
10
12
  resolveCreateSpaceConfig: () => resolveCreateSpaceConfig
11
13
  });
12
14
  function normalizeGoogleMeetAccessType(value) {
@@ -100,4 +102,4 @@ async function createAndJoinMeetFromParams(params) {
100
102
  };
101
103
  }
102
104
  //#endregion
103
- export { hasCreateSpaceConfigInput as n, resolveCreateSpaceConfig as r, create_exports as t };
105
+ export { hasGoogleMeetOAuth as i, create_exports as n, hasCreateSpaceConfigInput as r, createSpaceFromParams as t };
@@ -3,17 +3,9 @@ import { fetchWithSsrFGuard } from "openclaw/plugin-sdk/ssrf-runtime";
3
3
  //#region extensions/google-meet/src/google-api.ts
4
4
  const REAUTH_HINT = "Re-run `openclaw googlemeet auth login` and store the refreshed oauth block.";
5
5
  const GOOGLE_API_ERROR_BODY_LIMIT_BYTES = 8192;
6
- function scopeText(scopes) {
7
- return scopes.map((scope) => `\`${scope}\``).join(", ");
8
- }
9
6
  async function readGoogleApiErrorDetail(response) {
10
7
  return await readResponseTextLimited(response, GOOGLE_API_ERROR_BODY_LIMIT_BYTES);
11
8
  }
12
- async function googleApiError(params) {
13
- const detail = await readGoogleApiErrorDetail(params.response);
14
- const scopeHint = params.scopes && params.scopes.length > 0 ? ` Required OAuth scope: ${scopeText(params.scopes)}. ${REAUTH_HINT}` : "";
15
- return /* @__PURE__ */ new Error(`${params.prefix} failed (${params.response.status}): ${detail}${scopeHint}`);
16
- }
17
9
  function appendQuery(url, query) {
18
10
  if (!query) return url;
19
11
  const parsed = new URL(url);
@@ -36,11 +28,11 @@ async function requestGoogleApi(params, read) {
36
28
  timeoutMs: 3e4
37
29
  });
38
30
  try {
39
- if (!response.ok) throw await googleApiError({
40
- response,
41
- prefix: params.prefix,
42
- scopes: params.scopes
43
- });
31
+ if (!response.ok) {
32
+ const detail = await readGoogleApiErrorDetail(response);
33
+ const scopeHint = params.scopes && params.scopes.length > 0 ? ` Required OAuth scope: ${params.scopes.map((scope) => `\`${scope}\``).join(", ")}. ${REAUTH_HINT}` : "";
34
+ throw new Error(`${params.prefix} failed (${response.status}): ${detail}${scopeHint}`);
35
+ }
44
36
  return await read(response);
45
37
  } finally {
46
38
  await release();
@@ -1,6 +1,6 @@
1
1
  import { n as GOOGLE_MEET_NODE_COMMAND } from "./google-meet-platform-constants-CnRfrYX7.mjs";
2
- import { t as normalizeMeetUrl } from "./meet-url-BP2Jlvjs.mjs";
3
- import { a as isSameMeetUrlForReuse, i as isRecoverableMeetTab, n as forceMeetEnglishUi, o as normalizeMeetUrlForReuse, r as isEnglishMeetTab, s as readMeetAuthUser, t as createMeetWithBrowserProxyOnNode } from "./chrome-create-Bh1L33pr.mjs";
2
+ import { t as normalizeMeetUrl } from "./meet-url-i_QvIxnP.mjs";
3
+ import { a as isSameMeetUrlForReuse, i as isRecoverableMeetTab, n as forceMeetEnglishUi, o as normalizeMeetUrlForReuse, r as isEnglishMeetTab, s as readMeetAuthUser, t as createMeetWithBrowserProxyOnNode } from "./chrome-create-CW_QQzuj.mjs";
4
4
  import { asRecord, filterStringEntries, normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
5
5
  import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
6
6
  import { MeetingPlatformAdapter } from "openclaw/plugin-sdk/meeting-runtime";
@@ -804,7 +804,6 @@ function buildMeetDtmfSequence(params) {
804
804
  function prefixDtmfWait(sequence, delayMs) {
805
805
  if (!sequence || delayMs <= 0) return sequence;
806
806
  const waitCount = Math.ceil(delayMs / 500);
807
- if (waitCount <= 0) return sequence;
808
807
  return `${"w".repeat(waitCount)}${sequence}`;
809
808
  }
810
809
  //#endregion
@@ -3,12 +3,8 @@ import { normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runti
3
3
  function normalizeMeetUrl(input) {
4
4
  const raw = normalizeOptionalString(input);
5
5
  if (!raw) throw new Error("url required");
6
- let url;
7
- try {
8
- url = new URL(raw);
9
- } catch {
10
- throw new Error("url must be a valid Google Meet URL");
11
- }
6
+ const url = URL.parse(raw);
7
+ if (!url) throw new Error("url must be a valid Google Meet URL");
12
8
  if (url.protocol !== "https:" || url.hostname.toLowerCase() !== "meet.google.com" || url.port || url.username || url.password) throw new Error("url must be an explicit https://meet.google.com/... URL");
13
9
  if (!/^\/[a-z]{3}-[a-z]{4}-[a-z]{3}(?:$|[/?#])/i.test(url.pathname)) throw new Error("url must include a Google Meet meeting code");
14
10
  return url.toString();