gravity-cli 0.3.0__tar.gz → 0.3.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (96) hide show
  1. {gravity_cli-0.3.0 → gravity_cli-0.3.1}/PKG-INFO +2 -2
  2. {gravity_cli-0.3.0 → gravity_cli-0.3.1}/packages/gravity-schema/pyproject.toml +1 -1
  3. {gravity_cli-0.3.0 → gravity_cli-0.3.1}/pyproject.toml +2 -2
  4. {gravity_cli-0.3.0 → gravity_cli-0.3.1}/src/gravity_cli/api.py +33 -3
  5. {gravity_cli-0.3.0 → gravity_cli-0.3.1}/src/gravity_cli/commands/validate.py +4 -2
  6. {gravity_cli-0.3.0 → gravity_cli-0.3.1}/src/gravity_cli/ui.py +1 -1
  7. {gravity_cli-0.3.0 → gravity_cli-0.3.1}/tests/test_api.py +35 -0
  8. {gravity_cli-0.3.0 → gravity_cli-0.3.1}/uv.lock +2 -2
  9. {gravity_cli-0.3.0 → gravity_cli-0.3.1}/.env.example +0 -0
  10. {gravity_cli-0.3.0 → gravity_cli-0.3.1}/.github/workflows/ci.yml +0 -0
  11. {gravity_cli-0.3.0 → gravity_cli-0.3.1}/.github/workflows/release.yml +0 -0
  12. {gravity_cli-0.3.0 → gravity_cli-0.3.1}/.gitignore +0 -0
  13. {gravity_cli-0.3.0 → gravity_cli-0.3.1}/AGENT_STANDARD.md +0 -0
  14. {gravity_cli-0.3.0 → gravity_cli-0.3.1}/LICENSE +0 -0
  15. {gravity_cli-0.3.0 → gravity_cli-0.3.1}/NOTICE +0 -0
  16. {gravity_cli-0.3.0 → gravity_cli-0.3.1}/README.md +0 -0
  17. {gravity_cli-0.3.0 → gravity_cli-0.3.1}/TEMPLATES.md +0 -0
  18. {gravity_cli-0.3.0 → gravity_cli-0.3.1}/packages/gravity-schema/LICENSE +0 -0
  19. {gravity_cli-0.3.0 → gravity_cli-0.3.1}/packages/gravity-schema/NOTICE +0 -0
  20. {gravity_cli-0.3.0 → gravity_cli-0.3.1}/packages/gravity-schema/README.md +0 -0
  21. {gravity_cli-0.3.0 → gravity_cli-0.3.1}/packages/gravity-schema/gravity_schema/__init__.py +0 -0
  22. {gravity_cli-0.3.0 → gravity_cli-0.3.1}/packages/gravity-schema/gravity_schema/__main__.py +0 -0
  23. {gravity_cli-0.3.0 → gravity_cli-0.3.1}/packages/gravity-schema/gravity_schema/catalog.py +0 -0
  24. {gravity_cli-0.3.0 → gravity_cli-0.3.1}/packages/gravity-schema/gravity_schema/catalog.yaml +0 -0
  25. {gravity_cli-0.3.0 → gravity_cli-0.3.1}/packages/gravity-schema/gravity_schema/catalog_imported.yaml +0 -0
  26. {gravity_cli-0.3.0 → gravity_cli-0.3.1}/packages/gravity-schema/gravity_schema/contract.py +0 -0
  27. {gravity_cli-0.3.0 → gravity_cli-0.3.1}/packages/gravity-schema/gravity_schema/cron.py +0 -0
  28. {gravity_cli-0.3.0 → gravity_cli-0.3.1}/packages/gravity-schema/gravity_schema/errors.py +0 -0
  29. {gravity_cli-0.3.0 → gravity_cli-0.3.1}/packages/gravity-schema/gravity_schema/export.py +0 -0
  30. {gravity_cli-0.3.0 → gravity_cli-0.3.1}/packages/gravity-schema/gravity_schema/gen_catalog.py +0 -0
  31. {gravity_cli-0.3.0 → gravity_cli-0.3.1}/packages/gravity-schema/gravity_schema/manifest.py +0 -0
  32. {gravity_cli-0.3.0 → gravity_cli-0.3.1}/packages/gravity-schema/gravity_schema/validation.py +0 -0
  33. {gravity_cli-0.3.0 → gravity_cli-0.3.1}/packages/gravity-schema/schema/manifest.schema.json +0 -0
  34. {gravity_cli-0.3.0 → gravity_cli-0.3.1}/packages/gravity-schema/tests/conftest.py +0 -0
  35. {gravity_cli-0.3.0 → gravity_cli-0.3.1}/packages/gravity-schema/tests/test_catalog_parity.py +0 -0
  36. {gravity_cli-0.3.0 → gravity_cli-0.3.1}/packages/gravity-schema/tests/test_cron.py +0 -0
  37. {gravity_cli-0.3.0 → gravity_cli-0.3.1}/packages/gravity-schema/tests/test_errors.py +0 -0
  38. {gravity_cli-0.3.0 → gravity_cli-0.3.1}/packages/gravity-schema/tests/test_json_schema.py +0 -0
  39. {gravity_cli-0.3.0 → gravity_cli-0.3.1}/packages/gravity-schema/tests/test_kind_outputs.py +0 -0
  40. {gravity_cli-0.3.0 → gravity_cli-0.3.1}/packages/gravity-schema/tests/test_manifest.py +0 -0
  41. {gravity_cli-0.3.0 → gravity_cli-0.3.1}/packages/gravity-schema/tests/test_slots.py +0 -0
  42. {gravity_cli-0.3.0 → gravity_cli-0.3.1}/packages/gravity-schema/tests/test_validation.py +0 -0
  43. {gravity_cli-0.3.0 → gravity_cli-0.3.1}/src/gravity_cli/BUILDER_GUIDE.md +0 -0
  44. {gravity_cli-0.3.0 → gravity_cli-0.3.1}/src/gravity_cli/__init__.py +0 -0
  45. {gravity_cli-0.3.0 → gravity_cli-0.3.1}/src/gravity_cli/__main__.py +0 -0
  46. {gravity_cli-0.3.0 → gravity_cli-0.3.1}/src/gravity_cli/_runner_script.py +0 -0
  47. {gravity_cli-0.3.0 → gravity_cli-0.3.1}/src/gravity_cli/cli.py +0 -0
  48. {gravity_cli-0.3.0 → gravity_cli-0.3.1}/src/gravity_cli/commands/__init__.py +0 -0
  49. {gravity_cli-0.3.0 → gravity_cli-0.3.1}/src/gravity_cli/commands/adapt.py +0 -0
  50. {gravity_cli-0.3.0 → gravity_cli-0.3.1}/src/gravity_cli/commands/build.py +0 -0
  51. {gravity_cli-0.3.0 → gravity_cli-0.3.1}/src/gravity_cli/commands/guide.py +0 -0
  52. {gravity_cli-0.3.0 → gravity_cli-0.3.1}/src/gravity_cli/commands/init.py +0 -0
  53. {gravity_cli-0.3.0 → gravity_cli-0.3.1}/src/gravity_cli/commands/login.py +0 -0
  54. {gravity_cli-0.3.0 → gravity_cli-0.3.1}/src/gravity_cli/commands/push.py +0 -0
  55. {gravity_cli-0.3.0 → gravity_cli-0.3.1}/src/gravity_cli/commands/run.py +0 -0
  56. {gravity_cli-0.3.0 → gravity_cli-0.3.1}/src/gravity_cli/commands/runs.py +0 -0
  57. {gravity_cli-0.3.0 → gravity_cli-0.3.1}/src/gravity_cli/commands/schedule.py +0 -0
  58. {gravity_cli-0.3.0 → gravity_cli-0.3.1}/src/gravity_cli/commands/tools.py +0 -0
  59. {gravity_cli-0.3.0 → gravity_cli-0.3.1}/src/gravity_cli/config.py +0 -0
  60. {gravity_cli-0.3.0 → gravity_cli-0.3.1}/src/gravity_cli/manifest.py +0 -0
  61. {gravity_cli-0.3.0 → gravity_cli-0.3.1}/src/gravity_cli/project.py +0 -0
  62. {gravity_cli-0.3.0 → gravity_cli-0.3.1}/src/gravity_cli/templates/chat/.gitignore +0 -0
  63. {gravity_cli-0.3.0 → gravity_cli-0.3.1}/src/gravity_cli/templates/chat/manifest.yaml +0 -0
  64. {gravity_cli-0.3.0 → gravity_cli-0.3.1}/src/gravity_cli/templates/chat/requirements.txt +0 -0
  65. {gravity_cli-0.3.0 → gravity_cli-0.3.1}/src/gravity_cli/templates/chat/src/agent.py +0 -0
  66. {gravity_cli-0.3.0 → gravity_cli-0.3.1}/src/gravity_cli/templates/deepagent/.gitignore +0 -0
  67. {gravity_cli-0.3.0 → gravity_cli-0.3.1}/src/gravity_cli/templates/deepagent/manifest.yaml +0 -0
  68. {gravity_cli-0.3.0 → gravity_cli-0.3.1}/src/gravity_cli/templates/deepagent/requirements.txt +0 -0
  69. {gravity_cli-0.3.0 → gravity_cli-0.3.1}/src/gravity_cli/templates/deepagent/src/agent.py +0 -0
  70. {gravity_cli-0.3.0 → gravity_cli-0.3.1}/src/gravity_cli/templates/minimal/.gitignore +0 -0
  71. {gravity_cli-0.3.0 → gravity_cli-0.3.1}/src/gravity_cli/templates/minimal/manifest.yaml +0 -0
  72. {gravity_cli-0.3.0 → gravity_cli-0.3.1}/src/gravity_cli/templates/minimal/requirements.txt +0 -0
  73. {gravity_cli-0.3.0 → gravity_cli-0.3.1}/src/gravity_cli/templates/minimal/src/agent.py +0 -0
  74. {gravity_cli-0.3.0 → gravity_cli-0.3.1}/src/gravity_cli/templates/structured/.gitignore +0 -0
  75. {gravity_cli-0.3.0 → gravity_cli-0.3.1}/src/gravity_cli/templates/structured/manifest.yaml +0 -0
  76. {gravity_cli-0.3.0 → gravity_cli-0.3.1}/src/gravity_cli/templates/structured/requirements.txt +0 -0
  77. {gravity_cli-0.3.0 → gravity_cli-0.3.1}/src/gravity_cli/templates/structured/src/agent.py +0 -0
  78. {gravity_cli-0.3.0 → gravity_cli-0.3.1}/src/gravity_cli/templates/structured/src/gateway.py +0 -0
  79. {gravity_cli-0.3.0 → gravity_cli-0.3.1}/src/gravity_cli/templates/structured/src/nodes/agent.py +0 -0
  80. {gravity_cli-0.3.0 → gravity_cli-0.3.1}/src/gravity_cli/templates/structured/src/prompts.py +0 -0
  81. {gravity_cli-0.3.0 → gravity_cli-0.3.1}/src/gravity_cli/templates/structured/src/state.py +0 -0
  82. {gravity_cli-0.3.0 → gravity_cli-0.3.1}/src/gravity_cli/timeline.py +0 -0
  83. {gravity_cli-0.3.0 → gravity_cli-0.3.1}/starter/manifest.yaml +0 -0
  84. {gravity_cli-0.3.0 → gravity_cli-0.3.1}/starter/requirements.txt +0 -0
  85. {gravity_cli-0.3.0 → gravity_cli-0.3.1}/starter/src/agent.py +0 -0
  86. {gravity_cli-0.3.0 → gravity_cli-0.3.1}/starter/src/summary.py +0 -0
  87. {gravity_cli-0.3.0 → gravity_cli-0.3.1}/starter/tests/test_summary.py +0 -0
  88. {gravity_cli-0.3.0 → gravity_cli-0.3.1}/tests/conftest.py +0 -0
  89. {gravity_cli-0.3.0 → gravity_cli-0.3.1}/tests/test_adapt.py +0 -0
  90. {gravity_cli-0.3.0 → gravity_cli-0.3.1}/tests/test_build_push.py +0 -0
  91. {gravity_cli-0.3.0 → gravity_cli-0.3.1}/tests/test_manifest_integration.py +0 -0
  92. {gravity_cli-0.3.0 → gravity_cli-0.3.1}/tests/test_run.py +0 -0
  93. {gravity_cli-0.3.0 → gravity_cli-0.3.1}/tests/test_templates.py +0 -0
  94. {gravity_cli-0.3.0 → gravity_cli-0.3.1}/tests/test_timeline.py +0 -0
  95. {gravity_cli-0.3.0 → gravity_cli-0.3.1}/tests/test_ui.py +0 -0
  96. {gravity_cli-0.3.0 → gravity_cli-0.3.1}/tests/test_welcome.py +0 -0
@@ -1,12 +1,12 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: gravity-cli
3
- Version: 0.3.0
3
+ Version: 0.3.1
4
4
  Summary: gravity cli — init, run, and push python agents to the Gravity marketplace
5
5
  License-Expression: Apache-2.0
6
6
  License-File: LICENSE
7
7
  License-File: NOTICE
8
8
  Requires-Python: >=3.12
9
- Requires-Dist: gravity-schema==0.3.0
9
+ Requires-Dist: gravity-schema==0.3.1
10
10
  Requires-Dist: httpx>=0.27.0
11
11
  Requires-Dist: keyring>=25.0.0
12
12
  Requires-Dist: python-dotenv>=1.0.0
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "gravity-schema"
3
- version = "0.3.0"
3
+ version = "0.3.1"
4
4
  description = "Gravity agent manifest contract — models, validation, and JSON Schema export"
5
5
  readme = "README.md"
6
6
  license = "Apache-2.0"
@@ -1,13 +1,13 @@
1
1
  [project]
2
2
  name = "gravity-cli"
3
- version = "0.3.0"
3
+ version = "0.3.1"
4
4
  description = "gravity cli — init, run, and push python agents to the Gravity marketplace"
5
5
  readme = "README.md"
6
6
  license = "Apache-2.0"
7
7
  license-files = ["LICENSE", "NOTICE"]
8
8
  requires-python = ">=3.12"
9
9
  dependencies = [
10
- "gravity-schema==0.3.0", # released in lockstep: always the same version as this package
10
+ "gravity-schema==0.3.1", # released in lockstep: always the same version as this package
11
11
  "typer>=0.15.0",
12
12
  "httpx>=0.27.0",
13
13
  "rich>=13.9.0",
@@ -23,16 +23,22 @@ def _token() -> str:
23
23
  return token
24
24
 
25
25
 
26
- def _request(method: str, path: str, *, auth: bool = True, timeout: int = 60, headers: dict | None = None, **kw) -> Any:
26
+ def _request(method: str, path: str, *, auth: bool = True, timeout: int = 60, headers: dict | None = None, retry: bool = True, **kw) -> Any:
27
27
  """The JSON body. A refused connection or an error status becomes a Problem."""
28
- headers = {**({"Authorization": f"Bearer {_token()}"} if auth else {}), **(headers or {})}
28
+ extra = headers or {}
29
+ sent = {**({"Authorization": f"Bearer {_token()}"} if auth else {}), **extra}
29
30
  try:
30
- with httpx.Client(base_url=config.api_url(), timeout=timeout, headers=headers) as client:
31
+ with httpx.Client(base_url=config.api_url(), timeout=timeout, headers=sent) as client:
31
32
  resp = client.request(method, path, **kw)
32
33
  except httpx.TransportError as e:
33
34
  raise Problem(
34
35
  f"Can't reach Gravity at {config.api_url()}", str(e), "Check GRAVITY_API_URL in ~/.gravity/.env"
35
36
  ) from e
37
+ if resp.status_code == 401 and auth and not os.environ.get("GRAVITY_API_TOKEN"):
38
+ # A session's access token lasts an hour. Renew it once; if that can't be done, say so plainly.
39
+ if retry and _refresh():
40
+ return _request(method, path, auth=auth, timeout=timeout, headers=extra, retry=False, **kw)
41
+ raise Problem("Your sign-in has expired", fix="Run gravity login again.")
36
42
  if resp.status_code >= 400:
37
43
  try:
38
44
  body = resp.json()
@@ -47,6 +53,30 @@ def _request(method: str, path: str, *, auth: bool = True, timeout: int = 60, he
47
53
  return resp.json()
48
54
 
49
55
 
56
+ def _refresh() -> bool:
57
+ """A new session from the stored refresh token. Needs Gravity's config to name Supabase and its anon key."""
58
+ session = config.load_session()
59
+ settings = service_config()
60
+ url, key = settings.get("supabase_url"), settings.get("supabase_anon_key")
61
+ if not (session and url and key):
62
+ return False
63
+ try:
64
+ resp = httpx.post(
65
+ f"{url.rstrip('/')}/auth/v1/token",
66
+ params={"grant_type": "refresh_token"},
67
+ json={"refresh_token": session.refresh_token},
68
+ headers={"apikey": key},
69
+ timeout=15,
70
+ )
71
+ except httpx.TransportError:
72
+ return False
73
+ if resp.status_code != 200:
74
+ return False
75
+ data = resp.json()
76
+ config.save_session(config.Session(data["access_token"], data["refresh_token"]))
77
+ return True
78
+
79
+
50
80
  def service_config() -> dict:
51
81
  """Where Gravity's services are: api_url, gateway_url, min_cli_version. No sign-in needed."""
52
82
  return _request("GET", "/api/cli/config", auth=False, timeout=15)
@@ -8,7 +8,7 @@ import time
8
8
  from pathlib import Path
9
9
 
10
10
  import typer
11
- from gravity_schema import LOCKFILE_PLATFORM
11
+ from gravity_schema import DEV_TOOL_NAMES, LOCKFILE_PLATFORM
12
12
  from gravity_schema.contract import TOOL_NAME_RE as _TOOL_NAME_RE
13
13
 
14
14
  from .. import ui
@@ -111,7 +111,9 @@ def _scan_drift(project_dir: Path, declared_tools: list[str]) -> list[tuple[str,
111
111
  f"{', '.join(sorted(imported_network_libs))} imported, but permissions.tools is empty: hosted agents can only reach the gateway.",
112
112
  "Declare the tools you need under permissions.tools, or drop the import.",
113
113
  ))
114
- if undeclared := sorted(referenced_tool_names - set(declared_tools)):
114
+ # Only names under an app the catalog knows: "package.json" is a file, not a tool.
115
+ apps = {name.split(".")[0] for name in DEV_TOOL_NAMES}
116
+ if undeclared := sorted(n for n in referenced_tool_names - set(declared_tools) if n.split(".")[0] in apps):
115
117
  warnings.append((
116
118
  "Undeclared tool",
117
119
  f"{', '.join(undeclared)} appears in your code",
@@ -156,7 +156,7 @@ def table(headers: list[str], rows: list[list[str]], footer: str = "") -> None:
156
156
  if len(row) == 1:
157
157
  grid.add_row(Text(f"\n{row[0]}", style="bold"))
158
158
  else:
159
- grid.add_row(*(Text(str(cell)) for cell in row))
159
+ grid.add_row(*(Text("" if cell is None else str(cell)) for cell in row))
160
160
  if any(headers):
161
161
  say()
162
162
  say(Padding(grid, (0, 0, 0, 2)))
@@ -63,3 +63,38 @@ def test_whoami_works_with_only_the_env_token(control_plane, monkeypatch, capsys
63
63
  whoami_command()
64
64
  assert control_plane["auth"] == "Bearer cp_dev"
65
65
  assert "local-builder (dev token)" in capsys.readouterr().out
66
+
67
+
68
+ def test_an_expired_session_is_renewed_once_when_gravity_names_supabase(monkeypatch):
69
+ """A 401 with a stored session: refresh against Supabase, then retry with the new token."""
70
+ saved: list = []
71
+ monkeypatch.delenv("GRAVITY_API_TOKEN", raising=False)
72
+ monkeypatch.setattr(config, "load_session", lambda: saved[-1] if saved else SESSION)
73
+ monkeypatch.setattr(config, "save_session", saved.append)
74
+ monkeypatch.setattr(api, "service_config", lambda: {"supabase_url": "https://sb.example", "supabase_anon_key": "anon"})
75
+ calls: list = []
76
+
77
+ def handler(request: httpx.Request) -> httpx.Response:
78
+ calls.append((request.url.host, request.headers.get("Authorization") or request.headers.get("apikey")))
79
+ if request.url.host == "sb.example":
80
+ return httpx.Response(200, json={"access_token": "fresh", "refresh_token": "r2"})
81
+ if request.headers["Authorization"] == "Bearer fresh":
82
+ return httpx.Response(200, json=[{"name": "demo"}])
83
+ return httpx.Response(401, json={"message": "Unauthorized"})
84
+
85
+ real = httpx.Client
86
+ monkeypatch.setattr(api.httpx, "Client", lambda **kw: real(transport=httpx.MockTransport(handler), **kw))
87
+ monkeypatch.setattr(api.httpx, "post", lambda url, **kw: real(transport=httpx.MockTransport(handler)).post(url, **kw))
88
+ assert api.get("/api/cli/agents") == [{"name": "demo"}]
89
+ assert saved[-1].access_token == "fresh" and [c[0] for c in calls].count("sb.example") == 1
90
+
91
+
92
+ def test_an_expired_session_without_refresh_says_sign_in_again(monkeypatch):
93
+ monkeypatch.delenv("GRAVITY_API_TOKEN", raising=False)
94
+ monkeypatch.setattr(config, "load_session", lambda: SESSION)
95
+ monkeypatch.setattr(api, "service_config", dict) # no Supabase details yet
96
+ real = httpx.Client
97
+ monkeypatch.setattr(api.httpx, "Client", lambda **kw: real(transport=httpx.MockTransport(lambda r: httpx.Response(401)), **kw))
98
+ with pytest.raises(typer.Exit) as e:
99
+ api.get("/api/cli/agents")
100
+ assert "sign-in has expired" in str(e.value) and "gravity login" in str(e.value)
@@ -164,7 +164,7 @@ wheels = [
164
164
 
165
165
  [[package]]
166
166
  name = "gravity-cli"
167
- version = "0.3.0"
167
+ version = "0.3.1"
168
168
  source = { editable = "." }
169
169
  dependencies = [
170
170
  { name = "gravity-schema" },
@@ -201,7 +201,7 @@ dev = [
201
201
 
202
202
  [[package]]
203
203
  name = "gravity-schema"
204
- version = "0.3.0"
204
+ version = "0.3.1"
205
205
  source = { editable = "packages/gravity-schema" }
206
206
  dependencies = [
207
207
  { name = "pydantic" },
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes