agents.html
·
106 строк
·
7.5 KB
| 1 | {{define "content"}} |
| 2 | <div class="container" style="max-width:880px;"> |
| 3 | <div class="flex items-start justify-between flex-wrap gap-2 mb-4"> |
| 4 | <div> |
| 5 | <h1 class="text-xl font-bold tracking-tight" style="margin:0;">Polar for AI agents</h1> |
| 6 | <div class="text-xs text-muted mt-1">A practical guide to cloning, pushing, publishing and automating this Git host.</div> |
| 7 | </div> |
| 8 | <span class="badge badge-outline font-mono text-xs">GET /agents</span> |
| 9 | </div> |
| 10 | |
| 11 | <article class="card"> |
| 12 | <div class="card-body markdown-body"> |
| 13 | <h2>Overview</h2> |
| 14 | <p>Polar is a self-hosted Git forge at <code>{{.SiteURL}}</code>. It serves standard <strong>Git over HTTPS</strong> (smart HTTP) plus a web UI for issues, pull requests, releases and repository settings. Everything the UI does is a plain HTML form POST — there is no separate REST API for repositories, so agents drive the same endpoints.</p> |
| 15 | |
| 16 | <h2>Authentication: device flow</h2> |
| 17 | <p>Git over HTTPS uses the <strong>OAuth 2.0 Device Authorization Grant (RFC 8628)</strong>: the client asks for a short user code, the human approves it in the browser once, and the client receives a personal access token.</p> |
| 18 | <ol> |
| 19 | <li>Request a device code from <code>POST /api/v1/device/code</code> with a <code>client_id</code>.</li> |
| 20 | <li>Send the user to <code>verification_uri_complete</code> (they must be signed in to Polar).</li> |
| 21 | <li>Poll <code>POST /api/v1/device/token</code> with <code>device_code</code> every <code>interval</code> seconds (default 5). While the grant is pending the endpoint answers <code>429 {"error":"authorization_pending"}</code>.</li> |
| 22 | <li>On approval you receive <code>{"access_token":"...","token_type":"bearer","username":"...","scope":"git"}</code>.</li> |
| 23 | </ol> |
| 24 | <pre><code># 1. Start a device grant (no browser session required for this step) |
| 25 | curl -sX POST {{.SiteURL}}/api/v1/device/code -d 'client_id=my-agent' |
| 26 | # {"device_code":"...","user_code":"WDJB-MJHT","verification_uri":"{{.SiteURL}}/login/device", |
| 27 | # "verification_uri_complete":"{{.SiteURL}}/login/device?user_code=WDJB-MJHT","expires_in":600,"interval":5} |
| 28 | |
| 29 | # 2. Ask the user to open verification_uri_complete and approve. |
| 30 | |
| 31 | # 3. Exchange the approved device code for a token |
| 32 | curl -sX POST {{.SiteURL}}/api/v1/device/token -d 'device_code=DEVICE_CODE' |
| 33 | # {"access_token":"...","token_type":"bearer","username":"rin","scope":"git"}</code></pre> |
| 34 | <p>Use the token as the password for HTTPS Git with any username:</p> |
| 35 | <pre><code>git clone https://USERNAME:TOKEN@HOST/OWNER/REPO.git</code></pre> |
| 36 | <p>This is the same handoff a fresh <code>git push</code> triggers: Polar opens the approval page and the credential helper waits for it. For scripts, <code>scripts/git-credential-polar.sh</code> automates the flow and caches tokens in <code>~/.polar/credentials.json</code>:</p> |
| 37 | <pre><code>git config --global credential.helper '!sh /path/to/polar/scripts/git-credential-polar.sh'</code></pre> |
| 38 | <p>Tokens are listed and revocable under <em>Settings → Access tokens</em>. Device codes are single-use and expire after <code>expires_in</code> seconds.</p> |
| 39 | |
| 40 | <h2>Git operations</h2> |
| 41 | <ul> |
| 42 | <li><strong>Clone / fetch / push:</strong> <code>https://HOST/OWNER/REPO.git</code> — standard smart HTTP.</li> |
| 43 | <li><strong>Branches and tags:</strong> push them normally; Polar stores refs and shows them in the UI.</li> |
| 44 | <li><strong>Public repositories:</strong> readable anonymously when anonymous read is enabled on the instance.</li> |
| 45 | <li><strong>Private repositories:</strong> every Git request needs credentials — unauthenticated requests get <code>401</code>, while the web UI answers <code>404</code> so it does not leak existence.</li> |
| 46 | <li><strong>Empty repositories:</strong> the first <code>git push -u origin main</code> creates the default branch.</li> |
| 47 | </ul> |
| 48 | |
| 49 | <h2>Driving the web UI from a script</h2> |
| 50 | <p>The UI is server-rendered HTML with plain forms, which makes it fully scriptable:</p> |
| 51 | <ul> |
| 52 | <li>Sign in by POSTing <code>/login</code> (<code>username</code>, <code>password</code>) and keeping the <code>polar_session</code> cookie.</li> |
| 53 | <li>Every non-GET request needs the session CSRF token: read the hidden <code>_csrf</code> input from any page and send it as the <code>_csrf</code> form field or the <code>X-CSRF-Token</code> header.</li> |
| 54 | <li>File uploads are <code>multipart/form-data</code>.</li> |
| 55 | </ul> |
| 56 | <pre><code># Close an issue (state: open|closed) |
| 57 | curl -b cookies.txt -X POST https://HOST/OWNER/REPO/issues/1/state \ |
| 58 | -H "X-CSRF-Token: $CSRF" -d 'state=closed' |
| 59 | |
| 60 | # Comment on an issue |
| 61 | curl -b cookies.txt -X POST https://HOST/OWNER/REPO/issues/1/comments \ |
| 62 | -H "X-CSRF-Token: $CSRF" -d 'body=Fixed in the latest build.' |
| 63 | |
| 64 | # Open a pull request (head_branch must already be pushed) |
| 65 | curl -b cookies.txt -X POST https://HOST/OWNER/REPO/pulls/new \ |
| 66 | -H "X-CSRF-Token: $CSRF" \ |
| 67 | -d 'title=Rework uploads' -d 'body=Release notes...' \ |
| 68 | -d 'base_branch=main' -d 'head_branch=feature/uploads' |
| 69 | |
| 70 | # Review and merge a pull request |
| 71 | curl -b cookies.txt -X POST https://HOST/OWNER/REPO/pulls/4/review \ |
| 72 | -H "X-CSRF-Token: $CSRF" -d 'state=approved' -d 'body=LGTM' |
| 73 | curl -b cookies.txt -X POST https://HOST/OWNER/REPO/pulls/4/merge \ |
| 74 | -H "X-CSRF-Token: $CSRF"</code></pre> |
| 75 | <p>Other form endpoints: <code>POST /OWNER/REPO/issues/new</code> (<code>title</code>, <code>body</code>), <code>POST /OWNER/REPO/upload</code> (<code>files</code>, <code>ref</code>, <code>path</code>, <code>message</code>), <code>POST /OWNER/REPO/settings/collaborators/add</code>, <code>POST /OWNER/REPO/fork</code>.</p> |
| 76 | |
| 77 | <h2>Releases and artifacts</h2> |
| 78 | <p>Releases group a git tag, notes and uploaded files. Publishing one is a single multipart POST:</p> |
| 79 | <pre><code>curl -b cookies.txt -X POST https://HOST/OWNER/REPO/releases/new \ |
| 80 | -H "X-CSRF-Token: $CSRF" \ |
| 81 | -F 'tag_name=v1.2.0' -F 'target_ref=main' \ |
| 82 | -F 'title=v1.2.0' -F 'body=Release notes...' -F 'is_prerelease=true' \ |
| 83 | -F 'assets=@dist/app-linux-amd64' -F 'assets=@dist/app-windows-amd64.exe'</code></pre> |
| 84 | <ul> |
| 85 | <li>The tag is created automatically if missing, pointing at <code>target_ref</code>.</li> |
| 86 | <li>Up to 20 assets per release, 100 MB per file. Filenames are sanitized; assets can be deleted individually from the release card.</li> |
| 87 | <li>Asset downloads support HTTP <code>Range</code>: <code>GET /OWNER/REPO/releases/<id>/assets/<assetID>/download</code>.</li> |
| 88 | <li>Tag archives are always available: <code>GET /OWNER/REPO/archive/v1.2.0.zip</code> or <code>.tar.gz</code>.</li> |
| 89 | <li>Raw file contents: <code>GET /OWNER/REPO/raw/<ref>/<path></code>.</li> |
| 90 | </ul> |
| 91 | |
| 92 | <h2>Working agreements</h2> |
| 93 | <ul> |
| 94 | <li>Prefer branches and pull requests over pushing straight to the default branch in shared repositories.</li> |
| 95 | <li>Match the repository's commit message style; explain the change, not the process.</li> |
| 96 | <li>Use tags for release points and attach artifacts with stable names (for example <code>polar-linux-amd64</code>).</li> |
| 97 | <li>Treat tokens as secrets: never commit them, and revoke what you no longer need.</li> |
| 98 | <li>Poll the device token endpoint no faster than the advertised <code>interval</code>; back off on <code>429</code>.</li> |
| 99 | </ul> |
| 100 | |
| 101 | <hr> |
| 102 | <p class="text-xs text-muted">Static documentation. Endpoint names match this instance; if a field name ever changes, read it from the corresponding HTML form.</p> |
| 103 | </div> |
| 104 | </article> |
| 105 | </div> |
| 106 | {{end}} |