thinkwork-cli 0.13.2 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (231) hide show
  1. package/README.md +3 -14
  2. package/dist/{api-client-JYZXYQJR.js → api-client-4P4JZOBX.js} +1 -1
  3. package/dist/{chunk-Z2F6RJFR.js → chunk-4OMDWYZQ.js} +4 -5
  4. package/dist/{chunk-ILZKG5WV.js → chunk-AD2NIWB5.js} +434 -69
  5. package/dist/chunk-MWQ7RO76.js +137 -0
  6. package/dist/{chunk-DVPBI5HM.js → chunk-OLUQ4U2Z.js} +26 -26
  7. package/dist/{chunk-QOERT44A.js → chunk-YQCYD6NE.js} +57 -44
  8. package/dist/cli.js +4400 -16431
  9. package/dist/commands/enterprise/templates/deploy-repo/scripts/apply-release.mjs +0 -1
  10. package/dist/commands/enterprise/templates/deploy-repo/scripts/smoke.mjs +0 -1
  11. package/dist/commands/enterprise/templates/deploy-repo/terraform/main.tf +0 -14
  12. package/dist/{dist-es-CRV46D5B.js → dist-es-2OTNTL4N.js} +4 -4
  13. package/dist/{dist-es-ENBUC3JP.js → dist-es-2XVAH4BQ.js} +2 -2
  14. package/dist/{dist-es-DCY4IC3Y.js → dist-es-3DPDUJAH.js} +2 -2
  15. package/dist/{dist-es-G7PTG75M.js → dist-es-BNZTBBVC.js} +1 -1
  16. package/dist/{dist-es-GKDBE7E6.js → dist-es-CD2RBDAC.js} +1 -1
  17. package/dist/{dist-es-R7FGL2IB.js → dist-es-I4FW57PS.js} +1 -1
  18. package/dist/{dist-es-USORBXZ5.js → dist-es-KVFWJNDY.js} +4 -4
  19. package/dist/{dist-es-HY3535OI.js → dist-es-PS73DWNL.js} +2 -2
  20. package/dist/{dist-es-LDE437AM.js → dist-es-S3NZQFGT.js} +1 -1
  21. package/dist/{dist-es-G54LBMRN.js → dist-es-SGCA4P32.js} +1 -1
  22. package/dist/{dist-es-Q57SZXZ7.js → dist-es-WBM6J4RA.js} +2 -2
  23. package/dist/{dist-es-P26FWFLT.js → dist-es-ZGVADYIF.js} +8 -8
  24. package/dist/drizzle/0227_analyst_reader_role.sql +17 -26
  25. package/dist/drizzle/0230_analyst_rls.sql +28 -45
  26. package/dist/drizzle/0261_native_auth_control_plane.sql +7 -3
  27. package/dist/drizzle/0270_tool_execution_ledger.sql +115 -0
  28. package/dist/drizzle/0271_ontology_twin_declarations.sql +47 -0
  29. package/dist/drizzle/0272_identity_graph_projection_cursor.sql +23 -0
  30. package/dist/drizzle/0273_twin_materialization_suggestions.sql +29 -0
  31. package/dist/drizzle/0274_tenant_mcp_twin_keys.sql +59 -0
  32. package/dist/drizzle/0275_identity_bulk_rebuild_fence.sql +30 -0
  33. package/dist/drizzle/0276_tool_execution_ledger_allow_cascade_delete.sql +18 -0
  34. package/dist/drizzle/0277_knowledge_base_sources.sql +107 -0
  35. package/dist/drizzle/0278_kb_page_transcription.sql +55 -0
  36. package/dist/drizzle/0279_mcp_servers_off_plugin_provenance.sql +93 -0
  37. package/dist/drizzle/0280_work_item_external_refs_drop_plane_provider.sql +46 -0
  38. package/dist/drizzle/0281_tenant_mcp_twin_keys_expiry_suffix.sql +9 -0
  39. package/dist/drizzle/0282_tenant_mcp_twin_keys_grants.sql +24 -0
  40. package/dist/drizzle/0283_agents_agentcore_runtime_dispatch.sql +13 -0
  41. package/dist/drizzle/0284_user_brain_claims.sql +69 -0
  42. package/dist/drizzle/0285_tenant_mcp_twin_keys_trusted_subsystem.sql +30 -0
  43. package/dist/drizzle/0286_brain_analytics_key.sql +26 -0
  44. package/dist/drizzle/0287_brain_subject.sql +25 -0
  45. package/dist/drizzle/0288_multi_lane_identities.sql +25 -0
  46. package/dist/{loadSso-DJVB7BXE.js → loadSso-ETCXTR4I.js} +2 -2
  47. package/dist/{loadSso-JAMNA3WY.js → loadSso-UBD7KUHE.js} +2 -2
  48. package/dist/plugins/README.md +2 -2
  49. package/dist/plugins/catalog/package.json +0 -3
  50. package/dist/plugins/catalog/src/__tests__/build-catalog.test.ts +6 -3
  51. package/dist/plugins/catalog/src/__tests__/catalog.test.ts +21 -21
  52. package/dist/plugins/catalog/src/__tests__/contracts.test.ts +102 -25
  53. package/dist/plugins/catalog/src/__tests__/plugin-package.test.ts +7 -6
  54. package/dist/plugins/catalog/src/__tests__/plugin-registry.test.ts +0 -15
  55. package/dist/plugins/catalog/src/contracts.ts +15 -0
  56. package/dist/plugins/catalog/src/registry/generated-first-party.ts +0 -18
  57. package/dist/plugins/n8n/test/integrated-app-contract.test.ts +1 -1
  58. package/dist/plugins/twenty/package.json +2 -1
  59. package/dist/plugins/twenty/scripts/LAMBDA_DEPLOY.md +114 -0
  60. package/dist/plugins/twenty/scripts/build-lambda.sh +29 -0
  61. package/dist/plugins/twenty/scripts/configure-product-views.ts +195 -0
  62. package/dist/plugins/twenty/scripts/lambda-handler.ts +49 -0
  63. package/dist/plugins/twenty/scripts/lib/__tests__/load-attachments.test.ts +38 -0
  64. package/dist/plugins/twenty/scripts/lib/__tests__/load-records.test.ts +145 -2
  65. package/dist/plugins/twenty/scripts/lib/__tests__/mappers.test.ts +397 -151
  66. package/dist/plugins/twenty/scripts/lib/__tests__/members-ensure.test.ts +28 -129
  67. package/dist/plugins/twenty/scripts/lib/__tests__/provision-members-db.test.ts +10 -0
  68. package/dist/plugins/twenty/scripts/lib/__tests__/schema-ensure.test.ts +13 -8
  69. package/dist/plugins/twenty/scripts/lib/lastmile-reader.ts +222 -2
  70. package/dist/plugins/twenty/scripts/lib/load-attachments.ts +29 -0
  71. package/dist/plugins/twenty/scripts/lib/load-records.ts +160 -43
  72. package/dist/plugins/twenty/scripts/lib/mappers.ts +368 -152
  73. package/dist/plugins/twenty/scripts/lib/members-ensure.ts +43 -268
  74. package/dist/plugins/twenty/scripts/lib/provision-members-db.ts +57 -0
  75. package/dist/plugins/twenty/scripts/lib/schema-ensure.ts +136 -31
  76. package/dist/plugins/twenty/scripts/migrate-lastmile.ts +254 -152
  77. package/dist/plugins/twenty/scripts/provision-twenty-members.ts +44 -1
  78. package/dist/plugins/twenty/scripts/purge-lastmile-import.ts +178 -0
  79. package/dist/plugins/twenty/scripts/restructure-products.ts +312 -0
  80. package/dist/plugins/twenty/scripts/retag-stage-values.ts +362 -0
  81. package/dist/plugins/twenty/test/manifest.test.ts +2 -4
  82. package/dist/scripts/post-deploy.sh +79 -1
  83. package/dist/{signin-UGDWTGS4.js → signin-5QZNRSOO.js} +2 -2
  84. package/dist/{sso-oidc-Q4VXANXY.js → sso-oidc-XK673P54.js} +2 -2
  85. package/dist/{sts-CWLSE3GH.js → sts-OXI2UALF.js} +6 -134
  86. package/dist/{sts-DNZLNVOV.js → sts-SQW4NBK3.js} +6 -134
  87. package/dist/terraform/examples/greenfield/main.tf +233 -175
  88. package/dist/terraform/examples/greenfield/terraform.tfvars.example +12 -8
  89. package/dist/terraform/modules/app/agentcore-admin/main.tf +47 -0
  90. package/dist/terraform/modules/app/agentcore-code-interpreter/README.md +39 -47
  91. package/dist/terraform/modules/app/agentcore-code-interpreter/main.tf +13 -66
  92. package/dist/terraform/modules/app/agentcore-identity/main.tf +106 -26
  93. package/dist/terraform/modules/app/agentcore-identity/outputs.tf +6 -6
  94. package/dist/terraform/modules/app/agentcore-identity/scripts/bootstrap_twenty_oauth_client.sh +216 -99
  95. package/dist/terraform/modules/app/agentcore-identity/scripts/delete_identity.sh +6 -9
  96. package/dist/terraform/modules/app/agentcore-identity/scripts/delete_workload_identity.sh +19 -0
  97. package/dist/terraform/modules/app/agentcore-identity/scripts/ensure_workload_identity.sh +29 -0
  98. package/dist/terraform/modules/app/agentcore-identity/scripts/read_identity.sh +22 -7
  99. package/dist/terraform/modules/app/agentcore-identity/scripts/reconcile_twenty_identity.sh +17 -46
  100. package/dist/terraform/modules/app/agentcore-identity/scripts/reconcile_twenty_provider.mjs +65 -3
  101. package/dist/terraform/modules/app/agentcore-identity/tests/tainted-marker/after.tf +15 -0
  102. package/dist/terraform/modules/app/agentcore-identity/tests/tainted-marker/before.tf +7 -0
  103. package/dist/terraform/modules/app/agentcore-identity/tests/tainted-marker/identity-after.tf +8 -0
  104. package/dist/terraform/modules/app/agentcore-identity/tests/tainted-marker/identity-before.tf +9 -0
  105. package/dist/terraform/modules/app/agentcore-identity/tests/tainted-marker/thinkwork-after.tf +3 -0
  106. package/dist/terraform/modules/app/agentcore-identity/tests/tainted-marker/thinkwork-before.tf +3 -0
  107. package/dist/terraform/modules/app/agentcore-identity/variables.tf +6 -0
  108. package/dist/terraform/modules/app/agentcore-memory/README.md +46 -9
  109. package/dist/terraform/modules/app/agentcore-memory/main.tf +27 -10
  110. package/dist/terraform/modules/app/agentcore-memory/scripts/create_or_find_memory.sh +156 -24
  111. package/dist/terraform/modules/app/agentcore-pi/README.md +1 -3
  112. package/dist/terraform/modules/app/agentcore-pi/main.tf +42 -52
  113. package/dist/terraform/modules/app/agentcore-pi/outputs.tf +3 -3
  114. package/dist/terraform/modules/app/agentcore-pi/scripts/reconcile_pi_runtime.mjs +175 -0
  115. package/dist/terraform/modules/app/agentcore-pi/variables.tf +35 -28
  116. package/dist/terraform/modules/app/agentcore-platform/main.tf +68 -0
  117. package/dist/terraform/modules/app/agentcore-runtime/main.tf +4 -20
  118. package/dist/terraform/modules/app/capability-broker/main.tf +2 -2
  119. package/dist/terraform/modules/app/capability-broker/variables.tf +11 -0
  120. package/dist/terraform/modules/app/deployment-control-plane/README.md +264 -0
  121. package/dist/terraform/modules/app/deployment-control-plane/buildspec.yml +4 -1
  122. package/dist/terraform/modules/app/deployment-control-plane/main.tf +94 -0
  123. package/dist/terraform/modules/app/deployment-control-plane/outputs.tf +5 -0
  124. package/dist/terraform/modules/app/deployment-control-plane/runner.py +2641 -124
  125. package/dist/terraform/modules/app/deployment-control-plane/test_runner_bundle.py +2034 -154
  126. package/dist/terraform/modules/app/deployment-control-plane/variables.tf +33 -0
  127. package/dist/terraform/modules/app/lambda-api/auth-state.tf +72 -0
  128. package/dist/terraform/modules/app/lambda-api/chat-latency-observability.tf +466 -0
  129. package/dist/terraform/modules/app/lambda-api/dispatch-dlq.tf +55 -0
  130. package/dist/terraform/modules/app/lambda-api/handlers.tf +411 -723
  131. package/dist/terraform/modules/app/lambda-api/iam-grouped.tf +262 -198
  132. package/dist/terraform/modules/app/lambda-api/main.tf +51 -13
  133. package/dist/terraform/modules/app/lambda-api/mcp-oauth.tf +4 -181
  134. package/dist/terraform/modules/app/lambda-api/memory-alarms.tf +3 -28
  135. package/dist/terraform/modules/app/lambda-api/oauth-secrets.tf +1 -2
  136. package/dist/terraform/modules/app/lambda-api/outputs.tf +17 -45
  137. package/dist/terraform/modules/app/lambda-api/turn-assertion.tf +45 -0
  138. package/dist/terraform/modules/app/lambda-api/variables.tf +219 -126
  139. package/dist/{plugins/n8n/terraform → terraform/modules/app}/n8n/README.md +1 -1
  140. package/dist/{plugins/n8n/terraform → terraform/modules/app}/n8n/main.tf +18 -18
  141. package/dist/terraform/modules/app/sandbox-log-scrubber/README.md +11 -10
  142. package/dist/terraform/modules/app/sandbox-log-scrubber/main.tf +5 -5
  143. package/dist/terraform/modules/app/static-site/main.tf +1 -1
  144. package/dist/terraform/modules/app/www-dns/main.tf +9 -27
  145. package/dist/terraform/modules/app/www-dns/variables.tf +4 -10
  146. package/dist/terraform/modules/data/aurora-postgres/README.md +34 -0
  147. package/dist/terraform/modules/data/aurora-postgres/alarms.tf +82 -0
  148. package/dist/terraform/modules/data/aurora-postgres/main.tf +27 -57
  149. package/dist/terraform/modules/data/aurora-postgres/outputs.tf +0 -9
  150. package/dist/terraform/modules/data/aurora-postgres/variables.tf +41 -0
  151. package/dist/terraform/modules/data/s3-buckets/main.tf +5 -6
  152. package/dist/terraform/modules/foundation/cognito/main.tf +81 -8
  153. package/dist/terraform/modules/foundation/cognito/outputs.tf +5 -0
  154. package/dist/terraform/modules/foundation/cognito/variables.tf +24 -12
  155. package/dist/terraform/modules/thinkwork/README.md +5 -1
  156. package/dist/terraform/modules/thinkwork/main.tf +376 -374
  157. package/dist/terraform/modules/thinkwork/outputs.tf +12 -158
  158. package/dist/terraform/modules/thinkwork/variables.tf +246 -89
  159. package/dist/workspace-defaults/files/INSTRUCTIONS.md +9 -11
  160. package/dist/workspace-defaults/files/MEMORY_GUIDE.md +38 -35
  161. package/dist/workspace-defaults/files/ROUTER.md +1 -1
  162. package/dist/workspace-defaults/files/TOOLS.md +9 -39
  163. package/dist/workspace-defaults/files/agents/analyst/INSTRUCTIONS.md +6 -3
  164. package/dist/workspace-defaults/files/catalog-skills/brain-brief-builder/SKILL.md +165 -0
  165. package/dist/workspace-defaults/files/catalog-skills/n8n-workflow-operator/SKILL.md +75 -0
  166. package/dist/workspace-defaults/files/catalog-skills/n8n-workflow-operator/references/mcp-tooling.md +40 -0
  167. package/dist/workspace-defaults/files/catalog-skills/n8n-workflow-operator/references/validation-and-handoff.md +40 -0
  168. package/dist/workspace-defaults/files/catalog-skills/n8n-workflow-operator/references/workflow-authoring.md +46 -0
  169. package/dist/workspace-defaults/files/skills/artifact-builder/references/crm-dashboard.md +1 -1
  170. package/dist/workspace-defaults/files/skills/document-composer/SKILL.md +22 -3
  171. package/package.json +1 -1
  172. package/dist/drizzle/0261_managed_harness_multiplayer.sql +0 -313
  173. package/dist/drizzle/0262_parallel_harness_threads.sql +0 -9
  174. package/dist/drizzle/0264_harness_tool_execution_ledger.sql +0 -113
  175. package/dist/plugins/company-data/README.md +0 -49
  176. package/dist/plugins/company-data/package.json +0 -21
  177. package/dist/plugins/company-data/src/index.ts +0 -30
  178. package/dist/plugins/company-data/src/manifest.ts +0 -26
  179. package/dist/plugins/company-data/test/manifest.test.ts +0 -116
  180. package/dist/plugins/company-data/tsconfig.json +0 -8
  181. package/dist/plugins/company-etl/README.md +0 -46
  182. package/dist/plugins/company-etl/package.json +0 -21
  183. package/dist/plugins/company-etl/src/index.ts +0 -29
  184. package/dist/plugins/company-etl/src/manifest.ts +0 -26
  185. package/dist/plugins/company-etl/test/manifest.test.ts +0 -108
  186. package/dist/plugins/company-etl/tsconfig.json +0 -8
  187. package/dist/plugins/lastmile/README.md +0 -50
  188. package/dist/plugins/lastmile/package.json +0 -23
  189. package/dist/plugins/lastmile/smoke/lastmile-plugin-smoke.mjs +0 -661
  190. package/dist/plugins/lastmile/src/api/tasks-adapter.ts +0 -572
  191. package/dist/plugins/lastmile/src/discovery.fixture.ts +0 -64
  192. package/dist/plugins/lastmile/src/index.ts +0 -46
  193. package/dist/plugins/lastmile/src/manifest.ts +0 -139
  194. package/dist/plugins/lastmile/test/api/tasks-adapter.test.ts +0 -266
  195. package/dist/plugins/lastmile/test/discovery.test.ts +0 -88
  196. package/dist/plugins/lastmile/tsconfig.json +0 -8
  197. package/dist/plugins/n8n/smoke/n8n-managed-app-smoke.mjs +0 -409
  198. package/dist/plugins/twenty/smoke/twenty-managed-app-smoke.mjs +0 -359
  199. package/dist/terraform/modules/app/agentcore-code-interpreter/Dockerfile.sandbox-base +0 -61
  200. package/dist/terraform/modules/app/agentcore-code-interpreter/sandbox/sitecustomize.py +0 -227
  201. package/dist/terraform/modules/app/agentcore-code-interpreter/sandbox/test_sitecustomize.py +0 -277
  202. package/dist/terraform/modules/app/agentcore-code-interpreter/scripts/build_and_push_sandbox_base.sh +0 -70
  203. package/dist/terraform/modules/app/agentcore-gateway/main.tf +0 -305
  204. package/dist/terraform/modules/app/agentcore-gateway/outputs.tf +0 -44
  205. package/dist/terraform/modules/app/agentcore-gateway/scripts/delete_gateway.sh +0 -93
  206. package/dist/terraform/modules/app/agentcore-gateway/scripts/read_gateway.sh +0 -33
  207. package/dist/terraform/modules/app/agentcore-gateway/scripts/reconcile_gateway.sh +0 -543
  208. package/dist/terraform/modules/app/agentcore-gateway/variables.tf +0 -70
  209. package/dist/terraform/modules/app/agentcore-harness/main.tf +0 -330
  210. package/dist/terraform/modules/app/agentcore-harness/outputs.tf +0 -72
  211. package/dist/terraform/modules/app/agentcore-harness/scripts/delete_harness.sh +0 -5
  212. package/dist/terraform/modules/app/agentcore-harness/scripts/harness-lifecycle.mjs +0 -430
  213. package/dist/terraform/modules/app/agentcore-harness/scripts/harness-readback.mjs +0 -180
  214. package/dist/terraform/modules/app/agentcore-harness/scripts/harness-tool-contract.mjs +0 -193
  215. package/dist/terraform/modules/app/agentcore-harness/scripts/prune_harness.sh +0 -5
  216. package/dist/terraform/modules/app/agentcore-harness/scripts/read_harness.sh +0 -5
  217. package/dist/terraform/modules/app/agentcore-harness/scripts/reconcile_harness.sh +0 -5
  218. package/dist/terraform/modules/app/agentcore-harness/variables.tf +0 -96
  219. package/dist/terraform/modules/app/hindsight-memory/README.md +0 -89
  220. package/dist/terraform/modules/app/hindsight-memory/main.tf +0 -411
  221. package/dist/terraform/modules/app/lambda-api/agentcore-harness-alarms.tf +0 -88
  222. package/dist/terraform/modules/app/lambda-api/agentcore-identity-alarms.tf +0 -91
  223. package/dist/terraform/modules/data/bedrock-knowledge-base/main.tf +0 -102
  224. /package/dist/{plugins/n8n/terraform → terraform/modules/app}/n8n/outputs.tf +0 -0
  225. /package/dist/{plugins/n8n/terraform → terraform/modules/app}/n8n/scripts/sync-database.py +0 -0
  226. /package/dist/{plugins/n8n/terraform → terraform/modules/app}/n8n/scripts/test_sync_database.py +0 -0
  227. /package/dist/{plugins/n8n/terraform → terraform/modules/app}/n8n/variables.tf +0 -0
  228. /package/dist/{plugins/twenty/terraform → terraform/modules/app}/twenty/README.md +0 -0
  229. /package/dist/{plugins/twenty/terraform → terraform/modules/app}/twenty/main.tf +0 -0
  230. /package/dist/{plugins/twenty/terraform → terraform/modules/app}/twenty/outputs.tf +0 -0
  231. /package/dist/{plugins/twenty/terraform → terraform/modules/app}/twenty/variables.tf +0 -0
@@ -1,409 +0,0 @@
1
- #!/usr/bin/env node
2
- /**
3
- * Smoke test the n8n managed application deployment.
4
- *
5
- * Dry-run is the default. Set SMOKE_ENABLE_N8N_MANAGED_APP=1 after installing
6
- * and deploying the n8n plugin through ThinkWork to read deployment status,
7
- * inspect generated evidence, and probe the public n8n health path.
8
- *
9
- * Optional live env:
10
- * SMOKE_TERRAFORM_DIR=terraform/examples/greenfield
11
- * SMOKE_N8N_URL=https://n8n.example.com
12
- * SMOKE_N8N_HEALTH_PATH=/healthz
13
- * SMOKE_TENANT_ID=<tenant-id>
14
- * VITE_GRAPHQL_HTTP_URL, GRAPHQL_HTTP_URL, or API_GRAPHQL_URL
15
- * API_AUTH_SECRET, THINKWORK_API_SECRET, VITE_GRAPHQL_API_KEY, or GRAPHQL_API_KEY
16
- */
17
-
18
- import { execFileSync } from "node:child_process";
19
- import fs from "node:fs";
20
- import path from "node:path";
21
- import process from "node:process";
22
- import { attachSmokeEvidence } from "../../../scripts/smoke/deployment-evidence.mjs";
23
-
24
- const LIVE_ENABLED = process.env.SMOKE_ENABLE_N8N_MANAGED_APP === "1";
25
- const TIMEOUT_MS = Number(process.env.SMOKE_TIMEOUT_MS || 15_000);
26
-
27
- const env = {
28
- ...loadEnvFile(),
29
- ...process.env,
30
- };
31
-
32
- const terraformDir = env.SMOKE_TERRAFORM_DIR || "terraform/examples/greenfield";
33
- const healthPath = env.SMOKE_N8N_HEALTH_PATH || "/healthz";
34
- const apiUrl = first(
35
- env.VITE_GRAPHQL_HTTP_URL,
36
- env.GRAPHQL_HTTP_URL,
37
- env.API_GRAPHQL_URL,
38
- );
39
- const apiSecret = first(env.API_AUTH_SECRET, env.THINKWORK_API_SECRET);
40
- const apiKey = first(
41
- env.VITE_GRAPHQL_API_KEY,
42
- env.APPSYNC_API_KEY,
43
- env.GRAPHQL_API_KEY,
44
- );
45
- const tenantId = first(env.SMOKE_TENANT_ID, env.TENANT_ID);
46
-
47
- if (!LIVE_ENABLED) {
48
- console.log(
49
- JSON.stringify(
50
- await attachSmokeEvidence(
51
- "n8n-managed-app",
52
- {
53
- ok: true,
54
- skippedLive: true,
55
- reason:
56
- "set SMOKE_ENABLE_N8N_MANAGED_APP=1 to run the deployed n8n managed-app smoke",
57
- dryRun: {
58
- terraformDir,
59
- healthPath,
60
- requiredWhenRunning: [
61
- "n8n plugin installed and deployed through ThinkWork Settings -> Plugins",
62
- "Terraform outputs in SMOKE_TERRAFORM_DIR or SMOKE_N8N_URL",
63
- "n8n provisioned/runtime deployment status",
64
- ],
65
- optionalGraphqlEnv: [
66
- "SMOKE_TENANT_ID",
67
- "VITE_GRAPHQL_HTTP_URL or GRAPHQL_HTTP_URL or API_GRAPHQL_URL",
68
- "API_AUTH_SECRET or THINKWORK_API_SECRET or VITE_GRAPHQL_API_KEY or GRAPHQL_API_KEY",
69
- ],
70
- verifies: [
71
- "n8n_provisioned false skips cleanly",
72
- "n8n_runtime_enabled true requires HTTPS n8n_url",
73
- "public n8n health path responds successfully",
74
- "main and worker service evidence is present",
75
- "database, queue, storage, image digest, and service credential evidence is present",
76
- "package configuration digest is recorded when custom packages are configured",
77
- ],
78
- },
79
- },
80
- env,
81
- ),
82
- null,
83
- 2,
84
- ),
85
- );
86
- process.exit(0);
87
- }
88
-
89
- try {
90
- const result = await runLiveSmoke();
91
- console.log(
92
- JSON.stringify(
93
- await attachSmokeEvidence(
94
- "n8n-managed-app",
95
- { ok: true, ...result },
96
- env,
97
- ),
98
- null,
99
- 2,
100
- ),
101
- );
102
- } catch (error) {
103
- fail(error instanceof Error ? error.message : String(error));
104
- }
105
-
106
- async function runLiveSmoke() {
107
- const terraform = readTerraformOutputs(terraformDir);
108
- const graphql = await readGraphqlStatus().catch((error) => ({
109
- error: error instanceof Error ? error.message : String(error),
110
- }));
111
- const status = resolveStatus({ terraform, graphql });
112
-
113
- if (!status.provisioned) {
114
- return {
115
- skippedLive: true,
116
- reason: "n8n is not provisioned for this stage.",
117
- terraform,
118
- graphql,
119
- };
120
- }
121
-
122
- if (!status.runtimeEnabled) {
123
- return {
124
- skippedLive: true,
125
- reason: "n8n is provisioned but runtime is parked.",
126
- status,
127
- terraform,
128
- graphql,
129
- };
130
- }
131
-
132
- if (!status.url) {
133
- throw new Error("n8n runtime is enabled but no public URL was resolved.");
134
- }
135
- if (!status.url.startsWith("https://")) {
136
- throw new Error(`n8n URL must be HTTPS, got ${status.url}.`);
137
- }
138
-
139
- validateRuntimeEvidence(status.evidence, status.source);
140
- const publicHealth = await probeHealth(status.url);
141
-
142
- if (!publicHealth.healthy) {
143
- throw new Error(
144
- `n8n health probe failed: HTTP ${publicHealth.statusCode ?? "none"} ${publicHealth.message}`,
145
- );
146
- }
147
-
148
- return {
149
- status,
150
- publicHealth,
151
- terraform,
152
- graphql,
153
- };
154
- }
155
-
156
- function resolveStatus({ terraform, graphql }) {
157
- const app = graphql?.deploymentStatus?.managedApplications?.find(
158
- (entry) => entry.key === "n8n",
159
- );
160
- const source = app ? "graphql" : terraform.available ? "terraform" : "env";
161
- const evidence = {
162
- mainServiceName: first(
163
- terraform.n8n_main_service_name,
164
- app?.serviceNames?.[0],
165
- ),
166
- workerServiceName: first(
167
- terraform.n8n_worker_service_name,
168
- app?.serviceNames?.[1],
169
- ),
170
- mainLogGroupName: terraform.n8n_main_log_group_name,
171
- workerLogGroupName: terraform.n8n_worker_log_group_name,
172
- databaseName: first(terraform.n8n_database_name, app?.databaseName),
173
- databaseSecretArn: terraform.n8n_database_secret_arn,
174
- valkeyEndpoint: terraform.n8n_valkey_endpoint,
175
- storageBucketName: first(
176
- terraform.n8n_storage_bucket_name,
177
- app?.storageBucketName,
178
- ),
179
- storagePrefix: terraform.n8n_storage_prefix,
180
- imageDigest: terraform.n8n_image_digest,
181
- packageConfigDigest: terraform.n8n_package_config_digest,
182
- serviceCredentialSecretArn: terraform.n8n_service_credential_secret_arn,
183
- };
184
-
185
- return {
186
- provisioned: bool(
187
- firstDefined(
188
- app?.provisioned,
189
- terraform.n8n_provisioned,
190
- !!env.SMOKE_N8N_URL,
191
- ),
192
- ),
193
- runtimeEnabled: bool(
194
- firstDefined(
195
- app?.runtimeEnabled,
196
- terraform.n8n_runtime_enabled,
197
- !!env.SMOKE_N8N_URL,
198
- ),
199
- ),
200
- url: first(env.SMOKE_N8N_URL, app?.url, terraform.n8n_url),
201
- source,
202
- status: app?.status ?? null,
203
- message: app?.message ?? null,
204
- evidence,
205
- };
206
- }
207
-
208
- function validateRuntimeEvidence(evidence, source) {
209
- if (env.SMOKE_N8N_ALLOW_URL_ONLY === "1") {
210
- return;
211
- }
212
- const required = [
213
- "mainServiceName",
214
- "workerServiceName",
215
- "databaseName",
216
- "valkeyEndpoint",
217
- "storageBucketName",
218
- "imageDigest",
219
- "serviceCredentialSecretArn",
220
- ];
221
- const missing = required.filter((key) => !evidence[key]);
222
- if (missing.length > 0) {
223
- throw new Error(
224
- `n8n live smoke requires managed-app evidence from Terraform or GraphQL; missing ${missing.join(", ")} from ${source}. Set SMOKE_N8N_ALLOW_URL_ONLY=1 only for endpoint-only diagnostics.`,
225
- );
226
- }
227
- }
228
-
229
- async function probeHealth(baseUrl) {
230
- const endpoint = new URL(healthPath, baseUrl).toString();
231
- const started = Date.now();
232
- const response = await fetchWithTimeout(endpoint, {
233
- headers: { accept: "text/html,application/json,*/*" },
234
- });
235
- const body = await response.text().catch(() => "");
236
- return {
237
- endpoint,
238
- healthy: response.ok,
239
- statusCode: response.status,
240
- latencyMs: Date.now() - started,
241
- message: body.slice(0, 300),
242
- };
243
- }
244
-
245
- function readTerraformOutputs(dir) {
246
- const resolved = path.resolve(dir);
247
- if (!fs.existsSync(resolved)) {
248
- return { available: false, reason: `Terraform dir not found: ${dir}` };
249
- }
250
-
251
- try {
252
- const raw = execFileSync(
253
- "terraform",
254
- ["-chdir=" + resolved, "output", "-json"],
255
- {
256
- encoding: "utf8",
257
- stdio: ["ignore", "pipe", "pipe"],
258
- },
259
- );
260
- const parsed = JSON.parse(raw);
261
- return {
262
- available: true,
263
- n8n_provisioned: outputValue(parsed.n8n_provisioned),
264
- n8n_runtime_enabled: outputValue(parsed.n8n_runtime_enabled),
265
- n8n_url: outputValue(parsed.n8n_url),
266
- n8n_alb_arn: outputValue(parsed.n8n_alb_arn),
267
- n8n_target_group_arn: outputValue(parsed.n8n_target_group_arn),
268
- n8n_cluster_arn: outputValue(parsed.n8n_cluster_arn),
269
- n8n_main_service_name: outputValue(parsed.n8n_main_service_name),
270
- n8n_worker_service_name: outputValue(parsed.n8n_worker_service_name),
271
- n8n_main_log_group_name: outputValue(parsed.n8n_main_log_group_name),
272
- n8n_worker_log_group_name: outputValue(parsed.n8n_worker_log_group_name),
273
- n8n_database_name: outputValue(parsed.n8n_database_name),
274
- n8n_database_secret_arn: outputValue(parsed.n8n_database_secret_arn),
275
- n8n_valkey_endpoint: outputValue(parsed.n8n_valkey_endpoint),
276
- n8n_storage_bucket_name: outputValue(parsed.n8n_storage_bucket_name),
277
- n8n_storage_prefix: outputValue(parsed.n8n_storage_prefix),
278
- n8n_image_digest: outputValue(parsed.n8n_image_digest),
279
- n8n_package_config_digest: outputValue(parsed.n8n_package_config_digest),
280
- n8n_service_credential_secret_arn: outputValue(
281
- parsed.n8n_service_credential_secret_arn,
282
- ),
283
- };
284
- } catch (error) {
285
- return {
286
- available: false,
287
- reason: error instanceof Error ? error.message : String(error),
288
- };
289
- }
290
- }
291
-
292
- async function readGraphqlStatus() {
293
- if (!apiUrl || !tenantId || (!apiSecret && !apiKey)) {
294
- return {
295
- skipped: true,
296
- reason:
297
- "set API URL, tenant id, and API_AUTH_SECRET/THINKWORK_API_SECRET or GraphQL API key to compare GraphQL deployment status",
298
- };
299
- }
300
- const data = await gql(
301
- `query N8nManagedAppSmoke {
302
- deploymentStatus {
303
- managedApplications {
304
- key
305
- provisioned
306
- runtimeEnabled
307
- url
308
- status
309
- message
310
- serviceNames
311
- storageBucketName
312
- databaseName
313
- }
314
- }
315
- }`,
316
- {},
317
- );
318
- return { deploymentStatus: data.deploymentStatus };
319
- }
320
-
321
- async function gql(query, variables) {
322
- const headers = {
323
- "content-type": "application/json",
324
- "x-tenant-id": tenantId,
325
- };
326
- if (apiSecret) headers.authorization = `Bearer ${apiSecret}`;
327
- if (apiKey) headers["x-api-key"] = apiKey;
328
-
329
- const response = await fetchWithTimeout(apiUrl, {
330
- method: "POST",
331
- headers,
332
- body: JSON.stringify({ query, variables }),
333
- });
334
- const text = await response.text();
335
- if (!response.ok) {
336
- throw new Error(`GraphQL HTTP ${response.status}: ${text.slice(0, 500)}`);
337
- }
338
- const body = JSON.parse(text);
339
- if (body.errors?.length) {
340
- throw new Error(`GraphQL errors: ${JSON.stringify(body.errors)}`);
341
- }
342
- return body.data;
343
- }
344
-
345
- async function fetchWithTimeout(url, options = {}) {
346
- const controller = new AbortController();
347
- const timer = setTimeout(() => controller.abort(), TIMEOUT_MS);
348
- try {
349
- return await fetch(url, { ...options, signal: controller.signal });
350
- } finally {
351
- clearTimeout(timer);
352
- }
353
- }
354
-
355
- function fail(message) {
356
- console.error(JSON.stringify({ ok: false, error: message }, null, 2));
357
- process.exit(1);
358
- }
359
-
360
- function outputValue(output) {
361
- return output && typeof output === "object" && "value" in output
362
- ? output.value
363
- : undefined;
364
- }
365
-
366
- function bool(value) {
367
- if (typeof value === "boolean") return value;
368
- if (typeof value === "string") return value.toLowerCase() === "true";
369
- return Boolean(value);
370
- }
371
-
372
- function firstDefined(...values) {
373
- return values.find((value) => value !== undefined && value !== null);
374
- }
375
-
376
- function first(...values) {
377
- return (
378
- values.find((value) => typeof value === "string" && value.trim())?.trim() ??
379
- null
380
- );
381
- }
382
-
383
- function loadEnvFile() {
384
- const explicit = process.env.COMPUTER_ENV_FILE;
385
- if (explicit === "none") return {};
386
- const candidates = [explicit, "apps/web/.env", ".env"].filter(Boolean);
387
-
388
- for (const candidate of candidates) {
389
- const resolved = path.resolve(candidate);
390
- if (!fs.existsSync(resolved)) continue;
391
- return Object.fromEntries(
392
- fs
393
- .readFileSync(resolved, "utf8")
394
- .split(/\r?\n/)
395
- .map((line) => line.trim())
396
- .filter((line) => line && !line.startsWith("#") && line.includes("="))
397
- .map((line) => {
398
- const index = line.indexOf("=");
399
- const key = line.slice(0, index).trim();
400
- const value = line
401
- .slice(index + 1)
402
- .trim()
403
- .replace(/^['"]|['"]$/g, "");
404
- return [key, value];
405
- }),
406
- );
407
- }
408
- return {};
409
- }