flowai-cli 0.2.0__tar.gz → 0.3.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 (93) hide show
  1. flowai_cli-0.3.0/PKG-INFO +302 -0
  2. flowai_cli-0.3.0/README.md +247 -0
  3. flowai_cli-0.3.0/SECURITY.md +110 -0
  4. flowai_cli-0.2.0/examples/02_nvidia_deepseek.py → flowai_cli-0.3.0/examples/02_nvidia.py +13 -6
  5. {flowai_cli-0.2.0 → flowai_cli-0.3.0}/examples/04_fastmcp_agent.py +1 -1
  6. {flowai_cli-0.2.0 → flowai_cli-0.3.0}/flowai/__init__.py +1 -1
  7. {flowai_cli-0.2.0 → flowai_cli-0.3.0}/flowai/agents/standard.py +9 -1
  8. flowai_cli-0.3.0/flowai/cli/main.py +918 -0
  9. flowai_cli-0.3.0/flowai/cli/theme.py +135 -0
  10. flowai_cli-0.3.0/flowai/cli/tui.py +666 -0
  11. flowai_cli-0.3.0/flowai/cli/ui.py +216 -0
  12. {flowai_cli-0.2.0 → flowai_cli-0.3.0}/flowai/config/settings.py +92 -31
  13. {flowai_cli-0.2.0 → flowai_cli-0.3.0}/flowai/mcp/client.py +7 -4
  14. {flowai_cli-0.2.0 → flowai_cli-0.3.0}/flowai/providers/anthropic.py +2 -1
  15. {flowai_cli-0.2.0 → flowai_cli-0.3.0}/flowai/providers/base.py +18 -1
  16. {flowai_cli-0.2.0 → flowai_cli-0.3.0}/flowai/providers/gemini.py +13 -7
  17. flowai_cli-0.3.0/flowai/providers/nvidia.py +161 -0
  18. flowai_cli-0.3.0/flowai/providers/openai.py +486 -0
  19. {flowai_cli-0.2.0 → flowai_cli-0.3.0}/flowai/runtime/session.py +12 -4
  20. flowai_cli-0.3.0/flowai/security/__init__.py +43 -0
  21. flowai_cli-0.3.0/flowai/security/keystore.py +238 -0
  22. flowai_cli-0.3.0/flowai/security/policy.py +264 -0
  23. flowai_cli-0.3.0/flowai/security/secrets.py +160 -0
  24. flowai_cli-0.3.0/flowai/security/shield.py +106 -0
  25. flowai_cli-0.3.0/flowai/server/app.py +171 -0
  26. {flowai_cli-0.2.0 → flowai_cli-0.3.0}/flowai/tools/base.py +2 -0
  27. flowai_cli-0.3.0/flowai/tools/builtin.py +172 -0
  28. {flowai_cli-0.2.0 → flowai_cli-0.3.0}/flowai/tools/files.py +7 -1
  29. {flowai_cli-0.2.0 → flowai_cli-0.3.0}/flowai/tools/registry.py +48 -10
  30. {flowai_cli-0.2.0 → flowai_cli-0.3.0}/flowai/tools/web.py +45 -10
  31. {flowai_cli-0.2.0 → flowai_cli-0.3.0}/flowai.yaml +1 -1
  32. {flowai_cli-0.2.0 → flowai_cli-0.3.0}/pyproject.toml +5 -2
  33. flowai_cli-0.3.0/tests/conftest.py +9 -0
  34. {flowai_cli-0.2.0 → flowai_cli-0.3.0}/tests/test_builtin_tools.py +23 -4
  35. {flowai_cli-0.2.0 → flowai_cli-0.3.0}/tests/test_cli.py +62 -9
  36. flowai_cli-0.3.0/tests/test_nvidia.py +261 -0
  37. flowai_cli-0.3.0/tests/test_security.py +261 -0
  38. flowai_cli-0.3.0/tests/test_server.py +59 -0
  39. flowai_cli-0.3.0/tests/test_tui.py +176 -0
  40. flowai_cli-0.2.0/PKG-INFO +0 -223
  41. flowai_cli-0.2.0/README.md +0 -171
  42. flowai_cli-0.2.0/flowai/cli/main.py +0 -562
  43. flowai_cli-0.2.0/flowai/cli/ui.py +0 -98
  44. flowai_cli-0.2.0/flowai/providers/nvidia.py +0 -57
  45. flowai_cli-0.2.0/flowai/providers/openai.py +0 -274
  46. flowai_cli-0.2.0/flowai/server/app.py +0 -133
  47. flowai_cli-0.2.0/flowai/tools/builtin.py +0 -128
  48. flowai_cli-0.2.0/tests/test_server.py +0 -38
  49. {flowai_cli-0.2.0 → flowai_cli-0.3.0}/.gitignore +0 -0
  50. {flowai_cli-0.2.0 → flowai_cli-0.3.0}/LICENSE +0 -0
  51. {flowai_cli-0.2.0 → flowai_cli-0.3.0}/examples/01_quickstart.py +0 -0
  52. {flowai_cli-0.2.0 → flowai_cli-0.3.0}/examples/03_workflow_graph.py +0 -0
  53. {flowai_cli-0.2.0 → flowai_cli-0.3.0}/examples/05_debate_and_swarm.py +0 -0
  54. {flowai_cli-0.2.0 → flowai_cli-0.3.0}/flowai/__main__.py +0 -0
  55. {flowai_cli-0.2.0 → flowai_cli-0.3.0}/flowai/agents/__init__.py +0 -0
  56. {flowai_cli-0.2.0 → flowai_cli-0.3.0}/flowai/agents/base.py +0 -0
  57. {flowai_cli-0.2.0 → flowai_cli-0.3.0}/flowai/agents/patterns/__init__.py +0 -0
  58. {flowai_cli-0.2.0 → flowai_cli-0.3.0}/flowai/agents/patterns/debate.py +0 -0
  59. {flowai_cli-0.2.0 → flowai_cli-0.3.0}/flowai/agents/patterns/reflection.py +0 -0
  60. {flowai_cli-0.2.0 → flowai_cli-0.3.0}/flowai/agents/specialized.py +0 -0
  61. {flowai_cli-0.2.0 → flowai_cli-0.3.0}/flowai/cli/__init__.py +0 -0
  62. {flowai_cli-0.2.0 → flowai_cli-0.3.0}/flowai/config/__init__.py +0 -0
  63. {flowai_cli-0.2.0 → flowai_cli-0.3.0}/flowai/errors.py +0 -0
  64. {flowai_cli-0.2.0 → flowai_cli-0.3.0}/flowai/mcp/__init__.py +0 -0
  65. {flowai_cli-0.2.0 → flowai_cli-0.3.0}/flowai/memory/__init__.py +0 -0
  66. {flowai_cli-0.2.0 → flowai_cli-0.3.0}/flowai/memory/base.py +0 -0
  67. {flowai_cli-0.2.0 → flowai_cli-0.3.0}/flowai/memory/chroma.py +0 -0
  68. {flowai_cli-0.2.0 → flowai_cli-0.3.0}/flowai/memory/in_memory.py +0 -0
  69. {flowai_cli-0.2.0 → flowai_cli-0.3.0}/flowai/memory/qdrant.py +0 -0
  70. {flowai_cli-0.2.0 → flowai_cli-0.3.0}/flowai/observability/__init__.py +0 -0
  71. {flowai_cli-0.2.0 → flowai_cli-0.3.0}/flowai/observability/events.py +0 -0
  72. {flowai_cli-0.2.0 → flowai_cli-0.3.0}/flowai/observability/opentelemetry.py +0 -0
  73. {flowai_cli-0.2.0 → flowai_cli-0.3.0}/flowai/observability/tracer.py +0 -0
  74. {flowai_cli-0.2.0 → flowai_cli-0.3.0}/flowai/plugins/__init__.py +0 -0
  75. {flowai_cli-0.2.0 → flowai_cli-0.3.0}/flowai/plugins/loader.py +0 -0
  76. {flowai_cli-0.2.0 → flowai_cli-0.3.0}/flowai/providers/__init__.py +0 -0
  77. {flowai_cli-0.2.0 → flowai_cli-0.3.0}/flowai/providers/ollama.py +0 -0
  78. {flowai_cli-0.2.0 → flowai_cli-0.3.0}/flowai/providers/registry.py +0 -0
  79. {flowai_cli-0.2.0 → flowai_cli-0.3.0}/flowai/runtime/__init__.py +0 -0
  80. {flowai_cli-0.2.0 → flowai_cli-0.3.0}/flowai/server/__init__.py +0 -0
  81. {flowai_cli-0.2.0 → flowai_cli-0.3.0}/flowai/tools/__init__.py +0 -0
  82. {flowai_cli-0.2.0 → flowai_cli-0.3.0}/flowai/tools/decorator.py +0 -0
  83. {flowai_cli-0.2.0 → flowai_cli-0.3.0}/flowai/workflow/__init__.py +0 -0
  84. {flowai_cli-0.2.0 → flowai_cli-0.3.0}/flowai/workflow/checkpoint.py +0 -0
  85. {flowai_cli-0.2.0 → flowai_cli-0.3.0}/flowai/workflow/engine.py +0 -0
  86. {flowai_cli-0.2.0 → flowai_cli-0.3.0}/flowai/workflow/state.py +0 -0
  87. {flowai_cli-0.2.0 → flowai_cli-0.3.0}/tests/test_agents.py +0 -0
  88. {flowai_cli-0.2.0 → flowai_cli-0.3.0}/tests/test_memory.py +0 -0
  89. {flowai_cli-0.2.0 → flowai_cli-0.3.0}/tests/test_observability.py +0 -0
  90. {flowai_cli-0.2.0 → flowai_cli-0.3.0}/tests/test_session.py +0 -0
  91. {flowai_cli-0.2.0 → flowai_cli-0.3.0}/tests/test_streaming.py +0 -0
  92. {flowai_cli-0.2.0 → flowai_cli-0.3.0}/tests/test_tools.py +0 -0
  93. {flowai_cli-0.2.0 → flowai_cli-0.3.0}/tests/test_workflow.py +0 -0
@@ -0,0 +1,302 @@
1
+ Metadata-Version: 2.5
2
+ Name: flowai-cli
3
+ Version: 0.3.0
4
+ Summary: FlowAI: an open-source, extensible Python agent harness with a terminal-first CLI.
5
+ Project-URL: Homepage, https://github.com/Ajayramaswamy2005/FlowAI
6
+ Project-URL: Source, https://github.com/Ajayramaswamy2005/FlowAI
7
+ Project-URL: Issues, https://github.com/Ajayramaswamy2005/FlowAI/issues
8
+ Author: Ajay Rama Swamy Katragaada
9
+ License: MIT
10
+ License-File: LICENSE
11
+ Keywords: agent,cli,fastapi,llm,mcp,nvidia,ollama,pydantic,workflow
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: Environment :: Console
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: License :: OSI Approved :: MIT License
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Programming Language :: Python :: 3.13
20
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
21
+ Requires-Python: >=3.10
22
+ Requires-Dist: httpx>=0.27.0
23
+ Requires-Dist: keyring>=24.0
24
+ Requires-Dist: prompt-toolkit>=3.0.43
25
+ Requires-Dist: pydantic>=2.7.0
26
+ Requires-Dist: pyyaml>=6.0.1
27
+ Requires-Dist: rich>=13.7
28
+ Provides-Extra: all
29
+ Requires-Dist: chromadb>=0.5.0; extra == 'all'
30
+ Requires-Dist: fastapi>=0.111.0; extra == 'all'
31
+ Requires-Dist: mcp>=1.0.0; extra == 'all'
32
+ Requires-Dist: opentelemetry-api>=1.25.0; extra == 'all'
33
+ Requires-Dist: opentelemetry-sdk>=1.25.0; extra == 'all'
34
+ Requires-Dist: qdrant-client>=1.9.0; extra == 'all'
35
+ Requires-Dist: uvicorn[standard]>=0.30.0; extra == 'all'
36
+ Provides-Extra: chroma
37
+ Requires-Dist: chromadb>=0.5.0; extra == 'chroma'
38
+ Provides-Extra: dev
39
+ Requires-Dist: fastapi>=0.111.0; extra == 'dev'
40
+ Requires-Dist: httpx>=0.27.0; extra == 'dev'
41
+ Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
42
+ Requires-Dist: pytest>=8.0; extra == 'dev'
43
+ Requires-Dist: ruff>=0.5; extra == 'dev'
44
+ Provides-Extra: mcp
45
+ Requires-Dist: mcp>=1.0.0; extra == 'mcp'
46
+ Provides-Extra: observability
47
+ Requires-Dist: opentelemetry-api>=1.25.0; extra == 'observability'
48
+ Requires-Dist: opentelemetry-sdk>=1.25.0; extra == 'observability'
49
+ Provides-Extra: qdrant
50
+ Requires-Dist: qdrant-client>=1.9.0; extra == 'qdrant'
51
+ Provides-Extra: server
52
+ Requires-Dist: fastapi>=0.111.0; extra == 'server'
53
+ Requires-Dist: uvicorn[standard]>=0.30.0; extra == 'server'
54
+ Description-Content-Type: text/markdown
55
+
56
+ # FlowAI
57
+
58
+ FlowAI is an AI agent you run in your terminal, in the same spirit as Claude
59
+ Code or OpenCode. Bring your own key from Anthropic, OpenAI, NVIDIA, Google
60
+ Gemini or OpenRouter, or run fully local with Ollama or your own NVIDIA NIM.
61
+ The agent can read, search and edit the files in your project, run shell
62
+ commands (after asking you), and search the web, while a security layer keeps
63
+ your API keys out of its reach.
64
+
65
+ It is also a Python library: the same agents, tools, workflows and memory the
66
+ CLI uses are yours to build on.
67
+
68
+ ## Install
69
+
70
+ ```bash
71
+ pip install flowai-cli
72
+ pipx install flowai-cli # same, in its own isolated environment
73
+ ```
74
+
75
+ Python 3.10 or newer. The package is `flowai-cli` (the name `flowai` is taken on PyPI);
76
+ the command it installs is `flowai`, and the import is `import flowai`.
77
+
78
+ ## First run
79
+
80
+ ```bash
81
+ flowai
82
+ ```
83
+
84
+ The first time, FlowAI asks for a provider and an API key, stores the key in
85
+ your operating system's credential store, and sends one tiny request to check
86
+ it works. After that, `flowai` opens a session in the current directory.
87
+
88
+ ## The interface
89
+
90
+ A FlowAI session is drawn as a current: every turn flows down one rail.
91
+
92
+ ```
93
+ █▀▀ █ █▀█ █ █ █ ▄▀█ █ v0.3.0
94
+ █▀ █▄▄ █▄█ ▀▄▀▄▀ █▀█ █ an agent that moves with your code
95
+ ▅▇█▇▅▂▁▁▃▆▇█▇▅▂▁▁▃▆▇█▇▅▂▁▁▃▆██▇▅▂▁▁▃▆██▇▄▂▁▁▃▆
96
+
97
+ model nvidia › nvidia/nemotron-3.5-lightning-30b-a3b
98
+ workspace C:\Users\you\project
99
+ shield armed · key in Windows Credential Manager · approvals ask
100
+
101
+ ◆ fix the failing test in tests/test_api.py
102
+ ≋ undertow thought for 2.1s · 48 words /think to read it
103
+ ◇ read_file tests/test_api.py 0.0s ✓ 120 lines
104
+ ◇ bash pytest -q tests/test_api.py 1.9s ✗ exit 1
105
+
106
+ ┃ GATE edit_file wants to run risk LOW
107
+ ┃ src/api.py
108
+ ┃ - return a - b
109
+ ┃ + return a + b
110
+ ┃ [y] allow once (default) [a] always allow edit_file [n] deny
111
+ ● flowai
112
+ │ The bug was a sign error in add(). All 12 tests pass now.
113
+ ╰─ 6.3s · 3 tools · 4.1k tok tide ▂ 18%
114
+ ```
115
+
116
+ - **◆** is your prompt. **≋ undertow** is the model's reasoning, folded to one
117
+ line (`/think` reads it in full, `/think on` shows it live).
118
+ - **◇** is a tool call, with its timing and outcome.
119
+ - The **gate** asks before anything risky runs. Its rail is coloured by risk,
120
+ it shows commands with syntax highlighting and edits as a diff, and it says
121
+ why a call is risky.
122
+ - The answer is rendered markdown. While the model works, a swell of block
123
+ characters moves along the rail.
124
+ - The **tide** gauge shows how full the model's context window is. The strip at
125
+ the bottom of the prompt shows the model, tide, tokens spent and shield state.
126
+ - `/` completes commands. Esc then Enter adds a new line. Prompt history is
127
+ kept, with any pasted key masked.
128
+
129
+ Piped output, CI logs and `--plain` get plain text instead. `NO_COLOR` and
130
+ `FLOWAI_NO_ANIM` are respected.
131
+
132
+ ## NVIDIA
133
+
134
+ FlowAI works with every kind of NVIDIA key and endpoint:
135
+
136
+ | You have | Do this |
137
+ |---|---|
138
+ | A key from build.nvidia.com (`nvapi-...`) | `flowai configure`, pick NVIDIA, paste it |
139
+ | The key in an environment variable | Any of `NVIDIA_API_KEY`, `NVAPI_KEY`, `NGC_API_KEY`, `NIM_API_KEY` works |
140
+ | A NIM running on your own machine or cluster | `NVIDIA_BASE_URL=http://localhost:8000/v1` (or `flowai configure --provider nvidia --base-url ...`); no key needed |
141
+
142
+ The catalog's models do not all accept the same requests, and FlowAI adapts to
143
+ each one:
144
+
145
+ - **No tool support.** A model that refuses `tools` is sent the conversation
146
+ without them, and you are told so. The same goes for `stream_options`, the
147
+ `system` role and `max_tokens`. Any chat model on the catalog can be used.
148
+ - **Reasoning models** (Nemotron, DeepSeek, GPT-OSS) stream their thinking
149
+ separately, whether it arrives as `reasoning_content` or inside `<think>` tags.
150
+ - **Model names.** A bare name such as `glm-5.3-flash` is resolved to its
151
+ catalog id (`z-ai/glm-5.3-flash`).
152
+ - **Unavailable models.** Some models are listed but not available to every
153
+ account, and retired ones answer 410. FlowAI then suggests models your key
154
+ can call, and `flowai configure` finds a working one for you.
155
+ - **Rate limits.** The free tier's limits are retried with backoff,
156
+ honouring `Retry-After`.
157
+ - **Key mistakes** are named: a key without the `nvapi-` prefix (an old NGC
158
+ key cannot call the hosted API), or stray whitespace around it.
159
+
160
+ `flowai models nvidia [filter]` lists the catalog. In a session, `/models` does
161
+ the same, and `/model <id>` switches without losing the conversation.
162
+
163
+ ## Security
164
+
165
+ An agent that runs commands on your machine is an obvious target. Someone
166
+ would like it to read your API key and send it somewhere, usually by hiding
167
+ instructions in a file or web page the agent reads. FlowAI defends in layers:
168
+
169
+ | Layer | What it does |
170
+ |---|---|
171
+ | Key vault | Keys live in Windows Credential Manager, the macOS Keychain or the Secret Service. Without one, they go in a file only your user can read. Keys are never copied into the environment |
172
+ | Scrubbed children | `bash`, `python_repl` and MCP servers start with every credential-looking variable removed. Python runs in a separate process, so it cannot reach into FlowAI's memory |
173
+ | Redaction | Every key FlowAI knows, plus the shapes of common keys (`nvapi-`, `sk-`, `sk-ant-`, `AIza`, `ghp_`...), is masked in tool output before the model sees it. That includes base64, hex, reversed and URL-encoded forms. Transcripts, prompt history and errors are masked too |
174
+ | Tripwires | Dumping the environment, reading `.env`, SSH keys, the credential store or FlowAI's own config always asks you, even with `--yolo`. With no terminal attached, the answer is no |
175
+ | Exfiltration block | A call that reads secrets and sends data over the network in one go is refused outright, as is any tool call whose arguments contain a key |
176
+ | Network guard | `fetch_url` refuses localhost, private networks and cloud metadata addresses, checking every redirect |
177
+ | Injection fence | Web and MCP output is wrapped as untrusted data, and the system prompt tells the model to treat it that way |
178
+ | Terminal hygiene | Escape sequences are stripped from everything drawn, so a page cannot rewrite your clipboard or hide text |
179
+ | Server | `flowai serve` requires a bearer token, refuses non-local `Host` headers (DNS rebinding), and runs no shell tools unless started with `--allow-shell` |
180
+
181
+ `flowai shield` audits your setup and says what to fix. `flowai shield --fix`
182
+ moves plain-text keys into the keyring and locks the config directory, and
183
+ `flowai logout` deletes a saved key. These are tripwires, not a sandbox: read
184
+ [SECURITY.md](SECURITY.md) for what they do not cover.
185
+
186
+ ## Commands
187
+
188
+ | Command | What it does |
189
+ |---|---|
190
+ | `flowai` | Interactive session in the current directory |
191
+ | `flowai "add type hints to utils.py"` | Run one task and exit |
192
+ | `flowai chat --resume` (or `-c`) | Continue the most recent session |
193
+ | `flowai run -q "..."` | One task, print only the final answer (good for scripts) |
194
+ | `echo "..." \| flowai run` | Read the prompt from stdin |
195
+ | `flowai configure` | Choose provider, model and API key |
196
+ | `flowai models [provider] [filter]` | List the models a provider serves |
197
+ | `flowai shield [--fix]` | Audit how your keys are protected |
198
+ | `flowai logout [provider]` | Delete a saved key |
199
+ | `flowai init [dir]` | Create a `flowai.yaml` and a starter `main.py` |
200
+ | `flowai inspect` | Show config status, saved sessions, checkpoints |
201
+ | `flowai tools` | List the built-in tools |
202
+ | `flowai serve` | HTTP API with streaming (`pip install 'flowai-cli[server]'`) |
203
+
204
+ Options for `chat` and `run`: `--provider`, `--model`, `-w/--workspace DIR`,
205
+ `--yolo` (do not ask before shell/python run; secret access still asks),
206
+ `--no-tools`, `--max-iterations N`, `--plain`.
207
+
208
+ Inside a session: `/help`, `/model <id>`, `/models [filter]`,
209
+ `/provider <name> [model]`, `/tools`, `/think [on|off]`, `/cost`, `/shield`,
210
+ `/new`, `/reset`, `/clear`, `/yolo`, `/exit`.
211
+
212
+ ## What the agent can do
213
+
214
+ | Tool | Notes |
215
+ |---|---|
216
+ | `read_file`, `list_dir`, `glob_files`, `grep_files` | Read-only; never ask, except for files that hold secrets |
217
+ | `write_file`, `edit_file` | Change files inside the workspace only |
218
+ | `bash` | Asks first. Runs in the workspace, 120 s timeout |
219
+ | `python_repl` | Asks first. Runs in a separate interpreter, 60 s timeout |
220
+ | `search_web`, `fetch_url` | DuckDuckGo search and public page fetch |
221
+
222
+ Every file path is resolved against the workspace (the directory you started
223
+ in, or `-w`) and refused if it points outside.
224
+
225
+ ## Configuration
226
+
227
+ Settings are layered, later wins:
228
+
229
+ 1. `~/.flowai/config.json`, written by `flowai configure` (keys themselves go
230
+ to the OS keyring)
231
+ 2. `flowai.yaml` (or `.yml`, `.toml`, `.json`) in the current directory or any
232
+ parent, so it works from a subdirectory of your project
233
+ 3. Environment: `FLOWAI_PROVIDER`, `FLOWAI_MODEL`, and the provider key variables
234
+
235
+ See [flowai.yaml](flowai.yaml) for every option, including `mcp_servers` for
236
+ borrowing tools from Model Context Protocol servers (`pip install 'flowai-cli[mcp]'`).
237
+ Never put a key in `flowai.yaml`; `flowai shield` flags it if you do.
238
+
239
+ `FLOWAI_HOME` moves `~/.flowai`. `FLOWAI_PASS_ENV=GITHUB_TOKEN,...` hands
240
+ named variables to the agent's shell on purpose. `FLOWAI_KEYRING=0` turns
241
+ the keyring off.
242
+
243
+ ## Using it as a library
244
+
245
+ ```python
246
+ import asyncio
247
+ from flowai import Agent, tool
248
+
249
+ @tool
250
+ def compound_interest(principal: float, rate: float, years: int) -> float:
251
+ """Balance after compounding yearly."""
252
+ return round(principal * (1 + rate) ** years, 2)
253
+
254
+ agent = Agent(
255
+ name="Advisor",
256
+ provider="nvidia", # anthropic, ollama, openai, gemini, openrouter
257
+ model="nvidia/nemotron-3.5-lightning-30b-a3b",
258
+ tools=[compound_interest],
259
+ )
260
+
261
+ print(asyncio.run(agent.run("What will $5,000 be after 10 years at 8%?")))
262
+ ```
263
+
264
+ `agent.stream(prompt)` yields tokens as they arrive (and `reasoning_delta` for
265
+ reasoning models) and runs tool calls along the way. Tools you register in
266
+ your own code run without an approval prompt (you wrote them). To change that,
267
+ give a `ToolRegistry` an `approver` callback; to add the CLI's protections,
268
+ give it a `shield=Shield()` (`from flowai.security import Shield`).
269
+
270
+ Beyond a single agent the library has:
271
+
272
+ - **Specialised agents**: `PlannerAgent`, `CriticAgent`, `ToolAgent`, `SupervisorAgent`
273
+ - **Patterns**: `ReflectionPattern`, `PlanAndExecutePattern`, `DebatePattern`, `Swarm`
274
+ - **Workflows**: a graph engine with conditional edges, loops, parallel
275
+ branches and checkpointing (`Workflow`, `FileCheckpointer`)
276
+ - **Memory**: `InMemory`, `QdrantMemory` (`flowai-cli[qdrant]`), `ChromaMemory` (`flowai-cli[chroma]`)
277
+ - **Observability**: per-span token counts and estimated cost (`agent.tracer`),
278
+ an event bus, and an OpenTelemetry exporter (`flowai-cli[observability]`)
279
+ - **Plugins**: third-party providers via the `flowai.plugins` entry point
280
+
281
+ Runnable examples are in [examples/](examples/).
282
+
283
+ ## Developing FlowAI
284
+
285
+ ```bash
286
+ pip install -e '.[dev]'
287
+ pytest
288
+ ruff check flowai tests
289
+ ```
290
+
291
+ The tests are offline: providers are faked at the HTTP layer, so no key is
292
+ needed, and they never touch your real keyring.
293
+
294
+ ## Releasing to PyPI
295
+
296
+ 1. Bump `version` in `pyproject.toml` and `__version__` in `flowai/__init__.py`.
297
+ 2. Push a tag `flowai-vX.Y.Z`. The `flowai-release` workflow builds the
298
+ package and publishes it with PyPI Trusted Publishing.
299
+
300
+ ## License
301
+
302
+ MIT
@@ -0,0 +1,247 @@
1
+ # FlowAI
2
+
3
+ FlowAI is an AI agent you run in your terminal, in the same spirit as Claude
4
+ Code or OpenCode. Bring your own key from Anthropic, OpenAI, NVIDIA, Google
5
+ Gemini or OpenRouter, or run fully local with Ollama or your own NVIDIA NIM.
6
+ The agent can read, search and edit the files in your project, run shell
7
+ commands (after asking you), and search the web, while a security layer keeps
8
+ your API keys out of its reach.
9
+
10
+ It is also a Python library: the same agents, tools, workflows and memory the
11
+ CLI uses are yours to build on.
12
+
13
+ ## Install
14
+
15
+ ```bash
16
+ pip install flowai-cli
17
+ pipx install flowai-cli # same, in its own isolated environment
18
+ ```
19
+
20
+ Python 3.10 or newer. The package is `flowai-cli` (the name `flowai` is taken on PyPI);
21
+ the command it installs is `flowai`, and the import is `import flowai`.
22
+
23
+ ## First run
24
+
25
+ ```bash
26
+ flowai
27
+ ```
28
+
29
+ The first time, FlowAI asks for a provider and an API key, stores the key in
30
+ your operating system's credential store, and sends one tiny request to check
31
+ it works. After that, `flowai` opens a session in the current directory.
32
+
33
+ ## The interface
34
+
35
+ A FlowAI session is drawn as a current: every turn flows down one rail.
36
+
37
+ ```
38
+ █▀▀ █ █▀█ █ █ █ ▄▀█ █ v0.3.0
39
+ █▀ █▄▄ █▄█ ▀▄▀▄▀ █▀█ █ an agent that moves with your code
40
+ ▅▇█▇▅▂▁▁▃▆▇█▇▅▂▁▁▃▆▇█▇▅▂▁▁▃▆██▇▅▂▁▁▃▆██▇▄▂▁▁▃▆
41
+
42
+ model nvidia › nvidia/nemotron-3.5-lightning-30b-a3b
43
+ workspace C:\Users\you\project
44
+ shield armed · key in Windows Credential Manager · approvals ask
45
+
46
+ ◆ fix the failing test in tests/test_api.py
47
+ ≋ undertow thought for 2.1s · 48 words /think to read it
48
+ ◇ read_file tests/test_api.py 0.0s ✓ 120 lines
49
+ ◇ bash pytest -q tests/test_api.py 1.9s ✗ exit 1
50
+
51
+ ┃ GATE edit_file wants to run risk LOW
52
+ ┃ src/api.py
53
+ ┃ - return a - b
54
+ ┃ + return a + b
55
+ ┃ [y] allow once (default) [a] always allow edit_file [n] deny
56
+ ● flowai
57
+ │ The bug was a sign error in add(). All 12 tests pass now.
58
+ ╰─ 6.3s · 3 tools · 4.1k tok tide ▂ 18%
59
+ ```
60
+
61
+ - **◆** is your prompt. **≋ undertow** is the model's reasoning, folded to one
62
+ line (`/think` reads it in full, `/think on` shows it live).
63
+ - **◇** is a tool call, with its timing and outcome.
64
+ - The **gate** asks before anything risky runs. Its rail is coloured by risk,
65
+ it shows commands with syntax highlighting and edits as a diff, and it says
66
+ why a call is risky.
67
+ - The answer is rendered markdown. While the model works, a swell of block
68
+ characters moves along the rail.
69
+ - The **tide** gauge shows how full the model's context window is. The strip at
70
+ the bottom of the prompt shows the model, tide, tokens spent and shield state.
71
+ - `/` completes commands. Esc then Enter adds a new line. Prompt history is
72
+ kept, with any pasted key masked.
73
+
74
+ Piped output, CI logs and `--plain` get plain text instead. `NO_COLOR` and
75
+ `FLOWAI_NO_ANIM` are respected.
76
+
77
+ ## NVIDIA
78
+
79
+ FlowAI works with every kind of NVIDIA key and endpoint:
80
+
81
+ | You have | Do this |
82
+ |---|---|
83
+ | A key from build.nvidia.com (`nvapi-...`) | `flowai configure`, pick NVIDIA, paste it |
84
+ | The key in an environment variable | Any of `NVIDIA_API_KEY`, `NVAPI_KEY`, `NGC_API_KEY`, `NIM_API_KEY` works |
85
+ | A NIM running on your own machine or cluster | `NVIDIA_BASE_URL=http://localhost:8000/v1` (or `flowai configure --provider nvidia --base-url ...`); no key needed |
86
+
87
+ The catalog's models do not all accept the same requests, and FlowAI adapts to
88
+ each one:
89
+
90
+ - **No tool support.** A model that refuses `tools` is sent the conversation
91
+ without them, and you are told so. The same goes for `stream_options`, the
92
+ `system` role and `max_tokens`. Any chat model on the catalog can be used.
93
+ - **Reasoning models** (Nemotron, DeepSeek, GPT-OSS) stream their thinking
94
+ separately, whether it arrives as `reasoning_content` or inside `<think>` tags.
95
+ - **Model names.** A bare name such as `glm-5.3-flash` is resolved to its
96
+ catalog id (`z-ai/glm-5.3-flash`).
97
+ - **Unavailable models.** Some models are listed but not available to every
98
+ account, and retired ones answer 410. FlowAI then suggests models your key
99
+ can call, and `flowai configure` finds a working one for you.
100
+ - **Rate limits.** The free tier's limits are retried with backoff,
101
+ honouring `Retry-After`.
102
+ - **Key mistakes** are named: a key without the `nvapi-` prefix (an old NGC
103
+ key cannot call the hosted API), or stray whitespace around it.
104
+
105
+ `flowai models nvidia [filter]` lists the catalog. In a session, `/models` does
106
+ the same, and `/model <id>` switches without losing the conversation.
107
+
108
+ ## Security
109
+
110
+ An agent that runs commands on your machine is an obvious target. Someone
111
+ would like it to read your API key and send it somewhere, usually by hiding
112
+ instructions in a file or web page the agent reads. FlowAI defends in layers:
113
+
114
+ | Layer | What it does |
115
+ |---|---|
116
+ | Key vault | Keys live in Windows Credential Manager, the macOS Keychain or the Secret Service. Without one, they go in a file only your user can read. Keys are never copied into the environment |
117
+ | Scrubbed children | `bash`, `python_repl` and MCP servers start with every credential-looking variable removed. Python runs in a separate process, so it cannot reach into FlowAI's memory |
118
+ | Redaction | Every key FlowAI knows, plus the shapes of common keys (`nvapi-`, `sk-`, `sk-ant-`, `AIza`, `ghp_`...), is masked in tool output before the model sees it. That includes base64, hex, reversed and URL-encoded forms. Transcripts, prompt history and errors are masked too |
119
+ | Tripwires | Dumping the environment, reading `.env`, SSH keys, the credential store or FlowAI's own config always asks you, even with `--yolo`. With no terminal attached, the answer is no |
120
+ | Exfiltration block | A call that reads secrets and sends data over the network in one go is refused outright, as is any tool call whose arguments contain a key |
121
+ | Network guard | `fetch_url` refuses localhost, private networks and cloud metadata addresses, checking every redirect |
122
+ | Injection fence | Web and MCP output is wrapped as untrusted data, and the system prompt tells the model to treat it that way |
123
+ | Terminal hygiene | Escape sequences are stripped from everything drawn, so a page cannot rewrite your clipboard or hide text |
124
+ | Server | `flowai serve` requires a bearer token, refuses non-local `Host` headers (DNS rebinding), and runs no shell tools unless started with `--allow-shell` |
125
+
126
+ `flowai shield` audits your setup and says what to fix. `flowai shield --fix`
127
+ moves plain-text keys into the keyring and locks the config directory, and
128
+ `flowai logout` deletes a saved key. These are tripwires, not a sandbox: read
129
+ [SECURITY.md](SECURITY.md) for what they do not cover.
130
+
131
+ ## Commands
132
+
133
+ | Command | What it does |
134
+ |---|---|
135
+ | `flowai` | Interactive session in the current directory |
136
+ | `flowai "add type hints to utils.py"` | Run one task and exit |
137
+ | `flowai chat --resume` (or `-c`) | Continue the most recent session |
138
+ | `flowai run -q "..."` | One task, print only the final answer (good for scripts) |
139
+ | `echo "..." \| flowai run` | Read the prompt from stdin |
140
+ | `flowai configure` | Choose provider, model and API key |
141
+ | `flowai models [provider] [filter]` | List the models a provider serves |
142
+ | `flowai shield [--fix]` | Audit how your keys are protected |
143
+ | `flowai logout [provider]` | Delete a saved key |
144
+ | `flowai init [dir]` | Create a `flowai.yaml` and a starter `main.py` |
145
+ | `flowai inspect` | Show config status, saved sessions, checkpoints |
146
+ | `flowai tools` | List the built-in tools |
147
+ | `flowai serve` | HTTP API with streaming (`pip install 'flowai-cli[server]'`) |
148
+
149
+ Options for `chat` and `run`: `--provider`, `--model`, `-w/--workspace DIR`,
150
+ `--yolo` (do not ask before shell/python run; secret access still asks),
151
+ `--no-tools`, `--max-iterations N`, `--plain`.
152
+
153
+ Inside a session: `/help`, `/model <id>`, `/models [filter]`,
154
+ `/provider <name> [model]`, `/tools`, `/think [on|off]`, `/cost`, `/shield`,
155
+ `/new`, `/reset`, `/clear`, `/yolo`, `/exit`.
156
+
157
+ ## What the agent can do
158
+
159
+ | Tool | Notes |
160
+ |---|---|
161
+ | `read_file`, `list_dir`, `glob_files`, `grep_files` | Read-only; never ask, except for files that hold secrets |
162
+ | `write_file`, `edit_file` | Change files inside the workspace only |
163
+ | `bash` | Asks first. Runs in the workspace, 120 s timeout |
164
+ | `python_repl` | Asks first. Runs in a separate interpreter, 60 s timeout |
165
+ | `search_web`, `fetch_url` | DuckDuckGo search and public page fetch |
166
+
167
+ Every file path is resolved against the workspace (the directory you started
168
+ in, or `-w`) and refused if it points outside.
169
+
170
+ ## Configuration
171
+
172
+ Settings are layered, later wins:
173
+
174
+ 1. `~/.flowai/config.json`, written by `flowai configure` (keys themselves go
175
+ to the OS keyring)
176
+ 2. `flowai.yaml` (or `.yml`, `.toml`, `.json`) in the current directory or any
177
+ parent, so it works from a subdirectory of your project
178
+ 3. Environment: `FLOWAI_PROVIDER`, `FLOWAI_MODEL`, and the provider key variables
179
+
180
+ See [flowai.yaml](flowai.yaml) for every option, including `mcp_servers` for
181
+ borrowing tools from Model Context Protocol servers (`pip install 'flowai-cli[mcp]'`).
182
+ Never put a key in `flowai.yaml`; `flowai shield` flags it if you do.
183
+
184
+ `FLOWAI_HOME` moves `~/.flowai`. `FLOWAI_PASS_ENV=GITHUB_TOKEN,...` hands
185
+ named variables to the agent's shell on purpose. `FLOWAI_KEYRING=0` turns
186
+ the keyring off.
187
+
188
+ ## Using it as a library
189
+
190
+ ```python
191
+ import asyncio
192
+ from flowai import Agent, tool
193
+
194
+ @tool
195
+ def compound_interest(principal: float, rate: float, years: int) -> float:
196
+ """Balance after compounding yearly."""
197
+ return round(principal * (1 + rate) ** years, 2)
198
+
199
+ agent = Agent(
200
+ name="Advisor",
201
+ provider="nvidia", # anthropic, ollama, openai, gemini, openrouter
202
+ model="nvidia/nemotron-3.5-lightning-30b-a3b",
203
+ tools=[compound_interest],
204
+ )
205
+
206
+ print(asyncio.run(agent.run("What will $5,000 be after 10 years at 8%?")))
207
+ ```
208
+
209
+ `agent.stream(prompt)` yields tokens as they arrive (and `reasoning_delta` for
210
+ reasoning models) and runs tool calls along the way. Tools you register in
211
+ your own code run without an approval prompt (you wrote them). To change that,
212
+ give a `ToolRegistry` an `approver` callback; to add the CLI's protections,
213
+ give it a `shield=Shield()` (`from flowai.security import Shield`).
214
+
215
+ Beyond a single agent the library has:
216
+
217
+ - **Specialised agents**: `PlannerAgent`, `CriticAgent`, `ToolAgent`, `SupervisorAgent`
218
+ - **Patterns**: `ReflectionPattern`, `PlanAndExecutePattern`, `DebatePattern`, `Swarm`
219
+ - **Workflows**: a graph engine with conditional edges, loops, parallel
220
+ branches and checkpointing (`Workflow`, `FileCheckpointer`)
221
+ - **Memory**: `InMemory`, `QdrantMemory` (`flowai-cli[qdrant]`), `ChromaMemory` (`flowai-cli[chroma]`)
222
+ - **Observability**: per-span token counts and estimated cost (`agent.tracer`),
223
+ an event bus, and an OpenTelemetry exporter (`flowai-cli[observability]`)
224
+ - **Plugins**: third-party providers via the `flowai.plugins` entry point
225
+
226
+ Runnable examples are in [examples/](examples/).
227
+
228
+ ## Developing FlowAI
229
+
230
+ ```bash
231
+ pip install -e '.[dev]'
232
+ pytest
233
+ ruff check flowai tests
234
+ ```
235
+
236
+ The tests are offline: providers are faked at the HTTP layer, so no key is
237
+ needed, and they never touch your real keyring.
238
+
239
+ ## Releasing to PyPI
240
+
241
+ 1. Bump `version` in `pyproject.toml` and `__version__` in `flowai/__init__.py`.
242
+ 2. Push a tag `flowai-vX.Y.Z`. The `flowai-release` workflow builds the
243
+ package and publishes it with PyPI Trusted Publishing.
244
+
245
+ ## License
246
+
247
+ MIT
@@ -0,0 +1,110 @@
1
+ # Security
2
+
3
+ FlowAI is an agent that reads files, runs commands and fetches web pages on
4
+ your machine, using your API key. This page says what FlowAI does to protect
5
+ that key and that machine, and where the protection stops.
6
+
7
+ ## Reporting a vulnerability
8
+
9
+ Please do not open a public issue. Use GitHub's private vulnerability
10
+ reporting on this repository (Security tab, "Report a vulnerability"). Include
11
+ the FlowAI version (`flowai --version`), your OS, and the smallest prompt,
12
+ file or command that shows the problem.
13
+
14
+ ## The threat
15
+
16
+ The usual attack is **prompt injection**. An attacker cannot type into your
17
+ session, but they can write text the agent will read: a README in a cloned
18
+ repository, a comment in a web page, an issue body, the output of an MCP tool.
19
+ That text tells the model to read your API key and send it somewhere. Others
20
+ aim for persistence (git hooks, shell startup files) or for internal services
21
+ (`localhost`, your router, cloud metadata at 169.254.169.254).
22
+
23
+ ## What FlowAI does
24
+
25
+ **Keys are stored out of reach.**
26
+ - `flowai configure` saves keys in the OS credential store: Windows Credential
27
+ Manager, the macOS Keychain, or the Secret Service on Linux. The config file
28
+ holds only a marker.
29
+ - Without a keyring, the key goes into `~/.flowai/config.json`. The file is
30
+ created with owner-only permissions (mode 0600 on POSIX, a user-only ACL on
31
+ Windows) and written atomically.
32
+ - Keys are never placed in `os.environ`, so nothing FlowAI starts inherits them.
33
+ - Gemini keys travel in a header, not the URL, so they do not show up in
34
+ error messages or proxy logs.
35
+
36
+ **Tools cannot see keys.**
37
+ - `bash`, `python_repl` and MCP servers start with every credential-looking
38
+ environment variable removed (`*_API_KEY`, `*_TOKEN`, `*_SECRET`,
39
+ `*PASSWORD*` and similar). `FLOWAI_PASS_ENV` lets you pass named ones through
40
+ on purpose.
41
+ - `python_repl` runs in a separate interpreter, so it cannot import FlowAI and
42
+ read keys from its memory.
43
+ - `grep_files` never opens files that usually hold credentials (`.env`, `*.pem`,
44
+ `id_rsa`, `credentials.json`...). Reading one with `read_file` asks you first.
45
+
46
+ **The model never learns a key.**
47
+ - Every tool result is redacted before the model sees it. Redaction covers
48
+ each key FlowAI holds, any variable whose name looks like a credential, and
49
+ the shapes of common keys.
50
+ - Encoded copies are caught too: base64 at any alignment, hex, reversed and
51
+ URL-encoded.
52
+ - So even when a model is tricked into reading a key file, it sees
53
+ `[REDACTED:NVIDIA_API_KEY]`, and it has nothing to exfiltrate.
54
+ - Session transcripts, prompt history and error messages are redacted the same way.
55
+
56
+ **Dangerous calls are stopped or confirmed.**
57
+ - A tool call whose arguments contain a known key is refused.
58
+ - A command that reads secrets and sends data over the network in the same
59
+ call is refused (`cat .env | curl -d @- ...`).
60
+ - Commands that dump the environment, read credential files or stores, or
61
+ touch `~/.flowai` are rated critical. They ask you every time, also in
62
+ `--yolo` mode, and "always allow" is not offered for them.
63
+ - Uploads, remote scripts (`curl ... | sh`), destructive commands, privilege
64
+ escalation and persistence (git hooks, cron, shell profiles, the registry)
65
+ are rated high. The approval gate names the risk in plain words.
66
+ - With no terminal attached, nothing that needs approval runs.
67
+
68
+ **The network is fenced.**
69
+ - `fetch_url` only reaches public addresses. It resolves every host and
70
+ re-checks every redirect, refusing loopback, private ranges, link-local and
71
+ metadata addresses, and URLs with embedded credentials.
72
+ - Output from `fetch_url`, `search_web` and MCP tools is wrapped as untrusted
73
+ content. The system prompt tells the model never to follow instructions
74
+ found inside it.
75
+
76
+ **The terminal is protected.** Escape sequences and control characters are
77
+ stripped from model and tool output before they are drawn. A fetched page
78
+ cannot set your clipboard (OSC 52), retitle the window or hide text. Markdown
79
+ links are shown as text and URL, never as disguised hyperlinks.
80
+
81
+ **The HTTP server is closed by default.** `flowai serve`:
82
+ - binds to 127.0.0.1 and requires `Authorization: Bearer <token>` on every
83
+ route except `/health`
84
+ - rejects requests whose `Host` header is not local, which defeats DNS rebinding
85
+ - refuses shell and python tools unless started with `--allow-shell`
86
+ - never runs calls that touch secrets on behalf of an HTTP client
87
+
88
+ ## What FlowAI does not do
89
+
90
+ - **It is not a sandbox.** When you approve a shell command, it runs with your
91
+ user's full permissions. The risk rules are pattern-based tripwires: a
92
+ determined attacker can write a command they do not recognise. Read what the
93
+ gate shows you, and keep `--yolo` for throwaway environments.
94
+ - Redaction only knows the keys FlowAI holds and the formats it recognises. A
95
+ password typed into a file the agent reads, in a format it cannot recognise,
96
+ will reach the model.
97
+ - Name resolution is checked before connecting, but a hostile DNS server can
98
+ still answer differently on the second lookup (a rebinding race).
99
+ - MCP servers are programs you chose to run. FlowAI scrubs their environment
100
+ and asks before each of their tool calls, but cannot see what they do inside.
101
+ - A key you export in your shell is visible to any program you run in that
102
+ shell, FlowAI or not. Prefer `flowai configure`.
103
+
104
+ ## Checking your setup
105
+
106
+ ```bash
107
+ flowai shield # what protects your keys right now
108
+ flowai shield --fix # move plain-text keys into the keyring, lock permissions
109
+ flowai logout nvidia # delete a saved key
110
+ ```