mirror of
https://github.com/rennf93/roboco.git
synced 2026-08-03 07:23:24 +02:00
* feat(providers): Gemini CLI provider — ModelProvider.GEMINI Mirrors the grok blueprint with source-verified divergences (all facts pinned against google-gemini/gemini-cli @ 9681621c): no refresher daemon — Google's refresh tokens are reusable, so the RO host mount is COPIED to a writable container-local ~/.gemini and each container refreshes in-process independently (the write-back crash risk on RO never triggers); settings.json renders security.auth.selectedType 'oauth-personal', experimental.enableAgents=false (subagent ban), autoConfigureMemory=false with a bounded heap; tool scoping rides the tiered TOML Policy Engine (deny-only rules that yolo mode structurally cannot beat); gemini -p with --output-format stream-json; usage parsed from the run's own stdout stats — the adversarial pass caught the parser reading the json-mode nested shape while the entrypoint runs stream-json's FLAT shape (every real run would have priced $0 forever, hidden by fixtures sharing the assumption) — now flat-primary with the nested shape as cited fallback; rate-limit classified from structured error.type only (model-echo immune), native exit 41 auth passthrough; per-model pricing for the three GA models; migrations 084 (enum) + 085 (seed) complete the 082-085 finale chain. V1 excludes interactive intake/secretary. Stack-merge required two behavior-preserving complexity refactors in the shared park/usage plumbing (a park-pair loop; a usage-reader dispatch dict). * fix(providers): route gemini usage read through the containment barrier Mirrors the codex/grok fix — _gemini_usage_json now delegates to _read_usage_json_contained, so CodeQL's path-injection alert on the gemini read is resolved by the same resolve-and-contain guard. --------- Co-authored-by: Renn F <rennf93@users.noreply.github.com>
71 lines
2.3 KiB
Python
71 lines
2.3 KiB
Python
"""Idempotently seed the Gemini (Google) provider row.
|
|
|
|
The ``modelprovider`` enum carries ``'gemini'`` as of migration 084. This
|
|
migration seeds the corresponding ``provider_configs`` row so the Settings UI
|
|
can list it for role/agent model assignment.
|
|
|
|
Unlike Grok's row (migration 039), Gemini has no API-key mode: the CLI
|
|
authenticates from a mounted OAuth credential (``~/.gemini/oauth_creds.json``),
|
|
never a base URL / bearer token, so both columns stay NULL permanently. The
|
|
row starts disabled; an operator enables it once the host OAuth credential is
|
|
in place (``ROBOCO_HOST_GEMINI_DIR``). ON CONFLICT (name) DO NOTHING keeps
|
|
this safe to re-run.
|
|
|
|
RE-CHAIN CAVEAT: renumbered 083->085 (was 083 in this task's original
|
|
checkout at head 081) to merge after two sibling worktrees' 082 (routing) /
|
|
083 (codex `seed_openai_provider`) — see 084_modelprovider_gemini.py's
|
|
docstring for the full note. Neither sibling exists in this checkout, so this
|
|
worktree's own migration-graph-integrity / enum-parity tests fail on the
|
|
missing revisions until the real three-way merge lands.
|
|
|
|
Revision ID: 085_seed_gemini_provider
|
|
Revises: 084_modelprovider_gemini
|
|
Create Date: 2026-07-23
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import sqlalchemy as sa
|
|
from alembic import op
|
|
|
|
revision = "085_seed_gemini_provider"
|
|
down_revision = "084_modelprovider_gemini"
|
|
branch_labels = None
|
|
depends_on = None
|
|
|
|
|
|
def upgrade() -> None:
|
|
op.execute(
|
|
sa.text(
|
|
"""
|
|
INSERT INTO provider_configs
|
|
(id, name, type, base_url, auth_token_encrypted, enabled, created_at)
|
|
VALUES
|
|
(
|
|
gen_random_uuid(),
|
|
'Gemini (Google)',
|
|
'gemini',
|
|
NULL,
|
|
NULL,
|
|
false,
|
|
now()
|
|
)
|
|
ON CONFLICT (name) DO NOTHING
|
|
"""
|
|
)
|
|
)
|
|
|
|
|
|
def downgrade() -> None:
|
|
# Drop model_assignments pointing at the Gemini row first to avoid a FK
|
|
# RESTRICT violation on provider_configs.id.
|
|
op.execute(
|
|
sa.text(
|
|
"DELETE FROM model_assignments "
|
|
"WHERE provider_config_id IN ("
|
|
" SELECT id FROM provider_configs WHERE name = 'Gemini (Google)'"
|
|
")"
|
|
)
|
|
)
|
|
op.execute(sa.text("DELETE FROM provider_configs WHERE name = 'Gemini (Google)'"))
|