gravity-cli 0.1.0__py3-none-any.whl
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.
- gravity_cli/__init__.py +11 -0
- gravity_cli/__main__.py +4 -0
- gravity_cli/_runner_script.py +195 -0
- gravity_cli/api.py +88 -0
- gravity_cli/cli.py +124 -0
- gravity_cli/commands/__init__.py +0 -0
- gravity_cli/commands/adapt.py +155 -0
- gravity_cli/commands/build.py +83 -0
- gravity_cli/commands/init.py +78 -0
- gravity_cli/commands/login.py +58 -0
- gravity_cli/commands/push.py +98 -0
- gravity_cli/commands/run.py +281 -0
- gravity_cli/commands/runs.py +49 -0
- gravity_cli/commands/schedule.py +74 -0
- gravity_cli/commands/tools.py +27 -0
- gravity_cli/commands/validate.py +195 -0
- gravity_cli/config.py +91 -0
- gravity_cli/manifest.py +19 -0
- gravity_cli/project.py +42 -0
- gravity_cli/templates/chat/.gitignore +6 -0
- gravity_cli/templates/chat/manifest.yaml +28 -0
- gravity_cli/templates/chat/requirements.txt +3 -0
- gravity_cli/templates/chat/src/agent.py +80 -0
- gravity_cli/templates/deepagent/.gitignore +6 -0
- gravity_cli/templates/deepagent/manifest.yaml +28 -0
- gravity_cli/templates/deepagent/requirements.txt +4 -0
- gravity_cli/templates/deepagent/src/agent.py +54 -0
- gravity_cli/templates/minimal/.gitignore +6 -0
- gravity_cli/templates/minimal/manifest.yaml +30 -0
- gravity_cli/templates/minimal/requirements.txt +3 -0
- gravity_cli/templates/minimal/src/agent.py +58 -0
- gravity_cli/templates/structured/.gitignore +6 -0
- gravity_cli/templates/structured/manifest.yaml +33 -0
- gravity_cli/templates/structured/requirements.txt +3 -0
- gravity_cli/templates/structured/src/agent.py +15 -0
- gravity_cli/templates/structured/src/gateway.py +26 -0
- gravity_cli/templates/structured/src/nodes/agent.py +20 -0
- gravity_cli/templates/structured/src/prompts.py +6 -0
- gravity_cli/templates/structured/src/state.py +10 -0
- gravity_cli/timeline.py +254 -0
- gravity_cli-0.1.0.dist-info/METADATA +494 -0
- gravity_cli-0.1.0.dist-info/RECORD +46 -0
- gravity_cli-0.1.0.dist-info/WHEEL +4 -0
- gravity_cli-0.1.0.dist-info/entry_points.txt +2 -0
- gravity_cli-0.1.0.dist-info/licenses/LICENSE +202 -0
- gravity_cli-0.1.0.dist-info/licenses/NOTICE +4 -0
|
@@ -0,0 +1,494 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: gravity-cli
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: gravity cli — init, run, and push python agents to the Gravity marketplace
|
|
5
|
+
License-Expression: Apache-2.0
|
|
6
|
+
License-File: LICENSE
|
|
7
|
+
License-File: NOTICE
|
|
8
|
+
Requires-Python: >=3.12
|
|
9
|
+
Requires-Dist: gravity-schema==0.1.0
|
|
10
|
+
Requires-Dist: httpx>=0.27.0
|
|
11
|
+
Requires-Dist: keyring>=25.0.0
|
|
12
|
+
Requires-Dist: python-dotenv>=1.0.0
|
|
13
|
+
Requires-Dist: rich>=13.9.0
|
|
14
|
+
Requires-Dist: typer>=0.15.0
|
|
15
|
+
Requires-Dist: uv>=0.8.0
|
|
16
|
+
Description-Content-Type: text/markdown
|
|
17
|
+
|
|
18
|
+
<div align="center">
|
|
19
|
+
|
|
20
|
+
# Gravity CLI
|
|
21
|
+
|
|
22
|
+
**Build, test and publish AI agents on Gravity.**
|
|
23
|
+
|
|
24
|
+
Write ordinary LangGraph, declare what your agent may touch in one manifest,<br>
|
|
25
|
+
and run it against real tools with those permissions enforced.
|
|
26
|
+
|
|
27
|
+
[](https://pypi.org/project/gravity-cli/)
|
|
28
|
+
[](https://www.python.org/downloads/)
|
|
29
|
+
[](https://www.apache.org/licenses/LICENSE-2.0)
|
|
30
|
+
|
|
31
|
+
</div>
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
pipx install gravity-cli
|
|
35
|
+
gravity init my-agent
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
There is no SDK. Your agent reads three environment variables (`GRAVITY_GATEWAY_URL`, `GRAVITY_LLM_URL`, `GRAVITY_RUN_TOKEN`), and `gravity` is the only Gravity-specific tool you use.
|
|
39
|
+
|
|
40
|
+
Here is `gravity run` on an agent that tried a tool it never declared. The gateway refused the call, and the run carried on:
|
|
41
|
+
|
|
42
|
+
```
|
|
43
|
+
inbox-digest@1.0.0 · run grt_…4e7b · 2 tools declared
|
|
44
|
+
|
|
45
|
+
● read 0.9s
|
|
46
|
+
✓ gmail.read 3 found 0.8s
|
|
47
|
+
● digest 2.3s
|
|
48
|
+
◆ openai/gpt-6-luna 2.1s
|
|
49
|
+
✗ slack.send refused — not in permissions.tools 0.0s
|
|
50
|
+
|
|
51
|
+
╭────────────────────────── result ──────────────────────────╮
|
|
52
|
+
│ unread 3 │
|
|
53
|
+
│ headline Two invoices and a meeting request need replies. │
|
|
54
|
+
╰────────────────────────────────────────────────────────────╯
|
|
55
|
+
✔ done in 3.3s
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
New to building agents? Start with the [builder guide](BUILDER_GUIDE.md). The exact rules are in the [agent standard](AGENT_STANDARD.md), and [templates](TEMPLATES.md) helps you pick a starting point.
|
|
59
|
+
|
|
60
|
+
## Contents
|
|
61
|
+
|
|
62
|
+
- [How it works](#how-it-works)
|
|
63
|
+
- [Installation](#installation)
|
|
64
|
+
- [Quick start](#quick-start)
|
|
65
|
+
- [Environment variables](#environment-variables)
|
|
66
|
+
- [Running a local gateway](#running-a-local-gateway)
|
|
67
|
+
- [Authentication](#authentication)
|
|
68
|
+
- [Commands](#commands)
|
|
69
|
+
- [An agent project](#an-agent-project)
|
|
70
|
+
- [Troubleshooting](#troubleshooting)
|
|
71
|
+
- [Limitations](#limitations)
|
|
72
|
+
- [Development](#development)
|
|
73
|
+
- [Repository layout](#repository-layout)
|
|
74
|
+
- [License](#license)
|
|
75
|
+
|
|
76
|
+
## How it works
|
|
77
|
+
|
|
78
|
+
```
|
|
79
|
+
gravity init scaffold a working agent from a template
|
|
80
|
+
gravity validate check the manifest, entrypoint, lockfile and adapters, locally
|
|
81
|
+
gravity run run it through the gateway, with only the declared tools allowed
|
|
82
|
+
gravity build bundle it into a deterministic build/agent.zip
|
|
83
|
+
gravity push upload exactly that bundle to the control plane
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Three services are involved. The CLI talks to two of them; your agent talks only to the gateway.
|
|
87
|
+
|
|
88
|
+
| Service | What it does | Who talks to it |
|
|
89
|
+
|---|---|---|
|
|
90
|
+
| **Gateway** | Serves tools over MCP (`/mcp`) and models over an OpenAI-compatible API (`/v1`). Mints run tokens and refuses any tool a run was not granted. | `gravity run` mints a token; your agent makes every tool and model call through it |
|
|
91
|
+
| **Control plane** | Accounts, the tool catalog, pushed agents, builds and hosted runs. | `login`, `push`, `tools`, `agents`, `runs`, `logs` |
|
|
92
|
+
| **Your agent** | Your LangGraph code, started by `gravity run` in its own virtual environment. | Sees only a run token scoped to its declared tools |
|
|
93
|
+
|
|
94
|
+
On `gravity run`, the CLI trades your dev token for a run token scoped to `permissions.tools` and starts the agent with only that token. A call to an undeclared tool is refused by the gateway, exactly as it will be when the agent runs hosted. Calls listed under `approvals.required_for` pause until you approve them in the terminal.
|
|
95
|
+
|
|
96
|
+
## Installation
|
|
97
|
+
|
|
98
|
+
Requires Python 3.12 or newer, and [uv](https://docs.astral.sh/uv/) for your agents' own environments.
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
pipx install gravity-cli # recommended: gravity gets its own environment
|
|
102
|
+
uv tool install gravity-cli # the same, with uv
|
|
103
|
+
pip install gravity-cli # into the current environment
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
`gravity` then works from any folder. Upgrade with `pipx upgrade gravity-cli` or `uv tool upgrade gravity-cli`.
|
|
107
|
+
|
|
108
|
+
## Quick start
|
|
109
|
+
|
|
110
|
+
1. Set up `~/.gravity/.env` as described in [Environment variables](#environment-variables). At minimum, `gravity run` needs `GRAVITY_DEV_TOKEN`.
|
|
111
|
+
2. Make sure a gateway is reachable: either a [local one](#running-a-local-gateway) or the hosted dev gateway.
|
|
112
|
+
3. Then:
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
gravity init my-agent # creates ./my-agent from the minimal template
|
|
116
|
+
cd my-agent
|
|
117
|
+
uv venv && uv pip install -r requirements.txt
|
|
118
|
+
|
|
119
|
+
gravity validate # offline checks, compiles requirements.lock
|
|
120
|
+
gravity run --input query="hello" # live timeline of steps, model and tool calls
|
|
121
|
+
gravity build # writes build/agent.zip and build/build.json
|
|
122
|
+
gravity login # once, before your first push
|
|
123
|
+
gravity push # uploads the build; refuses if it is stale
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
## Environment variables
|
|
127
|
+
|
|
128
|
+
### Where to set them
|
|
129
|
+
|
|
130
|
+
Put them in `~/.gravity/.env` (on Windows, `%USERPROFILE%\.gravity\.env`). Every `gravity` command loads this file, from any directory, so you set a value once and never export it again. [`.env.example`](.env.example) in this repository is a commented template you can copy there:
|
|
131
|
+
|
|
132
|
+
```bash
|
|
133
|
+
mkdir -p ~/.gravity
|
|
134
|
+
cp .env.example ~/.gravity/.env # then fill in the values
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
When the same variable is set in more than one place, the first match wins:
|
|
138
|
+
|
|
139
|
+
1. a variable exported in your shell
|
|
140
|
+
2. `~/.gravity/.env`
|
|
141
|
+
3. `~/.gravity/config.json` (only the API URL, written by `gravity --api-url URL <command>`)
|
|
142
|
+
4. the built-in default
|
|
143
|
+
|
|
144
|
+
Never commit a `.env` file. This repository's `.gitignore`, and the one every template ships with, keep `.env` and `.gravity/` out of git.
|
|
145
|
+
|
|
146
|
+
### Which ones you need
|
|
147
|
+
|
|
148
|
+
| You want to... | Commands | Required | Usually also set |
|
|
149
|
+
|---|---|---|---|
|
|
150
|
+
| Scaffold, check and package an agent | `init`, `validate`, `build` | nothing | — |
|
|
151
|
+
| Run an agent locally | `run`, `schedule`, `adapt` | `GRAVITY_DEV_TOKEN` | `GRAVITY_GATEWAY_URL`, unless the gateway is local on port 8000 |
|
|
152
|
+
| Publish and inspect agents | `push`, `tools`, `agents`, `runs`, `logs`, `whoami` | `gravity login`, or `GRAVITY_API_TOKEN` | `GRAVITY_API_URL`, unless the control plane is at `http://localhost:3010` |
|
|
153
|
+
|
|
154
|
+
### Reference
|
|
155
|
+
|
|
156
|
+
| Variable | What it is | Required | Default |
|
|
157
|
+
|---|---|---|---|
|
|
158
|
+
| `GRAVITY_DEV_TOKEN` | Your gateway dev token. `gravity run` trades it for a run token scoped to the manifest's tools. A local gateway prints one at startup, or uses its `LOCAL_DEV_TOKEN`. | For `run`, `schedule`, `adapt` | none |
|
|
159
|
+
| `GRAVITY_GATEWAY_URL` | The gateway's MCP endpoint, ending in `/mcp`. The CLI derives the gateway's base URL from it by removing `/mcp`. | When the gateway is not local | `http://127.0.0.1:8000/mcp` |
|
|
160
|
+
| `GRAVITY_LLM_URL` | The OpenAI-compatible model endpoint. Override it per run with `--llm-url`. | No | the gateway's base URL + `/v1` |
|
|
161
|
+
| `GRAVITY_API_URL` | The control plane's base URL. | When the control plane is not local | `http://localhost:3010` |
|
|
162
|
+
| `GRAVITY_API_TOKEN` | The control plane's dev token. Commands then run as the control plane's dev user without `gravity login`, and it wins over a stored session. For development only. | Instead of `gravity login` | none |
|
|
163
|
+
| `GRAVITY_CONFIG_DIR` | Moves `~/.gravity` elsewhere: the `.env` file, `config.json` and the fallback session file. Useful for tests and CI. | No | `~/.gravity` |
|
|
164
|
+
| `GRAVITY_UNATTENDED` | When `1`, gated tool calls are rejected without prompting, as if nobody were at the terminal. `gravity schedule` sets it for you. | No | unset |
|
|
165
|
+
|
|
166
|
+
### Set for your agent, never by you
|
|
167
|
+
|
|
168
|
+
`gravity run` starts your agent with exactly these three, and the hosted runner does the same:
|
|
169
|
+
|
|
170
|
+
| Variable | Value |
|
|
171
|
+
|---|---|
|
|
172
|
+
| `GRAVITY_GATEWAY_URL` | the gateway's `/mcp` endpoint, for tools |
|
|
173
|
+
| `GRAVITY_LLM_URL` | the gateway's `/v1` endpoint, for models |
|
|
174
|
+
| `GRAVITY_RUN_TOKEN` | a token for this run only, scoped to the declared tools. Use it as the bearer token for tools and as the API key for models. |
|
|
175
|
+
|
|
176
|
+
Your own `GRAVITY_DEV_TOKEN` and `GRAVITY_API_TOKEN` are removed from the agent's environment, so agent code can never act as you. Read the three variables inside `graph()`, not at import time; see the [agent standard](AGENT_STANDARD.md).
|
|
177
|
+
|
|
178
|
+
### Example setups
|
|
179
|
+
|
|
180
|
+
Everything on your machine (local gateway and control plane):
|
|
181
|
+
|
|
182
|
+
```bash
|
|
183
|
+
# ~/.gravity/.env
|
|
184
|
+
GRAVITY_DEV_TOKEN=<printed by gravity-gateway serve, or its LOCAL_DEV_TOKEN>
|
|
185
|
+
GRAVITY_API_URL=http://localhost:3010
|
|
186
|
+
GRAVITY_API_TOKEN=<the control plane's PLATFORM_DEV_TOKEN>
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
Hosted dev gateway:
|
|
190
|
+
|
|
191
|
+
```bash
|
|
192
|
+
# ~/.gravity/.env
|
|
193
|
+
GRAVITY_GATEWAY_URL=https://<hosted gateway host>/mcp
|
|
194
|
+
GRAVITY_DEV_TOKEN=<the dev token you were issued>
|
|
195
|
+
GRAVITY_API_URL=https://<control plane host>
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
`GRAVITY_LLM_URL` is left out in both: it follows the gateway.
|
|
199
|
+
|
|
200
|
+
## Running a local gateway
|
|
201
|
+
|
|
202
|
+
The gateway is part of the Gravity platform repository, not this one. It is open to the Gravity team; outside builders use the hosted dev gateway instead. Start it with `gravity-gateway serve`; it listens on port 8000 and prints a dev token.
|
|
203
|
+
|
|
204
|
+
It reads its own `.env` file in the gateway's directory. None of its variables is needed for a first run: with nothing set it serves only the built-in `example.echo` tool, keeps tokens in memory, and prints a new dev token at every start.
|
|
205
|
+
|
|
206
|
+
| Variable | What it enables | When you need it |
|
|
207
|
+
|---|---|---|
|
|
208
|
+
| `COMPOSIO_API_KEY` | Real tools (Gmail, Slack, Linear and others) through Composio. Unset: only `example.echo`. | To call any real tool |
|
|
209
|
+
| `OPENROUTER_API_KEY` | Model calls on `/v1`, forwarded to OpenRouter. Unset: `/v1` returns 503. | For any agent that calls a model |
|
|
210
|
+
| `OPENROUTER_BASE_URL` | Where model calls go. Default `https://openrouter.ai/api/v1`. | Rarely |
|
|
211
|
+
| `LOCAL_DEV_TOKEN` | A fixed dev token (24+ random characters), so restarts don't change it. Put the same value in `~/.gravity/.env` as `GRAVITY_DEV_TOKEN`. | Recommended |
|
|
212
|
+
| `GATEWAY_PORT` | The listening port. Default `8000`. If you change it, set `GRAVITY_GATEWAY_URL=http://127.0.0.1:<port>/mcp`. | Only if 8000 is taken |
|
|
213
|
+
| `DATABASE_URL`, `PLATFORM_DB_SCHEMA` | Keeps run tokens in the platform's Postgres database instead of memory. | Platform development only |
|
|
214
|
+
| `PLATFORM_SERVICE_KEY`, `AWS_REGION` | Lets the platform's hosted runs mint tokens and report back. | Platform development only |
|
|
215
|
+
| `SUPABASE_URL`, `SUPABASE_SERVICE_ROLE_KEY` | The older token store, used when `DATABASE_URL` is unset. | Legacy setups only |
|
|
216
|
+
|
|
217
|
+
Real tools act on a connected account. On a local gateway every run uses one shared test identity; connect an account to it with `gravity-gateway connect --user-id builder:local --toolkit <app>`. See the gateway's own README for details.
|
|
218
|
+
|
|
219
|
+
## Authentication
|
|
220
|
+
|
|
221
|
+
There are two separate credentials, for two separate services:
|
|
222
|
+
|
|
223
|
+
| Credential | Used for | How you get it |
|
|
224
|
+
|---|---|---|
|
|
225
|
+
| Control-plane session | `push`, `tools`, `agents`, `runs`, `logs`, `whoami` | `gravity login`, or `GRAVITY_API_TOKEN` for development |
|
|
226
|
+
| Gateway dev token (`GRAVITY_DEV_TOKEN`) | `run`, `schedule`, `adapt` | Printed by a local gateway, or issued for the hosted one |
|
|
227
|
+
|
|
228
|
+
- **`gravity login`** opens the control plane's sign-in page and waits for you to approve a device code. The session is stored in the OS keychain, or in `~/.gravity/session.json` (readable only by you) on machines without one. `gravity logout` removes it.
|
|
229
|
+
- **`GRAVITY_API_TOKEN`** set to the control plane's dev token skips login entirely. `gravity whoami` then prints the dev user's id followed by `(dev token)`.
|
|
230
|
+
|
|
231
|
+
## Commands
|
|
232
|
+
|
|
233
|
+
| Command | What it does |
|
|
234
|
+
|---|---|
|
|
235
|
+
| `gravity init <name> [--template T] [--here]` | Scaffold a working agent. Templates: `minimal`, `structured`, `deepagent`, `chat`. `--here` writes only a manifest into the current directory, for an existing project. |
|
|
236
|
+
| `gravity validate [path]` | Check the manifest, the entrypoint, the declared adapters and the lockfile, and warn about tool names or network imports the manifest does not declare. Compiles `requirements.lock` for the platform image (ARM64 Linux). |
|
|
237
|
+
| `gravity run [path] --input k=v` | Run the agent locally through the gateway. `--input k=@file.txt` reads a value from a file. `--reply "..."` continues the last conversation of a chat agent. `--llm-url URL` overrides `GRAVITY_LLM_URL`. |
|
|
238
|
+
| `gravity schedule [path] [--cron EXPR] [--now]` | Run the agent on its `triggers.schedule` until Ctrl+C. Nobody is at the terminal, so gated calls are not approved. |
|
|
239
|
+
| `gravity build [path]` | Validate, then write `build/agent.zip` and `build/build.json`. The same source always produces the same bytes. Nothing leaves your machine. |
|
|
240
|
+
| `gravity push [path]` | Upload what `build` made. Refuses if there is no build or the source changed since. Published versions are frozen: bump `version` to publish again. |
|
|
241
|
+
| `gravity adapt <slot> <name> --tools a.b,c.d` | Have a coding agent write a new adapter for a slot. The file is kept only if the slot's conformance tests pass; otherwise nothing changes. |
|
|
242
|
+
| `gravity tools [search]` | List the tool catalog: the names you can put in `permissions.tools`. |
|
|
243
|
+
| `gravity agents` | Your published agents and their review status. |
|
|
244
|
+
| `gravity runs <agent>` / `gravity logs <run-id>` | Hosted run history and logs. |
|
|
245
|
+
| `gravity login` / `logout` / `whoami` | Sign in to and out of the control plane, and show who you are. |
|
|
246
|
+
|
|
247
|
+
Every command accepts `--help`. The global option `--api-url URL`, given before the command, saves a new control-plane URL to `~/.gravity/config.json`.
|
|
248
|
+
|
|
249
|
+
## An agent project
|
|
250
|
+
|
|
251
|
+
```
|
|
252
|
+
my-agent/
|
|
253
|
+
├── manifest.yaml what the agent is and may do
|
|
254
|
+
├── requirements.txt your dependencies
|
|
255
|
+
├── requirements.lock compiled by `gravity validate`; ships with the agent
|
|
256
|
+
└── src/
|
|
257
|
+
└── agent.py exports `graph`, an async factory returning a compiled LangGraph graph
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
### The manifest
|
|
261
|
+
|
|
262
|
+
`manifest.yaml` declares everything about the agent that Gravity enforces: what the consumer fills in, which tools it may call, which calls need approval, and how it is triggered. Only `name`, `runtime.entrypoint` and, for a useful agent, `permissions.tools` are needed; everything else has a default.
|
|
263
|
+
|
|
264
|
+
The smallest useful manifest:
|
|
265
|
+
|
|
266
|
+
```yaml
|
|
267
|
+
contract: v1
|
|
268
|
+
name: my-agent
|
|
269
|
+
version: 1.0.0
|
|
270
|
+
runtime:
|
|
271
|
+
entrypoint: src/agent.py:graph
|
|
272
|
+
inputs:
|
|
273
|
+
- name: query
|
|
274
|
+
type: text
|
|
275
|
+
label: "What should the agent do?"
|
|
276
|
+
required: true
|
|
277
|
+
permissions:
|
|
278
|
+
tools: [example.echo]
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
Every section, annotated. This one is valid as written:
|
|
282
|
+
|
|
283
|
+
```yaml
|
|
284
|
+
contract: v1 # manifest contract version; v1 is the only one
|
|
285
|
+
name: inbox-digest # lowercase letters, digits, single hyphens; appears in URLs
|
|
286
|
+
version: 1.0.0 # semver; a published version is frozen, so bump it to publish again
|
|
287
|
+
description: "Summarises unread email and sends a digest"
|
|
288
|
+
kind: interactive # transform | interactive | monitor
|
|
289
|
+
|
|
290
|
+
runtime:
|
|
291
|
+
framework: langgraph # the only framework in v1
|
|
292
|
+
python: "3.12" # the platform image runs Python 3.12
|
|
293
|
+
entrypoint: src/agent.py:graph # file:object, relative to the project folder
|
|
294
|
+
timeout_seconds: 300 # 10 to 28800
|
|
295
|
+
|
|
296
|
+
dependencies:
|
|
297
|
+
lockfile: requirements.lock # written by `gravity validate`
|
|
298
|
+
|
|
299
|
+
inputs: # each becomes a field in the consumer's form
|
|
300
|
+
- name: lookback
|
|
301
|
+
type: static-dropdown
|
|
302
|
+
label: "How far back?"
|
|
303
|
+
options: ["1 day", "7 days"]
|
|
304
|
+
default: "1 day"
|
|
305
|
+
- name: send_to
|
|
306
|
+
type: email
|
|
307
|
+
label: "Send the digest to"
|
|
308
|
+
required: true
|
|
309
|
+
|
|
310
|
+
permissions:
|
|
311
|
+
tools: # the only tools this agent's runs may call
|
|
312
|
+
- gmail.read
|
|
313
|
+
- gmail.send
|
|
314
|
+
models:
|
|
315
|
+
tier: standard # standard | premium
|
|
316
|
+
|
|
317
|
+
approvals:
|
|
318
|
+
required_for: # these calls pause until a person approves them
|
|
319
|
+
- gmail.send
|
|
320
|
+
|
|
321
|
+
resources:
|
|
322
|
+
memory_mb: 512 # 128 to 4096; a request the platform may lower
|
|
323
|
+
|
|
324
|
+
triggers:
|
|
325
|
+
manual: true # the consumer can start a run
|
|
326
|
+
schedule:
|
|
327
|
+
cron: "0 8 * * 1-5" # weekdays at 08:00, local time
|
|
328
|
+
consumer_can_change: true
|
|
329
|
+
|
|
330
|
+
state:
|
|
331
|
+
max_kb: 64 # memory kept between runs (1 to 256 KB); omit for none
|
|
332
|
+
|
|
333
|
+
slots: # a part the consumer chooses; each adapter gets only its own tools
|
|
334
|
+
deliver:
|
|
335
|
+
label: "Where should the digest go?"
|
|
336
|
+
default: email
|
|
337
|
+
adapters:
|
|
338
|
+
email: [gmail.draft] # code in src/adapters/deliver/email.py
|
|
339
|
+
slack: [slack.send] # code in src/adapters/deliver/slack.py
|
|
340
|
+
|
|
341
|
+
outputs: # extra formats the platform renders and delivers itself
|
|
342
|
+
report:
|
|
343
|
+
renderer: markdown # markdown | docx | pptx | rows
|
|
344
|
+
label: "Digest as a document"
|
|
345
|
+
destinations: [download]
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
### Field reference
|
|
349
|
+
|
|
350
|
+
| Field | Required | Default | Rules |
|
|
351
|
+
|---|---|---|---|
|
|
352
|
+
| `contract` | no | `v1` | Only `v1` exists. |
|
|
353
|
+
| `name` | **yes** | | Lowercase letters, digits and single hyphens, up to 64 characters. Appears in URLs. |
|
|
354
|
+
| `version` | no | `1.0.0` | Strict semver, e.g. `1.2.0`. A published version can never change; bump it to publish again. |
|
|
355
|
+
| `description` | no | empty | Up to 500 characters. |
|
|
356
|
+
| `kind` | no | `interactive` | `transform` (input in, result out), `interactive` (calls tools mid-run, may converse), `monitor` (runs on a schedule or webhook and remembers the last run; needs a trigger). |
|
|
357
|
+
| `runtime.entrypoint` | **yes** | | `path/to/file.py:object`, inside the project. The object is your `graph` factory. |
|
|
358
|
+
| `runtime.framework` | no | `langgraph` | Only `langgraph`. |
|
|
359
|
+
| `runtime.python` | no | `3.13` | `3.10` to `3.13` are accepted, but the platform image runs **3.12**: set `"3.12"` before you push. |
|
|
360
|
+
| `runtime.timeout_seconds` | no | `300` | 10 to 28800. |
|
|
361
|
+
| `dependencies.lockfile` | no | `requirements.lock` | Compiled by `gravity validate`; ships with the agent. |
|
|
362
|
+
| `inputs[]` | no | none | Up to 50. Each has `name` (lowercase identifier, becomes the key in `state["inputs"]`), `type`, `label` (shown in the form), and optionally `required`, `default`, `options`, `accept`. |
|
|
363
|
+
| `inputs[].type` | **yes** | | `text`, `textarea`, `number`, `boolean`, `static-dropdown` (needs `options`), `email`, `url`, `date`, `file` (no `default`; `accept` lists extensions such as `[pdf, csv]`). |
|
|
364
|
+
| `permissions.tools` | no | none | Up to 50 catalog names, e.g. `gmail.read`. Run `gravity tools` for the list. The run token allows exactly these. |
|
|
365
|
+
| `permissions.models.tier` | no | `standard` | `standard` or `premium`. |
|
|
366
|
+
| `approvals.required_for` | no | none | Tools that pause until a person approves. Each must also be declared under `permissions.tools` or a slot adapter. |
|
|
367
|
+
| `resources.memory_mb` | no | `512` | 128 to 4096. A request; the platform may lower it. |
|
|
368
|
+
| `triggers.manual` | no | `true` | Whether the consumer can start a run. Something must start the agent: `manual`, a `schedule` or a `webhook`. |
|
|
369
|
+
| `triggers.schedule` | no | none | `cron` (5 fields, local time, e.g. `"0 9 * * 1"`) and `consumer_can_change` (default `true`). Try it with `gravity schedule`. |
|
|
370
|
+
| `triggers.webhook` | no | none | A list of events; `github.push` is the only one today. |
|
|
371
|
+
| `state.max_kb` | no | no memory | Include `state:` to keep memory between runs, in `state["memory"]`. 1 to 256 KB. |
|
|
372
|
+
| `slots.<name>` | no | none | A swappable part: `label`, `default` adapter, and `adapters` mapping each adapter name to the tools it may call. Adapter code lives in `src/adapters/<slot>/<adapter>.py`. The pick arrives in `state["inputs"]` under the slot's name, so slot names cannot repeat an input's name. |
|
|
373
|
+
| `outputs.<name>` | no | none | Up to 8 extra formats the platform renders from the result: `renderer` (`markdown`, `docx`, `pptx`, `rows`), `label`, and `destinations` (`download`, or a catalog tool such as `gmail.send` that the platform calls itself). |
|
|
374
|
+
|
|
375
|
+
Unknown fields are rejected rather than ignored, so a typo is reported instead of silently dropped. `gravity validate` lists every problem at once, with a suggested fix for each.
|
|
376
|
+
|
|
377
|
+
### Your code
|
|
378
|
+
|
|
379
|
+
Inputs arrive in `state["inputs"]`, and the final `state["result"]` is what the consumer gets. Add a `messages` key to your state to make a chat agent. The exact rules are in the [agent standard](AGENT_STANDARD.md), and the machine-readable schema is [`manifest.schema.json`](packages/gravity-schema/schema/manifest.schema.json).
|
|
380
|
+
|
|
381
|
+
`graph` is an async factory, not a graph built at import time: the runner calls it fresh for every run, after that run's environment variables are set.
|
|
382
|
+
|
|
383
|
+
Files the CLI writes in a project:
|
|
384
|
+
|
|
385
|
+
| Path | Written by | Contents |
|
|
386
|
+
|---|---|---|
|
|
387
|
+
| `requirements.lock` | `validate` | exact pins for the platform image; commit it |
|
|
388
|
+
| `build/agent.zip`, `build/build.json` | `build` | the bundle `push` uploads, and its hashes |
|
|
389
|
+
| `.gravity/payload.json` | `run` | the inputs passed to the agent |
|
|
390
|
+
| `.gravity/thread.json` | `run` | the last conversation of a chat agent, for `--reply` |
|
|
391
|
+
| `.gravity/memory.json` | `run` | memory kept between runs, when the manifest declares `state` |
|
|
392
|
+
|
|
393
|
+
The template `.gitignore` excludes `build/` and `.gravity/`.
|
|
394
|
+
|
|
395
|
+
## Troubleshooting
|
|
396
|
+
|
|
397
|
+
| Message | Cause and fix |
|
|
398
|
+
|---|---|
|
|
399
|
+
| `missing: GRAVITY_DEV_TOKEN` | `run`, `schedule` and `adapt` need a gateway dev token. Add `GRAVITY_DEV_TOKEN` to `~/.gravity/.env`. |
|
|
400
|
+
| `can't reach the gateway at …` | No gateway at that address. Start a local one with `gravity-gateway serve`, or set `GRAVITY_GATEWAY_URL` to the hosted gateway's `/mcp` URL. |
|
|
401
|
+
| `the gateway refused this run — …` | The gateway rejected the token or a declared tool. A token from a restarted local gateway is no longer valid: set `LOCAL_DEV_TOKEN` there so it stays fixed. |
|
|
402
|
+
| `not logged in — run gravity login first, or set GRAVITY_API_TOKEN.` | A control-plane command without credentials. Run `gravity login`, or set `GRAVITY_API_TOKEN` for development. |
|
|
403
|
+
| `missing required input(s): …` | Pass each required input with `--input name=value`, or give it a `default` in the manifest. |
|
|
404
|
+
| `no build found` / `build is stale` | Run `gravity build` again after any change to `src/`, the manifest or the requirements. |
|
|
405
|
+
| `` `uv pip compile` failed for aarch64-manylinux2014 `` | A dependency has no wheel for the platform image (ARM64 Linux). Pick a version that publishes one, or a different package. |
|
|
406
|
+
| A tool row shows `refused — not in permissions.tools` | The agent called a tool its manifest does not declare. Add it under `permissions.tools`, then run again. |
|
|
407
|
+
|
|
408
|
+
## Limitations
|
|
409
|
+
|
|
410
|
+
- **No offline mode.** `gravity run` always talks to a real gateway. There is no mock-tool mode, so every local run needs a gateway, and real tools need a connected account.
|
|
411
|
+
- **Static drift check.** `gravity validate` finds undeclared tools by scanning source for imports and tool-shaped strings. Expect false positives; they are warnings, never failures.
|
|
412
|
+
- **Models are not restricted yet.** The gateway forwards whatever model name the agent asks for, and `permissions.models.tier` is not enforced.
|
|
413
|
+
- **Hosted history.** `gravity runs` and `gravity logs` show what the control plane returns, which is empty until hosted runs exist.
|
|
414
|
+
|
|
415
|
+
## Development
|
|
416
|
+
|
|
417
|
+
```bash
|
|
418
|
+
git clone https://github.com/AIGravity/gravity-cli.git
|
|
419
|
+
cd gravity-cli
|
|
420
|
+
uv sync # installs both packages and dev tools
|
|
421
|
+
uv run gravity --help # or activate .venv to use gravity directly
|
|
422
|
+
uv run pytest # CLI tests
|
|
423
|
+
cd packages/gravity-schema && uv run pytest && cd ../.. # manifest contract tests
|
|
424
|
+
uv run ruff check .
|
|
425
|
+
```
|
|
426
|
+
|
|
427
|
+
After changing the manifest model in `packages/gravity-schema`, regenerate the committed JSON Schema; a test fails until you do:
|
|
428
|
+
|
|
429
|
+
```bash
|
|
430
|
+
uv run python -m gravity_schema # --check only reports whether it is stale
|
|
431
|
+
```
|
|
432
|
+
|
|
433
|
+
After changing `catalog.yaml`, regenerate the tool lists in the platform repository:
|
|
434
|
+
|
|
435
|
+
```bash
|
|
436
|
+
uv run python -m gravity_schema.gen_catalog --root <platform repo>/code-first
|
|
437
|
+
```
|
|
438
|
+
|
|
439
|
+
### Continuous integration
|
|
440
|
+
|
|
441
|
+
Every pull request and every push to `main` runs [`ci.yml`](.github/workflows/ci.yml):
|
|
442
|
+
|
|
443
|
+
- `ruff check`, and a check that `uv.lock` matches `pyproject.toml`
|
|
444
|
+
- both test suites on Ubuntu and Windows, Python 3.12 and 3.13 (these include a check that the committed JSON Schema is current)
|
|
445
|
+
- a build of both packages, installed into a clean environment and used for real (`gravity init` and `gravity validate`), which catches files missing from the package before PyPI does
|
|
446
|
+
|
|
447
|
+
### Releasing
|
|
448
|
+
|
|
449
|
+
`gravity-cli` and `gravity-schema` are released together, always at the same version.
|
|
450
|
+
|
|
451
|
+
1. Set the new version in three places: `version` in `pyproject.toml`, `version` in `packages/gravity-schema/pyproject.toml`, and the `gravity-schema==` pin in `pyproject.toml`'s dependencies. Run `uv lock`, then merge to `main`.
|
|
452
|
+
2. Tag the merge commit and push the tag:
|
|
453
|
+
- `git tag v0.1.0rc1 && git push origin v0.1.0rc1` publishes a release candidate to TestPyPI.
|
|
454
|
+
- `git tag v0.1.0 && git push origin v0.1.0` publishes to PyPI.
|
|
455
|
+
3. Watch the Release run in the repository's Actions tab. A PyPI release also creates a GitHub Release with generated notes.
|
|
456
|
+
|
|
457
|
+
[`release.yml`](.github/workflows/release.yml) runs the full CI first, refuses a tag that doesn't match all three versions, and publishes with PyPI trusted publishing, so no PyPI token is stored anywhere. Each package is published in its own job and environment, because a trusted publisher can create only one new project per login.
|
|
458
|
+
|
|
459
|
+
One-time setup, done once by a maintainer:
|
|
460
|
+
|
|
461
|
+
| Where | Project | Workflow | Environment |
|
|
462
|
+
|---|---|---|---|
|
|
463
|
+
| pypi.org | `gravity-cli` | `release.yml` | `pypi` |
|
|
464
|
+
| pypi.org | `gravity-schema` | `release.yml` | `pypi-schema` |
|
|
465
|
+
| test.pypi.org | `gravity-cli` | `release.yml` | `testpypi` |
|
|
466
|
+
| test.pypi.org | `gravity-schema` | `release.yml` | `testpypi-schema` |
|
|
467
|
+
|
|
468
|
+
All four use the repository `AIGravity/gravity-cli`. On GitHub, under Settings → Environments, create the four environments named in the last column, each limited to tags matching `v*`.
|
|
469
|
+
|
|
470
|
+
To install a release candidate, which has its dependencies on the real PyPI:
|
|
471
|
+
|
|
472
|
+
```bash
|
|
473
|
+
pip install --index-url https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple/ gravity-cli==0.1.0rc2
|
|
474
|
+
```
|
|
475
|
+
|
|
476
|
+
## Repository layout
|
|
477
|
+
|
|
478
|
+
```
|
|
479
|
+
src/gravity_cli/ the CLI
|
|
480
|
+
commands/ one module per command
|
|
481
|
+
templates/ what `gravity init` copies
|
|
482
|
+
_runner_script.py runs an agent inside its own venv for `gravity run`
|
|
483
|
+
packages/gravity-schema/ the manifest contract: models, validation, tool catalog, JSON Schema
|
|
484
|
+
gravity_schema/catalog.yaml the curated tool catalog
|
|
485
|
+
gravity_schema/catalog_imported.yaml generated by the gateway's sync-tools; experimental tools
|
|
486
|
+
starter/ the builder guide's example agent (inbox digest)
|
|
487
|
+
tests/ CLI tests
|
|
488
|
+
```
|
|
489
|
+
|
|
490
|
+
`gravity-schema` is its own package because the CLI, the gateway, the hosted runner and the control plane's intake all validate the same manifest against the same rules.
|
|
491
|
+
|
|
492
|
+
## License
|
|
493
|
+
|
|
494
|
+
Apache License 2.0. See [LICENSE](LICENSE) and [NOTICE](NOTICE).
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
gravity_cli/__init__.py,sha256=uKvIolqKe1T541D_DzWhiIgld8lgBUyaf8mxk2qcJ1I,376
|
|
2
|
+
gravity_cli/__main__.py,sha256=Qd-f8z2Q2vpiEP2x6PBFsJrpACWDVxFKQk820MhFmHo,59
|
|
3
|
+
gravity_cli/_runner_script.py,sha256=TooP0huzBAM1m3ke5aXvyc4rz1IC4iLjxTdWs-PGFVY,7613
|
|
4
|
+
gravity_cli/api.py,sha256=baWc4Cokpmt2RbWRWayyPVSv3DyxCqDVc0IThSnBg9M,2592
|
|
5
|
+
gravity_cli/cli.py,sha256=PDaWBWLBRr31tP0qRWoh7uupwSujOWOsoe-rLMDpX4w,5438
|
|
6
|
+
gravity_cli/config.py,sha256=QLZdXsgWMIb4JKBWaf6kQl-YHy2uexe6Y1-PfbLOsqk,2879
|
|
7
|
+
gravity_cli/manifest.py,sha256=4asZFQgus5UpxnzvffwqZC8GsizuU7R3VTaIu3c2BNo,805
|
|
8
|
+
gravity_cli/project.py,sha256=aGLpiYu4AdECpwm2uSW2m-pFnx9203VSTTg-rsw91YA,1453
|
|
9
|
+
gravity_cli/timeline.py,sha256=KyDgrtkZR70QqljVgz1l-Nkfk55OGnvx0OcK4EbLMmE,10697
|
|
10
|
+
gravity_cli/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
11
|
+
gravity_cli/commands/adapt.py,sha256=UyidJCchWnEiRuOeW7AtK6iPlLq1Y0YumUY5-Vxymkc,7928
|
|
12
|
+
gravity_cli/commands/build.py,sha256=jTgKJ_GTbI127rmllssQWE-329pHqG1-D1jvV8ii-pI,2788
|
|
13
|
+
gravity_cli/commands/init.py,sha256=7lNSbWhCj83-QKVx2qXrkPWdvFR-YuS_8q5SqjUSUs8,2867
|
|
14
|
+
gravity_cli/commands/login.py,sha256=blgkQFtg_Gx-4Q-8ICYcXdeRAO-buzemCBhAtkpWqxA,1851
|
|
15
|
+
gravity_cli/commands/push.py,sha256=MAYgEhenJJxTSqhnk65TDEw4MYGqoGKbHqTUVCY4xdE,3488
|
|
16
|
+
gravity_cli/commands/run.py,sha256=CNSERIbaHiKX5NvqvMFrb2Oj7ZK82HRRPaXIFY0S0dg,11965
|
|
17
|
+
gravity_cli/commands/runs.py,sha256=Ig9xC7KTsU5R3QHHNFj_IzSw5xiJBsDIwUkSLlgb9mY,1488
|
|
18
|
+
gravity_cli/commands/schedule.py,sha256=2j3VgYaxFtV2bL6WOgziLXrGABryDvA2HLBNmLjMgmE,3044
|
|
19
|
+
gravity_cli/commands/tools.py,sha256=cqQQZqxKvR3qAzkube_YRoM5cACq0lg-vAamHRLM528,721
|
|
20
|
+
gravity_cli/commands/validate.py,sha256=u93cU5PoTFAEpQgveI9BXxbAurK9Ka3hzMTnwLsEI8s,7752
|
|
21
|
+
gravity_cli/templates/chat/.gitignore,sha256=9Xr50hJchaseSOcr8HanTsCh4dLF1R2AZQcjpQ0b0Oo,57
|
|
22
|
+
gravity_cli/templates/chat/manifest.yaml,sha256=6jUdj5iatNevnP7hosKvLg3aL3zOHe2WLrywH51tEmw,495
|
|
23
|
+
gravity_cli/templates/chat/requirements.txt,sha256=DX2Ql7x4oYM_mTKXhwoIdsxWPkWU9ujww-OTKaFaLBE,80
|
|
24
|
+
gravity_cli/templates/chat/src/agent.py,sha256=eIA6aS9W4o6TBPqS_2I3Z21LS55xIdS_MepSuEr9xTc,2948
|
|
25
|
+
gravity_cli/templates/deepagent/.gitignore,sha256=9Xr50hJchaseSOcr8HanTsCh4dLF1R2AZQcjpQ0b0Oo,57
|
|
26
|
+
gravity_cli/templates/deepagent/manifest.yaml,sha256=QwDsWp_VAaJc6S7ZECjYKVOTN8Evp1r5k1euALzqY6s,512
|
|
27
|
+
gravity_cli/templates/deepagent/requirements.txt,sha256=eERX7Ow3psafg1lNELMJlEq56Gao5M1yapVouFj20r4,101
|
|
28
|
+
gravity_cli/templates/deepagent/src/agent.py,sha256=Gl3KoClMTI678As4XAQxwydQGXSa84yl7OO6VDKbHtg,1939
|
|
29
|
+
gravity_cli/templates/minimal/.gitignore,sha256=9Xr50hJchaseSOcr8HanTsCh4dLF1R2AZQcjpQ0b0Oo,57
|
|
30
|
+
gravity_cli/templates/minimal/manifest.yaml,sha256=A28KBafo57oUvwUS1vcnVuc9-Prw6Iy8txu2hRHYnbA,952
|
|
31
|
+
gravity_cli/templates/minimal/requirements.txt,sha256=DX2Ql7x4oYM_mTKXhwoIdsxWPkWU9ujww-OTKaFaLBE,80
|
|
32
|
+
gravity_cli/templates/minimal/src/agent.py,sha256=KNfRcblrQ79JjzEMPh42h24nBA1T57vyTLiqZUEftvU,2020
|
|
33
|
+
gravity_cli/templates/structured/.gitignore,sha256=9Xr50hJchaseSOcr8HanTsCh4dLF1R2AZQcjpQ0b0Oo,57
|
|
34
|
+
gravity_cli/templates/structured/manifest.yaml,sha256=JqjzanSozs-7PR9KGT6J6zczOOAS0az8XVKGMU-dS-M,590
|
|
35
|
+
gravity_cli/templates/structured/requirements.txt,sha256=DX2Ql7x4oYM_mTKXhwoIdsxWPkWU9ujww-OTKaFaLBE,80
|
|
36
|
+
gravity_cli/templates/structured/src/agent.py,sha256=M_GX9cxwISi-rfdSVGMCjnNLhkpEAAT5g3PsinNLsaI,573
|
|
37
|
+
gravity_cli/templates/structured/src/gateway.py,sha256=3S_FplJjg5FS7KSH6SdGVIKrwwIhrtCpNKHX7yTrxqw,838
|
|
38
|
+
gravity_cli/templates/structured/src/prompts.py,sha256=i5ksqGkLkOGtNynssc9tVfeC8O-hR2dzOmjhERkYHNw,219
|
|
39
|
+
gravity_cli/templates/structured/src/state.py,sha256=X4jlEWnx6YGMvfOEYrTtWVcLXBFzetywei0S591t4KY,404
|
|
40
|
+
gravity_cli/templates/structured/src/nodes/agent.py,sha256=bLvK4iWF_PGdKnGeeOW_LC6LghsTEz5elmyEc22wj34,800
|
|
41
|
+
gravity_cli-0.1.0.dist-info/METADATA,sha256=vvzZJSXTiLJI-mZLYxqcx_rD2oqAz1WKNJnE2UfdGTo,28061
|
|
42
|
+
gravity_cli-0.1.0.dist-info/WHEEL,sha256=W3fkpkm7-wf9vBI5Z-7s0eWkeM-spu78I8Neb98DeEg,87
|
|
43
|
+
gravity_cli-0.1.0.dist-info/entry_points.txt,sha256=RbO35Iu0tAdOR1cuUvp2BTuMgdEy2O2so792Bl9480M,48
|
|
44
|
+
gravity_cli-0.1.0.dist-info/licenses/LICENSE,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358
|
|
45
|
+
gravity_cli-0.1.0.dist-info/licenses/NOTICE,sha256=4BP9rLcur0_HGk7WA-uC6bx-N0lotMQ4BBO6cPHXwMg,93
|
|
46
|
+
gravity_cli-0.1.0.dist-info/RECORD,,
|