sophhub 0.2.4 → 0.4.0
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/README.md +29 -0
- package/agents/ai-cs-admin/.config.json +34 -0
- package/agents/ai-cs-admin/AGENTS.md +293 -0
- package/agents/ai-cs-admin/BOOTSTRAP.md +19 -0
- package/agents/ai-cs-admin/HEARTBEAT.md +19 -0
- package/agents/ai-cs-admin/IDENTITY.md +6 -0
- package/agents/ai-cs-admin/MEMORY.md +22 -0
- package/agents/ai-cs-admin/SOUL.md +25 -0
- package/agents/ai-cs-admin/TOOLS.md +98 -0
- package/agents/ai-cs-admin/USER.md +17 -0
- package/agents/ai-cs-qa/.config.json +32 -0
- package/agents/ai-cs-qa/AGENTS.md +284 -0
- package/agents/ai-cs-qa/BOOTSTRAP.md +22 -0
- package/agents/ai-cs-qa/HEARTBEAT.md +20 -0
- package/agents/ai-cs-qa/IDENTITY.md +6 -0
- package/agents/ai-cs-qa/MEMORY.md +22 -0
- package/agents/ai-cs-qa/SOUL.md +33 -0
- package/agents/ai-cs-qa/TOOLS.md +35 -0
- package/agents/ai-cs-qa/USER.md +16 -0
- package/bin/sophhub.js +2 -0
- package/package.json +3 -2
- package/skills/notes-hub-assistant/skill.json +20 -0
- package/skills/notes-hub-assistant/src/SKILL.md +233 -0
- package/skills/notes-hub-assistant/src/scripts/_resolve_lark_cli.py +48 -0
- package/skills/notes-hub-assistant/src/scripts/openclaw_meeting_minutes.py +473 -0
- package/skills/notes-hub-assistant/src/scripts/openclaw_notes_crud.py +596 -0
- package/skills/notes-hub-assistant/src/scripts/openclaw_wolai_notes_crud.py +364 -0
- package/skills/notes-hub-assistant/src/scripts/run_meeting_minutes.py +79 -0
- package/skills/notes-hub-assistant/src/scripts/run_note_crud.py +37 -0
- package/skills/notes-hub-assistant/src/scripts/run_notionbot.py +36 -0
- package/skills/notes-hub-assistant/src/scripts/run_wolai_note_crud.py +27 -0
- package/src/commands/agent.js +112 -0
- package/src/utils/agents.js +36 -0
- package/src/utils/paths.js +12 -0
|
@@ -0,0 +1,473 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""OpenClaw downstream meeting-minutes orchestrator.
|
|
3
|
+
|
|
4
|
+
This script implements the reusable half of the OpenClaw flow:
|
|
5
|
+
|
|
6
|
+
minute_token -> lark-cli minutes/vc/docs -> Markdown -> JSON result
|
|
7
|
+
|
|
8
|
+
It intentionally does not upload audio into Feishu Minutes. That step must be
|
|
9
|
+
handled by an upstream ingest adapter which returns a minute_token.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
import argparse
|
|
15
|
+
import json
|
|
16
|
+
import os
|
|
17
|
+
import subprocess
|
|
18
|
+
import sys
|
|
19
|
+
from pathlib import Path
|
|
20
|
+
from typing import Any, Dict, Optional
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
REQUIRED_SCOPES = (
|
|
24
|
+
"vc:note:read "
|
|
25
|
+
"minutes:minutes:readonly "
|
|
26
|
+
"minutes:minutes.artifacts:read "
|
|
27
|
+
"minutes:minutes.transcript:export "
|
|
28
|
+
"docx:document:create"
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class CLIError(Exception):
|
|
33
|
+
def __init__(self, message: str, code: str = "cli_error", detail: Any = None):
|
|
34
|
+
super().__init__(message)
|
|
35
|
+
self.code = code
|
|
36
|
+
self.detail = detail
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def parse_args() -> argparse.Namespace:
|
|
40
|
+
parser = argparse.ArgumentParser(
|
|
41
|
+
description="Build meeting minutes Markdown from an existing minute_token."
|
|
42
|
+
)
|
|
43
|
+
parser.add_argument("--minute-token", required=True, help="Feishu minute_token")
|
|
44
|
+
parser.add_argument("--job-id", required=True, help="OpenClaw job ID")
|
|
45
|
+
parser.add_argument("--title", help="Override meeting title")
|
|
46
|
+
parser.add_argument("--folder-token", help="Target Feishu folder token")
|
|
47
|
+
parser.add_argument(
|
|
48
|
+
"--output-dir",
|
|
49
|
+
default=".openclaw-meeting-minutes",
|
|
50
|
+
help="Directory for transcript/artifact files",
|
|
51
|
+
)
|
|
52
|
+
parser.add_argument(
|
|
53
|
+
"--config-dir",
|
|
54
|
+
help="Override LARKSUITE_CLI_CONFIG_DIR for per-user isolation",
|
|
55
|
+
)
|
|
56
|
+
parser.add_argument(
|
|
57
|
+
"--lark-cli-prefix",
|
|
58
|
+
action="append",
|
|
59
|
+
default=[],
|
|
60
|
+
help="repeatable argv prefix, e.g. --lark-cli-prefix npx --lark-cli-prefix -y ...",
|
|
61
|
+
)
|
|
62
|
+
parser.add_argument(
|
|
63
|
+
"--lark-cli-bin",
|
|
64
|
+
default="lark-cli",
|
|
65
|
+
help="(legacy) Path to the lark-cli executable; ignored if --lark-cli-prefix is provided",
|
|
66
|
+
)
|
|
67
|
+
return parser.parse_args()
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def build_env(config_dir: Optional[str]) -> Dict[str, str]:
|
|
71
|
+
env = os.environ.copy()
|
|
72
|
+
if config_dir:
|
|
73
|
+
env["LARKSUITE_CLI_CONFIG_DIR"] = config_dir
|
|
74
|
+
return env
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def run_cli(
|
|
78
|
+
cli_prefix: list[str],
|
|
79
|
+
env: Dict[str, str],
|
|
80
|
+
args: list[str],
|
|
81
|
+
check: bool = True,
|
|
82
|
+
cwd: Optional[Path] = None,
|
|
83
|
+
) -> Any:
|
|
84
|
+
proc = subprocess.run(
|
|
85
|
+
cli_prefix + args,
|
|
86
|
+
capture_output=True,
|
|
87
|
+
text=True,
|
|
88
|
+
env=env,
|
|
89
|
+
cwd=str(cwd) if cwd else None,
|
|
90
|
+
)
|
|
91
|
+
stdout = proc.stdout.strip()
|
|
92
|
+
stderr = proc.stderr.strip()
|
|
93
|
+
|
|
94
|
+
parsed: Any = None
|
|
95
|
+
if stdout:
|
|
96
|
+
try:
|
|
97
|
+
parsed = json.loads(stdout)
|
|
98
|
+
except json.JSONDecodeError as exc:
|
|
99
|
+
raise CLIError(
|
|
100
|
+
f"lark-cli returned non-JSON stdout: {stdout[:400]}",
|
|
101
|
+
code="invalid_cli_output",
|
|
102
|
+
detail={"stderr": stderr, "args": args},
|
|
103
|
+
) from exc
|
|
104
|
+
|
|
105
|
+
if check and proc.returncode != 0:
|
|
106
|
+
error_detail = parsed if isinstance(parsed, dict) else {"stderr": stderr}
|
|
107
|
+
raise CLIError(
|
|
108
|
+
f"lark-cli command failed: {' '.join(args)}",
|
|
109
|
+
code="lark_cli_failed",
|
|
110
|
+
detail=error_detail,
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
return parsed
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def unwrap_payload(payload: Any) -> Any:
|
|
117
|
+
if isinstance(payload, dict) and "ok" in payload:
|
|
118
|
+
if not payload.get("ok", False):
|
|
119
|
+
error = payload.get("error") or {}
|
|
120
|
+
raise CLIError(
|
|
121
|
+
error.get("message", "lark-cli returned an error envelope"),
|
|
122
|
+
code=error.get("type", "lark_error"),
|
|
123
|
+
detail=payload,
|
|
124
|
+
)
|
|
125
|
+
return payload.get("data")
|
|
126
|
+
return payload
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def ensure_auth(cli_prefix: list[str], env: Dict[str, str]) -> None:
|
|
130
|
+
run_cli(cli_prefix, env, ["auth", "check", "--scope", REQUIRED_SCOPES], check=True)
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def minute_info(
|
|
134
|
+
cli_prefix: list[str], env: Dict[str, str], minute_token: str
|
|
135
|
+
) -> Dict[str, Any]:
|
|
136
|
+
payload = run_cli(
|
|
137
|
+
cli_prefix,
|
|
138
|
+
env,
|
|
139
|
+
[
|
|
140
|
+
"api",
|
|
141
|
+
"GET",
|
|
142
|
+
f"/open-apis/minutes/v1/minutes/{minute_token}",
|
|
143
|
+
"--as",
|
|
144
|
+
"bot",
|
|
145
|
+
"--format",
|
|
146
|
+
"json",
|
|
147
|
+
],
|
|
148
|
+
)
|
|
149
|
+
data = unwrap_payload(payload)
|
|
150
|
+
if isinstance(data, dict) and "data" in data and isinstance(data["data"], dict):
|
|
151
|
+
data = data["data"]
|
|
152
|
+
if not isinstance(data, dict):
|
|
153
|
+
raise CLIError("unexpected minutes.get payload", code="invalid_minutes_payload")
|
|
154
|
+
minute = data.get("minute")
|
|
155
|
+
if not isinstance(minute, dict):
|
|
156
|
+
raise CLIError("minutes.get missing minute object", code="missing_minute")
|
|
157
|
+
return minute
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def notes_info(
|
|
161
|
+
cli_prefix: list[str], env: Dict[str, str], minute_token: str, output_dir: Path
|
|
162
|
+
) -> Dict[str, Any]:
|
|
163
|
+
output_dir = output_dir.resolve()
|
|
164
|
+
payload = run_cli(
|
|
165
|
+
cli_prefix,
|
|
166
|
+
env,
|
|
167
|
+
[
|
|
168
|
+
"vc",
|
|
169
|
+
"+notes",
|
|
170
|
+
"--minute-tokens",
|
|
171
|
+
minute_token,
|
|
172
|
+
"--output-dir",
|
|
173
|
+
"vc-notes-artifacts",
|
|
174
|
+
"--format",
|
|
175
|
+
"json",
|
|
176
|
+
],
|
|
177
|
+
cwd=output_dir,
|
|
178
|
+
)
|
|
179
|
+
data = unwrap_payload(payload)
|
|
180
|
+
if not isinstance(data, dict):
|
|
181
|
+
raise CLIError("unexpected vc +notes payload", code="invalid_notes_payload")
|
|
182
|
+
notes = data.get("notes")
|
|
183
|
+
if not isinstance(notes, list) or not notes:
|
|
184
|
+
raise CLIError("vc +notes returned no notes", code="missing_notes")
|
|
185
|
+
first = notes[0]
|
|
186
|
+
if not isinstance(first, dict):
|
|
187
|
+
raise CLIError("invalid note item shape", code="invalid_note_item")
|
|
188
|
+
if first.get("error"):
|
|
189
|
+
raise CLIError(str(first["error"]), code="note_query_failed", detail=first)
|
|
190
|
+
artifacts = first.get("artifacts")
|
|
191
|
+
if isinstance(artifacts, dict):
|
|
192
|
+
transcript_file = artifacts.get("transcript_file")
|
|
193
|
+
if isinstance(transcript_file, str) and transcript_file:
|
|
194
|
+
transcript_path = Path(transcript_file)
|
|
195
|
+
if not transcript_path.is_absolute():
|
|
196
|
+
artifacts["transcript_file"] = str(
|
|
197
|
+
(output_dir / transcript_path).resolve()
|
|
198
|
+
)
|
|
199
|
+
return first
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
def read_text(path: Optional[str]) -> str:
|
|
203
|
+
if not path:
|
|
204
|
+
return ""
|
|
205
|
+
p = Path(path)
|
|
206
|
+
if not p.exists():
|
|
207
|
+
return ""
|
|
208
|
+
return p.read_text(encoding="utf-8").strip()
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
def transcript_excerpt(text: str, limit: int = 2000) -> str:
|
|
212
|
+
if not text:
|
|
213
|
+
return ""
|
|
214
|
+
if len(text) <= limit:
|
|
215
|
+
return text
|
|
216
|
+
return text[:limit].rstrip() + "\n..."
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
def format_inline(value: Any) -> str:
|
|
220
|
+
if value in (None, "", [], {}):
|
|
221
|
+
return "暂无 AI 产物"
|
|
222
|
+
if isinstance(value, str):
|
|
223
|
+
return value.strip() or "暂无 AI 产物"
|
|
224
|
+
if isinstance(value, list):
|
|
225
|
+
items = [format_list_item(item) for item in value]
|
|
226
|
+
items = [item for item in items if item]
|
|
227
|
+
return "\n".join(items) if items else "暂无 AI 产物"
|
|
228
|
+
if isinstance(value, dict):
|
|
229
|
+
return "```json\n" + json.dumps(value, ensure_ascii=False, indent=2) + "\n```"
|
|
230
|
+
return str(value)
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
def format_list_item(item: Any) -> str:
|
|
234
|
+
if isinstance(item, str):
|
|
235
|
+
return f"- {item}"
|
|
236
|
+
if isinstance(item, dict):
|
|
237
|
+
preferred = [
|
|
238
|
+
item.get("title"),
|
|
239
|
+
item.get("summary"),
|
|
240
|
+
item.get("content"),
|
|
241
|
+
item.get("text"),
|
|
242
|
+
item.get("todo"),
|
|
243
|
+
item.get("speaker"),
|
|
244
|
+
]
|
|
245
|
+
text = next((str(v).strip() for v in preferred if v), "")
|
|
246
|
+
if text:
|
|
247
|
+
extras: list[str] = []
|
|
248
|
+
for key in ("assignee", "owner", "deadline", "timestamp", "time"):
|
|
249
|
+
if item.get(key):
|
|
250
|
+
extras.append(f"{key}: {item[key]}")
|
|
251
|
+
if extras:
|
|
252
|
+
return f"- {text} ({', '.join(extras)})"
|
|
253
|
+
return f"- {text}"
|
|
254
|
+
return "- " + json.dumps(item, ensure_ascii=False, sort_keys=True)
|
|
255
|
+
return f"- {item}"
|
|
256
|
+
|
|
257
|
+
|
|
258
|
+
def build_markdown(
|
|
259
|
+
title: str,
|
|
260
|
+
minute_token: str,
|
|
261
|
+
minutes_url: str,
|
|
262
|
+
note: Dict[str, Any],
|
|
263
|
+
transcript_text: str,
|
|
264
|
+
doc_url: str = "",
|
|
265
|
+
) -> str:
|
|
266
|
+
artifacts = note.get("artifacts") or {}
|
|
267
|
+
if not isinstance(artifacts, dict):
|
|
268
|
+
artifacts = {}
|
|
269
|
+
lines = [
|
|
270
|
+
f"# {title}",
|
|
271
|
+
"",
|
|
272
|
+
"## 会议信息",
|
|
273
|
+
"",
|
|
274
|
+
f"- 标题:{title}",
|
|
275
|
+
f"- minute_token:`{minute_token}`",
|
|
276
|
+
f"- 妙记链接:{minutes_url or '暂无'}",
|
|
277
|
+
f"- 纪要文档 Token:`{note.get('note_doc_token') or '暂无'}`",
|
|
278
|
+
f"- 逐字稿文档 Token:`{note.get('verbatim_doc_token') or '暂无'}`",
|
|
279
|
+
f"- 逐字稿文件:{artifacts.get('transcript_file') or '暂无'}",
|
|
280
|
+
"",
|
|
281
|
+
"## 摘要",
|
|
282
|
+
"",
|
|
283
|
+
format_inline(artifacts.get("summary")),
|
|
284
|
+
"",
|
|
285
|
+
"## 待办",
|
|
286
|
+
"",
|
|
287
|
+
format_inline(artifacts.get("todos")),
|
|
288
|
+
"",
|
|
289
|
+
"## 章节",
|
|
290
|
+
"",
|
|
291
|
+
format_inline(artifacts.get("chapters")),
|
|
292
|
+
"",
|
|
293
|
+
"## 逐字稿",
|
|
294
|
+
"",
|
|
295
|
+
transcript_text or "暂无 AI 产物",
|
|
296
|
+
"",
|
|
297
|
+
"## 原始飞书产物",
|
|
298
|
+
"",
|
|
299
|
+
f"- 妙记链接:{minutes_url or '暂无'}",
|
|
300
|
+
f"- 飞书文档链接:{doc_url or '待创建'}",
|
|
301
|
+
f"- 纪要文档 Token:`{note.get('note_doc_token') or '暂无'}`",
|
|
302
|
+
f"- 逐字稿文档 Token:`{note.get('verbatim_doc_token') or '暂无'}`",
|
|
303
|
+
]
|
|
304
|
+
return "\n".join(lines).strip() + "\n"
|
|
305
|
+
|
|
306
|
+
|
|
307
|
+
def create_doc(
|
|
308
|
+
cli_prefix: list[str],
|
|
309
|
+
env: Dict[str, str],
|
|
310
|
+
title: str,
|
|
311
|
+
markdown: str,
|
|
312
|
+
folder_token: Optional[str],
|
|
313
|
+
) -> Dict[str, Any]:
|
|
314
|
+
args = [
|
|
315
|
+
"docs",
|
|
316
|
+
"+create",
|
|
317
|
+
"--as",
|
|
318
|
+
"bot",
|
|
319
|
+
"--title",
|
|
320
|
+
title,
|
|
321
|
+
"--markdown",
|
|
322
|
+
markdown,
|
|
323
|
+
]
|
|
324
|
+
if folder_token:
|
|
325
|
+
args.extend(["--folder-token", folder_token])
|
|
326
|
+
payload = run_cli(cli_prefix, env, args)
|
|
327
|
+
data = unwrap_payload(payload)
|
|
328
|
+
if not isinstance(data, dict):
|
|
329
|
+
raise CLIError("unexpected docs +create payload", code="invalid_doc_payload")
|
|
330
|
+
return data
|
|
331
|
+
|
|
332
|
+
|
|
333
|
+
def partial_result(
|
|
334
|
+
job_id: str,
|
|
335
|
+
title: str,
|
|
336
|
+
minute_token: str,
|
|
337
|
+
minutes_url: str,
|
|
338
|
+
markdown: str,
|
|
339
|
+
transcript_file: str,
|
|
340
|
+
transcript_text: str,
|
|
341
|
+
error: CLIError,
|
|
342
|
+
) -> Dict[str, Any]:
|
|
343
|
+
return {
|
|
344
|
+
"job_id": job_id,
|
|
345
|
+
"status": "partial",
|
|
346
|
+
"title": title,
|
|
347
|
+
"minute_token": minute_token,
|
|
348
|
+
"minutes_url": minutes_url,
|
|
349
|
+
"markdown": markdown,
|
|
350
|
+
"feishu_doc_url": "",
|
|
351
|
+
"feishu_doc_token": "",
|
|
352
|
+
"transcript_file": transcript_file,
|
|
353
|
+
"transcript_excerpt": transcript_excerpt(transcript_text),
|
|
354
|
+
"error_code": error.code,
|
|
355
|
+
"error_message": str(error),
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
|
|
359
|
+
def failed_result(job_id: str, minute_token: str, error: CLIError) -> Dict[str, Any]:
|
|
360
|
+
return {
|
|
361
|
+
"job_id": job_id,
|
|
362
|
+
"status": "failed",
|
|
363
|
+
"title": "",
|
|
364
|
+
"minute_token": minute_token,
|
|
365
|
+
"minutes_url": "",
|
|
366
|
+
"markdown": "",
|
|
367
|
+
"feishu_doc_url": "",
|
|
368
|
+
"feishu_doc_token": "",
|
|
369
|
+
"transcript_file": "",
|
|
370
|
+
"transcript_excerpt": "",
|
|
371
|
+
"error_code": error.code,
|
|
372
|
+
"error_message": str(error),
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
|
|
376
|
+
def main() -> int:
|
|
377
|
+
args = parse_args()
|
|
378
|
+
env = build_env(args.config_dir)
|
|
379
|
+
from _resolve_lark_cli import as_prefix
|
|
380
|
+
|
|
381
|
+
cli_prefix = as_prefix(args.lark_cli_bin, args.lark_cli_prefix)
|
|
382
|
+
base_output_dir = Path(args.output_dir).resolve()
|
|
383
|
+
job_dir = base_output_dir / args.job_id
|
|
384
|
+
job_dir.mkdir(parents=True, exist_ok=True)
|
|
385
|
+
|
|
386
|
+
try:
|
|
387
|
+
ensure_auth(cli_prefix, env)
|
|
388
|
+
minute = minute_info(cli_prefix, env, args.minute_token)
|
|
389
|
+
note = notes_info(cli_prefix, env, args.minute_token, job_dir)
|
|
390
|
+
except CLIError as exc:
|
|
391
|
+
json.dump(
|
|
392
|
+
failed_result(args.job_id, args.minute_token, exc),
|
|
393
|
+
sys.stdout,
|
|
394
|
+
ensure_ascii=False,
|
|
395
|
+
indent=2,
|
|
396
|
+
)
|
|
397
|
+
sys.stdout.write("\n")
|
|
398
|
+
return 0
|
|
399
|
+
|
|
400
|
+
title = args.title or str(minute.get("title") or "会议纪要")
|
|
401
|
+
minutes_url = str(minute.get("url") or "")
|
|
402
|
+
artifacts = note.get("artifacts") or {}
|
|
403
|
+
transcript_file = ""
|
|
404
|
+
if isinstance(artifacts, dict):
|
|
405
|
+
transcript_file = str(artifacts.get("transcript_file") or "")
|
|
406
|
+
transcript_text = read_text(transcript_file)
|
|
407
|
+
|
|
408
|
+
markdown = build_markdown(
|
|
409
|
+
title=title,
|
|
410
|
+
minute_token=args.minute_token,
|
|
411
|
+
minutes_url=minutes_url,
|
|
412
|
+
note=note,
|
|
413
|
+
transcript_text=transcript_text,
|
|
414
|
+
)
|
|
415
|
+
|
|
416
|
+
try:
|
|
417
|
+
doc = create_doc(
|
|
418
|
+
cli_prefix,
|
|
419
|
+
env,
|
|
420
|
+
title,
|
|
421
|
+
markdown,
|
|
422
|
+
args.folder_token,
|
|
423
|
+
)
|
|
424
|
+
except CLIError as exc:
|
|
425
|
+
json.dump(
|
|
426
|
+
partial_result(
|
|
427
|
+
job_id=args.job_id,
|
|
428
|
+
title=title,
|
|
429
|
+
minute_token=args.minute_token,
|
|
430
|
+
minutes_url=minutes_url,
|
|
431
|
+
markdown=markdown,
|
|
432
|
+
transcript_file=transcript_file,
|
|
433
|
+
transcript_text=transcript_text,
|
|
434
|
+
error=exc,
|
|
435
|
+
),
|
|
436
|
+
sys.stdout,
|
|
437
|
+
ensure_ascii=False,
|
|
438
|
+
indent=2,
|
|
439
|
+
)
|
|
440
|
+
sys.stdout.write("\n")
|
|
441
|
+
return 0
|
|
442
|
+
|
|
443
|
+
doc_url = str(doc.get("doc_url") or "")
|
|
444
|
+
doc_token = str(doc.get("doc_id") or "")
|
|
445
|
+
final_markdown = build_markdown(
|
|
446
|
+
title=title,
|
|
447
|
+
minute_token=args.minute_token,
|
|
448
|
+
minutes_url=minutes_url,
|
|
449
|
+
note=note,
|
|
450
|
+
transcript_text=transcript_text,
|
|
451
|
+
doc_url=doc_url,
|
|
452
|
+
)
|
|
453
|
+
result = {
|
|
454
|
+
"job_id": args.job_id,
|
|
455
|
+
"status": "succeeded",
|
|
456
|
+
"title": title,
|
|
457
|
+
"minute_token": args.minute_token,
|
|
458
|
+
"minutes_url": minutes_url,
|
|
459
|
+
"markdown": final_markdown,
|
|
460
|
+
"feishu_doc_url": doc_url,
|
|
461
|
+
"feishu_doc_token": doc_token,
|
|
462
|
+
"transcript_file": transcript_file,
|
|
463
|
+
"transcript_excerpt": transcript_excerpt(transcript_text),
|
|
464
|
+
"error_code": "",
|
|
465
|
+
"error_message": "",
|
|
466
|
+
}
|
|
467
|
+
json.dump(result, sys.stdout, ensure_ascii=False, indent=2)
|
|
468
|
+
sys.stdout.write("\n")
|
|
469
|
+
return 0
|
|
470
|
+
|
|
471
|
+
|
|
472
|
+
if __name__ == "__main__":
|
|
473
|
+
raise SystemExit(main())
|