gravity-cli 0.4.4__tar.gz → 0.5.2__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.
- {gravity_cli-0.4.4 → gravity_cli-0.5.2}/AGENT_STANDARD.md +15 -1
- gravity_cli-0.4.4/README.md → gravity_cli-0.5.2/PKG-INFO +48 -5
- gravity_cli-0.4.4/PKG-INFO → gravity_cli-0.5.2/README.md +31 -22
- {gravity_cli-0.4.4 → gravity_cli-0.5.2}/packages/gravity-schema/gravity_schema/__init__.py +6 -0
- {gravity_cli-0.4.4 → gravity_cli-0.5.2}/packages/gravity-schema/gravity_schema/catalog.py +11 -2
- {gravity_cli-0.4.4 → gravity_cli-0.5.2}/packages/gravity-schema/gravity_schema/catalog.yaml +45 -0
- {gravity_cli-0.4.4 → gravity_cli-0.5.2}/packages/gravity-schema/gravity_schema/catalog_imported.yaml +9632 -4276
- {gravity_cli-0.4.4 → gravity_cli-0.5.2}/packages/gravity-schema/gravity_schema/errors.py +2 -0
- gravity_cli-0.5.2/packages/gravity-schema/gravity_schema/source_scan.py +83 -0
- {gravity_cli-0.4.4 → gravity_cli-0.5.2}/packages/gravity-schema/gravity_schema/validation.py +23 -2
- {gravity_cli-0.4.4 → gravity_cli-0.5.2}/packages/gravity-schema/pyproject.toml +1 -1
- {gravity_cli-0.4.4 → gravity_cli-0.5.2}/packages/gravity-schema/tests/test_catalog_parity.py +11 -0
- gravity_cli-0.5.2/packages/gravity-schema/tests/test_source_scan.py +67 -0
- {gravity_cli-0.4.4 → gravity_cli-0.5.2}/packages/gravity-schema/tests/test_validation.py +25 -0
- {gravity_cli-0.4.4 → gravity_cli-0.5.2}/pyproject.toml +2 -2
- {gravity_cli-0.4.4 → gravity_cli-0.5.2}/src/gravity_cli/BUILDER_GUIDE.md +27 -12
- {gravity_cli-0.4.4 → gravity_cli-0.5.2}/src/gravity_cli/_runner_script.py +44 -4
- {gravity_cli-0.4.4 → gravity_cli-0.5.2}/src/gravity_cli/api.py +2 -1
- {gravity_cli-0.4.4 → gravity_cli-0.5.2}/src/gravity_cli/cli.py +2 -0
- gravity_cli-0.5.2/src/gravity_cli/commands/connect.py +82 -0
- {gravity_cli-0.4.4 → gravity_cli-0.5.2}/src/gravity_cli/commands/run.py +2 -0
- {gravity_cli-0.4.4 → gravity_cli-0.5.2}/src/gravity_cli/commands/tools.py +21 -2
- {gravity_cli-0.4.4 → gravity_cli-0.5.2}/src/gravity_cli/commands/validate.py +31 -37
- {gravity_cli-0.4.4 → gravity_cli-0.5.2}/src/gravity_cli/config.py +33 -8
- gravity_cli-0.5.2/src/gravity_cli/manifest.py +22 -0
- {gravity_cli-0.4.4 → gravity_cli-0.5.2}/src/gravity_cli/ui.py +3 -0
- {gravity_cli-0.4.4 → gravity_cli-0.5.2}/tests/test_api.py +2 -1
- gravity_cli-0.5.2/tests/test_connect.py +78 -0
- {gravity_cli-0.4.4 → gravity_cli-0.5.2}/tests/test_run.py +2 -1
- gravity_cli-0.5.2/tests/test_session.py +80 -0
- {gravity_cli-0.4.4 → gravity_cli-0.5.2}/tests/test_templates.py +68 -0
- gravity_cli-0.5.2/tests/test_tools.py +45 -0
- gravity_cli-0.5.2/tests/test_validate.py +128 -0
- {gravity_cli-0.4.4 → gravity_cli-0.5.2}/uv.lock +2 -2
- gravity_cli-0.4.4/src/gravity_cli/manifest.py +0 -9
- {gravity_cli-0.4.4 → gravity_cli-0.5.2}/.env.example +0 -0
- {gravity_cli-0.4.4 → gravity_cli-0.5.2}/.github/workflows/ci.yml +0 -0
- {gravity_cli-0.4.4 → gravity_cli-0.5.2}/.github/workflows/release.yml +0 -0
- {gravity_cli-0.4.4 → gravity_cli-0.5.2}/.gitignore +0 -0
- {gravity_cli-0.4.4 → gravity_cli-0.5.2}/LICENSE +0 -0
- {gravity_cli-0.4.4 → gravity_cli-0.5.2}/NOTICE +0 -0
- {gravity_cli-0.4.4 → gravity_cli-0.5.2}/RELEASING.md +0 -0
- {gravity_cli-0.4.4 → gravity_cli-0.5.2}/TEMPLATES.md +0 -0
- {gravity_cli-0.4.4 → gravity_cli-0.5.2}/packages/gravity-schema/LICENSE +0 -0
- {gravity_cli-0.4.4 → gravity_cli-0.5.2}/packages/gravity-schema/NOTICE +0 -0
- {gravity_cli-0.4.4 → gravity_cli-0.5.2}/packages/gravity-schema/README.md +0 -0
- {gravity_cli-0.4.4 → gravity_cli-0.5.2}/packages/gravity-schema/gravity_schema/__main__.py +0 -0
- {gravity_cli-0.4.4 → gravity_cli-0.5.2}/packages/gravity-schema/gravity_schema/contract.py +0 -0
- {gravity_cli-0.4.4 → gravity_cli-0.5.2}/packages/gravity-schema/gravity_schema/cron.py +0 -0
- {gravity_cli-0.4.4 → gravity_cli-0.5.2}/packages/gravity-schema/gravity_schema/export.py +0 -0
- {gravity_cli-0.4.4 → gravity_cli-0.5.2}/packages/gravity-schema/gravity_schema/gen_catalog.py +0 -0
- {gravity_cli-0.4.4 → gravity_cli-0.5.2}/packages/gravity-schema/gravity_schema/manifest.py +0 -0
- {gravity_cli-0.4.4 → gravity_cli-0.5.2}/packages/gravity-schema/schema/manifest.schema.json +0 -0
- {gravity_cli-0.4.4 → gravity_cli-0.5.2}/packages/gravity-schema/tests/conftest.py +0 -0
- {gravity_cli-0.4.4 → gravity_cli-0.5.2}/packages/gravity-schema/tests/test_cron.py +0 -0
- {gravity_cli-0.4.4 → gravity_cli-0.5.2}/packages/gravity-schema/tests/test_errors.py +0 -0
- {gravity_cli-0.4.4 → gravity_cli-0.5.2}/packages/gravity-schema/tests/test_json_schema.py +0 -0
- {gravity_cli-0.4.4 → gravity_cli-0.5.2}/packages/gravity-schema/tests/test_kind_outputs.py +0 -0
- {gravity_cli-0.4.4 → gravity_cli-0.5.2}/packages/gravity-schema/tests/test_manifest.py +0 -0
- {gravity_cli-0.4.4 → gravity_cli-0.5.2}/packages/gravity-schema/tests/test_slots.py +0 -0
- {gravity_cli-0.4.4 → gravity_cli-0.5.2}/src/gravity_cli/__init__.py +0 -0
- {gravity_cli-0.4.4 → gravity_cli-0.5.2}/src/gravity_cli/__main__.py +0 -0
- {gravity_cli-0.4.4 → gravity_cli-0.5.2}/src/gravity_cli/commands/__init__.py +0 -0
- {gravity_cli-0.4.4 → gravity_cli-0.5.2}/src/gravity_cli/commands/adapt.py +0 -0
- {gravity_cli-0.4.4 → gravity_cli-0.5.2}/src/gravity_cli/commands/build.py +0 -0
- {gravity_cli-0.4.4 → gravity_cli-0.5.2}/src/gravity_cli/commands/guide.py +0 -0
- {gravity_cli-0.4.4 → gravity_cli-0.5.2}/src/gravity_cli/commands/init.py +0 -0
- {gravity_cli-0.4.4 → gravity_cli-0.5.2}/src/gravity_cli/commands/login.py +0 -0
- {gravity_cli-0.4.4 → gravity_cli-0.5.2}/src/gravity_cli/commands/push.py +0 -0
- {gravity_cli-0.4.4 → gravity_cli-0.5.2}/src/gravity_cli/commands/runs.py +0 -0
- {gravity_cli-0.4.4 → gravity_cli-0.5.2}/src/gravity_cli/commands/schedule.py +0 -0
- {gravity_cli-0.4.4 → gravity_cli-0.5.2}/src/gravity_cli/project.py +0 -0
- {gravity_cli-0.4.4 → gravity_cli-0.5.2}/src/gravity_cli/templates/chat/.gitignore +0 -0
- {gravity_cli-0.4.4 → gravity_cli-0.5.2}/src/gravity_cli/templates/chat/manifest.yaml +0 -0
- {gravity_cli-0.4.4 → gravity_cli-0.5.2}/src/gravity_cli/templates/chat/requirements.txt +0 -0
- {gravity_cli-0.4.4 → gravity_cli-0.5.2}/src/gravity_cli/templates/chat/src/agent.py +0 -0
- {gravity_cli-0.4.4 → gravity_cli-0.5.2}/src/gravity_cli/templates/deepagent/.gitignore +0 -0
- {gravity_cli-0.4.4 → gravity_cli-0.5.2}/src/gravity_cli/templates/deepagent/manifest.yaml +0 -0
- {gravity_cli-0.4.4 → gravity_cli-0.5.2}/src/gravity_cli/templates/deepagent/requirements.txt +0 -0
- {gravity_cli-0.4.4 → gravity_cli-0.5.2}/src/gravity_cli/templates/deepagent/src/agent.py +0 -0
- {gravity_cli-0.4.4 → gravity_cli-0.5.2}/src/gravity_cli/templates/minimal/.gitignore +0 -0
- {gravity_cli-0.4.4 → gravity_cli-0.5.2}/src/gravity_cli/templates/minimal/manifest.yaml +0 -0
- {gravity_cli-0.4.4 → gravity_cli-0.5.2}/src/gravity_cli/templates/minimal/requirements.txt +0 -0
- {gravity_cli-0.4.4 → gravity_cli-0.5.2}/src/gravity_cli/templates/minimal/src/agent.py +0 -0
- {gravity_cli-0.4.4 → gravity_cli-0.5.2}/src/gravity_cli/templates/structured/.gitignore +0 -0
- {gravity_cli-0.4.4 → gravity_cli-0.5.2}/src/gravity_cli/templates/structured/manifest.yaml +0 -0
- {gravity_cli-0.4.4 → gravity_cli-0.5.2}/src/gravity_cli/templates/structured/requirements.txt +0 -0
- {gravity_cli-0.4.4 → gravity_cli-0.5.2}/src/gravity_cli/templates/structured/src/agent.py +0 -0
- {gravity_cli-0.4.4 → gravity_cli-0.5.2}/src/gravity_cli/templates/structured/src/gateway.py +0 -0
- {gravity_cli-0.4.4 → gravity_cli-0.5.2}/src/gravity_cli/templates/structured/src/nodes/agent.py +0 -0
- {gravity_cli-0.4.4 → gravity_cli-0.5.2}/src/gravity_cli/templates/structured/src/prompts.py +0 -0
- {gravity_cli-0.4.4 → gravity_cli-0.5.2}/src/gravity_cli/templates/structured/src/state.py +0 -0
- {gravity_cli-0.4.4 → gravity_cli-0.5.2}/src/gravity_cli/timeline.py +0 -0
- {gravity_cli-0.4.4 → gravity_cli-0.5.2}/starter/manifest.yaml +0 -0
- {gravity_cli-0.4.4 → gravity_cli-0.5.2}/starter/requirements.txt +0 -0
- {gravity_cli-0.4.4 → gravity_cli-0.5.2}/starter/src/agent.py +0 -0
- {gravity_cli-0.4.4 → gravity_cli-0.5.2}/starter/src/summary.py +0 -0
- {gravity_cli-0.4.4 → gravity_cli-0.5.2}/starter/tests/test_summary.py +0 -0
- {gravity_cli-0.4.4 → gravity_cli-0.5.2}/tests/conftest.py +0 -0
- {gravity_cli-0.4.4 → gravity_cli-0.5.2}/tests/test_adapt.py +0 -0
- {gravity_cli-0.4.4 → gravity_cli-0.5.2}/tests/test_build_push.py +0 -0
- {gravity_cli-0.4.4 → gravity_cli-0.5.2}/tests/test_gateway_token.py +0 -0
- {gravity_cli-0.4.4 → gravity_cli-0.5.2}/tests/test_manifest_integration.py +0 -0
- {gravity_cli-0.4.4 → gravity_cli-0.5.2}/tests/test_timeline.py +0 -0
- {gravity_cli-0.4.4 → gravity_cli-0.5.2}/tests/test_ui.py +0 -0
- {gravity_cli-0.4.4 → gravity_cli-0.5.2}/tests/test_welcome.py +0 -0
|
@@ -191,6 +191,17 @@ model = ChatOpenAI(model="openai/gpt-6-luna",
|
|
|
191
191
|
api_key=os.environ["GRAVITY_RUN_TOKEN"])
|
|
192
192
|
```
|
|
193
193
|
|
|
194
|
+
**Embeddings** use the same URL and token (`<GRAVITY_LLM_URL>/embeddings`), metered
|
|
195
|
+
and budgeted like chat calls:
|
|
196
|
+
|
|
197
|
+
```python
|
|
198
|
+
emb = OpenAIEmbeddings(model="voyageai/voyage-4-lite", # or google/gemini-embedding-2
|
|
199
|
+
base_url=os.environ["GRAVITY_LLM_URL"],
|
|
200
|
+
api_key=os.environ["GRAVITY_RUN_TOKEN"],
|
|
201
|
+
check_embedding_ctx_length=False) # send text, not OpenAI token ids
|
|
202
|
+
vectors = await emb.aembed_documents(["first text", "second text"])
|
|
203
|
+
```
|
|
204
|
+
|
|
194
205
|
Those three env vars — `GRAVITY_GATEWAY_URL`, `GRAVITY_LLM_URL`,
|
|
195
206
|
`GRAVITY_RUN_TOKEN` — are the entire Gravity surface, plus the `gravity`
|
|
196
207
|
package for tools on the platform (§8). No `OPENAI_API_KEY`: locally and in
|
|
@@ -271,7 +282,10 @@ start**: everything before the call happens twice.
|
|
|
271
282
|
replaced. State is kept per thread. A key the next run doesn't send keeps its
|
|
272
283
|
value (a monitor's `seen`, a draft). For memory shared by one consumer's
|
|
273
284
|
threads, use `langgraph.config.get_store()` (`aget` / `aput` / `asearch` by
|
|
274
|
-
namespace and filter; no semantic search).
|
|
285
|
+
namespace and filter; no semantic search). It is kept per consumer and agent,
|
|
286
|
+
across runs and versions, until the agent deletes an item (`aput(ns, key, None)`);
|
|
287
|
+
store one item per record, not one large blob. `gravity run` gives you the same
|
|
288
|
+
store locally, saved in `.gravity/store.json` (delete it to start over).
|
|
275
289
|
|
|
276
290
|
**Conversation.** `messages` must use the `add_messages` reducer:
|
|
277
291
|
`messages: Annotated[list, add_messages]`. Each turn the platform sends only
|
|
@@ -1,3 +1,20 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: gravity-cli
|
|
3
|
+
Version: 0.5.2
|
|
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.5.2
|
|
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
|
+
|
|
1
18
|
<h1 align="center">Gravity CLI</h1>
|
|
2
19
|
|
|
3
20
|
<p align="center">
|
|
@@ -161,8 +178,8 @@ gravity push
|
|
|
161
178
|
|
|
162
179
|
| Service | What it does | Used by |
|
|
163
180
|
|---|---|---|
|
|
164
|
-
| **Gateway** | Serves tools over MCP (`/mcp`) and models over an OpenAI-compatible API (`/v1`). Issues run tokens and refuses any tool a run was not granted. | `run`, `schedule`, `adapt`, and your agent |
|
|
165
|
-
| **Control plane** | Accounts,
|
|
181
|
+
| **Gateway** | Serves tools over MCP (`/mcp`) and models over an OpenAI-compatible API (`/v1`). Lists its tools at `/tools` and your connected accounts at `/connections`. Issues run tokens and refuses any tool a run was not granted. | `run`, `schedule`, `adapt`, `tools`, `connect`, and your agent |
|
|
182
|
+
| **Control plane** | Accounts, pushed agents, builds and hosted runs. | `login`, `logout`, `whoami`, `push`, `agents`, `runs`, `logs` |
|
|
166
183
|
|
|
167
184
|
On `gravity run`, the CLI trades your dev token for a run token scoped to the manifest's tools and starts the agent with only that token. Your own tokens are removed from the agent's environment, so agent code can never act as you.
|
|
168
185
|
|
|
@@ -190,6 +207,7 @@ Run `gravity help` for this list in the terminal, and add `-h` to any command fo
|
|
|
190
207
|
| **Account** | [`gravity login`](#gravity-login) | Sign in to Gravity in your browser |
|
|
191
208
|
| | [`gravity logout`](#gravity-logout) | Sign out |
|
|
192
209
|
| | [`gravity whoami`](#gravity-whoami) | Show who you are signed in as |
|
|
210
|
+
| | [`gravity connect`](#gravity-connect) | Connect an account your agents act on (Gmail, Slack...) |
|
|
193
211
|
| **Help** | [`gravity guide`](#gravity-guide) | Read the builder guide: how to build a good agent |
|
|
194
212
|
| | [`gravity help`](#gravity-help) | Show the list of commands |
|
|
195
213
|
|
|
@@ -238,7 +256,10 @@ What it checks:
|
|
|
238
256
|
- the entrypoint file exists and defines the object the manifest names
|
|
239
257
|
- `requirements.txt` exists, and compiles into `requirements.lock` for the platform image (ARM64 Linux) and the manifest's Python version
|
|
240
258
|
- every adapter a slot declares has a file, and every adapter file is declared
|
|
241
|
-
-
|
|
259
|
+
- every tool in the manifest exists in the catalog, the same list the platform's build uses, so a typo fails here instead of after a push
|
|
260
|
+
- every imported write tool is under `approvals.required_for`
|
|
261
|
+
- your code never names a write tool the manifest does not grant
|
|
262
|
+
- a warning, never a failure, for read tools your code names but the manifest does not declare
|
|
242
263
|
|
|
243
264
|
```bash
|
|
244
265
|
gravity validate
|
|
@@ -376,7 +397,9 @@ gravity logs <run_id>
|
|
|
376
397
|
|
|
377
398
|
### `gravity tools`
|
|
378
399
|
|
|
379
|
-
Browse the tool catalog: the names you can put under `permissions.tools`, with
|
|
400
|
+
Browse the tool catalog: the names you can put under `permissions.tools`, with what each one does. It is the gateway's own list, so it shows exactly what a run can call, and it needs no sign-in.
|
|
401
|
+
|
|
402
|
+
About 25 tools are curated, such as `gmail.read` and `web.search`. The other ~1,100 are imported from the same apps and unreviewed: any imported tool that changes something must also be under `approvals.required_for`, and `gravity validate` checks that.
|
|
380
403
|
|
|
381
404
|
```
|
|
382
405
|
gravity tools [search]
|
|
@@ -412,6 +435,23 @@ gravity whoami
|
|
|
412
435
|
gravity whoami --token # print the gateway token itself, for CI or a teammate's .env
|
|
413
436
|
```
|
|
414
437
|
|
|
438
|
+
### `gravity connect`
|
|
439
|
+
|
|
440
|
+
Connect an account your agents' tools act on, through the app's own sign-in page in your browser. With no app, list what is connected.
|
|
441
|
+
|
|
442
|
+
```
|
|
443
|
+
gravity connect [app]
|
|
444
|
+
```
|
|
445
|
+
|
|
446
|
+
```bash
|
|
447
|
+
gravity connect # what is connected, and whose accounts runs act on
|
|
448
|
+
gravity connect gmail # opens the sign-in page, then waits until it is done
|
|
449
|
+
gravity connect sheets # use the app name from tool names: sheets, calendar, drive, linear, slack, github...
|
|
450
|
+
```
|
|
451
|
+
|
|
452
|
+
- Uses the gateway token from `gravity login`.
|
|
453
|
+
- `web` tools need no account.
|
|
454
|
+
|
|
415
455
|
### `gravity guide`
|
|
416
456
|
|
|
417
457
|
Read the builder guide, which ships with the CLI: how an agent is put together, the rules your code must follow, formatting results, approvals, adapters, scheduling, memory, and a checklist.
|
|
@@ -472,7 +512,8 @@ Never commit a `.env` file. The `.gitignore` in every template keeps `.env` and
|
|
|
472
512
|
|---|---|---|---|
|
|
473
513
|
| Create, check and package an agent | `init`, `validate`, `build` | nothing | nothing |
|
|
474
514
|
| Run an agent locally | `run`, `schedule`, `adapt` | `gravity login` (or `GRAVITY_DEV_TOKEN`) | nothing |
|
|
475
|
-
| Publish and inspect agents | `push`, `
|
|
515
|
+
| Publish and inspect agents | `push`, `agents`, `runs`, `logs`, `whoami` | `gravity login` | nothing |
|
|
516
|
+
| Browse tools, connect accounts | `tools`, `connect` | nothing for `tools`; `gravity login` for `connect` | nothing |
|
|
476
517
|
|
|
477
518
|
Set `GRAVITY_API_URL` and `GRAVITY_GATEWAY_URL` only to work against services you run yourself; see [Running a local gateway](#running-a-local-gateway).
|
|
478
519
|
|
|
@@ -714,6 +755,8 @@ Real tools act on a connected account. Connect one with `gravity-gateway connect
|
|
|
714
755
|
|
|
715
756
|
## What's new
|
|
716
757
|
|
|
758
|
+
- **0.5.2** Jira, Confluence and Notion join the catalog: 1,351 tools across 11 apps. `get_store()` works in local runs, kept in `.gravity/store.json`. Renewing an expired sign-in keeps your gateway token, so `gravity run` keeps working after an hour. Commands start faster: the catalog loads in a quarter of the time.
|
|
759
|
+
- **0.5.0** `gravity connect` links your own accounts. `web.search` and `web.fetch` join the catalog. `gravity validate` now refuses a tool that isn't in the catalog, an imported write tool without an approval, and code that names a write tool the manifest doesn't grant. `gravity tools` reads the gateway's list and needs no sign-in. On Windows the session stays in the keychain instead of a file.
|
|
717
760
|
- **0.4.4** Same code as 0.4.3, released from this repository so the GitHub release, the tag and PyPI line up again. Adds `RELEASING.md`.
|
|
718
761
|
- **0.4.3** Documents the `secret` input type and the `supadata.get_transcript` tool.
|
|
719
762
|
- **0.4.1** New input type `secret` for an API key, password or other confidential value. New built-in tool `supadata.get_transcript` (`gravity tools supadata`).
|
|
@@ -1,20 +1,3 @@
|
|
|
1
|
-
Metadata-Version: 2.5
|
|
2
|
-
Name: gravity-cli
|
|
3
|
-
Version: 0.4.4
|
|
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.4.4
|
|
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
1
|
<h1 align="center">Gravity CLI</h1>
|
|
19
2
|
|
|
20
3
|
<p align="center">
|
|
@@ -178,8 +161,8 @@ gravity push
|
|
|
178
161
|
|
|
179
162
|
| Service | What it does | Used by |
|
|
180
163
|
|---|---|---|
|
|
181
|
-
| **Gateway** | Serves tools over MCP (`/mcp`) and models over an OpenAI-compatible API (`/v1`). Issues run tokens and refuses any tool a run was not granted. | `run`, `schedule`, `adapt`, and your agent |
|
|
182
|
-
| **Control plane** | Accounts,
|
|
164
|
+
| **Gateway** | Serves tools over MCP (`/mcp`) and models over an OpenAI-compatible API (`/v1`). Lists its tools at `/tools` and your connected accounts at `/connections`. Issues run tokens and refuses any tool a run was not granted. | `run`, `schedule`, `adapt`, `tools`, `connect`, and your agent |
|
|
165
|
+
| **Control plane** | Accounts, pushed agents, builds and hosted runs. | `login`, `logout`, `whoami`, `push`, `agents`, `runs`, `logs` |
|
|
183
166
|
|
|
184
167
|
On `gravity run`, the CLI trades your dev token for a run token scoped to the manifest's tools and starts the agent with only that token. Your own tokens are removed from the agent's environment, so agent code can never act as you.
|
|
185
168
|
|
|
@@ -207,6 +190,7 @@ Run `gravity help` for this list in the terminal, and add `-h` to any command fo
|
|
|
207
190
|
| **Account** | [`gravity login`](#gravity-login) | Sign in to Gravity in your browser |
|
|
208
191
|
| | [`gravity logout`](#gravity-logout) | Sign out |
|
|
209
192
|
| | [`gravity whoami`](#gravity-whoami) | Show who you are signed in as |
|
|
193
|
+
| | [`gravity connect`](#gravity-connect) | Connect an account your agents act on (Gmail, Slack...) |
|
|
210
194
|
| **Help** | [`gravity guide`](#gravity-guide) | Read the builder guide: how to build a good agent |
|
|
211
195
|
| | [`gravity help`](#gravity-help) | Show the list of commands |
|
|
212
196
|
|
|
@@ -255,7 +239,10 @@ What it checks:
|
|
|
255
239
|
- the entrypoint file exists and defines the object the manifest names
|
|
256
240
|
- `requirements.txt` exists, and compiles into `requirements.lock` for the platform image (ARM64 Linux) and the manifest's Python version
|
|
257
241
|
- every adapter a slot declares has a file, and every adapter file is declared
|
|
258
|
-
-
|
|
242
|
+
- every tool in the manifest exists in the catalog, the same list the platform's build uses, so a typo fails here instead of after a push
|
|
243
|
+
- every imported write tool is under `approvals.required_for`
|
|
244
|
+
- your code never names a write tool the manifest does not grant
|
|
245
|
+
- a warning, never a failure, for read tools your code names but the manifest does not declare
|
|
259
246
|
|
|
260
247
|
```bash
|
|
261
248
|
gravity validate
|
|
@@ -393,7 +380,9 @@ gravity logs <run_id>
|
|
|
393
380
|
|
|
394
381
|
### `gravity tools`
|
|
395
382
|
|
|
396
|
-
Browse the tool catalog: the names you can put under `permissions.tools`, with
|
|
383
|
+
Browse the tool catalog: the names you can put under `permissions.tools`, with what each one does. It is the gateway's own list, so it shows exactly what a run can call, and it needs no sign-in.
|
|
384
|
+
|
|
385
|
+
About 25 tools are curated, such as `gmail.read` and `web.search`. The other ~1,100 are imported from the same apps and unreviewed: any imported tool that changes something must also be under `approvals.required_for`, and `gravity validate` checks that.
|
|
397
386
|
|
|
398
387
|
```
|
|
399
388
|
gravity tools [search]
|
|
@@ -429,6 +418,23 @@ gravity whoami
|
|
|
429
418
|
gravity whoami --token # print the gateway token itself, for CI or a teammate's .env
|
|
430
419
|
```
|
|
431
420
|
|
|
421
|
+
### `gravity connect`
|
|
422
|
+
|
|
423
|
+
Connect an account your agents' tools act on, through the app's own sign-in page in your browser. With no app, list what is connected.
|
|
424
|
+
|
|
425
|
+
```
|
|
426
|
+
gravity connect [app]
|
|
427
|
+
```
|
|
428
|
+
|
|
429
|
+
```bash
|
|
430
|
+
gravity connect # what is connected, and whose accounts runs act on
|
|
431
|
+
gravity connect gmail # opens the sign-in page, then waits until it is done
|
|
432
|
+
gravity connect sheets # use the app name from tool names: sheets, calendar, drive, linear, slack, github...
|
|
433
|
+
```
|
|
434
|
+
|
|
435
|
+
- Uses the gateway token from `gravity login`.
|
|
436
|
+
- `web` tools need no account.
|
|
437
|
+
|
|
432
438
|
### `gravity guide`
|
|
433
439
|
|
|
434
440
|
Read the builder guide, which ships with the CLI: how an agent is put together, the rules your code must follow, formatting results, approvals, adapters, scheduling, memory, and a checklist.
|
|
@@ -489,7 +495,8 @@ Never commit a `.env` file. The `.gitignore` in every template keeps `.env` and
|
|
|
489
495
|
|---|---|---|---|
|
|
490
496
|
| Create, check and package an agent | `init`, `validate`, `build` | nothing | nothing |
|
|
491
497
|
| Run an agent locally | `run`, `schedule`, `adapt` | `gravity login` (or `GRAVITY_DEV_TOKEN`) | nothing |
|
|
492
|
-
| Publish and inspect agents | `push`, `
|
|
498
|
+
| Publish and inspect agents | `push`, `agents`, `runs`, `logs`, `whoami` | `gravity login` | nothing |
|
|
499
|
+
| Browse tools, connect accounts | `tools`, `connect` | nothing for `tools`; `gravity login` for `connect` | nothing |
|
|
493
500
|
|
|
494
501
|
Set `GRAVITY_API_URL` and `GRAVITY_GATEWAY_URL` only to work against services you run yourself; see [Running a local gateway](#running-a-local-gateway).
|
|
495
502
|
|
|
@@ -731,6 +738,8 @@ Real tools act on a connected account. Connect one with `gravity-gateway connect
|
|
|
731
738
|
|
|
732
739
|
## What's new
|
|
733
740
|
|
|
741
|
+
- **0.5.2** Jira, Confluence and Notion join the catalog: 1,351 tools across 11 apps. `get_store()` works in local runs, kept in `.gravity/store.json`. Renewing an expired sign-in keeps your gateway token, so `gravity run` keeps working after an hour. Commands start faster: the catalog loads in a quarter of the time.
|
|
742
|
+
- **0.5.0** `gravity connect` links your own accounts. `web.search` and `web.fetch` join the catalog. `gravity validate` now refuses a tool that isn't in the catalog, an imported write tool without an approval, and code that names a write tool the manifest doesn't grant. `gravity tools` reads the gateway's list and needs no sign-in. On Windows the session stays in the keychain instead of a file.
|
|
734
743
|
- **0.4.4** Same code as 0.4.3, released from this repository so the GitHub release, the tag and PyPI line up again. Adds `RELEASING.md`.
|
|
735
744
|
- **0.4.3** Documents the `secret` input type and the `supadata.get_transcript` tool.
|
|
736
745
|
- **0.4.1** New input type `secret` for an API key, password or other confidential value. New built-in tool `supadata.get_transcript` (`gravity tools supadata`).
|
|
@@ -36,7 +36,9 @@ from .manifest import (
|
|
|
36
36
|
StateSpec,
|
|
37
37
|
TriggersSpec,
|
|
38
38
|
)
|
|
39
|
+
from .source_scan import is_agent_source, undeclared_write_tools
|
|
39
40
|
from .validation import (
|
|
41
|
+
check_imported_write_approvals,
|
|
40
42
|
check_tool_references,
|
|
41
43
|
load_manifest_file,
|
|
42
44
|
load_manifest_text,
|
|
@@ -73,6 +75,10 @@ __all__ = [ # noqa: RUF022 - grouped by concern, not alphabetical
|
|
|
73
75
|
"load_manifest_text",
|
|
74
76
|
"load_manifest_file",
|
|
75
77
|
"check_tool_references",
|
|
78
|
+
"check_imported_write_approvals",
|
|
79
|
+
# source scan
|
|
80
|
+
"is_agent_source",
|
|
81
|
+
"undeclared_write_tools",
|
|
76
82
|
# catalog
|
|
77
83
|
"ToolCatalog",
|
|
78
84
|
"StaticToolCatalog",
|
|
@@ -10,6 +10,13 @@ from typing import Protocol
|
|
|
10
10
|
|
|
11
11
|
import yaml
|
|
12
12
|
|
|
13
|
+
# libyaml's parser, about 8x faster: every CLI command loads the whole catalog at import.
|
|
14
|
+
_LOADER = getattr(yaml, "CSafeLoader", yaml.SafeLoader)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def _load(text: str):
|
|
18
|
+
return yaml.load(text, Loader=_LOADER)
|
|
19
|
+
|
|
13
20
|
|
|
14
21
|
class ToolCatalog(Protocol):
|
|
15
22
|
def exists(self, tool_name: str) -> bool: ...
|
|
@@ -39,6 +46,7 @@ class ToolEntry:
|
|
|
39
46
|
breadth: str # generic | niche
|
|
40
47
|
guarantee: str # certified | community | experimental
|
|
41
48
|
side_effect: str # read | write
|
|
49
|
+
toolkit: str | None = None # the account a user connects for it; None when none is needed
|
|
42
50
|
|
|
43
51
|
|
|
44
52
|
_BREADTH = {"generic", "niche"}
|
|
@@ -48,12 +56,12 @@ _SIDE_EFFECT = {"read", "write"}
|
|
|
48
56
|
|
|
49
57
|
def load_catalog(path: Path | None = None) -> tuple[ToolEntry, ...]:
|
|
50
58
|
"""Parse and check catalog.yaml; a malformed entry fails at import."""
|
|
51
|
-
raw =
|
|
59
|
+
raw = _load((path or Path(__file__).with_name("catalog.yaml")).read_text(encoding="utf-8"))
|
|
52
60
|
# catalog_imported.yaml (`gravity-gateway sync-tools`): the same apps' other tools,
|
|
53
61
|
# experimental. Absent until the first sync; never read for an explicit path.
|
|
54
62
|
imported = Path(__file__).with_name("catalog_imported.yaml")
|
|
55
63
|
if path is None and imported.is_file():
|
|
56
|
-
raw = [*raw, *(
|
|
64
|
+
raw = [*raw, *(_load(imported.read_text(encoding="utf-8")) or [])]
|
|
57
65
|
entries: list[ToolEntry] = []
|
|
58
66
|
for item in raw:
|
|
59
67
|
entry = ToolEntry(
|
|
@@ -64,6 +72,7 @@ def load_catalog(path: Path | None = None) -> tuple[ToolEntry, ...]:
|
|
|
64
72
|
breadth=item["breadth"],
|
|
65
73
|
guarantee=item["guarantee"],
|
|
66
74
|
side_effect=item["side_effect"],
|
|
75
|
+
toolkit=item.get("toolkit"),
|
|
67
76
|
)
|
|
68
77
|
problems = []
|
|
69
78
|
if entry.breadth not in _BREADTH:
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
# Fields
|
|
11
11
|
# name the Gravity name builders write in permissions.tools
|
|
12
12
|
# slug the vendor action it routes to; null for a gateway built-in
|
|
13
|
+
# toolkit the account a user connects for it (gravity connect); null when none is needed
|
|
13
14
|
# description what a builder or a model reads to choose it
|
|
14
15
|
# scopes the consent a consumer grants when connecting
|
|
15
16
|
# breadth generic: ordinary plumbing any agent might use
|
|
@@ -21,14 +22,34 @@
|
|
|
21
22
|
|
|
22
23
|
- name: example.echo
|
|
23
24
|
slug: null
|
|
25
|
+
toolkit: null
|
|
24
26
|
description: echoes back its input, for templates and plumbing tests
|
|
25
27
|
scopes: []
|
|
26
28
|
breadth: generic
|
|
27
29
|
guarantee: certified
|
|
28
30
|
side_effect: read
|
|
29
31
|
|
|
32
|
+
- name: web.search
|
|
33
|
+
slug: COMPOSIO_SEARCH_WEB
|
|
34
|
+
toolkit: composio_search
|
|
35
|
+
description: search the web; returns results with titles, links and snippets. No account needed
|
|
36
|
+
scopes: []
|
|
37
|
+
breadth: generic
|
|
38
|
+
guarantee: certified
|
|
39
|
+
side_effect: read
|
|
40
|
+
|
|
41
|
+
- name: web.fetch
|
|
42
|
+
slug: COMPOSIO_SEARCH_FETCH_URL_CONTENT
|
|
43
|
+
toolkit: composio_search
|
|
44
|
+
description: read the text content of a web page by its URL. No account needed
|
|
45
|
+
scopes: []
|
|
46
|
+
breadth: generic
|
|
47
|
+
guarantee: certified
|
|
48
|
+
side_effect: read
|
|
49
|
+
|
|
30
50
|
- name: supadata.get_transcript
|
|
31
51
|
slug: null
|
|
52
|
+
toolkit: null
|
|
32
53
|
description: fetch the transcript of a YouTube video with the caller's own Supadata API key (api_key argument)
|
|
33
54
|
scopes: []
|
|
34
55
|
breadth: niche
|
|
@@ -37,6 +58,7 @@
|
|
|
37
58
|
|
|
38
59
|
- name: gmail.read
|
|
39
60
|
slug: GMAIL_FETCH_EMAILS
|
|
61
|
+
toolkit: gmail
|
|
40
62
|
description: read the connected Gmail inbox
|
|
41
63
|
scopes: [gmail.readonly]
|
|
42
64
|
breadth: generic
|
|
@@ -45,6 +67,7 @@
|
|
|
45
67
|
|
|
46
68
|
- name: gmail.draft
|
|
47
69
|
slug: GMAIL_CREATE_EMAIL_DRAFT
|
|
70
|
+
toolkit: gmail
|
|
48
71
|
description: create a Gmail draft
|
|
49
72
|
scopes: [gmail.compose]
|
|
50
73
|
breadth: generic
|
|
@@ -53,6 +76,7 @@
|
|
|
53
76
|
|
|
54
77
|
- name: gmail.send
|
|
55
78
|
slug: GMAIL_SEND_EMAIL
|
|
79
|
+
toolkit: gmail
|
|
56
80
|
description: send an email from the connected Gmail
|
|
57
81
|
scopes: [gmail.send]
|
|
58
82
|
breadth: generic
|
|
@@ -61,6 +85,7 @@
|
|
|
61
85
|
|
|
62
86
|
- name: sheets.read
|
|
63
87
|
slug: GOOGLESHEETS_BATCH_GET
|
|
88
|
+
toolkit: googlesheets
|
|
64
89
|
description: read values from a connected Google Sheet
|
|
65
90
|
scopes: [sheets]
|
|
66
91
|
breadth: generic
|
|
@@ -69,6 +94,7 @@
|
|
|
69
94
|
|
|
70
95
|
- name: sheets.append
|
|
71
96
|
slug: GOOGLESHEETS_BATCH_UPDATE
|
|
97
|
+
toolkit: googlesheets
|
|
72
98
|
description: append a row to a connected Google Sheet
|
|
73
99
|
scopes: [sheets]
|
|
74
100
|
breadth: generic
|
|
@@ -77,6 +103,7 @@
|
|
|
77
103
|
|
|
78
104
|
- name: slack.send
|
|
79
105
|
slug: SLACK_SEND_MESSAGE
|
|
106
|
+
toolkit: slack
|
|
80
107
|
description: post a message to a connected Slack channel
|
|
81
108
|
scopes: [slack.chat:write]
|
|
82
109
|
breadth: generic
|
|
@@ -85,6 +112,7 @@
|
|
|
85
112
|
|
|
86
113
|
- name: slack.list_channels
|
|
87
114
|
slug: SLACK_LIST_ALL_CHANNELS
|
|
115
|
+
toolkit: slack
|
|
88
116
|
description: list channels in the connected Slack workspace
|
|
89
117
|
scopes: [slack.channels:read]
|
|
90
118
|
breadth: generic
|
|
@@ -93,6 +121,7 @@
|
|
|
93
121
|
|
|
94
122
|
- name: slack.read_history
|
|
95
123
|
slug: SLACK_FETCH_CONVERSATION_HISTORY
|
|
124
|
+
toolkit: slack
|
|
96
125
|
description: read message history of a Slack channel
|
|
97
126
|
scopes: [slack.channels:history]
|
|
98
127
|
breadth: generic
|
|
@@ -101,6 +130,7 @@
|
|
|
101
130
|
|
|
102
131
|
- name: slack.find_user
|
|
103
132
|
slug: SLACK_FIND_USERS
|
|
133
|
+
toolkit: slack
|
|
104
134
|
description: find a Slack user by name, handle or email, for mentions
|
|
105
135
|
scopes: [slack.users:read]
|
|
106
136
|
breadth: generic
|
|
@@ -109,6 +139,7 @@
|
|
|
109
139
|
|
|
110
140
|
- name: calendar.read
|
|
111
141
|
slug: GOOGLECALENDAR_EVENTS_LIST
|
|
142
|
+
toolkit: googlecalendar
|
|
112
143
|
description: list events on the connected Google Calendar
|
|
113
144
|
scopes: [calendar.readonly]
|
|
114
145
|
breadth: generic
|
|
@@ -117,6 +148,7 @@
|
|
|
117
148
|
|
|
118
149
|
- name: drive.read
|
|
119
150
|
slug: GOOGLEDRIVE_DOWNLOAD_FILE
|
|
151
|
+
toolkit: googledrive
|
|
120
152
|
description: download a file from the connected Google Drive
|
|
121
153
|
scopes: [drive.readonly]
|
|
122
154
|
breadth: generic
|
|
@@ -125,6 +157,7 @@
|
|
|
125
157
|
|
|
126
158
|
- name: linear.create_issue
|
|
127
159
|
slug: LINEAR_CREATE_LINEAR_ISSUE
|
|
160
|
+
toolkit: linear
|
|
128
161
|
description: create an issue in a connected Linear team
|
|
129
162
|
scopes: [linear.write]
|
|
130
163
|
breadth: generic
|
|
@@ -133,6 +166,7 @@
|
|
|
133
166
|
|
|
134
167
|
- name: linear.list_teams
|
|
135
168
|
slug: LINEAR_LIST_LINEAR_TEAMS
|
|
169
|
+
toolkit: linear
|
|
136
170
|
description: list the connected Linear workspace teams
|
|
137
171
|
scopes: [linear.read]
|
|
138
172
|
breadth: generic
|
|
@@ -141,6 +175,7 @@
|
|
|
141
175
|
|
|
142
176
|
- name: linear.list_issues
|
|
143
177
|
slug: LINEAR_LIST_LINEAR_ISSUES
|
|
178
|
+
toolkit: linear
|
|
144
179
|
description: list open issues in the connected Linear workspace
|
|
145
180
|
scopes: [linear.read]
|
|
146
181
|
breadth: generic
|
|
@@ -149,6 +184,7 @@
|
|
|
149
184
|
|
|
150
185
|
- name: linear.get_issue
|
|
151
186
|
slug: LINEAR_GET_LINEAR_ISSUE
|
|
187
|
+
toolkit: linear
|
|
152
188
|
description: read one Linear issue by id
|
|
153
189
|
scopes: [linear.read]
|
|
154
190
|
breadth: generic
|
|
@@ -158,6 +194,7 @@
|
|
|
158
194
|
# GitHub: read-only, for change summaries and contribution profiles.
|
|
159
195
|
- name: github.list_commits
|
|
160
196
|
slug: GITHUB_LIST_COMMITS
|
|
197
|
+
toolkit: github
|
|
161
198
|
description: list commits on a repository branch, filterable by date range, author or path
|
|
162
199
|
scopes: [repo]
|
|
163
200
|
breadth: generic
|
|
@@ -166,6 +203,7 @@
|
|
|
166
203
|
|
|
167
204
|
- name: github.get_commit
|
|
168
205
|
slug: GITHUB_GET_A_COMMIT
|
|
206
|
+
toolkit: github
|
|
169
207
|
description: read one commit with its changed files, line counts and patches
|
|
170
208
|
scopes: [repo]
|
|
171
209
|
breadth: generic
|
|
@@ -174,6 +212,7 @@
|
|
|
174
212
|
|
|
175
213
|
- name: github.compare
|
|
176
214
|
slug: GITHUB_COMPARE_TWO_COMMITS
|
|
215
|
+
toolkit: github
|
|
177
216
|
description: compare two refs (base...head) with the commits and changed files between them
|
|
178
217
|
scopes: [repo]
|
|
179
218
|
breadth: generic
|
|
@@ -182,6 +221,7 @@
|
|
|
182
221
|
|
|
183
222
|
- name: github.list_pull_requests
|
|
184
223
|
slug: GITHUB_LIST_PULL_REQUESTS
|
|
224
|
+
toolkit: github
|
|
185
225
|
description: list pull requests on a repository, filterable by state and branch
|
|
186
226
|
scopes: [repo]
|
|
187
227
|
breadth: generic
|
|
@@ -190,6 +230,7 @@
|
|
|
190
230
|
|
|
191
231
|
- name: github.list_pr_files
|
|
192
232
|
slug: GITHUB_LIST_PULL_REQUESTS_FILES
|
|
233
|
+
toolkit: github
|
|
193
234
|
description: list the files a pull request changes, with line counts and patches
|
|
194
235
|
scopes: [repo]
|
|
195
236
|
breadth: generic
|
|
@@ -198,6 +239,7 @@
|
|
|
198
239
|
|
|
199
240
|
- name: github.list_pr_reviews
|
|
200
241
|
slug: GITHUB_LIST_REVIEWS_FOR_A_PULL_REQUEST
|
|
242
|
+
toolkit: github
|
|
201
243
|
description: list the reviews submitted on a pull request
|
|
202
244
|
scopes: [repo]
|
|
203
245
|
breadth: generic
|
|
@@ -206,6 +248,7 @@
|
|
|
206
248
|
|
|
207
249
|
- name: github.search_issues
|
|
208
250
|
slug: GITHUB_SEARCH_ISSUES_AND_PULL_REQUESTS
|
|
251
|
+
toolkit: github
|
|
209
252
|
description: search issues and pull requests with GitHub qualifiers (repo:, author:, reviewed-by:, is:pr, created:)
|
|
210
253
|
scopes: [repo]
|
|
211
254
|
breadth: generic
|
|
@@ -214,6 +257,7 @@
|
|
|
214
257
|
|
|
215
258
|
- name: linkedin.whoami
|
|
216
259
|
slug: LINKEDIN_GET_MY_INFO
|
|
260
|
+
toolkit: linkedin
|
|
217
261
|
description: read the connected LinkedIn member's id, name and headline (the author of their posts)
|
|
218
262
|
scopes: [linkedin.profile]
|
|
219
263
|
breadth: generic
|
|
@@ -222,6 +266,7 @@
|
|
|
222
266
|
|
|
223
267
|
- name: linkedin.create_post
|
|
224
268
|
slug: LINKEDIN_CREATE_LINKED_IN_POST
|
|
269
|
+
toolkit: linkedin
|
|
225
270
|
description: publish a post on LinkedIn as the connected member or an organisation they manage
|
|
226
271
|
scopes: [linkedin.w_member_social]
|
|
227
272
|
breadth: generic
|