thinkwork-cli 0.13.1 → 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-4DZ266J5.js → api-client-4P4JZOBX.js} +3 -1
- package/dist/bowser-6Z6JFAIR.js +2822 -0
- package/dist/{chunk-Y2WHJECR.js → chunk-4OMDWYZQ.js} +5 -44
- package/dist/chunk-AD2NIWB5.js +6516 -0
- package/dist/chunk-DV3MRBR5.js +43 -0
- package/dist/chunk-EI5ZX7CN.js +522 -0
- package/dist/chunk-EUJ7X6YD.js +126 -0
- package/dist/chunk-GG5WKBOX.js +436 -0
- package/dist/chunk-I64EZSU5.js +358 -0
- package/dist/chunk-KMIJ5KGT.js +128 -0
- package/dist/chunk-MWQ7RO76.js +137 -0
- package/dist/chunk-OCPHU34C.js +996 -0
- package/dist/chunk-OLUQ4U2Z.js +1728 -0
- package/dist/chunk-XYM7PE2A.js +2264 -0
- package/dist/chunk-YQCYD6NE.js +3299 -0
- package/dist/cli.js +49156 -31257
- 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-2OTNTL4N.js +315 -0
- package/dist/dist-es-2XVAH4BQ.js +14726 -0
- package/dist/dist-es-3DPDUJAH.js +2000 -0
- package/dist/dist-es-BNZTBBVC.js +176 -0
- package/dist/dist-es-CD2RBDAC.js +88 -0
- package/dist/dist-es-I4FW57PS.js +88 -0
- package/dist/dist-es-KL7Y5NB2.js +377 -0
- package/dist/dist-es-KVFWJNDY.js +318 -0
- package/dist/dist-es-PS73DWNL.js +70 -0
- package/dist/dist-es-S3NZQFGT.js +170 -0
- package/dist/dist-es-SGCA4P32.js +48 -0
- package/dist/dist-es-WBM6J4RA.js +70 -0
- package/dist/dist-es-ZGVADYIF.js +485 -0
- package/dist/dist-es-ZKC2QKD2.js +377 -0
- package/dist/drizzle/0076_scheduled_jobs_marco_backfill.sql +2 -1
- package/dist/drizzle/0079_seed_tenant_customize_catalog.sql +1 -0
- package/dist/drizzle/0090_brain_schema_extraction.sql +10 -0
- package/dist/drizzle/0132_drop_computer_tables.sql +3 -0
- package/dist/drizzle/0160_compliance_event_types_plugins.sql +2 -0
- package/dist/drizzle/0214_think137_drop_judge_evidence_roi.sql +1 -0
- package/dist/drizzle/0216_drop_agent_loop_versions_goal_worker_policy.sql +1 -0
- package/dist/drizzle/0227_analyst_reader_role.sql +17 -20
- package/dist/drizzle/0230_analyst_rls.sql +28 -36
- package/dist/drizzle/0250_kg_schema_extraction.sql +27 -0
- package/dist/drizzle/0251_brain_stragglers.sql +21 -0
- package/dist/drizzle/0253_drop_brain_substrate.sql +2 -0
- package/dist/drizzle/0257_agents_runtime_allow_harness.sql +24 -0
- package/dist/drizzle/0261_native_auth_control_plane.sql +332 -0
- package/dist/drizzle/0262_auth_subscription_tickets.sql +59 -0
- package/dist/drizzle/0263_agentcore_runtime_identifier.sql +69 -0
- package/dist/drizzle/0263_drop_workos_auth_runtime.sql +236 -0
- package/dist/drizzle/0264_auth_enrollment_attempt_limits.sql +15 -0
- package/dist/drizzle/0265_eval_runs_requester_identity.sql +28 -0
- package/dist/drizzle/0265_require_fully_valid_auth_publication.sql +27 -0
- package/dist/drizzle/0266_auth_identity_recovery_grants.sql +9 -0
- package/dist/drizzle/0266_eval_profiles_runtime_type.sql +12 -0
- package/dist/drizzle/0267_ontology_candidate_rejections.sql +53 -0
- package/dist/drizzle/0268_identity_crosswalk_routing.sql +182 -0
- package/dist/drizzle/0269_ontology_system_map.sql +31 -0
- 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/event-streams-7JNLJEEL.js +894 -0
- package/dist/event-streams-KDF5RWR6.js +1515 -0
- package/dist/loadSso-ETCXTR4I.js +576 -0
- package/dist/loadSso-UBD7KUHE.js +583 -0
- package/dist/plugins/README.md +2 -2
- package/dist/plugins/catalog/package.json +1 -5
- package/dist/plugins/catalog/src/__tests__/build-catalog.test.ts +6 -4
- package/dist/plugins/catalog/src/__tests__/catalog.test.ts +21 -21
- package/dist/plugins/catalog/src/__tests__/contracts.test.ts +102 -94
- package/dist/plugins/catalog/src/__tests__/plugin-package.test.ts +7 -8
- package/dist/plugins/catalog/src/__tests__/plugin-registry.test.ts +0 -20
- package/dist/plugins/catalog/src/contracts.ts +17 -179
- package/dist/plugins/catalog/src/registry/generated-first-party.ts +0 -24
- 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-5QZNRSOO.js +821 -0
- package/dist/sso-oidc-XK673P54.js +837 -0
- package/dist/sts-OXI2UALF.js +1113 -0
- package/dist/sts-SQW4NBK3.js +1117 -0
- package/dist/terraform/examples/greenfield/main.tf +391 -176
- package/dist/terraform/examples/greenfield/terraform.tfvars.example +26 -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 +253 -0
- package/dist/terraform/modules/app/agentcore-identity/outputs.tf +51 -0
- package/dist/terraform/modules/app/agentcore-identity/scripts/bootstrap_twenty_oauth_client.sh +239 -0
- package/dist/terraform/modules/app/agentcore-identity/scripts/delete_identity.sh +22 -0
- package/dist/terraform/modules/app/agentcore-identity/scripts/delete_twenty_identity.sh +16 -0
- 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 +55 -0
- package/dist/terraform/modules/app/agentcore-identity/scripts/reconcile_identity.sh +73 -0
- package/dist/terraform/modules/app/agentcore-identity/scripts/reconcile_twenty_identity.sh +45 -0
- package/dist/terraform/modules/app/agentcore-identity/scripts/reconcile_twenty_provider.mjs +123 -0
- 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 +60 -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/appsync-subscriptions/main.tf +127 -21
- package/dist/terraform/modules/app/appsync-subscriptions/outputs.tf +0 -6
- package/dist/terraform/modules/app/appsync-subscriptions/variables.tf +5 -0
- 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 +3689 -233
- package/dist/terraform/modules/app/deployment-control-plane/test_runner_bundle.py +2579 -140
- 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 +531 -619
- package/dist/terraform/modules/app/lambda-api/iam-grouped.tf +350 -241
- package/dist/terraform/modules/app/lambda-api/main.tf +75 -13
- 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 -10
- package/dist/terraform/modules/app/lambda-api/runtime-config.tf +3 -21
- package/dist/terraform/modules/app/lambda-api/turn-assertion.tf +45 -0
- package/dist/terraform/modules/app/lambda-api/variables.tf +307 -121
- 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 +367 -72
- package/dist/terraform/modules/foundation/cognito/outputs.tf +47 -0
- package/dist/terraform/modules/foundation/cognito/variables.tf +131 -9
- package/dist/terraform/modules/thinkwork/README.md +5 -1
- package/dist/terraform/modules/thinkwork/main.tf +459 -234
- package/dist/terraform/modules/thinkwork/outputs.tf +44 -62
- package/dist/terraform/modules/thinkwork/variables.tf +353 -89
- package/dist/terraform/schema.graphql +69 -36
- 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/USER.md +1 -0
- 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 +3 -2
- package/dist/drizzle/0261_thread_turns_origin_turn_index.sql +0 -11
- 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/plugins/workos-auth/package.json +0 -22
- package/dist/plugins/workos-auth/src/index.ts +0 -30
- package/dist/plugins/workos-auth/src/manifest.ts +0 -40
- package/dist/plugins/workos-auth/src/provider-contract.ts +0 -39
- package/dist/plugins/workos-auth/test/manifest.test.ts +0 -94
- package/dist/plugins/workos-auth/tsconfig.json +0 -8
- 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-harness/main.tf +0 -101
- package/dist/terraform/modules/app/agentcore-harness/outputs.tf +0 -9
- package/dist/terraform/modules/app/agentcore-harness/variables.tf +0 -25
- 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/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
package/dist/terraform/modules/app/agentcore-identity/scripts/bootstrap_twenty_oauth_client.sh
ADDED
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
: "${AWS_REGION:?AWS_REGION is required}"
|
|
5
|
+
: "${TWENTY_OAUTH_ISSUER:?TWENTY_OAUTH_ISSUER is required}"
|
|
6
|
+
: "${TWENTY_CLIENT_SECRET_ARN:?TWENTY_CLIENT_SECRET_ARN is required}"
|
|
7
|
+
: "${TWENTY_CREDENTIAL_PROVIDER_NAME:?TWENTY_CREDENTIAL_PROVIDER_NAME is required}"
|
|
8
|
+
|
|
9
|
+
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
10
|
+
issuer="${TWENTY_OAUTH_ISSUER%/}"
|
|
11
|
+
|
|
12
|
+
read_client_record() {
|
|
13
|
+
aws secretsmanager get-secret-value \
|
|
14
|
+
--region "$AWS_REGION" \
|
|
15
|
+
--secret-id "$TWENTY_CLIENT_SECRET_ARN" \
|
|
16
|
+
--query SecretString \
|
|
17
|
+
--output text 2>/dev/null || printf '{}'
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
write_client_record() {
|
|
21
|
+
aws secretsmanager put-secret-value \
|
|
22
|
+
--region "$AWS_REGION" \
|
|
23
|
+
--secret-id "$TWENTY_CLIENT_SECRET_ARN" \
|
|
24
|
+
--secret-string file:///dev/stdin >/dev/null
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
# Twenty's authorize endpoint is "$issuer/authorize" (NOT /oauth/authorize —
|
|
28
|
+
# observed live on tei-e2e: the previously hard-coded path 404s). Fetch the
|
|
29
|
+
# issuer's discovery document once and take both provider endpoints from it,
|
|
30
|
+
# falling back to the documented paths when discovery is unavailable.
|
|
31
|
+
discovery="$(curl --silent --fail --max-time 15 \
|
|
32
|
+
"$issuer/.well-known/oauth-authorization-server" 2>/dev/null || printf '')"
|
|
33
|
+
provider_endpoint() {
|
|
34
|
+
local field="$1" fallback="$2"
|
|
35
|
+
local discovered
|
|
36
|
+
discovered="$(jq -r --arg f "$field" '.[$f] // empty' <<<"$discovery" 2>/dev/null)"
|
|
37
|
+
if [[ -n "$discovered" ]]; then
|
|
38
|
+
printf '%s' "$discovered"
|
|
39
|
+
else
|
|
40
|
+
printf '%s' "$fallback"
|
|
41
|
+
fi
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
reconcile_provider() {
|
|
45
|
+
local client_id="$1"
|
|
46
|
+
local runtime_script="$script_dir/reconcile_twenty_provider.mjs"
|
|
47
|
+
if [[ -n "${THINKWORK_AGENTCORE_CONTROL_RUNTIME_DIR:-}" ]]; then
|
|
48
|
+
runtime_script="$THINKWORK_AGENTCORE_CONTROL_RUNTIME_DIR/reconcile_twenty_provider.js"
|
|
49
|
+
if [[ ! -f "$runtime_script" ]]; then
|
|
50
|
+
printf '%s\n' 'Managed AgentCore control runtime is missing reconcile_twenty_provider.js' >&2
|
|
51
|
+
return 66
|
|
52
|
+
fi
|
|
53
|
+
fi
|
|
54
|
+
jq -n \
|
|
55
|
+
--arg region "$AWS_REGION" \
|
|
56
|
+
--arg name "$TWENTY_CREDENTIAL_PROVIDER_NAME" \
|
|
57
|
+
--arg issuer "$issuer" \
|
|
58
|
+
--arg authorizationEndpoint "$(provider_endpoint authorization_endpoint "$issuer/authorize")" \
|
|
59
|
+
--arg tokenEndpoint "$(provider_endpoint token_endpoint "$issuer/oauth/token")" \
|
|
60
|
+
--arg clientId "$client_id" \
|
|
61
|
+
--arg secretArn "$TWENTY_CLIENT_SECRET_ARN" \
|
|
62
|
+
'{
|
|
63
|
+
region: $region,
|
|
64
|
+
name: $name,
|
|
65
|
+
issuer: $issuer,
|
|
66
|
+
authorizationEndpoint: $authorizationEndpoint,
|
|
67
|
+
tokenEndpoint: $tokenEndpoint,
|
|
68
|
+
clientId: $clientId,
|
|
69
|
+
secretArn: $secretArn
|
|
70
|
+
}' | node "$runtime_script"
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
verify_provider() {
|
|
74
|
+
local response="$1"
|
|
75
|
+
local expected_client_id="$2"
|
|
76
|
+
local expected_callback="$3"
|
|
77
|
+
jq -e \
|
|
78
|
+
--arg name "$TWENTY_CREDENTIAL_PROVIDER_NAME" \
|
|
79
|
+
--arg client_id "$expected_client_id" \
|
|
80
|
+
--arg callback "$expected_callback" \
|
|
81
|
+
--arg secret_arn "$TWENTY_CLIENT_SECRET_ARN" \
|
|
82
|
+
'.name == $name and
|
|
83
|
+
.clientId == $client_id and
|
|
84
|
+
.callbackUrl == $callback and
|
|
85
|
+
.clientSecretArn == $secret_arn and
|
|
86
|
+
.clientSecretJsonKey == "client_secret" and
|
|
87
|
+
.clientSecretSource == "EXTERNAL" and
|
|
88
|
+
.status == "READY"' <<<"$response" >/dev/null
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
client_record="$(read_client_record)"
|
|
92
|
+
if ! jq -e 'type == "object"' <<<"$client_record" >/dev/null 2>&1; then
|
|
93
|
+
printf '%s\n' 'Twenty client secret must contain a JSON object' >&2
|
|
94
|
+
exit 1
|
|
95
|
+
fi
|
|
96
|
+
|
|
97
|
+
client_id="$(jq -r '.client_id // empty' <<<"$client_record")"
|
|
98
|
+
client_secret_ready=false
|
|
99
|
+
if [[ -n "$client_id" ]] &&
|
|
100
|
+
jq -e '.client_secret | type == "string" and length > 0' \
|
|
101
|
+
<<<"$client_record" >/dev/null 2>&1 &&
|
|
102
|
+
[[ "$(jq -r '.bootstrap_state // empty' <<<"$client_record")" == "ready" ]]; then
|
|
103
|
+
client_secret_ready=true
|
|
104
|
+
else
|
|
105
|
+
placeholder_id="thinkwork-bootstrap-$(openssl rand -hex 12)"
|
|
106
|
+
placeholder_secret="$(openssl rand -base64 48 | tr -d '\n')"
|
|
107
|
+
client_record="$(jq -n \
|
|
108
|
+
--arg client_id "$placeholder_id" \
|
|
109
|
+
--arg client_secret "$placeholder_secret" \
|
|
110
|
+
'{
|
|
111
|
+
bootstrap_state: "placeholder",
|
|
112
|
+
client_id: $client_id,
|
|
113
|
+
client_secret: $client_secret,
|
|
114
|
+
token_endpoint_auth_method: "client_secret_post"
|
|
115
|
+
}')"
|
|
116
|
+
printf '%s' "$client_record" | write_client_record
|
|
117
|
+
client_id="$placeholder_id"
|
|
118
|
+
fi
|
|
119
|
+
|
|
120
|
+
# AgentCore issues a provider-specific callback only after the provider exists.
|
|
121
|
+
# The placeholder is never used for a user grant; it only lets AgentCore return
|
|
122
|
+
# the callback that Twenty must bind to the confidential client registration.
|
|
123
|
+
provider_response="$(reconcile_provider "$client_id")"
|
|
124
|
+
callback_url="$(jq -r '.callbackUrl // empty' <<<"$provider_response")"
|
|
125
|
+
if [[ -z "$callback_url" ]]; then
|
|
126
|
+
printf '%s\n' 'AgentCore Identity did not return a provider callback URL' >&2
|
|
127
|
+
exit 1
|
|
128
|
+
fi
|
|
129
|
+
|
|
130
|
+
registered_callback_matches=false
|
|
131
|
+
if $client_secret_ready &&
|
|
132
|
+
jq -e --arg callback "$callback_url" \
|
|
133
|
+
'.redirect_uris | type == "array" and index($callback) != null' \
|
|
134
|
+
<<<"$client_record" >/dev/null 2>&1 &&
|
|
135
|
+
[[ "$(jq -r '.token_endpoint_auth_method // empty' <<<"$client_record")" == \
|
|
136
|
+
"client_secret_post" ]]; then
|
|
137
|
+
registered_callback_matches=true
|
|
138
|
+
fi
|
|
139
|
+
|
|
140
|
+
if $registered_callback_matches; then
|
|
141
|
+
verify_provider "$provider_response" "$client_id" "$callback_url"
|
|
142
|
+
printf 'Twenty confidential OAuth client reused: provider=%s callback=%s\n' \
|
|
143
|
+
"$TWENTY_CREDENTIAL_PROVIDER_NAME" "$callback_url" >&2
|
|
144
|
+
printf '%s' "$provider_response"
|
|
145
|
+
exit 0
|
|
146
|
+
fi
|
|
147
|
+
|
|
148
|
+
if [[ -z "$discovery" ]]; then
|
|
149
|
+
printf '%s\n' 'Twenty discovery document is unavailable' >&2
|
|
150
|
+
exit 1
|
|
151
|
+
fi
|
|
152
|
+
registration_endpoint="$(jq -r '.registration_endpoint // empty' <<<"$discovery")"
|
|
153
|
+
if ! python3 - "$issuer" "$registration_endpoint" <<'PY'
|
|
154
|
+
import sys
|
|
155
|
+
from urllib.parse import urlsplit
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
def origin(url: str) -> tuple[str, str, int]:
|
|
159
|
+
parsed = urlsplit(url)
|
|
160
|
+
if parsed.scheme != "https" or not parsed.hostname or parsed.username or parsed.password:
|
|
161
|
+
raise ValueError("HTTPS origin without userinfo required")
|
|
162
|
+
return parsed.scheme, parsed.hostname.lower(), parsed.port or 443
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
try:
|
|
166
|
+
issuer_origin = origin(sys.argv[1])
|
|
167
|
+
registration_origin = origin(sys.argv[2])
|
|
168
|
+
except (ValueError, IndexError):
|
|
169
|
+
raise SystemExit(1)
|
|
170
|
+
raise SystemExit(0 if issuer_origin == registration_origin else 1)
|
|
171
|
+
PY
|
|
172
|
+
then
|
|
173
|
+
printf '%s\n' 'Twenty registration endpoint is not on the exact issuer origin' >&2
|
|
174
|
+
exit 1
|
|
175
|
+
fi
|
|
176
|
+
if ! jq -e \
|
|
177
|
+
--arg issuer "$issuer" \
|
|
178
|
+
'.issuer == $issuer and
|
|
179
|
+
(.grant_types_supported | index("authorization_code") != null) and
|
|
180
|
+
(.token_endpoint_auth_methods_supported | index("client_secret_post") != null) and
|
|
181
|
+
(.scopes_supported | index("api") != null) and
|
|
182
|
+
(.scopes_supported | index("profile") != null)' \
|
|
183
|
+
<<<"$discovery" >/dev/null; then
|
|
184
|
+
printf '%s\n' \
|
|
185
|
+
'Twenty discovery does not advertise same-origin confidential authorization-code DCR' >&2
|
|
186
|
+
exit 1
|
|
187
|
+
fi
|
|
188
|
+
|
|
189
|
+
registration_payload="$(jq -n \
|
|
190
|
+
--arg callback "$callback_url" \
|
|
191
|
+
'{
|
|
192
|
+
client_name: "ThinkWork AgentCore Identity Twenty CRM",
|
|
193
|
+
redirect_uris: [$callback],
|
|
194
|
+
grant_types: ["authorization_code"],
|
|
195
|
+
response_types: ["code"],
|
|
196
|
+
token_endpoint_auth_method: "client_secret_post",
|
|
197
|
+
scope: "api profile"
|
|
198
|
+
}')"
|
|
199
|
+
registration_response="$(curl --silent --show-error --fail-with-body \
|
|
200
|
+
-H 'content-type: application/json' \
|
|
201
|
+
--data-binary "$registration_payload" \
|
|
202
|
+
"$registration_endpoint")"
|
|
203
|
+
|
|
204
|
+
client_record="$(jq -e \
|
|
205
|
+
--arg callback "$callback_url" \
|
|
206
|
+
'{
|
|
207
|
+
bootstrap_state: "ready",
|
|
208
|
+
client_id: .client_id,
|
|
209
|
+
client_secret: .client_secret,
|
|
210
|
+
client_id_issued_at: .client_id_issued_at,
|
|
211
|
+
client_secret_expires_at: .client_secret_expires_at,
|
|
212
|
+
client_name: .client_name,
|
|
213
|
+
redirect_uris: .redirect_uris,
|
|
214
|
+
grant_types: .grant_types,
|
|
215
|
+
response_types: .response_types,
|
|
216
|
+
token_endpoint_auth_method: .token_endpoint_auth_method,
|
|
217
|
+
scope: (.scope // "api profile"),
|
|
218
|
+
registration_access_token: .registration_access_token,
|
|
219
|
+
registration_client_uri: .registration_client_uri
|
|
220
|
+
} | select(
|
|
221
|
+
(.client_id | type == "string" and length > 0) and
|
|
222
|
+
(.client_secret | type == "string" and length > 0) and
|
|
223
|
+
(.redirect_uris | type == "array" and index($callback) != null) and
|
|
224
|
+
.token_endpoint_auth_method == "client_secret_post"
|
|
225
|
+
)' <<<"$registration_response")" || {
|
|
226
|
+
printf '%s\n' 'Twenty DCR did not return a confidential callback-bound client' >&2
|
|
227
|
+
exit 1
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
# A Secrets Manager version write is atomic. The plaintext client secret never
|
|
231
|
+
# enters Terraform state or process arguments and is not printed to logs.
|
|
232
|
+
printf '%s' "$client_record" | write_client_record
|
|
233
|
+
client_id="$(jq -r '.client_id' <<<"$client_record")"
|
|
234
|
+
provider_response="$(reconcile_provider "$client_id")"
|
|
235
|
+
verify_provider "$provider_response" "$client_id" "$callback_url"
|
|
236
|
+
|
|
237
|
+
printf 'Twenty confidential OAuth client registered: provider=%s callback=%s\n' \
|
|
238
|
+
"$TWENTY_CREDENTIAL_PROVIDER_NAME" "$callback_url" >&2
|
|
239
|
+
printf '%s' "$provider_response"
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
: "${AWS_REGION:?AWS_REGION is required}"
|
|
5
|
+
: "${WORKLOAD_IDENTITY_NAME:?WORKLOAD_IDENTITY_NAME is required}"
|
|
6
|
+
: "${CREDENTIAL_PROVIDER_NAME:?CREDENTIAL_PROVIDER_NAME is required}"
|
|
7
|
+
|
|
8
|
+
if aws bedrock-agentcore-control get-oauth2-credential-provider \
|
|
9
|
+
--region "$AWS_REGION" \
|
|
10
|
+
--name "$CREDENTIAL_PROVIDER_NAME" >/dev/null 2>&1; then
|
|
11
|
+
aws bedrock-agentcore-control delete-oauth2-credential-provider \
|
|
12
|
+
--region "$AWS_REGION" \
|
|
13
|
+
--name "$CREDENTIAL_PROVIDER_NAME" >/dev/null
|
|
14
|
+
fi
|
|
15
|
+
|
|
16
|
+
# THINK-324: the workload identity is SHARED with the live Twenty connector
|
|
17
|
+
# (per-user grants live under it). Its deletion is owned by
|
|
18
|
+
# workload_identity_cleanup_owner / delete_workload_identity.sh — retiring
|
|
19
|
+
# the proof plane must never delete it. See main.tf for the proof-only-stage
|
|
20
|
+
# leak trade-off.
|
|
21
|
+
printf 'AgentCore Identity proof provider deleted (workload identity retained): workload=%s provider=%s\n' \
|
|
22
|
+
"$WORKLOAD_IDENTITY_NAME" "$CREDENTIAL_PROVIDER_NAME"
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
: "${AWS_REGION:?AWS_REGION is required}"
|
|
5
|
+
: "${TWENTY_CREDENTIAL_PROVIDER_NAME:?TWENTY_CREDENTIAL_PROVIDER_NAME is required}"
|
|
6
|
+
|
|
7
|
+
if aws bedrock-agentcore-control get-oauth2-credential-provider \
|
|
8
|
+
--region "$AWS_REGION" \
|
|
9
|
+
--name "$TWENTY_CREDENTIAL_PROVIDER_NAME" >/dev/null 2>&1; then
|
|
10
|
+
aws bedrock-agentcore-control delete-oauth2-credential-provider \
|
|
11
|
+
--region "$AWS_REGION" \
|
|
12
|
+
--name "$TWENTY_CREDENTIAL_PROVIDER_NAME" >/dev/null
|
|
13
|
+
fi
|
|
14
|
+
|
|
15
|
+
printf 'AgentCore Twenty provider deleted: provider=%s\n' \
|
|
16
|
+
"$TWENTY_CREDENTIAL_PROVIDER_NAME"
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
# THINK-324 — final deletion of the SHARED workload identity, owned by the
|
|
5
|
+
# Twenty identity half (workload_identity_cleanup_owner). Runs only when the
|
|
6
|
+
# Twenty user-federation identity is itself being removed.
|
|
7
|
+
|
|
8
|
+
: "${AWS_REGION:?AWS_REGION is required}"
|
|
9
|
+
: "${WORKLOAD_IDENTITY_NAME:?WORKLOAD_IDENTITY_NAME is required}"
|
|
10
|
+
|
|
11
|
+
if aws bedrock-agentcore-control get-workload-identity \
|
|
12
|
+
--region "$AWS_REGION" \
|
|
13
|
+
--name "$WORKLOAD_IDENTITY_NAME" >/dev/null 2>&1; then
|
|
14
|
+
aws bedrock-agentcore-control delete-workload-identity \
|
|
15
|
+
--region "$AWS_REGION" \
|
|
16
|
+
--name "$WORKLOAD_IDENTITY_NAME" >/dev/null
|
|
17
|
+
fi
|
|
18
|
+
|
|
19
|
+
printf 'AgentCore workload identity deleted: %s\n' "$WORKLOAD_IDENTITY_NAME"
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
# THINK-324 — Twenty-side workload-identity guarantee. Create-if-missing
|
|
5
|
+
# ONLY: when the identity already exists (created by the proof lifecycle or a
|
|
6
|
+
# prior run) this script must not touch it — reconcile_twenty_identity.sh
|
|
7
|
+
# owns the return-url allowlist and always runs afterwards.
|
|
8
|
+
|
|
9
|
+
: "${AWS_REGION:?AWS_REGION is required}"
|
|
10
|
+
: "${WORKLOAD_IDENTITY_NAME:?WORKLOAD_IDENTITY_NAME is required}"
|
|
11
|
+
: "${OAUTH_RETURN_URLS_JSON:?OAUTH_RETURN_URLS_JSON is required}"
|
|
12
|
+
|
|
13
|
+
jq -e 'type == "array" and length > 0 and all(.[]; type == "string" and length > 0)' \
|
|
14
|
+
<<<"$OAUTH_RETURN_URLS_JSON" >/dev/null
|
|
15
|
+
|
|
16
|
+
if aws bedrock-agentcore-control get-workload-identity \
|
|
17
|
+
--region "$AWS_REGION" \
|
|
18
|
+
--name "$WORKLOAD_IDENTITY_NAME" >/dev/null 2>&1; then
|
|
19
|
+
printf 'AgentCore workload identity already present: %s\n' "$WORKLOAD_IDENTITY_NAME"
|
|
20
|
+
exit 0
|
|
21
|
+
fi
|
|
22
|
+
|
|
23
|
+
aws bedrock-agentcore-control create-workload-identity \
|
|
24
|
+
--region "$AWS_REGION" \
|
|
25
|
+
--name "$WORKLOAD_IDENTITY_NAME" \
|
|
26
|
+
--allowed-resource-oauth2-return-urls "$OAUTH_RETURN_URLS_JSON" \
|
|
27
|
+
--tags '{"purpose":"twenty-user-federation","managed-by":"terraform"}' >/dev/null
|
|
28
|
+
|
|
29
|
+
printf 'AgentCore workload identity created: %s\n' "$WORKLOAD_IDENTITY_NAME"
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
input="$(cat)"
|
|
5
|
+
region="$(jq -r '.region // empty' <<<"$input")"
|
|
6
|
+
workload_name="$(jq -r '.workload_identity_name // empty' <<<"$input")"
|
|
7
|
+
provider_name="$(jq -r '.credential_provider_name // empty' <<<"$input")"
|
|
8
|
+
twenty_provider_name="$(jq -r '.twenty_credential_provider_name // empty' <<<"$input")"
|
|
9
|
+
# THINK-324: an empty provider name means that half is disabled (proof
|
|
10
|
+
# retired, or Twenty absent on a proof-only stage) — skip its reads and
|
|
11
|
+
# return empty fields instead of failing.
|
|
12
|
+
if [[ -z "$region" || -z "$workload_name" ]]; then
|
|
13
|
+
printf '%s\n' '{"error":"region and workload_identity_name are required"}' >&2
|
|
14
|
+
exit 1
|
|
15
|
+
fi
|
|
16
|
+
|
|
17
|
+
workload_json="$(aws bedrock-agentcore-control get-workload-identity \
|
|
18
|
+
--region "$region" --name "$workload_name" --output json)"
|
|
19
|
+
|
|
20
|
+
provider_json='{}'
|
|
21
|
+
if [[ -n "$provider_name" ]]; then
|
|
22
|
+
provider_json="$(aws bedrock-agentcore-control get-oauth2-credential-provider \
|
|
23
|
+
--region "$region" --name "$provider_name" --output json)"
|
|
24
|
+
fi
|
|
25
|
+
|
|
26
|
+
twenty_provider_json='{}'
|
|
27
|
+
if [[ -n "$twenty_provider_name" ]]; then
|
|
28
|
+
twenty_provider_json="$(aws bedrock-agentcore-control get-oauth2-credential-provider \
|
|
29
|
+
--region "$region" --name "$twenty_provider_name" --output json)"
|
|
30
|
+
fi
|
|
31
|
+
|
|
32
|
+
jq -n \
|
|
33
|
+
--arg proof_enabled "$provider_name" \
|
|
34
|
+
--arg twenty_enabled "$twenty_provider_name" \
|
|
35
|
+
--arg workload_identity_arn "$(jq -r '.workloadIdentityArn // empty' <<<"$workload_json")" \
|
|
36
|
+
--arg credential_provider_arn "$(jq -r '.credentialProviderArn // empty' <<<"$provider_json")" \
|
|
37
|
+
--arg credential_secret_arn "$(jq -r '
|
|
38
|
+
if (.clientSecretArn | type) == "object"
|
|
39
|
+
then .clientSecretArn.secretArn // empty
|
|
40
|
+
else .clientSecretArn // empty
|
|
41
|
+
end
|
|
42
|
+
' <<<"$provider_json")" \
|
|
43
|
+
--arg twenty_credential_provider_arn "$(jq -r '.credentialProviderArn // empty' <<<"$twenty_provider_json")" \
|
|
44
|
+
--arg twenty_oauth_callback_url "$(jq -r '.callbackUrl // empty' <<<"$twenty_provider_json")" \
|
|
45
|
+
'if ($workload_identity_arn == ""
|
|
46
|
+
or ($proof_enabled != "" and ($credential_provider_arn == "" or $credential_secret_arn == ""))
|
|
47
|
+
or ($twenty_enabled != "" and ($twenty_credential_provider_arn == "" or $twenty_oauth_callback_url == "")))
|
|
48
|
+
then error("AgentCore Identity readback omitted required ARNs")
|
|
49
|
+
else {
|
|
50
|
+
workload_identity_arn: $workload_identity_arn,
|
|
51
|
+
credential_provider_arn: $credential_provider_arn,
|
|
52
|
+
credential_secret_arn: $credential_secret_arn,
|
|
53
|
+
twenty_credential_provider_arn: $twenty_credential_provider_arn,
|
|
54
|
+
twenty_oauth_callback_url: $twenty_oauth_callback_url
|
|
55
|
+
} end'
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
: "${AWS_REGION:?AWS_REGION is required}"
|
|
5
|
+
: "${WORKLOAD_IDENTITY_NAME:?WORKLOAD_IDENTITY_NAME is required}"
|
|
6
|
+
: "${CREDENTIAL_PROVIDER_NAME:?CREDENTIAL_PROVIDER_NAME is required}"
|
|
7
|
+
: "${OAUTH_ISSUER:?OAUTH_ISSUER is required}"
|
|
8
|
+
: "${OAUTH_CLIENT_ID:?OAUTH_CLIENT_ID is required}"
|
|
9
|
+
: "${OAUTH_CLIENT_SECRET:?OAUTH_CLIENT_SECRET is required}"
|
|
10
|
+
: "${OAUTH_RETURN_URL:?OAUTH_RETURN_URL is required}"
|
|
11
|
+
|
|
12
|
+
if aws bedrock-agentcore-control get-workload-identity \
|
|
13
|
+
--region "$AWS_REGION" \
|
|
14
|
+
--name "$WORKLOAD_IDENTITY_NAME" >/dev/null 2>&1; then
|
|
15
|
+
aws bedrock-agentcore-control update-workload-identity \
|
|
16
|
+
--region "$AWS_REGION" \
|
|
17
|
+
--name "$WORKLOAD_IDENTITY_NAME" \
|
|
18
|
+
--allowed-resource-oauth2-return-urls "$OAUTH_RETURN_URL" >/dev/null
|
|
19
|
+
else
|
|
20
|
+
aws bedrock-agentcore-control create-workload-identity \
|
|
21
|
+
--region "$AWS_REGION" \
|
|
22
|
+
--name "$WORKLOAD_IDENTITY_NAME" \
|
|
23
|
+
--allowed-resource-oauth2-return-urls "$OAUTH_RETURN_URL" \
|
|
24
|
+
--tags '{"purpose":"think-316-proof","managed-by":"terraform"}' >/dev/null
|
|
25
|
+
fi
|
|
26
|
+
|
|
27
|
+
payload_file="$(mktemp)"
|
|
28
|
+
trap 'rm -f "$payload_file"' EXIT
|
|
29
|
+
jq -n \
|
|
30
|
+
--arg name "$CREDENTIAL_PROVIDER_NAME" \
|
|
31
|
+
--arg issuer "$OAUTH_ISSUER" \
|
|
32
|
+
--arg authorizationEndpoint "$OAUTH_ISSUER/authorize" \
|
|
33
|
+
--arg tokenEndpoint "$OAUTH_ISSUER/token" \
|
|
34
|
+
--arg clientId "$OAUTH_CLIENT_ID" \
|
|
35
|
+
--arg clientSecret "$OAUTH_CLIENT_SECRET" \
|
|
36
|
+
'{
|
|
37
|
+
name: $name,
|
|
38
|
+
credentialProviderVendor: "CustomOauth2",
|
|
39
|
+
oauth2ProviderConfigInput: {
|
|
40
|
+
customOauth2ProviderConfig: {
|
|
41
|
+
oauthDiscovery: {
|
|
42
|
+
authorizationServerMetadata: {
|
|
43
|
+
issuer: $issuer,
|
|
44
|
+
authorizationEndpoint: $authorizationEndpoint,
|
|
45
|
+
tokenEndpoint: $tokenEndpoint,
|
|
46
|
+
responseTypes: ["code"]
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
clientId: $clientId,
|
|
50
|
+
clientSecret: $clientSecret,
|
|
51
|
+
clientAuthenticationMethod: "CLIENT_SECRET_BASIC",
|
|
52
|
+
onBehalfOfTokenExchangeConfig: {
|
|
53
|
+
grantType: "TOKEN_EXCHANGE",
|
|
54
|
+
tokenExchangeGrantTypeConfig: {actorTokenContent: "NONE"}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}' >"$payload_file"
|
|
59
|
+
|
|
60
|
+
if aws bedrock-agentcore-control get-oauth2-credential-provider \
|
|
61
|
+
--region "$AWS_REGION" \
|
|
62
|
+
--name "$CREDENTIAL_PROVIDER_NAME" >/dev/null 2>&1; then
|
|
63
|
+
aws bedrock-agentcore-control update-oauth2-credential-provider \
|
|
64
|
+
--region "$AWS_REGION" \
|
|
65
|
+
--cli-input-json "file://$payload_file" >/dev/null
|
|
66
|
+
else
|
|
67
|
+
aws bedrock-agentcore-control create-oauth2-credential-provider \
|
|
68
|
+
--region "$AWS_REGION" \
|
|
69
|
+
--cli-input-json "file://$payload_file" >/dev/null
|
|
70
|
+
fi
|
|
71
|
+
|
|
72
|
+
printf 'AgentCore Identity proof resources reconciled: workload=%s provider=%s\n' \
|
|
73
|
+
"$WORKLOAD_IDENTITY_NAME" "$CREDENTIAL_PROVIDER_NAME"
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
: "${AWS_REGION:?AWS_REGION is required}"
|
|
5
|
+
: "${WORKLOAD_IDENTITY_NAME:?WORKLOAD_IDENTITY_NAME is required}"
|
|
6
|
+
: "${OAUTH_RETURN_URLS_JSON:?OAUTH_RETURN_URLS_JSON is required}"
|
|
7
|
+
: "${TWENTY_CREDENTIAL_PROVIDER_NAME:?TWENTY_CREDENTIAL_PROVIDER_NAME is required}"
|
|
8
|
+
: "${TWENTY_CLIENT_SECRET_ARN:?TWENTY_CLIENT_SECRET_ARN is required}"
|
|
9
|
+
: "${TWENTY_OAUTH_ISSUER:?TWENTY_OAUTH_ISSUER is required}"
|
|
10
|
+
: "${TWENTY_OAUTH_RESOURCE:?TWENTY_OAUTH_RESOURCE is required}"
|
|
11
|
+
|
|
12
|
+
jq -e 'type == "array" and length > 0 and all(.[]; type == "string" and length > 0)' \
|
|
13
|
+
<<<"$OAUTH_RETURN_URLS_JSON" >/dev/null
|
|
14
|
+
|
|
15
|
+
# The core lifecycle owns this workload. Extend its allowlist in place; never
|
|
16
|
+
# delete or recreate it while adding a downstream user-federation provider.
|
|
17
|
+
aws bedrock-agentcore-control update-workload-identity \
|
|
18
|
+
--region "$AWS_REGION" \
|
|
19
|
+
--name "$WORKLOAD_IDENTITY_NAME" \
|
|
20
|
+
--allowed-resource-oauth2-return-urls "$OAUTH_RETURN_URLS_JSON" >/dev/null
|
|
21
|
+
|
|
22
|
+
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
23
|
+
provider_response="$(bash "$script_dir/bootstrap_twenty_oauth_client.sh")"
|
|
24
|
+
callback_url="$(jq -r '.callbackUrl // empty' <<<"$provider_response")"
|
|
25
|
+
if [[ -z "$callback_url" || "$callback_url" == "None" ]]; then
|
|
26
|
+
printf '%s\n' 'AgentCore Identity did not return the Twenty callback URL' >&2
|
|
27
|
+
exit 1
|
|
28
|
+
fi
|
|
29
|
+
|
|
30
|
+
client_record="$(aws secretsmanager get-secret-value \
|
|
31
|
+
--region "$AWS_REGION" \
|
|
32
|
+
--secret-id "$TWENTY_CLIENT_SECRET_ARN" \
|
|
33
|
+
--query SecretString \
|
|
34
|
+
--output text)"
|
|
35
|
+
jq -e --arg callback "$callback_url" \
|
|
36
|
+
'.bootstrap_state == "ready" and
|
|
37
|
+
(.client_id | type == "string" and length > 0) and
|
|
38
|
+
(.client_secret | type == "string" and length > 0) and
|
|
39
|
+
(.redirect_uris | type == "array" and index($callback) != null) and
|
|
40
|
+
.token_endpoint_auth_method == "client_secret_post"' \
|
|
41
|
+
<<<"$client_record" >/dev/null
|
|
42
|
+
|
|
43
|
+
printf 'AgentCore Twenty provider reconciled: workload=%s provider=%s resource=%s callback=%s\n' \
|
|
44
|
+
"$WORKLOAD_IDENTITY_NAME" "$TWENTY_CREDENTIAL_PROVIDER_NAME" \
|
|
45
|
+
"$TWENTY_OAUTH_RESOURCE" "$callback_url"
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
BedrockAgentCoreControlClient,
|
|
5
|
+
CreateOauth2CredentialProviderCommand,
|
|
6
|
+
GetOauth2CredentialProviderCommand,
|
|
7
|
+
ResourceNotFoundException,
|
|
8
|
+
UpdateOauth2CredentialProviderCommand,
|
|
9
|
+
} from "@aws-sdk/client-bedrock-agentcore-control";
|
|
10
|
+
|
|
11
|
+
if (process.argv[2] === "--runtime-preflight") {
|
|
12
|
+
process.stdout.write(
|
|
13
|
+
JSON.stringify({
|
|
14
|
+
entrypoint: "reconcile_twenty_provider",
|
|
15
|
+
sdkImportReady:
|
|
16
|
+
typeof BedrockAgentCoreControlClient === "function" &&
|
|
17
|
+
typeof CreateOauth2CredentialProviderCommand === "function" &&
|
|
18
|
+
typeof GetOauth2CredentialProviderCommand === "function" &&
|
|
19
|
+
typeof UpdateOauth2CredentialProviderCommand === "function",
|
|
20
|
+
}),
|
|
21
|
+
);
|
|
22
|
+
process.exit(0);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const chunks = [];
|
|
26
|
+
for await (const chunk of process.stdin) chunks.push(chunk);
|
|
27
|
+
const input = JSON.parse(Buffer.concat(chunks).toString("utf8"));
|
|
28
|
+
|
|
29
|
+
const client = new BedrockAgentCoreControlClient({ region: input.region });
|
|
30
|
+
const providerInput = {
|
|
31
|
+
name: input.name,
|
|
32
|
+
credentialProviderVendor: "CustomOauth2",
|
|
33
|
+
oauth2ProviderConfigInput: {
|
|
34
|
+
customOauth2ProviderConfig: {
|
|
35
|
+
oauthDiscovery: {
|
|
36
|
+
authorizationServerMetadata: {
|
|
37
|
+
issuer: input.issuer,
|
|
38
|
+
authorizationEndpoint: input.authorizationEndpoint,
|
|
39
|
+
tokenEndpoint: input.tokenEndpoint,
|
|
40
|
+
responseTypes: ["code"],
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
clientId: input.clientId,
|
|
44
|
+
clientSecretConfig: {
|
|
45
|
+
secretId: input.secretArn,
|
|
46
|
+
jsonKey: "client_secret",
|
|
47
|
+
},
|
|
48
|
+
clientSecretSource: "EXTERNAL",
|
|
49
|
+
clientAuthenticationMethod: "CLIENT_SECRET_POST",
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
let exists = false;
|
|
55
|
+
try {
|
|
56
|
+
await client.send(
|
|
57
|
+
new GetOauth2CredentialProviderCommand({ name: input.name }),
|
|
58
|
+
);
|
|
59
|
+
exists = true;
|
|
60
|
+
} catch (error) {
|
|
61
|
+
if (!(error instanceof ResourceNotFoundException)) throw error;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (exists) {
|
|
65
|
+
await client.send(new UpdateOauth2CredentialProviderCommand(providerInput));
|
|
66
|
+
} else {
|
|
67
|
+
await client.send(new CreateOauth2CredentialProviderCommand(providerInput));
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const sleep = (milliseconds) =>
|
|
71
|
+
new Promise((resolve) => setTimeout(resolve, milliseconds));
|
|
72
|
+
|
|
73
|
+
let response;
|
|
74
|
+
for (let attempt = 0; attempt < 30; attempt += 1) {
|
|
75
|
+
response = await client.send(
|
|
76
|
+
new GetOauth2CredentialProviderCommand({ name: input.name }),
|
|
77
|
+
);
|
|
78
|
+
const status = response.status;
|
|
79
|
+
const provider =
|
|
80
|
+
response.oauth2ProviderConfigOutput?.customOauth2ProviderConfig;
|
|
81
|
+
const secretArn =
|
|
82
|
+
typeof response.clientSecretArn === "string"
|
|
83
|
+
? response.clientSecretArn
|
|
84
|
+
: response.clientSecretArn?.secretArn;
|
|
85
|
+
const ready =
|
|
86
|
+
response.callbackUrl &&
|
|
87
|
+
response.credentialProviderArn &&
|
|
88
|
+
provider?.clientId === input.clientId &&
|
|
89
|
+
secretArn === input.secretArn &&
|
|
90
|
+
response.clientSecretJsonKey === "client_secret" &&
|
|
91
|
+
response.clientSecretSource === "EXTERNAL" &&
|
|
92
|
+
status === "READY";
|
|
93
|
+
if (ready) break;
|
|
94
|
+
if (status?.endsWith("_FAILED")) {
|
|
95
|
+
throw new Error(`AgentCore OAuth provider ${input.name} entered ${status}`);
|
|
96
|
+
}
|
|
97
|
+
if (attempt === 29) {
|
|
98
|
+
throw new Error(
|
|
99
|
+
`AgentCore OAuth provider ${input.name} did not converge to the requested client and external secret`,
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
await sleep(2000);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const secretArn =
|
|
106
|
+
typeof response.clientSecretArn === "string"
|
|
107
|
+
? response.clientSecretArn
|
|
108
|
+
: response.clientSecretArn?.secretArn;
|
|
109
|
+
const provider =
|
|
110
|
+
response.oauth2ProviderConfigOutput?.customOauth2ProviderConfig;
|
|
111
|
+
|
|
112
|
+
process.stdout.write(
|
|
113
|
+
JSON.stringify({
|
|
114
|
+
name: response.name,
|
|
115
|
+
callbackUrl: response.callbackUrl,
|
|
116
|
+
credentialProviderArn: response.credentialProviderArn,
|
|
117
|
+
clientId: provider?.clientId,
|
|
118
|
+
clientSecretArn: secretArn,
|
|
119
|
+
clientSecretJsonKey: response.clientSecretJsonKey,
|
|
120
|
+
clientSecretSource: response.clientSecretSource,
|
|
121
|
+
status: response.status,
|
|
122
|
+
}),
|
|
123
|
+
);
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
terraform {
|
|
2
|
+
required_version = "= 1.8.5"
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
removed {
|
|
6
|
+
from = module.thinkwork.module.agentcore_proof_identity.terraform_data.twenty_identity_lifecycle
|
|
7
|
+
|
|
8
|
+
lifecycle {
|
|
9
|
+
destroy = false
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
module "thinkwork" {
|
|
14
|
+
source = "./thinkwork"
|
|
15
|
+
}
|