@growthub/cli 0.13.8 → 0.14.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 (32) hide show
  1. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/api/workspace/codex-sites/route.js +13 -0
  2. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/api/workspace/env-status/route.js +31 -0
  3. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/api/workspace/helper/apply/route.js +130 -5
  4. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/components/WorkspaceActivationPanel.jsx +17 -1
  5. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/components/WorkspaceHelperSetupModal.jsx +5 -2
  6. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/ApiRegistryCreationCockpit.jsx +200 -0
  7. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/DataModelShell.jsx +501 -5
  8. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/HelperSidecar.jsx +75 -55
  9. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/ReferencePicker.jsx +2 -2
  10. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/globals.css +215 -13
  11. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/settings/apps/codex-sites-data-model-card.jsx +81 -0
  12. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/settings/apps/page.jsx +31 -14
  13. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/settings/apps/settings-accordion-section.jsx +50 -0
  14. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/workflows/WorkflowSurface.jsx +176 -5
  15. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/workspace-builder.jsx +137 -5
  16. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/adapters/integrations/resolver-loader.js +2 -4
  17. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/api-registry-creation-flow.js +317 -0
  18. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/api-response-profile.js +207 -0
  19. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/codex-sites-local-state.js +139 -0
  20. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/codex-sites-workspace-adapter.js +156 -0
  21. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/creation-error-recovery.js +103 -0
  22. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/env-status.js +100 -0
  23. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/orchestration-graph.js +63 -0
  24. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/sandbox-serverless-flow.js +215 -0
  25. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/server-resolver-write.js +67 -0
  26. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/serverless-upgrade.js +89 -0
  27. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/workspace-activation.js +11 -4
  28. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/workspace-data-model.js +8 -1
  29. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/workspace-helper.js +7 -1
  30. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/workspace-resolver-proposal.js +200 -0
  31. package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/package.json +1 -1
  32. package/package.json +1 -1
@@ -69,6 +69,7 @@ function ToolCallCard({ proposal, content, onOpenArtifact }) {
69
69
  rationale: proposal?.rationale,
70
70
  confidence: proposal?.confidence,
71
71
  };
72
+ const showJson = meta.payload != null || meta.affectedField || meta.rationale;
72
73
  return (
73
74
  <div className="dm-helper-toolcall" data-toolcall-type={proposal?.type}>
74
75
  <button
@@ -89,9 +90,11 @@ function ToolCallCard({ proposal, content, onOpenArtifact }) {
89
90
  {open && (
90
91
  <div className="dm-helper-toolcall-body">
91
92
  {content && <div className="dm-helper-toolcall-content">{content}</div>}
92
- <pre className="dm-helper-toolcall-json">
93
- {JSON.stringify(meta, null, 2)}
94
- </pre>
93
+ {showJson && (
94
+ <pre className="dm-helper-toolcall-json">
95
+ {JSON.stringify(meta, null, 2)}
96
+ </pre>
97
+ )}
95
98
  </div>
96
99
  )}
97
100
  {canNavigate && (
@@ -108,6 +111,55 @@ function ToolCallCard({ proposal, content, onOpenArtifact }) {
108
111
  );
109
112
  }
110
113
 
114
+ function formatRunDuration(ms) {
115
+ const value = Number(ms);
116
+ if (!Number.isFinite(value) || value < 0) return "";
117
+ const totalSeconds = Math.max(0, Math.round(value / 1000));
118
+ const minutes = Math.floor(totalSeconds / 60);
119
+ const seconds = totalSeconds % 60;
120
+ if (minutes <= 0) return `${seconds}s`;
121
+ return `${minutes}m ${String(seconds).padStart(2, "0")}s`;
122
+ }
123
+
124
+ function ProposalReviewCard({ proposal, checked, disabled, onCheckedChange, onKeyDown }) {
125
+ const [open, setOpen] = useState(false);
126
+ const summary = summarizePayload(proposal);
127
+ return (
128
+ <div className="dm-helper-toolcall" data-proposal-item="" tabIndex={0} onKeyDown={onKeyDown}>
129
+ <div className="dm-helper-toolcall-row dm-helper-proposal-card-row">
130
+ <input
131
+ type="checkbox"
132
+ checked={!!checked}
133
+ onChange={(e) => onCheckedChange(e.target.checked)}
134
+ disabled={disabled}
135
+ data-proposal-accept=""
136
+ aria-label={`Select ${proposal.type}`}
137
+ />
138
+ <button
139
+ type="button"
140
+ className="dm-helper-proposal-card-toggle"
141
+ onClick={() => setOpen((v) => !v)}
142
+ aria-expanded={open}
143
+ >
144
+ <span className="dm-helper-toolcall-title">{proposal.type}</span>
145
+ <span className="dm-helper-proposal-field">→ {proposal.affectedField}</span>
146
+ <ChevronDown
147
+ size={14}
148
+ className={`dm-helper-toolcall-chevron${open ? " is-open" : ""}`}
149
+ aria-hidden="true"
150
+ />
151
+ </button>
152
+ </div>
153
+ {open && (
154
+ <div className="dm-helper-toolcall-body">
155
+ {summary && <p className="dm-helper-proposal-payload" data-proposal-payload="">{summary}</p>}
156
+ {proposal.rationale && <p className="dm-helper-proposal-rationale">{proposal.rationale}</p>}
157
+ </div>
158
+ )}
159
+ </div>
160
+ );
161
+ }
162
+
111
163
  // Pair a system apply-receipt message with the actual proposal payload
112
164
  // it confirms. The applyResult (rehydrated from row.lastApplied at thread
113
165
  // load time) carries the typed payloads keyed in order — we walk the
@@ -747,6 +799,11 @@ export function HelperSidecar({ open, onClose, workspaceConfig, initialIntent, i
747
799
  const acceptedCount = Object.values(accepted).filter(Boolean).length;
748
800
  const skippedCount = applyResult?.skipped?.length || 0;
749
801
  const hasProposals = result && (result.proposals || []).length > 0;
802
+ const visibleWarnings = (result?.warnings || []).filter((warning) => {
803
+ const text = String(warning || "");
804
+ return !/transcript does not include the actual registry row id or lastResponse payload/i.test(text)
805
+ && !/No credentials or env values should be stored/i.test(text);
806
+ });
750
807
 
751
808
  // Thread is "active" the moment the user has sent at least one message,
752
809
  // OR we have rehydrated a prior thread row. Pills only show on the
@@ -916,18 +973,9 @@ export function HelperSidecar({ open, onClose, workspaceConfig, initialIntent, i
916
973
  {/* Proposals */}
917
974
  {result && (
918
975
  <div className="dm-helper-result">
919
- <div className="dm-helper-summary">
920
- <span>{result.summary}</span>
921
- </div>
922
-
923
- {(result.warnings || []).length > 0 && (
924
- <div className="dm-helper-warnings">
925
- {result.warnings.map((w, i) => (
926
- <div key={i} className="dm-helper-warning">
927
- <AlertCircle size={12} />
928
- <span>{w}</span>
929
- </div>
930
- ))}
976
+ {!threadActive && result.summary && (
977
+ <div className="dm-helper-summary">
978
+ <span>{result.summary}</span>
931
979
  </div>
932
980
  )}
933
981
 
@@ -943,42 +991,17 @@ export function HelperSidecar({ open, onClose, workspaceConfig, initialIntent, i
943
991
  aria-label="Proposals"
944
992
  >
945
993
  {result.proposals.map((proposal, i) => {
946
- const summary = summarizePayload(proposal);
947
- const conf = typeof proposal.confidence === "number" ? Math.round(proposal.confidence * 100) : null;
948
994
  return (
949
- <label
995
+ <ProposalReviewCard
950
996
  key={i}
951
- className={`dm-helper-proposal${accepted[i] ? " accepted" : ""}`}
952
- data-proposal-item=""
953
- tabIndex={0}
997
+ proposal={proposal}
998
+ checked={accepted[i]}
999
+ disabled={applying}
1000
+ onCheckedChange={(checked) =>
1001
+ setAccepted((prev) => ({ ...prev, [i]: checked }))
1002
+ }
954
1003
  onKeyDown={(e) => handleProposalKeyDown(e, i)}
955
- >
956
- <input
957
- type="checkbox"
958
- checked={!!accepted[i]}
959
- onChange={(e) =>
960
- setAccepted((prev) => ({ ...prev, [i]: e.target.checked }))
961
- }
962
- disabled={applying}
963
- data-proposal-accept=""
964
- tabIndex={-1}
965
- />
966
- <div className="dm-helper-proposal-body">
967
- <div className="dm-helper-proposal-row">
968
- <span className="dm-helper-proposal-type">{proposal.type}</span>
969
- <span className="dm-helper-proposal-field">→ {proposal.affectedField}</span>
970
- {conf !== null && (
971
- <span className="dm-helper-proposal-confidence" data-proposal-confidence={conf}>
972
- {conf}%
973
- </span>
974
- )}
975
- </div>
976
- {summary && (
977
- <p className="dm-helper-proposal-payload" data-proposal-payload="">{summary}</p>
978
- )}
979
- <p className="dm-helper-proposal-rationale">{proposal.rationale}</p>
980
- </div>
981
- </label>
1004
+ />
982
1005
  );
983
1006
  })}
984
1007
  </div>
@@ -1055,13 +1078,10 @@ export function HelperSidecar({ open, onClose, workspaceConfig, initialIntent, i
1055
1078
  )}
1056
1079
 
1057
1080
  {result.receipts && (
1058
- <p className="dm-field-hint" style={{ marginTop: 8 }} data-helper-receipt="">
1059
- Run: {result.receipts.model} · confidence{" "}
1060
- {typeof result.receipts.confidence === "number"
1061
- ? `${Math.round(result.receipts.confidence * 100)}%`
1062
- : "n/a"}{" "}
1063
- · {result.receipts.latencyMs}ms
1064
- </p>
1081
+ <div className="dm-helper-run-meta" data-helper-receipt="">
1082
+ <span>{result.receipts.model || "run"}</span>
1083
+ <span>{formatRunDuration(result.receipts.latencyMs)}</span>
1084
+ </div>
1065
1085
  )}
1066
1086
  </div>
1067
1087
  )}
@@ -202,9 +202,9 @@ export function ReferencePicker({
202
202
  <div className="dm-reference-picker">
203
203
  {error && <p className="dm-field-error" style={{ fontSize: 11 }}>{error}</p>}
204
204
  {showRepair && (
205
- <p className="dm-validation-banner" style={{ fontSize: 11, marginBottom: 6 }}>
205
+ <p className="dm-reference-picker-warning">
206
206
  <AlertTriangle size={12} aria-hidden />
207
- <span>Selected reference is missing or filtered out. Pick a new row or adjust API Registry status.</span>
207
+ <span>Missing reference. Pick a row or test the API Registry.</span>
208
208
  </p>
209
209
  )}
210
210
  <SearchableSelect
@@ -857,7 +857,8 @@ body.workspace-rail-collapsed .workspace-builder.dm-workflow-page {
857
857
  .workspace-dashboard-title {
858
858
  min-width: 0;
859
859
  }
860
- .workspace-dashboard-title button {
860
+ .workspace-dashboard-title button,
861
+ .workspace-dashboard-title a {
861
862
  min-width: 0;
862
863
  display: inline-flex;
863
864
  align-items: center;
@@ -870,6 +871,7 @@ body.workspace-rail-collapsed .workspace-builder.dm-workflow-page {
870
871
  overflow: hidden;
871
872
  padding: 0;
872
873
  text-align: left;
874
+ text-decoration: none;
873
875
  text-overflow: ellipsis;
874
876
  white-space: nowrap;
875
877
  }
@@ -953,17 +955,24 @@ body.workspace-rail-collapsed .workspace-builder.dm-workflow-page {
953
955
  background: #ffffff;
954
956
  box-shadow: 0 12px 30px rgba(15, 23, 42, .16);
955
957
  }
956
- .workspace-row-action-menu button {
958
+ .workspace-row-action-menu button,
959
+ .workspace-row-action-menu a {
957
960
  width: 100%;
958
961
  min-height: 30px;
962
+ display: inline-flex;
963
+ align-items: center;
959
964
  justify-content: flex-start;
960
965
  border: 0;
961
966
  border-radius: 6px;
962
967
  background: transparent;
963
968
  color: #344054;
969
+ font: inherit;
970
+ font-size: 12px;
964
971
  padding: 0 9px;
972
+ text-decoration: none;
965
973
  }
966
- .workspace-row-action-menu button:hover {
974
+ .workspace-row-action-menu button:hover,
975
+ .workspace-row-action-menu a:hover {
967
976
  background: #f5f5f5;
968
977
  border-color: transparent;
969
978
  }
@@ -3079,6 +3088,55 @@ body.workspace-rail-collapsed .workspace-builder.dm-workflow-page {
3079
3088
  border-top: 1px solid #eeeeec;
3080
3089
  padding: 20px 0;
3081
3090
  }
3091
+ .workspace-settings-accordion {
3092
+ gap: 12px;
3093
+ }
3094
+ .workspace-settings-accordion-trigger {
3095
+ width: 100%;
3096
+ min-height: 34px;
3097
+ display: flex;
3098
+ align-items: center;
3099
+ justify-content: space-between;
3100
+ gap: 14px;
3101
+ border: 0;
3102
+ background: transparent;
3103
+ color: inherit;
3104
+ cursor: pointer;
3105
+ font: inherit;
3106
+ padding: 0;
3107
+ text-align: left;
3108
+ }
3109
+ .workspace-settings-accordion-trigger > span {
3110
+ min-width: 0;
3111
+ display: grid;
3112
+ gap: 3px;
3113
+ }
3114
+ .workspace-settings-accordion-trigger h3 {
3115
+ margin: 0;
3116
+ }
3117
+ .workspace-settings-accordion-trigger em {
3118
+ min-width: 0;
3119
+ display: block;
3120
+ color: #888;
3121
+ font-size: 12px;
3122
+ font-style: normal;
3123
+ overflow: hidden;
3124
+ text-overflow: ellipsis;
3125
+ white-space: nowrap;
3126
+ }
3127
+ .workspace-settings-accordion-trigger svg {
3128
+ flex: 0 0 auto;
3129
+ color: #777;
3130
+ transition: transform .16s ease;
3131
+ }
3132
+ .workspace-settings-accordion.is-open .workspace-settings-accordion-trigger svg {
3133
+ transform: rotate(180deg);
3134
+ }
3135
+ .workspace-settings-accordion-body {
3136
+ min-height: 0;
3137
+ display: grid;
3138
+ gap: 12px;
3139
+ }
3082
3140
  .workspace-apps-linkage-section {
3083
3141
  flex: 0 0 auto;
3084
3142
  padding-bottom: 16px;
@@ -3089,6 +3147,9 @@ body.workspace-rail-collapsed .workspace-builder.dm-workflow-page {
3089
3147
  grid-template-rows: auto minmax(0, 1fr);
3090
3148
  padding-bottom: 0;
3091
3149
  }
3150
+ .workspace-apps-list-section .workspace-settings-accordion-body {
3151
+ min-height: 0;
3152
+ }
3092
3153
  .workspace-settings-section:first-of-type {
3093
3154
  border-top: 0;
3094
3155
  padding-top: 0;
@@ -3577,6 +3638,52 @@ body.workspace-rail-collapsed .workspace-builder.dm-workflow-page {
3577
3638
  font-size: 12px;
3578
3639
  line-height: 1.4;
3579
3640
  }
3641
+ .workspace-settings-action {
3642
+ min-height: 32px;
3643
+ display: inline-flex;
3644
+ align-items: center;
3645
+ gap: 6px;
3646
+ border: 1px solid #dedede;
3647
+ border-radius: 6px;
3648
+ background: #fff;
3649
+ color: #333;
3650
+ font: inherit;
3651
+ font-size: 12px;
3652
+ padding: 0 10px;
3653
+ }
3654
+ .workspace-settings-codex-sites-list {
3655
+ display: grid;
3656
+ gap: 6px;
3657
+ padding: 0 14px 0;
3658
+ }
3659
+ .workspace-settings-codex-sites-list a,
3660
+ .workspace-settings-codex-sites-list button {
3661
+ min-height: 34px;
3662
+ display: grid;
3663
+ grid-template-columns: minmax(0, 1fr) minmax(0, .8fr) auto;
3664
+ gap: 10px;
3665
+ align-items: center;
3666
+ border: 1px solid #ececea;
3667
+ border-radius: 6px;
3668
+ background: #fff;
3669
+ color: #333;
3670
+ font: inherit;
3671
+ font-size: 12px;
3672
+ text-align: left;
3673
+ text-decoration: none;
3674
+ padding: 0 10px;
3675
+ }
3676
+ .workspace-settings-codex-sites-list span,
3677
+ .workspace-settings-codex-sites-list em {
3678
+ min-width: 0;
3679
+ overflow: hidden;
3680
+ text-overflow: ellipsis;
3681
+ white-space: nowrap;
3682
+ }
3683
+ .workspace-settings-codex-sites-list em {
3684
+ color: #777;
3685
+ font-style: normal;
3686
+ }
3580
3687
 
3581
3688
  /* Source picker */
3582
3689
  .workspace-source-list {
@@ -4639,7 +4746,7 @@ body.workspace-rail-collapsed .workspace-builder.dm-workflow-page {
4639
4746
  .dm-detail-v2-title h2 { margin: 0; font-size: 15px; font-weight: 600; color: #111827; flex: 1; }
4640
4747
  .dm-detail-v2-meta { display: flex; align-items: center; gap: 10px; padding-left: 21px; font-size: 12px; color: #9ca3af; }
4641
4748
  .dm-detail-v2-meta code { font-size: 11px; color: #6b7280; background: #f3f4f6; border-radius: 4px; padding: 2px 6px; }
4642
- .dm-detail-v3 { border: 1px solid #e5e7eb; border-radius: 12px; background: #fff; overflow: hidden; }
4749
+ .dm-detail-v3 { border: 1px solid #e5e7eb; border-radius: 12px; background: #fff; overflow: visible; }
4643
4750
  .dm-picker { position: relative; min-width: 280px; }
4644
4751
  .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; }
4645
4752
  .dm-picker.open .dm-picker-trigger { border-color: #94a3b8; box-shadow: 0 0 0 4px rgba(148,163,184,.14); }
@@ -4680,6 +4787,7 @@ body.workspace-rail-collapsed .workspace-builder.dm-workflow-page {
4680
4787
  .workspace-toolbar-actions .dm-picker-popover {
4681
4788
  left: auto;
4682
4789
  right: 0;
4790
+ z-index: 220;
4683
4791
  }
4684
4792
 
4685
4793
  /* Active-object title block in the workspace toolbar — replaces the
@@ -4729,13 +4837,13 @@ body.workspace-rail-collapsed .workspace-builder.dm-workflow-page {
4729
4837
  font-weight: 400;
4730
4838
  color: #6b7280;
4731
4839
  }
4732
- .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; }
4840
+ .dm-picker-popover { position: absolute; top: calc(100% + 8px); left: 0; z-index: 220; width: min(360px, calc(100vw - 32px)); max-height: min(520px, calc(100vh - 120px)); 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-y: auto; overflow-x: visible; }
4733
4841
  .dm-picker-tabs { display: inline-flex; gap: 6px; padding: 4px; border-radius: 999px; background: #f8fafc; }
4734
4842
  .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; }
4735
4843
  .dm-picker-tabs button.active { background: #fff; color: #111827; box-shadow: 0 1px 2px rgba(15,23,42,.08); }
4736
4844
  .dm-picker-section { display: grid; gap: 8px; }
4737
4845
  .dm-picker-section > p { margin: 0; font-size: 11px; font-weight: 700; color: #94a3b8; text-transform: uppercase; letter-spacing: .06em; }
4738
- .dm-picker-scroll { display: grid; gap: 4px; max-height: 220px; overflow-y: auto; padding-right: 10px; scrollbar-gutter: stable; }
4846
+ .dm-picker-scroll { display: grid; gap: 4px; max-height: none; overflow: visible; padding-right: 10px; scrollbar-gutter: stable; }
4739
4847
  .dm-picker-item { position: relative; display: grid; grid-template-columns: minmax(0,1fr) auto; gap: 8px; align-items: center; }
4740
4848
  .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; }
4741
4849
  .dm-picker-row:hover, .dm-picker-item.active .dm-picker-row { background: #f1f5f9; color: #111827; }
@@ -4746,7 +4854,7 @@ body.workspace-rail-collapsed .workspace-builder.dm-workflow-page {
4746
4854
  .dm-picker-icon-btn:hover { color: #111827; border-color: #cbd5e1; }
4747
4855
  .dm-picker-icon-btn.danger:hover { color: #b91c1c; border-color: #fecaca; background: #fef2f2; }
4748
4856
  .dm-picker-lock { margin-left: auto; color: #94a3b8; }
4749
- .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); }
4857
+ .dm-picker-menu { position: absolute; top: calc(100% + 6px); right: 0; z-index: 240; 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); }
4750
4858
  .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; }
4751
4859
  .dm-picker-menu button:hover { background: #f1f5f9; color: #111827; }
4752
4860
  .dm-picker-menu button.danger:hover { background: #fef2f2; color: #b91c1c; }
@@ -4922,7 +5030,7 @@ body.workspace-rail-collapsed .workspace-builder.dm-workflow-page {
4922
5030
  .dm-db-status.warn { border-color: #fde68a; background: #fffbeb; color: #92400e; }
4923
5031
  .dm-db-status.warn span { background: #f59e0b; }
4924
5032
  .dm-record-backdrop { position: fixed; inset: 0; z-index: 80; background: rgba(15,23,42,.12); }
4925
- .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); }
5033
+ .dm-record-drawer { position: fixed; top: 0; right: 0; bottom: 0; z-index: 81; display: flex; flex-direction: column; min-height: 0; width: min(440px, 100vw); background: #fff; border-left: 1px solid #dfe3e8; box-shadow: -10px 0 34px rgba(15,23,42,.16); overflow: hidden; }
4926
5034
  .dm-record-drawer-wide { width: min(780px, 100vw); }
4927
5035
  .dm-api-action-card {
4928
5036
  display: grid;
@@ -4962,6 +5070,59 @@ body.workspace-rail-collapsed .workspace-builder.dm-workflow-page {
4962
5070
  .dm-api-action-card-body h3 { margin: 0; font-size: 14px; font-weight: 650; color: #111827; }
4963
5071
  .dm-api-action-card-body p { margin: 0; font-size: 12px; line-height: 1.45; color: #4b5563; }
4964
5072
  .dm-api-action-card-cta { flex-shrink: 0; align-self: center; }
5073
+
5074
+ /* Governed creation cockpit — vertical journey inside the api-registry drawer.
5075
+ Overrides the action-card 3-col grid and reuses the workspace's own status
5076
+ chip (.dm-db-status) + dm-btn-* buttons. No new colors, no new primitive. */
5077
+ .dm-cockpit { display: block; grid-template-columns: none; transition: box-shadow .14s, background .14s; }
5078
+ .dm-cockpit.is-collapsed { padding-bottom: 12px; }
5079
+ .dm-cockpit-head { width: 100%; display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; border: 0; background: transparent; padding: 0; text-align: left; cursor: pointer; }
5080
+ .dm-cockpit-head:hover .dm-api-action-card-body h3 { color: #000; }
5081
+ .dm-cockpit-head:focus-visible { outline: 2px solid #bfdbfe; outline-offset: 4px; border-radius: 6px; }
5082
+ .dm-cockpit-count { flex-shrink: 0; font-size: 11px; font-weight: 700; color: #94a3b8; font-variant-numeric: tabular-nums; padding-top: 2px; }
5083
+ .dm-cockpit-steps { list-style: none; margin: 10px 0 0; padding: 0; }
5084
+ .dm-cockpit-step { display: grid; grid-template-columns: auto 1fr auto; gap: 10px; align-items: start; padding: 10px 0; border-top: 1px solid #edf0f3; }
5085
+ .dm-cockpit-step:first-child { border-top: 0; }
5086
+ .dm-cockpit-step-chip { flex-shrink: 0; align-self: start; margin-top: 1px; min-width: 64px; justify-content: center; }
5087
+ .dm-cockpit-step-body { min-width: 0; display: grid; gap: 2px; }
5088
+ .dm-cockpit-step-label { margin: 0; font-size: 13px; font-weight: 600; color: #111827; }
5089
+ .dm-cockpit-step-desc { margin: 0; font-size: 12px; line-height: 1.45; color: #64748b; }
5090
+ .dm-cockpit-step-hint { margin: 0; font-size: 11px; line-height: 1.4; color: #94a3b8; }
5091
+ .dm-cockpit-step button { flex-shrink: 0; align-self: center; }
5092
+ .dm-cockpit-step-muted .dm-cockpit-step-label,
5093
+ .dm-cockpit-step-muted .dm-cockpit-step-desc { color: #94a3b8; }
5094
+ .dm-cockpit-step-next { background: #f8fafc; margin: 0 -14px; padding-left: 14px; padding-right: 14px; border-radius: 6px; }
5095
+ .dm-cockpit-shape { margin-top: 12px; padding-top: 12px; border-top: 1px solid #edf0f3; display: grid; gap: 6px; }
5096
+ .dm-cockpit-shape-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
5097
+ .dm-cockpit-shape-head .dm-api-action-card-eyebrow { margin: 0; }
5098
+ .dm-cockpit-fields { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 2px; }
5099
+ .dm-cockpit-field { display: inline-flex; align-items: center; gap: 5px; font-size: 11px; color: #475569; background: #f8fafc; border: 1px solid #e2e8f0; border-radius: 6px; padding: 2px 8px; }
5100
+ .dm-cockpit-field b { font-weight: 700; color: #94a3b8; text-transform: uppercase; font-size: 10px; letter-spacing: .03em; }
5101
+ .dm-cockpit-receipts { margin-top: 12px; padding-top: 12px; border-top: 1px solid #edf0f3; }
5102
+ .dm-cockpit-receipts ul { list-style: none; margin: 6px 0 0; padding: 0; display: grid; gap: 6px; }
5103
+ .dm-cockpit-receipt { display: grid; grid-template-columns: auto 1fr; gap: 8px; align-items: start; }
5104
+ .dm-cockpit-receipt-chip { align-self: start; }
5105
+ .dm-cockpit-receipt-text { font-size: 12px; color: #64748b; line-height: 1.4; }
5106
+
5107
+ /* Serverless upgrade — toolbar button, one-time nudge, and cockpit panel. */
5108
+ .dm-workflow-upgrade-btn { position: relative; }
5109
+ .dm-workflow-upgrade-btn.is-serverless { color: #166534; }
5110
+ .dm-workflow-upgrade-btn.is-pulse { color: #111827; }
5111
+ .dm-workflow-upgrade-btn[data-tooltip]:hover::after {
5112
+ content: attr(data-tooltip); position: absolute; top: 100%; right: 0; margin-top: 6px;
5113
+ white-space: nowrap; background: #111827; color: #fff; font-size: 11px; font-weight: 500;
5114
+ padding: 5px 8px; border-radius: 6px; z-index: 40; box-shadow: 0 4px 12px rgba(15,23,42,.18);
5115
+ }
5116
+ .dm-workflow-upgrade-nudge { align-items: center; justify-content: space-between; gap: 16px; padding: 10px 16px; }
5117
+ .dm-workflow-upgrade-nudge > div:first-child { min-width: 0; padding-right: 10px; }
5118
+ .dm-workflow-upgrade-nudge strong,
5119
+ .dm-workflow-upgrade-nudge span { line-height: 1.35; }
5120
+ .dm-workflow-upgrade-nudge-actions { display: inline-flex; align-items: center; gap: 8px; }
5121
+ .dm-workflow-upgrade-panel { width: auto; margin: 12px 8px 16px; border: 1px solid #e5e7eb; border-radius: 8px; background: #fff; box-shadow: 0 2px 8px rgba(15,23,42,.06); }
5122
+ .dm-workflow-upgrade-panel-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; padding: 10px 14px 0; }
5123
+ .dm-workflow-upgrade-panel-head .dm-api-action-card-eyebrow { margin: 0; }
5124
+ .dm-workflow-upgrade-panel .dm-api-action-card { margin: 8px; box-shadow: none; }
5125
+
4965
5126
  .dm-api-review-banner {
4966
5127
  display: grid;
4967
5128
  grid-template-columns: auto 1fr auto;
@@ -5115,6 +5276,7 @@ body.workspace-rail-collapsed .workspace-builder.dm-workflow-page {
5115
5276
  display: flex;
5116
5277
  align-items: center;
5117
5278
  gap: 6px;
5279
+ padding-left: 16px;
5118
5280
  min-width: max-content;
5119
5281
  flex: 1 0 auto;
5120
5282
  color: #8b8b91;
@@ -5168,6 +5330,7 @@ body.workspace-rail-collapsed .workspace-builder.dm-workflow-page {
5168
5330
  display: flex;
5169
5331
  align-items: center;
5170
5332
  gap: 6px;
5333
+ padding-right: 16px;
5171
5334
  flex: 0 1 auto;
5172
5335
  min-width: 0;
5173
5336
  max-width: 58vw;
@@ -5917,6 +6080,7 @@ body.workspace-rail-collapsed .workspace-builder.dm-workflow-page {
5917
6080
  .dm-drawer-hidden-list { display: flex; flex-wrap: wrap; gap: 8px; }
5918
6081
  .dm-record-drawer-head p { margin: 0 0 3px; color: #94a3b8; font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .05em; }
5919
6082
  .dm-record-drawer-head h2 { margin: 0; color: #111827; font-size: 16px; font-weight: 650; }
6083
+ .dm-record-scroll { flex: 1 1 auto; min-height: 0; overflow-y: auto; overflow-x: hidden; overscroll-behavior: contain; scrollbar-gutter: stable; padding: 0 0 28px; }
5920
6084
  .dm-record-testbar { display: flex; align-items: center; gap: 8px; padding: 10px 18px; border-bottom: 1px solid #edf0f3; background: #fbfdff; }
5921
6085
  .dm-record-testbar > span:last-child { min-width: 0; color: #64748b; font-size: 12px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
5922
6086
  .dm-record-testbar[data-panel="sandbox-agent-auth"] {
@@ -6006,7 +6170,7 @@ body.workspace-rail-collapsed .workspace-builder.dm-workflow-page {
6006
6170
  white-space: pre-wrap;
6007
6171
  word-break: break-word;
6008
6172
  }
6009
- .dm-record-fields { display: grid; gap: 8px; padding: 14px 16px 28px; overflow-y: auto; }
6173
+ .dm-record-fields { display: grid; align-content: start; gap: 8px; min-height: 0; padding: 14px 16px 0; }
6010
6174
  .dm-record-field { display: grid; gap: 5px; }
6011
6175
  .dm-record-field span { color: #475569; font-size: 11px; font-weight: 650; }
6012
6176
  .dm-record-field input,
@@ -6029,7 +6193,7 @@ body.workspace-rail-collapsed .workspace-builder.dm-workflow-page {
6029
6193
  .dm-radio-row input[type="radio"], .dm-check-row input[type="checkbox"] { width: 14px; height: 14px; margin: 1px 0 0; padding: 0; box-shadow: none; accent-color: #111827; }
6030
6194
  .dm-radio-row span, .dm-check-row span { color: #1f2937; font-size: 12px; font-weight: 500; }
6031
6195
  .dm-check-row { cursor: pointer; }
6032
- .dm-select { position: relative; width: 100%; min-width: 180px; font-size: 11px; }
6196
+ .dm-select { position: relative; width: 100%; min-width: 0; font-size: 11px; }
6033
6197
  .dm-select-trigger { width: 100%; min-height: 32px; display: flex; align-items: center; justify-content: space-between; gap: 8px; border: 1px solid #cbd5e1; border-radius: 7px; background: #fff; color: #111827; box-shadow: 0 1px 2px rgba(15,23,42,.05); font: inherit; font-size: 11px; padding: 6px 10px; text-align: left; cursor: pointer; transition: border-color .12s, box-shadow .12s, background .12s; }
6034
6198
  .dm-select-trigger:hover:not(:disabled) { border-color: #94a3b8; box-shadow: 0 2px 8px rgba(15,23,42,.08); }
6035
6199
  .dm-select.open .dm-select-trigger { border-color: #64748b; box-shadow: 0 0 0 3px rgba(100,116,139,.12), 0 2px 8px rgba(15,23,42,.08); }
@@ -6054,8 +6218,12 @@ body.workspace-rail-collapsed .workspace-builder.dm-workflow-page {
6054
6218
  .dm-select-pager button { height: 26px; border: 1px solid #e2e8f0; border-radius: 6px; background: #fff; color: #334155; font: inherit; font-size: 12px; padding: 0 8px; cursor: pointer; }
6055
6219
  .dm-select-pager button:disabled { opacity: .45; cursor: not-allowed; }
6056
6220
  .dm-select-pager span { color: #64748b; font-size: 12px; font-weight: 650; }
6057
- .dm-db-grid td:has(.dm-select) { overflow: visible; padding-top: 5px; padding-bottom: 5px; }
6058
- .dm-db-grid td .dm-select { min-width: 230px; }
6221
+ .dm-reference-picker { display: grid; gap: 6px; width: min(240px, 100%); min-width: 0; }
6222
+ .dm-reference-picker-warning { display: inline-flex; align-items: center; gap: 5px; max-width: 100%; margin: 0; padding: 5px 7px; border: 1px solid #fde68a; border-radius: 6px; background: #fffbeb; color: #92400e; font-size: 11px; line-height: 1.25; }
6223
+ .dm-reference-picker-warning svg { flex: 0 0 auto; }
6224
+ .dm-reference-picker-warning span { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
6225
+ .dm-db-grid td:has(.dm-select) { overflow: visible; padding-top: 5px; padding-bottom: 5px; vertical-align: top; }
6226
+ .dm-db-grid td .dm-select { min-width: 0; }
6059
6227
  .dm-json-field { position: relative; }
6060
6228
  .dm-json-field > span { padding-right: 34px; }
6061
6229
  .dm-json-expand { position: absolute; top: 0; right: 0; display: inline-flex; align-items: center; justify-content: center; width: 26px; height: 24px; border: 1px solid #e2e8f0; border-radius: 6px; background: #fff; color: #64748b; box-shadow: 0 1px 2px rgba(15,23,42,.05); opacity: 0; cursor: pointer; transition: opacity .12s, border-color .12s, color .12s, box-shadow .12s; }
@@ -6499,6 +6667,40 @@ body.workspace-rail-collapsed .workspace-builder.dm-workflow-page {
6499
6667
  background: #f5f5f5;
6500
6668
  border-color: #9ca3af;
6501
6669
  }
6670
+ .dm-helper-run-meta {
6671
+ display: flex;
6672
+ align-items: center;
6673
+ justify-content: space-between;
6674
+ gap: 12px;
6675
+ padding-top: 8px;
6676
+ border-top: 1px solid #e5e7eb;
6677
+ color: #6b7280;
6678
+ font-size: 12px;
6679
+ line-height: 1.3;
6680
+ }
6681
+ .dm-helper-proposal-card-row {
6682
+ grid-template-columns: 18px 1fr;
6683
+ }
6684
+ .dm-helper-proposal-card-row input[type=checkbox] {
6685
+ width: 14px;
6686
+ height: 14px;
6687
+ margin: 0;
6688
+ accent-color: #111827;
6689
+ }
6690
+ .dm-helper-proposal-card-toggle {
6691
+ display: grid;
6692
+ grid-template-columns: minmax(0, max-content) minmax(0, 1fr) auto;
6693
+ align-items: center;
6694
+ gap: 6px;
6695
+ width: 100%;
6696
+ padding: 0;
6697
+ border: 0;
6698
+ background: transparent;
6699
+ color: inherit;
6700
+ font: inherit;
6701
+ text-align: left;
6702
+ cursor: pointer;
6703
+ }
6502
6704
 
6503
6705
  .dm-helper-typing {
6504
6706
  display: inline-flex;
@@ -8852,7 +9054,7 @@ body.workspace-rail-collapsed .workspace-builder.workspace-lens-page,
8852
9054
  .workspace-lens-helper-callout-btn { flex-shrink: 0; font-size: 12px; font-weight: 600; color: #111827; background: #ffffff; border: 1px solid #d1d5db; border-radius: 6px; padding: 7px 12px; cursor: pointer; }
8853
9055
  .workspace-lens-helper-callout-btn:hover:not(:disabled) { background: #f3f4f6; }
8854
9056
  .workspace-lens-helper-callout-btn:disabled { opacity: 0.6; cursor: default; }
8855
- .workspace-helper-setup-modal-backdrop { position: fixed; inset: 0; z-index: 1400; display: flex; align-items: center; justify-content: center; padding: 24px; background: rgba(17, 24, 39, 0.42); backdrop-filter: blur(3px); }
9057
+ .workspace-helper-setup-modal-backdrop { position: fixed; inset: 0; z-index: 5100; display: flex; align-items: center; justify-content: center; padding: 24px; background: rgba(17, 24, 39, 0.42); backdrop-filter: blur(3px); }
8856
9058
  .workspace-helper-setup-modal { position: relative; width: min(620px, 100%); max-height: min(720px, calc(100vh - 48px)); overflow: auto; background: #ffffff; border: 1px solid #d1d5db; border-radius: 8px; box-shadow: 0 24px 70px rgba(17, 24, 39, 0.28); padding: 18px; }
8857
9059
  .workspace-helper-setup-modal-close { position: absolute; top: 12px; right: 12px; width: 28px; height: 28px; display: inline-flex; align-items: center; justify-content: center; border: 1px solid #e5e7eb; border-radius: 6px; background: #fff; color: #6b7280; cursor: pointer; }
8858
9060
  .workspace-helper-setup-modal-close:hover { background: #f9fafb; color: #111827; }
@@ -0,0 +1,81 @@
1
+ "use client";
2
+
3
+ import { ExternalLink, Rocket } from "lucide-react";
4
+ import {
5
+ CODEX_SITES_OBJECT_ID,
6
+ ensureCodexSitesDataModel,
7
+ isCodexSiteUrl
8
+ } from "@/lib/codex-sites-workspace-adapter";
9
+ import { SettingsAccordionSection } from "./settings-accordion-section.jsx";
10
+
11
+ function CodexSitesDataModelCard({ apps, dataModel }) {
12
+ const objects = Array.isArray(dataModel?.objects) ? dataModel.objects : [];
13
+ const object = objects.find((item) => item?.id === CODEX_SITES_OBJECT_ID) || null;
14
+ const rows = Array.isArray(object?.rows) ? object.rows : [];
15
+ const liveRows = rows.filter((row) => {
16
+ const status = String(row?.status || "").toLowerCase();
17
+ return isCodexSiteUrl(row?.url) && (status === "live" || status === "active");
18
+ });
19
+ const liveCount = liveRows.length;
20
+ const appCount = new Set(rows.map((row) => row?.app).filter(Boolean)).size;
21
+
22
+ async function openDataModel() {
23
+ if (!object) {
24
+ const nextDataModel = ensureCodexSitesDataModel(dataModel, apps);
25
+ const response = await fetch("/api/workspace", {
26
+ method: "PATCH",
27
+ headers: { "content-type": "application/json" },
28
+ body: JSON.stringify({ dataModel: nextDataModel })
29
+ });
30
+ if (!response.ok) {
31
+ const payload = await response.json().catch(() => ({}));
32
+ window.alert(payload.error || "Failed to create Codex Sites object.");
33
+ return;
34
+ }
35
+ }
36
+ window.location.href = `/data-model?object=${encodeURIComponent(CODEX_SITES_OBJECT_ID)}`;
37
+ }
38
+
39
+ return <SettingsAccordionSection
40
+ id="codex-sites"
41
+ title="Codex Sites"
42
+ summary={`${rows.length} site${rows.length === 1 ? "" : "s"} · ${liveCount} live · ${appCount || apps?.length || 0} app${(appCount || apps?.length || 0) === 1 ? "" : "s"}`}
43
+ className="workspace-apps-linkage-section workspace-codex-sites-section"
44
+ >
45
+ <div className="workspace-app-row">
46
+ <span className="workspace-provider-mark"><Rocket size={15} /></span>
47
+ <div>
48
+ <strong>Codex Sites</strong>
49
+ <p>Manage Codex-hosted site URLs as a governed custom Data Model object attached to workspace apps and clients.</p>
50
+ <div className="workspace-integration-meta">
51
+ <span>{object ? "configured" : "not configured"}</span>
52
+ <span>{rows.length} site{rows.length === 1 ? "" : "s"}</span>
53
+ <span>{liveCount} live</span>
54
+ <span>{appCount || apps?.length || 0} app{(appCount || apps?.length || 0) === 1 ? "" : "s"}</span>
55
+ </div>
56
+ </div>
57
+ <button type="button" className="workspace-settings-action" onClick={openDataModel}>
58
+ <ExternalLink size={14} />{object ? "Manage" : "Set up"}
59
+ </button>
60
+ </div>
61
+ {rows.length ? <div className="workspace-settings-codex-sites-list">
62
+ {rows.slice(0, 4).map((row, index) => isCodexSiteUrl(row?.url) ? (
63
+ <a key={row.id || row.Name || index} href={row.url} target="_blank" rel="noreferrer">
64
+ <span>{row.Name || `Site ${index + 1}`}</span>
65
+ <em>{row.client || "Workspace"} · {row.app || "apps/workspace"}</em>
66
+ <ExternalLink size={13} />
67
+ </a>
68
+ ) : (
69
+ <button key={row.id || row.Name || index} type="button" onClick={openDataModel}>
70
+ <span>{row.Name || `Site ${index + 1}`}</span>
71
+ <em>{row.client || "Workspace"} · {row.status || "draft"}</em>
72
+ </button>
73
+ ))}
74
+ </div> : null}
75
+ </SettingsAccordionSection>;
76
+ }
77
+
78
+ export {
79
+ CODEX_SITES_OBJECT_ID,
80
+ CodexSitesDataModelCard
81
+ };