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
@@ -30,6 +30,41 @@ export const OPPORTUNITY_PRODUCT_OBJECT = {
30
30
  targetFieldLabel: "Products",
31
31
  } as const;
32
32
 
33
+ /**
34
+ * LastMile branches ("Golden West Oil Co..San Antonio (300)", displayed as
35
+ * "GWO 300"). Opportunities belong to exactly one, so it is a related object
36
+ * rather than a free-text field: 38 of them, filterable and roll-up-able.
37
+ */
38
+ export const ORGANIZATION_OBJECT = {
39
+ nameSingular: "organization",
40
+ namePlural: "organizations",
41
+ labelSingular: "Organization",
42
+ labelPlural: "Organizations",
43
+ icon: "IconBuildingCommunity",
44
+ targetFieldLabel: "Opportunities",
45
+ } as const;
46
+
47
+ /**
48
+ * TEI's product catalog: exactly the seven lines LastMile's "Product Line"
49
+ * picker offers. Opportunity product lines point at these, so a product is one
50
+ * record you can filter and report on rather than 19 free-text spellings.
51
+ */
52
+ export const PRODUCT_OBJECT = {
53
+ nameSingular: "product",
54
+ namePlural: "products",
55
+ labelSingular: "Product",
56
+ labelPlural: "Products",
57
+ icon: "IconBox",
58
+ targetFieldLabel: "Lines",
59
+ } as const;
60
+
61
+ /** Legacy fields from the first import, superseded by the product lines.
62
+ * `amount` on the opportunity is Twenty's native deal total and stays. */
63
+ export const LEGACY_OPPORTUNITY_FIELDS: readonly string[] = [
64
+ "product",
65
+ "quantity",
66
+ ];
67
+
33
68
  export const FIELD_SPECS: Array<{
34
69
  object: string;
35
70
  name: string;
@@ -45,6 +80,9 @@ export const FIELD_SPECS: Array<{
45
80
  isUnique: true,
46
81
  },
47
82
  { object: "company", name: "sourceHash", label: "Source Hash", type: "TEXT" },
83
+ // The customer's P21 account number (dispatch customer.external_id,
84
+ // e.g. "P21:13028") — TEI's ERP key, visible on the company.
85
+ { object: "company", name: "p21Code", label: "P21 Code", type: "TEXT" },
48
86
  {
49
87
  object: "person",
50
88
  name: "sourceId",
@@ -66,13 +104,9 @@ export const FIELD_SPECS: Array<{
66
104
  label: "Source Hash",
67
105
  type: "TEXT",
68
106
  },
69
- { object: "opportunity", name: "product", label: "Product", type: "TEXT" },
70
- {
71
- object: "opportunity",
72
- name: "quantity",
73
- label: "Quantity",
74
- type: "NUMBER",
75
- },
107
+ // product + quantity live on the opportunityProduct lines, not here — an
108
+ // opportunity can carry several. isMobil stays as a roll-up: true when any
109
+ // line is a Mobil product, so the pipeline can be filtered on it.
76
110
  {
77
111
  object: "opportunity",
78
112
  name: "isMobil",
@@ -87,6 +121,9 @@ export const FIELD_SPECS: Array<{
87
121
  isUnique: true,
88
122
  },
89
123
  { object: "note", name: "sourceHash", label: "Source Hash", type: "TEXT" },
124
+ // Twenty's createdBy actor is not settable through the API, so the LastMile
125
+ // author rides on the note itself.
126
+ { object: "note", name: "author", label: "Author", type: "TEXT" },
90
127
  {
91
128
  object: "attachment",
92
129
  name: "sourceId",
@@ -108,12 +145,6 @@ export const FIELD_SPECS: Array<{
108
145
  label: "Source Hash",
109
146
  type: "TEXT",
110
147
  },
111
- {
112
- object: "opportunityProduct",
113
- name: "product",
114
- label: "Product",
115
- type: "TEXT",
116
- },
117
148
  {
118
149
  object: "opportunityProduct",
119
150
  name: "quantity",
@@ -138,6 +169,25 @@ export const FIELD_SPECS: Array<{
138
169
  label: "Line",
139
170
  type: "NUMBER",
140
171
  },
172
+ {
173
+ object: "organization",
174
+ name: "sourceId",
175
+ label: "Source ID",
176
+ type: "TEXT",
177
+ isUnique: true,
178
+ },
179
+ {
180
+ object: "organization",
181
+ name: "sourceHash",
182
+ label: "Source Hash",
183
+ type: "TEXT",
184
+ },
185
+ {
186
+ object: "organization",
187
+ name: "fullName",
188
+ label: "Full Name",
189
+ type: "TEXT",
190
+ },
141
191
  ];
142
192
 
143
193
  /** Placeholder id used only in dry-run plans, never sent to Twenty. */
@@ -202,21 +252,34 @@ const CREATE_OBJECT_MUTATION = `
202
252
  * RELATION fields via `relationCreationPayload`, and Twenty exposes the FK to
203
253
  * the data API as `opportunityId` on the line's create input.
204
254
  */
205
- export async function ensureOpportunityProductObject(
255
+ async function ensureCustomObjectWithOpportunityRelation(
206
256
  client: TwentyClient,
207
257
  objects: Map<string, ObjectMetadata>,
208
258
  dryRun: boolean,
259
+ spec: {
260
+ nameSingular: string;
261
+ namePlural: string;
262
+ labelSingular: string;
263
+ labelPlural: string;
264
+ icon: string;
265
+ targetFieldLabel: string;
266
+ },
267
+ /** Which side owns the FK. "child" = many spec-rows -> one opportunity;
268
+ * "parent" = many opportunities -> one spec-row. */
269
+ direction: "child" | "parent",
209
270
  ): Promise<{ created: boolean; relationCreated: boolean }> {
210
- const existing = objects.get(OPPORTUNITY_PRODUCT_OBJECT.nameSingular);
271
+ const existing = objects.get(spec.nameSingular);
211
272
  const opportunity = objects.get("opportunity");
212
273
  if (!opportunity) throw new Error('Twenty object "opportunity" not found.');
213
274
 
214
- if (existing?.fields.has("opportunity")) {
275
+ const relationLives =
276
+ direction === "child"
277
+ ? existing?.fields.has("opportunity")
278
+ : opportunity.fields.has(spec.nameSingular);
279
+ if (existing && relationLives) {
215
280
  return { created: false, relationCreated: false };
216
281
  }
217
- if (dryRun) {
218
- return { created: !existing, relationCreated: true };
219
- }
282
+ if (dryRun) return { created: !existing, relationCreated: true };
220
283
 
221
284
  let objectId = existing?.id;
222
285
  if (!objectId) {
@@ -225,28 +288,36 @@ export async function ensureOpportunityProductObject(
225
288
  }>("/metadata", CREATE_OBJECT_MUTATION, {
226
289
  input: {
227
290
  object: {
228
- nameSingular: OPPORTUNITY_PRODUCT_OBJECT.nameSingular,
229
- namePlural: OPPORTUNITY_PRODUCT_OBJECT.namePlural,
230
- labelSingular: OPPORTUNITY_PRODUCT_OBJECT.labelSingular,
231
- labelPlural: OPPORTUNITY_PRODUCT_OBJECT.labelPlural,
232
- icon: OPPORTUNITY_PRODUCT_OBJECT.icon,
291
+ nameSingular: spec.nameSingular,
292
+ namePlural: spec.namePlural,
293
+ labelSingular: spec.labelSingular,
294
+ labelPlural: spec.labelPlural,
295
+ icon: spec.icon,
233
296
  },
234
297
  },
235
298
  });
236
299
  objectId = created.createOneObject.id;
237
300
  }
238
301
 
302
+ // MANY_TO_ONE always reads "many <owner> -> one <target>". For the child
303
+ // direction the line owns the FK; for the parent direction the opportunity
304
+ // does, so the field is created on opportunity pointing at spec's object.
305
+ const [ownerObjectId, targetObjectId, fieldName, fieldLabel] =
306
+ direction === "child"
307
+ ? [objectId, opportunity.id, "opportunity", "Opportunity"]
308
+ : [opportunity.id, objectId, spec.nameSingular, spec.labelSingular];
309
+
239
310
  await client.requestOnce("/metadata", CREATE_FIELD_MUTATION, {
240
311
  input: {
241
312
  field: {
242
- objectMetadataId: objectId,
243
- name: "opportunity",
244
- label: "Opportunity",
313
+ objectMetadataId: ownerObjectId,
314
+ name: fieldName,
315
+ label: fieldLabel,
245
316
  type: "RELATION",
246
317
  relationCreationPayload: {
247
- targetObjectMetadataId: opportunity.id,
248
- targetFieldLabel: OPPORTUNITY_PRODUCT_OBJECT.targetFieldLabel,
249
- targetFieldIcon: OPPORTUNITY_PRODUCT_OBJECT.icon,
318
+ targetObjectMetadataId: targetObjectId,
319
+ targetFieldLabel: spec.targetFieldLabel,
320
+ targetFieldIcon: spec.icon,
250
321
  type: "MANY_TO_ONE",
251
322
  },
252
323
  },
@@ -256,6 +327,36 @@ export async function ensureOpportunityProductObject(
256
327
  return { created: !existing, relationCreated: true };
257
328
  }
258
329
 
330
+ export async function ensureOpportunityProductObject(
331
+ client: TwentyClient,
332
+ objects: Map<string, ObjectMetadata>,
333
+ dryRun: boolean,
334
+ ): Promise<{ created: boolean; relationCreated: boolean }> {
335
+ return ensureCustomObjectWithOpportunityRelation(
336
+ client,
337
+ objects,
338
+ dryRun,
339
+ OPPORTUNITY_PRODUCT_OBJECT,
340
+ "child",
341
+ );
342
+ }
343
+
344
+ /** Many opportunities belong to one organization, so the FK lives on
345
+ * opportunity as `organizationId`. */
346
+ export async function ensureOrganizationObject(
347
+ client: TwentyClient,
348
+ objects: Map<string, ObjectMetadata>,
349
+ dryRun: boolean,
350
+ ): Promise<{ created: boolean; relationCreated: boolean }> {
351
+ return ensureCustomObjectWithOpportunityRelation(
352
+ client,
353
+ objects,
354
+ dryRun,
355
+ ORGANIZATION_OBJECT,
356
+ "parent",
357
+ );
358
+ }
359
+
259
360
  export async function fetchObjectMetadata(
260
361
  client: TwentyClient,
261
362
  ): Promise<Map<string, ObjectMetadata>> {
@@ -315,7 +416,11 @@ export function planSchemaEnsure(
315
416
  // ensureOpportunityProductObject creates this object earlier in the same
316
417
  // run; in a dry-run against a workspace that lacks it, its fields are
317
418
  // planned rather than an error.
318
- if (spec.object === OPPORTUNITY_PRODUCT_OBJECT.nameSingular) {
419
+ if (
420
+ spec.object === OPPORTUNITY_PRODUCT_OBJECT.nameSingular ||
421
+ spec.object === ORGANIZATION_OBJECT.nameSingular ||
422
+ spec.object === PRODUCT_OBJECT.nameSingular
423
+ ) {
319
424
  createFields.push({
320
425
  object: spec.object,
321
426
  objectMetadataId: PENDING_OBJECT_ID,