@tangle-network/sandbox-ui 0.8.4 → 0.9.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/README.md +5 -6
- package/dist/auth.js +2 -2
- package/dist/chat.js +3 -3
- package/dist/{chunk-XXDFEF72.js → chunk-7YWFOGKQ.js} +51 -50
- package/dist/{chunk-VOUV7GGB.js → chunk-DI3NZ5ZX.js} +8 -22
- package/dist/{chunk-BUOQTBTO.js → chunk-JLKYXLFN.js} +1 -1
- package/dist/{chunk-2QZ6G7NM.js → chunk-MKTSMWVD.js} +5 -5
- package/dist/{chunk-UXQMIR3D.js → chunk-OHPW55EV.js} +1 -1
- package/dist/{chunk-WXK43R62.js → chunk-PLTZB5BC.js} +1 -1
- package/dist/{chunk-HFMAXUHV.js → chunk-QC4BJEG6.js} +2 -2
- package/dist/{chunk-3CJ2SOEI.js → chunk-US6JKJKH.js} +2 -2
- package/dist/{chunk-7U2Z23NE.js → chunk-VX3XOUEB.js} +15 -1
- package/dist/dashboard.d.ts +23 -3
- package/dist/dashboard.js +41 -4
- package/dist/globals.css +520 -1038
- package/dist/index.d.ts +1 -1
- package/dist/index.js +8 -8
- package/dist/openui.js +2 -2
- package/dist/pages.d.ts +70 -5
- package/dist/pages.js +1194 -135
- package/dist/primitives.js +2 -2
- package/dist/styles.css +520 -1038
- package/dist/tokens.css +248 -857
- package/dist/{variant-list-C8wx2TqF.d.ts → variant-list-CsS6ydgm.d.ts} +1 -1
- package/dist/workspace.d.ts +1 -1
- package/dist/workspace.js +4 -4
- package/package.json +16 -3
package/dist/pages.js
CHANGED
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
DialogFooter,
|
|
6
6
|
DialogHeader,
|
|
7
7
|
DialogTitle
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-US6JKJKH.js";
|
|
9
9
|
import {
|
|
10
10
|
Tabs,
|
|
11
11
|
TabsContent,
|
|
@@ -17,13 +17,14 @@ import {
|
|
|
17
17
|
Input
|
|
18
18
|
} from "./chunk-MA7YKRUP.js";
|
|
19
19
|
import {
|
|
20
|
+
InfoPanel,
|
|
20
21
|
TemplateCard
|
|
21
|
-
} from "./chunk-
|
|
22
|
+
} from "./chunk-VX3XOUEB.js";
|
|
22
23
|
import {
|
|
23
24
|
BillingDashboard,
|
|
24
25
|
PricingPage,
|
|
25
26
|
UsageChart
|
|
26
|
-
} from "./chunk-
|
|
27
|
+
} from "./chunk-OHPW55EV.js";
|
|
27
28
|
import {
|
|
28
29
|
Skeleton,
|
|
29
30
|
SkeletonCard
|
|
@@ -34,7 +35,7 @@ import {
|
|
|
34
35
|
} from "./chunk-ZMNSRDMH.js";
|
|
35
36
|
import {
|
|
36
37
|
Button
|
|
37
|
-
} from "./chunk-
|
|
38
|
+
} from "./chunk-MKTSMWVD.js";
|
|
38
39
|
import {
|
|
39
40
|
cn
|
|
40
41
|
} from "./chunk-RQHJBTEU.js";
|
|
@@ -297,10 +298,11 @@ function BillingPage({
|
|
|
297
298
|
|
|
298
299
|
// src/pages/provisioning-wizard.tsx
|
|
299
300
|
import * as React2 from "react";
|
|
300
|
-
import { ArrowLeft, Layers, Cpu, Bot, Info, Loader2, Settings, Plus, Trash2 } from "lucide-react";
|
|
301
|
+
import { ArrowLeft, Layers, Cpu, Bot, Info, Loader2, Settings, Plus, Trash2, Check } from "lucide-react";
|
|
301
302
|
import { Fragment as Fragment2, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
303
|
+
var VALID_DRIVERS = /* @__PURE__ */ new Set(["docker", "firecracker", "tangle"]);
|
|
302
304
|
var STACK_DISPLAY = {
|
|
303
|
-
universal: { name: "
|
|
305
|
+
universal: { name: "Default", abbr: "D", color: "violet", textClass: "text-[var(--surface-violet-text)]" },
|
|
304
306
|
ethereum: { name: "Ethereum", abbr: "\u039E", color: "blue", textClass: "text-[var(--surface-info-text)]" },
|
|
305
307
|
solana: { name: "Solana", abbr: "S", color: "green", textClass: "text-[var(--surface-success-text)]" },
|
|
306
308
|
tangle: { name: "Tangle", abbr: "T", color: "purple", textClass: "text-[var(--surface-violet-text)]" },
|
|
@@ -345,17 +347,28 @@ function ProvisioningWizard({
|
|
|
345
347
|
variant = "flat",
|
|
346
348
|
defaultEnvironment,
|
|
347
349
|
defaultConfig,
|
|
348
|
-
skipToReview
|
|
350
|
+
skipToReview,
|
|
351
|
+
onLoadStartupScripts
|
|
349
352
|
}) {
|
|
350
353
|
const dc = defaultConfig;
|
|
351
354
|
const [envList, setEnvList] = React2.useState(environmentsProp ?? defaultEnvironments);
|
|
355
|
+
const onLoadEnvironmentsRef = React2.useRef(onLoadEnvironments);
|
|
356
|
+
onLoadEnvironmentsRef.current = onLoadEnvironments;
|
|
352
357
|
React2.useEffect(() => {
|
|
353
|
-
|
|
354
|
-
|
|
358
|
+
let cancelled = false;
|
|
359
|
+
if (onLoadEnvironmentsRef.current) {
|
|
360
|
+
onLoadEnvironmentsRef.current().then((entries) => {
|
|
361
|
+
if (!cancelled) setEnvList(entries.map(resolveEnvironment));
|
|
362
|
+
}).catch((err) => {
|
|
363
|
+
if (!cancelled) setLoadError(err instanceof Error ? err.message : "Failed to load environments");
|
|
364
|
+
});
|
|
355
365
|
} else if (environmentsProp) {
|
|
356
366
|
setEnvList(environmentsProp);
|
|
357
367
|
}
|
|
358
|
-
|
|
368
|
+
return () => {
|
|
369
|
+
cancelled = true;
|
|
370
|
+
};
|
|
371
|
+
}, [environmentsProp]);
|
|
359
372
|
const environments = envList;
|
|
360
373
|
const effectiveDefault = dc?.environment ?? defaultEnvironment;
|
|
361
374
|
const [selectedEnv, setSelectedEnv] = React2.useState(effectiveDefault ?? environments[0]?.id ?? "");
|
|
@@ -374,13 +387,41 @@ function ProvisioningWizard({
|
|
|
374
387
|
const [envVars, setEnvVars] = React2.useState(dc?.envVars ?? [{ key: "", value: "" }]);
|
|
375
388
|
const [driver, setDriver] = React2.useState(dc?.driver ?? "docker");
|
|
376
389
|
const [bare, setBare] = React2.useState(dc?.bare ?? false);
|
|
390
|
+
const [startupScriptIds, setStartupScriptIds] = React2.useState(dc?.startupScriptIds ?? []);
|
|
391
|
+
const [availableScripts, setAvailableScripts] = React2.useState([]);
|
|
377
392
|
const [showAdvanced, setShowAdvanced] = React2.useState(false);
|
|
393
|
+
const [loadError, setLoadError] = React2.useState(null);
|
|
394
|
+
const onLoadStartupScriptsRef = React2.useRef(onLoadStartupScripts);
|
|
395
|
+
onLoadStartupScriptsRef.current = onLoadStartupScripts;
|
|
396
|
+
React2.useEffect(() => {
|
|
397
|
+
let cancelled = false;
|
|
398
|
+
if (onLoadStartupScriptsRef.current) {
|
|
399
|
+
onLoadStartupScriptsRef.current().then((scripts) => {
|
|
400
|
+
if (!cancelled) setAvailableScripts(scripts);
|
|
401
|
+
}).catch((err) => {
|
|
402
|
+
if (!cancelled) setLoadError(err instanceof Error ? err.message : "Failed to load startup scripts");
|
|
403
|
+
});
|
|
404
|
+
}
|
|
405
|
+
return () => {
|
|
406
|
+
cancelled = true;
|
|
407
|
+
};
|
|
408
|
+
}, []);
|
|
378
409
|
const isMultistep = variant === "multistep";
|
|
379
410
|
const [currentStep, setCurrentStep] = React2.useState(skipToReview && dc && isMultistep ? 3 : 1);
|
|
380
411
|
const [isDeploying, setIsDeploying] = React2.useState(false);
|
|
381
|
-
const
|
|
412
|
+
const [deployError, setDeployError] = React2.useState(null);
|
|
413
|
+
const handleDeploy = async () => {
|
|
414
|
+
if (!onSubmit) return;
|
|
382
415
|
setIsDeploying(true);
|
|
383
|
-
|
|
416
|
+
setDeployError(null);
|
|
417
|
+
try {
|
|
418
|
+
const validScriptIds = new Set(availableScripts.filter((s) => s.enabled).map((s) => s.id));
|
|
419
|
+
await onSubmit({ environment: selectedEnv, cpuCores, ramGB, storageGB, modelTier, systemPrompt, name, gitUrl, envVars: envVars.filter((e) => e.key.trim() !== ""), driver, bare, startupScriptIds: startupScriptIds.filter((id) => validScriptIds.has(id)) });
|
|
420
|
+
} catch (err) {
|
|
421
|
+
setDeployError(err instanceof Error ? err.message : "Deployment failed");
|
|
422
|
+
} finally {
|
|
423
|
+
setIsDeploying(false);
|
|
424
|
+
}
|
|
384
425
|
};
|
|
385
426
|
const applyPreset = (cpu, ram, storage) => {
|
|
386
427
|
setCpuCores(cpu);
|
|
@@ -398,18 +439,21 @@ function ProvisioningWizard({
|
|
|
398
439
|
] }),
|
|
399
440
|
/* @__PURE__ */ jsxs2("div", { className: "grid grid-cols-12 gap-6 flex-1 min-h-0", children: [
|
|
400
441
|
/* @__PURE__ */ jsxs2("div", { className: "col-span-12 xl:col-span-8 flex flex-col min-h-0", children: [
|
|
401
|
-
isMultistep && /* @__PURE__ */ jsx2("div", { className: "flex items-center gap-2 mb-4
|
|
402
|
-
/* @__PURE__ */ jsx2("div", { className: cn(
|
|
403
|
-
|
|
442
|
+
isMultistep && /* @__PURE__ */ jsx2("div", { className: "flex items-center gap-2 mb-4 bg-card border border-border p-3 rounded-2xl mx-auto max-w-2xl justify-between shrink-0", children: [1, 2, 3].map((s) => /* @__PURE__ */ jsxs2("div", { className: "flex items-center", children: [
|
|
443
|
+
/* @__PURE__ */ jsx2("div", { className: cn(
|
|
444
|
+
"w-7 h-7 rounded-full flex items-center justify-center font-bold text-xs shrink-0 transition-all duration-200",
|
|
445
|
+
currentStep === s ? "bg-primary text-primary-foreground ring-2 ring-primary/30 ring-offset-2 ring-offset-card shadow-sm" : currentStep > s ? "bg-primary text-primary-foreground" : "bg-muted border border-border text-muted-foreground"
|
|
446
|
+
), children: currentStep > s ? /* @__PURE__ */ jsx2(Check, { className: "h-3.5 w-3.5" }) : s }),
|
|
447
|
+
/* @__PURE__ */ jsxs2("span", { className: cn("ml-2 sm:ml-3 font-bold text-sm tracking-tight hidden sm:inline transition-colors duration-200", currentStep === s ? "text-foreground" : currentStep > s ? "text-primary" : "text-muted-foreground"), children: [
|
|
404
448
|
s === 1 && "Environment",
|
|
405
449
|
s === 2 && "Resources",
|
|
406
450
|
s === 3 && "AI Agent"
|
|
407
451
|
] }),
|
|
408
|
-
s < 3 && /* @__PURE__ */ jsx2("div", { className: cn("w-4 sm:w-8 h-
|
|
452
|
+
s < 3 && /* @__PURE__ */ jsx2("div", { className: cn("w-4 sm:w-8 h-0.5 mx-2 sm:mx-4 rounded-full transition-colors duration-300", currentStep > s ? "bg-primary" : "bg-border") })
|
|
409
453
|
] }, s)) }),
|
|
410
|
-
dc && isMultistep && /* @__PURE__ */ jsxs2("div", { className: "flex items-center justify-between
|
|
454
|
+
dc && isMultistep && /* @__PURE__ */ jsxs2("div", { className: "flex items-center justify-between bg-card border border-border rounded-2xl px-4 py-3 shrink-0", children: [
|
|
411
455
|
/* @__PURE__ */ jsxs2("div", { className: "flex items-center gap-2 text-sm", children: [
|
|
412
|
-
/* @__PURE__ */ jsx2(Info, { className: "h-4 w-4 text-
|
|
456
|
+
/* @__PURE__ */ jsx2(Info, { className: "h-4 w-4 text-primary shrink-0" }),
|
|
413
457
|
/* @__PURE__ */ jsx2("span", { className: "text-muted-foreground", children: "Pre-configured from template." })
|
|
414
458
|
] }),
|
|
415
459
|
/* @__PURE__ */ jsx2(
|
|
@@ -418,22 +462,32 @@ function ProvisioningWizard({
|
|
|
418
462
|
type: "button",
|
|
419
463
|
onClick: () => {
|
|
420
464
|
setCurrentStep(1);
|
|
465
|
+
setSelectedEnv(environments[0]?.id ?? "");
|
|
421
466
|
setCpuCores(4);
|
|
422
467
|
setRamGB(16);
|
|
423
468
|
setStorageGB(128);
|
|
424
469
|
setModelTier("claude-sonnet");
|
|
425
470
|
setSystemPrompt("");
|
|
471
|
+
setName("");
|
|
472
|
+
setGitUrl("");
|
|
473
|
+
setEnvVars([{ key: "", value: "" }]);
|
|
474
|
+
setDriver("docker");
|
|
426
475
|
setBare(false);
|
|
476
|
+
setStartupScriptIds([]);
|
|
427
477
|
},
|
|
428
|
-
className: "text-xs font-bold text-
|
|
478
|
+
className: "text-xs font-bold text-primary hover:text-primary/70 transition-colors",
|
|
429
479
|
children: "Start from scratch"
|
|
430
480
|
}
|
|
431
481
|
)
|
|
432
482
|
] }),
|
|
483
|
+
loadError && /* @__PURE__ */ jsxs2("div", { className: "rounded-2xl border border-destructive/30 bg-destructive/10 p-3 flex items-center gap-2 shrink-0", children: [
|
|
484
|
+
/* @__PURE__ */ jsx2(Info, { className: "h-4 w-4 text-destructive shrink-0" }),
|
|
485
|
+
/* @__PURE__ */ jsx2("p", { className: "text-sm font-medium text-destructive", children: loadError })
|
|
486
|
+
] }),
|
|
433
487
|
/* @__PURE__ */ jsxs2("div", { className: "flex-1 overflow-y-auto min-h-0 space-y-4", children: [
|
|
434
|
-
(!isMultistep || currentStep === 1) && /* @__PURE__ */ jsx2(React2.Fragment, { children: /* @__PURE__ */ jsxs2("section", { className: "
|
|
488
|
+
(!isMultistep || currentStep === 1) && /* @__PURE__ */ jsx2(React2.Fragment, { children: /* @__PURE__ */ jsxs2("section", { className: "bg-card border border-border rounded-[24px] p-6 shadow-2xl relative overflow-hidden animate-in fade-in slide-in-from-bottom-4 duration-300", children: [
|
|
435
489
|
/* @__PURE__ */ jsxs2("div", { className: "flex items-center gap-3 mb-6", children: [
|
|
436
|
-
/* @__PURE__ */ jsx2("div", { className: "flex h-9 w-9 shrink-0 items-center justify-center rounded-xl bg-
|
|
490
|
+
/* @__PURE__ */ jsx2("div", { className: "flex h-9 w-9 shrink-0 items-center justify-center rounded-xl bg-primary/10 border border-primary/20 text-primary", children: /* @__PURE__ */ jsx2(Layers, { className: "h-5 w-5" }) }),
|
|
437
491
|
/* @__PURE__ */ jsx2("h2", { className: "text-lg font-bold text-foreground tracking-tight", children: "Environment Selection" })
|
|
438
492
|
] }),
|
|
439
493
|
/* @__PURE__ */ jsx2("div", { className: "grid grid-cols-1 md:grid-cols-3 gap-3", children: environments.map((env) => /* @__PURE__ */ jsxs2(
|
|
@@ -442,14 +496,17 @@ function ProvisioningWizard({
|
|
|
442
496
|
type: "button",
|
|
443
497
|
onClick: () => setSelectedEnv(env.id),
|
|
444
498
|
className: cn(
|
|
445
|
-
"group relative p-4 rounded-[16px]
|
|
446
|
-
selectedEnv === env.id ? "
|
|
499
|
+
"group relative p-4 rounded-[16px] text-left overflow-hidden border transition-all duration-200",
|
|
500
|
+
selectedEnv === env.id ? "bg-primary/5 border-primary ring-2 ring-primary/20 shadow-md" : "bg-card border-border hover:border-primary/30 hover:shadow-sm hover:-translate-y-0.5 active:scale-[0.98]"
|
|
447
501
|
),
|
|
448
502
|
children: [
|
|
449
|
-
selectedEnv === env.id && /* @__PURE__ */ jsx2("div", { className: "absolute inset-0 bg-gradient-to-br from-
|
|
503
|
+
selectedEnv === env.id && /* @__PURE__ */ jsx2("div", { className: "absolute inset-0 bg-gradient-to-br from-primary/8 to-transparent pointer-events-none" }),
|
|
450
504
|
/* @__PURE__ */ jsxs2("div", { className: "flex justify-between items-start mb-3 relative z-10", children: [
|
|
451
505
|
/* @__PURE__ */ jsx2("div", { className: "w-10 h-10 rounded-full flex items-center justify-center bg-muted/50 border border-border shadow-inner", children: env.icon }),
|
|
452
|
-
/* @__PURE__ */ jsx2("div", { className: cn(
|
|
506
|
+
/* @__PURE__ */ jsx2("div", { className: cn(
|
|
507
|
+
"w-5 h-5 rounded-full border-2 flex items-center justify-center transition-all duration-200",
|
|
508
|
+
selectedEnv === env.id ? "border-primary bg-primary" : "border-border group-hover:border-primary/40"
|
|
509
|
+
), children: selectedEnv === env.id && /* @__PURE__ */ jsx2(Check, { className: "h-3 w-3 text-primary-foreground animate-in zoom-in duration-200" }) })
|
|
453
510
|
] }),
|
|
454
511
|
/* @__PURE__ */ jsx2("h3", { className: "font-bold text-sm mb-0.5 text-foreground relative z-10", children: env.name }),
|
|
455
512
|
/* @__PURE__ */ jsx2("p", { className: "text-xs text-muted-foreground leading-relaxed relative z-10", children: env.description })
|
|
@@ -458,24 +515,24 @@ function ProvisioningWizard({
|
|
|
458
515
|
env.id
|
|
459
516
|
)) })
|
|
460
517
|
] }) }),
|
|
461
|
-
(!isMultistep || currentStep === 2) && /* @__PURE__ */ jsx2(React2.Fragment, { children: /* @__PURE__ */ jsxs2("section", { className: "
|
|
518
|
+
(!isMultistep || currentStep === 2) && /* @__PURE__ */ jsx2(React2.Fragment, { children: /* @__PURE__ */ jsxs2("section", { className: "bg-card border border-border rounded-[24px] p-6 shadow-2xl relative overflow-hidden animate-in fade-in slide-in-from-bottom-4 duration-300", children: [
|
|
462
519
|
/* @__PURE__ */ jsxs2("div", { className: "flex items-center gap-3 mb-5", children: [
|
|
463
|
-
/* @__PURE__ */ jsx2("div", { className: "flex h-9 w-9 shrink-0 items-center justify-center rounded-xl bg-
|
|
520
|
+
/* @__PURE__ */ jsx2("div", { className: "flex h-9 w-9 shrink-0 items-center justify-center rounded-xl bg-primary/10 border border-primary/20 text-primary", children: /* @__PURE__ */ jsx2(Cpu, { className: "h-5 w-5" }) }),
|
|
464
521
|
/* @__PURE__ */ jsx2("h2", { className: "text-lg font-bold text-foreground tracking-tight", children: "Resource Allocation" })
|
|
465
522
|
] }),
|
|
466
523
|
/* @__PURE__ */ jsxs2("div", { className: "mb-6", children: [
|
|
467
524
|
/* @__PURE__ */ jsx2("label", { className: "block font-label text-xs font-bold uppercase tracking-widest text-muted-foreground mb-3", children: "Compute Presets" }),
|
|
468
525
|
/* @__PURE__ */ jsxs2("div", { className: "grid grid-cols-3 gap-3", children: [
|
|
469
|
-
/* @__PURE__ */ jsxs2("button", { type: "button", onClick: () => applyPreset(2, 4, 50), className: cn("p-3 rounded-[14px] transition-all text-center group", cpuCores === 2 && ramGB === 4 && storageGB === 50 ? "
|
|
470
|
-
/* @__PURE__ */ jsx2("div", { className: cn("font-bold text-sm transition-colors", cpuCores === 2 && ramGB === 4 && storageGB === 50 ? "text-
|
|
526
|
+
/* @__PURE__ */ jsxs2("button", { type: "button", onClick: () => applyPreset(2, 4, 50), className: cn("p-3 rounded-[14px] transition-all duration-200 text-center group border", cpuCores === 2 && ramGB === 4 && storageGB === 50 ? "bg-primary/5 border-primary ring-1 ring-primary/20 shadow-sm" : "bg-card border-border hover:border-primary/30 hover:shadow-sm active:scale-[0.97]"), children: [
|
|
527
|
+
/* @__PURE__ */ jsx2("div", { className: cn("font-bold text-sm transition-colors duration-200", cpuCores === 2 && ramGB === 4 && storageGB === 50 ? "text-primary" : "text-foreground"), children: "Lightweight" }),
|
|
471
528
|
/* @__PURE__ */ jsx2("div", { className: "text-xs text-muted-foreground mt-0.5 font-mono", children: "2C / 4G / 50G" })
|
|
472
529
|
] }),
|
|
473
|
-
/* @__PURE__ */ jsxs2("button", { type: "button", onClick: () => applyPreset(4, 16, 128), className: cn("p-3 rounded-[14px] transition-all text-center group", cpuCores === 4 && ramGB === 16 && storageGB === 128 ? "
|
|
474
|
-
/* @__PURE__ */ jsx2("div", { className: cn("font-bold text-sm transition-colors", cpuCores === 4 && ramGB === 16 && storageGB === 128 ? "text-
|
|
530
|
+
/* @__PURE__ */ jsxs2("button", { type: "button", onClick: () => applyPreset(4, 16, 128), className: cn("p-3 rounded-[14px] transition-all duration-200 text-center group border", cpuCores === 4 && ramGB === 16 && storageGB === 128 ? "bg-primary/5 border-primary ring-1 ring-primary/20 shadow-sm" : "bg-card border-border hover:border-primary/30 hover:shadow-sm active:scale-[0.97]"), children: [
|
|
531
|
+
/* @__PURE__ */ jsx2("div", { className: cn("font-bold text-sm transition-colors duration-200", cpuCores === 4 && ramGB === 16 && storageGB === 128 ? "text-primary" : "text-foreground"), children: "Standard" }),
|
|
475
532
|
/* @__PURE__ */ jsx2("div", { className: "text-xs text-muted-foreground mt-0.5 font-mono", children: "4C / 16G / 128G" })
|
|
476
533
|
] }),
|
|
477
|
-
/* @__PURE__ */ jsxs2("button", { type: "button", onClick: () => applyPreset(8, 32, 256), className: cn("p-3 rounded-[14px] transition-all text-center group", cpuCores === 8 && ramGB === 32 && storageGB === 256 ? "
|
|
478
|
-
/* @__PURE__ */ jsx2("div", { className: cn("font-bold text-sm transition-colors", cpuCores === 8 && ramGB === 32 && storageGB === 256 ? "text-
|
|
534
|
+
/* @__PURE__ */ jsxs2("button", { type: "button", onClick: () => applyPreset(8, 32, 256), className: cn("p-3 rounded-[14px] transition-all duration-200 text-center group border", cpuCores === 8 && ramGB === 32 && storageGB === 256 ? "bg-primary/5 border-primary ring-1 ring-primary/20 shadow-sm" : "bg-card border-border hover:border-primary/30 hover:shadow-sm active:scale-[0.97]"), children: [
|
|
535
|
+
/* @__PURE__ */ jsx2("div", { className: cn("font-bold text-sm transition-colors duration-200", cpuCores === 8 && ramGB === 32 && storageGB === 256 ? "text-primary" : "text-foreground"), children: "Performance" }),
|
|
479
536
|
/* @__PURE__ */ jsx2("div", { className: "text-xs text-muted-foreground mt-0.5 font-mono", children: "8C / 32G / 256G" })
|
|
480
537
|
] })
|
|
481
538
|
] })
|
|
@@ -490,7 +547,7 @@ function ProvisioningWizard({
|
|
|
490
547
|
/* @__PURE__ */ jsxs2("span", { className: "text-xl font-bold text-foreground tracking-tight", children: [
|
|
491
548
|
value,
|
|
492
549
|
" ",
|
|
493
|
-
/* @__PURE__ */ jsx2("span", { className: "text-xs text-
|
|
550
|
+
/* @__PURE__ */ jsx2("span", { className: "text-xs text-primary ml-1", children: unit })
|
|
494
551
|
] })
|
|
495
552
|
] }),
|
|
496
553
|
/* @__PURE__ */ jsx2(
|
|
@@ -502,7 +559,7 @@ function ProvisioningWizard({
|
|
|
502
559
|
step: s,
|
|
503
560
|
value,
|
|
504
561
|
onChange: (e) => setter(+e.target.value),
|
|
505
|
-
className: "w-full h-
|
|
562
|
+
className: "w-full h-2 rounded-full appearance-none cursor-pointer accent-primary [&::-webkit-slider-runnable-track]:bg-border [&::-webkit-slider-runnable-track]:rounded-full [&::-webkit-slider-runnable-track]:h-2 [&::-moz-range-track]:bg-border [&::-moz-range-track]:rounded-full [&::-moz-range-track]:h-2 [&::-webkit-slider-thumb]:bg-primary [&::-webkit-slider-thumb]:rounded-full [&::-webkit-slider-thumb]:h-5 [&::-webkit-slider-thumb]:w-5 [&::-webkit-slider-thumb]:appearance-none [&::-webkit-slider-thumb]:-mt-[6px] [&::-webkit-slider-thumb]:shadow-md [&::-webkit-slider-thumb]:border-2 [&::-webkit-slider-thumb]:border-primary-foreground [&::-webkit-slider-thumb]:transition-transform [&::-webkit-slider-thumb]:hover:scale-110"
|
|
506
563
|
}
|
|
507
564
|
),
|
|
508
565
|
/* @__PURE__ */ jsxs2("div", { className: "flex justify-between text-[10px] font-mono text-muted-foreground/60 mt-1", children: [
|
|
@@ -517,9 +574,9 @@ function ProvisioningWizard({
|
|
|
517
574
|
] })
|
|
518
575
|
] }, label)) })
|
|
519
576
|
] }) }),
|
|
520
|
-
(!isMultistep || currentStep === 3) && /* @__PURE__ */ jsx2(React2.Fragment, { children: /* @__PURE__ */ jsxs2("section", { className: "
|
|
577
|
+
(!isMultistep || currentStep === 3) && /* @__PURE__ */ jsx2(React2.Fragment, { children: /* @__PURE__ */ jsxs2("section", { className: "bg-card border border-border rounded-[24px] p-6 shadow-2xl relative overflow-hidden animate-in fade-in slide-in-from-bottom-4 duration-300", children: [
|
|
521
578
|
/* @__PURE__ */ jsxs2("div", { className: "flex items-center gap-3 mb-5", children: [
|
|
522
|
-
/* @__PURE__ */ jsx2("div", { className: "flex h-9 w-9 shrink-0 items-center justify-center rounded-xl bg-
|
|
579
|
+
/* @__PURE__ */ jsx2("div", { className: "flex h-9 w-9 shrink-0 items-center justify-center rounded-xl bg-primary/10 border border-primary/20 text-primary", children: /* @__PURE__ */ jsx2(Bot, { className: "h-5 w-5" }) }),
|
|
523
580
|
/* @__PURE__ */ jsx2("h2", { className: "text-lg font-bold text-foreground tracking-tight", children: "AI Agent Capability" })
|
|
524
581
|
] }),
|
|
525
582
|
/* @__PURE__ */ jsxs2("div", { className: "space-y-5", children: [
|
|
@@ -530,7 +587,7 @@ function ProvisioningWizard({
|
|
|
530
587
|
{
|
|
531
588
|
value: modelTier,
|
|
532
589
|
onChange: (e) => setModelTier(e.target.value),
|
|
533
|
-
className: "w-full
|
|
590
|
+
className: "w-full bg-card border border-border rounded-xl h-12 px-4 font-bold text-sm text-foreground focus:outline-none focus:ring-2 focus:ring-primary focus:border-transparent appearance-none",
|
|
534
591
|
children: [
|
|
535
592
|
/* @__PURE__ */ jsx2("option", { value: "llama-3-8b", className: "bg-gray-900", children: "Llama-3-8B-Instruct (Lightweight)" }),
|
|
536
593
|
/* @__PURE__ */ jsx2("option", { value: "mistral-7b", className: "bg-gray-900", children: "Mistral-7B-v0.2 (Efficient)" }),
|
|
@@ -546,7 +603,8 @@ function ProvisioningWizard({
|
|
|
546
603
|
{
|
|
547
604
|
value: systemPrompt,
|
|
548
605
|
onChange: (e) => setSystemPrompt(e.target.value),
|
|
549
|
-
|
|
606
|
+
maxLength: 1e4,
|
|
607
|
+
className: "w-full bg-card border border-border rounded-xl p-4 font-mono text-sm text-foreground focus:outline-none focus:ring-2 focus:ring-primary focus:border-transparent h-32 resize-none placeholder:text-muted-foreground",
|
|
550
608
|
placeholder: "Define the autonomous directives or operational boundaries..."
|
|
551
609
|
}
|
|
552
610
|
)
|
|
@@ -574,7 +632,8 @@ function ProvisioningWizard({
|
|
|
574
632
|
type: "text",
|
|
575
633
|
value: name,
|
|
576
634
|
onChange: (e) => setName(e.target.value),
|
|
577
|
-
|
|
635
|
+
maxLength: 128,
|
|
636
|
+
className: "w-full bg-card border border-border rounded-xl h-12 px-4 font-bold text-sm text-foreground focus:outline-none focus:ring-2 focus:ring-primary focus:border-transparent placeholder:text-muted-foreground",
|
|
578
637
|
placeholder: "my-cool-sandbox"
|
|
579
638
|
}
|
|
580
639
|
)
|
|
@@ -585,8 +644,10 @@ function ProvisioningWizard({
|
|
|
585
644
|
"select",
|
|
586
645
|
{
|
|
587
646
|
value: driver,
|
|
588
|
-
onChange: (e) =>
|
|
589
|
-
|
|
647
|
+
onChange: (e) => {
|
|
648
|
+
if (VALID_DRIVERS.has(e.target.value)) setDriver(e.target.value);
|
|
649
|
+
},
|
|
650
|
+
className: "w-full bg-card border border-border rounded-xl h-12 px-4 font-bold text-sm text-foreground focus:outline-none focus:ring-2 focus:ring-primary focus:border-transparent appearance-none",
|
|
590
651
|
children: [
|
|
591
652
|
/* @__PURE__ */ jsx2("option", { value: "docker", className: "bg-gray-900", children: "Docker container (Default)" }),
|
|
592
653
|
/* @__PURE__ */ jsx2("option", { value: "firecracker", className: "bg-gray-900", children: "Firecracker microVM (Secure)" }),
|
|
@@ -604,7 +665,7 @@ function ProvisioningWizard({
|
|
|
604
665
|
type: "text",
|
|
605
666
|
value: gitUrl,
|
|
606
667
|
onChange: (e) => setGitUrl(e.target.value),
|
|
607
|
-
className: "w-full
|
|
668
|
+
className: "w-full bg-card border border-border rounded-xl h-12 px-4 font-bold text-sm text-foreground focus:outline-none focus:ring-2 focus:ring-primary focus:border-transparent placeholder:text-muted-foreground",
|
|
608
669
|
placeholder: "https://github.com/my-org/my-repo.git"
|
|
609
670
|
}
|
|
610
671
|
)
|
|
@@ -612,7 +673,7 @@ function ProvisioningWizard({
|
|
|
612
673
|
/* @__PURE__ */ jsxs2("div", { children: [
|
|
613
674
|
/* @__PURE__ */ jsxs2("div", { className: "flex justify-between items-center mb-2", children: [
|
|
614
675
|
/* @__PURE__ */ jsx2("label", { className: "block font-label text-xs font-bold uppercase tracking-widest text-muted-foreground", children: "Environment Variables" }),
|
|
615
|
-
/* @__PURE__ */ jsxs2("button", { type: "button", onClick: () => setEnvVars([...envVars, { key: "", value: "" }]), className: "flex items-center gap-1 text-xs text-
|
|
676
|
+
/* @__PURE__ */ jsxs2("button", { type: "button", onClick: () => setEnvVars([...envVars, { key: "", value: "" }]), className: "flex items-center gap-1 text-xs text-primary hover:text-primary/70 transition-colors font-bold", children: [
|
|
616
677
|
/* @__PURE__ */ jsx2(Plus, { className: "h-3 w-3" }),
|
|
617
678
|
" Add Var"
|
|
618
679
|
] })
|
|
@@ -625,7 +686,7 @@ function ProvisioningWizard({
|
|
|
625
686
|
type: "text",
|
|
626
687
|
value: env.key,
|
|
627
688
|
onChange: (e) => setEnvVars(envVars.map((v, idx) => idx === i ? { ...v, key: e.target.value } : v)),
|
|
628
|
-
className: "flex-1
|
|
689
|
+
className: "flex-1 bg-card border border-border rounded-xl h-10 px-3 font-mono text-sm text-foreground focus:outline-none focus:ring-1 focus:ring-primary placeholder:text-muted-foreground",
|
|
629
690
|
placeholder: "API_KEY"
|
|
630
691
|
}
|
|
631
692
|
),
|
|
@@ -635,22 +696,52 @@ function ProvisioningWizard({
|
|
|
635
696
|
type: "password",
|
|
636
697
|
value: env.value,
|
|
637
698
|
onChange: (e) => setEnvVars(envVars.map((v, idx) => idx === i ? { ...v, value: e.target.value } : v)),
|
|
638
|
-
className: "flex-[2]
|
|
699
|
+
className: "flex-[2] bg-card border border-border rounded-xl h-10 px-3 font-mono text-sm text-foreground focus:outline-none focus:ring-1 focus:ring-primary placeholder:text-muted-foreground",
|
|
639
700
|
placeholder: "sk-xxxxxxxxxxx"
|
|
640
701
|
}
|
|
641
702
|
),
|
|
642
|
-
/* @__PURE__ */ jsx2("button", { type: "button", onClick: () => setEnvVars(envVars.filter((_, idx) => idx !== i)), className: "h-10 w-10 flex items-center justify-center shrink-0 rounded-xl
|
|
703
|
+
/* @__PURE__ */ jsx2("button", { type: "button", onClick: () => setEnvVars(envVars.filter((_, idx) => idx !== i)), className: "h-10 w-10 flex items-center justify-center shrink-0 rounded-xl bg-card border border-border text-red-400 hover:bg-red-500/10 hover:border-red-500/30 transition-colors", children: /* @__PURE__ */ jsx2(Trash2, { className: "h-4 w-4" }) })
|
|
643
704
|
] }, i)),
|
|
644
|
-
envVars.length === 0 && /* @__PURE__ */ jsx2("div", { className: "text-center p-3
|
|
705
|
+
envVars.length === 0 && /* @__PURE__ */ jsx2("div", { className: "text-center p-3 bg-card border border-border rounded-xl text-muted-foreground/60 text-sm italic", children: "No environment variables set" })
|
|
645
706
|
] })
|
|
646
707
|
] }),
|
|
708
|
+
availableScripts.length > 0 && /* @__PURE__ */ jsxs2("div", { children: [
|
|
709
|
+
/* @__PURE__ */ jsx2("div", { className: "text-xs font-bold uppercase tracking-widest text-muted-foreground mb-2", children: "Startup Scripts" }),
|
|
710
|
+
/* @__PURE__ */ jsx2("div", { className: "space-y-2", children: availableScripts.filter((s) => s.enabled).map((script) => {
|
|
711
|
+
const selected = startupScriptIds.includes(script.id);
|
|
712
|
+
return /* @__PURE__ */ jsxs2("label", { className: "flex items-start gap-3 cursor-pointer group rounded-lg border border-border p-3 transition-colors hover:border-primary/30", children: [
|
|
713
|
+
/* @__PURE__ */ jsx2(
|
|
714
|
+
"input",
|
|
715
|
+
{
|
|
716
|
+
type: "checkbox",
|
|
717
|
+
checked: selected,
|
|
718
|
+
onChange: () => setStartupScriptIds(
|
|
719
|
+
(prev) => selected ? prev.filter((id) => id !== script.id) : [...prev, script.id]
|
|
720
|
+
),
|
|
721
|
+
className: "mt-0.5 h-4 w-4 rounded border-border text-primary focus:ring-primary/30"
|
|
722
|
+
}
|
|
723
|
+
),
|
|
724
|
+
/* @__PURE__ */ jsxs2("div", { className: "flex-1 min-w-0", children: [
|
|
725
|
+
/* @__PURE__ */ jsx2("div", { className: "text-sm font-medium text-foreground group-hover:text-primary transition-colors", children: script.name }),
|
|
726
|
+
script.description && /* @__PURE__ */ jsx2("div", { className: "text-xs text-muted-foreground mt-0.5", children: script.description }),
|
|
727
|
+
script.injectSecrets.length > 0 && /* @__PURE__ */ jsx2("div", { className: "flex flex-wrap gap-1 mt-1.5", children: script.injectSecrets.map((s) => /* @__PURE__ */ jsxs2("span", { className: "inline-flex items-center gap-0.5 rounded-full bg-muted px-2 py-0.5 text-[10px] text-muted-foreground", children: [
|
|
728
|
+
/* @__PURE__ */ jsxs2("svg", { className: "h-2.5 w-2.5", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [
|
|
729
|
+
/* @__PURE__ */ jsx2("rect", { x: "3", y: "11", width: "18", height: "11", rx: "2", ry: "2" }),
|
|
730
|
+
/* @__PURE__ */ jsx2("path", { d: "M7 11V7a5 5 0 0 1 10 0v4" })
|
|
731
|
+
] }),
|
|
732
|
+
s
|
|
733
|
+
] }, s)) })
|
|
734
|
+
] })
|
|
735
|
+
] }, script.id);
|
|
736
|
+
}) })
|
|
737
|
+
] }),
|
|
647
738
|
/* @__PURE__ */ jsx2("div", { className: "pt-2 border-t border-border", children: /* @__PURE__ */ jsxs2("label", { className: "flex items-center gap-3 cursor-pointer group", children: [
|
|
648
739
|
/* @__PURE__ */ jsxs2("div", { className: "relative flex items-center justify-center shrink-0", children: [
|
|
649
740
|
/* @__PURE__ */ jsx2("input", { type: "checkbox", className: "sr-only peer", checked: bare, onChange: (e) => setBare(e.target.checked) }),
|
|
650
|
-
/* @__PURE__ */ jsx2("div", { className: "w-10 h-6 bg-muted peer-focus:outline-none rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-
|
|
741
|
+
/* @__PURE__ */ jsx2("div", { className: "w-10 h-6 bg-muted peer-focus:outline-none rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-primary hover:bg-muted/80 transition-colors" })
|
|
651
742
|
] }),
|
|
652
743
|
/* @__PURE__ */ jsxs2("div", { children: [
|
|
653
|
-
/* @__PURE__ */ jsx2("div", { className: "text-sm font-bold text-foreground mb-0.5 group-hover:text-
|
|
744
|
+
/* @__PURE__ */ jsx2("div", { className: "text-sm font-bold text-foreground mb-0.5 group-hover:text-primary transition-colors", children: "Bare Mode" }),
|
|
654
745
|
/* @__PURE__ */ jsx2("div", { className: "text-xs text-muted-foreground", children: "Start as a raw container without an embedded AI Agent backend." })
|
|
655
746
|
] })
|
|
656
747
|
] }) })
|
|
@@ -661,7 +752,7 @@ function ProvisioningWizard({
|
|
|
661
752
|
] })
|
|
662
753
|
] }),
|
|
663
754
|
/* @__PURE__ */ jsxs2("div", { className: "col-span-12 xl:col-span-4 sticky top-4 space-y-4", children: [
|
|
664
|
-
/* @__PURE__ */ jsxs2("div", { className: "
|
|
755
|
+
/* @__PURE__ */ jsxs2("div", { className: "bg-card border border-primary/15 rounded-[24px] overflow-hidden shadow-2xl relative", children: [
|
|
665
756
|
/* @__PURE__ */ jsx2("div", { className: "absolute inset-0 bg-[radial-gradient(circle_at_top,rgba(173,163,255,0.05)_0,transparent_100%)] pointer-events-none" }),
|
|
666
757
|
/* @__PURE__ */ jsxs2("div", { className: "bg-muted/50 border-b border-border px-4 py-3 flex items-center gap-3", children: [
|
|
667
758
|
/* @__PURE__ */ jsxs2("div", { className: "flex gap-2", children: [
|
|
@@ -678,54 +769,54 @@ function ProvisioningWizard({
|
|
|
678
769
|
] }),
|
|
679
770
|
/* @__PURE__ */ jsx2("div", { className: "text-muted-foreground/70", children: "Initializing deployment handshake..." }),
|
|
680
771
|
/* @__PURE__ */ jsxs2("div", { className: "text-foreground/70", children: [
|
|
681
|
-
/* @__PURE__ */ jsx2("span", { className: "text-
|
|
772
|
+
/* @__PURE__ */ jsx2("span", { className: "text-primary mr-2", children: "\u2713" }),
|
|
682
773
|
" Bound Platform: ",
|
|
683
|
-
/* @__PURE__ */ jsx2("span", { className: "text-foreground font-bold", children: environments.find((e) => e.id === selectedEnv)?.name ?? "Node.js" })
|
|
774
|
+
/* @__PURE__ */ jsx2("span", { className: "text-foreground font-bold animate-in fade-in duration-300", children: environments.find((e) => e.id === selectedEnv)?.name ?? "Node.js" }, `env-${selectedEnv}`)
|
|
684
775
|
] }),
|
|
685
776
|
/* @__PURE__ */ jsxs2("div", { className: "text-foreground/70", children: [
|
|
686
|
-
/* @__PURE__ */ jsx2("span", { className: "text-
|
|
777
|
+
/* @__PURE__ */ jsx2("span", { className: "text-primary mr-2", children: "\u2713" }),
|
|
687
778
|
" Allocation CPU: ",
|
|
688
|
-
/* @__PURE__ */ jsxs2("span", { className: "text-foreground font-bold", children: [
|
|
779
|
+
/* @__PURE__ */ jsxs2("span", { className: "text-foreground font-bold animate-in fade-in duration-300", children: [
|
|
689
780
|
cpuCores,
|
|
690
781
|
" Cores"
|
|
691
|
-
] })
|
|
782
|
+
] }, `cpu-${cpuCores}`)
|
|
692
783
|
] }),
|
|
693
784
|
/* @__PURE__ */ jsxs2("div", { className: "text-foreground/70", children: [
|
|
694
|
-
/* @__PURE__ */ jsx2("span", { className: "text-
|
|
785
|
+
/* @__PURE__ */ jsx2("span", { className: "text-primary mr-2", children: "\u2713" }),
|
|
695
786
|
" Allocation RAM: ",
|
|
696
|
-
/* @__PURE__ */ jsxs2("span", { className: "text-foreground font-bold", children: [
|
|
787
|
+
/* @__PURE__ */ jsxs2("span", { className: "text-foreground font-bold animate-in fade-in duration-300", children: [
|
|
697
788
|
ramGB,
|
|
698
789
|
"GB"
|
|
699
|
-
] })
|
|
790
|
+
] }, `ram-${ramGB}`)
|
|
700
791
|
] }),
|
|
701
792
|
/* @__PURE__ */ jsxs2("div", { className: "text-foreground/70", children: [
|
|
702
|
-
/* @__PURE__ */ jsx2("span", { className: "text-
|
|
793
|
+
/* @__PURE__ */ jsx2("span", { className: "text-primary mr-2", children: "\u2713" }),
|
|
703
794
|
" Mounted Storage: ",
|
|
704
|
-
/* @__PURE__ */ jsxs2("span", { className: "text-foreground font-bold", children: [
|
|
795
|
+
/* @__PURE__ */ jsxs2("span", { className: "text-foreground font-bold animate-in fade-in duration-300", children: [
|
|
705
796
|
storageGB,
|
|
706
797
|
"GB NVMe"
|
|
707
|
-
] })
|
|
798
|
+
] }, `disk-${storageGB}`)
|
|
708
799
|
] }),
|
|
709
800
|
/* @__PURE__ */ jsxs2("div", { className: "pt-3 flex items-center gap-3", children: [
|
|
710
|
-
/* @__PURE__ */ jsx2("div", { className: "w-2 h-4 bg-
|
|
801
|
+
/* @__PURE__ */ jsx2("div", { className: "w-2 h-4 bg-primary animate-pulse" }),
|
|
711
802
|
/* @__PURE__ */ jsx2("span", { className: "text-muted-foreground", children: "Awaiting user confirmation..." })
|
|
712
803
|
] })
|
|
713
804
|
] })
|
|
714
805
|
] }),
|
|
715
|
-
/* @__PURE__ */ jsxs2("div", { className: "p-6 rounded-
|
|
716
|
-
/* @__PURE__ */ jsx2("div", { className: "
|
|
806
|
+
/* @__PURE__ */ jsxs2("div", { className: "p-6 rounded-[24px] bg-card border border-primary/15 relative overflow-hidden", children: [
|
|
807
|
+
/* @__PURE__ */ jsx2("div", { className: "hidden" }),
|
|
717
808
|
/* @__PURE__ */ jsxs2("div", { className: "flex justify-between items-center mb-4 relative z-10", children: [
|
|
718
809
|
/* @__PURE__ */ jsx2("span", { className: "font-label text-xs font-bold uppercase tracking-widest text-muted-foreground", children: "Run Cost" }),
|
|
719
|
-
/* @__PURE__ */ jsx2("div", { className: "h-7 w-7 rounded-full bg-muted/30 flex items-center justify-center border border-border", children: /* @__PURE__ */ jsx2(Info, { className: "h-3.5 w-3.5 text-
|
|
810
|
+
/* @__PURE__ */ jsx2("div", { className: "h-7 w-7 rounded-full bg-muted/30 flex items-center justify-center border border-border", children: /* @__PURE__ */ jsx2(Info, { className: "h-3.5 w-3.5 text-primary" }) })
|
|
720
811
|
] }),
|
|
721
812
|
/* @__PURE__ */ jsxs2("div", { className: "flex items-baseline gap-2 mb-5 relative z-10", children: [
|
|
722
|
-
/* @__PURE__ */ jsxs2("span", { className: "text-4xl font-black text-foreground tracking-tighter", children: [
|
|
813
|
+
/* @__PURE__ */ jsxs2("span", { className: "text-4xl font-black text-foreground tracking-tighter animate-in fade-in duration-200", children: [
|
|
723
814
|
"$",
|
|
724
815
|
hourCost
|
|
725
|
-
] }),
|
|
816
|
+
] }, hourCost),
|
|
726
817
|
/* @__PURE__ */ jsx2("span", { className: "text-muted-foreground text-sm font-bold", children: "/ hour" })
|
|
727
818
|
] }),
|
|
728
|
-
/* @__PURE__ */ jsxs2("div", { className: "space-y-2 relative z-10
|
|
819
|
+
/* @__PURE__ */ jsxs2("div", { className: "space-y-2 relative z-10 bg-card border border-border rounded-xl p-3", children: [
|
|
729
820
|
/* @__PURE__ */ jsxs2("div", { className: "flex justify-between text-xs font-mono tracking-widest text-muted-foreground", children: [
|
|
730
821
|
/* @__PURE__ */ jsx2("span", { children: "COMPUTE" }),
|
|
731
822
|
/* @__PURE__ */ jsxs2("span", { className: "text-foreground", children: [
|
|
@@ -743,32 +834,33 @@ function ProvisioningWizard({
|
|
|
743
834
|
] })
|
|
744
835
|
] })
|
|
745
836
|
] }),
|
|
837
|
+
deployError && /* @__PURE__ */ jsxs2("div", { className: "rounded-xl border border-destructive/30 bg-destructive/10 p-3 flex items-center gap-2", children: [
|
|
838
|
+
/* @__PURE__ */ jsx2(Info, { className: "h-4 w-4 text-destructive shrink-0" }),
|
|
839
|
+
/* @__PURE__ */ jsx2("p", { className: "text-sm font-medium text-destructive", children: deployError })
|
|
840
|
+
] }),
|
|
746
841
|
/* @__PURE__ */ jsx2("div", { className: "space-y-3", children: isMultistep ? /* @__PURE__ */ jsxs2(Fragment2, { children: [
|
|
747
842
|
currentStep < 3 ? /* @__PURE__ */ jsxs2(
|
|
748
843
|
"button",
|
|
749
844
|
{
|
|
750
845
|
type: "button",
|
|
751
846
|
onClick: () => setCurrentStep((s) => s + 1),
|
|
752
|
-
className: "w-full relative overflow-hidden h-12 bg-
|
|
847
|
+
className: "w-full relative overflow-hidden h-12 bg-primary text-primary-foreground font-extrabold text-sm rounded-2xl hover:brightness-110 transition-all active:scale-[0.98] shadow-md",
|
|
753
848
|
children: [
|
|
754
849
|
"Continue to ",
|
|
755
850
|
currentStep === 1 ? "Resources" : "Agent Config"
|
|
756
851
|
]
|
|
757
852
|
}
|
|
758
|
-
) : /* @__PURE__ */
|
|
853
|
+
) : /* @__PURE__ */ jsx2(
|
|
759
854
|
"button",
|
|
760
855
|
{
|
|
761
856
|
type: "button",
|
|
762
857
|
onClick: handleDeploy,
|
|
763
858
|
disabled: isDeploying || !selectedEnv,
|
|
764
|
-
className: "w-full
|
|
765
|
-
children: [
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
] }) : /* @__PURE__ */ jsx2("span", { className: "relative z-10", children: "Deploy Workspace" }),
|
|
770
|
-
/* @__PURE__ */ jsx2("div", { className: "absolute inset-0 bg-white/20 opacity-0 hover:opacity-100 transition-opacity" })
|
|
771
|
-
]
|
|
859
|
+
className: "w-full h-12 bg-primary text-primary-foreground font-extrabold text-sm rounded-2xl tracking-wide shadow-md disabled:opacity-50 hover:brightness-110 active:scale-[0.98] transition-all",
|
|
860
|
+
children: isDeploying ? /* @__PURE__ */ jsxs2("span", { className: "flex items-center justify-center gap-2", children: [
|
|
861
|
+
/* @__PURE__ */ jsx2(Loader2, { className: "h-4 w-4 animate-spin" }),
|
|
862
|
+
"Deploying..."
|
|
863
|
+
] }) : "Deploy Workspace"
|
|
772
864
|
}
|
|
773
865
|
),
|
|
774
866
|
currentStep > 1 && /* @__PURE__ */ jsx2(
|
|
@@ -776,24 +868,21 @@ function ProvisioningWizard({
|
|
|
776
868
|
{
|
|
777
869
|
type: "button",
|
|
778
870
|
onClick: () => setCurrentStep((s) => s - 1),
|
|
779
|
-
className: "w-full h-10
|
|
871
|
+
className: "w-full h-10 bg-secondary text-secondary-foreground border border-border font-bold text-sm rounded-2xl hover:brightness-95 active:scale-[0.98] transition-all",
|
|
780
872
|
children: "Back"
|
|
781
873
|
}
|
|
782
874
|
)
|
|
783
|
-
] }) : /* @__PURE__ */
|
|
875
|
+
] }) : /* @__PURE__ */ jsx2(
|
|
784
876
|
"button",
|
|
785
877
|
{
|
|
786
878
|
type: "button",
|
|
787
879
|
onClick: handleDeploy,
|
|
788
880
|
disabled: isDeploying || !selectedEnv,
|
|
789
|
-
className: "w-full
|
|
790
|
-
children: [
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
] }) : /* @__PURE__ */ jsx2("span", { className: "relative z-10", children: "Deploy Workspace" }),
|
|
795
|
-
/* @__PURE__ */ jsx2("div", { className: "absolute inset-0 bg-white/20 opacity-0 hover:opacity-100 transition-opacity" })
|
|
796
|
-
]
|
|
881
|
+
className: "w-full h-12 bg-primary text-primary-foreground font-extrabold text-sm rounded-2xl tracking-wide shadow-md disabled:opacity-50 hover:brightness-110 active:scale-[0.98] transition-all",
|
|
882
|
+
children: isDeploying ? /* @__PURE__ */ jsxs2("span", { className: "flex items-center justify-center gap-2", children: [
|
|
883
|
+
/* @__PURE__ */ jsx2(Loader2, { className: "h-4 w-4 animate-spin" }),
|
|
884
|
+
"Spinning up environment..."
|
|
885
|
+
] }) : "Deploy Workspace"
|
|
797
886
|
}
|
|
798
887
|
) })
|
|
799
888
|
] })
|
|
@@ -833,6 +922,7 @@ function StandalonePricingPage({
|
|
|
833
922
|
fetchTiers,
|
|
834
923
|
title = "Simple, transparent pricing",
|
|
835
924
|
subtitle = "Choose the plan that fits your needs. Upgrade or downgrade at any time.",
|
|
925
|
+
eyebrow,
|
|
836
926
|
className
|
|
837
927
|
}) {
|
|
838
928
|
const [state, setState] = React3.useState({
|
|
@@ -889,7 +979,8 @@ function StandalonePricingPage({
|
|
|
889
979
|
);
|
|
890
980
|
return /* @__PURE__ */ jsxs3("div", { className: cn("mx-auto max-w-6xl px-6 py-16 space-y-16", className), children: [
|
|
891
981
|
/* @__PURE__ */ jsxs3("div", { className: "space-y-4 text-center", children: [
|
|
892
|
-
/* @__PURE__ */ jsx3("
|
|
982
|
+
eyebrow && /* @__PURE__ */ jsx3("span", { className: "text-xs font-bold uppercase tracking-widest text-[var(--brand-emerald,#10B981)]", children: eyebrow }),
|
|
983
|
+
/* @__PURE__ */ jsx3("h1", { className: "text-4xl font-extrabold tracking-tight text-foreground sm:text-5xl font-display", children: title }),
|
|
893
984
|
/* @__PURE__ */ jsx3("p", { className: "mx-auto max-w-2xl text-lg text-muted-foreground", children: subtitle })
|
|
894
985
|
] }),
|
|
895
986
|
state.loading ? /* @__PURE__ */ jsxs3("div", { className: "space-y-8", children: [
|
|
@@ -1577,7 +1668,7 @@ function ProfileDetailDialog({
|
|
|
1577
1668
|
|
|
1578
1669
|
// src/pages/secrets-page.tsx
|
|
1579
1670
|
import * as React5 from "react";
|
|
1580
|
-
import { Lock, Plus as Plus3, Trash2 as Trash23, Eye, EyeOff, AlertCircle as AlertCircle2 } from "lucide-react";
|
|
1671
|
+
import { Lock, Plus as Plus3, Trash2 as Trash23, Eye, EyeOff, AlertCircle as AlertCircle2, Key, Shield, CheckCircle } from "lucide-react";
|
|
1581
1672
|
import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
1582
1673
|
function SecretsPage({ apiClient, className }) {
|
|
1583
1674
|
const [secrets, setSecrets] = React5.useState([]);
|
|
@@ -1593,23 +1684,27 @@ function SecretsPage({ apiClient, className }) {
|
|
|
1593
1684
|
const [isDeleting, setIsDeleting] = React5.useState(false);
|
|
1594
1685
|
const apiRef = React5.useRef(apiClient);
|
|
1595
1686
|
apiRef.current = apiClient;
|
|
1596
|
-
const
|
|
1687
|
+
const loadGenRef = React5.useRef(0);
|
|
1688
|
+
const loadSecrets = React5.useCallback(async (showSpinner = true) => {
|
|
1689
|
+
const gen = ++loadGenRef.current;
|
|
1597
1690
|
try {
|
|
1598
|
-
setLoading(true);
|
|
1691
|
+
if (showSpinner) setLoading(true);
|
|
1599
1692
|
setError(null);
|
|
1600
1693
|
const data = await apiRef.current.listSecrets();
|
|
1694
|
+
if (gen !== loadGenRef.current) return;
|
|
1601
1695
|
setSecrets(data);
|
|
1602
1696
|
} catch (err) {
|
|
1697
|
+
if (gen !== loadGenRef.current) return;
|
|
1603
1698
|
setError(err instanceof Error ? err.message : "Failed to load secrets");
|
|
1604
1699
|
} finally {
|
|
1605
|
-
setLoading(false);
|
|
1700
|
+
if (gen === loadGenRef.current) setLoading(false);
|
|
1606
1701
|
}
|
|
1607
1702
|
}, []);
|
|
1608
1703
|
React5.useEffect(() => {
|
|
1609
1704
|
loadSecrets();
|
|
1610
1705
|
}, [loadSecrets]);
|
|
1611
1706
|
const handleCreate = async () => {
|
|
1612
|
-
if (!newName.trim()) return;
|
|
1707
|
+
if (!newName.trim() || !newValue.trim()) return;
|
|
1613
1708
|
setIsCreating(true);
|
|
1614
1709
|
setCreateError(null);
|
|
1615
1710
|
try {
|
|
@@ -1618,7 +1713,7 @@ function SecretsPage({ apiClient, className }) {
|
|
|
1618
1713
|
setNewName("");
|
|
1619
1714
|
setNewValue("");
|
|
1620
1715
|
setShowValue(false);
|
|
1621
|
-
loadSecrets();
|
|
1716
|
+
await loadSecrets(false);
|
|
1622
1717
|
} catch (err) {
|
|
1623
1718
|
setCreateError(err instanceof Error ? err.message : "Failed to create secret");
|
|
1624
1719
|
} finally {
|
|
@@ -1630,7 +1725,7 @@ function SecretsPage({ apiClient, className }) {
|
|
|
1630
1725
|
try {
|
|
1631
1726
|
await apiRef.current.deleteSecret(name);
|
|
1632
1727
|
setDeleteTarget(null);
|
|
1633
|
-
loadSecrets();
|
|
1728
|
+
await loadSecrets(false);
|
|
1634
1729
|
} catch (err) {
|
|
1635
1730
|
setError(err instanceof Error ? err.message : "Failed to delete secret");
|
|
1636
1731
|
} finally {
|
|
@@ -1642,31 +1737,53 @@ function SecretsPage({ apiClient, className }) {
|
|
|
1642
1737
|
const ts = /^\d+$/.test(dateStr) ? Number(dateStr) : dateStr;
|
|
1643
1738
|
const date = new Date(ts);
|
|
1644
1739
|
if (Number.isNaN(date.getTime())) return dateStr;
|
|
1645
|
-
return date.toLocaleDateString();
|
|
1740
|
+
return date.toLocaleDateString("en-US");
|
|
1646
1741
|
} catch {
|
|
1647
1742
|
return dateStr;
|
|
1648
1743
|
}
|
|
1649
1744
|
};
|
|
1650
|
-
return /* @__PURE__ */ jsxs5("div", { className: cn("space-y-
|
|
1651
|
-
/* @__PURE__ */ jsxs5("div", { className: "flex items-
|
|
1745
|
+
return /* @__PURE__ */ jsxs5("div", { className: cn("mx-auto w-full max-w-7xl space-y-8", className), children: [
|
|
1746
|
+
/* @__PURE__ */ jsxs5("div", { className: "flex flex-col gap-4 md:flex-row md:items-end md:justify-between", children: [
|
|
1652
1747
|
/* @__PURE__ */ jsxs5("div", { children: [
|
|
1653
|
-
/* @__PURE__ */ jsx5("h1", { className: "font-
|
|
1654
|
-
/* @__PURE__ */ jsx5("p", { className: "mt-1 text-muted-foreground", children: "Secrets securely stored
|
|
1748
|
+
/* @__PURE__ */ jsx5("h1", { className: "font-display text-3xl font-extrabold tracking-tight text-foreground", children: "Environment Secrets" }),
|
|
1749
|
+
/* @__PURE__ */ jsx5("p", { className: "mt-1 text-sm text-muted-foreground", children: "Secrets are securely stored and automatically exposed as environment variables across all your sandboxes." })
|
|
1655
1750
|
] }),
|
|
1656
1751
|
/* @__PURE__ */ jsxs5(
|
|
1657
1752
|
"button",
|
|
1658
1753
|
{
|
|
1659
1754
|
type: "button",
|
|
1660
1755
|
onClick: () => setIsCreateOpen(true),
|
|
1661
|
-
className: "inline-flex items-center gap-2 rounded-lg bg-primary
|
|
1756
|
+
className: "inline-flex items-center gap-2 rounded-lg bg-[var(--btn-primary-bg)] border border-[var(--border-accent,transparent)] px-5 py-2.5 text-sm font-semibold text-[var(--btn-primary-text)] hover:bg-[var(--btn-primary-hover)] transition-colors active:scale-[0.97]",
|
|
1662
1757
|
children: [
|
|
1663
1758
|
/* @__PURE__ */ jsx5(Plus3, { className: "h-4 w-4" }),
|
|
1664
|
-
"
|
|
1759
|
+
"New Secret"
|
|
1665
1760
|
]
|
|
1666
1761
|
}
|
|
1667
1762
|
)
|
|
1668
1763
|
] }),
|
|
1669
|
-
|
|
1764
|
+
/* @__PURE__ */ jsxs5("div", { className: "grid grid-cols-1 gap-6 md:grid-cols-4", children: [
|
|
1765
|
+
/* @__PURE__ */ jsxs5("div", { className: "rounded-lg border border-border bg-card p-5 shadow-[var(--shadow-card)]", children: [
|
|
1766
|
+
/* @__PURE__ */ jsx5("p", { className: "text-[10px] font-bold uppercase tracking-widest text-muted-foreground", children: "Total Active Secrets" }),
|
|
1767
|
+
/* @__PURE__ */ jsx5("div", { className: "mt-2 flex items-baseline gap-2", children: /* @__PURE__ */ jsx5("span", { className: "font-display text-2xl font-extrabold text-foreground", children: secrets.length }) })
|
|
1768
|
+
] }),
|
|
1769
|
+
/* @__PURE__ */ jsxs5("div", { className: "rounded-lg border border-border bg-card p-5 shadow-[var(--shadow-card)]", children: [
|
|
1770
|
+
/* @__PURE__ */ jsx5("p", { className: "text-[10px] font-bold uppercase tracking-widest text-muted-foreground", children: "Status" }),
|
|
1771
|
+
/* @__PURE__ */ jsxs5("div", { className: "mt-2 flex items-center gap-2", children: [
|
|
1772
|
+
/* @__PURE__ */ jsx5(CheckCircle, { className: "h-4 w-4 text-[var(--surface-success-text,#047857)]" }),
|
|
1773
|
+
/* @__PURE__ */ jsx5("span", { className: "text-sm font-semibold text-[var(--surface-success-text,#047857)]", children: "Encrypted" })
|
|
1774
|
+
] })
|
|
1775
|
+
] }),
|
|
1776
|
+
/* @__PURE__ */ jsx5(
|
|
1777
|
+
InfoPanel,
|
|
1778
|
+
{
|
|
1779
|
+
className: "md:col-span-2",
|
|
1780
|
+
label: "Security Audit",
|
|
1781
|
+
title: "All engines operational.",
|
|
1782
|
+
description: "Secrets are encrypted at rest using AES-256."
|
|
1783
|
+
}
|
|
1784
|
+
)
|
|
1785
|
+
] }),
|
|
1786
|
+
error && /* @__PURE__ */ jsxs5("div", { className: "rounded-lg border border-destructive/30 bg-destructive/10 p-4 flex items-center gap-3", children: [
|
|
1670
1787
|
/* @__PURE__ */ jsx5(AlertCircle2, { className: "h-5 w-5 text-destructive shrink-0" }),
|
|
1671
1788
|
/* @__PURE__ */ jsx5("p", { className: "text-destructive text-sm font-medium", children: error })
|
|
1672
1789
|
] }),
|
|
@@ -1681,11 +1798,11 @@ function SecretsPage({ apiClient, className }) {
|
|
|
1681
1798
|
}, children: /* @__PURE__ */ jsxs5(DialogContent, { className: "max-w-md", children: [
|
|
1682
1799
|
/* @__PURE__ */ jsxs5(DialogHeader, { children: [
|
|
1683
1800
|
/* @__PURE__ */ jsx5(DialogTitle, { children: "Create Secret" }),
|
|
1684
|
-
/* @__PURE__ */ jsx5(DialogDescription, { children: "Secrets
|
|
1801
|
+
/* @__PURE__ */ jsx5(DialogDescription, { children: "Secrets are automatically exposed as environment variables across all your new sandboxes." })
|
|
1685
1802
|
] }),
|
|
1686
1803
|
/* @__PURE__ */ jsxs5("div", { className: "space-y-4", children: [
|
|
1687
1804
|
/* @__PURE__ */ jsxs5("div", { children: [
|
|
1688
|
-
/* @__PURE__ */ jsx5("label", { className: "block text-
|
|
1805
|
+
/* @__PURE__ */ jsx5("label", { className: "block text-xs font-bold uppercase tracking-widest text-muted-foreground mb-2", children: "Name" }),
|
|
1689
1806
|
/* @__PURE__ */ jsx5(
|
|
1690
1807
|
"input",
|
|
1691
1808
|
{
|
|
@@ -1693,12 +1810,12 @@ function SecretsPage({ apiClient, className }) {
|
|
|
1693
1810
|
value: newName,
|
|
1694
1811
|
onChange: (e) => setNewName(e.target.value.toUpperCase().replace(/[^A-Z0-9_]/g, "_")),
|
|
1695
1812
|
placeholder: "MY_SECRET_KEY",
|
|
1696
|
-
className: "w-full rounded-
|
|
1813
|
+
className: "w-full rounded-md border border-border bg-card px-3 py-2.5 text-sm font-mono text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring"
|
|
1697
1814
|
}
|
|
1698
1815
|
)
|
|
1699
1816
|
] }),
|
|
1700
1817
|
/* @__PURE__ */ jsxs5("div", { children: [
|
|
1701
|
-
/* @__PURE__ */ jsx5("label", { className: "block text-
|
|
1818
|
+
/* @__PURE__ */ jsx5("label", { className: "block text-xs font-bold uppercase tracking-widest text-muted-foreground mb-2", children: "Value" }),
|
|
1702
1819
|
/* @__PURE__ */ jsxs5("div", { className: "relative", children: [
|
|
1703
1820
|
/* @__PURE__ */ jsx5(
|
|
1704
1821
|
"input",
|
|
@@ -1707,7 +1824,7 @@ function SecretsPage({ apiClient, className }) {
|
|
|
1707
1824
|
value: newValue,
|
|
1708
1825
|
onChange: (e) => setNewValue(e.target.value),
|
|
1709
1826
|
placeholder: "Enter secret value...",
|
|
1710
|
-
className: "w-full rounded-
|
|
1827
|
+
className: "w-full rounded-md border border-border bg-card px-3 py-2.5 pr-10 text-sm font-mono text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring"
|
|
1711
1828
|
}
|
|
1712
1829
|
),
|
|
1713
1830
|
/* @__PURE__ */ jsx5(
|
|
@@ -1735,7 +1852,7 @@ function SecretsPage({ apiClient, className }) {
|
|
|
1735
1852
|
setNewValue("");
|
|
1736
1853
|
setCreateError(null);
|
|
1737
1854
|
},
|
|
1738
|
-
className: "rounded-
|
|
1855
|
+
className: "rounded-md border border-border bg-card px-4 py-2 text-sm font-medium text-foreground hover:bg-muted transition-colors",
|
|
1739
1856
|
children: "Cancel"
|
|
1740
1857
|
}
|
|
1741
1858
|
),
|
|
@@ -1744,8 +1861,8 @@ function SecretsPage({ apiClient, className }) {
|
|
|
1744
1861
|
{
|
|
1745
1862
|
type: "button",
|
|
1746
1863
|
onClick: handleCreate,
|
|
1747
|
-
disabled: !newName.trim() || !newValue || isCreating,
|
|
1748
|
-
className: "rounded-
|
|
1864
|
+
disabled: !newName.trim() || !newValue.trim() || isCreating,
|
|
1865
|
+
className: "rounded-md bg-[var(--btn-primary-bg)] px-4 py-2 text-sm font-bold text-[var(--btn-primary-text)] hover:bg-[var(--btn-primary-hover)] transition-colors disabled:opacity-50 active:scale-[0.97]",
|
|
1749
1866
|
children: isCreating ? "Creating..." : "Create Secret"
|
|
1750
1867
|
}
|
|
1751
1868
|
)
|
|
@@ -1768,7 +1885,7 @@ function SecretsPage({ apiClient, className }) {
|
|
|
1768
1885
|
{
|
|
1769
1886
|
type: "button",
|
|
1770
1887
|
onClick: () => setDeleteTarget(null),
|
|
1771
|
-
className: "rounded-
|
|
1888
|
+
className: "rounded-md border border-border bg-card px-4 py-2 text-sm font-medium text-foreground hover:bg-muted transition-colors",
|
|
1772
1889
|
children: "Cancel"
|
|
1773
1890
|
}
|
|
1774
1891
|
),
|
|
@@ -1778,60 +1895,79 @@ function SecretsPage({ apiClient, className }) {
|
|
|
1778
1895
|
type: "button",
|
|
1779
1896
|
onClick: () => deleteTarget && handleDelete(deleteTarget),
|
|
1780
1897
|
disabled: isDeleting,
|
|
1781
|
-
className: "rounded-
|
|
1898
|
+
className: "rounded-md bg-destructive px-4 py-2 text-sm font-bold text-destructive-foreground hover:bg-destructive/90 transition-colors disabled:opacity-50 active:scale-[0.97]",
|
|
1782
1899
|
children: isDeleting ? "Deleting..." : "Delete Secret"
|
|
1783
1900
|
}
|
|
1784
1901
|
)
|
|
1785
1902
|
] })
|
|
1786
1903
|
] }) }),
|
|
1787
|
-
/* @__PURE__ */ jsxs5("div", { className: "rounded-
|
|
1788
|
-
/* @__PURE__ */ jsxs5("div", { className: "border-b border-border
|
|
1789
|
-
/* @__PURE__ */ jsx5("
|
|
1904
|
+
/* @__PURE__ */ jsxs5("div", { className: "overflow-hidden rounded-lg border border-border bg-card shadow-[var(--shadow-card)]", children: [
|
|
1905
|
+
/* @__PURE__ */ jsxs5("div", { className: "border-b border-border px-6 py-4 flex items-center justify-between", children: [
|
|
1906
|
+
/* @__PURE__ */ jsx5("div", { className: "flex gap-6", children: /* @__PURE__ */ jsx5("button", { type: "button", className: "text-xs font-bold uppercase tracking-widest text-foreground border-b-2 border-foreground pb-1", children: "All Secrets" }) }),
|
|
1790
1907
|
/* @__PURE__ */ jsxs5("span", { className: "text-xs text-muted-foreground font-mono", children: [
|
|
1791
1908
|
secrets.length,
|
|
1792
1909
|
" secret",
|
|
1793
1910
|
secrets.length !== 1 ? "s" : ""
|
|
1794
1911
|
] })
|
|
1795
1912
|
] }),
|
|
1796
|
-
loading ? /* @__PURE__ */ jsx5("div", { className: "
|
|
1797
|
-
/* @__PURE__ */ jsx5(Lock, { className: "
|
|
1798
|
-
/* @__PURE__ */ jsx5("
|
|
1799
|
-
/* @__PURE__ */ jsx5("p", { className: "text-sm text-muted-foreground
|
|
1913
|
+
loading ? /* @__PURE__ */ jsx5("div", { className: "flex items-center justify-center py-16", children: /* @__PURE__ */ jsx5("div", { className: "h-6 w-6 animate-spin rounded-full border-2 border-muted-foreground border-t-transparent" }) }) : secrets.length === 0 ? /* @__PURE__ */ jsxs5("div", { className: "flex flex-col items-center justify-center py-16 text-center", children: [
|
|
1914
|
+
/* @__PURE__ */ jsx5(Lock, { className: "h-10 w-10 text-muted-foreground mb-4" }),
|
|
1915
|
+
/* @__PURE__ */ jsx5("h3", { className: "text-lg font-semibold text-foreground", children: "No secrets yet" }),
|
|
1916
|
+
/* @__PURE__ */ jsx5("p", { className: "mt-1 text-sm text-muted-foreground max-w-sm", children: "Create a secret to inject into your sandboxes." }),
|
|
1800
1917
|
/* @__PURE__ */ jsxs5(
|
|
1801
1918
|
"button",
|
|
1802
1919
|
{
|
|
1803
1920
|
type: "button",
|
|
1804
1921
|
onClick: () => setIsCreateOpen(true),
|
|
1805
|
-
className: "inline-flex items-center gap-2 rounded-
|
|
1922
|
+
className: "mt-6 inline-flex items-center gap-2 rounded-md bg-[var(--btn-primary-bg)] px-4 py-2 text-sm font-semibold text-[var(--btn-primary-text)] hover:bg-[var(--btn-primary-hover)] transition-colors active:scale-[0.97]",
|
|
1806
1923
|
children: [
|
|
1807
1924
|
/* @__PURE__ */ jsx5(Plus3, { className: "h-4 w-4" }),
|
|
1808
1925
|
"Create Secret"
|
|
1809
1926
|
]
|
|
1810
1927
|
}
|
|
1811
1928
|
)
|
|
1812
|
-
] }) : /* @__PURE__ */ jsxs5("table", { className: "w-full text-
|
|
1813
|
-
/* @__PURE__ */ jsx5("thead", { className: "bg-muted/
|
|
1814
|
-
/* @__PURE__ */ jsx5("th", { className: "px-6 py-
|
|
1815
|
-
/* @__PURE__ */ jsx5("th", { className: "px-6 py-
|
|
1816
|
-
/* @__PURE__ */ jsx5("th", { className: "px-6 py-
|
|
1929
|
+
] }) : /* @__PURE__ */ jsxs5("table", { className: "w-full text-left border-collapse", children: [
|
|
1930
|
+
/* @__PURE__ */ jsx5("thead", { children: /* @__PURE__ */ jsxs5("tr", { className: "bg-muted/30 border-b border-border", children: [
|
|
1931
|
+
/* @__PURE__ */ jsx5("th", { className: "px-6 py-4 text-[10px] font-bold uppercase tracking-widest text-muted-foreground", children: "Secret Name" }),
|
|
1932
|
+
/* @__PURE__ */ jsx5("th", { className: "px-6 py-4 text-[10px] font-bold uppercase tracking-widest text-muted-foreground", children: "Encrypted Value" }),
|
|
1933
|
+
/* @__PURE__ */ jsx5("th", { className: "px-6 py-4 text-[10px] font-bold uppercase tracking-widest text-muted-foreground text-right", children: "Created" }),
|
|
1934
|
+
/* @__PURE__ */ jsx5("th", { className: "px-6 py-4 w-12" })
|
|
1817
1935
|
] }) }),
|
|
1818
|
-
/* @__PURE__ */ jsx5("tbody", { className: "divide-y divide-border", children: secrets.map((secret) => /* @__PURE__ */ jsxs5("tr", { className: "hover:bg-muted/
|
|
1819
|
-
/* @__PURE__ */ jsx5("td", { className: "px-6 py-
|
|
1820
|
-
/* @__PURE__ */ jsx5(
|
|
1821
|
-
/* @__PURE__ */ jsx5("span", { className: "
|
|
1936
|
+
/* @__PURE__ */ jsx5("tbody", { className: "divide-y divide-border", children: secrets.map((secret) => /* @__PURE__ */ jsxs5("tr", { className: "hover:bg-muted/20 transition-colors", children: [
|
|
1937
|
+
/* @__PURE__ */ jsx5("td", { className: "px-6 py-4", children: /* @__PURE__ */ jsxs5("div", { className: "flex items-center gap-3", children: [
|
|
1938
|
+
/* @__PURE__ */ jsx5(Key, { className: "h-4 w-4 text-muted-foreground" }),
|
|
1939
|
+
/* @__PURE__ */ jsx5("span", { className: "text-sm font-bold font-mono text-foreground", children: secret.name })
|
|
1822
1940
|
] }) }),
|
|
1823
|
-
/* @__PURE__ */ jsx5("td", { className: "px-6 py-
|
|
1824
|
-
/* @__PURE__ */ jsx5("td", { className: "px-6 py-
|
|
1941
|
+
/* @__PURE__ */ jsx5("td", { className: "px-6 py-4", children: /* @__PURE__ */ jsx5("code", { className: "text-xs font-mono text-muted-foreground bg-muted px-2 py-1 rounded", children: "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022" }) }),
|
|
1942
|
+
/* @__PURE__ */ jsx5("td", { className: "px-6 py-4 text-right", children: /* @__PURE__ */ jsx5("span", { className: "text-xs text-muted-foreground", children: formatDate(secret.createdAt) }) }),
|
|
1943
|
+
/* @__PURE__ */ jsx5("td", { className: "px-6 py-4", children: /* @__PURE__ */ jsx5(
|
|
1825
1944
|
"button",
|
|
1826
1945
|
{
|
|
1827
1946
|
type: "button",
|
|
1828
1947
|
onClick: () => setDeleteTarget(secret.name),
|
|
1829
|
-
className: "p-1 text-muted-foreground hover:text-destructive transition-colors
|
|
1948
|
+
className: "p-1.5 rounded-md text-muted-foreground hover:text-destructive hover:bg-destructive/10 transition-colors",
|
|
1949
|
+
"aria-label": `Delete ${secret.name}`,
|
|
1830
1950
|
children: /* @__PURE__ */ jsx5(Trash23, { className: "h-4 w-4" })
|
|
1831
1951
|
}
|
|
1832
1952
|
) })
|
|
1833
1953
|
] }, secret.name)) })
|
|
1834
1954
|
] })
|
|
1955
|
+
] }),
|
|
1956
|
+
/* @__PURE__ */ jsxs5("div", { className: "grid grid-cols-1 gap-6 md:grid-cols-2", children: [
|
|
1957
|
+
/* @__PURE__ */ jsxs5("div", { className: "rounded-lg border border-border bg-card p-6 shadow-[var(--shadow-card)]", children: [
|
|
1958
|
+
/* @__PURE__ */ jsxs5("div", { className: "flex items-center gap-3 mb-3", children: [
|
|
1959
|
+
/* @__PURE__ */ jsx5("div", { className: "flex h-9 w-9 items-center justify-center rounded-md bg-[var(--brand-primary,hsl(var(--primary)))] text-[var(--btn-primary-text)]", children: /* @__PURE__ */ jsx5(Shield, { className: "h-5 w-5" }) }),
|
|
1960
|
+
/* @__PURE__ */ jsx5("h3", { className: "text-sm font-bold text-foreground", children: "Encryption Standard" })
|
|
1961
|
+
] }),
|
|
1962
|
+
/* @__PURE__ */ jsx5("p", { className: "text-sm text-muted-foreground leading-relaxed", children: "Your secrets are encrypted using AES-256-GCM at rest and TLS 1.3 in transit. Hardware Security Modules manage all root keys." })
|
|
1963
|
+
] }),
|
|
1964
|
+
/* @__PURE__ */ jsxs5("div", { className: "rounded-lg border border-border bg-card p-6 shadow-[var(--shadow-card)]", children: [
|
|
1965
|
+
/* @__PURE__ */ jsxs5("div", { className: "flex items-center gap-3 mb-3", children: [
|
|
1966
|
+
/* @__PURE__ */ jsx5("div", { className: "flex h-9 w-9 items-center justify-center rounded-md bg-[var(--brand-primary,hsl(var(--primary)))] text-[var(--btn-primary-text)]", children: /* @__PURE__ */ jsx5(Lock, { className: "h-5 w-5" }) }),
|
|
1967
|
+
/* @__PURE__ */ jsx5("h3", { className: "text-sm font-bold text-foreground", children: "Access Policy" })
|
|
1968
|
+
] }),
|
|
1969
|
+
/* @__PURE__ */ jsx5("p", { className: "text-sm text-muted-foreground leading-relaxed", children: "Secrets are injected at sandbox creation time and are never exposed in logs, API responses, or container metadata." })
|
|
1970
|
+
] })
|
|
1835
1971
|
] })
|
|
1836
1972
|
] });
|
|
1837
1973
|
}
|
|
@@ -1860,6 +1996,928 @@ function TemplatesPage({ templates, loading = false, onUseTemplate, className })
|
|
|
1860
1996
|
] });
|
|
1861
1997
|
}
|
|
1862
1998
|
|
|
1999
|
+
// src/pages/startup-scripts-page.tsx
|
|
2000
|
+
import * as React6 from "react";
|
|
2001
|
+
import {
|
|
2002
|
+
Play,
|
|
2003
|
+
Plus as Plus4,
|
|
2004
|
+
Trash2 as Trash24,
|
|
2005
|
+
Pencil,
|
|
2006
|
+
AlertCircle as AlertCircle3,
|
|
2007
|
+
Power,
|
|
2008
|
+
PowerOff,
|
|
2009
|
+
Shield as Shield2,
|
|
2010
|
+
Terminal,
|
|
2011
|
+
Clock,
|
|
2012
|
+
ChevronDown as ChevronDown2,
|
|
2013
|
+
ChevronUp,
|
|
2014
|
+
GripVertical,
|
|
2015
|
+
Lock as Lock2,
|
|
2016
|
+
Cpu as Cpu2,
|
|
2017
|
+
MemoryStick,
|
|
2018
|
+
Layers as Layers3
|
|
2019
|
+
} from "lucide-react";
|
|
2020
|
+
import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
2021
|
+
var SCRIPT_TYPE_META = {
|
|
2022
|
+
bash: {
|
|
2023
|
+
label: "Bash",
|
|
2024
|
+
ext: "sh",
|
|
2025
|
+
interpreter: "bash",
|
|
2026
|
+
template: "#!/bin/bash\nset -e\n\n# Your startup script here\n"
|
|
2027
|
+
},
|
|
2028
|
+
python: {
|
|
2029
|
+
label: "Python",
|
|
2030
|
+
ext: "py",
|
|
2031
|
+
interpreter: "python3",
|
|
2032
|
+
template: "#!/usr/bin/env python3\nimport os\nimport subprocess\n\n# Your startup script here\n"
|
|
2033
|
+
},
|
|
2034
|
+
node: {
|
|
2035
|
+
label: "Node.js",
|
|
2036
|
+
ext: "js",
|
|
2037
|
+
interpreter: "node",
|
|
2038
|
+
template: "#!/usr/bin/env node\nconst { execSync } = require('child_process');\n\n// Your startup script here\n"
|
|
2039
|
+
},
|
|
2040
|
+
ruby: {
|
|
2041
|
+
label: "Ruby",
|
|
2042
|
+
ext: "rb",
|
|
2043
|
+
interpreter: "ruby",
|
|
2044
|
+
template: "#!/usr/bin/env ruby\n\n# Your startup script here\n"
|
|
2045
|
+
},
|
|
2046
|
+
custom: {
|
|
2047
|
+
label: "Custom",
|
|
2048
|
+
ext: "sh",
|
|
2049
|
+
interpreter: "sh",
|
|
2050
|
+
template: "#!/bin/sh\n\n# Specify your interpreter in the shebang line above\n"
|
|
2051
|
+
}
|
|
2052
|
+
};
|
|
2053
|
+
var SCRIPT_TEMPLATES = [
|
|
2054
|
+
{
|
|
2055
|
+
name: "Install Claude Code",
|
|
2056
|
+
description: "Install and authenticate Claude Code CLI",
|
|
2057
|
+
scriptType: "bash",
|
|
2058
|
+
content: `#!/bin/bash
|
|
2059
|
+
set -e
|
|
2060
|
+
|
|
2061
|
+
# Install Claude Code
|
|
2062
|
+
npm install -g @anthropic-ai/claude-code
|
|
2063
|
+
|
|
2064
|
+
# Authenticate if API key secret is available
|
|
2065
|
+
if [ -n "$ANTHROPIC_API_KEY" ]; then
|
|
2066
|
+
echo "Claude Code installed and API key configured"
|
|
2067
|
+
fi`,
|
|
2068
|
+
injectSecrets: ["ANTHROPIC_API_KEY"]
|
|
2069
|
+
},
|
|
2070
|
+
{
|
|
2071
|
+
name: "Git Clone & Setup",
|
|
2072
|
+
description: "Clone a repo, install dependencies, and run setup",
|
|
2073
|
+
scriptType: "bash",
|
|
2074
|
+
content: `#!/bin/bash
|
|
2075
|
+
set -e
|
|
2076
|
+
|
|
2077
|
+
REPO_URL="\${GIT_REPO_URL:-https://github.com/your-org/your-repo.git}"
|
|
2078
|
+
BRANCH="\${GIT_BRANCH:-main}"
|
|
2079
|
+
|
|
2080
|
+
cd /home/agent
|
|
2081
|
+
git clone --depth 1 --branch "$BRANCH" "$REPO_URL" workspace
|
|
2082
|
+
cd workspace
|
|
2083
|
+
|
|
2084
|
+
# Detect and install dependencies
|
|
2085
|
+
if [ -f "package.json" ]; then
|
|
2086
|
+
npm install
|
|
2087
|
+
elif [ -f "requirements.txt" ]; then
|
|
2088
|
+
pip install -r requirements.txt
|
|
2089
|
+
elif [ -f "Cargo.toml" ]; then
|
|
2090
|
+
cargo fetch
|
|
2091
|
+
elif [ -f "go.mod" ]; then
|
|
2092
|
+
go mod download
|
|
2093
|
+
fi
|
|
2094
|
+
|
|
2095
|
+
echo "Repository cloned and dependencies installed"`,
|
|
2096
|
+
injectSecrets: ["GITHUB_TOKEN"]
|
|
2097
|
+
},
|
|
2098
|
+
{
|
|
2099
|
+
name: "Python Environment",
|
|
2100
|
+
description: "Create a virtual environment with common packages",
|
|
2101
|
+
scriptType: "python",
|
|
2102
|
+
content: `#!/usr/bin/env python3
|
|
2103
|
+
import subprocess
|
|
2104
|
+
import sys
|
|
2105
|
+
|
|
2106
|
+
# Create and activate virtual environment
|
|
2107
|
+
subprocess.run([sys.executable, "-m", "venv", "/home/agent/.venv"], check=True)
|
|
2108
|
+
|
|
2109
|
+
# Install common packages
|
|
2110
|
+
packages = [
|
|
2111
|
+
"requests",
|
|
2112
|
+
"httpx",
|
|
2113
|
+
"python-dotenv",
|
|
2114
|
+
"pydantic",
|
|
2115
|
+
]
|
|
2116
|
+
|
|
2117
|
+
subprocess.run(
|
|
2118
|
+
["/home/agent/.venv/bin/pip", "install", "--quiet"] + packages,
|
|
2119
|
+
check=True,
|
|
2120
|
+
)
|
|
2121
|
+
|
|
2122
|
+
print(f"Python venv created with {len(packages)} packages")`,
|
|
2123
|
+
injectSecrets: []
|
|
2124
|
+
},
|
|
2125
|
+
{
|
|
2126
|
+
name: "SSH Key Setup",
|
|
2127
|
+
description: "Configure SSH keys for Git access",
|
|
2128
|
+
scriptType: "bash",
|
|
2129
|
+
content: `#!/bin/bash
|
|
2130
|
+
set -e
|
|
2131
|
+
|
|
2132
|
+
mkdir -p ~/.ssh
|
|
2133
|
+
chmod 700 ~/.ssh
|
|
2134
|
+
|
|
2135
|
+
# Write deploy key if secret is available
|
|
2136
|
+
if [ -n "$SSH_PRIVATE_KEY" ]; then
|
|
2137
|
+
echo "$SSH_PRIVATE_KEY" > ~/.ssh/id_ed25519
|
|
2138
|
+
chmod 600 ~/.ssh/id_ed25519
|
|
2139
|
+
ssh-keyscan github.com >> ~/.ssh/known_hosts 2>/dev/null
|
|
2140
|
+
echo "SSH key configured for Git access"
|
|
2141
|
+
else
|
|
2142
|
+
echo "No SSH_PRIVATE_KEY secret found, skipping"
|
|
2143
|
+
fi`,
|
|
2144
|
+
injectSecrets: ["SSH_PRIVATE_KEY"]
|
|
2145
|
+
},
|
|
2146
|
+
{
|
|
2147
|
+
name: "Node.js Project Bootstrap",
|
|
2148
|
+
description: "Initialize a new Node.js project with TypeScript",
|
|
2149
|
+
scriptType: "node",
|
|
2150
|
+
content: `#!/usr/bin/env node
|
|
2151
|
+
const { execSync } = require('child_process');
|
|
2152
|
+
const fs = require('fs');
|
|
2153
|
+
const path = require('path');
|
|
2154
|
+
|
|
2155
|
+
const dir = '/home/agent/workspace';
|
|
2156
|
+
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
|
2157
|
+
process.chdir(dir);
|
|
2158
|
+
|
|
2159
|
+
if (!fs.existsSync('package.json')) {
|
|
2160
|
+
execSync('npm init -y', { stdio: 'inherit' });
|
|
2161
|
+
execSync('npm install typescript tsx @types/node --save-dev', { stdio: 'inherit' });
|
|
2162
|
+
execSync('npx tsc --init --target ES2022 --module NodeNext --moduleResolution NodeNext --outDir dist', { stdio: 'inherit' });
|
|
2163
|
+
console.log('Node.js + TypeScript project initialized');
|
|
2164
|
+
} else {
|
|
2165
|
+
execSync('npm install', { stdio: 'inherit' });
|
|
2166
|
+
console.log('Dependencies installed');
|
|
2167
|
+
}`,
|
|
2168
|
+
injectSecrets: []
|
|
2169
|
+
}
|
|
2170
|
+
];
|
|
2171
|
+
function makeDefaultFormData(scriptType = "bash") {
|
|
2172
|
+
return {
|
|
2173
|
+
name: "",
|
|
2174
|
+
description: "",
|
|
2175
|
+
scriptType,
|
|
2176
|
+
content: SCRIPT_TYPE_META[scriptType].template,
|
|
2177
|
+
environments: [],
|
|
2178
|
+
minCpuCores: null,
|
|
2179
|
+
minRamGB: null,
|
|
2180
|
+
runOrder: 100,
|
|
2181
|
+
timeoutSeconds: 300,
|
|
2182
|
+
continueOnFailure: false,
|
|
2183
|
+
runAsRoot: false,
|
|
2184
|
+
injectSecrets: [],
|
|
2185
|
+
enabled: true
|
|
2186
|
+
};
|
|
2187
|
+
}
|
|
2188
|
+
function StartupScriptsPage({ apiClient, className }) {
|
|
2189
|
+
const [scripts, setScripts] = React6.useState([]);
|
|
2190
|
+
const [secrets, setSecrets] = React6.useState([]);
|
|
2191
|
+
const [environments, setEnvironments] = React6.useState([]);
|
|
2192
|
+
const [loading, setLoading] = React6.useState(true);
|
|
2193
|
+
const [error, setError] = React6.useState(null);
|
|
2194
|
+
const [isDialogOpen, setIsDialogOpen] = React6.useState(false);
|
|
2195
|
+
const [dialogStep, setDialogStep] = React6.useState("picker");
|
|
2196
|
+
const [transitionDir, setTransitionDir] = React6.useState("forward");
|
|
2197
|
+
const [stepKey, setStepKey] = React6.useState(0);
|
|
2198
|
+
const [editingScript, setEditingScript] = React6.useState(null);
|
|
2199
|
+
const [formData, setFormData] = React6.useState(makeDefaultFormData());
|
|
2200
|
+
const [isSaving, setIsSaving] = React6.useState(false);
|
|
2201
|
+
const [formError, setFormError] = React6.useState(null);
|
|
2202
|
+
const [showConditions, setShowConditions] = React6.useState(false);
|
|
2203
|
+
const [deleteTarget, setDeleteTarget] = React6.useState(null);
|
|
2204
|
+
const [isDeleting, setIsDeleting] = React6.useState(false);
|
|
2205
|
+
const apiRef = React6.useRef(apiClient);
|
|
2206
|
+
apiRef.current = apiClient;
|
|
2207
|
+
const loadGenRef = React6.useRef(0);
|
|
2208
|
+
const loadData = React6.useCallback(async (showSpinner = true) => {
|
|
2209
|
+
const gen = ++loadGenRef.current;
|
|
2210
|
+
try {
|
|
2211
|
+
if (showSpinner) setLoading(true);
|
|
2212
|
+
setError(null);
|
|
2213
|
+
const [scriptData, secretData, envData] = await Promise.all([
|
|
2214
|
+
apiRef.current.listScripts(),
|
|
2215
|
+
apiRef.current.listSecrets?.() ?? Promise.resolve([]),
|
|
2216
|
+
apiRef.current.listEnvironments?.() ?? Promise.resolve([])
|
|
2217
|
+
]);
|
|
2218
|
+
if (gen !== loadGenRef.current) return;
|
|
2219
|
+
setScripts(scriptData);
|
|
2220
|
+
setSecrets(secretData);
|
|
2221
|
+
setEnvironments(envData);
|
|
2222
|
+
} catch (err) {
|
|
2223
|
+
if (gen !== loadGenRef.current) return;
|
|
2224
|
+
setError(err instanceof Error ? err.message : "Failed to load startup scripts");
|
|
2225
|
+
} finally {
|
|
2226
|
+
if (gen === loadGenRef.current) setLoading(false);
|
|
2227
|
+
}
|
|
2228
|
+
}, []);
|
|
2229
|
+
React6.useEffect(() => {
|
|
2230
|
+
loadData();
|
|
2231
|
+
}, [loadData]);
|
|
2232
|
+
const openCreate = () => {
|
|
2233
|
+
setEditingScript(null);
|
|
2234
|
+
setFormData(makeDefaultFormData());
|
|
2235
|
+
setFormError(null);
|
|
2236
|
+
setShowConditions(false);
|
|
2237
|
+
setDialogStep("picker");
|
|
2238
|
+
setIsDialogOpen(true);
|
|
2239
|
+
};
|
|
2240
|
+
const goToStep = (step) => {
|
|
2241
|
+
setTransitionDir(step === "form" ? "forward" : "back");
|
|
2242
|
+
setStepKey((k) => k + 1);
|
|
2243
|
+
setDialogStep(step);
|
|
2244
|
+
};
|
|
2245
|
+
const openBlankScript = (scriptType = "bash") => {
|
|
2246
|
+
setEditingScript(null);
|
|
2247
|
+
setFormData(makeDefaultFormData(scriptType));
|
|
2248
|
+
setFormError(null);
|
|
2249
|
+
setShowConditions(false);
|
|
2250
|
+
goToStep("form");
|
|
2251
|
+
};
|
|
2252
|
+
const openFromTemplate = (template) => {
|
|
2253
|
+
setEditingScript(null);
|
|
2254
|
+
setFormData({
|
|
2255
|
+
...makeDefaultFormData(template.scriptType),
|
|
2256
|
+
name: template.name,
|
|
2257
|
+
description: template.description,
|
|
2258
|
+
content: template.content,
|
|
2259
|
+
injectSecrets: template.injectSecrets.filter(
|
|
2260
|
+
(s) => secrets.some((sec) => sec.name === s)
|
|
2261
|
+
)
|
|
2262
|
+
});
|
|
2263
|
+
setFormError(null);
|
|
2264
|
+
setShowConditions(false);
|
|
2265
|
+
goToStep("form");
|
|
2266
|
+
};
|
|
2267
|
+
const openEdit = (script) => {
|
|
2268
|
+
setEditingScript(script);
|
|
2269
|
+
setFormData({
|
|
2270
|
+
name: script.name,
|
|
2271
|
+
description: script.description,
|
|
2272
|
+
scriptType: script.scriptType ?? "bash",
|
|
2273
|
+
content: script.content,
|
|
2274
|
+
environments: script.environments,
|
|
2275
|
+
minCpuCores: script.minCpuCores,
|
|
2276
|
+
minRamGB: script.minRamGB,
|
|
2277
|
+
runOrder: script.runOrder,
|
|
2278
|
+
timeoutSeconds: script.timeoutSeconds,
|
|
2279
|
+
continueOnFailure: script.continueOnFailure,
|
|
2280
|
+
runAsRoot: script.runAsRoot,
|
|
2281
|
+
injectSecrets: script.injectSecrets,
|
|
2282
|
+
enabled: script.enabled
|
|
2283
|
+
});
|
|
2284
|
+
setFormError(null);
|
|
2285
|
+
setShowConditions(
|
|
2286
|
+
script.environments.length > 0 || script.minCpuCores !== null || script.minRamGB !== null
|
|
2287
|
+
);
|
|
2288
|
+
setDialogStep("form");
|
|
2289
|
+
setIsDialogOpen(true);
|
|
2290
|
+
};
|
|
2291
|
+
const handleSave = async () => {
|
|
2292
|
+
setFormError(null);
|
|
2293
|
+
setIsSaving(true);
|
|
2294
|
+
try {
|
|
2295
|
+
if (editingScript) {
|
|
2296
|
+
await apiRef.current.updateScript(editingScript.id, formData);
|
|
2297
|
+
} else {
|
|
2298
|
+
await apiRef.current.createScript(formData);
|
|
2299
|
+
}
|
|
2300
|
+
setIsDialogOpen(false);
|
|
2301
|
+
await loadData(false);
|
|
2302
|
+
} catch (err) {
|
|
2303
|
+
setFormError(err instanceof Error ? err.message : "Failed to save script");
|
|
2304
|
+
} finally {
|
|
2305
|
+
setIsSaving(false);
|
|
2306
|
+
}
|
|
2307
|
+
};
|
|
2308
|
+
const handleToggle = async (script) => {
|
|
2309
|
+
try {
|
|
2310
|
+
await apiRef.current.toggleScript(script.id);
|
|
2311
|
+
await loadData(false);
|
|
2312
|
+
} catch (err) {
|
|
2313
|
+
setError(err instanceof Error ? err.message : "Failed to toggle script");
|
|
2314
|
+
}
|
|
2315
|
+
};
|
|
2316
|
+
const handleDelete = async () => {
|
|
2317
|
+
if (!deleteTarget) return;
|
|
2318
|
+
setIsDeleting(true);
|
|
2319
|
+
try {
|
|
2320
|
+
await apiRef.current.deleteScript(deleteTarget.id);
|
|
2321
|
+
setDeleteTarget(null);
|
|
2322
|
+
await loadData(false);
|
|
2323
|
+
} catch (err) {
|
|
2324
|
+
setError(err instanceof Error ? err.message : "Failed to delete script");
|
|
2325
|
+
} finally {
|
|
2326
|
+
setIsDeleting(false);
|
|
2327
|
+
}
|
|
2328
|
+
};
|
|
2329
|
+
const toggleSecret = (name) => {
|
|
2330
|
+
setFormData((prev) => ({
|
|
2331
|
+
...prev,
|
|
2332
|
+
injectSecrets: prev.injectSecrets.includes(name) ? prev.injectSecrets.filter((s) => s !== name) : [...prev.injectSecrets, name]
|
|
2333
|
+
}));
|
|
2334
|
+
};
|
|
2335
|
+
const toggleEnvironment = (env) => {
|
|
2336
|
+
setFormData((prev) => ({
|
|
2337
|
+
...prev,
|
|
2338
|
+
environments: prev.environments.includes(env) ? prev.environments.filter((e) => e !== env) : [...prev.environments, env]
|
|
2339
|
+
}));
|
|
2340
|
+
};
|
|
2341
|
+
const activeCount = scripts.filter((s) => s.enabled).length;
|
|
2342
|
+
return /* @__PURE__ */ jsxs7("div", { className: cn("space-y-6", className), children: [
|
|
2343
|
+
/* @__PURE__ */ jsxs7("div", { className: "flex flex-col gap-4 md:flex-row md:items-end md:justify-between", children: [
|
|
2344
|
+
/* @__PURE__ */ jsxs7("div", { children: [
|
|
2345
|
+
/* @__PURE__ */ jsx7("h1", { className: "font-display text-3xl font-extrabold tracking-tight text-foreground", children: "Startup Scripts" }),
|
|
2346
|
+
/* @__PURE__ */ jsx7("p", { className: "mt-1 text-sm text-muted-foreground", children: "Define scripts that run automatically when your sandboxes start. Scripts can access your encrypted secrets." })
|
|
2347
|
+
] }),
|
|
2348
|
+
/* @__PURE__ */ jsxs7(
|
|
2349
|
+
"button",
|
|
2350
|
+
{
|
|
2351
|
+
type: "button",
|
|
2352
|
+
onClick: openCreate,
|
|
2353
|
+
className: "inline-flex items-center gap-2 rounded-lg bg-[var(--btn-primary-bg)] px-4 py-2.5 text-sm font-bold text-[var(--btn-primary-text)] shadow-sm transition-colors hover:bg-[var(--btn-primary-hover)]",
|
|
2354
|
+
children: [
|
|
2355
|
+
/* @__PURE__ */ jsx7(Plus4, { className: "h-4 w-4" }),
|
|
2356
|
+
"New Script"
|
|
2357
|
+
]
|
|
2358
|
+
}
|
|
2359
|
+
)
|
|
2360
|
+
] }),
|
|
2361
|
+
/* @__PURE__ */ jsxs7("div", { className: "grid grid-cols-1 gap-6 md:grid-cols-4", children: [
|
|
2362
|
+
/* @__PURE__ */ jsxs7("div", { className: "rounded-lg border border-border bg-card p-5 shadow-[var(--shadow-card)]", children: [
|
|
2363
|
+
/* @__PURE__ */ jsx7("p", { className: "text-[10px] font-bold uppercase tracking-widest text-muted-foreground", children: "Total Scripts" }),
|
|
2364
|
+
/* @__PURE__ */ jsx7("div", { className: "mt-2 flex items-baseline gap-2", children: /* @__PURE__ */ jsx7("span", { className: "font-display text-2xl font-extrabold text-foreground", children: scripts.length }) })
|
|
2365
|
+
] }),
|
|
2366
|
+
/* @__PURE__ */ jsxs7("div", { className: "rounded-lg border border-border bg-card p-5 shadow-[var(--shadow-card)]", children: [
|
|
2367
|
+
/* @__PURE__ */ jsx7("p", { className: "text-[10px] font-bold uppercase tracking-widest text-muted-foreground", children: "Active" }),
|
|
2368
|
+
/* @__PURE__ */ jsxs7("div", { className: "mt-2 flex items-baseline gap-2", children: [
|
|
2369
|
+
/* @__PURE__ */ jsx7("span", { className: "font-display text-2xl font-extrabold text-foreground", children: activeCount }),
|
|
2370
|
+
/* @__PURE__ */ jsxs7("span", { className: "text-xs text-muted-foreground", children: [
|
|
2371
|
+
"of ",
|
|
2372
|
+
scripts.length
|
|
2373
|
+
] })
|
|
2374
|
+
] })
|
|
2375
|
+
] }),
|
|
2376
|
+
/* @__PURE__ */ jsx7(
|
|
2377
|
+
InfoPanel,
|
|
2378
|
+
{
|
|
2379
|
+
className: "md:col-span-2",
|
|
2380
|
+
label: "Execution",
|
|
2381
|
+
title: "Scripts run before your agent starts.",
|
|
2382
|
+
description: "They execute in order, with full access to injected secrets as environment variables."
|
|
2383
|
+
}
|
|
2384
|
+
)
|
|
2385
|
+
] }),
|
|
2386
|
+
error && /* @__PURE__ */ jsxs7("div", { className: "flex items-center gap-3 rounded-lg border border-destructive/30 bg-destructive/10 p-4", children: [
|
|
2387
|
+
/* @__PURE__ */ jsx7(AlertCircle3, { className: "h-5 w-5 shrink-0 text-destructive" }),
|
|
2388
|
+
/* @__PURE__ */ jsx7("p", { className: "text-sm font-medium text-destructive", children: error })
|
|
2389
|
+
] }),
|
|
2390
|
+
/* @__PURE__ */ jsx7(Dialog, { open: isDialogOpen, onOpenChange: setIsDialogOpen, children: /* @__PURE__ */ jsxs7(DialogContent, { className: "max-w-2xl max-h-[90vh] overflow-y-auto", children: [
|
|
2391
|
+
dialogStep === "picker" && /* @__PURE__ */ jsxs7(
|
|
2392
|
+
"div",
|
|
2393
|
+
{
|
|
2394
|
+
className: cn(
|
|
2395
|
+
"animate-in fade-in duration-300",
|
|
2396
|
+
transitionDir === "back" ? "slide-in-from-left-4" : "slide-in-from-right-4"
|
|
2397
|
+
),
|
|
2398
|
+
children: [
|
|
2399
|
+
/* @__PURE__ */ jsxs7(DialogHeader, { children: [
|
|
2400
|
+
/* @__PURE__ */ jsx7(DialogTitle, { children: "New Startup Script" }),
|
|
2401
|
+
/* @__PURE__ */ jsx7(DialogDescription, { children: "Start from a template or create a blank script." })
|
|
2402
|
+
] }),
|
|
2403
|
+
/* @__PURE__ */ jsxs7("div", { className: "space-y-4 py-2", children: [
|
|
2404
|
+
/* @__PURE__ */ jsxs7("div", { children: [
|
|
2405
|
+
/* @__PURE__ */ jsx7("p", { className: "text-xs font-bold uppercase tracking-widest text-muted-foreground mb-2", children: "Blank Script" }),
|
|
2406
|
+
/* @__PURE__ */ jsx7("div", { className: "flex flex-wrap gap-2", children: Object.entries(SCRIPT_TYPE_META).map(([type, meta]) => /* @__PURE__ */ jsx7(
|
|
2407
|
+
"button",
|
|
2408
|
+
{
|
|
2409
|
+
type: "button",
|
|
2410
|
+
onClick: () => openBlankScript(type),
|
|
2411
|
+
className: "rounded-lg border border-border bg-card px-4 py-2.5 text-sm font-medium text-foreground hover:border-primary/30 hover:bg-muted/50 transition-colors",
|
|
2412
|
+
children: meta.label
|
|
2413
|
+
},
|
|
2414
|
+
type
|
|
2415
|
+
)) })
|
|
2416
|
+
] }),
|
|
2417
|
+
/* @__PURE__ */ jsxs7("div", { children: [
|
|
2418
|
+
/* @__PURE__ */ jsx7("p", { className: "text-xs font-bold uppercase tracking-widest text-muted-foreground mb-2", children: "Templates" }),
|
|
2419
|
+
/* @__PURE__ */ jsx7("div", { className: "space-y-2", children: SCRIPT_TEMPLATES.map((tmpl) => /* @__PURE__ */ jsxs7(
|
|
2420
|
+
"button",
|
|
2421
|
+
{
|
|
2422
|
+
type: "button",
|
|
2423
|
+
onClick: () => openFromTemplate(tmpl),
|
|
2424
|
+
className: "w-full text-left rounded-lg border border-border bg-card p-4 hover:border-primary/30 hover:bg-muted/30 transition-colors group",
|
|
2425
|
+
children: [
|
|
2426
|
+
/* @__PURE__ */ jsxs7("div", { className: "flex items-center justify-between", children: [
|
|
2427
|
+
/* @__PURE__ */ jsxs7("div", { children: [
|
|
2428
|
+
/* @__PURE__ */ jsx7("h4", { className: "text-sm font-bold text-foreground group-hover:text-primary transition-colors", children: tmpl.name }),
|
|
2429
|
+
/* @__PURE__ */ jsx7("p", { className: "mt-0.5 text-xs text-muted-foreground", children: tmpl.description })
|
|
2430
|
+
] }),
|
|
2431
|
+
/* @__PURE__ */ jsx7("span", { className: "text-[10px] font-mono text-muted-foreground bg-muted rounded px-1.5 py-0.5", children: SCRIPT_TYPE_META[tmpl.scriptType].label })
|
|
2432
|
+
] }),
|
|
2433
|
+
tmpl.injectSecrets.length > 0 && /* @__PURE__ */ jsx7("div", { className: "mt-2 flex flex-wrap gap-1", children: tmpl.injectSecrets.map((s) => /* @__PURE__ */ jsxs7("span", { className: "inline-flex items-center gap-0.5 rounded-full bg-muted px-2 py-0.5 text-[10px] text-muted-foreground", children: [
|
|
2434
|
+
/* @__PURE__ */ jsx7(Lock2, { className: "h-2.5 w-2.5" }),
|
|
2435
|
+
s
|
|
2436
|
+
] }, s)) })
|
|
2437
|
+
]
|
|
2438
|
+
},
|
|
2439
|
+
tmpl.name
|
|
2440
|
+
)) })
|
|
2441
|
+
] })
|
|
2442
|
+
] })
|
|
2443
|
+
]
|
|
2444
|
+
},
|
|
2445
|
+
`picker-${stepKey}`
|
|
2446
|
+
),
|
|
2447
|
+
dialogStep === "form" && /* @__PURE__ */ jsxs7(
|
|
2448
|
+
"div",
|
|
2449
|
+
{
|
|
2450
|
+
className: cn(
|
|
2451
|
+
"animate-in fade-in duration-300",
|
|
2452
|
+
transitionDir === "forward" ? "slide-in-from-right-4" : "slide-in-from-left-4"
|
|
2453
|
+
),
|
|
2454
|
+
children: [
|
|
2455
|
+
/* @__PURE__ */ jsxs7(DialogHeader, { children: [
|
|
2456
|
+
/* @__PURE__ */ jsx7(DialogTitle, { children: editingScript ? "Edit Script" : "Create Startup Script" }),
|
|
2457
|
+
/* @__PURE__ */ jsx7(DialogDescription, { children: editingScript ? "Modify your startup script configuration." : "Define a shell script that runs when sandboxes start. Scripts execute before the AI agent." })
|
|
2458
|
+
] }),
|
|
2459
|
+
/* @__PURE__ */ jsxs7("div", { className: "space-y-5 py-2", children: [
|
|
2460
|
+
/* @__PURE__ */ jsxs7("div", { children: [
|
|
2461
|
+
/* @__PURE__ */ jsx7("label", { className: "text-xs font-bold uppercase tracking-widest text-muted-foreground", children: "Name" }),
|
|
2462
|
+
/* @__PURE__ */ jsx7(
|
|
2463
|
+
"input",
|
|
2464
|
+
{
|
|
2465
|
+
type: "text",
|
|
2466
|
+
value: formData.name,
|
|
2467
|
+
onChange: (e) => setFormData((p) => ({ ...p, name: e.target.value })),
|
|
2468
|
+
placeholder: "Install Claude Code",
|
|
2469
|
+
maxLength: 64,
|
|
2470
|
+
className: "mt-1.5 w-full rounded-lg border border-border bg-background px-3 py-2 text-sm text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-primary/30"
|
|
2471
|
+
}
|
|
2472
|
+
)
|
|
2473
|
+
] }),
|
|
2474
|
+
/* @__PURE__ */ jsxs7("div", { children: [
|
|
2475
|
+
/* @__PURE__ */ jsx7("label", { className: "text-xs font-bold uppercase tracking-widest text-muted-foreground", children: "Description" }),
|
|
2476
|
+
/* @__PURE__ */ jsx7(
|
|
2477
|
+
"input",
|
|
2478
|
+
{
|
|
2479
|
+
type: "text",
|
|
2480
|
+
value: formData.description,
|
|
2481
|
+
onChange: (e) => setFormData((p) => ({ ...p, description: e.target.value })),
|
|
2482
|
+
placeholder: "Sets up Claude Code with authentication",
|
|
2483
|
+
maxLength: 256,
|
|
2484
|
+
className: "mt-1.5 w-full rounded-lg border border-border bg-background px-3 py-2 text-sm text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-primary/30"
|
|
2485
|
+
}
|
|
2486
|
+
)
|
|
2487
|
+
] }),
|
|
2488
|
+
/* @__PURE__ */ jsxs7("div", { children: [
|
|
2489
|
+
/* @__PURE__ */ jsx7("label", { className: "text-xs font-bold uppercase tracking-widest text-muted-foreground", children: "Language" }),
|
|
2490
|
+
/* @__PURE__ */ jsx7("div", { className: "mt-1.5 flex flex-wrap gap-2", children: Object.entries(SCRIPT_TYPE_META).map(([type, meta]) => /* @__PURE__ */ jsx7(
|
|
2491
|
+
"button",
|
|
2492
|
+
{
|
|
2493
|
+
type: "button",
|
|
2494
|
+
onClick: () => {
|
|
2495
|
+
const currentMeta = SCRIPT_TYPE_META[formData.scriptType];
|
|
2496
|
+
const isDefaultContent = formData.content === currentMeta.template;
|
|
2497
|
+
setFormData((p) => ({
|
|
2498
|
+
...p,
|
|
2499
|
+
scriptType: type,
|
|
2500
|
+
content: isDefaultContent ? meta.template : p.content
|
|
2501
|
+
}));
|
|
2502
|
+
},
|
|
2503
|
+
className: cn(
|
|
2504
|
+
"rounded-lg px-3 py-1.5 text-xs font-medium border transition-colors",
|
|
2505
|
+
formData.scriptType === type ? "bg-primary/10 border-primary/30 text-primary" : "bg-background border-border text-muted-foreground hover:border-primary/20"
|
|
2506
|
+
),
|
|
2507
|
+
children: meta.label
|
|
2508
|
+
},
|
|
2509
|
+
type
|
|
2510
|
+
)) })
|
|
2511
|
+
] }),
|
|
2512
|
+
/* @__PURE__ */ jsxs7("div", { children: [
|
|
2513
|
+
/* @__PURE__ */ jsx7("label", { className: "text-xs font-bold uppercase tracking-widest text-muted-foreground", children: "Script" }),
|
|
2514
|
+
/* @__PURE__ */ jsx7(
|
|
2515
|
+
"textarea",
|
|
2516
|
+
{
|
|
2517
|
+
value: formData.content,
|
|
2518
|
+
onChange: (e) => setFormData((p) => ({ ...p, content: e.target.value })),
|
|
2519
|
+
rows: 12,
|
|
2520
|
+
spellCheck: false,
|
|
2521
|
+
className: "mt-1.5 w-full rounded-lg border border-border bg-[var(--depth-1,hsl(var(--muted)))] px-4 py-3 font-mono text-sm text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-primary/30 resize-y"
|
|
2522
|
+
}
|
|
2523
|
+
),
|
|
2524
|
+
/* @__PURE__ */ jsxs7("p", { className: "mt-1 text-xs text-muted-foreground", children: [
|
|
2525
|
+
SCRIPT_TYPE_META[formData.scriptType].label,
|
|
2526
|
+
" script. Injected secrets are available as environment variables (e.g. ",
|
|
2527
|
+
/* @__PURE__ */ jsx7("code", { className: "text-primary", children: "$GITHUB_TOKEN" }),
|
|
2528
|
+
")."
|
|
2529
|
+
] })
|
|
2530
|
+
] }),
|
|
2531
|
+
secrets.length > 0 && /* @__PURE__ */ jsxs7("div", { children: [
|
|
2532
|
+
/* @__PURE__ */ jsx7("label", { className: "text-xs font-bold uppercase tracking-widest text-muted-foreground", children: "Inject Secrets" }),
|
|
2533
|
+
/* @__PURE__ */ jsx7("p", { className: "mt-0.5 text-xs text-muted-foreground", children: "Select secrets to make available as environment variables." }),
|
|
2534
|
+
/* @__PURE__ */ jsx7("div", { className: "mt-2 flex flex-wrap gap-2", children: secrets.map((secret) => {
|
|
2535
|
+
const selected = formData.injectSecrets.includes(secret.name);
|
|
2536
|
+
return /* @__PURE__ */ jsxs7(
|
|
2537
|
+
"button",
|
|
2538
|
+
{
|
|
2539
|
+
type: "button",
|
|
2540
|
+
onClick: () => toggleSecret(secret.name),
|
|
2541
|
+
className: cn(
|
|
2542
|
+
"inline-flex items-center gap-1.5 rounded-full px-3 py-1.5 text-xs font-medium border transition-colors",
|
|
2543
|
+
selected ? "bg-primary/10 border-primary/30 text-primary" : "bg-muted border-border text-muted-foreground hover:border-primary/20"
|
|
2544
|
+
),
|
|
2545
|
+
children: [
|
|
2546
|
+
/* @__PURE__ */ jsx7(Lock2, { className: "h-3 w-3" }),
|
|
2547
|
+
secret.name
|
|
2548
|
+
]
|
|
2549
|
+
},
|
|
2550
|
+
secret.name
|
|
2551
|
+
);
|
|
2552
|
+
}) })
|
|
2553
|
+
] }),
|
|
2554
|
+
/* @__PURE__ */ jsxs7("div", { children: [
|
|
2555
|
+
/* @__PURE__ */ jsxs7(
|
|
2556
|
+
"button",
|
|
2557
|
+
{
|
|
2558
|
+
type: "button",
|
|
2559
|
+
onClick: () => setShowConditions(!showConditions),
|
|
2560
|
+
className: "flex items-center gap-2 text-xs font-bold uppercase tracking-widest text-muted-foreground hover:text-foreground transition-colors",
|
|
2561
|
+
children: [
|
|
2562
|
+
showConditions ? /* @__PURE__ */ jsx7(ChevronUp, { className: "h-3.5 w-3.5" }) : /* @__PURE__ */ jsx7(ChevronDown2, { className: "h-3.5 w-3.5" }),
|
|
2563
|
+
"Conditions & Execution"
|
|
2564
|
+
]
|
|
2565
|
+
}
|
|
2566
|
+
),
|
|
2567
|
+
showConditions && /* @__PURE__ */ jsxs7("div", { className: "mt-3 space-y-4 rounded-lg border border-border bg-muted/30 p-4", children: [
|
|
2568
|
+
/* @__PURE__ */ jsxs7("div", { children: [
|
|
2569
|
+
/* @__PURE__ */ jsxs7("label", { className: "text-xs font-bold uppercase tracking-widest text-muted-foreground flex items-center gap-1.5", children: [
|
|
2570
|
+
/* @__PURE__ */ jsx7(Layers3, { className: "h-3.5 w-3.5" }),
|
|
2571
|
+
"Environments"
|
|
2572
|
+
] }),
|
|
2573
|
+
/* @__PURE__ */ jsx7("p", { className: "mt-0.5 text-xs text-muted-foreground", children: environments.length > 0 ? "Only run for these environments. Leave empty to run for all." : "No templates configured. Script will run for all environments." }),
|
|
2574
|
+
environments.length > 0 && /* @__PURE__ */ jsx7("div", { className: "mt-2 flex flex-wrap gap-2", children: environments.map((env) => {
|
|
2575
|
+
const selected = formData.environments.includes(env.id);
|
|
2576
|
+
return /* @__PURE__ */ jsx7(
|
|
2577
|
+
"button",
|
|
2578
|
+
{
|
|
2579
|
+
type: "button",
|
|
2580
|
+
onClick: () => toggleEnvironment(env.id),
|
|
2581
|
+
className: cn(
|
|
2582
|
+
"rounded-full px-3 py-1 text-xs font-medium border transition-colors",
|
|
2583
|
+
selected ? "bg-primary/10 border-primary/30 text-primary" : "bg-background border-border text-muted-foreground hover:border-primary/20"
|
|
2584
|
+
),
|
|
2585
|
+
children: env.name || env.id
|
|
2586
|
+
},
|
|
2587
|
+
env.id
|
|
2588
|
+
);
|
|
2589
|
+
}) })
|
|
2590
|
+
] }),
|
|
2591
|
+
/* @__PURE__ */ jsxs7("div", { className: "grid grid-cols-2 gap-4", children: [
|
|
2592
|
+
/* @__PURE__ */ jsxs7("div", { children: [
|
|
2593
|
+
/* @__PURE__ */ jsxs7("label", { className: "text-xs font-bold uppercase tracking-widest text-muted-foreground flex items-center gap-1.5", children: [
|
|
2594
|
+
/* @__PURE__ */ jsx7(Cpu2, { className: "h-3.5 w-3.5" }),
|
|
2595
|
+
"Min CPU Cores"
|
|
2596
|
+
] }),
|
|
2597
|
+
/* @__PURE__ */ jsx7(
|
|
2598
|
+
"input",
|
|
2599
|
+
{
|
|
2600
|
+
type: "number",
|
|
2601
|
+
value: formData.minCpuCores ?? "",
|
|
2602
|
+
onChange: (e) => setFormData((p) => ({
|
|
2603
|
+
...p,
|
|
2604
|
+
minCpuCores: e.target.value ? Number(e.target.value) : null
|
|
2605
|
+
})),
|
|
2606
|
+
placeholder: "Any",
|
|
2607
|
+
min: 0.5,
|
|
2608
|
+
step: 0.5,
|
|
2609
|
+
className: "mt-1.5 w-full rounded-lg border border-border bg-background px-3 py-2 text-sm text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-primary/30"
|
|
2610
|
+
}
|
|
2611
|
+
)
|
|
2612
|
+
] }),
|
|
2613
|
+
/* @__PURE__ */ jsxs7("div", { children: [
|
|
2614
|
+
/* @__PURE__ */ jsxs7("label", { className: "text-xs font-bold uppercase tracking-widest text-muted-foreground flex items-center gap-1.5", children: [
|
|
2615
|
+
/* @__PURE__ */ jsx7(MemoryStick, { className: "h-3.5 w-3.5" }),
|
|
2616
|
+
"Min RAM (GB)"
|
|
2617
|
+
] }),
|
|
2618
|
+
/* @__PURE__ */ jsx7(
|
|
2619
|
+
"input",
|
|
2620
|
+
{
|
|
2621
|
+
type: "number",
|
|
2622
|
+
value: formData.minRamGB ?? "",
|
|
2623
|
+
onChange: (e) => setFormData((p) => ({
|
|
2624
|
+
...p,
|
|
2625
|
+
minRamGB: e.target.value ? Number(e.target.value) : null
|
|
2626
|
+
})),
|
|
2627
|
+
placeholder: "Any",
|
|
2628
|
+
min: 1,
|
|
2629
|
+
step: 1,
|
|
2630
|
+
className: "mt-1.5 w-full rounded-lg border border-border bg-background px-3 py-2 text-sm text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-primary/30"
|
|
2631
|
+
}
|
|
2632
|
+
)
|
|
2633
|
+
] })
|
|
2634
|
+
] }),
|
|
2635
|
+
/* @__PURE__ */ jsxs7("div", { className: "grid grid-cols-2 gap-4", children: [
|
|
2636
|
+
/* @__PURE__ */ jsxs7("div", { children: [
|
|
2637
|
+
/* @__PURE__ */ jsxs7("label", { className: "text-xs font-bold uppercase tracking-widest text-muted-foreground flex items-center gap-1.5", children: [
|
|
2638
|
+
/* @__PURE__ */ jsx7(GripVertical, { className: "h-3.5 w-3.5" }),
|
|
2639
|
+
"Run Order"
|
|
2640
|
+
] }),
|
|
2641
|
+
/* @__PURE__ */ jsx7(
|
|
2642
|
+
"input",
|
|
2643
|
+
{
|
|
2644
|
+
type: "number",
|
|
2645
|
+
value: formData.runOrder,
|
|
2646
|
+
onChange: (e) => setFormData((p) => ({ ...p, runOrder: e.target.value ? Math.max(0, Number(e.target.value)) : 0 })),
|
|
2647
|
+
min: 0,
|
|
2648
|
+
max: 9999,
|
|
2649
|
+
className: "mt-1.5 w-full rounded-lg border border-border bg-background px-3 py-2 text-sm text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-primary/30"
|
|
2650
|
+
}
|
|
2651
|
+
),
|
|
2652
|
+
/* @__PURE__ */ jsx7("p", { className: "mt-1 text-xs text-muted-foreground", children: "Lower runs first" })
|
|
2653
|
+
] }),
|
|
2654
|
+
/* @__PURE__ */ jsxs7("div", { children: [
|
|
2655
|
+
/* @__PURE__ */ jsxs7("label", { className: "text-xs font-bold uppercase tracking-widest text-muted-foreground flex items-center gap-1.5", children: [
|
|
2656
|
+
/* @__PURE__ */ jsx7(Clock, { className: "h-3.5 w-3.5" }),
|
|
2657
|
+
"Timeout (seconds)"
|
|
2658
|
+
] }),
|
|
2659
|
+
/* @__PURE__ */ jsx7(
|
|
2660
|
+
"input",
|
|
2661
|
+
{
|
|
2662
|
+
type: "number",
|
|
2663
|
+
value: formData.timeoutSeconds,
|
|
2664
|
+
onChange: (e) => setFormData((p) => ({ ...p, timeoutSeconds: e.target.value ? Math.max(5, Number(e.target.value)) : 300 })),
|
|
2665
|
+
min: 5,
|
|
2666
|
+
max: 3600,
|
|
2667
|
+
className: "mt-1.5 w-full rounded-lg border border-border bg-background px-3 py-2 text-sm text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-primary/30"
|
|
2668
|
+
}
|
|
2669
|
+
)
|
|
2670
|
+
] })
|
|
2671
|
+
] }),
|
|
2672
|
+
/* @__PURE__ */ jsxs7("div", { className: "space-y-3", children: [
|
|
2673
|
+
/* @__PURE__ */ jsxs7("label", { className: "flex items-center justify-between cursor-pointer", children: [
|
|
2674
|
+
/* @__PURE__ */ jsxs7("div", { children: [
|
|
2675
|
+
/* @__PURE__ */ jsx7("span", { className: "text-sm font-medium text-foreground", children: "Continue on failure" }),
|
|
2676
|
+
/* @__PURE__ */ jsx7("p", { className: "text-xs text-muted-foreground", children: "If script fails, continue starting the sandbox" })
|
|
2677
|
+
] }),
|
|
2678
|
+
/* @__PURE__ */ jsx7(
|
|
2679
|
+
"button",
|
|
2680
|
+
{
|
|
2681
|
+
type: "button",
|
|
2682
|
+
onClick: () => setFormData((p) => ({ ...p, continueOnFailure: !p.continueOnFailure })),
|
|
2683
|
+
className: cn(
|
|
2684
|
+
"relative h-6 w-11 rounded-full transition-colors",
|
|
2685
|
+
formData.continueOnFailure ? "bg-primary" : "bg-border"
|
|
2686
|
+
),
|
|
2687
|
+
children: /* @__PURE__ */ jsx7(
|
|
2688
|
+
"span",
|
|
2689
|
+
{
|
|
2690
|
+
className: cn(
|
|
2691
|
+
"absolute top-0.5 left-0.5 h-5 w-5 rounded-full bg-white shadow transition-transform",
|
|
2692
|
+
formData.continueOnFailure && "translate-x-5"
|
|
2693
|
+
)
|
|
2694
|
+
}
|
|
2695
|
+
)
|
|
2696
|
+
}
|
|
2697
|
+
)
|
|
2698
|
+
] }),
|
|
2699
|
+
/* @__PURE__ */ jsxs7("label", { className: "flex items-center justify-between cursor-pointer", children: [
|
|
2700
|
+
/* @__PURE__ */ jsxs7("div", { children: [
|
|
2701
|
+
/* @__PURE__ */ jsx7("span", { className: "text-sm font-medium text-foreground", children: "Run as root" }),
|
|
2702
|
+
/* @__PURE__ */ jsx7("p", { className: "text-xs text-muted-foreground", children: "Execute with root privileges instead of the agent user" })
|
|
2703
|
+
] }),
|
|
2704
|
+
/* @__PURE__ */ jsx7(
|
|
2705
|
+
"button",
|
|
2706
|
+
{
|
|
2707
|
+
type: "button",
|
|
2708
|
+
onClick: () => setFormData((p) => ({ ...p, runAsRoot: !p.runAsRoot })),
|
|
2709
|
+
className: cn(
|
|
2710
|
+
"relative h-6 w-11 rounded-full transition-colors",
|
|
2711
|
+
formData.runAsRoot ? "bg-primary" : "bg-border"
|
|
2712
|
+
),
|
|
2713
|
+
children: /* @__PURE__ */ jsx7(
|
|
2714
|
+
"span",
|
|
2715
|
+
{
|
|
2716
|
+
className: cn(
|
|
2717
|
+
"absolute top-0.5 left-0.5 h-5 w-5 rounded-full bg-white shadow transition-transform",
|
|
2718
|
+
formData.runAsRoot && "translate-x-5"
|
|
2719
|
+
)
|
|
2720
|
+
}
|
|
2721
|
+
)
|
|
2722
|
+
}
|
|
2723
|
+
)
|
|
2724
|
+
] })
|
|
2725
|
+
] })
|
|
2726
|
+
] })
|
|
2727
|
+
] }),
|
|
2728
|
+
formError && /* @__PURE__ */ jsxs7("div", { className: "flex items-center gap-2 rounded-lg border border-destructive/30 bg-destructive/10 p-3", children: [
|
|
2729
|
+
/* @__PURE__ */ jsx7(AlertCircle3, { className: "h-4 w-4 text-destructive" }),
|
|
2730
|
+
/* @__PURE__ */ jsx7("p", { className: "text-sm text-destructive", children: formError })
|
|
2731
|
+
] })
|
|
2732
|
+
] }),
|
|
2733
|
+
/* @__PURE__ */ jsxs7(DialogFooter, { className: "flex items-center justify-between sm:justify-between", children: [
|
|
2734
|
+
/* @__PURE__ */ jsx7("div", { children: !editingScript && /* @__PURE__ */ jsxs7(
|
|
2735
|
+
"button",
|
|
2736
|
+
{
|
|
2737
|
+
type: "button",
|
|
2738
|
+
onClick: () => goToStep("picker"),
|
|
2739
|
+
className: "inline-flex items-center gap-1.5 rounded-lg border border-border px-4 py-2 text-sm font-medium text-muted-foreground hover:text-foreground hover:bg-muted transition-colors",
|
|
2740
|
+
children: [
|
|
2741
|
+
/* @__PURE__ */ jsx7("svg", { className: "h-4 w-4", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx7("path", { d: "m15 18-6-6 6-6" }) }),
|
|
2742
|
+
"Back"
|
|
2743
|
+
]
|
|
2744
|
+
}
|
|
2745
|
+
) }),
|
|
2746
|
+
/* @__PURE__ */ jsxs7("div", { className: "flex items-center gap-2", children: [
|
|
2747
|
+
/* @__PURE__ */ jsx7(
|
|
2748
|
+
"button",
|
|
2749
|
+
{
|
|
2750
|
+
type: "button",
|
|
2751
|
+
onClick: () => setIsDialogOpen(false),
|
|
2752
|
+
className: "rounded-lg border border-border px-4 py-2 text-sm font-medium text-foreground hover:bg-muted transition-colors",
|
|
2753
|
+
children: "Cancel"
|
|
2754
|
+
}
|
|
2755
|
+
),
|
|
2756
|
+
/* @__PURE__ */ jsx7(
|
|
2757
|
+
"button",
|
|
2758
|
+
{
|
|
2759
|
+
type: "button",
|
|
2760
|
+
onClick: handleSave,
|
|
2761
|
+
disabled: isSaving || !formData.name.trim() || !formData.content.trim(),
|
|
2762
|
+
className: "rounded-lg bg-[var(--btn-primary-bg)] px-4 py-2 text-sm font-bold text-[var(--btn-primary-text)] shadow-sm transition-colors hover:bg-[var(--btn-primary-hover)] disabled:opacity-50",
|
|
2763
|
+
children: isSaving ? "Saving..." : editingScript ? "Save Changes" : "Create Script"
|
|
2764
|
+
}
|
|
2765
|
+
)
|
|
2766
|
+
] })
|
|
2767
|
+
] })
|
|
2768
|
+
]
|
|
2769
|
+
},
|
|
2770
|
+
`form-${stepKey}`
|
|
2771
|
+
)
|
|
2772
|
+
] }) }),
|
|
2773
|
+
/* @__PURE__ */ jsx7(Dialog, { open: !!deleteTarget, onOpenChange: () => setDeleteTarget(null), children: /* @__PURE__ */ jsxs7(DialogContent, { className: "max-w-md", children: [
|
|
2774
|
+
/* @__PURE__ */ jsxs7(DialogHeader, { children: [
|
|
2775
|
+
/* @__PURE__ */ jsx7(DialogTitle, { children: "Delete Startup Script" }),
|
|
2776
|
+
/* @__PURE__ */ jsxs7(DialogDescription, { children: [
|
|
2777
|
+
"Are you sure you want to delete \u201C",
|
|
2778
|
+
deleteTarget?.name,
|
|
2779
|
+
"\u201D? This action cannot be undone."
|
|
2780
|
+
] })
|
|
2781
|
+
] }),
|
|
2782
|
+
/* @__PURE__ */ jsxs7(DialogFooter, { children: [
|
|
2783
|
+
/* @__PURE__ */ jsx7(
|
|
2784
|
+
"button",
|
|
2785
|
+
{
|
|
2786
|
+
type: "button",
|
|
2787
|
+
onClick: () => setDeleteTarget(null),
|
|
2788
|
+
className: "rounded-lg border border-border px-4 py-2 text-sm font-medium text-foreground hover:bg-muted transition-colors",
|
|
2789
|
+
children: "Cancel"
|
|
2790
|
+
}
|
|
2791
|
+
),
|
|
2792
|
+
/* @__PURE__ */ jsx7(
|
|
2793
|
+
"button",
|
|
2794
|
+
{
|
|
2795
|
+
type: "button",
|
|
2796
|
+
onClick: handleDelete,
|
|
2797
|
+
disabled: isDeleting,
|
|
2798
|
+
className: "rounded-lg bg-destructive px-4 py-2 text-sm font-bold text-destructive-foreground shadow-sm transition-colors hover:bg-destructive/90 disabled:opacity-50",
|
|
2799
|
+
children: isDeleting ? "Deleting..." : "Delete"
|
|
2800
|
+
}
|
|
2801
|
+
)
|
|
2802
|
+
] })
|
|
2803
|
+
] }) }),
|
|
2804
|
+
/* @__PURE__ */ jsxs7("div", { className: "overflow-hidden rounded-lg border border-border bg-card shadow-[var(--shadow-card)]", children: [
|
|
2805
|
+
/* @__PURE__ */ jsxs7("div", { className: "border-b border-border px-6 py-4 flex items-center justify-between", children: [
|
|
2806
|
+
/* @__PURE__ */ jsx7("div", { className: "flex items-center gap-2", children: /* @__PURE__ */ jsx7("button", { className: "text-xs font-bold uppercase tracking-widest text-foreground", children: "All Scripts" }) }),
|
|
2807
|
+
/* @__PURE__ */ jsxs7("span", { className: "text-xs text-muted-foreground font-mono", children: [
|
|
2808
|
+
scripts.length,
|
|
2809
|
+
" script",
|
|
2810
|
+
scripts.length !== 1 ? "s" : ""
|
|
2811
|
+
] })
|
|
2812
|
+
] }),
|
|
2813
|
+
loading ? /* @__PURE__ */ jsx7("div", { className: "flex items-center justify-center py-16", children: /* @__PURE__ */ jsx7("div", { className: "h-6 w-6 animate-spin rounded-full border-2 border-primary border-t-transparent" }) }) : scripts.length === 0 ? /* @__PURE__ */ jsxs7("div", { className: "flex flex-col items-center justify-center py-16 text-center", children: [
|
|
2814
|
+
/* @__PURE__ */ jsx7(Terminal, { className: "h-10 w-10 text-muted-foreground mb-4" }),
|
|
2815
|
+
/* @__PURE__ */ jsx7("h3", { className: "text-lg font-semibold text-foreground", children: "No startup scripts yet" }),
|
|
2816
|
+
/* @__PURE__ */ jsx7("p", { className: "mt-1 text-sm text-muted-foreground max-w-sm", children: "Create a script to run automatically when your sandboxes start." }),
|
|
2817
|
+
/* @__PURE__ */ jsxs7(
|
|
2818
|
+
"button",
|
|
2819
|
+
{
|
|
2820
|
+
type: "button",
|
|
2821
|
+
onClick: openCreate,
|
|
2822
|
+
className: "mt-4 inline-flex items-center gap-2 rounded-lg bg-[var(--btn-primary-bg)] px-4 py-2.5 text-sm font-bold text-[var(--btn-primary-text)] shadow-sm transition-colors hover:bg-[var(--btn-primary-hover)]",
|
|
2823
|
+
children: [
|
|
2824
|
+
/* @__PURE__ */ jsx7(Plus4, { className: "h-4 w-4" }),
|
|
2825
|
+
"Create Script"
|
|
2826
|
+
]
|
|
2827
|
+
}
|
|
2828
|
+
)
|
|
2829
|
+
] }) : /* @__PURE__ */ jsx7("div", { className: "divide-y divide-border", children: scripts.map((script) => /* @__PURE__ */ jsxs7(
|
|
2830
|
+
"div",
|
|
2831
|
+
{
|
|
2832
|
+
className: cn(
|
|
2833
|
+
"group flex items-center gap-4 px-6 py-4 transition-colors hover:bg-muted/30",
|
|
2834
|
+
!script.enabled && "opacity-60"
|
|
2835
|
+
),
|
|
2836
|
+
children: [
|
|
2837
|
+
/* @__PURE__ */ jsx7(
|
|
2838
|
+
"button",
|
|
2839
|
+
{
|
|
2840
|
+
type: "button",
|
|
2841
|
+
onClick: () => handleToggle(script),
|
|
2842
|
+
title: script.enabled ? "Disable" : "Enable",
|
|
2843
|
+
className: cn(
|
|
2844
|
+
"flex h-8 w-8 shrink-0 items-center justify-center rounded-full transition-colors",
|
|
2845
|
+
script.enabled ? "bg-[var(--surface-success-bg,hsl(142 76% 90%))] text-[var(--surface-success-text,hsl(142 76% 36%))]" : "bg-muted text-muted-foreground"
|
|
2846
|
+
),
|
|
2847
|
+
children: script.enabled ? /* @__PURE__ */ jsx7(Power, { className: "h-4 w-4" }) : /* @__PURE__ */ jsx7(PowerOff, { className: "h-4 w-4" })
|
|
2848
|
+
}
|
|
2849
|
+
),
|
|
2850
|
+
/* @__PURE__ */ jsxs7("div", { className: "flex-1 min-w-0", children: [
|
|
2851
|
+
/* @__PURE__ */ jsxs7("div", { className: "flex items-center gap-2", children: [
|
|
2852
|
+
/* @__PURE__ */ jsx7("h4", { className: "text-sm font-bold text-foreground truncate", children: script.name }),
|
|
2853
|
+
/* @__PURE__ */ jsx7("span", { className: "text-[10px] font-mono text-muted-foreground bg-muted rounded px-1.5 py-0.5", children: SCRIPT_TYPE_META[script.scriptType ?? "bash"].label }),
|
|
2854
|
+
script.runOrder !== 100 && /* @__PURE__ */ jsxs7("span", { className: "text-[10px] font-mono text-muted-foreground bg-muted rounded px-1.5 py-0.5", children: [
|
|
2855
|
+
"#",
|
|
2856
|
+
script.runOrder
|
|
2857
|
+
] })
|
|
2858
|
+
] }),
|
|
2859
|
+
script.description && /* @__PURE__ */ jsx7("p", { className: "mt-0.5 text-xs text-muted-foreground truncate", children: script.description }),
|
|
2860
|
+
/* @__PURE__ */ jsxs7("div", { className: "mt-1.5 flex flex-wrap gap-1.5", children: [
|
|
2861
|
+
script.environments.map((env) => /* @__PURE__ */ jsx7("span", { className: "rounded-full bg-primary/10 px-2 py-0.5 text-[10px] font-medium text-primary", children: env }, env)),
|
|
2862
|
+
script.injectSecrets.map((s) => /* @__PURE__ */ jsxs7("span", { className: "rounded-full bg-muted px-2 py-0.5 text-[10px] font-medium text-muted-foreground flex items-center gap-0.5", children: [
|
|
2863
|
+
/* @__PURE__ */ jsx7(Lock2, { className: "h-2.5 w-2.5" }),
|
|
2864
|
+
s
|
|
2865
|
+
] }, s)),
|
|
2866
|
+
script.continueOnFailure && /* @__PURE__ */ jsx7("span", { className: "rounded-full bg-amber-500/10 px-2 py-0.5 text-[10px] font-medium text-amber-600", children: "soft fail" }),
|
|
2867
|
+
script.runAsRoot && /* @__PURE__ */ jsx7("span", { className: "rounded-full bg-destructive/10 px-2 py-0.5 text-[10px] font-medium text-destructive", children: "root" })
|
|
2868
|
+
] })
|
|
2869
|
+
] }),
|
|
2870
|
+
/* @__PURE__ */ jsxs7("div", { className: "hidden md:flex items-center gap-1 text-xs text-muted-foreground", children: [
|
|
2871
|
+
/* @__PURE__ */ jsx7(Clock, { className: "h-3.5 w-3.5" }),
|
|
2872
|
+
script.timeoutSeconds,
|
|
2873
|
+
"s"
|
|
2874
|
+
] }),
|
|
2875
|
+
/* @__PURE__ */ jsxs7("div", { className: "flex items-center gap-1 sm:opacity-0 sm:group-hover:opacity-100 sm:focus-within:opacity-100 transition-opacity", children: [
|
|
2876
|
+
/* @__PURE__ */ jsx7(
|
|
2877
|
+
"button",
|
|
2878
|
+
{
|
|
2879
|
+
type: "button",
|
|
2880
|
+
onClick: () => openEdit(script),
|
|
2881
|
+
className: "rounded-md p-2 text-muted-foreground hover:text-foreground hover:bg-muted transition-colors",
|
|
2882
|
+
"aria-label": `Edit ${script.name}`,
|
|
2883
|
+
children: /* @__PURE__ */ jsx7(Pencil, { className: "h-4 w-4" })
|
|
2884
|
+
}
|
|
2885
|
+
),
|
|
2886
|
+
/* @__PURE__ */ jsx7(
|
|
2887
|
+
"button",
|
|
2888
|
+
{
|
|
2889
|
+
type: "button",
|
|
2890
|
+
onClick: () => setDeleteTarget(script),
|
|
2891
|
+
className: "rounded-md p-2 text-muted-foreground hover:text-destructive hover:bg-destructive/10 transition-colors",
|
|
2892
|
+
"aria-label": `Delete ${script.name}`,
|
|
2893
|
+
children: /* @__PURE__ */ jsx7(Trash24, { className: "h-4 w-4" })
|
|
2894
|
+
}
|
|
2895
|
+
)
|
|
2896
|
+
] })
|
|
2897
|
+
]
|
|
2898
|
+
},
|
|
2899
|
+
script.id
|
|
2900
|
+
)) })
|
|
2901
|
+
] }),
|
|
2902
|
+
/* @__PURE__ */ jsxs7("div", { className: "grid grid-cols-1 gap-6 md:grid-cols-2", children: [
|
|
2903
|
+
/* @__PURE__ */ jsxs7("div", { className: "rounded-lg border border-border bg-card p-6 shadow-[var(--shadow-card)]", children: [
|
|
2904
|
+
/* @__PURE__ */ jsxs7("div", { className: "flex items-center gap-3 mb-3", children: [
|
|
2905
|
+
/* @__PURE__ */ jsx7("div", { className: "flex h-9 w-9 items-center justify-center rounded-md bg-[var(--brand-primary,hsl(var(--primary)))] text-[var(--btn-primary-text)]", children: /* @__PURE__ */ jsx7(Play, { className: "h-5 w-5" }) }),
|
|
2906
|
+
/* @__PURE__ */ jsx7("h3", { className: "text-sm font-bold text-foreground", children: "How Scripts Run" })
|
|
2907
|
+
] }),
|
|
2908
|
+
/* @__PURE__ */ jsx7("p", { className: "text-sm text-muted-foreground", children: 'Scripts execute in order after the container starts but before the AI agent. They run as bash scripts with full access to mounted tools (Nix profile) and workspace. Failed scripts abort sandbox creation unless "continue on failure" is enabled.' })
|
|
2909
|
+
] }),
|
|
2910
|
+
/* @__PURE__ */ jsxs7("div", { className: "rounded-lg border border-border bg-card p-6 shadow-[var(--shadow-card)]", children: [
|
|
2911
|
+
/* @__PURE__ */ jsxs7("div", { className: "flex items-center gap-3 mb-3", children: [
|
|
2912
|
+
/* @__PURE__ */ jsx7("div", { className: "flex h-9 w-9 items-center justify-center rounded-md bg-[var(--brand-primary,hsl(var(--primary)))] text-[var(--btn-primary-text)]", children: /* @__PURE__ */ jsx7(Shield2, { className: "h-5 w-5" }) }),
|
|
2913
|
+
/* @__PURE__ */ jsx7("h3", { className: "text-sm font-bold text-foreground", children: "Security & Secrets" })
|
|
2914
|
+
] }),
|
|
2915
|
+
/* @__PURE__ */ jsx7("p", { className: "text-sm text-muted-foreground", children: "Selected secrets are injected as environment variables at execution time. Secret values are never stored in the script itself \u2014 they are decrypted and injected only when the sandbox starts. Scripts can use conditions to restrict execution to specific environments or resource tiers." })
|
|
2916
|
+
] })
|
|
2917
|
+
] })
|
|
2918
|
+
] });
|
|
2919
|
+
}
|
|
2920
|
+
|
|
1863
2921
|
// src/lib/template-presets.ts
|
|
1864
2922
|
var BLOCKCHAIN_PATTERNS = [
|
|
1865
2923
|
"ethereum",
|
|
@@ -2017,6 +3075,7 @@ export {
|
|
|
2017
3075
|
ProvisioningWizard,
|
|
2018
3076
|
SecretsPage,
|
|
2019
3077
|
StandalonePricingPage,
|
|
3078
|
+
StartupScriptsPage,
|
|
2020
3079
|
TemplatesPage,
|
|
2021
3080
|
getPresetForTemplate,
|
|
2022
3081
|
resolveEnvironment
|