@openclaw/google-meet 2026.7.2-beta.5 → 2026.7.33
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-DMKruRFW.js → .setup/chrome-create-CFNlY-qH.mjs} +153 -132
- package/dist/{cli-GeuAaEIy.js → .setup/cli-DfOl5mwt.mjs} +716 -735
- package/dist/{config-BcaS1BDr.js → .setup/config-DeJhjNWQ.mjs} +95 -76
- package/dist/{create-af1MDlus.js → .setup/create-BVsTTJt1.mjs} +3 -21
- package/dist/{google-api-errors-CDUYkIcK.js → .setup/google-api-errors-BDYXfKRa.mjs} +1 -1
- package/dist/{oauth-DOfMKlGp.js → .setup/oauth-CtVbvmvR.mjs} +4 -3
- package/dist/doctor-contract-api.js +3 -1
- package/dist/index.js +3868 -1818
- package/node_modules/{setimmediate/LICENSE.txt → commander/LICENSE} +10 -8
- package/node_modules/commander/Readme.md +1172 -0
- package/node_modules/commander/index.js +21 -0
- package/node_modules/commander/lib/argument.js +147 -0
- package/node_modules/commander/lib/command.js +2790 -0
- package/node_modules/commander/lib/error.js +36 -0
- package/node_modules/commander/lib/help.js +731 -0
- package/node_modules/commander/lib/option.js +377 -0
- package/node_modules/commander/lib/suggestSimilar.js +99 -0
- package/node_modules/commander/package-support.json +19 -0
- package/node_modules/commander/package.json +64 -0
- package/node_modules/commander/typings/index.d.ts +1113 -0
- package/node_modules/typebox/build/compile/validator.d.mts +4 -1
- package/node_modules/typebox/build/guard/guard.d.mts +1 -1
- package/node_modules/typebox/build/guard/guard.mjs +2 -5
- package/node_modules/typebox/build/guard/string.mjs +4 -16
- package/node_modules/typebox/build/schema/engine/_functions.mjs +15 -34
- package/node_modules/typebox/build/type/action/readonly_object.d.mts +0 -5
- package/node_modules/typebox/build/type/action/readonly_object.mjs +0 -8
- 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 +32 -29
- package/openclaw.plugin.json +3 -4
- package/package.json +7 -9
- package/dist/cli-metadata.js +0 -13
- package/dist/cli-output-mode-BwH_6tH8.js +0 -28
- package/node_modules/core-util-is/LICENSE +0 -19
- package/node_modules/core-util-is/README.md +0 -3
- package/node_modules/core-util-is/lib/util.js +0 -107
- package/node_modules/core-util-is/package.json +0 -38
- package/node_modules/immediate/LICENSE.txt +0 -20
- package/node_modules/immediate/README.md +0 -93
- package/node_modules/immediate/dist/immediate.js +0 -75
- package/node_modules/immediate/dist/immediate.min.js +0 -1
- package/node_modules/immediate/lib/browser.js +0 -69
- package/node_modules/immediate/lib/index.js +0 -73
- package/node_modules/immediate/package.json +0 -42
- package/node_modules/inherits/LICENSE +0 -16
- package/node_modules/inherits/README.md +0 -42
- package/node_modules/inherits/inherits.js +0 -9
- package/node_modules/inherits/inherits_browser.js +0 -27
- package/node_modules/inherits/package.json +0 -29
- package/node_modules/isarray/.npmignore +0 -1
- package/node_modules/isarray/.travis.yml +0 -4
- package/node_modules/isarray/Makefile +0 -6
- package/node_modules/isarray/README.md +0 -60
- package/node_modules/isarray/component.json +0 -19
- package/node_modules/isarray/index.js +0 -5
- package/node_modules/isarray/package.json +0 -45
- package/node_modules/isarray/test.js +0 -20
- package/node_modules/jszip/.codeclimate.yml +0 -16
- package/node_modules/jszip/.editorconfig +0 -8
- package/node_modules/jszip/.eslintrc.js +0 -43
- package/node_modules/jszip/.github/workflows/pr.yaml +0 -58
- package/node_modules/jszip/.jekyll-metadata +0 -0
- package/node_modules/jszip/.travis.yml +0 -17
- package/node_modules/jszip/CHANGES.md +0 -204
- package/node_modules/jszip/LICENSE.markdown +0 -651
- package/node_modules/jszip/README.markdown +0 -33
- package/node_modules/jszip/deps.js +0 -37
- package/node_modules/jszip/dist/jszip.js +0 -11577
- package/node_modules/jszip/dist/jszip.min.js +0 -13
- package/node_modules/jszip/graph.svg +0 -601
- package/node_modules/jszip/index.d.ts +0 -330
- package/node_modules/jszip/lib/base64.js +0 -106
- package/node_modules/jszip/lib/compressedObject.js +0 -74
- package/node_modules/jszip/lib/compressions.js +0 -14
- package/node_modules/jszip/lib/crc32.js +0 -77
- package/node_modules/jszip/lib/defaults.js +0 -11
- package/node_modules/jszip/lib/external.js +0 -18
- package/node_modules/jszip/lib/flate.js +0 -85
- package/node_modules/jszip/lib/generate/ZipFileWorker.js +0 -539
- package/node_modules/jszip/lib/generate/index.js +0 -57
- package/node_modules/jszip/lib/index.js +0 -55
- package/node_modules/jszip/lib/license_header.js +0 -11
- package/node_modules/jszip/lib/load.js +0 -88
- package/node_modules/jszip/lib/nodejs/NodejsStreamInputAdapter.js +0 -74
- package/node_modules/jszip/lib/nodejs/NodejsStreamOutputAdapter.js +0 -42
- package/node_modules/jszip/lib/nodejsUtils.js +0 -57
- package/node_modules/jszip/lib/object.js +0 -384
- package/node_modules/jszip/lib/readable-stream-browser.js +0 -10
- package/node_modules/jszip/lib/reader/ArrayReader.js +0 -57
- package/node_modules/jszip/lib/reader/DataReader.js +0 -116
- package/node_modules/jszip/lib/reader/NodeBufferReader.js +0 -19
- package/node_modules/jszip/lib/reader/StringReader.js +0 -38
- package/node_modules/jszip/lib/reader/Uint8ArrayReader.js +0 -22
- package/node_modules/jszip/lib/reader/readerFor.js +0 -28
- package/node_modules/jszip/lib/signature.js +0 -7
- package/node_modules/jszip/lib/stream/ConvertWorker.js +0 -26
- package/node_modules/jszip/lib/stream/Crc32Probe.js +0 -24
- package/node_modules/jszip/lib/stream/DataLengthProbe.js +0 -29
- package/node_modules/jszip/lib/stream/DataWorker.js +0 -116
- package/node_modules/jszip/lib/stream/GenericWorker.js +0 -263
- package/node_modules/jszip/lib/stream/StreamHelper.js +0 -214
- package/node_modules/jszip/lib/support.js +0 -38
- package/node_modules/jszip/lib/utf8.js +0 -275
- package/node_modules/jszip/lib/utils.js +0 -501
- package/node_modules/jszip/lib/zipEntries.js +0 -261
- package/node_modules/jszip/lib/zipEntry.js +0 -293
- package/node_modules/jszip/lib/zipObject.js +0 -133
- package/node_modules/jszip/package.json +0 -67
- package/node_modules/jszip/sponsors.md +0 -21
- package/node_modules/jszip/tsconfig.json +0 -101
- package/node_modules/jszip/vendor/FileSaver.js +0 -247
- package/node_modules/lie/README.md +0 -62
- package/node_modules/lie/dist/lie.js +0 -350
- package/node_modules/lie/dist/lie.min.js +0 -1
- package/node_modules/lie/dist/lie.polyfill.js +0 -358
- package/node_modules/lie/dist/lie.polyfill.min.js +0 -1
- package/node_modules/lie/lib/browser.js +0 -273
- package/node_modules/lie/lib/index.js +0 -298
- package/node_modules/lie/license.md +0 -7
- package/node_modules/lie/lie.d.ts +0 -244
- package/node_modules/lie/package.json +0 -69
- package/node_modules/lie/polyfill.js +0 -4
- package/node_modules/pako/CHANGELOG.md +0 -164
- package/node_modules/pako/LICENSE +0 -21
- package/node_modules/pako/README.md +0 -191
- package/node_modules/pako/dist/pako.js +0 -6818
- package/node_modules/pako/dist/pako.min.js +0 -1
- package/node_modules/pako/dist/pako_deflate.js +0 -3997
- package/node_modules/pako/dist/pako_deflate.min.js +0 -1
- package/node_modules/pako/dist/pako_inflate.js +0 -3300
- package/node_modules/pako/dist/pako_inflate.min.js +0 -1
- package/node_modules/pako/index.js +0 -14
- package/node_modules/pako/lib/deflate.js +0 -400
- package/node_modules/pako/lib/inflate.js +0 -423
- package/node_modules/pako/lib/utils/common.js +0 -105
- package/node_modules/pako/lib/utils/strings.js +0 -187
- package/node_modules/pako/lib/zlib/README +0 -59
- package/node_modules/pako/lib/zlib/adler32.js +0 -51
- package/node_modules/pako/lib/zlib/constants.js +0 -68
- package/node_modules/pako/lib/zlib/crc32.js +0 -59
- package/node_modules/pako/lib/zlib/deflate.js +0 -1874
- package/node_modules/pako/lib/zlib/gzheader.js +0 -58
- package/node_modules/pako/lib/zlib/inffast.js +0 -345
- package/node_modules/pako/lib/zlib/inflate.js +0 -1556
- package/node_modules/pako/lib/zlib/inftrees.js +0 -343
- package/node_modules/pako/lib/zlib/messages.js +0 -32
- package/node_modules/pako/lib/zlib/trees.js +0 -1222
- package/node_modules/pako/lib/zlib/zstream.js +0 -47
- package/node_modules/pako/package.json +0 -44
- package/node_modules/parse-ms/index.d.ts +0 -30
- package/node_modules/parse-ms/index.js +0 -45
- package/node_modules/parse-ms/license +0 -9
- package/node_modules/parse-ms/package.json +0 -47
- package/node_modules/parse-ms/readme.md +0 -46
- package/node_modules/pretty-ms/index.d.ts +0 -157
- package/node_modules/pretty-ms/index.js +0 -149
- package/node_modules/pretty-ms/license +0 -9
- package/node_modules/pretty-ms/package.json +0 -55
- package/node_modules/pretty-ms/readme.md +0 -179
- package/node_modules/process-nextick-args/index.js +0 -45
- package/node_modules/process-nextick-args/license.md +0 -19
- package/node_modules/process-nextick-args/package.json +0 -25
- package/node_modules/process-nextick-args/readme.md +0 -18
- package/node_modules/readable-stream/.travis.yml +0 -34
- package/node_modules/readable-stream/CONTRIBUTING.md +0 -38
- package/node_modules/readable-stream/GOVERNANCE.md +0 -136
- package/node_modules/readable-stream/LICENSE +0 -47
- package/node_modules/readable-stream/README.md +0 -58
- package/node_modules/readable-stream/doc/wg-meetings/2015-01-30.md +0 -60
- package/node_modules/readable-stream/duplex-browser.js +0 -1
- package/node_modules/readable-stream/duplex.js +0 -1
- package/node_modules/readable-stream/lib/_stream_duplex.js +0 -131
- package/node_modules/readable-stream/lib/_stream_passthrough.js +0 -47
- package/node_modules/readable-stream/lib/_stream_readable.js +0 -1019
- package/node_modules/readable-stream/lib/_stream_transform.js +0 -214
- package/node_modules/readable-stream/lib/_stream_writable.js +0 -685
- package/node_modules/readable-stream/lib/internal/streams/BufferList.js +0 -78
- package/node_modules/readable-stream/lib/internal/streams/destroy.js +0 -84
- package/node_modules/readable-stream/lib/internal/streams/stream-browser.js +0 -1
- package/node_modules/readable-stream/lib/internal/streams/stream.js +0 -1
- package/node_modules/readable-stream/package.json +0 -52
- package/node_modules/readable-stream/passthrough.js +0 -1
- package/node_modules/readable-stream/readable-browser.js +0 -7
- package/node_modules/readable-stream/readable.js +0 -19
- package/node_modules/readable-stream/transform.js +0 -1
- package/node_modules/readable-stream/writable-browser.js +0 -1
- package/node_modules/readable-stream/writable.js +0 -8
- package/node_modules/safe-buffer/LICENSE +0 -21
- package/node_modules/safe-buffer/README.md +0 -584
- package/node_modules/safe-buffer/index.d.ts +0 -187
- package/node_modules/safe-buffer/index.js +0 -62
- package/node_modules/safe-buffer/package.json +0 -37
- package/node_modules/setimmediate/package.json +0 -30
- package/node_modules/setimmediate/setImmediate.js +0 -186
- package/node_modules/string_decoder/.travis.yml +0 -50
- package/node_modules/string_decoder/LICENSE +0 -48
- package/node_modules/string_decoder/README.md +0 -47
- package/node_modules/string_decoder/lib/string_decoder.js +0 -296
- package/node_modules/string_decoder/package.json +0 -31
- package/node_modules/util-deprecate/History.md +0 -16
- package/node_modules/util-deprecate/LICENSE +0 -24
- package/node_modules/util-deprecate/README.md +0 -53
- package/node_modules/util-deprecate/browser.js +0 -67
- package/node_modules/util-deprecate/node.js +0 -6
- package/node_modules/util-deprecate/package.json +0 -27
|
@@ -1,17 +1,15 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import { callGatewayFromCli } from "openclaw/plugin-sdk/gateway-runtime";
|
|
6
|
-
import { clampTimerTimeoutMs, parseStrictNonNegativeInteger, parseStrictPositiveInteger } from "openclaw/plugin-sdk/number-runtime";
|
|
1
|
+
import { a as buildGoogleMeetCalendarDayWindow, i as resolveGoogleMeetGatewayOperationTimeoutMs, o as findGoogleMeetCalendarEvent, s as listGoogleMeetCalendarEvents } from "./config-DeJhjNWQ.mjs";
|
|
2
|
+
import { _ as fetchLatestGoogleMeetConferenceRecord, d as buildGoogleMeetPreflightReport, f as createGoogleMeetSpace, g as fetchGoogleMeetSpace, h as fetchGoogleMeetAttendance, m as fetchGoogleMeetArtifacts, p as endGoogleMeetActiveConference } from "./chrome-create-CFNlY-qH.mjs";
|
|
3
|
+
import { buildGoogleMeetAuthUrl, createGoogleMeetOAuthState, createGoogleMeetPkce, exchangeGoogleMeetAuthCode, resolveGoogleMeetAccessToken, waitForGoogleMeetAuthCode } from "./oauth-CtVbvmvR.mjs";
|
|
4
|
+
import { hasCreateSpaceConfigInput, resolveCreateSpaceConfig } from "./create-BVsTTJt1.mjs";
|
|
7
5
|
import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
|
|
6
|
+
import { callGatewayFromCli } from "openclaw/plugin-sdk/gateway-runtime";
|
|
7
|
+
import { clampTimerTimeoutMs, parseStrictPositiveInteger } from "openclaw/plugin-sdk/number-runtime";
|
|
8
8
|
import path from "node:path";
|
|
9
9
|
import { mkdir, writeFile } from "node:fs/promises";
|
|
10
|
-
import JSZip from "jszip";
|
|
11
10
|
import { createInterface } from "node:readline/promises";
|
|
12
11
|
import { format } from "node:util";
|
|
13
|
-
|
|
14
|
-
//#region extensions/google-meet/src/cli-shared.ts
|
|
12
|
+
//#region extensions/google-meet/src/cli.ts
|
|
15
13
|
const testing = {
|
|
16
14
|
parsePositiveNumber,
|
|
17
15
|
resolveGoogleMeetGatewayOperationTimeoutMs,
|
|
@@ -105,7 +103,11 @@ async function callGoogleMeetGateway(params) {
|
|
|
105
103
|
}
|
|
106
104
|
function formatDuration(value) {
|
|
107
105
|
if (value === void 0) return "n/a";
|
|
108
|
-
|
|
106
|
+
const totalSeconds = Math.round(value / 1e3);
|
|
107
|
+
const hours = Math.floor(totalSeconds / 3600);
|
|
108
|
+
const minutes = Math.floor(totalSeconds % 3600 / 60);
|
|
109
|
+
const seconds = totalSeconds % 60;
|
|
110
|
+
return hours > 0 ? `${hours}h ${minutes.toString().padStart(2, "0")}m` : `${minutes}m ${seconds.toString().padStart(2, "0")}s`;
|
|
109
111
|
}
|
|
110
112
|
function writeDoctorStatus(status) {
|
|
111
113
|
if (!status.found) {
|
|
@@ -143,10 +145,10 @@ function writeDoctorStatus(status) {
|
|
|
143
145
|
writeStdoutLine("captioning: %s", formatBoolean(health?.captioning));
|
|
144
146
|
writeStdoutLine("transcript lines: %s", health?.transcriptLines ?? 0);
|
|
145
147
|
writeStdoutLine("last caption: %s", formatOptional(health?.lastCaptionAt));
|
|
146
|
-
writeStdoutLine("manual action: %s", formatBoolean(
|
|
147
|
-
if (health?.
|
|
148
|
-
writeStdoutLine("manual reason: %s", formatOptional(health.
|
|
149
|
-
writeStdoutLine("manual message: %s", formatOptional(health.
|
|
148
|
+
writeStdoutLine("manual action: %s", formatBoolean(health?.manualActionRequired));
|
|
149
|
+
if (health?.manualActionRequired) {
|
|
150
|
+
writeStdoutLine("manual reason: %s", formatOptional(health.manualActionReason));
|
|
151
|
+
writeStdoutLine("manual message: %s", formatOptional(health.manualActionMessage));
|
|
150
152
|
}
|
|
151
153
|
writeStdoutLine("speech ready: %s", formatBoolean(health?.speechReady));
|
|
152
154
|
if (health?.speechReady === false) {
|
|
@@ -175,6 +177,112 @@ function writeDoctorStatus(status) {
|
|
|
175
177
|
}
|
|
176
178
|
}
|
|
177
179
|
}
|
|
180
|
+
function sanitizeOAuthErrorMessage(error) {
|
|
181
|
+
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]");
|
|
182
|
+
}
|
|
183
|
+
async function buildOAuthDoctorReport(config, options) {
|
|
184
|
+
const clientId = options.clientId?.trim() || config.oauth.clientId;
|
|
185
|
+
const clientSecret = options.clientSecret?.trim() || config.oauth.clientSecret;
|
|
186
|
+
const refreshToken = options.refreshToken?.trim() || config.oauth.refreshToken;
|
|
187
|
+
const accessToken = options.accessToken?.trim() || config.oauth.accessToken;
|
|
188
|
+
const expiresAt = parseOptionalNumber(options.expiresAt) ?? config.oauth.expiresAt;
|
|
189
|
+
const checks = [];
|
|
190
|
+
const hasRefreshConfig = Boolean(clientId && refreshToken);
|
|
191
|
+
if (!hasRefreshConfig && !Boolean(accessToken)) {
|
|
192
|
+
checks.push({
|
|
193
|
+
id: "oauth-config",
|
|
194
|
+
ok: false,
|
|
195
|
+
message: "Missing Google Meet OAuth credentials. Configure oauth.clientId and oauth.refreshToken, or pass --client-id and --refresh-token."
|
|
196
|
+
});
|
|
197
|
+
return {
|
|
198
|
+
ok: false,
|
|
199
|
+
configured: false,
|
|
200
|
+
checks
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
checks.push({
|
|
204
|
+
id: "oauth-config",
|
|
205
|
+
ok: true,
|
|
206
|
+
message: hasRefreshConfig ? "Google Meet OAuth refresh credentials are configured" : "Google Meet cached access token is configured"
|
|
207
|
+
});
|
|
208
|
+
let token;
|
|
209
|
+
try {
|
|
210
|
+
token = await resolveGoogleMeetAccessToken({
|
|
211
|
+
clientId,
|
|
212
|
+
clientSecret,
|
|
213
|
+
refreshToken,
|
|
214
|
+
accessToken,
|
|
215
|
+
expiresAt
|
|
216
|
+
});
|
|
217
|
+
checks.push({
|
|
218
|
+
id: "oauth-token",
|
|
219
|
+
ok: true,
|
|
220
|
+
message: token.refreshed ? "Refresh token minted an access token" : "Cached access token is still valid"
|
|
221
|
+
});
|
|
222
|
+
} catch (error) {
|
|
223
|
+
checks.push({
|
|
224
|
+
id: "oauth-token",
|
|
225
|
+
ok: false,
|
|
226
|
+
message: sanitizeOAuthErrorMessage(error)
|
|
227
|
+
});
|
|
228
|
+
return {
|
|
229
|
+
ok: false,
|
|
230
|
+
configured: true,
|
|
231
|
+
checks
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
const report = {
|
|
235
|
+
ok: true,
|
|
236
|
+
configured: true,
|
|
237
|
+
tokenSource: token.refreshed ? "refresh-token" : "cached-access-token",
|
|
238
|
+
expiresAt: token.expiresAt,
|
|
239
|
+
checks
|
|
240
|
+
};
|
|
241
|
+
const meeting = options.meeting?.trim();
|
|
242
|
+
if (meeting) try {
|
|
243
|
+
const space = await fetchGoogleMeetSpace({
|
|
244
|
+
accessToken: token.accessToken,
|
|
245
|
+
meeting
|
|
246
|
+
});
|
|
247
|
+
checks.push({
|
|
248
|
+
id: "meet-spaces-get",
|
|
249
|
+
ok: true,
|
|
250
|
+
message: `Resolved ${space.name}`
|
|
251
|
+
});
|
|
252
|
+
report.meetingUri = space.meetingUri;
|
|
253
|
+
} catch (error) {
|
|
254
|
+
checks.push({
|
|
255
|
+
id: "meet-spaces-get",
|
|
256
|
+
ok: false,
|
|
257
|
+
message: sanitizeOAuthErrorMessage(error)
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
if (options.createSpace) try {
|
|
261
|
+
const created = await createGoogleMeetSpace({ accessToken: token.accessToken });
|
|
262
|
+
checks.push({
|
|
263
|
+
id: "meet-spaces-create",
|
|
264
|
+
ok: true,
|
|
265
|
+
message: `Created ${created.space.name}`
|
|
266
|
+
});
|
|
267
|
+
report.createdSpace = created.space.name;
|
|
268
|
+
report.meetingUri = created.meetingUri;
|
|
269
|
+
} catch (error) {
|
|
270
|
+
checks.push({
|
|
271
|
+
id: "meet-spaces-create",
|
|
272
|
+
ok: false,
|
|
273
|
+
message: sanitizeOAuthErrorMessage(error)
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
report.ok = checks.every((check) => check.ok);
|
|
277
|
+
return report;
|
|
278
|
+
}
|
|
279
|
+
function writeOAuthDoctorReport(report) {
|
|
280
|
+
writeStdoutLine("Google Meet OAuth: %s", report.ok ? "OK" : "needs attention");
|
|
281
|
+
writeStdoutLine("configured: %s", report.configured ? "yes" : "no");
|
|
282
|
+
if (report.tokenSource) writeStdoutLine("token source: %s", report.tokenSource);
|
|
283
|
+
if (report.meetingUri) writeStdoutLine("meeting uri: %s", report.meetingUri);
|
|
284
|
+
for (const check of report.checks) writeStdoutLine("[%s] %s: %s", check.ok ? "ok" : "fail", check.id, check.message);
|
|
285
|
+
}
|
|
178
286
|
function writeRecoverCurrentTabResult(result) {
|
|
179
287
|
writeStdoutLine("Google Meet current tab: %s", result.found ? "found" : "not found");
|
|
180
288
|
writeStdoutLine("transport: %s", result.transport);
|
|
@@ -208,15 +316,88 @@ function writeRecoverCurrentTabResult(result) {
|
|
|
208
316
|
}
|
|
209
317
|
});
|
|
210
318
|
}
|
|
211
|
-
function
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
319
|
+
function resolveMeetingInput(config, value) {
|
|
320
|
+
const meeting = value?.trim() || config.defaults.meeting;
|
|
321
|
+
if (!meeting) throw new Error("Meeting input is required. Pass a URL/meeting code or configure defaults.meeting.");
|
|
322
|
+
return meeting;
|
|
323
|
+
}
|
|
324
|
+
function resolveOAuthTokenOptions(config, options) {
|
|
325
|
+
return {
|
|
326
|
+
clientId: options.clientId?.trim() || config.oauth.clientId,
|
|
327
|
+
clientSecret: options.clientSecret?.trim() || config.oauth.clientSecret,
|
|
328
|
+
refreshToken: options.refreshToken?.trim() || config.oauth.refreshToken,
|
|
329
|
+
accessToken: options.accessToken?.trim() || config.oauth.accessToken,
|
|
330
|
+
expiresAt: parseOptionalNumber(options.expiresAt) ?? config.oauth.expiresAt
|
|
331
|
+
};
|
|
332
|
+
}
|
|
333
|
+
function resolveTokenOptions(config, options) {
|
|
334
|
+
return {
|
|
335
|
+
meeting: resolveMeetingInput(config, options.meeting),
|
|
336
|
+
...resolveOAuthTokenOptions(config, options)
|
|
337
|
+
};
|
|
338
|
+
}
|
|
339
|
+
function hasCalendarLookupOptions(options) {
|
|
340
|
+
return Boolean(options.today || options.event?.trim());
|
|
341
|
+
}
|
|
342
|
+
async function resolveCalendarMeetingInput(params) {
|
|
343
|
+
if (!hasCalendarLookupOptions(params.options)) return {};
|
|
344
|
+
const window = params.options.today ? buildGoogleMeetCalendarDayWindow() : {};
|
|
345
|
+
const calendarEvent = await findGoogleMeetCalendarEvent({
|
|
346
|
+
accessToken: params.accessToken,
|
|
347
|
+
calendarId: params.options.calendar,
|
|
348
|
+
eventQuery: params.options.event,
|
|
349
|
+
...window
|
|
350
|
+
});
|
|
351
|
+
return {
|
|
352
|
+
meeting: calendarEvent.meetingUri,
|
|
353
|
+
calendarEvent
|
|
354
|
+
};
|
|
355
|
+
}
|
|
356
|
+
async function resolveMeetingForToken(params) {
|
|
357
|
+
const calendarMeeting = await resolveCalendarMeetingInput({
|
|
358
|
+
accessToken: params.accessToken,
|
|
359
|
+
options: params.options
|
|
360
|
+
});
|
|
361
|
+
const meeting = calendarMeeting.meeting ?? params.configuredMeeting ?? params.config.defaults.meeting;
|
|
362
|
+
if (!meeting) throw new Error("Meeting input is required. Pass --meeting, --today, --event, or configure defaults.meeting.");
|
|
363
|
+
return calendarMeeting.calendarEvent ? {
|
|
364
|
+
meeting,
|
|
365
|
+
calendarEvent: calendarMeeting.calendarEvent
|
|
366
|
+
} : { meeting };
|
|
367
|
+
}
|
|
368
|
+
function resolveCreateTokenOptions(config, options) {
|
|
369
|
+
return {
|
|
370
|
+
clientId: options.clientId?.trim() || config.oauth.clientId,
|
|
371
|
+
clientSecret: options.clientSecret?.trim() || config.oauth.clientSecret,
|
|
372
|
+
refreshToken: options.refreshToken?.trim() || config.oauth.refreshToken,
|
|
373
|
+
accessToken: options.accessToken?.trim() || config.oauth.accessToken,
|
|
374
|
+
expiresAt: parseOptionalNumber(options.expiresAt) ?? config.oauth.expiresAt
|
|
375
|
+
};
|
|
376
|
+
}
|
|
377
|
+
function resolveArtifactTokenOptions(config, options) {
|
|
378
|
+
const meeting = options.meeting?.trim() || config.defaults.meeting;
|
|
379
|
+
const conferenceRecord = options.conferenceRecord?.trim();
|
|
380
|
+
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.");
|
|
381
|
+
return {
|
|
382
|
+
meeting,
|
|
383
|
+
conferenceRecord,
|
|
384
|
+
clientId: options.clientId?.trim() || config.oauth.clientId,
|
|
385
|
+
clientSecret: options.clientSecret?.trim() || config.oauth.clientSecret,
|
|
386
|
+
refreshToken: options.refreshToken?.trim() || config.oauth.refreshToken,
|
|
387
|
+
accessToken: options.accessToken?.trim() || config.oauth.accessToken,
|
|
388
|
+
expiresAt: parseOptionalNumber(options.expiresAt) ?? config.oauth.expiresAt,
|
|
389
|
+
pageSize: parsePositiveIntegerOption(options.pageSize, "page-size"),
|
|
390
|
+
includeTranscriptEntries: options.transcriptEntries !== false,
|
|
391
|
+
allConferenceRecords: Boolean(options.allConferenceRecords),
|
|
392
|
+
includeDocumentBodies: Boolean(options.includeDocBodies),
|
|
393
|
+
mergeDuplicateParticipants: options.mergeDuplicates !== false,
|
|
394
|
+
lateAfterMinutes: parseOptionalNumber(options.lateAfterMinutes),
|
|
395
|
+
earlyBeforeMinutes: parseOptionalNumber(options.earlyBeforeMinutes)
|
|
396
|
+
};
|
|
397
|
+
}
|
|
398
|
+
function hasCreateOAuth(config, options) {
|
|
399
|
+
return Boolean(options.accessToken?.trim() || options.refreshToken?.trim() || config.oauth.accessToken || config.oauth.refreshToken);
|
|
217
400
|
}
|
|
218
|
-
//#endregion
|
|
219
|
-
//#region extensions/google-meet/src/cli-export.ts
|
|
220
401
|
function writeArtifactsSummary(result) {
|
|
221
402
|
if (result.input) writeStdoutLine("input: %s", result.input);
|
|
222
403
|
if (result.space) writeStdoutLine("space: %s", result.space.name);
|
|
@@ -392,12 +573,9 @@ function renderAttendanceMarkdown(result) {
|
|
|
392
573
|
}
|
|
393
574
|
return `${lines.join("\n")}\n`;
|
|
394
575
|
}
|
|
395
|
-
function neutralizeSpreadsheetFormulaCell(text) {
|
|
396
|
-
return /^[ \t\r\n]*[=+\-@\uFF0B\uFF0D\uFF1D\uFF20]/u.test(text) || /^[\t\r\n]/.test(text) ? `'${text}` : text;
|
|
397
|
-
}
|
|
398
576
|
function csvCell(value) {
|
|
399
|
-
const
|
|
400
|
-
return /[",\
|
|
577
|
+
const text = value === void 0 || value === null ? "" : typeof value === "string" || typeof value === "number" || typeof value === "boolean" ? String(value) : JSON.stringify(value);
|
|
578
|
+
return /[",\n]/.test(text) ? `"${text.replaceAll("\"", "\"\"")}"` : text;
|
|
401
579
|
}
|
|
402
580
|
function renderAttendanceCsv(result) {
|
|
403
581
|
const rows = [[
|
|
@@ -543,6 +721,81 @@ function googleMeetExportFileNames() {
|
|
|
543
721
|
function defaultExportDirectory() {
|
|
544
722
|
return `google-meet-export-${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")}`;
|
|
545
723
|
}
|
|
724
|
+
const CRC32_TABLE = new Uint32Array(Array.from({ length: 256 }, (_, index) => {
|
|
725
|
+
let value = index;
|
|
726
|
+
for (let bit = 0; bit < 8; bit += 1) value = value & 1 ? 3988292384 ^ value >>> 1 : value >>> 1;
|
|
727
|
+
return value >>> 0;
|
|
728
|
+
}));
|
|
729
|
+
function crc32(buffer) {
|
|
730
|
+
let value = 4294967295;
|
|
731
|
+
for (const byte of buffer) value = CRC32_TABLE[(value ^ byte) & 255] ^ value >>> 8;
|
|
732
|
+
return (value ^ 4294967295) >>> 0;
|
|
733
|
+
}
|
|
734
|
+
function dosDateTime(date = /* @__PURE__ */ new Date()) {
|
|
735
|
+
return {
|
|
736
|
+
time: date.getHours() << 11 | date.getMinutes() << 5 | Math.floor(date.getSeconds() / 2),
|
|
737
|
+
date: date.getFullYear() - 1980 << 9 | date.getMonth() + 1 << 5 | date.getDate()
|
|
738
|
+
};
|
|
739
|
+
}
|
|
740
|
+
function buildZipArchive(files) {
|
|
741
|
+
const localParts = [];
|
|
742
|
+
const centralParts = [];
|
|
743
|
+
let offset = 0;
|
|
744
|
+
const stamp = dosDateTime();
|
|
745
|
+
for (const file of files) {
|
|
746
|
+
const name = Buffer.from(file.name, "utf8");
|
|
747
|
+
const content = Buffer.from(file.content, "utf8");
|
|
748
|
+
const checksum = crc32(content);
|
|
749
|
+
const local = Buffer.alloc(30);
|
|
750
|
+
local.writeUInt32LE(67324752, 0);
|
|
751
|
+
local.writeUInt16LE(20, 4);
|
|
752
|
+
local.writeUInt16LE(0, 6);
|
|
753
|
+
local.writeUInt16LE(0, 8);
|
|
754
|
+
local.writeUInt16LE(stamp.time, 10);
|
|
755
|
+
local.writeUInt16LE(stamp.date, 12);
|
|
756
|
+
local.writeUInt32LE(checksum, 14);
|
|
757
|
+
local.writeUInt32LE(content.length, 18);
|
|
758
|
+
local.writeUInt32LE(content.length, 22);
|
|
759
|
+
local.writeUInt16LE(name.length, 26);
|
|
760
|
+
local.writeUInt16LE(0, 28);
|
|
761
|
+
localParts.push(local, name, content);
|
|
762
|
+
const central = Buffer.alloc(46);
|
|
763
|
+
central.writeUInt32LE(33639248, 0);
|
|
764
|
+
central.writeUInt16LE(20, 4);
|
|
765
|
+
central.writeUInt16LE(20, 6);
|
|
766
|
+
central.writeUInt16LE(0, 8);
|
|
767
|
+
central.writeUInt16LE(0, 10);
|
|
768
|
+
central.writeUInt16LE(stamp.time, 12);
|
|
769
|
+
central.writeUInt16LE(stamp.date, 14);
|
|
770
|
+
central.writeUInt32LE(checksum, 16);
|
|
771
|
+
central.writeUInt32LE(content.length, 20);
|
|
772
|
+
central.writeUInt32LE(content.length, 24);
|
|
773
|
+
central.writeUInt16LE(name.length, 28);
|
|
774
|
+
central.writeUInt16LE(0, 30);
|
|
775
|
+
central.writeUInt16LE(0, 32);
|
|
776
|
+
central.writeUInt16LE(0, 34);
|
|
777
|
+
central.writeUInt16LE(0, 36);
|
|
778
|
+
central.writeUInt32LE(0, 38);
|
|
779
|
+
central.writeUInt32LE(offset, 42);
|
|
780
|
+
centralParts.push(central, name);
|
|
781
|
+
offset += local.length + name.length + content.length;
|
|
782
|
+
}
|
|
783
|
+
const centralDirectory = Buffer.concat(centralParts);
|
|
784
|
+
const end = Buffer.alloc(22);
|
|
785
|
+
end.writeUInt32LE(101010256, 0);
|
|
786
|
+
end.writeUInt16LE(0, 4);
|
|
787
|
+
end.writeUInt16LE(0, 6);
|
|
788
|
+
end.writeUInt16LE(files.length, 8);
|
|
789
|
+
end.writeUInt16LE(files.length, 10);
|
|
790
|
+
end.writeUInt32LE(centralDirectory.length, 12);
|
|
791
|
+
end.writeUInt32LE(offset, 16);
|
|
792
|
+
end.writeUInt16LE(0, 20);
|
|
793
|
+
return Buffer.concat([
|
|
794
|
+
...localParts,
|
|
795
|
+
centralDirectory,
|
|
796
|
+
end
|
|
797
|
+
]);
|
|
798
|
+
}
|
|
546
799
|
async function writeMeetExportBundle(params) {
|
|
547
800
|
const outputDir = params.outputDir?.trim() || defaultExportDirectory();
|
|
548
801
|
await mkdir(outputDir, { recursive: true });
|
|
@@ -588,319 +841,163 @@ async function writeMeetExportBundle(params) {
|
|
|
588
841
|
files: files.map((file) => path.join(outputDir, file.name))
|
|
589
842
|
};
|
|
590
843
|
if (zipFile) {
|
|
591
|
-
|
|
592
|
-
for (const file of files) zip.file(file.name, file.content);
|
|
593
|
-
await writeFile(zipFile, await zip.generateAsync({ type: "nodebuffer" }));
|
|
844
|
+
await writeFile(zipFile, buildZipArchive(files));
|
|
594
845
|
result.zipFile = zipFile;
|
|
595
846
|
}
|
|
596
847
|
return result;
|
|
597
848
|
}
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
const
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
const
|
|
605
|
-
|
|
606
|
-
const
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
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
|
|
849
|
+
function registerGoogleMeetCli(params) {
|
|
850
|
+
const callGateway = params.callGatewayFromCli ?? callGatewayFromCli;
|
|
851
|
+
const operationTimeoutMs = resolveGoogleMeetGatewayOperationTimeoutMs(params.config);
|
|
852
|
+
const root = params.program.command("googlemeet").description("Google Meet participant utilities").addHelpText("after", () => `\nDocs: https://docs.openclaw.ai/plugins/google-meet\n`);
|
|
853
|
+
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) => {
|
|
854
|
+
const clientId = options.clientId?.trim() || params.config.oauth.clientId;
|
|
855
|
+
const clientSecret = options.clientSecret?.trim() || params.config.oauth.clientSecret;
|
|
856
|
+
if (!clientId) throw new Error("Missing Google Meet OAuth client id. Configure oauth.clientId or pass --client-id.");
|
|
857
|
+
const { verifier, challenge } = createGoogleMeetPkce();
|
|
858
|
+
const state = createGoogleMeetOAuthState();
|
|
859
|
+
const authUrl = buildGoogleMeetAuthUrl({
|
|
860
|
+
clientId,
|
|
861
|
+
challenge,
|
|
862
|
+
state
|
|
620
863
|
});
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
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);
|
|
634
|
-
writeStdoutLine("token source: %s", token.refreshed ? "refresh-token" : "cached-access-token");
|
|
635
|
-
});
|
|
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,
|
|
642
|
-
accessToken: token.accessToken,
|
|
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
|
|
654
|
-
});
|
|
655
|
-
if (options.json) {
|
|
656
|
-
await writeCliOutput(options, JSON.stringify({
|
|
657
|
-
...result,
|
|
658
|
-
tokenSource: token.refreshed ? "refresh-token" : "cached-access-token"
|
|
659
|
-
}, null, 2));
|
|
660
|
-
return;
|
|
661
|
-
}
|
|
662
|
-
if (options.format === "markdown") {
|
|
663
|
-
await writeCliOutput(options, renderAttendanceMarkdown(result));
|
|
664
|
-
return;
|
|
665
|
-
}
|
|
666
|
-
if (options.format === "csv") {
|
|
667
|
-
await writeCliOutput(options, renderAttendanceCsv(result));
|
|
668
|
-
return;
|
|
669
|
-
}
|
|
670
|
-
if (options.format && options.format !== "summary") throw new Error("Unsupported format. Expected summary, markdown, or csv.");
|
|
671
|
-
writeAttendanceSummary(result);
|
|
672
|
-
writeStdoutLine("token source: %s", token.refreshed ? "refresh-token" : "cached-access-token");
|
|
673
|
-
});
|
|
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);
|
|
676
|
-
const token = await resolveGoogleMeetAccessToken(resolved);
|
|
677
|
-
const meetingResult = resolved.conferenceRecord ? { meeting: resolved.meeting } : await resolveMeetingForToken({
|
|
678
|
-
config: params.config,
|
|
679
|
-
options,
|
|
680
|
-
accessToken: token.accessToken,
|
|
681
|
-
configuredMeeting: resolved.meeting
|
|
682
|
-
});
|
|
683
|
-
const artifacts = await fetchGoogleMeetArtifacts({
|
|
684
|
-
accessToken: token.accessToken,
|
|
685
|
-
meeting: meetingResult.meeting,
|
|
686
|
-
conferenceRecord: resolved.conferenceRecord,
|
|
687
|
-
pageSize: resolved.pageSize,
|
|
688
|
-
includeTranscriptEntries: resolved.includeTranscriptEntries,
|
|
689
|
-
allConferenceRecords: resolved.allConferenceRecords,
|
|
690
|
-
includeDocumentBodies: resolved.includeDocumentBodies
|
|
691
|
-
});
|
|
692
|
-
const attendance = await fetchGoogleMeetAttendance({
|
|
693
|
-
accessToken: token.accessToken,
|
|
694
|
-
meeting: meetingResult.meeting,
|
|
695
|
-
conferenceRecord: resolved.conferenceRecord,
|
|
696
|
-
pageSize: resolved.pageSize,
|
|
697
|
-
allConferenceRecords: resolved.allConferenceRecords,
|
|
698
|
-
mergeDuplicateParticipants: resolved.mergeDuplicateParticipants,
|
|
699
|
-
lateAfterMinutes: resolved.lateAfterMinutes,
|
|
700
|
-
earlyBeforeMinutes: resolved.earlyBeforeMinutes
|
|
701
|
-
});
|
|
702
|
-
const resolvedMeeting = meetingResult.meeting ?? resolved.meeting;
|
|
703
|
-
const request = {
|
|
704
|
-
...resolvedMeeting ? { meeting: resolvedMeeting } : {},
|
|
705
|
-
...resolved.conferenceRecord ? { conferenceRecord: resolved.conferenceRecord } : {},
|
|
706
|
-
...meetingResult.calendarEvent?.event.id ? { calendarEventId: meetingResult.calendarEvent.event.id } : {},
|
|
707
|
-
...meetingResult.calendarEvent?.event.summary ? { calendarEventSummary: meetingResult.calendarEvent.event.summary } : {},
|
|
708
|
-
...options.calendar ? { calendarId: options.calendar } : {},
|
|
709
|
-
...resolved.pageSize !== void 0 ? { pageSize: resolved.pageSize } : {},
|
|
710
|
-
includeTranscriptEntries: resolved.includeTranscriptEntries,
|
|
711
|
-
includeDocumentBodies: resolved.includeDocumentBodies,
|
|
712
|
-
allConferenceRecords: resolved.allConferenceRecords,
|
|
713
|
-
mergeDuplicateParticipants: resolved.mergeDuplicateParticipants,
|
|
714
|
-
...resolved.lateAfterMinutes !== void 0 ? { lateAfterMinutes: resolved.lateAfterMinutes } : {},
|
|
715
|
-
...resolved.earlyBeforeMinutes !== void 0 ? { earlyBeforeMinutes: resolved.earlyBeforeMinutes } : {}
|
|
716
|
-
};
|
|
717
|
-
if (options.dryRun) {
|
|
718
|
-
writeStdoutJson({
|
|
719
|
-
dryRun: true,
|
|
720
|
-
manifest: buildGoogleMeetExportManifest({
|
|
721
|
-
artifacts,
|
|
722
|
-
attendance,
|
|
723
|
-
files: googleMeetExportFileNames(),
|
|
724
|
-
request,
|
|
725
|
-
tokenSource: token.refreshed ? "refresh-token" : "cached-access-token",
|
|
726
|
-
...meetingResult.calendarEvent ? { calendarEvent: meetingResult.calendarEvent } : {}
|
|
727
|
-
}),
|
|
728
|
-
...meetingResult.calendarEvent ? { calendarEvent: meetingResult.calendarEvent } : {},
|
|
729
|
-
tokenSource: token.refreshed ? "refresh-token" : "cached-access-token"
|
|
730
|
-
});
|
|
731
|
-
return;
|
|
732
|
-
}
|
|
733
|
-
const bundle = await writeMeetExportBundle({
|
|
734
|
-
outputDir: options.output,
|
|
735
|
-
artifacts,
|
|
736
|
-
attendance,
|
|
737
|
-
zip: Boolean(options.zip),
|
|
738
|
-
request,
|
|
739
|
-
tokenSource: token.refreshed ? "refresh-token" : "cached-access-token",
|
|
740
|
-
...meetingResult.calendarEvent ? { calendarEvent: meetingResult.calendarEvent } : {}
|
|
741
|
-
});
|
|
742
|
-
const payload = {
|
|
743
|
-
...bundle,
|
|
744
|
-
...meetingResult.calendarEvent ? { calendarEvent: meetingResult.calendarEvent } : {},
|
|
745
|
-
tokenSource: token.refreshed ? "refresh-token" : "cached-access-token"
|
|
746
|
-
};
|
|
747
|
-
if (options.json) {
|
|
748
|
-
writeStdoutJson(payload);
|
|
749
|
-
return;
|
|
750
|
-
}
|
|
751
|
-
writeStdoutLine("export: %s", bundle.outputDir);
|
|
752
|
-
for (const file of bundle.files) writeStdoutLine("- %s", file);
|
|
753
|
-
if (bundle.zipFile) writeStdoutLine("zip: %s", bundle.zipFile);
|
|
754
|
-
});
|
|
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."
|
|
864
|
+
const code = await waitForGoogleMeetAuthCode({
|
|
865
|
+
state,
|
|
866
|
+
manual: Boolean(options.manual),
|
|
867
|
+
timeoutMs: resolveGoogleMeetOAuthCallbackTimeoutMs(options.timeoutSec),
|
|
868
|
+
authUrl,
|
|
869
|
+
promptInput,
|
|
870
|
+
writeLine: (message) => writeStdoutLine("%s", message)
|
|
774
871
|
});
|
|
775
|
-
|
|
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"
|
|
785
|
-
});
|
|
786
|
-
let token;
|
|
787
|
-
try {
|
|
788
|
-
token = await resolveGoogleMeetAccessToken({
|
|
872
|
+
const tokens = await exchangeGoogleMeetAuthCode({
|
|
789
873
|
clientId,
|
|
790
874
|
clientSecret,
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
expiresAt
|
|
794
|
-
});
|
|
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)
|
|
875
|
+
code,
|
|
876
|
+
verifier
|
|
805
877
|
});
|
|
806
|
-
return
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
878
|
+
if (!tokens.refreshToken) throw new Error("Google OAuth did not return a refresh token. Re-run the flow with consent and offline access.");
|
|
879
|
+
const payload = {
|
|
880
|
+
oauth: {
|
|
881
|
+
clientId,
|
|
882
|
+
...clientSecret ? { clientSecret } : {},
|
|
883
|
+
refreshToken: tokens.refreshToken,
|
|
884
|
+
accessToken: tokens.accessToken,
|
|
885
|
+
expiresAt: tokens.expiresAt
|
|
886
|
+
},
|
|
887
|
+
scope: tokens.scope,
|
|
888
|
+
tokenType: tokens.tokenType
|
|
810
889
|
};
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
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;
|
|
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) => {
|
|
868
|
-
if (options.oauth) {
|
|
869
|
-
const report = await buildOAuthDoctorReport(params.config, options);
|
|
870
|
-
if (options.json) {
|
|
871
|
-
writeStdoutJson(report);
|
|
890
|
+
if (!options.json) writeStdoutLine("Paste this into plugins.entries.google-meet.config:");
|
|
891
|
+
writeStdoutJson(payload);
|
|
892
|
+
});
|
|
893
|
+
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) => {
|
|
894
|
+
if (options.join !== false) {
|
|
895
|
+
const delegated = await callGoogleMeetGateway({
|
|
896
|
+
callGateway,
|
|
897
|
+
method: "googlemeet.create",
|
|
898
|
+
payload: { ...options },
|
|
899
|
+
timeoutMs: operationTimeoutMs
|
|
900
|
+
});
|
|
901
|
+
if (delegated.ok) {
|
|
902
|
+
const payload = delegated.payload;
|
|
903
|
+
if (options.json) {
|
|
904
|
+
writeStdoutJson(payload);
|
|
905
|
+
return;
|
|
906
|
+
}
|
|
907
|
+
writeStdoutLine("meeting uri: %s", payload.meetingUri);
|
|
908
|
+
if (payload.space?.name) writeStdoutLine("space: %s", payload.space.name);
|
|
909
|
+
if (payload.space?.meetingCode) writeStdoutLine("meeting code: %s", payload.space.meetingCode);
|
|
910
|
+
if (payload.source) writeStdoutLine("source: %s", payload.source);
|
|
911
|
+
if (payload.browser?.nodeId) writeStdoutLine("node: %s", payload.browser.nodeId);
|
|
912
|
+
if (payload.tokenSource) writeStdoutLine("token source: %s", payload.tokenSource);
|
|
913
|
+
if (payload.joined && payload.join?.session?.id) writeStdoutLine("joined: %s", payload.join.session.id);
|
|
914
|
+
else writeStdoutLine("joined: no (run `openclaw googlemeet join %s`)", payload.meetingUri);
|
|
872
915
|
return;
|
|
873
916
|
}
|
|
874
|
-
writeOAuthDoctorReport(report);
|
|
875
|
-
return;
|
|
876
917
|
}
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
918
|
+
if (!hasCreateOAuth(params.config, options)) {
|
|
919
|
+
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.");
|
|
920
|
+
const rt = await params.ensureRuntime();
|
|
921
|
+
const result = await rt.createViaBrowser();
|
|
922
|
+
const join = options.join !== false ? await rt.join({
|
|
923
|
+
url: result.meetingUri,
|
|
924
|
+
transport: options.transport,
|
|
925
|
+
mode: options.mode,
|
|
926
|
+
message: options.message,
|
|
927
|
+
dialInNumber: options.dialInNumber,
|
|
928
|
+
pin: options.pin,
|
|
929
|
+
dtmfSequence: options.dtmfSequence
|
|
930
|
+
}) : void 0;
|
|
931
|
+
const payload = {
|
|
932
|
+
source: result.source,
|
|
933
|
+
meetingUri: result.meetingUri,
|
|
934
|
+
joined: Boolean(join),
|
|
935
|
+
...join ? { join } : {},
|
|
936
|
+
browser: {
|
|
937
|
+
nodeId: result.nodeId,
|
|
938
|
+
targetId: result.targetId,
|
|
939
|
+
browserUrl: result.browserUrl,
|
|
940
|
+
browserTitle: result.browserTitle
|
|
941
|
+
}
|
|
942
|
+
};
|
|
884
943
|
if (options.json) {
|
|
885
|
-
writeStdoutJson(
|
|
944
|
+
writeStdoutJson(payload);
|
|
886
945
|
return;
|
|
887
946
|
}
|
|
888
|
-
|
|
947
|
+
writeStdoutLine("meeting uri: %s", result.meetingUri);
|
|
948
|
+
writeStdoutLine("source: browser");
|
|
949
|
+
writeStdoutLine("node: %s", result.nodeId);
|
|
950
|
+
if (join) writeStdoutLine("joined: %s", join.session.id);
|
|
951
|
+
else writeStdoutLine("joined: no (run `openclaw googlemeet join %s`)", result.meetingUri);
|
|
889
952
|
return;
|
|
890
953
|
}
|
|
891
|
-
const
|
|
954
|
+
const token = await resolveGoogleMeetAccessToken(resolveCreateTokenOptions(params.config, options));
|
|
955
|
+
const result = await createGoogleMeetSpace({
|
|
956
|
+
accessToken: token.accessToken,
|
|
957
|
+
config: resolveCreateSpaceConfig(options)
|
|
958
|
+
});
|
|
959
|
+
const join = options.join !== false ? await (await params.ensureRuntime()).join({
|
|
960
|
+
url: result.meetingUri,
|
|
961
|
+
transport: options.transport,
|
|
962
|
+
mode: options.mode,
|
|
963
|
+
message: options.message,
|
|
964
|
+
dialInNumber: options.dialInNumber,
|
|
965
|
+
pin: options.pin,
|
|
966
|
+
dtmfSequence: options.dtmfSequence
|
|
967
|
+
}) : void 0;
|
|
892
968
|
if (options.json) {
|
|
893
|
-
writeStdoutJson(
|
|
969
|
+
writeStdoutJson({
|
|
970
|
+
...result,
|
|
971
|
+
tokenSource: token.refreshed ? "refresh-token" : "cached-access-token",
|
|
972
|
+
joined: Boolean(join),
|
|
973
|
+
...join ? { join } : {}
|
|
974
|
+
});
|
|
894
975
|
return;
|
|
895
976
|
}
|
|
896
|
-
|
|
977
|
+
writeStdoutLine("meeting uri: %s", result.meetingUri);
|
|
978
|
+
writeStdoutLine("space: %s", result.space.name);
|
|
979
|
+
if (result.space.meetingCode) writeStdoutLine("meeting code: %s", result.space.meetingCode);
|
|
980
|
+
writeStdoutLine("token source: %s", token.refreshed ? "refresh-token" : "cached-access-token");
|
|
981
|
+
if (join) writeStdoutLine("joined: %s", join.session.id);
|
|
982
|
+
else writeStdoutLine("joined: no (run `openclaw googlemeet join %s`)", result.meetingUri);
|
|
983
|
+
});
|
|
984
|
+
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) => {
|
|
985
|
+
const token = await resolveGoogleMeetAccessToken(resolveOAuthTokenOptions(params.config, options));
|
|
986
|
+
const result = await endGoogleMeetActiveConference({
|
|
987
|
+
accessToken: token.accessToken,
|
|
988
|
+
meeting: resolveMeetingInput(params.config, meeting ?? options.meeting)
|
|
989
|
+
});
|
|
990
|
+
if (options.json) {
|
|
991
|
+
writeStdoutJson({
|
|
992
|
+
...result,
|
|
993
|
+
tokenSource: token.refreshed ? "refresh-token" : "cached-access-token"
|
|
994
|
+
});
|
|
995
|
+
return;
|
|
996
|
+
}
|
|
997
|
+
writeStdoutLine("space: %s", result.space);
|
|
998
|
+
writeStdoutLine("ended: yes");
|
|
999
|
+
writeStdoutLine("token source: %s", token.refreshed ? "refresh-token" : "cached-access-token");
|
|
897
1000
|
});
|
|
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
1001
|
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
1002
|
const payload = {
|
|
906
1003
|
url: resolveMeetingInput(params.config, url),
|
|
@@ -961,470 +1058,354 @@ function registerGoogleMeetProbeCommands(context) {
|
|
|
961
1058
|
}
|
|
962
1059
|
writeStdoutJson(await (await params.ensureRuntime()).testListen(payload));
|
|
963
1060
|
});
|
|
964
|
-
}
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
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) {
|
|
1012
|
-
writeStdoutJson(result);
|
|
1013
|
-
return;
|
|
1014
|
-
}
|
|
1015
|
-
writeRecoverCurrentTabResult(result);
|
|
1016
|
-
});
|
|
1017
|
-
root.command("setup").description("Show Google Meet transport setup status").option("--transport <transport>", "Transport to check: chrome, chrome-node, or twilio").option("--mode <mode>", "Mode to check: agent, bidi, or transcribe").option("--json", "Print JSON output", false).action(async (options) => {
|
|
1018
|
-
const status = await (await params.ensureRuntime()).setupStatus({
|
|
1019
|
-
transport: options.transport,
|
|
1020
|
-
mode: options.mode
|
|
1021
|
-
});
|
|
1022
|
-
if (options.json) {
|
|
1023
|
-
writeStdoutJson(status);
|
|
1024
|
-
return;
|
|
1025
|
-
}
|
|
1026
|
-
writeSetupStatus(status);
|
|
1027
|
-
});
|
|
1028
|
-
root.command("leave").argument("<session-id>", "Meet session ID").action(async (sessionId) => {
|
|
1029
|
-
const delegated = await callGoogleMeetGateway({
|
|
1030
|
-
callGateway,
|
|
1031
|
-
method: "googlemeet.leave",
|
|
1032
|
-
payload: { sessionId }
|
|
1033
|
-
});
|
|
1034
|
-
if (delegated.ok) {
|
|
1035
|
-
const result = delegated.payload;
|
|
1036
|
-
if (!result.found) throw new Error("session not found");
|
|
1037
|
-
writeLeaveResult(sessionId, result);
|
|
1038
|
-
return;
|
|
1039
|
-
}
|
|
1040
|
-
const result = await (await params.ensureRuntime()).leave(sessionId);
|
|
1041
|
-
if (!result.found) throw new Error("session not found");
|
|
1042
|
-
writeLeaveResult(sessionId, result);
|
|
1043
|
-
});
|
|
1044
|
-
root.command("speak").argument("<session-id>", "Meet session ID").argument("[message]", "Realtime instructions to speak now").action(async (sessionId, message) => {
|
|
1045
|
-
const delegated = await callGoogleMeetGateway({
|
|
1046
|
-
callGateway,
|
|
1047
|
-
method: "googlemeet.speak",
|
|
1048
|
-
payload: {
|
|
1049
|
-
sessionId,
|
|
1050
|
-
message
|
|
1051
|
-
}
|
|
1052
|
-
});
|
|
1053
|
-
if (delegated.ok) {
|
|
1054
|
-
const result = delegated.payload;
|
|
1055
|
-
if (!result.found) throw new Error("session not found");
|
|
1056
|
-
if (!result.spoken) throw new Error(result.session?.chrome?.health?.speechBlockedMessage ?? "session has no active realtime audio bridge");
|
|
1057
|
-
writeStdoutLine("speaking on %s", sessionId);
|
|
1058
|
-
return;
|
|
1059
|
-
}
|
|
1060
|
-
const result = await (await params.ensureRuntime()).speak(sessionId, message);
|
|
1061
|
-
if (!result.found) throw new Error("session not found");
|
|
1062
|
-
if (!result.spoken) throw new Error(result.session?.chrome?.health?.speechBlockedMessage ?? "session has no active realtime audio bridge");
|
|
1063
|
-
writeStdoutLine("speaking on %s", sessionId);
|
|
1064
|
-
});
|
|
1065
|
-
}
|
|
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);
|
|
1061
|
+
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) => {
|
|
1062
|
+
const resolved = resolveTokenOptions(params.config, options);
|
|
1063
|
+
const token = await resolveGoogleMeetAccessToken(resolved);
|
|
1064
|
+
const space = await fetchGoogleMeetSpace({
|
|
1065
|
+
accessToken: token.accessToken,
|
|
1066
|
+
meeting: resolved.meeting
|
|
1067
|
+
});
|
|
1068
|
+
if (options.json) {
|
|
1069
|
+
writeStdoutJson(space);
|
|
1130
1070
|
return;
|
|
1131
1071
|
}
|
|
1132
|
-
|
|
1133
|
-
|
|
1072
|
+
writeStdoutLine("input: %s", resolved.meeting);
|
|
1073
|
+
writeStdoutLine("space: %s", space.name);
|
|
1074
|
+
if (space.meetingCode) writeStdoutLine("meeting code: %s", space.meetingCode);
|
|
1075
|
+
if (space.meetingUri) writeStdoutLine("meeting uri: %s", space.meetingUri);
|
|
1076
|
+
writeStdoutLine("active conference: %s", space.activeConference ? "yes" : "no");
|
|
1077
|
+
writeStdoutLine("token source: %s", token.refreshed ? "refresh-token" : "cached-access-token");
|
|
1078
|
+
});
|
|
1079
|
+
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) => {
|
|
1080
|
+
const resolved = resolveTokenOptions(params.config, options);
|
|
1081
|
+
const token = await resolveGoogleMeetAccessToken(resolved);
|
|
1082
|
+
const space = await fetchGoogleMeetSpace({
|
|
1134
1083
|
accessToken: token.accessToken,
|
|
1135
|
-
|
|
1084
|
+
meeting: resolved.meeting
|
|
1085
|
+
});
|
|
1086
|
+
const report = buildGoogleMeetPreflightReport({
|
|
1087
|
+
input: resolved.meeting,
|
|
1088
|
+
space,
|
|
1089
|
+
previewAcknowledged: params.config.preview.enrollmentAcknowledged,
|
|
1090
|
+
tokenSource: token.refreshed ? "refresh-token" : "cached-access-token"
|
|
1091
|
+
});
|
|
1092
|
+
if (options.json) {
|
|
1093
|
+
writeStdoutJson(report);
|
|
1094
|
+
return;
|
|
1095
|
+
}
|
|
1096
|
+
writeStdoutLine("input: %s", report.input);
|
|
1097
|
+
writeStdoutLine("resolved space: %s", report.resolvedSpaceName);
|
|
1098
|
+
if (report.meetingCode) writeStdoutLine("meeting code: %s", report.meetingCode);
|
|
1099
|
+
if (report.meetingUri) writeStdoutLine("meeting uri: %s", report.meetingUri);
|
|
1100
|
+
writeStdoutLine("active conference: %s", report.hasActiveConference ? "yes" : "no");
|
|
1101
|
+
writeStdoutLine("preview acknowledged: %s", report.previewAcknowledged ? "yes" : "no");
|
|
1102
|
+
writeStdoutLine("token source: %s", report.tokenSource);
|
|
1103
|
+
if (report.blockers.length === 0) {
|
|
1104
|
+
writeStdoutLine("blockers: none");
|
|
1105
|
+
return;
|
|
1106
|
+
}
|
|
1107
|
+
writeStdoutLine("blockers:");
|
|
1108
|
+
for (const blocker of report.blockers) writeStdoutLine("- %s", blocker);
|
|
1109
|
+
});
|
|
1110
|
+
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) => {
|
|
1111
|
+
const token = await resolveGoogleMeetAccessToken(resolveOAuthTokenOptions(params.config, options));
|
|
1112
|
+
const resolved = await resolveMeetingForToken({
|
|
1113
|
+
config: params.config,
|
|
1114
|
+
options,
|
|
1115
|
+
accessToken: token.accessToken,
|
|
1116
|
+
configuredMeeting: options.meeting?.trim()
|
|
1117
|
+
});
|
|
1118
|
+
const result = await fetchLatestGoogleMeetConferenceRecord({
|
|
1119
|
+
accessToken: token.accessToken,
|
|
1120
|
+
meeting: resolved.meeting
|
|
1136
1121
|
});
|
|
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
1122
|
if (options.json) {
|
|
1147
1123
|
writeStdoutJson({
|
|
1148
1124
|
...result,
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
...join ? { join } : {}
|
|
1125
|
+
...resolved.calendarEvent ? { calendarEvent: resolved.calendarEvent } : {},
|
|
1126
|
+
tokenSource: token.refreshed ? "refresh-token" : "cached-access-token"
|
|
1152
1127
|
});
|
|
1153
1128
|
return;
|
|
1154
1129
|
}
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1130
|
+
if (resolved.calendarEvent) {
|
|
1131
|
+
writeStdoutLine("calendar event: %s", resolved.calendarEvent.event.summary ?? "untitled");
|
|
1132
|
+
writeStdoutLine("calendar meet: %s", resolved.calendarEvent.meetingUri);
|
|
1133
|
+
}
|
|
1134
|
+
writeLatestConferenceRecordSummary(result);
|
|
1158
1135
|
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
1136
|
});
|
|
1162
|
-
root.command("
|
|
1137
|
+
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) => {
|
|
1163
1138
|
const token = await resolveGoogleMeetAccessToken(resolveOAuthTokenOptions(params.config, options));
|
|
1164
|
-
const
|
|
1139
|
+
const window = options.today ? buildGoogleMeetCalendarDayWindow() : {};
|
|
1140
|
+
const result = await listGoogleMeetCalendarEvents({
|
|
1165
1141
|
accessToken: token.accessToken,
|
|
1166
|
-
|
|
1142
|
+
calendarId: options.calendar,
|
|
1143
|
+
eventQuery: options.event,
|
|
1144
|
+
...window
|
|
1167
1145
|
});
|
|
1146
|
+
const payload = {
|
|
1147
|
+
...result,
|
|
1148
|
+
tokenSource: token.refreshed ? "refresh-token" : "cached-access-token"
|
|
1149
|
+
};
|
|
1168
1150
|
if (options.json) {
|
|
1169
|
-
writeStdoutJson(
|
|
1151
|
+
writeStdoutJson(payload);
|
|
1152
|
+
return;
|
|
1153
|
+
}
|
|
1154
|
+
writeCalendarEventsSummary(result);
|
|
1155
|
+
writeStdoutLine("token source: %s", token.refreshed ? "refresh-token" : "cached-access-token");
|
|
1156
|
+
});
|
|
1157
|
+
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) => {
|
|
1158
|
+
const resolved = resolveArtifactTokenOptions(params.config, options);
|
|
1159
|
+
const token = await resolveGoogleMeetAccessToken(resolved);
|
|
1160
|
+
const meeting = resolved.conferenceRecord ? resolved.meeting : (await resolveMeetingForToken({
|
|
1161
|
+
config: params.config,
|
|
1162
|
+
options,
|
|
1163
|
+
accessToken: token.accessToken,
|
|
1164
|
+
configuredMeeting: resolved.meeting
|
|
1165
|
+
})).meeting;
|
|
1166
|
+
const result = await fetchGoogleMeetArtifacts({
|
|
1167
|
+
accessToken: token.accessToken,
|
|
1168
|
+
meeting,
|
|
1169
|
+
conferenceRecord: resolved.conferenceRecord,
|
|
1170
|
+
pageSize: resolved.pageSize,
|
|
1171
|
+
includeTranscriptEntries: resolved.includeTranscriptEntries,
|
|
1172
|
+
allConferenceRecords: resolved.allConferenceRecords,
|
|
1173
|
+
includeDocumentBodies: resolved.includeDocumentBodies
|
|
1174
|
+
});
|
|
1175
|
+
if (options.json) {
|
|
1176
|
+
await writeCliOutput(options, JSON.stringify({
|
|
1170
1177
|
...result,
|
|
1171
1178
|
tokenSource: token.refreshed ? "refresh-token" : "cached-access-token"
|
|
1172
|
-
});
|
|
1179
|
+
}, null, 2));
|
|
1173
1180
|
return;
|
|
1174
1181
|
}
|
|
1175
|
-
|
|
1176
|
-
|
|
1182
|
+
if (options.format === "markdown") {
|
|
1183
|
+
await writeCliOutput(options, renderArtifactsMarkdown(result));
|
|
1184
|
+
return;
|
|
1185
|
+
}
|
|
1186
|
+
if (options.format && options.format !== "summary") throw new Error("Unsupported format. Expected summary or markdown.");
|
|
1187
|
+
writeArtifactsSummary(result);
|
|
1177
1188
|
writeStdoutLine("token source: %s", token.refreshed ? "refresh-token" : "cached-access-token");
|
|
1178
1189
|
});
|
|
1179
|
-
}
|
|
1180
|
-
|
|
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);
|
|
1190
|
+
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) => {
|
|
1191
|
+
const resolved = resolveArtifactTokenOptions(params.config, options);
|
|
1185
1192
|
const token = await resolveGoogleMeetAccessToken(resolved);
|
|
1186
|
-
const
|
|
1193
|
+
const meeting = resolved.conferenceRecord ? resolved.meeting : (await resolveMeetingForToken({
|
|
1194
|
+
config: params.config,
|
|
1195
|
+
options,
|
|
1187
1196
|
accessToken: token.accessToken,
|
|
1188
|
-
|
|
1197
|
+
configuredMeeting: resolved.meeting
|
|
1198
|
+
})).meeting;
|
|
1199
|
+
const result = await fetchGoogleMeetAttendance({
|
|
1200
|
+
accessToken: token.accessToken,
|
|
1201
|
+
meeting,
|
|
1202
|
+
conferenceRecord: resolved.conferenceRecord,
|
|
1203
|
+
pageSize: resolved.pageSize,
|
|
1204
|
+
allConferenceRecords: resolved.allConferenceRecords,
|
|
1205
|
+
mergeDuplicateParticipants: resolved.mergeDuplicateParticipants,
|
|
1206
|
+
lateAfterMinutes: resolved.lateAfterMinutes,
|
|
1207
|
+
earlyBeforeMinutes: resolved.earlyBeforeMinutes
|
|
1208
|
+
});
|
|
1209
|
+
if (options.json) {
|
|
1210
|
+
await writeCliOutput(options, JSON.stringify({
|
|
1211
|
+
...result,
|
|
1212
|
+
tokenSource: token.refreshed ? "refresh-token" : "cached-access-token"
|
|
1213
|
+
}, null, 2));
|
|
1214
|
+
return;
|
|
1215
|
+
}
|
|
1216
|
+
if (options.format === "markdown") {
|
|
1217
|
+
await writeCliOutput(options, renderAttendanceMarkdown(result));
|
|
1218
|
+
return;
|
|
1219
|
+
}
|
|
1220
|
+
if (options.format === "csv") {
|
|
1221
|
+
await writeCliOutput(options, renderAttendanceCsv(result));
|
|
1222
|
+
return;
|
|
1223
|
+
}
|
|
1224
|
+
if (options.format && options.format !== "summary") throw new Error("Unsupported format. Expected summary, markdown, or csv.");
|
|
1225
|
+
writeAttendanceSummary(result);
|
|
1226
|
+
writeStdoutLine("token source: %s", token.refreshed ? "refresh-token" : "cached-access-token");
|
|
1227
|
+
});
|
|
1228
|
+
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) => {
|
|
1229
|
+
const resolved = resolveArtifactTokenOptions(params.config, options);
|
|
1230
|
+
const token = await resolveGoogleMeetAccessToken(resolved);
|
|
1231
|
+
const meetingResult = resolved.conferenceRecord ? { meeting: resolved.meeting } : await resolveMeetingForToken({
|
|
1232
|
+
config: params.config,
|
|
1233
|
+
options,
|
|
1234
|
+
accessToken: token.accessToken,
|
|
1235
|
+
configuredMeeting: resolved.meeting
|
|
1236
|
+
});
|
|
1237
|
+
const artifacts = await fetchGoogleMeetArtifacts({
|
|
1238
|
+
accessToken: token.accessToken,
|
|
1239
|
+
meeting: meetingResult.meeting,
|
|
1240
|
+
conferenceRecord: resolved.conferenceRecord,
|
|
1241
|
+
pageSize: resolved.pageSize,
|
|
1242
|
+
includeTranscriptEntries: resolved.includeTranscriptEntries,
|
|
1243
|
+
allConferenceRecords: resolved.allConferenceRecords,
|
|
1244
|
+
includeDocumentBodies: resolved.includeDocumentBodies
|
|
1245
|
+
});
|
|
1246
|
+
const attendance = await fetchGoogleMeetAttendance({
|
|
1247
|
+
accessToken: token.accessToken,
|
|
1248
|
+
meeting: meetingResult.meeting,
|
|
1249
|
+
conferenceRecord: resolved.conferenceRecord,
|
|
1250
|
+
pageSize: resolved.pageSize,
|
|
1251
|
+
allConferenceRecords: resolved.allConferenceRecords,
|
|
1252
|
+
mergeDuplicateParticipants: resolved.mergeDuplicateParticipants,
|
|
1253
|
+
lateAfterMinutes: resolved.lateAfterMinutes,
|
|
1254
|
+
earlyBeforeMinutes: resolved.earlyBeforeMinutes
|
|
1255
|
+
});
|
|
1256
|
+
const resolvedMeeting = meetingResult.meeting ?? resolved.meeting;
|
|
1257
|
+
const request = {
|
|
1258
|
+
...resolvedMeeting ? { meeting: resolvedMeeting } : {},
|
|
1259
|
+
...resolved.conferenceRecord ? { conferenceRecord: resolved.conferenceRecord } : {},
|
|
1260
|
+
...meetingResult.calendarEvent?.event.id ? { calendarEventId: meetingResult.calendarEvent.event.id } : {},
|
|
1261
|
+
...meetingResult.calendarEvent?.event.summary ? { calendarEventSummary: meetingResult.calendarEvent.event.summary } : {},
|
|
1262
|
+
...options.calendar ? { calendarId: options.calendar } : {},
|
|
1263
|
+
...resolved.pageSize !== void 0 ? { pageSize: resolved.pageSize } : {},
|
|
1264
|
+
includeTranscriptEntries: resolved.includeTranscriptEntries,
|
|
1265
|
+
includeDocumentBodies: resolved.includeDocumentBodies,
|
|
1266
|
+
allConferenceRecords: resolved.allConferenceRecords,
|
|
1267
|
+
mergeDuplicateParticipants: resolved.mergeDuplicateParticipants,
|
|
1268
|
+
...resolved.lateAfterMinutes !== void 0 ? { lateAfterMinutes: resolved.lateAfterMinutes } : {},
|
|
1269
|
+
...resolved.earlyBeforeMinutes !== void 0 ? { earlyBeforeMinutes: resolved.earlyBeforeMinutes } : {}
|
|
1270
|
+
};
|
|
1271
|
+
if (options.dryRun) {
|
|
1272
|
+
writeStdoutJson({
|
|
1273
|
+
dryRun: true,
|
|
1274
|
+
manifest: buildGoogleMeetExportManifest({
|
|
1275
|
+
artifacts,
|
|
1276
|
+
attendance,
|
|
1277
|
+
files: googleMeetExportFileNames(),
|
|
1278
|
+
request,
|
|
1279
|
+
tokenSource: token.refreshed ? "refresh-token" : "cached-access-token",
|
|
1280
|
+
...meetingResult.calendarEvent ? { calendarEvent: meetingResult.calendarEvent } : {}
|
|
1281
|
+
}),
|
|
1282
|
+
...meetingResult.calendarEvent ? { calendarEvent: meetingResult.calendarEvent } : {},
|
|
1283
|
+
tokenSource: token.refreshed ? "refresh-token" : "cached-access-token"
|
|
1284
|
+
});
|
|
1285
|
+
return;
|
|
1286
|
+
}
|
|
1287
|
+
const bundle = await writeMeetExportBundle({
|
|
1288
|
+
outputDir: options.output,
|
|
1289
|
+
artifacts,
|
|
1290
|
+
attendance,
|
|
1291
|
+
zip: Boolean(options.zip),
|
|
1292
|
+
request,
|
|
1293
|
+
tokenSource: token.refreshed ? "refresh-token" : "cached-access-token",
|
|
1294
|
+
...meetingResult.calendarEvent ? { calendarEvent: meetingResult.calendarEvent } : {}
|
|
1295
|
+
});
|
|
1296
|
+
const payload = {
|
|
1297
|
+
...bundle,
|
|
1298
|
+
...meetingResult.calendarEvent ? { calendarEvent: meetingResult.calendarEvent } : {},
|
|
1299
|
+
tokenSource: token.refreshed ? "refresh-token" : "cached-access-token"
|
|
1300
|
+
};
|
|
1301
|
+
if (options.json) {
|
|
1302
|
+
writeStdoutJson(payload);
|
|
1303
|
+
return;
|
|
1304
|
+
}
|
|
1305
|
+
writeStdoutLine("export: %s", bundle.outputDir);
|
|
1306
|
+
for (const file of bundle.files) writeStdoutLine("- %s", file);
|
|
1307
|
+
if (bundle.zipFile) writeStdoutLine("zip: %s", bundle.zipFile);
|
|
1308
|
+
});
|
|
1309
|
+
root.command("status").argument("[session-id]", "Meet session ID").option("--json", "Print JSON output", false).action(async (sessionId) => {
|
|
1310
|
+
const delegated = await callGoogleMeetGateway({
|
|
1311
|
+
callGateway,
|
|
1312
|
+
method: "googlemeet.status",
|
|
1313
|
+
payload: { sessionId }
|
|
1189
1314
|
});
|
|
1190
|
-
if (
|
|
1191
|
-
writeStdoutJson(
|
|
1315
|
+
if (delegated.ok) {
|
|
1316
|
+
writeStdoutJson(delegated.payload);
|
|
1192
1317
|
return;
|
|
1193
1318
|
}
|
|
1194
|
-
|
|
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");
|
|
1319
|
+
writeStdoutJson(await (await params.ensureRuntime()).status(sessionId));
|
|
1200
1320
|
});
|
|
1201
|
-
root.command("
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1321
|
+
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) => {
|
|
1322
|
+
if (options.oauth) {
|
|
1323
|
+
const report = await buildOAuthDoctorReport(params.config, options);
|
|
1324
|
+
if (options.json) {
|
|
1325
|
+
writeStdoutJson(report);
|
|
1326
|
+
return;
|
|
1327
|
+
}
|
|
1328
|
+
writeOAuthDoctorReport(report);
|
|
1329
|
+
return;
|
|
1330
|
+
}
|
|
1331
|
+
const delegated = await callGoogleMeetGateway({
|
|
1332
|
+
callGateway,
|
|
1333
|
+
method: "googlemeet.status",
|
|
1334
|
+
payload: { sessionId }
|
|
1213
1335
|
});
|
|
1214
|
-
if (
|
|
1215
|
-
|
|
1336
|
+
if (delegated.ok) {
|
|
1337
|
+
const status = delegated.payload;
|
|
1338
|
+
if (options.json) {
|
|
1339
|
+
writeStdoutJson(status);
|
|
1340
|
+
return;
|
|
1341
|
+
}
|
|
1342
|
+
writeDoctorStatus(status);
|
|
1216
1343
|
return;
|
|
1217
1344
|
}
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
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");
|
|
1345
|
+
const status = await (await params.ensureRuntime()).status(sessionId);
|
|
1346
|
+
if (options.json) {
|
|
1347
|
+
writeStdoutJson(status);
|
|
1227
1348
|
return;
|
|
1228
1349
|
}
|
|
1229
|
-
|
|
1230
|
-
for (const blocker of report.blockers) writeStdoutLine("- %s", blocker);
|
|
1350
|
+
writeDoctorStatus(status);
|
|
1231
1351
|
});
|
|
1232
|
-
root.command("
|
|
1233
|
-
const
|
|
1234
|
-
|
|
1235
|
-
|
|
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
|
|
1352
|
+
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) => {
|
|
1353
|
+
const result = await (await params.ensureRuntime()).recoverCurrentTab({
|
|
1354
|
+
url,
|
|
1355
|
+
transport: options.transport
|
|
1243
1356
|
});
|
|
1244
1357
|
if (options.json) {
|
|
1245
|
-
writeStdoutJson(
|
|
1246
|
-
...result,
|
|
1247
|
-
...resolved.calendarEvent ? { calendarEvent: resolved.calendarEvent } : {},
|
|
1248
|
-
tokenSource: token.refreshed ? "refresh-token" : "cached-access-token"
|
|
1249
|
-
});
|
|
1358
|
+
writeStdoutJson(result);
|
|
1250
1359
|
return;
|
|
1251
1360
|
}
|
|
1252
|
-
|
|
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");
|
|
1361
|
+
writeRecoverCurrentTabResult(result);
|
|
1258
1362
|
});
|
|
1259
|
-
root.command("
|
|
1260
|
-
const
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
accessToken: token.accessToken,
|
|
1264
|
-
calendarId: options.calendar,
|
|
1265
|
-
eventQuery: options.event,
|
|
1266
|
-
...window
|
|
1363
|
+
root.command("setup").description("Show Google Meet transport setup status").option("--transport <transport>", "Transport to check: chrome, chrome-node, or twilio").option("--mode <mode>", "Mode to check: agent, bidi, or transcribe").option("--json", "Print JSON output", false).action(async (options) => {
|
|
1364
|
+
const status = await (await params.ensureRuntime()).setupStatus({
|
|
1365
|
+
transport: options.transport,
|
|
1366
|
+
mode: options.mode
|
|
1267
1367
|
});
|
|
1268
|
-
const payload = {
|
|
1269
|
-
...result,
|
|
1270
|
-
tokenSource: token.refreshed ? "refresh-token" : "cached-access-token"
|
|
1271
|
-
};
|
|
1272
1368
|
if (options.json) {
|
|
1273
|
-
writeStdoutJson(
|
|
1369
|
+
writeStdoutJson(status);
|
|
1274
1370
|
return;
|
|
1275
1371
|
}
|
|
1276
|
-
|
|
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
|
|
1372
|
+
writeSetupStatus(status);
|
|
1323
1373
|
});
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
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
|
|
1374
|
+
root.command("leave").argument("<session-id>", "Meet session ID").action(async (sessionId) => {
|
|
1375
|
+
const delegated = await callGoogleMeetGateway({
|
|
1376
|
+
callGateway,
|
|
1377
|
+
method: "googlemeet.leave",
|
|
1378
|
+
payload: { sessionId }
|
|
1378
1379
|
});
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1380
|
+
if (delegated.ok) {
|
|
1381
|
+
if (!delegated.payload.found) throw new Error("session not found");
|
|
1382
|
+
writeStdoutLine("left %s", sessionId);
|
|
1383
|
+
return;
|
|
1384
|
+
}
|
|
1385
|
+
if (!(await (await params.ensureRuntime()).leave(sessionId)).found) throw new Error("session not found");
|
|
1386
|
+
writeStdoutLine("left %s", sessionId);
|
|
1387
|
+
});
|
|
1388
|
+
root.command("speak").argument("<session-id>", "Meet session ID").argument("[message]", "Realtime instructions to speak now").action(async (sessionId, message) => {
|
|
1389
|
+
const delegated = await callGoogleMeetGateway({
|
|
1390
|
+
callGateway,
|
|
1391
|
+
method: "googlemeet.speak",
|
|
1392
|
+
payload: {
|
|
1393
|
+
sessionId,
|
|
1394
|
+
message
|
|
1395
|
+
}
|
|
1391
1396
|
});
|
|
1392
|
-
if (
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
};
|
|
1404
|
-
if (!options.json) writeStdoutLine("Paste this into plugins.entries.google-meet.config:");
|
|
1405
|
-
writeStdoutJson(payload);
|
|
1397
|
+
if (delegated.ok) {
|
|
1398
|
+
const result = delegated.payload;
|
|
1399
|
+
if (!result.found) throw new Error("session not found");
|
|
1400
|
+
if (!result.spoken) throw new Error(result.session?.chrome?.health?.speechBlockedMessage ?? "session has no active realtime audio bridge");
|
|
1401
|
+
writeStdoutLine("speaking on %s", sessionId);
|
|
1402
|
+
return;
|
|
1403
|
+
}
|
|
1404
|
+
const result = await (await params.ensureRuntime()).speak(sessionId, message);
|
|
1405
|
+
if (!result.found) throw new Error("session not found");
|
|
1406
|
+
if (!result.spoken) throw new Error(result.session?.chrome?.health?.speechBlockedMessage ?? "session has no active realtime audio bridge");
|
|
1407
|
+
writeStdoutLine("speaking on %s", sessionId);
|
|
1406
1408
|
});
|
|
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
1409
|
}
|
|
1429
1410
|
//#endregion
|
|
1430
1411
|
export { buildGoogleMeetExportManifest, googleMeetExportFileNames, registerGoogleMeetCli, testing, writeMeetExportBundle };
|