惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

J
Java Code Geeks
H
Hackread – Cybersecurity News, Data Breaches, AI and More
Blog — PlanetScale
Blog — PlanetScale
G
Google Developers Blog
Microsoft Security Blog
Microsoft Security Blog
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
腾讯CDC
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
Jina AI
Jina AI
雷峰网
雷峰网
T
Tailwind CSS Blog
爱范儿
爱范儿
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
酷 壳 – CoolShell
酷 壳 – CoolShell
大猫的无限游戏
大猫的无限游戏
月光博客
月光博客
博客园 - 司徒正美
I
InfoQ
Engineering at Meta
Engineering at Meta
Vercel News
Vercel News
小众软件
小众软件
U
Unit 42
Google DeepMind News
Google DeepMind News
D
DataBreaches.Net

Recent Commits to openclaw:main

test: merge chat side-result checks · openclaw/openclaw@ddd2c2a test: merge cron history checks · openclaw/openclaw@f7eb746 test: merge responsive navigation shell checks · openclaw/openclaw@c2e4b47 docs(changelog): add codex oauth fixes · openclaw/openclaw@628e6cd test: merge navigation routing cases · openclaw/openclaw@5d8cecb Tests: mock channel registry bundled fallback · openclaw/openclaw@2b08233 Secrets: avoid broad web search discovery for single plugin config · openclaw/openclaw@a464f59 test: merge config view browser checks · openclaw/openclaw@20cf511 fix(status): align oauth health with runtime · openclaw/openclaw@eed7116 feat: add macOS screen snapshots for monitor preview (#67954) thanks … · openclaw/openclaw@f377db1 fix: report shared auth scopes in hello-ok (#67810) thanks @BunsDev · openclaw/openclaw@0b6c39b Auto-reply: avoid eager bundled route fallback · openclaw/openclaw@3ea1bf4 Tests: narrow session binding contract setup · openclaw/openclaw@54e4e16 fix(macOS): enable undo/redo in webchat composer text input (#34962) · openclaw/openclaw@00951dc Tests: speed up channel setup promotion · openclaw/openclaw@82b529a Docs: refresh agent instructions · openclaw/openclaw@5775fe2 fix(auth): serialize OAuth refresh across agents to fix #26322 (#67876) · openclaw/openclaw@8e79080 test: allow ollama public surface boundary test · openclaw/openclaw@7d4f1a6 Docs: add test performance guardrails · openclaw/openclaw@89706d3 Tests: restore context-engine usage proof · openclaw/openclaw@e4c4f95 Tests: slim context engine runtime coverage · openclaw/openclaw@74c198f ci: retry failed custom checkouts · openclaw/openclaw@0ee5baf test: trim duplicate provider auth onboarding cases · openclaw/openclaw@1ffc02e matrix: fix sessions_spawn --thread subagent session spawning (#67643) · openclaw/openclaw@1ce2596 test: reduce auth choice fixture churn · openclaw/openclaw@857b9cd test: mock health status config boundaries · openclaw/openclaw@9d5ab4a test: mock onboard config io boundary · openclaw/openclaw@299694d test: mock legacy state plugin boundaries · openclaw/openclaw@2713089 test: mock channel install boundaries · openclaw/openclaw@b945248 test: mock doctor preview channel boundaries · openclaw/openclaw@b1a3ad4
feat(google-meet): export artifacts reports · openclaw/op...
steipete · 2026-04-25 · via Recent Commits to openclaw:main

@@ -1,3 +1,4 @@

1+

import { writeFile } from "node:fs/promises";

12

import { createInterface } from "node:readline/promises";

23

import { format } from "node:util";

34

import type { Command } from "commander";

@@ -52,6 +53,8 @@ type MeetArtifactOptions = ResolveSpaceOptions & {

5253

conferenceRecord?: string;

5354

pageSize?: string;

5455

transcriptEntries?: boolean;

56+

format?: "summary" | "markdown";

57+

output?: string;

5558

};

56595760

type SetupOptions = {

@@ -98,6 +101,15 @@ function writeStdoutLine(...values: unknown[]): void {

98101

process.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+101113

async function promptInput(message: string): Promise<string> {

102114

const rl = createInterface({

103115

input: 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+538667

export function registerGoogleMeetCli(params: {

539668

program: Command;

540669

config: 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) => {

862993

const resolved = resolveArtifactTokenOptions(params.config, options);

@@ -869,12 +1000,26 @@ export function registerGoogleMeetCli(params: {

8691000

includeTranscriptEntries: resolved.includeTranscriptEntries,

8701001

});

8711002

if (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+

);

8761014

return;

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+

}

8781023

writeArtifactsSummary(result);

8791024

writeStdoutLine(

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) => {

8981045

const resolved = resolveArtifactTokenOptions(params.config, options);

@@ -904,12 +1051,26 @@ export function registerGoogleMeetCli(params: {

9041051

pageSize: resolved.pageSize,

9051052

});

9061053

if (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+

);

9111065

return;

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+

}

9131074

writeAttendanceSummary(result);

9141075

writeStdoutLine(

9151076

"token source: %s",