gecko-surf 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (83) hide show
  1. gecko_surf-0.1.0/.dockerignore +19 -0
  2. gecko_surf-0.1.0/.env.example +287 -0
  3. gecko_surf-0.1.0/.gitignore +33 -0
  4. gecko_surf-0.1.0/.mcp.json.example +21 -0
  5. gecko_surf-0.1.0/Dockerfile +56 -0
  6. gecko_surf-0.1.0/LICENSE +21 -0
  7. gecko_surf-0.1.0/PKG-INFO +14 -0
  8. gecko_surf-0.1.0/README.md +250 -0
  9. gecko_surf-0.1.0/examples/__init__.py +0 -0
  10. gecko_surf-0.1.0/examples/_starter/README.md +45 -0
  11. gecko_surf-0.1.0/examples/_starter/__init__.py +1 -0
  12. gecko_surf-0.1.0/examples/_starter/app.py +71 -0
  13. gecko_surf-0.1.0/examples/sos_vzla_bot/README.md +80 -0
  14. gecko_surf-0.1.0/examples/sos_vzla_bot/__init__.py +0 -0
  15. gecko_surf-0.1.0/examples/sos_vzla_bot/__main__.py +6 -0
  16. gecko_surf-0.1.0/examples/sos_vzla_bot/agent.py +76 -0
  17. gecko_surf-0.1.0/examples/sos_vzla_bot/bot.py +234 -0
  18. gecko_surf-0.1.0/examples/sos_vzla_bot/config.py +90 -0
  19. gecko_surf-0.1.0/examples/sos_vzla_bot/providers.py +170 -0
  20. gecko_surf-0.1.0/examples/sos_vzla_bot/serve_sos_mcp.py +32 -0
  21. gecko_surf-0.1.0/examples/sos_vzla_bot/spec/sosvenezuela_openapi.json +212 -0
  22. gecko_surf-0.1.0/examples/sos_vzla_bot/surfcall_tools.py +90 -0
  23. gecko_surf-0.1.0/examples/sos_vzla_bot/tests/__init__.py +0 -0
  24. gecko_surf-0.1.0/examples/sos_vzla_bot/tests/test_agent_loop.py +124 -0
  25. gecko_surf-0.1.0/examples/sos_vzla_bot/tests/test_bot_handlers.py +52 -0
  26. gecko_surf-0.1.0/examples/sos_vzla_bot/tests/test_commands.py +81 -0
  27. gecko_surf-0.1.0/examples/sos_vzla_bot/tests/test_first_call_correct.py +24 -0
  28. gecko_surf-0.1.0/examples/sos_vzla_bot/tests/test_format.py +31 -0
  29. gecko_surf-0.1.0/examples/sos_vzla_bot/tests/test_providers.py +171 -0
  30. gecko_surf-0.1.0/examples/sos_vzla_bot/tests/test_surfcall_tools.py +66 -0
  31. gecko_surf-0.1.0/gecko/__init__.py +34 -0
  32. gecko_surf-0.1.0/gecko/access.py +143 -0
  33. gecko_surf-0.1.0/gecko/binding.py +76 -0
  34. gecko_surf-0.1.0/gecko/caller.py +103 -0
  35. gecko_surf-0.1.0/gecko/catalog.py +75 -0
  36. gecko_surf-0.1.0/gecko/client.py +110 -0
  37. gecko_surf-0.1.0/gecko/corpus.py +203 -0
  38. gecko_surf-0.1.0/gecko/deeplinks.py +48 -0
  39. gecko_surf-0.1.0/gecko/demo.py +148 -0
  40. gecko_surf-0.1.0/gecko/entitlements.py +92 -0
  41. gecko_surf-0.1.0/gecko/evaluate.py +61 -0
  42. gecko_surf-0.1.0/gecko/http_server.py +259 -0
  43. gecko_surf-0.1.0/gecko/ingest.py +154 -0
  44. gecko_surf-0.1.0/gecko/mcp_server.py +74 -0
  45. gecko_surf-0.1.0/gecko/netguard.py +159 -0
  46. gecko_surf-0.1.0/gecko/sample.py +53 -0
  47. gecko_surf-0.1.0/gecko/serve.py +148 -0
  48. gecko_surf-0.1.0/gecko/serve_mcp.py +52 -0
  49. gecko_surf-0.1.0/gecko/surfaces.py +82 -0
  50. gecko_surf-0.1.0/gecko/tools.py +121 -0
  51. gecko_surf-0.1.0/gecko/validator.py +52 -0
  52. gecko_surf-0.1.0/infra/cleanup-orphaned-stacks.sh +102 -0
  53. gecko_surf-0.1.0/infra/deploy.sh +153 -0
  54. gecko_surf-0.1.0/infra/ecs-stack.yml +431 -0
  55. gecko_surf-0.1.0/pyproject.toml +53 -0
  56. gecko_surf-0.1.0/scripts/SUBSCRIBE.md +28 -0
  57. gecko_surf-0.1.0/scripts/pegana_eval.py +110 -0
  58. gecko_surf-0.1.0/scripts/subscribe.py +208 -0
  59. gecko_surf-0.1.0/scripts/subscribe.ts +83 -0
  60. gecko_surf-0.1.0/tests/fixtures/pegana_openapi.json +1 -0
  61. gecko_surf-0.1.0/tests/fixtures/txodds_docs.yaml +3346 -0
  62. gecko_surf-0.1.0/tests/test_access.py +50 -0
  63. gecko_surf-0.1.0/tests/test_binding.py +127 -0
  64. gecko_surf-0.1.0/tests/test_caller.py +48 -0
  65. gecko_surf-0.1.0/tests/test_catalog.py +38 -0
  66. gecko_surf-0.1.0/tests/test_client_mcp.py +54 -0
  67. gecko_surf-0.1.0/tests/test_corpus_controlplane.py +175 -0
  68. gecko_surf-0.1.0/tests/test_deeplinks.py +43 -0
  69. gecko_surf-0.1.0/tests/test_entitlements.py +79 -0
  70. gecko_surf-0.1.0/tests/test_healthz.py +50 -0
  71. gecko_surf-0.1.0/tests/test_http_server.py +239 -0
  72. gecko_surf-0.1.0/tests/test_ingest.py +61 -0
  73. gecko_surf-0.1.0/tests/test_netguard.py +104 -0
  74. gecko_surf-0.1.0/tests/test_pegana.py +99 -0
  75. gecko_surf-0.1.0/tests/test_public_api.py +54 -0
  76. gecko_surf-0.1.0/tests/test_sample.py +35 -0
  77. gecko_surf-0.1.0/tests/test_security.py +102 -0
  78. gecko_surf-0.1.0/tests/test_serve.py +55 -0
  79. gecko_surf-0.1.0/tests/test_starter.py +57 -0
  80. gecko_surf-0.1.0/tests/test_surfaces.py +86 -0
  81. gecko_surf-0.1.0/tests/test_tools.py +50 -0
  82. gecko_surf-0.1.0/tests/test_validator_demo.py +28 -0
  83. gecko_surf-0.1.0/uv.lock +1332 -0
@@ -0,0 +1,19 @@
1
+ # Keep the build context small and secrets out of the image (invariant #1).
2
+ .git
3
+ .venv
4
+ __pycache__
5
+ *.pyc
6
+ .pytest_cache
7
+ .mypy_cache
8
+ .ruff_cache
9
+ # secrets / local env — must never enter the image
10
+ .env
11
+ .env.*
12
+ .env.tgbot
13
+ private/
14
+ # not needed at runtime
15
+ tests/
16
+ docs/
17
+ .github/
18
+ *.md
19
+ !README.md
@@ -0,0 +1,287 @@
1
+ # =============================================================================
2
+ # Gecko — environment variables (gecko-api side)
3
+ # =============================================================================
4
+ #
5
+ # Two consumers read this file:
6
+ #
7
+ # 1. Local dev: `uv run uvicorn gecko_api.main:app` reads from .env via
8
+ # set -a; source .env; set +a (Settings.from_env() reads os.environ).
9
+ #
10
+ # 2. Deploy: ./infra/push-ssm-params.sh maps each non-empty value into
11
+ # /gecko-api/<NAME> as a SecureString in AWS SSM. ECS tasks then read
12
+ # these via the `secrets:` block in the CloudFormation task definition.
13
+ #
14
+ # End users (Claude Code skill) never touch this file. Their wallet lives at
15
+ # ~/.agentwallet/config.json (frames.ag) and gecko-mcp talks to the deployed
16
+ # gecko-api at https://api.geckovision.tech — no local secrets needed.
17
+ # =============================================================================
18
+
19
+ # ---------------------------------------------------------------------------
20
+ # Required — database + ingestion
21
+ # ---------------------------------------------------------------------------
22
+ SUPABASE_URL=
23
+ SUPABASE_SERVICE_ROLE_KEY=
24
+ TAVILY_API_KEY=
25
+ OPENAI_API_KEY=
26
+
27
+ # Optional — only needed for `tests/eval/runner.py --live`.
28
+ # The eval harness uses Claude Sonnet 4.6 as the rubric judge (cross-family
29
+ # scoring reduces same-family bias). Mock mode does not need this.
30
+ ANTHROPIC_API_KEY=
31
+
32
+ # ---------------------------------------------------------------------------
33
+ # Required — LLM routing
34
+ # ---------------------------------------------------------------------------
35
+ # Two paths — pick one (uncomment the block):
36
+ #
37
+ # Path A: OpenAI direct (recommended for deploy until ClawRouter is hosted).
38
+ # GECKO_LLM_ENDPOINT=https://api.openai.com/v1
39
+ # GECKO_LLM_API_KEY=${OPENAI_API_KEY}
40
+ # CHAT_MODEL=gpt-4o-mini
41
+ #
42
+ # Path B: ClawRouter local proxy (v3 demo path; not viable on Fargate yet
43
+ # unless we run ClawRouter as a sidecar — defer post-MVP).
44
+ # GECKO_LLM_ENDPOINT=http://localhost:8402/v1
45
+ # GECKO_LLM_API_KEY=x402
46
+ # CHAT_MODEL=openai/gpt-4o
47
+ GECKO_LLM_ENDPOINT=https://api.openai.com/v1
48
+ GECKO_LLM_API_KEY=
49
+ CHAT_MODEL=gpt-4o-mini
50
+
51
+ # ---------------------------------------------------------------------------
52
+ # Pro tier — LLM routing (S1-01)
53
+ # ---------------------------------------------------------------------------
54
+ # Selects the OpenAI-compatible base URL the AG2 GroupChat hits. Per-agent
55
+ # model strings come from `gecko_core.orchestration.pro.router.model_matrix`.
56
+ # openai — direct api.openai.com (uses OPENAI_API_KEY)
57
+ # openrouter — https://openrouter.ai/api/v1 (uses OPENROUTER_API_KEY)
58
+ # clawrouter — http://localhost:8402/v1 (no auth; CLAWROUTER_URL overrides)
59
+ LLM_ROUTER=openai
60
+ # Required when LLM_ROUTER=openrouter. Ignored otherwise.
61
+ OPENROUTER_API_KEY=
62
+ # Optional — only used when LLM_ROUTER=clawrouter.
63
+ # CLAWROUTER_URL=http://localhost:8402/v1
64
+
65
+ # ---------------------------------------------------------------------------
66
+ # Required — x402 payments (devnet ↔ mainnet via SSM, no code changes)
67
+ # ---------------------------------------------------------------------------
68
+ # Mode:
69
+ # stub — local dev only; auto-settles, no real payment. NEVER deploy stub.
70
+ # live — real facilitator + on-chain settlement.
71
+ # x402 mode controls payment behavior:
72
+ # stub - synthetic receipts, no real USDC moved (DEFAULT for local dev)
73
+ # live - real Solana mainnet/devnet payments via x402 facilitator
74
+ # Flip to `live` only after:
75
+ # 1. Funding your client wallet with ~$1 SOL for gas
76
+ # 2. Setting GECKO_WALLET_ADDRESS to a treasury YOU control
77
+ # 3. Reading docs/test-plan.md → "Live-mode pre-flight"
78
+ # For one-off live calls, prefer: X402_MODE=live <command> (override per-call)
79
+ X402_MODE=stub
80
+
81
+ # Network. Friendly names — devnet for testing, mainnet for production.
82
+ # The CAIP-2 chain id, facilitator URL, and Solana cluster are all derived
83
+ # from this value (see gecko_core.payments.networks). Legacy CAIP-2 form
84
+ # (solana:EtW... / solana:5eyk...) is still accepted with a deprecation
85
+ # warning so existing SSM values don't break across one deploy.
86
+ X402_NETWORK=solana-devnet
87
+
88
+ # x402 facilitator URL. OPTIONAL — defaults to the per-network value:
89
+ # solana-devnet → https://www.x402.org/facilitator (free, no auth)
90
+ # solana-mainnet → https://api.cdp.coinbase.com/platform/v2/x402 (CDP)
91
+ # Set this only to override (e.g. staging, private gateway, PayAI fallback).
92
+ X402_FACILITATOR_URL=
93
+
94
+ # CDP credentials — REQUIRED when X402_NETWORK=solana-mainnet, ignored on
95
+ # devnet. Generate at https://portal.cdp.coinbase.com/access/api → "Create
96
+ # API Key". `KEY_ID` is public-ish; `KEY_SECRET` is the Ed25519 (or ES256)
97
+ # private key — keep it in 1Password, never commit. SSM holds both as
98
+ # SecureString. Sentinel `__unset__` triggers a clean startup error on
99
+ # mainnet rather than a confusing 401 at first paid request.
100
+ CDP_API_KEY_ID=
101
+ CDP_API_KEY_SECRET=
102
+
103
+ # Privy v2 server-side wallet credentials — REQUIRED for per-project wallet
104
+ # provisioning (S2-05). On `POST /projects` we create a Solana embedded
105
+ # wallet via Privy and persist its id/address on the project row. Without
106
+ # these set, `Settings.is_privy_configured()` returns False and provisioning
107
+ # is silently skipped — devnet without Privy keys still serves users without
108
+ # `project_id`. Generate at https://dashboard.privy.io → "Settings" → "API
109
+ # keys"; `APP_ID` (~26 chars, public-ish) and `APP_SECRET` (~105 chars,
110
+ # secret — keep in 1Password). SSM holds both as SecureString. Sentinel
111
+ # `__unset__` triggers the lazy-skip path rather than a hard boot error.
112
+ PRIVY_APP_ID=
113
+ PRIVY_APP_SECRET=
114
+
115
+ # ---------------------------------------------------------------------------
116
+ # twit.sh — X/Twitter data via x402 micropayments
117
+ # ---------------------------------------------------------------------------
118
+ # https://twit.sh — Pay $0.0025-$0.01 per X read via x402 on Base.
119
+ # The Gecko-owned wallet is server-managed (you fund it with USDC on Base).
120
+ # Disabled by default (TWITSH_ENABLED=false) — flip to "true" once funded.
121
+ # Sentinel "__unset__" on private key disables the integration silently
122
+ # regardless of TWITSH_ENABLED, so misconfigured deploys don't burn calls.
123
+ TWITSH_ENABLED=false
124
+ TWITSH_WALLET_PRIVATE_KEY=
125
+ TWITSH_WALLET_ADDRESS=
126
+ TWITSH_BASE_URL=https://x402.twit.sh
127
+
128
+ # Treasury address — Solana pubkey that RECEIVES USDC. Generate fresh for
129
+ # production (we only need the public key here; keep the secret in 1Password):
130
+ # uv run python -c "from solders.keypair import Keypair; kp=Keypair(); print(kp.pubkey())"
131
+ # Then send a tiny USDC transfer to it once so the SPL token account exists.
132
+ GECKO_WALLET_ADDRESS=
133
+
134
+ # Pricing (single quotes to keep the literal `$`, otherwise bash expands $0)
135
+ # '$0.10' — devnet/demo
136
+ # '$0.50' — mainnet starter
137
+ # '$20.00' — production target
138
+ RESEARCH_BASIC_PRICE='$0.10'
139
+ RESEARCH_PRO_PRICE='$0.75'
140
+
141
+ # ---------------------------------------------------------------------------
142
+ # Optional — V1 Decision Receipt (on-chain verdict-hash anchor, DEVNET ONLY)
143
+ # ---------------------------------------------------------------------------
144
+ # Anchors sha256(canonical_json(verdict_envelope)) as an SPL Memo on Solana
145
+ # devnet so a third party can verify "this agent checked before it acted".
146
+ # v0 = SPL Memo, devnet, NO real money (devnet airdrop pays the ~5000-lamport
147
+ # fee). DEFAULT OFF — the anchor signs + broadcasts a tx, so it is gated hard.
148
+ # Mainnet anchoring is the v1 PDA program, not this path.
149
+ #
150
+ # Enable the ANCHOR side (server that posts receipts):
151
+ # GECKO_RECEIPT_ENABLED=1
152
+ # GECKO_RECEIPT_RPC_URL=https://api.devnet.solana.com (must be devnet)
153
+ # GECKO_RECEIPT_ORACLE_KEYPAIR=/abs/path/devnet-oracle.json (gitignored)
154
+ # Generate the devnet keypair with: solana-keygen new -o devnet-oracle.json
155
+ # then `solana airdrop 1 <pubkey> --url devnet`. NEVER commit the keypair.
156
+ GECKO_RECEIPT_ENABLED=
157
+ GECKO_RECEIPT_RPC_URL=
158
+ GECKO_RECEIPT_ORACLE_KEYPAIR=
159
+ #
160
+ # Enable the VERIFIER side (public POST /v1/receipt/verify route). The PUBLIC
161
+ # key is safe to publish — it is the verification anchor. Set it + the devnet
162
+ # RPC so the route checks against the right oracle; unset → route returns 503.
163
+ GECKO_RECEIPT_ORACLE_PUBKEY=
164
+
165
+ # ---------------------------------------------------------------------------
166
+ # Optional — YouTube caption fallback
167
+ # ---------------------------------------------------------------------------
168
+ # Enables Deepgram nova-3 transcription when a YouTube video has no captions.
169
+ # Without it, those sources are silently skipped (~$0.0043/minute when used).
170
+ # DEEPGRAM_API_KEY=
171
+ # DEEPGRAM_MAX_AUDIO_MIN=30
172
+
173
+ # ---------------------------------------------------------------------------
174
+ # Client-side (gecko-mcp talking to gecko-api) — NOT pushed to SSM
175
+ # ---------------------------------------------------------------------------
176
+ # Lives in ~/.gecko/.env on the user's machine, not here. Default once
177
+ # deployed: https://api.geckovision.tech. Override only for local dev.
178
+ # GECKO_API_URL=http://localhost:8000
179
+
180
+ # ---------------------------------------------------------------------------
181
+ # Defaults — NOT pushed to SSM (pure code default)
182
+ # ---------------------------------------------------------------------------
183
+ GECKO_DEFAULT_TIER=basic
184
+
185
+ # ---------------------------------------------------------------------------
186
+ # Production judge transcript capture (S12-HARDEN-03)
187
+ # ---------------------------------------------------------------------------
188
+ # Every successful /research and /plan call writes one transcript record to
189
+ # disk for audit (compliance + post-hoc review of misranked verdicts surfaced
190
+ # by CDP Bazaar). Schema mirrors the eval-side capture in tests/eval/runner.
191
+ #
192
+ # Default: enabled in production, disabled under pytest (see tests/conftest.py).
193
+ # See docs/runbooks/cdp-bazaar.md § "Inspect public verdicts" for queries.
194
+ # GECKO_TRANSCRIPT_CAPTURE=true
195
+ #
196
+ # S12-HARDEN-05 — backend selection. Three modes:
197
+ # mongo → judge_transcripts collection in MongoDB (production default
198
+ # when MONGODB_URI is set; ECS Fargate has ephemeral disk so
199
+ # Mongo is the only durable store). Reuses MONGODB_URI and
200
+ # MONGODB_DB env vars already required for the twit.sh cache.
201
+ # If a write fails (network blip, auth fail), the dispatcher
202
+ # logs WARN and falls through to the filesystem store so a
203
+ # verdict is never lost.
204
+ # filesystem → one JSON file per session_id at GECKO_TRANSCRIPT_DIR (or
205
+ # /var/lib/gecko/judge_transcripts when writable, else
206
+ # /tmp/gecko/transcripts). Local dev / CI / fallback.
207
+ # noop → drop on the floor. Rare; differs from CAPTURE=false in
208
+ # intent only.
209
+ #
210
+ # Default when GECKO_TRANSCRIPT_STORE is unset: "mongo" if MONGODB_URI is set,
211
+ # otherwise "filesystem".
212
+ # GECKO_TRANSCRIPT_STORE=mongo
213
+ # GECKO_TRANSCRIPT_DIR=/var/lib/gecko/judge_transcripts # filesystem mode only
214
+ # MONGODB_URI=mongodb+srv://... # required for mongo mode
215
+ # MONGODB_DB=gecko_cache # default; shared with twit.sh cache
216
+
217
+ # ---------------------------------------------------------------------------
218
+ # S18-MONGO — chunk store (chunks + chunk_embedding_cache + chunks_write_audit)
219
+ # ---------------------------------------------------------------------------
220
+ # Cutover from Supabase pgvector to MongoDB Atlas Vector Search. Solo-user
221
+ # fresh-start cutover (no backfill). See docs/strategy/2026-05-02-s18-plan.md.
222
+ #
223
+ # GECKO_CHUNK_STORE selects the active store:
224
+ # supabase → legacy Postgres + pgvector (default during S18 dev)
225
+ # mongo → MongoDB Atlas (gecko_rag DB; flip after M5 cutover)
226
+ #
227
+ # MONGODB_URI is reused from the transcript store above. The chunk store lives
228
+ # in its own database (gecko_rag) and uses these collections:
229
+ # - chunks (vector + Atlas Search indexes; M1 schema)
230
+ # - chunk_embedding_cache (content-addressed embed cache)
231
+ # - chunks_write_audit (per-batch ingest observability)
232
+ #
233
+ # GECKO_CHUNK_STORE=supabase
234
+ # MONGODB_CHUNK_DB=gecko_rag # default for chunk store
235
+
236
+ # ---------------------------------------------------------------------------
237
+ # S22-VOYAGE-EMBED — embedding provider (default: voyage)
238
+ # ---------------------------------------------------------------------------
239
+ EMBED_PROVIDER=voyage
240
+ VOYAGE_API_KEY= # required when EMBED_PROVIDER=voyage
241
+ # EMBED_MODEL=voyage-context-3 # default — RAG-optimal context-aware embedding model
242
+ # # legacy voyage-3 still supported (same 1024 dim)
243
+
244
+ # ---------------------------------------------------------------------------
245
+ # S28 #25 — Cohere Rerank (post-retrieval semantic reranker).
246
+ # Wired into orchestration/trade_panel/retrieve_trade_corpus_chunks between
247
+ # `_apply_retrieval_boosts` and `_provider_quota_floor`. When unset, the
248
+ # rerank step degrades gracefully and the trade-panel falls through to the
249
+ # Path D baseline slate (still functional, no crash). Model: rerank-v3.5.
250
+ # Cost: ~$1.20 per 1000 verdicts at 180-doc pool.
251
+ # ---------------------------------------------------------------------------
252
+ COHERE_API_KEY= # optional — enables Cohere rerank-v3.5 in trade-panel retrieval
253
+
254
+ # ---------------------------------------------------------------------------
255
+ # OKX OnchainOS Market — developer key (OK-ACCESS-KEY header).
256
+ # Drives gecko_core.sources.okx_onchainos_market: token metrics (mcap /
257
+ # liquidity / 24h volume / holders count), top-20 holder concentration, and
258
+ # the manipulation-resistant composite index price. Distinct from OKX_API_KEY
259
+ # (the Bearer-token news adapter). Unset → client disabled, all methods
260
+ # fail-OPEN to None/[]. Base: https://web3.okx.com (override OKX_BASE_URL N/A;
261
+ # base is a client kwarg).
262
+ # ---------------------------------------------------------------------------
263
+ OKX_ONCHAINOS_API_KEY= # optional — OnchainOS Market developer key
264
+
265
+ # Helius API — get a key at https://dev.helius.xyz
266
+ HELIUS_API_KEY=
267
+ # Colosseum Copilot (optional) — for startup research skill
268
+ # Get a PAT at https://arena.colosseum.org/copilot
269
+ COLOSSEUM_COPILOT_PAT=
270
+ COLOSSEUM_COPILOT_API_BASE=https://copilot.colosseum.com/api/v1
271
+ # QEDGen formal verification (optional) — requires qedgen CLI
272
+ # Get a key at https://console.mistral.ai
273
+ MISTRAL_API_KEY=
274
+ # TheGrid (optional) — for Colosseum project graph queries
275
+ # Get a key at https://thegrid.id
276
+ THEGRID_API_KEY=
277
+ THEGRID_GRAPHQL_ENDPOINT=https://beta.node.thegrid.id/graphql
278
+ # QuickNode (optional) — for QuickNode RPC/Streams/DAS
279
+ # Get endpoints at https://www.quicknode.com
280
+ QUICKNODE_RPC_URL=
281
+ QUICKNODE_WSS_URL=
282
+ QUICKNODE_API_KEY=
283
+ # X/Twitter (optional) — for CT alpha research skill
284
+ # Get a bearer token at https://developer.x.com
285
+ X_BEARER_TOKEN=
286
+ # DFlow (optional) — for DFlow order flow integration
287
+ DFLOW_API_KEY=
@@ -0,0 +1,33 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ .venv/
5
+ *.egg-info/
6
+ build/
7
+ dist/
8
+
9
+ # Test / tooling
10
+ .pytest_cache/
11
+ .ruff_cache/
12
+ .mypy_cache/
13
+
14
+ # Env / secrets — ship .env.example only
15
+ .env
16
+ .env.*
17
+ !.env.example
18
+
19
+ # OS / editor
20
+ .DS_Store
21
+ .idea/
22
+ *.swp
23
+
24
+ # Strategy / business docs — never in the public tree (canonical set lives in Notion)
25
+ private/
26
+ docs/
27
+ CLAUDE.md
28
+
29
+ # Claude Code local config, agents, and skills (internal)
30
+ .claude/
31
+
32
+ # MCP — real config carries a token; ship .mcp.json.example instead
33
+ .mcp.json
@@ -0,0 +1,21 @@
1
+ {
2
+ "mcpServers": {
3
+ "context7": {
4
+ "command": "npx",
5
+ "args": [
6
+ "-y",
7
+ "@upstash/context7-mcp@latest"
8
+ ]
9
+ },
10
+ "github": {
11
+ "command": "npx",
12
+ "args": [
13
+ "-y",
14
+ "@modelcontextprotocol/server-github"
15
+ ],
16
+ "env": {
17
+ "GITHUB_PERSONAL_ACCESS_TOKEN": "${GITHUB_PERSONAL_ACCESS_TOKEN}"
18
+ }
19
+ }
20
+ }
21
+ }
@@ -0,0 +1,56 @@
1
+ # =============================================================================
2
+ # surfcall — MCP Streamable-HTTP container (Python + uv + uvicorn on port 8000)
3
+ #
4
+ # Single-package repo (NOT a uv workspace). Stage 1 builds the venv with the
5
+ # `serve` extra (mcp[cli] + uvicorn -> pulls starlette). Stage 2 copies the venv
6
+ # + the surfcall package + the one local OpenAPI spec the container serves.
7
+ # Stateless: no DB/SSM/secrets at build or run time (control plane only).
8
+ # =============================================================================
9
+
10
+ FROM python:3.12-slim AS builder
11
+
12
+ # uv (Astral): static binary from the official image (slim has no curl/wget).
13
+ COPY --from=ghcr.io/astral-sh/uv:0.5.30 /uv /usr/local/bin/uv
14
+
15
+ WORKDIR /app
16
+
17
+ # Manifests + source first, so `uv sync` can build the flat-layout package.
18
+ COPY pyproject.toml uv.lock README.md ./
19
+ COPY surfcall ./surfcall
20
+
21
+ # Engine + the serve extra (mcp[cli], uvicorn, starlette). --no-dev drops
22
+ # mypy/pytest/ruff; --frozen pins to uv.lock (must resolve the serve extra).
23
+ RUN --mount=type=cache,target=/root/.cache/uv \
24
+ uv sync --frozen --no-dev --extra serve
25
+
26
+ # -----------------------------------------------------------------------------
27
+
28
+ FROM python:3.12-slim AS runner
29
+
30
+ RUN useradd --create-home --shell /bin/bash surfcall
31
+
32
+ WORKDIR /app
33
+
34
+ COPY --from=builder /app/.venv ./.venv
35
+ COPY --from=builder /app/pyproject.toml /app/uv.lock /app/README.md ./
36
+ COPY surfcall ./surfcall
37
+ # The one OpenAPI spec the container comprehends + serves. Control plane only:
38
+ # no payloads, no secrets.
39
+ COPY examples/sos_vzla_bot/spec ./examples/sos_vzla_bot/spec
40
+
41
+ RUN chown -R surfcall:surfcall /app
42
+ USER surfcall
43
+
44
+ ENV PATH="/app/.venv/bin:$PATH" \
45
+ PYTHONUNBUFFERED=1 \
46
+ PYTHONDONTWRITEBYTECODE=1 \
47
+ PORT=8000
48
+
49
+ EXPOSE 8000
50
+
51
+ # Mirror the ALB target-group health check (GET /healthz, matcher 200) locally.
52
+ HEALTHCHECK --interval=30s --timeout=5s --start-period=20s --retries=3 \
53
+ CMD python -c "import urllib.request,sys; sys.exit(0 if urllib.request.urlopen('http://127.0.0.1:8000/healthz', timeout=3).status==200 else 1)" || exit 1
54
+
55
+ # Bind 0.0.0.0:$PORT, mode=live, allowlist mcp.geckovision.tech.
56
+ CMD ["python", "-m", "surfcall.serve_mcp"]
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Gecko
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,14 @@
1
+ Metadata-Version: 2.4
2
+ Name: gecko-surf
3
+ Version: 0.1.0
4
+ Summary: Make any API agent-usable without integration code. V1: comprehension layer — turn a human-shaped OpenAPI surface into question-shaped, first-call-correct agent tools. (Working name; rename on brand pick.)
5
+ License-File: LICENSE
6
+ Requires-Python: >=3.11
7
+ Requires-Dist: pyyaml>=6.0
8
+ Provides-Extra: serve
9
+ Requires-Dist: mcp[cli]>=1.28.1; extra == 'serve'
10
+ Requires-Dist: uvicorn>=0.49.0; extra == 'serve'
11
+ Provides-Extra: sosbot
12
+ Requires-Dist: anthropic>=0.40; extra == 'sosbot'
13
+ Requires-Dist: openai>=1.40; extra == 'sosbot'
14
+ Requires-Dist: python-telegram-bot>=21; extra == 'sosbot'