thinkwork-cli 0.13.2 → 1.0.0
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.
- package/README.md +3 -14
- package/dist/{api-client-JYZXYQJR.js → api-client-4P4JZOBX.js} +1 -1
- package/dist/{chunk-Z2F6RJFR.js → chunk-4OMDWYZQ.js} +4 -5
- package/dist/{chunk-ILZKG5WV.js → chunk-AD2NIWB5.js} +434 -69
- package/dist/chunk-MWQ7RO76.js +137 -0
- package/dist/{chunk-DVPBI5HM.js → chunk-OLUQ4U2Z.js} +26 -26
- package/dist/{chunk-QOERT44A.js → chunk-YQCYD6NE.js} +57 -44
- package/dist/cli.js +4400 -16431
- package/dist/commands/enterprise/templates/deploy-repo/scripts/apply-release.mjs +0 -1
- package/dist/commands/enterprise/templates/deploy-repo/scripts/smoke.mjs +0 -1
- package/dist/commands/enterprise/templates/deploy-repo/terraform/main.tf +0 -14
- package/dist/{dist-es-CRV46D5B.js → dist-es-2OTNTL4N.js} +4 -4
- package/dist/{dist-es-ENBUC3JP.js → dist-es-2XVAH4BQ.js} +2 -2
- package/dist/{dist-es-DCY4IC3Y.js → dist-es-3DPDUJAH.js} +2 -2
- package/dist/{dist-es-G7PTG75M.js → dist-es-BNZTBBVC.js} +1 -1
- package/dist/{dist-es-GKDBE7E6.js → dist-es-CD2RBDAC.js} +1 -1
- package/dist/{dist-es-R7FGL2IB.js → dist-es-I4FW57PS.js} +1 -1
- package/dist/{dist-es-USORBXZ5.js → dist-es-KVFWJNDY.js} +4 -4
- package/dist/{dist-es-HY3535OI.js → dist-es-PS73DWNL.js} +2 -2
- package/dist/{dist-es-LDE437AM.js → dist-es-S3NZQFGT.js} +1 -1
- package/dist/{dist-es-G54LBMRN.js → dist-es-SGCA4P32.js} +1 -1
- package/dist/{dist-es-Q57SZXZ7.js → dist-es-WBM6J4RA.js} +2 -2
- package/dist/{dist-es-P26FWFLT.js → dist-es-ZGVADYIF.js} +8 -8
- package/dist/drizzle/0227_analyst_reader_role.sql +17 -26
- package/dist/drizzle/0230_analyst_rls.sql +28 -45
- package/dist/drizzle/0261_native_auth_control_plane.sql +7 -3
- package/dist/drizzle/0270_tool_execution_ledger.sql +115 -0
- package/dist/drizzle/0271_ontology_twin_declarations.sql +47 -0
- package/dist/drizzle/0272_identity_graph_projection_cursor.sql +23 -0
- package/dist/drizzle/0273_twin_materialization_suggestions.sql +29 -0
- package/dist/drizzle/0274_tenant_mcp_twin_keys.sql +59 -0
- package/dist/drizzle/0275_identity_bulk_rebuild_fence.sql +30 -0
- package/dist/drizzle/0276_tool_execution_ledger_allow_cascade_delete.sql +18 -0
- package/dist/drizzle/0277_knowledge_base_sources.sql +107 -0
- package/dist/drizzle/0278_kb_page_transcription.sql +55 -0
- package/dist/drizzle/0279_mcp_servers_off_plugin_provenance.sql +93 -0
- package/dist/drizzle/0280_work_item_external_refs_drop_plane_provider.sql +46 -0
- package/dist/drizzle/0281_tenant_mcp_twin_keys_expiry_suffix.sql +9 -0
- package/dist/drizzle/0282_tenant_mcp_twin_keys_grants.sql +24 -0
- package/dist/drizzle/0283_agents_agentcore_runtime_dispatch.sql +13 -0
- package/dist/drizzle/0284_user_brain_claims.sql +69 -0
- package/dist/drizzle/0285_tenant_mcp_twin_keys_trusted_subsystem.sql +30 -0
- package/dist/drizzle/0286_brain_analytics_key.sql +26 -0
- package/dist/drizzle/0287_brain_subject.sql +25 -0
- package/dist/drizzle/0288_multi_lane_identities.sql +25 -0
- package/dist/{loadSso-DJVB7BXE.js → loadSso-ETCXTR4I.js} +2 -2
- package/dist/{loadSso-JAMNA3WY.js → loadSso-UBD7KUHE.js} +2 -2
- package/dist/plugins/README.md +2 -2
- package/dist/plugins/catalog/package.json +0 -3
- package/dist/plugins/catalog/src/__tests__/build-catalog.test.ts +6 -3
- package/dist/plugins/catalog/src/__tests__/catalog.test.ts +21 -21
- package/dist/plugins/catalog/src/__tests__/contracts.test.ts +102 -25
- package/dist/plugins/catalog/src/__tests__/plugin-package.test.ts +7 -6
- package/dist/plugins/catalog/src/__tests__/plugin-registry.test.ts +0 -15
- package/dist/plugins/catalog/src/contracts.ts +15 -0
- package/dist/plugins/catalog/src/registry/generated-first-party.ts +0 -18
- package/dist/plugins/n8n/test/integrated-app-contract.test.ts +1 -1
- package/dist/plugins/twenty/package.json +2 -1
- package/dist/plugins/twenty/scripts/LAMBDA_DEPLOY.md +114 -0
- package/dist/plugins/twenty/scripts/build-lambda.sh +29 -0
- package/dist/plugins/twenty/scripts/configure-product-views.ts +195 -0
- package/dist/plugins/twenty/scripts/lambda-handler.ts +49 -0
- package/dist/plugins/twenty/scripts/lib/__tests__/load-attachments.test.ts +38 -0
- package/dist/plugins/twenty/scripts/lib/__tests__/load-records.test.ts +145 -2
- package/dist/plugins/twenty/scripts/lib/__tests__/mappers.test.ts +397 -151
- package/dist/plugins/twenty/scripts/lib/__tests__/members-ensure.test.ts +28 -129
- package/dist/plugins/twenty/scripts/lib/__tests__/provision-members-db.test.ts +10 -0
- package/dist/plugins/twenty/scripts/lib/__tests__/schema-ensure.test.ts +13 -8
- package/dist/plugins/twenty/scripts/lib/lastmile-reader.ts +222 -2
- package/dist/plugins/twenty/scripts/lib/load-attachments.ts +29 -0
- package/dist/plugins/twenty/scripts/lib/load-records.ts +160 -43
- package/dist/plugins/twenty/scripts/lib/mappers.ts +368 -152
- package/dist/plugins/twenty/scripts/lib/members-ensure.ts +43 -268
- package/dist/plugins/twenty/scripts/lib/provision-members-db.ts +57 -0
- package/dist/plugins/twenty/scripts/lib/schema-ensure.ts +136 -31
- package/dist/plugins/twenty/scripts/migrate-lastmile.ts +254 -152
- package/dist/plugins/twenty/scripts/provision-twenty-members.ts +44 -1
- package/dist/plugins/twenty/scripts/purge-lastmile-import.ts +178 -0
- package/dist/plugins/twenty/scripts/restructure-products.ts +312 -0
- package/dist/plugins/twenty/scripts/retag-stage-values.ts +362 -0
- package/dist/plugins/twenty/test/manifest.test.ts +2 -4
- package/dist/scripts/post-deploy.sh +79 -1
- package/dist/{signin-UGDWTGS4.js → signin-5QZNRSOO.js} +2 -2
- package/dist/{sso-oidc-Q4VXANXY.js → sso-oidc-XK673P54.js} +2 -2
- package/dist/{sts-CWLSE3GH.js → sts-OXI2UALF.js} +6 -134
- package/dist/{sts-DNZLNVOV.js → sts-SQW4NBK3.js} +6 -134
- package/dist/terraform/examples/greenfield/main.tf +233 -175
- package/dist/terraform/examples/greenfield/terraform.tfvars.example +12 -8
- package/dist/terraform/modules/app/agentcore-admin/main.tf +47 -0
- package/dist/terraform/modules/app/agentcore-code-interpreter/README.md +39 -47
- package/dist/terraform/modules/app/agentcore-code-interpreter/main.tf +13 -66
- package/dist/terraform/modules/app/agentcore-identity/main.tf +106 -26
- package/dist/terraform/modules/app/agentcore-identity/outputs.tf +6 -6
- package/dist/terraform/modules/app/agentcore-identity/scripts/bootstrap_twenty_oauth_client.sh +216 -99
- package/dist/terraform/modules/app/agentcore-identity/scripts/delete_identity.sh +6 -9
- package/dist/terraform/modules/app/agentcore-identity/scripts/delete_workload_identity.sh +19 -0
- package/dist/terraform/modules/app/agentcore-identity/scripts/ensure_workload_identity.sh +29 -0
- package/dist/terraform/modules/app/agentcore-identity/scripts/read_identity.sh +22 -7
- package/dist/terraform/modules/app/agentcore-identity/scripts/reconcile_twenty_identity.sh +17 -46
- package/dist/terraform/modules/app/agentcore-identity/scripts/reconcile_twenty_provider.mjs +65 -3
- package/dist/terraform/modules/app/agentcore-identity/tests/tainted-marker/after.tf +15 -0
- package/dist/terraform/modules/app/agentcore-identity/tests/tainted-marker/before.tf +7 -0
- package/dist/terraform/modules/app/agentcore-identity/tests/tainted-marker/identity-after.tf +8 -0
- package/dist/terraform/modules/app/agentcore-identity/tests/tainted-marker/identity-before.tf +9 -0
- package/dist/terraform/modules/app/agentcore-identity/tests/tainted-marker/thinkwork-after.tf +3 -0
- package/dist/terraform/modules/app/agentcore-identity/tests/tainted-marker/thinkwork-before.tf +3 -0
- package/dist/terraform/modules/app/agentcore-identity/variables.tf +6 -0
- package/dist/terraform/modules/app/agentcore-memory/README.md +46 -9
- package/dist/terraform/modules/app/agentcore-memory/main.tf +27 -10
- package/dist/terraform/modules/app/agentcore-memory/scripts/create_or_find_memory.sh +156 -24
- package/dist/terraform/modules/app/agentcore-pi/README.md +1 -3
- package/dist/terraform/modules/app/agentcore-pi/main.tf +42 -52
- package/dist/terraform/modules/app/agentcore-pi/outputs.tf +3 -3
- package/dist/terraform/modules/app/agentcore-pi/scripts/reconcile_pi_runtime.mjs +175 -0
- package/dist/terraform/modules/app/agentcore-pi/variables.tf +35 -28
- package/dist/terraform/modules/app/agentcore-platform/main.tf +68 -0
- package/dist/terraform/modules/app/agentcore-runtime/main.tf +4 -20
- package/dist/terraform/modules/app/capability-broker/main.tf +2 -2
- package/dist/terraform/modules/app/capability-broker/variables.tf +11 -0
- package/dist/terraform/modules/app/deployment-control-plane/README.md +264 -0
- package/dist/terraform/modules/app/deployment-control-plane/buildspec.yml +4 -1
- package/dist/terraform/modules/app/deployment-control-plane/main.tf +94 -0
- package/dist/terraform/modules/app/deployment-control-plane/outputs.tf +5 -0
- package/dist/terraform/modules/app/deployment-control-plane/runner.py +2641 -124
- package/dist/terraform/modules/app/deployment-control-plane/test_runner_bundle.py +2034 -154
- package/dist/terraform/modules/app/deployment-control-plane/variables.tf +33 -0
- package/dist/terraform/modules/app/lambda-api/auth-state.tf +72 -0
- package/dist/terraform/modules/app/lambda-api/chat-latency-observability.tf +466 -0
- package/dist/terraform/modules/app/lambda-api/dispatch-dlq.tf +55 -0
- package/dist/terraform/modules/app/lambda-api/handlers.tf +411 -723
- package/dist/terraform/modules/app/lambda-api/iam-grouped.tf +262 -198
- package/dist/terraform/modules/app/lambda-api/main.tf +51 -13
- package/dist/terraform/modules/app/lambda-api/mcp-oauth.tf +4 -181
- package/dist/terraform/modules/app/lambda-api/memory-alarms.tf +3 -28
- package/dist/terraform/modules/app/lambda-api/oauth-secrets.tf +1 -2
- package/dist/terraform/modules/app/lambda-api/outputs.tf +17 -45
- package/dist/terraform/modules/app/lambda-api/turn-assertion.tf +45 -0
- package/dist/terraform/modules/app/lambda-api/variables.tf +219 -126
- package/dist/{plugins/n8n/terraform → terraform/modules/app}/n8n/README.md +1 -1
- package/dist/{plugins/n8n/terraform → terraform/modules/app}/n8n/main.tf +18 -18
- package/dist/terraform/modules/app/sandbox-log-scrubber/README.md +11 -10
- package/dist/terraform/modules/app/sandbox-log-scrubber/main.tf +5 -5
- package/dist/terraform/modules/app/static-site/main.tf +1 -1
- package/dist/terraform/modules/app/www-dns/main.tf +9 -27
- package/dist/terraform/modules/app/www-dns/variables.tf +4 -10
- package/dist/terraform/modules/data/aurora-postgres/README.md +34 -0
- package/dist/terraform/modules/data/aurora-postgres/alarms.tf +82 -0
- package/dist/terraform/modules/data/aurora-postgres/main.tf +27 -57
- package/dist/terraform/modules/data/aurora-postgres/outputs.tf +0 -9
- package/dist/terraform/modules/data/aurora-postgres/variables.tf +41 -0
- package/dist/terraform/modules/data/s3-buckets/main.tf +5 -6
- package/dist/terraform/modules/foundation/cognito/main.tf +81 -8
- package/dist/terraform/modules/foundation/cognito/outputs.tf +5 -0
- package/dist/terraform/modules/foundation/cognito/variables.tf +24 -12
- package/dist/terraform/modules/thinkwork/README.md +5 -1
- package/dist/terraform/modules/thinkwork/main.tf +376 -374
- package/dist/terraform/modules/thinkwork/outputs.tf +12 -158
- package/dist/terraform/modules/thinkwork/variables.tf +246 -89
- package/dist/workspace-defaults/files/INSTRUCTIONS.md +9 -11
- package/dist/workspace-defaults/files/MEMORY_GUIDE.md +38 -35
- package/dist/workspace-defaults/files/ROUTER.md +1 -1
- package/dist/workspace-defaults/files/TOOLS.md +9 -39
- package/dist/workspace-defaults/files/agents/analyst/INSTRUCTIONS.md +6 -3
- package/dist/workspace-defaults/files/catalog-skills/brain-brief-builder/SKILL.md +165 -0
- package/dist/workspace-defaults/files/catalog-skills/n8n-workflow-operator/SKILL.md +75 -0
- package/dist/workspace-defaults/files/catalog-skills/n8n-workflow-operator/references/mcp-tooling.md +40 -0
- package/dist/workspace-defaults/files/catalog-skills/n8n-workflow-operator/references/validation-and-handoff.md +40 -0
- package/dist/workspace-defaults/files/catalog-skills/n8n-workflow-operator/references/workflow-authoring.md +46 -0
- package/dist/workspace-defaults/files/skills/artifact-builder/references/crm-dashboard.md +1 -1
- package/dist/workspace-defaults/files/skills/document-composer/SKILL.md +22 -3
- package/package.json +1 -1
- package/dist/drizzle/0261_managed_harness_multiplayer.sql +0 -313
- package/dist/drizzle/0262_parallel_harness_threads.sql +0 -9
- package/dist/drizzle/0264_harness_tool_execution_ledger.sql +0 -113
- package/dist/plugins/company-data/README.md +0 -49
- package/dist/plugins/company-data/package.json +0 -21
- package/dist/plugins/company-data/src/index.ts +0 -30
- package/dist/plugins/company-data/src/manifest.ts +0 -26
- package/dist/plugins/company-data/test/manifest.test.ts +0 -116
- package/dist/plugins/company-data/tsconfig.json +0 -8
- package/dist/plugins/company-etl/README.md +0 -46
- package/dist/plugins/company-etl/package.json +0 -21
- package/dist/plugins/company-etl/src/index.ts +0 -29
- package/dist/plugins/company-etl/src/manifest.ts +0 -26
- package/dist/plugins/company-etl/test/manifest.test.ts +0 -108
- package/dist/plugins/company-etl/tsconfig.json +0 -8
- package/dist/plugins/lastmile/README.md +0 -50
- package/dist/plugins/lastmile/package.json +0 -23
- package/dist/plugins/lastmile/smoke/lastmile-plugin-smoke.mjs +0 -661
- package/dist/plugins/lastmile/src/api/tasks-adapter.ts +0 -572
- package/dist/plugins/lastmile/src/discovery.fixture.ts +0 -64
- package/dist/plugins/lastmile/src/index.ts +0 -46
- package/dist/plugins/lastmile/src/manifest.ts +0 -139
- package/dist/plugins/lastmile/test/api/tasks-adapter.test.ts +0 -266
- package/dist/plugins/lastmile/test/discovery.test.ts +0 -88
- package/dist/plugins/lastmile/tsconfig.json +0 -8
- package/dist/plugins/n8n/smoke/n8n-managed-app-smoke.mjs +0 -409
- package/dist/plugins/twenty/smoke/twenty-managed-app-smoke.mjs +0 -359
- package/dist/terraform/modules/app/agentcore-code-interpreter/Dockerfile.sandbox-base +0 -61
- package/dist/terraform/modules/app/agentcore-code-interpreter/sandbox/sitecustomize.py +0 -227
- package/dist/terraform/modules/app/agentcore-code-interpreter/sandbox/test_sitecustomize.py +0 -277
- package/dist/terraform/modules/app/agentcore-code-interpreter/scripts/build_and_push_sandbox_base.sh +0 -70
- package/dist/terraform/modules/app/agentcore-gateway/main.tf +0 -305
- package/dist/terraform/modules/app/agentcore-gateway/outputs.tf +0 -44
- package/dist/terraform/modules/app/agentcore-gateway/scripts/delete_gateway.sh +0 -93
- package/dist/terraform/modules/app/agentcore-gateway/scripts/read_gateway.sh +0 -33
- package/dist/terraform/modules/app/agentcore-gateway/scripts/reconcile_gateway.sh +0 -543
- package/dist/terraform/modules/app/agentcore-gateway/variables.tf +0 -70
- package/dist/terraform/modules/app/agentcore-harness/main.tf +0 -330
- package/dist/terraform/modules/app/agentcore-harness/outputs.tf +0 -72
- package/dist/terraform/modules/app/agentcore-harness/scripts/delete_harness.sh +0 -5
- package/dist/terraform/modules/app/agentcore-harness/scripts/harness-lifecycle.mjs +0 -430
- package/dist/terraform/modules/app/agentcore-harness/scripts/harness-readback.mjs +0 -180
- package/dist/terraform/modules/app/agentcore-harness/scripts/harness-tool-contract.mjs +0 -193
- package/dist/terraform/modules/app/agentcore-harness/scripts/prune_harness.sh +0 -5
- package/dist/terraform/modules/app/agentcore-harness/scripts/read_harness.sh +0 -5
- package/dist/terraform/modules/app/agentcore-harness/scripts/reconcile_harness.sh +0 -5
- package/dist/terraform/modules/app/agentcore-harness/variables.tf +0 -96
- package/dist/terraform/modules/app/hindsight-memory/README.md +0 -89
- package/dist/terraform/modules/app/hindsight-memory/main.tf +0 -411
- package/dist/terraform/modules/app/lambda-api/agentcore-harness-alarms.tf +0 -88
- package/dist/terraform/modules/app/lambda-api/agentcore-identity-alarms.tf +0 -91
- package/dist/terraform/modules/data/bedrock-knowledge-base/main.tf +0 -102
- /package/dist/{plugins/n8n/terraform → terraform/modules/app}/n8n/outputs.tf +0 -0
- /package/dist/{plugins/n8n/terraform → terraform/modules/app}/n8n/scripts/sync-database.py +0 -0
- /package/dist/{plugins/n8n/terraform → terraform/modules/app}/n8n/scripts/test_sync_database.py +0 -0
- /package/dist/{plugins/n8n/terraform → terraform/modules/app}/n8n/variables.tf +0 -0
- /package/dist/{plugins/twenty/terraform → terraform/modules/app}/twenty/README.md +0 -0
- /package/dist/{plugins/twenty/terraform → terraform/modules/app}/twenty/main.tf +0 -0
- /package/dist/{plugins/twenty/terraform → terraform/modules/app}/twenty/outputs.tf +0 -0
- /package/dist/{plugins/twenty/terraform → terraform/modules/app}/twenty/variables.tf +0 -0
|
@@ -1,277 +0,0 @@
|
|
|
1
|
-
"""Unit tests for the AgentCore sandbox base-image stdio token redactor.
|
|
2
|
-
|
|
3
|
-
Tests hit ``_RedactingStream`` directly with a test-owned stream instead of
|
|
4
|
-
mutating ``sys.stdout`` — that side-steps pytest's capture machinery, which
|
|
5
|
-
owns sys.stdout at test time and would otherwise see writes before the
|
|
6
|
-
wrapper does. Correctness of the sys.stdout replacement path is proven by
|
|
7
|
-
a separate end-to-end test that runs the module as a subprocess with its
|
|
8
|
-
auto-install path active.
|
|
9
|
-
"""
|
|
10
|
-
|
|
11
|
-
from __future__ import annotations
|
|
12
|
-
|
|
13
|
-
import base64
|
|
14
|
-
import binascii
|
|
15
|
-
import importlib.util
|
|
16
|
-
import io
|
|
17
|
-
import os
|
|
18
|
-
import subprocess
|
|
19
|
-
import sys
|
|
20
|
-
import urllib.parse
|
|
21
|
-
|
|
22
|
-
import pytest
|
|
23
|
-
|
|
24
|
-
os.environ["THINKWORK_SANDBOX_SCRUBBER"] = "off"
|
|
25
|
-
|
|
26
|
-
_HERE = os.path.dirname(os.path.abspath(__file__))
|
|
27
|
-
_SPEC = importlib.util.spec_from_file_location(
|
|
28
|
-
"sandbox_base_sitecustomize",
|
|
29
|
-
os.path.join(_HERE, "sitecustomize.py"),
|
|
30
|
-
)
|
|
31
|
-
assert _SPEC is not None and _SPEC.loader is not None
|
|
32
|
-
sc = importlib.util.module_from_spec(_SPEC)
|
|
33
|
-
_SPEC.loader.exec_module(sc)
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
class _StringSink:
|
|
37
|
-
"""Minimal sys.stdout-shaped target: the wrapper writes strings into us,
|
|
38
|
-
we collect them as UTF-8 bytes. Also exposes ``.buffer`` so bytes-path
|
|
39
|
-
writes have a path through the wrapper."""
|
|
40
|
-
|
|
41
|
-
def __init__(self) -> None:
|
|
42
|
-
self.str_bytes = bytearray()
|
|
43
|
-
self.buffer = io.BytesIO()
|
|
44
|
-
self.name = "test-sink"
|
|
45
|
-
self.encoding = "utf-8"
|
|
46
|
-
self.errors = "strict"
|
|
47
|
-
|
|
48
|
-
def write(self, s: str) -> int:
|
|
49
|
-
self.str_bytes.extend(s.encode("utf-8", errors="replace"))
|
|
50
|
-
return len(s)
|
|
51
|
-
|
|
52
|
-
def flush(self) -> None:
|
|
53
|
-
pass
|
|
54
|
-
|
|
55
|
-
def captured(self) -> str:
|
|
56
|
-
combined = bytes(self.str_bytes) + self.buffer.getvalue()
|
|
57
|
-
return combined.decode("utf-8", errors="replace")
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
@pytest.fixture
|
|
61
|
-
def wrapper() -> tuple:
|
|
62
|
-
"""A fresh redactor wrapping a new sink, with an empty token set."""
|
|
63
|
-
sc.clear_tokens()
|
|
64
|
-
sink = _StringSink()
|
|
65
|
-
stream = sc._RedactingStream(sink, "test-stdout") # type: ignore[attr-defined]
|
|
66
|
-
yield stream, sink
|
|
67
|
-
stream.flush()
|
|
68
|
-
sc.clear_tokens()
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
def test_cleartext_is_redacted(wrapper):
|
|
72
|
-
stream, sink = wrapper
|
|
73
|
-
token = "ghp_abc123abc123abc123"
|
|
74
|
-
sc.register_token(token)
|
|
75
|
-
stream.write(f"the token was {token} and not noticeable\n")
|
|
76
|
-
stream.flush()
|
|
77
|
-
text = sink.captured()
|
|
78
|
-
assert token not in text
|
|
79
|
-
assert "<redacted>" in text
|
|
80
|
-
assert "the token was" in text
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
def test_base64_form_is_redacted(wrapper):
|
|
84
|
-
stream, sink = wrapper
|
|
85
|
-
token = "ghp_abc123abc123abc123"
|
|
86
|
-
sc.register_token(token)
|
|
87
|
-
encoded = base64.b64encode(token.encode()).decode()
|
|
88
|
-
stream.write(f"encoded={encoded}\n")
|
|
89
|
-
stream.flush()
|
|
90
|
-
text = sink.captured()
|
|
91
|
-
assert encoded not in text
|
|
92
|
-
assert "<redacted>" in text
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
def test_urlsafe_base64_form_is_redacted(wrapper):
|
|
96
|
-
stream, sink = wrapper
|
|
97
|
-
token = "some_opaque_secret_for_urlsafe"
|
|
98
|
-
sc.register_token(token)
|
|
99
|
-
encoded = base64.urlsafe_b64encode(token.encode()).decode()
|
|
100
|
-
stream.write(f"encoded={encoded}\n")
|
|
101
|
-
stream.flush()
|
|
102
|
-
text = sink.captured()
|
|
103
|
-
assert encoded not in text
|
|
104
|
-
assert "<redacted>" in text
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
def test_url_encoded_form_is_redacted(wrapper):
|
|
108
|
-
stream, sink = wrapper
|
|
109
|
-
token = "token with spaces & special=chars"
|
|
110
|
-
sc.register_token(token)
|
|
111
|
-
encoded = urllib.parse.quote(token, safe="")
|
|
112
|
-
stream.write(f"in_url={encoded}\n")
|
|
113
|
-
stream.flush()
|
|
114
|
-
text = sink.captured()
|
|
115
|
-
assert encoded not in text
|
|
116
|
-
assert "<redacted>" in text
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
def test_hex_form_is_redacted(wrapper):
|
|
120
|
-
stream, sink = wrapper
|
|
121
|
-
token = "0123456789abcdef01234567"
|
|
122
|
-
sc.register_token(token)
|
|
123
|
-
encoded = binascii.hexlify(token.encode()).decode()
|
|
124
|
-
stream.write(f"hex={encoded}\n")
|
|
125
|
-
stream.flush()
|
|
126
|
-
text = sink.captured()
|
|
127
|
-
assert encoded not in text
|
|
128
|
-
assert "<redacted>" in text
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
def test_register_after_first_write_still_redacts_subsequent(wrapper):
|
|
132
|
-
stream, sink = wrapper
|
|
133
|
-
stream.write("before registration\n")
|
|
134
|
-
token = "late_registered_token_value"
|
|
135
|
-
sc.register_token(token)
|
|
136
|
-
stream.write(f"after {token} registration\n")
|
|
137
|
-
stream.flush()
|
|
138
|
-
text = sink.captured()
|
|
139
|
-
assert "before registration" in text
|
|
140
|
-
assert token not in text
|
|
141
|
-
assert "<redacted>" in text
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
def test_split_write_across_boundary_is_caught(wrapper):
|
|
145
|
-
stream, sink = wrapper
|
|
146
|
-
token = "ghp_splitfragmenttokenvalue"
|
|
147
|
-
sc.register_token(token)
|
|
148
|
-
stream.write(token[:10])
|
|
149
|
-
stream.write(token[10:])
|
|
150
|
-
stream.write("\n")
|
|
151
|
-
stream.flush()
|
|
152
|
-
text = sink.captured()
|
|
153
|
-
assert token not in text
|
|
154
|
-
assert "<redacted>" in text
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
def test_short_token_below_min_len_is_ignored(wrapper):
|
|
158
|
-
"""Redacting 3-char strings would produce wildcard false-positives."""
|
|
159
|
-
stream, sink = wrapper
|
|
160
|
-
sc.register_token("AQ")
|
|
161
|
-
stream.write("AQ should appear unchanged because AQ is too short to redact\n")
|
|
162
|
-
stream.flush()
|
|
163
|
-
text = sink.captured()
|
|
164
|
-
assert "AQ" in text
|
|
165
|
-
assert "<redacted>" not in text
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
def test_bytes_path_through_buffer(wrapper):
|
|
169
|
-
stream, sink = wrapper
|
|
170
|
-
token = "some_token_value_we_registered"
|
|
171
|
-
sc.register_token(token)
|
|
172
|
-
# Write bytes through the wrapper — it routes into sink.buffer.
|
|
173
|
-
stream.write(b"\x00\x01\x02binary with " + token.encode() + b" embedded\n")
|
|
174
|
-
stream.flush()
|
|
175
|
-
text = sink.captured()
|
|
176
|
-
assert token not in text
|
|
177
|
-
assert "<redacted>" in text
|
|
178
|
-
assert "binary with" in text
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
def test_clear_tokens_disables_redaction_for_later_writes(wrapper):
|
|
182
|
-
stream, sink = wrapper
|
|
183
|
-
token = "a_token_that_will_be_cleared_soon"
|
|
184
|
-
sc.register_token(token)
|
|
185
|
-
stream.write(f"before clear: {token}\n")
|
|
186
|
-
sc.clear_tokens()
|
|
187
|
-
stream.write(f"after clear: {token}\n")
|
|
188
|
-
stream.flush()
|
|
189
|
-
text = sink.captured()
|
|
190
|
-
assert text.count("<redacted>") == 1
|
|
191
|
-
assert text.count(token) == 1
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
def test_register_tokens_plural_is_equivalent(wrapper):
|
|
195
|
-
stream, sink = wrapper
|
|
196
|
-
sc.register_tokens(["first_registered_token_abc", "second_registered_token_def"])
|
|
197
|
-
stream.write("first_registered_token_abc / second_registered_token_def\n")
|
|
198
|
-
stream.flush()
|
|
199
|
-
text = sink.captured()
|
|
200
|
-
assert "first_registered_token_abc" not in text
|
|
201
|
-
assert "second_registered_token_def" not in text
|
|
202
|
-
assert text.count("<redacted>") >= 2
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
def test_empty_token_is_ignored(wrapper):
|
|
206
|
-
stream, sink = wrapper
|
|
207
|
-
sc.register_token("")
|
|
208
|
-
stream.write("nothing to redact here\n")
|
|
209
|
-
stream.flush()
|
|
210
|
-
text = sink.captured()
|
|
211
|
-
assert "nothing to redact here" in text
|
|
212
|
-
assert "<redacted>" not in text
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
def test_no_crash_on_raw_bytes_without_matching_token(wrapper):
|
|
216
|
-
stream, sink = wrapper
|
|
217
|
-
sc.register_token("something_registered_but_not_present")
|
|
218
|
-
stream.write(b"\x00\x01\x02binary bytes\n")
|
|
219
|
-
stream.flush()
|
|
220
|
-
text = sink.captured()
|
|
221
|
-
assert "binary bytes" in text
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
# --- End-to-end: the auto-install path ---------------------------------------
|
|
225
|
-
#
|
|
226
|
-
# Tests above exercise _RedactingStream directly, which is fine for the
|
|
227
|
-
# redaction logic itself but bypasses the sitecustomize auto-install step
|
|
228
|
-
# that replaces sys.stdout on interpreter startup. This subprocess test
|
|
229
|
-
# proves that step works — a fresh Python process, with PYTHONPATH pointed
|
|
230
|
-
# at the module directory and THINKWORK_SANDBOX_SCRUBBER unset, installs
|
|
231
|
-
# the wrapper on its own and redacts a registered token before stdout is
|
|
232
|
-
# flushed.
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
def test_auto_install_redacts_on_fresh_interpreter(tmp_path):
|
|
236
|
-
script = tmp_path / "run.py"
|
|
237
|
-
script.write_text(
|
|
238
|
-
"""
|
|
239
|
-
import sys, sitecustomize
|
|
240
|
-
sitecustomize.register_token("ghp_autoInstallValueTokenX")
|
|
241
|
-
print("auto-install redacts ghp_autoInstallValueTokenX mid-line here")
|
|
242
|
-
""".strip()
|
|
243
|
-
)
|
|
244
|
-
env = os.environ.copy()
|
|
245
|
-
# Strip our opt-out so sitecustomize installs on import.
|
|
246
|
-
env.pop("THINKWORK_SANDBOX_SCRUBBER", None)
|
|
247
|
-
# Prepend the module dir so Python's startup hook finds our
|
|
248
|
-
# sitecustomize.py before any installed one.
|
|
249
|
-
env["PYTHONPATH"] = _HERE + os.pathsep + env.get("PYTHONPATH", "")
|
|
250
|
-
out = subprocess.run(
|
|
251
|
-
[sys.executable, str(script)],
|
|
252
|
-
env=env,
|
|
253
|
-
check=True,
|
|
254
|
-
capture_output=True,
|
|
255
|
-
text=True,
|
|
256
|
-
)
|
|
257
|
-
assert "ghp_autoInstallValueTokenX" not in out.stdout
|
|
258
|
-
assert "<redacted>" in out.stdout
|
|
259
|
-
assert "auto-install redacts" in out.stdout
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
def test_installed_reports_true_after_auto_install(tmp_path):
|
|
263
|
-
"""Probe ``installed()`` from a fresh interpreter — confirms the startup
|
|
264
|
-
hook ran (and set the flag)."""
|
|
265
|
-
script = tmp_path / "run.py"
|
|
266
|
-
script.write_text("import sitecustomize; print(sitecustomize.installed())")
|
|
267
|
-
env = os.environ.copy()
|
|
268
|
-
env.pop("THINKWORK_SANDBOX_SCRUBBER", None)
|
|
269
|
-
env["PYTHONPATH"] = _HERE + os.pathsep + env.get("PYTHONPATH", "")
|
|
270
|
-
out = subprocess.run(
|
|
271
|
-
[sys.executable, str(script)],
|
|
272
|
-
env=env,
|
|
273
|
-
check=True,
|
|
274
|
-
capture_output=True,
|
|
275
|
-
text=True,
|
|
276
|
-
)
|
|
277
|
-
assert out.stdout.strip() == "True"
|
package/dist/terraform/modules/app/agentcore-code-interpreter/scripts/build_and_push_sandbox_base.sh
DELETED
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
################################################################################
|
|
3
|
-
# build_and_push_sandbox_base.sh — build + push the AgentCore sandbox base
|
|
4
|
-
# image to the stage's ECR repo. Invoked by CI; also safe to run locally
|
|
5
|
-
# once ``thinkwork doctor`` reports green.
|
|
6
|
-
#
|
|
7
|
-
# Usage (from repo root):
|
|
8
|
-
# bash terraform/modules/app/agentcore-code-interpreter/scripts/build_and_push_sandbox_base.sh \
|
|
9
|
-
# --stage dev --region us-east-1
|
|
10
|
-
#
|
|
11
|
-
# The script:
|
|
12
|
-
# 1. Reads the ECR repo URL from ``terraform output -raw`` (must be run
|
|
13
|
-
# after ``terraform apply`` lands the agentcore-code-interpreter
|
|
14
|
-
# module for the target stage).
|
|
15
|
-
# 2. docker build the Dockerfile.sandbox-base with the repo root as
|
|
16
|
-
# context (so the COPY can reach packages/).
|
|
17
|
-
# 3. Tags with the current git SHA (immutable tag per the ECR repo
|
|
18
|
-
# config) and ``:latest``.
|
|
19
|
-
# 4. docker push both tags after ``aws ecr get-login-password``.
|
|
20
|
-
################################################################################
|
|
21
|
-
set -euo pipefail
|
|
22
|
-
|
|
23
|
-
STAGE=""
|
|
24
|
-
REGION=""
|
|
25
|
-
|
|
26
|
-
while [[ $# -gt 0 ]]; do
|
|
27
|
-
case "$1" in
|
|
28
|
-
--stage) STAGE="$2"; shift 2 ;;
|
|
29
|
-
--region) REGION="$2"; shift 2 ;;
|
|
30
|
-
*) echo "unknown arg: $1" >&2; exit 2 ;;
|
|
31
|
-
esac
|
|
32
|
-
done
|
|
33
|
-
|
|
34
|
-
: "${STAGE:?--stage is required}"
|
|
35
|
-
: "${REGION:?--region is required}"
|
|
36
|
-
|
|
37
|
-
# Resolve the repo URL from terraform state. Tolerant: if the caller runs
|
|
38
|
-
# this before the module applies, fall back to the deterministic name.
|
|
39
|
-
REPO_URL="$(
|
|
40
|
-
terraform -chdir="terraform/examples/greenfield" output -raw \
|
|
41
|
-
sandbox_base_ecr_repository_url 2>/dev/null \
|
|
42
|
-
|| echo ""
|
|
43
|
-
)"
|
|
44
|
-
if [[ -z "$REPO_URL" ]]; then
|
|
45
|
-
ACCOUNT_ID="$(aws sts get-caller-identity --query Account --output text)"
|
|
46
|
-
REPO_URL="${ACCOUNT_ID}.dkr.ecr.${REGION}.amazonaws.com/thinkwork-${STAGE}-sandbox-base"
|
|
47
|
-
fi
|
|
48
|
-
|
|
49
|
-
GIT_SHA="$(git rev-parse --short HEAD)"
|
|
50
|
-
IMAGE_TAG="${GIT_SHA}"
|
|
51
|
-
|
|
52
|
-
echo "[sandbox-base] repo: ${REPO_URL}"
|
|
53
|
-
echo "[sandbox-base] tag: ${IMAGE_TAG}"
|
|
54
|
-
|
|
55
|
-
# ECR login
|
|
56
|
-
aws ecr get-login-password --region "${REGION}" \
|
|
57
|
-
| docker login --username AWS --password-stdin "${REPO_URL%/*}"
|
|
58
|
-
|
|
59
|
-
# Build with repo root as the context so COPY can reach packages/.
|
|
60
|
-
docker build \
|
|
61
|
-
--platform=linux/amd64 \
|
|
62
|
-
-f terraform/modules/app/agentcore-code-interpreter/Dockerfile.sandbox-base \
|
|
63
|
-
-t "${REPO_URL}:${IMAGE_TAG}" \
|
|
64
|
-
-t "${REPO_URL}:latest" \
|
|
65
|
-
.
|
|
66
|
-
|
|
67
|
-
docker push "${REPO_URL}:${IMAGE_TAG}"
|
|
68
|
-
docker push "${REPO_URL}:latest"
|
|
69
|
-
|
|
70
|
-
echo "[sandbox-base] pushed ${REPO_URL}:${IMAGE_TAG}"
|
|
@@ -1,305 +0,0 @@
|
|
|
1
|
-
################################################################################
|
|
2
|
-
# THINK-316 U1 — AgentCore Gateway + Cedar proof boundary
|
|
3
|
-
#
|
|
4
|
-
# AgentCore Gateway and Policy do not yet have complete Terraform resources.
|
|
5
|
-
# The module therefore owns their lifecycle with idempotent AWS CLI scripts,
|
|
6
|
-
# matching the repo's existing AgentCore Memory pattern. All resources are
|
|
7
|
-
# proof-only and opt in; destroy removes policy -> target -> gateway -> engine.
|
|
8
|
-
################################################################################
|
|
9
|
-
|
|
10
|
-
terraform {
|
|
11
|
-
required_providers {
|
|
12
|
-
external = {
|
|
13
|
-
source = "hashicorp/external"
|
|
14
|
-
version = ">= 2.3.0"
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
locals {
|
|
20
|
-
gateway_name = "thinkwork-${var.stage}-multiplayer-proof"
|
|
21
|
-
target_name = "Thinkwork${replace(title(replace(var.stage, "-", " ")), " ", "")}OwnerProof"
|
|
22
|
-
# Policy resources accept only letters, digits, and underscores and must
|
|
23
|
-
# begin with a letter. Gateway names have a separate, hyphen-friendly API.
|
|
24
|
-
policy_engine_name = "Thinkwork${replace(title(replace(var.stage, "-", " ")), " ", "")}MultiplayerProof"
|
|
25
|
-
policy_name = "Thinkwork${replace(title(replace(var.stage, "-", " ")), " ", "")}OwnerIsolation"
|
|
26
|
-
# The reconciler owns the Gateway target schemas and Cedar boundary. Hash
|
|
27
|
-
# the executable itself so contract-only edits cannot leave AWS stale.
|
|
28
|
-
reconciler_hash = filesha256("${path.module}/scripts/reconcile_gateway.sh")
|
|
29
|
-
configuration_hash = nonsensitive(sha256(jsonencode({
|
|
30
|
-
discovery_url = var.discovery_url
|
|
31
|
-
audience = var.gateway_audience
|
|
32
|
-
target_url = var.target_base_url
|
|
33
|
-
provider_arn = var.oauth_credential_provider_arn
|
|
34
|
-
secret_arn = var.oauth_credential_secret_arn
|
|
35
|
-
return_url = var.oauth_return_url
|
|
36
|
-
owners = var.proof_owner_allowlist
|
|
37
|
-
capability_contract = "mcp-list-call-sandbox-builtin-platform-v7-user-questions"
|
|
38
|
-
# URL-mode OAuth elicitation requires the 2025-11-25 protocol and cannot
|
|
39
|
-
# be added to an existing Gateway, so changing this contract must replace
|
|
40
|
-
# the script-owned Gateway lifecycle.
|
|
41
|
-
protocol_versions = ["2025-03-26", "2025-11-25"]
|
|
42
|
-
reconciler = local.reconciler_hash
|
|
43
|
-
})))
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
resource "aws_iam_role" "gateway_execution" {
|
|
47
|
-
count = var.enabled ? 1 : 0
|
|
48
|
-
name = "thinkwork-${var.stage}-agentcore-proof-gateway-role"
|
|
49
|
-
|
|
50
|
-
assume_role_policy = jsonencode({
|
|
51
|
-
Version = "2012-10-17"
|
|
52
|
-
Statement = [{
|
|
53
|
-
Sid = "AllowAgentCoreGateway"
|
|
54
|
-
Effect = "Allow"
|
|
55
|
-
Principal = { Service = "bedrock-agentcore.amazonaws.com" }
|
|
56
|
-
Action = "sts:AssumeRole"
|
|
57
|
-
Condition = {
|
|
58
|
-
StringEquals = { "aws:SourceAccount" = var.account_id }
|
|
59
|
-
ArnLike = { "aws:SourceArn" = "arn:aws:bedrock-agentcore:${var.region}:${var.account_id}:*" }
|
|
60
|
-
}
|
|
61
|
-
}]
|
|
62
|
-
})
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
resource "aws_iam_role_policy" "gateway_execution" {
|
|
66
|
-
count = var.enabled ? 1 : 0
|
|
67
|
-
name = "thinkwork-${var.stage}-agentcore-proof-gateway"
|
|
68
|
-
role = aws_iam_role.gateway_execution[0].id
|
|
69
|
-
|
|
70
|
-
# U1 bootstraps service-generated Gateway/Policy ids, so the dedicated
|
|
71
|
-
# proof role uses type-scoped wildcards for those two resource families.
|
|
72
|
-
# Identity and Secrets access are already scoped to exact returned ARNs;
|
|
73
|
-
# U9 replaces the bootstrap wildcards with the selected resource ids.
|
|
74
|
-
policy = jsonencode({
|
|
75
|
-
Version = "2012-10-17"
|
|
76
|
-
Statement = [
|
|
77
|
-
{
|
|
78
|
-
Sid = "PolicyEngineConfiguration"
|
|
79
|
-
Effect = "Allow"
|
|
80
|
-
Action = ["bedrock-agentcore:GetPolicyEngine"]
|
|
81
|
-
Resource = "arn:aws:bedrock-agentcore:${var.region}:${var.account_id}:policy-engine/*"
|
|
82
|
-
},
|
|
83
|
-
{
|
|
84
|
-
Sid = "PolicyAuthorization"
|
|
85
|
-
Effect = "Allow"
|
|
86
|
-
Action = [
|
|
87
|
-
"bedrock-agentcore:AuthorizeAction",
|
|
88
|
-
"bedrock-agentcore:PartiallyAuthorizeActions",
|
|
89
|
-
]
|
|
90
|
-
Resource = [
|
|
91
|
-
"arn:aws:bedrock-agentcore:${var.region}:${var.account_id}:policy-engine/*",
|
|
92
|
-
"arn:aws:bedrock-agentcore:${var.region}:${var.account_id}:gateway/*",
|
|
93
|
-
]
|
|
94
|
-
},
|
|
95
|
-
{
|
|
96
|
-
Sid = "GatewayWorkloadToken"
|
|
97
|
-
Effect = "Allow"
|
|
98
|
-
# A CUSTOM_JWT Gateway uses the JWT-specific exchange action at
|
|
99
|
-
# runtime even though the current outbound-auth guide shows only the
|
|
100
|
-
# generic workload-token action in its example role.
|
|
101
|
-
Action = [
|
|
102
|
-
"bedrock-agentcore:GetWorkloadAccessToken",
|
|
103
|
-
"bedrock-agentcore:GetWorkloadAccessTokenForJWT",
|
|
104
|
-
]
|
|
105
|
-
Resource = [
|
|
106
|
-
"arn:aws:bedrock-agentcore:${var.region}:${var.account_id}:workload-identity-directory/default",
|
|
107
|
-
"arn:aws:bedrock-agentcore:${var.region}:${var.account_id}:workload-identity-directory/default/workload-identity/${local.gateway_name}-*",
|
|
108
|
-
]
|
|
109
|
-
},
|
|
110
|
-
{
|
|
111
|
-
Sid = "ExactOauthCredential"
|
|
112
|
-
Effect = "Allow"
|
|
113
|
-
Action = ["bedrock-agentcore:GetResourceOauth2Token"]
|
|
114
|
-
# Identity evaluates the request against the directory, concrete
|
|
115
|
-
# Gateway-managed workload identity, token vault, and provider. All
|
|
116
|
-
# four resource families must be present for resource-scoped access.
|
|
117
|
-
Resource = [
|
|
118
|
-
"arn:aws:bedrock-agentcore:${var.region}:${var.account_id}:workload-identity-directory/default",
|
|
119
|
-
"arn:aws:bedrock-agentcore:${var.region}:${var.account_id}:workload-identity-directory/default/workload-identity/${local.gateway_name}-*",
|
|
120
|
-
"arn:aws:bedrock-agentcore:${var.region}:${var.account_id}:token-vault/default",
|
|
121
|
-
var.oauth_credential_provider_arn,
|
|
122
|
-
]
|
|
123
|
-
},
|
|
124
|
-
{
|
|
125
|
-
Sid = "ExactOauthSecret"
|
|
126
|
-
Effect = "Allow"
|
|
127
|
-
Action = ["secretsmanager:GetSecretValue"]
|
|
128
|
-
Resource = var.oauth_credential_secret_arn
|
|
129
|
-
},
|
|
130
|
-
]
|
|
131
|
-
})
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
# Keep the stable Gateway/target/policy identity separate from configuration
|
|
135
|
-
# reconciliation. A terraform_data replacement runs destroy provisioners, so
|
|
136
|
-
# coupling the configuration hash to this resource used to tear down the live
|
|
137
|
-
# Gateway before recreating it on every ordinary contract update.
|
|
138
|
-
moved {
|
|
139
|
-
from = terraform_data.gateway_lifecycle
|
|
140
|
-
to = terraform_data.gateway_identity
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
resource "terraform_data" "gateway_identity" {
|
|
144
|
-
count = var.enabled ? 1 : 0
|
|
145
|
-
|
|
146
|
-
input = {
|
|
147
|
-
region = var.region
|
|
148
|
-
gateway_name = local.gateway_name
|
|
149
|
-
target_name = local.target_name
|
|
150
|
-
policy_engine_name = local.policy_engine_name
|
|
151
|
-
policy_name = local.policy_name
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
# The moved predecessor stored the full configuration hash in
|
|
155
|
-
# triggers_replace. Ignore that retired state-only attribute during the
|
|
156
|
-
# one-time split so Terraform can move the live identity without running its
|
|
157
|
-
# destroy provisioner. The dedicated configuration resource below owns all
|
|
158
|
-
# subsequent reconciliation revisions.
|
|
159
|
-
lifecycle {
|
|
160
|
-
ignore_changes = [triggers_replace]
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
# A brand-new identity must exist before the external data source reads it.
|
|
164
|
-
# The configuration resource performs the same idempotent reconciliation a
|
|
165
|
-
# second time on initial creation, then becomes the sole revision trigger.
|
|
166
|
-
provisioner "local-exec" {
|
|
167
|
-
command = "bash ${path.module}/scripts/reconcile_gateway.sh"
|
|
168
|
-
environment = {
|
|
169
|
-
AWS_REGION = var.region
|
|
170
|
-
GATEWAY_NAME = local.gateway_name
|
|
171
|
-
GATEWAY_ROLE_ARN = aws_iam_role.gateway_execution[0].arn
|
|
172
|
-
GATEWAY_DISCOVERY_URL = var.discovery_url
|
|
173
|
-
GATEWAY_AUDIENCE = var.gateway_audience
|
|
174
|
-
TARGET_NAME = local.target_name
|
|
175
|
-
TARGET_BASE_URL = trimsuffix(var.target_base_url, "/")
|
|
176
|
-
OAUTH_CREDENTIAL_PROVIDER_ARN = var.oauth_credential_provider_arn
|
|
177
|
-
OAUTH_RETURN_URL = var.oauth_return_url
|
|
178
|
-
POLICY_ENGINE_NAME = local.policy_engine_name
|
|
179
|
-
POLICY_NAME = local.policy_name
|
|
180
|
-
PROOF_OWNER_ALLOWLIST = var.proof_owner_allowlist
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
provisioner "local-exec" {
|
|
185
|
-
when = destroy
|
|
186
|
-
command = "bash ${path.module}/scripts/delete_gateway.sh"
|
|
187
|
-
environment = {
|
|
188
|
-
AWS_REGION = self.input.region
|
|
189
|
-
GATEWAY_NAME = self.input.gateway_name
|
|
190
|
-
TARGET_NAME = self.input.target_name
|
|
191
|
-
POLICY_ENGINE_NAME = self.input.policy_engine_name
|
|
192
|
-
POLICY_NAME = self.input.policy_name
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
depends_on = [aws_iam_role_policy.gateway_execution]
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
# Configuration revisions reconcile the stable identity in place. This
|
|
200
|
-
# terraform_data instance intentionally has no destroy provisioner: replacing
|
|
201
|
-
# it is only a signal to rerun the idempotent reconciler, never permission to
|
|
202
|
-
# delete a Gateway, target, policy, or engine.
|
|
203
|
-
resource "terraform_data" "gateway_configuration" {
|
|
204
|
-
count = var.enabled ? 1 : 0
|
|
205
|
-
|
|
206
|
-
input = local.configuration_hash
|
|
207
|
-
triggers_replace = [local.configuration_hash]
|
|
208
|
-
|
|
209
|
-
provisioner "local-exec" {
|
|
210
|
-
command = "bash ${path.module}/scripts/reconcile_gateway.sh"
|
|
211
|
-
environment = {
|
|
212
|
-
AWS_REGION = var.region
|
|
213
|
-
GATEWAY_NAME = local.gateway_name
|
|
214
|
-
GATEWAY_ROLE_ARN = aws_iam_role.gateway_execution[0].arn
|
|
215
|
-
GATEWAY_DISCOVERY_URL = var.discovery_url
|
|
216
|
-
GATEWAY_AUDIENCE = var.gateway_audience
|
|
217
|
-
TARGET_NAME = local.target_name
|
|
218
|
-
TARGET_BASE_URL = trimsuffix(var.target_base_url, "/")
|
|
219
|
-
OAUTH_CREDENTIAL_PROVIDER_ARN = var.oauth_credential_provider_arn
|
|
220
|
-
OAUTH_RETURN_URL = var.oauth_return_url
|
|
221
|
-
POLICY_ENGINE_NAME = local.policy_engine_name
|
|
222
|
-
POLICY_NAME = local.policy_name
|
|
223
|
-
PROOF_OWNER_ALLOWLIST = var.proof_owner_allowlist
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
depends_on = [terraform_data.gateway_identity]
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
data "external" "gateway_state" {
|
|
231
|
-
count = var.enabled ? 1 : 0
|
|
232
|
-
# Identity creation reconciles the Gateway before this read. Do not depend
|
|
233
|
-
# on the revision signal: doing so defers this read on every config update,
|
|
234
|
-
# makes the stable Gateway id unknown during planning, and falsely forces
|
|
235
|
-
# all CloudWatch delivery resources to replace.
|
|
236
|
-
depends_on = [terraform_data.gateway_identity]
|
|
237
|
-
program = ["bash", "${path.module}/scripts/read_gateway.sh"]
|
|
238
|
-
|
|
239
|
-
query = {
|
|
240
|
-
region = var.region
|
|
241
|
-
gateway_name = local.gateway_name
|
|
242
|
-
target_name = local.target_name
|
|
243
|
-
policy_engine_name = local.policy_engine_name
|
|
244
|
-
policy_name = local.policy_name
|
|
245
|
-
}
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
################################################################################
|
|
249
|
-
# Gateway audit evidence
|
|
250
|
-
#
|
|
251
|
-
# AgentCore does not create an application-log destination for Gateways by
|
|
252
|
-
# default. Policy decisions are enforced inside the managed Gateway, so the
|
|
253
|
-
# target Lambda cannot truthfully invent a Cedar decision id. Deliver the
|
|
254
|
-
# service-owned Gateway records instead; certification correlates those records
|
|
255
|
-
# with the target-side append-only ledger and the canonical turn.
|
|
256
|
-
################################################################################
|
|
257
|
-
|
|
258
|
-
resource "aws_cloudwatch_log_group" "gateway_application" {
|
|
259
|
-
count = var.enabled ? 1 : 0
|
|
260
|
-
name = "/aws/vendedlogs/bedrock-agentcore/gateway/APPLICATION_LOGS/${data.external.gateway_state[0].result.gateway_id}"
|
|
261
|
-
retention_in_days = var.observability_retention_days
|
|
262
|
-
|
|
263
|
-
tags = {
|
|
264
|
-
Stage = var.stage
|
|
265
|
-
Purpose = "agentcore-gateway-policy-audit"
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
resource "aws_cloudwatch_log_delivery_source" "gateway_application" {
|
|
270
|
-
count = var.enabled ? 1 : 0
|
|
271
|
-
name = "thinkwork-${var.stage}-agentcore-gateway-application"
|
|
272
|
-
log_type = "APPLICATION_LOGS"
|
|
273
|
-
resource_arn = data.external.gateway_state[0].result.gateway_arn
|
|
274
|
-
|
|
275
|
-
tags = {
|
|
276
|
-
Stage = var.stage
|
|
277
|
-
Purpose = "agentcore-gateway-policy-audit"
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
resource "aws_cloudwatch_log_delivery_destination" "gateway_application" {
|
|
282
|
-
count = var.enabled ? 1 : 0
|
|
283
|
-
name = "thinkwork-${var.stage}-agentcore-gateway-cloudwatch"
|
|
284
|
-
output_format = "json"
|
|
285
|
-
|
|
286
|
-
delivery_destination_configuration {
|
|
287
|
-
destination_resource_arn = aws_cloudwatch_log_group.gateway_application[0].arn
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
tags = {
|
|
291
|
-
Stage = var.stage
|
|
292
|
-
Purpose = "agentcore-gateway-policy-audit"
|
|
293
|
-
}
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
resource "aws_cloudwatch_log_delivery" "gateway_application" {
|
|
297
|
-
count = var.enabled ? 1 : 0
|
|
298
|
-
delivery_source_name = aws_cloudwatch_log_delivery_source.gateway_application[0].name
|
|
299
|
-
delivery_destination_arn = aws_cloudwatch_log_delivery_destination.gateway_application[0].arn
|
|
300
|
-
|
|
301
|
-
tags = {
|
|
302
|
-
Stage = var.stage
|
|
303
|
-
Purpose = "agentcore-gateway-policy-audit"
|
|
304
|
-
}
|
|
305
|
-
}
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
output "gateway_id" {
|
|
2
|
-
description = "AgentCore Gateway id. Empty when disabled."
|
|
3
|
-
value = var.enabled ? data.external.gateway_state[0].result.gateway_id : ""
|
|
4
|
-
}
|
|
5
|
-
|
|
6
|
-
output "gateway_arn" {
|
|
7
|
-
description = "AgentCore Gateway ARN. Empty when disabled."
|
|
8
|
-
value = var.enabled ? data.external.gateway_state[0].result.gateway_arn : ""
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
output "gateway_url" {
|
|
12
|
-
description = "AgentCore Gateway MCP endpoint. Empty when disabled."
|
|
13
|
-
value = var.enabled ? data.external.gateway_state[0].result.gateway_url : ""
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
output "target_id" {
|
|
17
|
-
description = "Controlled OpenAPI target id. Empty when disabled."
|
|
18
|
-
value = var.enabled ? data.external.gateway_state[0].result.target_id : ""
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
output "target_name" {
|
|
22
|
-
description = "Controlled OpenAPI target name."
|
|
23
|
-
value = var.enabled ? local.target_name : ""
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
output "policy_engine_id" {
|
|
27
|
-
description = "Policy engine id. Empty when disabled."
|
|
28
|
-
value = var.enabled ? data.external.gateway_state[0].result.policy_engine_id : ""
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
output "policy_id" {
|
|
32
|
-
description = "Owner-isolation Cedar policy id. Empty when disabled."
|
|
33
|
-
value = var.enabled ? data.external.gateway_state[0].result.policy_id : ""
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
output "execution_role_arn" {
|
|
37
|
-
description = "Dedicated proof Gateway execution role ARN. Empty when disabled."
|
|
38
|
-
value = var.enabled ? aws_iam_role.gateway_execution[0].arn : ""
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
output "application_log_group_name" {
|
|
42
|
-
description = "CloudWatch log group receiving AgentCore Gateway application and policy records."
|
|
43
|
-
value = var.enabled ? aws_cloudwatch_log_group.gateway_application[0].name : ""
|
|
44
|
-
}
|