clawcontrol 0.1.6 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +1682 -717
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -4,8 +4,8 @@ import { createCliRenderer } from "@opentui/core";
|
|
|
4
4
|
import { createRoot } from "@opentui/react";
|
|
5
5
|
|
|
6
6
|
// src/App.tsx
|
|
7
|
-
import { useState as
|
|
8
|
-
import { useRenderer } from "@opentui/react";
|
|
7
|
+
import { useState as useState12, useCallback as useCallback4, useRef as useRef6 } from "react";
|
|
8
|
+
import { useRenderer as useRenderer2 } from "@opentui/react";
|
|
9
9
|
|
|
10
10
|
// src/components/Home.tsx
|
|
11
11
|
import { useState } from "react";
|
|
@@ -142,10 +142,12 @@ var LOGO = `
|
|
|
142
142
|
`;
|
|
143
143
|
var COMMANDS = [
|
|
144
144
|
{ name: "/new", description: "Initialize a new deployment" },
|
|
145
|
+
{ name: "/list", description: "List, edit, or fork deployments" },
|
|
145
146
|
{ name: "/deploy", description: "Deploy an initialized configuration" },
|
|
146
147
|
{ name: "/status", description: "View deployment status" },
|
|
147
148
|
{ name: "/ssh", description: "SSH into a deployment" },
|
|
148
149
|
{ name: "/logs", description: "View deployment logs" },
|
|
150
|
+
{ name: "/dashboard", description: "Open OpenClaw dashboard in browser" },
|
|
149
151
|
{ name: "/destroy", description: "Destroy a deployment" },
|
|
150
152
|
{ name: "/templates", description: "Manage deployment templates" },
|
|
151
153
|
{ name: "/help", description: "Show help" }
|
|
@@ -158,10 +160,12 @@ function Home({ context }) {
|
|
|
158
160
|
setError(null);
|
|
159
161
|
const viewMap = {
|
|
160
162
|
"/new": "new",
|
|
163
|
+
"/list": "list",
|
|
161
164
|
"/deploy": "deploy",
|
|
162
165
|
"/status": "status",
|
|
163
166
|
"/ssh": "ssh",
|
|
164
167
|
"/logs": "logs",
|
|
168
|
+
"/dashboard": "dashboard",
|
|
165
169
|
"/destroy": "destroy",
|
|
166
170
|
"/templates": "templates",
|
|
167
171
|
"/help": "help"
|
|
@@ -223,7 +227,7 @@ function Home({ context }) {
|
|
|
223
227
|
children: [
|
|
224
228
|
/* @__PURE__ */ jsx("text", { fg: t.fg.primary, children: "Available Commands" }),
|
|
225
229
|
/* @__PURE__ */ jsx("box", { flexDirection: "column", marginTop: 1, children: COMMANDS.map((cmd) => /* @__PURE__ */ jsxs("box", { flexDirection: "row", children: [
|
|
226
|
-
/* @__PURE__ */ jsx("text", { fg: t.accent, width:
|
|
230
|
+
/* @__PURE__ */ jsx("text", { fg: t.accent, width: 14, children: cmd.name }),
|
|
227
231
|
/* @__PURE__ */ jsx("text", { fg: t.fg.secondary, children: cmd.description })
|
|
228
232
|
] }, cmd.name)) })
|
|
229
233
|
]
|
|
@@ -372,7 +376,8 @@ var DeploymentConfigSchema = z.object({
|
|
|
372
376
|
hetzner: HetznerConfigSchema.optional(),
|
|
373
377
|
digitalocean: DigitalOceanConfigSchema.optional(),
|
|
374
378
|
openclawConfig: OpenClawConfigSchema,
|
|
375
|
-
openclawAgent: OpenClawAgentConfigSchema.optional()
|
|
379
|
+
openclawAgent: OpenClawAgentConfigSchema.optional(),
|
|
380
|
+
skipTailscale: z.boolean().optional()
|
|
376
381
|
});
|
|
377
382
|
var DeploymentStateSchema = z.object({
|
|
378
383
|
status: DeploymentStatusSchema,
|
|
@@ -388,6 +393,7 @@ var DeploymentStateSchema = z.object({
|
|
|
388
393
|
retryCount: z.number()
|
|
389
394
|
})
|
|
390
395
|
),
|
|
396
|
+
gatewayToken: z.string().optional(),
|
|
391
397
|
lastError: z.string().optional(),
|
|
392
398
|
deployedAt: z.string().optional(),
|
|
393
399
|
updatedAt: z.string()
|
|
@@ -531,6 +537,14 @@ function deleteDeployment(name) {
|
|
|
531
537
|
}
|
|
532
538
|
rmSync(deploymentDir, { recursive: true, force: true });
|
|
533
539
|
}
|
|
540
|
+
function updateDeploymentConfig(name, config) {
|
|
541
|
+
const configPath = getConfigPath(name);
|
|
542
|
+
if (!existsSync(configPath)) {
|
|
543
|
+
throw new Error(`Deployment "${name}" not found`);
|
|
544
|
+
}
|
|
545
|
+
const validated = DeploymentConfigSchema.parse(config);
|
|
546
|
+
writeFileSync(configPath, JSON.stringify(validated, null, 2));
|
|
547
|
+
}
|
|
534
548
|
function validateDeploymentName(name) {
|
|
535
549
|
if (!name || name.trim() === "") {
|
|
536
550
|
return { valid: false, error: "Deployment name cannot be empty" };
|
|
@@ -1196,20 +1210,36 @@ var DO_DROPLET_SIZES = [
|
|
|
1196
1210
|
{ slug: "s-4vcpu-8gb", label: "4 vCPU, 8GB RAM, 160GB SSD", price: "$48/mo" },
|
|
1197
1211
|
{ slug: "s-8vcpu-16gb", label: "8 vCPU, 16GB RAM, 320GB SSD", price: "$96/mo" }
|
|
1198
1212
|
];
|
|
1199
|
-
function getStepList(provider, activeTemplate) {
|
|
1213
|
+
function getStepList(provider, activeTemplate, editMode) {
|
|
1214
|
+
if (editMode === "edit") {
|
|
1215
|
+
const steps = ["api_key_choose"];
|
|
1216
|
+
if (provider === "digitalocean") {
|
|
1217
|
+
steps.push("droplet_size");
|
|
1218
|
+
}
|
|
1219
|
+
steps.push("ai_api_key_choose", "model", "telegram_token_choose", "telegram_allow_choose", "confirm");
|
|
1220
|
+
return steps;
|
|
1221
|
+
}
|
|
1222
|
+
if (editMode === "fork") {
|
|
1223
|
+
const steps = ["name", "api_key_choose"];
|
|
1224
|
+
if (provider === "digitalocean") {
|
|
1225
|
+
steps.push("droplet_size");
|
|
1226
|
+
}
|
|
1227
|
+
steps.push("ai_api_key_choose", "model", "telegram_token_choose", "telegram_allow_choose", "tailscale", "confirm");
|
|
1228
|
+
return steps;
|
|
1229
|
+
}
|
|
1200
1230
|
const base = ["template_choice"];
|
|
1201
1231
|
if (!activeTemplate) {
|
|
1202
1232
|
base.push("name", "provider", "api_key_choose");
|
|
1203
1233
|
if (provider === "digitalocean") {
|
|
1204
1234
|
base.push("droplet_size");
|
|
1205
1235
|
}
|
|
1206
|
-
base.push("ai_provider", "ai_api_key_choose", "model", "telegram_token_choose", "telegram_allow_choose", "confirm");
|
|
1236
|
+
base.push("ai_provider", "ai_api_key_choose", "model", "telegram_token_choose", "telegram_allow_choose", "tailscale", "confirm");
|
|
1207
1237
|
} else {
|
|
1208
1238
|
base.push("name", "api_key_choose");
|
|
1209
1239
|
if (activeTemplate.provider === "digitalocean") {
|
|
1210
1240
|
base.push("droplet_size");
|
|
1211
1241
|
}
|
|
1212
|
-
base.push("ai_api_key_choose", "model", "telegram_token_choose", "telegram_allow_choose", "confirm");
|
|
1242
|
+
base.push("ai_api_key_choose", "model", "telegram_token_choose", "telegram_allow_choose", "tailscale", "confirm");
|
|
1213
1243
|
}
|
|
1214
1244
|
return base;
|
|
1215
1245
|
}
|
|
@@ -1236,19 +1266,39 @@ function resolveStepForProgress(s) {
|
|
|
1236
1266
|
}
|
|
1237
1267
|
}
|
|
1238
1268
|
function NewDeployment({ context }) {
|
|
1269
|
+
const [editingConfig, setEditingConfig] = useState2(null);
|
|
1270
|
+
const [editMode, setEditMode] = useState2(null);
|
|
1239
1271
|
const [templateChoices, setTemplateChoices] = useState2([]);
|
|
1240
1272
|
const [selectedTemplateIndex, setSelectedTemplateIndex] = useState2(0);
|
|
1241
1273
|
const [activeTemplate, setActiveTemplate] = useState2(null);
|
|
1242
1274
|
const [step, setStep] = useState2(() => {
|
|
1275
|
+
if (context.editingDeployment) {
|
|
1276
|
+
return context.editingDeployment.mode === "edit" ? "api_key_choose" : "name";
|
|
1277
|
+
}
|
|
1243
1278
|
if (context.selectedTemplate) return "name";
|
|
1244
1279
|
return "template_choice";
|
|
1245
1280
|
});
|
|
1246
|
-
const [name, setName] = useState2(
|
|
1281
|
+
const [name, setName] = useState2(() => {
|
|
1282
|
+
if (context.editingDeployment?.mode === "edit") return context.editingDeployment.config.name;
|
|
1283
|
+
return "";
|
|
1284
|
+
});
|
|
1247
1285
|
const [provider, setProvider] = useState2(() => {
|
|
1286
|
+
if (context.editingDeployment) return context.editingDeployment.config.provider;
|
|
1248
1287
|
return context.selectedTemplate?.provider ?? "hetzner";
|
|
1249
1288
|
});
|
|
1250
|
-
const [apiKey, setApiKey] = useState2(
|
|
1289
|
+
const [apiKey, setApiKey] = useState2(() => {
|
|
1290
|
+
if (context.editingDeployment) {
|
|
1291
|
+
const cfg = context.editingDeployment.config;
|
|
1292
|
+
if (cfg.provider === "hetzner" && cfg.hetzner) return cfg.hetzner.apiKey;
|
|
1293
|
+
if (cfg.provider === "digitalocean" && cfg.digitalocean) return cfg.digitalocean.apiKey;
|
|
1294
|
+
}
|
|
1295
|
+
return "";
|
|
1296
|
+
});
|
|
1251
1297
|
const [selectedDropletSizeIndex, setSelectedDropletSizeIndex] = useState2(() => {
|
|
1298
|
+
if (context.editingDeployment?.config.digitalocean) {
|
|
1299
|
+
const idx = DO_DROPLET_SIZES.findIndex((s) => s.slug === context.editingDeployment.config.digitalocean.size);
|
|
1300
|
+
return idx >= 0 ? idx : 0;
|
|
1301
|
+
}
|
|
1252
1302
|
if (context.selectedTemplate?.digitalocean) {
|
|
1253
1303
|
const idx = DO_DROPLET_SIZES.findIndex((s) => s.slug === context.selectedTemplate.digitalocean.size);
|
|
1254
1304
|
return idx >= 0 ? idx : 0;
|
|
@@ -1256,17 +1306,40 @@ function NewDeployment({ context }) {
|
|
|
1256
1306
|
return 0;
|
|
1257
1307
|
});
|
|
1258
1308
|
const [aiProvider, setAiProvider] = useState2(() => {
|
|
1309
|
+
if (context.editingDeployment?.config.openclawAgent) return context.editingDeployment.config.openclawAgent.aiProvider;
|
|
1259
1310
|
return context.selectedTemplate?.aiProvider ?? "";
|
|
1260
1311
|
});
|
|
1261
|
-
const [aiApiKey, setAiApiKey] = useState2(
|
|
1312
|
+
const [aiApiKey, setAiApiKey] = useState2(() => {
|
|
1313
|
+
if (context.editingDeployment?.config.openclawAgent) return context.editingDeployment.config.openclawAgent.aiApiKey;
|
|
1314
|
+
return "";
|
|
1315
|
+
});
|
|
1262
1316
|
const [model, setModel] = useState2(() => {
|
|
1317
|
+
if (context.editingDeployment?.config.openclawAgent) return context.editingDeployment.config.openclawAgent.model;
|
|
1263
1318
|
return context.selectedTemplate?.model ?? "";
|
|
1264
1319
|
});
|
|
1265
|
-
const [telegramBotToken, setTelegramBotToken] = useState2(
|
|
1266
|
-
|
|
1320
|
+
const [telegramBotToken, setTelegramBotToken] = useState2(() => {
|
|
1321
|
+
if (context.editingDeployment?.config.openclawAgent) return context.editingDeployment.config.openclawAgent.telegramBotToken;
|
|
1322
|
+
return "";
|
|
1323
|
+
});
|
|
1324
|
+
const [telegramAllowFrom, setTelegramAllowFrom] = useState2(() => {
|
|
1325
|
+
if (context.editingDeployment?.config.openclawAgent) return context.editingDeployment.config.openclawAgent.telegramAllowFrom ?? "";
|
|
1326
|
+
return "";
|
|
1327
|
+
});
|
|
1328
|
+
const [enableTailscale, setEnableTailscale] = useState2(() => {
|
|
1329
|
+
if (context.editingDeployment?.mode === "fork") return !context.editingDeployment.config.skipTailscale;
|
|
1330
|
+
return false;
|
|
1331
|
+
});
|
|
1332
|
+
const [selectedTailscaleIndex, setSelectedTailscaleIndex] = useState2(() => {
|
|
1333
|
+
if (context.editingDeployment?.mode === "fork" && !context.editingDeployment.config.skipTailscale) return 1;
|
|
1334
|
+
return 0;
|
|
1335
|
+
});
|
|
1267
1336
|
const [error, setError] = useState2(null);
|
|
1268
1337
|
const [isValidating, setIsValidating] = useState2(false);
|
|
1269
1338
|
const [selectedProviderIndex, setSelectedProviderIndex] = useState2(() => {
|
|
1339
|
+
if (context.editingDeployment) {
|
|
1340
|
+
const idx = SUPPORTED_PROVIDERS.indexOf(context.editingDeployment.config.provider);
|
|
1341
|
+
return idx >= 0 ? idx : 0;
|
|
1342
|
+
}
|
|
1270
1343
|
if (context.selectedTemplate) {
|
|
1271
1344
|
const idx = SUPPORTED_PROVIDERS.indexOf(context.selectedTemplate.provider);
|
|
1272
1345
|
return idx >= 0 ? idx : 0;
|
|
@@ -1274,6 +1347,10 @@ function NewDeployment({ context }) {
|
|
|
1274
1347
|
return 0;
|
|
1275
1348
|
});
|
|
1276
1349
|
const [selectedAiProviderIndex, setSelectedAiProviderIndex] = useState2(() => {
|
|
1350
|
+
if (context.editingDeployment?.config.openclawAgent) {
|
|
1351
|
+
const idx = AI_PROVIDERS.findIndex((p) => p.name === context.editingDeployment.config.openclawAgent.aiProvider);
|
|
1352
|
+
return idx >= 0 ? idx : 0;
|
|
1353
|
+
}
|
|
1277
1354
|
if (context.selectedTemplate) {
|
|
1278
1355
|
const idx = AI_PROVIDERS.findIndex((p) => p.name === context.selectedTemplate.aiProvider);
|
|
1279
1356
|
return idx >= 0 ? idx : 0;
|
|
@@ -1286,12 +1363,39 @@ function NewDeployment({ context }) {
|
|
|
1286
1363
|
const [savedTelegramUsers, setSavedTelegramUsers] = useState2([]);
|
|
1287
1364
|
const [selectedSavedKeyIndex, setSelectedSavedKeyIndex] = useState2(0);
|
|
1288
1365
|
const [newKeyName, setNewKeyName] = useState2("");
|
|
1289
|
-
const [apiKeyFromSaved, setApiKeyFromSaved] = useState2(
|
|
1290
|
-
const [aiApiKeyFromSaved, setAiApiKeyFromSaved] = useState2(
|
|
1291
|
-
const [telegramTokenFromSaved, setTelegramTokenFromSaved] = useState2(
|
|
1292
|
-
const [telegramAllowFromSaved, setTelegramAllowFromSaved] = useState2(
|
|
1366
|
+
const [apiKeyFromSaved, setApiKeyFromSaved] = useState2(() => !!context.editingDeployment);
|
|
1367
|
+
const [aiApiKeyFromSaved, setAiApiKeyFromSaved] = useState2(() => !!context.editingDeployment);
|
|
1368
|
+
const [telegramTokenFromSaved, setTelegramTokenFromSaved] = useState2(() => !!context.editingDeployment);
|
|
1369
|
+
const [telegramAllowFromSaved, setTelegramAllowFromSaved] = useState2(() => !!context.editingDeployment);
|
|
1293
1370
|
useEffect(() => {
|
|
1294
|
-
if (context.
|
|
1371
|
+
if (context.editingDeployment) {
|
|
1372
|
+
const ed = context.editingDeployment;
|
|
1373
|
+
setEditingConfig(ed.config);
|
|
1374
|
+
setEditMode(ed.mode);
|
|
1375
|
+
const syntheticTemplate = {
|
|
1376
|
+
id: "__editing__",
|
|
1377
|
+
name: ed.config.name,
|
|
1378
|
+
description: "Editing deployment",
|
|
1379
|
+
builtIn: false,
|
|
1380
|
+
createdAt: ed.config.createdAt,
|
|
1381
|
+
provider: ed.config.provider,
|
|
1382
|
+
hetzner: ed.config.hetzner ? {
|
|
1383
|
+
serverType: ed.config.hetzner.serverType,
|
|
1384
|
+
location: ed.config.hetzner.location,
|
|
1385
|
+
image: ed.config.hetzner.image
|
|
1386
|
+
} : void 0,
|
|
1387
|
+
digitalocean: ed.config.digitalocean ? {
|
|
1388
|
+
size: ed.config.digitalocean.size,
|
|
1389
|
+
region: ed.config.digitalocean.region,
|
|
1390
|
+
image: ed.config.digitalocean.image
|
|
1391
|
+
} : void 0,
|
|
1392
|
+
aiProvider: ed.config.openclawAgent?.aiProvider ?? "",
|
|
1393
|
+
model: ed.config.openclawAgent?.model ?? "",
|
|
1394
|
+
channel: ed.config.openclawAgent?.channel ?? "telegram"
|
|
1395
|
+
};
|
|
1396
|
+
setActiveTemplate(syntheticTemplate);
|
|
1397
|
+
context.setEditingDeployment(null);
|
|
1398
|
+
} else if (context.selectedTemplate) {
|
|
1295
1399
|
setActiveTemplate(context.selectedTemplate);
|
|
1296
1400
|
context.setSelectedTemplate(null);
|
|
1297
1401
|
}
|
|
@@ -1336,6 +1440,8 @@ function NewDeployment({ context }) {
|
|
|
1336
1440
|
step,
|
|
1337
1441
|
selectedDropletSizeIndex,
|
|
1338
1442
|
activeTemplate,
|
|
1443
|
+
editingConfig,
|
|
1444
|
+
editMode,
|
|
1339
1445
|
savedProviderKeys,
|
|
1340
1446
|
savedAiKeys,
|
|
1341
1447
|
savedTelegramTokens,
|
|
@@ -1345,7 +1451,9 @@ function NewDeployment({ context }) {
|
|
|
1345
1451
|
apiKeyFromSaved,
|
|
1346
1452
|
aiApiKeyFromSaved,
|
|
1347
1453
|
telegramTokenFromSaved,
|
|
1348
|
-
telegramAllowFromSaved
|
|
1454
|
+
telegramAllowFromSaved,
|
|
1455
|
+
enableTailscale,
|
|
1456
|
+
selectedTailscaleIndex
|
|
1349
1457
|
});
|
|
1350
1458
|
stateRef.current = {
|
|
1351
1459
|
name,
|
|
@@ -1359,6 +1467,8 @@ function NewDeployment({ context }) {
|
|
|
1359
1467
|
step,
|
|
1360
1468
|
selectedDropletSizeIndex,
|
|
1361
1469
|
activeTemplate,
|
|
1470
|
+
editingConfig,
|
|
1471
|
+
editMode,
|
|
1362
1472
|
savedProviderKeys,
|
|
1363
1473
|
savedAiKeys,
|
|
1364
1474
|
savedTelegramTokens,
|
|
@@ -1368,10 +1478,12 @@ function NewDeployment({ context }) {
|
|
|
1368
1478
|
apiKeyFromSaved,
|
|
1369
1479
|
aiApiKeyFromSaved,
|
|
1370
1480
|
telegramTokenFromSaved,
|
|
1371
|
-
telegramAllowFromSaved
|
|
1481
|
+
telegramAllowFromSaved,
|
|
1482
|
+
enableTailscale,
|
|
1483
|
+
selectedTailscaleIndex
|
|
1372
1484
|
};
|
|
1373
1485
|
debugLog(`RENDER: step=${step}, apiKey.length=${apiKey?.length ?? "null"}`);
|
|
1374
|
-
const stepList = getStepList(provider, activeTemplate);
|
|
1486
|
+
const stepList = getStepList(provider, activeTemplate, editMode ?? void 0);
|
|
1375
1487
|
const handleConfirmFromRef = () => {
|
|
1376
1488
|
const s = stateRef.current;
|
|
1377
1489
|
debugLog(`handleConfirmFromRef CALLED: apiKey.length=${s.apiKey?.length ?? "null"}`);
|
|
@@ -1410,7 +1522,7 @@ function NewDeployment({ context }) {
|
|
|
1410
1522
|
const config = {
|
|
1411
1523
|
name: s.name,
|
|
1412
1524
|
provider: s.provider,
|
|
1413
|
-
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1525
|
+
createdAt: s.editingConfig?.createdAt ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
1414
1526
|
hetzner: s.provider === "hetzner" ? {
|
|
1415
1527
|
apiKey: s.apiKey,
|
|
1416
1528
|
serverType: tmpl?.hetzner?.serverType ?? "cpx11",
|
|
@@ -1424,6 +1536,7 @@ function NewDeployment({ context }) {
|
|
|
1424
1536
|
image: tmpl?.digitalocean?.image ?? "ubuntu-24-04-x64"
|
|
1425
1537
|
} : void 0,
|
|
1426
1538
|
openclawConfig: void 0,
|
|
1539
|
+
skipTailscale: !s.enableTailscale,
|
|
1427
1540
|
openclawAgent: {
|
|
1428
1541
|
aiProvider: s.aiProvider,
|
|
1429
1542
|
aiApiKey: s.aiApiKey,
|
|
@@ -1433,11 +1546,16 @@ function NewDeployment({ context }) {
|
|
|
1433
1546
|
telegramAllowFrom: s.telegramAllowFrom
|
|
1434
1547
|
}
|
|
1435
1548
|
};
|
|
1436
|
-
|
|
1549
|
+
if (s.editingConfig && s.editMode === "edit") {
|
|
1550
|
+
const updatedConfig = { ...config, name: s.editingConfig.name, createdAt: s.editingConfig.createdAt };
|
|
1551
|
+
updateDeploymentConfig(s.editingConfig.name, updatedConfig);
|
|
1552
|
+
} else {
|
|
1553
|
+
createDeployment(config);
|
|
1554
|
+
}
|
|
1437
1555
|
context.refreshDeployments();
|
|
1438
1556
|
setStep("complete");
|
|
1439
1557
|
} catch (err) {
|
|
1440
|
-
setError(`Failed to create deployment: ${err instanceof Error ? err.message : String(err)}`);
|
|
1558
|
+
setError(`Failed to ${s.editMode === "edit" ? "update" : "create"} deployment: ${err instanceof Error ? err.message : String(err)}`);
|
|
1441
1559
|
}
|
|
1442
1560
|
};
|
|
1443
1561
|
useKeyboard((key) => {
|
|
@@ -1629,7 +1747,7 @@ function NewDeployment({ context }) {
|
|
|
1629
1747
|
setTelegramAllowFrom(saved.value);
|
|
1630
1748
|
setTelegramAllowFromSaved(true);
|
|
1631
1749
|
setError(null);
|
|
1632
|
-
setStep("confirm");
|
|
1750
|
+
setStep(currentState.editMode === "edit" ? "confirm" : "tailscale");
|
|
1633
1751
|
}
|
|
1634
1752
|
} else if (key.name === "escape") {
|
|
1635
1753
|
setStep("telegram_token_choose");
|
|
@@ -1666,13 +1784,26 @@ function NewDeployment({ context }) {
|
|
|
1666
1784
|
setStep("telegram_allow_name");
|
|
1667
1785
|
setNewKeyName("");
|
|
1668
1786
|
} else if (key.name === "n" || key.name === "escape") {
|
|
1787
|
+
setStep(currentState.editMode === "edit" ? "confirm" : "tailscale");
|
|
1788
|
+
}
|
|
1789
|
+
} else if (currentState.step === "tailscale") {
|
|
1790
|
+
const TAILSCALE_OPTIONS = ["Skip", "Configure Tailscale"];
|
|
1791
|
+
if (key.name === "up") {
|
|
1792
|
+
setSelectedTailscaleIndex((prev) => Math.max(0, prev - 1));
|
|
1793
|
+
} else if (key.name === "down") {
|
|
1794
|
+
setSelectedTailscaleIndex((prev) => Math.min(TAILSCALE_OPTIONS.length - 1, prev + 1));
|
|
1795
|
+
} else if (key.name === "return") {
|
|
1796
|
+
setEnableTailscale(currentState.selectedTailscaleIndex === 1);
|
|
1797
|
+
setError(null);
|
|
1669
1798
|
setStep("confirm");
|
|
1799
|
+
} else if (key.name === "escape") {
|
|
1800
|
+
setStep("telegram_allow_choose");
|
|
1670
1801
|
}
|
|
1671
1802
|
} else if (currentState.step === "confirm") {
|
|
1672
1803
|
if (key.name === "y" || key.name === "return") {
|
|
1673
1804
|
handleConfirmFromRef();
|
|
1674
1805
|
} else if (key.name === "n" || key.name === "escape") {
|
|
1675
|
-
setStep("telegram_allow_choose");
|
|
1806
|
+
setStep(currentState.editMode === "edit" ? "telegram_allow_choose" : "tailscale");
|
|
1676
1807
|
}
|
|
1677
1808
|
} else if (currentState.step === "complete") {
|
|
1678
1809
|
context.navigateTo("home");
|
|
@@ -1794,7 +1925,7 @@ function NewDeployment({ context }) {
|
|
|
1794
1925
|
}
|
|
1795
1926
|
setError(null);
|
|
1796
1927
|
if (telegramAllowFromSaved) {
|
|
1797
|
-
setStep("confirm");
|
|
1928
|
+
setStep(editMode === "edit" ? "confirm" : "tailscale");
|
|
1798
1929
|
} else {
|
|
1799
1930
|
setStep("telegram_allow_save");
|
|
1800
1931
|
}
|
|
@@ -2508,10 +2639,10 @@ function NewDeployment({ context }) {
|
|
|
2508
2639
|
setNewKeyName(value);
|
|
2509
2640
|
}
|
|
2510
2641
|
},
|
|
2511
|
-
onSubmit: () => handleSaveKeyName("telegram-user", telegramAllowFrom, "confirm"),
|
|
2642
|
+
onSubmit: () => handleSaveKeyName("telegram-user", telegramAllowFrom, editMode === "edit" ? "confirm" : "tailscale"),
|
|
2512
2643
|
onKeyDown: (e) => {
|
|
2513
2644
|
if (e.name === "escape") {
|
|
2514
|
-
setStep("confirm");
|
|
2645
|
+
setStep(editMode === "edit" ? "confirm" : "tailscale");
|
|
2515
2646
|
}
|
|
2516
2647
|
}
|
|
2517
2648
|
}
|
|
@@ -2519,6 +2650,41 @@ function NewDeployment({ context }) {
|
|
|
2519
2650
|
error && /* @__PURE__ */ jsx2("text", { fg: t.status.error, marginTop: 1, children: error }),
|
|
2520
2651
|
/* @__PURE__ */ jsx2("text", { fg: t.fg.muted, marginTop: 2, children: "Press Enter to save, Esc to skip" })
|
|
2521
2652
|
] });
|
|
2653
|
+
case "tailscale": {
|
|
2654
|
+
const tailscaleOptions = [
|
|
2655
|
+
{ label: "Skip", description: "Use SSH tunnel via /dashboard instead" },
|
|
2656
|
+
{ label: "Configure Tailscale", description: "Private VPN between your devices and the server" }
|
|
2657
|
+
];
|
|
2658
|
+
return /* @__PURE__ */ jsxs2("box", { flexDirection: "column", children: [
|
|
2659
|
+
/* @__PURE__ */ jsxs2("text", { fg: t.accent, children: [
|
|
2660
|
+
"Step ",
|
|
2661
|
+
currentStepNumber("tailscale"),
|
|
2662
|
+
": Tailscale Setup (Optional)"
|
|
2663
|
+
] }),
|
|
2664
|
+
/* @__PURE__ */ jsx2("text", { fg: t.fg.secondary, marginTop: 1, children: "Tailscale creates a private VPN between your devices and your OpenClaw server." }),
|
|
2665
|
+
/* @__PURE__ */ jsx2("text", { fg: t.fg.secondary, children: "You can skip this and use an SSH tunnel instead (the /dashboard command sets one up automatically)." }),
|
|
2666
|
+
/* @__PURE__ */ jsx2(
|
|
2667
|
+
"box",
|
|
2668
|
+
{
|
|
2669
|
+
flexDirection: "column",
|
|
2670
|
+
borderStyle: "single",
|
|
2671
|
+
borderColor: t.border.default,
|
|
2672
|
+
marginTop: 1,
|
|
2673
|
+
padding: 1,
|
|
2674
|
+
children: tailscaleOptions.map((opt, i) => {
|
|
2675
|
+
const isSelected = i === selectedTailscaleIndex;
|
|
2676
|
+
return /* @__PURE__ */ jsxs2("box", { flexDirection: "row", backgroundColor: isSelected ? t.selection.bg : void 0, children: [
|
|
2677
|
+
/* @__PURE__ */ jsx2("text", { fg: isSelected ? t.selection.indicator : t.fg.primary, children: isSelected ? "> " : " " }),
|
|
2678
|
+
/* @__PURE__ */ jsx2("text", { fg: isSelected ? t.selection.fg : t.fg.primary, children: opt.label }),
|
|
2679
|
+
/* @__PURE__ */ jsx2("text", { fg: isSelected ? t.fg.primary : t.fg.secondary, children: " - " + opt.description })
|
|
2680
|
+
] }, opt.label);
|
|
2681
|
+
})
|
|
2682
|
+
}
|
|
2683
|
+
),
|
|
2684
|
+
error && /* @__PURE__ */ jsx2("text", { fg: t.status.error, marginTop: 1, children: error }),
|
|
2685
|
+
/* @__PURE__ */ jsx2("text", { fg: t.fg.muted, marginTop: 1, children: "Press Enter to select, Esc to go back" })
|
|
2686
|
+
] });
|
|
2687
|
+
}
|
|
2522
2688
|
case "confirm":
|
|
2523
2689
|
return /* @__PURE__ */ jsxs2("box", { flexDirection: "column", children: [
|
|
2524
2690
|
/* @__PURE__ */ jsxs2("text", { fg: t.accent, children: [
|
|
@@ -2578,6 +2744,10 @@ function NewDeployment({ context }) {
|
|
|
2578
2744
|
/* @__PURE__ */ jsxs2("box", { flexDirection: "row", children: [
|
|
2579
2745
|
/* @__PURE__ */ jsx2("text", { fg: t.fg.secondary, width: 20, children: "Allow From:" }),
|
|
2580
2746
|
/* @__PURE__ */ jsx2("text", { fg: t.fg.primary, children: telegramAllowFrom || "N/A" })
|
|
2747
|
+
] }),
|
|
2748
|
+
/* @__PURE__ */ jsxs2("box", { flexDirection: "row", children: [
|
|
2749
|
+
/* @__PURE__ */ jsx2("text", { fg: t.fg.secondary, width: 20, children: "Tailscale:" }),
|
|
2750
|
+
/* @__PURE__ */ jsx2("text", { fg: enableTailscale ? t.status.success : t.fg.muted, children: enableTailscale ? "Enabled" : "Skipped" })
|
|
2581
2751
|
] })
|
|
2582
2752
|
]
|
|
2583
2753
|
}
|
|
@@ -2587,7 +2757,7 @@ function NewDeployment({ context }) {
|
|
|
2587
2757
|
] });
|
|
2588
2758
|
case "complete":
|
|
2589
2759
|
return /* @__PURE__ */ jsxs2("box", { flexDirection: "column", children: [
|
|
2590
|
-
/* @__PURE__ */ jsx2("text", { fg: t.status.success, children: "Deployment Configuration Created!" }),
|
|
2760
|
+
/* @__PURE__ */ jsx2("text", { fg: t.status.success, children: editMode === "edit" ? "Deployment Updated Successfully!" : "Deployment Configuration Created!" }),
|
|
2591
2761
|
/* @__PURE__ */ jsxs2(
|
|
2592
2762
|
"box",
|
|
2593
2763
|
{
|
|
@@ -2597,14 +2767,10 @@ function NewDeployment({ context }) {
|
|
|
2597
2767
|
padding: 1,
|
|
2598
2768
|
marginTop: 1,
|
|
2599
2769
|
children: [
|
|
2600
|
-
/* @__PURE__ */
|
|
2601
|
-
'Your deployment "',
|
|
2602
|
-
name,
|
|
2603
|
-
'" has been initialized.'
|
|
2604
|
-
] }),
|
|
2770
|
+
/* @__PURE__ */ jsx2("text", { fg: t.fg.primary, children: editMode === "edit" ? `Your deployment "${editingConfig?.name ?? name}" has been updated.` : `Your deployment "${name}" has been initialized.` }),
|
|
2605
2771
|
/* @__PURE__ */ jsxs2("text", { fg: t.fg.secondary, marginTop: 1, children: [
|
|
2606
2772
|
"Configuration saved to: ~/.clawcontrol/deployments/",
|
|
2607
|
-
name,
|
|
2773
|
+
editingConfig?.name ?? name,
|
|
2608
2774
|
"/"
|
|
2609
2775
|
] }),
|
|
2610
2776
|
/* @__PURE__ */ jsxs2("text", { fg: t.fg.secondary, marginTop: 1, children: [
|
|
@@ -2621,15 +2787,15 @@ function NewDeployment({ context }) {
|
|
|
2621
2787
|
]
|
|
2622
2788
|
}
|
|
2623
2789
|
),
|
|
2624
|
-
/* @__PURE__ */ jsx2("text", { fg: t.accent, marginTop: 2, children: "Next step: Run /deploy to deploy this configuration" }),
|
|
2790
|
+
editMode !== "edit" && /* @__PURE__ */ jsx2("text", { fg: t.accent, marginTop: 2, children: "Next step: Run /deploy to deploy this configuration" }),
|
|
2625
2791
|
/* @__PURE__ */ jsx2("text", { fg: t.fg.muted, marginTop: 2, children: "Press any key to return to home" })
|
|
2626
2792
|
] });
|
|
2627
2793
|
}
|
|
2628
2794
|
};
|
|
2629
2795
|
return /* @__PURE__ */ jsxs2("box", { flexDirection: "column", width: "100%", padding: 1, children: [
|
|
2630
2796
|
/* @__PURE__ */ jsxs2("box", { flexDirection: "row", marginBottom: 2, children: [
|
|
2631
|
-
/* @__PURE__ */ jsx2("text", { fg: t.accent, children: "/new" }),
|
|
2632
|
-
/* @__PURE__ */ jsx2("text", { fg: t.fg.secondary, children: " - Initialize a new deployment" })
|
|
2797
|
+
/* @__PURE__ */ jsx2("text", { fg: t.accent, children: editMode === "edit" ? "/list" : "/new" }),
|
|
2798
|
+
/* @__PURE__ */ jsx2("text", { fg: t.fg.secondary, children: editMode === "edit" ? ` - Edit Deployment: ${editingConfig?.name ?? ""}` : editMode === "fork" ? " - Fork Deployment" : " - Initialize a new deployment" })
|
|
2633
2799
|
] }),
|
|
2634
2800
|
/* @__PURE__ */ jsx2("box", { flexDirection: "row", marginBottom: 2, children: stepList.map((s, i) => {
|
|
2635
2801
|
const resolvedStep = resolveStepForProgress(step);
|
|
@@ -2644,46 +2810,81 @@ function NewDeployment({ context }) {
|
|
|
2644
2810
|
] });
|
|
2645
2811
|
}
|
|
2646
2812
|
|
|
2647
|
-
// src/components/
|
|
2648
|
-
import { useState as useState3 } from "react";
|
|
2813
|
+
// src/components/ListView.tsx
|
|
2814
|
+
import { useState as useState3, useRef as useRef2 } from "react";
|
|
2649
2815
|
import { useKeyboard as useKeyboard2 } from "@opentui/react";
|
|
2650
|
-
import { jsx as jsx3, jsxs as jsxs3 } from "@opentui/react/jsx-runtime";
|
|
2651
|
-
function
|
|
2816
|
+
import { Fragment, jsx as jsx3, jsxs as jsxs3 } from "@opentui/react/jsx-runtime";
|
|
2817
|
+
function ListView({ context }) {
|
|
2818
|
+
const [viewState, setViewState] = useState3("listing");
|
|
2652
2819
|
const [selectedIndex, setSelectedIndex] = useState3(0);
|
|
2653
|
-
const [
|
|
2820
|
+
const [confirmText, setConfirmText] = useState3("");
|
|
2821
|
+
const [error, setError] = useState3(null);
|
|
2822
|
+
const [deletedName, setDeletedName] = useState3("");
|
|
2654
2823
|
const deployments = context.deployments;
|
|
2655
|
-
const
|
|
2656
|
-
const
|
|
2657
|
-
|
|
2658
|
-
const selectedDeployment = allDeployments[selectedIndex];
|
|
2824
|
+
const selectedDeployment = deployments[selectedIndex];
|
|
2825
|
+
const stateRef = useRef2({ viewState, selectedIndex });
|
|
2826
|
+
stateRef.current = { viewState, selectedIndex };
|
|
2659
2827
|
useKeyboard2((key) => {
|
|
2660
|
-
|
|
2828
|
+
const current = stateRef.current;
|
|
2829
|
+
if (deployments.length === 0) {
|
|
2661
2830
|
context.navigateTo("home");
|
|
2662
2831
|
return;
|
|
2663
2832
|
}
|
|
2664
|
-
if (
|
|
2665
|
-
if (key.name === "
|
|
2666
|
-
|
|
2667
|
-
} else if (key.name === "
|
|
2668
|
-
|
|
2669
|
-
}
|
|
2670
|
-
} else {
|
|
2671
|
-
if (key.name === "up" && selectedIndex > 0) {
|
|
2672
|
-
setSelectedIndex(selectedIndex - 1);
|
|
2673
|
-
} else if (key.name === "down" && selectedIndex < allDeployments.length - 1) {
|
|
2674
|
-
setSelectedIndex(selectedIndex + 1);
|
|
2833
|
+
if (current.viewState === "listing") {
|
|
2834
|
+
if (key.name === "up" && current.selectedIndex > 0) {
|
|
2835
|
+
setSelectedIndex(current.selectedIndex - 1);
|
|
2836
|
+
} else if (key.name === "down" && current.selectedIndex < deployments.length - 1) {
|
|
2837
|
+
setSelectedIndex(current.selectedIndex + 1);
|
|
2675
2838
|
} else if (key.name === "return") {
|
|
2676
|
-
|
|
2839
|
+
setViewState("detail");
|
|
2677
2840
|
} else if (key.name === "escape") {
|
|
2678
2841
|
context.navigateTo("home");
|
|
2679
2842
|
}
|
|
2843
|
+
} else if (current.viewState === "detail") {
|
|
2844
|
+
const dep = deployments[current.selectedIndex];
|
|
2845
|
+
if (!dep) return;
|
|
2846
|
+
if (key.name === "e" && dep.state.status === "initialized") {
|
|
2847
|
+
context.setEditingDeployment({ config: dep.config, mode: "edit" });
|
|
2848
|
+
context.navigateTo("new");
|
|
2849
|
+
} else if (key.name === "f") {
|
|
2850
|
+
context.setEditingDeployment({ config: dep.config, mode: "fork" });
|
|
2851
|
+
context.navigateTo("new");
|
|
2852
|
+
} else if (key.name === "d") {
|
|
2853
|
+
if (dep.state.status === "initialized") {
|
|
2854
|
+
setConfirmText("");
|
|
2855
|
+
setError(null);
|
|
2856
|
+
setViewState("delete_confirm");
|
|
2857
|
+
} else {
|
|
2858
|
+
setError("Deployed agents must be destroyed first using the /destroy command.");
|
|
2859
|
+
}
|
|
2860
|
+
} else if (key.name === "escape") {
|
|
2861
|
+
setError(null);
|
|
2862
|
+
setViewState("listing");
|
|
2863
|
+
}
|
|
2864
|
+
} else if (current.viewState === "success") {
|
|
2865
|
+
context.navigateTo("home");
|
|
2866
|
+
} else if (current.viewState === "error") {
|
|
2867
|
+
setError(null);
|
|
2868
|
+
setViewState("listing");
|
|
2680
2869
|
}
|
|
2681
2870
|
});
|
|
2682
|
-
|
|
2871
|
+
const handleDelete = (name) => {
|
|
2872
|
+
setViewState("deleting");
|
|
2873
|
+
try {
|
|
2874
|
+
deleteDeployment(name);
|
|
2875
|
+
setDeletedName(name);
|
|
2876
|
+
context.refreshDeployments();
|
|
2877
|
+
setViewState("success");
|
|
2878
|
+
} catch (err) {
|
|
2879
|
+
setError(err instanceof Error ? err.message : String(err));
|
|
2880
|
+
setViewState("error");
|
|
2881
|
+
}
|
|
2882
|
+
};
|
|
2883
|
+
if (deployments.length === 0) {
|
|
2683
2884
|
return /* @__PURE__ */ jsxs3("box", { flexDirection: "column", width: "100%", padding: 1, children: [
|
|
2684
2885
|
/* @__PURE__ */ jsxs3("box", { flexDirection: "row", marginBottom: 2, children: [
|
|
2685
|
-
/* @__PURE__ */ jsx3("text", { fg: t.accent, children: "/
|
|
2686
|
-
/* @__PURE__ */ jsx3("text", { fg: t.fg.secondary, children: " -
|
|
2886
|
+
/* @__PURE__ */ jsx3("text", { fg: t.accent, children: "/list" }),
|
|
2887
|
+
/* @__PURE__ */ jsx3("text", { fg: t.fg.secondary, children: " - Deployments" })
|
|
2687
2888
|
] }),
|
|
2688
2889
|
/* @__PURE__ */ jsxs3(
|
|
2689
2890
|
"box",
|
|
@@ -2694,157 +2895,432 @@ function DeployView({ context }) {
|
|
|
2694
2895
|
padding: 1,
|
|
2695
2896
|
children: [
|
|
2696
2897
|
/* @__PURE__ */ jsx3("text", { fg: t.status.warning, children: "No deployments found!" }),
|
|
2697
|
-
/* @__PURE__ */ jsx3("text", { fg: t.fg.secondary, marginTop: 1, children: "Run /new
|
|
2898
|
+
/* @__PURE__ */ jsx3("text", { fg: t.fg.secondary, marginTop: 1, children: "Run /new to create a deployment." })
|
|
2698
2899
|
]
|
|
2699
2900
|
}
|
|
2700
2901
|
),
|
|
2701
2902
|
/* @__PURE__ */ jsx3("text", { fg: t.fg.muted, marginTop: 2, children: "Press any key to return to home" })
|
|
2702
2903
|
] });
|
|
2703
2904
|
}
|
|
2704
|
-
if (
|
|
2905
|
+
if (viewState === "listing") {
|
|
2705
2906
|
return /* @__PURE__ */ jsxs3("box", { flexDirection: "column", width: "100%", padding: 1, children: [
|
|
2706
2907
|
/* @__PURE__ */ jsxs3("box", { flexDirection: "row", marginBottom: 2, children: [
|
|
2707
|
-
/* @__PURE__ */ jsx3("text", { fg: t.accent, children: "/
|
|
2708
|
-
/* @__PURE__ */
|
|
2908
|
+
/* @__PURE__ */ jsx3("text", { fg: t.accent, children: "/list" }),
|
|
2909
|
+
/* @__PURE__ */ jsxs3("text", { fg: t.fg.secondary, children: [
|
|
2910
|
+
" - Deployments (",
|
|
2911
|
+
deployments.length,
|
|
2912
|
+
")"
|
|
2913
|
+
] })
|
|
2709
2914
|
] }),
|
|
2710
|
-
/* @__PURE__ */
|
|
2915
|
+
/* @__PURE__ */ jsx3(
|
|
2711
2916
|
"box",
|
|
2712
2917
|
{
|
|
2713
2918
|
flexDirection: "column",
|
|
2714
2919
|
borderStyle: "single",
|
|
2715
2920
|
borderColor: t.border.default,
|
|
2716
2921
|
padding: 1,
|
|
2922
|
+
children: deployments.map((dep, index) => {
|
|
2923
|
+
const isSelected = index === selectedIndex;
|
|
2924
|
+
return /* @__PURE__ */ jsxs3(
|
|
2925
|
+
"box",
|
|
2926
|
+
{
|
|
2927
|
+
flexDirection: "row",
|
|
2928
|
+
backgroundColor: isSelected ? t.selection.bg : void 0,
|
|
2929
|
+
children: [
|
|
2930
|
+
/* @__PURE__ */ jsx3("text", { fg: isSelected ? t.selection.indicator : t.fg.primary, children: isSelected ? "> " : " " }),
|
|
2931
|
+
/* @__PURE__ */ jsx3("text", { fg: isSelected ? t.selection.fg : t.fg.primary, width: 25, children: dep.config.name }),
|
|
2932
|
+
/* @__PURE__ */ jsxs3("text", { fg: statusColor(dep.state.status), width: 16, children: [
|
|
2933
|
+
"[",
|
|
2934
|
+
dep.state.status,
|
|
2935
|
+
"]"
|
|
2936
|
+
] }),
|
|
2937
|
+
/* @__PURE__ */ jsx3("text", { fg: isSelected ? t.fg.secondary : t.fg.muted, children: PROVIDER_NAMES[dep.config.provider] })
|
|
2938
|
+
]
|
|
2939
|
+
},
|
|
2940
|
+
dep.config.name
|
|
2941
|
+
);
|
|
2942
|
+
})
|
|
2943
|
+
}
|
|
2944
|
+
),
|
|
2945
|
+
/* @__PURE__ */ jsx3("text", { fg: t.fg.muted, marginTop: 2, children: "Up/Down: Select | Enter: Details | Esc: Back" })
|
|
2946
|
+
] });
|
|
2947
|
+
}
|
|
2948
|
+
if (viewState === "detail" && selectedDeployment) {
|
|
2949
|
+
const dep = selectedDeployment;
|
|
2950
|
+
const isInitialized = dep.state.status === "initialized";
|
|
2951
|
+
return /* @__PURE__ */ jsxs3("box", { flexDirection: "column", width: "100%", padding: 1, children: [
|
|
2952
|
+
/* @__PURE__ */ jsxs3("box", { flexDirection: "row", marginBottom: 2, children: [
|
|
2953
|
+
/* @__PURE__ */ jsx3("text", { fg: t.accent, children: "/list" }),
|
|
2954
|
+
/* @__PURE__ */ jsxs3("text", { fg: t.fg.secondary, children: [
|
|
2955
|
+
" - ",
|
|
2956
|
+
dep.config.name
|
|
2957
|
+
] })
|
|
2958
|
+
] }),
|
|
2959
|
+
/* @__PURE__ */ jsxs3(
|
|
2960
|
+
"box",
|
|
2961
|
+
{
|
|
2962
|
+
flexDirection: "column",
|
|
2963
|
+
borderStyle: "single",
|
|
2964
|
+
borderColor: t.border.focus,
|
|
2965
|
+
padding: 1,
|
|
2966
|
+
marginBottom: 1,
|
|
2717
2967
|
children: [
|
|
2718
|
-
/* @__PURE__ */
|
|
2719
|
-
|
|
2720
|
-
|
|
2721
|
-
|
|
2968
|
+
/* @__PURE__ */ jsx3("text", { fg: t.accent, marginBottom: 1, children: "Deployment Details" }),
|
|
2969
|
+
/* @__PURE__ */ jsxs3("box", { flexDirection: "row", children: [
|
|
2970
|
+
/* @__PURE__ */ jsx3("text", { fg: t.fg.secondary, width: 20, children: "Name:" }),
|
|
2971
|
+
/* @__PURE__ */ jsx3("text", { fg: t.fg.primary, children: dep.config.name })
|
|
2722
2972
|
] }),
|
|
2723
|
-
/* @__PURE__ */
|
|
2724
|
-
|
|
2725
|
-
"
|
|
2726
|
-
|
|
2973
|
+
/* @__PURE__ */ jsxs3("box", { flexDirection: "row", children: [
|
|
2974
|
+
/* @__PURE__ */ jsx3("text", { fg: t.fg.secondary, width: 20, children: "Status:" }),
|
|
2975
|
+
/* @__PURE__ */ jsx3("text", { fg: statusColor(dep.state.status), children: dep.state.status })
|
|
2976
|
+
] }),
|
|
2977
|
+
/* @__PURE__ */ jsxs3("box", { flexDirection: "row", children: [
|
|
2978
|
+
/* @__PURE__ */ jsx3("text", { fg: t.fg.secondary, width: 20, children: "Provider:" }),
|
|
2979
|
+
/* @__PURE__ */ jsx3("text", { fg: t.fg.primary, children: PROVIDER_NAMES[dep.config.provider] })
|
|
2980
|
+
] }),
|
|
2981
|
+
/* @__PURE__ */ jsxs3("box", { flexDirection: "row", children: [
|
|
2982
|
+
/* @__PURE__ */ jsx3("text", { fg: t.fg.secondary, width: 20, children: "Created:" }),
|
|
2983
|
+
/* @__PURE__ */ jsx3("text", { fg: t.fg.primary, children: new Date(dep.config.createdAt).toLocaleString() })
|
|
2984
|
+
] }),
|
|
2985
|
+
dep.state.serverIp && /* @__PURE__ */ jsxs3("box", { flexDirection: "row", children: [
|
|
2986
|
+
/* @__PURE__ */ jsx3("text", { fg: t.fg.secondary, width: 20, children: "Server IP:" }),
|
|
2987
|
+
/* @__PURE__ */ jsx3("text", { fg: t.accent, children: dep.state.serverIp })
|
|
2988
|
+
] }),
|
|
2989
|
+
dep.config.provider === "hetzner" && dep.config.hetzner && /* @__PURE__ */ jsxs3("box", { flexDirection: "row", children: [
|
|
2990
|
+
/* @__PURE__ */ jsx3("text", { fg: t.fg.secondary, width: 20, children: "Server Type:" }),
|
|
2991
|
+
/* @__PURE__ */ jsx3("text", { fg: t.fg.primary, children: dep.config.hetzner.serverType })
|
|
2727
2992
|
] }),
|
|
2728
|
-
/* @__PURE__ */
|
|
2729
|
-
|
|
2730
|
-
|
|
2993
|
+
dep.config.provider === "digitalocean" && dep.config.digitalocean && /* @__PURE__ */ jsxs3("box", { flexDirection: "row", children: [
|
|
2994
|
+
/* @__PURE__ */ jsx3("text", { fg: t.fg.secondary, width: 20, children: "Droplet Size:" }),
|
|
2995
|
+
/* @__PURE__ */ jsx3("text", { fg: t.fg.primary, children: dep.config.digitalocean.size })
|
|
2996
|
+
] }),
|
|
2997
|
+
dep.config.openclawAgent && /* @__PURE__ */ jsxs3(Fragment, { children: [
|
|
2998
|
+
/* @__PURE__ */ jsxs3("box", { flexDirection: "row", children: [
|
|
2999
|
+
/* @__PURE__ */ jsx3("text", { fg: t.fg.secondary, width: 20, children: "AI Provider:" }),
|
|
3000
|
+
/* @__PURE__ */ jsx3("text", { fg: t.fg.primary, children: dep.config.openclawAgent.aiProvider })
|
|
3001
|
+
] }),
|
|
3002
|
+
/* @__PURE__ */ jsxs3("box", { flexDirection: "row", children: [
|
|
3003
|
+
/* @__PURE__ */ jsx3("text", { fg: t.fg.secondary, width: 20, children: "Model:" }),
|
|
3004
|
+
/* @__PURE__ */ jsx3("text", { fg: t.fg.primary, children: dep.config.openclawAgent.model })
|
|
3005
|
+
] }),
|
|
3006
|
+
/* @__PURE__ */ jsxs3("box", { flexDirection: "row", children: [
|
|
3007
|
+
/* @__PURE__ */ jsx3("text", { fg: t.fg.secondary, width: 20, children: "Channel:" }),
|
|
3008
|
+
/* @__PURE__ */ jsx3("text", { fg: t.fg.primary, children: dep.config.openclawAgent.channel })
|
|
3009
|
+
] })
|
|
3010
|
+
] })
|
|
2731
3011
|
]
|
|
2732
3012
|
}
|
|
2733
3013
|
),
|
|
2734
|
-
/* @__PURE__ */
|
|
3014
|
+
/* @__PURE__ */ jsxs3("box", { flexDirection: "row", marginBottom: 1, children: [
|
|
3015
|
+
isInitialized && /* @__PURE__ */ jsx3("text", { fg: t.accent, marginRight: 2, children: "[E]dit" }),
|
|
3016
|
+
/* @__PURE__ */ jsx3("text", { fg: t.accent, marginRight: 2, children: "[F]ork" }),
|
|
3017
|
+
isInitialized ? /* @__PURE__ */ jsx3("text", { fg: t.status.error, children: "[D]elete" }) : /* @__PURE__ */ jsx3("text", { fg: t.fg.muted, children: "[D]elete (use /destroy)" })
|
|
3018
|
+
] }),
|
|
3019
|
+
error && /* @__PURE__ */ jsx3("text", { fg: t.status.error, marginTop: 1, children: error }),
|
|
3020
|
+
/* @__PURE__ */ jsx3("text", { fg: t.fg.muted, marginTop: 1, children: "Esc: Back to list" })
|
|
2735
3021
|
] });
|
|
2736
3022
|
}
|
|
2737
|
-
|
|
2738
|
-
/* @__PURE__ */ jsxs3("box", { flexDirection: "
|
|
2739
|
-
/* @__PURE__ */ jsx3("text", { fg: t.
|
|
2740
|
-
/* @__PURE__ */
|
|
2741
|
-
|
|
2742
|
-
|
|
2743
|
-
|
|
2744
|
-
|
|
2745
|
-
|
|
2746
|
-
|
|
2747
|
-
|
|
2748
|
-
|
|
2749
|
-
|
|
2750
|
-
|
|
2751
|
-
|
|
2752
|
-
|
|
2753
|
-
"
|
|
2754
|
-
{
|
|
2755
|
-
|
|
2756
|
-
|
|
2757
|
-
|
|
2758
|
-
|
|
2759
|
-
|
|
2760
|
-
|
|
2761
|
-
|
|
2762
|
-
|
|
2763
|
-
|
|
2764
|
-
|
|
2765
|
-
|
|
2766
|
-
|
|
2767
|
-
|
|
2768
|
-
|
|
2769
|
-
|
|
2770
|
-
|
|
2771
|
-
|
|
2772
|
-
|
|
2773
|
-
|
|
2774
|
-
|
|
2775
|
-
|
|
2776
|
-
|
|
2777
|
-
|
|
2778
|
-
|
|
2779
|
-
|
|
2780
|
-
|
|
2781
|
-
|
|
2782
|
-
|
|
2783
|
-
|
|
2784
|
-
|
|
2785
|
-
|
|
2786
|
-
|
|
2787
|
-
|
|
2788
|
-
|
|
2789
|
-
|
|
2790
|
-
|
|
2791
|
-
|
|
2792
|
-
}
|
|
2793
|
-
|
|
2794
|
-
|
|
2795
|
-
|
|
2796
|
-
|
|
2797
|
-
|
|
2798
|
-
|
|
2799
|
-
|
|
2800
|
-
|
|
2801
|
-
|
|
2802
|
-
|
|
2803
|
-
|
|
2804
|
-
|
|
2805
|
-
|
|
2806
|
-
|
|
2807
|
-
|
|
2808
|
-
|
|
2809
|
-
|
|
2810
|
-
|
|
2811
|
-
|
|
2812
|
-
|
|
2813
|
-
|
|
2814
|
-
|
|
2815
|
-
|
|
2816
|
-
|
|
2817
|
-
|
|
2818
|
-
|
|
2819
|
-
|
|
2820
|
-
|
|
2821
|
-
|
|
2822
|
-
|
|
2823
|
-
|
|
2824
|
-
|
|
2825
|
-
|
|
2826
|
-
|
|
2827
|
-
|
|
2828
|
-
|
|
2829
|
-
|
|
2830
|
-
|
|
2831
|
-
|
|
2832
|
-
|
|
2833
|
-
|
|
2834
|
-
|
|
2835
|
-
|
|
2836
|
-
|
|
2837
|
-
|
|
2838
|
-
|
|
2839
|
-
|
|
2840
|
-
|
|
2841
|
-
|
|
2842
|
-
|
|
2843
|
-
|
|
2844
|
-
|
|
2845
|
-
|
|
2846
|
-
|
|
2847
|
-
|
|
3023
|
+
if (viewState === "delete_confirm" && selectedDeployment) {
|
|
3024
|
+
return /* @__PURE__ */ jsxs3("box", { flexDirection: "column", width: "100%", padding: 1, children: [
|
|
3025
|
+
/* @__PURE__ */ jsx3("box", { flexDirection: "row", marginBottom: 2, children: /* @__PURE__ */ jsx3("text", { fg: t.status.error, children: "Confirm Deletion" }) }),
|
|
3026
|
+
/* @__PURE__ */ jsxs3(
|
|
3027
|
+
"box",
|
|
3028
|
+
{
|
|
3029
|
+
flexDirection: "column",
|
|
3030
|
+
borderStyle: "double",
|
|
3031
|
+
borderColor: t.status.error,
|
|
3032
|
+
padding: 1,
|
|
3033
|
+
children: [
|
|
3034
|
+
/* @__PURE__ */ jsx3("text", { fg: t.status.error, children: "You are about to delete:" }),
|
|
3035
|
+
/* @__PURE__ */ jsxs3("text", { fg: t.fg.primary, marginTop: 1, children: [
|
|
3036
|
+
"Deployment: ",
|
|
3037
|
+
selectedDeployment.config.name
|
|
3038
|
+
] }),
|
|
3039
|
+
/* @__PURE__ */ jsx3("text", { fg: t.status.error, marginTop: 1, children: "This will permanently delete:" }),
|
|
3040
|
+
/* @__PURE__ */ jsx3("text", { fg: t.fg.secondary, children: " Local configuration files" }),
|
|
3041
|
+
/* @__PURE__ */ jsx3("text", { fg: t.fg.secondary, children: " SSH keys" })
|
|
3042
|
+
]
|
|
3043
|
+
}
|
|
3044
|
+
),
|
|
3045
|
+
/* @__PURE__ */ jsx3("text", { fg: t.status.warning, marginTop: 2, children: "Type the deployment name to confirm:" }),
|
|
3046
|
+
/* @__PURE__ */ jsx3("text", { fg: t.fg.primary, marginTop: 1, children: "Confirm:" }),
|
|
3047
|
+
/* @__PURE__ */ jsx3(
|
|
3048
|
+
"input",
|
|
3049
|
+
{
|
|
3050
|
+
value: confirmText,
|
|
3051
|
+
placeholder: selectedDeployment.config.name,
|
|
3052
|
+
focused: true,
|
|
3053
|
+
onInput: (value) => setConfirmText(value),
|
|
3054
|
+
onSubmit: (value) => {
|
|
3055
|
+
if (value === selectedDeployment.config.name) {
|
|
3056
|
+
handleDelete(selectedDeployment.config.name);
|
|
3057
|
+
} else {
|
|
3058
|
+
setError("Name does not match. Please type the exact deployment name.");
|
|
3059
|
+
}
|
|
3060
|
+
},
|
|
3061
|
+
onKeyDown: (e) => {
|
|
3062
|
+
if (e.name === "escape") {
|
|
3063
|
+
setViewState("detail");
|
|
3064
|
+
setConfirmText("");
|
|
3065
|
+
setError(null);
|
|
3066
|
+
}
|
|
3067
|
+
}
|
|
3068
|
+
}
|
|
3069
|
+
),
|
|
3070
|
+
error && /* @__PURE__ */ jsx3("text", { fg: t.status.error, marginTop: 1, children: error }),
|
|
3071
|
+
/* @__PURE__ */ jsx3("text", { fg: t.fg.muted, marginTop: 2, children: "Press Esc to cancel" })
|
|
3072
|
+
] });
|
|
3073
|
+
}
|
|
3074
|
+
if (viewState === "deleting") {
|
|
3075
|
+
return /* @__PURE__ */ jsxs3("box", { flexDirection: "column", width: "100%", padding: 1, children: [
|
|
3076
|
+
/* @__PURE__ */ jsx3("text", { fg: t.status.warning, children: "Deleting deployment..." }),
|
|
3077
|
+
/* @__PURE__ */ jsx3("text", { fg: t.fg.secondary, marginTop: 1, children: "Removing local configuration files..." })
|
|
3078
|
+
] });
|
|
3079
|
+
}
|
|
3080
|
+
if (viewState === "success") {
|
|
3081
|
+
return /* @__PURE__ */ jsxs3("box", { flexDirection: "column", width: "100%", padding: 1, children: [
|
|
3082
|
+
/* @__PURE__ */ jsxs3(
|
|
3083
|
+
"box",
|
|
3084
|
+
{
|
|
3085
|
+
flexDirection: "column",
|
|
3086
|
+
borderStyle: "single",
|
|
3087
|
+
borderColor: t.status.success,
|
|
3088
|
+
padding: 1,
|
|
3089
|
+
children: [
|
|
3090
|
+
/* @__PURE__ */ jsx3("text", { fg: t.status.success, children: "Deployment Deleted" }),
|
|
3091
|
+
/* @__PURE__ */ jsxs3("text", { fg: t.fg.primary, marginTop: 1, children: [
|
|
3092
|
+
'The deployment "',
|
|
3093
|
+
deletedName,
|
|
3094
|
+
'" has been permanently deleted.'
|
|
3095
|
+
] })
|
|
3096
|
+
]
|
|
3097
|
+
}
|
|
3098
|
+
),
|
|
3099
|
+
/* @__PURE__ */ jsx3("text", { fg: t.fg.muted, marginTop: 2, children: "Press any key to return to home" })
|
|
3100
|
+
] });
|
|
3101
|
+
}
|
|
3102
|
+
if (viewState === "error") {
|
|
3103
|
+
return /* @__PURE__ */ jsxs3("box", { flexDirection: "column", width: "100%", padding: 1, children: [
|
|
3104
|
+
/* @__PURE__ */ jsxs3(
|
|
3105
|
+
"box",
|
|
3106
|
+
{
|
|
3107
|
+
flexDirection: "column",
|
|
3108
|
+
borderStyle: "single",
|
|
3109
|
+
borderColor: t.status.error,
|
|
3110
|
+
padding: 1,
|
|
3111
|
+
children: [
|
|
3112
|
+
/* @__PURE__ */ jsx3("text", { fg: t.status.error, children: "Deletion Failed" }),
|
|
3113
|
+
/* @__PURE__ */ jsx3("text", { fg: t.fg.primary, marginTop: 1, children: error })
|
|
3114
|
+
]
|
|
3115
|
+
}
|
|
3116
|
+
),
|
|
3117
|
+
/* @__PURE__ */ jsx3("text", { fg: t.fg.muted, marginTop: 2, children: "Press any key to go back" })
|
|
3118
|
+
] });
|
|
3119
|
+
}
|
|
3120
|
+
return null;
|
|
3121
|
+
}
|
|
3122
|
+
|
|
3123
|
+
// src/components/DeployView.tsx
|
|
3124
|
+
import { useState as useState4 } from "react";
|
|
3125
|
+
import { useKeyboard as useKeyboard3 } from "@opentui/react";
|
|
3126
|
+
import { jsx as jsx4, jsxs as jsxs4 } from "@opentui/react/jsx-runtime";
|
|
3127
|
+
function DeployView({ context }) {
|
|
3128
|
+
const [selectedIndex, setSelectedIndex] = useState4(0);
|
|
3129
|
+
const [confirmMode, setConfirmMode] = useState4(false);
|
|
3130
|
+
const deployments = context.deployments;
|
|
3131
|
+
const notDeployed = deployments.filter((d) => d.state.status !== "deployed");
|
|
3132
|
+
const deployed = deployments.filter((d) => d.state.status === "deployed");
|
|
3133
|
+
const allDeployments = [...notDeployed, ...deployed];
|
|
3134
|
+
const selectedDeployment = allDeployments[selectedIndex];
|
|
3135
|
+
useKeyboard3((key) => {
|
|
3136
|
+
if (allDeployments.length === 0) {
|
|
3137
|
+
context.navigateTo("home");
|
|
3138
|
+
return;
|
|
3139
|
+
}
|
|
3140
|
+
if (confirmMode) {
|
|
3141
|
+
if (key.name === "y" || key.name === "return") {
|
|
3142
|
+
context.navigateTo("deploying", selectedDeployment.config.name);
|
|
3143
|
+
} else if (key.name === "n" || key.name === "escape") {
|
|
3144
|
+
setConfirmMode(false);
|
|
3145
|
+
}
|
|
3146
|
+
} else {
|
|
3147
|
+
if (key.name === "up" && selectedIndex > 0) {
|
|
3148
|
+
setSelectedIndex(selectedIndex - 1);
|
|
3149
|
+
} else if (key.name === "down" && selectedIndex < allDeployments.length - 1) {
|
|
3150
|
+
setSelectedIndex(selectedIndex + 1);
|
|
3151
|
+
} else if (key.name === "return") {
|
|
3152
|
+
setConfirmMode(true);
|
|
3153
|
+
} else if (key.name === "escape") {
|
|
3154
|
+
context.navigateTo("home");
|
|
3155
|
+
}
|
|
3156
|
+
}
|
|
3157
|
+
});
|
|
3158
|
+
if (allDeployments.length === 0) {
|
|
3159
|
+
return /* @__PURE__ */ jsxs4("box", { flexDirection: "column", width: "100%", padding: 1, children: [
|
|
3160
|
+
/* @__PURE__ */ jsxs4("box", { flexDirection: "row", marginBottom: 2, children: [
|
|
3161
|
+
/* @__PURE__ */ jsx4("text", { fg: t.accent, children: "/deploy" }),
|
|
3162
|
+
/* @__PURE__ */ jsx4("text", { fg: t.fg.secondary, children: " - Deploy a configuration" })
|
|
3163
|
+
] }),
|
|
3164
|
+
/* @__PURE__ */ jsxs4(
|
|
3165
|
+
"box",
|
|
3166
|
+
{
|
|
3167
|
+
flexDirection: "column",
|
|
3168
|
+
borderStyle: "single",
|
|
3169
|
+
borderColor: t.border.default,
|
|
3170
|
+
padding: 1,
|
|
3171
|
+
children: [
|
|
3172
|
+
/* @__PURE__ */ jsx4("text", { fg: t.status.warning, children: "No deployments found!" }),
|
|
3173
|
+
/* @__PURE__ */ jsx4("text", { fg: t.fg.secondary, marginTop: 1, children: "Run /new first to create a deployment configuration." })
|
|
3174
|
+
]
|
|
3175
|
+
}
|
|
3176
|
+
),
|
|
3177
|
+
/* @__PURE__ */ jsx4("text", { fg: t.fg.muted, marginTop: 2, children: "Press any key to return to home" })
|
|
3178
|
+
] });
|
|
3179
|
+
}
|
|
3180
|
+
if (confirmMode) {
|
|
3181
|
+
return /* @__PURE__ */ jsxs4("box", { flexDirection: "column", width: "100%", padding: 1, children: [
|
|
3182
|
+
/* @__PURE__ */ jsxs4("box", { flexDirection: "row", marginBottom: 2, children: [
|
|
3183
|
+
/* @__PURE__ */ jsx4("text", { fg: t.accent, children: "/deploy" }),
|
|
3184
|
+
/* @__PURE__ */ jsx4("text", { fg: t.fg.secondary, children: " - Confirm deployment" })
|
|
3185
|
+
] }),
|
|
3186
|
+
/* @__PURE__ */ jsxs4(
|
|
3187
|
+
"box",
|
|
3188
|
+
{
|
|
3189
|
+
flexDirection: "column",
|
|
3190
|
+
borderStyle: "single",
|
|
3191
|
+
borderColor: t.border.default,
|
|
3192
|
+
padding: 1,
|
|
3193
|
+
children: [
|
|
3194
|
+
/* @__PURE__ */ jsxs4("text", { fg: t.status.warning, children: [
|
|
3195
|
+
'Deploy "',
|
|
3196
|
+
selectedDeployment.config.name,
|
|
3197
|
+
'"?'
|
|
3198
|
+
] }),
|
|
3199
|
+
/* @__PURE__ */ jsx4("text", { fg: t.fg.secondary, marginTop: 1, children: "This will:" }),
|
|
3200
|
+
/* @__PURE__ */ jsxs4("text", { fg: t.fg.primary, children: [
|
|
3201
|
+
"\u2022 Create a VPS on ",
|
|
3202
|
+
selectedDeployment.config.provider
|
|
3203
|
+
] }),
|
|
3204
|
+
/* @__PURE__ */ jsx4("text", { fg: t.fg.primary, children: "\u2022 Install and configure OpenClaw" }),
|
|
3205
|
+
/* @__PURE__ */ jsx4("text", { fg: t.fg.primary, children: "\u2022 Set up Tailscale for secure access" }),
|
|
3206
|
+
/* @__PURE__ */ jsx4("text", { fg: t.fg.secondary, marginTop: 1, children: "Estimated cost: ~$4.99/month (Hetzner CPX11)" })
|
|
3207
|
+
]
|
|
3208
|
+
}
|
|
3209
|
+
),
|
|
3210
|
+
/* @__PURE__ */ jsx4("text", { fg: t.status.warning, marginTop: 2, children: "Press Y to confirm, N to cancel" })
|
|
3211
|
+
] });
|
|
3212
|
+
}
|
|
3213
|
+
return /* @__PURE__ */ jsxs4("box", { flexDirection: "column", width: "100%", padding: 1, children: [
|
|
3214
|
+
/* @__PURE__ */ jsxs4("box", { flexDirection: "row", marginBottom: 2, children: [
|
|
3215
|
+
/* @__PURE__ */ jsx4("text", { fg: t.accent, children: "/deploy" }),
|
|
3216
|
+
/* @__PURE__ */ jsx4("text", { fg: t.fg.secondary, children: " - Select a deployment to deploy" })
|
|
3217
|
+
] }),
|
|
3218
|
+
/* @__PURE__ */ jsx4("text", { fg: t.fg.secondary, marginBottom: 1, children: "Use arrow keys to select, Enter to deploy:" }),
|
|
3219
|
+
/* @__PURE__ */ jsx4(
|
|
3220
|
+
"box",
|
|
3221
|
+
{
|
|
3222
|
+
flexDirection: "column",
|
|
3223
|
+
borderStyle: "single",
|
|
3224
|
+
borderColor: t.border.default,
|
|
3225
|
+
padding: 1,
|
|
3226
|
+
children: allDeployments.map((deployment, index) => {
|
|
3227
|
+
const isSelected = index === selectedIndex;
|
|
3228
|
+
return /* @__PURE__ */ jsxs4(
|
|
3229
|
+
"box",
|
|
3230
|
+
{
|
|
3231
|
+
flexDirection: "row",
|
|
3232
|
+
backgroundColor: isSelected ? t.selection.bg : void 0,
|
|
3233
|
+
children: [
|
|
3234
|
+
/* @__PURE__ */ jsx4("text", { fg: isSelected ? t.selection.fg : t.fg.secondary, children: isSelected ? "> " : " " }),
|
|
3235
|
+
/* @__PURE__ */ jsx4("text", { fg: isSelected ? t.selection.fg : t.fg.secondary, width: 25, children: deployment.config.name }),
|
|
3236
|
+
/* @__PURE__ */ jsxs4("text", { fg: statusColor(deployment.state.status), width: 15, children: [
|
|
3237
|
+
"[",
|
|
3238
|
+
deployment.state.status,
|
|
3239
|
+
"]"
|
|
3240
|
+
] }),
|
|
3241
|
+
/* @__PURE__ */ jsx4("text", { fg: t.fg.secondary, children: deployment.config.provider })
|
|
3242
|
+
]
|
|
3243
|
+
},
|
|
3244
|
+
deployment.config.name
|
|
3245
|
+
);
|
|
3246
|
+
})
|
|
3247
|
+
}
|
|
3248
|
+
),
|
|
3249
|
+
/* @__PURE__ */ jsx4("text", { fg: t.fg.muted, marginTop: 2, children: "Press Esc to go back" })
|
|
3250
|
+
] });
|
|
3251
|
+
}
|
|
3252
|
+
|
|
3253
|
+
// src/components/DeployingView.tsx
|
|
3254
|
+
import { useState as useState5, useEffect as useEffect2, useCallback, useRef as useRef3 } from "react";
|
|
3255
|
+
import { useKeyboard as useKeyboard4 } from "@opentui/react";
|
|
3256
|
+
import open from "open";
|
|
3257
|
+
|
|
3258
|
+
// src/services/ssh.ts
|
|
3259
|
+
import { Client } from "ssh2";
|
|
3260
|
+
import { generateKeyPairSync, randomBytes } from "crypto";
|
|
3261
|
+
import { readFileSync as readFileSync4, writeFileSync as writeFileSync4, chmodSync, existsSync as existsSync4 } from "fs";
|
|
3262
|
+
import { mkdirSync as mkdirSync4 } from "fs";
|
|
3263
|
+
function generateSSHKeyPair(comment = "clawcontrol") {
|
|
3264
|
+
const { privateKey, publicKey } = generateKeyPairSync("ed25519", {
|
|
3265
|
+
publicKeyEncoding: {
|
|
3266
|
+
type: "spki",
|
|
3267
|
+
format: "pem"
|
|
3268
|
+
},
|
|
3269
|
+
privateKeyEncoding: {
|
|
3270
|
+
type: "pkcs8",
|
|
3271
|
+
format: "pem"
|
|
3272
|
+
}
|
|
3273
|
+
});
|
|
3274
|
+
const pubKeyDer = extractDERFromPEM(publicKey);
|
|
3275
|
+
const privKeyDer = extractDERFromPEM(privateKey);
|
|
3276
|
+
const publicKeyBytes = pubKeyDer.slice(-32);
|
|
3277
|
+
const privateKeyBytes = privKeyDer.slice(-32);
|
|
3278
|
+
const publicKeyOpenSSH = buildOpenSSHPublicKey(publicKeyBytes, comment);
|
|
3279
|
+
const privateKeyOpenSSH = buildOpenSSHPrivateKey(privateKeyBytes, publicKeyBytes, comment);
|
|
3280
|
+
return {
|
|
3281
|
+
privateKey: privateKeyOpenSSH,
|
|
3282
|
+
publicKey: publicKeyOpenSSH
|
|
3283
|
+
};
|
|
3284
|
+
}
|
|
3285
|
+
function extractDERFromPEM(pem) {
|
|
3286
|
+
const lines = pem.split("\n").filter((line) => !line.startsWith("-----") && line.trim() !== "");
|
|
3287
|
+
return Buffer.from(lines.join(""), "base64");
|
|
3288
|
+
}
|
|
3289
|
+
function buildOpenSSHPublicKey(publicKeyBytes, comment) {
|
|
3290
|
+
const keyType = Buffer.from("ssh-ed25519");
|
|
3291
|
+
const keyTypeLen = Buffer.alloc(4);
|
|
3292
|
+
keyTypeLen.writeUInt32BE(keyType.length);
|
|
3293
|
+
const keyLen = Buffer.alloc(4);
|
|
3294
|
+
keyLen.writeUInt32BE(publicKeyBytes.length);
|
|
3295
|
+
const opensshKey = Buffer.concat([keyTypeLen, keyType, keyLen, publicKeyBytes]);
|
|
3296
|
+
return `ssh-ed25519 ${opensshKey.toString("base64")} ${comment}`;
|
|
3297
|
+
}
|
|
3298
|
+
function buildOpenSSHPrivateKey(privateKeyBytes, publicKeyBytes, comment) {
|
|
3299
|
+
const AUTH_MAGIC = Buffer.from("openssh-key-v1\0");
|
|
3300
|
+
const cipherName = Buffer.from("none");
|
|
3301
|
+
const kdfName = Buffer.from("none");
|
|
3302
|
+
const kdfOptions = Buffer.alloc(0);
|
|
3303
|
+
const numKeys = 1;
|
|
3304
|
+
const keyType = Buffer.from("ssh-ed25519");
|
|
3305
|
+
const pubKeyBlob = Buffer.concat([
|
|
3306
|
+
uint32BE(keyType.length),
|
|
3307
|
+
keyType,
|
|
3308
|
+
uint32BE(publicKeyBytes.length),
|
|
3309
|
+
publicKeyBytes
|
|
3310
|
+
]);
|
|
3311
|
+
const checkInt = randomBytes(4);
|
|
3312
|
+
const ed25519PrivKey = Buffer.concat([privateKeyBytes, publicKeyBytes]);
|
|
3313
|
+
const commentBuf = Buffer.from(comment);
|
|
3314
|
+
const privateSection = Buffer.concat([
|
|
3315
|
+
checkInt,
|
|
3316
|
+
checkInt,
|
|
3317
|
+
// Two identical check integers
|
|
3318
|
+
uint32BE(keyType.length),
|
|
3319
|
+
keyType,
|
|
3320
|
+
uint32BE(publicKeyBytes.length),
|
|
3321
|
+
publicKeyBytes,
|
|
3322
|
+
uint32BE(ed25519PrivKey.length),
|
|
3323
|
+
ed25519PrivKey,
|
|
2848
3324
|
uint32BE(commentBuf.length),
|
|
2849
3325
|
commentBuf
|
|
2850
3326
|
]);
|
|
@@ -3090,6 +3566,9 @@ async function waitForSSH(host, privateKey, timeoutMs = 18e4, pollIntervalMs = 5
|
|
|
3090
3566
|
throw new Error(`SSH not available after ${timeoutMs / 1e3} seconds`);
|
|
3091
3567
|
}
|
|
3092
3568
|
|
|
3569
|
+
// src/services/deployment.ts
|
|
3570
|
+
import { randomBytes as randomBytes2 } from "crypto";
|
|
3571
|
+
|
|
3093
3572
|
// src/services/setup/index.ts
|
|
3094
3573
|
async function execOrFail(ssh, command, errorMessage) {
|
|
3095
3574
|
const result = await ssh.exec(command);
|
|
@@ -3241,7 +3720,7 @@ async function installOpenClaw(ssh) {
|
|
|
3241
3720
|
throw new Error("OpenClaw installation verification failed");
|
|
3242
3721
|
}
|
|
3243
3722
|
}
|
|
3244
|
-
async function configureOpenClaw(ssh, customConfig, agentConfig) {
|
|
3723
|
+
async function configureOpenClaw(ssh, customConfig, agentConfig, gatewayToken) {
|
|
3245
3724
|
await ssh.exec("mkdir -p ~/.openclaw");
|
|
3246
3725
|
const config = {
|
|
3247
3726
|
browser: {
|
|
@@ -3263,6 +3742,7 @@ async function configureOpenClaw(ssh, customConfig, agentConfig) {
|
|
|
3263
3742
|
port: 18789,
|
|
3264
3743
|
mode: "local",
|
|
3265
3744
|
bind: "loopback",
|
|
3745
|
+
...gatewayToken ? { auth: { token: gatewayToken } } : {},
|
|
3266
3746
|
tailscale: {
|
|
3267
3747
|
mode: "serve",
|
|
3268
3748
|
resetOnExit: false
|
|
@@ -3510,6 +3990,41 @@ async function getOpenClawLogs(ssh, lines = 100) {
|
|
|
3510
3990
|
const result = await ssh.exec(`journalctl -u openclaw -n ${lines} --no-pager`);
|
|
3511
3991
|
return result.stdout;
|
|
3512
3992
|
}
|
|
3993
|
+
async function getDashboardUrl(ssh) {
|
|
3994
|
+
const nvmPrefix = "source ~/.nvm/nvm.sh &&";
|
|
3995
|
+
const dashResult = await ssh.exec(
|
|
3996
|
+
`${nvmPrefix} timeout 10 openclaw dashboard 2>&1 || true`
|
|
3997
|
+
);
|
|
3998
|
+
const rawOutput = dashResult.stdout + "\n" + dashResult.stderr;
|
|
3999
|
+
const output = rawOutput.replace(/\x1b\[[0-9;]*m/g, "");
|
|
4000
|
+
const urlMatch = output.match(/https?:\/\/[^\s\])'"<>]+/);
|
|
4001
|
+
if (urlMatch) {
|
|
4002
|
+
try {
|
|
4003
|
+
const parsed = new URL(urlMatch[0]);
|
|
4004
|
+
return { url: urlMatch[0], port: parseInt(parsed.port) || 18789 };
|
|
4005
|
+
} catch {
|
|
4006
|
+
}
|
|
4007
|
+
}
|
|
4008
|
+
const tokenResult = await ssh.exec(
|
|
4009
|
+
`${nvmPrefix} openclaw config get gateway.auth.token 2>/dev/null || true`
|
|
4010
|
+
);
|
|
4011
|
+
const token = tokenResult.stdout.trim().replace(/^["']|["']$/g, "");
|
|
4012
|
+
if (token) {
|
|
4013
|
+
return {
|
|
4014
|
+
url: `http://127.0.0.1:18789/?token=${encodeURIComponent(token)}`,
|
|
4015
|
+
port: 18789
|
|
4016
|
+
};
|
|
4017
|
+
}
|
|
4018
|
+
const statusResult = await ssh.exec(
|
|
4019
|
+
"systemctl is-active openclaw 2>/dev/null || true"
|
|
4020
|
+
);
|
|
4021
|
+
if (statusResult.stdout.trim() !== "active") {
|
|
4022
|
+
throw new Error("OpenClaw gateway is not running on this server");
|
|
4023
|
+
}
|
|
4024
|
+
throw new Error(
|
|
4025
|
+
"Could not retrieve dashboard URL. Try running 'openclaw dashboard' on the server manually."
|
|
4026
|
+
);
|
|
4027
|
+
}
|
|
3513
4028
|
|
|
3514
4029
|
// src/services/deployment.ts
|
|
3515
4030
|
var MAX_RETRIES = 3;
|
|
@@ -3608,6 +4123,7 @@ var DeploymentOrchestrator = class {
|
|
|
3608
4123
|
onConfirm;
|
|
3609
4124
|
onOpenUrl;
|
|
3610
4125
|
onSpawnTerminal;
|
|
4126
|
+
tailscaleSkipped = false;
|
|
3611
4127
|
constructor(deploymentName, onProgress, onConfirm, onOpenUrl, onSpawnTerminal) {
|
|
3612
4128
|
this.deploymentName = deploymentName;
|
|
3613
4129
|
this.deployment = readDeployment(deploymentName);
|
|
@@ -3991,18 +4507,28 @@ Would you like to retry from the beginning?`
|
|
|
3991
4507
|
const ssh = await this.ensureSSHConnected();
|
|
3992
4508
|
const customConfig = this.deployment.config.openclawConfig;
|
|
3993
4509
|
const agentConfig = this.deployment.config.openclawAgent;
|
|
3994
|
-
|
|
4510
|
+
const gatewayToken = randomBytes2(32).toString("hex");
|
|
4511
|
+
await configureOpenClaw(ssh, customConfig, agentConfig, gatewayToken);
|
|
4512
|
+
updateDeploymentState(this.deploymentName, { gatewayToken });
|
|
3995
4513
|
if (agentConfig) {
|
|
3996
4514
|
this.reportProgress("openclaw_configured", "Writing AI provider environment...");
|
|
3997
4515
|
await writeOpenClawEnvFile(ssh, agentConfig);
|
|
3998
4516
|
}
|
|
3999
4517
|
}
|
|
4000
4518
|
async installTailscale() {
|
|
4519
|
+
if (this.deployment.config.skipTailscale) {
|
|
4520
|
+
this.tailscaleSkipped = true;
|
|
4521
|
+
this.reportProgress("tailscale_installed", "Tailscale setup skipped.");
|
|
4522
|
+
return;
|
|
4523
|
+
}
|
|
4001
4524
|
const ssh = await this.ensureSSHConnected();
|
|
4002
4525
|
await installTailscale(ssh);
|
|
4003
4526
|
}
|
|
4004
4527
|
async authenticateTailscale() {
|
|
4528
|
+
if (this.tailscaleSkipped) return;
|
|
4005
4529
|
const ssh = await this.ensureSSHConnected();
|
|
4530
|
+
const check = await ssh.exec("which tailscale 2>/dev/null");
|
|
4531
|
+
if (check.code !== 0) return;
|
|
4006
4532
|
const authUrl = await getTailscaleAuthUrl(ssh);
|
|
4007
4533
|
if (authUrl) {
|
|
4008
4534
|
const confirmed = await this.onConfirm(
|
|
@@ -4025,7 +4551,10 @@ URL: ${authUrl}`
|
|
|
4025
4551
|
}
|
|
4026
4552
|
}
|
|
4027
4553
|
async configureTailscale() {
|
|
4554
|
+
if (this.tailscaleSkipped) return;
|
|
4028
4555
|
const ssh = await this.ensureSSHConnected();
|
|
4556
|
+
const check = await ssh.exec("which tailscale 2>/dev/null");
|
|
4557
|
+
if (check.code !== 0) return;
|
|
4029
4558
|
const tailscaleIp = await configureTailscaleServe(ssh);
|
|
4030
4559
|
updateDeploymentState(this.deploymentName, {
|
|
4031
4560
|
tailscaleIp
|
|
@@ -4510,14 +5039,14 @@ function getTerminalDisplayName(app) {
|
|
|
4510
5039
|
}
|
|
4511
5040
|
|
|
4512
5041
|
// src/components/DeployingView.tsx
|
|
4513
|
-
import { jsx as
|
|
5042
|
+
import { jsx as jsx5, jsxs as jsxs5 } from "@opentui/react/jsx-runtime";
|
|
4514
5043
|
function DeployingView({ context }) {
|
|
4515
|
-
const [deployState, setDeployState] =
|
|
4516
|
-
const [progress, setProgress] =
|
|
4517
|
-
const [logs, setLogs] =
|
|
4518
|
-
const [error, setError] =
|
|
4519
|
-
const [confirmPrompt, setConfirmPrompt] =
|
|
4520
|
-
const [terminalResolve, setTerminalResolve] =
|
|
5044
|
+
const [deployState, setDeployState] = useState5("deploying");
|
|
5045
|
+
const [progress, setProgress] = useState5(null);
|
|
5046
|
+
const [logs, setLogs] = useState5([]);
|
|
5047
|
+
const [error, setError] = useState5(null);
|
|
5048
|
+
const [confirmPrompt, setConfirmPrompt] = useState5(null);
|
|
5049
|
+
const [terminalResolve, setTerminalResolve] = useState5(null);
|
|
4521
5050
|
const deploymentName = context.selectedDeployment;
|
|
4522
5051
|
const addLog = useCallback((message) => {
|
|
4523
5052
|
setLogs((prev) => [...prev.slice(-20), `[${(/* @__PURE__ */ new Date()).toLocaleTimeString()}] ${message}`]);
|
|
@@ -4565,9 +5094,9 @@ function DeployingView({ context }) {
|
|
|
4565
5094
|
setDeployState("deploying");
|
|
4566
5095
|
addLog("Terminal session confirmed complete, continuing deployment...");
|
|
4567
5096
|
}, [terminalResolve, addLog]);
|
|
4568
|
-
const stateRef =
|
|
5097
|
+
const stateRef = useRef3({ deployState, terminalResolve });
|
|
4569
5098
|
stateRef.current = { deployState, terminalResolve };
|
|
4570
|
-
|
|
5099
|
+
useKeyboard4((key) => {
|
|
4571
5100
|
const currentState = stateRef.current;
|
|
4572
5101
|
if (currentState.deployState === "waiting_terminal") {
|
|
4573
5102
|
if (key.name === "return") {
|
|
@@ -4621,19 +5150,19 @@ function DeployingView({ context }) {
|
|
|
4621
5150
|
const width = 40;
|
|
4622
5151
|
const filled = Math.round(progress.progress / 100 * width);
|
|
4623
5152
|
const empty = width - filled;
|
|
4624
|
-
return /* @__PURE__ */
|
|
4625
|
-
/* @__PURE__ */
|
|
4626
|
-
/* @__PURE__ */
|
|
4627
|
-
/* @__PURE__ */
|
|
4628
|
-
/* @__PURE__ */
|
|
4629
|
-
/* @__PURE__ */
|
|
4630
|
-
/* @__PURE__ */
|
|
5153
|
+
return /* @__PURE__ */ jsxs5("box", { flexDirection: "column", marginBottom: 1, children: [
|
|
5154
|
+
/* @__PURE__ */ jsxs5("box", { flexDirection: "row", children: [
|
|
5155
|
+
/* @__PURE__ */ jsx5("text", { fg: t.fg.secondary, children: "[" }),
|
|
5156
|
+
/* @__PURE__ */ jsx5("text", { fg: t.status.success, children: "\u2588".repeat(filled) }),
|
|
5157
|
+
/* @__PURE__ */ jsx5("text", { fg: t.fg.muted, children: "\u2591".repeat(empty) }),
|
|
5158
|
+
/* @__PURE__ */ jsx5("text", { fg: t.fg.secondary, children: "]" }),
|
|
5159
|
+
/* @__PURE__ */ jsxs5("text", { fg: t.fg.primary, children: [
|
|
4631
5160
|
" ",
|
|
4632
5161
|
Math.round(progress.progress),
|
|
4633
5162
|
"%"
|
|
4634
5163
|
] })
|
|
4635
5164
|
] }),
|
|
4636
|
-
/* @__PURE__ */
|
|
5165
|
+
/* @__PURE__ */ jsxs5("text", { fg: t.accent, marginTop: 1, children: [
|
|
4637
5166
|
"Current: ",
|
|
4638
5167
|
progress.message
|
|
4639
5168
|
] })
|
|
@@ -4642,7 +5171,7 @@ function DeployingView({ context }) {
|
|
|
4642
5171
|
const renderConfirmDialog = () => {
|
|
4643
5172
|
if (!confirmPrompt) return null;
|
|
4644
5173
|
const lines = confirmPrompt.message.split("\n");
|
|
4645
|
-
return /* @__PURE__ */
|
|
5174
|
+
return /* @__PURE__ */ jsxs5(
|
|
4646
5175
|
"box",
|
|
4647
5176
|
{
|
|
4648
5177
|
flexDirection: "column",
|
|
@@ -4651,16 +5180,16 @@ function DeployingView({ context }) {
|
|
|
4651
5180
|
padding: 1,
|
|
4652
5181
|
marginBottom: 1,
|
|
4653
5182
|
children: [
|
|
4654
|
-
/* @__PURE__ */
|
|
4655
|
-
/* @__PURE__ */
|
|
4656
|
-
/* @__PURE__ */
|
|
5183
|
+
/* @__PURE__ */ jsx5("text", { fg: t.status.warning, children: "Confirmation Required" }),
|
|
5184
|
+
/* @__PURE__ */ jsx5("box", { flexDirection: "column", marginTop: 1, children: lines.map((line, i) => /* @__PURE__ */ jsx5("text", { fg: t.fg.primary, children: line }, i)) }),
|
|
5185
|
+
/* @__PURE__ */ jsx5("text", { fg: t.status.warning, marginTop: 1, children: "Press Y for Yes, N for No" })
|
|
4657
5186
|
]
|
|
4658
5187
|
}
|
|
4659
5188
|
);
|
|
4660
5189
|
};
|
|
4661
5190
|
const renderWaitingTerminal = () => {
|
|
4662
|
-
return /* @__PURE__ */
|
|
4663
|
-
/* @__PURE__ */
|
|
5191
|
+
return /* @__PURE__ */ jsxs5("box", { flexDirection: "column", flexGrow: 1, children: [
|
|
5192
|
+
/* @__PURE__ */ jsxs5(
|
|
4664
5193
|
"box",
|
|
4665
5194
|
{
|
|
4666
5195
|
flexDirection: "column",
|
|
@@ -4669,12 +5198,12 @@ function DeployingView({ context }) {
|
|
|
4669
5198
|
padding: 1,
|
|
4670
5199
|
marginBottom: 1,
|
|
4671
5200
|
children: [
|
|
4672
|
-
/* @__PURE__ */
|
|
4673
|
-
/* @__PURE__ */
|
|
5201
|
+
/* @__PURE__ */ jsx5("text", { fg: t.accent, children: "Interactive Setup" }),
|
|
5202
|
+
/* @__PURE__ */ jsx5("text", { fg: t.fg.primary, marginTop: 1, children: "A terminal window has been opened." })
|
|
4674
5203
|
]
|
|
4675
5204
|
}
|
|
4676
5205
|
),
|
|
4677
|
-
/* @__PURE__ */
|
|
5206
|
+
/* @__PURE__ */ jsxs5(
|
|
4678
5207
|
"box",
|
|
4679
5208
|
{
|
|
4680
5209
|
flexDirection: "column",
|
|
@@ -4683,32 +5212,32 @@ function DeployingView({ context }) {
|
|
|
4683
5212
|
padding: 1,
|
|
4684
5213
|
marginBottom: 1,
|
|
4685
5214
|
children: [
|
|
4686
|
-
/* @__PURE__ */
|
|
4687
|
-
/* @__PURE__ */
|
|
4688
|
-
/* @__PURE__ */
|
|
4689
|
-
/* @__PURE__ */
|
|
4690
|
-
/* @__PURE__ */
|
|
4691
|
-
/* @__PURE__ */
|
|
5215
|
+
/* @__PURE__ */ jsx5("text", { fg: t.status.warning, children: "Instructions:" }),
|
|
5216
|
+
/* @__PURE__ */ jsxs5("box", { flexDirection: "column", marginTop: 1, children: [
|
|
5217
|
+
/* @__PURE__ */ jsx5("text", { fg: t.fg.primary, children: "1. Complete the setup in the terminal window" }),
|
|
5218
|
+
/* @__PURE__ */ jsx5("text", { fg: t.fg.primary, children: "2. Follow the prompts shown in the terminal" }),
|
|
5219
|
+
/* @__PURE__ */ jsx5("text", { fg: t.fg.primary, children: "3. When done, close the terminal window" }),
|
|
5220
|
+
/* @__PURE__ */ jsx5("text", { fg: t.fg.primary, children: "4. Press Enter here to continue" })
|
|
4692
5221
|
] })
|
|
4693
5222
|
]
|
|
4694
5223
|
}
|
|
4695
5224
|
),
|
|
4696
|
-
/* @__PURE__ */
|
|
5225
|
+
/* @__PURE__ */ jsx5(
|
|
4697
5226
|
"box",
|
|
4698
5227
|
{
|
|
4699
5228
|
flexDirection: "column",
|
|
4700
5229
|
borderStyle: "single",
|
|
4701
5230
|
borderColor: t.status.success,
|
|
4702
5231
|
padding: 1,
|
|
4703
|
-
children: /* @__PURE__ */
|
|
5232
|
+
children: /* @__PURE__ */ jsx5("text", { fg: t.status.success, children: "Press Enter when you have completed the setup in the terminal" })
|
|
4704
5233
|
}
|
|
4705
5234
|
)
|
|
4706
5235
|
] });
|
|
4707
5236
|
};
|
|
4708
5237
|
const renderSuccess = () => {
|
|
4709
5238
|
const state = readDeploymentState(deploymentName);
|
|
4710
|
-
return /* @__PURE__ */
|
|
4711
|
-
/* @__PURE__ */
|
|
5239
|
+
return /* @__PURE__ */ jsxs5("box", { flexDirection: "column", children: [
|
|
5240
|
+
/* @__PURE__ */ jsxs5(
|
|
4712
5241
|
"box",
|
|
4713
5242
|
{
|
|
4714
5243
|
flexDirection: "column",
|
|
@@ -4717,12 +5246,12 @@ function DeployingView({ context }) {
|
|
|
4717
5246
|
padding: 1,
|
|
4718
5247
|
marginBottom: 1,
|
|
4719
5248
|
children: [
|
|
4720
|
-
/* @__PURE__ */
|
|
4721
|
-
/* @__PURE__ */
|
|
5249
|
+
/* @__PURE__ */ jsx5("text", { fg: t.status.success, children: "Deployment Successful!" }),
|
|
5250
|
+
/* @__PURE__ */ jsx5("text", { fg: t.status.success, marginTop: 1, children: "Your OpenClaw instance is now running." })
|
|
4722
5251
|
]
|
|
4723
5252
|
}
|
|
4724
5253
|
),
|
|
4725
|
-
/* @__PURE__ */
|
|
5254
|
+
/* @__PURE__ */ jsxs5(
|
|
4726
5255
|
"box",
|
|
4727
5256
|
{
|
|
4728
5257
|
flexDirection: "column",
|
|
@@ -4731,36 +5260,36 @@ function DeployingView({ context }) {
|
|
|
4731
5260
|
padding: 1,
|
|
4732
5261
|
marginBottom: 1,
|
|
4733
5262
|
children: [
|
|
4734
|
-
/* @__PURE__ */
|
|
4735
|
-
/* @__PURE__ */
|
|
4736
|
-
/* @__PURE__ */
|
|
4737
|
-
/* @__PURE__ */
|
|
5263
|
+
/* @__PURE__ */ jsx5("text", { fg: t.fg.primary, children: "Connection Details" }),
|
|
5264
|
+
/* @__PURE__ */ jsxs5("box", { flexDirection: "row", marginTop: 1, children: [
|
|
5265
|
+
/* @__PURE__ */ jsx5("text", { fg: t.fg.secondary, width: 15, children: "Server IP:" }),
|
|
5266
|
+
/* @__PURE__ */ jsx5("text", { fg: t.accent, children: state.serverIp || "N/A" })
|
|
4738
5267
|
] }),
|
|
4739
|
-
/* @__PURE__ */
|
|
4740
|
-
/* @__PURE__ */
|
|
4741
|
-
/* @__PURE__ */
|
|
5268
|
+
/* @__PURE__ */ jsxs5("box", { flexDirection: "row", children: [
|
|
5269
|
+
/* @__PURE__ */ jsx5("text", { fg: t.fg.secondary, width: 15, children: "Tailscale IP:" }),
|
|
5270
|
+
/* @__PURE__ */ jsx5("text", { fg: t.accent, children: state.tailscaleIp || "N/A" })
|
|
4742
5271
|
] }),
|
|
4743
|
-
/* @__PURE__ */
|
|
4744
|
-
/* @__PURE__ */
|
|
4745
|
-
/* @__PURE__ */
|
|
5272
|
+
/* @__PURE__ */ jsxs5("box", { flexDirection: "row", children: [
|
|
5273
|
+
/* @__PURE__ */ jsx5("text", { fg: t.fg.secondary, width: 15, children: "Gateway Port:" }),
|
|
5274
|
+
/* @__PURE__ */ jsx5("text", { fg: t.accent, children: "18789" })
|
|
4746
5275
|
] })
|
|
4747
5276
|
]
|
|
4748
5277
|
}
|
|
4749
5278
|
),
|
|
4750
|
-
/* @__PURE__ */
|
|
4751
|
-
/* @__PURE__ */
|
|
4752
|
-
/* @__PURE__ */
|
|
4753
|
-
/* @__PURE__ */
|
|
5279
|
+
/* @__PURE__ */ jsx5("text", { fg: t.status.success, children: "Next steps:" }),
|
|
5280
|
+
/* @__PURE__ */ jsx5("text", { fg: t.fg.primary, children: " /ssh - Connect to your server" }),
|
|
5281
|
+
/* @__PURE__ */ jsx5("text", { fg: t.fg.primary, children: " /logs - View OpenClaw logs" }),
|
|
5282
|
+
/* @__PURE__ */ jsxs5("text", { fg: t.fg.primary, children: [
|
|
4754
5283
|
" Gateway: http://",
|
|
4755
5284
|
state.tailscaleIp || state.serverIp,
|
|
4756
5285
|
":18789/"
|
|
4757
5286
|
] }),
|
|
4758
|
-
/* @__PURE__ */
|
|
5287
|
+
/* @__PURE__ */ jsx5("text", { fg: t.fg.muted, marginTop: 2, children: "Press any key to return to home" })
|
|
4759
5288
|
] });
|
|
4760
5289
|
};
|
|
4761
5290
|
const renderFailed = () => {
|
|
4762
|
-
return /* @__PURE__ */
|
|
4763
|
-
/* @__PURE__ */
|
|
5291
|
+
return /* @__PURE__ */ jsxs5("box", { flexDirection: "column", children: [
|
|
5292
|
+
/* @__PURE__ */ jsxs5(
|
|
4764
5293
|
"box",
|
|
4765
5294
|
{
|
|
4766
5295
|
flexDirection: "column",
|
|
@@ -4769,26 +5298,26 @@ function DeployingView({ context }) {
|
|
|
4769
5298
|
padding: 1,
|
|
4770
5299
|
marginBottom: 1,
|
|
4771
5300
|
children: [
|
|
4772
|
-
/* @__PURE__ */
|
|
4773
|
-
/* @__PURE__ */
|
|
4774
|
-
/* @__PURE__ */
|
|
5301
|
+
/* @__PURE__ */ jsx5("text", { fg: t.status.error, children: "Deployment Failed" }),
|
|
5302
|
+
/* @__PURE__ */ jsx5("text", { fg: t.fg.primary, marginTop: 1, children: "Something went wrong during deployment." }),
|
|
5303
|
+
/* @__PURE__ */ jsxs5("text", { fg: t.status.error, marginTop: 1, children: [
|
|
4775
5304
|
"Error: ",
|
|
4776
5305
|
error
|
|
4777
5306
|
] })
|
|
4778
5307
|
]
|
|
4779
5308
|
}
|
|
4780
5309
|
),
|
|
4781
|
-
/* @__PURE__ */
|
|
4782
|
-
/* @__PURE__ */
|
|
4783
|
-
/* @__PURE__ */
|
|
4784
|
-
/* @__PURE__ */
|
|
4785
|
-
/* @__PURE__ */
|
|
5310
|
+
/* @__PURE__ */ jsxs5("box", { flexDirection: "column", marginBottom: 1, children: [
|
|
5311
|
+
/* @__PURE__ */ jsx5("text", { fg: t.fg.primary, children: "What you can do:" }),
|
|
5312
|
+
/* @__PURE__ */ jsx5("text", { fg: t.fg.secondary, children: " 1. Run /deploy again - it will resume from the last successful step" }),
|
|
5313
|
+
/* @__PURE__ */ jsx5("text", { fg: t.fg.secondary, children: " 2. Run /status to check the current state of your deployment" }),
|
|
5314
|
+
/* @__PURE__ */ jsx5("text", { fg: t.fg.secondary, children: " 3. Run /destroy and /new to start fresh if the issue persists" })
|
|
4786
5315
|
] }),
|
|
4787
|
-
/* @__PURE__ */
|
|
5316
|
+
/* @__PURE__ */ jsx5("text", { fg: t.fg.muted, marginTop: 1, children: "Press any key to return to home" })
|
|
4788
5317
|
] });
|
|
4789
5318
|
};
|
|
4790
|
-
return /* @__PURE__ */
|
|
4791
|
-
/* @__PURE__ */
|
|
5319
|
+
return /* @__PURE__ */ jsxs5("box", { flexDirection: "column", width: "100%", padding: 1, children: [
|
|
5320
|
+
/* @__PURE__ */ jsx5("box", { flexDirection: "row", marginBottom: 2, children: /* @__PURE__ */ jsxs5("text", { fg: t.accent, children: [
|
|
4792
5321
|
"Deploying: ",
|
|
4793
5322
|
deploymentName
|
|
4794
5323
|
] }) }),
|
|
@@ -4797,7 +5326,7 @@ function DeployingView({ context }) {
|
|
|
4797
5326
|
deployState === "waiting_terminal" && renderWaitingTerminal(),
|
|
4798
5327
|
deployState === "success" && renderSuccess(),
|
|
4799
5328
|
deployState === "failed" && renderFailed(),
|
|
4800
|
-
/* @__PURE__ */
|
|
5329
|
+
/* @__PURE__ */ jsxs5(
|
|
4801
5330
|
"box",
|
|
4802
5331
|
{
|
|
4803
5332
|
flexDirection: "column",
|
|
@@ -4805,8 +5334,8 @@ function DeployingView({ context }) {
|
|
|
4805
5334
|
borderColor: t.border.default,
|
|
4806
5335
|
padding: 1,
|
|
4807
5336
|
children: [
|
|
4808
|
-
/* @__PURE__ */
|
|
4809
|
-
/* @__PURE__ */
|
|
5337
|
+
/* @__PURE__ */ jsx5("text", { fg: t.fg.secondary, children: "Deployment Log" }),
|
|
5338
|
+
/* @__PURE__ */ jsx5("box", { flexDirection: "column", marginTop: 1, children: logs.map((log, i) => /* @__PURE__ */ jsx5("text", { fg: t.fg.muted, children: log }, i)) })
|
|
4810
5339
|
]
|
|
4811
5340
|
}
|
|
4812
5341
|
)
|
|
@@ -4814,13 +5343,13 @@ function DeployingView({ context }) {
|
|
|
4814
5343
|
}
|
|
4815
5344
|
|
|
4816
5345
|
// src/components/StatusView.tsx
|
|
4817
|
-
import { useState as
|
|
4818
|
-
import { useKeyboard as
|
|
4819
|
-
import { jsx as
|
|
5346
|
+
import { useState as useState6 } from "react";
|
|
5347
|
+
import { useKeyboard as useKeyboard5 } from "@opentui/react";
|
|
5348
|
+
import { jsx as jsx6, jsxs as jsxs6 } from "@opentui/react/jsx-runtime";
|
|
4820
5349
|
function StatusView({ context }) {
|
|
4821
|
-
const [selectedIndex, setSelectedIndex] =
|
|
4822
|
-
const [healthStatus, setHealthStatus] =
|
|
4823
|
-
const [checking, setChecking] =
|
|
5350
|
+
const [selectedIndex, setSelectedIndex] = useState6(0);
|
|
5351
|
+
const [healthStatus, setHealthStatus] = useState6(/* @__PURE__ */ new Map());
|
|
5352
|
+
const [checking, setChecking] = useState6(null);
|
|
4824
5353
|
const deployments = context.deployments;
|
|
4825
5354
|
const checkHealth = async (deployment) => {
|
|
4826
5355
|
const name = deployment.config.name;
|
|
@@ -4845,7 +5374,7 @@ function StatusView({ context }) {
|
|
|
4845
5374
|
setHealthStatus((prev) => new Map(prev).set(name, health));
|
|
4846
5375
|
setChecking(null);
|
|
4847
5376
|
};
|
|
4848
|
-
|
|
5377
|
+
useKeyboard5((key) => {
|
|
4849
5378
|
if (deployments.length === 0) {
|
|
4850
5379
|
context.navigateTo("home");
|
|
4851
5380
|
return;
|
|
@@ -4861,12 +5390,12 @@ function StatusView({ context }) {
|
|
|
4861
5390
|
}
|
|
4862
5391
|
});
|
|
4863
5392
|
if (deployments.length === 0) {
|
|
4864
|
-
return /* @__PURE__ */
|
|
4865
|
-
/* @__PURE__ */
|
|
4866
|
-
/* @__PURE__ */
|
|
4867
|
-
/* @__PURE__ */
|
|
5393
|
+
return /* @__PURE__ */ jsxs6("box", { flexDirection: "column", width: "100%", padding: 1, children: [
|
|
5394
|
+
/* @__PURE__ */ jsxs6("box", { flexDirection: "row", marginBottom: 2, children: [
|
|
5395
|
+
/* @__PURE__ */ jsx6("text", { fg: t.accent, children: "/status" }),
|
|
5396
|
+
/* @__PURE__ */ jsx6("text", { fg: t.fg.secondary, children: " - Deployment Status" })
|
|
4868
5397
|
] }),
|
|
4869
|
-
/* @__PURE__ */
|
|
5398
|
+
/* @__PURE__ */ jsxs6(
|
|
4870
5399
|
"box",
|
|
4871
5400
|
{
|
|
4872
5401
|
flexDirection: "column",
|
|
@@ -4874,22 +5403,22 @@ function StatusView({ context }) {
|
|
|
4874
5403
|
borderColor: t.border.default,
|
|
4875
5404
|
padding: 1,
|
|
4876
5405
|
children: [
|
|
4877
|
-
/* @__PURE__ */
|
|
4878
|
-
/* @__PURE__ */
|
|
5406
|
+
/* @__PURE__ */ jsx6("text", { fg: t.status.warning, children: "No deployments found!" }),
|
|
5407
|
+
/* @__PURE__ */ jsx6("text", { fg: t.fg.secondary, marginTop: 1, children: "Run /new to create a deployment." })
|
|
4879
5408
|
]
|
|
4880
5409
|
}
|
|
4881
5410
|
),
|
|
4882
|
-
/* @__PURE__ */
|
|
5411
|
+
/* @__PURE__ */ jsx6("text", { fg: t.fg.muted, marginTop: 2, children: "Press any key to return to home" })
|
|
4883
5412
|
] });
|
|
4884
5413
|
}
|
|
4885
5414
|
const selectedDeployment = deployments[selectedIndex];
|
|
4886
5415
|
const selectedHealth = healthStatus.get(selectedDeployment.config.name);
|
|
4887
|
-
return /* @__PURE__ */
|
|
4888
|
-
/* @__PURE__ */
|
|
4889
|
-
/* @__PURE__ */
|
|
4890
|
-
/* @__PURE__ */
|
|
5416
|
+
return /* @__PURE__ */ jsxs6("box", { flexDirection: "column", width: "100%", padding: 1, children: [
|
|
5417
|
+
/* @__PURE__ */ jsxs6("box", { flexDirection: "row", marginBottom: 2, children: [
|
|
5418
|
+
/* @__PURE__ */ jsx6("text", { fg: t.accent, children: "/status" }),
|
|
5419
|
+
/* @__PURE__ */ jsx6("text", { fg: t.fg.secondary, children: " - Deployment Status" })
|
|
4891
5420
|
] }),
|
|
4892
|
-
/* @__PURE__ */
|
|
5421
|
+
/* @__PURE__ */ jsxs6(
|
|
4893
5422
|
"box",
|
|
4894
5423
|
{
|
|
4895
5424
|
flexDirection: "column",
|
|
@@ -4898,22 +5427,22 @@ function StatusView({ context }) {
|
|
|
4898
5427
|
padding: 1,
|
|
4899
5428
|
marginBottom: 1,
|
|
4900
5429
|
children: [
|
|
4901
|
-
/* @__PURE__ */
|
|
5430
|
+
/* @__PURE__ */ jsxs6("text", { fg: t.fg.primary, marginBottom: 1, children: [
|
|
4902
5431
|
"Deployments (",
|
|
4903
5432
|
deployments.length,
|
|
4904
5433
|
")"
|
|
4905
5434
|
] }),
|
|
4906
5435
|
deployments.map((deployment, index) => {
|
|
4907
5436
|
const isSelected = index === selectedIndex;
|
|
4908
|
-
return /* @__PURE__ */
|
|
5437
|
+
return /* @__PURE__ */ jsxs6(
|
|
4909
5438
|
"box",
|
|
4910
5439
|
{
|
|
4911
5440
|
flexDirection: "row",
|
|
4912
5441
|
backgroundColor: isSelected ? t.selection.bg : void 0,
|
|
4913
5442
|
children: [
|
|
4914
|
-
/* @__PURE__ */
|
|
4915
|
-
/* @__PURE__ */
|
|
4916
|
-
/* @__PURE__ */
|
|
5443
|
+
/* @__PURE__ */ jsx6("text", { fg: isSelected ? t.selection.fg : t.fg.secondary, children: isSelected ? "> " : " " }),
|
|
5444
|
+
/* @__PURE__ */ jsx6("text", { fg: isSelected ? t.selection.fg : t.fg.secondary, width: 25, children: deployment.config.name }),
|
|
5445
|
+
/* @__PURE__ */ jsxs6("text", { fg: statusColor(deployment.state.status), children: [
|
|
4917
5446
|
"[",
|
|
4918
5447
|
deployment.state.status,
|
|
4919
5448
|
"]"
|
|
@@ -4926,7 +5455,7 @@ function StatusView({ context }) {
|
|
|
4926
5455
|
]
|
|
4927
5456
|
}
|
|
4928
5457
|
),
|
|
4929
|
-
/* @__PURE__ */
|
|
5458
|
+
/* @__PURE__ */ jsxs6(
|
|
4930
5459
|
"box",
|
|
4931
5460
|
{
|
|
4932
5461
|
flexDirection: "column",
|
|
@@ -4935,78 +5464,78 @@ function StatusView({ context }) {
|
|
|
4935
5464
|
padding: 1,
|
|
4936
5465
|
marginBottom: 1,
|
|
4937
5466
|
children: [
|
|
4938
|
-
/* @__PURE__ */
|
|
5467
|
+
/* @__PURE__ */ jsxs6("text", { fg: t.accent, children: [
|
|
4939
5468
|
"Details: ",
|
|
4940
5469
|
selectedDeployment.config.name
|
|
4941
5470
|
] }),
|
|
4942
|
-
/* @__PURE__ */
|
|
4943
|
-
/* @__PURE__ */
|
|
4944
|
-
/* @__PURE__ */
|
|
5471
|
+
/* @__PURE__ */ jsxs6("box", { flexDirection: "row", marginTop: 1, children: [
|
|
5472
|
+
/* @__PURE__ */ jsx6("text", { fg: t.fg.secondary, width: 18, children: "Status:" }),
|
|
5473
|
+
/* @__PURE__ */ jsx6("text", { fg: statusColor(selectedDeployment.state.status), children: selectedDeployment.state.status })
|
|
4945
5474
|
] }),
|
|
4946
|
-
/* @__PURE__ */
|
|
4947
|
-
/* @__PURE__ */
|
|
4948
|
-
/* @__PURE__ */
|
|
5475
|
+
/* @__PURE__ */ jsxs6("box", { flexDirection: "row", children: [
|
|
5476
|
+
/* @__PURE__ */ jsx6("text", { fg: t.fg.secondary, width: 18, children: "Provider:" }),
|
|
5477
|
+
/* @__PURE__ */ jsx6("text", { fg: t.fg.primary, children: selectedDeployment.config.provider })
|
|
4949
5478
|
] }),
|
|
4950
|
-
/* @__PURE__ */
|
|
4951
|
-
/* @__PURE__ */
|
|
4952
|
-
/* @__PURE__ */
|
|
5479
|
+
/* @__PURE__ */ jsxs6("box", { flexDirection: "row", children: [
|
|
5480
|
+
/* @__PURE__ */ jsx6("text", { fg: t.fg.secondary, width: 18, children: "Server IP:" }),
|
|
5481
|
+
/* @__PURE__ */ jsx6("text", { fg: t.accent, children: selectedDeployment.state.serverIp || "Not deployed" })
|
|
4953
5482
|
] }),
|
|
4954
|
-
/* @__PURE__ */
|
|
4955
|
-
/* @__PURE__ */
|
|
4956
|
-
/* @__PURE__ */
|
|
5483
|
+
/* @__PURE__ */ jsxs6("box", { flexDirection: "row", children: [
|
|
5484
|
+
/* @__PURE__ */ jsx6("text", { fg: t.fg.secondary, width: 18, children: "Tailscale IP:" }),
|
|
5485
|
+
/* @__PURE__ */ jsx6("text", { fg: t.accent, children: selectedDeployment.state.tailscaleIp || "Not configured" })
|
|
4957
5486
|
] }),
|
|
4958
|
-
/* @__PURE__ */
|
|
4959
|
-
/* @__PURE__ */
|
|
4960
|
-
/* @__PURE__ */
|
|
5487
|
+
/* @__PURE__ */ jsxs6("box", { flexDirection: "row", children: [
|
|
5488
|
+
/* @__PURE__ */ jsx6("text", { fg: t.fg.secondary, width: 18, children: "Created:" }),
|
|
5489
|
+
/* @__PURE__ */ jsx6("text", { fg: t.fg.primary, children: new Date(selectedDeployment.config.createdAt).toLocaleString() })
|
|
4961
5490
|
] }),
|
|
4962
|
-
selectedDeployment.state.deployedAt && /* @__PURE__ */
|
|
4963
|
-
/* @__PURE__ */
|
|
4964
|
-
/* @__PURE__ */
|
|
5491
|
+
selectedDeployment.state.deployedAt && /* @__PURE__ */ jsxs6("box", { flexDirection: "row", children: [
|
|
5492
|
+
/* @__PURE__ */ jsx6("text", { fg: t.fg.secondary, width: 18, children: "Deployed:" }),
|
|
5493
|
+
/* @__PURE__ */ jsx6("text", { fg: t.fg.primary, children: new Date(selectedDeployment.state.deployedAt).toLocaleString() })
|
|
4965
5494
|
] }),
|
|
4966
|
-
selectedDeployment.state.lastError && /* @__PURE__ */
|
|
4967
|
-
/* @__PURE__ */
|
|
4968
|
-
/* @__PURE__ */
|
|
5495
|
+
selectedDeployment.state.lastError && /* @__PURE__ */ jsxs6("box", { flexDirection: "row", children: [
|
|
5496
|
+
/* @__PURE__ */ jsx6("text", { fg: t.fg.secondary, width: 18, children: "Last Error:" }),
|
|
5497
|
+
/* @__PURE__ */ jsx6("text", { fg: t.status.error, children: selectedDeployment.state.lastError })
|
|
4969
5498
|
] }),
|
|
4970
|
-
selectedHealth && /* @__PURE__ */
|
|
4971
|
-
/* @__PURE__ */
|
|
4972
|
-
/* @__PURE__ */
|
|
4973
|
-
/* @__PURE__ */
|
|
4974
|
-
/* @__PURE__ */
|
|
5499
|
+
selectedHealth && /* @__PURE__ */ jsxs6("box", { flexDirection: "column", marginTop: 1, children: [
|
|
5500
|
+
/* @__PURE__ */ jsx6("text", { fg: t.fg.primary, children: "Health Check:" }),
|
|
5501
|
+
/* @__PURE__ */ jsxs6("box", { flexDirection: "row", children: [
|
|
5502
|
+
/* @__PURE__ */ jsx6("text", { fg: t.fg.secondary, width: 18, children: "SSH:" }),
|
|
5503
|
+
/* @__PURE__ */ jsx6("text", { fg: selectedHealth.sshConnectable ? t.status.success : t.status.error, children: selectedHealth.sshConnectable ? "Connected" : "Unreachable" })
|
|
4975
5504
|
] }),
|
|
4976
|
-
/* @__PURE__ */
|
|
4977
|
-
/* @__PURE__ */
|
|
4978
|
-
/* @__PURE__ */
|
|
5505
|
+
/* @__PURE__ */ jsxs6("box", { flexDirection: "row", children: [
|
|
5506
|
+
/* @__PURE__ */ jsx6("text", { fg: t.fg.secondary, width: 18, children: "OpenClaw:" }),
|
|
5507
|
+
/* @__PURE__ */ jsx6("text", { fg: selectedHealth.openclawRunning ? t.status.success : t.status.error, children: selectedHealth.openclawRunning ? "Running" : "Not running" })
|
|
4979
5508
|
] }),
|
|
4980
|
-
/* @__PURE__ */
|
|
5509
|
+
/* @__PURE__ */ jsxs6("text", { fg: t.fg.muted, children: [
|
|
4981
5510
|
"Last checked: ",
|
|
4982
5511
|
selectedHealth.lastChecked.toLocaleTimeString()
|
|
4983
5512
|
] })
|
|
4984
5513
|
] }),
|
|
4985
|
-
checking === selectedDeployment.config.name && /* @__PURE__ */
|
|
5514
|
+
checking === selectedDeployment.config.name && /* @__PURE__ */ jsx6("text", { fg: t.status.warning, marginTop: 1, children: "Checking health..." })
|
|
4986
5515
|
]
|
|
4987
5516
|
}
|
|
4988
5517
|
),
|
|
4989
|
-
selectedDeployment.state.checkpoints.length > 0 && /* @__PURE__ */
|
|
4990
|
-
/* @__PURE__ */
|
|
4991
|
-
/* @__PURE__ */
|
|
5518
|
+
selectedDeployment.state.checkpoints.length > 0 && /* @__PURE__ */ jsxs6("box", { flexDirection: "row", marginBottom: 1, children: [
|
|
5519
|
+
/* @__PURE__ */ jsx6("text", { fg: t.fg.secondary, children: "Checkpoints: " }),
|
|
5520
|
+
/* @__PURE__ */ jsxs6("text", { fg: t.status.success, children: [
|
|
4992
5521
|
selectedDeployment.state.checkpoints.length,
|
|
4993
5522
|
" completed"
|
|
4994
5523
|
] })
|
|
4995
5524
|
] }),
|
|
4996
|
-
/* @__PURE__ */
|
|
5525
|
+
/* @__PURE__ */ jsx6("text", { fg: t.fg.muted, children: "Up/Down: Select | Enter: Health check | Esc: Back" })
|
|
4997
5526
|
] });
|
|
4998
5527
|
}
|
|
4999
5528
|
|
|
5000
5529
|
// src/components/SSHView.tsx
|
|
5001
|
-
import { useState as
|
|
5002
|
-
import { useKeyboard as
|
|
5003
|
-
import { jsx as
|
|
5530
|
+
import { useState as useState7, useCallback as useCallback2 } from "react";
|
|
5531
|
+
import { useKeyboard as useKeyboard6 } from "@opentui/react";
|
|
5532
|
+
import { jsx as jsx7, jsxs as jsxs7 } from "@opentui/react/jsx-runtime";
|
|
5004
5533
|
function SSHView({ context }) {
|
|
5005
|
-
const [viewState, setViewState] =
|
|
5006
|
-
const [selectedIndex, setSelectedIndex] =
|
|
5007
|
-
const [error, setError] =
|
|
5008
|
-
const [connectedDeployment, setConnectedDeployment] =
|
|
5009
|
-
const [terminalName, setTerminalName] =
|
|
5534
|
+
const [viewState, setViewState] = useState7("selecting");
|
|
5535
|
+
const [selectedIndex, setSelectedIndex] = useState7(0);
|
|
5536
|
+
const [error, setError] = useState7(null);
|
|
5537
|
+
const [connectedDeployment, setConnectedDeployment] = useState7(null);
|
|
5538
|
+
const [terminalName, setTerminalName] = useState7("");
|
|
5010
5539
|
const deployedDeployments = context.deployments.filter(
|
|
5011
5540
|
(d) => d.state.status === "deployed" && d.state.serverIp
|
|
5012
5541
|
);
|
|
@@ -5025,7 +5554,7 @@ function SSHView({ context }) {
|
|
|
5025
5554
|
}
|
|
5026
5555
|
}, []);
|
|
5027
5556
|
const selectedDeployment = deployedDeployments[selectedIndex];
|
|
5028
|
-
|
|
5557
|
+
useKeyboard6((key) => {
|
|
5029
5558
|
if (deployedDeployments.length === 0) {
|
|
5030
5559
|
context.navigateTo("home");
|
|
5031
5560
|
return;
|
|
@@ -5049,12 +5578,12 @@ function SSHView({ context }) {
|
|
|
5049
5578
|
}
|
|
5050
5579
|
});
|
|
5051
5580
|
if (deployedDeployments.length === 0) {
|
|
5052
|
-
return /* @__PURE__ */
|
|
5053
|
-
/* @__PURE__ */
|
|
5054
|
-
/* @__PURE__ */
|
|
5055
|
-
/* @__PURE__ */
|
|
5581
|
+
return /* @__PURE__ */ jsxs7("box", { flexDirection: "column", width: "100%", padding: 1, children: [
|
|
5582
|
+
/* @__PURE__ */ jsxs7("box", { flexDirection: "row", marginBottom: 2, children: [
|
|
5583
|
+
/* @__PURE__ */ jsx7("text", { fg: t.accent, children: "/ssh" }),
|
|
5584
|
+
/* @__PURE__ */ jsx7("text", { fg: t.fg.secondary, children: " - SSH into deployment" })
|
|
5056
5585
|
] }),
|
|
5057
|
-
/* @__PURE__ */
|
|
5586
|
+
/* @__PURE__ */ jsxs7(
|
|
5058
5587
|
"box",
|
|
5059
5588
|
{
|
|
5060
5589
|
flexDirection: "column",
|
|
@@ -5062,23 +5591,23 @@ function SSHView({ context }) {
|
|
|
5062
5591
|
borderColor: t.border.default,
|
|
5063
5592
|
padding: 1,
|
|
5064
5593
|
children: [
|
|
5065
|
-
/* @__PURE__ */
|
|
5066
|
-
/* @__PURE__ */
|
|
5594
|
+
/* @__PURE__ */ jsx7("text", { fg: t.status.warning, children: "No deployed instances found!" }),
|
|
5595
|
+
/* @__PURE__ */ jsx7("text", { fg: t.fg.secondary, marginTop: 1, children: "Deploy an instance first with /deploy" })
|
|
5067
5596
|
]
|
|
5068
5597
|
}
|
|
5069
5598
|
),
|
|
5070
|
-
/* @__PURE__ */
|
|
5599
|
+
/* @__PURE__ */ jsx7("text", { fg: t.fg.muted, marginTop: 2, children: "Press any key to return to home" })
|
|
5071
5600
|
] });
|
|
5072
5601
|
}
|
|
5073
5602
|
if (viewState === "selecting") {
|
|
5074
5603
|
const terminal = detectTerminal();
|
|
5075
5604
|
const terminalDisplayName = getTerminalDisplayName(terminal.app);
|
|
5076
|
-
return /* @__PURE__ */
|
|
5077
|
-
/* @__PURE__ */
|
|
5078
|
-
/* @__PURE__ */
|
|
5079
|
-
/* @__PURE__ */
|
|
5605
|
+
return /* @__PURE__ */ jsxs7("box", { flexDirection: "column", width: "100%", padding: 1, children: [
|
|
5606
|
+
/* @__PURE__ */ jsxs7("box", { flexDirection: "row", marginBottom: 2, children: [
|
|
5607
|
+
/* @__PURE__ */ jsx7("text", { fg: t.accent, children: "/ssh" }),
|
|
5608
|
+
/* @__PURE__ */ jsx7("text", { fg: t.fg.secondary, children: " - Select a deployment to connect" })
|
|
5080
5609
|
] }),
|
|
5081
|
-
/* @__PURE__ */
|
|
5610
|
+
/* @__PURE__ */ jsx7(
|
|
5082
5611
|
"box",
|
|
5083
5612
|
{
|
|
5084
5613
|
flexDirection: "column",
|
|
@@ -5088,15 +5617,15 @@ function SSHView({ context }) {
|
|
|
5088
5617
|
marginBottom: 1,
|
|
5089
5618
|
children: deployedDeployments.map((deployment, index) => {
|
|
5090
5619
|
const isSelected = index === selectedIndex;
|
|
5091
|
-
return /* @__PURE__ */
|
|
5620
|
+
return /* @__PURE__ */ jsxs7(
|
|
5092
5621
|
"box",
|
|
5093
5622
|
{
|
|
5094
5623
|
flexDirection: "row",
|
|
5095
5624
|
backgroundColor: isSelected ? t.selection.bg : void 0,
|
|
5096
5625
|
children: [
|
|
5097
|
-
/* @__PURE__ */
|
|
5098
|
-
/* @__PURE__ */
|
|
5099
|
-
/* @__PURE__ */
|
|
5626
|
+
/* @__PURE__ */ jsx7("text", { fg: isSelected ? t.selection.fg : t.fg.secondary, children: isSelected ? "> " : " " }),
|
|
5627
|
+
/* @__PURE__ */ jsx7("text", { fg: isSelected ? t.selection.fg : t.fg.secondary, width: 25, children: deployment.config.name }),
|
|
5628
|
+
/* @__PURE__ */ jsx7("text", { fg: t.accent, children: deployment.state.serverIp })
|
|
5100
5629
|
]
|
|
5101
5630
|
},
|
|
5102
5631
|
deployment.config.name
|
|
@@ -5104,24 +5633,24 @@ function SSHView({ context }) {
|
|
|
5104
5633
|
})
|
|
5105
5634
|
}
|
|
5106
5635
|
),
|
|
5107
|
-
/* @__PURE__ */
|
|
5108
|
-
/* @__PURE__ */
|
|
5109
|
-
/* @__PURE__ */
|
|
5110
|
-
/* @__PURE__ */
|
|
5636
|
+
/* @__PURE__ */ jsxs7("box", { flexDirection: "row", marginBottom: 1, children: [
|
|
5637
|
+
/* @__PURE__ */ jsx7("text", { fg: t.fg.secondary, children: "Terminal: " }),
|
|
5638
|
+
/* @__PURE__ */ jsx7("text", { fg: t.status.success, children: terminalDisplayName }),
|
|
5639
|
+
/* @__PURE__ */ jsx7("text", { fg: t.fg.secondary, children: " (will open in a new window)" })
|
|
5111
5640
|
] }),
|
|
5112
|
-
/* @__PURE__ */
|
|
5641
|
+
/* @__PURE__ */ jsx7("text", { fg: t.fg.muted, children: "Arrow keys to select | Enter to connect | Esc to go back" })
|
|
5113
5642
|
] });
|
|
5114
5643
|
}
|
|
5115
5644
|
if (viewState === "connected") {
|
|
5116
|
-
return /* @__PURE__ */
|
|
5117
|
-
/* @__PURE__ */
|
|
5118
|
-
/* @__PURE__ */
|
|
5119
|
-
/* @__PURE__ */
|
|
5645
|
+
return /* @__PURE__ */ jsxs7("box", { flexDirection: "column", width: "100%", padding: 1, children: [
|
|
5646
|
+
/* @__PURE__ */ jsxs7("box", { flexDirection: "row", marginBottom: 2, children: [
|
|
5647
|
+
/* @__PURE__ */ jsx7("text", { fg: t.status.success, children: "/ssh" }),
|
|
5648
|
+
/* @__PURE__ */ jsxs7("text", { fg: t.fg.secondary, children: [
|
|
5120
5649
|
" - Connected to ",
|
|
5121
5650
|
connectedDeployment
|
|
5122
5651
|
] })
|
|
5123
5652
|
] }),
|
|
5124
|
-
/* @__PURE__ */
|
|
5653
|
+
/* @__PURE__ */ jsxs7(
|
|
5125
5654
|
"box",
|
|
5126
5655
|
{
|
|
5127
5656
|
flexDirection: "column",
|
|
@@ -5130,8 +5659,8 @@ function SSHView({ context }) {
|
|
|
5130
5659
|
padding: 1,
|
|
5131
5660
|
marginBottom: 1,
|
|
5132
5661
|
children: [
|
|
5133
|
-
/* @__PURE__ */
|
|
5134
|
-
/* @__PURE__ */
|
|
5662
|
+
/* @__PURE__ */ jsx7("text", { fg: t.status.success, children: "SSH Session Opened" }),
|
|
5663
|
+
/* @__PURE__ */ jsxs7("text", { fg: t.fg.primary, marginTop: 1, children: [
|
|
5135
5664
|
"A new ",
|
|
5136
5665
|
terminalName,
|
|
5137
5666
|
" window/tab has been opened."
|
|
@@ -5139,7 +5668,7 @@ function SSHView({ context }) {
|
|
|
5139
5668
|
]
|
|
5140
5669
|
}
|
|
5141
5670
|
),
|
|
5142
|
-
/* @__PURE__ */
|
|
5671
|
+
/* @__PURE__ */ jsxs7(
|
|
5143
5672
|
"box",
|
|
5144
5673
|
{
|
|
5145
5674
|
flexDirection: "column",
|
|
@@ -5148,41 +5677,41 @@ function SSHView({ context }) {
|
|
|
5148
5677
|
padding: 1,
|
|
5149
5678
|
marginBottom: 1,
|
|
5150
5679
|
children: [
|
|
5151
|
-
/* @__PURE__ */
|
|
5680
|
+
/* @__PURE__ */ jsxs7("text", { fg: t.fg.primary, children: [
|
|
5152
5681
|
"Your SSH session is running in ",
|
|
5153
5682
|
terminalName,
|
|
5154
5683
|
"."
|
|
5155
5684
|
] }),
|
|
5156
|
-
/* @__PURE__ */
|
|
5685
|
+
/* @__PURE__ */ jsx7("text", { fg: t.fg.primary, marginTop: 1, children: "When you're done, type 'exit' or close the tab." })
|
|
5157
5686
|
]
|
|
5158
5687
|
}
|
|
5159
5688
|
),
|
|
5160
|
-
/* @__PURE__ */
|
|
5689
|
+
/* @__PURE__ */ jsx7("text", { fg: t.fg.muted, marginTop: 1, children: "Press Enter or Esc to return to ClawControl" })
|
|
5161
5690
|
] });
|
|
5162
5691
|
}
|
|
5163
5692
|
if (viewState === "error") {
|
|
5164
|
-
return /* @__PURE__ */
|
|
5165
|
-
/* @__PURE__ */
|
|
5166
|
-
/* @__PURE__ */
|
|
5693
|
+
return /* @__PURE__ */ jsxs7("box", { flexDirection: "column", width: "100%", padding: 1, children: [
|
|
5694
|
+
/* @__PURE__ */ jsx7("box", { flexDirection: "row", marginBottom: 2, children: /* @__PURE__ */ jsx7("text", { fg: t.status.error, children: "SSH Error" }) }),
|
|
5695
|
+
/* @__PURE__ */ jsx7(
|
|
5167
5696
|
"box",
|
|
5168
5697
|
{
|
|
5169
5698
|
flexDirection: "column",
|
|
5170
5699
|
borderStyle: "single",
|
|
5171
5700
|
borderColor: t.status.error,
|
|
5172
5701
|
padding: 1,
|
|
5173
|
-
children: /* @__PURE__ */
|
|
5702
|
+
children: /* @__PURE__ */ jsx7("text", { fg: t.status.error, children: error })
|
|
5174
5703
|
}
|
|
5175
5704
|
),
|
|
5176
|
-
/* @__PURE__ */
|
|
5705
|
+
/* @__PURE__ */ jsx7("text", { fg: t.fg.muted, marginTop: 2, children: "Press any key to return to home" })
|
|
5177
5706
|
] });
|
|
5178
5707
|
}
|
|
5179
5708
|
return null;
|
|
5180
5709
|
}
|
|
5181
5710
|
|
|
5182
5711
|
// src/components/LogsView.tsx
|
|
5183
|
-
import { useState as
|
|
5184
|
-
import { useKeyboard as
|
|
5185
|
-
import { Fragment, jsx as
|
|
5712
|
+
import { useState as useState8, useEffect as useEffect3, useRef as useRef4 } from "react";
|
|
5713
|
+
import { useKeyboard as useKeyboard7 } from "@opentui/react";
|
|
5714
|
+
import { Fragment as Fragment2, jsx as jsx8, jsxs as jsxs8 } from "@opentui/react/jsx-runtime";
|
|
5186
5715
|
function parseLogLine(line) {
|
|
5187
5716
|
if (!line.trim()) return null;
|
|
5188
5717
|
const match = line.match(/^(\w+\s+\d+\s+[\d:]+)\s+\S+\s+\S+:\s*(.*)$/);
|
|
@@ -5201,14 +5730,14 @@ function truncateLine(line, maxWidth = 120) {
|
|
|
5201
5730
|
return line.substring(0, maxWidth - 3) + "...";
|
|
5202
5731
|
}
|
|
5203
5732
|
function LogsView({ context }) {
|
|
5204
|
-
const [viewState, setViewState] =
|
|
5205
|
-
const [selectedIndex, setSelectedIndex] =
|
|
5206
|
-
const [logs, setLogs] =
|
|
5207
|
-
const [error, setError] =
|
|
5208
|
-
const [autoRefresh, setAutoRefresh] =
|
|
5209
|
-
const [lastFetched, setLastFetched] =
|
|
5210
|
-
const sshRef =
|
|
5211
|
-
const refreshIntervalRef =
|
|
5733
|
+
const [viewState, setViewState] = useState8("selecting");
|
|
5734
|
+
const [selectedIndex, setSelectedIndex] = useState8(0);
|
|
5735
|
+
const [logs, setLogs] = useState8([]);
|
|
5736
|
+
const [error, setError] = useState8(null);
|
|
5737
|
+
const [autoRefresh, setAutoRefresh] = useState8(false);
|
|
5738
|
+
const [lastFetched, setLastFetched] = useState8(null);
|
|
5739
|
+
const sshRef = useRef4(null);
|
|
5740
|
+
const refreshIntervalRef = useRef4(null);
|
|
5212
5741
|
const deployedDeployments = context.deployments.filter(
|
|
5213
5742
|
(d) => d.state.status === "deployed" && d.state.serverIp
|
|
5214
5743
|
);
|
|
@@ -5259,7 +5788,7 @@ function LogsView({ context }) {
|
|
|
5259
5788
|
setLogs([]);
|
|
5260
5789
|
};
|
|
5261
5790
|
const selectedDeployment = deployedDeployments[selectedIndex];
|
|
5262
|
-
|
|
5791
|
+
useKeyboard7((key) => {
|
|
5263
5792
|
if (deployedDeployments.length === 0) {
|
|
5264
5793
|
context.navigateTo("home");
|
|
5265
5794
|
return;
|
|
@@ -5292,12 +5821,12 @@ function LogsView({ context }) {
|
|
|
5292
5821
|
}
|
|
5293
5822
|
});
|
|
5294
5823
|
if (deployedDeployments.length === 0) {
|
|
5295
|
-
return /* @__PURE__ */
|
|
5296
|
-
/* @__PURE__ */
|
|
5297
|
-
/* @__PURE__ */
|
|
5298
|
-
/* @__PURE__ */
|
|
5824
|
+
return /* @__PURE__ */ jsxs8("box", { flexDirection: "column", width: "100%", padding: 1, children: [
|
|
5825
|
+
/* @__PURE__ */ jsxs8("box", { flexDirection: "row", marginBottom: 2, children: [
|
|
5826
|
+
/* @__PURE__ */ jsx8("text", { fg: t.accent, children: "/logs" }),
|
|
5827
|
+
/* @__PURE__ */ jsx8("text", { fg: t.fg.secondary, children: " - View deployment logs" })
|
|
5299
5828
|
] }),
|
|
5300
|
-
/* @__PURE__ */
|
|
5829
|
+
/* @__PURE__ */ jsxs8(
|
|
5301
5830
|
"box",
|
|
5302
5831
|
{
|
|
5303
5832
|
flexDirection: "column",
|
|
@@ -5305,21 +5834,21 @@ function LogsView({ context }) {
|
|
|
5305
5834
|
borderColor: t.border.default,
|
|
5306
5835
|
padding: 1,
|
|
5307
5836
|
children: [
|
|
5308
|
-
/* @__PURE__ */
|
|
5309
|
-
/* @__PURE__ */
|
|
5837
|
+
/* @__PURE__ */ jsx8("text", { fg: t.status.warning, children: "No deployed instances found!" }),
|
|
5838
|
+
/* @__PURE__ */ jsx8("text", { fg: t.fg.secondary, marginTop: 1, children: "Deploy an instance first with /deploy" })
|
|
5310
5839
|
]
|
|
5311
5840
|
}
|
|
5312
5841
|
),
|
|
5313
|
-
/* @__PURE__ */
|
|
5842
|
+
/* @__PURE__ */ jsx8("text", { fg: t.fg.muted, marginTop: 2, children: "Press any key to return to home" })
|
|
5314
5843
|
] });
|
|
5315
5844
|
}
|
|
5316
5845
|
if (viewState === "selecting") {
|
|
5317
|
-
return /* @__PURE__ */
|
|
5318
|
-
/* @__PURE__ */
|
|
5319
|
-
/* @__PURE__ */
|
|
5320
|
-
/* @__PURE__ */
|
|
5846
|
+
return /* @__PURE__ */ jsxs8("box", { flexDirection: "column", width: "100%", padding: 1, children: [
|
|
5847
|
+
/* @__PURE__ */ jsxs8("box", { flexDirection: "row", marginBottom: 2, children: [
|
|
5848
|
+
/* @__PURE__ */ jsx8("text", { fg: t.accent, children: "/logs" }),
|
|
5849
|
+
/* @__PURE__ */ jsx8("text", { fg: t.fg.secondary, children: " - Select a deployment" })
|
|
5321
5850
|
] }),
|
|
5322
|
-
/* @__PURE__ */
|
|
5851
|
+
/* @__PURE__ */ jsx8(
|
|
5323
5852
|
"box",
|
|
5324
5853
|
{
|
|
5325
5854
|
flexDirection: "column",
|
|
@@ -5328,15 +5857,15 @@ function LogsView({ context }) {
|
|
|
5328
5857
|
padding: 1,
|
|
5329
5858
|
children: deployedDeployments.map((deployment, index) => {
|
|
5330
5859
|
const isSelected = index === selectedIndex;
|
|
5331
|
-
return /* @__PURE__ */
|
|
5860
|
+
return /* @__PURE__ */ jsxs8(
|
|
5332
5861
|
"box",
|
|
5333
5862
|
{
|
|
5334
5863
|
flexDirection: "row",
|
|
5335
5864
|
backgroundColor: isSelected ? t.selection.bg : void 0,
|
|
5336
5865
|
children: [
|
|
5337
|
-
/* @__PURE__ */
|
|
5338
|
-
/* @__PURE__ */
|
|
5339
|
-
/* @__PURE__ */
|
|
5866
|
+
/* @__PURE__ */ jsx8("text", { fg: isSelected ? t.selection.fg : t.fg.secondary, children: isSelected ? "> " : " " }),
|
|
5867
|
+
/* @__PURE__ */ jsx8("text", { fg: isSelected ? t.selection.fg : t.fg.secondary, width: 25, children: deployment.config.name }),
|
|
5868
|
+
/* @__PURE__ */ jsx8("text", { fg: t.accent, children: deployment.state.serverIp })
|
|
5340
5869
|
]
|
|
5341
5870
|
},
|
|
5342
5871
|
deployment.config.name
|
|
@@ -5344,70 +5873,70 @@ function LogsView({ context }) {
|
|
|
5344
5873
|
})
|
|
5345
5874
|
}
|
|
5346
5875
|
),
|
|
5347
|
-
/* @__PURE__ */
|
|
5876
|
+
/* @__PURE__ */ jsx8("text", { fg: t.fg.muted, marginTop: 2, children: "Arrow keys to select | Enter to view logs | Esc to go back" })
|
|
5348
5877
|
] });
|
|
5349
5878
|
}
|
|
5350
5879
|
if (viewState === "loading") {
|
|
5351
|
-
return /* @__PURE__ */
|
|
5352
|
-
/* @__PURE__ */
|
|
5353
|
-
/* @__PURE__ */
|
|
5880
|
+
return /* @__PURE__ */ jsxs8("box", { flexDirection: "column", width: "100%", padding: 1, children: [
|
|
5881
|
+
/* @__PURE__ */ jsx8("text", { fg: t.accent, children: "Loading logs..." }),
|
|
5882
|
+
/* @__PURE__ */ jsx8("text", { fg: t.fg.secondary, marginTop: 1, children: "Fetching OpenClaw logs from server..." })
|
|
5354
5883
|
] });
|
|
5355
5884
|
}
|
|
5356
5885
|
if (viewState === "error") {
|
|
5357
|
-
return /* @__PURE__ */
|
|
5358
|
-
/* @__PURE__ */
|
|
5359
|
-
/* @__PURE__ */
|
|
5886
|
+
return /* @__PURE__ */ jsxs8("box", { flexDirection: "column", width: "100%", padding: 1, children: [
|
|
5887
|
+
/* @__PURE__ */ jsx8("box", { flexDirection: "row", marginBottom: 2, children: /* @__PURE__ */ jsx8("text", { fg: t.status.error, children: "Error Loading Logs" }) }),
|
|
5888
|
+
/* @__PURE__ */ jsx8(
|
|
5360
5889
|
"box",
|
|
5361
5890
|
{
|
|
5362
5891
|
flexDirection: "column",
|
|
5363
5892
|
borderStyle: "single",
|
|
5364
5893
|
borderColor: t.status.error,
|
|
5365
5894
|
padding: 1,
|
|
5366
|
-
children: /* @__PURE__ */
|
|
5895
|
+
children: /* @__PURE__ */ jsx8("text", { fg: t.status.error, children: error })
|
|
5367
5896
|
}
|
|
5368
5897
|
),
|
|
5369
|
-
/* @__PURE__ */
|
|
5898
|
+
/* @__PURE__ */ jsx8("text", { fg: t.fg.muted, marginTop: 2, children: "Press any key to go back" })
|
|
5370
5899
|
] });
|
|
5371
5900
|
}
|
|
5372
5901
|
const visibleLogs = logs;
|
|
5373
|
-
return /* @__PURE__ */
|
|
5374
|
-
/* @__PURE__ */
|
|
5375
|
-
/* @__PURE__ */
|
|
5902
|
+
return /* @__PURE__ */ jsxs8("box", { flexDirection: "column", width: "100%", padding: 1, children: [
|
|
5903
|
+
/* @__PURE__ */ jsxs8("box", { flexDirection: "row", marginBottom: 1, children: [
|
|
5904
|
+
/* @__PURE__ */ jsxs8("text", { fg: t.accent, children: [
|
|
5376
5905
|
"Logs: ",
|
|
5377
5906
|
selectedDeployment.config.name
|
|
5378
5907
|
] }),
|
|
5379
|
-
/* @__PURE__ */
|
|
5380
|
-
/* @__PURE__ */
|
|
5908
|
+
/* @__PURE__ */ jsx8("text", { fg: t.fg.muted, children: " | " }),
|
|
5909
|
+
/* @__PURE__ */ jsxs8("text", { fg: autoRefresh ? t.status.success : t.fg.muted, children: [
|
|
5381
5910
|
"Auto: ",
|
|
5382
5911
|
autoRefresh ? "ON (5s)" : "OFF"
|
|
5383
5912
|
] }),
|
|
5384
|
-
lastFetched && /* @__PURE__ */
|
|
5385
|
-
/* @__PURE__ */
|
|
5386
|
-
/* @__PURE__ */
|
|
5913
|
+
lastFetched && /* @__PURE__ */ jsxs8(Fragment2, { children: [
|
|
5914
|
+
/* @__PURE__ */ jsx8("text", { fg: t.fg.muted, children: " | " }),
|
|
5915
|
+
/* @__PURE__ */ jsxs8("text", { fg: t.fg.muted, children: [
|
|
5387
5916
|
"Fetched: ",
|
|
5388
5917
|
lastFetched.toLocaleTimeString()
|
|
5389
5918
|
] })
|
|
5390
5919
|
] })
|
|
5391
5920
|
] }),
|
|
5392
|
-
/* @__PURE__ */
|
|
5921
|
+
/* @__PURE__ */ jsx8(
|
|
5393
5922
|
"box",
|
|
5394
5923
|
{
|
|
5395
5924
|
flexDirection: "column",
|
|
5396
5925
|
borderStyle: "single",
|
|
5397
5926
|
borderColor: t.border.default,
|
|
5398
5927
|
padding: 1,
|
|
5399
|
-
children: /* @__PURE__ */
|
|
5928
|
+
children: /* @__PURE__ */ jsx8("box", { flexDirection: "column", children: visibleLogs.map((line, i) => {
|
|
5400
5929
|
const parsed = parseLogLine(line);
|
|
5401
5930
|
if (!parsed) return null;
|
|
5402
5931
|
const displayLine = parsed.timestamp ? `${parsed.timestamp} ${truncateLine(parsed.message, 100)}` : truncateLine(parsed.message, 120);
|
|
5403
|
-
return /* @__PURE__ */
|
|
5932
|
+
return /* @__PURE__ */ jsx8("text", { fg: logLevelColor(parsed.level), children: displayLine }, i);
|
|
5404
5933
|
}) })
|
|
5405
5934
|
}
|
|
5406
5935
|
),
|
|
5407
|
-
/* @__PURE__ */
|
|
5408
|
-
/* @__PURE__ */
|
|
5409
|
-
/* @__PURE__ */
|
|
5410
|
-
/* @__PURE__ */
|
|
5936
|
+
/* @__PURE__ */ jsxs8("box", { flexDirection: "row", marginTop: 1, children: [
|
|
5937
|
+
/* @__PURE__ */ jsx8("text", { fg: t.fg.muted, children: "R: Refresh | A: Toggle auto-refresh | Esc: Back" }),
|
|
5938
|
+
/* @__PURE__ */ jsx8("text", { fg: t.fg.muted, children: " | " }),
|
|
5939
|
+
/* @__PURE__ */ jsxs8("text", { fg: t.accent, children: [
|
|
5411
5940
|
"Showing last ",
|
|
5412
5941
|
visibleLogs.length,
|
|
5413
5942
|
" lines"
|
|
@@ -5417,14 +5946,14 @@ function LogsView({ context }) {
|
|
|
5417
5946
|
}
|
|
5418
5947
|
|
|
5419
5948
|
// src/components/DestroyView.tsx
|
|
5420
|
-
import { useState as
|
|
5421
|
-
import { useKeyboard as
|
|
5422
|
-
import { jsx as
|
|
5949
|
+
import { useState as useState9 } from "react";
|
|
5950
|
+
import { useKeyboard as useKeyboard8 } from "@opentui/react";
|
|
5951
|
+
import { jsx as jsx9, jsxs as jsxs9 } from "@opentui/react/jsx-runtime";
|
|
5423
5952
|
function DestroyView({ context }) {
|
|
5424
|
-
const [viewState, setViewState] =
|
|
5425
|
-
const [selectedIndex, setSelectedIndex] =
|
|
5426
|
-
const [error, setError] =
|
|
5427
|
-
const [confirmText, setConfirmText] =
|
|
5953
|
+
const [viewState, setViewState] = useState9("selecting");
|
|
5954
|
+
const [selectedIndex, setSelectedIndex] = useState9(0);
|
|
5955
|
+
const [error, setError] = useState9(null);
|
|
5956
|
+
const [confirmText, setConfirmText] = useState9("");
|
|
5428
5957
|
const deployments = context.deployments;
|
|
5429
5958
|
const destroyDeployment = async (name) => {
|
|
5430
5959
|
setViewState("destroying");
|
|
@@ -5469,7 +5998,7 @@ function DestroyView({ context }) {
|
|
|
5469
5998
|
}
|
|
5470
5999
|
};
|
|
5471
6000
|
const selectedDeployment = deployments[selectedIndex];
|
|
5472
|
-
|
|
6001
|
+
useKeyboard8((key) => {
|
|
5473
6002
|
if (deployments.length === 0) {
|
|
5474
6003
|
context.navigateTo("home");
|
|
5475
6004
|
return;
|
|
@@ -5494,31 +6023,31 @@ function DestroyView({ context }) {
|
|
|
5494
6023
|
}
|
|
5495
6024
|
});
|
|
5496
6025
|
if (deployments.length === 0) {
|
|
5497
|
-
return /* @__PURE__ */
|
|
5498
|
-
/* @__PURE__ */
|
|
5499
|
-
/* @__PURE__ */
|
|
5500
|
-
/* @__PURE__ */
|
|
6026
|
+
return /* @__PURE__ */ jsxs9("box", { flexDirection: "column", width: "100%", padding: 1, children: [
|
|
6027
|
+
/* @__PURE__ */ jsxs9("box", { flexDirection: "row", marginBottom: 2, children: [
|
|
6028
|
+
/* @__PURE__ */ jsx9("text", { fg: t.status.error, children: "/destroy" }),
|
|
6029
|
+
/* @__PURE__ */ jsx9("text", { fg: t.fg.secondary, children: " - Destroy deployment" })
|
|
5501
6030
|
] }),
|
|
5502
|
-
/* @__PURE__ */
|
|
6031
|
+
/* @__PURE__ */ jsx9(
|
|
5503
6032
|
"box",
|
|
5504
6033
|
{
|
|
5505
6034
|
flexDirection: "column",
|
|
5506
6035
|
borderStyle: "single",
|
|
5507
6036
|
borderColor: t.border.default,
|
|
5508
6037
|
padding: 1,
|
|
5509
|
-
children: /* @__PURE__ */
|
|
6038
|
+
children: /* @__PURE__ */ jsx9("text", { fg: t.status.warning, children: "No deployments found!" })
|
|
5510
6039
|
}
|
|
5511
6040
|
),
|
|
5512
|
-
/* @__PURE__ */
|
|
6041
|
+
/* @__PURE__ */ jsx9("text", { fg: t.fg.muted, marginTop: 2, children: "Press any key to return to home" })
|
|
5513
6042
|
] });
|
|
5514
6043
|
}
|
|
5515
6044
|
if (viewState === "selecting") {
|
|
5516
|
-
return /* @__PURE__ */
|
|
5517
|
-
/* @__PURE__ */
|
|
5518
|
-
/* @__PURE__ */
|
|
5519
|
-
/* @__PURE__ */
|
|
6045
|
+
return /* @__PURE__ */ jsxs9("box", { flexDirection: "column", width: "100%", padding: 1, children: [
|
|
6046
|
+
/* @__PURE__ */ jsxs9("box", { flexDirection: "row", marginBottom: 2, children: [
|
|
6047
|
+
/* @__PURE__ */ jsx9("text", { fg: t.status.error, children: "/destroy" }),
|
|
6048
|
+
/* @__PURE__ */ jsx9("text", { fg: t.fg.secondary, children: " - Select a deployment to destroy" })
|
|
5520
6049
|
] }),
|
|
5521
|
-
/* @__PURE__ */
|
|
6050
|
+
/* @__PURE__ */ jsxs9(
|
|
5522
6051
|
"box",
|
|
5523
6052
|
{
|
|
5524
6053
|
flexDirection: "column",
|
|
@@ -5526,18 +6055,18 @@ function DestroyView({ context }) {
|
|
|
5526
6055
|
borderColor: t.status.error,
|
|
5527
6056
|
padding: 1,
|
|
5528
6057
|
children: [
|
|
5529
|
-
/* @__PURE__ */
|
|
6058
|
+
/* @__PURE__ */ jsx9("text", { fg: t.status.error, marginBottom: 1, children: "WARNING: This action cannot be undone!" }),
|
|
5530
6059
|
deployments.map((deployment, index) => {
|
|
5531
6060
|
const isSelected = index === selectedIndex;
|
|
5532
|
-
return /* @__PURE__ */
|
|
6061
|
+
return /* @__PURE__ */ jsxs9(
|
|
5533
6062
|
"box",
|
|
5534
6063
|
{
|
|
5535
6064
|
flexDirection: "row",
|
|
5536
6065
|
backgroundColor: isSelected ? t.selection.bg : void 0,
|
|
5537
6066
|
children: [
|
|
5538
|
-
/* @__PURE__ */
|
|
5539
|
-
/* @__PURE__ */
|
|
5540
|
-
/* @__PURE__ */
|
|
6067
|
+
/* @__PURE__ */ jsx9("text", { fg: isSelected ? t.selection.fg : t.fg.secondary, children: isSelected ? "> " : " " }),
|
|
6068
|
+
/* @__PURE__ */ jsx9("text", { fg: isSelected ? t.selection.fg : t.fg.secondary, width: 25, children: deployment.config.name }),
|
|
6069
|
+
/* @__PURE__ */ jsxs9("text", { fg: isSelected ? t.selection.fg : t.fg.secondary, children: [
|
|
5541
6070
|
"[",
|
|
5542
6071
|
deployment.state.status,
|
|
5543
6072
|
"]"
|
|
@@ -5550,13 +6079,13 @@ function DestroyView({ context }) {
|
|
|
5550
6079
|
]
|
|
5551
6080
|
}
|
|
5552
6081
|
),
|
|
5553
|
-
/* @__PURE__ */
|
|
6082
|
+
/* @__PURE__ */ jsx9("text", { fg: t.fg.muted, marginTop: 2, children: "Arrow keys to select | Enter to destroy | Esc to go back" })
|
|
5554
6083
|
] });
|
|
5555
6084
|
}
|
|
5556
6085
|
if (viewState === "confirming") {
|
|
5557
|
-
return /* @__PURE__ */
|
|
5558
|
-
/* @__PURE__ */
|
|
5559
|
-
/* @__PURE__ */
|
|
6086
|
+
return /* @__PURE__ */ jsxs9("box", { flexDirection: "column", width: "100%", padding: 1, children: [
|
|
6087
|
+
/* @__PURE__ */ jsx9("box", { flexDirection: "row", marginBottom: 2, children: /* @__PURE__ */ jsx9("text", { fg: t.status.error, children: "Confirm Destruction" }) }),
|
|
6088
|
+
/* @__PURE__ */ jsxs9(
|
|
5560
6089
|
"box",
|
|
5561
6090
|
{
|
|
5562
6091
|
flexDirection: "column",
|
|
@@ -5564,26 +6093,26 @@ function DestroyView({ context }) {
|
|
|
5564
6093
|
borderColor: t.status.error,
|
|
5565
6094
|
padding: 1,
|
|
5566
6095
|
children: [
|
|
5567
|
-
/* @__PURE__ */
|
|
5568
|
-
/* @__PURE__ */
|
|
6096
|
+
/* @__PURE__ */ jsx9("text", { fg: t.status.error, children: "You are about to destroy:" }),
|
|
6097
|
+
/* @__PURE__ */ jsxs9("text", { fg: t.fg.primary, marginTop: 1, children: [
|
|
5569
6098
|
"Deployment: ",
|
|
5570
6099
|
selectedDeployment.config.name
|
|
5571
6100
|
] }),
|
|
5572
|
-
selectedDeployment.state.serverIp && /* @__PURE__ */
|
|
6101
|
+
selectedDeployment.state.serverIp && /* @__PURE__ */ jsxs9("text", { fg: t.fg.primary, children: [
|
|
5573
6102
|
"Server IP: ",
|
|
5574
6103
|
selectedDeployment.state.serverIp
|
|
5575
6104
|
] }),
|
|
5576
|
-
/* @__PURE__ */
|
|
5577
|
-
/* @__PURE__ */
|
|
5578
|
-
/* @__PURE__ */
|
|
5579
|
-
/* @__PURE__ */
|
|
5580
|
-
/* @__PURE__ */
|
|
6105
|
+
/* @__PURE__ */ jsx9("text", { fg: t.status.error, marginTop: 1, children: "This will permanently delete:" }),
|
|
6106
|
+
/* @__PURE__ */ jsx9("text", { fg: t.fg.secondary, children: "\u2022 The VPS server (if deployed)" }),
|
|
6107
|
+
/* @__PURE__ */ jsx9("text", { fg: t.fg.secondary, children: "\u2022 All data on the server" }),
|
|
6108
|
+
/* @__PURE__ */ jsx9("text", { fg: t.fg.secondary, children: "\u2022 Local configuration files" }),
|
|
6109
|
+
/* @__PURE__ */ jsx9("text", { fg: t.fg.secondary, children: "\u2022 SSH keys" })
|
|
5581
6110
|
]
|
|
5582
6111
|
}
|
|
5583
6112
|
),
|
|
5584
|
-
/* @__PURE__ */
|
|
5585
|
-
/* @__PURE__ */
|
|
5586
|
-
/* @__PURE__ */
|
|
6113
|
+
/* @__PURE__ */ jsx9("text", { fg: t.status.warning, marginTop: 2, children: "Type the deployment name to confirm:" }),
|
|
6114
|
+
/* @__PURE__ */ jsx9("text", { fg: t.fg.primary, marginTop: 1, children: "Confirm:" }),
|
|
6115
|
+
/* @__PURE__ */ jsx9(
|
|
5587
6116
|
"input",
|
|
5588
6117
|
{
|
|
5589
6118
|
value: confirmText,
|
|
@@ -5606,19 +6135,19 @@ function DestroyView({ context }) {
|
|
|
5606
6135
|
}
|
|
5607
6136
|
}
|
|
5608
6137
|
),
|
|
5609
|
-
error && /* @__PURE__ */
|
|
5610
|
-
/* @__PURE__ */
|
|
6138
|
+
error && /* @__PURE__ */ jsx9("text", { fg: t.status.error, marginTop: 1, children: error }),
|
|
6139
|
+
/* @__PURE__ */ jsx9("text", { fg: t.fg.muted, marginTop: 2, children: "Press Esc to cancel" })
|
|
5611
6140
|
] });
|
|
5612
6141
|
}
|
|
5613
6142
|
if (viewState === "destroying") {
|
|
5614
|
-
return /* @__PURE__ */
|
|
5615
|
-
/* @__PURE__ */
|
|
5616
|
-
/* @__PURE__ */
|
|
6143
|
+
return /* @__PURE__ */ jsxs9("box", { flexDirection: "column", width: "100%", padding: 1, children: [
|
|
6144
|
+
/* @__PURE__ */ jsx9("text", { fg: t.status.error, children: "Destroying deployment..." }),
|
|
6145
|
+
/* @__PURE__ */ jsx9("text", { fg: t.fg.secondary, marginTop: 1, children: "Deleting server and cleaning up resources..." })
|
|
5617
6146
|
] });
|
|
5618
6147
|
}
|
|
5619
6148
|
if (viewState === "success") {
|
|
5620
|
-
return /* @__PURE__ */
|
|
5621
|
-
/* @__PURE__ */
|
|
6149
|
+
return /* @__PURE__ */ jsxs9("box", { flexDirection: "column", width: "100%", padding: 1, children: [
|
|
6150
|
+
/* @__PURE__ */ jsxs9(
|
|
5622
6151
|
"box",
|
|
5623
6152
|
{
|
|
5624
6153
|
flexDirection: "column",
|
|
@@ -5626,8 +6155,8 @@ function DestroyView({ context }) {
|
|
|
5626
6155
|
borderColor: t.status.success,
|
|
5627
6156
|
padding: 1,
|
|
5628
6157
|
children: [
|
|
5629
|
-
/* @__PURE__ */
|
|
5630
|
-
/* @__PURE__ */
|
|
6158
|
+
/* @__PURE__ */ jsx9("text", { fg: t.status.success, children: "Deployment Destroyed" }),
|
|
6159
|
+
/* @__PURE__ */ jsxs9("text", { fg: t.fg.primary, marginTop: 1, children: [
|
|
5631
6160
|
'The deployment "',
|
|
5632
6161
|
selectedDeployment.config.name,
|
|
5633
6162
|
'" has been permanently deleted.'
|
|
@@ -5635,12 +6164,12 @@ function DestroyView({ context }) {
|
|
|
5635
6164
|
]
|
|
5636
6165
|
}
|
|
5637
6166
|
),
|
|
5638
|
-
/* @__PURE__ */
|
|
6167
|
+
/* @__PURE__ */ jsx9("text", { fg: t.fg.muted, marginTop: 2, children: "Press any key to return to home" })
|
|
5639
6168
|
] });
|
|
5640
6169
|
}
|
|
5641
6170
|
if (viewState === "error") {
|
|
5642
|
-
return /* @__PURE__ */
|
|
5643
|
-
/* @__PURE__ */
|
|
6171
|
+
return /* @__PURE__ */ jsxs9("box", { flexDirection: "column", width: "100%", padding: 1, children: [
|
|
6172
|
+
/* @__PURE__ */ jsxs9(
|
|
5644
6173
|
"box",
|
|
5645
6174
|
{
|
|
5646
6175
|
flexDirection: "column",
|
|
@@ -5648,30 +6177,30 @@ function DestroyView({ context }) {
|
|
|
5648
6177
|
borderColor: t.status.error,
|
|
5649
6178
|
padding: 1,
|
|
5650
6179
|
children: [
|
|
5651
|
-
/* @__PURE__ */
|
|
5652
|
-
/* @__PURE__ */
|
|
6180
|
+
/* @__PURE__ */ jsx9("text", { fg: t.status.error, children: "Destruction Failed" }),
|
|
6181
|
+
/* @__PURE__ */ jsx9("text", { fg: t.fg.primary, marginTop: 1, children: error })
|
|
5653
6182
|
]
|
|
5654
6183
|
}
|
|
5655
6184
|
),
|
|
5656
|
-
/* @__PURE__ */
|
|
6185
|
+
/* @__PURE__ */ jsx9("text", { fg: t.fg.muted, marginTop: 2, children: "Press any key to go back" })
|
|
5657
6186
|
] });
|
|
5658
6187
|
}
|
|
5659
6188
|
return null;
|
|
5660
6189
|
}
|
|
5661
6190
|
|
|
5662
6191
|
// src/components/HelpView.tsx
|
|
5663
|
-
import { useKeyboard as
|
|
5664
|
-
import { jsx as
|
|
6192
|
+
import { useKeyboard as useKeyboard9 } from "@opentui/react";
|
|
6193
|
+
import { jsx as jsx10, jsxs as jsxs10 } from "@opentui/react/jsx-runtime";
|
|
5665
6194
|
function HelpView({ context }) {
|
|
5666
|
-
|
|
6195
|
+
useKeyboard9(() => {
|
|
5667
6196
|
context.navigateTo("home");
|
|
5668
6197
|
});
|
|
5669
|
-
return /* @__PURE__ */
|
|
5670
|
-
/* @__PURE__ */
|
|
5671
|
-
/* @__PURE__ */
|
|
5672
|
-
/* @__PURE__ */
|
|
6198
|
+
return /* @__PURE__ */ jsxs10("box", { flexDirection: "column", width: "100%", padding: 1, children: [
|
|
6199
|
+
/* @__PURE__ */ jsxs10("box", { flexDirection: "row", marginBottom: 2, children: [
|
|
6200
|
+
/* @__PURE__ */ jsx10("text", { fg: t.accent, children: "/help" }),
|
|
6201
|
+
/* @__PURE__ */ jsx10("text", { fg: t.fg.secondary, children: " - ClawControl Help" })
|
|
5673
6202
|
] }),
|
|
5674
|
-
/* @__PURE__ */
|
|
6203
|
+
/* @__PURE__ */ jsxs10(
|
|
5675
6204
|
"box",
|
|
5676
6205
|
{
|
|
5677
6206
|
flexDirection: "column",
|
|
@@ -5680,12 +6209,12 @@ function HelpView({ context }) {
|
|
|
5680
6209
|
padding: 1,
|
|
5681
6210
|
marginBottom: 1,
|
|
5682
6211
|
children: [
|
|
5683
|
-
/* @__PURE__ */
|
|
5684
|
-
/* @__PURE__ */
|
|
6212
|
+
/* @__PURE__ */ jsx10("text", { fg: t.accent, children: "What is ClawControl?" }),
|
|
6213
|
+
/* @__PURE__ */ jsx10("text", { fg: t.fg.primary, marginTop: 1, children: "ClawControl is a CLI tool that simplifies deploying OpenClaw instances to cloud providers. It handles all the complex setup including VPS provisioning, Node.js installation, OpenClaw configuration, and Tailscale VPN setup." })
|
|
5685
6214
|
]
|
|
5686
6215
|
}
|
|
5687
6216
|
),
|
|
5688
|
-
/* @__PURE__ */
|
|
6217
|
+
/* @__PURE__ */ jsxs10(
|
|
5689
6218
|
"box",
|
|
5690
6219
|
{
|
|
5691
6220
|
flexDirection: "column",
|
|
@@ -5694,48 +6223,48 @@ function HelpView({ context }) {
|
|
|
5694
6223
|
padding: 1,
|
|
5695
6224
|
marginBottom: 1,
|
|
5696
6225
|
children: [
|
|
5697
|
-
/* @__PURE__ */
|
|
5698
|
-
/* @__PURE__ */
|
|
5699
|
-
/* @__PURE__ */
|
|
5700
|
-
/* @__PURE__ */
|
|
5701
|
-
/* @__PURE__ */
|
|
6226
|
+
/* @__PURE__ */ jsx10("text", { fg: t.fg.primary, children: "Available Commands" }),
|
|
6227
|
+
/* @__PURE__ */ jsxs10("box", { flexDirection: "column", marginTop: 1, children: [
|
|
6228
|
+
/* @__PURE__ */ jsxs10("box", { flexDirection: "row", children: [
|
|
6229
|
+
/* @__PURE__ */ jsx10("text", { fg: t.accent, width: 12, children: "/new" }),
|
|
6230
|
+
/* @__PURE__ */ jsx10("text", { fg: t.fg.primary, children: "Initialize a new deployment configuration" })
|
|
5702
6231
|
] }),
|
|
5703
|
-
/* @__PURE__ */
|
|
5704
|
-
/* @__PURE__ */
|
|
5705
|
-
/* @__PURE__ */
|
|
5706
|
-
/* @__PURE__ */
|
|
6232
|
+
/* @__PURE__ */ jsx10("text", { fg: t.fg.secondary, children: " Creates deployment config in ~/.clawcontrol/deployments/" }),
|
|
6233
|
+
/* @__PURE__ */ jsxs10("box", { flexDirection: "row", marginTop: 1, children: [
|
|
6234
|
+
/* @__PURE__ */ jsx10("text", { fg: t.accent, width: 12, children: "/deploy" }),
|
|
6235
|
+
/* @__PURE__ */ jsx10("text", { fg: t.fg.primary, children: "Deploy a configured instance to the cloud" })
|
|
5707
6236
|
] }),
|
|
5708
|
-
/* @__PURE__ */
|
|
5709
|
-
/* @__PURE__ */
|
|
5710
|
-
/* @__PURE__ */
|
|
5711
|
-
/* @__PURE__ */
|
|
6237
|
+
/* @__PURE__ */ jsx10("text", { fg: t.fg.secondary, children: " Provisions VPS, installs dependencies, configures OpenClaw" }),
|
|
6238
|
+
/* @__PURE__ */ jsxs10("box", { flexDirection: "row", marginTop: 1, children: [
|
|
6239
|
+
/* @__PURE__ */ jsx10("text", { fg: t.accent, width: 12, children: "/status" }),
|
|
6240
|
+
/* @__PURE__ */ jsx10("text", { fg: t.fg.primary, children: "View status of all deployments" })
|
|
5712
6241
|
] }),
|
|
5713
|
-
/* @__PURE__ */
|
|
5714
|
-
/* @__PURE__ */
|
|
5715
|
-
/* @__PURE__ */
|
|
5716
|
-
/* @__PURE__ */
|
|
6242
|
+
/* @__PURE__ */ jsx10("text", { fg: t.fg.secondary, children: " Shows deployment state, health checks, and connection info" }),
|
|
6243
|
+
/* @__PURE__ */ jsxs10("box", { flexDirection: "row", marginTop: 1, children: [
|
|
6244
|
+
/* @__PURE__ */ jsx10("text", { fg: t.accent, width: 12, children: "/ssh" }),
|
|
6245
|
+
/* @__PURE__ */ jsx10("text", { fg: t.fg.primary, children: "SSH into a deployed instance" })
|
|
5717
6246
|
] }),
|
|
5718
|
-
/* @__PURE__ */
|
|
5719
|
-
/* @__PURE__ */
|
|
5720
|
-
/* @__PURE__ */
|
|
5721
|
-
/* @__PURE__ */
|
|
6247
|
+
/* @__PURE__ */ jsx10("text", { fg: t.fg.secondary, children: " Opens interactive SSH session to your server" }),
|
|
6248
|
+
/* @__PURE__ */ jsxs10("box", { flexDirection: "row", marginTop: 1, children: [
|
|
6249
|
+
/* @__PURE__ */ jsx10("text", { fg: t.accent, width: 12, children: "/logs" }),
|
|
6250
|
+
/* @__PURE__ */ jsx10("text", { fg: t.fg.primary, children: "View OpenClaw logs from a deployment" })
|
|
5722
6251
|
] }),
|
|
5723
|
-
/* @__PURE__ */
|
|
5724
|
-
/* @__PURE__ */
|
|
5725
|
-
/* @__PURE__ */
|
|
5726
|
-
/* @__PURE__ */
|
|
6252
|
+
/* @__PURE__ */ jsx10("text", { fg: t.fg.secondary, children: " Streams logs from journalctl with auto-refresh option" }),
|
|
6253
|
+
/* @__PURE__ */ jsxs10("box", { flexDirection: "row", marginTop: 1, children: [
|
|
6254
|
+
/* @__PURE__ */ jsx10("text", { fg: t.status.error, width: 12, children: "/destroy" }),
|
|
6255
|
+
/* @__PURE__ */ jsx10("text", { fg: t.fg.primary, children: "Permanently delete a deployment" })
|
|
5727
6256
|
] }),
|
|
5728
|
-
/* @__PURE__ */
|
|
5729
|
-
/* @__PURE__ */
|
|
5730
|
-
/* @__PURE__ */
|
|
5731
|
-
/* @__PURE__ */
|
|
6257
|
+
/* @__PURE__ */ jsx10("text", { fg: t.fg.secondary, children: " Deletes VPS, SSH keys, and local configuration" }),
|
|
6258
|
+
/* @__PURE__ */ jsxs10("box", { flexDirection: "row", marginTop: 1, children: [
|
|
6259
|
+
/* @__PURE__ */ jsx10("text", { fg: t.accent, width: 12, children: "/templates" }),
|
|
6260
|
+
/* @__PURE__ */ jsx10("text", { fg: t.fg.primary, children: "Manage deployment templates" })
|
|
5732
6261
|
] }),
|
|
5733
|
-
/* @__PURE__ */
|
|
6262
|
+
/* @__PURE__ */ jsx10("text", { fg: t.fg.secondary, children: " View, fork, and use reusable deployment presets" })
|
|
5734
6263
|
] })
|
|
5735
6264
|
]
|
|
5736
6265
|
}
|
|
5737
6266
|
),
|
|
5738
|
-
/* @__PURE__ */
|
|
6267
|
+
/* @__PURE__ */ jsxs10(
|
|
5739
6268
|
"box",
|
|
5740
6269
|
{
|
|
5741
6270
|
flexDirection: "column",
|
|
@@ -5744,17 +6273,17 @@ function HelpView({ context }) {
|
|
|
5744
6273
|
padding: 1,
|
|
5745
6274
|
marginBottom: 1,
|
|
5746
6275
|
children: [
|
|
5747
|
-
/* @__PURE__ */
|
|
5748
|
-
/* @__PURE__ */
|
|
5749
|
-
/* @__PURE__ */
|
|
5750
|
-
/* @__PURE__ */
|
|
5751
|
-
/* @__PURE__ */
|
|
5752
|
-
/* @__PURE__ */
|
|
5753
|
-
/* @__PURE__ */
|
|
6276
|
+
/* @__PURE__ */ jsx10("text", { fg: t.status.success, children: "Typical Workflow" }),
|
|
6277
|
+
/* @__PURE__ */ jsx10("text", { fg: t.fg.primary, marginTop: 1, children: "1. Run /templates to browse or create reusable presets" }),
|
|
6278
|
+
/* @__PURE__ */ jsx10("text", { fg: t.fg.primary, children: "2. Run /new to create a deployment config (optionally from a template)" }),
|
|
6279
|
+
/* @__PURE__ */ jsx10("text", { fg: t.fg.primary, children: "3. Run /deploy to deploy to the cloud" }),
|
|
6280
|
+
/* @__PURE__ */ jsx10("text", { fg: t.fg.primary, children: "4. Authenticate Tailscale when prompted" }),
|
|
6281
|
+
/* @__PURE__ */ jsx10("text", { fg: t.fg.primary, children: "5. Complete OpenClaw onboarding via SSH" }),
|
|
6282
|
+
/* @__PURE__ */ jsx10("text", { fg: t.fg.primary, children: "6. Use /status and /logs to monitor" })
|
|
5754
6283
|
]
|
|
5755
6284
|
}
|
|
5756
6285
|
),
|
|
5757
|
-
/* @__PURE__ */
|
|
6286
|
+
/* @__PURE__ */ jsxs10(
|
|
5758
6287
|
"box",
|
|
5759
6288
|
{
|
|
5760
6289
|
flexDirection: "column",
|
|
@@ -5763,36 +6292,36 @@ function HelpView({ context }) {
|
|
|
5763
6292
|
padding: 1,
|
|
5764
6293
|
marginBottom: 1,
|
|
5765
6294
|
children: [
|
|
5766
|
-
/* @__PURE__ */
|
|
5767
|
-
/* @__PURE__ */
|
|
5768
|
-
/* @__PURE__ */
|
|
5769
|
-
/* @__PURE__ */
|
|
6295
|
+
/* @__PURE__ */ jsx10("text", { fg: t.fg.primary, children: "Supported Cloud Providers" }),
|
|
6296
|
+
/* @__PURE__ */ jsxs10("box", { flexDirection: "row", marginTop: 1, children: [
|
|
6297
|
+
/* @__PURE__ */ jsx10("text", { fg: t.status.success, children: "\u2713 " }),
|
|
6298
|
+
/* @__PURE__ */ jsx10("text", { fg: t.fg.primary, children: "Hetzner Cloud - ~$4.99/mo for CPX11 (US East)" })
|
|
5770
6299
|
] }),
|
|
5771
|
-
/* @__PURE__ */
|
|
5772
|
-
/* @__PURE__ */
|
|
5773
|
-
/* @__PURE__ */
|
|
6300
|
+
/* @__PURE__ */ jsxs10("box", { flexDirection: "row", children: [
|
|
6301
|
+
/* @__PURE__ */ jsx10("text", { fg: t.status.success, children: "\u2713 " }),
|
|
6302
|
+
/* @__PURE__ */ jsx10("text", { fg: t.fg.primary, children: "DigitalOcean - Starting at $12/mo (NYC1)" })
|
|
5774
6303
|
] }),
|
|
5775
|
-
/* @__PURE__ */
|
|
5776
|
-
/* @__PURE__ */
|
|
5777
|
-
/* @__PURE__ */
|
|
6304
|
+
/* @__PURE__ */ jsxs10("box", { flexDirection: "row", children: [
|
|
6305
|
+
/* @__PURE__ */ jsx10("text", { fg: t.fg.muted, children: "\u25CB " }),
|
|
6306
|
+
/* @__PURE__ */ jsx10("text", { fg: t.fg.secondary, children: "Vultr - Coming soon" })
|
|
5778
6307
|
] })
|
|
5779
6308
|
]
|
|
5780
6309
|
}
|
|
5781
6310
|
),
|
|
5782
|
-
/* @__PURE__ */
|
|
5783
|
-
/* @__PURE__ */
|
|
5784
|
-
/* @__PURE__ */
|
|
5785
|
-
/* @__PURE__ */
|
|
5786
|
-
/* @__PURE__ */
|
|
6311
|
+
/* @__PURE__ */ jsxs10("box", { flexDirection: "column", marginBottom: 1, children: [
|
|
6312
|
+
/* @__PURE__ */ jsx10("text", { fg: t.fg.primary, children: "Useful Links" }),
|
|
6313
|
+
/* @__PURE__ */ jsx10("text", { fg: t.fg.secondary, children: "\u2022 OpenClaw Docs: https://docs.openclaw.ai/" }),
|
|
6314
|
+
/* @__PURE__ */ jsx10("text", { fg: t.fg.secondary, children: "\u2022 Hetzner API: https://docs.hetzner.cloud/" }),
|
|
6315
|
+
/* @__PURE__ */ jsx10("text", { fg: t.fg.secondary, children: "\u2022 Tailscale: https://tailscale.com/" })
|
|
5787
6316
|
] }),
|
|
5788
|
-
/* @__PURE__ */
|
|
6317
|
+
/* @__PURE__ */ jsx10("text", { fg: t.fg.muted, children: "Press any key to return to home" })
|
|
5789
6318
|
] });
|
|
5790
6319
|
}
|
|
5791
6320
|
|
|
5792
6321
|
// src/components/TemplatesView.tsx
|
|
5793
|
-
import { useState as
|
|
5794
|
-
import { useKeyboard as
|
|
5795
|
-
import { Fragment as
|
|
6322
|
+
import { useState as useState10, useRef as useRef5, useEffect as useEffect4 } from "react";
|
|
6323
|
+
import { useKeyboard as useKeyboard10 } from "@opentui/react";
|
|
6324
|
+
import { Fragment as Fragment3, jsx as jsx11, jsxs as jsxs11 } from "@opentui/react/jsx-runtime";
|
|
5796
6325
|
var DO_DROPLET_SIZES2 = [
|
|
5797
6326
|
{ slug: "s-1vcpu-2gb", label: "1 vCPU, 2GB RAM, 50GB SSD", price: "$12/mo" },
|
|
5798
6327
|
{ slug: "s-2vcpu-2gb", label: "2 vCPU, 2GB RAM, 60GB SSD", price: "$18/mo" },
|
|
@@ -5801,20 +6330,20 @@ var DO_DROPLET_SIZES2 = [
|
|
|
5801
6330
|
{ slug: "s-8vcpu-16gb", label: "8 vCPU, 16GB RAM, 320GB SSD", price: "$96/mo" }
|
|
5802
6331
|
];
|
|
5803
6332
|
function TemplatesView({ context }) {
|
|
5804
|
-
const [viewState, setViewState] =
|
|
5805
|
-
const [templates, setTemplates] =
|
|
5806
|
-
const [selectedIndex, setSelectedIndex] =
|
|
5807
|
-
const [selectedTemplate, setSelectedTemplate] =
|
|
5808
|
-
const [error, setError] =
|
|
5809
|
-
const [forkStep, setForkStep] =
|
|
5810
|
-
const [forkName, setForkName] =
|
|
5811
|
-
const [forkProvider, setForkProvider] =
|
|
5812
|
-
const [forkProviderIndex, setForkProviderIndex] =
|
|
5813
|
-
const [forkDropletSizeIndex, setForkDropletSizeIndex] =
|
|
5814
|
-
const [forkAiProvider, setForkAiProvider] =
|
|
5815
|
-
const [forkAiProviderIndex, setForkAiProviderIndex] =
|
|
5816
|
-
const [forkModel, setForkModel] =
|
|
5817
|
-
const stateRef =
|
|
6333
|
+
const [viewState, setViewState] = useState10("listing");
|
|
6334
|
+
const [templates, setTemplates] = useState10([]);
|
|
6335
|
+
const [selectedIndex, setSelectedIndex] = useState10(0);
|
|
6336
|
+
const [selectedTemplate, setSelectedTemplate] = useState10(null);
|
|
6337
|
+
const [error, setError] = useState10(null);
|
|
6338
|
+
const [forkStep, setForkStep] = useState10("fork_name");
|
|
6339
|
+
const [forkName, setForkName] = useState10("");
|
|
6340
|
+
const [forkProvider, setForkProvider] = useState10("hetzner");
|
|
6341
|
+
const [forkProviderIndex, setForkProviderIndex] = useState10(0);
|
|
6342
|
+
const [forkDropletSizeIndex, setForkDropletSizeIndex] = useState10(0);
|
|
6343
|
+
const [forkAiProvider, setForkAiProvider] = useState10("");
|
|
6344
|
+
const [forkAiProviderIndex, setForkAiProviderIndex] = useState10(0);
|
|
6345
|
+
const [forkModel, setForkModel] = useState10("");
|
|
6346
|
+
const stateRef = useRef5({
|
|
5818
6347
|
viewState,
|
|
5819
6348
|
selectedIndex,
|
|
5820
6349
|
selectedTemplate,
|
|
@@ -5928,7 +6457,7 @@ function TemplatesView({ context }) {
|
|
|
5928
6457
|
setViewState("viewing");
|
|
5929
6458
|
}
|
|
5930
6459
|
};
|
|
5931
|
-
|
|
6460
|
+
useKeyboard10((key) => {
|
|
5932
6461
|
const s = stateRef.current;
|
|
5933
6462
|
if (s.viewState === "listing") {
|
|
5934
6463
|
if (key.name === "up") {
|
|
@@ -6020,37 +6549,37 @@ function TemplatesView({ context }) {
|
|
|
6020
6549
|
}
|
|
6021
6550
|
}
|
|
6022
6551
|
});
|
|
6023
|
-
const renderListing = () => /* @__PURE__ */
|
|
6024
|
-
/* @__PURE__ */
|
|
6025
|
-
/* @__PURE__ */
|
|
6552
|
+
const renderListing = () => /* @__PURE__ */ jsxs11("box", { flexDirection: "column", children: [
|
|
6553
|
+
/* @__PURE__ */ jsx11("text", { fg: t.fg.secondary, marginBottom: 1, children: "Select a template to view details. Use arrow keys and Enter." }),
|
|
6554
|
+
/* @__PURE__ */ jsx11(
|
|
6026
6555
|
"box",
|
|
6027
6556
|
{
|
|
6028
6557
|
flexDirection: "column",
|
|
6029
6558
|
borderStyle: "single",
|
|
6030
6559
|
borderColor: t.border.default,
|
|
6031
6560
|
padding: 1,
|
|
6032
|
-
children: templates.length === 0 ? /* @__PURE__ */
|
|
6561
|
+
children: templates.length === 0 ? /* @__PURE__ */ jsx11("text", { fg: t.fg.muted, children: "No templates found." }) : templates.map((tmpl, i) => {
|
|
6033
6562
|
const isSelected = i === selectedIndex;
|
|
6034
6563
|
const badge = tmpl.builtIn ? "[built-in]" : "[custom]";
|
|
6035
6564
|
const badgeColor = tmpl.builtIn ? t.status.info : t.status.success;
|
|
6036
|
-
return /* @__PURE__ */
|
|
6037
|
-
/* @__PURE__ */
|
|
6038
|
-
/* @__PURE__ */
|
|
6039
|
-
/* @__PURE__ */
|
|
6040
|
-
/* @__PURE__ */
|
|
6565
|
+
return /* @__PURE__ */ jsxs11("box", { flexDirection: "row", backgroundColor: isSelected ? t.selection.bg : void 0, children: [
|
|
6566
|
+
/* @__PURE__ */ jsx11("text", { fg: isSelected ? t.selection.indicator : t.fg.primary, children: isSelected ? "> " : " " }),
|
|
6567
|
+
/* @__PURE__ */ jsx11("text", { fg: isSelected ? t.selection.fg : t.fg.primary, children: tmpl.name }),
|
|
6568
|
+
/* @__PURE__ */ jsx11("text", { fg: badgeColor, children: " " + badge }),
|
|
6569
|
+
/* @__PURE__ */ jsx11("text", { fg: t.fg.muted, children: " \xB7 " + tmpl.channel })
|
|
6041
6570
|
] }, tmpl.id);
|
|
6042
6571
|
})
|
|
6043
6572
|
}
|
|
6044
6573
|
),
|
|
6045
|
-
/* @__PURE__ */
|
|
6574
|
+
/* @__PURE__ */ jsx11("text", { fg: t.fg.muted, marginTop: 1, children: "Enter to view, Esc to go back" })
|
|
6046
6575
|
] });
|
|
6047
6576
|
const renderViewing = () => {
|
|
6048
6577
|
if (!selectedTemplate) return null;
|
|
6049
6578
|
const tmpl = selectedTemplate;
|
|
6050
|
-
return /* @__PURE__ */
|
|
6051
|
-
/* @__PURE__ */
|
|
6052
|
-
/* @__PURE__ */
|
|
6053
|
-
/* @__PURE__ */
|
|
6579
|
+
return /* @__PURE__ */ jsxs11("box", { flexDirection: "column", children: [
|
|
6580
|
+
/* @__PURE__ */ jsx11("text", { fg: t.accent, marginBottom: 1, children: tmpl.name }),
|
|
6581
|
+
/* @__PURE__ */ jsx11("text", { fg: t.fg.secondary, marginBottom: 1, children: tmpl.description }),
|
|
6582
|
+
/* @__PURE__ */ jsxs11(
|
|
6054
6583
|
"box",
|
|
6055
6584
|
{
|
|
6056
6585
|
flexDirection: "column",
|
|
@@ -6059,89 +6588,89 @@ function TemplatesView({ context }) {
|
|
|
6059
6588
|
padding: 1,
|
|
6060
6589
|
marginBottom: 1,
|
|
6061
6590
|
children: [
|
|
6062
|
-
/* @__PURE__ */
|
|
6063
|
-
/* @__PURE__ */
|
|
6064
|
-
/* @__PURE__ */
|
|
6591
|
+
/* @__PURE__ */ jsxs11("box", { flexDirection: "row", children: [
|
|
6592
|
+
/* @__PURE__ */ jsx11("text", { fg: t.fg.secondary, width: 20, children: "Type:" }),
|
|
6593
|
+
/* @__PURE__ */ jsx11("text", { fg: tmpl.builtIn ? t.status.info : t.status.success, children: tmpl.builtIn ? "Built-in" : "Custom" })
|
|
6065
6594
|
] }),
|
|
6066
|
-
/* @__PURE__ */
|
|
6067
|
-
/* @__PURE__ */
|
|
6068
|
-
/* @__PURE__ */
|
|
6595
|
+
/* @__PURE__ */ jsxs11("box", { flexDirection: "row", children: [
|
|
6596
|
+
/* @__PURE__ */ jsx11("text", { fg: t.fg.secondary, width: 20, children: "Provider:" }),
|
|
6597
|
+
/* @__PURE__ */ jsx11("text", { fg: t.fg.primary, children: PROVIDER_NAMES[tmpl.provider] })
|
|
6069
6598
|
] }),
|
|
6070
|
-
tmpl.hetzner && /* @__PURE__ */
|
|
6071
|
-
/* @__PURE__ */
|
|
6072
|
-
/* @__PURE__ */
|
|
6073
|
-
/* @__PURE__ */
|
|
6599
|
+
tmpl.hetzner && /* @__PURE__ */ jsxs11(Fragment3, { children: [
|
|
6600
|
+
/* @__PURE__ */ jsxs11("box", { flexDirection: "row", children: [
|
|
6601
|
+
/* @__PURE__ */ jsx11("text", { fg: t.fg.secondary, width: 20, children: "Server Type:" }),
|
|
6602
|
+
/* @__PURE__ */ jsx11("text", { fg: t.fg.primary, children: tmpl.hetzner.serverType })
|
|
6074
6603
|
] }),
|
|
6075
|
-
/* @__PURE__ */
|
|
6076
|
-
/* @__PURE__ */
|
|
6077
|
-
/* @__PURE__ */
|
|
6604
|
+
/* @__PURE__ */ jsxs11("box", { flexDirection: "row", children: [
|
|
6605
|
+
/* @__PURE__ */ jsx11("text", { fg: t.fg.secondary, width: 20, children: "Location:" }),
|
|
6606
|
+
/* @__PURE__ */ jsx11("text", { fg: t.fg.primary, children: tmpl.hetzner.location })
|
|
6078
6607
|
] })
|
|
6079
6608
|
] }),
|
|
6080
|
-
tmpl.digitalocean && /* @__PURE__ */
|
|
6081
|
-
/* @__PURE__ */
|
|
6082
|
-
/* @__PURE__ */
|
|
6083
|
-
/* @__PURE__ */
|
|
6609
|
+
tmpl.digitalocean && /* @__PURE__ */ jsxs11(Fragment3, { children: [
|
|
6610
|
+
/* @__PURE__ */ jsxs11("box", { flexDirection: "row", children: [
|
|
6611
|
+
/* @__PURE__ */ jsx11("text", { fg: t.fg.secondary, width: 20, children: "Droplet Size:" }),
|
|
6612
|
+
/* @__PURE__ */ jsx11("text", { fg: t.fg.primary, children: tmpl.digitalocean.size })
|
|
6084
6613
|
] }),
|
|
6085
|
-
/* @__PURE__ */
|
|
6086
|
-
/* @__PURE__ */
|
|
6087
|
-
/* @__PURE__ */
|
|
6614
|
+
/* @__PURE__ */ jsxs11("box", { flexDirection: "row", children: [
|
|
6615
|
+
/* @__PURE__ */ jsx11("text", { fg: t.fg.secondary, width: 20, children: "Region:" }),
|
|
6616
|
+
/* @__PURE__ */ jsx11("text", { fg: t.fg.primary, children: tmpl.digitalocean.region })
|
|
6088
6617
|
] })
|
|
6089
6618
|
] }),
|
|
6090
|
-
/* @__PURE__ */
|
|
6091
|
-
/* @__PURE__ */
|
|
6092
|
-
/* @__PURE__ */
|
|
6619
|
+
/* @__PURE__ */ jsxs11("box", { flexDirection: "row", children: [
|
|
6620
|
+
/* @__PURE__ */ jsx11("text", { fg: t.fg.secondary, width: 20, children: "AI Provider:" }),
|
|
6621
|
+
/* @__PURE__ */ jsx11("text", { fg: t.fg.primary, children: tmpl.aiProvider })
|
|
6093
6622
|
] }),
|
|
6094
|
-
/* @__PURE__ */
|
|
6095
|
-
/* @__PURE__ */
|
|
6096
|
-
/* @__PURE__ */
|
|
6623
|
+
/* @__PURE__ */ jsxs11("box", { flexDirection: "row", children: [
|
|
6624
|
+
/* @__PURE__ */ jsx11("text", { fg: t.fg.secondary, width: 20, children: "Model:" }),
|
|
6625
|
+
/* @__PURE__ */ jsx11("text", { fg: t.fg.primary, children: tmpl.model })
|
|
6097
6626
|
] }),
|
|
6098
|
-
/* @__PURE__ */
|
|
6099
|
-
/* @__PURE__ */
|
|
6100
|
-
/* @__PURE__ */
|
|
6627
|
+
/* @__PURE__ */ jsxs11("box", { flexDirection: "row", children: [
|
|
6628
|
+
/* @__PURE__ */ jsx11("text", { fg: t.fg.secondary, width: 20, children: "Channel:" }),
|
|
6629
|
+
/* @__PURE__ */ jsx11("text", { fg: t.fg.primary, children: tmpl.channel })
|
|
6101
6630
|
] })
|
|
6102
6631
|
]
|
|
6103
6632
|
}
|
|
6104
6633
|
),
|
|
6105
|
-
/* @__PURE__ */
|
|
6106
|
-
/* @__PURE__ */
|
|
6107
|
-
/* @__PURE__ */
|
|
6108
|
-
/* @__PURE__ */
|
|
6109
|
-
/* @__PURE__ */
|
|
6110
|
-
!tmpl.builtIn && /* @__PURE__ */
|
|
6111
|
-
/* @__PURE__ */
|
|
6112
|
-
/* @__PURE__ */
|
|
6634
|
+
/* @__PURE__ */ jsxs11("box", { flexDirection: "row", children: [
|
|
6635
|
+
/* @__PURE__ */ jsx11("text", { fg: t.accent, children: "[F]" }),
|
|
6636
|
+
/* @__PURE__ */ jsx11("text", { fg: t.fg.primary, children: "ork " }),
|
|
6637
|
+
/* @__PURE__ */ jsx11("text", { fg: t.accent, children: "[U]" }),
|
|
6638
|
+
/* @__PURE__ */ jsx11("text", { fg: t.fg.primary, children: "se " }),
|
|
6639
|
+
!tmpl.builtIn && /* @__PURE__ */ jsxs11(Fragment3, { children: [
|
|
6640
|
+
/* @__PURE__ */ jsx11("text", { fg: t.status.error, children: "[D]" }),
|
|
6641
|
+
/* @__PURE__ */ jsx11("text", { fg: t.fg.primary, children: "elete " })
|
|
6113
6642
|
] }),
|
|
6114
|
-
/* @__PURE__ */
|
|
6643
|
+
/* @__PURE__ */ jsx11("text", { fg: t.fg.muted, children: "[Esc] Back" })
|
|
6115
6644
|
] }),
|
|
6116
|
-
error && /* @__PURE__ */
|
|
6645
|
+
error && /* @__PURE__ */ jsx11("text", { fg: t.status.error, marginTop: 1, children: error })
|
|
6117
6646
|
] });
|
|
6118
6647
|
};
|
|
6119
|
-
const renderDeleteConfirm = () => /* @__PURE__ */
|
|
6120
|
-
/* @__PURE__ */
|
|
6121
|
-
/* @__PURE__ */
|
|
6648
|
+
const renderDeleteConfirm = () => /* @__PURE__ */ jsxs11("box", { flexDirection: "column", children: [
|
|
6649
|
+
/* @__PURE__ */ jsx11("text", { fg: t.status.error, children: "Delete Template" }),
|
|
6650
|
+
/* @__PURE__ */ jsxs11("text", { fg: t.fg.primary, marginTop: 1, children: [
|
|
6122
6651
|
'Are you sure you want to delete "',
|
|
6123
6652
|
selectedTemplate?.name,
|
|
6124
6653
|
'"?'
|
|
6125
6654
|
] }),
|
|
6126
|
-
/* @__PURE__ */
|
|
6127
|
-
/* @__PURE__ */
|
|
6655
|
+
/* @__PURE__ */ jsx11("text", { fg: t.status.warning, marginTop: 1, children: "This action cannot be undone." }),
|
|
6656
|
+
/* @__PURE__ */ jsx11("text", { fg: t.fg.muted, marginTop: 2, children: "Press Y to confirm, N to cancel" })
|
|
6128
6657
|
] });
|
|
6129
|
-
const renderForkComplete = () => /* @__PURE__ */
|
|
6130
|
-
/* @__PURE__ */
|
|
6131
|
-
/* @__PURE__ */
|
|
6658
|
+
const renderForkComplete = () => /* @__PURE__ */ jsxs11("box", { flexDirection: "column", children: [
|
|
6659
|
+
/* @__PURE__ */ jsx11("text", { fg: t.status.success, children: "Template Created!" }),
|
|
6660
|
+
/* @__PURE__ */ jsxs11("text", { fg: t.fg.primary, marginTop: 1, children: [
|
|
6132
6661
|
'Your template "',
|
|
6133
6662
|
forkName,
|
|
6134
6663
|
'" has been saved.'
|
|
6135
6664
|
] }),
|
|
6136
|
-
/* @__PURE__ */
|
|
6665
|
+
/* @__PURE__ */ jsx11("text", { fg: t.fg.muted, marginTop: 2, children: "Press any key to return to templates list" })
|
|
6137
6666
|
] });
|
|
6138
6667
|
const renderForking = () => {
|
|
6139
6668
|
switch (forkStep) {
|
|
6140
6669
|
case "fork_name":
|
|
6141
|
-
return /* @__PURE__ */
|
|
6142
|
-
/* @__PURE__ */
|
|
6143
|
-
/* @__PURE__ */
|
|
6144
|
-
/* @__PURE__ */
|
|
6670
|
+
return /* @__PURE__ */ jsxs11("box", { flexDirection: "column", children: [
|
|
6671
|
+
/* @__PURE__ */ jsx11("text", { fg: t.accent, children: "Fork Template - Name" }),
|
|
6672
|
+
/* @__PURE__ */ jsx11("text", { fg: t.fg.secondary, marginTop: 1, children: "Enter a name for the new template:" }),
|
|
6673
|
+
/* @__PURE__ */ jsx11(
|
|
6145
6674
|
"input",
|
|
6146
6675
|
{
|
|
6147
6676
|
value: forkName,
|
|
@@ -6164,14 +6693,14 @@ function TemplatesView({ context }) {
|
|
|
6164
6693
|
}
|
|
6165
6694
|
}
|
|
6166
6695
|
),
|
|
6167
|
-
error && /* @__PURE__ */
|
|
6168
|
-
/* @__PURE__ */
|
|
6696
|
+
error && /* @__PURE__ */ jsx11("text", { fg: t.status.error, marginTop: 1, children: error }),
|
|
6697
|
+
/* @__PURE__ */ jsx11("text", { fg: t.fg.muted, marginTop: 2, children: "Press Enter to continue, Esc to go back" })
|
|
6169
6698
|
] });
|
|
6170
6699
|
case "fork_provider":
|
|
6171
|
-
return /* @__PURE__ */
|
|
6172
|
-
/* @__PURE__ */
|
|
6173
|
-
/* @__PURE__ */
|
|
6174
|
-
/* @__PURE__ */
|
|
6700
|
+
return /* @__PURE__ */ jsxs11("box", { flexDirection: "column", children: [
|
|
6701
|
+
/* @__PURE__ */ jsx11("text", { fg: t.accent, children: "Fork Template - Provider" }),
|
|
6702
|
+
/* @__PURE__ */ jsx11("text", { fg: t.fg.secondary, marginTop: 1, children: "Select cloud provider:" }),
|
|
6703
|
+
/* @__PURE__ */ jsx11(
|
|
6175
6704
|
"box",
|
|
6176
6705
|
{
|
|
6177
6706
|
flexDirection: "column",
|
|
@@ -6181,20 +6710,20 @@ function TemplatesView({ context }) {
|
|
|
6181
6710
|
padding: 1,
|
|
6182
6711
|
children: SUPPORTED_PROVIDERS.map((p, i) => {
|
|
6183
6712
|
const isSelected = i === forkProviderIndex;
|
|
6184
|
-
return /* @__PURE__ */
|
|
6185
|
-
/* @__PURE__ */
|
|
6186
|
-
/* @__PURE__ */
|
|
6713
|
+
return /* @__PURE__ */ jsxs11("box", { flexDirection: "row", backgroundColor: isSelected ? t.selection.bg : void 0, children: [
|
|
6714
|
+
/* @__PURE__ */ jsx11("text", { fg: isSelected ? t.selection.indicator : t.fg.primary, children: isSelected ? "> " : " " }),
|
|
6715
|
+
/* @__PURE__ */ jsx11("text", { fg: isSelected ? t.selection.fg : t.fg.primary, children: PROVIDER_NAMES[p] })
|
|
6187
6716
|
] }, p);
|
|
6188
6717
|
})
|
|
6189
6718
|
}
|
|
6190
6719
|
),
|
|
6191
|
-
/* @__PURE__ */
|
|
6720
|
+
/* @__PURE__ */ jsx11("text", { fg: t.fg.muted, marginTop: 1, children: "Enter to select, Esc to go back" })
|
|
6192
6721
|
] });
|
|
6193
6722
|
case "fork_droplet_size":
|
|
6194
|
-
return /* @__PURE__ */
|
|
6195
|
-
/* @__PURE__ */
|
|
6196
|
-
/* @__PURE__ */
|
|
6197
|
-
/* @__PURE__ */
|
|
6723
|
+
return /* @__PURE__ */ jsxs11("box", { flexDirection: "column", children: [
|
|
6724
|
+
/* @__PURE__ */ jsx11("text", { fg: t.accent, children: "Fork Template - Droplet Size" }),
|
|
6725
|
+
/* @__PURE__ */ jsx11("text", { fg: t.fg.secondary, marginTop: 1, children: "Select DigitalOcean droplet size:" }),
|
|
6726
|
+
/* @__PURE__ */ jsx11(
|
|
6198
6727
|
"box",
|
|
6199
6728
|
{
|
|
6200
6729
|
flexDirection: "column",
|
|
@@ -6204,21 +6733,21 @@ function TemplatesView({ context }) {
|
|
|
6204
6733
|
padding: 1,
|
|
6205
6734
|
children: DO_DROPLET_SIZES2.map((size, i) => {
|
|
6206
6735
|
const isSelected = i === forkDropletSizeIndex;
|
|
6207
|
-
return /* @__PURE__ */
|
|
6208
|
-
/* @__PURE__ */
|
|
6209
|
-
/* @__PURE__ */
|
|
6210
|
-
/* @__PURE__ */
|
|
6736
|
+
return /* @__PURE__ */ jsxs11("box", { flexDirection: "row", backgroundColor: isSelected ? t.selection.bg : void 0, children: [
|
|
6737
|
+
/* @__PURE__ */ jsx11("text", { fg: isSelected ? t.selection.indicator : t.fg.primary, children: isSelected ? "> " : " " }),
|
|
6738
|
+
/* @__PURE__ */ jsx11("text", { fg: isSelected ? t.selection.fg : t.fg.primary, children: size.slug }),
|
|
6739
|
+
/* @__PURE__ */ jsx11("text", { fg: isSelected ? t.fg.primary : t.fg.secondary, children: " - " + size.label + " - " + size.price })
|
|
6211
6740
|
] }, size.slug);
|
|
6212
6741
|
})
|
|
6213
6742
|
}
|
|
6214
6743
|
),
|
|
6215
|
-
/* @__PURE__ */
|
|
6744
|
+
/* @__PURE__ */ jsx11("text", { fg: t.fg.muted, marginTop: 1, children: "Enter to select, Esc to go back" })
|
|
6216
6745
|
] });
|
|
6217
6746
|
case "fork_ai_provider":
|
|
6218
|
-
return /* @__PURE__ */
|
|
6219
|
-
/* @__PURE__ */
|
|
6220
|
-
/* @__PURE__ */
|
|
6221
|
-
/* @__PURE__ */
|
|
6747
|
+
return /* @__PURE__ */ jsxs11("box", { flexDirection: "column", children: [
|
|
6748
|
+
/* @__PURE__ */ jsx11("text", { fg: t.accent, children: "Fork Template - AI Provider" }),
|
|
6749
|
+
/* @__PURE__ */ jsx11("text", { fg: t.fg.secondary, marginTop: 1, children: "Select AI provider:" }),
|
|
6750
|
+
/* @__PURE__ */ jsx11(
|
|
6222
6751
|
"box",
|
|
6223
6752
|
{
|
|
6224
6753
|
flexDirection: "column",
|
|
@@ -6228,21 +6757,21 @@ function TemplatesView({ context }) {
|
|
|
6228
6757
|
padding: 1,
|
|
6229
6758
|
children: AI_PROVIDERS.map((p, i) => {
|
|
6230
6759
|
const isSelected = i === forkAiProviderIndex;
|
|
6231
|
-
return /* @__PURE__ */
|
|
6232
|
-
/* @__PURE__ */
|
|
6233
|
-
/* @__PURE__ */
|
|
6234
|
-
/* @__PURE__ */
|
|
6760
|
+
return /* @__PURE__ */ jsxs11("box", { flexDirection: "row", backgroundColor: isSelected ? t.selection.bg : void 0, children: [
|
|
6761
|
+
/* @__PURE__ */ jsx11("text", { fg: isSelected ? t.selection.indicator : t.fg.primary, children: isSelected ? "> " : " " }),
|
|
6762
|
+
/* @__PURE__ */ jsx11("text", { fg: isSelected ? t.selection.fg : t.fg.primary, children: p.label }),
|
|
6763
|
+
/* @__PURE__ */ jsx11("text", { fg: isSelected ? t.fg.primary : t.fg.secondary, children: " - " + p.description })
|
|
6235
6764
|
] }, p.name);
|
|
6236
6765
|
})
|
|
6237
6766
|
}
|
|
6238
6767
|
),
|
|
6239
|
-
/* @__PURE__ */
|
|
6768
|
+
/* @__PURE__ */ jsx11("text", { fg: t.fg.muted, marginTop: 1, children: "Enter to select, Esc to go back" })
|
|
6240
6769
|
] });
|
|
6241
6770
|
case "fork_model":
|
|
6242
|
-
return /* @__PURE__ */
|
|
6243
|
-
/* @__PURE__ */
|
|
6244
|
-
/* @__PURE__ */
|
|
6245
|
-
/* @__PURE__ */
|
|
6771
|
+
return /* @__PURE__ */ jsxs11("box", { flexDirection: "column", children: [
|
|
6772
|
+
/* @__PURE__ */ jsx11("text", { fg: t.accent, children: "Fork Template - Model" }),
|
|
6773
|
+
/* @__PURE__ */ jsx11("text", { fg: t.fg.secondary, marginTop: 1, children: "Enter the model identifier:" }),
|
|
6774
|
+
/* @__PURE__ */ jsx11(
|
|
6246
6775
|
"input",
|
|
6247
6776
|
{
|
|
6248
6777
|
value: forkModel,
|
|
@@ -6265,13 +6794,13 @@ function TemplatesView({ context }) {
|
|
|
6265
6794
|
}
|
|
6266
6795
|
}
|
|
6267
6796
|
),
|
|
6268
|
-
error && /* @__PURE__ */
|
|
6269
|
-
/* @__PURE__ */
|
|
6797
|
+
error && /* @__PURE__ */ jsx11("text", { fg: t.status.error, marginTop: 1, children: error }),
|
|
6798
|
+
/* @__PURE__ */ jsx11("text", { fg: t.fg.muted, marginTop: 2, children: "Press Enter to continue, Esc to go back" })
|
|
6270
6799
|
] });
|
|
6271
6800
|
case "fork_confirm":
|
|
6272
|
-
return /* @__PURE__ */
|
|
6273
|
-
/* @__PURE__ */
|
|
6274
|
-
/* @__PURE__ */
|
|
6801
|
+
return /* @__PURE__ */ jsxs11("box", { flexDirection: "column", children: [
|
|
6802
|
+
/* @__PURE__ */ jsx11("text", { fg: t.accent, children: "Fork Template - Confirm" }),
|
|
6803
|
+
/* @__PURE__ */ jsxs11(
|
|
6275
6804
|
"box",
|
|
6276
6805
|
{
|
|
6277
6806
|
flexDirection: "column",
|
|
@@ -6280,35 +6809,35 @@ function TemplatesView({ context }) {
|
|
|
6280
6809
|
padding: 1,
|
|
6281
6810
|
marginTop: 1,
|
|
6282
6811
|
children: [
|
|
6283
|
-
/* @__PURE__ */
|
|
6284
|
-
/* @__PURE__ */
|
|
6285
|
-
/* @__PURE__ */
|
|
6812
|
+
/* @__PURE__ */ jsxs11("box", { flexDirection: "row", children: [
|
|
6813
|
+
/* @__PURE__ */ jsx11("text", { fg: t.fg.secondary, width: 20, children: "Name:" }),
|
|
6814
|
+
/* @__PURE__ */ jsx11("text", { fg: t.fg.primary, children: forkName })
|
|
6286
6815
|
] }),
|
|
6287
|
-
/* @__PURE__ */
|
|
6288
|
-
/* @__PURE__ */
|
|
6289
|
-
/* @__PURE__ */
|
|
6816
|
+
/* @__PURE__ */ jsxs11("box", { flexDirection: "row", children: [
|
|
6817
|
+
/* @__PURE__ */ jsx11("text", { fg: t.fg.secondary, width: 20, children: "Provider:" }),
|
|
6818
|
+
/* @__PURE__ */ jsx11("text", { fg: t.fg.primary, children: PROVIDER_NAMES[forkProvider] })
|
|
6290
6819
|
] }),
|
|
6291
|
-
forkProvider === "digitalocean" && /* @__PURE__ */
|
|
6292
|
-
/* @__PURE__ */
|
|
6293
|
-
/* @__PURE__ */
|
|
6820
|
+
forkProvider === "digitalocean" && /* @__PURE__ */ jsxs11("box", { flexDirection: "row", children: [
|
|
6821
|
+
/* @__PURE__ */ jsx11("text", { fg: t.fg.secondary, width: 20, children: "Droplet Size:" }),
|
|
6822
|
+
/* @__PURE__ */ jsx11("text", { fg: t.fg.primary, children: DO_DROPLET_SIZES2[forkDropletSizeIndex].slug })
|
|
6294
6823
|
] }),
|
|
6295
|
-
/* @__PURE__ */
|
|
6296
|
-
/* @__PURE__ */
|
|
6297
|
-
/* @__PURE__ */
|
|
6824
|
+
/* @__PURE__ */ jsxs11("box", { flexDirection: "row", children: [
|
|
6825
|
+
/* @__PURE__ */ jsx11("text", { fg: t.fg.secondary, width: 20, children: "AI Provider:" }),
|
|
6826
|
+
/* @__PURE__ */ jsx11("text", { fg: t.fg.primary, children: forkAiProvider })
|
|
6298
6827
|
] }),
|
|
6299
|
-
/* @__PURE__ */
|
|
6300
|
-
/* @__PURE__ */
|
|
6301
|
-
/* @__PURE__ */
|
|
6828
|
+
/* @__PURE__ */ jsxs11("box", { flexDirection: "row", children: [
|
|
6829
|
+
/* @__PURE__ */ jsx11("text", { fg: t.fg.secondary, width: 20, children: "Model:" }),
|
|
6830
|
+
/* @__PURE__ */ jsx11("text", { fg: t.fg.primary, children: forkModel })
|
|
6302
6831
|
] }),
|
|
6303
|
-
/* @__PURE__ */
|
|
6304
|
-
/* @__PURE__ */
|
|
6305
|
-
/* @__PURE__ */
|
|
6832
|
+
/* @__PURE__ */ jsxs11("box", { flexDirection: "row", children: [
|
|
6833
|
+
/* @__PURE__ */ jsx11("text", { fg: t.fg.secondary, width: 20, children: "Channel:" }),
|
|
6834
|
+
/* @__PURE__ */ jsx11("text", { fg: t.fg.primary, children: "telegram" })
|
|
6306
6835
|
] })
|
|
6307
6836
|
]
|
|
6308
6837
|
}
|
|
6309
6838
|
),
|
|
6310
|
-
error && /* @__PURE__ */
|
|
6311
|
-
/* @__PURE__ */
|
|
6839
|
+
error && /* @__PURE__ */ jsx11("text", { fg: t.status.error, marginTop: 1, children: error }),
|
|
6840
|
+
/* @__PURE__ */ jsx11("text", { fg: t.status.warning, marginTop: 2, children: "Press Y to confirm, N to go back" })
|
|
6312
6841
|
] });
|
|
6313
6842
|
}
|
|
6314
6843
|
};
|
|
@@ -6326,34 +6855,464 @@ function TemplatesView({ context }) {
|
|
|
6326
6855
|
return renderForking();
|
|
6327
6856
|
}
|
|
6328
6857
|
};
|
|
6329
|
-
return /* @__PURE__ */
|
|
6330
|
-
/* @__PURE__ */
|
|
6331
|
-
/* @__PURE__ */
|
|
6332
|
-
/* @__PURE__ */
|
|
6858
|
+
return /* @__PURE__ */ jsxs11("box", { flexDirection: "column", width: "100%", padding: 1, children: [
|
|
6859
|
+
/* @__PURE__ */ jsxs11("box", { flexDirection: "row", marginBottom: 2, children: [
|
|
6860
|
+
/* @__PURE__ */ jsx11("text", { fg: t.accent, children: "/templates" }),
|
|
6861
|
+
/* @__PURE__ */ jsx11("text", { fg: t.fg.secondary, children: " - Manage deployment templates" })
|
|
6333
6862
|
] }),
|
|
6334
6863
|
renderContent()
|
|
6335
6864
|
] });
|
|
6336
6865
|
}
|
|
6337
6866
|
|
|
6867
|
+
// src/components/DashboardView.tsx
|
|
6868
|
+
import { useState as useState11, useCallback as useCallback3 } from "react";
|
|
6869
|
+
import { useKeyboard as useKeyboard11, useRenderer } from "@opentui/react";
|
|
6870
|
+
import { spawnSync as spawnSync2 } from "child_process";
|
|
6871
|
+
import { platform as platform2 } from "os";
|
|
6872
|
+
|
|
6873
|
+
// src/services/tunnel.ts
|
|
6874
|
+
import { spawn as spawn2 } from "child_process";
|
|
6875
|
+
import { createServer } from "net";
|
|
6876
|
+
var activeTunnels = /* @__PURE__ */ new Map();
|
|
6877
|
+
function isPortAvailable(port) {
|
|
6878
|
+
return new Promise((resolve) => {
|
|
6879
|
+
const server = createServer();
|
|
6880
|
+
server.once("error", () => resolve(false));
|
|
6881
|
+
server.once("listening", () => {
|
|
6882
|
+
server.close(() => resolve(true));
|
|
6883
|
+
});
|
|
6884
|
+
server.listen(port, "127.0.0.1");
|
|
6885
|
+
});
|
|
6886
|
+
}
|
|
6887
|
+
async function findAvailablePort(startPort) {
|
|
6888
|
+
for (let port = startPort; port < startPort + 100; port++) {
|
|
6889
|
+
if (await isPortAvailable(port)) return port;
|
|
6890
|
+
}
|
|
6891
|
+
throw new Error(`No available port found in range ${startPort}\u2013${startPort + 99}`);
|
|
6892
|
+
}
|
|
6893
|
+
async function startTunnel(deploymentName, serverIp, remotePort = 18789) {
|
|
6894
|
+
const existing = getTunnel(deploymentName);
|
|
6895
|
+
if (existing) return existing;
|
|
6896
|
+
const sshKeyPath = getSSHKeyPath(deploymentName);
|
|
6897
|
+
const localPort = await findAvailablePort(remotePort);
|
|
6898
|
+
const proc = spawn2("ssh", [
|
|
6899
|
+
"-N",
|
|
6900
|
+
"-L",
|
|
6901
|
+
`${localPort}:127.0.0.1:${remotePort}`,
|
|
6902
|
+
"-i",
|
|
6903
|
+
sshKeyPath,
|
|
6904
|
+
"-o",
|
|
6905
|
+
"StrictHostKeyChecking=no",
|
|
6906
|
+
"-o",
|
|
6907
|
+
"UserKnownHostsFile=/dev/null",
|
|
6908
|
+
"-o",
|
|
6909
|
+
"ServerAliveInterval=15",
|
|
6910
|
+
"-o",
|
|
6911
|
+
"ServerAliveCountMax=3",
|
|
6912
|
+
"-o",
|
|
6913
|
+
"ExitOnForwardFailure=yes",
|
|
6914
|
+
`root@${serverIp}`
|
|
6915
|
+
], {
|
|
6916
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
6917
|
+
detached: false
|
|
6918
|
+
});
|
|
6919
|
+
const tunnel = {
|
|
6920
|
+
deploymentName,
|
|
6921
|
+
serverIp,
|
|
6922
|
+
localPort,
|
|
6923
|
+
remotePort,
|
|
6924
|
+
process: proc,
|
|
6925
|
+
dashboardUrl: null,
|
|
6926
|
+
startedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
6927
|
+
};
|
|
6928
|
+
activeTunnels.set(deploymentName, tunnel);
|
|
6929
|
+
proc.on("exit", () => {
|
|
6930
|
+
activeTunnels.delete(deploymentName);
|
|
6931
|
+
});
|
|
6932
|
+
await new Promise((resolve) => setTimeout(resolve, 2500));
|
|
6933
|
+
if (proc.exitCode !== null || proc.killed) {
|
|
6934
|
+
activeTunnels.delete(deploymentName);
|
|
6935
|
+
throw new Error("SSH tunnel failed to establish. Check that the server is reachable.");
|
|
6936
|
+
}
|
|
6937
|
+
return tunnel;
|
|
6938
|
+
}
|
|
6939
|
+
function stopTunnel(deploymentName) {
|
|
6940
|
+
const tunnel = activeTunnels.get(deploymentName);
|
|
6941
|
+
if (tunnel) {
|
|
6942
|
+
tunnel.process.kill();
|
|
6943
|
+
activeTunnels.delete(deploymentName);
|
|
6944
|
+
}
|
|
6945
|
+
}
|
|
6946
|
+
function getTunnel(deploymentName) {
|
|
6947
|
+
const tunnel = activeTunnels.get(deploymentName);
|
|
6948
|
+
if (tunnel && tunnel.process.exitCode === null && !tunnel.process.killed) {
|
|
6949
|
+
return tunnel;
|
|
6950
|
+
}
|
|
6951
|
+
if (tunnel) activeTunnels.delete(deploymentName);
|
|
6952
|
+
return null;
|
|
6953
|
+
}
|
|
6954
|
+
function getActiveTunnels() {
|
|
6955
|
+
for (const [name, tunnel] of activeTunnels) {
|
|
6956
|
+
if (tunnel.process.exitCode !== null || tunnel.process.killed) {
|
|
6957
|
+
activeTunnels.delete(name);
|
|
6958
|
+
}
|
|
6959
|
+
}
|
|
6960
|
+
return Array.from(activeTunnels.values());
|
|
6961
|
+
}
|
|
6962
|
+
|
|
6963
|
+
// src/components/DashboardView.tsx
|
|
6964
|
+
import { jsx as jsx12, jsxs as jsxs12 } from "@opentui/react/jsx-runtime";
|
|
6965
|
+
function openInBrowser(url) {
|
|
6966
|
+
try {
|
|
6967
|
+
if (platform2() === "darwin") {
|
|
6968
|
+
spawnSync2("open", [url]);
|
|
6969
|
+
} else {
|
|
6970
|
+
spawnSync2("xdg-open", [url]);
|
|
6971
|
+
}
|
|
6972
|
+
return true;
|
|
6973
|
+
} catch {
|
|
6974
|
+
return false;
|
|
6975
|
+
}
|
|
6976
|
+
}
|
|
6977
|
+
function DashboardView({ context }) {
|
|
6978
|
+
const renderer = useRenderer();
|
|
6979
|
+
const [viewState, setViewState] = useState11("selecting");
|
|
6980
|
+
const [selectedIndex, setSelectedIndex] = useState11(0);
|
|
6981
|
+
const [error, setError] = useState11(null);
|
|
6982
|
+
const [activeTunnel, setActiveTunnel] = useState11(null);
|
|
6983
|
+
const [dashboardUrl, setDashboardUrl] = useState11(null);
|
|
6984
|
+
const [connectMessage, setConnectMessage] = useState11("");
|
|
6985
|
+
const [copied, setCopied] = useState11(false);
|
|
6986
|
+
const deployedDeployments = context.deployments.filter(
|
|
6987
|
+
(d) => d.state.status === "deployed" && d.state.serverIp
|
|
6988
|
+
);
|
|
6989
|
+
const connectToDashboard = useCallback3(async (deployment) => {
|
|
6990
|
+
setViewState("connecting");
|
|
6991
|
+
setError(null);
|
|
6992
|
+
setCopied(false);
|
|
6993
|
+
setConnectMessage("Establishing SSH tunnel...");
|
|
6994
|
+
try {
|
|
6995
|
+
const serverIp = deployment.state.serverIp;
|
|
6996
|
+
const remotePort = deployment.config.openclawConfig?.gateway?.port || 18789;
|
|
6997
|
+
const tunnel = await startTunnel(
|
|
6998
|
+
deployment.config.name,
|
|
6999
|
+
serverIp,
|
|
7000
|
+
remotePort
|
|
7001
|
+
);
|
|
7002
|
+
setActiveTunnel(tunnel);
|
|
7003
|
+
if (tunnel.dashboardUrl) {
|
|
7004
|
+
setDashboardUrl(tunnel.dashboardUrl);
|
|
7005
|
+
setViewState("active");
|
|
7006
|
+
return;
|
|
7007
|
+
}
|
|
7008
|
+
setConnectMessage("Retrieving dashboard URL...");
|
|
7009
|
+
const localToken = deployment.state.gatewayToken;
|
|
7010
|
+
if (localToken) {
|
|
7011
|
+
const localUrl = `http://127.0.0.1:${tunnel.localPort}/?token=${encodeURIComponent(localToken)}`;
|
|
7012
|
+
tunnel.dashboardUrl = localUrl;
|
|
7013
|
+
setDashboardUrl(localUrl);
|
|
7014
|
+
setViewState("active");
|
|
7015
|
+
return;
|
|
7016
|
+
}
|
|
7017
|
+
const ssh = await connectToDeployment(
|
|
7018
|
+
deployment.config.name,
|
|
7019
|
+
serverIp
|
|
7020
|
+
);
|
|
7021
|
+
try {
|
|
7022
|
+
const info = await getDashboardUrl(ssh);
|
|
7023
|
+
const parsed = new URL(info.url);
|
|
7024
|
+
parsed.hostname = "127.0.0.1";
|
|
7025
|
+
parsed.port = String(tunnel.localPort);
|
|
7026
|
+
const localUrl = parsed.toString();
|
|
7027
|
+
tunnel.dashboardUrl = localUrl;
|
|
7028
|
+
setDashboardUrl(localUrl);
|
|
7029
|
+
setViewState("active");
|
|
7030
|
+
} finally {
|
|
7031
|
+
ssh.disconnect();
|
|
7032
|
+
}
|
|
7033
|
+
} catch (err) {
|
|
7034
|
+
setError(err instanceof Error ? err.message : String(err));
|
|
7035
|
+
setViewState("error");
|
|
7036
|
+
}
|
|
7037
|
+
}, []);
|
|
7038
|
+
useKeyboard11((key) => {
|
|
7039
|
+
if (viewState === "selecting") {
|
|
7040
|
+
if (deployedDeployments.length === 0) {
|
|
7041
|
+
if (key.name === "escape" || key.name === "return") {
|
|
7042
|
+
context.navigateTo("home");
|
|
7043
|
+
}
|
|
7044
|
+
return;
|
|
7045
|
+
}
|
|
7046
|
+
if (key.name === "up" && selectedIndex > 0) {
|
|
7047
|
+
setSelectedIndex(selectedIndex - 1);
|
|
7048
|
+
} else if (key.name === "down" && selectedIndex < deployedDeployments.length - 1) {
|
|
7049
|
+
setSelectedIndex(selectedIndex + 1);
|
|
7050
|
+
} else if (key.name === "return") {
|
|
7051
|
+
const deployment = deployedDeployments[selectedIndex];
|
|
7052
|
+
const existing = getTunnel(deployment.config.name);
|
|
7053
|
+
if (existing && existing.dashboardUrl) {
|
|
7054
|
+
setActiveTunnel(existing);
|
|
7055
|
+
setDashboardUrl(existing.dashboardUrl);
|
|
7056
|
+
setViewState("active");
|
|
7057
|
+
return;
|
|
7058
|
+
}
|
|
7059
|
+
connectToDashboard(deployment);
|
|
7060
|
+
} else if (key.name === "escape") {
|
|
7061
|
+
context.navigateTo("home");
|
|
7062
|
+
}
|
|
7063
|
+
} else if (viewState === "active") {
|
|
7064
|
+
if (key.sequence === "o" || key.sequence === "O") {
|
|
7065
|
+
if (dashboardUrl) openInBrowser(dashboardUrl);
|
|
7066
|
+
} else if (key.sequence === "c" || key.sequence === "C") {
|
|
7067
|
+
if (dashboardUrl) {
|
|
7068
|
+
renderer.copyToClipboardOSC52(dashboardUrl);
|
|
7069
|
+
setCopied(true);
|
|
7070
|
+
setTimeout(() => setCopied(false), 3e3);
|
|
7071
|
+
}
|
|
7072
|
+
} else if (key.sequence === "d" || key.sequence === "D") {
|
|
7073
|
+
if (activeTunnel) {
|
|
7074
|
+
stopTunnel(activeTunnel.deploymentName);
|
|
7075
|
+
setActiveTunnel(null);
|
|
7076
|
+
setDashboardUrl(null);
|
|
7077
|
+
setViewState("selecting");
|
|
7078
|
+
}
|
|
7079
|
+
} else if (key.name === "escape") {
|
|
7080
|
+
setViewState("selecting");
|
|
7081
|
+
}
|
|
7082
|
+
} else if (viewState === "error") {
|
|
7083
|
+
if (key.name === "escape" || key.name === "return") {
|
|
7084
|
+
setViewState("selecting");
|
|
7085
|
+
}
|
|
7086
|
+
}
|
|
7087
|
+
});
|
|
7088
|
+
if (deployedDeployments.length === 0) {
|
|
7089
|
+
return /* @__PURE__ */ jsxs12("box", { flexDirection: "column", width: "100%", padding: 1, children: [
|
|
7090
|
+
/* @__PURE__ */ jsxs12("box", { flexDirection: "row", marginBottom: 2, children: [
|
|
7091
|
+
/* @__PURE__ */ jsx12("text", { fg: t.accent, children: "/dashboard" }),
|
|
7092
|
+
/* @__PURE__ */ jsx12("text", { fg: t.fg.secondary, children: " - Open OpenClaw dashboard" })
|
|
7093
|
+
] }),
|
|
7094
|
+
/* @__PURE__ */ jsxs12(
|
|
7095
|
+
"box",
|
|
7096
|
+
{
|
|
7097
|
+
flexDirection: "column",
|
|
7098
|
+
borderStyle: "single",
|
|
7099
|
+
borderColor: t.border.default,
|
|
7100
|
+
padding: 1,
|
|
7101
|
+
children: [
|
|
7102
|
+
/* @__PURE__ */ jsx12("text", { fg: t.status.warning, children: "No deployed instances found!" }),
|
|
7103
|
+
/* @__PURE__ */ jsx12("text", { fg: t.fg.secondary, marginTop: 1, children: "Deploy an instance first with /deploy" })
|
|
7104
|
+
]
|
|
7105
|
+
}
|
|
7106
|
+
),
|
|
7107
|
+
/* @__PURE__ */ jsx12("text", { fg: t.fg.muted, marginTop: 2, children: "Press any key to return to home" })
|
|
7108
|
+
] });
|
|
7109
|
+
}
|
|
7110
|
+
if (viewState === "selecting") {
|
|
7111
|
+
const tunnels = getActiveTunnels();
|
|
7112
|
+
return /* @__PURE__ */ jsxs12("box", { flexDirection: "column", width: "100%", padding: 1, children: [
|
|
7113
|
+
/* @__PURE__ */ jsxs12("box", { flexDirection: "row", marginBottom: 2, children: [
|
|
7114
|
+
/* @__PURE__ */ jsx12("text", { fg: t.accent, children: "/dashboard" }),
|
|
7115
|
+
/* @__PURE__ */ jsxs12("text", { fg: t.fg.secondary, children: [
|
|
7116
|
+
" ",
|
|
7117
|
+
"- Select a deployment to open its dashboard"
|
|
7118
|
+
] })
|
|
7119
|
+
] }),
|
|
7120
|
+
tunnels.length > 0 && /* @__PURE__ */ jsxs12(
|
|
7121
|
+
"box",
|
|
7122
|
+
{
|
|
7123
|
+
flexDirection: "column",
|
|
7124
|
+
borderStyle: "single",
|
|
7125
|
+
borderColor: t.status.success,
|
|
7126
|
+
padding: 1,
|
|
7127
|
+
marginBottom: 1,
|
|
7128
|
+
children: [
|
|
7129
|
+
/* @__PURE__ */ jsxs12("text", { fg: t.status.success, children: [
|
|
7130
|
+
tunnels.length,
|
|
7131
|
+
" active tunnel",
|
|
7132
|
+
tunnels.length > 1 ? "s" : ""
|
|
7133
|
+
] }),
|
|
7134
|
+
tunnels.map((tun) => /* @__PURE__ */ jsxs12("text", { fg: t.fg.secondary, children: [
|
|
7135
|
+
tun.deploymentName,
|
|
7136
|
+
": 127.0.0.1:",
|
|
7137
|
+
tun.localPort,
|
|
7138
|
+
" \u2192",
|
|
7139
|
+
" ",
|
|
7140
|
+
tun.serverIp,
|
|
7141
|
+
":",
|
|
7142
|
+
tun.remotePort
|
|
7143
|
+
] }, tun.deploymentName))
|
|
7144
|
+
]
|
|
7145
|
+
}
|
|
7146
|
+
),
|
|
7147
|
+
/* @__PURE__ */ jsx12(
|
|
7148
|
+
"box",
|
|
7149
|
+
{
|
|
7150
|
+
flexDirection: "column",
|
|
7151
|
+
borderStyle: "single",
|
|
7152
|
+
borderColor: t.border.default,
|
|
7153
|
+
padding: 1,
|
|
7154
|
+
marginBottom: 1,
|
|
7155
|
+
children: deployedDeployments.map((deployment, index) => {
|
|
7156
|
+
const isSelected = index === selectedIndex;
|
|
7157
|
+
const hasTunnel = getTunnel(deployment.config.name) !== null;
|
|
7158
|
+
return /* @__PURE__ */ jsxs12(
|
|
7159
|
+
"box",
|
|
7160
|
+
{
|
|
7161
|
+
flexDirection: "row",
|
|
7162
|
+
backgroundColor: isSelected ? t.selection.bg : void 0,
|
|
7163
|
+
children: [
|
|
7164
|
+
/* @__PURE__ */ jsx12("text", { fg: isSelected ? t.selection.fg : t.fg.secondary, children: isSelected ? "> " : " " }),
|
|
7165
|
+
/* @__PURE__ */ jsx12(
|
|
7166
|
+
"text",
|
|
7167
|
+
{
|
|
7168
|
+
fg: isSelected ? t.selection.fg : t.fg.primary,
|
|
7169
|
+
width: 25,
|
|
7170
|
+
children: deployment.config.name
|
|
7171
|
+
}
|
|
7172
|
+
),
|
|
7173
|
+
/* @__PURE__ */ jsx12("text", { fg: t.accent, width: 18, children: deployment.state.serverIp }),
|
|
7174
|
+
hasTunnel && /* @__PURE__ */ jsx12("text", { fg: t.status.success, children: "[tunnel active]" })
|
|
7175
|
+
]
|
|
7176
|
+
},
|
|
7177
|
+
deployment.config.name
|
|
7178
|
+
);
|
|
7179
|
+
})
|
|
7180
|
+
}
|
|
7181
|
+
),
|
|
7182
|
+
/* @__PURE__ */ jsx12("text", { fg: t.fg.muted, children: "Arrow keys to select | Enter to connect | Esc to go back" })
|
|
7183
|
+
] });
|
|
7184
|
+
}
|
|
7185
|
+
if (viewState === "connecting") {
|
|
7186
|
+
return /* @__PURE__ */ jsxs12("box", { flexDirection: "column", width: "100%", padding: 1, children: [
|
|
7187
|
+
/* @__PURE__ */ jsxs12("box", { flexDirection: "row", marginBottom: 2, children: [
|
|
7188
|
+
/* @__PURE__ */ jsx12("text", { fg: t.accent, children: "/dashboard" }),
|
|
7189
|
+
/* @__PURE__ */ jsx12("text", { fg: t.fg.secondary, children: " - Connecting..." })
|
|
7190
|
+
] }),
|
|
7191
|
+
/* @__PURE__ */ jsxs12(
|
|
7192
|
+
"box",
|
|
7193
|
+
{
|
|
7194
|
+
flexDirection: "column",
|
|
7195
|
+
borderStyle: "single",
|
|
7196
|
+
borderColor: t.border.focus,
|
|
7197
|
+
padding: 1,
|
|
7198
|
+
children: [
|
|
7199
|
+
/* @__PURE__ */ jsx12("text", { fg: t.status.info, children: connectMessage }),
|
|
7200
|
+
/* @__PURE__ */ jsx12("text", { fg: t.fg.muted, marginTop: 1, children: "This may take a few seconds..." })
|
|
7201
|
+
]
|
|
7202
|
+
}
|
|
7203
|
+
)
|
|
7204
|
+
] });
|
|
7205
|
+
}
|
|
7206
|
+
if (viewState === "active" && activeTunnel && dashboardUrl) {
|
|
7207
|
+
return /* @__PURE__ */ jsxs12("box", { flexDirection: "column", width: "100%", padding: 1, children: [
|
|
7208
|
+
/* @__PURE__ */ jsxs12("box", { flexDirection: "row", marginBottom: 2, children: [
|
|
7209
|
+
/* @__PURE__ */ jsx12("text", { fg: t.status.success, children: "/dashboard" }),
|
|
7210
|
+
/* @__PURE__ */ jsxs12("text", { fg: t.fg.secondary, children: [
|
|
7211
|
+
" ",
|
|
7212
|
+
"- ",
|
|
7213
|
+
activeTunnel.deploymentName
|
|
7214
|
+
] })
|
|
7215
|
+
] }),
|
|
7216
|
+
/* @__PURE__ */ jsxs12(
|
|
7217
|
+
"box",
|
|
7218
|
+
{
|
|
7219
|
+
flexDirection: "column",
|
|
7220
|
+
borderStyle: "double",
|
|
7221
|
+
borderColor: t.status.success,
|
|
7222
|
+
padding: 1,
|
|
7223
|
+
marginBottom: 1,
|
|
7224
|
+
children: [
|
|
7225
|
+
/* @__PURE__ */ jsx12("text", { fg: t.status.success, children: "Dashboard Ready" }),
|
|
7226
|
+
/* @__PURE__ */ jsxs12("text", { fg: t.fg.primary, marginTop: 1, children: [
|
|
7227
|
+
"Tunnel: 127.0.0.1:",
|
|
7228
|
+
activeTunnel.localPort,
|
|
7229
|
+
" \u2192",
|
|
7230
|
+
" ",
|
|
7231
|
+
activeTunnel.serverIp,
|
|
7232
|
+
":",
|
|
7233
|
+
activeTunnel.remotePort
|
|
7234
|
+
] })
|
|
7235
|
+
]
|
|
7236
|
+
}
|
|
7237
|
+
),
|
|
7238
|
+
/* @__PURE__ */ jsxs12(
|
|
7239
|
+
"box",
|
|
7240
|
+
{
|
|
7241
|
+
flexDirection: "column",
|
|
7242
|
+
borderStyle: "single",
|
|
7243
|
+
borderColor: t.accent,
|
|
7244
|
+
padding: 1,
|
|
7245
|
+
marginBottom: 1,
|
|
7246
|
+
children: [
|
|
7247
|
+
/* @__PURE__ */ jsx12("text", { fg: t.fg.secondary, children: "Dashboard URL:" }),
|
|
7248
|
+
/* @__PURE__ */ jsx12("text", { fg: t.accent, marginTop: 1, children: dashboardUrl })
|
|
7249
|
+
]
|
|
7250
|
+
}
|
|
7251
|
+
),
|
|
7252
|
+
/* @__PURE__ */ jsxs12("box", { flexDirection: "column", marginBottom: 1, children: [
|
|
7253
|
+
/* @__PURE__ */ jsxs12("box", { flexDirection: "row", children: [
|
|
7254
|
+
/* @__PURE__ */ jsx12("text", { fg: t.accent, width: 4, children: "O" }),
|
|
7255
|
+
/* @__PURE__ */ jsx12("text", { fg: t.fg.primary, children: "Open in browser" })
|
|
7256
|
+
] }),
|
|
7257
|
+
/* @__PURE__ */ jsxs12("box", { flexDirection: "row", children: [
|
|
7258
|
+
/* @__PURE__ */ jsx12("text", { fg: t.accent, width: 4, children: "C" }),
|
|
7259
|
+
/* @__PURE__ */ jsx12("text", { fg: t.fg.primary, children: "Copy URL to clipboard" })
|
|
7260
|
+
] }),
|
|
7261
|
+
/* @__PURE__ */ jsxs12("box", { flexDirection: "row", children: [
|
|
7262
|
+
/* @__PURE__ */ jsx12("text", { fg: t.accent, width: 4, children: "D" }),
|
|
7263
|
+
/* @__PURE__ */ jsx12("text", { fg: t.fg.primary, children: "Disconnect tunnel" })
|
|
7264
|
+
] }),
|
|
7265
|
+
/* @__PURE__ */ jsxs12("box", { flexDirection: "row", children: [
|
|
7266
|
+
/* @__PURE__ */ jsx12("text", { fg: t.accent, width: 4, children: "Esc" }),
|
|
7267
|
+
/* @__PURE__ */ jsx12("text", { fg: t.fg.primary, children: "Back to selection (tunnel stays active)" })
|
|
7268
|
+
] })
|
|
7269
|
+
] }),
|
|
7270
|
+
copied && /* @__PURE__ */ jsx12("text", { fg: t.status.success, children: "URL copied to clipboard!" })
|
|
7271
|
+
] });
|
|
7272
|
+
}
|
|
7273
|
+
if (viewState === "error") {
|
|
7274
|
+
return /* @__PURE__ */ jsxs12("box", { flexDirection: "column", width: "100%", padding: 1, children: [
|
|
7275
|
+
/* @__PURE__ */ jsxs12("box", { flexDirection: "row", marginBottom: 2, children: [
|
|
7276
|
+
/* @__PURE__ */ jsx12("text", { fg: t.status.error, children: "/dashboard" }),
|
|
7277
|
+
/* @__PURE__ */ jsx12("text", { fg: t.fg.secondary, children: " - Connection failed" })
|
|
7278
|
+
] }),
|
|
7279
|
+
/* @__PURE__ */ jsx12(
|
|
7280
|
+
"box",
|
|
7281
|
+
{
|
|
7282
|
+
flexDirection: "column",
|
|
7283
|
+
borderStyle: "single",
|
|
7284
|
+
borderColor: t.status.error,
|
|
7285
|
+
padding: 1,
|
|
7286
|
+
marginBottom: 1,
|
|
7287
|
+
children: /* @__PURE__ */ jsx12("text", { fg: t.status.error, children: error })
|
|
7288
|
+
}
|
|
7289
|
+
),
|
|
7290
|
+
/* @__PURE__ */ jsx12("text", { fg: t.fg.muted, children: "Press Enter or Esc to go back" })
|
|
7291
|
+
] });
|
|
7292
|
+
}
|
|
7293
|
+
return null;
|
|
7294
|
+
}
|
|
7295
|
+
|
|
6338
7296
|
// src/App.tsx
|
|
6339
|
-
import { jsx as
|
|
7297
|
+
import { jsx as jsx13 } from "@opentui/react/jsx-runtime";
|
|
6340
7298
|
function App() {
|
|
6341
|
-
const renderer =
|
|
6342
|
-
const [currentView, setCurrentView] =
|
|
6343
|
-
const [selectedDeployment, setSelectedDeployment] =
|
|
6344
|
-
const [deployments, setDeployments] =
|
|
7299
|
+
const renderer = useRenderer2();
|
|
7300
|
+
const [currentView, setCurrentView] = useState12("home");
|
|
7301
|
+
const [selectedDeployment, setSelectedDeployment] = useState12(null);
|
|
7302
|
+
const [deployments, setDeployments] = useState12(() => {
|
|
6345
7303
|
try {
|
|
6346
7304
|
return getAllDeployments();
|
|
6347
7305
|
} catch {
|
|
6348
7306
|
return [];
|
|
6349
7307
|
}
|
|
6350
7308
|
});
|
|
6351
|
-
const [selectedTemplate, setSelectedTemplate] =
|
|
6352
|
-
const
|
|
6353
|
-
const
|
|
7309
|
+
const [selectedTemplate, setSelectedTemplate] = useState12(null);
|
|
7310
|
+
const [editingDeployment, setEditingDeployment] = useState12(null);
|
|
7311
|
+
const wasDraggingRef = useRef6(false);
|
|
7312
|
+
const handleMouseDrag = useCallback4(() => {
|
|
6354
7313
|
wasDraggingRef.current = true;
|
|
6355
7314
|
}, []);
|
|
6356
|
-
const handleMouseUp =
|
|
7315
|
+
const handleMouseUp = useCallback4(() => {
|
|
6357
7316
|
if (!wasDraggingRef.current) return;
|
|
6358
7317
|
wasDraggingRef.current = false;
|
|
6359
7318
|
const selection = renderer.getSelection();
|
|
@@ -6364,14 +7323,14 @@ function App() {
|
|
|
6364
7323
|
}
|
|
6365
7324
|
}
|
|
6366
7325
|
}, [renderer]);
|
|
6367
|
-
const refreshDeployments =
|
|
7326
|
+
const refreshDeployments = useCallback4(() => {
|
|
6368
7327
|
try {
|
|
6369
7328
|
setDeployments(getAllDeployments());
|
|
6370
7329
|
} catch {
|
|
6371
7330
|
setDeployments([]);
|
|
6372
7331
|
}
|
|
6373
7332
|
}, []);
|
|
6374
|
-
const navigateTo =
|
|
7333
|
+
const navigateTo = useCallback4((view, deployment) => {
|
|
6375
7334
|
if (deployment !== void 0) {
|
|
6376
7335
|
setSelectedDeployment(deployment);
|
|
6377
7336
|
}
|
|
@@ -6384,35 +7343,41 @@ function App() {
|
|
|
6384
7343
|
deployments,
|
|
6385
7344
|
refreshDeployments,
|
|
6386
7345
|
selectedTemplate,
|
|
6387
|
-
setSelectedTemplate
|
|
7346
|
+
setSelectedTemplate,
|
|
7347
|
+
editingDeployment,
|
|
7348
|
+
setEditingDeployment
|
|
6388
7349
|
};
|
|
6389
7350
|
const renderView = () => {
|
|
6390
7351
|
switch (currentView) {
|
|
6391
7352
|
case "home":
|
|
6392
|
-
return /* @__PURE__ */
|
|
7353
|
+
return /* @__PURE__ */ jsx13(Home, { context });
|
|
6393
7354
|
case "new":
|
|
6394
|
-
return /* @__PURE__ */
|
|
7355
|
+
return /* @__PURE__ */ jsx13(NewDeployment, { context });
|
|
7356
|
+
case "list":
|
|
7357
|
+
return /* @__PURE__ */ jsx13(ListView, { context });
|
|
6395
7358
|
case "deploy":
|
|
6396
|
-
return /* @__PURE__ */
|
|
7359
|
+
return /* @__PURE__ */ jsx13(DeployView, { context });
|
|
6397
7360
|
case "deploying":
|
|
6398
|
-
return /* @__PURE__ */
|
|
7361
|
+
return /* @__PURE__ */ jsx13(DeployingView, { context });
|
|
6399
7362
|
case "status":
|
|
6400
|
-
return /* @__PURE__ */
|
|
7363
|
+
return /* @__PURE__ */ jsx13(StatusView, { context });
|
|
6401
7364
|
case "ssh":
|
|
6402
|
-
return /* @__PURE__ */
|
|
7365
|
+
return /* @__PURE__ */ jsx13(SSHView, { context });
|
|
6403
7366
|
case "logs":
|
|
6404
|
-
return /* @__PURE__ */
|
|
7367
|
+
return /* @__PURE__ */ jsx13(LogsView, { context });
|
|
7368
|
+
case "dashboard":
|
|
7369
|
+
return /* @__PURE__ */ jsx13(DashboardView, { context });
|
|
6405
7370
|
case "destroy":
|
|
6406
|
-
return /* @__PURE__ */
|
|
7371
|
+
return /* @__PURE__ */ jsx13(DestroyView, { context });
|
|
6407
7372
|
case "help":
|
|
6408
|
-
return /* @__PURE__ */
|
|
7373
|
+
return /* @__PURE__ */ jsx13(HelpView, { context });
|
|
6409
7374
|
case "templates":
|
|
6410
|
-
return /* @__PURE__ */
|
|
7375
|
+
return /* @__PURE__ */ jsx13(TemplatesView, { context });
|
|
6411
7376
|
default:
|
|
6412
|
-
return /* @__PURE__ */
|
|
7377
|
+
return /* @__PURE__ */ jsx13(Home, { context });
|
|
6413
7378
|
}
|
|
6414
7379
|
};
|
|
6415
|
-
return /* @__PURE__ */
|
|
7380
|
+
return /* @__PURE__ */ jsx13(
|
|
6416
7381
|
"scrollbox",
|
|
6417
7382
|
{
|
|
6418
7383
|
width: "100%",
|
|
@@ -6439,7 +7404,7 @@ function App() {
|
|
|
6439
7404
|
}
|
|
6440
7405
|
|
|
6441
7406
|
// src/index.tsx
|
|
6442
|
-
import { jsx as
|
|
7407
|
+
import { jsx as jsx14 } from "@opentui/react/jsx-runtime";
|
|
6443
7408
|
var args = process.argv.slice(2);
|
|
6444
7409
|
if (args.includes("--version") || args.includes("-v")) {
|
|
6445
7410
|
const require2 = createRequire(import.meta.url);
|
|
@@ -6473,6 +7438,6 @@ async function main() {
|
|
|
6473
7438
|
useMouse: true
|
|
6474
7439
|
});
|
|
6475
7440
|
const root = createRoot(renderer);
|
|
6476
|
-
root.render(/* @__PURE__ */
|
|
7441
|
+
root.render(/* @__PURE__ */ jsx14(App, {}));
|
|
6477
7442
|
}
|
|
6478
7443
|
main().catch(console.error);
|