





















@@ -1,3 +1,4 @@
1+import { writeFile } from "node:fs/promises";
12import { createInterface } from "node:readline/promises";
23import { format } from "node:util";
34import type { Command } from "commander";
@@ -52,6 +53,8 @@ type MeetArtifactOptions = ResolveSpaceOptions & {
5253conferenceRecord?: string;
5354pageSize?: string;
5455transcriptEntries?: boolean;
56+format?: "summary" | "markdown";
57+output?: string;
5558};
56595760type SetupOptions = {
@@ -98,6 +101,15 @@ function writeStdoutLine(...values: unknown[]): void {
98101process.stdout.write(`${format(...values)}\n`);
99102}
100103104+async function writeCliOutput(options: { output?: string }, text: string): Promise<void> {
105+if (options.output?.trim()) {
106+await writeFile(options.output, text.endsWith("\n") ? text : `${text}\n`, "utf8");
107+writeStdoutLine("wrote: %s", options.output);
108+return;
109+}
110+process.stdout.write(text.endsWith("\n") ? text : `${text}\n`);
111+}
112+101113async function promptInput(message: string): Promise<string> {
102114const rl = createInterface({
103115input: process.stdin,
@@ -535,6 +547,123 @@ function writeAttendanceSummary(result: GoogleMeetAttendanceResult): void {
535547}
536548}
537549550+function pushMarkdownLine(lines: string[], text = ""): void {
551+lines.push(text);
552+}
553+554+function formatMarkdownOptional(value: unknown): string {
555+return typeof value === "string" && value.trim() ? value : "n/a";
556+}
557+558+function formatMarkdownIdentity(row: GoogleMeetAttendanceResult["attendance"][number]): string {
559+return row.displayName || row.user || row.participant;
560+}
561+562+function renderArtifactsMarkdown(result: GoogleMeetArtifactsResult): string {
563+const lines: string[] = ["# Google Meet Artifacts"];
564+if (result.input) {
565+pushMarkdownLine(lines, `Input: ${result.input}`);
566+}
567+if (result.space) {
568+pushMarkdownLine(lines, `Space: ${result.space.name}`);
569+}
570+pushMarkdownLine(lines);
571+pushMarkdownLine(lines, `Conference records: ${result.conferenceRecords.length}`);
572+for (const entry of result.artifacts) {
573+pushMarkdownLine(lines);
574+pushMarkdownLine(lines, `## ${entry.conferenceRecord.name}`);
575+pushMarkdownLine(lines, `Started: ${formatMarkdownOptional(entry.conferenceRecord.startTime)}`);
576+pushMarkdownLine(lines, `Ended: ${formatMarkdownOptional(entry.conferenceRecord.endTime)}`);
577+pushMarkdownLine(lines);
578+pushMarkdownLine(lines, `Participants: ${entry.participants.length}`);
579+pushMarkdownLine(lines, `Recordings: ${entry.recordings.length}`);
580+pushMarkdownLine(lines, `Transcripts: ${entry.transcripts.length}`);
581+pushMarkdownLine(
582+lines,
583+`Transcript entries: ${entry.transcriptEntries.reduce(
584+ (count, transcript) => count + transcript.entries.length,
585+ 0,
586+ )}`,
587+);
588+pushMarkdownLine(lines, `Smart notes: ${entry.smartNotes.length}`);
589+if (entry.recordings.length > 0) {
590+pushMarkdownLine(lines);
591+pushMarkdownLine(lines, "### Recordings");
592+for (const recording of entry.recordings) {
593+pushMarkdownLine(lines, `- ${recording.name}`);
594+}
595+}
596+if (entry.transcripts.length > 0) {
597+pushMarkdownLine(lines);
598+pushMarkdownLine(lines, "### Transcripts");
599+for (const transcript of entry.transcripts) {
600+pushMarkdownLine(lines, `- ${transcript.name}`);
601+}
602+}
603+for (const transcriptEntries of entry.transcriptEntries) {
604+pushMarkdownLine(lines);
605+pushMarkdownLine(lines, `### Transcript Entries: ${transcriptEntries.transcript}`);
606+if (transcriptEntries.entriesError) {
607+pushMarkdownLine(lines, `Warning: ${transcriptEntries.entriesError}`);
608+continue;
609+}
610+if (transcriptEntries.entries.length === 0) {
611+pushMarkdownLine(lines, "_No transcript entries._");
612+continue;
613+}
614+for (const transcriptEntry of transcriptEntries.entries) {
615+const times =
616+transcriptEntry.startTime || transcriptEntry.endTime
617+ ? ` (${formatMarkdownOptional(transcriptEntry.startTime)} -> ${formatMarkdownOptional(
618+ transcriptEntry.endTime,
619+ )})`
620+ : "";
621+const speaker = transcriptEntry.participant ? `${transcriptEntry.participant}: ` : "";
622+pushMarkdownLine(lines, `- ${speaker}${transcriptEntry.text ?? ""}${times}`);
623+}
624+}
625+if (entry.smartNotes.length > 0) {
626+pushMarkdownLine(lines);
627+pushMarkdownLine(lines, "### Smart Notes");
628+for (const smartNote of entry.smartNotes) {
629+pushMarkdownLine(lines, `- ${smartNote.name}`);
630+}
631+}
632+}
633+return `${lines.join("\n")}\n`;
634+}
635+636+function renderAttendanceMarkdown(result: GoogleMeetAttendanceResult): string {
637+const lines: string[] = ["# Google Meet Attendance"];
638+if (result.input) {
639+pushMarkdownLine(lines, `Input: ${result.input}`);
640+}
641+if (result.space) {
642+pushMarkdownLine(lines, `Space: ${result.space.name}`);
643+}
644+pushMarkdownLine(lines);
645+pushMarkdownLine(lines, `Conference records: ${result.conferenceRecords.length}`);
646+pushMarkdownLine(lines, `Attendance rows: ${result.attendance.length}`);
647+for (const row of result.attendance) {
648+pushMarkdownLine(lines);
649+pushMarkdownLine(lines, `## ${formatMarkdownIdentity(row)}`);
650+pushMarkdownLine(lines, `Record: ${row.conferenceRecord}`);
651+pushMarkdownLine(lines, `Resource: ${row.participant}`);
652+pushMarkdownLine(lines, `First joined: ${formatMarkdownOptional(row.earliestStartTime)}`);
653+pushMarkdownLine(lines, `Last left: ${formatMarkdownOptional(row.latestEndTime)}`);
654+pushMarkdownLine(lines, `Sessions: ${row.sessions.length}`);
655+for (const session of row.sessions) {
656+pushMarkdownLine(
657+lines,
658+`- ${session.name}: ${formatMarkdownOptional(session.startTime)} -> ${formatMarkdownOptional(
659+ session.endTime,
660+ )}`,
661+);
662+}
663+}
664+return `${lines.join("\n")}\n`;
665+}
666+538667export function registerGoogleMeetCli(params: {
539668program: Command;
540669config: GoogleMeetConfig;
@@ -857,6 +986,8 @@ export function registerGoogleMeetCli(params: {
857986.option("--expires-at <ms>", "Cached access token expiry as unix epoch milliseconds")
858987.option("--page-size <n>", "Max resources per Meet API page")
859988.option("--no-transcript-entries", "Skip structured transcript entry lookup")
989+.option("--format <format>", "Output format: summary or markdown", "summary")
990+.option("--output <path>", "Write output to a file instead of stdout")
860991.option("--json", "Print JSON output", false)
861992.action(async (options: MeetArtifactOptions) => {
862993const resolved = resolveArtifactTokenOptions(params.config, options);
@@ -869,12 +1000,26 @@ export function registerGoogleMeetCli(params: {
8691000includeTranscriptEntries: resolved.includeTranscriptEntries,
8701001});
8711002if (options.json) {
872-writeStdoutJson({
873- ...result,
874-tokenSource: token.refreshed ? "refresh-token" : "cached-access-token",
875-});
1003+await writeCliOutput(
1004+options,
1005+JSON.stringify(
1006+{
1007+ ...result,
1008+tokenSource: token.refreshed ? "refresh-token" : "cached-access-token",
1009+},
1010+null,
1011+2,
1012+),
1013+);
8761014return;
8771015}
1016+if (options.format === "markdown") {
1017+await writeCliOutput(options, renderArtifactsMarkdown(result));
1018+return;
1019+}
1020+if (options.format && options.format !== "summary") {
1021+throw new Error("Unsupported format. Expected summary or markdown.");
1022+}
8781023writeArtifactsSummary(result);
8791024writeStdoutLine(
8801025"token source: %s",
@@ -893,6 +1038,8 @@ export function registerGoogleMeetCli(params: {
8931038.option("--client-secret <secret>", "OAuth client secret override")
8941039.option("--expires-at <ms>", "Cached access token expiry as unix epoch milliseconds")
8951040.option("--page-size <n>", "Max resources per Meet API page")
1041+.option("--format <format>", "Output format: summary or markdown", "summary")
1042+.option("--output <path>", "Write output to a file instead of stdout")
8961043.option("--json", "Print JSON output", false)
8971044.action(async (options: MeetArtifactOptions) => {
8981045const resolved = resolveArtifactTokenOptions(params.config, options);
@@ -904,12 +1051,26 @@ export function registerGoogleMeetCli(params: {
9041051pageSize: resolved.pageSize,
9051052});
9061053if (options.json) {
907-writeStdoutJson({
908- ...result,
909-tokenSource: token.refreshed ? "refresh-token" : "cached-access-token",
910-});
1054+await writeCliOutput(
1055+options,
1056+JSON.stringify(
1057+{
1058+ ...result,
1059+tokenSource: token.refreshed ? "refresh-token" : "cached-access-token",
1060+},
1061+null,
1062+2,
1063+),
1064+);
9111065return;
9121066}
1067+if (options.format === "markdown") {
1068+await writeCliOutput(options, renderAttendanceMarkdown(result));
1069+return;
1070+}
1071+if (options.format && options.format !== "summary") {
1072+throw new Error("Unsupported format. Expected summary or markdown.");
1073+}
9131074writeAttendanceSummary(result);
9141075writeStdoutLine(
9151076"token source: %s",
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。