mirror of
https://github.com/Twigpine/zero.git
synced 2026-11-10 17:57:54 -10:00
* feat(aimlapi): AI/ML API guided onboarding (top-up + key issuance) Add the AI/ML API provider integration to the zero CLI: a guided TUI onboarding sub-flow that takes an existing key or an email top-up and writes the issued key into the provider profile, attributed to the Gitlawb rebate partner. - internal/aimlapi: OpenAI-compatible client, partner-checkout top-up, key validation, streaming, and endpoint config (Paths A/B/C). - providercatalog / providermodelcatalog: register the aimlapi provider with default headers and models. - tui: aimlapi onboarding screen wired into the provider wizard and onboarding flow. Default endpoints point at production (auth/app/api/pay/aimlapi.com); each is overridable via AIMLAPI_*_URL env vars. Co-Authored-By: Claude Opus 4.8 <[email protected]> * fix(aimlapi): address review findings in onboarding flow - topup: use errors.As for the 5xx retry check so a wrapped APIError still retries instead of aborting a paid checkout. - onboarding: a Path-A top-up on an email with no account now lands on a single coherent "key saved, balance not topped up" screen instead of a green "ready" plus a red error. - onboarding: an empty amount field is rejected instead of silently falling through to the $25 default. - collapse the two identical top-up prompt constants into MsgTopUpPrompt and drop the dead amountPrompt() branch. - reuse stdlib maps.Clone instead of a third bespoke header-copy helper. Co-Authored-By: Claude Opus 4.8 <[email protected]> * fix(aimlapi): retry transient poll errors and sharpen amount validation * fix(aimlapi): retry transient poll errors and surface real amount errors * docs(aimlapi): document exported client API; drop out-of-scope gitignore entry Addresses the two CodeRabbit PR checks on #655: - Add doc comments to the exported aimlapi client API (Client, APIError, the session/checkout/onboarding types and their methods) and ParseAmountUSD, to satisfy the docstring-coverage check. - Remove the unrelated /zero.exe~ .gitignore entry that an earlier review-fix commit added; it is out of scope for the provider work, so the PR no longer touches .gitignore. Co-Authored-By: Claude Opus 4.8 <[email protected]> * fix(aimlapi): render aimlapi row like other recommended providers * fix(aimlapi): resolve onboarding review findings (Path A funding, base URL, request cancellation) * fix(aimlapi): validate pasted API keys * test(aimlapi): clarify pasted key validation * test(aimlapi): cover key-balance low-balance transition * fix(aimlapi): reject non-finite top-up amounts * fix(cli): scope and canonicalize provider headers * fix(tui): distinguish invalid verification codes * feat(aimlapi): resume account-bound top-ups safely * feat(tui): offer top-up for existing AIMLAPI keys * feat(tui): reuse AIMLAPI env key on first run * feat(tui): reuse configured AIMLAPI profiles * test(aimlapi): trim redundant flow coverage * fix(tui): preserve existing AIMLAPI profile metadata * fix(tui): clone AIMLAPI headers in env fallback * fix(aimlapi): address remaining review findings * fix(aimlapi): harden onboarding lifecycle * fix(aimlapi): preserve validated checkout context * fix(aimlapi): harden checkout retry state * fix(tui): clarify AIMLAPI configured choices * fix(aimlapi): make checkout retries idempotent * fix(aimlapi): validate checkout responses * fix(providers): reconcile aimlapi catalog after rebase --------- Co-authored-by: Lookoff123 <[email protected]> Co-authored-by: Claude Opus 4.8 <[email protected]>
56 lines
2.0 KiB
Go
56 lines
2.0 KiB
Go
package aimlapi
|
|
|
|
import "testing"
|
|
|
|
func TestResolveEndpointsDefaults(t *testing.T) {
|
|
t.Setenv("AIMLAPI_AUTH_URL", "")
|
|
t.Setenv("AIMLAPI_APP_URL", "")
|
|
t.Setenv("AIMLAPI_INFERENCE_URL", "")
|
|
t.Setenv("AIMLAPI_PAY_URL", "")
|
|
|
|
endpoints := ResolveEndpoints()
|
|
if endpoints.AuthBaseURL != "https://auth.aimlapi.com" ||
|
|
endpoints.AppBaseURL != "https://app.aimlapi.com" ||
|
|
endpoints.InferenceBaseURL != "https://api.aimlapi.com/v1" ||
|
|
endpoints.PayBaseURL != "https://pay.aimlapi.com" {
|
|
t.Fatalf("unexpected endpoints: %#v", endpoints)
|
|
}
|
|
}
|
|
|
|
func TestResolvedPartnerHeaderUsesEnvironmentOverrideWithoutDuplicates(t *testing.T) {
|
|
t.Setenv("AIMLAPI_PARTNER_ID", "part_override")
|
|
|
|
headers := WithResolvedPartnerHeader(map[string]string{
|
|
"x-aimlapi-partner-id": "part_catalog",
|
|
"X-AIMLAPI-Integration-Repo": "Gitlawb/zero",
|
|
})
|
|
|
|
if got := headers[PartnerHeaderName]; got != "part_override" {
|
|
t.Fatalf("partner header = %q, want part_override", got)
|
|
}
|
|
if _, ok := headers["x-aimlapi-partner-id"]; ok {
|
|
t.Fatalf("case-insensitive partner header was duplicated: %#v", headers)
|
|
}
|
|
if got := headers["X-AIMLAPI-Integration-Repo"]; got != "Gitlawb/zero" {
|
|
t.Fatalf("unrelated header changed: %#v", headers)
|
|
}
|
|
}
|
|
|
|
func TestBuildPartnerReturnURL(t *testing.T) {
|
|
t.Setenv("AIMLAPI_RETURN_URL", "")
|
|
// The frontend base (the web app the flow was opened from) is returned as-is,
|
|
// trailing slash trimmed — an https page the browser can actually land on.
|
|
if got := BuildPartnerReturnURL("https://aimlapi.com/app/"); got != "https://aimlapi.com/app" {
|
|
t.Fatalf("return URL = %q, want the frontend base", got)
|
|
}
|
|
// An empty base falls back to the https default, never a custom scheme.
|
|
if got := BuildPartnerReturnURL(""); got != DefaultReturnURL {
|
|
t.Fatalf("empty base return URL = %q, want %q", got, DefaultReturnURL)
|
|
}
|
|
|
|
t.Setenv("AIMLAPI_RETURN_URL", "https://example.test/done")
|
|
if got := BuildPartnerReturnURL("https://aimlapi.com/app"); got != "https://example.test/done" {
|
|
t.Fatalf("env override return URL = %q", got)
|
|
}
|
|
}
|