mirror of
https://github.com/HKUDS/CLI-Anything.git
synced 2026-08-31 01:42:25 +08:00
fix: address all 12 Copilot review comments
- setup.py: add include_package_data + MANIFEST.in for .md files - setup.py: bump python_requires to >=3.10 (repl_skin.py uses PEP 604) - client.py: is_alive() now uses self.get() with auth; get() allows timeout override - wiremock_cli.py: consistent --json output (raw API JSON, no envelope) - wiremock_cli.py: add @file support in stub create - wiremock_cli.py: shutdown respects --json mode in error path - wiremock_cli.py: remove unused sys import and pass_client - stubs.py: use `is not None` instead of truthiness for limit/offset - requests_log.py: use `is not None` instead of truthiness for limit/since - scenarios.py: URL-encode scenario name in path - README.md: fix WIREMOCK.md path reference - SKILL.md: remove incorrect envelope documentation
This commit is contained in:
@@ -0,0 +1 @@
|
||||
recursive-include cli_anything *.md
|
||||
@@ -44,4 +44,4 @@ export WIREMOCK_PORT=8080
|
||||
export WIREMOCK_SCHEME=http
|
||||
```
|
||||
|
||||
See `WIREMOCK.md` at the repository root for the full SOP.
|
||||
See `../WIREMOCK.md` for the full SOP.
|
||||
|
||||
@@ -7,9 +7,9 @@ class RequestsLog:
|
||||
|
||||
def list(self, limit: int = None, since: str = None) -> dict:
|
||||
params = {}
|
||||
if limit:
|
||||
if limit is not None:
|
||||
params["limit"] = limit
|
||||
if since:
|
||||
if since is not None:
|
||||
params["since"] = since
|
||||
r = self.client.get("/requests", params=params)
|
||||
r.raise_for_status()
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
from urllib.parse import quote
|
||||
|
||||
from cli_anything.wiremock.utils.client import WireMockClient
|
||||
|
||||
|
||||
@@ -15,5 +17,5 @@ class ScenariosManager:
|
||||
r.raise_for_status()
|
||||
|
||||
def set_state(self, name: str, state: str) -> None:
|
||||
r = self.client.put(f"/scenarios/{name}/state", json={"state": state})
|
||||
r = self.client.put(f"/scenarios/{quote(name, safe='')}/state", json={"state": state})
|
||||
r.raise_for_status()
|
||||
|
||||
@@ -8,9 +8,9 @@ class StubsManager:
|
||||
|
||||
def list(self, limit: int = None, offset: int = None) -> dict:
|
||||
params = {}
|
||||
if limit:
|
||||
if limit is not None:
|
||||
params["limit"] = limit
|
||||
if offset:
|
||||
if offset is not None:
|
||||
params["offset"] = offset
|
||||
r = self.client.get("/mappings", params=params)
|
||||
r.raise_for_status()
|
||||
|
||||
@@ -85,7 +85,7 @@ cli-anything-wiremock stub create '{
|
||||
|
||||
# Verify a POST was made exactly once
|
||||
cli-anything-wiremock --json request count '{"method":"POST","url":"/api/orders"}'
|
||||
# → {"status":"ok","message":"...","data":{"count":1}}
|
||||
# → {"count": 1}
|
||||
|
||||
# Scenario: advance state
|
||||
cli-anything-wiremock scenario set "cart-flow" "item-added"
|
||||
@@ -100,11 +100,18 @@ cli-anything-wiremock record stop
|
||||
|
||||
### Always use `--json` in agent contexts
|
||||
|
||||
Use `--json` for all invocations in scripts or agent tool calls. This produces a stable, parseable envelope:
|
||||
Use `--json` for all invocations in scripts or agent tool calls. This returns the raw WireMock API response JSON directly (no envelope wrapper):
|
||||
|
||||
```json
|
||||
{ "status": "ok", "message": "Created stub abc-123", "data": { ... } }
|
||||
{ "status": "error", "message": "Connection refused" }
|
||||
```bash
|
||||
# Example: create a stub and get the raw WireMock response
|
||||
cli-anything-wiremock --json stub quick GET /api/hello 200 --body '{"hello":"world"}'
|
||||
# → {"id": "abc-123", "request": {...}, "response": {...}, ...}
|
||||
|
||||
# Commands with no response body return:
|
||||
# → {"status": "ok"}
|
||||
|
||||
# Errors return:
|
||||
# → {"status": "error", "message": "Connection refused"}
|
||||
```
|
||||
|
||||
### Connection via environment
|
||||
@@ -134,4 +141,4 @@ export WIREMOCK_PORT=8080
|
||||
|
||||
### Error handling
|
||||
|
||||
Non-zero exit code on all errors. Check `status` field in JSON output. The `data` field is `null` on errors.
|
||||
Non-zero exit code on all errors. In `--json` mode, errors return `{"status": "error", "message": "..."}`. Success returns the raw WireMock API response.
|
||||
|
||||
@@ -17,8 +17,9 @@ class WireMockClient:
|
||||
return f"{self.scheme}://{self.host}:{self.port}{self.admin_prefix}"
|
||||
|
||||
def get(self, path: str, **kwargs) -> requests.Response:
|
||||
timeout = kwargs.pop("timeout", self.timeout)
|
||||
return requests.get(
|
||||
f"{self.base_url()}{path}", auth=self.auth, timeout=self.timeout, **kwargs
|
||||
f"{self.base_url()}{path}", auth=self.auth, timeout=timeout, **kwargs
|
||||
)
|
||||
|
||||
def post(self, path: str, json=None, **kwargs) -> requests.Response:
|
||||
@@ -55,7 +56,7 @@ class WireMockClient:
|
||||
|
||||
def is_alive(self) -> bool:
|
||||
try:
|
||||
r = requests.get(f"{self.base_url()}/health", timeout=3)
|
||||
r = self.get("/health", timeout=3)
|
||||
return r.status_code == 200
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import json
|
||||
import sys
|
||||
import click
|
||||
from cli_anything.wiremock.utils.client import WireMockClient
|
||||
from cli_anything.wiremock.utils.output import error, print_json, print_table, success
|
||||
@@ -10,9 +9,6 @@ from cli_anything.wiremock.core.session import Session
|
||||
from cli_anything.wiremock.core.settings import SettingsManager
|
||||
from cli_anything.wiremock.core.stubs import StubsManager
|
||||
|
||||
pass_client = click.make_pass_decorator(WireMockClient)
|
||||
|
||||
|
||||
@click.group()
|
||||
@click.option(
|
||||
"--host",
|
||||
@@ -139,9 +135,16 @@ def stub_create(ctx, mapping_json):
|
||||
client = ctx.obj
|
||||
json_mode = ctx.meta.get("json_mode", False)
|
||||
try:
|
||||
mapping = json.loads(mapping_json)
|
||||
if mapping_json.startswith("@"):
|
||||
with open(mapping_json[1:]) as f:
|
||||
mapping = json.load(f)
|
||||
else:
|
||||
mapping = json.loads(mapping_json)
|
||||
data = StubsManager(client).create(mapping)
|
||||
success(f"Created stub {data.get('id')}", data, json_mode)
|
||||
if json_mode:
|
||||
print_json(data)
|
||||
else:
|
||||
success(f"Created stub {data.get('id')}", data)
|
||||
except Exception as e:
|
||||
error(str(e), json_mode)
|
||||
|
||||
@@ -159,7 +162,10 @@ def stub_quick(ctx, method, url, status, body, content_type):
|
||||
json_mode = ctx.meta.get("json_mode", False)
|
||||
try:
|
||||
data = StubsManager(client).quick_stub(method, url, status, body, content_type)
|
||||
success(f"Created stub {data.get('id')}", data, json_mode)
|
||||
if json_mode:
|
||||
print_json(data)
|
||||
else:
|
||||
success(f"Created stub {data.get('id')}", data)
|
||||
except Exception as e:
|
||||
error(str(e), json_mode)
|
||||
|
||||
@@ -173,7 +179,10 @@ def stub_delete(ctx, stub_id):
|
||||
json_mode = ctx.meta.get("json_mode", False)
|
||||
try:
|
||||
StubsManager(client).delete(stub_id)
|
||||
success(f"Deleted stub {stub_id}", json_mode=json_mode)
|
||||
if json_mode:
|
||||
print_json({"status": "ok"})
|
||||
else:
|
||||
success(f"Deleted stub {stub_id}")
|
||||
except Exception as e:
|
||||
error(str(e), json_mode)
|
||||
|
||||
@@ -186,7 +195,10 @@ def stub_reset(ctx):
|
||||
json_mode = ctx.meta.get("json_mode", False)
|
||||
try:
|
||||
StubsManager(client).reset()
|
||||
success("Stubs reset to default mappings", json_mode=json_mode)
|
||||
if json_mode:
|
||||
print_json({"status": "ok"})
|
||||
else:
|
||||
success("Stubs reset to default mappings")
|
||||
except Exception as e:
|
||||
error(str(e), json_mode)
|
||||
|
||||
@@ -199,7 +211,10 @@ def stub_save(ctx):
|
||||
json_mode = ctx.meta.get("json_mode", False)
|
||||
try:
|
||||
StubsManager(client).save()
|
||||
success("Mappings saved to disk", json_mode=json_mode)
|
||||
if json_mode:
|
||||
print_json({"status": "ok"})
|
||||
else:
|
||||
success("Mappings saved to disk")
|
||||
except Exception as e:
|
||||
error(str(e), json_mode)
|
||||
|
||||
@@ -215,7 +230,10 @@ def stub_import(ctx, file_path):
|
||||
with open(file_path) as f:
|
||||
data = json.load(f)
|
||||
result = StubsManager(client).import_stubs(data)
|
||||
success("Stubs imported", result, json_mode)
|
||||
if json_mode:
|
||||
print_json(result)
|
||||
else:
|
||||
success("Stubs imported", result)
|
||||
except Exception as e:
|
||||
error(str(e), json_mode)
|
||||
|
||||
@@ -316,7 +334,10 @@ def request_reset(ctx):
|
||||
json_mode = ctx.meta.get("json_mode", False)
|
||||
try:
|
||||
RequestsLog(client).reset()
|
||||
success("Request journal cleared", json_mode=json_mode)
|
||||
if json_mode:
|
||||
print_json({"status": "ok"})
|
||||
else:
|
||||
success("Request journal cleared")
|
||||
except Exception as e:
|
||||
error(str(e), json_mode)
|
||||
|
||||
@@ -370,7 +391,10 @@ def scenario_set(ctx, name, state):
|
||||
json_mode = ctx.meta.get("json_mode", False)
|
||||
try:
|
||||
ScenariosManager(client).set_state(name, state)
|
||||
success(f"Scenario '{name}' set to state '{state}'", json_mode=json_mode)
|
||||
if json_mode:
|
||||
print_json({"status": "ok"})
|
||||
else:
|
||||
success(f"Scenario '{name}' set to state '{state}'")
|
||||
except Exception as e:
|
||||
error(str(e), json_mode)
|
||||
|
||||
@@ -383,7 +407,10 @@ def scenario_reset(ctx):
|
||||
json_mode = ctx.meta.get("json_mode", False)
|
||||
try:
|
||||
ScenariosManager(client).reset_all()
|
||||
success("All scenarios reset", json_mode=json_mode)
|
||||
if json_mode:
|
||||
print_json({"status": "ok"})
|
||||
else:
|
||||
success("All scenarios reset")
|
||||
except Exception as e:
|
||||
error(str(e), json_mode)
|
||||
|
||||
@@ -412,7 +439,10 @@ def record_start(ctx, target_url, match_header):
|
||||
data = RecordingManager(client).start(
|
||||
target_url, list(match_header) or None
|
||||
)
|
||||
success(f"Recording started → {target_url}", data, json_mode)
|
||||
if json_mode:
|
||||
print_json(data)
|
||||
else:
|
||||
success(f"Recording started → {target_url}", data)
|
||||
except Exception as e:
|
||||
error(str(e), json_mode)
|
||||
|
||||
@@ -429,7 +459,7 @@ def record_stop(ctx):
|
||||
print_json(data)
|
||||
else:
|
||||
count = len(data.get("mappings", []))
|
||||
success(f"Recording stopped. {count} stubs captured.", data, json_mode)
|
||||
success(f"Recording stopped. {count} stubs captured.", data)
|
||||
except Exception as e:
|
||||
error(str(e), json_mode)
|
||||
|
||||
@@ -458,8 +488,11 @@ def record_snapshot(ctx):
|
||||
json_mode = ctx.meta.get("json_mode", False)
|
||||
try:
|
||||
data = RecordingManager(client).snapshot()
|
||||
count = len(data.get("mappings", []))
|
||||
success(f"Snapshot: {count} stubs captured", data, json_mode)
|
||||
if json_mode:
|
||||
print_json(data)
|
||||
else:
|
||||
count = len(data.get("mappings", []))
|
||||
success(f"Snapshot: {count} stubs captured", data)
|
||||
except Exception as e:
|
||||
error(str(e), json_mode)
|
||||
|
||||
@@ -538,7 +571,10 @@ def reset_all(ctx):
|
||||
try:
|
||||
r = client.post("/reset")
|
||||
r.raise_for_status()
|
||||
success("Full reset complete", json_mode=json_mode)
|
||||
if json_mode:
|
||||
print_json({"status": "ok"})
|
||||
else:
|
||||
success("Full reset complete")
|
||||
except Exception as e:
|
||||
error(str(e), json_mode)
|
||||
|
||||
@@ -552,10 +588,16 @@ def shutdown(ctx):
|
||||
json_mode = ctx.meta.get("json_mode", False)
|
||||
try:
|
||||
client.post("/shutdown")
|
||||
success("Shutdown signal sent", json_mode=json_mode)
|
||||
except Exception as e:
|
||||
if json_mode:
|
||||
print_json({"status": "ok", "message": "Shutdown signal sent"})
|
||||
else:
|
||||
success("Shutdown signal sent")
|
||||
except Exception:
|
||||
# Server may drop connection before responding
|
||||
click.echo("Shutdown signal sent (connection may have closed)")
|
||||
if json_mode:
|
||||
print_json({"status": "ok", "message": "Shutdown signal sent"})
|
||||
else:
|
||||
click.echo("Shutdown signal sent (connection may have closed)")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -4,11 +4,12 @@ setup(
|
||||
name="cli-anything-wiremock",
|
||||
version="0.1.0",
|
||||
packages=find_namespace_packages(include=["cli_anything.*"]),
|
||||
include_package_data=True,
|
||||
install_requires=["click>=8.0", "requests>=2.28", "rich>=13.0"],
|
||||
entry_points={
|
||||
"console_scripts": [
|
||||
"cli-anything-wiremock=cli_anything.wiremock.wiremock_cli:cli"
|
||||
]
|
||||
},
|
||||
python_requires=">=3.9",
|
||||
python_requires=">=3.10",
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user