An admin signs in once, and then a person or an AI agent can import chord pages and publish songs to hopam.tyvn.online. There are two ways to do it:
- The
hopamfreeCLI, which is in the site's repo. - The HTTP API used with
curl, which works from anywhere.
This page is also served as plain Markdown at /agents.md, so an agent can fetch it directly.
Workflow for an AI agent
To publish songs an admin asked for:
- Check the token. Run
hopamfree whoami, or callGET /api/cli/whoami. If it fails, ask the admin to sign in (see below). Never approve a sign-in code yourself. - Check for duplicates. For each song, search by title first, with
hopamfree search "<title>"orGET /api/songs?q=<title>. Skip the song if it is already listed with the same artist. - Import the page. Run
hopamfree import "<link>" > song.json, or callPOST /api/import.- 409 means this link is already published; the response includes the slug. Skip it.
- 422 means the page could not be fetched: private address, timeout, or not HTML. Try another source.
- 502 means the AI could not read the page. Try another source, or write the draft yourself.
- Review the draft. Nothing is published until you create it.
unknownChordsmust be empty. Fix or remove any chord the site cannot draw.- Title, artist, and key should match the source.
- Remove ads, comments, and navigation text from
sheet.
- Publish. Run
hopamfree create song.json, or callPOST /api/songs.- 400 lists the fields that are invalid.
- 409 means a duplicate.
- Report back. Give the admin the published URLs.
Only publish songs the admin asked for, and respect the source site's terms and copyright.
Song draft
The server validates every draft:
{
"title": "Nó",
"artist": "Thắng",
"composer": "",
"key": "Am",
"genre": "Pop",
"rhythm": "Ballad",
"sheet": "[Verse]\n[Am]Lời bài [F]hát...",
"sourceUrl": "https://hopamchuan.com/song/70362/no/"
}
- In
sheet, put each chord in brackets right before the syllable it falls on. Put section headings such as[Verse]and[Chorus]on their own line. A sheet holds at most 20,000 characters. keymust match^[A-G][#b]?m?$, for exampleC,F#morBb.sourceUrlis optional, and a song'ssourceUrlcan only be published once.
After a song is published, the site picks suitable strumming rhythms (điệu) for it in the background.
The hopamfree CLI
The CLI needs Node 24 or newer. Run it from the repo root:
pnpm cli <command> # or: node cli/hopamfree.mts <command>
The default site is https://hopam.tyvn.online. To use another one, pass --url <base> or set HOPAMFREE_URL, for example http://localhost:3000.
The CLI writes data as JSON to stdout and messages to stderr. It exits with 0 on success and 1 on error, and error messages include the HTTP status.
| Command | What it does |
|---|---|
login [--no-browser] |
Signs in as admin. Prints a code and opens <site>/cli?code=.... |
whoami |
Prints {email, isAdmin}. |
search [query] |
Searches published songs by title, artist, composer, or exact chord. No sign-in needed. |
import <link> |
Crawls the page and asks the AI to clean it into a draft. Prints {draft, unknownChords}. Takes about 30 s and publishes nothing. |
create <file|-> |
Publishes a draft and prints {slug, path, url}. Accepts the whole import output or a bare draft. |
logout |
Deletes the local token. It does not revoke the token; do that on /cli. |
Sign-in steps:
- Run
pnpm cli login. - Sign in on the page that opens with an admin account, and check that the code on the page matches the code in the terminal.
- Click Cho phép.
The token is saved to ~/.config/hopamfree/config.json with file mode 600.
HTTP API
Base URL: https://hopam.tyvn.online. Every request and response body is JSON, and every error has the form {"error": "..."}.
Sign in (device flow)
# 1. Start a sign-in. Keep deviceCode secret; show userCode and verificationUrl to the admin.
curl -s -X POST https://hopam.tyvn.online/api/cli/device \
-H 'content-type: application/json' -d '{"clientName":"my agent"}'
# → {"deviceCode":"hafd_…","userCode":"ABCD-EFGH","verificationUrl":"https://hopam.tyvn.online/cli?code=ABCD-EFGH","expiresAt":"…","intervalSeconds":3}
# 2. The admin opens verificationUrl, signs in and clicks "Cho phép".
# 3. Poll every intervalSeconds until you get a token (it is returned only once).
curl -s -X POST https://hopam.tyvn.online/api/cli/token \
-H 'content-type: application/json' -d '{"deviceCode":"hafd_…"}'
# 428 {"status":"pending"} → keep polling
# 200 {"token":"haf_…","email":"…"} → done
# 410 → the code expired (after 10 minutes); start again
Send the token as Authorization: Bearer haf_…. The admin can see and revoke tokens on /cli. Removing an email from the admin list also stops its tokens from working.
Endpoints
| Method and path | Auth | Body | Response |
|---|---|---|---|
GET /api/cli/whoami |
token | – | {email, isAdmin} or 401 |
GET /api/songs?q=<query> |
none | – | {songs: [{slug, title, artist, key, sourceUrl, path}]}, at most 50 |
POST /api/import |
admin token | {url} |
{draft, unknownChords}, or 409, 422 or 502 |
POST /api/songs |
admin token | a draft | 201 {slug, path}, or 400 or 409 |
TOKEN=haf_…
curl -s -X POST https://hopam.tyvn.online/api/import -H "authorization: Bearer $TOKEN" \
-H 'content-type: application/json' -d '{"url":"https://example.com/some-song"}' > song.json
# Review and edit song.json, then publish its "draft" object:
jq .draft song.json | curl -s -X POST https://hopam.tyvn.online/api/songs -H "authorization: Bearer $TOKEN" \
-H 'content-type: application/json' --data-binary @-