@growthub/cli 0.9.18 → 0.10.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.
Files changed (56) hide show
  1. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/api/workspace/reference-options/route.js +62 -0
  2. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/api/workspace/refresh-sources/route.js +13 -2
  3. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/api/workspace/resolver-templates/route.js +23 -0
  4. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/api/workspace/sandbox-run/route.js +35 -5
  5. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/api/workspace/test-source/route.js +15 -1
  6. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/DataModelShell.jsx +2277 -0
  7. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/DataTable.jsx +1 -0
  8. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/FieldEditor.jsx +1 -0
  9. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/FieldManager.jsx +9 -0
  10. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/ObjectSidebar.jsx +41 -0
  11. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/RecordDrawer.jsx +1 -0
  12. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/ReferencePicker.jsx +244 -0
  13. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/SandboxRunPanel.jsx +21 -0
  14. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/SourceTestPanel.jsx +15 -0
  15. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/StatusPill.jsx +13 -0
  16. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/ToggleField.jsx +41 -0
  17. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/dm-shared.jsx +99 -0
  18. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/page.jsx +2 -1528
  19. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/globals.css +99 -6
  20. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/docs/connector-template-authoring.md +8 -0
  21. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/docs/data-model-reference-fields.md +15 -0
  22. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/docs/mcp-chrome-tool-connectors.md +12 -0
  23. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/docs/resolver-template-library.md +17 -0
  24. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/adapters/integrations/source-resolver-registry.js +13 -0
  25. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/adapters/integrations/templates/README.md +12 -0
  26. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/adapters/integrations/templates/chrome-bridge.js +22 -0
  27. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/adapters/integrations/templates/custom-http.js +23 -0
  28. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/adapters/integrations/templates/generic-commerce.js +22 -0
  29. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/adapters/integrations/templates/generic-crm.js +23 -0
  30. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/adapters/integrations/templates/generic-project-management.js +22 -0
  31. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/adapters/integrations/templates/generic-spreadsheet.js +22 -0
  32. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/adapters/integrations/templates/mcp-tool.js +22 -0
  33. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/adapters/integrations/templates/template-registry.js +50 -0
  34. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/adapters/integrations/templates/webhook.js +22 -0
  35. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/adapters/references/collect-reference-options.js +133 -0
  36. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/adapters/references/reference-resolver-registry.js +17 -0
  37. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/adapters/references/resolver-loader.js +6 -0
  38. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/adapters/references/resolvers/README.md +8 -0
  39. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/adapters/references/resolvers/local-data-model.js +11 -0
  40. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/adapters/references/resolvers/source-records.js +34 -0
  41. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/adapters/sandboxes/adapters/README.md +5 -3
  42. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/adapters/sandboxes/default-local-intelligence.js +203 -0
  43. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/adapters/sandboxes/index.js +1 -0
  44. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/data-model/field-contracts.js +81 -0
  45. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/data-model/reference-option-schema.js +59 -0
  46. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/data-model/reference-options.js +29 -0
  47. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/workspace-data-model.js +534 -23
  48. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/workspace-schema.js +131 -1
  49. package/assets/worker-kits/growthub-custom-workspace-starter-v1/helpers/export-training-traces.mjs +144 -0
  50. package/assets/worker-kits/growthub-custom-workspace-starter-v1/helpers/grade-raw-pairs.mjs +279 -0
  51. package/assets/worker-kits/growthub-custom-workspace-starter-v1/helpers/harvest-cursor-traces.mjs +288 -0
  52. package/assets/worker-kits/growthub-custom-workspace-starter-v1/helpers/upload-graded-traces.mjs +128 -0
  53. package/assets/worker-kits/growthub-custom-workspace-starter-v1/kit.json +10 -0
  54. package/assets/worker-kits/growthub-custom-workspace-starter-v1/templates/seeded-configs/alignment-loop.config.json +264 -0
  55. package/dist/index.js +486 -1
  56. package/package.json +1 -1
@@ -3965,6 +3965,37 @@ code { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 0
3965
3965
  .dm-detail-v2-title h2 { margin: 0; font-size: 15px; font-weight: 600; color: #111827; flex: 1; }
3966
3966
  .dm-detail-v2-meta { display: flex; align-items: center; gap: 10px; padding-left: 21px; font-size: 12px; color: #9ca3af; }
3967
3967
  .dm-detail-v2-meta code { font-size: 11px; color: #6b7280; background: #f3f4f6; border-radius: 4px; padding: 2px 6px; }
3968
+ .dm-detail-v3 { border: 1px solid #e5e7eb; border-radius: 12px; background: #fff; overflow: hidden; }
3969
+ .dm-detail-v3-head { padding-bottom: 14px; }
3970
+ .dm-picker { position: relative; min-width: 280px; }
3971
+ .dm-picker-trigger { width: min(420px, 100%); display: inline-flex; align-items: center; gap: 10px; min-height: 40px; border: 1px solid #dbe2ea; border-radius: 10px; background: #fff; color: #0f172a; box-shadow: 0 1px 2px rgba(15,23,42,.05); font: inherit; padding: 0 12px; cursor: pointer; text-align: left; }
3972
+ .dm-picker.open .dm-picker-trigger { border-color: #94a3b8; box-shadow: 0 0 0 4px rgba(148,163,184,.14); }
3973
+ .dm-picker-trigger-copy { display: grid; min-width: 0; flex: 1; }
3974
+ .dm-picker-trigger-copy strong { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 13px; font-weight: 650; color: #111827; }
3975
+ .dm-picker-trigger-copy em { font-style: normal; font-size: 11px; color: #94a3b8; }
3976
+ .dm-picker-popover { position: absolute; top: calc(100% + 8px); left: 0; z-index: 50; width: 360px; display: grid; gap: 10px; padding: 12px; border: 1px solid #dbe2ea; border-radius: 14px; background: #fff; box-shadow: 0 28px 80px rgba(15,23,42,.24); overflow: visible; }
3977
+ .dm-picker-tabs { display: inline-flex; gap: 6px; padding: 4px; border-radius: 999px; background: #f8fafc; }
3978
+ .dm-picker-tabs button { height: 28px; border: 0; border-radius: 999px; background: transparent; color: #64748b; font: inherit; font-size: 12px; padding: 0 10px; cursor: pointer; }
3979
+ .dm-picker-tabs button.active { background: #fff; color: #111827; box-shadow: 0 1px 2px rgba(15,23,42,.08); }
3980
+ .dm-picker-section { display: grid; gap: 8px; }
3981
+ .dm-picker-section > p { margin: 0; font-size: 11px; font-weight: 700; color: #94a3b8; text-transform: uppercase; letter-spacing: .06em; }
3982
+ .dm-picker-scroll { display: grid; gap: 4px; max-height: 220px; overflow-y: auto; padding-right: 10px; scrollbar-gutter: stable; }
3983
+ .dm-picker-item { position: relative; display: grid; grid-template-columns: minmax(0,1fr) auto; gap: 8px; align-items: center; }
3984
+ .dm-picker-row { width: 100%; display: inline-flex; align-items: center; gap: 8px; min-width: 0; min-height: 34px; border: 0; border-radius: 9px; background: transparent; color: #334155; font: inherit; font-size: 12px; font-weight: 500; padding: 0 10px; cursor: pointer; text-align: left; }
3985
+ .dm-picker-row:hover, .dm-picker-item.active .dm-picker-row { background: #f1f5f9; color: #111827; }
3986
+ .dm-picker-item.active .dm-picker-row span { font-weight: 650; }
3987
+ .dm-picker-row span { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
3988
+ .dm-picker-actions { position: relative; display: inline-flex; align-items: center; justify-content: center; }
3989
+ .dm-picker-icon-btn { display: inline-flex; align-items: center; justify-content: center; width: 26px; height: 26px; border: 1px solid #e2e8f0; border-radius: 7px; background: #fff; color: #94a3b8; cursor: pointer; flex: 0 0 auto; }
3990
+ .dm-picker-icon-btn:hover { color: #111827; border-color: #cbd5e1; }
3991
+ .dm-picker-icon-btn.danger:hover { color: #b91c1c; border-color: #fecaca; background: #fef2f2; }
3992
+ .dm-picker-lock { margin-left: auto; color: #94a3b8; }
3993
+ .dm-picker-menu { position: absolute; top: calc(100% + 6px); right: 0; z-index: 55; display: grid; gap: 4px; min-width: 156px; padding: 8px; border: 1px solid #dbe2ea; border-radius: 10px; background: #fff; box-shadow: 0 20px 44px rgba(15,23,42,.2), 0 4px 12px rgba(15,23,42,.08); }
3994
+ .dm-picker-menu button { display: inline-flex; align-items: center; gap: 8px; height: 30px; border: 0; border-radius: 7px; background: transparent; color: #334155; font: inherit; font-size: 12px; padding: 0 10px; cursor: pointer; text-align: left; }
3995
+ .dm-picker-menu button:hover { background: #f1f5f9; color: #111827; }
3996
+ .dm-picker-menu button.danger:hover { background: #fef2f2; color: #b91c1c; }
3997
+ .dm-picker-create { display: grid; gap: 8px; border-top: 1px solid #edf0f3; padding-top: 10px; }
3998
+ .dm-picker-create input { width: 100%; height: 34px; border: 1px solid #cbd5e1; border-radius: 8px; background: #fff; color: #111827; font: inherit; font-size: 12px; padding: 0 10px; }
3968
3999
 
3969
4000
  /* Source validation banner */
3970
4001
  .dm-validation-banner { display: flex; align-items: center; gap: 7px; margin-top: 8px; padding: 7px 10px; background: #fffbeb; border: 1px solid #fde68a; border-radius: 6px; font-size: 12px; color: #92400e; }
@@ -3999,9 +4030,10 @@ code { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 0
3999
4030
  .dm-records-tab {}
4000
4031
  .dm-records-toolbar { display: flex; align-items: center; justify-content: space-between; gap: 10px; margin-bottom: 12px; }
4001
4032
  .dm-stat-label { font-size: 12px; color: #9ca3af; font-weight: 600; }
4002
- .dm-records-actions { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
4033
+ .dm-records-actions { display: flex; align-items: center; justify-content: flex-end; gap: 6px; flex-wrap: wrap; margin-left: auto; }
4003
4034
  .dm-btn-ghost { display: inline-flex; align-items: center; gap: 5px; height: 28px; border: 1px solid #e5e7eb; border-radius: 6px; background: #fff; color: #555; font: inherit; font-size: 12px; padding: 0 10px; cursor: pointer; transition: background .1s, border-color .1s; }
4004
4035
  .dm-btn-ghost:hover { background: #f5f5f5; border-color: #ccc; color: #222; }
4036
+ .dm-btn-outline { display: inline-flex; align-items: center; justify-content: center; gap: 5px; height: 28px; border: 1px solid #d1d5db; border-radius: 6px; background: #fff; color: #475569; font: inherit; font-size: 12px; padding: 0 10px; cursor: pointer; }
4005
4037
  .dm-btn-primary-sm { display: inline-flex; align-items: center; gap: 5px; height: 28px; border: 0; border-radius: 6px; background: #111; color: #fff; font: inherit; font-size: 12px; padding: 0 11px; cursor: pointer; font-weight: 500; transition: background .12s; }
4006
4038
  .dm-btn-primary-sm:hover { background: #333; }
4007
4039
  .dm-btn-primary-sm:disabled { opacity: .45; cursor: not-allowed; }
@@ -4023,24 +4055,77 @@ code { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 0
4023
4055
  .dm-csv-textarea { width: 100%; margin-bottom: 8px; border: 1px solid #d1d5db; border-radius: 6px; background: #fff; font-family: ui-monospace,SFMono-Regular,Menlo,monospace; font-size: 12px; padding: 7px 10px; box-sizing: border-box; resize: vertical; }
4024
4056
 
4025
4057
  /* Data Model database grid */
4026
- .dm-db-surface { display: flex; flex-direction: column; gap: 10px; padding: 14px; min-height: 0; flex: 1; background: #fff; }
4027
- .dm-db-toolbar { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
4058
+ .dm-db-surface { display: flex; flex-direction: column; gap: 10px; padding: 14px; min-height: 0; flex: 1; background: #fff; overflow: visible; }
4059
+ .dm-db-toolbar { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; overflow: visible; }
4028
4060
  .dm-db-toolbar-title { display: flex; align-items: baseline; gap: 8px; min-width: 0; }
4029
4061
  .dm-db-toolbar-title strong { font-size: 13px; color: #111827; font-weight: 650; }
4030
4062
  .dm-db-toolbar-title span { font-size: 12px; color: #9ca3af; }
4031
- .dm-db-grid-wrap { flex: 1; min-height: 420px; overflow: auto; border: 1px solid #dfe3e8; border-radius: 7px; background: #fff; }
4063
+ .dm-inline-panel { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; padding: 10px 12px; border: 1px solid #dfe3e8; border-radius: 10px; background: #fbfdff; }
4064
+ .dm-inline-panel input { height: 32px; border: 1px solid #cbd5e1; border-radius: 8px; background: #fff; color: #111827; font: inherit; font-size: 12px; padding: 0 10px; }
4065
+ .dm-filter-chip-row { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; min-height: 32px; }
4066
+ .dm-filter-chip { display: inline-flex; align-items: center; gap: 6px; height: 30px; border: 1px solid #c7d2fe; border-radius: 8px; background: #eef2ff; color: #3730a3; font: inherit; font-size: 12px; padding: 0 10px; cursor: pointer; }
4067
+ .dm-selection-count { border-color: #d1d5db; background: #fff; color: #475569; cursor: default; }
4068
+ .dm-filter-chip span { max-width: 240px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
4069
+ .dm-filter-popover { position: absolute; z-index: 40; display: grid; gap: 8px; width: 320px; padding: 10px; border: 1px solid #dbe2ea; border-radius: 10px; background: #fff; box-shadow: 0 18px 42px rgba(15,23,42,.18), 0 3px 10px rgba(15,23,42,.08); }
4070
+ .dm-filter-popover input { height: 32px; border: 1px solid #cbd5e1; border-radius: 8px; background: #fff; color: #111827; font: inherit; font-size: 12px; padding: 0 10px; }
4071
+ .dm-filter-anchor { position: relative; display: inline-flex; }
4072
+ .dm-filter-popover-toolbar { top: calc(100% + 6px); left: 0; right: auto; }
4073
+ .dm-filter-popover-column { top: calc(100% + 6px); left: 0; }
4074
+ .dm-filter-popover-actions { display: flex; justify-content: flex-end; gap: 8px; }
4075
+ .dm-db-grid-wrap { display: flex; flex: 1; min-height: 420px; flex-direction: column; overflow: hidden; border: 1px solid #dfe3e8; border-radius: 7px; background: #fff; }
4076
+ .dm-db-grid-wrap > .dm-db-grid-scroll { flex: 1 1 auto; overflow: auto; }
4032
4077
  .dm-db-grid { width: 100%; border-collapse: separate; border-spacing: 0; font-size: 12px; color: #1f2937; }
4033
- .dm-db-grid th { position: sticky; top: 0; z-index: 1; height: 32px; background: #f8fafc; color: #475569; border-bottom: 1px solid #dfe3e8; border-right: 1px solid #edf0f3; padding: 0 10px; text-align: left; font-weight: 650; white-space: nowrap; }
4078
+ .dm-db-grid th { position: sticky; top: 0; z-index: 1; height: 32px; background: #f8fafc; color: #475569; border-bottom: 1px solid #dfe3e8; border-right: 1px solid #edf0f3; padding: 0; text-align: left; font-weight: 650; white-space: nowrap; }
4034
4079
  .dm-db-grid td { height: 32px; max-width: 260px; border-bottom: 1px solid #f1f5f9; border-right: 1px solid #f1f5f9; padding: 0 10px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; cursor: pointer; }
4035
4080
  .dm-db-grid tbody tr:hover td { background: #f8fafc; }
4036
4081
  .dm-db-grid tbody tr.selected td { background: #eef2ff; }
4082
+ .dm-db-grid tbody tr.multi-selected td { background: #f8fafc; }
4083
+ .dm-db-head-btn { display: inline-flex; align-items: center; justify-content: flex-start; width: 100%; height: 32px; gap: 6px; border: 0; background: transparent; color: inherit; font: inherit; font-size: 12px; font-weight: inherit; padding: 0 10px; cursor: pointer; }
4037
4084
  .dm-db-rownum { width: 42px; min-width: 42px; text-align: center !important; color: #94a3b8 !important; background: #f8fafc; font-variant-numeric: tabular-nums; }
4085
+ .dm-row-select { position: relative; display: inline-flex; align-items: center; justify-content: center; width: 100%; height: 32px; border: 0; background: transparent; color: inherit; font: inherit; cursor: pointer; }
4086
+ .dm-row-select-box { position: relative; display: none; width: 16px; height: 16px; border: 1px solid #d1d5db; border-radius: 5px; background: #fff; box-sizing: border-box; }
4087
+ .dm-row-select:hover .dm-row-select-box, .dm-row-select[aria-pressed="true"] .dm-row-select-box { border-color: #cbd5e1; background: #fff; box-shadow: 0 1px 3px rgba(15,23,42,.14); }
4088
+ .dm-row-select[aria-pressed="true"] .dm-row-select-box { display: inline-flex; }
4089
+ .dm-row-select[aria-pressed="true"] .dm-row-select-box::after { content: ""; position: absolute; left: 5px; top: 3px; width: 4px; height: 7px; border: solid #334155; border-width: 0 1.5px 1.5px 0; transform: rotate(45deg); }
4090
+ .dm-db-grid tbody tr:hover .dm-row-select-box { display: inline-flex; }
4091
+ .dm-row-select[aria-pressed="true"] .dm-row-number, .dm-db-grid tbody tr:hover .dm-row-select .dm-row-number { display: none; }
4092
+ .dm-db-rownum-head { position: sticky; overflow: visible; z-index: 4 !important; }
4093
+ .dm-row-select-head-wrap { position: relative; display: flex; align-items: center; justify-content: center; height: 32px; }
4094
+ .dm-row-select-all { width: 26px; flex: 0 0 26px; }
4095
+ .dm-row-select-menu-btn { display: none; }
4096
+ .dm-db-rownum-head:hover .dm-row-select-menu-btn, .dm-row-select-menu-btn[aria-expanded="true"] { display: none; }
4097
+ .dm-row-select-menu-btn:hover { background: #eef2f7; color: #475569; }
4098
+ .dm-db-rownum-head:hover .dm-row-select-all .dm-row-select-box, .dm-row-select-all[aria-pressed="true"] .dm-row-select-box { display: inline-flex; }
4099
+ .dm-db-rownum-head:hover .dm-row-select-all .dm-row-number, .dm-row-select-all[aria-pressed="true"] .dm-row-number { display: none; }
4100
+ .dm-row-select-menu { position: absolute; top: calc(100% + 6px); left: 6px; z-index: 50; display: grid; gap: 3px; min-width: 150px; padding: 6px; border: 1px solid #dbe2ea; border-radius: 8px; background: #fff; box-shadow: 0 14px 34px rgba(15,23,42,.16), 0 2px 8px rgba(15,23,42,.08); }
4101
+ .dm-row-select-menu button { display: flex; align-items: center; height: 28px; border: 0; border-radius: 6px; background: transparent; color: #334155; font: inherit; font-size: 12px; padding: 0 8px; text-align: left; cursor: pointer; }
4102
+ .dm-row-select-menu button:hover { background: #f1f5f9; }
4103
+ .dm-row-select-menu button:disabled { opacity: .45; cursor: not-allowed; }
4038
4104
  .dm-db-field-type { display: inline-flex; align-items: center; justify-content: center; width: 18px; height: 18px; margin-right: 5px; border-radius: 4px; color: #64748b; background: #eef2f7; vertical-align: middle; }
4039
- .dm-db-add-field { min-width: 118px; }
4105
+ .dm-db-add-field { position: relative; min-width: 118px; }
4040
4106
  .dm-db-add-field button { display: inline-flex; align-items: center; gap: 4px; border: 0; background: transparent; color: #64748b; font: inherit; font-size: 12px; cursor: pointer; }
4041
4107
  .dm-db-add-field input { width: 110px; height: 24px; border: 1px solid #cbd5e1; border-radius: 5px; padding: 0 7px; font: inherit; font-size: 12px; }
4108
+ .dm-col-menu { position: absolute; top: calc(100% + 6px); left: 0; z-index: 25; display: grid; gap: 3px; min-width: 180px; padding: 8px; border: 1px solid #dbe2ea; border-radius: 10px; background: #fff; box-shadow: 0 18px 42px rgba(15,23,42,.18), 0 3px 10px rgba(15,23,42,.08); }
4109
+ .dm-col-menu button { display: inline-flex; align-items: center; gap: 8px; height: 30px; border: 0; border-radius: 7px; background: transparent; color: #334155; font: inherit; font-size: 12px; padding: 0 10px; cursor: pointer; text-align: left; }
4110
+ .dm-col-menu button:hover { background: #f1f5f9; }
4111
+ .dm-field-creator-popover { position: absolute; top: calc(100% + 8px); right: 0; z-index: 35; }
4112
+ .dm-field-creator { display: grid; gap: 8px; width: 248px; padding: 10px; border: 1px solid #dbe2ea; border-radius: 10px; background: #fff; box-shadow: 0 18px 42px rgba(15,23,42,.18), 0 3px 10px rgba(15,23,42,.08); }
4113
+ .dm-field-creator > input { width: 100%; height: 32px; border: 1px solid #cbd5e1; border-radius: 8px; background: #fff !important; color: #111827 !important; -webkit-text-fill-color: #111827; padding: 0 10px; font: inherit; font-size: 12px; appearance: none; }
4114
+ .dm-field-type-grid { display: grid; grid-template-columns: repeat(2, minmax(0,1fr)); gap: 6px; }
4115
+ .dm-field-type-grid button { display: inline-flex; align-items: center; gap: 6px; justify-content: flex-start; height: 32px; border: 1px solid #e2e8f0; border-radius: 8px; background: #fff; color: #475569; font: inherit; font-size: 12px; padding: 0 9px; cursor: pointer; }
4116
+ .dm-field-type-grid button.active { border-color: #c7d2fe; background: #eef2ff; color: #3730a3; }
4117
+ .dm-field-creator-actions { display: flex; justify-content: flex-end; gap: 8px; }
4042
4118
  .dm-db-empty-cell { background: #fbfdff; }
4043
4119
  .dm-db-new-row td { color: #64748b; background: #fbfdff; font-weight: 600; }
4120
+ .dm-pagination-bar { display: flex; flex: 0 0 auto; align-items: center; justify-content: space-between; gap: 12px; min-height: 36px; padding: 8px 14px; background: #fff; color: #64748b; font-size: 12px; border-top: 1px solid #edf0f3; }
4121
+ .dm-pagination-summary { white-space: nowrap; }
4122
+ .dm-pagination-controls { display: inline-flex; align-items: center; gap: 8px; }
4123
+ .dm-page-size-control { display: inline-flex; align-items: center; gap: 6px; color: #64748b; }
4124
+ .dm-page-size-control select { height: 28px; border: 1px solid #dbe2ea; border-radius: 6px; background: #fff; color: #334155; font: inherit; font-size: 12px; padding: 0 22px 0 8px; }
4125
+ .dm-pagination-btn { height: 28px; border: 1px solid #dbe2ea; border-radius: 6px; background: #fff; color: #334155; font: inherit; font-size: 12px; padding: 0 10px; cursor: pointer; }
4126
+ .dm-pagination-btn:hover:not(:disabled) { background: #f8fafc; border-color: #cbd5e1; }
4127
+ .dm-pagination-btn:disabled { opacity: .45; cursor: not-allowed; }
4128
+ .dm-pagination-page { min-width: 44px; text-align: center; color: #64748b; }
4044
4129
  .dm-db-status { display: inline-flex; align-items: center; gap: 5px; height: 20px; border: 1px solid #e2e8f0; border-radius: 999px; padding: 0 8px; background: #f8fafc; color: #64748b; font-size: 11px; font-weight: 650; }
4045
4130
  .dm-db-status span { width: 6px; height: 6px; border-radius: 50%; background: #94a3b8; }
4046
4131
  .dm-db-status.ok { border-color: #bbf7d0; background: #f0fdf4; color: #166534; }
@@ -4050,6 +4135,14 @@ code { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 0
4050
4135
  .dm-record-backdrop { position: fixed; inset: 0; z-index: 80; background: rgba(15,23,42,.12); }
4051
4136
  .dm-record-drawer { position: fixed; top: 0; right: 0; bottom: 0; z-index: 81; display: flex; flex-direction: column; width: min(440px, 100vw); background: #fff; border-left: 1px solid #dfe3e8; box-shadow: -10px 0 34px rgba(15,23,42,.16); }
4052
4137
  .dm-record-drawer-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 16px 18px; border-bottom: 1px solid #edf0f3; }
4138
+ .dm-record-drawer-actions { display: inline-flex; align-items: center; gap: 8px; }
4139
+ .dm-record-drawer-foot { display: flex; justify-content: flex-end; gap: 8px; padding: 14px 16px; border-top: 1px solid #edf0f3; background: #fff; }
4140
+ .dm-drawer-field-editor { display: grid; gap: 8px; }
4141
+ .dm-drawer-field-row { display: grid; grid-template-columns: minmax(0,1fr) auto auto auto; gap: 8px; align-items: center; }
4142
+ .dm-drawer-field-row input { width: 100%; height: 34px; border: 1px solid #cbd5e1; border-radius: 8px; background: #fff; color: #111827; font: inherit; font-size: 12px; padding: 0 10px; }
4143
+ .dm-drawer-hidden-fields { display: grid; gap: 8px; margin-top: 10px; }
4144
+ .dm-drawer-hidden-fields > span { color: #64748b; font-size: 12px; font-weight: 600; }
4145
+ .dm-drawer-hidden-list { display: flex; flex-wrap: wrap; gap: 8px; }
4053
4146
  .dm-record-drawer-head p { margin: 0 0 3px; color: #94a3b8; font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .05em; }
4054
4147
  .dm-record-drawer-head h2 { margin: 0; color: #111827; font-size: 16px; font-weight: 650; }
4055
4148
  .dm-record-testbar { display: flex; align-items: center; gap: 8px; padding: 10px 18px; border-bottom: 1px solid #edf0f3; background: #fbfdff; }
@@ -0,0 +1,8 @@
1
+ # Connector template authoring
2
+
3
+ 1. Add `lib/adapters/integrations/templates/<your-template>.js` exporting a `default` object with `schemaVersion: "growthub-resolver-template-v1"`.
4
+ 2. Import it from `template-registry.js` and append to the `ALL` array.
5
+ 3. Keep `connectorKind`, `capabilities`, and `supportedLanes` honest — they drive operator UX only.
6
+ 4. Document the expected resolver `integrationId` and any env refs (`authRef` pattern) in the template `configSchema` entries.
7
+
8
+ Do not import vendor SDKs into template modules. Provider code stays in resolver files under `resolvers/`.
@@ -0,0 +1,15 @@
1
+ # Data Model reference fields
2
+
3
+ Governed reference columns (for example `registryId` on a Data Source or `schedulerRegistryId` on a Sandbox Environment) are declared on each `dataModel.objects[]` entry under `relations[]`. Preset object types ship default relation metadata; older objects pick up the same defaults through `effectiveRelations()` at read time.
4
+
5
+ ## Server option loading
6
+
7
+ `POST /api/workspace/reference-options` returns normalized `{ value, label, secondaryLabel?, source, objectType?, status?, metadata? }[]` rows. The default path scans local `dataModel.objects[]` rows whose `objectType` matches `targetObjectType`, using optional `valueField`, `labelField`, `statusField`, and `statusAllowlist` on the relation descriptor.
8
+
9
+ ## UI
10
+
11
+ The Data Model page uses `ReferencePicker` for objects that have a stable `objectId`. Widget-bound tables without an `objectId` keep the legacy client-only picker.
12
+
13
+ ## Authority
14
+
15
+ The browser never receives provider secrets. Reference options for workspace rows are derived from config only; resolver-backed `listEntities` runs server-side with env/bridge resolution identical to `POST /api/workspace/test-source`.
@@ -0,0 +1,12 @@
1
+ # MCP, Chrome, and tool connectors
2
+
3
+ AWaC keeps **authority on the server**: MCP sessions, Chrome bridges, and local tool CLIs are reached through resolver `fetchRecords` / optional `listEntities`, sandbox adapters, or serverless scheduler rows — never by embedding tokens into `growthub.config.json`.
4
+
5
+ Recommended pattern:
6
+
7
+ 1. Model the connector as an **API Registry** row plus optional **Data Source** with `binding.sourceStorage: "workspace-source-records"`.
8
+ 2. Implement a resolver whose `integrationId` matches the registry row.
9
+ 3. Use `POST /api/workspace/test-source` before binding widgets to the Data Source.
10
+ 4. For actions that mutate external systems, prefer **sandbox** lanes (`runLocality: local` with `local-agent-host`, or `serverless` with a scheduler API Registry row) so outputs land in `growthub.source-records.json`.
11
+
12
+ The `mcp-tool` and `chrome-bridge` templates are scaffolds — replace `integrationId` values with your registered resolver slug after you add resolver files.
@@ -0,0 +1,17 @@
1
+ # Resolver template library
2
+
3
+ Templates live under `lib/adapters/integrations/templates/` and are registered in `template-registry.js`. Each template is **metadata only**: it suggests default API Registry / Data Source shapes and a `configSchema` for operators.
4
+
5
+ ## HTTP API
6
+
7
+ - `GET /api/workspace/resolver-templates` — list all templates.
8
+ - `GET /api/workspace/resolver-templates?templateId=<id>` — fetch one template.
9
+
10
+ ## Wiring a real resolver
11
+
12
+ 1. Choose a template (for example `custom-http`).
13
+ 2. Create an API Registry row whose `integrationId` matches the resolver you will register under `lib/adapters/integrations/resolvers/<id>.js`.
14
+ 3. Drop a resolver file that calls `registerSourceResolver({ integrationId, fetchRecords, ... })`.
15
+ 4. Test with `POST /api/workspace/test-source` and refresh with `POST /api/workspace/refresh-sources`.
16
+
17
+ Templates never execute network calls themselves.
@@ -12,6 +12,11 @@
12
12
  * entityTypes: string[], // e.g. ["project.tasks", "workspace.users"]
13
13
  * listEntities: async (config, connection) => NormalizedEntity[],
14
14
  * fetchRecords: async (config, connection, binding) => Record[]
15
+ * connectorKind?: string, // http | mcp | chrome | tool | custom
16
+ * templateId?: string,
17
+ * capabilities?: string[],
18
+ * configSchema?: SchemaField[],
19
+ * referenceSchema?: Record<string, unknown>
15
20
  * }
16
21
  *
17
22
  * The route and the refresh button reference this registry only — they have
@@ -71,6 +76,7 @@ function listRegisteredResolvers() {
71
76
  * entityTypes: string[], // declared by the resolver
72
77
  * hasListEntities: boolean, // true if resolver.listEntities is a function
73
78
  * configSchema: SchemaField[] | null // optional declarative params schema
79
+ * connectorKind, templateId, capabilities, referenceSchema — optional metadata
74
80
  * }
75
81
  */
76
82
  function describeRegisteredResolvers() {
@@ -79,6 +85,13 @@ function describeRegisteredResolvers() {
79
85
  entityTypes: Array.isArray(resolver.entityTypes) ? resolver.entityTypes : [],
80
86
  hasListEntities: typeof resolver.listEntities === "function",
81
87
  configSchema: Array.isArray(resolver.configSchema) ? resolver.configSchema : null,
88
+ connectorKind: typeof resolver.connectorKind === "string" ? resolver.connectorKind : null,
89
+ templateId: typeof resolver.templateId === "string" ? resolver.templateId : null,
90
+ capabilities: Array.isArray(resolver.capabilities) ? resolver.capabilities : null,
91
+ referenceSchema:
92
+ resolver.referenceSchema && typeof resolver.referenceSchema === "object" && !Array.isArray(resolver.referenceSchema)
93
+ ? resolver.referenceSchema
94
+ : null
82
95
  }));
83
96
  }
84
97
 
@@ -0,0 +1,12 @@
1
+ # Resolver template library
2
+
3
+ Shipped templates under this directory are **metadata-only** seeds. They do not register resolvers or execute providers.
4
+
5
+ - Add a new `*.js` file exporting a `default` object with `schemaVersion: "growthub-resolver-template-v1"`.
6
+ - Register the module in `template-registry.js`.
7
+ - Implement real HTTP/MCP/tool behavior in `lib/adapters/integrations/resolvers/<integrationId>.js` using `registerSourceResolver`.
8
+
9
+ Runtime surfaces:
10
+
11
+ - `GET /api/workspace/resolver-templates` — list templates
12
+ - `GET /api/workspace/resolver-templates?templateId=<id>` — fetch one
@@ -0,0 +1,22 @@
1
+ const template = {
2
+ schemaVersion: "growthub-resolver-template-v1",
3
+ templateId: "chrome-bridge",
4
+ label: "Chrome extension bridge",
5
+ connectorKind: "chrome",
6
+ capabilities: ["fetchRecords", "runAction"],
7
+ apiRegistryDefaults: {
8
+ integrationId: "chrome-bridge",
9
+ method: "POST"
10
+ },
11
+ dataSourceDefaults: {
12
+ objectType: "data-source",
13
+ binding: { sourceStorage: "workspace-source-records" }
14
+ },
15
+ configSchema: [
16
+ { name: "baseUrl", label: "Bridge base URL", type: "url", required: true },
17
+ { name: "authRef", label: "Bridge token ref", type: "secretRef", required: true }
18
+ ],
19
+ supportedLanes: ["sandbox-local", "data-source"]
20
+ };
21
+
22
+ export default template;
@@ -0,0 +1,23 @@
1
+ const template = {
2
+ schemaVersion: "growthub-resolver-template-v1",
3
+ templateId: "custom-http",
4
+ label: "Custom HTTP connector",
5
+ connectorKind: "http",
6
+ capabilities: ["listEntities", "fetchRecords"],
7
+ apiRegistryDefaults: {
8
+ integrationId: "custom-http",
9
+ method: "GET"
10
+ },
11
+ dataSourceDefaults: {
12
+ objectType: "data-source",
13
+ binding: { sourceStorage: "workspace-source-records" }
14
+ },
15
+ configSchema: [
16
+ { name: "baseUrl", label: "Base URL", type: "url", required: true },
17
+ { name: "endpoint", label: "Endpoint path", type: "text", required: false },
18
+ { name: "authRef", label: "Auth env ref", type: "secretRef", required: false }
19
+ ],
20
+ supportedLanes: ["data-source", "sandbox-local", "sandbox-serverless"]
21
+ };
22
+
23
+ export default template;
@@ -0,0 +1,22 @@
1
+ const template = {
2
+ schemaVersion: "growthub-resolver-template-v1",
3
+ templateId: "generic-commerce",
4
+ label: "Generic commerce / orders API",
5
+ connectorKind: "http",
6
+ capabilities: ["fetchRecords"],
7
+ apiRegistryDefaults: {
8
+ integrationId: "generic-commerce",
9
+ method: "GET"
10
+ },
11
+ dataSourceDefaults: {
12
+ objectType: "data-source",
13
+ binding: { sourceStorage: "workspace-source-records" }
14
+ },
15
+ configSchema: [
16
+ { name: "baseUrl", label: "Store API base", type: "url", required: true },
17
+ { name: "authRef", label: "Auth ref", type: "secretRef", required: true }
18
+ ],
19
+ supportedLanes: ["data-source"]
20
+ };
21
+
22
+ export default template;
@@ -0,0 +1,23 @@
1
+ const template = {
2
+ schemaVersion: "growthub-resolver-template-v1",
3
+ templateId: "generic-crm",
4
+ label: "Generic CRM feed",
5
+ connectorKind: "http",
6
+ capabilities: ["listEntities", "fetchRecords"],
7
+ apiRegistryDefaults: {
8
+ integrationId: "generic-crm",
9
+ method: "GET"
10
+ },
11
+ dataSourceDefaults: {
12
+ objectType: "data-source",
13
+ binding: { sourceStorage: "workspace-source-records" }
14
+ },
15
+ configSchema: [
16
+ { name: "baseUrl", label: "CRM base URL", type: "url", required: true },
17
+ { name: "entityType", label: "Entity type", type: "text", required: true },
18
+ { name: "authRef", label: "Auth ref", type: "secretRef", required: true }
19
+ ],
20
+ supportedLanes: ["data-source"]
21
+ };
22
+
23
+ export default template;
@@ -0,0 +1,22 @@
1
+ const template = {
2
+ schemaVersion: "growthub-resolver-template-v1",
3
+ templateId: "generic-project-management",
4
+ label: "Generic project management API",
5
+ connectorKind: "http",
6
+ capabilities: ["listEntities", "fetchRecords"],
7
+ apiRegistryDefaults: {
8
+ integrationId: "generic-pm",
9
+ method: "GET"
10
+ },
11
+ dataSourceDefaults: {
12
+ objectType: "data-source",
13
+ binding: { sourceStorage: "workspace-source-records" }
14
+ },
15
+ configSchema: [
16
+ { name: "baseUrl", label: "API base URL", type: "url", required: true },
17
+ { name: "authRef", label: "Auth ref", type: "secretRef", required: true }
18
+ ],
19
+ supportedLanes: ["data-source", "sandbox-serverless"]
20
+ };
21
+
22
+ export default template;
@@ -0,0 +1,22 @@
1
+ const template = {
2
+ schemaVersion: "growthub-resolver-template-v1",
3
+ templateId: "generic-spreadsheet",
4
+ label: "Generic spreadsheet / table feed",
5
+ connectorKind: "http",
6
+ capabilities: ["fetchRecords"],
7
+ apiRegistryDefaults: {
8
+ integrationId: "generic-spreadsheet",
9
+ method: "GET"
10
+ },
11
+ dataSourceDefaults: {
12
+ objectType: "data-source",
13
+ binding: { sourceStorage: "workspace-source-records" }
14
+ },
15
+ configSchema: [
16
+ { name: "baseUrl", label: "Export URL", type: "url", required: true },
17
+ { name: "authRef", label: "Auth ref", type: "secretRef", required: false }
18
+ ],
19
+ supportedLanes: ["data-source"]
20
+ };
21
+
22
+ export default template;
@@ -0,0 +1,22 @@
1
+ const template = {
2
+ schemaVersion: "growthub-resolver-template-v1",
3
+ templateId: "mcp-tool",
4
+ label: "MCP tool bridge",
5
+ connectorKind: "mcp",
6
+ capabilities: ["listEntities", "fetchRecords", "runAction"],
7
+ apiRegistryDefaults: {
8
+ integrationId: "mcp-local-bridge",
9
+ method: "POST"
10
+ },
11
+ dataSourceDefaults: {
12
+ objectType: "data-source",
13
+ binding: { sourceStorage: "workspace-source-records" }
14
+ },
15
+ configSchema: [
16
+ { name: "baseUrl", label: "MCP endpoint", type: "url", required: true },
17
+ { name: "authRef", label: "Auth ref", type: "secretRef", required: false }
18
+ ],
19
+ supportedLanes: ["data-source", "sandbox-local"]
20
+ };
21
+
22
+ export default template;
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Resolver template registry — operator-facing seeds for API Registry / Data Source rows.
3
+ *
4
+ * @typedef {Object} ResolverTemplate
5
+ * @property {"growthub-resolver-template-v1"} schemaVersion
6
+ * @property {string} templateId
7
+ * @property {string} label
8
+ * @property {"http"|"mcp"|"chrome"|"tool"|"custom"} connectorKind
9
+ * @property {Array<"listEntities"|"fetchRecords"|"runAction">} capabilities
10
+ * @property {Object} apiRegistryDefaults
11
+ * @property {string} apiRegistryDefaults.integrationId
12
+ * @property {string} [apiRegistryDefaults.authRef]
13
+ * @property {string} [apiRegistryDefaults.baseUrl]
14
+ * @property {string} [apiRegistryDefaults.endpoint]
15
+ * @property {"GET"|"POST"|"PUT"|"PATCH"|"DELETE"} [apiRegistryDefaults.method]
16
+ * @property {{ objectType: "data-source", binding: { sourceStorage: string } }} [dataSourceDefaults]
17
+ * @property {Array<{ name: string, label: string, type: string, required?: boolean }>} configSchema
18
+ * @property {Array<"data-source"|"sandbox-local"|"sandbox-serverless">} supportedLanes
19
+ */
20
+
21
+ import customHttp from "./custom-http.js";
22
+ import webhook from "./webhook.js";
23
+ import mcpTool from "./mcp-tool.js";
24
+ import chromeBridge from "./chrome-bridge.js";
25
+ import genericCrm from "./generic-crm.js";
26
+ import genericSpreadsheet from "./generic-spreadsheet.js";
27
+ import genericProjectManagement from "./generic-project-management.js";
28
+ import genericCommerce from "./generic-commerce.js";
29
+
30
+ const ALL = [
31
+ customHttp,
32
+ webhook,
33
+ mcpTool,
34
+ chromeBridge,
35
+ genericCrm,
36
+ genericSpreadsheet,
37
+ genericProjectManagement,
38
+ genericCommerce
39
+ ];
40
+
41
+ function listResolverTemplates() {
42
+ return ALL.map((t) => ({ ...t }));
43
+ }
44
+
45
+ function getResolverTemplate(templateId) {
46
+ const id = String(templateId || "").trim();
47
+ return ALL.find((t) => t.templateId === id) || null;
48
+ }
49
+
50
+ export { listResolverTemplates, getResolverTemplate };
@@ -0,0 +1,22 @@
1
+ const template = {
2
+ schemaVersion: "growthub-resolver-template-v1",
3
+ templateId: "webhook",
4
+ label: "Inbound webhook",
5
+ connectorKind: "http",
6
+ capabilities: ["fetchRecords"],
7
+ apiRegistryDefaults: {
8
+ integrationId: "webhook-ingest",
9
+ method: "POST"
10
+ },
11
+ dataSourceDefaults: {
12
+ objectType: "data-source",
13
+ binding: { sourceStorage: "workspace-source-records" }
14
+ },
15
+ configSchema: [
16
+ { name: "endpoint", label: "Webhook path", type: "text", required: true },
17
+ { name: "authRef", label: "Signing secret ref", type: "secretRef", required: false }
18
+ ],
19
+ supportedLanes: ["data-source", "sandbox-serverless"]
20
+ };
21
+
22
+ export default template;
@@ -0,0 +1,133 @@
1
+ /**
2
+ * Server-side orchestration for POST /api/workspace/reference-options.
3
+ * Dispatches: workspace-rows (default), source-records sidecar, or resolver listEntities.
4
+ */
5
+
6
+ import { readAdapterConfig } from "@/lib/adapters/env";
7
+ import { listGovernedWorkspaceIntegrations } from "@/lib/adapters/integrations";
8
+ import { loadAllResolvers } from "@/lib/adapters/integrations/resolver-loader";
9
+ import { getSourceResolver } from "@/lib/adapters/integrations/source-resolver-registry";
10
+ import {
11
+ findRelationForField,
12
+ normalizeManualObjects,
13
+ normalizeReferenceOption,
14
+ resolveLocalReferenceOptions
15
+ } from "@/lib/workspace-data-model";
16
+ import { resolveSourceRecordReferenceOptions } from "./resolvers/source-records.js";
17
+
18
+ function normalizeListEntity(entry) {
19
+ if (!entry || typeof entry !== "object") return null;
20
+ const id = String(entry.id ?? entry.entityType ?? "").trim();
21
+ if (!id) return null;
22
+ return normalizeReferenceOption({
23
+ value: id,
24
+ label: String(entry.label ?? id).trim() || id,
25
+ secondaryLabel: entry.secondaryLabel ? String(entry.secondaryLabel) : undefined,
26
+ source: "resolver",
27
+ provider: typeof entry.provider === "string" ? entry.provider : undefined,
28
+ metadata: entry.meta && typeof entry.meta === "object" ? entry.meta : undefined
29
+ });
30
+ }
31
+
32
+ async function collectReferenceOptions(workspaceConfig, parsed) {
33
+ const { objectId, field, query, cursor, limit, context } = parsed;
34
+ const objects = normalizeManualObjects(workspaceConfig);
35
+ const objectItem = objects.find((o) => o.id === objectId);
36
+ if (!objectItem) {
37
+ return {
38
+ ok: false,
39
+ options: [],
40
+ nextCursor: null,
41
+ reason: "unknown-object",
42
+ error: "dataModel object not found"
43
+ };
44
+ }
45
+ const relation = findRelationForField(objectItem, field);
46
+ if (!relation) {
47
+ return {
48
+ ok: false,
49
+ options: [],
50
+ nextCursor: null,
51
+ reason: "unknown-field",
52
+ error: "no relation metadata for this field"
53
+ };
54
+ }
55
+
56
+ if (relation.referenceSource === "source-records") {
57
+ const sourceId =
58
+ typeof relation.sidecarSourceId === "string" && relation.sidecarSourceId.trim()
59
+ ? relation.sidecarSourceId.trim()
60
+ : typeof context.sourceId === "string"
61
+ ? context.sourceId.trim()
62
+ : "";
63
+ const sr = await resolveSourceRecordReferenceOptions(sourceId, { query, limit });
64
+ return { ok: true, options: sr.options, nextCursor: null, reason: sr.reason || null };
65
+ }
66
+
67
+ if (relation.resolver && typeof relation.resolver.integrationId === "string" && relation.resolver.integrationId.trim()) {
68
+ await loadAllResolvers();
69
+ const integId = relation.resolver.integrationId.trim();
70
+ const resolver = getSourceResolver(integId);
71
+ if (!resolver) {
72
+ return {
73
+ ok: true,
74
+ options: [],
75
+ nextCursor: null,
76
+ reason: "no-resolver",
77
+ resolverIntegrationId: integId
78
+ };
79
+ }
80
+ if (typeof resolver.listEntities !== "function") {
81
+ return { ok: true, options: [], nextCursor: null, reason: "no-list-entities" };
82
+ }
83
+ const adapterConfig = readAdapterConfig();
84
+ let connection = null;
85
+ try {
86
+ const integrations = await listGovernedWorkspaceIntegrations();
87
+ connection =
88
+ integrations.find((i) => i.provider === integId || i.id === integId) || null;
89
+ } catch {
90
+ /* non-fatal */
91
+ }
92
+ let entities;
93
+ try {
94
+ entities = await resolver.listEntities(adapterConfig, connection, {
95
+ query,
96
+ cursor,
97
+ limit,
98
+ context,
99
+ field,
100
+ objectId
101
+ });
102
+ } catch (err) {
103
+ return {
104
+ ok: false,
105
+ options: [],
106
+ nextCursor: null,
107
+ reason: "list-error",
108
+ error: err?.message || "listEntities failed"
109
+ };
110
+ }
111
+ const raw = Array.isArray(entities) ? entities : [];
112
+ const options = raw.map(normalizeListEntity).filter(Boolean);
113
+ return { ok: true, options: options.slice(0, limit), nextCursor: null, reason: null };
114
+ }
115
+
116
+ const local = resolveLocalReferenceOptions(workspaceConfig, {
117
+ objectId,
118
+ field,
119
+ query,
120
+ cursor,
121
+ limit,
122
+ relation
123
+ });
124
+ return {
125
+ ok: true,
126
+ options: local.options,
127
+ nextCursor: local.nextCursor,
128
+ reason: local.reason,
129
+ total: local.total
130
+ };
131
+ }
132
+
133
+ export { collectReferenceOptions };