thinkwork-cli 0.13.1 → 0.13.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (130) hide show
  1. package/dist/{api-client-4DZ266J5.js → api-client-JYZXYQJR.js} +3 -1
  2. package/dist/bowser-6Z6JFAIR.js +2822 -0
  3. package/dist/chunk-DV3MRBR5.js +43 -0
  4. package/dist/chunk-DVPBI5HM.js +1728 -0
  5. package/dist/chunk-EI5ZX7CN.js +522 -0
  6. package/dist/chunk-EUJ7X6YD.js +126 -0
  7. package/dist/chunk-GG5WKBOX.js +436 -0
  8. package/dist/chunk-I64EZSU5.js +358 -0
  9. package/dist/chunk-ILZKG5WV.js +6151 -0
  10. package/dist/chunk-KMIJ5KGT.js +128 -0
  11. package/dist/chunk-OCPHU34C.js +996 -0
  12. package/dist/chunk-QOERT44A.js +3286 -0
  13. package/dist/chunk-XYM7PE2A.js +2264 -0
  14. package/dist/{chunk-Y2WHJECR.js → chunk-Z2F6RJFR.js} +1 -39
  15. package/dist/cli.js +43080 -13150
  16. package/dist/dist-es-CRV46D5B.js +315 -0
  17. package/dist/dist-es-DCY4IC3Y.js +2000 -0
  18. package/dist/dist-es-ENBUC3JP.js +14726 -0
  19. package/dist/dist-es-G54LBMRN.js +48 -0
  20. package/dist/dist-es-G7PTG75M.js +176 -0
  21. package/dist/dist-es-GKDBE7E6.js +88 -0
  22. package/dist/dist-es-HY3535OI.js +70 -0
  23. package/dist/dist-es-KL7Y5NB2.js +377 -0
  24. package/dist/dist-es-LDE437AM.js +170 -0
  25. package/dist/dist-es-P26FWFLT.js +485 -0
  26. package/dist/dist-es-Q57SZXZ7.js +70 -0
  27. package/dist/dist-es-R7FGL2IB.js +88 -0
  28. package/dist/dist-es-USORBXZ5.js +318 -0
  29. package/dist/dist-es-ZKC2QKD2.js +377 -0
  30. package/dist/drizzle/0076_scheduled_jobs_marco_backfill.sql +2 -1
  31. package/dist/drizzle/0079_seed_tenant_customize_catalog.sql +1 -0
  32. package/dist/drizzle/0090_brain_schema_extraction.sql +10 -0
  33. package/dist/drizzle/0132_drop_computer_tables.sql +3 -0
  34. package/dist/drizzle/0160_compliance_event_types_plugins.sql +2 -0
  35. package/dist/drizzle/0214_think137_drop_judge_evidence_roi.sql +1 -0
  36. package/dist/drizzle/0216_drop_agent_loop_versions_goal_worker_policy.sql +1 -0
  37. package/dist/drizzle/0227_analyst_reader_role.sql +6 -0
  38. package/dist/drizzle/0230_analyst_rls.sql +9 -0
  39. package/dist/drizzle/0250_kg_schema_extraction.sql +27 -0
  40. package/dist/drizzle/0251_brain_stragglers.sql +21 -0
  41. package/dist/drizzle/0253_drop_brain_substrate.sql +2 -0
  42. package/dist/drizzle/0257_agents_runtime_allow_harness.sql +24 -0
  43. package/dist/drizzle/0261_managed_harness_multiplayer.sql +313 -0
  44. package/dist/drizzle/0261_native_auth_control_plane.sql +328 -0
  45. package/dist/drizzle/0262_auth_subscription_tickets.sql +59 -0
  46. package/dist/drizzle/0262_parallel_harness_threads.sql +9 -0
  47. package/dist/drizzle/0263_agentcore_runtime_identifier.sql +69 -0
  48. package/dist/drizzle/0263_drop_workos_auth_runtime.sql +236 -0
  49. package/dist/drizzle/0264_auth_enrollment_attempt_limits.sql +15 -0
  50. package/dist/drizzle/0264_harness_tool_execution_ledger.sql +113 -0
  51. package/dist/drizzle/0265_eval_runs_requester_identity.sql +28 -0
  52. package/dist/drizzle/0265_require_fully_valid_auth_publication.sql +27 -0
  53. package/dist/drizzle/0266_auth_identity_recovery_grants.sql +9 -0
  54. package/dist/drizzle/0266_eval_profiles_runtime_type.sql +12 -0
  55. package/dist/drizzle/0267_ontology_candidate_rejections.sql +53 -0
  56. package/dist/drizzle/0268_identity_crosswalk_routing.sql +182 -0
  57. package/dist/drizzle/0269_ontology_system_map.sql +31 -0
  58. package/dist/event-streams-7JNLJEEL.js +894 -0
  59. package/dist/event-streams-KDF5RWR6.js +1515 -0
  60. package/dist/loadSso-DJVB7BXE.js +576 -0
  61. package/dist/loadSso-JAMNA3WY.js +583 -0
  62. package/dist/plugins/catalog/package.json +1 -2
  63. package/dist/plugins/catalog/src/__tests__/build-catalog.test.ts +0 -1
  64. package/dist/plugins/catalog/src/__tests__/contracts.test.ts +0 -69
  65. package/dist/plugins/catalog/src/__tests__/plugin-package.test.ts +0 -2
  66. package/dist/plugins/catalog/src/__tests__/plugin-registry.test.ts +0 -5
  67. package/dist/plugins/catalog/src/contracts.ts +2 -179
  68. package/dist/plugins/catalog/src/registry/generated-first-party.ts +0 -6
  69. package/dist/signin-UGDWTGS4.js +821 -0
  70. package/dist/sso-oidc-Q4VXANXY.js +837 -0
  71. package/dist/sts-CWLSE3GH.js +1241 -0
  72. package/dist/sts-DNZLNVOV.js +1245 -0
  73. package/dist/terraform/examples/greenfield/main.tf +223 -66
  74. package/dist/terraform/examples/greenfield/terraform.tfvars.example +15 -1
  75. package/dist/terraform/modules/app/agentcore-gateway/main.tf +305 -0
  76. package/dist/terraform/modules/app/agentcore-gateway/outputs.tf +44 -0
  77. package/dist/terraform/modules/app/agentcore-gateway/scripts/delete_gateway.sh +93 -0
  78. package/dist/terraform/modules/app/agentcore-gateway/scripts/read_gateway.sh +33 -0
  79. package/dist/terraform/modules/app/agentcore-gateway/scripts/reconcile_gateway.sh +543 -0
  80. package/dist/terraform/modules/app/agentcore-gateway/variables.tf +70 -0
  81. package/dist/terraform/modules/app/agentcore-harness/main.tf +237 -8
  82. package/dist/terraform/modules/app/agentcore-harness/outputs.tf +63 -0
  83. package/dist/terraform/modules/app/agentcore-harness/scripts/delete_harness.sh +5 -0
  84. package/dist/terraform/modules/app/agentcore-harness/scripts/harness-lifecycle.mjs +430 -0
  85. package/dist/terraform/modules/app/agentcore-harness/scripts/harness-readback.mjs +180 -0
  86. package/dist/terraform/modules/app/agentcore-harness/scripts/harness-tool-contract.mjs +193 -0
  87. package/dist/terraform/modules/app/agentcore-harness/scripts/prune_harness.sh +5 -0
  88. package/dist/terraform/modules/app/agentcore-harness/scripts/read_harness.sh +5 -0
  89. package/dist/terraform/modules/app/agentcore-harness/scripts/reconcile_harness.sh +5 -0
  90. package/dist/terraform/modules/app/agentcore-harness/variables.tf +72 -1
  91. package/dist/terraform/modules/app/agentcore-identity/main.tf +173 -0
  92. package/dist/terraform/modules/app/agentcore-identity/outputs.tf +51 -0
  93. package/dist/terraform/modules/app/agentcore-identity/scripts/bootstrap_twenty_oauth_client.sh +122 -0
  94. package/dist/terraform/modules/app/agentcore-identity/scripts/delete_identity.sh +25 -0
  95. package/dist/terraform/modules/app/agentcore-identity/scripts/delete_twenty_identity.sh +16 -0
  96. package/dist/terraform/modules/app/agentcore-identity/scripts/read_identity.sh +40 -0
  97. package/dist/terraform/modules/app/agentcore-identity/scripts/reconcile_identity.sh +73 -0
  98. package/dist/terraform/modules/app/agentcore-identity/scripts/reconcile_twenty_identity.sh +74 -0
  99. package/dist/terraform/modules/app/agentcore-identity/scripts/reconcile_twenty_provider.mjs +61 -0
  100. package/dist/terraform/modules/app/agentcore-identity/variables.tf +54 -0
  101. package/dist/terraform/modules/app/appsync-subscriptions/main.tf +127 -21
  102. package/dist/terraform/modules/app/appsync-subscriptions/outputs.tf +0 -6
  103. package/dist/terraform/modules/app/appsync-subscriptions/variables.tf +5 -0
  104. package/dist/terraform/modules/app/deployment-control-plane/runner.py +966 -27
  105. package/dist/terraform/modules/app/deployment-control-plane/test_runner_bundle.py +581 -22
  106. package/dist/terraform/modules/app/lambda-api/agentcore-harness-alarms.tf +88 -0
  107. package/dist/terraform/modules/app/lambda-api/agentcore-identity-alarms.tf +91 -0
  108. package/dist/terraform/modules/app/lambda-api/handlers.tf +298 -74
  109. package/dist/terraform/modules/app/lambda-api/iam-grouped.tf +99 -54
  110. package/dist/terraform/modules/app/lambda-api/main.tf +24 -0
  111. package/dist/terraform/modules/app/lambda-api/mcp-oauth.tf +181 -4
  112. package/dist/terraform/modules/app/lambda-api/outputs.tf +35 -0
  113. package/dist/terraform/modules/app/lambda-api/runtime-config.tf +3 -21
  114. package/dist/terraform/modules/app/lambda-api/variables.tf +109 -16
  115. package/dist/terraform/modules/foundation/cognito/main.tf +294 -72
  116. package/dist/terraform/modules/foundation/cognito/outputs.tf +42 -0
  117. package/dist/terraform/modules/foundation/cognito/variables.tf +114 -4
  118. package/dist/terraform/modules/thinkwork/main.tf +269 -46
  119. package/dist/terraform/modules/thinkwork/outputs.tf +136 -8
  120. package/dist/terraform/modules/thinkwork/variables.tf +124 -17
  121. package/dist/terraform/schema.graphql +69 -36
  122. package/dist/workspace-defaults/files/USER.md +1 -0
  123. package/package.json +3 -2
  124. package/dist/drizzle/0261_thread_turns_origin_turn_index.sql +0 -11
  125. package/dist/plugins/workos-auth/package.json +0 -22
  126. package/dist/plugins/workos-auth/src/index.ts +0 -30
  127. package/dist/plugins/workos-auth/src/manifest.ts +0 -40
  128. package/dist/plugins/workos-auth/src/provider-contract.ts +0 -39
  129. package/dist/plugins/workos-auth/test/manifest.test.ts +0 -94
  130. package/dist/plugins/workos-auth/tsconfig.json +0 -8
@@ -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,40 @@
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
+ if [[ -z "$region" || -z "$workload_name" || -z "$provider_name" || -z "$twenty_provider_name" ]]; then
10
+ printf '%s\n' '{"error":"region and identity names are required"}' >&2
11
+ exit 1
12
+ fi
13
+
14
+ workload_json="$(aws bedrock-agentcore-control get-workload-identity \
15
+ --region "$region" --name "$workload_name" --output json)"
16
+ provider_json="$(aws bedrock-agentcore-control get-oauth2-credential-provider \
17
+ --region "$region" --name "$provider_name" --output json)"
18
+ twenty_provider_json="$(aws bedrock-agentcore-control get-oauth2-credential-provider \
19
+ --region "$region" --name "$twenty_provider_name" --output json)"
20
+
21
+ jq -n \
22
+ --arg workload_identity_arn "$(jq -r '.workloadIdentityArn // empty' <<<"$workload_json")" \
23
+ --arg credential_provider_arn "$(jq -r '.credentialProviderArn // empty' <<<"$provider_json")" \
24
+ --arg credential_secret_arn "$(jq -r '
25
+ if (.clientSecretArn | type) == "object"
26
+ then .clientSecretArn.secretArn // empty
27
+ else .clientSecretArn // empty
28
+ end
29
+ ' <<<"$provider_json")" \
30
+ --arg twenty_credential_provider_arn "$(jq -r '.credentialProviderArn // empty' <<<"$twenty_provider_json")" \
31
+ --arg twenty_oauth_callback_url "$(jq -r '.callbackUrl // empty' <<<"$twenty_provider_json")" \
32
+ 'if ($workload_identity_arn == "" or $credential_provider_arn == "" or $credential_secret_arn == "" or $twenty_credential_provider_arn == "" or $twenty_oauth_callback_url == "")
33
+ then error("AgentCore Identity readback omitted required ARNs")
34
+ else {
35
+ workload_identity_arn: $workload_identity_arn,
36
+ credential_provider_arn: $credential_provider_arn,
37
+ credential_secret_arn: $credential_secret_arn,
38
+ twenty_credential_provider_arn: $twenty_credential_provider_arn,
39
+ twenty_oauth_callback_url: $twenty_oauth_callback_url
40
+ } 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,74 @@
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
+ client_record="$(aws secretsmanager get-secret-value \
23
+ --region "$AWS_REGION" \
24
+ --secret-id "$TWENTY_CLIENT_SECRET_ARN" \
25
+ --query SecretString \
26
+ --output text 2>/dev/null || true)"
27
+ if [[ -z "$client_record" ]]; then
28
+ client_record='{}'
29
+ fi
30
+ client_id="$(jq -r '.client_id // empty' <<<"$client_record")"
31
+ if [[ -z "$client_id" ]] ||
32
+ ! jq -e '.client_secret | type == "string" and length > 0' \
33
+ <<<"$client_record" >/dev/null 2>&1; then
34
+ printf '%s\n' \
35
+ 'Twenty confidential client is not bootstrapped; run bootstrap_twenty_oauth_client.sh with an authenticated administrator token secret' >&2
36
+ exit 1
37
+ fi
38
+
39
+ provider_payload="$(jq -n \
40
+ --arg region "$AWS_REGION" \
41
+ --arg name "$TWENTY_CREDENTIAL_PROVIDER_NAME" \
42
+ --arg issuer "$TWENTY_OAUTH_ISSUER" \
43
+ --arg authorizationEndpoint "${TWENTY_OAUTH_ISSUER%/}/authorize" \
44
+ --arg tokenEndpoint "${TWENTY_OAUTH_ISSUER%/}/oauth/token" \
45
+ --arg clientId "$client_id" \
46
+ --arg secretArn "$TWENTY_CLIENT_SECRET_ARN" \
47
+ '{
48
+ region: $region,
49
+ name: $name,
50
+ issuer: $issuer,
51
+ authorizationEndpoint: $authorizationEndpoint,
52
+ tokenEndpoint: $tokenEndpoint,
53
+ clientId: $clientId,
54
+ secretArn: $secretArn
55
+ }')"
56
+
57
+ script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
58
+ provider_response="$(printf '%s' "$provider_payload" | \
59
+ node "$script_dir/reconcile_twenty_provider.mjs")"
60
+ callback_url="$(jq -r '.callbackUrl // empty' <<<"$provider_response")"
61
+ if [[ -z "$callback_url" || "$callback_url" == "None" ]]; then
62
+ printf '%s\n' 'AgentCore Identity did not return the Twenty callback URL' >&2
63
+ exit 1
64
+ fi
65
+
66
+ registered_callback="$(jq -r '.redirect_uris[0] // empty' <<<"$client_record")"
67
+ if [[ "$registered_callback" != "$callback_url" ]]; then
68
+ printf 'Twenty callback mismatch: registered=%s agentcore=%s\n' \
69
+ "$registered_callback" "$callback_url" >&2
70
+ exit 1
71
+ fi
72
+
73
+ printf 'AgentCore Twenty provider reconciled: workload=%s provider=%s resource=%s\n' \
74
+ "$WORKLOAD_IDENTITY_NAME" "$TWENTY_CREDENTIAL_PROVIDER_NAME" "$TWENTY_OAUTH_RESOURCE"
@@ -0,0 +1,61 @@
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
+ const chunks = [];
12
+ for await (const chunk of process.stdin) chunks.push(chunk);
13
+ const input = JSON.parse(Buffer.concat(chunks).toString("utf8"));
14
+
15
+ const client = new BedrockAgentCoreControlClient({ region: input.region });
16
+ const providerInput = {
17
+ name: input.name,
18
+ credentialProviderVendor: "CustomOauth2",
19
+ oauth2ProviderConfigInput: {
20
+ customOauth2ProviderConfig: {
21
+ oauthDiscovery: {
22
+ authorizationServerMetadata: {
23
+ issuer: input.issuer,
24
+ authorizationEndpoint: input.authorizationEndpoint,
25
+ tokenEndpoint: input.tokenEndpoint,
26
+ responseTypes: ["code"],
27
+ },
28
+ },
29
+ clientId: input.clientId,
30
+ clientSecretConfig: {
31
+ secretId: input.secretArn,
32
+ jsonKey: "client_secret",
33
+ },
34
+ clientSecretSource: "EXTERNAL",
35
+ clientAuthenticationMethod: "CLIENT_SECRET_POST",
36
+ },
37
+ },
38
+ };
39
+
40
+ let exists = false;
41
+ try {
42
+ await client.send(
43
+ new GetOauth2CredentialProviderCommand({ name: input.name }),
44
+ );
45
+ exists = true;
46
+ } catch (error) {
47
+ if (!(error instanceof ResourceNotFoundException)) throw error;
48
+ }
49
+
50
+ const response = exists
51
+ ? await client.send(new UpdateOauth2CredentialProviderCommand(providerInput))
52
+ : await client.send(new CreateOauth2CredentialProviderCommand(providerInput));
53
+
54
+ process.stdout.write(
55
+ JSON.stringify({
56
+ name: response.name,
57
+ callbackUrl: response.callbackUrl,
58
+ credentialProviderArn: response.credentialProviderArn,
59
+ clientSecretSource: response.clientSecretSource,
60
+ }),
61
+ );
@@ -0,0 +1,54 @@
1
+ variable "enabled" {
2
+ description = "Reconcile the non-production THINK-316 Identity proof resources."
3
+ type = bool
4
+ default = false
5
+ }
6
+
7
+ variable "stage" {
8
+ description = "Deployment stage."
9
+ type = string
10
+ }
11
+
12
+ variable "region" {
13
+ description = "AWS region."
14
+ type = string
15
+ }
16
+
17
+ variable "account_id" {
18
+ description = "AWS account id used to scope the external OAuth client-secret policy."
19
+ type = string
20
+ }
21
+
22
+ variable "oauth_issuer" {
23
+ description = "Synthetic provider issuer URL exposed through the owned HTTP API."
24
+ type = string
25
+ }
26
+
27
+ variable "oauth_client_id" {
28
+ description = "Synthetic OAuth client id."
29
+ type = string
30
+ }
31
+
32
+ variable "oauth_client_secret" {
33
+ description = "Synthetic OAuth client secret; passed to the reconciler through its environment only."
34
+ type = string
35
+ sensitive = true
36
+ }
37
+
38
+ variable "user_federation_return_urls" {
39
+ description = "Exact ThinkWork callbacks AgentCore may redirect to after user-federation authorization."
40
+ type = list(string)
41
+ default = []
42
+ }
43
+
44
+ variable "twenty_oauth_issuer" {
45
+ description = "Twenty OAuth authorization-server issuer."
46
+ type = string
47
+ default = "https://crm.thinkwork.ai"
48
+ }
49
+
50
+ variable "twenty_oauth_resource" {
51
+ description = "RFC 8707 resource identifier requested for the Twenty MCP server."
52
+ type = string
53
+ default = "https://crm.thinkwork.ai/mcp"
54
+ }
@@ -18,21 +18,19 @@
18
18
 
19
19
  resource "aws_appsync_graphql_api" "subscriptions" {
20
20
  name = "thinkwork-${var.stage}-subscriptions"
21
- authentication_type = "API_KEY"
21
+ authentication_type = "AWS_LAMBDA"
22
22
  xray_enabled = false
23
23
 
24
24
  schema = var.subscription_schema
25
25
 
26
- additional_authentication_provider {
27
- authentication_type = "AWS_IAM"
26
+ lambda_authorizer_config {
27
+ authorizer_uri = "arn:aws:lambda:${var.region}:${var.account_id}:function:thinkwork-${var.stage}-api-appsync-subscription-authorizer"
28
+ authorizer_result_ttl_in_seconds = 0
29
+ identity_validation_expression = "^twsub1_[A-Za-z0-9_-]+$"
28
30
  }
29
31
 
30
32
  additional_authentication_provider {
31
- authentication_type = "AMAZON_COGNITO_USER_POOLS"
32
-
33
- user_pool_config {
34
- user_pool_id = var.user_pool_id
35
- }
33
+ authentication_type = "AWS_IAM"
36
34
  }
37
35
 
38
36
  tags = {
@@ -40,19 +38,6 @@ resource "aws_appsync_graphql_api" "subscriptions" {
40
38
  }
41
39
  }
42
40
 
43
- ################################################################################
44
- # API Key — 365 day expiry
45
- ################################################################################
46
-
47
- resource "aws_appsync_api_key" "main" {
48
- api_id = aws_appsync_graphql_api.subscriptions.id
49
- expires = timeadd(timestamp(), "8760h")
50
-
51
- lifecycle {
52
- ignore_changes = [expires]
53
- }
54
- }
55
-
56
41
  ################################################################################
57
42
  # NONE Data Source (passthrough for notification mutations)
58
43
  ################################################################################
@@ -84,6 +69,20 @@ locals {
84
69
  "notifyEvalRunUpdate",
85
70
  "notifyWorkspaceAccessRevoked",
86
71
  ]
72
+ subscription_scopes = {
73
+ onAgentStatusChanged = { argument = "tenantId", kind = "tenant" }
74
+ onNewMessage = { argument = "threadId", kind = "thread" }
75
+ onHeartbeatActivity = { argument = "tenantId", kind = "tenant" }
76
+ onThreadUpdated = { argument = "tenantId", kind = "tenant" }
77
+ onThreadActivity = { argument = "userId", kind = "user" }
78
+ onInboxItemStatusChanged = { argument = "tenantId", kind = "tenant" }
79
+ onThreadTurnUpdated = { argument = "tenantId", kind = "tenant" }
80
+ onThreadTurnStep = { argument = "threadId", kind = "thread" }
81
+ onOrgUpdated = { argument = "tenantId", kind = "tenant" }
82
+ onCostRecorded = { argument = "tenantId", kind = "tenant" }
83
+ onEvalRunUpdated = { argument = "tenantId", kind = "tenant" }
84
+ onWorkspaceAccessRevoked = { argument = "userId", kind = "user" }
85
+ }
87
86
  }
88
87
 
89
88
  resource "aws_appsync_resolver" "notifications" {
@@ -113,6 +112,113 @@ resource "aws_appsync_resolver" "notifications" {
113
112
  EOF
114
113
  }
115
114
 
115
+ ################################################################################
116
+ # Subscription registration filters + active-connection invalidation
117
+ ################################################################################
118
+
119
+ resource "aws_appsync_resolver" "subscription_admission" {
120
+ for_each = local.subscription_scopes
121
+
122
+ api_id = aws_appsync_graphql_api.subscriptions.id
123
+ type = "Subscription"
124
+ field = each.key
125
+ data_source = aws_appsync_datasource.none.name
126
+
127
+ runtime {
128
+ name = "APPSYNC_JS"
129
+ runtime_version = "1.0.0"
130
+ }
131
+
132
+ code = <<-JS
133
+ import { util, extensions } from '@aws-appsync/utils';
134
+
135
+ export function request() {
136
+ return { payload: null };
137
+ }
138
+
139
+ export function response(ctx) {
140
+ const identity = (ctx.identity && ctx.identity.resolverContext) || {};
141
+ const tenantId = identity.tenantId;
142
+ const userId = identity.stableUserId;
143
+ const resourceKind = identity.resourceKind;
144
+ const resourceId = identity.resourceId;
145
+ if (!tenantId || !userId || !resourceKind || !resourceId) {
146
+ util.unauthorized();
147
+ }
148
+
149
+ extensions.setSubscriptionFilter(util.transform.toSubscriptionFilter({
150
+ ${each.value.argument}: { eq: ctx.args.${each.value.argument} },
151
+ }));
152
+ extensions.setSubscriptionInvalidationFilter({
153
+ filterGroup: [
154
+ { filters: [
155
+ { fieldName: 'scope', operator: 'eq', value: 'tenant' },
156
+ { fieldName: 'tenantId', operator: 'eq', value: tenantId },
157
+ ]},
158
+ { filters: [
159
+ { fieldName: 'scope', operator: 'eq', value: 'user' },
160
+ { fieldName: 'tenantId', operator: 'eq', value: tenantId },
161
+ { fieldName: 'userId', operator: 'eq', value: userId },
162
+ ]},
163
+ { filters: [
164
+ { fieldName: 'scope', operator: 'eq', value: 'resource' },
165
+ { fieldName: 'tenantId', operator: 'eq', value: tenantId },
166
+ { fieldName: 'userId', operator: 'eq', value: userId },
167
+ { fieldName: 'resourceKind', operator: 'eq', value: resourceKind },
168
+ { fieldName: 'resourceId', operator: 'eq', value: resourceId },
169
+ ]},
170
+ ],
171
+ });
172
+ return null;
173
+ }
174
+ JS
175
+ }
176
+
177
+ resource "aws_appsync_resolver" "subscription_invalidation" {
178
+ api_id = aws_appsync_graphql_api.subscriptions.id
179
+ type = "Mutation"
180
+ field = "invalidateSubscription"
181
+ data_source = aws_appsync_datasource.none.name
182
+
183
+ runtime {
184
+ name = "APPSYNC_JS"
185
+ runtime_version = "1.0.0"
186
+ }
187
+
188
+ code = <<-JS
189
+ import { util, extensions } from '@aws-appsync/utils';
190
+
191
+ const fields = {
192
+ ON_AGENT_STATUS_CHANGED: 'onAgentStatusChanged',
193
+ ON_NEW_MESSAGE: 'onNewMessage',
194
+ ON_HEARTBEAT_ACTIVITY: 'onHeartbeatActivity',
195
+ ON_THREAD_UPDATED: 'onThreadUpdated',
196
+ ON_THREAD_ACTIVITY: 'onThreadActivity',
197
+ ON_INBOX_ITEM_STATUS_CHANGED: 'onInboxItemStatusChanged',
198
+ ON_THREAD_TURN_UPDATED: 'onThreadTurnUpdated',
199
+ ON_THREAD_TURN_STEP: 'onThreadTurnStep',
200
+ ON_ORG_UPDATED: 'onOrgUpdated',
201
+ ON_COST_RECORDED: 'onCostRecorded',
202
+ ON_EVAL_RUN_UPDATED: 'onEvalRunUpdated',
203
+ ON_WORKSPACE_ACCESS_REVOKED: 'onWorkspaceAccessRevoked',
204
+ };
205
+
206
+ export function request(ctx) {
207
+ return { payload: { ...ctx.args, subscriptionField: fields[ctx.args.subscriptionField] } };
208
+ }
209
+
210
+ export function response(ctx) {
211
+ const payload = ctx.result;
212
+ if (!payload.subscriptionField) util.error('Invalid subscription field');
213
+ extensions.invalidateSubscriptions({
214
+ subscriptionField: payload.subscriptionField,
215
+ payload,
216
+ });
217
+ return payload;
218
+ }
219
+ JS
220
+ }
221
+
116
222
  ################################################################################
117
223
  # Custom Domain (optional)
118
224
  ################################################################################
@@ -17,9 +17,3 @@ output "graphql_realtime_url" {
17
17
  description = "AppSync realtime WebSocket URL (used by frontend subscription clients)"
18
18
  value = aws_appsync_graphql_api.subscriptions.uris["REALTIME"]
19
19
  }
20
-
21
- output "graphql_api_key" {
22
- description = "AppSync API key"
23
- value = aws_appsync_api_key.main.key
24
- sensitive = true
25
- }
@@ -8,6 +8,11 @@ variable "region" {
8
8
  type = string
9
9
  }
10
10
 
11
+ variable "account_id" {
12
+ description = "AWS account ID hosting the AppSync authorizer Lambda"
13
+ type = string
14
+ }
15
+
11
16
  variable "user_pool_id" {
12
17
  description = "Cognito user pool ID for authentication"
13
18
  type = string