trustgraph-cli 2.4.4__tar.gz → 2.4.6__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 (103) hide show
  1. {trustgraph_cli-2.4.4 → trustgraph_cli-2.4.6}/PKG-INFO +1 -1
  2. {trustgraph_cli-2.4.4 → trustgraph_cli-2.4.6}/pyproject.toml +14 -0
  3. trustgraph_cli-2.4.6/trustgraph/cli/_iam.py +75 -0
  4. trustgraph_cli-2.4.6/trustgraph/cli/bootstrap_iam.py +94 -0
  5. trustgraph_cli-2.4.6/trustgraph/cli/change_password.py +46 -0
  6. trustgraph_cli-2.4.6/trustgraph/cli/create_api_key.py +71 -0
  7. trustgraph_cli-2.4.6/trustgraph/cli/create_user.py +87 -0
  8. trustgraph_cli-2.4.6/trustgraph/cli/create_workspace.py +46 -0
  9. trustgraph_cli-2.4.6/trustgraph/cli/delete_user.py +62 -0
  10. trustgraph_cli-2.4.6/trustgraph/cli/disable_user.py +45 -0
  11. trustgraph_cli-2.4.6/trustgraph/cli/enable_user.py +45 -0
  12. trustgraph_cli-2.4.6/trustgraph/cli/list_api_keys.py +69 -0
  13. trustgraph_cli-2.4.6/trustgraph/cli/list_users.py +65 -0
  14. trustgraph_cli-2.4.6/trustgraph/cli/list_workspaces.py +53 -0
  15. trustgraph_cli-2.4.6/trustgraph/cli/login.py +62 -0
  16. trustgraph_cli-2.4.6/trustgraph/cli/reset_password.py +54 -0
  17. trustgraph_cli-2.4.6/trustgraph/cli/revoke_api_key.py +44 -0
  18. trustgraph_cli-2.4.6/trustgraph/cli_version.py +1 -0
  19. {trustgraph_cli-2.4.4 → trustgraph_cli-2.4.6}/trustgraph_cli.egg-info/PKG-INFO +1 -1
  20. {trustgraph_cli-2.4.4 → trustgraph_cli-2.4.6}/trustgraph_cli.egg-info/SOURCES.txt +15 -0
  21. {trustgraph_cli-2.4.4 → trustgraph_cli-2.4.6}/trustgraph_cli.egg-info/entry_points.txt +14 -0
  22. trustgraph_cli-2.4.4/trustgraph/cli_version.py +0 -1
  23. {trustgraph_cli-2.4.4 → trustgraph_cli-2.4.6}/README.md +0 -0
  24. {trustgraph_cli-2.4.4 → trustgraph_cli-2.4.6}/setup.cfg +0 -0
  25. {trustgraph_cli-2.4.4 → trustgraph_cli-2.4.6}/trustgraph/cli/__init__.py +0 -0
  26. {trustgraph_cli-2.4.4 → trustgraph_cli-2.4.6}/trustgraph/cli/add_library_document.py +0 -0
  27. {trustgraph_cli-2.4.4 → trustgraph_cli-2.4.6}/trustgraph/cli/delete_collection.py +0 -0
  28. {trustgraph_cli-2.4.4 → trustgraph_cli-2.4.6}/trustgraph/cli/delete_config_item.py +0 -0
  29. {trustgraph_cli-2.4.4 → trustgraph_cli-2.4.6}/trustgraph/cli/delete_flow_blueprint.py +0 -0
  30. {trustgraph_cli-2.4.4 → trustgraph_cli-2.4.6}/trustgraph/cli/delete_kg_core.py +0 -0
  31. {trustgraph_cli-2.4.4 → trustgraph_cli-2.4.6}/trustgraph/cli/delete_mcp_tool.py +0 -0
  32. {trustgraph_cli-2.4.4 → trustgraph_cli-2.4.6}/trustgraph/cli/delete_tool.py +0 -0
  33. {trustgraph_cli-2.4.4 → trustgraph_cli-2.4.6}/trustgraph/cli/dump_msgpack.py +0 -0
  34. {trustgraph_cli-2.4.4 → trustgraph_cli-2.4.6}/trustgraph/cli/dump_queues.py +0 -0
  35. {trustgraph_cli-2.4.4 → trustgraph_cli-2.4.6}/trustgraph/cli/export_workspace_config.py +0 -0
  36. {trustgraph_cli-2.4.4 → trustgraph_cli-2.4.6}/trustgraph/cli/get_config_item.py +0 -0
  37. {trustgraph_cli-2.4.4 → trustgraph_cli-2.4.6}/trustgraph/cli/get_document_content.py +0 -0
  38. {trustgraph_cli-2.4.4 → trustgraph_cli-2.4.6}/trustgraph/cli/get_flow_blueprint.py +0 -0
  39. {trustgraph_cli-2.4.4 → trustgraph_cli-2.4.6}/trustgraph/cli/get_kg_core.py +0 -0
  40. {trustgraph_cli-2.4.4 → trustgraph_cli-2.4.6}/trustgraph/cli/graph_to_turtle.py +0 -0
  41. {trustgraph_cli-2.4.4 → trustgraph_cli-2.4.6}/trustgraph/cli/import_workspace_config.py +0 -0
  42. {trustgraph_cli-2.4.4 → trustgraph_cli-2.4.6}/trustgraph/cli/init_pulsar_manager.py +0 -0
  43. {trustgraph_cli-2.4.4 → trustgraph_cli-2.4.6}/trustgraph/cli/invoke_agent.py +0 -0
  44. {trustgraph_cli-2.4.4 → trustgraph_cli-2.4.6}/trustgraph/cli/invoke_document_embeddings.py +0 -0
  45. {trustgraph_cli-2.4.4 → trustgraph_cli-2.4.6}/trustgraph/cli/invoke_document_rag.py +0 -0
  46. {trustgraph_cli-2.4.4 → trustgraph_cli-2.4.6}/trustgraph/cli/invoke_embeddings.py +0 -0
  47. {trustgraph_cli-2.4.4 → trustgraph_cli-2.4.6}/trustgraph/cli/invoke_graph_embeddings.py +0 -0
  48. {trustgraph_cli-2.4.4 → trustgraph_cli-2.4.6}/trustgraph/cli/invoke_graph_rag.py +0 -0
  49. {trustgraph_cli-2.4.4 → trustgraph_cli-2.4.6}/trustgraph/cli/invoke_llm.py +0 -0
  50. {trustgraph_cli-2.4.4 → trustgraph_cli-2.4.6}/trustgraph/cli/invoke_mcp_tool.py +0 -0
  51. {trustgraph_cli-2.4.4 → trustgraph_cli-2.4.6}/trustgraph/cli/invoke_nlp_query.py +0 -0
  52. {trustgraph_cli-2.4.4 → trustgraph_cli-2.4.6}/trustgraph/cli/invoke_prompt.py +0 -0
  53. {trustgraph_cli-2.4.4 → trustgraph_cli-2.4.6}/trustgraph/cli/invoke_row_embeddings.py +0 -0
  54. {trustgraph_cli-2.4.4 → trustgraph_cli-2.4.6}/trustgraph/cli/invoke_rows_query.py +0 -0
  55. {trustgraph_cli-2.4.4 → trustgraph_cli-2.4.6}/trustgraph/cli/invoke_sparql_query.py +0 -0
  56. {trustgraph_cli-2.4.4 → trustgraph_cli-2.4.6}/trustgraph/cli/invoke_structured_query.py +0 -0
  57. {trustgraph_cli-2.4.4 → trustgraph_cli-2.4.6}/trustgraph/cli/list_collections.py +0 -0
  58. {trustgraph_cli-2.4.4 → trustgraph_cli-2.4.6}/trustgraph/cli/list_config_items.py +0 -0
  59. {trustgraph_cli-2.4.4 → trustgraph_cli-2.4.6}/trustgraph/cli/list_explain_traces.py +0 -0
  60. {trustgraph_cli-2.4.4 → trustgraph_cli-2.4.6}/trustgraph/cli/load_doc_embeds.py +0 -0
  61. {trustgraph_cli-2.4.4 → trustgraph_cli-2.4.6}/trustgraph/cli/load_kg_core.py +0 -0
  62. {trustgraph_cli-2.4.4 → trustgraph_cli-2.4.6}/trustgraph/cli/load_knowledge.py +0 -0
  63. {trustgraph_cli-2.4.4 → trustgraph_cli-2.4.6}/trustgraph/cli/load_sample_documents.py +0 -0
  64. {trustgraph_cli-2.4.4 → trustgraph_cli-2.4.6}/trustgraph/cli/load_structured_data.py +0 -0
  65. {trustgraph_cli-2.4.4 → trustgraph_cli-2.4.6}/trustgraph/cli/load_turtle.py +0 -0
  66. {trustgraph_cli-2.4.4 → trustgraph_cli-2.4.6}/trustgraph/cli/monitor_prompts.py +0 -0
  67. {trustgraph_cli-2.4.4 → trustgraph_cli-2.4.6}/trustgraph/cli/put_config_item.py +0 -0
  68. {trustgraph_cli-2.4.4 → trustgraph_cli-2.4.6}/trustgraph/cli/put_flow_blueprint.py +0 -0
  69. {trustgraph_cli-2.4.4 → trustgraph_cli-2.4.6}/trustgraph/cli/put_kg_core.py +0 -0
  70. {trustgraph_cli-2.4.4 → trustgraph_cli-2.4.6}/trustgraph/cli/query_graph.py +0 -0
  71. {trustgraph_cli-2.4.4 → trustgraph_cli-2.4.6}/trustgraph/cli/remove_library_document.py +0 -0
  72. {trustgraph_cli-2.4.4 → trustgraph_cli-2.4.6}/trustgraph/cli/save_doc_embeds.py +0 -0
  73. {trustgraph_cli-2.4.4 → trustgraph_cli-2.4.6}/trustgraph/cli/set_collection.py +0 -0
  74. {trustgraph_cli-2.4.4 → trustgraph_cli-2.4.6}/trustgraph/cli/set_mcp_tool.py +0 -0
  75. {trustgraph_cli-2.4.4 → trustgraph_cli-2.4.6}/trustgraph/cli/set_prompt.py +0 -0
  76. {trustgraph_cli-2.4.4 → trustgraph_cli-2.4.6}/trustgraph/cli/set_token_costs.py +0 -0
  77. {trustgraph_cli-2.4.4 → trustgraph_cli-2.4.6}/trustgraph/cli/set_tool.py +0 -0
  78. {trustgraph_cli-2.4.4 → trustgraph_cli-2.4.6}/trustgraph/cli/show_config.py +0 -0
  79. {trustgraph_cli-2.4.4 → trustgraph_cli-2.4.6}/trustgraph/cli/show_explain_trace.py +0 -0
  80. {trustgraph_cli-2.4.4 → trustgraph_cli-2.4.6}/trustgraph/cli/show_extraction_provenance.py +0 -0
  81. {trustgraph_cli-2.4.4 → trustgraph_cli-2.4.6}/trustgraph/cli/show_flow_blueprints.py +0 -0
  82. {trustgraph_cli-2.4.4 → trustgraph_cli-2.4.6}/trustgraph/cli/show_flow_state.py +0 -0
  83. {trustgraph_cli-2.4.4 → trustgraph_cli-2.4.6}/trustgraph/cli/show_flows.py +0 -0
  84. {trustgraph_cli-2.4.4 → trustgraph_cli-2.4.6}/trustgraph/cli/show_graph.py +0 -0
  85. {trustgraph_cli-2.4.4 → trustgraph_cli-2.4.6}/trustgraph/cli/show_kg_cores.py +0 -0
  86. {trustgraph_cli-2.4.4 → trustgraph_cli-2.4.6}/trustgraph/cli/show_library_documents.py +0 -0
  87. {trustgraph_cli-2.4.4 → trustgraph_cli-2.4.6}/trustgraph/cli/show_library_processing.py +0 -0
  88. {trustgraph_cli-2.4.4 → trustgraph_cli-2.4.6}/trustgraph/cli/show_mcp_tools.py +0 -0
  89. {trustgraph_cli-2.4.4 → trustgraph_cli-2.4.6}/trustgraph/cli/show_parameter_types.py +0 -0
  90. {trustgraph_cli-2.4.4 → trustgraph_cli-2.4.6}/trustgraph/cli/show_processor_state.py +0 -0
  91. {trustgraph_cli-2.4.4 → trustgraph_cli-2.4.6}/trustgraph/cli/show_prompts.py +0 -0
  92. {trustgraph_cli-2.4.4 → trustgraph_cli-2.4.6}/trustgraph/cli/show_token_costs.py +0 -0
  93. {trustgraph_cli-2.4.4 → trustgraph_cli-2.4.6}/trustgraph/cli/show_token_rate.py +0 -0
  94. {trustgraph_cli-2.4.4 → trustgraph_cli-2.4.6}/trustgraph/cli/show_tools.py +0 -0
  95. {trustgraph_cli-2.4.4 → trustgraph_cli-2.4.6}/trustgraph/cli/start_flow.py +0 -0
  96. {trustgraph_cli-2.4.4 → trustgraph_cli-2.4.6}/trustgraph/cli/start_library_processing.py +0 -0
  97. {trustgraph_cli-2.4.4 → trustgraph_cli-2.4.6}/trustgraph/cli/stop_flow.py +0 -0
  98. {trustgraph_cli-2.4.4 → trustgraph_cli-2.4.6}/trustgraph/cli/stop_library_processing.py +0 -0
  99. {trustgraph_cli-2.4.4 → trustgraph_cli-2.4.6}/trustgraph/cli/unload_kg_core.py +0 -0
  100. {trustgraph_cli-2.4.4 → trustgraph_cli-2.4.6}/trustgraph/cli/verify_system_status.py +0 -0
  101. {trustgraph_cli-2.4.4 → trustgraph_cli-2.4.6}/trustgraph_cli.egg-info/dependency_links.txt +0 -0
  102. {trustgraph_cli-2.4.4 → trustgraph_cli-2.4.6}/trustgraph_cli.egg-info/requires.txt +0 -0
  103. {trustgraph_cli-2.4.4 → trustgraph_cli-2.4.6}/trustgraph_cli.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: trustgraph-cli
3
- Version: 2.4.4
3
+ Version: 2.4.6
4
4
  Summary: TrustGraph provides a means to run a pipeline of flexible AI processing components in a flexible means to achieve a processing pipeline.
5
5
  Author-email: "trustgraph.ai" <security@trustgraph.ai>
6
6
  Project-URL: Homepage, https://github.com/trustgraph-ai/trustgraph
@@ -40,6 +40,20 @@ tg-get-flow-blueprint = "trustgraph.cli.get_flow_blueprint:main"
40
40
  tg-get-kg-core = "trustgraph.cli.get_kg_core:main"
41
41
  tg-get-document-content = "trustgraph.cli.get_document_content:main"
42
42
  tg-graph-to-turtle = "trustgraph.cli.graph_to_turtle:main"
43
+ tg-bootstrap-iam = "trustgraph.cli.bootstrap_iam:main"
44
+ tg-login = "trustgraph.cli.login:main"
45
+ tg-create-user = "trustgraph.cli.create_user:main"
46
+ tg-list-users = "trustgraph.cli.list_users:main"
47
+ tg-disable-user = "trustgraph.cli.disable_user:main"
48
+ tg-enable-user = "trustgraph.cli.enable_user:main"
49
+ tg-delete-user = "trustgraph.cli.delete_user:main"
50
+ tg-change-password = "trustgraph.cli.change_password:main"
51
+ tg-reset-password = "trustgraph.cli.reset_password:main"
52
+ tg-create-api-key = "trustgraph.cli.create_api_key:main"
53
+ tg-list-api-keys = "trustgraph.cli.list_api_keys:main"
54
+ tg-revoke-api-key = "trustgraph.cli.revoke_api_key:main"
55
+ tg-list-workspaces = "trustgraph.cli.list_workspaces:main"
56
+ tg-create-workspace = "trustgraph.cli.create_workspace:main"
43
57
  tg-invoke-agent = "trustgraph.cli.invoke_agent:main"
44
58
  tg-invoke-document-rag = "trustgraph.cli.invoke_document_rag:main"
45
59
  tg-invoke-graph-rag = "trustgraph.cli.invoke_graph_rag:main"
@@ -0,0 +1,75 @@
1
+ """
2
+ Shared helpers for IAM CLI tools.
3
+
4
+ All IAM operations go through the gateway's ``/api/v1/iam`` forwarder,
5
+ with the three public auth operations (``login``, ``bootstrap``,
6
+ ``change-password``) served via ``/api/v1/auth/...`` instead. These
7
+ helpers encapsulate the HTTP plumbing so each CLI can stay focused
8
+ on its own argument parsing and output formatting.
9
+ """
10
+
11
+ import json
12
+ import os
13
+ import sys
14
+
15
+ import requests
16
+
17
+
18
+ DEFAULT_URL = os.getenv("TRUSTGRAPH_URL", "http://localhost:8088/")
19
+ DEFAULT_TOKEN = os.getenv("TRUSTGRAPH_TOKEN", None)
20
+
21
+
22
+ def _fmt_error(resp_json):
23
+ err = resp_json.get("error", {})
24
+ if isinstance(err, dict):
25
+ t = err.get("type", "")
26
+ m = err.get("message", "")
27
+ return f"{t}: {m}" if t else m or "error"
28
+ return str(err)
29
+
30
+
31
+ def _post(url, path, token, body):
32
+ endpoint = url.rstrip("/") + path
33
+ headers = {"Content-Type": "application/json"}
34
+ if token:
35
+ headers["Authorization"] = f"Bearer {token}"
36
+
37
+ resp = requests.post(
38
+ endpoint, headers=headers, data=json.dumps(body),
39
+ )
40
+
41
+ if resp.status_code != 200:
42
+ try:
43
+ payload = resp.json()
44
+ detail = _fmt_error(payload)
45
+ except Exception:
46
+ detail = resp.text
47
+ raise RuntimeError(f"HTTP {resp.status_code}: {detail}")
48
+
49
+ body = resp.json()
50
+ if "error" in body:
51
+ raise RuntimeError(_fmt_error(body))
52
+ return body
53
+
54
+
55
+ def call_iam(url, token, request):
56
+ """Forward an IAM request through ``/api/v1/iam``. ``request`` is
57
+ the ``IamRequest`` dict shape."""
58
+ return _post(url, "/api/v1/iam", token, request)
59
+
60
+
61
+ def call_auth(url, path, token, body):
62
+ """Hit one of the public auth endpoints
63
+ (``/api/v1/auth/login``, ``/api/v1/auth/change-password``, etc.).
64
+ ``token`` is optional — login and bootstrap don't need one."""
65
+ return _post(url, path, token, body)
66
+
67
+
68
+ def run_main(fn, parser):
69
+ """Standard error-handling wrapper for CLI main() bodies."""
70
+ args = parser.parse_args()
71
+ try:
72
+ fn(args)
73
+ except Exception as e:
74
+ print("Exception:", e, file=sys.stderr, flush=True)
75
+ sys.exit(1)
@@ -0,0 +1,94 @@
1
+ """
2
+ Bootstraps the IAM service. Only works when iam-svc is running in
3
+ bootstrap mode with empty tables. Prints the initial admin API key
4
+ to stdout.
5
+
6
+ This is a one-time, trust-sensitive operation. The resulting token
7
+ is shown once and never again — capture it on use. Rotate and
8
+ revoke it as soon as a real admin API key has been issued.
9
+ """
10
+
11
+ import argparse
12
+ import json
13
+ import os
14
+ import sys
15
+
16
+ import requests
17
+
18
+ default_url = os.getenv("TRUSTGRAPH_URL", "http://localhost:8088/")
19
+
20
+
21
+ def bootstrap(url):
22
+
23
+ # Unauthenticated public endpoint — IAM refuses the bootstrap
24
+ # operation unless the service is running in bootstrap mode with
25
+ # empty tables, so the safety gate lives on the server side.
26
+ endpoint = url.rstrip("/") + "/api/v1/auth/bootstrap"
27
+
28
+ headers = {"Content-Type": "application/json"}
29
+
30
+ resp = requests.post(
31
+ endpoint,
32
+ headers=headers,
33
+ data=json.dumps({}),
34
+ )
35
+
36
+ if resp.status_code != 200:
37
+ raise RuntimeError(
38
+ f"HTTP {resp.status_code}: {resp.text}"
39
+ )
40
+
41
+ body = resp.json()
42
+
43
+ if "error" in body:
44
+ raise RuntimeError(
45
+ f"IAM {body['error'].get('type', 'error')}: "
46
+ f"{body['error'].get('message', '')}"
47
+ )
48
+
49
+ api_key = body.get("bootstrap_admin_api_key")
50
+ user_id = body.get("bootstrap_admin_user_id")
51
+
52
+ if not api_key:
53
+ raise RuntimeError(
54
+ "IAM response did not contain a bootstrap token — the "
55
+ "service may already be bootstrapped, or may be running "
56
+ "in token mode."
57
+ )
58
+
59
+ return user_id, api_key
60
+
61
+
62
+ def main():
63
+
64
+ parser = argparse.ArgumentParser(
65
+ prog="tg-bootstrap-iam",
66
+ description=__doc__,
67
+ )
68
+
69
+ parser.add_argument(
70
+ "-u", "--api-url",
71
+ default=default_url,
72
+ help=f"API URL (default: {default_url})",
73
+ )
74
+
75
+ args = parser.parse_args()
76
+
77
+ try:
78
+ user_id, api_key = bootstrap(args.api_url)
79
+ except Exception as e:
80
+ print("Exception:", e, file=sys.stderr, flush=True)
81
+ sys.exit(1)
82
+
83
+ # Stdout gets machine-readable output (the key). Any operator
84
+ # context goes to stderr.
85
+ print(f"Admin user id: {user_id}", file=sys.stderr)
86
+ print(
87
+ "Admin API key (shown once, capture now):",
88
+ file=sys.stderr,
89
+ )
90
+ print(api_key)
91
+
92
+
93
+ if __name__ == "__main__":
94
+ main()
@@ -0,0 +1,46 @@
1
+ """
2
+ Change your own password. Requires the current password.
3
+ """
4
+
5
+ import argparse
6
+ import getpass
7
+
8
+ from ._iam import DEFAULT_URL, DEFAULT_TOKEN, call_auth, run_main
9
+
10
+
11
+ def do_change_password(args):
12
+ current = args.current or getpass.getpass("Current password: ")
13
+ new = args.new or getpass.getpass("New password: ")
14
+
15
+ call_auth(
16
+ args.api_url, "/api/v1/auth/change-password", args.token,
17
+ {"current_password": current, "new_password": new},
18
+ )
19
+ print("Password changed.")
20
+
21
+
22
+ def main():
23
+ parser = argparse.ArgumentParser(
24
+ prog="tg-change-password", description=__doc__,
25
+ )
26
+ parser.add_argument(
27
+ "-u", "--api-url", default=DEFAULT_URL,
28
+ help=f"API URL (default: {DEFAULT_URL})",
29
+ )
30
+ parser.add_argument(
31
+ "-t", "--token", default=DEFAULT_TOKEN,
32
+ help="Auth token (default: $TRUSTGRAPH_TOKEN)",
33
+ )
34
+ parser.add_argument(
35
+ "--current", default=None,
36
+ help="Current password (prompted if omitted)",
37
+ )
38
+ parser.add_argument(
39
+ "--new", default=None,
40
+ help="New password (prompted if omitted)",
41
+ )
42
+ run_main(do_change_password, parser)
43
+
44
+
45
+ if __name__ == "__main__":
46
+ main()
@@ -0,0 +1,71 @@
1
+ """
2
+ Create an API key for a user. Prints the plaintext key to stdout —
3
+ shown once only.
4
+ """
5
+
6
+ import argparse
7
+ import sys
8
+
9
+ from ._iam import DEFAULT_URL, DEFAULT_TOKEN, call_iam, run_main
10
+
11
+
12
+ def do_create_api_key(args):
13
+ key = {
14
+ "user_id": args.user_id,
15
+ "name": args.name,
16
+ }
17
+ if args.expires:
18
+ key["expires"] = args.expires
19
+
20
+ req = {"operation": "create-api-key", "key": key}
21
+ if args.workspace:
22
+ req["workspace"] = args.workspace
23
+ resp = call_iam(args.api_url, args.token, req)
24
+
25
+ plaintext = resp.get("api_key_plaintext", "")
26
+ rec = resp.get("api_key", {})
27
+ print(f"Key id: {rec.get('id', '')}", file=sys.stderr)
28
+ print(f"Name: {rec.get('name', '')}", file=sys.stderr)
29
+ print(f"Prefix: {rec.get('prefix', '')}", file=sys.stderr)
30
+ print(
31
+ "API key (shown once, capture now):", file=sys.stderr,
32
+ )
33
+ print(plaintext)
34
+
35
+
36
+ def main():
37
+ parser = argparse.ArgumentParser(
38
+ prog="tg-create-api-key", description=__doc__,
39
+ )
40
+ parser.add_argument(
41
+ "-u", "--api-url", default=DEFAULT_URL,
42
+ help=f"API URL (default: {DEFAULT_URL})",
43
+ )
44
+ parser.add_argument(
45
+ "-t", "--token", default=DEFAULT_TOKEN,
46
+ help="Auth token (default: $TRUSTGRAPH_TOKEN)",
47
+ )
48
+ parser.add_argument(
49
+ "--user-id", required=True,
50
+ help="Owner user id",
51
+ )
52
+ parser.add_argument(
53
+ "--name", required=True,
54
+ help="Operator-facing label (e.g. 'laptop', 'ci')",
55
+ )
56
+ parser.add_argument(
57
+ "--expires", default=None,
58
+ help="ISO-8601 expiry (optional; empty = no expiry)",
59
+ )
60
+ parser.add_argument(
61
+ "-w", "--workspace", default=None,
62
+ help=(
63
+ "Target workspace (admin only; defaults to caller's "
64
+ "assigned workspace)"
65
+ ),
66
+ )
67
+ run_main(do_create_api_key, parser)
68
+
69
+
70
+ if __name__ == "__main__":
71
+ main()
@@ -0,0 +1,87 @@
1
+ """
2
+ Create a user in the caller's workspace. Prints the new user id.
3
+ """
4
+
5
+ import argparse
6
+ import getpass
7
+ import sys
8
+
9
+ from ._iam import DEFAULT_URL, DEFAULT_TOKEN, call_iam, run_main
10
+
11
+
12
+ def do_create_user(args):
13
+ password = args.password
14
+ if not password:
15
+ password = getpass.getpass(
16
+ f"Password for new user {args.username}: "
17
+ )
18
+
19
+ user = {
20
+ "username": args.username,
21
+ "password": password,
22
+ "roles": args.roles,
23
+ }
24
+ if args.name:
25
+ user["name"] = args.name
26
+ if args.email:
27
+ user["email"] = args.email
28
+ if args.must_change_password:
29
+ user["must_change_password"] = True
30
+
31
+ req = {"operation": "create-user", "user": user}
32
+ if args.workspace:
33
+ req["workspace"] = args.workspace
34
+ resp = call_iam(args.api_url, args.token, req)
35
+
36
+ rec = resp.get("user", {})
37
+ print(f"User id: {rec.get('id', '')}", file=sys.stderr)
38
+ print(f"Username: {rec.get('username', '')}", file=sys.stderr)
39
+ print(f"Roles: {', '.join(rec.get('roles', []))}", file=sys.stderr)
40
+ print(rec.get("id", ""))
41
+
42
+
43
+ def main():
44
+ parser = argparse.ArgumentParser(
45
+ prog="tg-create-user", description=__doc__,
46
+ )
47
+ parser.add_argument(
48
+ "-u", "--api-url", default=DEFAULT_URL,
49
+ help=f"API URL (default: {DEFAULT_URL})",
50
+ )
51
+ parser.add_argument(
52
+ "-t", "--token", default=DEFAULT_TOKEN,
53
+ help="Auth token (default: $TRUSTGRAPH_TOKEN)",
54
+ )
55
+ parser.add_argument(
56
+ "--username", required=True, help="Username (unique in workspace)",
57
+ )
58
+ parser.add_argument(
59
+ "--password", default=None,
60
+ help="Password (prompted if omitted)",
61
+ )
62
+ parser.add_argument(
63
+ "--name", default=None, help="Display name",
64
+ )
65
+ parser.add_argument(
66
+ "--email", default=None, help="Email",
67
+ )
68
+ parser.add_argument(
69
+ "--roles", nargs="+", default=["reader"],
70
+ help="One or more role names (default: reader)",
71
+ )
72
+ parser.add_argument(
73
+ "--must-change-password", action="store_true",
74
+ help="Force password change on next login",
75
+ )
76
+ parser.add_argument(
77
+ "-w", "--workspace", default=None,
78
+ help=(
79
+ "Target workspace (admin only; defaults to caller's "
80
+ "assigned workspace)"
81
+ ),
82
+ )
83
+ run_main(do_create_user, parser)
84
+
85
+
86
+ if __name__ == "__main__":
87
+ main()
@@ -0,0 +1,46 @@
1
+ """
2
+ Create a workspace (system-level; requires admin).
3
+ """
4
+
5
+ import argparse
6
+
7
+ from ._iam import DEFAULT_URL, DEFAULT_TOKEN, call_iam, run_main
8
+
9
+
10
+ def do_create_workspace(args):
11
+ ws = {"id": args.workspace_id, "enabled": True}
12
+ if args.name:
13
+ ws["name"] = args.name
14
+
15
+ resp = call_iam(args.api_url, args.token, {
16
+ "operation": "create-workspace",
17
+ "workspace_record": ws,
18
+ })
19
+ rec = resp.get("workspace", {})
20
+ print(f"Workspace created: {rec.get('id', '')}")
21
+
22
+
23
+ def main():
24
+ parser = argparse.ArgumentParser(
25
+ prog="tg-create-workspace", description=__doc__,
26
+ )
27
+ parser.add_argument(
28
+ "-u", "--api-url", default=DEFAULT_URL,
29
+ help=f"API URL (default: {DEFAULT_URL})",
30
+ )
31
+ parser.add_argument(
32
+ "-t", "--token", default=DEFAULT_TOKEN,
33
+ help="Auth token (default: $TRUSTGRAPH_TOKEN)",
34
+ )
35
+ parser.add_argument(
36
+ "--workspace-id", required=True,
37
+ help="New workspace id (must not start with '_')",
38
+ )
39
+ parser.add_argument(
40
+ "--name", default=None, help="Display name",
41
+ )
42
+ run_main(do_create_workspace, parser)
43
+
44
+
45
+ if __name__ == "__main__":
46
+ main()
@@ -0,0 +1,62 @@
1
+ """
2
+ Delete a user. Removes the user record, their username lookup,
3
+ and all their API keys. The freed username becomes available for
4
+ re-use.
5
+
6
+ Irreversible. Use tg-disable-user if you want to preserve the
7
+ record (audit trail, username squatting protection).
8
+ """
9
+
10
+ import argparse
11
+
12
+ from ._iam import DEFAULT_URL, DEFAULT_TOKEN, call_iam, run_main
13
+
14
+
15
+ def do_delete_user(args):
16
+ if not args.yes:
17
+ confirm = input(
18
+ f"Delete user {args.user_id}? This is irreversible. "
19
+ f"[type 'yes' to confirm]: "
20
+ )
21
+ if confirm.strip() != "yes":
22
+ print("Aborted.")
23
+ return
24
+
25
+ req = {"operation": "delete-user", "user_id": args.user_id}
26
+ if args.workspace:
27
+ req["workspace"] = args.workspace
28
+ call_iam(args.api_url, args.token, req)
29
+ print(f"Deleted user {args.user_id}")
30
+
31
+
32
+ def main():
33
+ parser = argparse.ArgumentParser(
34
+ prog="tg-delete-user", description=__doc__,
35
+ )
36
+ parser.add_argument(
37
+ "-u", "--api-url", default=DEFAULT_URL,
38
+ help=f"API URL (default: {DEFAULT_URL})",
39
+ )
40
+ parser.add_argument(
41
+ "-t", "--token", default=DEFAULT_TOKEN,
42
+ help="Auth token (default: $TRUSTGRAPH_TOKEN)",
43
+ )
44
+ parser.add_argument(
45
+ "--user-id", required=True, help="User id to delete",
46
+ )
47
+ parser.add_argument(
48
+ "-w", "--workspace", default=None,
49
+ help=(
50
+ "Target workspace (admin only; defaults to caller's "
51
+ "assigned workspace)"
52
+ ),
53
+ )
54
+ parser.add_argument(
55
+ "--yes", action="store_true",
56
+ help="Skip the interactive confirmation prompt",
57
+ )
58
+ run_main(do_delete_user, parser)
59
+
60
+
61
+ if __name__ == "__main__":
62
+ main()
@@ -0,0 +1,45 @@
1
+ """
2
+ Disable a user. Soft-deletes (enabled=false) and revokes all their
3
+ API keys.
4
+ """
5
+
6
+ import argparse
7
+
8
+ from ._iam import DEFAULT_URL, DEFAULT_TOKEN, call_iam, run_main
9
+
10
+
11
+ def do_disable_user(args):
12
+ req = {"operation": "disable-user", "user_id": args.user_id}
13
+ if args.workspace:
14
+ req["workspace"] = args.workspace
15
+ call_iam(args.api_url, args.token, req)
16
+ print(f"Disabled user {args.user_id}")
17
+
18
+
19
+ def main():
20
+ parser = argparse.ArgumentParser(
21
+ prog="tg-disable-user", description=__doc__,
22
+ )
23
+ parser.add_argument(
24
+ "-u", "--api-url", default=DEFAULT_URL,
25
+ help=f"API URL (default: {DEFAULT_URL})",
26
+ )
27
+ parser.add_argument(
28
+ "-t", "--token", default=DEFAULT_TOKEN,
29
+ help="Auth token (default: $TRUSTGRAPH_TOKEN)",
30
+ )
31
+ parser.add_argument(
32
+ "--user-id", required=True, help="User id to disable",
33
+ )
34
+ parser.add_argument(
35
+ "-w", "--workspace", default=None,
36
+ help=(
37
+ "Target workspace (admin only; defaults to caller's "
38
+ "assigned workspace)"
39
+ ),
40
+ )
41
+ run_main(do_disable_user, parser)
42
+
43
+
44
+ if __name__ == "__main__":
45
+ main()
@@ -0,0 +1,45 @@
1
+ """
2
+ Re-enable a previously disabled user. Does not restore their API
3
+ keys — those must be re-issued by an admin.
4
+ """
5
+
6
+ import argparse
7
+
8
+ from ._iam import DEFAULT_URL, DEFAULT_TOKEN, call_iam, run_main
9
+
10
+
11
+ def do_enable_user(args):
12
+ req = {"operation": "enable-user", "user_id": args.user_id}
13
+ if args.workspace:
14
+ req["workspace"] = args.workspace
15
+ call_iam(args.api_url, args.token, req)
16
+ print(f"Enabled user {args.user_id}")
17
+
18
+
19
+ def main():
20
+ parser = argparse.ArgumentParser(
21
+ prog="tg-enable-user", description=__doc__,
22
+ )
23
+ parser.add_argument(
24
+ "-u", "--api-url", default=DEFAULT_URL,
25
+ help=f"API URL (default: {DEFAULT_URL})",
26
+ )
27
+ parser.add_argument(
28
+ "-t", "--token", default=DEFAULT_TOKEN,
29
+ help="Auth token (default: $TRUSTGRAPH_TOKEN)",
30
+ )
31
+ parser.add_argument(
32
+ "--user-id", required=True, help="User id to enable",
33
+ )
34
+ parser.add_argument(
35
+ "-w", "--workspace", default=None,
36
+ help=(
37
+ "Target workspace (admin only; defaults to caller's "
38
+ "assigned workspace)"
39
+ ),
40
+ )
41
+ run_main(do_enable_user, parser)
42
+
43
+
44
+ if __name__ == "__main__":
45
+ main()
@@ -0,0 +1,69 @@
1
+ """
2
+ List the API keys for a user.
3
+ """
4
+
5
+ import argparse
6
+
7
+ import tabulate
8
+
9
+ from ._iam import DEFAULT_URL, DEFAULT_TOKEN, call_iam, run_main
10
+
11
+
12
+ def do_list_api_keys(args):
13
+ req = {"operation": "list-api-keys", "user_id": args.user_id}
14
+ if args.workspace:
15
+ req["workspace"] = args.workspace
16
+ resp = call_iam(args.api_url, args.token, req)
17
+
18
+ keys = resp.get("api_keys", [])
19
+ if not keys:
20
+ print("No keys.")
21
+ return
22
+
23
+ rows = [
24
+ [
25
+ k.get("id", ""),
26
+ k.get("name", ""),
27
+ k.get("prefix", ""),
28
+ k.get("created", ""),
29
+ k.get("last_used", "") or "—",
30
+ k.get("expires", "") or "never",
31
+ ]
32
+ for k in keys
33
+ ]
34
+ print(tabulate.tabulate(
35
+ rows,
36
+ headers=["id", "name", "prefix", "created", "last used", "expires"],
37
+ tablefmt="pretty",
38
+ stralign="left",
39
+ ))
40
+
41
+
42
+ def main():
43
+ parser = argparse.ArgumentParser(
44
+ prog="tg-list-api-keys", description=__doc__,
45
+ )
46
+ parser.add_argument(
47
+ "-u", "--api-url", default=DEFAULT_URL,
48
+ help=f"API URL (default: {DEFAULT_URL})",
49
+ )
50
+ parser.add_argument(
51
+ "-t", "--token", default=DEFAULT_TOKEN,
52
+ help="Auth token (default: $TRUSTGRAPH_TOKEN)",
53
+ )
54
+ parser.add_argument(
55
+ "--user-id", required=True,
56
+ help="Owner user id",
57
+ )
58
+ parser.add_argument(
59
+ "-w", "--workspace", default=None,
60
+ help=(
61
+ "Target workspace (admin only; defaults to caller's "
62
+ "assigned workspace)"
63
+ ),
64
+ )
65
+ run_main(do_list_api_keys, parser)
66
+
67
+
68
+ if __name__ == "__main__":
69
+ main()