@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
|
@@ -1,17 +1,17 @@
|
|
|
1
|
+
import { n as hasCreateSpaceConfigInput, r as resolveCreateSpaceConfig } from "./create-af1MDlus.js";
|
|
1
2
|
import { a as buildGoogleMeetCalendarDayWindow, i as resolveGoogleMeetGatewayOperationTimeoutMs, o as findGoogleMeetCalendarEvent, s as listGoogleMeetCalendarEvents } from "./config-BcaS1BDr.js";
|
|
2
|
-
import { _ as
|
|
3
|
+
import { _ as endGoogleMeetActiveConference, g as createGoogleMeetSpace, h as fetchGoogleMeetAttendance, m as fetchGoogleMeetArtifacts, p as buildGoogleMeetPreflightReport, v as fetchGoogleMeetSpace, y as fetchLatestGoogleMeetConferenceRecord } from "./chrome-create-DMKruRFW.js";
|
|
3
4
|
import { buildGoogleMeetAuthUrl, createGoogleMeetOAuthState, createGoogleMeetPkce, exchangeGoogleMeetAuthCode, resolveGoogleMeetAccessToken, waitForGoogleMeetAuthCode } from "./oauth-DOfMKlGp.js";
|
|
4
|
-
import { hasCreateSpaceConfigInput, resolveCreateSpaceConfig } from "./create-DK5lNDEp.js";
|
|
5
|
-
import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
|
|
6
5
|
import { callGatewayFromCli } from "openclaw/plugin-sdk/gateway-runtime";
|
|
7
6
|
import { clampTimerTimeoutMs, parseStrictNonNegativeInteger, parseStrictPositiveInteger } from "openclaw/plugin-sdk/number-runtime";
|
|
7
|
+
import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
|
|
8
8
|
import path from "node:path";
|
|
9
9
|
import { mkdir, writeFile } from "node:fs/promises";
|
|
10
|
+
import JSZip from "jszip";
|
|
10
11
|
import { createInterface } from "node:readline/promises";
|
|
11
12
|
import { format } from "node:util";
|
|
12
|
-
import JSZip from "jszip";
|
|
13
13
|
import prettyMilliseconds from "pretty-ms";
|
|
14
|
-
//#region extensions/google-meet/src/cli.ts
|
|
14
|
+
//#region extensions/google-meet/src/cli-shared.ts
|
|
15
15
|
const testing = {
|
|
16
16
|
parsePositiveNumber,
|
|
17
17
|
resolveGoogleMeetGatewayOperationTimeoutMs,
|
|
@@ -143,10 +143,10 @@ function writeDoctorStatus(status) {
|
|
|
143
143
|
writeStdoutLine("captioning: %s", formatBoolean(health?.captioning));
|
|
144
144
|
writeStdoutLine("transcript lines: %s", health?.transcriptLines ?? 0);
|
|
145
145
|
writeStdoutLine("last caption: %s", formatOptional(health?.lastCaptionAt));
|
|
146
|
-
writeStdoutLine("manual action: %s", formatBoolean(health?.
|
|
147
|
-
if (health?.
|
|
148
|
-
writeStdoutLine("manual reason: %s", formatOptional(health.
|
|
149
|
-
writeStdoutLine("manual message: %s", formatOptional(health.
|
|
146
|
+
writeStdoutLine("manual action: %s", formatBoolean(Boolean(health?.manualAction)));
|
|
147
|
+
if (health?.manualAction) {
|
|
148
|
+
writeStdoutLine("manual reason: %s", formatOptional(health.manualAction.reason));
|
|
149
|
+
writeStdoutLine("manual message: %s", formatOptional(health.manualAction.message));
|
|
150
150
|
}
|
|
151
151
|
writeStdoutLine("speech ready: %s", formatBoolean(health?.speechReady));
|
|
152
152
|
if (health?.speechReady === false) {
|
|
@@ -175,112 +175,6 @@ function writeDoctorStatus(status) {
|
|
|
175
175
|
}
|
|
176
176
|
}
|
|
177
177
|
}
|
|
178
|
-
function sanitizeOAuthErrorMessage(error) {
|
|
179
|
-
return (error instanceof Error ? error.message : String(error)).replace(/(access_token["'=:\s]+)[^"',\s&]+/gi, "$1[redacted]").replace(/(refresh_token["'=:\s]+)[^"',\s&]+/gi, "$1[redacted]").replace(/(client_secret["'=:\s]+)[^"',\s&]+/gi, "$1[redacted]");
|
|
180
|
-
}
|
|
181
|
-
async function buildOAuthDoctorReport(config, options) {
|
|
182
|
-
const clientId = options.clientId?.trim() || config.oauth.clientId;
|
|
183
|
-
const clientSecret = options.clientSecret?.trim() || config.oauth.clientSecret;
|
|
184
|
-
const refreshToken = options.refreshToken?.trim() || config.oauth.refreshToken;
|
|
185
|
-
const accessToken = options.accessToken?.trim() || config.oauth.accessToken;
|
|
186
|
-
const expiresAt = parseOptionalNumber(options.expiresAt) ?? config.oauth.expiresAt;
|
|
187
|
-
const checks = [];
|
|
188
|
-
const hasRefreshConfig = Boolean(clientId && refreshToken);
|
|
189
|
-
if (!hasRefreshConfig && !Boolean(accessToken)) {
|
|
190
|
-
checks.push({
|
|
191
|
-
id: "oauth-config",
|
|
192
|
-
ok: false,
|
|
193
|
-
message: "Missing Google Meet OAuth credentials. Configure oauth.clientId and oauth.refreshToken, or pass --client-id and --refresh-token."
|
|
194
|
-
});
|
|
195
|
-
return {
|
|
196
|
-
ok: false,
|
|
197
|
-
configured: false,
|
|
198
|
-
checks
|
|
199
|
-
};
|
|
200
|
-
}
|
|
201
|
-
checks.push({
|
|
202
|
-
id: "oauth-config",
|
|
203
|
-
ok: true,
|
|
204
|
-
message: hasRefreshConfig ? "Google Meet OAuth refresh credentials are configured" : "Google Meet cached access token is configured"
|
|
205
|
-
});
|
|
206
|
-
let token;
|
|
207
|
-
try {
|
|
208
|
-
token = await resolveGoogleMeetAccessToken({
|
|
209
|
-
clientId,
|
|
210
|
-
clientSecret,
|
|
211
|
-
refreshToken,
|
|
212
|
-
accessToken,
|
|
213
|
-
expiresAt
|
|
214
|
-
});
|
|
215
|
-
checks.push({
|
|
216
|
-
id: "oauth-token",
|
|
217
|
-
ok: true,
|
|
218
|
-
message: token.refreshed ? "Refresh token minted an access token" : "Cached access token is still valid"
|
|
219
|
-
});
|
|
220
|
-
} catch (error) {
|
|
221
|
-
checks.push({
|
|
222
|
-
id: "oauth-token",
|
|
223
|
-
ok: false,
|
|
224
|
-
message: sanitizeOAuthErrorMessage(error)
|
|
225
|
-
});
|
|
226
|
-
return {
|
|
227
|
-
ok: false,
|
|
228
|
-
configured: true,
|
|
229
|
-
checks
|
|
230
|
-
};
|
|
231
|
-
}
|
|
232
|
-
const report = {
|
|
233
|
-
ok: true,
|
|
234
|
-
configured: true,
|
|
235
|
-
tokenSource: token.refreshed ? "refresh-token" : "cached-access-token",
|
|
236
|
-
expiresAt: token.expiresAt,
|
|
237
|
-
checks
|
|
238
|
-
};
|
|
239
|
-
const meeting = options.meeting?.trim();
|
|
240
|
-
if (meeting) try {
|
|
241
|
-
const space = await fetchGoogleMeetSpace({
|
|
242
|
-
accessToken: token.accessToken,
|
|
243
|
-
meeting
|
|
244
|
-
});
|
|
245
|
-
checks.push({
|
|
246
|
-
id: "meet-spaces-get",
|
|
247
|
-
ok: true,
|
|
248
|
-
message: `Resolved ${space.name}`
|
|
249
|
-
});
|
|
250
|
-
report.meetingUri = space.meetingUri;
|
|
251
|
-
} catch (error) {
|
|
252
|
-
checks.push({
|
|
253
|
-
id: "meet-spaces-get",
|
|
254
|
-
ok: false,
|
|
255
|
-
message: sanitizeOAuthErrorMessage(error)
|
|
256
|
-
});
|
|
257
|
-
}
|
|
258
|
-
if (options.createSpace) try {
|
|
259
|
-
const created = await createGoogleMeetSpace({ accessToken: token.accessToken });
|
|
260
|
-
checks.push({
|
|
261
|
-
id: "meet-spaces-create",
|
|
262
|
-
ok: true,
|
|
263
|
-
message: `Created ${created.space.name}`
|
|
264
|
-
});
|
|
265
|
-
report.createdSpace = created.space.name;
|
|
266
|
-
report.meetingUri = created.meetingUri;
|
|
267
|
-
} catch (error) {
|
|
268
|
-
checks.push({
|
|
269
|
-
id: "meet-spaces-create",
|
|
270
|
-
ok: false,
|
|
271
|
-
message: sanitizeOAuthErrorMessage(error)
|
|
272
|
-
});
|
|
273
|
-
}
|
|
274
|
-
report.ok = checks.every((check) => check.ok);
|
|
275
|
-
return report;
|
|
276
|
-
}
|
|
277
|
-
function writeOAuthDoctorReport(report) {
|
|
278
|
-
writeStdoutLine("Google Meet OAuth: %s", report.ok ? "OK" : "needs attention");
|
|
279
|
-
writeStdoutLine("configured: %s", report.configured ? "yes" : "no");
|
|
280
|
-
if (report.tokenSource) writeStdoutLine("token source: %s", report.tokenSource);
|
|
281
|
-
if (report.meetingUri) writeStdoutLine("meeting uri: %s", report.meetingUri);
|
|
282
|
-
for (const check of report.checks) writeStdoutLine("[%s] %s: %s", check.ok ? "ok" : "fail", check.id, check.message);
|
|
283
|
-
}
|
|
284
178
|
function writeRecoverCurrentTabResult(result) {
|
|
285
179
|
writeStdoutLine("Google Meet current tab: %s", result.found ? "found" : "not found");
|
|
286
180
|
writeStdoutLine("transport: %s", result.transport);
|
|
@@ -321,88 +215,8 @@ function writeLeaveResult(sessionId, result) {
|
|
|
321
215
|
}
|
|
322
216
|
writeStdoutLine("left %s", sessionId);
|
|
323
217
|
}
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
if (!meeting) throw new Error("Meeting input is required. Pass a URL/meeting code or configure defaults.meeting.");
|
|
327
|
-
return meeting;
|
|
328
|
-
}
|
|
329
|
-
function resolveOAuthTokenOptions(config, options) {
|
|
330
|
-
return {
|
|
331
|
-
clientId: options.clientId?.trim() || config.oauth.clientId,
|
|
332
|
-
clientSecret: options.clientSecret?.trim() || config.oauth.clientSecret,
|
|
333
|
-
refreshToken: options.refreshToken?.trim() || config.oauth.refreshToken,
|
|
334
|
-
accessToken: options.accessToken?.trim() || config.oauth.accessToken,
|
|
335
|
-
expiresAt: parseOptionalNumber(options.expiresAt) ?? config.oauth.expiresAt
|
|
336
|
-
};
|
|
337
|
-
}
|
|
338
|
-
function resolveTokenOptions(config, options) {
|
|
339
|
-
return {
|
|
340
|
-
meeting: resolveMeetingInput(config, options.meeting),
|
|
341
|
-
...resolveOAuthTokenOptions(config, options)
|
|
342
|
-
};
|
|
343
|
-
}
|
|
344
|
-
function hasCalendarLookupOptions(options) {
|
|
345
|
-
return Boolean(options.today || options.event?.trim());
|
|
346
|
-
}
|
|
347
|
-
async function resolveCalendarMeetingInput(params) {
|
|
348
|
-
if (!hasCalendarLookupOptions(params.options)) return {};
|
|
349
|
-
const window = params.options.today ? buildGoogleMeetCalendarDayWindow() : {};
|
|
350
|
-
const calendarEvent = await findGoogleMeetCalendarEvent({
|
|
351
|
-
accessToken: params.accessToken,
|
|
352
|
-
calendarId: params.options.calendar,
|
|
353
|
-
eventQuery: params.options.event,
|
|
354
|
-
...window
|
|
355
|
-
});
|
|
356
|
-
return {
|
|
357
|
-
meeting: calendarEvent.meetingUri,
|
|
358
|
-
calendarEvent
|
|
359
|
-
};
|
|
360
|
-
}
|
|
361
|
-
async function resolveMeetingForToken(params) {
|
|
362
|
-
const calendarMeeting = await resolveCalendarMeetingInput({
|
|
363
|
-
accessToken: params.accessToken,
|
|
364
|
-
options: params.options
|
|
365
|
-
});
|
|
366
|
-
const meeting = calendarMeeting.meeting ?? params.configuredMeeting ?? params.config.defaults.meeting;
|
|
367
|
-
if (!meeting) throw new Error("Meeting input is required. Pass --meeting, --today, --event, or configure defaults.meeting.");
|
|
368
|
-
return calendarMeeting.calendarEvent ? {
|
|
369
|
-
meeting,
|
|
370
|
-
calendarEvent: calendarMeeting.calendarEvent
|
|
371
|
-
} : { meeting };
|
|
372
|
-
}
|
|
373
|
-
function resolveCreateTokenOptions(config, options) {
|
|
374
|
-
return {
|
|
375
|
-
clientId: options.clientId?.trim() || config.oauth.clientId,
|
|
376
|
-
clientSecret: options.clientSecret?.trim() || config.oauth.clientSecret,
|
|
377
|
-
refreshToken: options.refreshToken?.trim() || config.oauth.refreshToken,
|
|
378
|
-
accessToken: options.accessToken?.trim() || config.oauth.accessToken,
|
|
379
|
-
expiresAt: parseOptionalNumber(options.expiresAt) ?? config.oauth.expiresAt
|
|
380
|
-
};
|
|
381
|
-
}
|
|
382
|
-
function resolveArtifactTokenOptions(config, options) {
|
|
383
|
-
const meeting = options.meeting?.trim() || config.defaults.meeting;
|
|
384
|
-
const conferenceRecord = options.conferenceRecord?.trim();
|
|
385
|
-
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.");
|
|
386
|
-
return {
|
|
387
|
-
meeting,
|
|
388
|
-
conferenceRecord,
|
|
389
|
-
clientId: options.clientId?.trim() || config.oauth.clientId,
|
|
390
|
-
clientSecret: options.clientSecret?.trim() || config.oauth.clientSecret,
|
|
391
|
-
refreshToken: options.refreshToken?.trim() || config.oauth.refreshToken,
|
|
392
|
-
accessToken: options.accessToken?.trim() || config.oauth.accessToken,
|
|
393
|
-
expiresAt: parseOptionalNumber(options.expiresAt) ?? config.oauth.expiresAt,
|
|
394
|
-
pageSize: parsePositiveIntegerOption(options.pageSize, "page-size"),
|
|
395
|
-
includeTranscriptEntries: options.transcriptEntries !== false,
|
|
396
|
-
allConferenceRecords: Boolean(options.allConferenceRecords),
|
|
397
|
-
includeDocumentBodies: Boolean(options.includeDocBodies),
|
|
398
|
-
mergeDuplicateParticipants: options.mergeDuplicates !== false,
|
|
399
|
-
lateAfterMinutes: parseOptionalNumber(options.lateAfterMinutes),
|
|
400
|
-
earlyBeforeMinutes: parseOptionalNumber(options.earlyBeforeMinutes)
|
|
401
|
-
};
|
|
402
|
-
}
|
|
403
|
-
function hasCreateOAuth(config, options) {
|
|
404
|
-
return Boolean(options.accessToken?.trim() || options.refreshToken?.trim() || config.oauth.accessToken || config.oauth.refreshToken);
|
|
405
|
-
}
|
|
218
|
+
//#endregion
|
|
219
|
+
//#region extensions/google-meet/src/cli-export.ts
|
|
406
220
|
function writeArtifactsSummary(result) {
|
|
407
221
|
if (result.input) writeStdoutLine("input: %s", result.input);
|
|
408
222
|
if (result.space) writeStdoutLine("space: %s", result.space.name);
|
|
@@ -781,401 +595,99 @@ async function writeMeetExportBundle(params) {
|
|
|
781
595
|
}
|
|
782
596
|
return result;
|
|
783
597
|
}
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
const
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
const
|
|
791
|
-
|
|
792
|
-
const
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
authUrl,
|
|
807
|
-
promptInput,
|
|
808
|
-
writeLine: (message) => writeStdoutLine("%s", message)
|
|
809
|
-
}),
|
|
810
|
-
verifier
|
|
598
|
+
//#endregion
|
|
599
|
+
//#region extensions/google-meet/src/cli-artifact-commands.ts
|
|
600
|
+
function registerGoogleMeetArtifactCommands(context) {
|
|
601
|
+
const params = context;
|
|
602
|
+
const { root, resolveArtifactTokenOptions, resolveMeetingForToken } = context;
|
|
603
|
+
root.command("artifacts").description("List Meet conference records and available participant/artifact metadata").option("--meeting <value>", "Meet URL, meeting code, or spaces/{id}").option("--conference-record <name>", "Conference record name or id").option("--today", "Find a Meet link on today's calendar").option("--event <query>", "Find a matching calendar event with a Meet link").option("--calendar <id>", "Calendar id for --today or --event", "primary").option("--access-token <token>", "Access token override").option("--refresh-token <token>", "Refresh token override").option("--client-id <id>", "OAuth client id override").option("--client-secret <secret>", "OAuth client secret override").option("--expires-at <ms>", "Cached access token expiry as unix epoch milliseconds").option("--page-size <n>", "Max resources per Meet API page").option("--all-conference-records", "Fetch every conference record for --meeting").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) => {
|
|
604
|
+
const resolved = resolveArtifactTokenOptions(params.config, options);
|
|
605
|
+
const token = await resolveGoogleMeetAccessToken(resolved);
|
|
606
|
+
const meeting = resolved.conferenceRecord ? resolved.meeting : (await resolveMeetingForToken({
|
|
607
|
+
config: params.config,
|
|
608
|
+
options,
|
|
609
|
+
accessToken: token.accessToken,
|
|
610
|
+
configuredMeeting: resolved.meeting
|
|
611
|
+
})).meeting;
|
|
612
|
+
const result = await fetchGoogleMeetArtifacts({
|
|
613
|
+
accessToken: token.accessToken,
|
|
614
|
+
meeting,
|
|
615
|
+
conferenceRecord: resolved.conferenceRecord,
|
|
616
|
+
pageSize: resolved.pageSize,
|
|
617
|
+
includeTranscriptEntries: resolved.includeTranscriptEntries,
|
|
618
|
+
allConferenceRecords: resolved.allConferenceRecords,
|
|
619
|
+
includeDocumentBodies: resolved.includeDocumentBodies
|
|
811
620
|
});
|
|
812
|
-
if (!tokens.refreshToken) throw new Error("Google OAuth did not return a refresh token. Re-run the flow with consent and offline access.");
|
|
813
|
-
const payload = {
|
|
814
|
-
oauth: {
|
|
815
|
-
clientId,
|
|
816
|
-
...clientSecret ? { clientSecret } : {},
|
|
817
|
-
refreshToken: tokens.refreshToken,
|
|
818
|
-
accessToken: tokens.accessToken,
|
|
819
|
-
expiresAt: tokens.expiresAt
|
|
820
|
-
},
|
|
821
|
-
scope: tokens.scope,
|
|
822
|
-
tokenType: tokens.tokenType
|
|
823
|
-
};
|
|
824
|
-
if (!options.json) writeStdoutLine("Paste this into plugins.entries.google-meet.config:");
|
|
825
|
-
writeStdoutJson(payload);
|
|
826
|
-
});
|
|
827
|
-
root.command("create").description("Create a new Google Meet space and print its meeting URL").option("--access-token <token>", "Access token override").option("--refresh-token <token>", "Refresh token override").option("--client-id <id>", "OAuth client id override").option("--client-secret <secret>", "OAuth client secret override").option("--expires-at <ms>", "Cached access token expiry as unix epoch milliseconds").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) => {
|
|
828
|
-
if (options.join !== false) {
|
|
829
|
-
const delegated = await callGoogleMeetGateway({
|
|
830
|
-
callGateway,
|
|
831
|
-
method: "googlemeet.create",
|
|
832
|
-
payload: { ...options },
|
|
833
|
-
timeoutMs: operationTimeoutMs
|
|
834
|
-
});
|
|
835
|
-
if (delegated.ok) {
|
|
836
|
-
const payload = delegated.payload;
|
|
837
|
-
if (options.json) {
|
|
838
|
-
writeStdoutJson(payload);
|
|
839
|
-
return;
|
|
840
|
-
}
|
|
841
|
-
writeStdoutLine("meeting uri: %s", payload.meetingUri);
|
|
842
|
-
if (payload.space?.name) writeStdoutLine("space: %s", payload.space.name);
|
|
843
|
-
if (payload.space?.meetingCode) writeStdoutLine("meeting code: %s", payload.space.meetingCode);
|
|
844
|
-
if (payload.source) writeStdoutLine("source: %s", payload.source);
|
|
845
|
-
if (payload.browser?.nodeId) writeStdoutLine("node: %s", payload.browser.nodeId);
|
|
846
|
-
if (payload.tokenSource) writeStdoutLine("token source: %s", payload.tokenSource);
|
|
847
|
-
if (payload.joined && payload.join?.session?.id) writeStdoutLine("joined: %s", payload.join.session.id);
|
|
848
|
-
else writeStdoutLine("joined: no (run `openclaw googlemeet join %s`)", payload.meetingUri);
|
|
849
|
-
return;
|
|
850
|
-
}
|
|
851
|
-
}
|
|
852
|
-
if (!hasCreateOAuth(params.config, options)) {
|
|
853
|
-
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.");
|
|
854
|
-
const rt = await params.ensureRuntime();
|
|
855
|
-
const result = await rt.createViaBrowser();
|
|
856
|
-
const join = options.join !== false ? await rt.join({
|
|
857
|
-
url: result.meetingUri,
|
|
858
|
-
transport: options.transport,
|
|
859
|
-
mode: options.mode,
|
|
860
|
-
message: options.message,
|
|
861
|
-
dialInNumber: options.dialInNumber,
|
|
862
|
-
pin: options.pin,
|
|
863
|
-
dtmfSequence: options.dtmfSequence
|
|
864
|
-
}) : void 0;
|
|
865
|
-
const payload = {
|
|
866
|
-
source: result.source,
|
|
867
|
-
meetingUri: result.meetingUri,
|
|
868
|
-
joined: Boolean(join),
|
|
869
|
-
...join ? { join } : {},
|
|
870
|
-
browser: {
|
|
871
|
-
nodeId: result.nodeId,
|
|
872
|
-
targetId: result.targetId,
|
|
873
|
-
browserUrl: result.browserUrl,
|
|
874
|
-
browserTitle: result.browserTitle
|
|
875
|
-
}
|
|
876
|
-
};
|
|
877
|
-
if (options.json) {
|
|
878
|
-
writeStdoutJson(payload);
|
|
879
|
-
return;
|
|
880
|
-
}
|
|
881
|
-
writeStdoutLine("meeting uri: %s", result.meetingUri);
|
|
882
|
-
writeStdoutLine("source: browser");
|
|
883
|
-
writeStdoutLine("node: %s", result.nodeId);
|
|
884
|
-
if (join) writeStdoutLine("joined: %s", join.session.id);
|
|
885
|
-
else writeStdoutLine("joined: no (run `openclaw googlemeet join %s`)", result.meetingUri);
|
|
886
|
-
return;
|
|
887
|
-
}
|
|
888
|
-
const token = await resolveGoogleMeetAccessToken(resolveCreateTokenOptions(params.config, options));
|
|
889
|
-
const result = await createGoogleMeetSpace({
|
|
890
|
-
accessToken: token.accessToken,
|
|
891
|
-
config: resolveCreateSpaceConfig(options)
|
|
892
|
-
});
|
|
893
|
-
const join = options.join !== false ? await (await params.ensureRuntime()).join({
|
|
894
|
-
url: result.meetingUri,
|
|
895
|
-
transport: options.transport,
|
|
896
|
-
mode: options.mode,
|
|
897
|
-
message: options.message,
|
|
898
|
-
dialInNumber: options.dialInNumber,
|
|
899
|
-
pin: options.pin,
|
|
900
|
-
dtmfSequence: options.dtmfSequence
|
|
901
|
-
}) : void 0;
|
|
902
621
|
if (options.json) {
|
|
903
|
-
|
|
622
|
+
await writeCliOutput(options, JSON.stringify({
|
|
904
623
|
...result,
|
|
905
|
-
tokenSource: token.refreshed ? "refresh-token" : "cached-access-token"
|
|
906
|
-
|
|
907
|
-
...join ? { join } : {}
|
|
908
|
-
});
|
|
624
|
+
tokenSource: token.refreshed ? "refresh-token" : "cached-access-token"
|
|
625
|
+
}, null, 2));
|
|
909
626
|
return;
|
|
910
627
|
}
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
628
|
+
if (options.format === "markdown") {
|
|
629
|
+
await writeCliOutput(options, renderArtifactsMarkdown(result));
|
|
630
|
+
return;
|
|
631
|
+
}
|
|
632
|
+
if (options.format && options.format !== "summary") throw new Error("Unsupported format. Expected summary or markdown.");
|
|
633
|
+
writeArtifactsSummary(result);
|
|
914
634
|
writeStdoutLine("token source: %s", token.refreshed ? "refresh-token" : "cached-access-token");
|
|
915
|
-
if (join) writeStdoutLine("joined: %s", join.session.id);
|
|
916
|
-
else writeStdoutLine("joined: no (run `openclaw googlemeet join %s`)", result.meetingUri);
|
|
917
635
|
});
|
|
918
|
-
root.command("
|
|
919
|
-
const
|
|
920
|
-
const
|
|
636
|
+
root.command("attendance").description("List Meet participants and participant sessions").option("--meeting <value>", "Meet URL, meeting code, or spaces/{id}").option("--conference-record <name>", "Conference record name or id").option("--today", "Find a Meet link on today's calendar").option("--event <query>", "Find a matching calendar event with a Meet link").option("--calendar <id>", "Calendar id for --today or --event", "primary").option("--access-token <token>", "Access token override").option("--refresh-token <token>", "Refresh token override").option("--client-id <id>", "OAuth client id override").option("--client-secret <secret>", "OAuth client secret override").option("--expires-at <ms>", "Cached access token expiry as unix epoch milliseconds").option("--page-size <n>", "Max resources per Meet API page").option("--all-conference-records", "Fetch every conference record for --meeting").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) => {
|
|
637
|
+
const resolved = resolveArtifactTokenOptions(params.config, options);
|
|
638
|
+
const token = await resolveGoogleMeetAccessToken(resolved);
|
|
639
|
+
const meeting = resolved.conferenceRecord ? resolved.meeting : (await resolveMeetingForToken({
|
|
640
|
+
config: params.config,
|
|
641
|
+
options,
|
|
921
642
|
accessToken: token.accessToken,
|
|
922
|
-
|
|
643
|
+
configuredMeeting: resolved.meeting
|
|
644
|
+
})).meeting;
|
|
645
|
+
const result = await fetchGoogleMeetAttendance({
|
|
646
|
+
accessToken: token.accessToken,
|
|
647
|
+
meeting,
|
|
648
|
+
conferenceRecord: resolved.conferenceRecord,
|
|
649
|
+
pageSize: resolved.pageSize,
|
|
650
|
+
allConferenceRecords: resolved.allConferenceRecords,
|
|
651
|
+
mergeDuplicateParticipants: resolved.mergeDuplicateParticipants,
|
|
652
|
+
lateAfterMinutes: resolved.lateAfterMinutes,
|
|
653
|
+
earlyBeforeMinutes: resolved.earlyBeforeMinutes
|
|
923
654
|
});
|
|
924
655
|
if (options.json) {
|
|
925
|
-
|
|
656
|
+
await writeCliOutput(options, JSON.stringify({
|
|
926
657
|
...result,
|
|
927
658
|
tokenSource: token.refreshed ? "refresh-token" : "cached-access-token"
|
|
928
|
-
});
|
|
929
|
-
return;
|
|
930
|
-
}
|
|
931
|
-
writeStdoutLine("space: %s", result.space);
|
|
932
|
-
writeStdoutLine("ended: yes");
|
|
933
|
-
writeStdoutLine("token source: %s", token.refreshed ? "refresh-token" : "cached-access-token");
|
|
934
|
-
});
|
|
935
|
-
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) => {
|
|
936
|
-
const payload = {
|
|
937
|
-
url: resolveMeetingInput(params.config, url),
|
|
938
|
-
transport: options.transport,
|
|
939
|
-
mode: options.mode,
|
|
940
|
-
message: options.message,
|
|
941
|
-
dialInNumber: options.dialInNumber,
|
|
942
|
-
pin: options.pin,
|
|
943
|
-
dtmfSequence: options.dtmfSequence
|
|
944
|
-
};
|
|
945
|
-
const delegated = await callGoogleMeetGateway({
|
|
946
|
-
callGateway,
|
|
947
|
-
method: "googlemeet.join",
|
|
948
|
-
payload,
|
|
949
|
-
timeoutMs: operationTimeoutMs
|
|
950
|
-
});
|
|
951
|
-
if (delegated.ok) {
|
|
952
|
-
const result = delegated.payload;
|
|
953
|
-
writeStdoutJson(result.session ?? delegated.payload);
|
|
954
|
-
return;
|
|
955
|
-
}
|
|
956
|
-
writeStdoutJson((await (await params.ensureRuntime()).join(payload)).session);
|
|
957
|
-
});
|
|
958
|
-
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) => {
|
|
959
|
-
const payload = {
|
|
960
|
-
url: resolveMeetingInput(params.config, url),
|
|
961
|
-
transport: options.transport,
|
|
962
|
-
mode: options.mode,
|
|
963
|
-
message: options.message
|
|
964
|
-
};
|
|
965
|
-
const delegated = await callGoogleMeetGateway({
|
|
966
|
-
callGateway,
|
|
967
|
-
method: "googlemeet.testSpeech",
|
|
968
|
-
payload,
|
|
969
|
-
timeoutMs: operationTimeoutMs
|
|
970
|
-
});
|
|
971
|
-
if (delegated.ok) {
|
|
972
|
-
writeStdoutJson(delegated.payload);
|
|
659
|
+
}, null, 2));
|
|
973
660
|
return;
|
|
974
661
|
}
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
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) => {
|
|
978
|
-
const payload = {
|
|
979
|
-
url: resolveMeetingInput(params.config, url),
|
|
980
|
-
transport: options.transport,
|
|
981
|
-
timeoutMs: parsePositiveNumber(options.timeoutMs, "timeout-ms")
|
|
982
|
-
};
|
|
983
|
-
const delegated = await callGoogleMeetGateway({
|
|
984
|
-
callGateway,
|
|
985
|
-
method: "googlemeet.testListen",
|
|
986
|
-
payload,
|
|
987
|
-
timeoutMs: operationTimeoutMs
|
|
988
|
-
});
|
|
989
|
-
if (delegated.ok) {
|
|
990
|
-
writeStdoutJson(delegated.payload);
|
|
662
|
+
if (options.format === "markdown") {
|
|
663
|
+
await writeCliOutput(options, renderAttendanceMarkdown(result));
|
|
991
664
|
return;
|
|
992
665
|
}
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
root.command("resolve-space").description("Resolve a Meet URL, meeting code, or spaces/{id} to its canonical space").option("--meeting <value>", "Meet URL, meeting code, or spaces/{id}").option("--access-token <token>", "Access token override").option("--refresh-token <token>", "Refresh token override").option("--client-id <id>", "OAuth client id override").option("--client-secret <secret>", "OAuth client secret override").option("--expires-at <ms>", "Cached access token expiry as unix epoch milliseconds").option("--json", "Print JSON output", false).action(async (options) => {
|
|
996
|
-
const resolved = resolveTokenOptions(params.config, options);
|
|
997
|
-
const token = await resolveGoogleMeetAccessToken(resolved);
|
|
998
|
-
const space = await fetchGoogleMeetSpace({
|
|
999
|
-
accessToken: token.accessToken,
|
|
1000
|
-
meeting: resolved.meeting
|
|
1001
|
-
});
|
|
1002
|
-
if (options.json) {
|
|
1003
|
-
writeStdoutJson(space);
|
|
666
|
+
if (options.format === "csv") {
|
|
667
|
+
await writeCliOutput(options, renderAttendanceCsv(result));
|
|
1004
668
|
return;
|
|
1005
669
|
}
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
if (space.meetingCode) writeStdoutLine("meeting code: %s", space.meetingCode);
|
|
1009
|
-
if (space.meetingUri) writeStdoutLine("meeting uri: %s", space.meetingUri);
|
|
1010
|
-
writeStdoutLine("active conference: %s", space.activeConference ? "yes" : "no");
|
|
670
|
+
if (options.format && options.format !== "summary") throw new Error("Unsupported format. Expected summary, markdown, or csv.");
|
|
671
|
+
writeAttendanceSummary(result);
|
|
1011
672
|
writeStdoutLine("token source: %s", token.refreshed ? "refresh-token" : "cached-access-token");
|
|
1012
673
|
});
|
|
1013
|
-
root.command("
|
|
1014
|
-
const resolved =
|
|
674
|
+
root.command("export").description("Write Meet artifacts, attendance, transcript, and raw JSON into a folder").option("--meeting <value>", "Meet URL, meeting code, or spaces/{id}").option("--conference-record <name>", "Conference record name or id").option("--today", "Find a Meet link on today's calendar").option("--event <query>", "Find a matching calendar event with a Meet link").option("--calendar <id>", "Calendar id for --today or --event", "primary").option("--access-token <token>", "Access token override").option("--refresh-token <token>", "Refresh token override").option("--client-id <id>", "OAuth client id override").option("--client-secret <secret>", "OAuth client secret override").option("--expires-at <ms>", "Cached access token expiry as unix epoch milliseconds").option("--page-size <n>", "Max resources per Meet API page").option("--all-conference-records", "Fetch every conference record for --meeting").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) => {
|
|
675
|
+
const resolved = resolveArtifactTokenOptions(params.config, options);
|
|
1015
676
|
const token = await resolveGoogleMeetAccessToken(resolved);
|
|
1016
|
-
const
|
|
1017
|
-
accessToken: token.accessToken,
|
|
1018
|
-
meeting: resolved.meeting
|
|
1019
|
-
});
|
|
1020
|
-
const report = buildGoogleMeetPreflightReport({
|
|
1021
|
-
input: resolved.meeting,
|
|
1022
|
-
space,
|
|
1023
|
-
previewAcknowledged: params.config.preview.enrollmentAcknowledged,
|
|
1024
|
-
tokenSource: token.refreshed ? "refresh-token" : "cached-access-token"
|
|
1025
|
-
});
|
|
1026
|
-
if (options.json) {
|
|
1027
|
-
writeStdoutJson(report);
|
|
1028
|
-
return;
|
|
1029
|
-
}
|
|
1030
|
-
writeStdoutLine("input: %s", report.input);
|
|
1031
|
-
writeStdoutLine("resolved space: %s", report.resolvedSpaceName);
|
|
1032
|
-
if (report.meetingCode) writeStdoutLine("meeting code: %s", report.meetingCode);
|
|
1033
|
-
if (report.meetingUri) writeStdoutLine("meeting uri: %s", report.meetingUri);
|
|
1034
|
-
writeStdoutLine("active conference: %s", report.hasActiveConference ? "yes" : "no");
|
|
1035
|
-
writeStdoutLine("preview acknowledged: %s", report.previewAcknowledged ? "yes" : "no");
|
|
1036
|
-
writeStdoutLine("token source: %s", report.tokenSource);
|
|
1037
|
-
if (report.blockers.length === 0) {
|
|
1038
|
-
writeStdoutLine("blockers: none");
|
|
1039
|
-
return;
|
|
1040
|
-
}
|
|
1041
|
-
writeStdoutLine("blockers:");
|
|
1042
|
-
for (const blocker of report.blockers) writeStdoutLine("- %s", blocker);
|
|
1043
|
-
});
|
|
1044
|
-
root.command("latest").description("Find the latest Meet conference record for a meeting").option("--meeting <value>", "Meet URL, meeting code, or spaces/{id}").option("--today", "Find a Meet link on today's calendar").option("--event <query>", "Find a matching calendar event with a Meet link").option("--calendar <id>", "Calendar id for --today or --event", "primary").option("--access-token <token>", "Access token override").option("--refresh-token <token>", "Refresh token override").option("--client-id <id>", "OAuth client id override").option("--client-secret <secret>", "OAuth client secret override").option("--expires-at <ms>", "Cached access token expiry as unix epoch milliseconds").option("--json", "Print JSON output", false).action(async (options) => {
|
|
1045
|
-
const token = await resolveGoogleMeetAccessToken(resolveOAuthTokenOptions(params.config, options));
|
|
1046
|
-
const resolved = await resolveMeetingForToken({
|
|
677
|
+
const meetingResult = resolved.conferenceRecord ? { meeting: resolved.meeting } : await resolveMeetingForToken({
|
|
1047
678
|
config: params.config,
|
|
1048
679
|
options,
|
|
1049
680
|
accessToken: token.accessToken,
|
|
1050
|
-
configuredMeeting:
|
|
681
|
+
configuredMeeting: resolved.meeting
|
|
1051
682
|
});
|
|
1052
|
-
const
|
|
683
|
+
const artifacts = await fetchGoogleMeetArtifacts({
|
|
1053
684
|
accessToken: token.accessToken,
|
|
1054
|
-
meeting:
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
tokenSource: token.refreshed ? "refresh-token" : "cached-access-token"
|
|
1061
|
-
});
|
|
1062
|
-
return;
|
|
1063
|
-
}
|
|
1064
|
-
if (resolved.calendarEvent) {
|
|
1065
|
-
writeStdoutLine("calendar event: %s", resolved.calendarEvent.event.summary ?? "untitled");
|
|
1066
|
-
writeStdoutLine("calendar meet: %s", resolved.calendarEvent.meetingUri);
|
|
1067
|
-
}
|
|
1068
|
-
writeLatestConferenceRecordSummary(result);
|
|
1069
|
-
writeStdoutLine("token source: %s", token.refreshed ? "refresh-token" : "cached-access-token");
|
|
1070
|
-
});
|
|
1071
|
-
root.command("calendar-events").description("Preview Calendar events with Google Meet links").option("--today", "Find Meet links on today's calendar").option("--event <query>", "Find matching calendar events with Meet links").option("--calendar <id>", "Calendar id for lookup", "primary").option("--access-token <token>", "Access token override").option("--refresh-token <token>", "Refresh token override").option("--client-id <id>", "OAuth client id override").option("--client-secret <secret>", "OAuth client secret override").option("--expires-at <ms>", "Cached access token expiry as unix epoch milliseconds").option("--json", "Print JSON output", false).action(async (options) => {
|
|
1072
|
-
const token = await resolveGoogleMeetAccessToken(resolveOAuthTokenOptions(params.config, options));
|
|
1073
|
-
const window = options.today ? buildGoogleMeetCalendarDayWindow() : {};
|
|
1074
|
-
const result = await listGoogleMeetCalendarEvents({
|
|
1075
|
-
accessToken: token.accessToken,
|
|
1076
|
-
calendarId: options.calendar,
|
|
1077
|
-
eventQuery: options.event,
|
|
1078
|
-
...window
|
|
1079
|
-
});
|
|
1080
|
-
const payload = {
|
|
1081
|
-
...result,
|
|
1082
|
-
tokenSource: token.refreshed ? "refresh-token" : "cached-access-token"
|
|
1083
|
-
};
|
|
1084
|
-
if (options.json) {
|
|
1085
|
-
writeStdoutJson(payload);
|
|
1086
|
-
return;
|
|
1087
|
-
}
|
|
1088
|
-
writeCalendarEventsSummary(result);
|
|
1089
|
-
writeStdoutLine("token source: %s", token.refreshed ? "refresh-token" : "cached-access-token");
|
|
1090
|
-
});
|
|
1091
|
-
root.command("artifacts").description("List Meet conference records and available participant/artifact metadata").option("--meeting <value>", "Meet URL, meeting code, or spaces/{id}").option("--conference-record <name>", "Conference record name or id").option("--today", "Find a Meet link on today's calendar").option("--event <query>", "Find a matching calendar event with a Meet link").option("--calendar <id>", "Calendar id for --today or --event", "primary").option("--access-token <token>", "Access token override").option("--refresh-token <token>", "Refresh token override").option("--client-id <id>", "OAuth client id override").option("--client-secret <secret>", "OAuth client secret override").option("--expires-at <ms>", "Cached access token expiry as unix epoch milliseconds").option("--page-size <n>", "Max resources per Meet API page").option("--all-conference-records", "Fetch every conference record for --meeting").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) => {
|
|
1092
|
-
const resolved = resolveArtifactTokenOptions(params.config, options);
|
|
1093
|
-
const token = await resolveGoogleMeetAccessToken(resolved);
|
|
1094
|
-
const meeting = resolved.conferenceRecord ? resolved.meeting : (await resolveMeetingForToken({
|
|
1095
|
-
config: params.config,
|
|
1096
|
-
options,
|
|
1097
|
-
accessToken: token.accessToken,
|
|
1098
|
-
configuredMeeting: resolved.meeting
|
|
1099
|
-
})).meeting;
|
|
1100
|
-
const result = await fetchGoogleMeetArtifacts({
|
|
1101
|
-
accessToken: token.accessToken,
|
|
1102
|
-
meeting,
|
|
1103
|
-
conferenceRecord: resolved.conferenceRecord,
|
|
1104
|
-
pageSize: resolved.pageSize,
|
|
1105
|
-
includeTranscriptEntries: resolved.includeTranscriptEntries,
|
|
1106
|
-
allConferenceRecords: resolved.allConferenceRecords,
|
|
1107
|
-
includeDocumentBodies: resolved.includeDocumentBodies
|
|
1108
|
-
});
|
|
1109
|
-
if (options.json) {
|
|
1110
|
-
await writeCliOutput(options, JSON.stringify({
|
|
1111
|
-
...result,
|
|
1112
|
-
tokenSource: token.refreshed ? "refresh-token" : "cached-access-token"
|
|
1113
|
-
}, null, 2));
|
|
1114
|
-
return;
|
|
1115
|
-
}
|
|
1116
|
-
if (options.format === "markdown") {
|
|
1117
|
-
await writeCliOutput(options, renderArtifactsMarkdown(result));
|
|
1118
|
-
return;
|
|
1119
|
-
}
|
|
1120
|
-
if (options.format && options.format !== "summary") throw new Error("Unsupported format. Expected summary or markdown.");
|
|
1121
|
-
writeArtifactsSummary(result);
|
|
1122
|
-
writeStdoutLine("token source: %s", token.refreshed ? "refresh-token" : "cached-access-token");
|
|
1123
|
-
});
|
|
1124
|
-
root.command("attendance").description("List Meet participants and participant sessions").option("--meeting <value>", "Meet URL, meeting code, or spaces/{id}").option("--conference-record <name>", "Conference record name or id").option("--today", "Find a Meet link on today's calendar").option("--event <query>", "Find a matching calendar event with a Meet link").option("--calendar <id>", "Calendar id for --today or --event", "primary").option("--access-token <token>", "Access token override").option("--refresh-token <token>", "Refresh token override").option("--client-id <id>", "OAuth client id override").option("--client-secret <secret>", "OAuth client secret override").option("--expires-at <ms>", "Cached access token expiry as unix epoch milliseconds").option("--page-size <n>", "Max resources per Meet API page").option("--all-conference-records", "Fetch every conference record for --meeting").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) => {
|
|
1125
|
-
const resolved = resolveArtifactTokenOptions(params.config, options);
|
|
1126
|
-
const token = await resolveGoogleMeetAccessToken(resolved);
|
|
1127
|
-
const meeting = resolved.conferenceRecord ? resolved.meeting : (await resolveMeetingForToken({
|
|
1128
|
-
config: params.config,
|
|
1129
|
-
options,
|
|
1130
|
-
accessToken: token.accessToken,
|
|
1131
|
-
configuredMeeting: resolved.meeting
|
|
1132
|
-
})).meeting;
|
|
1133
|
-
const result = await fetchGoogleMeetAttendance({
|
|
1134
|
-
accessToken: token.accessToken,
|
|
1135
|
-
meeting,
|
|
1136
|
-
conferenceRecord: resolved.conferenceRecord,
|
|
1137
|
-
pageSize: resolved.pageSize,
|
|
1138
|
-
allConferenceRecords: resolved.allConferenceRecords,
|
|
1139
|
-
mergeDuplicateParticipants: resolved.mergeDuplicateParticipants,
|
|
1140
|
-
lateAfterMinutes: resolved.lateAfterMinutes,
|
|
1141
|
-
earlyBeforeMinutes: resolved.earlyBeforeMinutes
|
|
1142
|
-
});
|
|
1143
|
-
if (options.json) {
|
|
1144
|
-
await writeCliOutput(options, JSON.stringify({
|
|
1145
|
-
...result,
|
|
1146
|
-
tokenSource: token.refreshed ? "refresh-token" : "cached-access-token"
|
|
1147
|
-
}, null, 2));
|
|
1148
|
-
return;
|
|
1149
|
-
}
|
|
1150
|
-
if (options.format === "markdown") {
|
|
1151
|
-
await writeCliOutput(options, renderAttendanceMarkdown(result));
|
|
1152
|
-
return;
|
|
1153
|
-
}
|
|
1154
|
-
if (options.format === "csv") {
|
|
1155
|
-
await writeCliOutput(options, renderAttendanceCsv(result));
|
|
1156
|
-
return;
|
|
1157
|
-
}
|
|
1158
|
-
if (options.format && options.format !== "summary") throw new Error("Unsupported format. Expected summary, markdown, or csv.");
|
|
1159
|
-
writeAttendanceSummary(result);
|
|
1160
|
-
writeStdoutLine("token source: %s", token.refreshed ? "refresh-token" : "cached-access-token");
|
|
1161
|
-
});
|
|
1162
|
-
root.command("export").description("Write Meet artifacts, attendance, transcript, and raw JSON into a folder").option("--meeting <value>", "Meet URL, meeting code, or spaces/{id}").option("--conference-record <name>", "Conference record name or id").option("--today", "Find a Meet link on today's calendar").option("--event <query>", "Find a matching calendar event with a Meet link").option("--calendar <id>", "Calendar id for --today or --event", "primary").option("--access-token <token>", "Access token override").option("--refresh-token <token>", "Refresh token override").option("--client-id <id>", "OAuth client id override").option("--client-secret <secret>", "OAuth client secret override").option("--expires-at <ms>", "Cached access token expiry as unix epoch milliseconds").option("--page-size <n>", "Max resources per Meet API page").option("--all-conference-records", "Fetch every conference record for --meeting").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) => {
|
|
1163
|
-
const resolved = resolveArtifactTokenOptions(params.config, options);
|
|
1164
|
-
const token = await resolveGoogleMeetAccessToken(resolved);
|
|
1165
|
-
const meetingResult = resolved.conferenceRecord ? { meeting: resolved.meeting } : await resolveMeetingForToken({
|
|
1166
|
-
config: params.config,
|
|
1167
|
-
options,
|
|
1168
|
-
accessToken: token.accessToken,
|
|
1169
|
-
configuredMeeting: resolved.meeting
|
|
1170
|
-
});
|
|
1171
|
-
const artifacts = await fetchGoogleMeetArtifacts({
|
|
1172
|
-
accessToken: token.accessToken,
|
|
1173
|
-
meeting: meetingResult.meeting,
|
|
1174
|
-
conferenceRecord: resolved.conferenceRecord,
|
|
1175
|
-
pageSize: resolved.pageSize,
|
|
1176
|
-
includeTranscriptEntries: resolved.includeTranscriptEntries,
|
|
1177
|
-
allConferenceRecords: resolved.allConferenceRecords,
|
|
1178
|
-
includeDocumentBodies: resolved.includeDocumentBodies
|
|
685
|
+
meeting: meetingResult.meeting,
|
|
686
|
+
conferenceRecord: resolved.conferenceRecord,
|
|
687
|
+
pageSize: resolved.pageSize,
|
|
688
|
+
includeTranscriptEntries: resolved.includeTranscriptEntries,
|
|
689
|
+
allConferenceRecords: resolved.allConferenceRecords,
|
|
690
|
+
includeDocumentBodies: resolved.includeDocumentBodies
|
|
1179
691
|
});
|
|
1180
692
|
const attendance = await fetchGoogleMeetAttendance({
|
|
1181
693
|
accessToken: token.accessToken,
|
|
@@ -1240,40 +752,118 @@ function registerGoogleMeetCli(params) {
|
|
|
1240
752
|
for (const file of bundle.files) writeStdoutLine("- %s", file);
|
|
1241
753
|
if (bundle.zipFile) writeStdoutLine("zip: %s", bundle.zipFile);
|
|
1242
754
|
});
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
755
|
+
}
|
|
756
|
+
//#endregion
|
|
757
|
+
//#region extensions/google-meet/src/cli-doctor.ts
|
|
758
|
+
function sanitizeOAuthErrorMessage(error) {
|
|
759
|
+
return (error instanceof Error ? error.message : String(error)).replace(/(access_token["'=:\s]+)[^"',\s&]+/gi, "$1[redacted]").replace(/(refresh_token["'=:\s]+)[^"',\s&]+/gi, "$1[redacted]").replace(/(client_secret["'=:\s]+)[^"',\s&]+/gi, "$1[redacted]");
|
|
760
|
+
}
|
|
761
|
+
async function buildOAuthDoctorReport(config, options) {
|
|
762
|
+
const clientId = options.clientId?.trim() || config.oauth.clientId;
|
|
763
|
+
const clientSecret = options.clientSecret?.trim() || config.oauth.clientSecret;
|
|
764
|
+
const refreshToken = options.refreshToken?.trim() || config.oauth.refreshToken;
|
|
765
|
+
const accessToken = options.accessToken?.trim() || config.oauth.accessToken;
|
|
766
|
+
const expiresAt = parseOptionalNumber(options.expiresAt) ?? config.oauth.expiresAt;
|
|
767
|
+
const checks = [];
|
|
768
|
+
const hasRefreshConfig = Boolean(clientId && refreshToken);
|
|
769
|
+
if (!hasRefreshConfig && !Boolean(accessToken)) {
|
|
770
|
+
checks.push({
|
|
771
|
+
id: "oauth-config",
|
|
772
|
+
ok: false,
|
|
773
|
+
message: "Missing Google Meet OAuth credentials. Configure oauth.clientId and oauth.refreshToken, or pass --client-id and --refresh-token."
|
|
1248
774
|
});
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
775
|
+
return {
|
|
776
|
+
ok: false,
|
|
777
|
+
configured: false,
|
|
778
|
+
checks
|
|
779
|
+
};
|
|
780
|
+
}
|
|
781
|
+
checks.push({
|
|
782
|
+
id: "oauth-config",
|
|
783
|
+
ok: true,
|
|
784
|
+
message: hasRefreshConfig ? "Google Meet OAuth refresh credentials are configured" : "Google Meet cached access token is configured"
|
|
1254
785
|
});
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
...sinceIndex === void 0 ? {} : { sinceIndex }
|
|
1264
|
-
}
|
|
786
|
+
let token;
|
|
787
|
+
try {
|
|
788
|
+
token = await resolveGoogleMeetAccessToken({
|
|
789
|
+
clientId,
|
|
790
|
+
clientSecret,
|
|
791
|
+
refreshToken,
|
|
792
|
+
accessToken,
|
|
793
|
+
expiresAt
|
|
1265
794
|
});
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
795
|
+
checks.push({
|
|
796
|
+
id: "oauth-token",
|
|
797
|
+
ok: true,
|
|
798
|
+
message: token.refreshed ? "Refresh token minted an access token" : "Cached access token is still valid"
|
|
799
|
+
});
|
|
800
|
+
} catch (error) {
|
|
801
|
+
checks.push({
|
|
802
|
+
id: "oauth-token",
|
|
803
|
+
ok: false,
|
|
804
|
+
message: sanitizeOAuthErrorMessage(error)
|
|
805
|
+
});
|
|
806
|
+
return {
|
|
807
|
+
ok: false,
|
|
808
|
+
configured: true,
|
|
809
|
+
checks
|
|
810
|
+
};
|
|
811
|
+
}
|
|
812
|
+
const report = {
|
|
813
|
+
ok: true,
|
|
814
|
+
configured: true,
|
|
815
|
+
tokenSource: token.refreshed ? "refresh-token" : "cached-access-token",
|
|
816
|
+
expiresAt: token.expiresAt,
|
|
817
|
+
checks
|
|
818
|
+
};
|
|
819
|
+
const meeting = options.meeting?.trim();
|
|
820
|
+
if (meeting) try {
|
|
821
|
+
const space = await fetchGoogleMeetSpace({
|
|
822
|
+
accessToken: token.accessToken,
|
|
823
|
+
meeting
|
|
824
|
+
});
|
|
825
|
+
checks.push({
|
|
826
|
+
id: "meet-spaces-get",
|
|
827
|
+
ok: true,
|
|
828
|
+
message: `Resolved ${space.name}`
|
|
829
|
+
});
|
|
830
|
+
report.meetingUri = space.meetingUri;
|
|
831
|
+
} catch (error) {
|
|
832
|
+
checks.push({
|
|
833
|
+
id: "meet-spaces-get",
|
|
834
|
+
ok: false,
|
|
835
|
+
message: sanitizeOAuthErrorMessage(error)
|
|
836
|
+
});
|
|
837
|
+
}
|
|
838
|
+
if (options.createSpace) try {
|
|
839
|
+
const created = await createGoogleMeetSpace({ accessToken: token.accessToken });
|
|
840
|
+
checks.push({
|
|
841
|
+
id: "meet-spaces-create",
|
|
842
|
+
ok: true,
|
|
843
|
+
message: `Created ${created.space.name}`
|
|
844
|
+
});
|
|
845
|
+
report.createdSpace = created.space.name;
|
|
846
|
+
report.meetingUri = created.meetingUri;
|
|
847
|
+
} catch (error) {
|
|
848
|
+
checks.push({
|
|
849
|
+
id: "meet-spaces-create",
|
|
850
|
+
ok: false,
|
|
851
|
+
message: sanitizeOAuthErrorMessage(error)
|
|
852
|
+
});
|
|
853
|
+
}
|
|
854
|
+
report.ok = checks.every((check) => check.ok);
|
|
855
|
+
return report;
|
|
856
|
+
}
|
|
857
|
+
function writeOAuthDoctorReport(report) {
|
|
858
|
+
writeStdoutLine("Google Meet OAuth: %s", report.ok ? "OK" : "needs attention");
|
|
859
|
+
writeStdoutLine("configured: %s", report.configured ? "yes" : "no");
|
|
860
|
+
if (report.tokenSource) writeStdoutLine("token source: %s", report.tokenSource);
|
|
861
|
+
if (report.meetingUri) writeStdoutLine("meeting uri: %s", report.meetingUri);
|
|
862
|
+
for (const check of report.checks) writeStdoutLine("[%s] %s: %s", check.ok ? "ok" : "fail", check.id, check.message);
|
|
863
|
+
}
|
|
864
|
+
function registerGoogleMeetDoctorCommand(context) {
|
|
865
|
+
const params = context;
|
|
866
|
+
const { root, callGateway } = context;
|
|
1277
867
|
root.command("doctor").description("Show human-readable Meet session/browser/realtime health").argument("[session-id]", "Meet session ID").option("--oauth", "Verify Google Meet OAuth token refresh without printing secrets", false).option("--meeting <value>", "Also verify spaces.get for a Meet URL, code, or spaces/{id}").option("--create-space", "Also verify spaces.create by creating a throwaway Meet space", false).option("--access-token <token>", "Access token override").option("--refresh-token <token>", "Refresh token override").option("--client-id <id>", "OAuth client id override").option("--client-secret <secret>", "OAuth client secret override").option("--expires-at <ms>", "Cached access token expiry as unix epoch milliseconds").option("--json", "Print JSON output", false).action(async (sessionId, options) => {
|
|
1278
868
|
if (options.oauth) {
|
|
1279
869
|
const report = await buildOAuthDoctorReport(params.config, options);
|
|
@@ -1305,12 +895,120 @@ function registerGoogleMeetCli(params) {
|
|
|
1305
895
|
}
|
|
1306
896
|
writeDoctorStatus(status);
|
|
1307
897
|
});
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
898
|
+
}
|
|
899
|
+
//#endregion
|
|
900
|
+
//#region extensions/google-meet/src/cli-runtime-commands.ts
|
|
901
|
+
function registerGoogleMeetProbeCommands(context) {
|
|
902
|
+
const params = context;
|
|
903
|
+
const { root, callGateway, operationTimeoutMs, resolveMeetingInput } = context;
|
|
904
|
+
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) => {
|
|
905
|
+
const payload = {
|
|
906
|
+
url: resolveMeetingInput(params.config, url),
|
|
907
|
+
transport: options.transport,
|
|
908
|
+
mode: options.mode,
|
|
909
|
+
message: options.message,
|
|
910
|
+
dialInNumber: options.dialInNumber,
|
|
911
|
+
pin: options.pin,
|
|
912
|
+
dtmfSequence: options.dtmfSequence
|
|
913
|
+
};
|
|
914
|
+
const delegated = await callGoogleMeetGateway({
|
|
915
|
+
callGateway,
|
|
916
|
+
method: "googlemeet.join",
|
|
917
|
+
payload,
|
|
918
|
+
timeoutMs: operationTimeoutMs
|
|
919
|
+
});
|
|
920
|
+
if (delegated.ok) {
|
|
921
|
+
const result = delegated.payload;
|
|
922
|
+
writeStdoutJson(result.session ?? delegated.payload);
|
|
923
|
+
return;
|
|
924
|
+
}
|
|
925
|
+
writeStdoutJson((await (await params.ensureRuntime()).join(payload)).session);
|
|
926
|
+
});
|
|
927
|
+
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) => {
|
|
928
|
+
const payload = {
|
|
929
|
+
url: resolveMeetingInput(params.config, url),
|
|
930
|
+
transport: options.transport,
|
|
931
|
+
mode: options.mode,
|
|
932
|
+
message: options.message
|
|
933
|
+
};
|
|
934
|
+
const delegated = await callGoogleMeetGateway({
|
|
935
|
+
callGateway,
|
|
936
|
+
method: "googlemeet.testSpeech",
|
|
937
|
+
payload,
|
|
938
|
+
timeoutMs: operationTimeoutMs
|
|
939
|
+
});
|
|
940
|
+
if (delegated.ok) {
|
|
941
|
+
writeStdoutJson(delegated.payload);
|
|
942
|
+
return;
|
|
943
|
+
}
|
|
944
|
+
writeStdoutJson(await (await params.ensureRuntime()).testSpeech(payload));
|
|
945
|
+
});
|
|
946
|
+
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) => {
|
|
947
|
+
const payload = {
|
|
948
|
+
url: resolveMeetingInput(params.config, url),
|
|
949
|
+
transport: options.transport,
|
|
950
|
+
timeoutMs: parsePositiveNumber(options.timeoutMs, "timeout-ms")
|
|
951
|
+
};
|
|
952
|
+
const delegated = await callGoogleMeetGateway({
|
|
953
|
+
callGateway,
|
|
954
|
+
method: "googlemeet.testListen",
|
|
955
|
+
payload,
|
|
956
|
+
timeoutMs: operationTimeoutMs
|
|
957
|
+
});
|
|
958
|
+
if (delegated.ok) {
|
|
959
|
+
writeStdoutJson(delegated.payload);
|
|
960
|
+
return;
|
|
961
|
+
}
|
|
962
|
+
writeStdoutJson(await (await params.ensureRuntime()).testListen(payload));
|
|
963
|
+
});
|
|
964
|
+
}
|
|
965
|
+
function registerGoogleMeetSessionCommands(context) {
|
|
966
|
+
const params = context;
|
|
967
|
+
const { root, callGateway } = context;
|
|
968
|
+
root.command("status").argument("[session-id]", "Meet session ID").option("--json", "Print JSON output", false).action(async (sessionId) => {
|
|
969
|
+
const delegated = await callGoogleMeetGateway({
|
|
970
|
+
callGateway,
|
|
971
|
+
method: "googlemeet.status",
|
|
972
|
+
payload: { sessionId }
|
|
973
|
+
});
|
|
974
|
+
if (delegated.ok) {
|
|
975
|
+
writeStdoutJson(delegated.payload);
|
|
976
|
+
return;
|
|
977
|
+
}
|
|
978
|
+
writeStdoutJson(await (await params.ensureRuntime()).status(sessionId));
|
|
979
|
+
});
|
|
980
|
+
root.command("transcript").description("Print the bounded caption transcript for a Meet session").argument("<session-id>", "Meet session ID").option("--since <index>", "Resume from the previous response's nextIndex").option("--json", "Print JSON output", false).action(async (sessionId, options) => {
|
|
981
|
+
const sinceIndex = parseStrictNonNegativeInteger(options.since);
|
|
982
|
+
if (options.since !== void 0 && sinceIndex === void 0) throw new Error("--since must be a non-negative safe integer");
|
|
983
|
+
const delegated = await callGoogleMeetGateway({
|
|
984
|
+
callGateway,
|
|
985
|
+
method: "googlemeet.transcript",
|
|
986
|
+
payload: {
|
|
987
|
+
sessionId,
|
|
988
|
+
...sinceIndex === void 0 ? {} : { sinceIndex }
|
|
989
|
+
}
|
|
990
|
+
});
|
|
991
|
+
const result = delegated.ok ? delegated.payload : await (await params.ensureRuntime()).transcript(sessionId, sinceIndex === void 0 ? {} : { sinceIndex });
|
|
992
|
+
if (!result.found) throw new Error("session not found");
|
|
993
|
+
if (options.json) {
|
|
994
|
+
writeStdoutJson(result);
|
|
995
|
+
return;
|
|
996
|
+
}
|
|
997
|
+
if (result.evicted) writeStdoutLine("# transcript evicted from runtime memory");
|
|
998
|
+
else if (result.droppedLines) writeStdoutLine("# %d earlier lines dropped by the transcript cap", result.droppedLines);
|
|
999
|
+
for (const line of result.lines ?? []) writeStdoutLine("%s%s%s", line.at ? `[${line.at}] ` : "", line.speaker ? `${line.speaker}: ` : "", line.text);
|
|
1000
|
+
writeStdoutLine("# nextIndex: %d", result.nextIndex ?? 0);
|
|
1001
|
+
});
|
|
1002
|
+
}
|
|
1003
|
+
function registerGoogleMeetLifecycleCommands(context) {
|
|
1004
|
+
const params = context;
|
|
1005
|
+
const { root, callGateway } = context;
|
|
1006
|
+
root.command("recover-tab").description("Focus and inspect an existing Google Meet tab").argument("[url]", "Optional Meet URL to match").option("--transport <transport>", "Transport to inspect: chrome or chrome-node").option("--json", "Print JSON output", false).action(async (url, options) => {
|
|
1007
|
+
const result = await (await params.ensureRuntime()).recoverCurrentTab({
|
|
1008
|
+
url,
|
|
1009
|
+
transport: options.transport
|
|
1010
|
+
});
|
|
1011
|
+
if (options.json) {
|
|
1314
1012
|
writeStdoutJson(result);
|
|
1315
1013
|
return;
|
|
1316
1014
|
}
|
|
@@ -1366,4 +1064,367 @@ function registerGoogleMeetCli(params) {
|
|
|
1366
1064
|
});
|
|
1367
1065
|
}
|
|
1368
1066
|
//#endregion
|
|
1067
|
+
//#region extensions/google-meet/src/cli-space-commands.ts
|
|
1068
|
+
function registerGoogleMeetCreateCommands(context) {
|
|
1069
|
+
const params = context;
|
|
1070
|
+
const { root, callGateway, operationTimeoutMs, hasCreateOAuth, resolveCreateTokenOptions, resolveMeetingInput, resolveOAuthTokenOptions } = context;
|
|
1071
|
+
root.command("create").description("Create a new Google Meet space and print its meeting URL").option("--access-token <token>", "Access token override").option("--refresh-token <token>", "Refresh token override").option("--client-id <id>", "OAuth client id override").option("--client-secret <secret>", "OAuth client secret override").option("--expires-at <ms>", "Cached access token expiry as unix epoch milliseconds").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) => {
|
|
1072
|
+
if (options.join !== false) {
|
|
1073
|
+
const delegated = await callGoogleMeetGateway({
|
|
1074
|
+
callGateway,
|
|
1075
|
+
method: "googlemeet.create",
|
|
1076
|
+
payload: { ...options },
|
|
1077
|
+
timeoutMs: operationTimeoutMs
|
|
1078
|
+
});
|
|
1079
|
+
if (delegated.ok) {
|
|
1080
|
+
const payload = delegated.payload;
|
|
1081
|
+
if (options.json) {
|
|
1082
|
+
writeStdoutJson(payload);
|
|
1083
|
+
return;
|
|
1084
|
+
}
|
|
1085
|
+
writeStdoutLine("meeting uri: %s", payload.meetingUri);
|
|
1086
|
+
if (payload.space?.name) writeStdoutLine("space: %s", payload.space.name);
|
|
1087
|
+
if (payload.space?.meetingCode) writeStdoutLine("meeting code: %s", payload.space.meetingCode);
|
|
1088
|
+
if (payload.source) writeStdoutLine("source: %s", payload.source);
|
|
1089
|
+
if (payload.browser?.nodeId) writeStdoutLine("node: %s", payload.browser.nodeId);
|
|
1090
|
+
if (payload.tokenSource) writeStdoutLine("token source: %s", payload.tokenSource);
|
|
1091
|
+
if (payload.joined && payload.join?.session?.id) writeStdoutLine("joined: %s", payload.join.session.id);
|
|
1092
|
+
else writeStdoutLine("joined: no (run `openclaw googlemeet join %s`)", payload.meetingUri);
|
|
1093
|
+
return;
|
|
1094
|
+
}
|
|
1095
|
+
}
|
|
1096
|
+
if (!hasCreateOAuth(params.config, options)) {
|
|
1097
|
+
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.");
|
|
1098
|
+
const rt = await params.ensureRuntime();
|
|
1099
|
+
const result = await rt.createViaBrowser();
|
|
1100
|
+
const join = options.join !== false ? await rt.join({
|
|
1101
|
+
url: result.meetingUri,
|
|
1102
|
+
transport: options.transport,
|
|
1103
|
+
mode: options.mode,
|
|
1104
|
+
message: options.message,
|
|
1105
|
+
dialInNumber: options.dialInNumber,
|
|
1106
|
+
pin: options.pin,
|
|
1107
|
+
dtmfSequence: options.dtmfSequence
|
|
1108
|
+
}) : void 0;
|
|
1109
|
+
const payload = {
|
|
1110
|
+
source: result.source,
|
|
1111
|
+
meetingUri: result.meetingUri,
|
|
1112
|
+
joined: Boolean(join),
|
|
1113
|
+
...join ? { join } : {},
|
|
1114
|
+
browser: {
|
|
1115
|
+
nodeId: result.nodeId,
|
|
1116
|
+
targetId: result.targetId,
|
|
1117
|
+
browserUrl: result.browserUrl,
|
|
1118
|
+
browserTitle: result.browserTitle
|
|
1119
|
+
}
|
|
1120
|
+
};
|
|
1121
|
+
if (options.json) {
|
|
1122
|
+
writeStdoutJson(payload);
|
|
1123
|
+
return;
|
|
1124
|
+
}
|
|
1125
|
+
writeStdoutLine("meeting uri: %s", result.meetingUri);
|
|
1126
|
+
writeStdoutLine("source: browser");
|
|
1127
|
+
writeStdoutLine("node: %s", result.nodeId);
|
|
1128
|
+
if (join) writeStdoutLine("joined: %s", join.session.id);
|
|
1129
|
+
else writeStdoutLine("joined: no (run `openclaw googlemeet join %s`)", result.meetingUri);
|
|
1130
|
+
return;
|
|
1131
|
+
}
|
|
1132
|
+
const token = await resolveGoogleMeetAccessToken(resolveCreateTokenOptions(params.config, options));
|
|
1133
|
+
const result = await createGoogleMeetSpace({
|
|
1134
|
+
accessToken: token.accessToken,
|
|
1135
|
+
config: resolveCreateSpaceConfig(options)
|
|
1136
|
+
});
|
|
1137
|
+
const join = options.join !== false ? await (await params.ensureRuntime()).join({
|
|
1138
|
+
url: result.meetingUri,
|
|
1139
|
+
transport: options.transport,
|
|
1140
|
+
mode: options.mode,
|
|
1141
|
+
message: options.message,
|
|
1142
|
+
dialInNumber: options.dialInNumber,
|
|
1143
|
+
pin: options.pin,
|
|
1144
|
+
dtmfSequence: options.dtmfSequence
|
|
1145
|
+
}) : void 0;
|
|
1146
|
+
if (options.json) {
|
|
1147
|
+
writeStdoutJson({
|
|
1148
|
+
...result,
|
|
1149
|
+
tokenSource: token.refreshed ? "refresh-token" : "cached-access-token",
|
|
1150
|
+
joined: Boolean(join),
|
|
1151
|
+
...join ? { join } : {}
|
|
1152
|
+
});
|
|
1153
|
+
return;
|
|
1154
|
+
}
|
|
1155
|
+
writeStdoutLine("meeting uri: %s", result.meetingUri);
|
|
1156
|
+
writeStdoutLine("space: %s", result.space.name);
|
|
1157
|
+
if (result.space.meetingCode) writeStdoutLine("meeting code: %s", result.space.meetingCode);
|
|
1158
|
+
writeStdoutLine("token source: %s", token.refreshed ? "refresh-token" : "cached-access-token");
|
|
1159
|
+
if (join) writeStdoutLine("joined: %s", join.session.id);
|
|
1160
|
+
else writeStdoutLine("joined: no (run `openclaw googlemeet join %s`)", result.meetingUri);
|
|
1161
|
+
});
|
|
1162
|
+
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("--access-token <token>", "Access token override").option("--refresh-token <token>", "Refresh token override").option("--client-id <id>", "OAuth client id override").option("--client-secret <secret>", "OAuth client secret override").option("--expires-at <ms>", "Cached access token expiry as unix epoch milliseconds").option("--json", "Print JSON output", false).action(async (meeting, options) => {
|
|
1163
|
+
const token = await resolveGoogleMeetAccessToken(resolveOAuthTokenOptions(params.config, options));
|
|
1164
|
+
const result = await endGoogleMeetActiveConference({
|
|
1165
|
+
accessToken: token.accessToken,
|
|
1166
|
+
meeting: resolveMeetingInput(params.config, meeting ?? options.meeting)
|
|
1167
|
+
});
|
|
1168
|
+
if (options.json) {
|
|
1169
|
+
writeStdoutJson({
|
|
1170
|
+
...result,
|
|
1171
|
+
tokenSource: token.refreshed ? "refresh-token" : "cached-access-token"
|
|
1172
|
+
});
|
|
1173
|
+
return;
|
|
1174
|
+
}
|
|
1175
|
+
writeStdoutLine("space: %s", result.space);
|
|
1176
|
+
writeStdoutLine("ended: yes");
|
|
1177
|
+
writeStdoutLine("token source: %s", token.refreshed ? "refresh-token" : "cached-access-token");
|
|
1178
|
+
});
|
|
1179
|
+
}
|
|
1180
|
+
function registerGoogleMeetApiCommands(context) {
|
|
1181
|
+
const params = context;
|
|
1182
|
+
const { root, resolveMeetingForToken, resolveOAuthTokenOptions, resolveTokenOptions } = context;
|
|
1183
|
+
root.command("resolve-space").description("Resolve a Meet URL, meeting code, or spaces/{id} to its canonical space").option("--meeting <value>", "Meet URL, meeting code, or spaces/{id}").option("--access-token <token>", "Access token override").option("--refresh-token <token>", "Refresh token override").option("--client-id <id>", "OAuth client id override").option("--client-secret <secret>", "OAuth client secret override").option("--expires-at <ms>", "Cached access token expiry as unix epoch milliseconds").option("--json", "Print JSON output", false).action(async (options) => {
|
|
1184
|
+
const resolved = resolveTokenOptions(params.config, options);
|
|
1185
|
+
const token = await resolveGoogleMeetAccessToken(resolved);
|
|
1186
|
+
const space = await fetchGoogleMeetSpace({
|
|
1187
|
+
accessToken: token.accessToken,
|
|
1188
|
+
meeting: resolved.meeting
|
|
1189
|
+
});
|
|
1190
|
+
if (options.json) {
|
|
1191
|
+
writeStdoutJson(space);
|
|
1192
|
+
return;
|
|
1193
|
+
}
|
|
1194
|
+
writeStdoutLine("input: %s", resolved.meeting);
|
|
1195
|
+
writeStdoutLine("space: %s", space.name);
|
|
1196
|
+
if (space.meetingCode) writeStdoutLine("meeting code: %s", space.meetingCode);
|
|
1197
|
+
if (space.meetingUri) writeStdoutLine("meeting uri: %s", space.meetingUri);
|
|
1198
|
+
writeStdoutLine("active conference: %s", space.activeConference ? "yes" : "no");
|
|
1199
|
+
writeStdoutLine("token source: %s", token.refreshed ? "refresh-token" : "cached-access-token");
|
|
1200
|
+
});
|
|
1201
|
+
root.command("preflight").description("Validate OAuth + meeting resolution prerequisites for Meet media work").option("--meeting <value>", "Meet URL, meeting code, or spaces/{id}").option("--access-token <token>", "Access token override").option("--refresh-token <token>", "Refresh token override").option("--client-id <id>", "OAuth client id override").option("--client-secret <secret>", "OAuth client secret override").option("--expires-at <ms>", "Cached access token expiry as unix epoch milliseconds").option("--json", "Print JSON output", false).action(async (options) => {
|
|
1202
|
+
const resolved = resolveTokenOptions(params.config, options);
|
|
1203
|
+
const token = await resolveGoogleMeetAccessToken(resolved);
|
|
1204
|
+
const space = await fetchGoogleMeetSpace({
|
|
1205
|
+
accessToken: token.accessToken,
|
|
1206
|
+
meeting: resolved.meeting
|
|
1207
|
+
});
|
|
1208
|
+
const report = buildGoogleMeetPreflightReport({
|
|
1209
|
+
input: resolved.meeting,
|
|
1210
|
+
space,
|
|
1211
|
+
previewAcknowledged: params.config.preview.enrollmentAcknowledged,
|
|
1212
|
+
tokenSource: token.refreshed ? "refresh-token" : "cached-access-token"
|
|
1213
|
+
});
|
|
1214
|
+
if (options.json) {
|
|
1215
|
+
writeStdoutJson(report);
|
|
1216
|
+
return;
|
|
1217
|
+
}
|
|
1218
|
+
writeStdoutLine("input: %s", report.input);
|
|
1219
|
+
writeStdoutLine("resolved space: %s", report.resolvedSpaceName);
|
|
1220
|
+
if (report.meetingCode) writeStdoutLine("meeting code: %s", report.meetingCode);
|
|
1221
|
+
if (report.meetingUri) writeStdoutLine("meeting uri: %s", report.meetingUri);
|
|
1222
|
+
writeStdoutLine("active conference: %s", report.hasActiveConference ? "yes" : "no");
|
|
1223
|
+
writeStdoutLine("preview acknowledged: %s", report.previewAcknowledged ? "yes" : "no");
|
|
1224
|
+
writeStdoutLine("token source: %s", report.tokenSource);
|
|
1225
|
+
if (report.blockers.length === 0) {
|
|
1226
|
+
writeStdoutLine("blockers: none");
|
|
1227
|
+
return;
|
|
1228
|
+
}
|
|
1229
|
+
writeStdoutLine("blockers:");
|
|
1230
|
+
for (const blocker of report.blockers) writeStdoutLine("- %s", blocker);
|
|
1231
|
+
});
|
|
1232
|
+
root.command("latest").description("Find the latest Meet conference record for a meeting").option("--meeting <value>", "Meet URL, meeting code, or spaces/{id}").option("--today", "Find a Meet link on today's calendar").option("--event <query>", "Find a matching calendar event with a Meet link").option("--calendar <id>", "Calendar id for --today or --event", "primary").option("--access-token <token>", "Access token override").option("--refresh-token <token>", "Refresh token override").option("--client-id <id>", "OAuth client id override").option("--client-secret <secret>", "OAuth client secret override").option("--expires-at <ms>", "Cached access token expiry as unix epoch milliseconds").option("--json", "Print JSON output", false).action(async (options) => {
|
|
1233
|
+
const token = await resolveGoogleMeetAccessToken(resolveOAuthTokenOptions(params.config, options));
|
|
1234
|
+
const resolved = await resolveMeetingForToken({
|
|
1235
|
+
config: params.config,
|
|
1236
|
+
options,
|
|
1237
|
+
accessToken: token.accessToken,
|
|
1238
|
+
configuredMeeting: options.meeting?.trim()
|
|
1239
|
+
});
|
|
1240
|
+
const result = await fetchLatestGoogleMeetConferenceRecord({
|
|
1241
|
+
accessToken: token.accessToken,
|
|
1242
|
+
meeting: resolved.meeting
|
|
1243
|
+
});
|
|
1244
|
+
if (options.json) {
|
|
1245
|
+
writeStdoutJson({
|
|
1246
|
+
...result,
|
|
1247
|
+
...resolved.calendarEvent ? { calendarEvent: resolved.calendarEvent } : {},
|
|
1248
|
+
tokenSource: token.refreshed ? "refresh-token" : "cached-access-token"
|
|
1249
|
+
});
|
|
1250
|
+
return;
|
|
1251
|
+
}
|
|
1252
|
+
if (resolved.calendarEvent) {
|
|
1253
|
+
writeStdoutLine("calendar event: %s", resolved.calendarEvent.event.summary ?? "untitled");
|
|
1254
|
+
writeStdoutLine("calendar meet: %s", resolved.calendarEvent.meetingUri);
|
|
1255
|
+
}
|
|
1256
|
+
writeLatestConferenceRecordSummary(result);
|
|
1257
|
+
writeStdoutLine("token source: %s", token.refreshed ? "refresh-token" : "cached-access-token");
|
|
1258
|
+
});
|
|
1259
|
+
root.command("calendar-events").description("Preview Calendar events with Google Meet links").option("--today", "Find Meet links on today's calendar").option("--event <query>", "Find matching calendar events with Meet links").option("--calendar <id>", "Calendar id for lookup", "primary").option("--access-token <token>", "Access token override").option("--refresh-token <token>", "Refresh token override").option("--client-id <id>", "OAuth client id override").option("--client-secret <secret>", "OAuth client secret override").option("--expires-at <ms>", "Cached access token expiry as unix epoch milliseconds").option("--json", "Print JSON output", false).action(async (options) => {
|
|
1260
|
+
const token = await resolveGoogleMeetAccessToken(resolveOAuthTokenOptions(params.config, options));
|
|
1261
|
+
const window = options.today ? buildGoogleMeetCalendarDayWindow() : {};
|
|
1262
|
+
const result = await listGoogleMeetCalendarEvents({
|
|
1263
|
+
accessToken: token.accessToken,
|
|
1264
|
+
calendarId: options.calendar,
|
|
1265
|
+
eventQuery: options.event,
|
|
1266
|
+
...window
|
|
1267
|
+
});
|
|
1268
|
+
const payload = {
|
|
1269
|
+
...result,
|
|
1270
|
+
tokenSource: token.refreshed ? "refresh-token" : "cached-access-token"
|
|
1271
|
+
};
|
|
1272
|
+
if (options.json) {
|
|
1273
|
+
writeStdoutJson(payload);
|
|
1274
|
+
return;
|
|
1275
|
+
}
|
|
1276
|
+
writeCalendarEventsSummary(result);
|
|
1277
|
+
writeStdoutLine("token source: %s", token.refreshed ? "refresh-token" : "cached-access-token");
|
|
1278
|
+
});
|
|
1279
|
+
}
|
|
1280
|
+
//#endregion
|
|
1281
|
+
//#region extensions/google-meet/src/cli.ts
|
|
1282
|
+
function resolveMeetingInput(config, value) {
|
|
1283
|
+
const meeting = value?.trim() || config.defaults.meeting;
|
|
1284
|
+
if (!meeting) throw new Error("Meeting input is required. Pass a URL/meeting code or configure defaults.meeting.");
|
|
1285
|
+
return meeting;
|
|
1286
|
+
}
|
|
1287
|
+
function resolveOAuthTokenOptions(config, options) {
|
|
1288
|
+
return {
|
|
1289
|
+
clientId: options.clientId?.trim() || config.oauth.clientId,
|
|
1290
|
+
clientSecret: options.clientSecret?.trim() || config.oauth.clientSecret,
|
|
1291
|
+
refreshToken: options.refreshToken?.trim() || config.oauth.refreshToken,
|
|
1292
|
+
accessToken: options.accessToken?.trim() || config.oauth.accessToken,
|
|
1293
|
+
expiresAt: parseOptionalNumber(options.expiresAt) ?? config.oauth.expiresAt
|
|
1294
|
+
};
|
|
1295
|
+
}
|
|
1296
|
+
function resolveTokenOptions(config, options) {
|
|
1297
|
+
return {
|
|
1298
|
+
meeting: resolveMeetingInput(config, options.meeting),
|
|
1299
|
+
...resolveOAuthTokenOptions(config, options)
|
|
1300
|
+
};
|
|
1301
|
+
}
|
|
1302
|
+
function hasCalendarLookupOptions(options) {
|
|
1303
|
+
return Boolean(options.today || options.event?.trim());
|
|
1304
|
+
}
|
|
1305
|
+
async function resolveCalendarMeetingInput(params) {
|
|
1306
|
+
if (!hasCalendarLookupOptions(params.options)) return {};
|
|
1307
|
+
const window = params.options.today ? buildGoogleMeetCalendarDayWindow() : {};
|
|
1308
|
+
const calendarEvent = await findGoogleMeetCalendarEvent({
|
|
1309
|
+
accessToken: params.accessToken,
|
|
1310
|
+
calendarId: params.options.calendar,
|
|
1311
|
+
eventQuery: params.options.event,
|
|
1312
|
+
...window
|
|
1313
|
+
});
|
|
1314
|
+
return {
|
|
1315
|
+
meeting: calendarEvent.meetingUri,
|
|
1316
|
+
calendarEvent
|
|
1317
|
+
};
|
|
1318
|
+
}
|
|
1319
|
+
async function resolveMeetingForToken(params) {
|
|
1320
|
+
const calendarMeeting = await resolveCalendarMeetingInput({
|
|
1321
|
+
accessToken: params.accessToken,
|
|
1322
|
+
options: params.options
|
|
1323
|
+
});
|
|
1324
|
+
const meeting = calendarMeeting.meeting ?? params.configuredMeeting ?? params.config.defaults.meeting;
|
|
1325
|
+
if (!meeting) throw new Error("Meeting input is required. Pass --meeting, --today, --event, or configure defaults.meeting.");
|
|
1326
|
+
return calendarMeeting.calendarEvent ? {
|
|
1327
|
+
meeting,
|
|
1328
|
+
calendarEvent: calendarMeeting.calendarEvent
|
|
1329
|
+
} : { meeting };
|
|
1330
|
+
}
|
|
1331
|
+
function resolveCreateTokenOptions(config, options) {
|
|
1332
|
+
return {
|
|
1333
|
+
clientId: options.clientId?.trim() || config.oauth.clientId,
|
|
1334
|
+
clientSecret: options.clientSecret?.trim() || config.oauth.clientSecret,
|
|
1335
|
+
refreshToken: options.refreshToken?.trim() || config.oauth.refreshToken,
|
|
1336
|
+
accessToken: options.accessToken?.trim() || config.oauth.accessToken,
|
|
1337
|
+
expiresAt: parseOptionalNumber(options.expiresAt) ?? config.oauth.expiresAt
|
|
1338
|
+
};
|
|
1339
|
+
}
|
|
1340
|
+
function resolveArtifactTokenOptions(config, options) {
|
|
1341
|
+
const meeting = options.meeting?.trim() || config.defaults.meeting;
|
|
1342
|
+
const conferenceRecord = options.conferenceRecord?.trim();
|
|
1343
|
+
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.");
|
|
1344
|
+
return {
|
|
1345
|
+
meeting,
|
|
1346
|
+
conferenceRecord,
|
|
1347
|
+
clientId: options.clientId?.trim() || config.oauth.clientId,
|
|
1348
|
+
clientSecret: options.clientSecret?.trim() || config.oauth.clientSecret,
|
|
1349
|
+
refreshToken: options.refreshToken?.trim() || config.oauth.refreshToken,
|
|
1350
|
+
accessToken: options.accessToken?.trim() || config.oauth.accessToken,
|
|
1351
|
+
expiresAt: parseOptionalNumber(options.expiresAt) ?? config.oauth.expiresAt,
|
|
1352
|
+
pageSize: parsePositiveIntegerOption(options.pageSize, "page-size"),
|
|
1353
|
+
includeTranscriptEntries: options.transcriptEntries !== false,
|
|
1354
|
+
allConferenceRecords: Boolean(options.allConferenceRecords),
|
|
1355
|
+
includeDocumentBodies: Boolean(options.includeDocBodies),
|
|
1356
|
+
mergeDuplicateParticipants: options.mergeDuplicates !== false,
|
|
1357
|
+
lateAfterMinutes: parseOptionalNumber(options.lateAfterMinutes),
|
|
1358
|
+
earlyBeforeMinutes: parseOptionalNumber(options.earlyBeforeMinutes)
|
|
1359
|
+
};
|
|
1360
|
+
}
|
|
1361
|
+
function hasCreateOAuth(config, options) {
|
|
1362
|
+
return Boolean(options.accessToken?.trim() || options.refreshToken?.trim() || config.oauth.accessToken || config.oauth.refreshToken);
|
|
1363
|
+
}
|
|
1364
|
+
function registerGoogleMeetCli(params) {
|
|
1365
|
+
const callGateway = params.callGatewayFromCli ?? callGatewayFromCli;
|
|
1366
|
+
const operationTimeoutMs = resolveGoogleMeetGatewayOperationTimeoutMs(params.config);
|
|
1367
|
+
const root = params.program.command("googlemeet").description("Google Meet participant utilities").addHelpText("after", () => `\nDocs: https://docs.openclaw.ai/plugins/google-meet\n`);
|
|
1368
|
+
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) => {
|
|
1369
|
+
const clientId = options.clientId?.trim() || params.config.oauth.clientId;
|
|
1370
|
+
const clientSecret = options.clientSecret?.trim() || params.config.oauth.clientSecret;
|
|
1371
|
+
if (!clientId) throw new Error("Missing Google Meet OAuth client id. Configure oauth.clientId or pass --client-id.");
|
|
1372
|
+
const { verifier, challenge } = createGoogleMeetPkce();
|
|
1373
|
+
const state = createGoogleMeetOAuthState();
|
|
1374
|
+
const authUrl = buildGoogleMeetAuthUrl({
|
|
1375
|
+
clientId,
|
|
1376
|
+
challenge,
|
|
1377
|
+
state
|
|
1378
|
+
});
|
|
1379
|
+
const tokens = await exchangeGoogleMeetAuthCode({
|
|
1380
|
+
clientId,
|
|
1381
|
+
clientSecret,
|
|
1382
|
+
code: await waitForGoogleMeetAuthCode({
|
|
1383
|
+
state,
|
|
1384
|
+
manual: Boolean(options.manual),
|
|
1385
|
+
timeoutMs: resolveGoogleMeetOAuthCallbackTimeoutMs(options.timeoutSec),
|
|
1386
|
+
authUrl,
|
|
1387
|
+
promptInput,
|
|
1388
|
+
writeLine: (message) => writeStdoutLine("%s", message)
|
|
1389
|
+
}),
|
|
1390
|
+
verifier
|
|
1391
|
+
});
|
|
1392
|
+
if (!tokens.refreshToken) throw new Error("Google OAuth did not return a refresh token. Re-run the flow with consent and offline access.");
|
|
1393
|
+
const payload = {
|
|
1394
|
+
oauth: {
|
|
1395
|
+
clientId,
|
|
1396
|
+
...clientSecret ? { clientSecret } : {},
|
|
1397
|
+
refreshToken: tokens.refreshToken,
|
|
1398
|
+
accessToken: tokens.accessToken,
|
|
1399
|
+
expiresAt: tokens.expiresAt
|
|
1400
|
+
},
|
|
1401
|
+
scope: tokens.scope,
|
|
1402
|
+
tokenType: tokens.tokenType
|
|
1403
|
+
};
|
|
1404
|
+
if (!options.json) writeStdoutLine("Paste this into plugins.entries.google-meet.config:");
|
|
1405
|
+
writeStdoutJson(payload);
|
|
1406
|
+
});
|
|
1407
|
+
const context = {
|
|
1408
|
+
root,
|
|
1409
|
+
config: params.config,
|
|
1410
|
+
ensureRuntime: params.ensureRuntime,
|
|
1411
|
+
callGateway,
|
|
1412
|
+
operationTimeoutMs,
|
|
1413
|
+
resolveMeetingInput,
|
|
1414
|
+
resolveOAuthTokenOptions,
|
|
1415
|
+
resolveTokenOptions,
|
|
1416
|
+
resolveMeetingForToken,
|
|
1417
|
+
resolveCreateTokenOptions,
|
|
1418
|
+
resolveArtifactTokenOptions,
|
|
1419
|
+
hasCreateOAuth
|
|
1420
|
+
};
|
|
1421
|
+
registerGoogleMeetCreateCommands(context);
|
|
1422
|
+
registerGoogleMeetProbeCommands(context);
|
|
1423
|
+
registerGoogleMeetApiCommands(context);
|
|
1424
|
+
registerGoogleMeetArtifactCommands(context);
|
|
1425
|
+
registerGoogleMeetSessionCommands(context);
|
|
1426
|
+
registerGoogleMeetDoctorCommand(context);
|
|
1427
|
+
registerGoogleMeetLifecycleCommands(context);
|
|
1428
|
+
}
|
|
1429
|
+
//#endregion
|
|
1369
1430
|
export { buildGoogleMeetExportManifest, googleMeetExportFileNames, registerGoogleMeetCli, testing, writeMeetExportBundle };
|