@stigmer/react 3.6.0 → 3.7.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.
- package/billing/CreditLedgerTable.d.ts.map +1 -1
- package/billing/CreditLedgerTable.js +2 -7
- package/billing/CreditLedgerTable.js.map +1 -1
- package/composer/ComposerToolbar.d.ts +6 -1
- package/composer/ComposerToolbar.d.ts.map +1 -1
- package/composer/ComposerToolbar.js +2 -2
- package/composer/ComposerToolbar.js.map +1 -1
- package/composer/SessionComposer.d.ts +9 -0
- package/composer/SessionComposer.d.ts.map +1 -1
- package/composer/SessionComposer.js +13 -2
- package/composer/SessionComposer.js.map +1 -1
- package/execution/useCreateAgentExecution.d.ts +14 -0
- package/execution/useCreateAgentExecution.d.ts.map +1 -1
- package/execution/useCreateAgentExecution.js +5 -0
- package/execution/useCreateAgentExecution.js.map +1 -1
- package/index.d.ts +2 -2
- package/index.d.ts.map +1 -1
- package/index.js +6 -5
- package/index.js.map +1 -1
- package/internal/Pagination.d.ts +15 -0
- package/internal/Pagination.d.ts.map +1 -0
- package/internal/Pagination.js +20 -0
- package/internal/Pagination.js.map +1 -0
- package/models/ModelSelector.d.ts +26 -1
- package/models/ModelSelector.d.ts.map +1 -1
- package/models/ModelSelector.js +27 -6
- package/models/ModelSelector.js.map +1 -1
- package/models/index.d.ts +2 -0
- package/models/index.d.ts.map +1 -1
- package/models/index.js +1 -0
- package/models/index.js.map +1 -1
- package/models/registry.d.ts +10 -0
- package/models/registry.d.ts.map +1 -1
- package/models/registry.js +3 -0
- package/models/registry.js.map +1 -1
- package/models/service-tier.d.ts +27 -0
- package/models/service-tier.d.ts.map +1 -0
- package/models/service-tier.js +34 -0
- package/models/service-tier.js.map +1 -0
- package/package.json +4 -4
- package/schedule/ScheduleDetailView.d.ts +31 -8
- package/schedule/ScheduleDetailView.d.ts.map +1 -1
- package/schedule/ScheduleDetailView.js +441 -29
- package/schedule/ScheduleDetailView.js.map +1 -1
- package/schedule/ScheduleForm.d.ts +7 -4
- package/schedule/ScheduleForm.d.ts.map +1 -1
- package/schedule/ScheduleForm.js +93 -8
- package/schedule/ScheduleForm.js.map +1 -1
- package/schedule/ScheduleRunsTable.d.ts +72 -0
- package/schedule/ScheduleRunsTable.d.ts.map +1 -0
- package/schedule/ScheduleRunsTable.js +161 -0
- package/schedule/ScheduleRunsTable.js.map +1 -0
- package/schedule/index.d.ts +6 -0
- package/schedule/index.d.ts.map +1 -1
- package/schedule/index.js +3 -0
- package/schedule/index.js.map +1 -1
- package/schedule/useScheduleRuns.d.ts +44 -0
- package/schedule/useScheduleRuns.d.ts.map +1 -0
- package/schedule/useScheduleRuns.js +48 -0
- package/schedule/useScheduleRuns.js.map +1 -0
- package/schedule/useTriggerSchedule.d.ts +23 -10
- package/schedule/useTriggerSchedule.d.ts.map +1 -1
- package/schedule/useTriggerSchedule.js +28 -9
- package/schedule/useTriggerSchedule.js.map +1 -1
- package/schedule/useUpdateScheduleSpec.d.ts +49 -0
- package/schedule/useUpdateScheduleSpec.d.ts.map +1 -0
- package/schedule/useUpdateScheduleSpec.js +70 -0
- package/schedule/useUpdateScheduleSpec.js.map +1 -0
- package/session/useNewSessionFlow.d.ts.map +1 -1
- package/session/useNewSessionFlow.js +1 -0
- package/session/useNewSessionFlow.js.map +1 -1
- package/session/useSessionConversation.d.ts +11 -0
- package/session/useSessionConversation.d.ts.map +1 -1
- package/session/useSessionConversation.js +1 -0
- package/session/useSessionConversation.js.map +1 -1
- package/session/useSessionPageFlow.d.ts.map +1 -1
- package/session/useSessionPageFlow.js +1 -0
- package/session/useSessionPageFlow.js.map +1 -1
- package/src/billing/CreditLedgerTable.tsx +3 -42
- package/src/composer/ComposerToolbar.tsx +9 -0
- package/src/composer/SessionComposer.tsx +24 -1
- package/src/composer/__tests__/SessionComposer-serviceTier.test.tsx +156 -0
- package/src/execution/useCreateAgentExecution.ts +18 -0
- package/src/index.ts +9 -4
- package/src/internal/Pagination.tsx +72 -0
- package/src/models/ModelSelector.tsx +100 -3
- package/src/models/__tests__/ModelSelector-serviceTier.test.tsx +172 -0
- package/src/models/__tests__/useModelRegistry.test.tsx +15 -0
- package/src/models/index.ts +2 -0
- package/src/models/registry.ts +16 -0
- package/src/models/service-tier.ts +49 -0
- package/src/schedule/ScheduleDetailView.tsx +1179 -111
- package/src/schedule/ScheduleForm.tsx +217 -7
- package/src/schedule/ScheduleRunsTable.tsx +424 -0
- package/src/schedule/__tests__/ScheduleDetailView.test.tsx +431 -8
- package/src/schedule/__tests__/scheduleCreation.test.tsx +197 -0
- package/src/schedule/__tests__/scheduleHooks.test.tsx +94 -7
- package/src/schedule/index.ts +12 -0
- package/src/schedule/useScheduleRuns.ts +93 -0
- package/src/schedule/useTriggerSchedule.ts +39 -14
- package/src/schedule/useUpdateScheduleSpec.ts +108 -0
- package/src/session/useNewSessionFlow.ts +1 -0
- package/src/session/useSessionConversation.ts +12 -0
- package/src/session/useSessionPageFlow.ts +1 -0
- package/src/workflow/__tests__/inspector-forms.test.tsx +119 -2
- package/src/workflow/inspector/forms/AgentCallForm.tsx +260 -37
- package/src/workflow/inspector/tabs/RuntimeTab.tsx +72 -33
- package/src/workflow/starter-workflow-yaml.ts +2 -1
- package/styles.css +1 -1
- package/workflow/inspector/forms/AgentCallForm.d.ts +18 -2
- package/workflow/inspector/forms/AgentCallForm.d.ts.map +1 -1
- package/workflow/inspector/forms/AgentCallForm.js +87 -9
- package/workflow/inspector/forms/AgentCallForm.js.map +1 -1
- package/workflow/inspector/tabs/RuntimeTab.d.ts +2 -1
- package/workflow/inspector/tabs/RuntimeTab.d.ts.map +1 -1
- package/workflow/inspector/tabs/RuntimeTab.js +31 -5
- package/workflow/inspector/tabs/RuntimeTab.js.map +1 -1
- package/workflow/starter-workflow-yaml.d.ts +1 -1
- package/workflow/starter-workflow-yaml.d.ts.map +1 -1
- package/workflow/starter-workflow-yaml.js +2 -1
- package/workflow/starter-workflow-yaml.js.map +1 -1
|
@@ -2,12 +2,24 @@
|
|
|
2
2
|
|
|
3
3
|
import { useCallback, useState, type FormEvent } from "react";
|
|
4
4
|
import { cn } from "@stigmer/theme";
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
getUserMessage,
|
|
7
|
+
type ResourceRef,
|
|
8
|
+
type RunConfigInput,
|
|
9
|
+
} from "@stigmer/sdk";
|
|
6
10
|
import type { Schedule } from "@stigmer/protos/ai/stigmer/agentic/schedule/v1/api_pb";
|
|
11
|
+
import { ApiResourceVisibility } from "@stigmer/protos/ai/stigmer/commons/apiresource/enum_pb";
|
|
7
12
|
import { Popover } from "@base-ui/react/popover";
|
|
8
13
|
import { AgentPicker } from "../agent/AgentPicker.js";
|
|
14
|
+
import { EnvironmentPicker } from "../environment/EnvironmentPicker.js";
|
|
15
|
+
import { useGitHubConnection } from "../github/useGitHubConnection.js";
|
|
16
|
+
import { ModelSelector } from "../models/ModelSelector.js";
|
|
17
|
+
import { toProtoHarness, type HarnessOption } from "../models/harness.js";
|
|
18
|
+
import { toProtoServiceTier, type ServiceTierOption } from "../models/service-tier.js";
|
|
9
19
|
import { Switch } from "../switch/Switch.js";
|
|
10
20
|
import { useStigmerPortalContainer } from "../portal-container.js";
|
|
21
|
+
import { useWorkspaceEntries } from "../workspace/useWorkspaceEntries.js";
|
|
22
|
+
import { WorkspaceEditor } from "../workspace/WorkspaceEditor.js";
|
|
11
23
|
import { CadenceField } from "./CadenceField.js";
|
|
12
24
|
import { TimeZoneField, browserTimeZone } from "./TimeZoneField.js";
|
|
13
25
|
import { useCreateSchedule } from "./useCreateSchedule.js";
|
|
@@ -33,7 +45,7 @@ export interface ScheduleFormProps {
|
|
|
33
45
|
readonly className?: string;
|
|
34
46
|
}
|
|
35
47
|
|
|
36
|
-
/**
|
|
48
|
+
/** invocation.proto pins AgentInvocation.message to 8192 characters. */
|
|
37
49
|
const MESSAGE_MAX_LEN = 8192;
|
|
38
50
|
/** Show the remaining-characters counter once within this margin. */
|
|
39
51
|
const MESSAGE_COUNTER_THRESHOLD = 500;
|
|
@@ -41,10 +53,13 @@ const MESSAGE_COUNTER_THRESHOLD = 500;
|
|
|
41
53
|
/**
|
|
42
54
|
* Single-page form for creating a {@link Schedule}.
|
|
43
55
|
*
|
|
44
|
-
*
|
|
45
|
-
* agent, what message,
|
|
46
|
-
*
|
|
47
|
-
*
|
|
56
|
+
* The form speaks the composer's vocabulary (DD-018 D-5): a schedule is
|
|
57
|
+
* a saved run — which agent, what message, what workspace, which
|
|
58
|
+
* engine and model, what budget — plus a cadence. It reuses the
|
|
59
|
+
* composer's own pickers ({@link ModelSelector}, {@link WorkspaceEditor})
|
|
60
|
+
* so the interactive and scheduled surfaces cannot drift, minus only
|
|
61
|
+
* what unattended makes impossible (no local folders, no attachments,
|
|
62
|
+
* no approval-mode choice).
|
|
48
63
|
*
|
|
49
64
|
* Enabled defaults to OFF, deliberately: it matches the proto/YAML
|
|
50
65
|
* default (an omitted `enabled` is false) and the platform's
|
|
@@ -83,6 +98,32 @@ export function ScheduleForm({
|
|
|
83
98
|
const [timeZone, setTimeZone] = useState(browserTimeZone);
|
|
84
99
|
const [enabled, setEnabled] = useState(false);
|
|
85
100
|
const [pickerOpen, setPickerOpen] = useState(false);
|
|
101
|
+
const [environmentRefs, setEnvironmentRefs] = useState<ResourceRef[]>([]);
|
|
102
|
+
|
|
103
|
+
// Engine & model are one atomic choice: picking a model pins BOTH the
|
|
104
|
+
// harness and the model (the registry scopes models per harness, so a
|
|
105
|
+
// model without its harness is meaningless). Empty model = nothing
|
|
106
|
+
// pinned = the platform defaults apply to both. Browsing harnesses in
|
|
107
|
+
// the popover without picking a model pins nothing.
|
|
108
|
+
const [modelName, setModelName] = useState("");
|
|
109
|
+
const [modelHarness, setModelHarness] = useState<HarnessOption>("cursor");
|
|
110
|
+
// Service tier rides the model choice (#357): the ModelSelector renders
|
|
111
|
+
// the fast toggle only for models whose registry entry prices a fast
|
|
112
|
+
// variant, and resets it when the user switches to one that does not.
|
|
113
|
+
// No model pinned = no tier pinned (fast requires a model, and the form's
|
|
114
|
+
// reset button clears both).
|
|
115
|
+
const [serviceTier, setServiceTier] = useState<ServiceTierOption>("standard");
|
|
116
|
+
|
|
117
|
+
const [budgetUsd, setBudgetUsd] = useState("");
|
|
118
|
+
|
|
119
|
+
// Workspace the fresh per-fire session clones — git sources only
|
|
120
|
+
// (write-time validated server-side: no client is connected at fire
|
|
121
|
+
// time to serve a local folder). The GitHub repo picker lights up
|
|
122
|
+
// when the user already has a connection; otherwise the editor's
|
|
123
|
+
// manual URL+branch input applies, with no OAuth flow forced into
|
|
124
|
+
// this form.
|
|
125
|
+
const workspace = useWorkspaceEntries();
|
|
126
|
+
const gitHub = useGitHubConnection(org);
|
|
86
127
|
|
|
87
128
|
const trimmedName = name.trim();
|
|
88
129
|
const trimmedMessage = message.trim();
|
|
@@ -108,6 +149,7 @@ export function ScheduleForm({
|
|
|
108
149
|
if (!canSubmit || !agentRef) return;
|
|
109
150
|
|
|
110
151
|
clearError();
|
|
152
|
+
const runConfig = buildRunConfig(modelName, budgetUsd, serviceTier);
|
|
111
153
|
try {
|
|
112
154
|
const schedule = await create({
|
|
113
155
|
name: trimmedName,
|
|
@@ -115,7 +157,20 @@ export function ScheduleForm({
|
|
|
115
157
|
cron,
|
|
116
158
|
timeZone,
|
|
117
159
|
enabled,
|
|
118
|
-
agent: {
|
|
160
|
+
agent: {
|
|
161
|
+
agentRef,
|
|
162
|
+
message: trimmedMessage,
|
|
163
|
+
// The harness travels with the model it was picked under;
|
|
164
|
+
// no model means no harness — the platform defaults apply.
|
|
165
|
+
...(modelName !== ""
|
|
166
|
+
? { harness: toProtoHarness(modelHarness) }
|
|
167
|
+
: {}),
|
|
168
|
+
...(workspace.hasEntries
|
|
169
|
+
? { workspaceEntries: workspace.toInput() }
|
|
170
|
+
: {}),
|
|
171
|
+
...(environmentRefs.length > 0 ? { environmentRefs } : {}),
|
|
172
|
+
...(runConfig ? { runConfig } : {}),
|
|
173
|
+
},
|
|
119
174
|
});
|
|
120
175
|
onComplete?.(schedule);
|
|
121
176
|
} catch {
|
|
@@ -133,6 +188,12 @@ export function ScheduleForm({
|
|
|
133
188
|
timeZone,
|
|
134
189
|
enabled,
|
|
135
190
|
trimmedMessage,
|
|
191
|
+
environmentRefs,
|
|
192
|
+
modelName,
|
|
193
|
+
modelHarness,
|
|
194
|
+
serviceTier,
|
|
195
|
+
budgetUsd,
|
|
196
|
+
workspace,
|
|
136
197
|
onComplete,
|
|
137
198
|
],
|
|
138
199
|
);
|
|
@@ -224,6 +285,53 @@ export function ScheduleForm({
|
|
|
224
285
|
)}
|
|
225
286
|
</div>
|
|
226
287
|
|
|
288
|
+
{/* Workspace — what each fire's fresh session operates on
|
|
289
|
+
(DD-018 D-4). Git sources only; the server refuses local
|
|
290
|
+
folders at write time because no client is connected when a
|
|
291
|
+
schedule fires. */}
|
|
292
|
+
<div className="space-y-1">
|
|
293
|
+
<span className={labelClasses}>
|
|
294
|
+
Workspace{" "}
|
|
295
|
+
<span className="font-normal text-muted-foreground">(optional)</span>
|
|
296
|
+
</span>
|
|
297
|
+
<WorkspaceEditor
|
|
298
|
+
workspace={workspace}
|
|
299
|
+
gitHubConnection={gitHub.isConnected ? gitHub : undefined}
|
|
300
|
+
enableGitHub
|
|
301
|
+
enableLocal={false}
|
|
302
|
+
disabled={isCreating}
|
|
303
|
+
/>
|
|
304
|
+
<p className={hintClasses}>
|
|
305
|
+
Each run clones these repositories fresh. Private repositories need
|
|
306
|
+
a <code className="font-mono">GITHUB_TOKEN</code> in one of this
|
|
307
|
+
schedule’s environments; public ones need nothing.
|
|
308
|
+
</p>
|
|
309
|
+
</div>
|
|
310
|
+
|
|
311
|
+
{/* Environments — how a tool-using agent becomes schedulable
|
|
312
|
+
(DD-017 D-2). Only org-shared environments resolve for a
|
|
313
|
+
schedule fire, so the picker is filtered to visibility_org —
|
|
314
|
+
the same credential surface a channel binding uses. */}
|
|
315
|
+
<div className="space-y-1">
|
|
316
|
+
<span id="stgm-new-schedule-env-label" className={labelClasses}>
|
|
317
|
+
Environments <span className="font-normal text-muted-foreground">(optional)</span>
|
|
318
|
+
</span>
|
|
319
|
+
<EnvironmentPicker
|
|
320
|
+
org={org}
|
|
321
|
+
value={environmentRefs}
|
|
322
|
+
onChange={setEnvironmentRefs}
|
|
323
|
+
disabled={isCreating}
|
|
324
|
+
filterEnvironment={(env) =>
|
|
325
|
+
env.metadata?.visibility === ApiResourceVisibility.visibility_org
|
|
326
|
+
}
|
|
327
|
+
/>
|
|
328
|
+
<p className={hintClasses}>
|
|
329
|
+
Bind org-shared credentials (for example an MCP server’s secret)
|
|
330
|
+
so the agent’s tools work on an unattended fire. Without this, an
|
|
331
|
+
agent whose tools need credentials will be refused every run.
|
|
332
|
+
</p>
|
|
333
|
+
</div>
|
|
334
|
+
|
|
227
335
|
{/* Cadence */}
|
|
228
336
|
<div className="space-y-1">
|
|
229
337
|
<span className={labelClasses}>Runs</span>
|
|
@@ -235,6 +343,75 @@ export function ScheduleForm({
|
|
|
235
343
|
/>
|
|
236
344
|
</div>
|
|
237
345
|
|
|
346
|
+
{/* Engine & model — the composer's own picker (DD-018 D-5),
|
|
347
|
+
replacing the free-text model box whose typos surfaced as
|
|
348
|
+
fire-time failures. Nothing is pinned until a model is picked. */}
|
|
349
|
+
<div className="space-y-1">
|
|
350
|
+
<span className={labelClasses}>
|
|
351
|
+
Engine & model{" "}
|
|
352
|
+
<span className="font-normal text-muted-foreground">(optional)</span>
|
|
353
|
+
</span>
|
|
354
|
+
<div className="flex items-center gap-2">
|
|
355
|
+
<ModelSelector
|
|
356
|
+
value={modelName}
|
|
357
|
+
onValueChange={setModelName}
|
|
358
|
+
initialHarness={modelHarness}
|
|
359
|
+
onHarnessChange={setModelHarness}
|
|
360
|
+
serviceTier={serviceTier}
|
|
361
|
+
onServiceTierChange={setServiceTier}
|
|
362
|
+
placeholderLabel="Platform default"
|
|
363
|
+
disabled={isCreating}
|
|
364
|
+
/>
|
|
365
|
+
{modelName !== "" && (
|
|
366
|
+
<button
|
|
367
|
+
type="button"
|
|
368
|
+
onClick={() => {
|
|
369
|
+
setModelName("");
|
|
370
|
+
setServiceTier("standard");
|
|
371
|
+
}}
|
|
372
|
+
disabled={isCreating}
|
|
373
|
+
className={cn(
|
|
374
|
+
"rounded-md px-2 py-1 text-[0.65rem] text-muted-foreground",
|
|
375
|
+
"hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
|
|
376
|
+
"disabled:pointer-events-none disabled:opacity-50",
|
|
377
|
+
)}
|
|
378
|
+
>
|
|
379
|
+
Reset to platform default
|
|
380
|
+
</button>
|
|
381
|
+
)}
|
|
382
|
+
</div>
|
|
383
|
+
<p className={hintClasses}>
|
|
384
|
+
Runs use the platform’s default engine and model unless you
|
|
385
|
+
pick one here. Picking a model pins the engine it belongs to.
|
|
386
|
+
</p>
|
|
387
|
+
</div>
|
|
388
|
+
|
|
389
|
+
{/* Budget — the one run bound that matters for an unattended
|
|
390
|
+
surface: nobody is watching a 3 AM fire (DD-018 D-5). Clamped
|
|
391
|
+
by the platform profile; tool-round bounds stay API-only. */}
|
|
392
|
+
<div className="space-y-1">
|
|
393
|
+
<label htmlFor="stgm-new-schedule-budget" className={labelClasses}>
|
|
394
|
+
Budget per run (USD){" "}
|
|
395
|
+
<span className="font-normal text-muted-foreground">(optional)</span>
|
|
396
|
+
</label>
|
|
397
|
+
<input
|
|
398
|
+
id="stgm-new-schedule-budget"
|
|
399
|
+
type="number"
|
|
400
|
+
min="0"
|
|
401
|
+
step="any"
|
|
402
|
+
value={budgetUsd}
|
|
403
|
+
onChange={(e) => setBudgetUsd(e.target.value)}
|
|
404
|
+
placeholder="platform default"
|
|
405
|
+
disabled={isCreating}
|
|
406
|
+
className={cn(inputClasses, "sm:max-w-48")}
|
|
407
|
+
/>
|
|
408
|
+
<p className={hintClasses}>
|
|
409
|
+
Each run stops when it reaches this spend. You can lower the
|
|
410
|
+
platform’s per-run cap, never raise it past the
|
|
411
|
+
platform’s ceiling.
|
|
412
|
+
</p>
|
|
413
|
+
</div>
|
|
414
|
+
|
|
238
415
|
{/* Time zone */}
|
|
239
416
|
<div className="space-y-1">
|
|
240
417
|
<label htmlFor="stgm-new-schedule-tz" className={labelClasses}>
|
|
@@ -318,6 +495,39 @@ export function ScheduleForm({
|
|
|
318
495
|
|
|
319
496
|
const labelClasses = "block text-xs font-medium text-foreground";
|
|
320
497
|
const hintClasses = "text-[0.65rem] text-muted-foreground";
|
|
498
|
+
|
|
499
|
+
/**
|
|
500
|
+
* Assemble a {@link RunConfigInput} from the model choice and budget, or
|
|
501
|
+
* `undefined` when both are unset — an all-empty run_config carries no
|
|
502
|
+
* meaning, and omitting it keeps the schedule on the platform defaults
|
|
503
|
+
* (the server's own "empty = inherit" contract, DD-017 D-3 as carried
|
|
504
|
+
* into DD-018 D-2). Non-numeric or negative budgets are dropped rather
|
|
505
|
+
* than sent; the proto's `gte = 0` constraint would reject them anyway,
|
|
506
|
+
* and a blank field must not become a zero override. `max_tool_rounds`
|
|
507
|
+
* is deliberately not collected here — an implementation knob, not a
|
|
508
|
+
* user concept; API-reachable for operators (DD-018 D-5).
|
|
509
|
+
*/
|
|
510
|
+
function buildRunConfig(
|
|
511
|
+
modelName: string,
|
|
512
|
+
budgetUsd: string,
|
|
513
|
+
serviceTier: ServiceTierOption,
|
|
514
|
+
): RunConfigInput | undefined {
|
|
515
|
+
const model = modelName.trim();
|
|
516
|
+
const cost = Number.parseFloat(budgetUsd);
|
|
517
|
+
|
|
518
|
+
const config: RunConfigInput = {};
|
|
519
|
+
if (model !== "") config.modelName = model;
|
|
520
|
+
if (Number.isFinite(cost) && cost > 0) config.maxCostUsd = cost;
|
|
521
|
+
// Carried only when the user actively chose fast AND pinned a model
|
|
522
|
+
// (fast is a per-model price; the server refuses it without one). An
|
|
523
|
+
// untouched toggle stays absent — unspecified-vs-explicit is a
|
|
524
|
+
// load-bearing ledger distinction (#357).
|
|
525
|
+
if (serviceTier === "fast" && model !== "") {
|
|
526
|
+
config.serviceTier = toProtoServiceTier(serviceTier);
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
return Object.keys(config).length > 0 ? config : undefined;
|
|
530
|
+
}
|
|
321
531
|
const inputClasses = cn(
|
|
322
532
|
"w-full rounded-md border border-input bg-background px-2.5 py-1.5 text-xs text-foreground",
|
|
323
533
|
"placeholder:text-muted-foreground",
|