thinkwork-cli 0.13.1 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (262) hide show
  1. package/README.md +3 -14
  2. package/dist/{api-client-4DZ266J5.js → api-client-4P4JZOBX.js} +3 -1
  3. package/dist/bowser-6Z6JFAIR.js +2822 -0
  4. package/dist/{chunk-Y2WHJECR.js → chunk-4OMDWYZQ.js} +5 -44
  5. package/dist/chunk-AD2NIWB5.js +6516 -0
  6. package/dist/chunk-DV3MRBR5.js +43 -0
  7. package/dist/chunk-EI5ZX7CN.js +522 -0
  8. package/dist/chunk-EUJ7X6YD.js +126 -0
  9. package/dist/chunk-GG5WKBOX.js +436 -0
  10. package/dist/chunk-I64EZSU5.js +358 -0
  11. package/dist/chunk-KMIJ5KGT.js +128 -0
  12. package/dist/chunk-MWQ7RO76.js +137 -0
  13. package/dist/chunk-OCPHU34C.js +996 -0
  14. package/dist/chunk-OLUQ4U2Z.js +1728 -0
  15. package/dist/chunk-XYM7PE2A.js +2264 -0
  16. package/dist/chunk-YQCYD6NE.js +3299 -0
  17. package/dist/cli.js +49156 -31257
  18. package/dist/commands/enterprise/templates/deploy-repo/scripts/apply-release.mjs +0 -1
  19. package/dist/commands/enterprise/templates/deploy-repo/scripts/smoke.mjs +0 -1
  20. package/dist/commands/enterprise/templates/deploy-repo/terraform/main.tf +0 -14
  21. package/dist/dist-es-2OTNTL4N.js +315 -0
  22. package/dist/dist-es-2XVAH4BQ.js +14726 -0
  23. package/dist/dist-es-3DPDUJAH.js +2000 -0
  24. package/dist/dist-es-BNZTBBVC.js +176 -0
  25. package/dist/dist-es-CD2RBDAC.js +88 -0
  26. package/dist/dist-es-I4FW57PS.js +88 -0
  27. package/dist/dist-es-KL7Y5NB2.js +377 -0
  28. package/dist/dist-es-KVFWJNDY.js +318 -0
  29. package/dist/dist-es-PS73DWNL.js +70 -0
  30. package/dist/dist-es-S3NZQFGT.js +170 -0
  31. package/dist/dist-es-SGCA4P32.js +48 -0
  32. package/dist/dist-es-WBM6J4RA.js +70 -0
  33. package/dist/dist-es-ZGVADYIF.js +485 -0
  34. package/dist/dist-es-ZKC2QKD2.js +377 -0
  35. package/dist/drizzle/0076_scheduled_jobs_marco_backfill.sql +2 -1
  36. package/dist/drizzle/0079_seed_tenant_customize_catalog.sql +1 -0
  37. package/dist/drizzle/0090_brain_schema_extraction.sql +10 -0
  38. package/dist/drizzle/0132_drop_computer_tables.sql +3 -0
  39. package/dist/drizzle/0160_compliance_event_types_plugins.sql +2 -0
  40. package/dist/drizzle/0214_think137_drop_judge_evidence_roi.sql +1 -0
  41. package/dist/drizzle/0216_drop_agent_loop_versions_goal_worker_policy.sql +1 -0
  42. package/dist/drizzle/0227_analyst_reader_role.sql +17 -20
  43. package/dist/drizzle/0230_analyst_rls.sql +28 -36
  44. package/dist/drizzle/0250_kg_schema_extraction.sql +27 -0
  45. package/dist/drizzle/0251_brain_stragglers.sql +21 -0
  46. package/dist/drizzle/0253_drop_brain_substrate.sql +2 -0
  47. package/dist/drizzle/0257_agents_runtime_allow_harness.sql +24 -0
  48. package/dist/drizzle/0261_native_auth_control_plane.sql +332 -0
  49. package/dist/drizzle/0262_auth_subscription_tickets.sql +59 -0
  50. package/dist/drizzle/0263_agentcore_runtime_identifier.sql +69 -0
  51. package/dist/drizzle/0263_drop_workos_auth_runtime.sql +236 -0
  52. package/dist/drizzle/0264_auth_enrollment_attempt_limits.sql +15 -0
  53. package/dist/drizzle/0265_eval_runs_requester_identity.sql +28 -0
  54. package/dist/drizzle/0265_require_fully_valid_auth_publication.sql +27 -0
  55. package/dist/drizzle/0266_auth_identity_recovery_grants.sql +9 -0
  56. package/dist/drizzle/0266_eval_profiles_runtime_type.sql +12 -0
  57. package/dist/drizzle/0267_ontology_candidate_rejections.sql +53 -0
  58. package/dist/drizzle/0268_identity_crosswalk_routing.sql +182 -0
  59. package/dist/drizzle/0269_ontology_system_map.sql +31 -0
  60. package/dist/drizzle/0270_tool_execution_ledger.sql +115 -0
  61. package/dist/drizzle/0271_ontology_twin_declarations.sql +47 -0
  62. package/dist/drizzle/0272_identity_graph_projection_cursor.sql +23 -0
  63. package/dist/drizzle/0273_twin_materialization_suggestions.sql +29 -0
  64. package/dist/drizzle/0274_tenant_mcp_twin_keys.sql +59 -0
  65. package/dist/drizzle/0275_identity_bulk_rebuild_fence.sql +30 -0
  66. package/dist/drizzle/0276_tool_execution_ledger_allow_cascade_delete.sql +18 -0
  67. package/dist/drizzle/0277_knowledge_base_sources.sql +107 -0
  68. package/dist/drizzle/0278_kb_page_transcription.sql +55 -0
  69. package/dist/drizzle/0279_mcp_servers_off_plugin_provenance.sql +93 -0
  70. package/dist/drizzle/0280_work_item_external_refs_drop_plane_provider.sql +46 -0
  71. package/dist/drizzle/0281_tenant_mcp_twin_keys_expiry_suffix.sql +9 -0
  72. package/dist/drizzle/0282_tenant_mcp_twin_keys_grants.sql +24 -0
  73. package/dist/drizzle/0283_agents_agentcore_runtime_dispatch.sql +13 -0
  74. package/dist/drizzle/0284_user_brain_claims.sql +69 -0
  75. package/dist/drizzle/0285_tenant_mcp_twin_keys_trusted_subsystem.sql +30 -0
  76. package/dist/drizzle/0286_brain_analytics_key.sql +26 -0
  77. package/dist/drizzle/0287_brain_subject.sql +25 -0
  78. package/dist/drizzle/0288_multi_lane_identities.sql +25 -0
  79. package/dist/event-streams-7JNLJEEL.js +894 -0
  80. package/dist/event-streams-KDF5RWR6.js +1515 -0
  81. package/dist/loadSso-ETCXTR4I.js +576 -0
  82. package/dist/loadSso-UBD7KUHE.js +583 -0
  83. package/dist/plugins/README.md +2 -2
  84. package/dist/plugins/catalog/package.json +1 -5
  85. package/dist/plugins/catalog/src/__tests__/build-catalog.test.ts +6 -4
  86. package/dist/plugins/catalog/src/__tests__/catalog.test.ts +21 -21
  87. package/dist/plugins/catalog/src/__tests__/contracts.test.ts +102 -94
  88. package/dist/plugins/catalog/src/__tests__/plugin-package.test.ts +7 -8
  89. package/dist/plugins/catalog/src/__tests__/plugin-registry.test.ts +0 -20
  90. package/dist/plugins/catalog/src/contracts.ts +17 -179
  91. package/dist/plugins/catalog/src/registry/generated-first-party.ts +0 -24
  92. package/dist/plugins/n8n/test/integrated-app-contract.test.ts +1 -1
  93. package/dist/plugins/twenty/package.json +2 -1
  94. package/dist/plugins/twenty/scripts/LAMBDA_DEPLOY.md +114 -0
  95. package/dist/plugins/twenty/scripts/build-lambda.sh +29 -0
  96. package/dist/plugins/twenty/scripts/configure-product-views.ts +195 -0
  97. package/dist/plugins/twenty/scripts/lambda-handler.ts +49 -0
  98. package/dist/plugins/twenty/scripts/lib/__tests__/load-attachments.test.ts +38 -0
  99. package/dist/plugins/twenty/scripts/lib/__tests__/load-records.test.ts +145 -2
  100. package/dist/plugins/twenty/scripts/lib/__tests__/mappers.test.ts +397 -151
  101. package/dist/plugins/twenty/scripts/lib/__tests__/members-ensure.test.ts +28 -129
  102. package/dist/plugins/twenty/scripts/lib/__tests__/provision-members-db.test.ts +10 -0
  103. package/dist/plugins/twenty/scripts/lib/__tests__/schema-ensure.test.ts +13 -8
  104. package/dist/plugins/twenty/scripts/lib/lastmile-reader.ts +222 -2
  105. package/dist/plugins/twenty/scripts/lib/load-attachments.ts +29 -0
  106. package/dist/plugins/twenty/scripts/lib/load-records.ts +160 -43
  107. package/dist/plugins/twenty/scripts/lib/mappers.ts +368 -152
  108. package/dist/plugins/twenty/scripts/lib/members-ensure.ts +43 -268
  109. package/dist/plugins/twenty/scripts/lib/provision-members-db.ts +57 -0
  110. package/dist/plugins/twenty/scripts/lib/schema-ensure.ts +136 -31
  111. package/dist/plugins/twenty/scripts/migrate-lastmile.ts +254 -152
  112. package/dist/plugins/twenty/scripts/provision-twenty-members.ts +44 -1
  113. package/dist/plugins/twenty/scripts/purge-lastmile-import.ts +178 -0
  114. package/dist/plugins/twenty/scripts/restructure-products.ts +312 -0
  115. package/dist/plugins/twenty/scripts/retag-stage-values.ts +362 -0
  116. package/dist/plugins/twenty/test/manifest.test.ts +2 -4
  117. package/dist/scripts/post-deploy.sh +79 -1
  118. package/dist/signin-5QZNRSOO.js +821 -0
  119. package/dist/sso-oidc-XK673P54.js +837 -0
  120. package/dist/sts-OXI2UALF.js +1113 -0
  121. package/dist/sts-SQW4NBK3.js +1117 -0
  122. package/dist/terraform/examples/greenfield/main.tf +391 -176
  123. package/dist/terraform/examples/greenfield/terraform.tfvars.example +26 -8
  124. package/dist/terraform/modules/app/agentcore-admin/main.tf +47 -0
  125. package/dist/terraform/modules/app/agentcore-code-interpreter/README.md +39 -47
  126. package/dist/terraform/modules/app/agentcore-code-interpreter/main.tf +13 -66
  127. package/dist/terraform/modules/app/agentcore-identity/main.tf +253 -0
  128. package/dist/terraform/modules/app/agentcore-identity/outputs.tf +51 -0
  129. package/dist/terraform/modules/app/agentcore-identity/scripts/bootstrap_twenty_oauth_client.sh +239 -0
  130. package/dist/terraform/modules/app/agentcore-identity/scripts/delete_identity.sh +22 -0
  131. package/dist/terraform/modules/app/agentcore-identity/scripts/delete_twenty_identity.sh +16 -0
  132. package/dist/terraform/modules/app/agentcore-identity/scripts/delete_workload_identity.sh +19 -0
  133. package/dist/terraform/modules/app/agentcore-identity/scripts/ensure_workload_identity.sh +29 -0
  134. package/dist/terraform/modules/app/agentcore-identity/scripts/read_identity.sh +55 -0
  135. package/dist/terraform/modules/app/agentcore-identity/scripts/reconcile_identity.sh +73 -0
  136. package/dist/terraform/modules/app/agentcore-identity/scripts/reconcile_twenty_identity.sh +45 -0
  137. package/dist/terraform/modules/app/agentcore-identity/scripts/reconcile_twenty_provider.mjs +123 -0
  138. package/dist/terraform/modules/app/agentcore-identity/tests/tainted-marker/after.tf +15 -0
  139. package/dist/terraform/modules/app/agentcore-identity/tests/tainted-marker/before.tf +7 -0
  140. package/dist/terraform/modules/app/agentcore-identity/tests/tainted-marker/identity-after.tf +8 -0
  141. package/dist/terraform/modules/app/agentcore-identity/tests/tainted-marker/identity-before.tf +9 -0
  142. package/dist/terraform/modules/app/agentcore-identity/tests/tainted-marker/thinkwork-after.tf +3 -0
  143. package/dist/terraform/modules/app/agentcore-identity/tests/tainted-marker/thinkwork-before.tf +3 -0
  144. package/dist/terraform/modules/app/agentcore-identity/variables.tf +60 -0
  145. package/dist/terraform/modules/app/agentcore-memory/README.md +46 -9
  146. package/dist/terraform/modules/app/agentcore-memory/main.tf +27 -10
  147. package/dist/terraform/modules/app/agentcore-memory/scripts/create_or_find_memory.sh +156 -24
  148. package/dist/terraform/modules/app/agentcore-pi/README.md +1 -3
  149. package/dist/terraform/modules/app/agentcore-pi/main.tf +42 -52
  150. package/dist/terraform/modules/app/agentcore-pi/outputs.tf +3 -3
  151. package/dist/terraform/modules/app/agentcore-pi/scripts/reconcile_pi_runtime.mjs +175 -0
  152. package/dist/terraform/modules/app/agentcore-pi/variables.tf +35 -28
  153. package/dist/terraform/modules/app/agentcore-platform/main.tf +68 -0
  154. package/dist/terraform/modules/app/agentcore-runtime/main.tf +4 -20
  155. package/dist/terraform/modules/app/appsync-subscriptions/main.tf +127 -21
  156. package/dist/terraform/modules/app/appsync-subscriptions/outputs.tf +0 -6
  157. package/dist/terraform/modules/app/appsync-subscriptions/variables.tf +5 -0
  158. package/dist/terraform/modules/app/capability-broker/main.tf +2 -2
  159. package/dist/terraform/modules/app/capability-broker/variables.tf +11 -0
  160. package/dist/terraform/modules/app/deployment-control-plane/README.md +264 -0
  161. package/dist/terraform/modules/app/deployment-control-plane/buildspec.yml +4 -1
  162. package/dist/terraform/modules/app/deployment-control-plane/main.tf +94 -0
  163. package/dist/terraform/modules/app/deployment-control-plane/outputs.tf +5 -0
  164. package/dist/terraform/modules/app/deployment-control-plane/runner.py +3689 -233
  165. package/dist/terraform/modules/app/deployment-control-plane/test_runner_bundle.py +2579 -140
  166. package/dist/terraform/modules/app/deployment-control-plane/variables.tf +33 -0
  167. package/dist/terraform/modules/app/lambda-api/auth-state.tf +72 -0
  168. package/dist/terraform/modules/app/lambda-api/chat-latency-observability.tf +466 -0
  169. package/dist/terraform/modules/app/lambda-api/dispatch-dlq.tf +55 -0
  170. package/dist/terraform/modules/app/lambda-api/handlers.tf +531 -619
  171. package/dist/terraform/modules/app/lambda-api/iam-grouped.tf +350 -241
  172. package/dist/terraform/modules/app/lambda-api/main.tf +75 -13
  173. package/dist/terraform/modules/app/lambda-api/memory-alarms.tf +3 -28
  174. package/dist/terraform/modules/app/lambda-api/oauth-secrets.tf +1 -2
  175. package/dist/terraform/modules/app/lambda-api/outputs.tf +17 -10
  176. package/dist/terraform/modules/app/lambda-api/runtime-config.tf +3 -21
  177. package/dist/terraform/modules/app/lambda-api/turn-assertion.tf +45 -0
  178. package/dist/terraform/modules/app/lambda-api/variables.tf +307 -121
  179. package/dist/{plugins/n8n/terraform → terraform/modules/app}/n8n/README.md +1 -1
  180. package/dist/{plugins/n8n/terraform → terraform/modules/app}/n8n/main.tf +18 -18
  181. package/dist/terraform/modules/app/sandbox-log-scrubber/README.md +11 -10
  182. package/dist/terraform/modules/app/sandbox-log-scrubber/main.tf +5 -5
  183. package/dist/terraform/modules/app/static-site/main.tf +1 -1
  184. package/dist/terraform/modules/app/www-dns/main.tf +9 -27
  185. package/dist/terraform/modules/app/www-dns/variables.tf +4 -10
  186. package/dist/terraform/modules/data/aurora-postgres/README.md +34 -0
  187. package/dist/terraform/modules/data/aurora-postgres/alarms.tf +82 -0
  188. package/dist/terraform/modules/data/aurora-postgres/main.tf +27 -57
  189. package/dist/terraform/modules/data/aurora-postgres/outputs.tf +0 -9
  190. package/dist/terraform/modules/data/aurora-postgres/variables.tf +41 -0
  191. package/dist/terraform/modules/data/s3-buckets/main.tf +5 -6
  192. package/dist/terraform/modules/foundation/cognito/main.tf +367 -72
  193. package/dist/terraform/modules/foundation/cognito/outputs.tf +47 -0
  194. package/dist/terraform/modules/foundation/cognito/variables.tf +131 -9
  195. package/dist/terraform/modules/thinkwork/README.md +5 -1
  196. package/dist/terraform/modules/thinkwork/main.tf +459 -234
  197. package/dist/terraform/modules/thinkwork/outputs.tf +44 -62
  198. package/dist/terraform/modules/thinkwork/variables.tf +353 -89
  199. package/dist/terraform/schema.graphql +69 -36
  200. package/dist/workspace-defaults/files/INSTRUCTIONS.md +9 -11
  201. package/dist/workspace-defaults/files/MEMORY_GUIDE.md +38 -35
  202. package/dist/workspace-defaults/files/ROUTER.md +1 -1
  203. package/dist/workspace-defaults/files/TOOLS.md +9 -39
  204. package/dist/workspace-defaults/files/USER.md +1 -0
  205. package/dist/workspace-defaults/files/agents/analyst/INSTRUCTIONS.md +6 -3
  206. package/dist/workspace-defaults/files/catalog-skills/brain-brief-builder/SKILL.md +165 -0
  207. package/dist/workspace-defaults/files/catalog-skills/n8n-workflow-operator/SKILL.md +75 -0
  208. package/dist/workspace-defaults/files/catalog-skills/n8n-workflow-operator/references/mcp-tooling.md +40 -0
  209. package/dist/workspace-defaults/files/catalog-skills/n8n-workflow-operator/references/validation-and-handoff.md +40 -0
  210. package/dist/workspace-defaults/files/catalog-skills/n8n-workflow-operator/references/workflow-authoring.md +46 -0
  211. package/dist/workspace-defaults/files/skills/artifact-builder/references/crm-dashboard.md +1 -1
  212. package/dist/workspace-defaults/files/skills/document-composer/SKILL.md +22 -3
  213. package/package.json +3 -2
  214. package/dist/drizzle/0261_thread_turns_origin_turn_index.sql +0 -11
  215. package/dist/plugins/company-data/README.md +0 -49
  216. package/dist/plugins/company-data/package.json +0 -21
  217. package/dist/plugins/company-data/src/index.ts +0 -30
  218. package/dist/plugins/company-data/src/manifest.ts +0 -26
  219. package/dist/plugins/company-data/test/manifest.test.ts +0 -116
  220. package/dist/plugins/company-data/tsconfig.json +0 -8
  221. package/dist/plugins/company-etl/README.md +0 -46
  222. package/dist/plugins/company-etl/package.json +0 -21
  223. package/dist/plugins/company-etl/src/index.ts +0 -29
  224. package/dist/plugins/company-etl/src/manifest.ts +0 -26
  225. package/dist/plugins/company-etl/test/manifest.test.ts +0 -108
  226. package/dist/plugins/company-etl/tsconfig.json +0 -8
  227. package/dist/plugins/lastmile/README.md +0 -50
  228. package/dist/plugins/lastmile/package.json +0 -23
  229. package/dist/plugins/lastmile/smoke/lastmile-plugin-smoke.mjs +0 -661
  230. package/dist/plugins/lastmile/src/api/tasks-adapter.ts +0 -572
  231. package/dist/plugins/lastmile/src/discovery.fixture.ts +0 -64
  232. package/dist/plugins/lastmile/src/index.ts +0 -46
  233. package/dist/plugins/lastmile/src/manifest.ts +0 -139
  234. package/dist/plugins/lastmile/test/api/tasks-adapter.test.ts +0 -266
  235. package/dist/plugins/lastmile/test/discovery.test.ts +0 -88
  236. package/dist/plugins/lastmile/tsconfig.json +0 -8
  237. package/dist/plugins/n8n/smoke/n8n-managed-app-smoke.mjs +0 -409
  238. package/dist/plugins/twenty/smoke/twenty-managed-app-smoke.mjs +0 -359
  239. package/dist/plugins/workos-auth/package.json +0 -22
  240. package/dist/plugins/workos-auth/src/index.ts +0 -30
  241. package/dist/plugins/workos-auth/src/manifest.ts +0 -40
  242. package/dist/plugins/workos-auth/src/provider-contract.ts +0 -39
  243. package/dist/plugins/workos-auth/test/manifest.test.ts +0 -94
  244. package/dist/plugins/workos-auth/tsconfig.json +0 -8
  245. package/dist/terraform/modules/app/agentcore-code-interpreter/Dockerfile.sandbox-base +0 -61
  246. package/dist/terraform/modules/app/agentcore-code-interpreter/sandbox/sitecustomize.py +0 -227
  247. package/dist/terraform/modules/app/agentcore-code-interpreter/sandbox/test_sitecustomize.py +0 -277
  248. package/dist/terraform/modules/app/agentcore-code-interpreter/scripts/build_and_push_sandbox_base.sh +0 -70
  249. package/dist/terraform/modules/app/agentcore-harness/main.tf +0 -101
  250. package/dist/terraform/modules/app/agentcore-harness/outputs.tf +0 -9
  251. package/dist/terraform/modules/app/agentcore-harness/variables.tf +0 -25
  252. package/dist/terraform/modules/app/hindsight-memory/README.md +0 -89
  253. package/dist/terraform/modules/app/hindsight-memory/main.tf +0 -411
  254. package/dist/terraform/modules/data/bedrock-knowledge-base/main.tf +0 -102
  255. /package/dist/{plugins/n8n/terraform → terraform/modules/app}/n8n/outputs.tf +0 -0
  256. /package/dist/{plugins/n8n/terraform → terraform/modules/app}/n8n/scripts/sync-database.py +0 -0
  257. /package/dist/{plugins/n8n/terraform → terraform/modules/app}/n8n/scripts/test_sync_database.py +0 -0
  258. /package/dist/{plugins/n8n/terraform → terraform/modules/app}/n8n/variables.tf +0 -0
  259. /package/dist/{plugins/twenty/terraform → terraform/modules/app}/twenty/README.md +0 -0
  260. /package/dist/{plugins/twenty/terraform → terraform/modules/app}/twenty/main.tf +0 -0
  261. /package/dist/{plugins/twenty/terraform → terraform/modules/app}/twenty/outputs.tf +0 -0
  262. /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,