venn-cli 0.1.0__tar.gz → 0.2.0__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.
- {venn_cli-0.1.0 → venn_cli-0.2.0}/PKG-INFO +2 -1
- {venn_cli-0.1.0 → venn_cli-0.2.0}/pyproject.toml +2 -1
- venn_cli-0.2.0/src/venn_cli/cli.py +461 -0
- {venn_cli-0.1.0 → venn_cli-0.2.0}/src/venn_cli/client.py +5 -1
- venn_cli-0.2.0/src/venn_cli/output.py +366 -0
- {venn_cli-0.1.0 → venn_cli-0.2.0}/tests/test_cli.py +50 -3
- venn_cli-0.1.0/src/venn_cli/cli.py +0 -265
- venn_cli-0.1.0/src/venn_cli/output.py +0 -167
- {venn_cli-0.1.0 → venn_cli-0.2.0}/.github/workflows/ci.yml +0 -0
- {venn_cli-0.1.0 → venn_cli-0.2.0}/.github/workflows/publish-pypi.yaml +0 -0
- {venn_cli-0.1.0 → venn_cli-0.2.0}/.gitignore +0 -0
- {venn_cli-0.1.0 → venn_cli-0.2.0}/LICENSE +0 -0
- {venn_cli-0.1.0 → venn_cli-0.2.0}/VERSION +0 -0
- {venn_cli-0.1.0 → venn_cli-0.2.0}/docs/openapi.json +0 -0
- {venn_cli-0.1.0 → venn_cli-0.2.0}/src/venn_cli/__init__.py +0 -0
- {venn_cli-0.1.0 → venn_cli-0.2.0}/tests/__init__.py +0 -0
- {venn_cli-0.1.0 → venn_cli-0.2.0}/tests/conftest.py +0 -0
- {venn_cli-0.1.0 → venn_cli-0.2.0}/tests/test_client.py +0 -0
- {venn_cli-0.1.0 → venn_cli-0.2.0}/tests/test_integration.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: venn-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.0
|
|
4
4
|
Summary: CLI for the Venn ToolIQ API
|
|
5
5
|
Project-URL: Homepage, https://github.com/moda-labs/venn-cli
|
|
6
6
|
Project-URL: Repository, https://github.com/moda-labs/venn-cli
|
|
@@ -21,6 +21,7 @@ Requires-Python: >=3.11
|
|
|
21
21
|
Requires-Dist: click>=8.1
|
|
22
22
|
Requires-Dist: httpx>=0.27
|
|
23
23
|
Requires-Dist: python-dotenv>=1.0
|
|
24
|
+
Requires-Dist: pyyaml>=6.0
|
|
24
25
|
Requires-Dist: rich>=13.0
|
|
25
26
|
Provides-Extra: dev
|
|
26
27
|
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "venn-cli"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.2.0"
|
|
4
4
|
description = "CLI for the Venn ToolIQ API"
|
|
5
5
|
requires-python = ">=3.11"
|
|
6
6
|
license = "MIT"
|
|
@@ -21,6 +21,7 @@ classifiers = [
|
|
|
21
21
|
dependencies = [
|
|
22
22
|
"click>=8.1",
|
|
23
23
|
"httpx>=0.27",
|
|
24
|
+
"pyyaml>=6.0",
|
|
24
25
|
"rich>=13.0",
|
|
25
26
|
"python-dotenv>=1.0",
|
|
26
27
|
]
|
|
@@ -0,0 +1,461 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import os
|
|
5
|
+
import sys
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
import click
|
|
9
|
+
from dotenv import load_dotenv
|
|
10
|
+
|
|
11
|
+
from venn_cli.client import VennClient
|
|
12
|
+
from venn_cli import output
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def _load_env() -> None:
|
|
16
|
+
for p in [Path.cwd() / ".env", Path(__file__).resolve().parents[2] / ".env"]:
|
|
17
|
+
if p.exists():
|
|
18
|
+
load_dotenv(p)
|
|
19
|
+
return
|
|
20
|
+
load_dotenv()
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def _client(ctx: click.Context) -> VennClient:
|
|
24
|
+
return ctx.obj["client"]
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def _mode(ctx: click.Context) -> str:
|
|
28
|
+
return ctx.obj["mode"]
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def _out(ctx: click.Context, *, yaml_fn, table_fn, data):
|
|
32
|
+
mode = _mode(ctx)
|
|
33
|
+
if mode == "json":
|
|
34
|
+
output.print_json(data)
|
|
35
|
+
elif mode == "table":
|
|
36
|
+
table_fn()
|
|
37
|
+
else:
|
|
38
|
+
yaml_fn()
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
@click.group()
|
|
42
|
+
@click.option("--api-key", envvar="VENN_API_KEY", help="Venn API key (or set VENN_API_KEY)")
|
|
43
|
+
@click.option("--base-url", envvar="VENN_BASE_URL", default=None, help="Override API base URL")
|
|
44
|
+
@click.option("--json", "use_json", is_flag=True, help="Output raw JSON")
|
|
45
|
+
@click.option("--table", "use_table", is_flag=True, help="Output rich tables (for terminals)")
|
|
46
|
+
@click.version_option(package_name="venn-cli")
|
|
47
|
+
@click.pass_context
|
|
48
|
+
def main(ctx: click.Context, api_key: str | None, base_url: str | None, use_json: bool, use_table: bool) -> None:
|
|
49
|
+
"""Venn ToolIQ CLI — discover, inspect, and execute tools."""
|
|
50
|
+
_load_env()
|
|
51
|
+
ctx.ensure_object(dict)
|
|
52
|
+
if use_json:
|
|
53
|
+
ctx.obj["mode"] = "json"
|
|
54
|
+
elif use_table:
|
|
55
|
+
ctx.obj["mode"] = "table"
|
|
56
|
+
else:
|
|
57
|
+
ctx.obj["mode"] = "text"
|
|
58
|
+
api_key = api_key or os.environ.get("VENN_API_KEY")
|
|
59
|
+
if ctx.invoked_subcommand == "docs":
|
|
60
|
+
return
|
|
61
|
+
if not api_key:
|
|
62
|
+
click.echo("Error: VENN_API_KEY not set. Pass --api-key or add it to .env", err=True)
|
|
63
|
+
raise SystemExit(1)
|
|
64
|
+
kwargs = {"api_key": api_key}
|
|
65
|
+
if base_url:
|
|
66
|
+
kwargs["base_url"] = base_url
|
|
67
|
+
ctx.obj["client"] = VennClient(**kwargs)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
# ---------------------------------------------------------------------------
|
|
71
|
+
# venn docs
|
|
72
|
+
# ---------------------------------------------------------------------------
|
|
73
|
+
|
|
74
|
+
DOCS = """\
|
|
75
|
+
# Venn CLI
|
|
76
|
+
|
|
77
|
+
Tool discovery and execution across connected MCP servers.
|
|
78
|
+
Default output is compact text optimized for LLM context windows.
|
|
79
|
+
Use `--json` for raw JSON or `--table` for rich terminal tables.
|
|
80
|
+
|
|
81
|
+
## Setup
|
|
82
|
+
|
|
83
|
+
Set VENN_API_KEY as an environment variable or in a .env file.
|
|
84
|
+
|
|
85
|
+
## Typical workflow
|
|
86
|
+
|
|
87
|
+
1. `venn help list_servers` — see connected servers and their IDs
|
|
88
|
+
2. `venn tools search "<intent>"` — find tools by natural language
|
|
89
|
+
3. `venn tools describe -s <server_id> -t <tool>` — get full schema and params
|
|
90
|
+
4. `venn tools execute -s <server_id> -t <tool> -a '<json args>'` — run it
|
|
91
|
+
For write operations, add `--confirm` to auto-obtain a confirmation token.
|
|
92
|
+
|
|
93
|
+
## Commands
|
|
94
|
+
|
|
95
|
+
### venn tools search <query>
|
|
96
|
+
Search for tools across all connected servers using semantic similarity.
|
|
97
|
+
Returns ranked results — tools and skills.
|
|
98
|
+
-n, --limit INT Max results (default: 10)
|
|
99
|
+
--offset INT Skip N results (default: 0)
|
|
100
|
+
--min-score FLOAT Minimum similarity (0-1, default: 0.3)
|
|
101
|
+
--refresh Bypass cache
|
|
102
|
+
Example: venn tools search "create calendar event" -n 5
|
|
103
|
+
|
|
104
|
+
### venn tools list -s <slug>
|
|
105
|
+
List all tools for a specific server/toolset.
|
|
106
|
+
-s, --slug TEXT Toolset slug (e.g. "gmail", "slack")
|
|
107
|
+
-d, --directory-id TEXT Server directory UUID
|
|
108
|
+
-i, --instance-id TEXT Server instance UUID
|
|
109
|
+
At least one identifier is required.
|
|
110
|
+
Example: venn tools list -s gmail
|
|
111
|
+
|
|
112
|
+
### venn tools describe -s <server_id> -t <tool_name>
|
|
113
|
+
Get detailed schema for one or more tools — description, params, types.
|
|
114
|
+
-s, --server-id TEXT Server identifier [required]
|
|
115
|
+
-t, --tool-name TEXT Tool name, repeatable for multiple tools [required]
|
|
116
|
+
--refresh Bypass cache
|
|
117
|
+
Example: venn tools describe -s personal-gmail -t send_email -t create_draft
|
|
118
|
+
|
|
119
|
+
### venn tools execute -s <server_id> -t <tool_name>
|
|
120
|
+
Execute a tool on a connected server.
|
|
121
|
+
-s, --server-id TEXT Server identifier [required]
|
|
122
|
+
-t, --tool-name TEXT Tool name [required]
|
|
123
|
+
-a, --args TEXT Tool arguments as a JSON string
|
|
124
|
+
--confirm Auto-obtain confirmation token for write operations
|
|
125
|
+
Example (read): venn tools execute -s personal-gmail -t list_emails -a '{"maxResults": 5}'
|
|
126
|
+
Example (write): venn tools execute -s personal-gmail -t send_email --confirm -a '{"sender":"me","to":"bob@example.com","subject":"hi","body":"hello"}'
|
|
127
|
+
|
|
128
|
+
### venn tools confirm
|
|
129
|
+
Get a standalone confirmation token for write operations.
|
|
130
|
+
Returns a single-use token and its expiry. Pass it to execute via --confirm
|
|
131
|
+
or manually via the API.
|
|
132
|
+
|
|
133
|
+
### venn workflow run
|
|
134
|
+
Execute Python code in a sandboxed environment. Two async functions are
|
|
135
|
+
injected automatically (no imports needed):
|
|
136
|
+
|
|
137
|
+
async_call_tool(server_id, tool_name, **kwargs) -> dict
|
|
138
|
+
async_call_skill(skill_name, **kwargs) -> dict
|
|
139
|
+
|
|
140
|
+
Both must be awaited. Return results with `return`.
|
|
141
|
+
|
|
142
|
+
Options:
|
|
143
|
+
-c, --code TEXT Inline Python code
|
|
144
|
+
-f, --file PATH Read code from a file
|
|
145
|
+
--timeout INT Max execution seconds (1-360, default: 180)
|
|
146
|
+
--confirm Obtain confirmation for write operations
|
|
147
|
+
|
|
148
|
+
Sandbox builtins: len, sum, max, min, filter, map, sorted, range,
|
|
149
|
+
enumerate, zip, dict, set, list, isinstance, type, asyncio
|
|
150
|
+
Not available: network (requests/httpx/urllib), filesystem (open),
|
|
151
|
+
subprocess, os, sys. Use async_call_tool for all external access.
|
|
152
|
+
Do NOT use asyncio.run() or loop.run_until_complete() — use await and
|
|
153
|
+
asyncio.gather() only.
|
|
154
|
+
|
|
155
|
+
Examples:
|
|
156
|
+
|
|
157
|
+
Single tool call:
|
|
158
|
+
venn workflow run -c "
|
|
159
|
+
result = await async_call_tool('personal-gmail', 'list_emails', userId='me', maxResults=5)
|
|
160
|
+
return result
|
|
161
|
+
"
|
|
162
|
+
|
|
163
|
+
Chain tool calls:
|
|
164
|
+
venn workflow run -c "
|
|
165
|
+
emails = await async_call_tool('personal-gmail', 'list_emails', userId='me', maxResults=1)
|
|
166
|
+
msg_id = emails['messages'][0]['id']
|
|
167
|
+
detail = await async_call_tool('personal-gmail', 'find_email', userId='me', id=msg_id)
|
|
168
|
+
return {'subject': detail.get('subject'), 'snippet': detail.get('snippet')}
|
|
169
|
+
"
|
|
170
|
+
|
|
171
|
+
Parallel execution:
|
|
172
|
+
venn workflow run -c "
|
|
173
|
+
import asyncio
|
|
174
|
+
results = await asyncio.gather(
|
|
175
|
+
async_call_tool('personal-gmail', 'list_emails', userId='me', maxResults=3),
|
|
176
|
+
async_call_tool('work-gmail', 'list_emails', userId='me', maxResults=3),
|
|
177
|
+
)
|
|
178
|
+
return {'personal': len(results[0].get('messages',[])), 'work': len(results[1].get('messages',[]))}
|
|
179
|
+
"
|
|
180
|
+
|
|
181
|
+
Write operation (requires --confirm):
|
|
182
|
+
venn workflow run --confirm -c "
|
|
183
|
+
result = await async_call_tool('personal-gmail', 'send_email', sender='me', to='bob@example.com', subject='hi', body='hello')
|
|
184
|
+
return result
|
|
185
|
+
"
|
|
186
|
+
|
|
187
|
+
### venn help [action]
|
|
188
|
+
Get help info, server status, and auth URLs.
|
|
189
|
+
Actions: getting_started, connector_help, auth_helper, list_servers
|
|
190
|
+
-s, --server-id TEXT Required for auth_helper
|
|
191
|
+
--refresh Bypass cache
|
|
192
|
+
Example: venn help list_servers
|
|
193
|
+
|
|
194
|
+
### venn skills upsert -f <file.yaml>
|
|
195
|
+
Create or update an executable skill from a YAML file.
|
|
196
|
+
-f, --file PATH Skill YAML file [required]
|
|
197
|
+
|
|
198
|
+
### venn history toolset <key>
|
|
199
|
+
Show revision history for a 1st-party toolset.
|
|
200
|
+
-n, --limit INT Max revisions (default: 50)
|
|
201
|
+
--before INT Only show versions before this number
|
|
202
|
+
|
|
203
|
+
### venn history server <directory_id>
|
|
204
|
+
Show revision history for a server instance.
|
|
205
|
+
-n, --limit INT Max revisions (default: 50)
|
|
206
|
+
--before INT Only show versions before this number
|
|
207
|
+
|
|
208
|
+
## Output modes
|
|
209
|
+
|
|
210
|
+
Default output is YAML — structured, minimal tokens, ideal for agents.
|
|
211
|
+
--json Raw JSON (full API response)
|
|
212
|
+
--table Rich formatted tables (for human terminal use)
|
|
213
|
+
|
|
214
|
+
## Write safety
|
|
215
|
+
|
|
216
|
+
Tools with write operations require confirmation. The --confirm flag on
|
|
217
|
+
`venn tools execute` and `venn workflow run` handles this automatically:
|
|
218
|
+
it fetches a single-use token and includes it in the request. Without
|
|
219
|
+
--confirm, write operations will be rejected by the server.\
|
|
220
|
+
"""
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
@main.command("docs")
|
|
224
|
+
def docs_cmd():
|
|
225
|
+
"""Print the full CLI reference for agent consumption."""
|
|
226
|
+
click.echo(DOCS)
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
# ---------------------------------------------------------------------------
|
|
230
|
+
# venn tools
|
|
231
|
+
# ---------------------------------------------------------------------------
|
|
232
|
+
|
|
233
|
+
@main.group()
|
|
234
|
+
def tools():
|
|
235
|
+
"""Discover, inspect, and execute tools."""
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
@tools.command("list")
|
|
239
|
+
@click.option("--slug", "-s", help="Toolset slug (e.g. 'salesforce', 'gmail')")
|
|
240
|
+
@click.option("--directory-id", "-d", help="Server directory UUID")
|
|
241
|
+
@click.option("--instance-id", "-i", help="Server instance UUID")
|
|
242
|
+
@click.pass_context
|
|
243
|
+
def tools_list(ctx, slug, directory_id, instance_id):
|
|
244
|
+
"""List tools for a server."""
|
|
245
|
+
if not any([slug, directory_id, instance_id]):
|
|
246
|
+
click.echo("Provide at least one of: --slug, --directory-id, --instance-id", err=True)
|
|
247
|
+
raise SystemExit(1)
|
|
248
|
+
data = _client(ctx).list_tools(slug=slug, directory_id=directory_id, instance_id=instance_id)
|
|
249
|
+
result = data.get("result", {})
|
|
250
|
+
_out(ctx,
|
|
251
|
+
yaml_fn=lambda: output.yaml_tool_list(result),
|
|
252
|
+
table_fn=lambda: output.table_tool_list(result),
|
|
253
|
+
data=data)
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
@tools.command("search")
|
|
257
|
+
@click.argument("query")
|
|
258
|
+
@click.option("--limit", "-n", default=10, type=int, help="Max results")
|
|
259
|
+
@click.option("--offset", default=0, type=int, help="Skip N results")
|
|
260
|
+
@click.option("--min-score", default=0.3, type=float, help="Minimum similarity score")
|
|
261
|
+
@click.option("--refresh", is_flag=True, help="Bypass cache")
|
|
262
|
+
@click.pass_context
|
|
263
|
+
def tools_search(ctx, query, limit, offset, min_score, refresh):
|
|
264
|
+
"""Search tools by natural language query."""
|
|
265
|
+
data = _client(ctx).search_tools(query, limit=limit, offset=offset, min_score=min_score, refresh=refresh)
|
|
266
|
+
result = data.get("result", {})
|
|
267
|
+
_out(ctx,
|
|
268
|
+
yaml_fn=lambda: output.yaml_search_results(result),
|
|
269
|
+
table_fn=lambda: output.table_search_results(result),
|
|
270
|
+
data=data)
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
@tools.command("describe")
|
|
274
|
+
@click.option("--server-id", "-s", required=True, help="Server identifier")
|
|
275
|
+
@click.option("--tool-name", "-t", required=True, multiple=True, help="Tool name (repeatable)")
|
|
276
|
+
@click.option("--refresh", is_flag=True, help="Bypass cache")
|
|
277
|
+
@click.pass_context
|
|
278
|
+
def tools_describe(ctx, server_id, tool_name, refresh):
|
|
279
|
+
"""Get detailed schema for one or more tools."""
|
|
280
|
+
tool_ids = [{"server_id": server_id, "tool_name": t} for t in tool_name]
|
|
281
|
+
data = _client(ctx).describe_tools(tool_ids, refresh=refresh)
|
|
282
|
+
results = data.get("result", {}).get("results", [])
|
|
283
|
+
_out(ctx,
|
|
284
|
+
yaml_fn=lambda: output.yaml_tool_details(results),
|
|
285
|
+
table_fn=lambda: output.table_tool_details(results),
|
|
286
|
+
data=data)
|
|
287
|
+
|
|
288
|
+
|
|
289
|
+
@tools.command("execute")
|
|
290
|
+
@click.option("--server-id", "-s", required=True, help="Server identifier")
|
|
291
|
+
@click.option("--tool-name", "-t", required=True, help="Tool name")
|
|
292
|
+
@click.option("--args", "-a", "tool_args", default=None, help="Tool arguments as JSON string")
|
|
293
|
+
@click.option("--confirm", "do_confirm", is_flag=True, help="Auto-obtain confirmation token for write ops")
|
|
294
|
+
@click.pass_context
|
|
295
|
+
def tools_execute(ctx, server_id, tool_name, tool_args, do_confirm):
|
|
296
|
+
"""Execute a tool."""
|
|
297
|
+
client = _client(ctx)
|
|
298
|
+
parsed_args = json.loads(tool_args) if tool_args else None
|
|
299
|
+
|
|
300
|
+
confirmation_token = None
|
|
301
|
+
if do_confirm:
|
|
302
|
+
confirm_data = client.confirm_write()
|
|
303
|
+
confirmation_token = confirm_data.get("result", {}).get("confirmation_token")
|
|
304
|
+
|
|
305
|
+
data = client.execute_tool(
|
|
306
|
+
server_id=server_id,
|
|
307
|
+
tool_name=tool_name,
|
|
308
|
+
tool_args=parsed_args,
|
|
309
|
+
confirmed=do_confirm,
|
|
310
|
+
confirmation_token=confirmation_token,
|
|
311
|
+
)
|
|
312
|
+
_out(ctx,
|
|
313
|
+
yaml_fn=lambda: output.yaml_execute_result(data),
|
|
314
|
+
table_fn=lambda: output.table_execute_result(data),
|
|
315
|
+
data=data)
|
|
316
|
+
|
|
317
|
+
|
|
318
|
+
@tools.command("confirm")
|
|
319
|
+
@click.pass_context
|
|
320
|
+
def tools_confirm(ctx):
|
|
321
|
+
"""Get a confirmation token for write operations."""
|
|
322
|
+
data = _client(ctx).confirm_write()
|
|
323
|
+
result = data.get("result", {})
|
|
324
|
+
_out(ctx,
|
|
325
|
+
yaml_fn=lambda: output.yaml_confirm(result),
|
|
326
|
+
table_fn=lambda: output.table_confirm(result),
|
|
327
|
+
data=data)
|
|
328
|
+
|
|
329
|
+
|
|
330
|
+
# ---------------------------------------------------------------------------
|
|
331
|
+
# venn workflow
|
|
332
|
+
# ---------------------------------------------------------------------------
|
|
333
|
+
|
|
334
|
+
@main.group()
|
|
335
|
+
def workflow():
|
|
336
|
+
"""Execute code in a sandboxed environment."""
|
|
337
|
+
|
|
338
|
+
|
|
339
|
+
@workflow.command("run")
|
|
340
|
+
@click.option("--code", "-c", help="Python code to execute")
|
|
341
|
+
@click.option("--file", "-f", "code_file", type=click.Path(exists=True), help="Read code from file")
|
|
342
|
+
@click.option("--timeout", default=180, type=int, help="Max execution time in seconds")
|
|
343
|
+
@click.option("--confirm", "do_confirm", is_flag=True, help="Obtain confirmation for write ops")
|
|
344
|
+
@click.pass_context
|
|
345
|
+
def workflow_run(ctx, code, code_file, timeout, do_confirm):
|
|
346
|
+
"""Execute Python code in the Venn sandbox.
|
|
347
|
+
|
|
348
|
+
Two async functions are injected (no imports needed):
|
|
349
|
+
|
|
350
|
+
\b
|
|
351
|
+
await async_call_tool(server_id, tool_name, **kwargs) -> dict
|
|
352
|
+
await async_call_skill(skill_name, **kwargs) -> dict
|
|
353
|
+
|
|
354
|
+
Return results with `return`. Use asyncio.gather() for parallel calls.
|
|
355
|
+
Run `venn docs` for full sandbox reference and examples.
|
|
356
|
+
"""
|
|
357
|
+
if code_file:
|
|
358
|
+
code = Path(code_file).read_text()
|
|
359
|
+
if not code:
|
|
360
|
+
click.echo("Provide --code or --file", err=True)
|
|
361
|
+
raise SystemExit(1)
|
|
362
|
+
|
|
363
|
+
client = _client(ctx)
|
|
364
|
+
confirmation_token = None
|
|
365
|
+
if do_confirm:
|
|
366
|
+
confirm_data = client.confirm_write()
|
|
367
|
+
confirmation_token = confirm_data.get("result", {}).get("confirmation_token")
|
|
368
|
+
|
|
369
|
+
data = client.execute_workflow(
|
|
370
|
+
code=code,
|
|
371
|
+
timeout=timeout,
|
|
372
|
+
confirmed=do_confirm,
|
|
373
|
+
confirmation_token=confirmation_token,
|
|
374
|
+
)
|
|
375
|
+
_out(ctx,
|
|
376
|
+
yaml_fn=lambda: output.yaml_execute_result(data),
|
|
377
|
+
table_fn=lambda: output.table_execute_result(data),
|
|
378
|
+
data=data)
|
|
379
|
+
|
|
380
|
+
|
|
381
|
+
# ---------------------------------------------------------------------------
|
|
382
|
+
# venn help
|
|
383
|
+
# ---------------------------------------------------------------------------
|
|
384
|
+
|
|
385
|
+
@main.command("help")
|
|
386
|
+
@click.argument("action", required=False, type=click.Choice(
|
|
387
|
+
["getting_started", "connector_help", "auth_helper", "list_servers"],
|
|
388
|
+
case_sensitive=False,
|
|
389
|
+
))
|
|
390
|
+
@click.option("--server-id", "-s", help="Server ID (required for auth_helper)")
|
|
391
|
+
@click.option("--refresh", is_flag=True, help="Bypass cache")
|
|
392
|
+
@click.pass_context
|
|
393
|
+
def help_cmd(ctx, action, server_id, refresh):
|
|
394
|
+
"""Get help, server status, or auth URLs."""
|
|
395
|
+
data = _client(ctx).help(action=action, server_id=server_id, refresh=refresh)
|
|
396
|
+
result = data.get("result")
|
|
397
|
+
_out(ctx,
|
|
398
|
+
yaml_fn=lambda: output.yaml_help_result(result),
|
|
399
|
+
table_fn=lambda: output.table_help_result(result),
|
|
400
|
+
data=data)
|
|
401
|
+
|
|
402
|
+
|
|
403
|
+
# ---------------------------------------------------------------------------
|
|
404
|
+
# venn skills
|
|
405
|
+
# ---------------------------------------------------------------------------
|
|
406
|
+
|
|
407
|
+
@main.group()
|
|
408
|
+
def skills():
|
|
409
|
+
"""Manage executable skills."""
|
|
410
|
+
|
|
411
|
+
|
|
412
|
+
@skills.command("upsert")
|
|
413
|
+
@click.option("--file", "-f", "skill_file", required=True, type=click.Path(exists=True), help="Skill YAML file")
|
|
414
|
+
@click.pass_context
|
|
415
|
+
def skills_upsert(ctx, skill_file):
|
|
416
|
+
"""Create or update a skill from a YAML file."""
|
|
417
|
+
content = Path(skill_file).read_text()
|
|
418
|
+
data = _client(ctx).upsert_skill(content)
|
|
419
|
+
_out(ctx,
|
|
420
|
+
yaml_fn=lambda: output.yaml_skill_upsert(data),
|
|
421
|
+
table_fn=lambda: output.table_skill_upsert(data),
|
|
422
|
+
data=data)
|
|
423
|
+
|
|
424
|
+
|
|
425
|
+
# ---------------------------------------------------------------------------
|
|
426
|
+
# venn history
|
|
427
|
+
# ---------------------------------------------------------------------------
|
|
428
|
+
|
|
429
|
+
@main.group()
|
|
430
|
+
def history():
|
|
431
|
+
"""View catalog and server revision history."""
|
|
432
|
+
|
|
433
|
+
|
|
434
|
+
@history.command("toolset")
|
|
435
|
+
@click.argument("toolset_key")
|
|
436
|
+
@click.option("--limit", "-n", default=50, type=int, help="Max revisions")
|
|
437
|
+
@click.option("--before", "before_version", default=None, type=int, help="Only show versions before this")
|
|
438
|
+
@click.pass_context
|
|
439
|
+
def history_toolset(ctx, toolset_key, limit, before_version):
|
|
440
|
+
"""Show revision history for a toolset."""
|
|
441
|
+
data = _client(ctx).toolset_history(toolset_key, limit=limit, before_version=before_version)
|
|
442
|
+
result = data.get("result", {})
|
|
443
|
+
_out(ctx,
|
|
444
|
+
yaml_fn=lambda: output.yaml_history(result),
|
|
445
|
+
table_fn=lambda: output.table_history(result),
|
|
446
|
+
data=data)
|
|
447
|
+
|
|
448
|
+
|
|
449
|
+
@history.command("server")
|
|
450
|
+
@click.argument("directory_id")
|
|
451
|
+
@click.option("--limit", "-n", default=50, type=int, help="Max revisions")
|
|
452
|
+
@click.option("--before", "before_version", default=None, type=int, help="Only show versions before this")
|
|
453
|
+
@click.pass_context
|
|
454
|
+
def history_server(ctx, directory_id, limit, before_version):
|
|
455
|
+
"""Show revision history for a server instance."""
|
|
456
|
+
data = _client(ctx).server_history(directory_id, limit=limit, before_version=before_version)
|
|
457
|
+
result = data.get("result", {})
|
|
458
|
+
_out(ctx,
|
|
459
|
+
yaml_fn=lambda: output.yaml_history(result),
|
|
460
|
+
table_fn=lambda: output.table_history(result),
|
|
461
|
+
data=data)
|
|
@@ -14,7 +14,11 @@ class VennClient:
|
|
|
14
14
|
def __init__(self, api_key: str, base_url: str = BASE_URL, timeout: float = 120):
|
|
15
15
|
self._http = httpx.Client(
|
|
16
16
|
base_url=base_url,
|
|
17
|
-
headers={
|
|
17
|
+
headers={
|
|
18
|
+
"Authorization": f"Bearer {api_key}",
|
|
19
|
+
"User-Agent": "venn-cli/1.0",
|
|
20
|
+
"Accept": "*/*",
|
|
21
|
+
},
|
|
18
22
|
timeout=timeout,
|
|
19
23
|
)
|
|
20
24
|
|