thinkwork-cli 0.9.0 → 0.9.1
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/LICENSE +202 -0
- package/README.md +2 -2
- package/dist/cli.js +1187 -315
- package/dist/terraform/examples/greenfield/main.tf +325 -19
- package/dist/terraform/examples/greenfield/terraform.tfvars.example +14 -0
- package/dist/terraform/modules/app/agentcore-code-interpreter/Dockerfile.sandbox-base +61 -0
- package/dist/terraform/modules/app/agentcore-code-interpreter/README.md +54 -0
- package/dist/terraform/modules/app/agentcore-code-interpreter/main.tf +197 -0
- package/dist/terraform/modules/app/agentcore-code-interpreter/scripts/build_and_push_sandbox_base.sh +70 -0
- package/dist/terraform/modules/app/agentcore-flue/README.md +58 -0
- package/dist/terraform/modules/app/agentcore-flue/main.tf +322 -0
- package/dist/terraform/modules/app/agentcore-flue/outputs.tf +23 -0
- package/dist/terraform/modules/app/agentcore-flue/variables.tf +91 -0
- package/dist/terraform/modules/app/agentcore-memory/scripts/create_or_find_memory.sh +0 -0
- package/dist/terraform/modules/app/agentcore-runtime/main.tf +165 -0
- package/dist/terraform/modules/app/appsync-subscriptions/main.tf +4 -0
- package/dist/terraform/modules/app/appsync-subscriptions/outputs.tf +5 -0
- package/dist/terraform/modules/app/computer-runtime/README.md +15 -0
- package/dist/terraform/modules/app/computer-runtime/main.tf +406 -0
- package/dist/terraform/modules/app/computer-runtime/outputs.tf +75 -0
- package/dist/terraform/modules/app/computer-runtime/variables.tf +66 -0
- package/dist/terraform/modules/app/hindsight-memory/main.tf +6 -0
- package/dist/terraform/modules/app/lambda-api/eval-fanout.tf +128 -0
- package/dist/terraform/modules/app/lambda-api/handlers.tf +1454 -43
- package/dist/terraform/modules/app/lambda-api/main.tf +221 -12
- package/dist/terraform/modules/app/lambda-api/mcp-oauth.tf +118 -0
- package/dist/terraform/modules/app/lambda-api/oauth-secrets.tf +49 -0
- package/dist/terraform/modules/app/lambda-api/outputs.tf +38 -0
- package/dist/terraform/modules/app/lambda-api/slack-app-secrets.tf +43 -0
- package/dist/terraform/modules/app/lambda-api/stripe-secrets.tf +53 -0
- package/dist/terraform/modules/app/lambda-api/variables.tf +349 -2
- package/dist/terraform/modules/app/lambda-api/workspace-events.tf +125 -0
- package/dist/terraform/modules/app/routines-stepfunctions/main.tf +453 -0
- package/dist/terraform/modules/app/sandbox-log-scrubber/README.md +66 -0
- package/dist/terraform/modules/app/sandbox-log-scrubber/main.tf +200 -0
- package/dist/terraform/modules/app/static-site/main.tf +146 -5
- package/dist/terraform/modules/app/www-dns/main.tf +118 -15
- package/dist/terraform/modules/app/www-dns/outputs.tf +10 -0
- package/dist/terraform/modules/app/www-dns/variables.tf +42 -0
- package/dist/terraform/modules/data/aurora-postgres/main.tf +164 -3
- package/dist/terraform/modules/data/aurora-postgres/outputs.tf +34 -0
- package/dist/terraform/modules/data/aurora-postgres/variables.tf +16 -0
- package/dist/terraform/modules/data/compliance-audit-bucket/README.md +145 -0
- package/dist/terraform/modules/data/compliance-audit-bucket/main.tf +573 -0
- package/dist/terraform/modules/data/compliance-audit-bucket/outputs.tf +43 -0
- package/dist/terraform/modules/data/compliance-audit-bucket/variables.tf +93 -0
- package/dist/terraform/modules/data/compliance-exports-bucket/main.tf +269 -0
- package/dist/terraform/modules/data/compliance-exports-bucket/outputs.tf +23 -0
- package/dist/terraform/modules/data/compliance-exports-bucket/variables.tf +50 -0
- package/dist/terraform/modules/data/s3-backups-bucket/main.tf +123 -0
- package/dist/terraform/modules/data/s3-buckets/main.tf +13 -0
- package/dist/terraform/modules/foundation/cognito/variables.tf +2 -2
- package/dist/terraform/modules/thinkwork/main.tf +439 -21
- package/dist/terraform/modules/thinkwork/outputs.tf +121 -0
- package/dist/terraform/modules/thinkwork/variables.tf +153 -2
- package/dist/terraform/schema.graphql +17 -0
- package/package.json +15 -14
|
@@ -111,6 +111,12 @@ variable "lambda_zips_dir" {
|
|
|
111
111
|
default = ""
|
|
112
112
|
}
|
|
113
113
|
|
|
114
|
+
variable "enable_workspace_orchestration" {
|
|
115
|
+
description = "Enable S3 EventBridge/SQS routing and the workspace event dispatcher for folder-native workspace orchestration."
|
|
116
|
+
type = bool
|
|
117
|
+
default = false
|
|
118
|
+
}
|
|
119
|
+
|
|
114
120
|
variable "api_auth_secret" {
|
|
115
121
|
description = "Shared secret for inter-service API authentication"
|
|
116
122
|
type = string
|
|
@@ -136,26 +142,203 @@ variable "ses_inbound_domain" {
|
|
|
136
142
|
default = ""
|
|
137
143
|
}
|
|
138
144
|
|
|
139
|
-
variable "
|
|
145
|
+
variable "stripe_price_ids_json" {
|
|
146
|
+
description = "JSON object mapping internal plan names to Stripe price IDs for this stage, e.g. {\"starter\":\"price_...\",\"team\":\"price_...\"}. Non-secret; per-stage. Exposed to Lambdas as STRIPE_PRICE_IDS_JSON env var. The secret keys themselves live in AWS Secrets Manager at thinkwork/<stage>/stripe/api-credentials — never in tfvars."
|
|
147
|
+
type = string
|
|
148
|
+
default = "{}"
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
variable "wiki_compile_model_id" {
|
|
152
|
+
description = <<-EOT
|
|
153
|
+
Bedrock model id the wiki-compile Lambda uses for the leaf planner,
|
|
154
|
+
aggregation planner, and section writer. Any Converse-compatible
|
|
155
|
+
model works; change without a code deploy.
|
|
156
|
+
|
|
157
|
+
Default: openai.gpt-oss-120b-1:0 (strong output quality at a lower
|
|
158
|
+
per-minute throttle risk than Claude Haiku 4.5 on shared dev
|
|
159
|
+
accounts). Swap to us.anthropic.claude-haiku-4-5-20251001-v1:0 for
|
|
160
|
+
Claude, or amazon.nova-micro-v1:0 for a low-cost spike.
|
|
161
|
+
EOT
|
|
162
|
+
type = string
|
|
163
|
+
default = "openai.gpt-oss-120b-1:0"
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
variable "company_brain_source_agent_model_id" {
|
|
167
|
+
description = <<-EOT
|
|
168
|
+
Bedrock model id the GraphQL Company Brain source-agent runtime uses
|
|
169
|
+
for JSON tool/action turns. Defaults to Claude Haiku 4.5 for reliable
|
|
170
|
+
action JSON while wiki compile can stay on gpt-oss for throughput.
|
|
171
|
+
EOT
|
|
172
|
+
type = string
|
|
173
|
+
default = "us.anthropic.claude-haiku-4-5-20251001-v1:0"
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
variable "wiki_aggregation_pass_enabled" {
|
|
177
|
+
description = <<-EOT
|
|
178
|
+
Feature flag for the wiki aggregation pass — the second LLM call
|
|
179
|
+
per compile job that builds parent/hub rollup sections and promotes
|
|
180
|
+
dense sections into their own topic pages.
|
|
181
|
+
|
|
182
|
+
Accepts a string so the Lambda reads the env var verbatim; must be
|
|
183
|
+
"true" / "1" / "yes" to enable. Set to "false" to stop the pipeline
|
|
184
|
+
after the leaf pass (no rollups, no promotions).
|
|
185
|
+
EOT
|
|
186
|
+
type = string
|
|
187
|
+
default = "true"
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
variable "google_places_api_key" {
|
|
191
|
+
description = <<-EOT
|
|
192
|
+
Google Places API (New) key used by wiki-compile to enrich POI records
|
|
193
|
+
with city/state/country hierarchy during compile. When empty, compile
|
|
194
|
+
gracefully degrades to metadata-only place rows (no hierarchy, no
|
|
195
|
+
backing pages), so this is opt-in. Stored as a SecureString at
|
|
196
|
+
/thinkwork/<stage>/google-places/api-key — see
|
|
197
|
+
terraform/modules/app/lambda-api/handlers.tf for the SSM resource.
|
|
198
|
+
|
|
199
|
+
The parameter's value has lifecycle.ignore_changes set, so you can
|
|
200
|
+
rotate via `aws ssm put-parameter --overwrite` without terraform
|
|
201
|
+
fighting you on the next apply.
|
|
202
|
+
EOT
|
|
203
|
+
type = string
|
|
204
|
+
default = ""
|
|
205
|
+
sensitive = true
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
variable "mapbox_public_token" {
|
|
140
209
|
description = <<-EOT
|
|
141
|
-
|
|
210
|
+
Mapbox public pk.* token consumed by apps/computer's MapView primitive
|
|
211
|
+
(in @thinkwork/computer-stdlib) for inline map tile rendering inside
|
|
212
|
+
generated applets. Flows through to scripts/build-computer.sh →
|
|
213
|
+
apps/computer/.env.production as VITE_MAPBOX_PUBLIC_TOKEN.
|
|
214
|
+
|
|
215
|
+
Mapbox tokens are designed to ship in public bundles; URL allowlist
|
|
216
|
+
on the Mapbox dashboard is the security boundary. Restrict the token
|
|
217
|
+
to the deployed `computer.<apex>` host (and any dev hosts).
|
|
218
|
+
|
|
219
|
+
Empty string is acceptable: MapView falls back to OpenStreetMap tiles
|
|
220
|
+
when the build-time env var is unset, so dev environments without an
|
|
221
|
+
operator-provisioned token still render maps.
|
|
222
|
+
EOT
|
|
223
|
+
type = string
|
|
224
|
+
default = ""
|
|
225
|
+
sensitive = true
|
|
226
|
+
}
|
|
142
227
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
228
|
+
variable "nova_act_api_key" {
|
|
229
|
+
description = <<-EOT
|
|
230
|
+
Nova Act API key used by the Strands Browser Automation tool. When empty,
|
|
231
|
+
Terraform creates /thinkwork/<stage>/agentcore/nova-act-api-key with a
|
|
232
|
+
placeholder value; populate or rotate the real key with:
|
|
233
|
+
aws ssm put-parameter --overwrite --name /thinkwork/<stage>/agentcore/nova-act-api-key --type SecureString --value <KEY>
|
|
147
234
|
|
|
148
|
-
|
|
149
|
-
|
|
235
|
+
The parameter's value has lifecycle.ignore_changes set, so operator
|
|
236
|
+
rotation sticks across applies.
|
|
150
237
|
EOT
|
|
151
238
|
type = string
|
|
152
239
|
default = ""
|
|
240
|
+
sensitive = true
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
variable "wiki_deterministic_linking_enabled" {
|
|
244
|
+
description = <<-EOT
|
|
245
|
+
Feature flag for deterministic compile-time link emission:
|
|
246
|
+
- city/journal parent references from parent-expander candidates
|
|
247
|
+
- entity↔entity co-mention edges via wiki_section_sources
|
|
248
|
+
|
|
249
|
+
Accepts a string so the Lambda reads the env var verbatim; must be
|
|
250
|
+
"true" / "1" / "yes" to enable. Rollback is a targeted DELETE:
|
|
251
|
+
`DELETE FROM wiki_page_links WHERE context LIKE 'deterministic:%' OR
|
|
252
|
+
context LIKE 'co_mention:%'` — provenance is preserved on every row.
|
|
253
|
+
EOT
|
|
254
|
+
type = string
|
|
255
|
+
default = "true"
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
variable "agentcore_code_interpreter_id" {
|
|
259
|
+
description = "AgentCore Code Interpreter id used by routine-task-python for SFN python recipe states."
|
|
260
|
+
type = string
|
|
261
|
+
default = ""
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
variable "mcp_custom_domain" {
|
|
265
|
+
description = <<-EOT
|
|
266
|
+
MCP custom domain (e.g. "mcp.thinkwork.ai"). Empty disables the
|
|
267
|
+
custom-domain setup — the MCP endpoint stays reachable at the API
|
|
268
|
+
Gateway execute-api URL. When set, an ACM cert is created on the
|
|
269
|
+
first apply; flip `mcp_custom_domain_ready = true` on a second
|
|
270
|
+
apply after DNS validation completes. See
|
|
271
|
+
docs/solutions/patterns/mcp-custom-domain-setup-2026-04-23.md.
|
|
272
|
+
EOT
|
|
273
|
+
type = string
|
|
274
|
+
default = ""
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
variable "mcp_custom_domain_ready" {
|
|
278
|
+
description = <<-EOT
|
|
279
|
+
Second-apply gate for the MCP custom domain. Stays false on the
|
|
280
|
+
first apply (cert creation + DNS validation). Flip to true after
|
|
281
|
+
ACM shows the cert as ISSUED so the second apply can create the
|
|
282
|
+
API Gateway custom domain + mapping. Ignored when
|
|
283
|
+
mcp_custom_domain is empty.
|
|
284
|
+
EOT
|
|
285
|
+
type = bool
|
|
286
|
+
default = false
|
|
153
287
|
}
|
|
154
288
|
|
|
155
289
|
locals {
|
|
156
290
|
www_dns_enabled = var.www_domain != "" && var.cloudflare_zone_id != ""
|
|
157
291
|
docs_domain = var.www_domain != "" ? "docs.${var.www_domain}" : ""
|
|
158
292
|
admin_domain = var.www_domain != "" ? "admin.${var.www_domain}" : ""
|
|
293
|
+
computer_domain = var.www_domain != "" ? "computer.${var.www_domain}" : ""
|
|
294
|
+
sandbox_domain = var.www_domain != "" ? "sandbox.${var.www_domain}" : ""
|
|
295
|
+
api_domain = var.www_domain != "" ? "api.${var.www_domain}" : ""
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
resource "aws_acm_certificate" "computer_sandbox" {
|
|
299
|
+
count = local.www_dns_enabled ? 1 : 0
|
|
300
|
+
|
|
301
|
+
domain_name = local.sandbox_domain
|
|
302
|
+
validation_method = "DNS"
|
|
303
|
+
|
|
304
|
+
lifecycle {
|
|
305
|
+
create_before_destroy = true
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
tags = {
|
|
309
|
+
Name = "thinkwork-${var.stage}-computer-sandbox"
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
resource "cloudflare_record" "computer_sandbox_acm_validation" {
|
|
314
|
+
for_each = {
|
|
315
|
+
for dvo in flatten([
|
|
316
|
+
for cert in aws_acm_certificate.computer_sandbox : tolist(cert.domain_validation_options)
|
|
317
|
+
]) : dvo.domain_name => {
|
|
318
|
+
name = dvo.resource_record_name
|
|
319
|
+
value = dvo.resource_record_value
|
|
320
|
+
type = dvo.resource_record_type
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
zone_id = var.cloudflare_zone_id
|
|
325
|
+
name = trimsuffix(each.value.name, ".")
|
|
326
|
+
content = trimsuffix(each.value.value, ".")
|
|
327
|
+
type = each.value.type
|
|
328
|
+
ttl = 60
|
|
329
|
+
proxied = false
|
|
330
|
+
comment = "ACM DNS validation for ${each.key}"
|
|
331
|
+
|
|
332
|
+
allow_overwrite = true
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
resource "aws_acm_certificate_validation" "computer_sandbox" {
|
|
336
|
+
count = local.www_dns_enabled ? 1 : 0
|
|
337
|
+
|
|
338
|
+
certificate_arn = aws_acm_certificate.computer_sandbox[0].arn
|
|
339
|
+
validation_record_fqdns = [
|
|
340
|
+
for record in cloudflare_record.computer_sandbox_acm_validation : record.hostname
|
|
341
|
+
]
|
|
159
342
|
}
|
|
160
343
|
|
|
161
344
|
module "thinkwork" {
|
|
@@ -165,14 +348,15 @@ module "thinkwork" {
|
|
|
165
348
|
region = var.region
|
|
166
349
|
account_id = var.account_id
|
|
167
350
|
|
|
168
|
-
db_password
|
|
169
|
-
database_engine
|
|
170
|
-
enable_hindsight
|
|
171
|
-
google_oauth_client_id
|
|
172
|
-
google_oauth_client_secret
|
|
173
|
-
pre_signup_lambda_zip
|
|
174
|
-
lambda_zips_dir
|
|
175
|
-
|
|
351
|
+
db_password = var.db_password
|
|
352
|
+
database_engine = var.database_engine
|
|
353
|
+
enable_hindsight = var.enable_hindsight
|
|
354
|
+
google_oauth_client_id = var.google_oauth_client_id
|
|
355
|
+
google_oauth_client_secret = var.google_oauth_client_secret
|
|
356
|
+
pre_signup_lambda_zip = var.pre_signup_lambda_zip
|
|
357
|
+
lambda_zips_dir = var.lambda_zips_dir
|
|
358
|
+
enable_workspace_orchestration = var.enable_workspace_orchestration
|
|
359
|
+
api_auth_secret = var.api_auth_secret
|
|
176
360
|
|
|
177
361
|
# Public website custom domain (optional — wired only when www_domain is set)
|
|
178
362
|
www_domain = var.www_domain
|
|
@@ -188,12 +372,46 @@ module "thinkwork" {
|
|
|
188
372
|
admin_domain = local.www_dns_enabled ? local.admin_domain : ""
|
|
189
373
|
admin_certificate_arn = local.www_dns_enabled ? module.www_dns[0].certificate_arn : ""
|
|
190
374
|
|
|
375
|
+
# Computer SPA custom domain (derived from www_domain — computer.<apex>).
|
|
376
|
+
# Same ACM cert covers it via the include_computer SAN gate on www_dns.
|
|
377
|
+
computer_domain = local.www_dns_enabled ? local.computer_domain : ""
|
|
378
|
+
computer_certificate_arn = local.www_dns_enabled ? module.www_dns[0].certificate_arn : ""
|
|
379
|
+
|
|
380
|
+
# Computer iframe sandbox (derived from www_domain — sandbox.<apex>).
|
|
381
|
+
# Uses its own ACM certificate so sandbox bootstrap/rotation cannot replace
|
|
382
|
+
# the shared apex/www/docs/admin/computer/api certificate.
|
|
383
|
+
computer_sandbox_domain = local.www_dns_enabled ? local.sandbox_domain : ""
|
|
384
|
+
computer_sandbox_certificate_arn = local.www_dns_enabled ? aws_acm_certificate_validation.computer_sandbox[0].certificate_arn : ""
|
|
385
|
+
computer_sandbox_allowed_parent_origins = local.www_dns_enabled ? "https://${local.computer_domain},https://${local.admin_domain}" : ""
|
|
386
|
+
|
|
191
387
|
# SES inbound email subdomain (delegated Route53 subzone).
|
|
192
388
|
ses_inbound_domain = var.ses_inbound_domain
|
|
193
389
|
|
|
194
|
-
#
|
|
195
|
-
#
|
|
196
|
-
|
|
390
|
+
# Wiki compile Lambda config. Pinned so unrelated terraform applies
|
|
391
|
+
# don't wipe the Bedrock model or the aggregation flag back to
|
|
392
|
+
# whatever the Lambda env defaults to.
|
|
393
|
+
wiki_compile_model_id = var.wiki_compile_model_id
|
|
394
|
+
company_brain_source_agent_model_id = var.company_brain_source_agent_model_id
|
|
395
|
+
wiki_aggregation_pass_enabled = var.wiki_aggregation_pass_enabled
|
|
396
|
+
wiki_deterministic_linking_enabled = var.wiki_deterministic_linking_enabled
|
|
397
|
+
google_places_api_key = var.google_places_api_key
|
|
398
|
+
nova_act_api_key = var.nova_act_api_key
|
|
399
|
+
agentcore_code_interpreter_id = var.agentcore_code_interpreter_id
|
|
400
|
+
|
|
401
|
+
# Mapbox public token for apps/computer MapView primitive. Flows through
|
|
402
|
+
# to scripts/build-computer.sh → VITE_MAPBOX_PUBLIC_TOKEN.
|
|
403
|
+
mapbox_public_token = var.mapbox_public_token
|
|
404
|
+
|
|
405
|
+
# Stripe billing — internal-plan → price-id map (per-stage, non-secret).
|
|
406
|
+
stripe_price_ids_json = var.stripe_price_ids_json
|
|
407
|
+
|
|
408
|
+
# MCP custom domain (e.g. mcp.thinkwork.ai). Two-apply flow: the first
|
|
409
|
+
# apply creates the ACM cert (mcp_custom_domain_ready=false), the
|
|
410
|
+
# second apply attaches the domain + API mapping after DNS validation
|
|
411
|
+
# (mcp_custom_domain_ready=true). Runbook:
|
|
412
|
+
# docs/solutions/patterns/mcp-custom-domain-setup-2026-04-23.md
|
|
413
|
+
mcp_custom_domain = var.mcp_custom_domain
|
|
414
|
+
mcp_custom_domain_ready = var.mcp_custom_domain_ready
|
|
197
415
|
|
|
198
416
|
# Greenfield: create everything (all defaults are true)
|
|
199
417
|
}
|
|
@@ -221,6 +439,27 @@ module "www_dns" {
|
|
|
221
439
|
# Admin: same cycle-avoidance pattern.
|
|
222
440
|
include_admin = true
|
|
223
441
|
admin_cloudfront_domain_name = module.thinkwork.admin_distribution_domain
|
|
442
|
+
|
|
443
|
+
# Computer: same cycle-avoidance pattern as docs/admin. Phase one of the
|
|
444
|
+
# two-phase first apply (#971) used a static empty string here to dodge an
|
|
445
|
+
# "Invalid count argument" plan error while module.thinkwork.computer_site
|
|
446
|
+
# didn't yet exist in state. Now that the distribution is created and its
|
|
447
|
+
# domain is known, this can read the real output — `!= ""` resolves to a
|
|
448
|
+
# static true and the Cloudflare CNAME for computer.<domain> gets created
|
|
449
|
+
# on this apply.
|
|
450
|
+
include_computer = true
|
|
451
|
+
computer_cloudfront_domain_name = module.thinkwork.computer_distribution_domain
|
|
452
|
+
|
|
453
|
+
# Computer iframe sandbox: same cycle-avoidance pattern as computer.
|
|
454
|
+
include_computer_sandbox = true
|
|
455
|
+
computer_sandbox_cloudfront_domain_name = module.thinkwork.computer_sandbox_distribution_domain
|
|
456
|
+
|
|
457
|
+
# API custom domain (api.<apex>). Same cycle-avoidance — the ACM cert SAN
|
|
458
|
+
# list is gated on include_api (a plain bool), while api_gateway_id (which
|
|
459
|
+
# the cert doesn't depend on) is read at record-creation time.
|
|
460
|
+
include_api = true
|
|
461
|
+
api_gateway_id = module.thinkwork.api_id
|
|
462
|
+
api_gateway_stage_name = "$default"
|
|
224
463
|
}
|
|
225
464
|
|
|
226
465
|
################################################################################
|
|
@@ -257,6 +496,11 @@ output "api_endpoint" {
|
|
|
257
496
|
value = module.thinkwork.api_endpoint
|
|
258
497
|
}
|
|
259
498
|
|
|
499
|
+
output "api_domain" {
|
|
500
|
+
description = "Custom domain for the HTTP API (e.g. api.thinkwork.ai). Empty string when www_domain/cloudflare_zone_id aren't configured. Read by scripts/build-www.sh to set PUBLIC_API_URL at build time."
|
|
501
|
+
value = local.www_dns_enabled ? local.api_domain : ""
|
|
502
|
+
}
|
|
503
|
+
|
|
260
504
|
output "appsync_api_url" {
|
|
261
505
|
description = "AppSync GraphQL URL"
|
|
262
506
|
value = module.thinkwork.appsync_api_url
|
|
@@ -273,6 +517,12 @@ output "appsync_api_key" {
|
|
|
273
517
|
sensitive = true
|
|
274
518
|
}
|
|
275
519
|
|
|
520
|
+
output "mapbox_public_token" {
|
|
521
|
+
description = "Mapbox public token used by apps/computer MapView. Read by scripts/build-computer.sh to inline VITE_MAPBOX_PUBLIC_TOKEN at build time; empty string lets MapView fall back to OpenStreetMap tiles."
|
|
522
|
+
value = module.thinkwork.mapbox_public_token
|
|
523
|
+
sensitive = true
|
|
524
|
+
}
|
|
525
|
+
|
|
276
526
|
output "auth_domain" {
|
|
277
527
|
description = "Cognito hosted UI domain"
|
|
278
528
|
value = module.thinkwork.auth_domain
|
|
@@ -348,6 +598,41 @@ output "admin_bucket_name" {
|
|
|
348
598
|
value = module.thinkwork.admin_bucket_name
|
|
349
599
|
}
|
|
350
600
|
|
|
601
|
+
output "computer_url" {
|
|
602
|
+
description = "Computer app URL"
|
|
603
|
+
value = local.www_dns_enabled ? "https://${local.computer_domain}" : "https://${module.thinkwork.computer_distribution_domain}"
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
output "computer_distribution_id" {
|
|
607
|
+
description = "CloudFront distribution ID for computer (for cache invalidation)"
|
|
608
|
+
value = module.thinkwork.computer_distribution_id
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
output "computer_bucket_name" {
|
|
612
|
+
description = "S3 bucket for computer app assets"
|
|
613
|
+
value = module.thinkwork.computer_bucket_name
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
output "computer_sandbox_distribution_id" {
|
|
617
|
+
description = "CloudFront distribution ID for the Computer iframe sandbox (for cache invalidation)"
|
|
618
|
+
value = module.thinkwork.computer_sandbox_distribution_id
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
output "computer_sandbox_bucket_name" {
|
|
622
|
+
description = "S3 bucket for the Computer iframe sandbox shell assets"
|
|
623
|
+
value = module.thinkwork.computer_sandbox_bucket_name
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
output "computer_sandbox_url" {
|
|
627
|
+
description = "Computer iframe sandbox URL"
|
|
628
|
+
value = module.thinkwork.computer_sandbox_url
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
output "computer_sandbox_allowed_parent_origins" {
|
|
632
|
+
description = "Trusted parent origins for the Computer iframe sandbox"
|
|
633
|
+
value = module.thinkwork.computer_sandbox_allowed_parent_origins
|
|
634
|
+
}
|
|
635
|
+
|
|
351
636
|
output "docs_url" {
|
|
352
637
|
description = "Docs site URL"
|
|
353
638
|
value = local.www_dns_enabled ? "https://${local.docs_domain}" : "https://${module.thinkwork.docs_distribution_domain}"
|
|
@@ -397,3 +682,24 @@ output "ses_inbound_mx_target" {
|
|
|
397
682
|
description = "MX target host for the email subdomain. Already written into the subzone by Terraform — informational."
|
|
398
683
|
value = module.thinkwork.ses_inbound_mx_target
|
|
399
684
|
}
|
|
685
|
+
|
|
686
|
+
# MCP custom domain — consumed by `pnpm cf:sync-mcp`.
|
|
687
|
+
output "mcp_custom_domain" {
|
|
688
|
+
description = "Configured MCP custom domain (e.g., mcp.thinkwork.ai), or empty when disabled."
|
|
689
|
+
value = module.thinkwork.mcp_custom_domain
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
output "mcp_custom_domain_cert_arn" {
|
|
693
|
+
description = "ACM cert ARN for the MCP custom domain. Pass to `pnpm cf:sync-mcp --cert-arn` in direct-args mode."
|
|
694
|
+
value = module.thinkwork.mcp_custom_domain_cert_arn
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
output "mcp_custom_domain_validation" {
|
|
698
|
+
description = "DNS validation records to add to Cloudflare for ACM to issue the cert. Each record: { name, type, value }."
|
|
699
|
+
value = module.thinkwork.mcp_custom_domain_validation
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
output "mcp_custom_domain_target" {
|
|
703
|
+
description = "Regional target for the final mcp CNAME — only populated on the second apply after mcp_custom_domain_ready=true. { target_domain_name, hosted_zone_id } or null."
|
|
704
|
+
value = module.thinkwork.mcp_custom_domain_target
|
|
705
|
+
}
|
|
@@ -30,6 +30,20 @@ db_password = "CHANGE_ME_strong_password_here"
|
|
|
30
30
|
# Pre-signup Lambda (optional — leave empty if not using custom pre-signup logic)
|
|
31
31
|
# pre_signup_lambda_zip = "./lambdas/pre-signup.zip"
|
|
32
32
|
|
|
33
|
+
# Google Places API key (optional — leave empty to run compile pipeline
|
|
34
|
+
# without live place-hierarchy enrichment; records fall back to
|
|
35
|
+
# metadata-only rows and no country/city backing pages are auto-created).
|
|
36
|
+
#
|
|
37
|
+
# When set, terraform creates a SSM SecureString parameter at
|
|
38
|
+
# /thinkwork/<stage>/google-places/api-key and the wiki-compile Lambda
|
|
39
|
+
# reads it on cold start. Alternative: leave blank here and set the key
|
|
40
|
+
# manually after apply via:
|
|
41
|
+
# aws ssm put-parameter --type SecureString --overwrite \
|
|
42
|
+
# --name /thinkwork/<stage>/google-places/api-key --value <KEY>
|
|
43
|
+
# (terraform's lifecycle.ignore_changes on the value keeps your manual
|
|
44
|
+
# value from being overwritten on future applies.)
|
|
45
|
+
# google_places_api_key = ""
|
|
46
|
+
|
|
33
47
|
# Public website (apps/www) custom domain.
|
|
34
48
|
# Leave www_domain empty to skip the custom domain and serve on the raw
|
|
35
49
|
# CloudFront URL. When set, you must also provide cloudflare_zone_id below —
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# Sandbox base image for the AgentCore Code Interpreter.
|
|
2
|
+
#
|
|
3
|
+
# Builds a Python 3.12 container with a small, pinned set of libraries
|
|
4
|
+
# template authors can rely on without paying the first-turn `pip install`
|
|
5
|
+
# tax. Installs sitecustomize.py so the stdout/stderr token redactor is
|
|
6
|
+
# active before any user code or framework traceback runs on cold start.
|
|
7
|
+
#
|
|
8
|
+
# Image tagging is CI-driven (see scripts/build_and_push_sandbox_base.sh).
|
|
9
|
+
# Tags are immutable; a bump ships as a reviewable PR.
|
|
10
|
+
#
|
|
11
|
+
# Build context is the repository root so the COPY line can reach the
|
|
12
|
+
# Python source under packages/agentcore-strands/agent-container-sandbox/.
|
|
13
|
+
#
|
|
14
|
+
# docker build -f terraform/modules/app/agentcore-code-interpreter/Dockerfile.sandbox-base .
|
|
15
|
+
|
|
16
|
+
FROM python:3.12-slim AS base
|
|
17
|
+
|
|
18
|
+
# ---------------------------------------------------------------------------
|
|
19
|
+
# System packages — minimal. The sandbox runs agent code, not a full distro.
|
|
20
|
+
# ---------------------------------------------------------------------------
|
|
21
|
+
RUN apt-get update && apt-get install -y --no-install-recommends \
|
|
22
|
+
ca-certificates \
|
|
23
|
+
curl \
|
|
24
|
+
git \
|
|
25
|
+
&& rm -rf /var/lib/apt/lists/*
|
|
26
|
+
|
|
27
|
+
# ---------------------------------------------------------------------------
|
|
28
|
+
# Blessed Python libraries — pinned to explicit versions per plan R14/R24.
|
|
29
|
+
# Bumps happen in reviewable PRs. Runtime `pip install` is still allowed
|
|
30
|
+
# (per R15; T2 residual is named), but these baseline packages install once
|
|
31
|
+
# at image bake and don't pay the per-turn cost.
|
|
32
|
+
# ---------------------------------------------------------------------------
|
|
33
|
+
RUN pip install --no-cache-dir \
|
|
34
|
+
pandas==2.2.3 \
|
|
35
|
+
numpy==2.1.3 \
|
|
36
|
+
requests==2.32.3 \
|
|
37
|
+
boto3==1.38.0 \
|
|
38
|
+
httpx==0.28.1 \
|
|
39
|
+
pyyaml==6.0.2 \
|
|
40
|
+
openpyxl==3.1.5 \
|
|
41
|
+
python-dateutil==2.9.0.post0
|
|
42
|
+
|
|
43
|
+
# ---------------------------------------------------------------------------
|
|
44
|
+
# sitecustomize.py — the R13 stdout/stderr token redactor. Python
|
|
45
|
+
# auto-imports any module named "sitecustomize" on interpreter startup, so
|
|
46
|
+
# placing it in site-packages makes the redactor load before any user code
|
|
47
|
+
# (and before the framework traceback on cold start). See the module
|
|
48
|
+
# docstring for the honestly-scoped invariant and named residual gaps.
|
|
49
|
+
# ---------------------------------------------------------------------------
|
|
50
|
+
COPY packages/agentcore-strands/agent-container-sandbox/sitecustomize.py \
|
|
51
|
+
/usr/local/lib/python3.12/site-packages/sitecustomize.py
|
|
52
|
+
|
|
53
|
+
# ---------------------------------------------------------------------------
|
|
54
|
+
# Startup assertion — fail loudly if the module isn't wired up or
|
|
55
|
+
# THINKWORK_SANDBOX_SCRUBBER=off slips through to prod. Runs in the same
|
|
56
|
+
# image smoke-test CI job that pushes the tag.
|
|
57
|
+
# ---------------------------------------------------------------------------
|
|
58
|
+
RUN python -c "import sitecustomize; assert sitecustomize.installed(), 'sitecustomize did not install its stdio wrapper on import'; print('sitecustomize: OK')"
|
|
59
|
+
|
|
60
|
+
# AgentCore Code Interpreter supplies its own entrypoint; we provide the
|
|
61
|
+
# libraries + scrubber only. No CMD/ENTRYPOINT override.
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# `agentcore-code-interpreter` — stage-level base image substrate
|
|
2
|
+
|
|
3
|
+
Stage-scoped artifacts for the AgentCore Code Interpreter sandbox. **Per-tenant
|
|
4
|
+
Code Interpreter instances are created at runtime** by the `agentcore-admin`
|
|
5
|
+
Lambda (see `docs/adrs/per-tenant-aws-resource-fanout.md`) — this module
|
|
6
|
+
stops at the substrate everything else depends on.
|
|
7
|
+
|
|
8
|
+
## What it creates
|
|
9
|
+
|
|
10
|
+
- **ECR repository** `thinkwork-{stage}-sandbox-base` — immutable tags, last 10
|
|
11
|
+
kept, scan-on-push.
|
|
12
|
+
- **Outputs** consumed by downstream modules + the provisioning Lambda:
|
|
13
|
+
- `ecr_repository_url` — where the blessed image lives.
|
|
14
|
+
- `environment_ids` + `environments` — the v1 environment catalog (`default-public`,
|
|
15
|
+
`internal-only`) with network modes.
|
|
16
|
+
- `tenant_role_trust_policy_template` / `tenant_role_inline_policy_template` —
|
|
17
|
+
JSON templates the provisioning Lambda substitutes `{tenant_id}` into at
|
|
18
|
+
`CreateRole` time (plan Unit 5).
|
|
19
|
+
|
|
20
|
+
## Files
|
|
21
|
+
|
|
22
|
+
| File | Purpose |
|
|
23
|
+
|---|---|
|
|
24
|
+
| `main.tf` | ECR + outputs. No per-tenant resources. |
|
|
25
|
+
| `Dockerfile.sandbox-base` | Python 3.12 + pinned libs + `sitecustomize.py`. Build context is the **repo root** so the COPY can reach `packages/agentcore-strands/agent-container-sandbox/sitecustomize.py`. |
|
|
26
|
+
| `scripts/build_and_push_sandbox_base.sh` | CI builds + pushes. Not called from Terraform — image lifecycle is a reviewable-PR concern. |
|
|
27
|
+
|
|
28
|
+
The Python source `sitecustomize.py` and its pytest suite `test_sitecustomize.py`
|
|
29
|
+
live under `packages/agentcore-strands/agent-container-sandbox/` (colocated with
|
|
30
|
+
the rest of the Strands agent-container Python, per handoff P2 #10). That's
|
|
31
|
+
where `uv run pytest` finds them without extra config.
|
|
32
|
+
|
|
33
|
+
## What this module does **not** do
|
|
34
|
+
|
|
35
|
+
- Create per-tenant Code Interpreter instances — that's Unit 5 (agentcore-admin Lambda).
|
|
36
|
+
- Build or push the Docker image — that's CI, via the shell script above.
|
|
37
|
+
- Grant the provisioning Lambda IAM permissions on the new resources — that's added in Unit 5 when the Lambda resource lands.
|
|
38
|
+
|
|
39
|
+
## Bumping the image
|
|
40
|
+
|
|
41
|
+
1. Edit `Dockerfile.sandbox-base` (pin a new pandas / add a lib / etc.).
|
|
42
|
+
2. Edit `packages/agentcore-strands/agent-container-sandbox/sitecustomize.py` if the R13 invariant or its coverage gaps shift.
|
|
43
|
+
3. PR. CI runs `pytest` on the scrubber plus a build-then-startup-assertion smoke test.
|
|
44
|
+
4. After merge, `build_and_push_sandbox_base.sh` tags with the merge-commit SHA.
|
|
45
|
+
|
|
46
|
+
## Named residual
|
|
47
|
+
|
|
48
|
+
The R13 invariant this module's scrubber enforces is **honestly scoped** to
|
|
49
|
+
Python-stdio-mediated writes + known-shape CloudWatch patterns. See the
|
|
50
|
+
brainstorm and the `sitecustomize.py` module docstring for the full list of
|
|
51
|
+
stdio-bypass classes (os.write, subprocess env dumps, C-extension direct
|
|
52
|
+
writes, multiprocessing workers, adversarial split-writes) that the Unit 12
|
|
53
|
+
pattern backstop catches partially and the v2 in-process credential proxy
|
|
54
|
+
addresses structurally.
|