dooers-cli 0.7.2__tar.gz → 0.7.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.
- {dooers_cli-0.7.2 → dooers_cli-0.7.3}/PKG-INFO +1 -1
- {dooers_cli-0.7.2 → dooers_cli-0.7.3}/pyproject.toml +1 -1
- {dooers_cli-0.7.2 → dooers_cli-0.7.3}/src/dooers/cli/agent_store.py +11 -8
- {dooers_cli-0.7.2 → dooers_cli-0.7.3}/src/dooers/cli/cli.py +14 -1
- {dooers_cli-0.7.2 → dooers_cli-0.7.3}/src/dooers/cli/core_client.py +4 -1
- {dooers_cli-0.7.2 → dooers_cli-0.7.3}/tests/test_agent_store.py +21 -0
- {dooers_cli-0.7.2 → dooers_cli-0.7.3}/uv.lock +1 -1
- {dooers_cli-0.7.2 → dooers_cli-0.7.3}/.gitignore +0 -0
- {dooers_cli-0.7.2 → dooers_cli-0.7.3}/README.md +0 -0
- {dooers_cli-0.7.2 → dooers_cli-0.7.3}/src/dooers/cli/__init__.py +0 -0
- {dooers_cli-0.7.2 → dooers_cli-0.7.3}/src/dooers/cli/agents.py +0 -0
- {dooers_cli-0.7.2 → dooers_cli-0.7.3}/src/dooers/cli/auth.py +0 -0
- {dooers_cli-0.7.2 → dooers_cli-0.7.3}/src/dooers/cli/config.py +0 -0
- {dooers_cli-0.7.2 → dooers_cli-0.7.3}/src/dooers/cli/env_prod.py +0 -0
- {dooers_cli-0.7.2 → dooers_cli-0.7.3}/src/dooers/cli/ignore.py +0 -0
- {dooers_cli-0.7.2 → dooers_cli-0.7.3}/src/dooers/cli/manifest_sync.py +0 -0
- {dooers_cli-0.7.2 → dooers_cli-0.7.3}/src/dooers/cli/org.py +0 -0
- {dooers_cli-0.7.2 → dooers_cli-0.7.3}/src/dooers/cli/push.py +0 -0
- {dooers_cli-0.7.2 → dooers_cli-0.7.3}/src/dooers/cli/push_client.py +0 -0
- {dooers_cli-0.7.2 → dooers_cli-0.7.3}/src/dooers/cli/settings.py +0 -0
- {dooers_cli-0.7.2 → dooers_cli-0.7.3}/src/dooers/cli/token_store.py +0 -0
- {dooers_cli-0.7.2 → dooers_cli-0.7.3}/src/dooers/cli/user_config.py +0 -0
- {dooers_cli-0.7.2 → dooers_cli-0.7.3}/src/dooers/cli/validate.py +0 -0
- {dooers_cli-0.7.2 → dooers_cli-0.7.3}/src/dooers/py.typed +0 -0
- {dooers_cli-0.7.2 → dooers_cli-0.7.3}/tests/__init__.py +0 -0
- {dooers_cli-0.7.2 → dooers_cli-0.7.3}/tests/test_agents_delete.py +0 -0
- {dooers_cli-0.7.2 → dooers_cli-0.7.3}/tests/test_config.py +0 -0
- {dooers_cli-0.7.2 → dooers_cli-0.7.3}/tests/test_core_client.py +0 -0
- {dooers_cli-0.7.2 → dooers_cli-0.7.3}/tests/test_env_prod.py +0 -0
- {dooers_cli-0.7.2 → dooers_cli-0.7.3}/tests/test_ignore.py +0 -0
- {dooers_cli-0.7.2 → dooers_cli-0.7.3}/tests/test_manifest_sync.py +0 -0
- {dooers_cli-0.7.2 → dooers_cli-0.7.3}/tests/test_org.py +0 -0
- {dooers_cli-0.7.2 → dooers_cli-0.7.3}/tests/test_push_client_async.py +0 -0
- {dooers_cli-0.7.2 → dooers_cli-0.7.3}/tests/test_push_failure_display.py +0 -0
- {dooers_cli-0.7.2 → dooers_cli-0.7.3}/tests/test_push_hosting_guard.py +0 -0
- {dooers_cli-0.7.2 → dooers_cli-0.7.3}/tests/test_push_poll.py +0 -0
- {dooers_cli-0.7.2 → dooers_cli-0.7.3}/tests/test_push_teardown.py +0 -0
- {dooers_cli-0.7.2 → dooers_cli-0.7.3}/tests/test_smoke.py +0 -0
- {dooers_cli-0.7.2 → dooers_cli-0.7.3}/tests/test_token_store.py +0 -0
- {dooers_cli-0.7.2 → dooers_cli-0.7.3}/tests/test_validate.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: dooers-cli
|
|
3
|
-
Version: 0.7.
|
|
3
|
+
Version: 0.7.3
|
|
4
4
|
Summary: Dooers CLI — push agents to Dooers, manage agent records, authenticate from the terminal
|
|
5
5
|
Project-URL: Repository, https://github.com/Dooers-ai/dooers-cli
|
|
6
6
|
Author-email: Dooers <dev@dooers.ai>
|
|
@@ -54,6 +54,15 @@ class HTTPCoreAgentStore:
|
|
|
54
54
|
def _h(self) -> dict[str, str]:
|
|
55
55
|
return {"Authorization": f"Bearer {self.token}"}
|
|
56
56
|
|
|
57
|
+
def _post_json(self, url: str, body: dict | None = None) -> httpx.Response:
|
|
58
|
+
"""POST with application/json — required to bypass core CSRF for Bearer-only clients."""
|
|
59
|
+
return httpx.post(
|
|
60
|
+
url,
|
|
61
|
+
headers={**self._h(), "Content-Type": "application/json"},
|
|
62
|
+
json=body if body is not None else {},
|
|
63
|
+
timeout=self._timeout,
|
|
64
|
+
)
|
|
65
|
+
|
|
57
66
|
def create(self, req: CreateAgentRequest) -> AgentRecord:
|
|
58
67
|
body = json.dumps({"organizationId": req.organization_id, "name": req.name})
|
|
59
68
|
r = httpx.post(
|
|
@@ -82,9 +91,7 @@ class HTTPCoreAgentStore:
|
|
|
82
91
|
return _record(_data(r))
|
|
83
92
|
|
|
84
93
|
def archive(self, agent_id: str) -> None:
|
|
85
|
-
r =
|
|
86
|
-
f"{self.api}/agents/{agent_id}/archive", headers=self._h(), timeout=self._timeout
|
|
87
|
-
)
|
|
94
|
+
r = self._post_json(f"{self.api}/agents/{agent_id}/archive")
|
|
88
95
|
_data(r) # raises AgentStoreError on {success: false}; no record to parse
|
|
89
96
|
|
|
90
97
|
def delete(self, agent_id: str) -> None:
|
|
@@ -94,9 +101,5 @@ class HTTPCoreAgentStore:
|
|
|
94
101
|
_data(r) # success body is {success, message} with no data key — do NOT call _record()
|
|
95
102
|
|
|
96
103
|
def regenerate_runtime_api_key(self, agent_id: str) -> AgentRecord:
|
|
97
|
-
r =
|
|
98
|
-
f"{self.api}/agents/{agent_id}/runtime-api-key/regenerate",
|
|
99
|
-
headers=self._h(),
|
|
100
|
-
timeout=self._timeout,
|
|
101
|
-
)
|
|
104
|
+
r = self._post_json(f"{self.api}/agents/{agent_id}/runtime-api-key/regenerate")
|
|
102
105
|
return _record(_data(r))
|
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
"""Top-level Typer app — login/whoami/logout + agents group + push."""
|
|
2
2
|
|
|
3
|
+
import warnings
|
|
4
|
+
|
|
5
|
+
# Global `dooers-agents-server` installs `dooers.protocol.frames` with a legacy
|
|
6
|
+
# `schema` field name that Pydantic warns on at import. The CLI does not use WS
|
|
7
|
+
# frames; suppress so every command stays quiet when both packages share `dooers.*`.
|
|
8
|
+
warnings.filterwarnings(
|
|
9
|
+
"ignore",
|
|
10
|
+
message=r'Field name "schema" in "SettingsPublicSchemaResultPayload" shadows an attribute in parent "BaseModel"',
|
|
11
|
+
category=UserWarning,
|
|
12
|
+
)
|
|
13
|
+
|
|
3
14
|
import typer
|
|
4
15
|
|
|
5
16
|
from . import agents, auth, org, push, validate
|
|
@@ -28,7 +39,9 @@ app.command(name="login", help="Authenticate with Dooers (OTP via email).")(auth
|
|
|
28
39
|
app.command(name="whoami", help="Show the currently authenticated user.")(auth.whoami)
|
|
29
40
|
app.command(name="logout", help="Clear local credentials.")(auth.logout)
|
|
30
41
|
app.add_typer(
|
|
31
|
-
agents.app,
|
|
42
|
+
agents.app,
|
|
43
|
+
name="agents",
|
|
44
|
+
help="Manage agents — subcommands: list | create | rotate-key | show | delete.",
|
|
32
45
|
)
|
|
33
46
|
app.add_typer(org.app, name="org", help="Manage organization — subcommands: list | use.")
|
|
34
47
|
app.command(
|
|
@@ -89,7 +89,10 @@ class CoreClient:
|
|
|
89
89
|
def revoke(self) -> None:
|
|
90
90
|
try:
|
|
91
91
|
httpx.post(
|
|
92
|
-
f"{self.api}/identity/revoke",
|
|
92
|
+
f"{self.api}/identity/revoke",
|
|
93
|
+
headers={**self._headers(), "Content-Type": "application/json"},
|
|
94
|
+
json={},
|
|
95
|
+
timeout=self._timeout,
|
|
93
96
|
)
|
|
94
97
|
except httpx.HTTPError:
|
|
95
98
|
pass # best-effort
|
|
@@ -75,6 +75,27 @@ def test_archive_posts_to_archive_route():
|
|
|
75
75
|
)
|
|
76
76
|
HTTPCoreAgentStore(BASE, "tok").archive(A) # should not raise
|
|
77
77
|
assert route.called
|
|
78
|
+
req = route.calls.last.request
|
|
79
|
+
assert req.headers["content-type"] == "application/json"
|
|
80
|
+
assert req.read() == b"{}"
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
@respx.mock
|
|
84
|
+
def test_regenerate_runtime_api_key_posts_json_body():
|
|
85
|
+
data = {
|
|
86
|
+
"agentId": A,
|
|
87
|
+
"name": "x",
|
|
88
|
+
"organizationId": "o1",
|
|
89
|
+
"runtimeApiKey": "new-secret-key",
|
|
90
|
+
}
|
|
91
|
+
route = respx.post(f"{BASE}/api/v2/agents/{A}/runtime-api-key/regenerate").mock(
|
|
92
|
+
return_value=httpx.Response(200, json={"success": True, "data": data})
|
|
93
|
+
)
|
|
94
|
+
rec = HTTPCoreAgentStore(BASE, "tok").regenerate_runtime_api_key(A)
|
|
95
|
+
assert rec.runtime_api_key == "new-secret-key"
|
|
96
|
+
req = route.calls.last.request
|
|
97
|
+
assert req.headers["content-type"] == "application/json"
|
|
98
|
+
assert req.read() == b"{}"
|
|
78
99
|
|
|
79
100
|
|
|
80
101
|
@respx.mock
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|