deepflow-openagent-cli 0.1.0__tar.gz → 0.1.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.
@@ -1,9 +1,9 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: deepflow-openagent-cli
3
- Version: 0.1.0
4
- Summary: Command-line client for NewAgent OpenAPI (agents, conversations, SSE chat, tools, channels, knowledge bases).
3
+ Version: 0.1.3
4
+ Summary: Command-line client for OpenAgent OpenAPI (agents, conversations, SSE chat, tools, channels, knowledge bases).
5
5
  Author: DeepFlow AI
6
- Project-URL: Repository, https://github.com/DeepFlowAI/newagent
6
+ Project-URL: Repository, https://github.com/DeepFlowAI/OpenAgent
7
7
  Keywords: deepflow,openagent,newagent,openapi,cli,sse,chat
8
8
  Classifier: Development Status :: 4 - Beta
9
9
  Classifier: Environment :: Console
@@ -16,11 +16,12 @@ Requires-Python: >=3.11
16
16
  Description-Content-Type: text/markdown
17
17
  Provides-Extra: dev
18
18
  Requires-Dist: build>=1.0; extra == "dev"
19
+ Requires-Dist: pytest>=8.0; extra == "dev"
19
20
  Requires-Dist: twine>=5.0; extra == "dev"
20
21
 
21
22
  # deepflow-openagent-cli
22
23
 
23
- Python command-line client for the NewAgent OpenAPI: agents, conversations, SSE chat, tools, channels, and knowledge bases.
24
+ Python command-line client for the OpenAgent OpenAPI: agents, conversations, SSE chat, tools, channels, documents, and knowledge bases.
24
25
 
25
26
  ## Install
26
27
 
@@ -31,8 +32,8 @@ pip install deepflow-openagent-cli
31
32
  ## Usage
32
33
 
33
34
  ```bash
34
- export NEWAGENT_HOST="https://your-api-host"
35
- export NEWAGENT_API_KEY="sk-..."
35
+ export OPENAGENT_HOST="https://your-api-host"
36
+ export OPENAGENT_API_KEY="sk-..."
36
37
  deepflow_openagent_cli docs list
37
38
  deepflow_openagent_cli agents list
38
39
  ```
@@ -47,15 +48,36 @@ python -m deepflow_openagent_cli docs list
47
48
 
48
49
  | Method | Variable / flag |
49
50
  |--------|------------------|
50
- | Host | `NEWAGENT_HOST` or `--host` |
51
- | API key | `NEWAGENT_API_KEY` or `--api-key` |
51
+ | Host | `OPENAGENT_HOST` or `--host` |
52
+ | API key | `OPENAGENT_API_KEY` or `--api-key` |
53
+
54
+ Legacy `NEWAGENT_HOST` and `NEWAGENT_API_KEY` are still read as fallbacks.
55
+
56
+ When using flags, put global flags before the command:
57
+
58
+ ```bash
59
+ deepflow_openagent_cli --host http://localhost:5001 --api-key sk-... agents list
60
+ ```
61
+
62
+ ## Common commands
63
+
64
+ ```bash
65
+ deepflow_openagent_cli docs show chat.stream --json
66
+ deepflow_openagent_cli knowledge list --page 1 --per-page 10
67
+ deepflow_openagent_cli knowledge documents 1
68
+ deepflow_openagent_cli knowledge query-document 6670 --slice-id 512208 --line 1720
69
+ deepflow_openagent_cli chat 1 --message "Hello"
70
+ deepflow_openagent_cli steps 1 42
71
+ deepflow_openagent_cli steps get 1 42 100
72
+ ```
52
73
 
53
74
  ## Development (build wheel/sdist locally)
54
75
 
55
76
  From this directory:
56
77
 
57
78
  ```bash
58
- pip install build twine
79
+ pip install -e ".[dev]"
80
+ pytest
59
81
  python -m build
60
82
  ```
61
83
 
@@ -0,0 +1,69 @@
1
+ # deepflow-openagent-cli
2
+
3
+ Python command-line client for the OpenAgent OpenAPI: agents, conversations, SSE chat, tools, channels, documents, and knowledge bases.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ pip install deepflow-openagent-cli
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ ```bash
14
+ export OPENAGENT_HOST="https://your-api-host"
15
+ export OPENAGENT_API_KEY="sk-..."
16
+ deepflow_openagent_cli docs list
17
+ deepflow_openagent_cli agents list
18
+ ```
19
+
20
+ Equivalent module invocation:
21
+
22
+ ```bash
23
+ python -m deepflow_openagent_cli docs list
24
+ ```
25
+
26
+ ## Configure API host and key
27
+
28
+ | Method | Variable / flag |
29
+ |--------|------------------|
30
+ | Host | `OPENAGENT_HOST` or `--host` |
31
+ | API key | `OPENAGENT_API_KEY` or `--api-key` |
32
+
33
+ Legacy `NEWAGENT_HOST` and `NEWAGENT_API_KEY` are still read as fallbacks.
34
+
35
+ When using flags, put global flags before the command:
36
+
37
+ ```bash
38
+ deepflow_openagent_cli --host http://localhost:5001 --api-key sk-... agents list
39
+ ```
40
+
41
+ ## Common commands
42
+
43
+ ```bash
44
+ deepflow_openagent_cli docs show chat.stream --json
45
+ deepflow_openagent_cli knowledge list --page 1 --per-page 10
46
+ deepflow_openagent_cli knowledge documents 1
47
+ deepflow_openagent_cli knowledge query-document 6670 --slice-id 512208 --line 1720
48
+ deepflow_openagent_cli chat 1 --message "Hello"
49
+ deepflow_openagent_cli steps 1 42
50
+ deepflow_openagent_cli steps get 1 42 100
51
+ ```
52
+
53
+ ## Development (build wheel/sdist locally)
54
+
55
+ From this directory:
56
+
57
+ ```bash
58
+ pip install -e ".[dev]"
59
+ pytest
60
+ python -m build
61
+ ```
62
+
63
+ Upload to PyPI (use a **new** API token; never commit tokens):
64
+
65
+ ```bash
66
+ python -m twine upload dist/*
67
+ ```
68
+
69
+ When prompted, username is `__token__` and password is the token value.
@@ -4,8 +4,8 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "deepflow-openagent-cli"
7
- version = "0.1.0"
8
- description = "Command-line client for NewAgent OpenAPI (agents, conversations, SSE chat, tools, channels, knowledge bases)."
7
+ version = "0.1.3"
8
+ description = "Command-line client for OpenAgent OpenAPI (agents, conversations, SSE chat, tools, channels, knowledge bases)."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.11"
11
11
  authors = [{ name = "DeepFlow AI" }]
@@ -23,10 +23,10 @@ classifiers = [
23
23
  dependencies = []
24
24
 
25
25
  [project.optional-dependencies]
26
- dev = ["build>=1.0", "twine>=5.0"]
26
+ dev = ["build>=1.0", "pytest>=8.0", "twine>=5.0"]
27
27
 
28
28
  [project.urls]
29
- Repository = "https://github.com/DeepFlowAI/newagent"
29
+ Repository = "https://github.com/DeepFlowAI/OpenAgent"
30
30
 
31
31
  [project.scripts]
32
32
  deepflow_openagent_cli = "deepflow_openagent_cli.cli:main"
@@ -1,3 +1,3 @@
1
1
  """DeepFlow OpenAgent CLI — published PyPI package."""
2
2
 
3
- __version__ = "0.1.0"
3
+ __version__ = "0.1.3"
@@ -1,5 +1,5 @@
1
1
  """
2
- NewAgent OpenAPI command line client.
2
+ OpenAgent OpenAPI command line client.
3
3
 
4
4
  Run with:
5
5
  deepflow_openagent_cli docs list
@@ -20,8 +20,10 @@ from urllib.request import Request, urlopen
20
20
 
21
21
 
22
22
  DEFAULT_HOST = "http://localhost:5001"
23
- ENV_HOST = "NEWAGENT_HOST"
24
- ENV_API_KEY = "NEWAGENT_API_KEY"
23
+ ENV_HOST = "OPENAGENT_HOST"
24
+ ENV_API_KEY = "OPENAGENT_API_KEY"
25
+ LEGACY_ENV_HOST = "NEWAGENT_HOST"
26
+ LEGACY_ENV_API_KEY = "NEWAGENT_API_KEY"
25
27
 
26
28
 
27
29
  class CliError(Exception):
@@ -56,6 +58,20 @@ def _param(name: str, type_: str, required: bool, description: str) -> ParamDoc:
56
58
 
57
59
 
58
60
  ENDPOINTS: tuple[EndpointDoc, ...] = (
61
+ EndpointDoc(
62
+ key="knowledge.list",
63
+ method="GET",
64
+ path="/api/v1/knowledge-bases",
65
+ scope="chat",
66
+ summary="List knowledge bases for the current tenant.",
67
+ query_params=[
68
+ _param("page", "integer", False, "Page number."),
69
+ _param("per_page", "integer", False, "Items per page, 1-100."),
70
+ ],
71
+ response="Paginated list of public knowledge base fields.",
72
+ errors=["401 missing or invalid API key", "403 scope missing", "422 validation error"],
73
+ example="deepflow_openagent_cli knowledge list --page 1 --per-page 10",
74
+ ),
59
75
  EndpointDoc(
60
76
  key="knowledge.search",
61
77
  method="POST",
@@ -90,6 +106,36 @@ ENDPOINTS: tuple[EndpointDoc, ...] = (
90
106
  errors=["401 missing or invalid API key", "404 document not found"],
91
107
  example="deepflow_openagent_cli knowledge markdown 1 20 --raw",
92
108
  ),
109
+ EndpointDoc(
110
+ key="knowledge.documents",
111
+ method="GET",
112
+ path="/api/v1/knowledge-bases/{kb_id}/documents",
113
+ scope="chat",
114
+ summary="List documents in a knowledge base without embedding Markdown content.",
115
+ path_params=[_param("kb_id", "integer", True, "Knowledge base ID.")],
116
+ query_params=[
117
+ _param("page", "integer", False, "Page number."),
118
+ _param("per_page", "integer", False, "Items per page, 1-100."),
119
+ ],
120
+ response="Paginated list with id, file_path, title, slice_count, updated_at, doc_meta, and markdown_url.",
121
+ errors=["401 missing or invalid API key", "403 scope missing", "404 knowledge base not found", "422 validation error"],
122
+ example="deepflow_openagent_cli knowledge documents 1 --page 1 --per-page 20",
123
+ ),
124
+ EndpointDoc(
125
+ key="documents.query",
126
+ method="POST",
127
+ path="/api/v1/documents/query",
128
+ scope="chat",
129
+ summary="Resolve a document reference by doc_id with optional slice_id and line.",
130
+ body_fields=[
131
+ _param("doc_id", "integer", True, "Document ID, positive integer."),
132
+ _param("slice_id", "integer", False, "Optional slice ID, must belong to doc_id when provided."),
133
+ _param("line", "integer", False, "Optional Markdown source line number, starting at 1."),
134
+ ],
135
+ response="Resolved document fields with optional slice and line details.",
136
+ errors=["401 missing or invalid API key", "403 scope missing", "422 validation error"],
137
+ example="deepflow_openagent_cli knowledge query-document 6670 --slice-id 512208 --line 1720",
138
+ ),
93
139
  EndpointDoc(
94
140
  key="conversations.create",
95
141
  method="POST",
@@ -195,6 +241,21 @@ ENDPOINTS: tuple[EndpointDoc, ...] = (
195
241
  errors=["401 missing or invalid API key", "404 conversation not found"],
196
242
  example="deepflow_openagent_cli steps 1 42",
197
243
  ),
244
+ EndpointDoc(
245
+ key="steps.get",
246
+ method="GET",
247
+ path="/api/v1/agents/{agent_id}/conversations/{conversation_id}/steps/{step_id}",
248
+ scope="chat",
249
+ summary="Get full execution step detail, including LLM request/response payload fields.",
250
+ path_params=[
251
+ _param("agent_id", "integer", True, "Agent ID."),
252
+ _param("conversation_id", "integer", True, "Conversation ID."),
253
+ _param("step_id", "integer", True, "Step ID."),
254
+ ],
255
+ response="StepDetailResponse with request_messages, request_tools, request_params, response fields, and child tool calls.",
256
+ errors=["401 missing or invalid API key", "404 step not found"],
257
+ example="deepflow_openagent_cli steps get 1 42 100",
258
+ ),
198
259
  EndpointDoc(
199
260
  key="agents.list",
200
261
  method="GET",
@@ -270,7 +331,7 @@ ENDPOINTS: tuple[EndpointDoc, ...] = (
270
331
  summary="Partially update system_prompt, model, selected_tool_ids, context, or pre_recall.",
271
332
  path_params=[_param("agent_id", "integer", True, "Agent ID.")],
272
333
  body_fields=[
273
- _param("system_prompt", "string", False, "System prompt, max 10000 chars."),
334
+ _param("system_prompt", "string", False, "System prompt, max 30000 chars."),
274
335
  _param("model", "object", False, "Model config."),
275
336
  _param("selected_tool_ids", "array[integer]", False, "Enabled tool IDs."),
276
337
  _param("context", "object", False, "Context policy."),
@@ -301,7 +362,7 @@ ENDPOINTS: tuple[EndpointDoc, ...] = (
301
362
  body_fields=[
302
363
  _param("name", "string", True, "Tool name, 1-128 chars."),
303
364
  _param("description", "string", False, "Tool description."),
304
- _param("tool_type", "string", True, "search, doc_query, notebook, tool_response_fetch, or python_code."),
365
+ _param("tool_type", "string", True, "search, doc_query, doc_grep, notebook, tool_response_fetch, or python_code."),
305
366
  _param("config", "object", False, "Tool config."),
306
367
  ],
307
368
  response="AgentToolResponse.",
@@ -475,7 +536,7 @@ ENDPOINT_BY_KEY = {endpoint.key: endpoint for endpoint in ENDPOINTS}
475
536
 
476
537
 
477
538
  class OpenApiClient:
478
- """Small HTTP client for NewAgent OpenAPI endpoints."""
539
+ """Small HTTP client for OpenAgent OpenAPI endpoints."""
479
540
 
480
541
  def __init__(self, host: str, api_key: str | None) -> None:
481
542
  self.host = host.rstrip("/")
@@ -639,8 +700,8 @@ def handle_docs(args: argparse.Namespace) -> int:
639
700
 
640
701
 
641
702
  def client_from_args(args: argparse.Namespace) -> OpenApiClient:
642
- host = args.host or os.getenv(ENV_HOST) or DEFAULT_HOST
643
- api_key = args.api_key or os.getenv(ENV_API_KEY)
703
+ host = args.host or os.getenv(ENV_HOST) or os.getenv(LEGACY_ENV_HOST) or DEFAULT_HOST
704
+ api_key = args.api_key or os.getenv(ENV_API_KEY) or os.getenv(LEGACY_ENV_API_KEY)
644
705
  return OpenApiClient(host=host, api_key=api_key)
645
706
 
646
707
 
@@ -665,9 +726,28 @@ def run_request(
665
726
 
666
727
 
667
728
  def handle_knowledge(args: argparse.Namespace) -> int:
729
+ if args.knowledge_action == "list":
730
+ return run_request(args, "knowledge.list", query={"page": args.page, "per_page": args.per_page})
668
731
  if args.knowledge_action == "search":
669
732
  body = merge_payload(args, {"query": args.query})
670
733
  return run_request(args, "knowledge.search", {"kb_id": args.kb_id}, body=body)
734
+ if args.knowledge_action == "documents":
735
+ return run_request(
736
+ args,
737
+ "knowledge.documents",
738
+ {"kb_id": args.kb_id},
739
+ query={"page": args.page, "per_page": args.per_page},
740
+ )
741
+ if args.knowledge_action == "query-document":
742
+ return run_request(
743
+ args,
744
+ "documents.query",
745
+ body={
746
+ "doc_id": args.doc_id,
747
+ "slice_id": args.slice_id,
748
+ "line": args.line,
749
+ },
750
+ )
671
751
  return run_request(args, "knowledge.markdown", {"kb_id": args.kb_id, "doc_id": args.doc_id})
672
752
 
673
753
 
@@ -720,10 +800,24 @@ def handle_chat(args: argparse.Namespace) -> int:
720
800
 
721
801
 
722
802
  def handle_steps(args: argparse.Namespace) -> int:
803
+ action, values = parse_steps_args(args.steps_args)
804
+ if action == "get":
805
+ agent_id, conversation_id, step_id = values
806
+ return run_request(
807
+ args,
808
+ "steps.get",
809
+ {
810
+ "agent_id": agent_id,
811
+ "conversation_id": conversation_id,
812
+ "step_id": step_id,
813
+ },
814
+ )
815
+
816
+ agent_id, conversation_id = values
723
817
  return run_request(
724
818
  args,
725
819
  "steps.list",
726
- {"agent_id": args.agent_id, "conversation_id": args.conversation_id},
820
+ {"agent_id": agent_id, "conversation_id": conversation_id},
727
821
  )
728
822
 
729
823
 
@@ -829,7 +923,7 @@ def add_common_args(parser: argparse.ArgumentParser) -> None:
829
923
 
830
924
 
831
925
  def build_parser() -> argparse.ArgumentParser:
832
- parser = argparse.ArgumentParser(description="NewAgent OpenAPI CLI")
926
+ parser = argparse.ArgumentParser(description="OpenAgent OpenAPI CLI")
833
927
  add_common_args(parser)
834
928
  subparsers = parser.add_subparsers(dest="command", required=True)
835
929
 
@@ -843,10 +937,21 @@ def build_parser() -> argparse.ArgumentParser:
843
937
 
844
938
  knowledge = subparsers.add_parser("knowledge", help="Knowledge base endpoints.")
845
939
  knowledge_sub = knowledge.add_subparsers(dest="knowledge_action", required=True)
940
+ knowledge_list = knowledge_sub.add_parser("list")
941
+ knowledge_list.add_argument("--page", type=int)
942
+ knowledge_list.add_argument("--per-page", type=int)
846
943
  knowledge_search = knowledge_sub.add_parser("search")
847
944
  knowledge_search.add_argument("kb_id", type=int)
848
945
  knowledge_search.add_argument("--query", required=True)
849
946
  add_json_args(knowledge_search)
947
+ knowledge_documents = knowledge_sub.add_parser("documents")
948
+ knowledge_documents.add_argument("kb_id", type=int)
949
+ knowledge_documents.add_argument("--page", type=int)
950
+ knowledge_documents.add_argument("--per-page", type=int)
951
+ knowledge_query_document = knowledge_sub.add_parser("query-document")
952
+ knowledge_query_document.add_argument("doc_id", type=int)
953
+ knowledge_query_document.add_argument("--slice-id", type=int)
954
+ knowledge_query_document.add_argument("--line", type=int)
850
955
  knowledge_markdown = knowledge_sub.add_parser("markdown")
851
956
  knowledge_markdown.add_argument("kb_id", type=int)
852
957
  knowledge_markdown.add_argument("doc_id", type=int)
@@ -891,9 +996,16 @@ def build_parser() -> argparse.ArgumentParser:
891
996
  add_json_args(chat)
892
997
  chat.set_defaults(handler=handle_chat)
893
998
 
894
- steps = subparsers.add_parser("steps", help="List conversation execution steps.")
895
- steps.add_argument("agent_id", type=int)
896
- steps.add_argument("conversation_id", type=int)
999
+ steps = subparsers.add_parser(
1000
+ "steps",
1001
+ help="List conversation execution steps or get full step detail.",
1002
+ description=(
1003
+ "List conversation execution steps or get full step detail. "
1004
+ "Forms: steps <agent_id> <conversation_id>; "
1005
+ "steps get <agent_id> <conversation_id> <step_id>."
1006
+ ),
1007
+ )
1008
+ steps.add_argument("steps_args", nargs="+", metavar="ARGS")
897
1009
  steps.set_defaults(handler=handle_steps)
898
1010
 
899
1011
  agents = subparsers.add_parser("agents", help="Agent endpoints.")
@@ -929,7 +1041,7 @@ def build_parser() -> argparse.ArgumentParser:
929
1041
  tool_create.add_argument("agent_id", type=int)
930
1042
  tool_create.add_argument("--name", required=True)
931
1043
  tool_create.add_argument("--description")
932
- tool_create.add_argument("--tool-type", required=True, choices=["search", "doc_query", "notebook", "tool_response_fetch", "python_code"])
1044
+ tool_create.add_argument("--tool-type", required=True, choices=["search", "doc_query", "doc_grep", "notebook", "tool_response_fetch", "python_code"])
933
1045
  add_json_args(tool_create)
934
1046
  for name in ("get", "delete"):
935
1047
  tool_item = tool_sub.add_parser(name)
@@ -987,6 +1099,30 @@ def parse_bool(value: str) -> bool:
987
1099
  raise argparse.ArgumentTypeError("Expected boolean: true/false")
988
1100
 
989
1101
 
1102
+ def parse_steps_args(values: list[str]) -> tuple[str, tuple[int, ...]]:
1103
+ action = "list"
1104
+ raw_values = values
1105
+ if values[0] in {"list", "get", "detail"}:
1106
+ action = "get" if values[0] == "detail" else values[0]
1107
+ raw_values = values[1:]
1108
+ elif len(values) == 3:
1109
+ action = "get"
1110
+
1111
+ expected = 3 if action == "get" else 2
1112
+ if len(raw_values) != expected:
1113
+ raise CliError(
1114
+ "Usage: steps <agent_id> <conversation_id> or "
1115
+ "steps get <agent_id> <conversation_id> <step_id>."
1116
+ )
1117
+
1118
+ try:
1119
+ parsed = tuple(int(value) for value in raw_values)
1120
+ except ValueError as exc:
1121
+ raise CliError("steps arguments must be integers.") from exc
1122
+
1123
+ return action, parsed
1124
+
1125
+
990
1126
  def main(argv: list[str] | None = None) -> int:
991
1127
  parser = build_parser()
992
1128
  args = parser.parse_args(argv)
@@ -1,9 +1,9 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: deepflow-openagent-cli
3
- Version: 0.1.0
4
- Summary: Command-line client for NewAgent OpenAPI (agents, conversations, SSE chat, tools, channels, knowledge bases).
3
+ Version: 0.1.3
4
+ Summary: Command-line client for OpenAgent OpenAPI (agents, conversations, SSE chat, tools, channels, knowledge bases).
5
5
  Author: DeepFlow AI
6
- Project-URL: Repository, https://github.com/DeepFlowAI/newagent
6
+ Project-URL: Repository, https://github.com/DeepFlowAI/OpenAgent
7
7
  Keywords: deepflow,openagent,newagent,openapi,cli,sse,chat
8
8
  Classifier: Development Status :: 4 - Beta
9
9
  Classifier: Environment :: Console
@@ -16,11 +16,12 @@ Requires-Python: >=3.11
16
16
  Description-Content-Type: text/markdown
17
17
  Provides-Extra: dev
18
18
  Requires-Dist: build>=1.0; extra == "dev"
19
+ Requires-Dist: pytest>=8.0; extra == "dev"
19
20
  Requires-Dist: twine>=5.0; extra == "dev"
20
21
 
21
22
  # deepflow-openagent-cli
22
23
 
23
- Python command-line client for the NewAgent OpenAPI: agents, conversations, SSE chat, tools, channels, and knowledge bases.
24
+ Python command-line client for the OpenAgent OpenAPI: agents, conversations, SSE chat, tools, channels, documents, and knowledge bases.
24
25
 
25
26
  ## Install
26
27
 
@@ -31,8 +32,8 @@ pip install deepflow-openagent-cli
31
32
  ## Usage
32
33
 
33
34
  ```bash
34
- export NEWAGENT_HOST="https://your-api-host"
35
- export NEWAGENT_API_KEY="sk-..."
35
+ export OPENAGENT_HOST="https://your-api-host"
36
+ export OPENAGENT_API_KEY="sk-..."
36
37
  deepflow_openagent_cli docs list
37
38
  deepflow_openagent_cli agents list
38
39
  ```
@@ -47,15 +48,36 @@ python -m deepflow_openagent_cli docs list
47
48
 
48
49
  | Method | Variable / flag |
49
50
  |--------|------------------|
50
- | Host | `NEWAGENT_HOST` or `--host` |
51
- | API key | `NEWAGENT_API_KEY` or `--api-key` |
51
+ | Host | `OPENAGENT_HOST` or `--host` |
52
+ | API key | `OPENAGENT_API_KEY` or `--api-key` |
53
+
54
+ Legacy `NEWAGENT_HOST` and `NEWAGENT_API_KEY` are still read as fallbacks.
55
+
56
+ When using flags, put global flags before the command:
57
+
58
+ ```bash
59
+ deepflow_openagent_cli --host http://localhost:5001 --api-key sk-... agents list
60
+ ```
61
+
62
+ ## Common commands
63
+
64
+ ```bash
65
+ deepflow_openagent_cli docs show chat.stream --json
66
+ deepflow_openagent_cli knowledge list --page 1 --per-page 10
67
+ deepflow_openagent_cli knowledge documents 1
68
+ deepflow_openagent_cli knowledge query-document 6670 --slice-id 512208 --line 1720
69
+ deepflow_openagent_cli chat 1 --message "Hello"
70
+ deepflow_openagent_cli steps 1 42
71
+ deepflow_openagent_cli steps get 1 42 100
72
+ ```
52
73
 
53
74
  ## Development (build wheel/sdist locally)
54
75
 
55
76
  From this directory:
56
77
 
57
78
  ```bash
58
- pip install build twine
79
+ pip install -e ".[dev]"
80
+ pytest
59
81
  python -m build
60
82
  ```
61
83
 
@@ -8,4 +8,5 @@ src/deepflow_openagent_cli.egg-info/SOURCES.txt
8
8
  src/deepflow_openagent_cli.egg-info/dependency_links.txt
9
9
  src/deepflow_openagent_cli.egg-info/entry_points.txt
10
10
  src/deepflow_openagent_cli.egg-info/requires.txt
11
- src/deepflow_openagent_cli.egg-info/top_level.txt
11
+ src/deepflow_openagent_cli.egg-info/top_level.txt
12
+ tests/test_cli.py
@@ -0,0 +1,249 @@
1
+ import json
2
+ from pathlib import Path
3
+ import tomllib
4
+
5
+ import pytest
6
+
7
+ from deepflow_openagent_cli import __version__
8
+ from deepflow_openagent_cli.cli import (
9
+ CliError,
10
+ ENDPOINTS,
11
+ OpenApiClient,
12
+ build_parser,
13
+ client_from_args,
14
+ get_endpoint,
15
+ load_json_payload,
16
+ main,
17
+ )
18
+
19
+
20
+ def test_version_matches_release() -> None:
21
+ pyproject = Path(__file__).resolve().parents[1] / "pyproject.toml"
22
+ metadata = tomllib.loads(pyproject.read_text(encoding="utf-8"))
23
+
24
+ assert __version__ == metadata["project"]["version"]
25
+
26
+
27
+ def test_catalog_has_stable_unique_keys() -> None:
28
+ keys = [endpoint.key for endpoint in ENDPOINTS]
29
+
30
+ assert len(keys) == len(set(keys))
31
+ assert "knowledge.list" in keys
32
+ assert "knowledge.documents" in keys
33
+ assert "documents.query" in keys
34
+ assert "chat.stream" in keys
35
+ assert "steps.get" in keys
36
+ assert "channels.secret_key" in keys
37
+
38
+
39
+ def test_get_endpoint_unknown_key_raises_clear_error() -> None:
40
+ with pytest.raises(CliError, match="Unknown endpoint"):
41
+ get_endpoint("missing.endpoint")
42
+
43
+
44
+ def test_load_json_payload_rejects_non_object() -> None:
45
+ with pytest.raises(CliError, match="must be an object"):
46
+ load_json_payload("[1, 2, 3]", None)
47
+
48
+
49
+ def test_build_url_formats_path_and_query() -> None:
50
+ client = OpenApiClient("http://localhost:5001/", "sk-test")
51
+ endpoint = get_endpoint("knowledge.documents")
52
+
53
+ url = client._build_url(
54
+ endpoint,
55
+ {"kb_id": 1},
56
+ {"page": 2, "per_page": 20, "source": None},
57
+ )
58
+
59
+ assert url == "http://localhost:5001/api/v1/knowledge-bases/1/documents?page=2&per_page=20"
60
+
61
+
62
+ def test_client_from_args_reads_openagent_env(monkeypatch: pytest.MonkeyPatch) -> None:
63
+ monkeypatch.setenv("OPENAGENT_HOST", "https://api.example.com")
64
+ monkeypatch.setenv("OPENAGENT_API_KEY", "sk-openagent")
65
+ args = build_parser().parse_args(["docs", "list"])
66
+
67
+ client = client_from_args(args)
68
+
69
+ assert client.host == "https://api.example.com"
70
+ assert client.api_key == "sk-openagent"
71
+
72
+
73
+ def test_client_from_args_reads_legacy_newagent_env(monkeypatch: pytest.MonkeyPatch) -> None:
74
+ monkeypatch.delenv("OPENAGENT_HOST", raising=False)
75
+ monkeypatch.delenv("OPENAGENT_API_KEY", raising=False)
76
+ monkeypatch.setenv("NEWAGENT_HOST", "https://legacy.example.com")
77
+ monkeypatch.setenv("NEWAGENT_API_KEY", "sk-legacy")
78
+ args = build_parser().parse_args(["docs", "list"])
79
+
80
+ client = client_from_args(args)
81
+
82
+ assert client.host == "https://legacy.example.com"
83
+ assert client.api_key == "sk-legacy"
84
+
85
+
86
+ def test_docs_show_json_outputs_endpoint_detail(capsys: pytest.CaptureFixture[str]) -> None:
87
+ exit_code = main(["docs", "show", "documents.query", "--json"])
88
+
89
+ captured = capsys.readouterr()
90
+ assert exit_code == 0
91
+ payload = json.loads(captured.out)
92
+ assert payload["method"] == "POST"
93
+ assert payload["scope"] == "chat"
94
+
95
+
96
+ def test_query_document_command_posts_body(
97
+ monkeypatch: pytest.MonkeyPatch,
98
+ capsys: pytest.CaptureFixture[str],
99
+ ) -> None:
100
+ calls = []
101
+
102
+ def fake_request(self, endpoint_key, path_params=None, query=None, body=None, *, stream=False):
103
+ calls.append((endpoint_key, path_params, query, body, stream))
104
+ return b'{"ok": true}'
105
+
106
+ monkeypatch.setattr(OpenApiClient, "request", fake_request)
107
+
108
+ exit_code = main(
109
+ [
110
+ "--host",
111
+ "http://localhost:5001",
112
+ "--api-key",
113
+ "sk-test",
114
+ "knowledge",
115
+ "query-document",
116
+ "6670",
117
+ "--slice-id",
118
+ "512208",
119
+ "--line",
120
+ "1720",
121
+ ]
122
+ )
123
+
124
+ captured = capsys.readouterr()
125
+ assert exit_code == 0
126
+ assert json.loads(captured.out) == {"ok": True}
127
+ assert calls == [
128
+ (
129
+ "documents.query",
130
+ None,
131
+ None,
132
+ {"doc_id": 6670, "slice_id": 512208, "line": 1720},
133
+ False,
134
+ )
135
+ ]
136
+
137
+
138
+ def test_steps_list_keeps_legacy_short_form(
139
+ monkeypatch: pytest.MonkeyPatch,
140
+ capsys: pytest.CaptureFixture[str],
141
+ ) -> None:
142
+ calls = []
143
+
144
+ def fake_request(self, endpoint_key, path_params=None, query=None, body=None, *, stream=False):
145
+ calls.append((endpoint_key, path_params, query, body, stream))
146
+ return b'{"ok": true}'
147
+
148
+ monkeypatch.setattr(OpenApiClient, "request", fake_request)
149
+
150
+ exit_code = main(
151
+ [
152
+ "--host",
153
+ "http://localhost:5001",
154
+ "--api-key",
155
+ "sk-test",
156
+ "steps",
157
+ "1",
158
+ "42",
159
+ ]
160
+ )
161
+
162
+ captured = capsys.readouterr()
163
+ assert exit_code == 0
164
+ assert json.loads(captured.out) == {"ok": True}
165
+ assert calls == [
166
+ (
167
+ "steps.list",
168
+ {"agent_id": 1, "conversation_id": 42},
169
+ None,
170
+ None,
171
+ False,
172
+ )
173
+ ]
174
+
175
+
176
+ def test_steps_get_fetches_full_step_detail(
177
+ monkeypatch: pytest.MonkeyPatch,
178
+ capsys: pytest.CaptureFixture[str],
179
+ ) -> None:
180
+ calls = []
181
+
182
+ def fake_request(self, endpoint_key, path_params=None, query=None, body=None, *, stream=False):
183
+ calls.append((endpoint_key, path_params, query, body, stream))
184
+ return b'{"ok": true}'
185
+
186
+ monkeypatch.setattr(OpenApiClient, "request", fake_request)
187
+
188
+ exit_code = main(
189
+ [
190
+ "--host",
191
+ "http://localhost:5001",
192
+ "--api-key",
193
+ "sk-test",
194
+ "steps",
195
+ "get",
196
+ "1",
197
+ "42",
198
+ "100",
199
+ ]
200
+ )
201
+
202
+ captured = capsys.readouterr()
203
+ assert exit_code == 0
204
+ assert json.loads(captured.out) == {"ok": True}
205
+ assert calls == [
206
+ (
207
+ "steps.get",
208
+ {"agent_id": 1, "conversation_id": 42, "step_id": 100},
209
+ None,
210
+ None,
211
+ False,
212
+ )
213
+ ]
214
+
215
+
216
+ def test_tools_create_accepts_doc_grep(
217
+ monkeypatch: pytest.MonkeyPatch,
218
+ capsys: pytest.CaptureFixture[str],
219
+ ) -> None:
220
+ calls = []
221
+
222
+ def fake_request(self, endpoint_key, path_params=None, query=None, body=None, *, stream=False):
223
+ calls.append((endpoint_key, path_params, query, body, stream))
224
+ return b'{"ok": true}'
225
+
226
+ monkeypatch.setattr(OpenApiClient, "request", fake_request)
227
+
228
+ exit_code = main(
229
+ [
230
+ "--host",
231
+ "http://localhost:5001",
232
+ "--api-key",
233
+ "sk-test",
234
+ "tools",
235
+ "create",
236
+ "1",
237
+ "--name",
238
+ "Doc Grep",
239
+ "--tool-type",
240
+ "doc_grep",
241
+ ]
242
+ )
243
+
244
+ captured = capsys.readouterr()
245
+ assert exit_code == 0
246
+ assert json.loads(captured.out) == {"ok": True}
247
+ assert calls[0][0] == "tools.create"
248
+ assert calls[0][1] == {"agent_id": 1}
249
+ assert calls[0][3]["tool_type"] == "doc_grep"
@@ -1,48 +0,0 @@
1
- # deepflow-openagent-cli
2
-
3
- Python command-line client for the NewAgent OpenAPI: agents, conversations, SSE chat, tools, channels, and knowledge bases.
4
-
5
- ## Install
6
-
7
- ```bash
8
- pip install deepflow-openagent-cli
9
- ```
10
-
11
- ## Usage
12
-
13
- ```bash
14
- export NEWAGENT_HOST="https://your-api-host"
15
- export NEWAGENT_API_KEY="sk-..."
16
- deepflow_openagent_cli docs list
17
- deepflow_openagent_cli agents list
18
- ```
19
-
20
- Equivalent module invocation:
21
-
22
- ```bash
23
- python -m deepflow_openagent_cli docs list
24
- ```
25
-
26
- ## Configure API host and key
27
-
28
- | Method | Variable / flag |
29
- |--------|------------------|
30
- | Host | `NEWAGENT_HOST` or `--host` |
31
- | API key | `NEWAGENT_API_KEY` or `--api-key` |
32
-
33
- ## Development (build wheel/sdist locally)
34
-
35
- From this directory:
36
-
37
- ```bash
38
- pip install build twine
39
- python -m build
40
- ```
41
-
42
- Upload to PyPI (use a **new** API token; never commit tokens):
43
-
44
- ```bash
45
- python -m twine upload dist/*
46
- ```
47
-
48
- When prompted, username is `__token__` and password is the token value.