T18: courses provider + courses:// resources #17
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "t18-courses"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
T18: Courses provider (course materials from GitLab) +
courses://inreadStacked on #16 (
t7-fixtures) → … → #1. Merge those first.What
courses.fit.cvut.cz/data/courses-all.json. It's public, so no token is sent. 818 courses in semester B261, 499 with apagesRepo. Cached for 6 h per isolate, read with a size cap and validated.BI-PA1→BI-PA1.21), because Sirius and the catalogue use the study-plan suffix.pagesRepo→ GitLab project only when the repo is on the configured GitLab host over https with a sane path. A repository URL on any other host is ignored, so the GitLab token can never be sent elsewhere (tested with a foreign-host course).fit.coursesnamespace, Czech and English keywords):list({ enrolledOnly = true, query? }): your courses from the Sirius timetable (−30 to +89 days), or a catalogue search (which needsquery, up to 50 results). Doesn't need GitLab. If Sirius fails you get a warning, not an error.tree({ code, path?, ref? }): up to 3×100 entries withtruncated. Submodules are skipped, and a missing folder, ref or repo is an error, never an empty listing.read({ code, path, ref? }): text up to 256 KB measured in UTF-8 bytes, cut at a character boundary, streamed and cancelled after the cap (Content-Length isn't trusted). Binaries (NUL bytes or known binary MIME types) return metadata and a web link. Empty files count as text.search({ code | "all", query }): GitLab blob search.allcovers up to 8 enrolled courses, and inaccessible repos produce per-course warnings instead of silent "no matches". Snippets are capped at 400 characters.cloneInfo({ code }): SSH and HTTPS URLs, the default branch, plusgit clone …andrg …commands.readnow resolvescourses://<CODE>/<path>throughfit.courses.read. The URI is parsed by hand so the code keeps its case.fit://helplists the new resource.FnContext.gitlabBaseUrlcomes fromGITLAB_BASE_URL(default gitlab.fit.cvut.cz)..or..segments, no control characters, length caps, URL-encoded in API paths. GitLab 401 →reauth_required, 403 → "not a member", redirects never followed.Verified live (your FIT token + the GitLab PAT you connected)
list→ your 7 courses this semester (B261).tree BI-PA1→ 17 entries; a missing folder → "Folder not found".read BI-PA1 NEWS.adoc→ text frommasterwith a web link; a missing file → "File not found".search BI-PA1 "rekurze"→ 17 hits.search all "zkouška"→ 22 hits across 3 courses.cloneInfo BI-PA1→git@gitlab.fit.cvut.cz:BI-PA1/bi-pa1.git.read courses://BI-PA1/elearning/functions/recursion.adoc→ 1.5k characters.Tests
bun run lint && bun run typecheck && bun run test: 231 tests, 21 of them for courses. They cover code resolution, host pinning, the enrolled list with cache and Sirius-failure warning, catalogue search, tree (submodules, 404), read (text, binary, truncation, empty, UTF-8 byte limit, unbounded chunked stream,application/x-shstays text, traversal, missing file), search (one course, all, 404 warning), cloneInfo, not connected, revoked token → reauth, bounded reader cancellation, UTF-8 trimming, search keywords, andcourses://parsing.Astra review (gpt-6-astra), 2 rounds
No token-exfiltration route found through code, path, ref or
pagesRepo.Fixed:
res.text()when Content-Length was missing →readBoundedstreams with a hard cap and cancels the rest. Also applied to JSON and the catalogue.application/x-*was treated as binary → explicit binary MIME list.🤖 Generated with Claude Code