@growthub/cli 0.13.6 → 0.13.8
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/helper/query/route.js +98 -34
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/api/workspace/metadata-graph/route.js +1 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/api/workspace/swarm-condition/route.js +106 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/components/WorkspaceActivationPanel.jsx +189 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/components/WorkspaceContributionGraph.jsx +119 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/components/WorkspaceHelperSetupModal.jsx +357 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/components/WorkspaceLensPanel.jsx +488 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/components/WorkspaceLensWalkthrough.jsx +69 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/HelperSidecar.jsx +37 -2
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/NangoConnectionPanel.jsx +37 -2
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/globals.css +437 -26
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/workflows/WorkflowSurface.jsx +44 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/workspace-builder.jsx +592 -41
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/workspace-lens/page.jsx +76 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/workspace-rail.jsx +148 -4
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/workspace-activation.js +1559 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/workspace-data-model.js +3 -3
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/workspace-helper-apply.js +24 -8
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/workspace-metadata-store.js +82 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/kit.json +8 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/templates/seeded-configs/project-management.config.json +4 -4
- package/dist/index.js +5224 -5225
- package/package.json +1 -1
|
@@ -0,0 +1,357 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useEffect, useState } from "react";
|
|
4
|
+
import { X } from "lucide-react";
|
|
5
|
+
|
|
6
|
+
const HELPER_SANDBOX_OBJECT_ID = "workspace-helper-sandbox";
|
|
7
|
+
const HELPER_HANDOFF_DISMISS_FLAG = "workspaceHelperHandoffComplete";
|
|
8
|
+
|
|
9
|
+
const HELPER_AGENT_CHOICES = [
|
|
10
|
+
{ id: "codex_local", label: "Codex CLI (local)", body: "Recommended. Uses your local Codex CLI." },
|
|
11
|
+
{ id: "claude_local", label: "Claude Code (local)", body: "Uses your local Claude Code session." },
|
|
12
|
+
{ id: "cursor", label: "Cursor Agent (local)", body: "Uses Cursor Agent." },
|
|
13
|
+
{ id: "gemini_local", label: "Gemini CLI (local)", body: "Uses Gemini CLI." },
|
|
14
|
+
{ id: "opencode_local", label: "OpenCode (local)", body: "Uses OpenCode on this machine." },
|
|
15
|
+
{ id: "pi_local", label: "Pi (local)", body: "Uses Pi on this machine." },
|
|
16
|
+
{ id: "qwen_code_local", label: "Qwen Code (local)", body: "Uses Qwen Code on this machine." },
|
|
17
|
+
];
|
|
18
|
+
|
|
19
|
+
const HELPER_EXECUTION_ADAPTERS = [
|
|
20
|
+
{ id: "local-agent-host", label: "Local agent host (Paperclip thin adapter)" },
|
|
21
|
+
{ id: "agent-host", label: "Agent host (Paperclip)" },
|
|
22
|
+
{ id: "local-intelligence", label: "Local intelligence (OpenAI-compatible)" },
|
|
23
|
+
{ id: "local-process", label: "Local process (default)" },
|
|
24
|
+
];
|
|
25
|
+
|
|
26
|
+
function getHelperSandboxObject(config) {
|
|
27
|
+
const objects = Array.isArray(config?.dataModel?.objects) ? config.dataModel.objects : [];
|
|
28
|
+
return objects.find((o) => o?.id === HELPER_SANDBOX_OBJECT_ID && o?.objectType === "sandbox-environment") || null;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function getHelperSandboxRow(config) {
|
|
32
|
+
const helper = getHelperSandboxObject(config);
|
|
33
|
+
return Array.isArray(helper?.rows) ? helper.rows[0] : null;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function isHelperConfigured(config) {
|
|
37
|
+
const row = getHelperSandboxRow(config);
|
|
38
|
+
return Boolean(String(row?.adapter || "").trim() === "local-agent-host" && String(row?.agentHost || "").trim());
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function readWorkspaceUiCacheFlag(config, key) {
|
|
42
|
+
const objects = Array.isArray(config?.dataModel?.objects) ? config.dataModel.objects : [];
|
|
43
|
+
const cache = objects.find((o) => o?.id === "workspace-ui-cache");
|
|
44
|
+
const row = Array.isArray(cache?.rows) ? cache.rows.find((r) => r?.id === "activation") : null;
|
|
45
|
+
return row?.[key];
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function withWorkspaceUiCacheFlag(config, key, value) {
|
|
49
|
+
const dataModel = config?.dataModel && typeof config.dataModel === "object" ? config.dataModel : {};
|
|
50
|
+
const objects = Array.isArray(dataModel.objects) ? dataModel.objects : [];
|
|
51
|
+
const existing = objects.find((o) => o?.id === "workspace-ui-cache");
|
|
52
|
+
const cacheObject = existing || {
|
|
53
|
+
id: "workspace-ui-cache",
|
|
54
|
+
label: "Workspace UI Cache",
|
|
55
|
+
source: "Workspace UI Cache",
|
|
56
|
+
objectType: "custom",
|
|
57
|
+
icon: "Settings",
|
|
58
|
+
columns: ["id", key],
|
|
59
|
+
rows: [],
|
|
60
|
+
binding: { mode: "manual", source: "Workspace UI Cache" },
|
|
61
|
+
};
|
|
62
|
+
const columns = Array.from(new Set([...(Array.isArray(cacheObject.columns) ? cacheObject.columns : ["id"]), key]));
|
|
63
|
+
const rows = Array.isArray(cacheObject.rows) ? cacheObject.rows : [];
|
|
64
|
+
const hasRow = rows.some((r) => r?.id === "activation");
|
|
65
|
+
const nextRows = hasRow
|
|
66
|
+
? rows.map((r) => (r?.id === "activation" ? { ...r, [key]: value } : r))
|
|
67
|
+
: [...rows, { id: "activation", [key]: value }];
|
|
68
|
+
const nextCache = { ...cacheObject, columns, rows: nextRows };
|
|
69
|
+
const nextObjects = existing
|
|
70
|
+
? objects.map((o) => (o?.id === "workspace-ui-cache" ? nextCache : o))
|
|
71
|
+
: [...objects, nextCache];
|
|
72
|
+
return { ...config, dataModel: { ...dataModel, objects: nextObjects } };
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function isHelperHandoffDismissed(config) {
|
|
76
|
+
const value = readWorkspaceUiCacheFlag(config, HELPER_HANDOFF_DISMISS_FLAG);
|
|
77
|
+
return value === true || String(value || "") === "true";
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function upsertHelperSandbox(config, draft) {
|
|
81
|
+
const dataModel = config?.dataModel && typeof config.dataModel === "object" ? config.dataModel : {};
|
|
82
|
+
const objects = Array.isArray(dataModel.objects) ? dataModel.objects : [];
|
|
83
|
+
const COLUMNS = [
|
|
84
|
+
"Name", "lifecycleStatus", "runLocality", "runtime", "adapter", "agentHost", "intelligenceType",
|
|
85
|
+
"workspaceRead", "proposalApply", "sandboxRun", "networkPolicy", "instructions", "timeoutMs", "status",
|
|
86
|
+
];
|
|
87
|
+
const row = {
|
|
88
|
+
Name: "workspace-helper",
|
|
89
|
+
lifecycleStatus: "live",
|
|
90
|
+
runLocality: draft.runLocality || "local",
|
|
91
|
+
runtime: draft.runtime || "node",
|
|
92
|
+
adapter: draft.adapter || "local-agent-host",
|
|
93
|
+
agentHost: draft.agentHost,
|
|
94
|
+
intelligenceType: "agent-host",
|
|
95
|
+
workspaceRead: draft.workspaceRead ? "enabled" : "disabled",
|
|
96
|
+
proposalApply: draft.proposalApply ? "approval-required" : "disabled",
|
|
97
|
+
sandboxRun: draft.sandboxRun ? "enabled" : "disabled",
|
|
98
|
+
networkPolicy: draft.networkPolicy || "workspace-only",
|
|
99
|
+
instructions: "Use the existing Workspace Helper widget to answer with workspace context and propose safe workspace changes through the governed helper apply flow.",
|
|
100
|
+
timeoutMs: String(draft.timeoutMs || "120000"),
|
|
101
|
+
status: "live",
|
|
102
|
+
};
|
|
103
|
+
const existing = getHelperSandboxObject(config);
|
|
104
|
+
const base = existing || {
|
|
105
|
+
id: HELPER_SANDBOX_OBJECT_ID,
|
|
106
|
+
label: "Workspace Helper Sandbox",
|
|
107
|
+
source: "Workspace Helper Sandbox",
|
|
108
|
+
objectType: "sandbox-environment",
|
|
109
|
+
icon: "Terminal",
|
|
110
|
+
columns: COLUMNS,
|
|
111
|
+
rows: [],
|
|
112
|
+
binding: { mode: "manual", source: "Workspace Helper Sandbox" },
|
|
113
|
+
};
|
|
114
|
+
const next = {
|
|
115
|
+
...base,
|
|
116
|
+
columns: Array.from(new Set([...(Array.isArray(base.columns) ? base.columns : []), ...COLUMNS])),
|
|
117
|
+
rows: Array.isArray(base.rows) && base.rows.length > 0
|
|
118
|
+
? base.rows.map((r, i) => (i === 0 ? { ...r, ...row } : r))
|
|
119
|
+
: [row],
|
|
120
|
+
};
|
|
121
|
+
const nextObjects = existing
|
|
122
|
+
? objects.map((o) => (o?.id === HELPER_SANDBOX_OBJECT_ID ? next : o))
|
|
123
|
+
: [...objects, next];
|
|
124
|
+
return withWorkspaceUiCacheFlag(
|
|
125
|
+
{ ...config, dataModel: { ...dataModel, objects: nextObjects } },
|
|
126
|
+
HELPER_HANDOFF_DISMISS_FLAG,
|
|
127
|
+
true,
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function WorkspaceHelperSetupModal({ workspaceConfig, open, onClose, onSaved }) {
|
|
132
|
+
const helperRow = getHelperSandboxRow(workspaceConfig);
|
|
133
|
+
const [step, setStep] = useState(1);
|
|
134
|
+
const [draft, setDraft] = useState({
|
|
135
|
+
agentHost: helperRow?.agentHost || "codex_local",
|
|
136
|
+
runLocality: helperRow?.runLocality || "local",
|
|
137
|
+
runtime: helperRow?.runtime || "node",
|
|
138
|
+
adapter: helperRow?.adapter || "local-agent-host",
|
|
139
|
+
timeoutMs: helperRow?.timeoutMs || "120000",
|
|
140
|
+
networkPolicy: helperRow?.networkPolicy || "workspace-only",
|
|
141
|
+
workspaceRead: helperRow?.workspaceRead !== "disabled",
|
|
142
|
+
proposalApply: helperRow?.proposalApply !== "disabled",
|
|
143
|
+
sandboxRun: helperRow?.sandboxRun !== "disabled",
|
|
144
|
+
});
|
|
145
|
+
const [saving, setSaving] = useState(false);
|
|
146
|
+
const [error, setError] = useState("");
|
|
147
|
+
|
|
148
|
+
useEffect(() => {
|
|
149
|
+
if (!open) return;
|
|
150
|
+
setStep(1);
|
|
151
|
+
setDraft({
|
|
152
|
+
agentHost: helperRow?.agentHost || "codex_local",
|
|
153
|
+
runLocality: helperRow?.runLocality || "local",
|
|
154
|
+
runtime: helperRow?.runtime || "node",
|
|
155
|
+
adapter: helperRow?.adapter || "local-agent-host",
|
|
156
|
+
timeoutMs: helperRow?.timeoutMs || "120000",
|
|
157
|
+
networkPolicy: helperRow?.networkPolicy || "workspace-only",
|
|
158
|
+
workspaceRead: helperRow?.workspaceRead !== "disabled",
|
|
159
|
+
proposalApply: helperRow?.proposalApply !== "disabled",
|
|
160
|
+
sandboxRun: helperRow?.sandboxRun !== "disabled",
|
|
161
|
+
});
|
|
162
|
+
setSaving(false);
|
|
163
|
+
setError("");
|
|
164
|
+
}, [open, helperRow?.adapter, helperRow?.agentHost, helperRow?.networkPolicy, helperRow?.proposalApply, helperRow?.runLocality, helperRow?.runtime, helperRow?.sandboxRun, helperRow?.timeoutMs, helperRow?.workspaceRead]);
|
|
165
|
+
|
|
166
|
+
if (!open) return null;
|
|
167
|
+
|
|
168
|
+
async function saveSetup() {
|
|
169
|
+
if (saving) return;
|
|
170
|
+
setSaving(true);
|
|
171
|
+
setError("");
|
|
172
|
+
try {
|
|
173
|
+
const next = upsertHelperSandbox(workspaceConfig || {}, draft);
|
|
174
|
+
const res = await fetch("/api/workspace", {
|
|
175
|
+
method: "PATCH",
|
|
176
|
+
headers: { "content-type": "application/json" },
|
|
177
|
+
body: JSON.stringify({ dataModel: next.dataModel }),
|
|
178
|
+
});
|
|
179
|
+
const body = await res.json().catch(() => ({}));
|
|
180
|
+
if (!res.ok) throw new Error(body?.error || "Could not save helper setup");
|
|
181
|
+
onSaved?.(body?.workspaceConfig || next);
|
|
182
|
+
} catch (err) {
|
|
183
|
+
setError(err?.message || "Could not save helper setup");
|
|
184
|
+
setSaving(false);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
return (
|
|
189
|
+
<div className="workspace-helper-setup-modal-backdrop" role="presentation">
|
|
190
|
+
<div className="workspace-helper-setup-modal" role="dialog" aria-modal="true" aria-label="Set up workspace helper">
|
|
191
|
+
<button type="button" className="workspace-helper-setup-modal-close" onClick={onClose} aria-label="Close setup">
|
|
192
|
+
<X size={16} aria-hidden="true" />
|
|
193
|
+
</button>
|
|
194
|
+
<div className="workspace-helper-setup-breadcrumbs" aria-label="Setup steps">
|
|
195
|
+
<span className={step === 1 ? "active" : ""}>1. Helper</span>
|
|
196
|
+
<span className={step === 2 ? "active" : ""}>2. Agent</span>
|
|
197
|
+
<span className={step === 3 ? "active" : ""}>3. Start</span>
|
|
198
|
+
</div>
|
|
199
|
+
{step === 1 ? (
|
|
200
|
+
<section className="workspace-helper-setup-step">
|
|
201
|
+
<p className="workspace-helper-setup-eyebrow">Helper setup</p>
|
|
202
|
+
<h2>Make the helper the workspace operator</h2>
|
|
203
|
+
<p>The helper uses one sandbox config and the same widget you already use.</p>
|
|
204
|
+
<div className="workspace-helper-setup-card-grid">
|
|
205
|
+
<label className="workspace-helper-setup-toggle">
|
|
206
|
+
<input
|
|
207
|
+
type="checkbox"
|
|
208
|
+
checked={draft.workspaceRead}
|
|
209
|
+
onChange={(e) => setDraft((d) => ({ ...d, workspaceRead: e.target.checked }))}
|
|
210
|
+
/>
|
|
211
|
+
<span><strong>Workspace context</strong><small>Read config, lenses, objects, and receipts.</small></span>
|
|
212
|
+
</label>
|
|
213
|
+
<label className="workspace-helper-setup-toggle">
|
|
214
|
+
<input
|
|
215
|
+
type="checkbox"
|
|
216
|
+
checked={draft.proposalApply}
|
|
217
|
+
onChange={(e) => setDraft((d) => ({ ...d, proposalApply: e.target.checked }))}
|
|
218
|
+
/>
|
|
219
|
+
<span><strong>Approve applies</strong><small>Draft changes first; user approves writes.</small></span>
|
|
220
|
+
</label>
|
|
221
|
+
<label className="workspace-helper-setup-toggle">
|
|
222
|
+
<input
|
|
223
|
+
type="checkbox"
|
|
224
|
+
checked={draft.sandboxRun}
|
|
225
|
+
onChange={(e) => setDraft((d) => ({ ...d, sandboxRun: e.target.checked }))}
|
|
226
|
+
/>
|
|
227
|
+
<span><strong>Sandbox runs</strong><small>Use the governed sandbox-run surface.</small></span>
|
|
228
|
+
</label>
|
|
229
|
+
</div>
|
|
230
|
+
</section>
|
|
231
|
+
) : step === 2 ? (
|
|
232
|
+
<section className="workspace-helper-setup-step">
|
|
233
|
+
<p className="workspace-helper-setup-eyebrow">Agent</p>
|
|
234
|
+
<h2>Configure the helper sandbox</h2>
|
|
235
|
+
<div className="workspace-helper-setup-radio-group" aria-label="Where it runs">
|
|
236
|
+
<span>Where it runs</span>
|
|
237
|
+
<label>
|
|
238
|
+
<input
|
|
239
|
+
type="radio"
|
|
240
|
+
name="helper-run-locality"
|
|
241
|
+
checked={draft.runLocality === "local"}
|
|
242
|
+
onChange={() => setDraft((d) => ({ ...d, runLocality: "local" }))}
|
|
243
|
+
/>
|
|
244
|
+
local
|
|
245
|
+
</label>
|
|
246
|
+
<label>
|
|
247
|
+
<input
|
|
248
|
+
type="radio"
|
|
249
|
+
name="helper-run-locality"
|
|
250
|
+
checked={draft.runLocality === "serverless"}
|
|
251
|
+
onChange={() => setDraft((d) => ({ ...d, runLocality: "serverless" }))}
|
|
252
|
+
/>
|
|
253
|
+
serverless
|
|
254
|
+
</label>
|
|
255
|
+
<small>Local uses process sandbox or Paperclip agent host on this machine. Serverless delegates to an API Registry URL.</small>
|
|
256
|
+
</div>
|
|
257
|
+
<div className="workspace-helper-setup-field-stack">
|
|
258
|
+
<label>
|
|
259
|
+
Execution adapter
|
|
260
|
+
<select
|
|
261
|
+
value={draft.adapter}
|
|
262
|
+
onChange={(e) => setDraft((d) => ({ ...d, adapter: e.target.value }))}
|
|
263
|
+
>
|
|
264
|
+
{HELPER_EXECUTION_ADAPTERS.map((adapter) => (
|
|
265
|
+
<option key={adapter.id} value={adapter.id}>{adapter.label}</option>
|
|
266
|
+
))}
|
|
267
|
+
</select>
|
|
268
|
+
</label>
|
|
269
|
+
<label>
|
|
270
|
+
Agent host (Paperclip)
|
|
271
|
+
<select
|
|
272
|
+
value={draft.agentHost}
|
|
273
|
+
onChange={(e) => setDraft((d) => ({ ...d, agentHost: e.target.value }))}
|
|
274
|
+
>
|
|
275
|
+
{HELPER_AGENT_CHOICES.map((choice) => (
|
|
276
|
+
<option key={choice.id} value={choice.id}>{choice.label}</option>
|
|
277
|
+
))}
|
|
278
|
+
</select>
|
|
279
|
+
</label>
|
|
280
|
+
<div className="workspace-helper-setup-two-col">
|
|
281
|
+
<label>
|
|
282
|
+
Runtime
|
|
283
|
+
<select
|
|
284
|
+
value={draft.runtime}
|
|
285
|
+
onChange={(e) => setDraft((d) => ({ ...d, runtime: e.target.value }))}
|
|
286
|
+
>
|
|
287
|
+
<option value="node">node</option>
|
|
288
|
+
<option value="shell">shell</option>
|
|
289
|
+
</select>
|
|
290
|
+
</label>
|
|
291
|
+
<label>
|
|
292
|
+
Timeout
|
|
293
|
+
<select
|
|
294
|
+
value={draft.timeoutMs}
|
|
295
|
+
onChange={(e) => setDraft((d) => ({ ...d, timeoutMs: e.target.value }))}
|
|
296
|
+
>
|
|
297
|
+
<option value="60000">1 minute</option>
|
|
298
|
+
<option value="120000">2 minutes</option>
|
|
299
|
+
<option value="300000">5 minutes</option>
|
|
300
|
+
</select>
|
|
301
|
+
</label>
|
|
302
|
+
<label>
|
|
303
|
+
Network
|
|
304
|
+
<select
|
|
305
|
+
value={draft.networkPolicy}
|
|
306
|
+
onChange={(e) => setDraft((d) => ({ ...d, networkPolicy: e.target.value }))}
|
|
307
|
+
>
|
|
308
|
+
<option value="workspace-only">Workspace only</option>
|
|
309
|
+
<option value="local-network">Local network</option>
|
|
310
|
+
<option value="egress-allowed">Allow egress</option>
|
|
311
|
+
</select>
|
|
312
|
+
</label>
|
|
313
|
+
</div>
|
|
314
|
+
</div>
|
|
315
|
+
</section>
|
|
316
|
+
) : (
|
|
317
|
+
<section className="workspace-helper-setup-step">
|
|
318
|
+
<p className="workspace-helper-setup-eyebrow">Start</p>
|
|
319
|
+
<h2>Save and open helper</h2>
|
|
320
|
+
<dl className="workspace-helper-setup-review">
|
|
321
|
+
<div><dt>Helper</dt><dd>Workspace Helper widget</dd></div>
|
|
322
|
+
<div><dt>Sandbox</dt><dd>workspace-helper-sandbox</dd></div>
|
|
323
|
+
<div><dt>Adapter</dt><dd>{draft.adapter}</dd></div>
|
|
324
|
+
<div><dt>Agent host</dt><dd>{draft.agentHost}</dd></div>
|
|
325
|
+
<div><dt>Access</dt><dd>{draft.workspaceRead ? "workspace context" : "no workspace context"} · {draft.proposalApply ? "approve applies" : "no applies"} · {draft.sandboxRun ? "sandbox runs" : "no runs"}</dd></div>
|
|
326
|
+
<div><dt>Runtime</dt><dd>{draft.runLocality} · {draft.runtime} · {draft.networkPolicy}</dd></div>
|
|
327
|
+
</dl>
|
|
328
|
+
{error ? <p className="workspace-helper-setup-error" role="alert">{error}</p> : null}
|
|
329
|
+
</section>
|
|
330
|
+
)}
|
|
331
|
+
<div className="workspace-helper-setup-actions">
|
|
332
|
+
<button type="button" onClick={() => step === 1 ? onClose?.() : setStep((s) => s - 1)}>
|
|
333
|
+
{step === 1 ? "Cancel" : "Back"}
|
|
334
|
+
</button>
|
|
335
|
+
{step < 3 ? (
|
|
336
|
+
<button type="button" className="primary" onClick={() => setStep((s) => s + 1)}>Next</button>
|
|
337
|
+
) : (
|
|
338
|
+
<button type="button" className="primary" onClick={saveSetup} disabled={saving || !draft.agentHost}>
|
|
339
|
+
{saving ? "Saving..." : "Save & open helper"}
|
|
340
|
+
</button>
|
|
341
|
+
)}
|
|
342
|
+
</div>
|
|
343
|
+
</div>
|
|
344
|
+
</div>
|
|
345
|
+
);
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
export {
|
|
349
|
+
HELPER_SANDBOX_OBJECT_ID,
|
|
350
|
+
HELPER_HANDOFF_DISMISS_FLAG,
|
|
351
|
+
HELPER_AGENT_CHOICES,
|
|
352
|
+
getHelperSandboxRow,
|
|
353
|
+
isHelperHandoffDismissed,
|
|
354
|
+
isHelperConfigured,
|
|
355
|
+
upsertHelperSandbox,
|
|
356
|
+
WorkspaceHelperSetupModal,
|
|
357
|
+
};
|