@macaw-ai/client 0.0.0-stage → 0.9.11

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 (142) hide show
  1. package/LICENSE.md +339 -0
  2. package/bin/install.js +422 -0
  3. package/package.json +37 -4
  4. package/payload/README.md +56 -0
  5. package/payload/secureAI/LICENSE +190 -0
  6. package/payload/secureAI/NOTICE +5 -0
  7. package/payload/secureAI/demos/policy-controls/1_uncontrolled.py +139 -0
  8. package/payload/secureAI/demos/policy-controls/2_with_macaw.py +139 -0
  9. package/payload/secureAI/demos/policy-controls/3_with_user_policies.py +162 -0
  10. package/payload/secureAI/demos/policy-controls/credentials.txt +12 -0
  11. package/payload/secureAI/demos/policy-controls/malicious_report.txt +21 -0
  12. package/payload/secureAI/demos/policy-controls/sales_report.txt +20 -0
  13. package/payload/secureAI/demos/tutorial-1-rbac/README.md +143 -0
  14. package/payload/secureAI/demos/tutorial-1-rbac/app.py +147 -0
  15. package/payload/secureAI/demos/tutorial-1-rbac/authprompts_demo.py +306 -0
  16. package/payload/secureAI/demos/tutorial-1-rbac/config/claims-config.yaml +152 -0
  17. package/payload/secureAI/demos/tutorial-1-rbac/demo_secureopenai_with_policies.py +150 -0
  18. package/payload/secureAI/demos/tutorial-1-rbac/policies/bu_Analytics.json +8 -0
  19. package/payload/secureAI/demos/tutorial-1-rbac/policies/company_FinTech_Corp.json +70 -0
  20. package/payload/secureAI/demos/tutorial-1-rbac/policies/llm-base.json +69 -0
  21. package/payload/secureAI/demos/tutorial-1-rbac/policies/team_Reporting.json +8 -0
  22. package/payload/secureAI/demos/tutorial-1-rbac/policies/user_alice.json +32 -0
  23. package/payload/secureAI/demos/tutorial-1-rbac/policies/user_bob.json +28 -0
  24. package/payload/secureAI/demos/tutorial-1-rbac/policies/user_carol.json +34 -0
  25. package/payload/secureAI/demos/tutorial-1-rbac/requirements.txt +6 -0
  26. package/payload/secureAI/demos/tutorial-1-rbac/setup/auth0_complete_setup.sh +392 -0
  27. package/payload/secureAI/demos/tutorial-1-rbac/setup/keycloak_complete_setup.sh +320 -0
  28. package/payload/secureAI/demos/tutorial-2-intent/Policies/alation-remote-proxy.json +155 -0
  29. package/payload/secureAI/demos/tutorial-2-intent/Policies/bu.json +22 -0
  30. package/payload/secureAI/demos/tutorial-2-intent/Policies/company.json +17 -0
  31. package/payload/secureAI/demos/tutorial-2-intent/Policies/user_alice.json +48 -0
  32. package/payload/secureAI/demos/tutorial-2-intent/Policies/user_bob.json +83 -0
  33. package/payload/secureAI/demos/tutorial-2-intent/README.md +213 -0
  34. package/payload/secureAI/demos/tutorial-2-intent/script.py +188 -0
  35. package/payload/secureAI/demos/tutorial-2-intent/setup/alation_verifier.py +177 -0
  36. package/payload/secureAI/demos/tutorial-2-intent/setup/get_alation_user_token.py +180 -0
  37. package/payload/secureAI/examples/README.md +244 -0
  38. package/payload/secureAI/examples/anthropic/anthropic_1a_dropin_simple.py +103 -0
  39. package/payload/secureAI/examples/anthropic/anthropic_1b_multiuser_bind.py +190 -0
  40. package/payload/secureAI/examples/anthropic/anthropic_1b_multiuser_bind_streaming.py +190 -0
  41. package/payload/secureAI/examples/anthropic/anthropic_1c_a2a_invoke.py +156 -0
  42. package/payload/secureAI/examples/attestations/1a_trade_alice.py +215 -0
  43. package/payload/secureAI/examples/attestations/1a_trade_bob.py +164 -0
  44. package/payload/secureAI/examples/attestations/1b_grant_admin.py +170 -0
  45. package/payload/secureAI/examples/attestations/1b_grant_researcher.py +231 -0
  46. package/payload/secureAI/examples/attestations/README.md +128 -0
  47. package/payload/secureAI/examples/langchain/langchain_1a_dropin_simple.py +199 -0
  48. package/payload/secureAI/examples/langchain/langchain_1b_multiuser.py +262 -0
  49. package/payload/secureAI/examples/langchain/langchain_1c_orchestration.py +332 -0
  50. package/payload/secureAI/examples/langchain/langchain_1d_llm_openai.py +130 -0
  51. package/payload/secureAI/examples/langchain/langchain_1e_llm_anthropic.py +130 -0
  52. package/payload/secureAI/examples/langchain/langchain_1f_memory.py +214 -0
  53. package/payload/secureAI/examples/litellm/litellm_1a_dropin_simple.py +131 -0
  54. package/payload/secureAI/examples/mcp/1a_simple_invocation.py +115 -0
  55. package/payload/secureAI/examples/mcp/1b_discovery_and_resources.py +142 -0
  56. package/payload/secureAI/examples/mcp/1c_logging_client.py +100 -0
  57. package/payload/secureAI/examples/mcp/1d_progress_client.py +100 -0
  58. package/payload/secureAI/examples/mcp/1e_sampling_client.py +150 -0
  59. package/payload/secureAI/examples/mcp/1e_sampling_server.py +90 -0
  60. package/payload/secureAI/examples/mcp/1f_elicitation_client.py +127 -0
  61. package/payload/secureAI/examples/mcp/1f_elicitation_server.py +73 -0
  62. package/payload/secureAI/examples/mcp/1g_roots_client.py +122 -0
  63. package/payload/secureAI/examples/mcp/1g_roots_server.py +85 -0
  64. package/payload/secureAI/examples/mcp/2a_proxy_snowflake.py +212 -0
  65. package/payload/secureAI/examples/mcp/2b_proxy_databricks.py +299 -0
  66. package/payload/secureAI/examples/mcp/2c_proxy_stdio_fetch.py +91 -0
  67. package/payload/secureAI/examples/mcp/2d_proxy_stdio_salesforce.py +119 -0
  68. package/payload/secureAI/examples/mcp/2e_proxy_multiuser.py +136 -0
  69. package/payload/secureAI/examples/mcp/2f_proxy_stdio_databricks.py +144 -0
  70. package/payload/secureAI/examples/mcp/3a_native_mcp_client.py +104 -0
  71. package/payload/secureAI/examples/mcp/3b_native_mcp_proxy.py +114 -0
  72. package/payload/secureAI/examples/mcp/3c_native_mcp_sampling.py +180 -0
  73. package/payload/secureAI/examples/mcp/3d_native_mcp_2026.py +135 -0
  74. package/payload/secureAI/examples/mcp/securemcp_calculator.py +248 -0
  75. package/payload/secureAI/examples/mcp/securemcp_server.py +75 -0
  76. package/payload/secureAI/examples/openai/openai_1a_dropin_simple.py +102 -0
  77. package/payload/secureAI/examples/openai/openai_1b_multiuser_bind.py +193 -0
  78. package/payload/secureAI/examples/openai/openai_1b_multiuser_bind_streaming.py +199 -0
  79. package/payload/secureAI/examples/openai/openai_1c_a2a_invoke.py +156 -0
  80. package/payload/secureAI/examples/openai/openai_1d_invocation_patterns.py +337 -0
  81. package/payload/secureAI/examples/phishing-resistance/1a_login_alice.py +180 -0
  82. package/payload/secureAI/examples/phishing-resistance/1b_trade_alice.py +241 -0
  83. package/payload/secureAI/examples/phishing-resistance/1b_trade_bob.py +164 -0
  84. package/payload/secureAI/examples/phishing-resistance/README.md +124 -0
  85. package/payload/secureAI/examples/phishing-resistance/get_token.py +114 -0
  86. package/payload/secureAI/examples/pydantic_ai/README.md +88 -0
  87. package/payload/secureAI/examples/pydantic_ai/policies/app_pydantic-agent.json +10 -0
  88. package/payload/secureAI/examples/pydantic_ai/pydanticai_1a_dropin_simple.py +90 -0
  89. package/payload/secureAI/examples/pydantic_ai/pydanticai_1b_model_routing.py +85 -0
  90. package/payload/secureAI/examples/pydantic_ai/pydanticai_1c_native_tools.py +83 -0
  91. package/payload/secureAI/examples/pydantic_ai/pydanticai_1d_multiuser_bind.py +104 -0
  92. package/payload/secureAI/examples/pydantic_ai/pydanticai_1e_mcp_compose.py +92 -0
  93. package/payload/secureAI/macaw_adapters/__init__.py +55 -0
  94. package/payload/secureAI/macaw_adapters/anthropic/REFERENCE.md +374 -0
  95. package/payload/secureAI/macaw_adapters/anthropic/__init__.py +22 -0
  96. package/payload/secureAI/macaw_adapters/anthropic/secure_anthropic.py +792 -0
  97. package/payload/secureAI/macaw_adapters/anthropic_ext_tools/__init__.py +25 -0
  98. package/payload/secureAI/macaw_adapters/anthropic_ext_tools/secure_anthropic.py +840 -0
  99. package/payload/secureAI/macaw_adapters/langchain/REFERENCE.md +234 -0
  100. package/payload/secureAI/macaw_adapters/langchain/__init__.py +100 -0
  101. package/payload/secureAI/macaw_adapters/langchain/_utils.py +83 -0
  102. package/payload/secureAI/macaw_adapters/langchain/agents.py +296 -0
  103. package/payload/secureAI/macaw_adapters/langchain/anthropic.py +456 -0
  104. package/payload/secureAI/macaw_adapters/langchain/callbacks.py +306 -0
  105. package/payload/secureAI/macaw_adapters/langchain/memory.py +352 -0
  106. package/payload/secureAI/macaw_adapters/langchain/openai.py +410 -0
  107. package/payload/secureAI/macaw_adapters/langchain/tools.py +174 -0
  108. package/payload/secureAI/macaw_adapters/litellm/README.md +216 -0
  109. package/payload/secureAI/macaw_adapters/litellm/__init__.py +143 -0
  110. package/payload/secureAI/macaw_adapters/litellm/secure_litellm.py +686 -0
  111. package/payload/secureAI/macaw_adapters/mcp/REFERENCE.md +460 -0
  112. package/payload/secureAI/macaw_adapters/mcp/__init__.py +65 -0
  113. package/payload/secureAI/macaw_adapters/mcp/_endpoint.py +193 -0
  114. package/payload/secureAI/macaw_adapters/mcp/_endpoint2.py +225 -0
  115. package/payload/secureAI/macaw_adapters/mcp/_serve.py +25 -0
  116. package/payload/secureAI/macaw_adapters/mcp/client.py +721 -0
  117. package/payload/secureAI/macaw_adapters/mcp/decorators.py +228 -0
  118. package/payload/secureAI/macaw_adapters/mcp/fastmcp_compat.py +353 -0
  119. package/payload/secureAI/macaw_adapters/mcp/mcp.py +785 -0
  120. package/payload/secureAI/macaw_adapters/mcp/proxy.py +626 -0
  121. package/payload/secureAI/macaw_adapters/mcp/server.py +317 -0
  122. package/payload/secureAI/macaw_adapters/mcp/subscriptions.py +450 -0
  123. package/payload/secureAI/macaw_adapters/openai/REFERENCE.md +368 -0
  124. package/payload/secureAI/macaw_adapters/openai/__init__.py +22 -0
  125. package/payload/secureAI/macaw_adapters/openai/secure_openai.py +833 -0
  126. package/payload/secureAI/macaw_adapters/openai_ext_tools/REFERENCE.md +368 -0
  127. package/payload/secureAI/macaw_adapters/openai_ext_tools/__init__.py +25 -0
  128. package/payload/secureAI/macaw_adapters/openai_ext_tools/secure_openai.py +876 -0
  129. package/payload/secureAI/macaw_adapters/pydantic_ai/__init__.py +36 -0
  130. package/payload/secureAI/macaw_adapters/pydantic_ai/_core.py +257 -0
  131. package/payload/secureAI/macaw_adapters/pydantic_ai/agent.py +218 -0
  132. package/payload/secureAI/macaw_adapters/pydantic_ai/model.py +99 -0
  133. package/payload/secureAI/macaw_adapters/pydantic_ai/toolset.py +86 -0
  134. package/payload/secureAI/test_harness_quick.py +296 -0
  135. package/wheels/macaw_adapters-0.9.10.1-py3-none-any.whl +0 -0
  136. package/wheels/macaw_client-0.9.10.1-cp312-cp312-macosx_11_0_arm64.whl +0 -0
  137. package/wheels/macaw_client-0.9.10.1-cp312-cp312-manylinux_2_17_x86_64.whl +0 -0
  138. package/wheels/macaw_client-0.9.10.1-cp314-cp314-macosx_11_0_arm64.whl +0 -0
  139. package/wheels/macaw_client-0.9.10.1-cp314-cp314-manylinux_2_17_x86_64.whl +0 -0
  140. package/wheels/macaw_client-0.9.10.1-cp314-cp314-win_amd64.whl +0 -0
  141. package/wheels/macaw_client-0.9.10.1-cp39-cp39-macosx_11_0_arm64.whl +0 -0
  142. package/README.md +0 -3
@@ -0,0 +1,215 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ 1a_trade_alice.py - External Attestation Trade Request
4
+
5
+ Alice wants to execute a high-value trade ($15,000).
6
+ Policy requires manager approval via external attestation for amounts > $10,000.
7
+
8
+ This demonstrates:
9
+ - Conditional attestations: only required when amount > 10000
10
+ - Blocking flow: request waits for approval
11
+ - Role-based approval: only users with role:manager can approve
12
+
13
+ NOTE: This example requires interactive approval and is not suitable
14
+ for automated test harnesses.
15
+
16
+ Prerequisites:
17
+ - MACAW SDK installed (pip install macaw-client macaw-adapters)
18
+ - Identity Provider configured (Console -> Settings -> Identity Bridge)
19
+ - Test users: alice/Alice123!, bob/Bob@123! (bob needs manager role)
20
+
21
+ Run:
22
+ # Terminal 1: Run Alice's trade request (will block waiting)
23
+ python 1a_trade_alice.py
24
+
25
+ # Terminal 2: Run Bob's approval
26
+ python 1a_trade_bob.py
27
+ """
28
+
29
+ import json
30
+
31
+ from macaw_client import MACAWClient, RemoteIdentityProvider
32
+
33
+
34
+ # Trade configuration
35
+ TRADE_SYMBOL = "AAPL"
36
+ TRADE_AMOUNT = 15000 # High-value trade requiring approval
37
+
38
+
39
+ def execute_trade_handler(params):
40
+ """Execute trade - the actual tool implementation."""
41
+ print(f"\n [TOOL] execute_trade invoked!")
42
+ print(f" [TOOL] Received parameters:")
43
+ print(f" - symbol: {params.get('symbol')}")
44
+ print(f" - amount: ${params.get('amount'):,}")
45
+ print(f" - action: {params.get('action')}")
46
+
47
+ # Simulate trade execution
48
+ result = {
49
+ "status": "executed",
50
+ "symbol": params.get("symbol"),
51
+ "amount": params.get("amount"),
52
+ "action": params.get("action"),
53
+ "trade_id": "TRD-12345",
54
+ "message": f"Successfully {params.get('action')} ${params.get('amount'):,} of {params.get('symbol')}"
55
+ }
56
+ print(f" [TOOL] Returning: {result}")
57
+ return result
58
+
59
+
60
+ def main():
61
+ print("=" * 60)
62
+ print("Example 1a: External Attestation - Trade Request (Alice)")
63
+ print("=" * 60)
64
+
65
+ # Step 1: Create Trading Service (provides execute_trade tool)
66
+ print("\n[Step 1] Creating Trading Service...")
67
+
68
+ try:
69
+ trading_service = MACAWClient(
70
+ app_name="trading",
71
+ agent_type="service",
72
+ tools={
73
+ "tool:trading/execute_trade": {
74
+ "handler": execute_trade_handler,
75
+ "description": "Execute a stock trade"
76
+ }
77
+ }
78
+ )
79
+
80
+ if not trading_service.register():
81
+ print(" ERROR: Failed to register Trading Service")
82
+ print("\n Make sure LocalAgent is running:")
83
+ print(" python3 -m macaw_agent.main")
84
+ return 1
85
+
86
+ print(f" Service ID: {trading_service.agent_id}")
87
+ print(f" Provides: tool:trading/execute_trade")
88
+
89
+ except Exception as e:
90
+ print(f" ERROR: Failed to create Trading Service: {e}")
91
+ return 1
92
+
93
+ # Step 2: Authenticate as Alice
94
+ print("\n[Step 2] Authenticating as alice...")
95
+ try:
96
+ jwt_token, _ = RemoteIdentityProvider().login("alice", "Alice123!")
97
+ print(" Got JWT token")
98
+ except Exception as e:
99
+ print(f" ERROR: Failed to authenticate: {e}")
100
+ print("\n Make sure your identity provider is running and alice user exists")
101
+ trading_service.unregister()
102
+ return 1
103
+
104
+ # Step 3: Create Alice's user agent with attestation policy
105
+ print("\n[Step 3] Creating Alice's user agent...")
106
+ try:
107
+ # MAPL v3 format for attestations:
108
+ # - "attestations" array: defines WHEN attestation is required
109
+ # - "constraints.attestations": defines HOW attestation works
110
+ alice = MACAWClient(
111
+ user_name="alice",
112
+ iam_token=jwt_token,
113
+ agent_type="user",
114
+ app_name="trading-app",
115
+ intent_policy={
116
+ "resources": ["tool:trading/execute_trade"],
117
+ # Attestation required when amount > 10000
118
+ "attestations": [
119
+ "trade-approved::{params.amount > 10000}"
120
+ ],
121
+ # Attestation metadata
122
+ "constraints": {
123
+ "attestations": {
124
+ "trade-approved": {
125
+ "approval_criteria": "role:manager",
126
+ "timeout": 300, # 5 minutes to approve
127
+ "time_to_live": 3600, # Valid for 1 hour after approval
128
+ "one_time": True # Consumed after single use
129
+ }
130
+ }
131
+ }
132
+ }
133
+ )
134
+
135
+ if not alice.register():
136
+ print(" ERROR: Failed to register Alice's agent")
137
+ trading_service.unregister()
138
+ return 1
139
+
140
+ print(f" Agent ID: {alice.agent_id}")
141
+
142
+ except Exception as e:
143
+ print(f" ERROR: Failed to create Alice's agent: {e}")
144
+ trading_service.unregister()
145
+ return 1
146
+
147
+ # Step 4: Execute trade (will block waiting for approval)
148
+ print("\n[Step 4] Executing trade...")
149
+ print(f" Symbol: {TRADE_SYMBOL}")
150
+ print(f" Amount: ${TRADE_AMOUNT:,}")
151
+ print(f"\n Policy check: amount ({TRADE_AMOUNT}) > 10000 -> attestation REQUIRED")
152
+ print(f" Attestation: 'trade-approved' requires role:manager")
153
+ print(f"\n Creating PENDING attestation and BLOCKING...")
154
+ print(f" Bob has 5 minutes (timeout=300s) to approve.")
155
+ print(f"\n >>> Run 1a_trade_bob.py in another terminal <<<")
156
+ print("-" * 60)
157
+
158
+ try:
159
+ # This will block internally while waiting for attestation
160
+ result = alice.invoke_tool(
161
+ tool_name="tool:trading/execute_trade",
162
+ target_agent=trading_service.agent_id,
163
+ parameters={
164
+ "symbol": TRADE_SYMBOL,
165
+ "amount": TRADE_AMOUNT,
166
+ "action": "buy"
167
+ }
168
+ )
169
+
170
+ print("\n" + "=" * 60)
171
+ print("TRADE EXECUTED SUCCESSFULLY!")
172
+ print("=" * 60)
173
+ print(f"Result: {json.dumps(result, indent=2)}")
174
+
175
+ except Exception as e:
176
+ error_msg = str(e)
177
+ if "attestation" in error_msg.lower() or "denied" in error_msg.lower():
178
+ print("\n" + "=" * 60)
179
+ print("TRADE BLOCKED - ATTESTATION REQUIRED OR DENIED")
180
+ print("=" * 60)
181
+ print(f"\nError: {error_msg}")
182
+ print(f"\nTo approve, Bob should run:")
183
+ print(f" python 1a_trade_bob.py")
184
+ else:
185
+ print(f"\n ERROR: {e}")
186
+
187
+ finally:
188
+ # Cleanup
189
+ print("\n[Cleanup] Unregistering agents...")
190
+ try:
191
+ alice.unregister()
192
+ trading_service.unregister()
193
+ except:
194
+ pass
195
+
196
+ return 0
197
+
198
+
199
+ if __name__ == "__main__":
200
+ import sys
201
+ try:
202
+ sys.exit(main())
203
+ except Exception as e:
204
+ err = str(e)
205
+ print("\n" + "=" * 60)
206
+ if "Local provider does not support" in err:
207
+ print("ERROR: Identity Provider not configured")
208
+ print("Fix: Console -> Settings -> Identity Bridge")
209
+ elif "Connection refused" in err or "connect" in err.lower():
210
+ print("ERROR: Cannot connect to MACAW")
211
+ print("Fix: Ensure LocalAgent is running")
212
+ else:
213
+ print(f"ERROR: {e}")
214
+ print("=" * 60)
215
+ sys.exit(1)
@@ -0,0 +1,164 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ 1a_trade_bob.py - External Attestation Manager Approval
4
+
5
+ Bob reviews and approves/denies pending attestation requests.
6
+ Alice's trade request is BLOCKING, waiting for Bob's approval.
7
+
8
+ This demonstrates:
9
+ - list_attestations(): Find pending requests you can approve
10
+ - approve_attestation(): Grant the request
11
+ - deny_attestation(): Reject the request with reason
12
+
13
+ NOTE: This example requires interactive input and is not suitable
14
+ for automated test harnesses.
15
+
16
+ Prerequisites:
17
+ - MACAW SDK installed (pip install macaw-client macaw-adapters)
18
+ - Identity Provider configured (Console -> Settings -> Identity Bridge)
19
+ - Test users: alice/Alice123!, bob/Bob@123! (bob needs manager role)
20
+ - Alice has already requested a trade (1a_trade_alice.py running)
21
+
22
+ Run:
23
+ # Terminal 1: Run Alice's trade request first
24
+ python 1a_trade_alice.py
25
+
26
+ # Terminal 2: Run Bob's approval
27
+ python 1a_trade_bob.py
28
+ """
29
+
30
+ import json
31
+
32
+ from macaw_client import MACAWClient, RemoteIdentityProvider
33
+
34
+
35
+ def main():
36
+ print("=" * 60)
37
+ print("Example 1a: External Attestation - Manager Approval (Bob)")
38
+ print("=" * 60)
39
+
40
+ # Step 1: Authenticate as Bob
41
+ print("\n[Step 1] Authenticating as bob...")
42
+ try:
43
+ jwt_token, _ = RemoteIdentityProvider().login("bob", "Bob@123!")
44
+ print(" Got JWT token")
45
+ except Exception as e:
46
+ print(f" ERROR: Failed to authenticate: {e}")
47
+ print("\n Make sure your identity provider is running and bob user exists")
48
+ return 1
49
+
50
+ # Step 2: Create Bob's user agent
51
+ print("\n[Step 2] Creating Bob's user agent...")
52
+ try:
53
+ bob = MACAWClient(
54
+ user_name="bob",
55
+ iam_token=jwt_token,
56
+ agent_type="user",
57
+ app_name="attestation-manager",
58
+ intent_policy={
59
+ "resources": ["attestation:*"],
60
+ "constraints": {
61
+ "roles": ["manager"]
62
+ }
63
+ }
64
+ )
65
+
66
+ if not bob.register():
67
+ print(" ERROR: Failed to register Bob's agent")
68
+ print("\n Make sure LocalAgent is running:")
69
+ print(" python3 -m macaw_agent.main")
70
+ return 1
71
+
72
+ print(f" Agent ID: {bob.agent_id}")
73
+
74
+ except Exception as e:
75
+ print(f" ERROR: Failed to create Bob's agent: {e}")
76
+ return 1
77
+
78
+ # Step 3: List pending attestations
79
+ print("\n[Step 3] Listing pending attestations...")
80
+ try:
81
+ attestations = bob.list_attestations(status="pending")
82
+ except Exception as e:
83
+ print(f" ERROR: Failed to list attestations: {e}")
84
+ bob.unregister()
85
+ return 1
86
+
87
+ if not attestations:
88
+ print(" No pending attestations found.")
89
+ print("\n Make sure Alice has requested a trade first:")
90
+ print(" python 1a_trade_alice.py")
91
+ bob.unregister()
92
+ return 0
93
+
94
+ print(f"\n Found {len(attestations)} pending attestation(s):\n")
95
+
96
+ for i, att in enumerate(attestations, 1):
97
+ print(f" [{i}] Key: {att.get('key')}")
98
+ print(f" For Agent: {att.get('for_agent')}")
99
+ print(f" Approval Criteria: {att.get('approval_criteria')}")
100
+ print(f" One-Time: {att.get('one_time', False)}")
101
+ if att.get('value'):
102
+ print(f" Value: {json.dumps(att.get('value', {}))}")
103
+ print()
104
+
105
+ # Step 4: Approve/Deny attestation(s)
106
+ print("[Step 4] Process attestations...")
107
+ print(" Options: [y] Approve [d] Deny [s] Skip")
108
+
109
+ for i, att in enumerate(attestations, 1):
110
+ key = att.get('key')
111
+ for_agent = att.get('for_agent')
112
+
113
+ response = input(f"\n '{key}' for {for_agent}? [y/d/s]: ").strip().lower()
114
+
115
+ if response == 'y':
116
+ print(f" Approving...")
117
+ try:
118
+ if bob.approve_attestation(att, reason="Manager approved"):
119
+ print(f" APPROVED - Alice's request will proceed")
120
+ else:
121
+ print(f" Failed to approve")
122
+ except Exception as e:
123
+ print(f" ERROR: {e}")
124
+
125
+ elif response == 'd':
126
+ print(f" Denying...")
127
+ try:
128
+ if bob.deny_attestation(att, reason="Manager denied"):
129
+ print(f" DENIED - Alice's request will be rejected")
130
+ else:
131
+ print(f" Failed to deny")
132
+ except Exception as e:
133
+ print(f" ERROR: {e}")
134
+
135
+ else:
136
+ print(f" Skipped")
137
+
138
+ # Cleanup
139
+ bob.unregister()
140
+
141
+ print("\n" + "=" * 60)
142
+ print("Done. Alice's request should now proceed (or fail if denied).")
143
+ print("=" * 60)
144
+
145
+ return 0
146
+
147
+
148
+ if __name__ == "__main__":
149
+ import sys
150
+ try:
151
+ sys.exit(main())
152
+ except Exception as e:
153
+ err = str(e)
154
+ print("\n" + "=" * 60)
155
+ if "Local provider does not support" in err:
156
+ print("ERROR: Identity Provider not configured")
157
+ print("Fix: Console -> Settings -> Identity Bridge")
158
+ elif "Connection refused" in err or "connect" in err.lower():
159
+ print("ERROR: Cannot connect to MACAW")
160
+ print("Fix: Ensure LocalAgent is running")
161
+ else:
162
+ print(f"ERROR: {e}")
163
+ print("=" * 60)
164
+ sys.exit(1)
@@ -0,0 +1,170 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ 1b_grant_admin.py - Reusable Capability Grant (Admin Approval)
4
+
5
+ Admin approves capability grants for researchers.
6
+ Since one_time=False, this approval is REUSABLE.
7
+
8
+ This demonstrates:
9
+ - Identifying reusable vs one-time attestations
10
+ - Granting capabilities that persist
11
+ - Future invocations reuse this approval (no blocking)
12
+
13
+ NOTE: This example requires interactive input and is not suitable
14
+ for automated test harnesses.
15
+
16
+ Prerequisites:
17
+ - MACAW SDK installed (pip install macaw-client macaw-adapters)
18
+ - Identity Provider configured (Console -> Settings -> Identity Bridge)
19
+ - Test users: alice/Alice123!, bob/Bob@123! (bob needs admin role)
20
+ - Researcher script already waiting (1b_grant_researcher.py running)
21
+
22
+ Run:
23
+ # Terminal 1: Run researcher request first
24
+ python 1b_grant_researcher.py
25
+
26
+ # Terminal 2: Run admin approval
27
+ python 1b_grant_admin.py
28
+ """
29
+
30
+ import json
31
+
32
+ from macaw_client import MACAWClient, RemoteIdentityProvider
33
+
34
+
35
+ def main():
36
+ print("=" * 60)
37
+ print("Example 1b: Reusable Capability Grant - Admin")
38
+ print("=" * 60)
39
+
40
+ # Step 1: Authenticate as admin (using bob with admin role)
41
+ print("\n[Step 1] Authenticating as admin...")
42
+ try:
43
+ jwt_token, _ = RemoteIdentityProvider().login("bob", "Bob@123!")
44
+ print(" Got JWT token")
45
+ except Exception as e:
46
+ print(f" ERROR: Failed to authenticate: {e}")
47
+ return 1
48
+
49
+ # Step 2: Create admin agent
50
+ print("\n[Step 2] Creating admin agent...")
51
+ try:
52
+ admin = MACAWClient(
53
+ user_name="admin",
54
+ iam_token=jwt_token,
55
+ agent_type="user",
56
+ app_name="admin-console",
57
+ intent_policy={
58
+ "resources": ["attestation:*"],
59
+ "constraints": {
60
+ "roles": ["admin"]
61
+ }
62
+ }
63
+ )
64
+
65
+ if not admin.register():
66
+ print(" ERROR: Failed to register admin agent")
67
+ return 1
68
+
69
+ print(f" Agent ID: {admin.agent_id}")
70
+
71
+ except Exception as e:
72
+ print(f" ERROR: {e}")
73
+ return 1
74
+
75
+ # Step 3: List pending attestations
76
+ print("\n[Step 3] Listing pending attestations...")
77
+ try:
78
+ attestations = admin.list_attestations(status="pending")
79
+ except Exception as e:
80
+ print(f" ERROR: {e}")
81
+ admin.unregister()
82
+ return 1
83
+
84
+ if not attestations:
85
+ print(" No pending attestations found.")
86
+ print("\n Make sure the researcher script is running and waiting.")
87
+ admin.unregister()
88
+ return 0
89
+
90
+ print(f"\n Found {len(attestations)} pending attestation(s):\n")
91
+
92
+ for i, att in enumerate(attestations, 1):
93
+ one_time = att.get('one_time', True)
94
+ print(f" [{i}] Key: {att.get('key')}")
95
+ print(f" For Agent: {att.get('for_agent')}")
96
+ print(f" Approval Criteria: {att.get('approval_criteria')}")
97
+ print(f" One-Time: {one_time}")
98
+ if not one_time:
99
+ print(f" *** REUSABLE CAPABILITY - approval persists! ***")
100
+ print()
101
+
102
+ # Step 4: Approve attestation(s)
103
+ print("[Step 4] Process attestations...")
104
+ print(" Options: [y] Approve [d] Deny [s] Skip")
105
+
106
+ for i, att in enumerate(attestations, 1):
107
+ key = att.get('key')
108
+ for_agent = att.get('for_agent')
109
+ one_time = att.get('one_time', True)
110
+
111
+ prompt = f"\n '{key}' for {for_agent}"
112
+ if not one_time:
113
+ prompt += " (REUSABLE)"
114
+ prompt += "? [y/d/s]: "
115
+
116
+ response = input(prompt).strip().lower()
117
+
118
+ if response == 'y':
119
+ print(f" Approving...")
120
+ try:
121
+ if admin.approve_attestation(att, reason="Admin granted capability"):
122
+ print(f" APPROVED")
123
+ if not one_time:
124
+ print(f" *** This approval will be REUSED for future invocations ***")
125
+ else:
126
+ print(f" Failed to approve")
127
+ except Exception as e:
128
+ print(f" ERROR: {e}")
129
+
130
+ elif response == 'd':
131
+ print(f" Denying...")
132
+ try:
133
+ if admin.deny_attestation(att, reason="Admin denied capability"):
134
+ print(f" DENIED")
135
+ else:
136
+ print(f" Failed to deny")
137
+ except Exception as e:
138
+ print(f" ERROR: {e}")
139
+
140
+ else:
141
+ print(f" Skipped")
142
+
143
+ # Cleanup
144
+ admin.unregister()
145
+
146
+ print("\n" + "=" * 60)
147
+ print("Done. Researcher should now proceed.")
148
+ print("Future invocations will reuse this approval (no blocking).")
149
+ print("=" * 60)
150
+
151
+ return 0
152
+
153
+
154
+ if __name__ == "__main__":
155
+ import sys
156
+ try:
157
+ sys.exit(main())
158
+ except Exception as e:
159
+ err = str(e)
160
+ print("\n" + "=" * 60)
161
+ if "Local provider does not support" in err:
162
+ print("ERROR: Identity Provider not configured")
163
+ print("Fix: Console -> Settings -> Identity Bridge")
164
+ elif "Connection refused" in err or "connect" in err.lower():
165
+ print("ERROR: Cannot connect to MACAW")
166
+ print("Fix: Ensure LocalAgent is running")
167
+ else:
168
+ print(f"ERROR: {e}")
169
+ print("=" * 60)
170
+ sys.exit(1)