Files
longbridge__developers/scripts/audit_api_docs.py
Jason Lee 0add508ae2 feat: add Skill page with interactive demo (#376)
## Summary

- **New `/skill` page** with full trilingual support (en / zh-CN /
zh-HK)
- **Interactive chat demo**: 4 scenarios (Live Quote, Portfolio,
Subscription, Earnings) × 5 AI clients (OpenClaw, ChatGPT, Claude,
Claude Code, Codex) with typewriter animation
- **Rich response rendering**: tables with Shadcn-style borders, mini
SVG sparkline charts, syntax-highlighted code blocks
- **Scenario cards section** below the demo describing use cases
- **Nav update**: Skill link added to all three locale nav configs

## Bug Fixes

- **Syntax highlighter**: replaced chained-regex approach with a
single-pass tokenizer. The old code ran number → keyword → string
regexes sequentially on the same string — the keyword regex matched
`class` inside generated `<span class="hl-n">` attributes, and the
string regex matched `"hl-n"` as a string literal, corrupting the HTML
structure entirely
- **v-html routing**: replaced `currentMessages[2]?.rich` template
condition with an explicit `isRichResponse` ref set in `runAnimation()`
to prevent incorrect branch selection when switching client tabs

## Test plan

- [ ] Visit `/skill`, `/zh-CN/skill`, `/zh-HK/skill` — page loads in all
locales
- [ ] Click through all 4 scenario tabs — animation plays correctly for
each
- [ ] Click through all 5 client tabs — correct message shown, Claude
Code tab shows syntax-highlighted code (not raw HTML)
- [ ] Verify sparkline charts render in the Live Quote / OpenClaw
scenario
- [ ] Verify table borders render correctly (outer border + row
dividers, no missing bottom border)
- [ ] Verify gain/loss values show green/red colors in tables

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-23 20:03:14 +08:00

118 lines
3.7 KiB
Python

#!/usr/bin/env python3
import re
import json
import requests
from pathlib import Path
from collections import defaultdict
ROOT = Path(__file__).resolve().parents[1]
DOCS = ROOT / 'docs'
LOCALES = ['en', 'zh-CN', 'zh-HK']
METHOD_RE = re.compile(r'HTTP Method</td><td>\s*([^<\n]+)', re.I)
URL_RE = re.compile(r'HTTP URL</td><td>\s*([^<\n]+)', re.I)
TITLE_RE = re.compile(r'^title:\s*(.+)$', re.M)
def collect(locale):
rows = []
for p in (DOCS / locale / 'docs').rglob('*.md'):
txt = p.read_text(encoding='utf-8')
m = METHOD_RE.search(txt)
u = URL_RE.search(txt)
if not (m and u):
continue
title = (TITLE_RE.search(txt).group(1).strip().strip('"\'') if TITLE_RE.search(txt) else p.stem)
rows.append({
'locale': locale,
'method': m.group(1).strip().upper(),
'path': u.group(1).strip(),
'title': title,
'file': str(p.relative_to(ROOT)).replace('\\', '/'),
})
return rows
def check_endpoint(base, method, path):
url = base.rstrip('/') + path
try:
r = requests.request(method, url, timeout=5)
return {'status': r.status_code, 'code': None, 'message': None}
except Exception as e:
return {'status': None, 'error': str(e)}
def main():
all_rows = []
for lc in LOCALES:
all_rows.extend(collect(lc))
by_key = defaultdict(list)
for r in all_rows:
by_key[(r['method'], r['path'])].append(r)
keys = sorted(by_key.keys())
# locale parity
missing = []
for k in keys:
exists = {x['locale'] for x in by_key[k]}
for lc in LOCALES:
if lc not in exists:
missing.append({'method': k[0], 'path': k[1], 'missing_locale': lc})
# runtime existence check (non-404 means route exists)
runtime = []
for method, path in keys:
prod = check_endpoint('https://openapi.longbridge.com', method, path)
test = check_endpoint('https://openapi.longbridge.com', method, path)
runtime.append({
'method': method,
'path': path,
'prod_status': prod.get('status'),
'test_status': test.get('status')
})
out = {
'total_unique_http_apis': len(keys),
'apis': runtime,
'locale_missing': missing,
}
out_dir = ROOT / 'openapi' / 'audit'
out_dir.mkdir(parents=True, exist_ok=True)
(out_dir / 'api-audit.json').write_text(json.dumps(out, indent=2, ensure_ascii=False) + '\n', encoding='utf-8')
md = []
md.append('# API Docs Audit')
md.append('')
md.append(f'- Total unique HTTP APIs in docs: **{len(keys)}**')
md.append(f'- Locale parity gaps: **{len(missing)}**')
md.append('')
md.append('## Runtime route check (non-404 = endpoint exists)')
md.append('')
md.append('| Method | Path | openapi.longbridge.com | openapi.longbridge.com |')
md.append('| --- | --- | --- | --- |')
for r in runtime:
md.append(f"| `{r['method']}` | `{r['path']}` | `{r['prod_status']}` | `{r['test_status']}` |")
md.append('')
if missing:
md.append('## Locale parity gaps')
md.append('')
md.append('| Method | Path | Missing Locale |')
md.append('| --- | --- | --- |')
for m in missing:
md.append(f"| `{m['method']}` | `{m['path']}` | `{m['missing_locale']}` |")
else:
md.append('## Locale parity gaps')
md.append('')
md.append('No locale gaps found for currently documented HTTP APIs.')
(out_dir / 'api-audit.md').write_text('\n'.join(md) + '\n', encoding='utf-8')
print('wrote', out_dir / 'api-audit.json')
print('wrote', out_dir / 'api-audit.md')
if __name__ == '__main__':
main()