@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/dist/pages.js CHANGED
@@ -5,7 +5,7 @@ import {
5
5
  DialogFooter,
6
6
  DialogHeader,
7
7
  DialogTitle
8
- } from "./chunk-3CJ2SOEI.js";
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-7U2Z23NE.js";
22
+ } from "./chunk-VX3XOUEB.js";
22
23
  import {
23
24
  BillingDashboard,
24
25
  PricingPage,
25
26
  UsageChart
26
- } from "./chunk-UXQMIR3D.js";
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-2QZ6G7NM.js";
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: "Universal", abbr: "U", color: "violet", textClass: "text-[var(--surface-violet-text)]" },
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
- if (onLoadEnvironments) {
354
- onLoadEnvironments().then((entries) => setEnvList(entries.map(resolveEnvironment)));
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
- }, [onLoadEnvironments, environmentsProp]);
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 handleDeploy = () => {
412
+ const [deployError, setDeployError] = React2.useState(null);
413
+ const handleDeploy = async () => {
414
+ if (!onSubmit) return;
382
415
  setIsDeploying(true);
383
- onSubmit?.({ environment: selectedEnv, cpuCores, ramGB, storageGB, modelTier, systemPrompt, name, gitUrl, envVars: envVars.filter((e) => e.key.trim() !== ""), driver, bare });
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 glass-panel 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: [
402
- /* @__PURE__ */ jsx2("div", { className: cn("w-7 h-7 rounded-full flex items-center justify-center font-bold text-xs transition-colors shrink-0", currentStep === s ? "bg-primary text-primary-foreground" : currentStep > s ? "bg-primary/40 text-primary-foreground" : "bg-muted text-muted-foreground"), children: s }),
403
- /* @__PURE__ */ jsxs2("span", { className: cn("ml-2 sm:ml-3 font-bold text-sm tracking-tight hidden sm:inline", currentStep === s ? "text-foreground" : currentStep > s ? "text-primary/60" : "text-muted-foreground"), children: [
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-px mx-2 sm:mx-4 transition-colors", currentStep > s ? "bg-primary/40" : "bg-border") })
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 glass-panel rounded-2xl px-4 py-3 shrink-0", children: [
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-accent shrink-0" }),
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-accent hover:text-accent-deep transition-colors",
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: "glass-panel rounded-[24px] p-6 shadow-2xl relative overflow-hidden", children: [
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-accent/10 border border-accent/20 text-accent glow-primary", children: /* @__PURE__ */ jsx2(Layers, { className: "h-5 w-5" }) }),
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] transition-all text-left overflow-hidden border border-transparent",
446
- selectedEnv === env.id ? "glass-panel-heavy border-accent glow-primary" : "glass-panel hover:border-[var(--glass-border-color)]"
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-accent/10 to-transparent pointer-events-none" }),
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("w-5 h-5 rounded-full border-2 flex items-center justify-center transition-colors", selectedEnv === env.id ? "border-accent bg-accent/20" : "border-border"), children: selectedEnv === env.id && /* @__PURE__ */ jsx2("div", { className: "w-2.5 h-2.5 bg-accent rounded-full animate-in zoom-in" }) })
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: "glass-panel rounded-[24px] p-6 shadow-2xl relative overflow-hidden", children: [
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-accent/10 border border-accent/20 text-accent glow-primary", children: /* @__PURE__ */ jsx2(Cpu, { className: "h-5 w-5" }) }),
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 ? "glass-panel-heavy border-accent glow-primary" : "glass-panel hover:border-[var(--glass-border-color)]"), children: [
470
- /* @__PURE__ */ jsx2("div", { className: cn("font-bold text-sm transition-colors", cpuCores === 2 && ramGB === 4 && storageGB === 50 ? "text-accent" : "text-foreground"), children: "Lightweight" }),
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 ? "glass-panel-heavy border-accent glow-primary" : "glass-panel hover:border-[var(--glass-border-color)]"), children: [
474
- /* @__PURE__ */ jsx2("div", { className: cn("font-bold text-sm transition-colors", cpuCores === 4 && ramGB === 16 && storageGB === 128 ? "text-accent" : "text-foreground"), children: "Standard" }),
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 ? "glass-panel-heavy border-accent glow-primary" : "glass-panel hover:border-[var(--glass-border-color)]"), children: [
478
- /* @__PURE__ */ jsx2("div", { className: cn("font-bold text-sm transition-colors", cpuCores === 8 && ramGB === 32 && storageGB === 256 ? "text-accent" : "text-foreground"), children: "Performance" }),
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-accent/80 ml-1", children: unit })
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-1.5 bg-muted rounded-full appearance-none cursor-pointer accent-accent"
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: "glass-panel rounded-[24px] p-6 shadow-2xl relative overflow-hidden", children: [
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-accent/10 border border-accent/20 text-accent glow-primary", children: /* @__PURE__ */ jsx2(Bot, { className: "h-5 w-5" }) }),
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 glass-panel rounded-xl h-12 px-4 font-bold text-sm text-foreground focus:outline-none focus:ring-2 focus:ring-accent focus:border-transparent appearance-none",
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
- className: "w-full glass-panel rounded-xl p-4 font-mono text-sm text-foreground focus:outline-none focus:ring-2 focus:ring-accent focus:border-transparent h-32 resize-none placeholder:text-muted-foreground",
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
- className: "w-full glass-panel rounded-xl h-12 px-4 font-bold text-sm text-foreground focus:outline-none focus:ring-2 focus:ring-accent focus:border-transparent placeholder:text-muted-foreground",
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) => setDriver(e.target.value),
589
- className: "w-full glass-panel rounded-xl h-12 px-4 font-bold text-sm text-foreground focus:outline-none focus:ring-2 focus:ring-accent focus:border-transparent appearance-none",
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 glass-panel rounded-xl h-12 px-4 font-bold text-sm text-foreground focus:outline-none focus:ring-2 focus:ring-accent focus:border-transparent placeholder:text-muted-foreground",
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-accent hover:text-accent-deep transition-colors font-bold", children: [
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 glass-panel rounded-xl h-10 px-3 font-mono text-sm text-foreground focus:outline-none focus:ring-1 focus:ring-accent placeholder:text-muted-foreground",
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] glass-panel rounded-xl h-10 px-3 font-mono text-sm text-foreground focus:outline-none focus:ring-1 focus:ring-accent placeholder:text-muted-foreground",
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 glass-panel text-red-400 hover:bg-red-500/10 hover:border-red-500/30 transition-colors", children: /* @__PURE__ */ jsx2(Trash2, { className: "h-4 w-4" }) })
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 glass-panel rounded-xl text-muted-foreground/60 text-sm italic", children: "No environment variables set" })
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-accent hover:bg-white/20 transition-colors" })
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-accent transition-colors", children: "Bare Mode" }),
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: "glass-panel-heavy rounded-[24px] overflow-hidden shadow-2xl relative", children: [
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-accent mr-2", children: "\u2713" }),
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-accent mr-2", children: "\u2713" }),
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-accent mr-2", children: "\u2713" }),
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-accent mr-2", children: "\u2713" }),
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-accent animate-pulse" }),
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-2xl bg-accent/5 backdrop-blur-md border border-accent/20 shadow-[0_0_30px_rgba(173,163,255,0.1)] relative overflow-hidden", children: [
716
- /* @__PURE__ */ jsx2("div", { className: "absolute -top-20 -right-20 w-40 h-40 bg-accent/20 blur-[50px] rounded-full" }),
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-accent" }) })
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 glass-panel rounded-xl p-3 border border-border", children: [
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-accent/10 text-accent font-extrabold text-sm rounded-2xl border border-accent/20 hover:bg-accent/20 transition-colors shadow-sm shadow-accent/10",
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__ */ jsxs2(
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 relative overflow-hidden h-12 bg-gradient-to-r from-accent to-accent-deep text-white font-extrabold text-sm rounded-2xl tracking-wide shadow-[0_0_20px_rgba(130,99,255,0.2)] disabled:opacity-50 transition-transform active:scale-[0.98]",
765
- children: [
766
- isDeploying ? /* @__PURE__ */ jsxs2("span", { className: "relative z-10 flex items-center justify-center gap-2", children: [
767
- /* @__PURE__ */ jsx2(Loader2, { className: "h-4 w-4 animate-spin" }),
768
- "Deploying..."
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 glass-panel text-foreground/70 font-bold text-sm rounded-2xl hover:text-foreground hover:border-[var(--glass-border-color)] transition-colors",
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__ */ jsxs2(
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 relative overflow-hidden h-12 bg-gradient-to-r from-accent to-accent-deep text-white font-extrabold text-sm rounded-2xl tracking-wide shadow-[0_0_20px_rgba(130,99,255,0.2)] disabled:opacity-50 transition-transform active:scale-[0.98]",
790
- children: [
791
- isDeploying ? /* @__PURE__ */ jsxs2("span", { className: "relative z-10 flex items-center justify-center gap-2", children: [
792
- /* @__PURE__ */ jsx2(Loader2, { className: "h-4 w-4 animate-spin" }),
793
- "Spinning up environment..."
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("h1", { className: "text-4xl font-extrabold tracking-tight text-foreground sm:text-5xl", children: title }),
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 loadSecrets = React5.useCallback(async () => {
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-6", className), children: [
1651
- /* @__PURE__ */ jsxs5("div", { className: "flex items-center justify-between", children: [
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-bold text-3xl text-foreground", children: "Secrets" }),
1654
- /* @__PURE__ */ jsx5("p", { className: "mt-1 text-muted-foreground", children: "Secrets securely stored here will be automatically exposed as environment variables across all your new sandboxes." })
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/20 border border-primary/30 px-4 py-2 text-sm font-bold text-primary hover:bg-primary hover:text-primary-foreground transition-colors active:scale-[0.97]",
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
- "Create Secret"
1759
+ "New Secret"
1665
1760
  ]
1666
1761
  }
1667
1762
  )
1668
1763
  ] }),
1669
- error && /* @__PURE__ */ jsxs5("div", { className: "rounded-xl border border-destructive/30 bg-destructive/10 p-4 flex items-center gap-3", children: [
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 securely stored here will be automatically exposed as environment variables across all your new sandboxes." })
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-sm font-medium text-foreground mb-1.5", children: "Name" }),
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-lg border border-border bg-background px-3 py-2 text-sm font-mono text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring"
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-sm font-medium text-foreground mb-1.5", children: "Value" }),
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-lg border border-border bg-background px-3 py-2 pr-10 text-sm font-mono text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring"
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-lg border border-border bg-muted px-4 py-2 text-sm font-medium text-foreground hover:bg-muted/80 transition-colors",
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-lg bg-primary px-4 py-2 text-sm font-bold text-primary-foreground hover:bg-primary/90 transition-colors disabled:opacity-50 active:scale-[0.97]",
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-lg border border-border bg-muted px-4 py-2 text-sm font-medium text-foreground hover:bg-muted/80 transition-colors",
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-lg 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]",
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-xl border border-border bg-card shadow-sm overflow-hidden", children: [
1788
- /* @__PURE__ */ jsxs5("div", { className: "border-b border-border bg-muted/20 px-6 py-4 flex items-center justify-between", children: [
1789
- /* @__PURE__ */ jsx5("h2", { className: "text-sm font-bold text-foreground", children: "Your Secrets" }),
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: "p-12 text-center", children: /* @__PURE__ */ jsx5("div", { className: "mx-auto h-6 w-6 animate-spin rounded-full border-2 border-primary border-t-transparent" }) }) : secrets.length === 0 ? /* @__PURE__ */ jsxs5("div", { className: "p-12 text-center", children: [
1797
- /* @__PURE__ */ jsx5(Lock, { className: "mx-auto mb-3 h-10 w-10 text-muted-foreground" }),
1798
- /* @__PURE__ */ jsx5("p", { className: "text-sm font-medium text-foreground mb-1", children: "No secrets yet" }),
1799
- /* @__PURE__ */ jsx5("p", { className: "text-sm text-muted-foreground mb-4", children: "Create a secret to inject into your sandboxes." }),
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-lg bg-primary/20 border border-primary/30 px-4 py-2 text-sm font-bold text-primary hover:bg-primary hover:text-primary-foreground transition-colors",
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-sm", children: [
1813
- /* @__PURE__ */ jsx5("thead", { className: "bg-muted/10 border-b border-border", children: /* @__PURE__ */ jsxs5("tr", { children: [
1814
- /* @__PURE__ */ jsx5("th", { className: "px-6 py-2.5 text-left text-xs font-medium text-muted-foreground", children: "Name" }),
1815
- /* @__PURE__ */ jsx5("th", { className: "px-6 py-2.5 text-left text-xs font-medium text-muted-foreground", children: "Created" }),
1816
- /* @__PURE__ */ jsx5("th", { className: "px-6 py-2.5 text-right text-xs font-medium text-muted-foreground w-16" })
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/30 transition-colors", children: [
1819
- /* @__PURE__ */ jsx5("td", { className: "px-6 py-3", children: /* @__PURE__ */ jsxs5("div", { className: "flex items-center gap-2", children: [
1820
- /* @__PURE__ */ jsx5(Lock, { className: "h-3.5 w-3.5 text-muted-foreground" }),
1821
- /* @__PURE__ */ jsx5("span", { className: "font-mono text-xs font-bold text-foreground", children: secret.name })
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-3 text-xs text-muted-foreground", children: formatDate(secret.createdAt) }),
1824
- /* @__PURE__ */ jsx5("td", { className: "px-6 py-3 text-right", children: /* @__PURE__ */ jsx5(
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 rounded",
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