Commit Graph
51 Commits
Author SHA1 Message Date
zhifu gaoandLauraGPT 2b3f2eaa37 feat(asr): add local FunASR provider (#1044)
Signed-off-by: LauraGPT <199134975+LauraGPT@users.noreply.github.com>
Co-authored-by: LauraGPT <199134975+LauraGPT@users.noreply.github.com>
2026-08-05 03:03:56 -04:00
littlebullGitandwyuc 3d85525665 [codex] Add Amazon Bedrock LLM provider support (#538)
* Enable AWS-hosted text models without exposing ambient credentials

Rebase the Bedrock provider onto current main and close the review-requested security, credential lifecycle, usage attribution, configuration, and metadata gaps.

Constraint: Bedrock may use ambient AWS credentials only when explicitly enabled by the server operator
Rejected: Trust client-supplied provider types | permits built-in keyless IDs to reach Bedrock credentials
Confidence: high
Scope-risk: moderate
Directive: Keep provider ID/type validation at both request resolution and model construction boundaries
Tested: targeted Bedrock/resolver/config/usage tests; TypeScript; ESLint; i18n alignment; production build
Not-tested: Full suite has 27 failures in unchanged quiz/runtime and runtime/chat-storage tests

* Give Bedrock a recognizable provider identity

Use the official AWS architecture service icon so Bedrock no longer falls back to the generic provider cube in settings and model selection.

Constraint: Preserve the AWS-provided artwork without redesigning the service mark
Confidence: high
Scope-risk: narrow
Tested: SVG XML validation; provider unit test; TypeScript; ESLint; production build

---------

Co-authored-by: wyuc <wang-yc24@mails.tsinghua.edu.cn>
2026-08-03 23:10:50 -04:00
wyucandCodex e39c64cd9e feat(persistence): one-command server-backed stack (compose profile + embedded API + docs) (#982)
* feat(persistence): one-command server-backed stack — embedded API + compose postgres profile

docker compose --profile server-persistence up --build runs exactly two
containers: the app (persistence HTTP server embedded at /api/persistence,
enabled only when DATABASE_URL is set — unset keeps today's browser-only
behavior byte-for-byte) and PostgreSQL 16. Client bootstrap configures both
storage seams against the same-origin route when NEXT_PUBLIC_PERSISTENCE=1,
riding the existing lazy migrations so prior browser data reaches the
server per-course on first open. Dev auth (bearer token + client-asserted
learner header) lives in one overridable module and is loudly documented
as development-grade. Verified end-to-end against the real stack:
document PUT/GET and runtime session create land in Postgres; bad tokens 401.

Co-authored-by: Codex <codex@openai.com>

* fix(persistence): review round — structural bootstrap ordering, retryable init, honest failure surfaces

The bootstrap side-effect import moves into the two storage seam entry
modules, so any module graph that can resolve a store necessarily
evaluates it first — no client-entry ordering luck, and the two configure
calls are all-or-nothing. A failed server init clears its cached promise
(next request retries instead of permanent 500s). listStages rethrows
backend failures and the dashboard surfaces a persistence-unavailable
error instead of rendering an empty list that reads as data loss. Docs
state the build-time nature of NEXT_PUBLIC_PERSISTENCE, the real token
boundary (any visitor can extract it and impersonate any learner —
localhost/trusted-network only), and the correct postgres password
rotation. Dev-token compare is timing-safe; the handler singleton lives
on globalThis so HMR cannot leak pools.

Co-authored-by: Codex <codex@openai.com>

* fix(persistence): review response — no-confidentiality wording + adapter round-trip coverage

The token docs now state plainly that the NEXT_PUBLIC token is compiled
into the public bundle and provides no confidentiality or user isolation
(its only role is keeping unrelated scanners off a trusted network). The
Fetch/Node adapter — the route's most bug-prone code — gains a round-trip
integration test: PUT body streaming, 201 with multi-value headers, 204
empty body, and encoded path segments reaching the handler un-decoded.

---------

Co-authored-by: Codex <codex@openai.com>
2026-07-23 16:28:03 +08:00
wyucandClaude Fable 5 04acb17c1a release: v0.3.1
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-21 02:25:32 -04:00
杨慎andClaude Opus 4.8 84b1907255 feat(video-export): service-backed MP4 render + in-app one-click export (#866) (#937)
* feat(video-export): service-backed MP4 render + in-app one-click export (#866)

Adds the last mile of classroom video export: turning the self-contained
Hyperframes project ZIP (#865) into an MP4 via an isolated render service,
one-click in-app.

- render-service/: standalone Node 22 + Chromium + FFmpeg container wrapping
  @hyperframes/producer's library API. Async job model (POST /render -> 202
  jobId, GET poll, GET download, DELETE cancel). Swappable JobStore /
  ArtifactStore seams (in-memory + local-disk now; Redis/S3 + presigned-302
  download later) so it scales horizontally without changing the HTTP contract.
  Concurrency + per-user guards are config knobs.
- App integration: thin Next proxy routes under app/api/export-video/* (forward
  only, no rendering) + capability probe. use-render-video.ts uploads the ZIP,
  polls via runPolledTask, downloads the MP4; shared buildExportZip prefix with
  the existing ZIP path. Export menu gains resolution/fps/quality selectors and
  a progress bar; degrades to ZIP download when RENDER_SERVICE_URL is unset.
- docker-compose: render-service under an opt-in "video-export" profile.
- Entry is main.ts (not server.ts): the producer auto-starts its own server on
  :9847 when the process entry path ends with /src/server.ts.

Verified end-to-end in the container: rendered a real 640s (10.7 min) classroom
ZIP to a valid H.264 720p + AAC MP4 (duration matches source) in ~9.6 min
(~0.9x realtime, 4-worker frame capture). Degrade path, queued-cancel + cleanup,
and per-user 429 guard all exercised. pnpm check / lint / tsc / i18n pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(video-export): global render progress store, percent+ETA UI, ring on export button (#866)

Addresses two UX issues found while driving the in-app MP4 export:

1. Progress display was raw and unfriendly (showed producer's English stage
   strings like "Capturing frame 5130/19220") and had no time estimate. Now the
   menu shows only "<percent>% · about <remaining> left". ETA is computed from a
   recent-speed estimate (percent-per-ms over the last sample), EMA-smoothed —
   which tracks the render's non-uniform pace (prep -> frame capture with a
   4->1 worker drop -> encode) far better than a whole-run average, and never
   shows a stale/rising ETA.
2. Switching scenes mid-render unmounted the export menu and lost the progress
   (and reset the local "already rendering" ref, allowing a duplicate submit).
   The whole render lifecycle now lives in a global store
   (lib/store/video-render.ts), so progress survives menu close / scene switch
   and duplicate submits are guarded by status. A persistent CircularProgress
   ring on the export button shows live progress whether or not the menu is open.

Also fixes the progress scale: the producer reports progress as 0..100, but our
HTTP contract (and success path) is 0..1 — the service now normalizes it, so the
client no longer showed "2000%".

- lib/store/video-render.ts: new global store owning submit->poll->download,
  recent-speed ETA, duplicate-submit guard.
- lib/video-export-app/use-render-video.ts: thin facade over the store.
- components/ui/circular-progress.tsx: lightweight SVG progress ring.
- components/stage/{header-controls,video-export-menu}.tsx: ring on the export
  button; menu shows percent + ETA, subscribes to the store.
- render-service/src/render-manager.ts: normalize producer progress 0..100 -> 0..1.
- i18n: percent/ETA strings across all 8 locales (drops the stage-based string).
- render-service/package-lock.json: complete integrity hashes (reproducible npm ci).

Verified: ETA logic checked against the real segmented render curve (worker drop
raises ETA, encode speedup drives it to ~0); progress scale fix confirmed live
against the container (0.2 -> 20%). tsc / lint / prettier / i18n pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(video-export): persist render options in the store, not the menu component

Selecting 720p/24fps/draft, switching scenes, and reopening the export menu
showed the defaults again (1080p/30/standard). The selections lived in the
VideoExportMenu component's local state, which reset when the menu unmounted on
a scene switch — the running render still used the chosen options, but the UI
misrepresented them.

Move resolution/fps/quality into the global video-render store (with a
setOptions action). The menu now reads/writes the store, so selections survive
menu close / scene switch, and while a render runs the selectors reflect the
options that render is actually using. startRender() reads options from the
store instead of taking them as an argument.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(video-export): deployment correctness + resource/isolation controls (PR #937 review)

Addresses the blocking findings from wyuc's review. Output fidelity was fine;
these harden deployment and production resource/isolation boundaries.

#1 Compose advertised MP4 but couldn't render in prod:
- Capability now probes the service's /health (checkRenderServiceHealth), so a
  configured-but-absent service reports disabled and the UI degrades to ZIP
  instead of 502-ing.
- RENDER_SERVICE_URL is operator-supplied trusted config, so the proxy no longer
  runs it through the SSRF guard — the one-command `docker compose --profile
  video-export up` now works without globally weakening SSRF via
  ALLOW_LOCAL_NETWORKS. resolveRenderServiceUrl() is now synchronous.
- Client degrades to ZIP on any failed submit (not only 501).

#2 Unbounded upload/queue (ZIP-bomb / DoS):
- unzip.ts bounds the archive via fflate's filter BEFORE decompression: entry
  count, per-entry and total expanded size, and compression ratio.
- Proxy rejects oversized uploads (413) by Content-Length before forwarding.
- RenderManager enforces a global queue-depth cap (RENDER_MAX_QUEUE).
- All limits are env-tunable knobs in config.ts.

#3 Per-user guard was ineffective + admission ran after extraction:
- Identity is derived server-side (client IP) and forwarded as x-openmaic-client;
  the service ignores any client-supplied userId, and the proxy strips it.
- Admission is split into reserve()/submit()/release(): the slot is reserved
  BEFORE extraction, so a rejected caller never triggers a decompression.

Additional risks:
- Per-job wall-clock watchdog (RENDER_JOB_DEADLINE_MS) aborts + fails a hung
  render so it can't hold a slot/scratch forever.
- Download proxy bounds only the time-to-headers, not the body stream, so large
  MP4s over slow links no longer truncate.
- Client cancels the server job (DELETE) when a started render fails/times out.
- Compose puts render-service on an internal:true network (no host/internet
  route), sandboxing the Chromium that runs the uploaded HTML; the export ZIP is
  self-contained so no outbound is needed. README documents the standalone caveat.

Not closing #866: the smoke/golden-render CI acceptance criterion remains a
follow-up (see PR description).

Verified in-container: legal render 202; ZIP-bomb (entry-count + compression-
ratio) rejected 400 before any decompression; per-identity guard 429 with a
spoofed multipart userId ignored; reserve-before-extract leaves no scratch dir
on rejection; watchdog aborts an overrunning job and frees the slot. tsc / lint /
prettier / i18n pass; render-service tsc passes.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(video-export): real audio durations + burned-in subtitles (PR #937 review)

Two export-fidelity issues found in wyuc's deeper E2E:

A. Narration was scheduled from estimated durations, cutting audio off mid-
   sentence and advancing the timeline early. The scheduler trusted
   AudioFileRecord.duration (recorded only since #861), so the many existing
   classrooms without it fell back to text-length estimates — measured 4.35s
   average / 10.23s max underestimate across 47 clips. timeline-deps now probes
   the real duration from each narration blob via an off-document <audio>
   (symmetric to the existing video probe), preferring it over the stored
   duration, then the estimate only when no audio asset exists. Everything
   downstream (narration starts, scene/total duration, subtitle cues) re-derives
   from the corrected value in the pure compiler — no compiler change needed.

B. The final MP4 had no subtitles (only H.264+AAC), and the ZIP's SRT/VTT used
   the same estimated boundaries. The emitter now renders a burned-in subtitle
   overlay: one caption box + a hidden div per cue, revealed/hidden by the paused
   GSAP timeline at each cue's start/end (corrected timings from A), so Chromium's
   frame capture bakes them in. The producer has no subtitle track of its own, so
   burn-in is the v1 approach.

Verified: emitter unit tests + snapshot updated (subtitle overlay + toggle
statements, escaped text, hidden-by-default); 82 video-export tests pass incl.
the determinism red-line proxy. Rendered a synthetic subtitle project through the
container and confirmed by pixel analysis that captions appear only within their
cue window (2429 near-white px in the caption band at t=1.5s vs 0 at t=0.05s).
tsc / lint / prettier / i18n pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(video-export): subtitle layout + upload/admission hardening (PR #937 review)

Address the three P1 blockers plus actionable P2s from the 6a585c29 review.

P1:
- emit-hyperframes: stack every subtitle cue in one grid cell and toggle
  display:none/inline-block, so inactive cues leave the flow instead of
  pushing the active cue up into the slide title. Adds a multi-cue
  regression test the single-cue snapshot couldn't catch.
- render route + service: cap the upload by actual bytes (capBodyStream),
  not the spoofable Content-Length; the app now streams the multipart body
  through instead of buffering it via formData(). maxUploadBytes is now read.
- render-service: move makeProjectDir() inside the release()-guarded block
  so an ENOENT/ENOSPC no longer permanently leaks the admission slot; mkdir
  the scratch root at startup for the standalone path.

P2:
- config: allow RENDER_MAX_JOBS_PER_USER=0 to disable the per-identity guard.
- timeline-deps: per-probe timeout + bounded concurrency so a stuck audio
  blob can't wedge export in "compiling" forever.
- render route: only trust x-forwarded-for/x-real-ip under
  TRUST_PROXY_HEADERS=true; otherwise all callers share one "direct" bucket.
- render-service: add vitest tests (unzip limits/traversal, reservation
  arithmetic, body cap, config zero-disable) and a dedicated CI job.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(video-export): sync render-service lockfile so `npm ci` passes in CI

The vitest devDependency's transitive esbuild@0.28.1 (and its platform
optionals) were missing from package-lock.json, so the new CI job's
`npm ci` failed with EUSAGE. Regenerated the lockfile from a clean install.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(video-export): dedupe esbuild so render-service `npm ci` installs on linux

@hyperframes/core pins esbuild@0.25.12 exactly, hoisting it to the top and
forcing vite@8 (via vitest) to keep a nested esbuild@0.28.1 copy. npm fails to
flag that nested copy's platform-specific optionals as optional, so `npm ci`
tried to install @esbuild/aix-ppc64 on linux and died with EBADPLATFORM.

Add an `esbuild: 0.28.1` override so a single copy is shared (satisfies tsx
~0.28 and vite ^0.27||^0.28); esbuild is build-time only, so pinning the
producer's bundled build tool is runtime-inert.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(video-export): resource/isolation hardening (PR #937 round-2 review)

Address the round-2 P1/P2/P3 findings (P1#1 lockfile EBADPLATFORM was already
fixed by the earlier esbuild-dedupe commit; CI Render Service job is green).

P1:
- Admission before buffering (#2): the render service now reserve()s the slot
  from the header identity BEFORE parsing/buffering the multipart, so concurrent
  near-cap uploads are bounded by the queue depth, not just each body by the cap.
- Chromium egress lockdown (#3): the producer exposes no browser-arg hook and the
  render shares the internal network with the app, so a container entrypoint
  installs an iptables egress lockdown (drop all outbound except loopback +
  established replies) then drops privileges. Needs CAP_NET_ADMIN (added in
  compose); graceful warn-and-continue if unavailable. The self-contained ZIP
  needs no outbound.
- Default one-render bottleneck (#4): with no trusted proxy every caller is
  "direct", so RENDER_MAX_JOBS_PER_USER=1 throttled the whole deployment. Default
  compose now sets it to 0 and relies on concurrency + global queue caps.
- Non-blocking bounded extraction (#5): unzipSync -> fflate async unzip (worker,
  off the event loop), keeping the pre-decompression filter; default expanded
  ceiling 1GB -> 512MB; a semaphore caps concurrent extractions; compose adds a
  container mem_limit.

P2:
- Raise the app submit timeout/maxDuration (300MB upload can't finish in 60s).
- video-render store: only degrade to ZIP when the service is genuinely
  unavailable (501/unreachable); surface real 429/413/5xx instead of an
  unsolicited download.
- useExportVideo dedupe guard moved to module scope so it survives the menu
  unmounting (no second concurrent ZIP pipeline).
- .env.example: RENDER_SERVICE_URL bypasses SSRF; drop the ALLOW_LOCAL_NETWORKS note.

P3:
- Deadline overruns are marked failed (not cancelled).
- submit() decrements the identity slot if jobs.create throws (no leak).
- CI sets PUPPETEER_SKIP_DOWNLOAD; unzip tests use tiny fixtures + low env limits.

Tests: render-service now 22 tests (unzip limits/traversal, admission incl.
create-leak, body cap, semaphore, config); app video-export suite unchanged.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(video-export): buffer under the extraction permit + fail-closed egress (PR #937 round-3)

Address the two remaining round-3 P1 boundary blockers.

P1#1 — buffering was outside the gate: `bounded.formData()` materialized the
whole uploaded file into memory BEFORE `extractionGate.run()`, so up to
RENDER_MAX_QUEUE (20) admitted bodies could each buffer ~300MB (≈6GB vs the 4g
mem_limit) before the 2-permit gate. Move the entire RAM-heavy section —
formData buffering, file read, and unzip — INSIDE the permit; the queue
reservation still runs first (a rejected caller consumes nothing). Requests
beyond the permit wait with their body unconsumed (socket backpressure), so at
most maxConcurrentExtractions bodies are buffered at once. Refactored main.ts
into a testable `createApp(deps)` factory and added an integration test proving
peak concurrency in the buffering+extraction section never exceeds the permits.

P1#2 — egress lockdown failed open: the entrypoint warned and started normally
if iptables setup failed, so /health stayed green while Chromium could reach the
app. With RENDER_EGRESS_LOCKDOWN=true (default) it now FAILS CLOSED — exits
non-zero if not root, iptables is missing, or the rules don't apply. Operators
accepting an unisolated setup opt out with RENDER_EGRESS_LOCKDOWN=false. Added
scripts/egress-smoke.sh to assert the boundary (lockdown active, loopback works,
new outbound blocked).

Verified: image builds; container boots as `render` with lockdown active and
serves /health; fail-closed exits 1 without CAP_NET_ADMIN; egress smoke passes
(outbound blocked); 23/23 render-service tests + tsc; app tsc + root prettier clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-20 11:28:34 +08:00
3b63710376 feat(ai): add Azure OpenAI provider (#916)
* feat(ai): add Azure OpenAI provider

Add deployment-based Azure OpenAI configuration for both client and server-managed setups, normalize Azure portal endpoints, and expose the provider in settings.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: b0d3a94f-089a-470e-9987-608f1506ca6f

* style: format Azure provider type

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: b0d3a94f-089a-470e-9987-608f1506ca6f

---------

Co-authored-by: MarshellOnMoon <121332425+MarshellOnMoon@users.noreply.github.com>
2026-07-13 23:12:23 +08:00
wyuc da0b394b81 release: v0.3.0 2026-06-28 22:34:18 -04:00
wyucandClaude Opus 4.8 beac93ab3d chore: relicense from AGPL-3.0 to MIT
Switch the OpenMAIC root and the in-house @maic/* SDK packages
(@maic/dsl, @maic/importer, @maic/renderer) from AGPL-3.0 to the MIT
License. Updates LICENSE files, package.json license fields, README
badges and license sections (EN/ZH), CONTRIBUTING, and renderer FONTS
note.

Third-party vendored packages are left untouched: packages/mathml2omml
remains LGPL-3.0, packages/pptxgenjs remains MIT.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-17 04:00:00 -04:00
1780eaea73 docs: correct stale i18n location and supported-language list (#640)
- README / README-zh advertised 4 UI languages and labelled the i18n
  directory "(zh-CN, en-US)", but the app ships 7 locales:
  zh-CN, zh-TW, en-US, ja-JP, ru-RU, ar-SA, pt-BR.
- CONTRIBUTING's project structure listed a top-level `messages/`
  directory for translations that does not exist; translations live
  in `lib/i18n/locales/`.

Co-authored-by: ly-wang19 <ly-wang19@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: wyuc <wang-yc24@mails.tsinghua.edu.cn>
2026-06-07 10:18:08 -04:00
wyuc 4da3a3a8a0 docs: add v0.2.2 to README news 2026-06-02 07:20:03 -04:00
86c8e0c543 feat: inline external assets at export for offline classrooms (#596) (#613)
* feat(export): add external-asset reference scanner for HTML inlining

* feat(export): add cached asset fetcher and data-uri encoder

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat(export): recursively inline url() refs inside stylesheets

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat(export): orchestrate inlining of links, scripts, images, and inline css

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* refactor(export): drop dead escapeAttr, harden importmap skip in scanner

* feat(export): inline importmap modules incl three/addons graph closure

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* refactor(export): extract shared module to break inline-assets import cycle

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat(export): inline interactive-scene assets into .maic.zip at export

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat(export): inline interactive assets in Resource Pack export too

* docs: document offline export asset inlining

* style: prettier formatting

* fix(test): resolve tsc type errors in export-classroom-inline test

* fix(export): keep importmap prefix fallback on partial failure, chunk base64, preserve media attr, log pptx inline failures

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* perf(export): parallelize asset fetching, share fetcher across scenes, promise-cache

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(export): retry transient asset fetches, bound concurrency, warn user on partial inline

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* perf(export): inline only woff2 fonts, drop woff/ttf siblings to halve size

Within @font-face blocks that have a woff2 url(), rewrite sibling woff/ttf/otf/eot
refs to url(about:invalid) instead of inlining them. Falls back to full inline for
ttf-only blocks. Drops katex output from ~1.79MB to ~0.71MB (~3x fewer font fetches).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* i18n(export): translate partial-inline warning and surface failed asset hosts

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat(proxy-media): follow redirects with per-hop SSRF revalidation (#398)

Replace blanket redirect rejection with a bounded 5-hop follow loop that
re-validates every Location target through validateUrlForSSRF before
fetching, preventing redirect-to-internal SSRF while allowing CDN
redirects (e.g. cdn.tailwindcss.com 302→/3.4.17).

* feat(export): route asset inlining through proxy-media to bypass CORS

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(export): keep importmap prefix fallback, forward proxy 4xx, cap proxy size, re-export FetchAsset

* style: prettier formatting for proxy-media + proxied-fetch

* fix(export): report CSS-nested asset failures in inline report

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: 杨慎 <117187635+cosarah@users.noreply.github.com>
2026-05-31 18:36:39 +08:00
DHQ1204and蛋花棋 ebff58e77b Fix commercial licensing contact email (#604)
Co-authored-by: 蛋花棋 <zhengdanqi@zhengdanqideMacBook-Air.local>
2026-05-27 14:29:01 +08:00
Helck 679130a6b9 feat(ai): add Xiaomi MiMo Token Plan support (#578) 2026-05-17 15:24:30 +08:00
be759a8252 feat: add lemonade to provider (#508)
* feat: add lemonade to provider

* fix(lemonade): correct TTS/ASR model IDs and expand Kokoro voices

- TTS model: kokoro → kokoro-v1
- ASR model: single 'whisper' → 6 Whisper variants (Base/Large-v3/Large-v3-Turbo/Medium/Small/Tiny), default Whisper-Base
- Expand lemonade-tts voices to full Kokoro v1 set (54 voices across en-US/en-GB/zh-CN/ja-JP/es/fr-FR/hi/it-IT/pt-BR)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(lemonade): zh-TW i18n parity, real Kokoro default voice, language code consistency

- zh-TW.json: add missing providerLemonadeTTS/ASR/Image keys
- TTS default voice: 'default' → 'af_heart' (Kokoro expects real voice ID, not placeholder)
- Voice language codes: 'es'/'hi' → 'es-ES'/'hi-IN' for consistency

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* test(lemonade): add unit tests for image/TTS/ASR adapters and wav-utils

Covers:
- lib/media/adapters/lemonade-image-adapter: request shape, base URL fallback, custom model, optional Bearer auth, error paths, connectivity probe
- lib/audio/tts-providers (lemonade-tts case): request body (model/voice/format), af_heart fallback, optional auth, error path
- lib/audio/asr-providers (lemonade-asr case): WAV-only validation, model/language form fields, "audio is empty" graceful empty result, error propagation, default model fallback
- lib/audio/wav-utils: isWavBlob detection by MIME and filename, normalizeASRUploadAudio pass-through and wav handling

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(lemonade): remove text path fork and harden fallbacks

* style: format lemonade tts files

* i18n: add lemonade voice language labels

* fix(lemonade): align image default model and harden wav detection

* test(lemonade): fix File BlobPart typing

* fix(lemonade): simplify tts voice selection and clear asr test state

* docs: highlight Lemonade support

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: wyuc <wang-yc24@mails.tsinghua.edu.cn>
2026-05-08 13:53:37 +08:00
wyucandClaude Opus 4.7 93c4cbbe67 docs: tighten VoxCPM2 README section (#502)
- drop "voice profiles live in your browser, so the serverless setup
  model is preserved" / "not synced to the server" — overclaims
  privacy: clone reference audio is sent to the VoxCPM backend on
  every synthesis (that's how cloning works).
- correct UI label: voice manager → "VoxCPM Voices" / "VoxCPM 音色"
  (matches voxcpmVoicesTitle).
- drop "voice cloning requires a backend that supports reference
  audio" tail — all three shipped backends do, and when
  voxCPMBackendSupportsReferenceAudio returns false, voice-resolver
  filters clone profiles out of the picker (no silent fallback).
- replace stacked em-dashes in list items with colons; trim AI-tone
  intro to one short line.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 20:15:26 +08:00
wyucandClaude Opus 4.7 d4f21f1fd5 docs: add VoxCPM2 setup guide to README (#500)
Adds Optional: VoxCPM2 (Self-Hosted TTS with Voice Cloning) section
under Quick Start in both README.md and README-zh.md, mirroring the
MinerU optional-block style. Three-step structure: pick a backend
(vLLM-Omni / Python API / Nano-vLLM comparison table), configure in
Settings -> Text-to-Speech -> VoxCPM2 with a UI screenshot, and
manage voices (Auto / Prompt / Clone) with a UI screenshot.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 17:43:52 +08:00
wyuc a4cbed4b43 release: v0.2.1 2026-04-26 16:09:24 +00:00
wyuc 4754bbae6d support gpt-5.5 api (#487) 2026-04-25 14:37:22 +08:00
wyuc a95aa87bf1 support recent model providers (#481) 2026-04-24 15:29:12 +08:00
wyuc 85c7038ab3 release: v0.2.0 2026-04-19 23:53:57 +08:00
Kei ChanandKei Chan 85b17d0fa0 Renew the readme (#463)
Co-authored-by: Kei Chan <keichan@KeideMacBook-Pro.local>
2026-04-19 23:36:58 +08:00
2f542cf33f feat(providers): add international base URL chips for GLM/Kimi/MiniMax (#449)
* feat(providers): add international base URL chips for GLM/Kimi/MiniMax

- Add optional alternateBaseUrls field to ProviderConfig
- Populate GLM, Kimi, MiniMax with China/International endpoints
- Settings UI renders chips below base URL input; clicking fills and auto-saves
- i18n: settings.baseUrlRegion.{china,international} in all 5 locales
- README / README-zh: list GLM and add quick-examples block

Closes #448

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* docs: use glm-5.1 as the GLM default model example

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: 杨慎 <117187635+cosarah@users.noreply.github.com>
2026-04-18 14:00:14 +08:00
wyucandClaude Opus 4.6 ae08cf47d1 chore: update Discord invite links
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 12:02:13 +08:00
wyuc 77f64f567e release: v0.1.1 2026-04-14 21:15:37 +08:00
wyucandHi-Jiajun 9badd59856 feat: add and expand MiniMax provider support (#182)
Add MiniMax LLM models (M2–M2.7), TTS provider (speech-2.8/2.6/02/01),
image generation (image-01), and video generation (Hailuo 2.3/02,
T2V-01). Unify base URLs to api.minimaxi.com and add MiniMax-specific
base URL normalization for the Anthropic-compatible endpoint.

Co-authored-by: Hi-Jiajun <3138947285@qq.com>
2026-03-30 12:41:43 +08:00
wyuc 1d0514424c release: v0.1.0 2026-03-26 23:45:52 +08:00
Madoka e92d1958a6 feat: add Grok (xAI) support as an LLM, image generation, and video generation provider (#113)
* feat: add Grok (xAI) provider support for LLM, Image and Video generation

- Add Grok as LLM provider (OpenAI-compatible, 11 models including grok-4.20, grok-4, grok-3)
- Add Grok Image generation provider (grok-imagine-image, grok-imagine-image-pro)
- Add Grok Video generation provider with async polling (grok-imagine-video)
- Add env vars: GROK_*, IMAGE_GROK_*, VIDEO_GROK_*
- Add web search note: Grok search is via chat completions, not standalone API
- Update README/README-zh with Grok in supported providers

Closes #87

* fix: ensure new image/video providers are auto-added to persisted settings

Add ensureBuiltInImageProviders and ensureBuiltInVideoProviders functions
that run on every store rehydrate, mirroring the existing LLM provider
sync logic. Without this, Grok Image/Video providers would not appear
for users with existing localStorage settings.

* fix: add missing English translation for providerGrokVideo

* style: fix prettier formatting for grok video provider

* fix: remove unsupported grok multi-agent model and improve video error
2026-03-22 21:42:34 +08:00
1/2极客andhumingfeng 0a9b5abe34 fix(build):Next.js 16 要求 Node.js >= 20.9.0 (#21)
* fix(build):
1、README.md / README-zh.md — Node.js 版本要求 >= 18 → >= 20
2、package.json — 新增 engines: { node: ">=20.9.0" }
3、ci.yml — node-version: 20 → 22(与 Dockerfile 一致)
4、.nvmrc — 新建,内容 22

Co-authored-by: humingfeng <humfsss@gmail.com>
2026-03-17 09:01:04 +08:00
wyucandClaude Opus 4.6 e25e585e66 docs: revamp OpenClaw TIP section with playful copy and clearer install flow
Restructure the OpenClaw quickstart: install skill first, then pick mode.
Add meme-style nudge for OpenClaw to star the repo. Enlarge logo and
put "OpenClaw" in the TIP heading for visibility.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 21:04:51 +08:00
wyucandClaude Opus 4.6 ca05a036fa docs: use plain URL for video embed to avoid details wrapper
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 15:37:19 +08:00
wyucandClaude Opus 4.6 fc41ecf8f0 docs: add promo videos to READMEs
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 15:36:20 +08:00
wyucandClaude Opus 4.6 79f5794203 docs: add star history chart to READMEs
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 14:11:02 +08:00
wyucandClaude Opus 4.6 34fae425a0 docs(skill): add prominent OpenClaw hook to READMEs and secure access code flow
- Add [!TIP] callout block between Overview and Quick Start showing
  hosted mode (3-step zero-setup) and self-hosted options
- Update OpenClaw Integration section to distinguish hosted vs self-hosted
- Add accessCode field to skill config examples in both READMEs
- SKILL.md: auto-detect accessCode from config, skip mode selection prompt
- hosted-mode.md: read access code from config file instead of chat input

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 13:48:14 +08:00
wyuc 1538a6fac5 docs: remove access code information from README files 2026-03-16 01:38:55 +08:00
wyucandClaude Opus 4.6 b4c354bd62 docs: add Discord and Feishu community badges to READMEs
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 22:35:24 +08:00
wyucandClaude Opus 4.6 9e0f1435d1 docs: add live demo access code to READMEs
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 22:00:23 +08:00
wyucandClaude Opus 4.6 49b01c4025 docs: use side-by-side layout for demo GIFs and add OpenClaw quick-start hint
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-14 17:17:49 +08:00
wyucandClaude Opus 4.6 eec20ae93c docs: move OpenClaw integration into Features section with logo
- Add OpenClaw badge to header badge row and nav links
- Mention OpenClaw integration in Overview paragraph and Highlights
- Move OpenClaw section from standalone h2 into Features as h3,
  between Multi-Agent Interaction and Export
- Collapse config/details into <details> for cleaner reading
- Add portrait demo GIF placeholder for Feishu walkthrough

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-14 15:46:22 +08:00
root 5f9de71582 docs: update openclaw skill docs 2026-03-14 14:23:44 +08:00
root 631bedfa2d refactor: replace openclaw plugin with guided skill 2026-03-14 11:29:41 +08:00
yangshenandClaude Opus 4.6 80bea8be2e docs: fix OpenClaw plugin install command and config path
Update to correct `openclaw plugins install` command and
`~/.openclaw/openclaw.json` config path with proper structure.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-14 03:13:04 +08:00
yangshenandClaude Opus 4.6 aabaa5d0f9 docs: add OpenClaw plugin section to README and README-zh
Document how to install, configure, and use the OpenClaw plugin
for managing and generating classrooms via AI agents. Also add
openclaw/ to the project structure tree in both READMEs.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-14 01:51:43 +08:00
wyucandClaude Opus 4.6 6696c20c79 docs: add JCST paper citation and badge to READMEs
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 21:35:15 +08:00
yangshenandClaude Opus 4.6 5302804ec4 Merge branch 'main' into fix/mineru-deploy
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 19:16:10 +08:00
yangshenandClaude Opus 4.6 697bc547e0 fix: correct MinerU deployment docs and add PDF provider test button
- Remove bundled MinerU Docker service and Dockerfile; link to official API and self-host docs instead
- Set MinerU requiresApiKey to false (self-hosted doesn't need it)
- Add test connection button to PDF settings panel with backend proxy to avoid CORS
- Add verify-pdf-provider API route for server-side connection testing
- Set Vercel API body size limit to 50MB for document uploads

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 19:06:45 +08:00
wyuc bd6f1db256 chore: remove whiteboard GIF from README files
Deleted the whiteboard GIF from both English and Chinese README files to streamline content presentation.
2026-03-13 14:32:47 +08:00
wyucandClaude Opus 4.6 3c75b88cc1 chore: switch license from MIT to AGPL-3.0 and expand READMEs
Replace MIT license with AGPL-3.0 for compatibility with PPTist-derived
code. Add commercial licensing section, project structure diagram, and
key architecture pointers to both English and Chinese READMEs.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 14:30:01 +08:00
wyucandClaude Opus 4.6 8047bcc241 fix: correct env var names in READMEs and add license field to package.json
- GOOGLE_GENERATIVE_AI_API_KEY → GOOGLE_API_KEY (EN/ZH README)
- MINERU_API_URL → PDF_MINERU_BASE_URL (ZH README)
- Add "license": "MIT" to package.json

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 22:40:19 +08:00
wyucandClaude Opus 4.6 a3546ce202 feat: add polished README (EN/ZH), Docker support, and project assets
- Add README.md (English) and README-zh.md (Chinese) with banner, GIF
  demos, feature showcase, use cases, and quick start guide
- Add Dockerfile (multi-stage build), docker-compose.yml, .dockerignore
- Add .env.example with all supported environment variables
- Add MIT LICENSE file
- Move all README assets into assets/ directory
- Update i18n: new slogan and input placeholder text
- Add Vercel one-click deploy button and instructions

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 22:04:19 +08:00
yangshenandClaude Opus 4.6 81aab0125c fix: remove required env var from Vercel deploy button
Make all provider API keys optional in the one-click deploy link so
users are not forced to provide an OpenAI key.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 20:25:25 +08:00