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