gravity-cli 0.5.0__tar.gz → 0.5.3__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.5.0 → gravity_cli-0.5.3}/AGENT_STANDARD.md +45 -2
- gravity_cli-0.5.0/README.md → gravity_cli-0.5.3/PKG-INFO +43 -0
- gravity_cli-0.5.0/PKG-INFO → gravity_cli-0.5.3/README.md +26 -17
- {gravity_cli-0.5.0 → gravity_cli-0.5.3}/packages/gravity-schema/gravity_schema/catalog.py +11 -2
- {gravity_cli-0.5.0 → gravity_cli-0.5.3}/packages/gravity-schema/gravity_schema/catalog.yaml +29 -0
- {gravity_cli-0.5.0 → gravity_cli-0.5.3}/packages/gravity-schema/gravity_schema/catalog_imported.yaml +10119 -4285
- {gravity_cli-0.5.0 → gravity_cli-0.5.3}/packages/gravity-schema/pyproject.toml +1 -1
- {gravity_cli-0.5.0 → gravity_cli-0.5.3}/packages/gravity-schema/tests/test_catalog_parity.py +11 -0
- {gravity_cli-0.5.0 → gravity_cli-0.5.3}/pyproject.toml +2 -2
- {gravity_cli-0.5.0 → gravity_cli-0.5.3}/src/gravity_cli/BUILDER_GUIDE.md +2 -1
- {gravity_cli-0.5.0 → gravity_cli-0.5.3}/src/gravity_cli/_runner_script.py +45 -4
- {gravity_cli-0.5.0 → gravity_cli-0.5.3}/src/gravity_cli/api.py +2 -1
- {gravity_cli-0.5.0 → gravity_cli-0.5.3}/src/gravity_cli/cli.py +2 -0
- {gravity_cli-0.5.0 → gravity_cli-0.5.3}/src/gravity_cli/commands/connect.py +11 -7
- gravity_cli-0.5.3/src/gravity_cli/commands/dev.py +40 -0
- {gravity_cli-0.5.0 → gravity_cli-0.5.3}/src/gravity_cli/commands/run.py +159 -63
- {gravity_cli-0.5.0 → gravity_cli-0.5.3}/src/gravity_cli/commands/tools.py +8 -2
- gravity_cli-0.5.3/src/gravity_cli/dev.py +309 -0
- gravity_cli-0.5.3/src/gravity_cli/dev_page/index.html +406 -0
- gravity_cli-0.5.3/src/gravity_cli/localrun.py +228 -0
- {gravity_cli-0.5.0 → gravity_cli-0.5.3}/tests/test_api.py +2 -1
- {gravity_cli-0.5.0 → gravity_cli-0.5.3}/tests/test_connect.py +20 -3
- gravity_cli-0.5.3/tests/test_dev.py +267 -0
- gravity_cli-0.5.3/tests/test_localrun.py +83 -0
- {gravity_cli-0.5.0 → gravity_cli-0.5.3}/tests/test_run.py +26 -1
- {gravity_cli-0.5.0 → gravity_cli-0.5.3}/tests/test_templates.py +68 -0
- gravity_cli-0.5.3/tests/test_tools.py +45 -0
- {gravity_cli-0.5.0 → gravity_cli-0.5.3}/uv.lock +6 -6
- {gravity_cli-0.5.0 → gravity_cli-0.5.3}/.env.example +0 -0
- {gravity_cli-0.5.0 → gravity_cli-0.5.3}/.github/workflows/ci.yml +0 -0
- {gravity_cli-0.5.0 → gravity_cli-0.5.3}/.github/workflows/release.yml +0 -0
- {gravity_cli-0.5.0 → gravity_cli-0.5.3}/.gitignore +0 -0
- {gravity_cli-0.5.0 → gravity_cli-0.5.3}/LICENSE +0 -0
- {gravity_cli-0.5.0 → gravity_cli-0.5.3}/NOTICE +0 -0
- {gravity_cli-0.5.0 → gravity_cli-0.5.3}/RELEASING.md +0 -0
- {gravity_cli-0.5.0 → gravity_cli-0.5.3}/TEMPLATES.md +0 -0
- {gravity_cli-0.5.0 → gravity_cli-0.5.3}/packages/gravity-schema/LICENSE +0 -0
- {gravity_cli-0.5.0 → gravity_cli-0.5.3}/packages/gravity-schema/NOTICE +0 -0
- {gravity_cli-0.5.0 → gravity_cli-0.5.3}/packages/gravity-schema/README.md +0 -0
- {gravity_cli-0.5.0 → gravity_cli-0.5.3}/packages/gravity-schema/gravity_schema/__init__.py +0 -0
- {gravity_cli-0.5.0 → gravity_cli-0.5.3}/packages/gravity-schema/gravity_schema/__main__.py +0 -0
- {gravity_cli-0.5.0 → gravity_cli-0.5.3}/packages/gravity-schema/gravity_schema/contract.py +0 -0
- {gravity_cli-0.5.0 → gravity_cli-0.5.3}/packages/gravity-schema/gravity_schema/cron.py +0 -0
- {gravity_cli-0.5.0 → gravity_cli-0.5.3}/packages/gravity-schema/gravity_schema/errors.py +0 -0
- {gravity_cli-0.5.0 → gravity_cli-0.5.3}/packages/gravity-schema/gravity_schema/export.py +0 -0
- {gravity_cli-0.5.0 → gravity_cli-0.5.3}/packages/gravity-schema/gravity_schema/gen_catalog.py +0 -0
- {gravity_cli-0.5.0 → gravity_cli-0.5.3}/packages/gravity-schema/gravity_schema/manifest.py +0 -0
- {gravity_cli-0.5.0 → gravity_cli-0.5.3}/packages/gravity-schema/gravity_schema/source_scan.py +0 -0
- {gravity_cli-0.5.0 → gravity_cli-0.5.3}/packages/gravity-schema/gravity_schema/validation.py +0 -0
- {gravity_cli-0.5.0 → gravity_cli-0.5.3}/packages/gravity-schema/schema/manifest.schema.json +0 -0
- {gravity_cli-0.5.0 → gravity_cli-0.5.3}/packages/gravity-schema/tests/conftest.py +0 -0
- {gravity_cli-0.5.0 → gravity_cli-0.5.3}/packages/gravity-schema/tests/test_cron.py +0 -0
- {gravity_cli-0.5.0 → gravity_cli-0.5.3}/packages/gravity-schema/tests/test_errors.py +0 -0
- {gravity_cli-0.5.0 → gravity_cli-0.5.3}/packages/gravity-schema/tests/test_json_schema.py +0 -0
- {gravity_cli-0.5.0 → gravity_cli-0.5.3}/packages/gravity-schema/tests/test_kind_outputs.py +0 -0
- {gravity_cli-0.5.0 → gravity_cli-0.5.3}/packages/gravity-schema/tests/test_manifest.py +0 -0
- {gravity_cli-0.5.0 → gravity_cli-0.5.3}/packages/gravity-schema/tests/test_slots.py +0 -0
- {gravity_cli-0.5.0 → gravity_cli-0.5.3}/packages/gravity-schema/tests/test_source_scan.py +0 -0
- {gravity_cli-0.5.0 → gravity_cli-0.5.3}/packages/gravity-schema/tests/test_validation.py +0 -0
- {gravity_cli-0.5.0 → gravity_cli-0.5.3}/src/gravity_cli/__init__.py +0 -0
- {gravity_cli-0.5.0 → gravity_cli-0.5.3}/src/gravity_cli/__main__.py +0 -0
- {gravity_cli-0.5.0 → gravity_cli-0.5.3}/src/gravity_cli/commands/__init__.py +0 -0
- {gravity_cli-0.5.0 → gravity_cli-0.5.3}/src/gravity_cli/commands/adapt.py +0 -0
- {gravity_cli-0.5.0 → gravity_cli-0.5.3}/src/gravity_cli/commands/build.py +0 -0
- {gravity_cli-0.5.0 → gravity_cli-0.5.3}/src/gravity_cli/commands/guide.py +0 -0
- {gravity_cli-0.5.0 → gravity_cli-0.5.3}/src/gravity_cli/commands/init.py +0 -0
- {gravity_cli-0.5.0 → gravity_cli-0.5.3}/src/gravity_cli/commands/login.py +0 -0
- {gravity_cli-0.5.0 → gravity_cli-0.5.3}/src/gravity_cli/commands/push.py +0 -0
- {gravity_cli-0.5.0 → gravity_cli-0.5.3}/src/gravity_cli/commands/runs.py +0 -0
- {gravity_cli-0.5.0 → gravity_cli-0.5.3}/src/gravity_cli/commands/schedule.py +0 -0
- {gravity_cli-0.5.0 → gravity_cli-0.5.3}/src/gravity_cli/commands/validate.py +0 -0
- {gravity_cli-0.5.0 → gravity_cli-0.5.3}/src/gravity_cli/config.py +0 -0
- {gravity_cli-0.5.0 → gravity_cli-0.5.3}/src/gravity_cli/manifest.py +0 -0
- {gravity_cli-0.5.0 → gravity_cli-0.5.3}/src/gravity_cli/project.py +0 -0
- {gravity_cli-0.5.0 → gravity_cli-0.5.3}/src/gravity_cli/templates/chat/.gitignore +0 -0
- {gravity_cli-0.5.0 → gravity_cli-0.5.3}/src/gravity_cli/templates/chat/manifest.yaml +0 -0
- {gravity_cli-0.5.0 → gravity_cli-0.5.3}/src/gravity_cli/templates/chat/requirements.txt +0 -0
- {gravity_cli-0.5.0 → gravity_cli-0.5.3}/src/gravity_cli/templates/chat/src/agent.py +0 -0
- {gravity_cli-0.5.0 → gravity_cli-0.5.3}/src/gravity_cli/templates/deepagent/.gitignore +0 -0
- {gravity_cli-0.5.0 → gravity_cli-0.5.3}/src/gravity_cli/templates/deepagent/manifest.yaml +0 -0
- {gravity_cli-0.5.0 → gravity_cli-0.5.3}/src/gravity_cli/templates/deepagent/requirements.txt +0 -0
- {gravity_cli-0.5.0 → gravity_cli-0.5.3}/src/gravity_cli/templates/deepagent/src/agent.py +0 -0
- {gravity_cli-0.5.0 → gravity_cli-0.5.3}/src/gravity_cli/templates/minimal/.gitignore +0 -0
- {gravity_cli-0.5.0 → gravity_cli-0.5.3}/src/gravity_cli/templates/minimal/manifest.yaml +0 -0
- {gravity_cli-0.5.0 → gravity_cli-0.5.3}/src/gravity_cli/templates/minimal/requirements.txt +0 -0
- {gravity_cli-0.5.0 → gravity_cli-0.5.3}/src/gravity_cli/templates/minimal/src/agent.py +0 -0
- {gravity_cli-0.5.0 → gravity_cli-0.5.3}/src/gravity_cli/templates/structured/.gitignore +0 -0
- {gravity_cli-0.5.0 → gravity_cli-0.5.3}/src/gravity_cli/templates/structured/manifest.yaml +0 -0
- {gravity_cli-0.5.0 → gravity_cli-0.5.3}/src/gravity_cli/templates/structured/requirements.txt +0 -0
- {gravity_cli-0.5.0 → gravity_cli-0.5.3}/src/gravity_cli/templates/structured/src/agent.py +0 -0
- {gravity_cli-0.5.0 → gravity_cli-0.5.3}/src/gravity_cli/templates/structured/src/gateway.py +0 -0
- {gravity_cli-0.5.0 → gravity_cli-0.5.3}/src/gravity_cli/templates/structured/src/nodes/agent.py +0 -0
- {gravity_cli-0.5.0 → gravity_cli-0.5.3}/src/gravity_cli/templates/structured/src/prompts.py +0 -0
- {gravity_cli-0.5.0 → gravity_cli-0.5.3}/src/gravity_cli/templates/structured/src/state.py +0 -0
- {gravity_cli-0.5.0 → gravity_cli-0.5.3}/src/gravity_cli/timeline.py +0 -0
- {gravity_cli-0.5.0 → gravity_cli-0.5.3}/src/gravity_cli/ui.py +0 -0
- {gravity_cli-0.5.0 → gravity_cli-0.5.3}/starter/manifest.yaml +0 -0
- {gravity_cli-0.5.0 → gravity_cli-0.5.3}/starter/requirements.txt +0 -0
- {gravity_cli-0.5.0 → gravity_cli-0.5.3}/starter/src/agent.py +0 -0
- {gravity_cli-0.5.0 → gravity_cli-0.5.3}/starter/src/summary.py +0 -0
- {gravity_cli-0.5.0 → gravity_cli-0.5.3}/starter/tests/test_summary.py +0 -0
- {gravity_cli-0.5.0 → gravity_cli-0.5.3}/tests/conftest.py +0 -0
- {gravity_cli-0.5.0 → gravity_cli-0.5.3}/tests/test_adapt.py +0 -0
- {gravity_cli-0.5.0 → gravity_cli-0.5.3}/tests/test_build_push.py +0 -0
- {gravity_cli-0.5.0 → gravity_cli-0.5.3}/tests/test_gateway_token.py +0 -0
- {gravity_cli-0.5.0 → gravity_cli-0.5.3}/tests/test_manifest_integration.py +0 -0
- {gravity_cli-0.5.0 → gravity_cli-0.5.3}/tests/test_session.py +0 -0
- {gravity_cli-0.5.0 → gravity_cli-0.5.3}/tests/test_timeline.py +0 -0
- {gravity_cli-0.5.0 → gravity_cli-0.5.3}/tests/test_ui.py +0 -0
- {gravity_cli-0.5.0 → gravity_cli-0.5.3}/tests/test_validate.py +0 -0
- {gravity_cli-0.5.0 → gravity_cli-0.5.3}/tests/test_welcome.py +0 -0
|
@@ -191,6 +191,37 @@ 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
|
+
|
|
205
|
+
**Images** come from an image model through the same URL and token: no tool to
|
|
206
|
+
declare. Ask for image output; the picture comes back as a data URL, which is
|
|
207
|
+
exactly what the corpus takes (§8):
|
|
208
|
+
|
|
209
|
+
```python
|
|
210
|
+
async with httpx.AsyncClient(timeout=120) as http:
|
|
211
|
+
resp = await http.post(f"{os.environ['GRAVITY_LLM_URL']}/chat/completions",
|
|
212
|
+
headers={"Authorization": f"Bearer {os.environ['GRAVITY_RUN_TOKEN']}"},
|
|
213
|
+
json={"model": "google/gemini-2.5-flash-image", # or openai/gpt-5-image-mini
|
|
214
|
+
"modalities": ["image", "text"],
|
|
215
|
+
"messages": [{"role": "user", "content": "A flat illustration of ..., no text"}]})
|
|
216
|
+
resp.raise_for_status()
|
|
217
|
+
src = resp.json()["choices"][0]["message"]["images"][0]["image_url"]["url"] # data:image/png;base64,...
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
An image is a few MB and the whole state is checkpointed after every step, so
|
|
221
|
+
make pictures in the **last** node (or the one that builds the corpus) and keep
|
|
222
|
+
them out of state until then. Each one is metered and counts toward the run's
|
|
223
|
+
budget like any model call.
|
|
224
|
+
|
|
194
225
|
Those three env vars — `GRAVITY_GATEWAY_URL`, `GRAVITY_LLM_URL`,
|
|
195
226
|
`GRAVITY_RUN_TOKEN` — are the entire Gravity surface, plus the `gravity`
|
|
196
227
|
package for tools on the platform (§8). No `OPENAI_API_KEY`: locally and in
|
|
@@ -217,6 +248,11 @@ undeclared tool is refused locally exactly as it will be when hosted. Your
|
|
|
217
248
|
agent only ever sees the run token. `GRAVITY_GATEWAY_URL` defaults to
|
|
218
249
|
`http://127.0.0.1:8000/mcp` and `GRAVITY_LLM_URL` to the same host's `/v1`.
|
|
219
250
|
|
|
251
|
+
`gravity dev` opens a local dashboard over the same runs: a form from your
|
|
252
|
+
manifest, a live timeline, approvals, the result and a history. A run from
|
|
253
|
+
`gravity run` in the same folder shows up in it, and the other way round (both
|
|
254
|
+
leave their record in `.gravity/runs/`; one run is live per folder).
|
|
255
|
+
|
|
220
256
|
## 7. What's checked where
|
|
221
257
|
|
|
222
258
|
| Stage | Catches |
|
|
@@ -271,7 +307,10 @@ start**: everything before the call happens twice.
|
|
|
271
307
|
replaced. State is kept per thread. A key the next run doesn't send keeps its
|
|
272
308
|
value (a monitor's `seen`, a draft). For memory shared by one consumer's
|
|
273
309
|
threads, use `langgraph.config.get_store()` (`aget` / `aput` / `asearch` by
|
|
274
|
-
namespace and filter; no semantic search).
|
|
310
|
+
namespace and filter; no semantic search). It is kept per consumer and agent,
|
|
311
|
+
across runs and versions, until the agent deletes an item (`aput(ns, key, None)`);
|
|
312
|
+
store one item per record, not one large blob. `gravity run` gives you the same
|
|
313
|
+
store locally, saved in `.gravity/store.json` (delete it to start over).
|
|
275
314
|
|
|
276
315
|
**Conversation.** `messages` must use the `add_messages` reducer:
|
|
277
316
|
`messages: Annotated[list, add_messages]`. Each turn the platform sends only
|
|
@@ -314,6 +353,7 @@ corpus = gravity.Corpus(
|
|
|
314
353
|
facts=[gravity.Fact(label="TAM", value="$18B", source="s1")], # value: text or a number
|
|
315
354
|
table=gravity.Table(columns=["Segment", "Teams"], rows=[["Founders", "1.2M"]]),
|
|
316
355
|
notes="speaker notes / read-out text",
|
|
356
|
+
images=[gravity.Image(src=src, alt="server rack", caption="Option A")], # data URL, see §4
|
|
317
357
|
sections=[gravity.Section(heading="one level of nesting, same shape")],
|
|
318
358
|
)],
|
|
319
359
|
sources=[gravity.Source(id="s1", title="Report", url="https://example.com")],
|
|
@@ -322,7 +362,10 @@ return {**corpus.to_state(), "result": {"title": corpus.title}}
|
|
|
322
362
|
```
|
|
323
363
|
|
|
324
364
|
A plain dict of the same shape works too. The models catch a typo when the
|
|
325
|
-
corpus is built rather than when it's rendered.
|
|
365
|
+
corpus is built rather than when it's rendered. Images (`png`, `jpeg`, `webp`
|
|
366
|
+
or `gif` data URLs, at most 8 MB each and 12 per corpus) are embedded in the
|
|
367
|
+
Word, PowerPoint and Markdown downloads and shown on `/app` and in `gravity
|
|
368
|
+
dev`; an emailed output names each one as `[image: alt — caption]` instead. Set `result` as well: it's
|
|
326
369
|
what the run returns.
|
|
327
370
|
|
|
328
371
|
## 9. Checklist
|
|
@@ -1,3 +1,20 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: gravity-cli
|
|
3
|
+
Version: 0.5.3
|
|
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.3
|
|
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">
|
|
@@ -179,6 +196,7 @@ Run `gravity help` for this list in the terminal, and add `-h` to any command fo
|
|
|
179
196
|
| **Build an agent** | [`gravity init`](#gravity-init) | Create a new agent from a template |
|
|
180
197
|
| | [`gravity validate`](#gravity-validate) | Check your agent for mistakes before it runs |
|
|
181
198
|
| | [`gravity run`](#gravity-run) | Run your agent locally against real tools |
|
|
199
|
+
| | [`gravity dev`](#gravity-dev) | Open a local dashboard to run and watch your agent |
|
|
182
200
|
| | [`gravity schedule`](#gravity-schedule) | Run your agent on its schedule, locally |
|
|
183
201
|
| | [`gravity adapt`](#gravity-adapt) | Write a new slot adapter with AI, kept only if its tests pass |
|
|
184
202
|
| **Publish** | [`gravity build`](#gravity-build) | Package your agent into `build/agent.zip` |
|
|
@@ -274,6 +292,29 @@ gravity run --reply "make it shorter"
|
|
|
274
292
|
- Requires a gateway token: the one `gravity login` stores, or `GRAVITY_DEV_TOKEN`.
|
|
275
293
|
- A call to a tool in `approvals.required_for` pauses, shows what it would do, and waits for `y` or `n`. With no terminal to ask in, it is rejected.
|
|
276
294
|
- Exits with code 0 when the run finishes and 1 when it fails.
|
|
295
|
+
- Leaves a record in `.gravity/runs/<id>/` (`run.json` and `events.jsonl`), which is what `gravity dev` reads. One run is live per folder at a time, because runs share the folder's memory, store and thread files.
|
|
296
|
+
|
|
297
|
+
### `gravity dev`
|
|
298
|
+
|
|
299
|
+
Open a local dashboard for the agent in this folder: a form built from the manifest's inputs, a live timeline of each step, model call and tool call, approval buttons for held calls, the result, and a history of runs.
|
|
300
|
+
|
|
301
|
+
```
|
|
302
|
+
gravity dev [path] [--port 4747] [--no-open] [--llm-url URL]
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
| Option | Description |
|
|
306
|
+
|---|---|
|
|
307
|
+
| `--port N` | Port to listen on (default 4747). If it is taken, a free one is used and printed. |
|
|
308
|
+
| `--no-open` | Print the address without opening a browser (SSH, WSL, CI) |
|
|
309
|
+
| `--llm-url URL` | Model endpoint to use instead of `GRAVITY_LLM_URL` |
|
|
310
|
+
|
|
311
|
+
- A run from `gravity run` in the same folder shows up in the dashboard live, and a run started in the dashboard is the same run (same engine, same memory, store and thread files), so `gravity run --reply` continues it.
|
|
312
|
+
- It listens on `127.0.0.1` only, behind a random token in the printed address, for as long as the command runs. Ctrl+C stops it and any run it started. Nothing keeps running in the background.
|
|
313
|
+
- Connected accounts: it shows the accounts your tools need and a Connect button for each one that is missing.
|
|
314
|
+
- Approvals: a call held for approval can be decided in the terminal or in the dashboard; whichever answers first wins.
|
|
315
|
+
- Cancel a run from the dashboard only if the dashboard started it; stop a terminal run with Ctrl+C in that terminal.
|
|
316
|
+
- Results show as text. Word, PowerPoint and CSV files are produced by Gravity after you push. A file input is read as text here.
|
|
317
|
+
- Same requirements as `gravity run`: a gateway token from `gravity login`, or `GRAVITY_DEV_TOKEN`.
|
|
277
318
|
|
|
278
319
|
### `gravity schedule`
|
|
279
320
|
|
|
@@ -738,6 +779,8 @@ Real tools act on a connected account. Connect one with `gravity-gateway connect
|
|
|
738
779
|
|
|
739
780
|
## What's new
|
|
740
781
|
|
|
782
|
+
- **0.5.3** `gravity dev` opens a local dashboard for your agent: a form from your manifest, a live timeline, approvals, the result and run history; runs from `gravity run` in the same folder show up in it live, and the other way round. WhatsApp joins the catalog (51 tools; 1,374 across 12 apps). Agents can return generated images: put them in a corpus section's `images` and they land in the Word, PowerPoint and Markdown outputs. The agent standard now shows how to generate images and embeddings through the gateway. `NEEDS_RECONNECT` on the shared test account tells you to ask Gravity, not to connect your own account.
|
|
783
|
+
- **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.
|
|
741
784
|
- **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.
|
|
742
785
|
- **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`.
|
|
743
786
|
- **0.4.3** Documents the `secret` input type and the `supadata.get_transcript` tool.
|
|
@@ -1,20 +1,3 @@
|
|
|
1
|
-
Metadata-Version: 2.5
|
|
2
|
-
Name: gravity-cli
|
|
3
|
-
Version: 0.5.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.5.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
1
|
<h1 align="center">Gravity CLI</h1>
|
|
19
2
|
|
|
20
3
|
<p align="center">
|
|
@@ -196,6 +179,7 @@ Run `gravity help` for this list in the terminal, and add `-h` to any command fo
|
|
|
196
179
|
| **Build an agent** | [`gravity init`](#gravity-init) | Create a new agent from a template |
|
|
197
180
|
| | [`gravity validate`](#gravity-validate) | Check your agent for mistakes before it runs |
|
|
198
181
|
| | [`gravity run`](#gravity-run) | Run your agent locally against real tools |
|
|
182
|
+
| | [`gravity dev`](#gravity-dev) | Open a local dashboard to run and watch your agent |
|
|
199
183
|
| | [`gravity schedule`](#gravity-schedule) | Run your agent on its schedule, locally |
|
|
200
184
|
| | [`gravity adapt`](#gravity-adapt) | Write a new slot adapter with AI, kept only if its tests pass |
|
|
201
185
|
| **Publish** | [`gravity build`](#gravity-build) | Package your agent into `build/agent.zip` |
|
|
@@ -291,6 +275,29 @@ gravity run --reply "make it shorter"
|
|
|
291
275
|
- Requires a gateway token: the one `gravity login` stores, or `GRAVITY_DEV_TOKEN`.
|
|
292
276
|
- A call to a tool in `approvals.required_for` pauses, shows what it would do, and waits for `y` or `n`. With no terminal to ask in, it is rejected.
|
|
293
277
|
- Exits with code 0 when the run finishes and 1 when it fails.
|
|
278
|
+
- Leaves a record in `.gravity/runs/<id>/` (`run.json` and `events.jsonl`), which is what `gravity dev` reads. One run is live per folder at a time, because runs share the folder's memory, store and thread files.
|
|
279
|
+
|
|
280
|
+
### `gravity dev`
|
|
281
|
+
|
|
282
|
+
Open a local dashboard for the agent in this folder: a form built from the manifest's inputs, a live timeline of each step, model call and tool call, approval buttons for held calls, the result, and a history of runs.
|
|
283
|
+
|
|
284
|
+
```
|
|
285
|
+
gravity dev [path] [--port 4747] [--no-open] [--llm-url URL]
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
| Option | Description |
|
|
289
|
+
|---|---|
|
|
290
|
+
| `--port N` | Port to listen on (default 4747). If it is taken, a free one is used and printed. |
|
|
291
|
+
| `--no-open` | Print the address without opening a browser (SSH, WSL, CI) |
|
|
292
|
+
| `--llm-url URL` | Model endpoint to use instead of `GRAVITY_LLM_URL` |
|
|
293
|
+
|
|
294
|
+
- A run from `gravity run` in the same folder shows up in the dashboard live, and a run started in the dashboard is the same run (same engine, same memory, store and thread files), so `gravity run --reply` continues it.
|
|
295
|
+
- It listens on `127.0.0.1` only, behind a random token in the printed address, for as long as the command runs. Ctrl+C stops it and any run it started. Nothing keeps running in the background.
|
|
296
|
+
- Connected accounts: it shows the accounts your tools need and a Connect button for each one that is missing.
|
|
297
|
+
- Approvals: a call held for approval can be decided in the terminal or in the dashboard; whichever answers first wins.
|
|
298
|
+
- Cancel a run from the dashboard only if the dashboard started it; stop a terminal run with Ctrl+C in that terminal.
|
|
299
|
+
- Results show as text. Word, PowerPoint and CSV files are produced by Gravity after you push. A file input is read as text here.
|
|
300
|
+
- Same requirements as `gravity run`: a gateway token from `gravity login`, or `GRAVITY_DEV_TOKEN`.
|
|
294
301
|
|
|
295
302
|
### `gravity schedule`
|
|
296
303
|
|
|
@@ -755,6 +762,8 @@ Real tools act on a connected account. Connect one with `gravity-gateway connect
|
|
|
755
762
|
|
|
756
763
|
## What's new
|
|
757
764
|
|
|
765
|
+
- **0.5.3** `gravity dev` opens a local dashboard for your agent: a form from your manifest, a live timeline, approvals, the result and run history; runs from `gravity run` in the same folder show up in it live, and the other way round. WhatsApp joins the catalog (51 tools; 1,374 across 12 apps). Agents can return generated images: put them in a corpus section's `images` and they land in the Word, PowerPoint and Markdown outputs. The agent standard now shows how to generate images and embeddings through the gateway. `NEEDS_RECONNECT` on the shared test account tells you to ask Gravity, not to connect your own account.
|
|
766
|
+
- **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.
|
|
758
767
|
- **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.
|
|
759
768
|
- **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`.
|
|
760
769
|
- **0.4.3** Documents the `secret` input type and the `supadata.get_transcript` tool.
|
|
@@ -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,6 +22,7 @@
|
|
|
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
|
|
@@ -29,6 +31,7 @@
|
|
|
29
31
|
|
|
30
32
|
- name: web.search
|
|
31
33
|
slug: COMPOSIO_SEARCH_WEB
|
|
34
|
+
toolkit: composio_search
|
|
32
35
|
description: search the web; returns results with titles, links and snippets. No account needed
|
|
33
36
|
scopes: []
|
|
34
37
|
breadth: generic
|
|
@@ -37,6 +40,7 @@
|
|
|
37
40
|
|
|
38
41
|
- name: web.fetch
|
|
39
42
|
slug: COMPOSIO_SEARCH_FETCH_URL_CONTENT
|
|
43
|
+
toolkit: composio_search
|
|
40
44
|
description: read the text content of a web page by its URL. No account needed
|
|
41
45
|
scopes: []
|
|
42
46
|
breadth: generic
|
|
@@ -45,6 +49,7 @@
|
|
|
45
49
|
|
|
46
50
|
- name: supadata.get_transcript
|
|
47
51
|
slug: null
|
|
52
|
+
toolkit: null
|
|
48
53
|
description: fetch the transcript of a YouTube video with the caller's own Supadata API key (api_key argument)
|
|
49
54
|
scopes: []
|
|
50
55
|
breadth: niche
|
|
@@ -53,6 +58,7 @@
|
|
|
53
58
|
|
|
54
59
|
- name: gmail.read
|
|
55
60
|
slug: GMAIL_FETCH_EMAILS
|
|
61
|
+
toolkit: gmail
|
|
56
62
|
description: read the connected Gmail inbox
|
|
57
63
|
scopes: [gmail.readonly]
|
|
58
64
|
breadth: generic
|
|
@@ -61,6 +67,7 @@
|
|
|
61
67
|
|
|
62
68
|
- name: gmail.draft
|
|
63
69
|
slug: GMAIL_CREATE_EMAIL_DRAFT
|
|
70
|
+
toolkit: gmail
|
|
64
71
|
description: create a Gmail draft
|
|
65
72
|
scopes: [gmail.compose]
|
|
66
73
|
breadth: generic
|
|
@@ -69,6 +76,7 @@
|
|
|
69
76
|
|
|
70
77
|
- name: gmail.send
|
|
71
78
|
slug: GMAIL_SEND_EMAIL
|
|
79
|
+
toolkit: gmail
|
|
72
80
|
description: send an email from the connected Gmail
|
|
73
81
|
scopes: [gmail.send]
|
|
74
82
|
breadth: generic
|
|
@@ -77,6 +85,7 @@
|
|
|
77
85
|
|
|
78
86
|
- name: sheets.read
|
|
79
87
|
slug: GOOGLESHEETS_BATCH_GET
|
|
88
|
+
toolkit: googlesheets
|
|
80
89
|
description: read values from a connected Google Sheet
|
|
81
90
|
scopes: [sheets]
|
|
82
91
|
breadth: generic
|
|
@@ -85,6 +94,7 @@
|
|
|
85
94
|
|
|
86
95
|
- name: sheets.append
|
|
87
96
|
slug: GOOGLESHEETS_BATCH_UPDATE
|
|
97
|
+
toolkit: googlesheets
|
|
88
98
|
description: append a row to a connected Google Sheet
|
|
89
99
|
scopes: [sheets]
|
|
90
100
|
breadth: generic
|
|
@@ -93,6 +103,7 @@
|
|
|
93
103
|
|
|
94
104
|
- name: slack.send
|
|
95
105
|
slug: SLACK_SEND_MESSAGE
|
|
106
|
+
toolkit: slack
|
|
96
107
|
description: post a message to a connected Slack channel
|
|
97
108
|
scopes: [slack.chat:write]
|
|
98
109
|
breadth: generic
|
|
@@ -101,6 +112,7 @@
|
|
|
101
112
|
|
|
102
113
|
- name: slack.list_channels
|
|
103
114
|
slug: SLACK_LIST_ALL_CHANNELS
|
|
115
|
+
toolkit: slack
|
|
104
116
|
description: list channels in the connected Slack workspace
|
|
105
117
|
scopes: [slack.channels:read]
|
|
106
118
|
breadth: generic
|
|
@@ -109,6 +121,7 @@
|
|
|
109
121
|
|
|
110
122
|
- name: slack.read_history
|
|
111
123
|
slug: SLACK_FETCH_CONVERSATION_HISTORY
|
|
124
|
+
toolkit: slack
|
|
112
125
|
description: read message history of a Slack channel
|
|
113
126
|
scopes: [slack.channels:history]
|
|
114
127
|
breadth: generic
|
|
@@ -117,6 +130,7 @@
|
|
|
117
130
|
|
|
118
131
|
- name: slack.find_user
|
|
119
132
|
slug: SLACK_FIND_USERS
|
|
133
|
+
toolkit: slack
|
|
120
134
|
description: find a Slack user by name, handle or email, for mentions
|
|
121
135
|
scopes: [slack.users:read]
|
|
122
136
|
breadth: generic
|
|
@@ -125,6 +139,7 @@
|
|
|
125
139
|
|
|
126
140
|
- name: calendar.read
|
|
127
141
|
slug: GOOGLECALENDAR_EVENTS_LIST
|
|
142
|
+
toolkit: googlecalendar
|
|
128
143
|
description: list events on the connected Google Calendar
|
|
129
144
|
scopes: [calendar.readonly]
|
|
130
145
|
breadth: generic
|
|
@@ -133,6 +148,7 @@
|
|
|
133
148
|
|
|
134
149
|
- name: drive.read
|
|
135
150
|
slug: GOOGLEDRIVE_DOWNLOAD_FILE
|
|
151
|
+
toolkit: googledrive
|
|
136
152
|
description: download a file from the connected Google Drive
|
|
137
153
|
scopes: [drive.readonly]
|
|
138
154
|
breadth: generic
|
|
@@ -141,6 +157,7 @@
|
|
|
141
157
|
|
|
142
158
|
- name: linear.create_issue
|
|
143
159
|
slug: LINEAR_CREATE_LINEAR_ISSUE
|
|
160
|
+
toolkit: linear
|
|
144
161
|
description: create an issue in a connected Linear team
|
|
145
162
|
scopes: [linear.write]
|
|
146
163
|
breadth: generic
|
|
@@ -149,6 +166,7 @@
|
|
|
149
166
|
|
|
150
167
|
- name: linear.list_teams
|
|
151
168
|
slug: LINEAR_LIST_LINEAR_TEAMS
|
|
169
|
+
toolkit: linear
|
|
152
170
|
description: list the connected Linear workspace teams
|
|
153
171
|
scopes: [linear.read]
|
|
154
172
|
breadth: generic
|
|
@@ -157,6 +175,7 @@
|
|
|
157
175
|
|
|
158
176
|
- name: linear.list_issues
|
|
159
177
|
slug: LINEAR_LIST_LINEAR_ISSUES
|
|
178
|
+
toolkit: linear
|
|
160
179
|
description: list open issues in the connected Linear workspace
|
|
161
180
|
scopes: [linear.read]
|
|
162
181
|
breadth: generic
|
|
@@ -165,6 +184,7 @@
|
|
|
165
184
|
|
|
166
185
|
- name: linear.get_issue
|
|
167
186
|
slug: LINEAR_GET_LINEAR_ISSUE
|
|
187
|
+
toolkit: linear
|
|
168
188
|
description: read one Linear issue by id
|
|
169
189
|
scopes: [linear.read]
|
|
170
190
|
breadth: generic
|
|
@@ -174,6 +194,7 @@
|
|
|
174
194
|
# GitHub: read-only, for change summaries and contribution profiles.
|
|
175
195
|
- name: github.list_commits
|
|
176
196
|
slug: GITHUB_LIST_COMMITS
|
|
197
|
+
toolkit: github
|
|
177
198
|
description: list commits on a repository branch, filterable by date range, author or path
|
|
178
199
|
scopes: [repo]
|
|
179
200
|
breadth: generic
|
|
@@ -182,6 +203,7 @@
|
|
|
182
203
|
|
|
183
204
|
- name: github.get_commit
|
|
184
205
|
slug: GITHUB_GET_A_COMMIT
|
|
206
|
+
toolkit: github
|
|
185
207
|
description: read one commit with its changed files, line counts and patches
|
|
186
208
|
scopes: [repo]
|
|
187
209
|
breadth: generic
|
|
@@ -190,6 +212,7 @@
|
|
|
190
212
|
|
|
191
213
|
- name: github.compare
|
|
192
214
|
slug: GITHUB_COMPARE_TWO_COMMITS
|
|
215
|
+
toolkit: github
|
|
193
216
|
description: compare two refs (base...head) with the commits and changed files between them
|
|
194
217
|
scopes: [repo]
|
|
195
218
|
breadth: generic
|
|
@@ -198,6 +221,7 @@
|
|
|
198
221
|
|
|
199
222
|
- name: github.list_pull_requests
|
|
200
223
|
slug: GITHUB_LIST_PULL_REQUESTS
|
|
224
|
+
toolkit: github
|
|
201
225
|
description: list pull requests on a repository, filterable by state and branch
|
|
202
226
|
scopes: [repo]
|
|
203
227
|
breadth: generic
|
|
@@ -206,6 +230,7 @@
|
|
|
206
230
|
|
|
207
231
|
- name: github.list_pr_files
|
|
208
232
|
slug: GITHUB_LIST_PULL_REQUESTS_FILES
|
|
233
|
+
toolkit: github
|
|
209
234
|
description: list the files a pull request changes, with line counts and patches
|
|
210
235
|
scopes: [repo]
|
|
211
236
|
breadth: generic
|
|
@@ -214,6 +239,7 @@
|
|
|
214
239
|
|
|
215
240
|
- name: github.list_pr_reviews
|
|
216
241
|
slug: GITHUB_LIST_REVIEWS_FOR_A_PULL_REQUEST
|
|
242
|
+
toolkit: github
|
|
217
243
|
description: list the reviews submitted on a pull request
|
|
218
244
|
scopes: [repo]
|
|
219
245
|
breadth: generic
|
|
@@ -222,6 +248,7 @@
|
|
|
222
248
|
|
|
223
249
|
- name: github.search_issues
|
|
224
250
|
slug: GITHUB_SEARCH_ISSUES_AND_PULL_REQUESTS
|
|
251
|
+
toolkit: github
|
|
225
252
|
description: search issues and pull requests with GitHub qualifiers (repo:, author:, reviewed-by:, is:pr, created:)
|
|
226
253
|
scopes: [repo]
|
|
227
254
|
breadth: generic
|
|
@@ -230,6 +257,7 @@
|
|
|
230
257
|
|
|
231
258
|
- name: linkedin.whoami
|
|
232
259
|
slug: LINKEDIN_GET_MY_INFO
|
|
260
|
+
toolkit: linkedin
|
|
233
261
|
description: read the connected LinkedIn member's id, name and headline (the author of their posts)
|
|
234
262
|
scopes: [linkedin.profile]
|
|
235
263
|
breadth: generic
|
|
@@ -238,6 +266,7 @@
|
|
|
238
266
|
|
|
239
267
|
- name: linkedin.create_post
|
|
240
268
|
slug: LINKEDIN_CREATE_LINKED_IN_POST
|
|
269
|
+
toolkit: linkedin
|
|
241
270
|
description: publish a post on LinkedIn as the connected member or an organisation they manage
|
|
242
271
|
scopes: [linkedin.w_member_social]
|
|
243
272
|
breadth: generic
|