@growthub/cli 0.14.9 → 0.14.11
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.
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/api/workspace/add-ons/[providerId]/callback/route.js +35 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/api/workspace/add-ons/[providerId]/failure/route.js +35 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/api/workspace/add-ons/[providerId]/schedule/route.js +423 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/api/workspace/add-ons/providers/[providerId]/connect/route.js +78 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/api/workspace/add-ons/providers/[providerId]/credentials/route.js +276 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/api/workspace/add-ons/providers/[providerId]/products/[productId]/resources/route.js +173 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/api/workspace/add-ons/providers/[providerId]/products/sync/route.js +347 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/api/workspace/add-ons/providers/[providerId]/sync/route.js +293 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/api/workspace/add-ons/upstash/provider/connect/route.js +7 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/api/workspace/add-ons/upstash/provider/sync/route.js +7 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/api/workspace/add-ons/upstash/sync/route.js +197 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/api/workspace/apps/route.js +1 -1
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/api/workspace/patch/preflight/route.js +38 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/api/workspace/sandbox-run/route.js +3 -20
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/api/workspace/test-api-record/route.js +3 -20
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/api/workspace/workflow/publish/route.js +407 -290
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/api/workspace/workflows/[providerId]/route.js +209 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/components/WorkspaceAddOnsMarketplace.jsx +806 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/ApiRegistryActionCard.jsx +141 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/CeoCockpit.jsx +15 -3
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/HelperSidecar.jsx +42 -5
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/OrchestrationGraphCanvas.jsx +5 -1
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/OrchestrationNodeConfigPanel.jsx +86 -20
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/ScheduleCockpit.jsx +363 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/helper-commands.js +8 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/globals.css +322 -1
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/page.jsx +2 -2
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/settings/add-ons/add-ons-client.jsx +197 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/settings/add-ons/page.jsx +23 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/settings/settings-shell.jsx +1 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/workflows/WorkflowSurface.jsx +734 -61
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/workspace-rail.jsx +15 -10
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/env-status.js +2 -7
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/orchestration-graph-runner.js +29 -19
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/sandbox-serverless-flow.js +8 -4
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/schedule-cockpit-console.js +287 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/scheduler-orchestration.js +449 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/server-secrets.js +77 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/serverless-readiness.js +583 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/workspace-add-on-callback.js +63 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/workspace-add-on-scheduler.js +519 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/workspace-add-ons.js +957 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/workspace-app-readiness.js +212 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/workspace-config.js +607 -63
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/workspace-contract-compliance.js +168 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/workspace-data-model.js +21 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/workspace-operator-auth.js +32 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/workspace-patch-impact.js +133 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/workspace-provenance-lineage.js +214 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/workspace-stale-surfaces.js +217 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/workspace-workflow-impact.js +170 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/public/integrations/upstash/provider.png +0 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/public/integrations/upstash/qstash.png +0 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/public/integrations/upstash/redis.png +0 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/public/integrations/upstash/search.png +0 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/public/integrations/upstash/vector.png +0 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/scripts/scheduler-ingress-smoke.mjs +26 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/kit.json +6 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/skills/governed-workspace-mutation/SKILL.md +3 -1
- package/dist/index.js +3024 -4191
- package/package.json +1 -1
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { Check, ExternalLink, Play, Terminal, X } from "lucide-react";
|
|
4
|
+
import { getApiRegistrySandboxToolState } from "@/lib/orchestration-graph";
|
|
5
|
+
|
|
6
|
+
export function ApiRegistryActionCard({
|
|
7
|
+
registryRow,
|
|
8
|
+
workspaceConfig,
|
|
9
|
+
disabled,
|
|
10
|
+
onCreateSandboxTool,
|
|
11
|
+
onOpenSandboxTool,
|
|
12
|
+
onRunSandboxTool,
|
|
13
|
+
onTestConnection,
|
|
14
|
+
testing,
|
|
15
|
+
sandboxRunning
|
|
16
|
+
}) {
|
|
17
|
+
const state = getApiRegistrySandboxToolState(registryRow, workspaceConfig);
|
|
18
|
+
|
|
19
|
+
if (state.kind === "incomplete") {
|
|
20
|
+
const checklist = state.checklist || [];
|
|
21
|
+
return (
|
|
22
|
+
<section className="dm-api-action-card dm-api-action-card-muted" aria-label="Complete API setup">
|
|
23
|
+
<div className="dm-api-action-card-body">
|
|
24
|
+
<p className="dm-api-action-card-eyebrow">API Registry</p>
|
|
25
|
+
<h3>Complete API setup</h3>
|
|
26
|
+
<p>
|
|
27
|
+
This API needs a registry ID, base URL, endpoint, method, and auth reference before it can become a sandbox tool.
|
|
28
|
+
</p>
|
|
29
|
+
<ul className="dm-api-action-checklist">
|
|
30
|
+
{checklist.map((item) => (
|
|
31
|
+
<li key={item.field} className={item.ok ? "is-done" : "is-pending"}>
|
|
32
|
+
{item.ok ? <Check size={14} aria-hidden="true" /> : <X size={14} aria-hidden="true" />}
|
|
33
|
+
<span>{item.field}</span>
|
|
34
|
+
</li>
|
|
35
|
+
))}
|
|
36
|
+
</ul>
|
|
37
|
+
</div>
|
|
38
|
+
</section>
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (state.kind === "untested") {
|
|
43
|
+
return (
|
|
44
|
+
<section className="dm-api-action-card dm-api-action-card-muted" aria-label="Test API first">
|
|
45
|
+
<div className="dm-api-action-card-body">
|
|
46
|
+
<p className="dm-api-action-card-eyebrow">Sandbox tool</p>
|
|
47
|
+
<h3>Test this API first</h3>
|
|
48
|
+
<p>{state.message}</p>
|
|
49
|
+
</div>
|
|
50
|
+
<button
|
|
51
|
+
type="button"
|
|
52
|
+
className="dm-btn-primary-sm dm-api-action-card-cta"
|
|
53
|
+
disabled={disabled || testing}
|
|
54
|
+
onClick={onTestConnection}
|
|
55
|
+
>
|
|
56
|
+
{testing ? "Testing…" : "Test connection"}
|
|
57
|
+
</button>
|
|
58
|
+
</section>
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (state.kind === "failed") {
|
|
63
|
+
return (
|
|
64
|
+
<section className="dm-api-action-card dm-api-action-card-muted" aria-label="API test failed">
|
|
65
|
+
<div className="dm-api-action-card-body">
|
|
66
|
+
<p className="dm-api-action-card-eyebrow">Connection</p>
|
|
67
|
+
<h3>API test failed</h3>
|
|
68
|
+
<p>{state.message}</p>
|
|
69
|
+
</div>
|
|
70
|
+
<button
|
|
71
|
+
type="button"
|
|
72
|
+
className="dm-btn-outline dm-api-action-card-cta"
|
|
73
|
+
disabled={disabled || testing}
|
|
74
|
+
onClick={onTestConnection}
|
|
75
|
+
>
|
|
76
|
+
{testing ? "Testing…" : "Retest"}
|
|
77
|
+
</button>
|
|
78
|
+
</section>
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (state.kind === "existing") {
|
|
83
|
+
const toolName = String(state.row?.Name || "").trim();
|
|
84
|
+
return (
|
|
85
|
+
<section className="dm-api-action-card" aria-label="Sandbox tool ready">
|
|
86
|
+
<div className="dm-api-action-card-icon" aria-hidden="true">
|
|
87
|
+
<Terminal size={18} />
|
|
88
|
+
</div>
|
|
89
|
+
<div className="dm-api-action-card-body">
|
|
90
|
+
<p className="dm-api-action-card-eyebrow">Sandbox tool</p>
|
|
91
|
+
<h3>Sandbox tool ready</h3>
|
|
92
|
+
<p>{toolName} — governed row linked to this API Registry entry.</p>
|
|
93
|
+
</div>
|
|
94
|
+
<div className="dm-api-action-card-actions">
|
|
95
|
+
<button
|
|
96
|
+
type="button"
|
|
97
|
+
className="dm-btn-outline dm-api-action-card-cta"
|
|
98
|
+
disabled={disabled || !toolName}
|
|
99
|
+
onClick={() => onOpenSandboxTool?.({ name: toolName })}
|
|
100
|
+
>
|
|
101
|
+
<ExternalLink size={14} aria-hidden="true" />
|
|
102
|
+
Open sandbox tool
|
|
103
|
+
</button>
|
|
104
|
+
<button
|
|
105
|
+
type="button"
|
|
106
|
+
className="dm-btn-primary-sm dm-api-action-card-cta"
|
|
107
|
+
disabled={disabled || sandboxRunning || !toolName}
|
|
108
|
+
onClick={() => onRunSandboxTool?.({ name: toolName })}
|
|
109
|
+
>
|
|
110
|
+
<Play size={14} aria-hidden="true" />
|
|
111
|
+
{sandboxRunning ? "Running…" : "Run sandbox"}
|
|
112
|
+
</button>
|
|
113
|
+
</div>
|
|
114
|
+
</section>
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return (
|
|
119
|
+
<section className="dm-api-action-card" aria-label="Create sandbox tool">
|
|
120
|
+
<div className="dm-api-action-card-icon" aria-hidden="true">
|
|
121
|
+
<Terminal size={18} />
|
|
122
|
+
</div>
|
|
123
|
+
<div className="dm-api-action-card-body">
|
|
124
|
+
<p className="dm-api-action-card-eyebrow">API connected</p>
|
|
125
|
+
<h3>Create sandbox tool</h3>
|
|
126
|
+
<p>
|
|
127
|
+
This API is connected. Turn it into a sandbox tool that agents can run safely from this workspace.
|
|
128
|
+
</p>
|
|
129
|
+
<p className="dm-api-action-card-note">No secrets are stored. Nothing runs until you test the sandbox.</p>
|
|
130
|
+
</div>
|
|
131
|
+
<button
|
|
132
|
+
type="button"
|
|
133
|
+
className="dm-btn-primary-sm dm-api-action-card-cta"
|
|
134
|
+
disabled={disabled}
|
|
135
|
+
onClick={onCreateSandboxTool}
|
|
136
|
+
>
|
|
137
|
+
Create sandbox tool
|
|
138
|
+
</button>
|
|
139
|
+
</section>
|
|
140
|
+
);
|
|
141
|
+
}
|
|
@@ -120,7 +120,7 @@ function CeoReportCard({ report, onOpen, emphasis }) {
|
|
|
120
120
|
// cap is purely by count and disclosed.
|
|
121
121
|
const CEO_FLEET_VISIBLE_CAP = 50;
|
|
122
122
|
|
|
123
|
-
function CeoFleetView({ model, onOpenArtifact }) {
|
|
123
|
+
function CeoFleetView({ model, onOpenArtifact, onOpenSchedule }) {
|
|
124
124
|
const { fleet, attention, reports, governance } = model;
|
|
125
125
|
// Filter by stable reportId, not name — duplicate Names must never drop or
|
|
126
126
|
// merge a record from the fleet.
|
|
@@ -138,6 +138,18 @@ function CeoFleetView({ model, onOpenArtifact }) {
|
|
|
138
138
|
{`${governance.blockedAttempts} blocked attempt${governance.blockedAttempts === 1 ? "" : "s"}`}
|
|
139
139
|
</span>
|
|
140
140
|
)}
|
|
141
|
+
{typeof onOpenSchedule === "function" && (
|
|
142
|
+
<button
|
|
143
|
+
type="button"
|
|
144
|
+
className="dm-btn-ghost dm-swarm-card-action"
|
|
145
|
+
onClick={() => onOpenSchedule(null)}
|
|
146
|
+
title="Open the schedule cockpit (/schedule)"
|
|
147
|
+
aria-label="Open schedule cockpit"
|
|
148
|
+
>
|
|
149
|
+
Schedule
|
|
150
|
+
<ArrowUpRight size={12} aria-hidden="true" />
|
|
151
|
+
</button>
|
|
152
|
+
)}
|
|
141
153
|
</div>
|
|
142
154
|
|
|
143
155
|
{reports.length === 0 && (
|
|
@@ -335,7 +347,7 @@ function CeoBootstrapView({ model, onAction, actionBusy, error }) {
|
|
|
335
347
|
// Container — derives the mode and wires actions to governed surfaces
|
|
336
348
|
// ---------------------------------------------------------------------------
|
|
337
349
|
|
|
338
|
-
export function CeoCockpit({ workspaceConfig, onOpenArtifact, onConfigRefresh, onSeedSwarm, onOpenSetup }) {
|
|
350
|
+
export function CeoCockpit({ workspaceConfig, onOpenArtifact, onConfigRefresh, onSeedSwarm, onOpenSetup, onOpenSchedule }) {
|
|
339
351
|
// Optional governance rollup — read-only, graceful fallback to config-only.
|
|
340
352
|
const [receipts, setReceipts] = useState([]);
|
|
341
353
|
const [activeOperationalTab, setActiveOperationalTab] = useState("history");
|
|
@@ -513,7 +525,7 @@ export function CeoCockpit({ workspaceConfig, onOpenArtifact, onConfigRefresh, o
|
|
|
513
525
|
</button>
|
|
514
526
|
</div>
|
|
515
527
|
{activeOperationalTab === "history" ? (
|
|
516
|
-
<CeoFleetView model={fleetModel} onOpenArtifact={handleOpenArtifact} />
|
|
528
|
+
<CeoFleetView model={fleetModel} onOpenArtifact={handleOpenArtifact} onOpenSchedule={onOpenSchedule} />
|
|
517
529
|
) : (
|
|
518
530
|
<CeoAgentTeamsSection
|
|
519
531
|
teams={teamsModel}
|
|
@@ -49,6 +49,7 @@ import {
|
|
|
49
49
|
} from "../../components/WorkspaceHelperSetupModal.jsx";
|
|
50
50
|
import { SwarmRunCockpit, SwarmAgentTranscript } from "./SwarmRunCockpit.jsx";
|
|
51
51
|
import { CeoCockpit } from "./CeoCockpit.jsx";
|
|
52
|
+
import { ScheduleCockpit } from "./ScheduleCockpit.jsx";
|
|
52
53
|
import { SidecarExpandView } from "./SidecarExpandView.jsx";
|
|
53
54
|
import { parseSlashInput } from "./helper-commands.js";
|
|
54
55
|
import {
|
|
@@ -380,6 +381,9 @@ export function HelperSidecar({ open, onClose, workspaceConfig, initialIntent, i
|
|
|
380
381
|
const [activeView, setActiveView] = useState("chat");
|
|
381
382
|
// Focused workflow row when opened from an apply receipt's Open button.
|
|
382
383
|
const [swarmFocus, setSwarmFocus] = useState(null);
|
|
384
|
+
// Optional focus handed into the schedule cockpit (e.g. CEO → /schedule for a
|
|
385
|
+
// selected workflow record). Read-only filter/focus hint, never a mutation.
|
|
386
|
+
const [scheduleFocus, setScheduleFocus] = useState(null);
|
|
383
387
|
// Expanded transcript agent (tool-output view) + full-width takeover flag.
|
|
384
388
|
const [expandedAgent, setExpandedAgent] = useState(null);
|
|
385
389
|
const [expandActive, setExpandActive] = useState(false);
|
|
@@ -904,6 +908,7 @@ export function HelperSidecar({ open, onClose, workspaceConfig, initialIntent, i
|
|
|
904
908
|
if (cmd.view) {
|
|
905
909
|
setPrompt("");
|
|
906
910
|
setSwarmFocus(null);
|
|
911
|
+
if (cmd.view === "schedule") setScheduleFocus(null);
|
|
907
912
|
setActiveView(cmd.view);
|
|
908
913
|
return;
|
|
909
914
|
}
|
|
@@ -980,6 +985,9 @@ export function HelperSidecar({ open, onClose, workspaceConfig, initialIntent, i
|
|
|
980
985
|
// oversight surface). Kept separate from inSwarmView so the swarm cockpit's
|
|
981
986
|
// run machinery and header affordances are untouched.
|
|
982
987
|
const inCeoView = activeView === "ceo";
|
|
988
|
+
// Schedule cockpit shares the same sidecar shell (read-only operations surface
|
|
989
|
+
// over the existing schedule routes). Same precedent as the CEO view.
|
|
990
|
+
const inScheduleView = activeView === "schedule";
|
|
983
991
|
const canOpenSwarmWorkflow = Boolean(
|
|
984
992
|
inSwarmView
|
|
985
993
|
&& activeTab === "assistant"
|
|
@@ -1014,7 +1022,7 @@ export function HelperSidecar({ open, onClose, workspaceConfig, initialIntent, i
|
|
|
1014
1022
|
{/* Header — title left; gear toggles Assistant ↔ Setup, then close. */}
|
|
1015
1023
|
<div className="dm-sidecar-header">
|
|
1016
1024
|
<div className="dm-sidecar-header-left">
|
|
1017
|
-
{(inSwarmView || inCeoView) && (
|
|
1025
|
+
{(inSwarmView || inCeoView || inScheduleView) && (
|
|
1018
1026
|
<button
|
|
1019
1027
|
type="button"
|
|
1020
1028
|
className="dm-sidecar-icon-btn"
|
|
@@ -1035,9 +1043,11 @@ export function HelperSidecar({ open, onClose, workspaceConfig, initialIntent, i
|
|
|
1035
1043
|
? "Background tasks"
|
|
1036
1044
|
: inCeoView
|
|
1037
1045
|
? "CEO Cockpit"
|
|
1038
|
-
:
|
|
1039
|
-
?
|
|
1040
|
-
:
|
|
1046
|
+
: inScheduleView
|
|
1047
|
+
? "Schedule Cockpit"
|
|
1048
|
+
: threadActive
|
|
1049
|
+
? deriveThreadDisplayTitle(initialThread, "Workspace Helper")
|
|
1050
|
+
: "Workspace Helper"}
|
|
1041
1051
|
</span>
|
|
1042
1052
|
</div>
|
|
1043
1053
|
<div className="dm-sidecar-header-right">
|
|
@@ -1113,6 +1123,33 @@ export function HelperSidecar({ open, onClose, workspaceConfig, initialIntent, i
|
|
|
1113
1123
|
);
|
|
1114
1124
|
}}
|
|
1115
1125
|
onOpenSetup={() => setActiveTab("setup")}
|
|
1126
|
+
onOpenSchedule={(focus) => { setScheduleFocus(focus || null); setActiveView("schedule"); }}
|
|
1127
|
+
/>
|
|
1128
|
+
</div>
|
|
1129
|
+
)}
|
|
1130
|
+
|
|
1131
|
+
{/* Schedule cockpit view — the daily operations surface for scheduled
|
|
1132
|
+
workflows. Same sidecar shell, read-only with respect to mutation:
|
|
1133
|
+
every action hands off to an EXISTING governed schedule route
|
|
1134
|
+
(install/pause/resume/readiness/uninstall) or the Add-ons marketplace
|
|
1135
|
+
setup path. No new route, no client-side PATCH, no second runtime. */}
|
|
1136
|
+
{activeTab === "assistant" && inScheduleView && (
|
|
1137
|
+
<div className="dm-sidecar-body dm-swarm-body" data-schedule-view={activeView}>
|
|
1138
|
+
<ScheduleCockpit
|
|
1139
|
+
workspaceConfig={workspaceConfig}
|
|
1140
|
+
focus={scheduleFocus}
|
|
1141
|
+
onConfigRefresh={refreshWorkspaceConfig}
|
|
1142
|
+
onOpenArtifact={(artifact) => { if (artifact) handleOpenArtifact(artifact); }}
|
|
1143
|
+
onSeedSwarm={(seedPrompt) => {
|
|
1144
|
+
setActiveView("chat");
|
|
1145
|
+
onPickIntent("swarm");
|
|
1146
|
+
setPrompt(
|
|
1147
|
+
typeof seedPrompt === "string" && seedPrompt.trim()
|
|
1148
|
+
? `${seedPrompt.trim()} `
|
|
1149
|
+
: "Propose a governed agent swarm: "
|
|
1150
|
+
);
|
|
1151
|
+
}}
|
|
1152
|
+
onOpenSetup={() => setActiveTab("setup")}
|
|
1116
1153
|
/>
|
|
1117
1154
|
</div>
|
|
1118
1155
|
)}
|
|
@@ -1121,7 +1158,7 @@ export function HelperSidecar({ open, onClose, workspaceConfig, initialIntent, i
|
|
|
1121
1158
|
conversation/result area on top (flex:1), bottom-anchored composer
|
|
1122
1159
|
holds chip stack (empty state) → mode row (active thread) →
|
|
1123
1160
|
textarea with attach + mode + send-arrow action row. */}
|
|
1124
|
-
{activeTab === "assistant" && !inSwarmView && !inCeoView && (
|
|
1161
|
+
{activeTab === "assistant" && !inSwarmView && !inCeoView && !inScheduleView && (
|
|
1125
1162
|
<div className="dm-sidecar-body dm-helper-body">
|
|
1126
1163
|
<div className="dm-helper-conversation" ref={conversationRef}>
|
|
1127
1164
|
{/* Conversation — ChatGPT-grade multi-turn. User bubble
|
|
@@ -130,6 +130,7 @@ export function OrchestrationGraphCanvas({
|
|
|
130
130
|
nodeStatuses,
|
|
131
131
|
onNodeStatusClick,
|
|
132
132
|
statusLabel = "Draft",
|
|
133
|
+
readinessFlags,
|
|
133
134
|
}) {
|
|
134
135
|
const parsed = useMemo(() => parseOrchestrationGraph(graph) || graph, [graph]);
|
|
135
136
|
const nodes = useMemo(() => orderedGraphNodes(parsed), [parsed]);
|
|
@@ -242,6 +243,9 @@ export function OrchestrationGraphCanvas({
|
|
|
242
243
|
const nodeStatusChip = nodeStatuses
|
|
243
244
|
? NODE_STATUS_CHIP[String(nodeStatuses[id] || "").toLowerCase()] || null
|
|
244
245
|
: null;
|
|
246
|
+
// Serverless-readiness flag — ultrathin orange border ONLY. The color
|
|
247
|
+
// is the guidance; no badge, no text. Driven by the readiness scan.
|
|
248
|
+
const readinessFlag = readinessFlags ? readinessFlags[id] || null : null;
|
|
245
249
|
|
|
246
250
|
return (
|
|
247
251
|
<div key={id || index} className="dm-orchestration-canvas__step">
|
|
@@ -292,7 +296,7 @@ export function OrchestrationGraphCanvas({
|
|
|
292
296
|
<div
|
|
293
297
|
role="button"
|
|
294
298
|
tabIndex={0}
|
|
295
|
-
className={`dm-orchestration-node${isSelected ? " dm-orchestration-node--selected" : ""}`}
|
|
299
|
+
className={`dm-orchestration-node${isSelected ? " dm-orchestration-node--selected" : ""}${readinessFlag ? ` dm-orchestration-node--readiness is-${readinessFlag.severity || "warning"}` : ""}`}
|
|
296
300
|
title={hoverHint(node)}
|
|
297
301
|
onClick={() => {
|
|
298
302
|
setInternalSelected(id);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
3
|
import { useMemo, useState } from "react";
|
|
4
|
-
import { Check } from "lucide-react";
|
|
4
|
+
import { CalendarClock, Check, ChevronDown, Database, FileInput, ListTree } from "lucide-react";
|
|
5
5
|
import {
|
|
6
6
|
detectFieldIdsFromLastResponse,
|
|
7
7
|
FILTER_CONJUNCTIONS,
|
|
@@ -316,7 +316,7 @@ function FieldMapRows({ fieldMap, onChange, disabled, fieldOptions = [] }) {
|
|
|
316
316
|
);
|
|
317
317
|
}
|
|
318
318
|
|
|
319
|
-
function PayloadKeyRows({ payload, onChange, disabled }) {
|
|
319
|
+
function PayloadKeyRows({ payload, onChange, disabled, flagClassName = "" }) {
|
|
320
320
|
const entries = Object.entries(payload && typeof payload === "object" && !Array.isArray(payload) ? payload : {});
|
|
321
321
|
|
|
322
322
|
function setEntries(nextEntries) {
|
|
@@ -324,7 +324,7 @@ function PayloadKeyRows({ payload, onChange, disabled }) {
|
|
|
324
324
|
}
|
|
325
325
|
|
|
326
326
|
return (
|
|
327
|
-
<div className=
|
|
327
|
+
<div className={`dm-orchestration-config__payload${flagClassName}`}>
|
|
328
328
|
<span className="dm-orchestration-config__field-label">Test payload fields</span>
|
|
329
329
|
{entries.map(([key, value], index) => (
|
|
330
330
|
<div key={index} className="dm-orchestration-config__payload-row">
|
|
@@ -370,7 +370,8 @@ function PayloadKeyRows({ payload, onChange, disabled }) {
|
|
|
370
370
|
);
|
|
371
371
|
}
|
|
372
372
|
|
|
373
|
-
function VersionDeltaControls({ node, config, sandboxRow, onChange, disabled }) {
|
|
373
|
+
function VersionDeltaControls({ node, config, sandboxRow, onChange, disabled, flaggedTags, flagSeverity = "warning" }) {
|
|
374
|
+
const flagged = flaggedTags instanceof Set ? flaggedTags : new Set(Array.isArray(flaggedTags) ? flaggedTags : []);
|
|
374
375
|
const explicitTags = normalizeTags(config?.deltaTags);
|
|
375
376
|
const inferredTags = inferDeltaTagsForNode(node, config);
|
|
376
377
|
const selectedTags = explicitTags.length > 0 ? explicitTags : inferredTags;
|
|
@@ -409,7 +410,7 @@ function VersionDeltaControls({ node, config, sandboxRow, onChange, disabled })
|
|
|
409
410
|
{selectedTags.length > 0 && (
|
|
410
411
|
<div className="dm-version-delta__tag-fields">
|
|
411
412
|
{selectedTags.map((tag) => (
|
|
412
|
-
<label key={tag} className=
|
|
413
|
+
<label key={tag} className={`dm-orchestration-config__field${flagged.has(tag) ? ` dm-field--readiness is-${flagSeverity}` : ""}`}>
|
|
413
414
|
<span>{tag} value</span>
|
|
414
415
|
<input
|
|
415
416
|
value={deltaValues[tag] ?? getDeltaTagDefaultValue(tag, node, config, sandboxRow)}
|
|
@@ -430,7 +431,9 @@ function LocalAgentHostControls({
|
|
|
430
431
|
objectId,
|
|
431
432
|
rowName,
|
|
432
433
|
disabled,
|
|
433
|
-
onSandboxRowPatch
|
|
434
|
+
onSandboxRowPatch,
|
|
435
|
+
adapterFlagClass = "",
|
|
436
|
+
hostFlagClass = ""
|
|
434
437
|
}) {
|
|
435
438
|
const row = sandboxRow && typeof sandboxRow === "object" ? sandboxRow : {};
|
|
436
439
|
const adapter = String(row.adapter || "local-process").trim() || "local-process";
|
|
@@ -453,7 +456,7 @@ function LocalAgentHostControls({
|
|
|
453
456
|
<p className="dm-orchestration-config__hint">
|
|
454
457
|
Same runtime fields as the Data Model sandbox sidecar. Local agent host uses the Paperclip thin adapter on this machine.
|
|
455
458
|
</p>
|
|
456
|
-
<label className=
|
|
459
|
+
<label className={`dm-orchestration-config__field${adapterFlagClass}`}>
|
|
457
460
|
<span>Execution adapter</span>
|
|
458
461
|
<select
|
|
459
462
|
value={adapter}
|
|
@@ -473,7 +476,7 @@ function LocalAgentHostControls({
|
|
|
473
476
|
</select>
|
|
474
477
|
</label>
|
|
475
478
|
{adapter === "local-agent-host" && (
|
|
476
|
-
<label className=
|
|
479
|
+
<label className={`dm-orchestration-config__field${hostFlagClass}`}>
|
|
477
480
|
<span>Agent host (Paperclip)</span>
|
|
478
481
|
<select
|
|
479
482
|
value={agentHost}
|
|
@@ -568,10 +571,15 @@ export function OrchestrationNodeConfigPanel({
|
|
|
568
571
|
objectId,
|
|
569
572
|
rowName,
|
|
570
573
|
onSandboxRowPatch,
|
|
574
|
+
inputScheduleControls,
|
|
575
|
+
serverlessScheduleOptionAvailable = false,
|
|
576
|
+
serverlessScheduleAvailable = false,
|
|
577
|
+
readinessFlag,
|
|
571
578
|
activeTab: controlledTab,
|
|
572
579
|
onTabChange
|
|
573
580
|
}) {
|
|
574
581
|
const [internalTab, setInternalTab] = useState("node");
|
|
582
|
+
const [inputModeOpen, setInputModeOpen] = useState(false);
|
|
575
583
|
const rawActiveTab = controlledTab ?? internalTab;
|
|
576
584
|
|
|
577
585
|
function setActiveTab(tab) {
|
|
@@ -594,6 +602,14 @@ export function OrchestrationNodeConfigPanel({
|
|
|
594
602
|
|
|
595
603
|
const config = node.config || {};
|
|
596
604
|
const type = String(node.type || "");
|
|
605
|
+
const schedulerAvailable = Boolean(serverlessScheduleOptionAvailable || serverlessScheduleAvailable);
|
|
606
|
+
const inputModeOptions = [
|
|
607
|
+
{ value: "manual", label: "Manual", Icon: FileInput },
|
|
608
|
+
{ value: "record", label: "Record", Icon: Database },
|
|
609
|
+
{ value: "source-record", label: "Source Record", Icon: ListTree },
|
|
610
|
+
...(schedulerAvailable ? [{ value: "serverless-schedule", label: "Serverless Schedule", Icon: CalendarClock }] : []),
|
|
611
|
+
];
|
|
612
|
+
const selectedInputMode = inputModeOptions.find((option) => option.value === (config.inputMode || "manual")) || inputModeOptions[0];
|
|
597
613
|
const meta = config.requestHeadersMetadata || {};
|
|
598
614
|
const workspaceObjects = (Array.isArray(workspaceConfig?.dataModel?.objects) ? workspaceConfig.dataModel.objects : [])
|
|
599
615
|
.filter((object) => object?.id && object?.objectType !== "sandbox-environment" && object?.objectType !== "api-registry");
|
|
@@ -605,6 +621,21 @@ export function OrchestrationNodeConfigPanel({
|
|
|
605
621
|
onConfigChange?.({ ...config, ...patch });
|
|
606
622
|
}
|
|
607
623
|
|
|
624
|
+
// Serverless-readiness atomic field flag. The scan maps each alert to the exact
|
|
625
|
+
// config / sandbox-row field(s) that must change; we fill ONLY those fields
|
|
626
|
+
// light-orange (the color is the guidance — no extra copy). `row:`-prefixed
|
|
627
|
+
// hints (e.g. the execution adapter) match without the prefix here too.
|
|
628
|
+
const readinessSeverity = readinessFlag?.severity === "blocked" ? "blocked" : "warning";
|
|
629
|
+
const readinessFieldSet = new Set([
|
|
630
|
+
...(Array.isArray(readinessFlag?.configFields) ? readinessFlag.configFields : []),
|
|
631
|
+
...(Array.isArray(readinessFlag?.rowFields) ? readinessFlag.rowFields : []),
|
|
632
|
+
...(Array.isArray(readinessFlag?.fields) ? readinessFlag.fields.map((f) => String(f).replace(/^row:/, "")) : []),
|
|
633
|
+
]);
|
|
634
|
+
const readinessTagSet = new Set(Array.isArray(readinessFlag?.deltaTags) ? readinessFlag.deltaTags : []);
|
|
635
|
+
function flagFieldClass(...keys) {
|
|
636
|
+
return keys.some((k) => readinessFieldSet.has(k)) ? ` dm-field--readiness is-${readinessSeverity}` : "";
|
|
637
|
+
}
|
|
638
|
+
|
|
608
639
|
const tabsForType = type === "api-registry-call" || type === "core-action"
|
|
609
640
|
? ["configuration", "test", "advanced"]
|
|
610
641
|
: type === "input" || type === "transform-filter" || type === "data-action" || type === "data-trigger" || type === "ai-agent" || type === "flow-control" || type === "human-input"
|
|
@@ -658,22 +689,55 @@ export function OrchestrationNodeConfigPanel({
|
|
|
658
689
|
|
|
659
690
|
{activeTab === "configuration" && type === "input" && (
|
|
660
691
|
<div className="dm-orchestration-config__pane">
|
|
661
|
-
<
|
|
692
|
+
<div className="dm-orchestration-config__field">
|
|
662
693
|
<span>Input mode</span>
|
|
663
|
-
<
|
|
664
|
-
<
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
694
|
+
<div className={`dm-select dm-input-mode-select${inputModeOpen ? " open" : ""}${disabled ? " disabled" : ""}`}>
|
|
695
|
+
<button
|
|
696
|
+
type="button"
|
|
697
|
+
className="dm-select-trigger"
|
|
698
|
+
disabled={disabled}
|
|
699
|
+
aria-haspopup="listbox"
|
|
700
|
+
aria-expanded={inputModeOpen}
|
|
701
|
+
onClick={() => setInputModeOpen((open) => !open)}
|
|
702
|
+
>
|
|
703
|
+
<span>{selectedInputMode.label}</span>
|
|
704
|
+
<ChevronDown size={15} aria-hidden="true" />
|
|
705
|
+
</button>
|
|
706
|
+
{inputModeOpen ? (
|
|
707
|
+
<div className="dm-select-popover">
|
|
708
|
+
<div className="dm-select-list" role="listbox" aria-label="Input mode">
|
|
709
|
+
{inputModeOptions.map((option) => (
|
|
710
|
+
<button
|
|
711
|
+
key={option.value}
|
|
712
|
+
type="button"
|
|
713
|
+
role="option"
|
|
714
|
+
aria-selected={option.value === selectedInputMode.value}
|
|
715
|
+
className={`dm-select-option${option.value === selectedInputMode.value ? " selected" : ""}`}
|
|
716
|
+
onMouseDown={(event) => {
|
|
717
|
+
event.preventDefault();
|
|
718
|
+
patchConfig({ inputMode: option.value });
|
|
719
|
+
setInputModeOpen(false);
|
|
720
|
+
}}
|
|
721
|
+
>
|
|
722
|
+
<option.Icon size={14} aria-hidden="true" />
|
|
723
|
+
<span>{option.label}</span>
|
|
724
|
+
</button>
|
|
725
|
+
))}
|
|
726
|
+
</div>
|
|
727
|
+
</div>
|
|
728
|
+
) : null}
|
|
729
|
+
</div>
|
|
730
|
+
</div>
|
|
669
731
|
<PayloadKeyRows
|
|
670
732
|
payload={config.samplePayload}
|
|
671
733
|
disabled={disabled}
|
|
672
734
|
onChange={(samplePayload) => patchConfig({ samplePayload })}
|
|
735
|
+
flagClassName={flagFieldClass("samplePayload", "triggerInput")}
|
|
673
736
|
/>
|
|
674
737
|
<p className="dm-orchestration-config__hint">
|
|
675
738
|
Bind values with {"{{input.key}}"} in the API endpoint or body template.
|
|
676
739
|
</p>
|
|
740
|
+
{inputScheduleControls || null}
|
|
677
741
|
</div>
|
|
678
742
|
)}
|
|
679
743
|
|
|
@@ -694,15 +758,15 @@ export function OrchestrationNodeConfigPanel({
|
|
|
694
758
|
))}
|
|
695
759
|
</select>
|
|
696
760
|
</label>
|
|
697
|
-
<label className=
|
|
761
|
+
<label className={`dm-orchestration-config__field${flagFieldClass("endpoint")}`}>
|
|
698
762
|
<span>Endpoint</span>
|
|
699
763
|
<input value={config.endpoint || ""} disabled={disabled} onChange={(e) => patchConfig({ endpoint: e.target.value })} />
|
|
700
764
|
</label>
|
|
701
|
-
<label className=
|
|
765
|
+
<label className={`dm-orchestration-config__field${flagFieldClass("bodyTemplate")}`}>
|
|
702
766
|
<span>Body template</span>
|
|
703
767
|
<textarea rows={3} value={config.bodyTemplate || ""} disabled={disabled} onChange={(e) => patchConfig({ bodyTemplate: e.target.value })} />
|
|
704
768
|
</label>
|
|
705
|
-
<label className=
|
|
769
|
+
<label className={`dm-orchestration-config__field${flagFieldClass("authRef", "registryId", "integrationId")}`}>
|
|
706
770
|
<span>Auth reference</span>
|
|
707
771
|
<input value={config.authRef || ""} disabled={disabled} onChange={(e) => patchConfig({ authRef: e.target.value })} />
|
|
708
772
|
</label>
|
|
@@ -865,7 +929,7 @@ export function OrchestrationNodeConfigPanel({
|
|
|
865
929
|
</label>
|
|
866
930
|
</div>
|
|
867
931
|
|
|
868
|
-
<VersionDeltaControls node={node} config={config} sandboxRow={sandboxRow} disabled={disabled} onChange={onConfigChange} />
|
|
932
|
+
<VersionDeltaControls node={node} config={config} sandboxRow={sandboxRow} disabled={disabled} onChange={onConfigChange} flaggedTags={readinessTagSet} flagSeverity={readinessSeverity} />
|
|
869
933
|
|
|
870
934
|
<details className="dm-orchestration-config__advanced-json dm-orchestration-config__node-json">
|
|
871
935
|
<summary>Node JSON</summary>
|
|
@@ -1075,6 +1139,8 @@ export function OrchestrationNodeConfigPanel({
|
|
|
1075
1139
|
rowName={rowName}
|
|
1076
1140
|
disabled={disabled}
|
|
1077
1141
|
onSandboxRowPatch={onSandboxRowPatch}
|
|
1142
|
+
adapterFlagClass={flagFieldClass("adapter")}
|
|
1143
|
+
hostFlagClass={flagFieldClass("agentHost")}
|
|
1078
1144
|
/>
|
|
1079
1145
|
)}
|
|
1080
1146
|
<label className="dm-orchestration-config__field">
|
|
@@ -1473,7 +1539,7 @@ export function OrchestrationNodeConfigPanel({
|
|
|
1473
1539
|
)}
|
|
1474
1540
|
|
|
1475
1541
|
{activeTab === "configuration" && type !== "thinAdapter" && (
|
|
1476
|
-
<VersionDeltaControls node={node} config={config} sandboxRow={sandboxRow} disabled={disabled} onChange={onConfigChange} />
|
|
1542
|
+
<VersionDeltaControls node={node} config={config} sandboxRow={sandboxRow} disabled={disabled} onChange={onConfigChange} flaggedTags={readinessTagSet} flagSeverity={readinessSeverity} />
|
|
1477
1543
|
)}
|
|
1478
1544
|
<div className="dm-workflow-node-config-foot">
|
|
1479
1545
|
<button type="button" className="dm-workflow-node-options" disabled={disabled}>
|