clawcontrol 0.1.5 → 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/bin/clawcontrol.js +34 -1
- package/dist/index.js +1713 -719
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
// src/index.tsx
|
|
2
|
+
import { createRequire } from "module";
|
|
2
3
|
import { createCliRenderer } from "@opentui/core";
|
|
3
4
|
import { createRoot } from "@opentui/react";
|
|
4
5
|
|
|
5
6
|
// src/App.tsx
|
|
6
|
-
import { useState as
|
|
7
|
-
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";
|
|
8
9
|
|
|
9
10
|
// src/components/Home.tsx
|
|
10
11
|
import { useState } from "react";
|
|
@@ -141,10 +142,12 @@ var LOGO = `
|
|
|
141
142
|
`;
|
|
142
143
|
var COMMANDS = [
|
|
143
144
|
{ name: "/new", description: "Initialize a new deployment" },
|
|
145
|
+
{ name: "/list", description: "List, edit, or fork deployments" },
|
|
144
146
|
{ name: "/deploy", description: "Deploy an initialized configuration" },
|
|
145
147
|
{ name: "/status", description: "View deployment status" },
|
|
146
148
|
{ name: "/ssh", description: "SSH into a deployment" },
|
|
147
149
|
{ name: "/logs", description: "View deployment logs" },
|
|
150
|
+
{ name: "/dashboard", description: "Open OpenClaw dashboard in browser" },
|
|
148
151
|
{ name: "/destroy", description: "Destroy a deployment" },
|
|
149
152
|
{ name: "/templates", description: "Manage deployment templates" },
|
|
150
153
|
{ name: "/help", description: "Show help" }
|
|
@@ -157,10 +160,12 @@ function Home({ context }) {
|
|
|
157
160
|
setError(null);
|
|
158
161
|
const viewMap = {
|
|
159
162
|
"/new": "new",
|
|
163
|
+
"/list": "list",
|
|
160
164
|
"/deploy": "deploy",
|
|
161
165
|
"/status": "status",
|
|
162
166
|
"/ssh": "ssh",
|
|
163
167
|
"/logs": "logs",
|
|
168
|
+
"/dashboard": "dashboard",
|
|
164
169
|
"/destroy": "destroy",
|
|
165
170
|
"/templates": "templates",
|
|
166
171
|
"/help": "help"
|
|
@@ -222,7 +227,7 @@ function Home({ context }) {
|
|
|
222
227
|
children: [
|
|
223
228
|
/* @__PURE__ */ jsx("text", { fg: t.fg.primary, children: "Available Commands" }),
|
|
224
229
|
/* @__PURE__ */ jsx("box", { flexDirection: "column", marginTop: 1, children: COMMANDS.map((cmd) => /* @__PURE__ */ jsxs("box", { flexDirection: "row", children: [
|
|
225
|
-
/* @__PURE__ */ jsx("text", { fg: t.accent, width:
|
|
230
|
+
/* @__PURE__ */ jsx("text", { fg: t.accent, width: 14, children: cmd.name }),
|
|
226
231
|
/* @__PURE__ */ jsx("text", { fg: t.fg.secondary, children: cmd.description })
|
|
227
232
|
] }, cmd.name)) })
|
|
228
233
|
]
|
|
@@ -371,7 +376,8 @@ var DeploymentConfigSchema = z.object({
|
|
|
371
376
|
hetzner: HetznerConfigSchema.optional(),
|
|
372
377
|
digitalocean: DigitalOceanConfigSchema.optional(),
|
|
373
378
|
openclawConfig: OpenClawConfigSchema,
|
|
374
|
-
openclawAgent: OpenClawAgentConfigSchema.optional()
|
|
379
|
+
openclawAgent: OpenClawAgentConfigSchema.optional(),
|
|
380
|
+
skipTailscale: z.boolean().optional()
|
|
375
381
|
});
|
|
376
382
|
var DeploymentStateSchema = z.object({
|
|
377
383
|
status: DeploymentStatusSchema,
|
|
@@ -387,6 +393,7 @@ var DeploymentStateSchema = z.object({
|
|
|
387
393
|
retryCount: z.number()
|
|
388
394
|
})
|
|
389
395
|
),
|
|
396
|
+
gatewayToken: z.string().optional(),
|
|
390
397
|
lastError: z.string().optional(),
|
|
391
398
|
deployedAt: z.string().optional(),
|
|
392
399
|
updatedAt: z.string()
|
|
@@ -530,6 +537,14 @@ function deleteDeployment(name) {
|
|
|
530
537
|
}
|
|
531
538
|
rmSync(deploymentDir, { recursive: true, force: true });
|
|
532
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
|
+
}
|
|
533
548
|
function validateDeploymentName(name) {
|
|
534
549
|
if (!name || name.trim() === "") {
|
|
535
550
|
return { valid: false, error: "Deployment name cannot be empty" };
|
|
@@ -1195,20 +1210,36 @@ var DO_DROPLET_SIZES = [
|
|
|
1195
1210
|
{ slug: "s-4vcpu-8gb", label: "4 vCPU, 8GB RAM, 160GB SSD", price: "$48/mo" },
|
|
1196
1211
|
{ slug: "s-8vcpu-16gb", label: "8 vCPU, 16GB RAM, 320GB SSD", price: "$96/mo" }
|
|
1197
1212
|
];
|
|
1198
|
-
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
|
+
}
|
|
1199
1230
|
const base = ["template_choice"];
|
|
1200
1231
|
if (!activeTemplate) {
|
|
1201
1232
|
base.push("name", "provider", "api_key_choose");
|
|
1202
1233
|
if (provider === "digitalocean") {
|
|
1203
1234
|
base.push("droplet_size");
|
|
1204
1235
|
}
|
|
1205
|
-
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");
|
|
1206
1237
|
} else {
|
|
1207
1238
|
base.push("name", "api_key_choose");
|
|
1208
1239
|
if (activeTemplate.provider === "digitalocean") {
|
|
1209
1240
|
base.push("droplet_size");
|
|
1210
1241
|
}
|
|
1211
|
-
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");
|
|
1212
1243
|
}
|
|
1213
1244
|
return base;
|
|
1214
1245
|
}
|
|
@@ -1235,19 +1266,39 @@ function resolveStepForProgress(s) {
|
|
|
1235
1266
|
}
|
|
1236
1267
|
}
|
|
1237
1268
|
function NewDeployment({ context }) {
|
|
1269
|
+
const [editingConfig, setEditingConfig] = useState2(null);
|
|
1270
|
+
const [editMode, setEditMode] = useState2(null);
|
|
1238
1271
|
const [templateChoices, setTemplateChoices] = useState2([]);
|
|
1239
1272
|
const [selectedTemplateIndex, setSelectedTemplateIndex] = useState2(0);
|
|
1240
1273
|
const [activeTemplate, setActiveTemplate] = useState2(null);
|
|
1241
1274
|
const [step, setStep] = useState2(() => {
|
|
1275
|
+
if (context.editingDeployment) {
|
|
1276
|
+
return context.editingDeployment.mode === "edit" ? "api_key_choose" : "name";
|
|
1277
|
+
}
|
|
1242
1278
|
if (context.selectedTemplate) return "name";
|
|
1243
1279
|
return "template_choice";
|
|
1244
1280
|
});
|
|
1245
|
-
const [name, setName] = useState2(
|
|
1281
|
+
const [name, setName] = useState2(() => {
|
|
1282
|
+
if (context.editingDeployment?.mode === "edit") return context.editingDeployment.config.name;
|
|
1283
|
+
return "";
|
|
1284
|
+
});
|
|
1246
1285
|
const [provider, setProvider] = useState2(() => {
|
|
1286
|
+
if (context.editingDeployment) return context.editingDeployment.config.provider;
|
|
1247
1287
|
return context.selectedTemplate?.provider ?? "hetzner";
|
|
1248
1288
|
});
|
|
1249
|
-
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
|
+
});
|
|
1250
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
|
+
}
|
|
1251
1302
|
if (context.selectedTemplate?.digitalocean) {
|
|
1252
1303
|
const idx = DO_DROPLET_SIZES.findIndex((s) => s.slug === context.selectedTemplate.digitalocean.size);
|
|
1253
1304
|
return idx >= 0 ? idx : 0;
|
|
@@ -1255,17 +1306,40 @@ function NewDeployment({ context }) {
|
|
|
1255
1306
|
return 0;
|
|
1256
1307
|
});
|
|
1257
1308
|
const [aiProvider, setAiProvider] = useState2(() => {
|
|
1309
|
+
if (context.editingDeployment?.config.openclawAgent) return context.editingDeployment.config.openclawAgent.aiProvider;
|
|
1258
1310
|
return context.selectedTemplate?.aiProvider ?? "";
|
|
1259
1311
|
});
|
|
1260
|
-
const [aiApiKey, setAiApiKey] = useState2(
|
|
1312
|
+
const [aiApiKey, setAiApiKey] = useState2(() => {
|
|
1313
|
+
if (context.editingDeployment?.config.openclawAgent) return context.editingDeployment.config.openclawAgent.aiApiKey;
|
|
1314
|
+
return "";
|
|
1315
|
+
});
|
|
1261
1316
|
const [model, setModel] = useState2(() => {
|
|
1317
|
+
if (context.editingDeployment?.config.openclawAgent) return context.editingDeployment.config.openclawAgent.model;
|
|
1262
1318
|
return context.selectedTemplate?.model ?? "";
|
|
1263
1319
|
});
|
|
1264
|
-
const [telegramBotToken, setTelegramBotToken] = useState2(
|
|
1265
|
-
|
|
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
|
+
});
|
|
1266
1336
|
const [error, setError] = useState2(null);
|
|
1267
1337
|
const [isValidating, setIsValidating] = useState2(false);
|
|
1268
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
|
+
}
|
|
1269
1343
|
if (context.selectedTemplate) {
|
|
1270
1344
|
const idx = SUPPORTED_PROVIDERS.indexOf(context.selectedTemplate.provider);
|
|
1271
1345
|
return idx >= 0 ? idx : 0;
|
|
@@ -1273,6 +1347,10 @@ function NewDeployment({ context }) {
|
|
|
1273
1347
|
return 0;
|
|
1274
1348
|
});
|
|
1275
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
|
+
}
|
|
1276
1354
|
if (context.selectedTemplate) {
|
|
1277
1355
|
const idx = AI_PROVIDERS.findIndex((p) => p.name === context.selectedTemplate.aiProvider);
|
|
1278
1356
|
return idx >= 0 ? idx : 0;
|
|
@@ -1285,12 +1363,39 @@ function NewDeployment({ context }) {
|
|
|
1285
1363
|
const [savedTelegramUsers, setSavedTelegramUsers] = useState2([]);
|
|
1286
1364
|
const [selectedSavedKeyIndex, setSelectedSavedKeyIndex] = useState2(0);
|
|
1287
1365
|
const [newKeyName, setNewKeyName] = useState2("");
|
|
1288
|
-
const [apiKeyFromSaved, setApiKeyFromSaved] = useState2(
|
|
1289
|
-
const [aiApiKeyFromSaved, setAiApiKeyFromSaved] = useState2(
|
|
1290
|
-
const [telegramTokenFromSaved, setTelegramTokenFromSaved] = useState2(
|
|
1291
|
-
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);
|
|
1292
1370
|
useEffect(() => {
|
|
1293
|
-
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) {
|
|
1294
1399
|
setActiveTemplate(context.selectedTemplate);
|
|
1295
1400
|
context.setSelectedTemplate(null);
|
|
1296
1401
|
}
|
|
@@ -1335,6 +1440,8 @@ function NewDeployment({ context }) {
|
|
|
1335
1440
|
step,
|
|
1336
1441
|
selectedDropletSizeIndex,
|
|
1337
1442
|
activeTemplate,
|
|
1443
|
+
editingConfig,
|
|
1444
|
+
editMode,
|
|
1338
1445
|
savedProviderKeys,
|
|
1339
1446
|
savedAiKeys,
|
|
1340
1447
|
savedTelegramTokens,
|
|
@@ -1344,7 +1451,9 @@ function NewDeployment({ context }) {
|
|
|
1344
1451
|
apiKeyFromSaved,
|
|
1345
1452
|
aiApiKeyFromSaved,
|
|
1346
1453
|
telegramTokenFromSaved,
|
|
1347
|
-
telegramAllowFromSaved
|
|
1454
|
+
telegramAllowFromSaved,
|
|
1455
|
+
enableTailscale,
|
|
1456
|
+
selectedTailscaleIndex
|
|
1348
1457
|
});
|
|
1349
1458
|
stateRef.current = {
|
|
1350
1459
|
name,
|
|
@@ -1358,6 +1467,8 @@ function NewDeployment({ context }) {
|
|
|
1358
1467
|
step,
|
|
1359
1468
|
selectedDropletSizeIndex,
|
|
1360
1469
|
activeTemplate,
|
|
1470
|
+
editingConfig,
|
|
1471
|
+
editMode,
|
|
1361
1472
|
savedProviderKeys,
|
|
1362
1473
|
savedAiKeys,
|
|
1363
1474
|
savedTelegramTokens,
|
|
@@ -1367,10 +1478,12 @@ function NewDeployment({ context }) {
|
|
|
1367
1478
|
apiKeyFromSaved,
|
|
1368
1479
|
aiApiKeyFromSaved,
|
|
1369
1480
|
telegramTokenFromSaved,
|
|
1370
|
-
telegramAllowFromSaved
|
|
1481
|
+
telegramAllowFromSaved,
|
|
1482
|
+
enableTailscale,
|
|
1483
|
+
selectedTailscaleIndex
|
|
1371
1484
|
};
|
|
1372
1485
|
debugLog(`RENDER: step=${step}, apiKey.length=${apiKey?.length ?? "null"}`);
|
|
1373
|
-
const stepList = getStepList(provider, activeTemplate);
|
|
1486
|
+
const stepList = getStepList(provider, activeTemplate, editMode ?? void 0);
|
|
1374
1487
|
const handleConfirmFromRef = () => {
|
|
1375
1488
|
const s = stateRef.current;
|
|
1376
1489
|
debugLog(`handleConfirmFromRef CALLED: apiKey.length=${s.apiKey?.length ?? "null"}`);
|
|
@@ -1409,7 +1522,7 @@ function NewDeployment({ context }) {
|
|
|
1409
1522
|
const config = {
|
|
1410
1523
|
name: s.name,
|
|
1411
1524
|
provider: s.provider,
|
|
1412
|
-
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1525
|
+
createdAt: s.editingConfig?.createdAt ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
1413
1526
|
hetzner: s.provider === "hetzner" ? {
|
|
1414
1527
|
apiKey: s.apiKey,
|
|
1415
1528
|
serverType: tmpl?.hetzner?.serverType ?? "cpx11",
|
|
@@ -1423,6 +1536,7 @@ function NewDeployment({ context }) {
|
|
|
1423
1536
|
image: tmpl?.digitalocean?.image ?? "ubuntu-24-04-x64"
|
|
1424
1537
|
} : void 0,
|
|
1425
1538
|
openclawConfig: void 0,
|
|
1539
|
+
skipTailscale: !s.enableTailscale,
|
|
1426
1540
|
openclawAgent: {
|
|
1427
1541
|
aiProvider: s.aiProvider,
|
|
1428
1542
|
aiApiKey: s.aiApiKey,
|
|
@@ -1432,11 +1546,16 @@ function NewDeployment({ context }) {
|
|
|
1432
1546
|
telegramAllowFrom: s.telegramAllowFrom
|
|
1433
1547
|
}
|
|
1434
1548
|
};
|
|
1435
|
-
|
|
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
|
+
}
|
|
1436
1555
|
context.refreshDeployments();
|
|
1437
1556
|
setStep("complete");
|
|
1438
1557
|
} catch (err) {
|
|
1439
|
-
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)}`);
|
|
1440
1559
|
}
|
|
1441
1560
|
};
|
|
1442
1561
|
useKeyboard((key) => {
|
|
@@ -1628,7 +1747,7 @@ function NewDeployment({ context }) {
|
|
|
1628
1747
|
setTelegramAllowFrom(saved.value);
|
|
1629
1748
|
setTelegramAllowFromSaved(true);
|
|
1630
1749
|
setError(null);
|
|
1631
|
-
setStep("confirm");
|
|
1750
|
+
setStep(currentState.editMode === "edit" ? "confirm" : "tailscale");
|
|
1632
1751
|
}
|
|
1633
1752
|
} else if (key.name === "escape") {
|
|
1634
1753
|
setStep("telegram_token_choose");
|
|
@@ -1665,13 +1784,26 @@ function NewDeployment({ context }) {
|
|
|
1665
1784
|
setStep("telegram_allow_name");
|
|
1666
1785
|
setNewKeyName("");
|
|
1667
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);
|
|
1668
1798
|
setStep("confirm");
|
|
1799
|
+
} else if (key.name === "escape") {
|
|
1800
|
+
setStep("telegram_allow_choose");
|
|
1669
1801
|
}
|
|
1670
1802
|
} else if (currentState.step === "confirm") {
|
|
1671
1803
|
if (key.name === "y" || key.name === "return") {
|
|
1672
1804
|
handleConfirmFromRef();
|
|
1673
1805
|
} else if (key.name === "n" || key.name === "escape") {
|
|
1674
|
-
setStep("telegram_allow_choose");
|
|
1806
|
+
setStep(currentState.editMode === "edit" ? "telegram_allow_choose" : "tailscale");
|
|
1675
1807
|
}
|
|
1676
1808
|
} else if (currentState.step === "complete") {
|
|
1677
1809
|
context.navigateTo("home");
|
|
@@ -1793,7 +1925,7 @@ function NewDeployment({ context }) {
|
|
|
1793
1925
|
}
|
|
1794
1926
|
setError(null);
|
|
1795
1927
|
if (telegramAllowFromSaved) {
|
|
1796
|
-
setStep("confirm");
|
|
1928
|
+
setStep(editMode === "edit" ? "confirm" : "tailscale");
|
|
1797
1929
|
} else {
|
|
1798
1930
|
setStep("telegram_allow_save");
|
|
1799
1931
|
}
|
|
@@ -2507,10 +2639,10 @@ function NewDeployment({ context }) {
|
|
|
2507
2639
|
setNewKeyName(value);
|
|
2508
2640
|
}
|
|
2509
2641
|
},
|
|
2510
|
-
onSubmit: () => handleSaveKeyName("telegram-user", telegramAllowFrom, "confirm"),
|
|
2642
|
+
onSubmit: () => handleSaveKeyName("telegram-user", telegramAllowFrom, editMode === "edit" ? "confirm" : "tailscale"),
|
|
2511
2643
|
onKeyDown: (e) => {
|
|
2512
2644
|
if (e.name === "escape") {
|
|
2513
|
-
setStep("confirm");
|
|
2645
|
+
setStep(editMode === "edit" ? "confirm" : "tailscale");
|
|
2514
2646
|
}
|
|
2515
2647
|
}
|
|
2516
2648
|
}
|
|
@@ -2518,6 +2650,41 @@ function NewDeployment({ context }) {
|
|
|
2518
2650
|
error && /* @__PURE__ */ jsx2("text", { fg: t.status.error, marginTop: 1, children: error }),
|
|
2519
2651
|
/* @__PURE__ */ jsx2("text", { fg: t.fg.muted, marginTop: 2, children: "Press Enter to save, Esc to skip" })
|
|
2520
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
|
+
}
|
|
2521
2688
|
case "confirm":
|
|
2522
2689
|
return /* @__PURE__ */ jsxs2("box", { flexDirection: "column", children: [
|
|
2523
2690
|
/* @__PURE__ */ jsxs2("text", { fg: t.accent, children: [
|
|
@@ -2577,6 +2744,10 @@ function NewDeployment({ context }) {
|
|
|
2577
2744
|
/* @__PURE__ */ jsxs2("box", { flexDirection: "row", children: [
|
|
2578
2745
|
/* @__PURE__ */ jsx2("text", { fg: t.fg.secondary, width: 20, children: "Allow From:" }),
|
|
2579
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" })
|
|
2580
2751
|
] })
|
|
2581
2752
|
]
|
|
2582
2753
|
}
|
|
@@ -2586,7 +2757,7 @@ function NewDeployment({ context }) {
|
|
|
2586
2757
|
] });
|
|
2587
2758
|
case "complete":
|
|
2588
2759
|
return /* @__PURE__ */ jsxs2("box", { flexDirection: "column", children: [
|
|
2589
|
-
/* @__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!" }),
|
|
2590
2761
|
/* @__PURE__ */ jsxs2(
|
|
2591
2762
|
"box",
|
|
2592
2763
|
{
|
|
@@ -2596,14 +2767,10 @@ function NewDeployment({ context }) {
|
|
|
2596
2767
|
padding: 1,
|
|
2597
2768
|
marginTop: 1,
|
|
2598
2769
|
children: [
|
|
2599
|
-
/* @__PURE__ */
|
|
2600
|
-
'Your deployment "',
|
|
2601
|
-
name,
|
|
2602
|
-
'" has been initialized.'
|
|
2603
|
-
] }),
|
|
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.` }),
|
|
2604
2771
|
/* @__PURE__ */ jsxs2("text", { fg: t.fg.secondary, marginTop: 1, children: [
|
|
2605
2772
|
"Configuration saved to: ~/.clawcontrol/deployments/",
|
|
2606
|
-
name,
|
|
2773
|
+
editingConfig?.name ?? name,
|
|
2607
2774
|
"/"
|
|
2608
2775
|
] }),
|
|
2609
2776
|
/* @__PURE__ */ jsxs2("text", { fg: t.fg.secondary, marginTop: 1, children: [
|
|
@@ -2620,15 +2787,15 @@ function NewDeployment({ context }) {
|
|
|
2620
2787
|
]
|
|
2621
2788
|
}
|
|
2622
2789
|
),
|
|
2623
|
-
/* @__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" }),
|
|
2624
2791
|
/* @__PURE__ */ jsx2("text", { fg: t.fg.muted, marginTop: 2, children: "Press any key to return to home" })
|
|
2625
2792
|
] });
|
|
2626
2793
|
}
|
|
2627
2794
|
};
|
|
2628
2795
|
return /* @__PURE__ */ jsxs2("box", { flexDirection: "column", width: "100%", padding: 1, children: [
|
|
2629
2796
|
/* @__PURE__ */ jsxs2("box", { flexDirection: "row", marginBottom: 2, children: [
|
|
2630
|
-
/* @__PURE__ */ jsx2("text", { fg: t.accent, children: "/new" }),
|
|
2631
|
-
/* @__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" })
|
|
2632
2799
|
] }),
|
|
2633
2800
|
/* @__PURE__ */ jsx2("box", { flexDirection: "row", marginBottom: 2, children: stepList.map((s, i) => {
|
|
2634
2801
|
const resolvedStep = resolveStepForProgress(step);
|
|
@@ -2643,46 +2810,81 @@ function NewDeployment({ context }) {
|
|
|
2643
2810
|
] });
|
|
2644
2811
|
}
|
|
2645
2812
|
|
|
2646
|
-
// src/components/
|
|
2647
|
-
import { useState as useState3 } from "react";
|
|
2813
|
+
// src/components/ListView.tsx
|
|
2814
|
+
import { useState as useState3, useRef as useRef2 } from "react";
|
|
2648
2815
|
import { useKeyboard as useKeyboard2 } from "@opentui/react";
|
|
2649
|
-
import { jsx as jsx3, jsxs as jsxs3 } from "@opentui/react/jsx-runtime";
|
|
2650
|
-
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");
|
|
2651
2819
|
const [selectedIndex, setSelectedIndex] = useState3(0);
|
|
2652
|
-
const [
|
|
2820
|
+
const [confirmText, setConfirmText] = useState3("");
|
|
2821
|
+
const [error, setError] = useState3(null);
|
|
2822
|
+
const [deletedName, setDeletedName] = useState3("");
|
|
2653
2823
|
const deployments = context.deployments;
|
|
2654
|
-
const
|
|
2655
|
-
const
|
|
2656
|
-
|
|
2657
|
-
const selectedDeployment = allDeployments[selectedIndex];
|
|
2824
|
+
const selectedDeployment = deployments[selectedIndex];
|
|
2825
|
+
const stateRef = useRef2({ viewState, selectedIndex });
|
|
2826
|
+
stateRef.current = { viewState, selectedIndex };
|
|
2658
2827
|
useKeyboard2((key) => {
|
|
2659
|
-
|
|
2828
|
+
const current = stateRef.current;
|
|
2829
|
+
if (deployments.length === 0) {
|
|
2660
2830
|
context.navigateTo("home");
|
|
2661
2831
|
return;
|
|
2662
2832
|
}
|
|
2663
|
-
if (
|
|
2664
|
-
if (key.name === "
|
|
2665
|
-
|
|
2666
|
-
} else if (key.name === "
|
|
2667
|
-
|
|
2668
|
-
}
|
|
2669
|
-
} else {
|
|
2670
|
-
if (key.name === "up" && selectedIndex > 0) {
|
|
2671
|
-
setSelectedIndex(selectedIndex - 1);
|
|
2672
|
-
} else if (key.name === "down" && selectedIndex < allDeployments.length - 1) {
|
|
2673
|
-
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);
|
|
2674
2838
|
} else if (key.name === "return") {
|
|
2675
|
-
|
|
2839
|
+
setViewState("detail");
|
|
2676
2840
|
} else if (key.name === "escape") {
|
|
2677
2841
|
context.navigateTo("home");
|
|
2678
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");
|
|
2679
2869
|
}
|
|
2680
2870
|
});
|
|
2681
|
-
|
|
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) {
|
|
2682
2884
|
return /* @__PURE__ */ jsxs3("box", { flexDirection: "column", width: "100%", padding: 1, children: [
|
|
2683
2885
|
/* @__PURE__ */ jsxs3("box", { flexDirection: "row", marginBottom: 2, children: [
|
|
2684
|
-
/* @__PURE__ */ jsx3("text", { fg: t.accent, children: "/
|
|
2685
|
-
/* @__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" })
|
|
2686
2888
|
] }),
|
|
2687
2889
|
/* @__PURE__ */ jsxs3(
|
|
2688
2890
|
"box",
|
|
@@ -2693,157 +2895,432 @@ function DeployView({ context }) {
|
|
|
2693
2895
|
padding: 1,
|
|
2694
2896
|
children: [
|
|
2695
2897
|
/* @__PURE__ */ jsx3("text", { fg: t.status.warning, children: "No deployments found!" }),
|
|
2696
|
-
/* @__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." })
|
|
2697
2899
|
]
|
|
2698
2900
|
}
|
|
2699
2901
|
),
|
|
2700
2902
|
/* @__PURE__ */ jsx3("text", { fg: t.fg.muted, marginTop: 2, children: "Press any key to return to home" })
|
|
2701
2903
|
] });
|
|
2702
2904
|
}
|
|
2703
|
-
if (
|
|
2905
|
+
if (viewState === "listing") {
|
|
2704
2906
|
return /* @__PURE__ */ jsxs3("box", { flexDirection: "column", width: "100%", padding: 1, children: [
|
|
2705
2907
|
/* @__PURE__ */ jsxs3("box", { flexDirection: "row", marginBottom: 2, children: [
|
|
2706
|
-
/* @__PURE__ */ jsx3("text", { fg: t.accent, children: "/
|
|
2707
|
-
/* @__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
|
+
] })
|
|
2708
2914
|
] }),
|
|
2709
|
-
/* @__PURE__ */
|
|
2915
|
+
/* @__PURE__ */ jsx3(
|
|
2710
2916
|
"box",
|
|
2711
2917
|
{
|
|
2712
2918
|
flexDirection: "column",
|
|
2713
2919
|
borderStyle: "single",
|
|
2714
2920
|
borderColor: t.border.default,
|
|
2715
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,
|
|
2716
2967
|
children: [
|
|
2717
|
-
/* @__PURE__ */
|
|
2718
|
-
|
|
2719
|
-
|
|
2720
|
-
|
|
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 })
|
|
2721
2972
|
] }),
|
|
2722
|
-
/* @__PURE__ */
|
|
2723
|
-
|
|
2724
|
-
"
|
|
2725
|
-
|
|
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 })
|
|
2726
2992
|
] }),
|
|
2727
|
-
/* @__PURE__ */
|
|
2728
|
-
|
|
2729
|
-
|
|
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
|
+
] })
|
|
2730
3011
|
]
|
|
2731
3012
|
}
|
|
2732
3013
|
),
|
|
2733
|
-
/* @__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" })
|
|
2734
3021
|
] });
|
|
2735
3022
|
}
|
|
2736
|
-
|
|
2737
|
-
/* @__PURE__ */ jsxs3("box", { flexDirection: "
|
|
2738
|
-
/* @__PURE__ */ jsx3("text", { fg: t.
|
|
2739
|
-
/* @__PURE__ */
|
|
2740
|
-
|
|
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
|
-
|
|
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,
|
|
2847
3324
|
uint32BE(commentBuf.length),
|
|
2848
3325
|
commentBuf
|
|
2849
3326
|
]);
|
|
@@ -3089,6 +3566,9 @@ async function waitForSSH(host, privateKey, timeoutMs = 18e4, pollIntervalMs = 5
|
|
|
3089
3566
|
throw new Error(`SSH not available after ${timeoutMs / 1e3} seconds`);
|
|
3090
3567
|
}
|
|
3091
3568
|
|
|
3569
|
+
// src/services/deployment.ts
|
|
3570
|
+
import { randomBytes as randomBytes2 } from "crypto";
|
|
3571
|
+
|
|
3092
3572
|
// src/services/setup/index.ts
|
|
3093
3573
|
async function execOrFail(ssh, command, errorMessage) {
|
|
3094
3574
|
const result = await ssh.exec(command);
|
|
@@ -3240,7 +3720,7 @@ async function installOpenClaw(ssh) {
|
|
|
3240
3720
|
throw new Error("OpenClaw installation verification failed");
|
|
3241
3721
|
}
|
|
3242
3722
|
}
|
|
3243
|
-
async function configureOpenClaw(ssh, customConfig, agentConfig) {
|
|
3723
|
+
async function configureOpenClaw(ssh, customConfig, agentConfig, gatewayToken) {
|
|
3244
3724
|
await ssh.exec("mkdir -p ~/.openclaw");
|
|
3245
3725
|
const config = {
|
|
3246
3726
|
browser: {
|
|
@@ -3262,6 +3742,7 @@ async function configureOpenClaw(ssh, customConfig, agentConfig) {
|
|
|
3262
3742
|
port: 18789,
|
|
3263
3743
|
mode: "local",
|
|
3264
3744
|
bind: "loopback",
|
|
3745
|
+
...gatewayToken ? { auth: { token: gatewayToken } } : {},
|
|
3265
3746
|
tailscale: {
|
|
3266
3747
|
mode: "serve",
|
|
3267
3748
|
resetOnExit: false
|
|
@@ -3509,6 +3990,41 @@ async function getOpenClawLogs(ssh, lines = 100) {
|
|
|
3509
3990
|
const result = await ssh.exec(`journalctl -u openclaw -n ${lines} --no-pager`);
|
|
3510
3991
|
return result.stdout;
|
|
3511
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
|
+
}
|
|
3512
4028
|
|
|
3513
4029
|
// src/services/deployment.ts
|
|
3514
4030
|
var MAX_RETRIES = 3;
|
|
@@ -3607,6 +4123,7 @@ var DeploymentOrchestrator = class {
|
|
|
3607
4123
|
onConfirm;
|
|
3608
4124
|
onOpenUrl;
|
|
3609
4125
|
onSpawnTerminal;
|
|
4126
|
+
tailscaleSkipped = false;
|
|
3610
4127
|
constructor(deploymentName, onProgress, onConfirm, onOpenUrl, onSpawnTerminal) {
|
|
3611
4128
|
this.deploymentName = deploymentName;
|
|
3612
4129
|
this.deployment = readDeployment(deploymentName);
|
|
@@ -3990,18 +4507,28 @@ Would you like to retry from the beginning?`
|
|
|
3990
4507
|
const ssh = await this.ensureSSHConnected();
|
|
3991
4508
|
const customConfig = this.deployment.config.openclawConfig;
|
|
3992
4509
|
const agentConfig = this.deployment.config.openclawAgent;
|
|
3993
|
-
|
|
4510
|
+
const gatewayToken = randomBytes2(32).toString("hex");
|
|
4511
|
+
await configureOpenClaw(ssh, customConfig, agentConfig, gatewayToken);
|
|
4512
|
+
updateDeploymentState(this.deploymentName, { gatewayToken });
|
|
3994
4513
|
if (agentConfig) {
|
|
3995
4514
|
this.reportProgress("openclaw_configured", "Writing AI provider environment...");
|
|
3996
4515
|
await writeOpenClawEnvFile(ssh, agentConfig);
|
|
3997
4516
|
}
|
|
3998
4517
|
}
|
|
3999
4518
|
async installTailscale() {
|
|
4519
|
+
if (this.deployment.config.skipTailscale) {
|
|
4520
|
+
this.tailscaleSkipped = true;
|
|
4521
|
+
this.reportProgress("tailscale_installed", "Tailscale setup skipped.");
|
|
4522
|
+
return;
|
|
4523
|
+
}
|
|
4000
4524
|
const ssh = await this.ensureSSHConnected();
|
|
4001
4525
|
await installTailscale(ssh);
|
|
4002
4526
|
}
|
|
4003
4527
|
async authenticateTailscale() {
|
|
4528
|
+
if (this.tailscaleSkipped) return;
|
|
4004
4529
|
const ssh = await this.ensureSSHConnected();
|
|
4530
|
+
const check = await ssh.exec("which tailscale 2>/dev/null");
|
|
4531
|
+
if (check.code !== 0) return;
|
|
4005
4532
|
const authUrl = await getTailscaleAuthUrl(ssh);
|
|
4006
4533
|
if (authUrl) {
|
|
4007
4534
|
const confirmed = await this.onConfirm(
|
|
@@ -4024,7 +4551,10 @@ URL: ${authUrl}`
|
|
|
4024
4551
|
}
|
|
4025
4552
|
}
|
|
4026
4553
|
async configureTailscale() {
|
|
4554
|
+
if (this.tailscaleSkipped) return;
|
|
4027
4555
|
const ssh = await this.ensureSSHConnected();
|
|
4556
|
+
const check = await ssh.exec("which tailscale 2>/dev/null");
|
|
4557
|
+
if (check.code !== 0) return;
|
|
4028
4558
|
const tailscaleIp = await configureTailscaleServe(ssh);
|
|
4029
4559
|
updateDeploymentState(this.deploymentName, {
|
|
4030
4560
|
tailscaleIp
|
|
@@ -4263,8 +4793,8 @@ function openGhostty(command) {
|
|
|
4263
4793
|
" end tell",
|
|
4264
4794
|
"end tell"
|
|
4265
4795
|
];
|
|
4266
|
-
const
|
|
4267
|
-
const result = spawnSync("osascript",
|
|
4796
|
+
const args2 = appleScript.flatMap((line) => ["-e", line]);
|
|
4797
|
+
const result = spawnSync("osascript", args2, { timeout: 1e4, stdio: "pipe" });
|
|
4268
4798
|
if (result.status === 0) {
|
|
4269
4799
|
return { success: true };
|
|
4270
4800
|
}
|
|
@@ -4509,14 +5039,14 @@ function getTerminalDisplayName(app) {
|
|
|
4509
5039
|
}
|
|
4510
5040
|
|
|
4511
5041
|
// src/components/DeployingView.tsx
|
|
4512
|
-
import { jsx as
|
|
5042
|
+
import { jsx as jsx5, jsxs as jsxs5 } from "@opentui/react/jsx-runtime";
|
|
4513
5043
|
function DeployingView({ context }) {
|
|
4514
|
-
const [deployState, setDeployState] =
|
|
4515
|
-
const [progress, setProgress] =
|
|
4516
|
-
const [logs, setLogs] =
|
|
4517
|
-
const [error, setError] =
|
|
4518
|
-
const [confirmPrompt, setConfirmPrompt] =
|
|
4519
|
-
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);
|
|
4520
5050
|
const deploymentName = context.selectedDeployment;
|
|
4521
5051
|
const addLog = useCallback((message) => {
|
|
4522
5052
|
setLogs((prev) => [...prev.slice(-20), `[${(/* @__PURE__ */ new Date()).toLocaleTimeString()}] ${message}`]);
|
|
@@ -4564,9 +5094,9 @@ function DeployingView({ context }) {
|
|
|
4564
5094
|
setDeployState("deploying");
|
|
4565
5095
|
addLog("Terminal session confirmed complete, continuing deployment...");
|
|
4566
5096
|
}, [terminalResolve, addLog]);
|
|
4567
|
-
const stateRef =
|
|
5097
|
+
const stateRef = useRef3({ deployState, terminalResolve });
|
|
4568
5098
|
stateRef.current = { deployState, terminalResolve };
|
|
4569
|
-
|
|
5099
|
+
useKeyboard4((key) => {
|
|
4570
5100
|
const currentState = stateRef.current;
|
|
4571
5101
|
if (currentState.deployState === "waiting_terminal") {
|
|
4572
5102
|
if (key.name === "return") {
|
|
@@ -4620,19 +5150,19 @@ function DeployingView({ context }) {
|
|
|
4620
5150
|
const width = 40;
|
|
4621
5151
|
const filled = Math.round(progress.progress / 100 * width);
|
|
4622
5152
|
const empty = width - filled;
|
|
4623
|
-
return /* @__PURE__ */
|
|
4624
|
-
/* @__PURE__ */
|
|
4625
|
-
/* @__PURE__ */
|
|
4626
|
-
/* @__PURE__ */
|
|
4627
|
-
/* @__PURE__ */
|
|
4628
|
-
/* @__PURE__ */
|
|
4629
|
-
/* @__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: [
|
|
4630
5160
|
" ",
|
|
4631
5161
|
Math.round(progress.progress),
|
|
4632
5162
|
"%"
|
|
4633
5163
|
] })
|
|
4634
5164
|
] }),
|
|
4635
|
-
/* @__PURE__ */
|
|
5165
|
+
/* @__PURE__ */ jsxs5("text", { fg: t.accent, marginTop: 1, children: [
|
|
4636
5166
|
"Current: ",
|
|
4637
5167
|
progress.message
|
|
4638
5168
|
] })
|
|
@@ -4641,7 +5171,7 @@ function DeployingView({ context }) {
|
|
|
4641
5171
|
const renderConfirmDialog = () => {
|
|
4642
5172
|
if (!confirmPrompt) return null;
|
|
4643
5173
|
const lines = confirmPrompt.message.split("\n");
|
|
4644
|
-
return /* @__PURE__ */
|
|
5174
|
+
return /* @__PURE__ */ jsxs5(
|
|
4645
5175
|
"box",
|
|
4646
5176
|
{
|
|
4647
5177
|
flexDirection: "column",
|
|
@@ -4650,16 +5180,16 @@ function DeployingView({ context }) {
|
|
|
4650
5180
|
padding: 1,
|
|
4651
5181
|
marginBottom: 1,
|
|
4652
5182
|
children: [
|
|
4653
|
-
/* @__PURE__ */
|
|
4654
|
-
/* @__PURE__ */
|
|
4655
|
-
/* @__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" })
|
|
4656
5186
|
]
|
|
4657
5187
|
}
|
|
4658
5188
|
);
|
|
4659
5189
|
};
|
|
4660
5190
|
const renderWaitingTerminal = () => {
|
|
4661
|
-
return /* @__PURE__ */
|
|
4662
|
-
/* @__PURE__ */
|
|
5191
|
+
return /* @__PURE__ */ jsxs5("box", { flexDirection: "column", flexGrow: 1, children: [
|
|
5192
|
+
/* @__PURE__ */ jsxs5(
|
|
4663
5193
|
"box",
|
|
4664
5194
|
{
|
|
4665
5195
|
flexDirection: "column",
|
|
@@ -4668,12 +5198,12 @@ function DeployingView({ context }) {
|
|
|
4668
5198
|
padding: 1,
|
|
4669
5199
|
marginBottom: 1,
|
|
4670
5200
|
children: [
|
|
4671
|
-
/* @__PURE__ */
|
|
4672
|
-
/* @__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." })
|
|
4673
5203
|
]
|
|
4674
5204
|
}
|
|
4675
5205
|
),
|
|
4676
|
-
/* @__PURE__ */
|
|
5206
|
+
/* @__PURE__ */ jsxs5(
|
|
4677
5207
|
"box",
|
|
4678
5208
|
{
|
|
4679
5209
|
flexDirection: "column",
|
|
@@ -4682,32 +5212,32 @@ function DeployingView({ context }) {
|
|
|
4682
5212
|
padding: 1,
|
|
4683
5213
|
marginBottom: 1,
|
|
4684
5214
|
children: [
|
|
4685
|
-
/* @__PURE__ */
|
|
4686
|
-
/* @__PURE__ */
|
|
4687
|
-
/* @__PURE__ */
|
|
4688
|
-
/* @__PURE__ */
|
|
4689
|
-
/* @__PURE__ */
|
|
4690
|
-
/* @__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" })
|
|
4691
5221
|
] })
|
|
4692
5222
|
]
|
|
4693
5223
|
}
|
|
4694
5224
|
),
|
|
4695
|
-
/* @__PURE__ */
|
|
5225
|
+
/* @__PURE__ */ jsx5(
|
|
4696
5226
|
"box",
|
|
4697
5227
|
{
|
|
4698
5228
|
flexDirection: "column",
|
|
4699
5229
|
borderStyle: "single",
|
|
4700
5230
|
borderColor: t.status.success,
|
|
4701
5231
|
padding: 1,
|
|
4702
|
-
children: /* @__PURE__ */
|
|
5232
|
+
children: /* @__PURE__ */ jsx5("text", { fg: t.status.success, children: "Press Enter when you have completed the setup in the terminal" })
|
|
4703
5233
|
}
|
|
4704
5234
|
)
|
|
4705
5235
|
] });
|
|
4706
5236
|
};
|
|
4707
5237
|
const renderSuccess = () => {
|
|
4708
5238
|
const state = readDeploymentState(deploymentName);
|
|
4709
|
-
return /* @__PURE__ */
|
|
4710
|
-
/* @__PURE__ */
|
|
5239
|
+
return /* @__PURE__ */ jsxs5("box", { flexDirection: "column", children: [
|
|
5240
|
+
/* @__PURE__ */ jsxs5(
|
|
4711
5241
|
"box",
|
|
4712
5242
|
{
|
|
4713
5243
|
flexDirection: "column",
|
|
@@ -4716,12 +5246,12 @@ function DeployingView({ context }) {
|
|
|
4716
5246
|
padding: 1,
|
|
4717
5247
|
marginBottom: 1,
|
|
4718
5248
|
children: [
|
|
4719
|
-
/* @__PURE__ */
|
|
4720
|
-
/* @__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." })
|
|
4721
5251
|
]
|
|
4722
5252
|
}
|
|
4723
5253
|
),
|
|
4724
|
-
/* @__PURE__ */
|
|
5254
|
+
/* @__PURE__ */ jsxs5(
|
|
4725
5255
|
"box",
|
|
4726
5256
|
{
|
|
4727
5257
|
flexDirection: "column",
|
|
@@ -4730,36 +5260,36 @@ function DeployingView({ context }) {
|
|
|
4730
5260
|
padding: 1,
|
|
4731
5261
|
marginBottom: 1,
|
|
4732
5262
|
children: [
|
|
4733
|
-
/* @__PURE__ */
|
|
4734
|
-
/* @__PURE__ */
|
|
4735
|
-
/* @__PURE__ */
|
|
4736
|
-
/* @__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" })
|
|
4737
5267
|
] }),
|
|
4738
|
-
/* @__PURE__ */
|
|
4739
|
-
/* @__PURE__ */
|
|
4740
|
-
/* @__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" })
|
|
4741
5271
|
] }),
|
|
4742
|
-
/* @__PURE__ */
|
|
4743
|
-
/* @__PURE__ */
|
|
4744
|
-
/* @__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" })
|
|
4745
5275
|
] })
|
|
4746
5276
|
]
|
|
4747
5277
|
}
|
|
4748
5278
|
),
|
|
4749
|
-
/* @__PURE__ */
|
|
4750
|
-
/* @__PURE__ */
|
|
4751
|
-
/* @__PURE__ */
|
|
4752
|
-
/* @__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: [
|
|
4753
5283
|
" Gateway: http://",
|
|
4754
5284
|
state.tailscaleIp || state.serverIp,
|
|
4755
5285
|
":18789/"
|
|
4756
5286
|
] }),
|
|
4757
|
-
/* @__PURE__ */
|
|
5287
|
+
/* @__PURE__ */ jsx5("text", { fg: t.fg.muted, marginTop: 2, children: "Press any key to return to home" })
|
|
4758
5288
|
] });
|
|
4759
5289
|
};
|
|
4760
5290
|
const renderFailed = () => {
|
|
4761
|
-
return /* @__PURE__ */
|
|
4762
|
-
/* @__PURE__ */
|
|
5291
|
+
return /* @__PURE__ */ jsxs5("box", { flexDirection: "column", children: [
|
|
5292
|
+
/* @__PURE__ */ jsxs5(
|
|
4763
5293
|
"box",
|
|
4764
5294
|
{
|
|
4765
5295
|
flexDirection: "column",
|
|
@@ -4768,26 +5298,26 @@ function DeployingView({ context }) {
|
|
|
4768
5298
|
padding: 1,
|
|
4769
5299
|
marginBottom: 1,
|
|
4770
5300
|
children: [
|
|
4771
|
-
/* @__PURE__ */
|
|
4772
|
-
/* @__PURE__ */
|
|
4773
|
-
/* @__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: [
|
|
4774
5304
|
"Error: ",
|
|
4775
5305
|
error
|
|
4776
5306
|
] })
|
|
4777
5307
|
]
|
|
4778
5308
|
}
|
|
4779
5309
|
),
|
|
4780
|
-
/* @__PURE__ */
|
|
4781
|
-
/* @__PURE__ */
|
|
4782
|
-
/* @__PURE__ */
|
|
4783
|
-
/* @__PURE__ */
|
|
4784
|
-
/* @__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" })
|
|
4785
5315
|
] }),
|
|
4786
|
-
/* @__PURE__ */
|
|
5316
|
+
/* @__PURE__ */ jsx5("text", { fg: t.fg.muted, marginTop: 1, children: "Press any key to return to home" })
|
|
4787
5317
|
] });
|
|
4788
5318
|
};
|
|
4789
|
-
return /* @__PURE__ */
|
|
4790
|
-
/* @__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: [
|
|
4791
5321
|
"Deploying: ",
|
|
4792
5322
|
deploymentName
|
|
4793
5323
|
] }) }),
|
|
@@ -4796,7 +5326,7 @@ function DeployingView({ context }) {
|
|
|
4796
5326
|
deployState === "waiting_terminal" && renderWaitingTerminal(),
|
|
4797
5327
|
deployState === "success" && renderSuccess(),
|
|
4798
5328
|
deployState === "failed" && renderFailed(),
|
|
4799
|
-
/* @__PURE__ */
|
|
5329
|
+
/* @__PURE__ */ jsxs5(
|
|
4800
5330
|
"box",
|
|
4801
5331
|
{
|
|
4802
5332
|
flexDirection: "column",
|
|
@@ -4804,8 +5334,8 @@ function DeployingView({ context }) {
|
|
|
4804
5334
|
borderColor: t.border.default,
|
|
4805
5335
|
padding: 1,
|
|
4806
5336
|
children: [
|
|
4807
|
-
/* @__PURE__ */
|
|
4808
|
-
/* @__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)) })
|
|
4809
5339
|
]
|
|
4810
5340
|
}
|
|
4811
5341
|
)
|
|
@@ -4813,13 +5343,13 @@ function DeployingView({ context }) {
|
|
|
4813
5343
|
}
|
|
4814
5344
|
|
|
4815
5345
|
// src/components/StatusView.tsx
|
|
4816
|
-
import { useState as
|
|
4817
|
-
import { useKeyboard as
|
|
4818
|
-
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";
|
|
4819
5349
|
function StatusView({ context }) {
|
|
4820
|
-
const [selectedIndex, setSelectedIndex] =
|
|
4821
|
-
const [healthStatus, setHealthStatus] =
|
|
4822
|
-
const [checking, setChecking] =
|
|
5350
|
+
const [selectedIndex, setSelectedIndex] = useState6(0);
|
|
5351
|
+
const [healthStatus, setHealthStatus] = useState6(/* @__PURE__ */ new Map());
|
|
5352
|
+
const [checking, setChecking] = useState6(null);
|
|
4823
5353
|
const deployments = context.deployments;
|
|
4824
5354
|
const checkHealth = async (deployment) => {
|
|
4825
5355
|
const name = deployment.config.name;
|
|
@@ -4844,7 +5374,7 @@ function StatusView({ context }) {
|
|
|
4844
5374
|
setHealthStatus((prev) => new Map(prev).set(name, health));
|
|
4845
5375
|
setChecking(null);
|
|
4846
5376
|
};
|
|
4847
|
-
|
|
5377
|
+
useKeyboard5((key) => {
|
|
4848
5378
|
if (deployments.length === 0) {
|
|
4849
5379
|
context.navigateTo("home");
|
|
4850
5380
|
return;
|
|
@@ -4860,12 +5390,12 @@ function StatusView({ context }) {
|
|
|
4860
5390
|
}
|
|
4861
5391
|
});
|
|
4862
5392
|
if (deployments.length === 0) {
|
|
4863
|
-
return /* @__PURE__ */
|
|
4864
|
-
/* @__PURE__ */
|
|
4865
|
-
/* @__PURE__ */
|
|
4866
|
-
/* @__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" })
|
|
4867
5397
|
] }),
|
|
4868
|
-
/* @__PURE__ */
|
|
5398
|
+
/* @__PURE__ */ jsxs6(
|
|
4869
5399
|
"box",
|
|
4870
5400
|
{
|
|
4871
5401
|
flexDirection: "column",
|
|
@@ -4873,22 +5403,22 @@ function StatusView({ context }) {
|
|
|
4873
5403
|
borderColor: t.border.default,
|
|
4874
5404
|
padding: 1,
|
|
4875
5405
|
children: [
|
|
4876
|
-
/* @__PURE__ */
|
|
4877
|
-
/* @__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." })
|
|
4878
5408
|
]
|
|
4879
5409
|
}
|
|
4880
5410
|
),
|
|
4881
|
-
/* @__PURE__ */
|
|
5411
|
+
/* @__PURE__ */ jsx6("text", { fg: t.fg.muted, marginTop: 2, children: "Press any key to return to home" })
|
|
4882
5412
|
] });
|
|
4883
5413
|
}
|
|
4884
5414
|
const selectedDeployment = deployments[selectedIndex];
|
|
4885
5415
|
const selectedHealth = healthStatus.get(selectedDeployment.config.name);
|
|
4886
|
-
return /* @__PURE__ */
|
|
4887
|
-
/* @__PURE__ */
|
|
4888
|
-
/* @__PURE__ */
|
|
4889
|
-
/* @__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" })
|
|
4890
5420
|
] }),
|
|
4891
|
-
/* @__PURE__ */
|
|
5421
|
+
/* @__PURE__ */ jsxs6(
|
|
4892
5422
|
"box",
|
|
4893
5423
|
{
|
|
4894
5424
|
flexDirection: "column",
|
|
@@ -4897,22 +5427,22 @@ function StatusView({ context }) {
|
|
|
4897
5427
|
padding: 1,
|
|
4898
5428
|
marginBottom: 1,
|
|
4899
5429
|
children: [
|
|
4900
|
-
/* @__PURE__ */
|
|
5430
|
+
/* @__PURE__ */ jsxs6("text", { fg: t.fg.primary, marginBottom: 1, children: [
|
|
4901
5431
|
"Deployments (",
|
|
4902
5432
|
deployments.length,
|
|
4903
5433
|
")"
|
|
4904
5434
|
] }),
|
|
4905
5435
|
deployments.map((deployment, index) => {
|
|
4906
5436
|
const isSelected = index === selectedIndex;
|
|
4907
|
-
return /* @__PURE__ */
|
|
5437
|
+
return /* @__PURE__ */ jsxs6(
|
|
4908
5438
|
"box",
|
|
4909
5439
|
{
|
|
4910
5440
|
flexDirection: "row",
|
|
4911
5441
|
backgroundColor: isSelected ? t.selection.bg : void 0,
|
|
4912
5442
|
children: [
|
|
4913
|
-
/* @__PURE__ */
|
|
4914
|
-
/* @__PURE__ */
|
|
4915
|
-
/* @__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: [
|
|
4916
5446
|
"[",
|
|
4917
5447
|
deployment.state.status,
|
|
4918
5448
|
"]"
|
|
@@ -4925,7 +5455,7 @@ function StatusView({ context }) {
|
|
|
4925
5455
|
]
|
|
4926
5456
|
}
|
|
4927
5457
|
),
|
|
4928
|
-
/* @__PURE__ */
|
|
5458
|
+
/* @__PURE__ */ jsxs6(
|
|
4929
5459
|
"box",
|
|
4930
5460
|
{
|
|
4931
5461
|
flexDirection: "column",
|
|
@@ -4934,78 +5464,78 @@ function StatusView({ context }) {
|
|
|
4934
5464
|
padding: 1,
|
|
4935
5465
|
marginBottom: 1,
|
|
4936
5466
|
children: [
|
|
4937
|
-
/* @__PURE__ */
|
|
5467
|
+
/* @__PURE__ */ jsxs6("text", { fg: t.accent, children: [
|
|
4938
5468
|
"Details: ",
|
|
4939
5469
|
selectedDeployment.config.name
|
|
4940
5470
|
] }),
|
|
4941
|
-
/* @__PURE__ */
|
|
4942
|
-
/* @__PURE__ */
|
|
4943
|
-
/* @__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 })
|
|
4944
5474
|
] }),
|
|
4945
|
-
/* @__PURE__ */
|
|
4946
|
-
/* @__PURE__ */
|
|
4947
|
-
/* @__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 })
|
|
4948
5478
|
] }),
|
|
4949
|
-
/* @__PURE__ */
|
|
4950
|
-
/* @__PURE__ */
|
|
4951
|
-
/* @__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" })
|
|
4952
5482
|
] }),
|
|
4953
|
-
/* @__PURE__ */
|
|
4954
|
-
/* @__PURE__ */
|
|
4955
|
-
/* @__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" })
|
|
4956
5486
|
] }),
|
|
4957
|
-
/* @__PURE__ */
|
|
4958
|
-
/* @__PURE__ */
|
|
4959
|
-
/* @__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() })
|
|
4960
5490
|
] }),
|
|
4961
|
-
selectedDeployment.state.deployedAt && /* @__PURE__ */
|
|
4962
|
-
/* @__PURE__ */
|
|
4963
|
-
/* @__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() })
|
|
4964
5494
|
] }),
|
|
4965
|
-
selectedDeployment.state.lastError && /* @__PURE__ */
|
|
4966
|
-
/* @__PURE__ */
|
|
4967
|
-
/* @__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 })
|
|
4968
5498
|
] }),
|
|
4969
|
-
selectedHealth && /* @__PURE__ */
|
|
4970
|
-
/* @__PURE__ */
|
|
4971
|
-
/* @__PURE__ */
|
|
4972
|
-
/* @__PURE__ */
|
|
4973
|
-
/* @__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" })
|
|
4974
5504
|
] }),
|
|
4975
|
-
/* @__PURE__ */
|
|
4976
|
-
/* @__PURE__ */
|
|
4977
|
-
/* @__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" })
|
|
4978
5508
|
] }),
|
|
4979
|
-
/* @__PURE__ */
|
|
5509
|
+
/* @__PURE__ */ jsxs6("text", { fg: t.fg.muted, children: [
|
|
4980
5510
|
"Last checked: ",
|
|
4981
5511
|
selectedHealth.lastChecked.toLocaleTimeString()
|
|
4982
5512
|
] })
|
|
4983
5513
|
] }),
|
|
4984
|
-
checking === selectedDeployment.config.name && /* @__PURE__ */
|
|
5514
|
+
checking === selectedDeployment.config.name && /* @__PURE__ */ jsx6("text", { fg: t.status.warning, marginTop: 1, children: "Checking health..." })
|
|
4985
5515
|
]
|
|
4986
5516
|
}
|
|
4987
5517
|
),
|
|
4988
|
-
selectedDeployment.state.checkpoints.length > 0 && /* @__PURE__ */
|
|
4989
|
-
/* @__PURE__ */
|
|
4990
|
-
/* @__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: [
|
|
4991
5521
|
selectedDeployment.state.checkpoints.length,
|
|
4992
5522
|
" completed"
|
|
4993
5523
|
] })
|
|
4994
5524
|
] }),
|
|
4995
|
-
/* @__PURE__ */
|
|
5525
|
+
/* @__PURE__ */ jsx6("text", { fg: t.fg.muted, children: "Up/Down: Select | Enter: Health check | Esc: Back" })
|
|
4996
5526
|
] });
|
|
4997
5527
|
}
|
|
4998
5528
|
|
|
4999
5529
|
// src/components/SSHView.tsx
|
|
5000
|
-
import { useState as
|
|
5001
|
-
import { useKeyboard as
|
|
5002
|
-
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";
|
|
5003
5533
|
function SSHView({ context }) {
|
|
5004
|
-
const [viewState, setViewState] =
|
|
5005
|
-
const [selectedIndex, setSelectedIndex] =
|
|
5006
|
-
const [error, setError] =
|
|
5007
|
-
const [connectedDeployment, setConnectedDeployment] =
|
|
5008
|
-
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("");
|
|
5009
5539
|
const deployedDeployments = context.deployments.filter(
|
|
5010
5540
|
(d) => d.state.status === "deployed" && d.state.serverIp
|
|
5011
5541
|
);
|
|
@@ -5024,7 +5554,7 @@ function SSHView({ context }) {
|
|
|
5024
5554
|
}
|
|
5025
5555
|
}, []);
|
|
5026
5556
|
const selectedDeployment = deployedDeployments[selectedIndex];
|
|
5027
|
-
|
|
5557
|
+
useKeyboard6((key) => {
|
|
5028
5558
|
if (deployedDeployments.length === 0) {
|
|
5029
5559
|
context.navigateTo("home");
|
|
5030
5560
|
return;
|
|
@@ -5048,12 +5578,12 @@ function SSHView({ context }) {
|
|
|
5048
5578
|
}
|
|
5049
5579
|
});
|
|
5050
5580
|
if (deployedDeployments.length === 0) {
|
|
5051
|
-
return /* @__PURE__ */
|
|
5052
|
-
/* @__PURE__ */
|
|
5053
|
-
/* @__PURE__ */
|
|
5054
|
-
/* @__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" })
|
|
5055
5585
|
] }),
|
|
5056
|
-
/* @__PURE__ */
|
|
5586
|
+
/* @__PURE__ */ jsxs7(
|
|
5057
5587
|
"box",
|
|
5058
5588
|
{
|
|
5059
5589
|
flexDirection: "column",
|
|
@@ -5061,23 +5591,23 @@ function SSHView({ context }) {
|
|
|
5061
5591
|
borderColor: t.border.default,
|
|
5062
5592
|
padding: 1,
|
|
5063
5593
|
children: [
|
|
5064
|
-
/* @__PURE__ */
|
|
5065
|
-
/* @__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" })
|
|
5066
5596
|
]
|
|
5067
5597
|
}
|
|
5068
5598
|
),
|
|
5069
|
-
/* @__PURE__ */
|
|
5599
|
+
/* @__PURE__ */ jsx7("text", { fg: t.fg.muted, marginTop: 2, children: "Press any key to return to home" })
|
|
5070
5600
|
] });
|
|
5071
5601
|
}
|
|
5072
5602
|
if (viewState === "selecting") {
|
|
5073
5603
|
const terminal = detectTerminal();
|
|
5074
5604
|
const terminalDisplayName = getTerminalDisplayName(terminal.app);
|
|
5075
|
-
return /* @__PURE__ */
|
|
5076
|
-
/* @__PURE__ */
|
|
5077
|
-
/* @__PURE__ */
|
|
5078
|
-
/* @__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" })
|
|
5079
5609
|
] }),
|
|
5080
|
-
/* @__PURE__ */
|
|
5610
|
+
/* @__PURE__ */ jsx7(
|
|
5081
5611
|
"box",
|
|
5082
5612
|
{
|
|
5083
5613
|
flexDirection: "column",
|
|
@@ -5087,15 +5617,15 @@ function SSHView({ context }) {
|
|
|
5087
5617
|
marginBottom: 1,
|
|
5088
5618
|
children: deployedDeployments.map((deployment, index) => {
|
|
5089
5619
|
const isSelected = index === selectedIndex;
|
|
5090
|
-
return /* @__PURE__ */
|
|
5620
|
+
return /* @__PURE__ */ jsxs7(
|
|
5091
5621
|
"box",
|
|
5092
5622
|
{
|
|
5093
5623
|
flexDirection: "row",
|
|
5094
5624
|
backgroundColor: isSelected ? t.selection.bg : void 0,
|
|
5095
5625
|
children: [
|
|
5096
|
-
/* @__PURE__ */
|
|
5097
|
-
/* @__PURE__ */
|
|
5098
|
-
/* @__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 })
|
|
5099
5629
|
]
|
|
5100
5630
|
},
|
|
5101
5631
|
deployment.config.name
|
|
@@ -5103,24 +5633,24 @@ function SSHView({ context }) {
|
|
|
5103
5633
|
})
|
|
5104
5634
|
}
|
|
5105
5635
|
),
|
|
5106
|
-
/* @__PURE__ */
|
|
5107
|
-
/* @__PURE__ */
|
|
5108
|
-
/* @__PURE__ */
|
|
5109
|
-
/* @__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)" })
|
|
5110
5640
|
] }),
|
|
5111
|
-
/* @__PURE__ */
|
|
5641
|
+
/* @__PURE__ */ jsx7("text", { fg: t.fg.muted, children: "Arrow keys to select | Enter to connect | Esc to go back" })
|
|
5112
5642
|
] });
|
|
5113
5643
|
}
|
|
5114
5644
|
if (viewState === "connected") {
|
|
5115
|
-
return /* @__PURE__ */
|
|
5116
|
-
/* @__PURE__ */
|
|
5117
|
-
/* @__PURE__ */
|
|
5118
|
-
/* @__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: [
|
|
5119
5649
|
" - Connected to ",
|
|
5120
5650
|
connectedDeployment
|
|
5121
5651
|
] })
|
|
5122
5652
|
] }),
|
|
5123
|
-
/* @__PURE__ */
|
|
5653
|
+
/* @__PURE__ */ jsxs7(
|
|
5124
5654
|
"box",
|
|
5125
5655
|
{
|
|
5126
5656
|
flexDirection: "column",
|
|
@@ -5129,8 +5659,8 @@ function SSHView({ context }) {
|
|
|
5129
5659
|
padding: 1,
|
|
5130
5660
|
marginBottom: 1,
|
|
5131
5661
|
children: [
|
|
5132
|
-
/* @__PURE__ */
|
|
5133
|
-
/* @__PURE__ */
|
|
5662
|
+
/* @__PURE__ */ jsx7("text", { fg: t.status.success, children: "SSH Session Opened" }),
|
|
5663
|
+
/* @__PURE__ */ jsxs7("text", { fg: t.fg.primary, marginTop: 1, children: [
|
|
5134
5664
|
"A new ",
|
|
5135
5665
|
terminalName,
|
|
5136
5666
|
" window/tab has been opened."
|
|
@@ -5138,7 +5668,7 @@ function SSHView({ context }) {
|
|
|
5138
5668
|
]
|
|
5139
5669
|
}
|
|
5140
5670
|
),
|
|
5141
|
-
/* @__PURE__ */
|
|
5671
|
+
/* @__PURE__ */ jsxs7(
|
|
5142
5672
|
"box",
|
|
5143
5673
|
{
|
|
5144
5674
|
flexDirection: "column",
|
|
@@ -5147,41 +5677,41 @@ function SSHView({ context }) {
|
|
|
5147
5677
|
padding: 1,
|
|
5148
5678
|
marginBottom: 1,
|
|
5149
5679
|
children: [
|
|
5150
|
-
/* @__PURE__ */
|
|
5680
|
+
/* @__PURE__ */ jsxs7("text", { fg: t.fg.primary, children: [
|
|
5151
5681
|
"Your SSH session is running in ",
|
|
5152
5682
|
terminalName,
|
|
5153
5683
|
"."
|
|
5154
5684
|
] }),
|
|
5155
|
-
/* @__PURE__ */
|
|
5685
|
+
/* @__PURE__ */ jsx7("text", { fg: t.fg.primary, marginTop: 1, children: "When you're done, type 'exit' or close the tab." })
|
|
5156
5686
|
]
|
|
5157
5687
|
}
|
|
5158
5688
|
),
|
|
5159
|
-
/* @__PURE__ */
|
|
5689
|
+
/* @__PURE__ */ jsx7("text", { fg: t.fg.muted, marginTop: 1, children: "Press Enter or Esc to return to ClawControl" })
|
|
5160
5690
|
] });
|
|
5161
5691
|
}
|
|
5162
5692
|
if (viewState === "error") {
|
|
5163
|
-
return /* @__PURE__ */
|
|
5164
|
-
/* @__PURE__ */
|
|
5165
|
-
/* @__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(
|
|
5166
5696
|
"box",
|
|
5167
5697
|
{
|
|
5168
5698
|
flexDirection: "column",
|
|
5169
5699
|
borderStyle: "single",
|
|
5170
5700
|
borderColor: t.status.error,
|
|
5171
5701
|
padding: 1,
|
|
5172
|
-
children: /* @__PURE__ */
|
|
5702
|
+
children: /* @__PURE__ */ jsx7("text", { fg: t.status.error, children: error })
|
|
5173
5703
|
}
|
|
5174
5704
|
),
|
|
5175
|
-
/* @__PURE__ */
|
|
5705
|
+
/* @__PURE__ */ jsx7("text", { fg: t.fg.muted, marginTop: 2, children: "Press any key to return to home" })
|
|
5176
5706
|
] });
|
|
5177
5707
|
}
|
|
5178
5708
|
return null;
|
|
5179
5709
|
}
|
|
5180
5710
|
|
|
5181
5711
|
// src/components/LogsView.tsx
|
|
5182
|
-
import { useState as
|
|
5183
|
-
import { useKeyboard as
|
|
5184
|
-
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";
|
|
5185
5715
|
function parseLogLine(line) {
|
|
5186
5716
|
if (!line.trim()) return null;
|
|
5187
5717
|
const match = line.match(/^(\w+\s+\d+\s+[\d:]+)\s+\S+\s+\S+:\s*(.*)$/);
|
|
@@ -5200,14 +5730,14 @@ function truncateLine(line, maxWidth = 120) {
|
|
|
5200
5730
|
return line.substring(0, maxWidth - 3) + "...";
|
|
5201
5731
|
}
|
|
5202
5732
|
function LogsView({ context }) {
|
|
5203
|
-
const [viewState, setViewState] =
|
|
5204
|
-
const [selectedIndex, setSelectedIndex] =
|
|
5205
|
-
const [logs, setLogs] =
|
|
5206
|
-
const [error, setError] =
|
|
5207
|
-
const [autoRefresh, setAutoRefresh] =
|
|
5208
|
-
const [lastFetched, setLastFetched] =
|
|
5209
|
-
const sshRef =
|
|
5210
|
-
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);
|
|
5211
5741
|
const deployedDeployments = context.deployments.filter(
|
|
5212
5742
|
(d) => d.state.status === "deployed" && d.state.serverIp
|
|
5213
5743
|
);
|
|
@@ -5258,7 +5788,7 @@ function LogsView({ context }) {
|
|
|
5258
5788
|
setLogs([]);
|
|
5259
5789
|
};
|
|
5260
5790
|
const selectedDeployment = deployedDeployments[selectedIndex];
|
|
5261
|
-
|
|
5791
|
+
useKeyboard7((key) => {
|
|
5262
5792
|
if (deployedDeployments.length === 0) {
|
|
5263
5793
|
context.navigateTo("home");
|
|
5264
5794
|
return;
|
|
@@ -5291,12 +5821,12 @@ function LogsView({ context }) {
|
|
|
5291
5821
|
}
|
|
5292
5822
|
});
|
|
5293
5823
|
if (deployedDeployments.length === 0) {
|
|
5294
|
-
return /* @__PURE__ */
|
|
5295
|
-
/* @__PURE__ */
|
|
5296
|
-
/* @__PURE__ */
|
|
5297
|
-
/* @__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" })
|
|
5298
5828
|
] }),
|
|
5299
|
-
/* @__PURE__ */
|
|
5829
|
+
/* @__PURE__ */ jsxs8(
|
|
5300
5830
|
"box",
|
|
5301
5831
|
{
|
|
5302
5832
|
flexDirection: "column",
|
|
@@ -5304,21 +5834,21 @@ function LogsView({ context }) {
|
|
|
5304
5834
|
borderColor: t.border.default,
|
|
5305
5835
|
padding: 1,
|
|
5306
5836
|
children: [
|
|
5307
|
-
/* @__PURE__ */
|
|
5308
|
-
/* @__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" })
|
|
5309
5839
|
]
|
|
5310
5840
|
}
|
|
5311
5841
|
),
|
|
5312
|
-
/* @__PURE__ */
|
|
5842
|
+
/* @__PURE__ */ jsx8("text", { fg: t.fg.muted, marginTop: 2, children: "Press any key to return to home" })
|
|
5313
5843
|
] });
|
|
5314
5844
|
}
|
|
5315
5845
|
if (viewState === "selecting") {
|
|
5316
|
-
return /* @__PURE__ */
|
|
5317
|
-
/* @__PURE__ */
|
|
5318
|
-
/* @__PURE__ */
|
|
5319
|
-
/* @__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" })
|
|
5320
5850
|
] }),
|
|
5321
|
-
/* @__PURE__ */
|
|
5851
|
+
/* @__PURE__ */ jsx8(
|
|
5322
5852
|
"box",
|
|
5323
5853
|
{
|
|
5324
5854
|
flexDirection: "column",
|
|
@@ -5327,15 +5857,15 @@ function LogsView({ context }) {
|
|
|
5327
5857
|
padding: 1,
|
|
5328
5858
|
children: deployedDeployments.map((deployment, index) => {
|
|
5329
5859
|
const isSelected = index === selectedIndex;
|
|
5330
|
-
return /* @__PURE__ */
|
|
5860
|
+
return /* @__PURE__ */ jsxs8(
|
|
5331
5861
|
"box",
|
|
5332
5862
|
{
|
|
5333
5863
|
flexDirection: "row",
|
|
5334
5864
|
backgroundColor: isSelected ? t.selection.bg : void 0,
|
|
5335
5865
|
children: [
|
|
5336
|
-
/* @__PURE__ */
|
|
5337
|
-
/* @__PURE__ */
|
|
5338
|
-
/* @__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 })
|
|
5339
5869
|
]
|
|
5340
5870
|
},
|
|
5341
5871
|
deployment.config.name
|
|
@@ -5343,70 +5873,70 @@ function LogsView({ context }) {
|
|
|
5343
5873
|
})
|
|
5344
5874
|
}
|
|
5345
5875
|
),
|
|
5346
|
-
/* @__PURE__ */
|
|
5876
|
+
/* @__PURE__ */ jsx8("text", { fg: t.fg.muted, marginTop: 2, children: "Arrow keys to select | Enter to view logs | Esc to go back" })
|
|
5347
5877
|
] });
|
|
5348
5878
|
}
|
|
5349
5879
|
if (viewState === "loading") {
|
|
5350
|
-
return /* @__PURE__ */
|
|
5351
|
-
/* @__PURE__ */
|
|
5352
|
-
/* @__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..." })
|
|
5353
5883
|
] });
|
|
5354
5884
|
}
|
|
5355
5885
|
if (viewState === "error") {
|
|
5356
|
-
return /* @__PURE__ */
|
|
5357
|
-
/* @__PURE__ */
|
|
5358
|
-
/* @__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(
|
|
5359
5889
|
"box",
|
|
5360
5890
|
{
|
|
5361
5891
|
flexDirection: "column",
|
|
5362
5892
|
borderStyle: "single",
|
|
5363
5893
|
borderColor: t.status.error,
|
|
5364
5894
|
padding: 1,
|
|
5365
|
-
children: /* @__PURE__ */
|
|
5895
|
+
children: /* @__PURE__ */ jsx8("text", { fg: t.status.error, children: error })
|
|
5366
5896
|
}
|
|
5367
5897
|
),
|
|
5368
|
-
/* @__PURE__ */
|
|
5898
|
+
/* @__PURE__ */ jsx8("text", { fg: t.fg.muted, marginTop: 2, children: "Press any key to go back" })
|
|
5369
5899
|
] });
|
|
5370
5900
|
}
|
|
5371
5901
|
const visibleLogs = logs;
|
|
5372
|
-
return /* @__PURE__ */
|
|
5373
|
-
/* @__PURE__ */
|
|
5374
|
-
/* @__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: [
|
|
5375
5905
|
"Logs: ",
|
|
5376
5906
|
selectedDeployment.config.name
|
|
5377
5907
|
] }),
|
|
5378
|
-
/* @__PURE__ */
|
|
5379
|
-
/* @__PURE__ */
|
|
5908
|
+
/* @__PURE__ */ jsx8("text", { fg: t.fg.muted, children: " | " }),
|
|
5909
|
+
/* @__PURE__ */ jsxs8("text", { fg: autoRefresh ? t.status.success : t.fg.muted, children: [
|
|
5380
5910
|
"Auto: ",
|
|
5381
5911
|
autoRefresh ? "ON (5s)" : "OFF"
|
|
5382
5912
|
] }),
|
|
5383
|
-
lastFetched && /* @__PURE__ */
|
|
5384
|
-
/* @__PURE__ */
|
|
5385
|
-
/* @__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: [
|
|
5386
5916
|
"Fetched: ",
|
|
5387
5917
|
lastFetched.toLocaleTimeString()
|
|
5388
5918
|
] })
|
|
5389
5919
|
] })
|
|
5390
5920
|
] }),
|
|
5391
|
-
/* @__PURE__ */
|
|
5921
|
+
/* @__PURE__ */ jsx8(
|
|
5392
5922
|
"box",
|
|
5393
5923
|
{
|
|
5394
5924
|
flexDirection: "column",
|
|
5395
5925
|
borderStyle: "single",
|
|
5396
5926
|
borderColor: t.border.default,
|
|
5397
5927
|
padding: 1,
|
|
5398
|
-
children: /* @__PURE__ */
|
|
5928
|
+
children: /* @__PURE__ */ jsx8("box", { flexDirection: "column", children: visibleLogs.map((line, i) => {
|
|
5399
5929
|
const parsed = parseLogLine(line);
|
|
5400
5930
|
if (!parsed) return null;
|
|
5401
5931
|
const displayLine = parsed.timestamp ? `${parsed.timestamp} ${truncateLine(parsed.message, 100)}` : truncateLine(parsed.message, 120);
|
|
5402
|
-
return /* @__PURE__ */
|
|
5932
|
+
return /* @__PURE__ */ jsx8("text", { fg: logLevelColor(parsed.level), children: displayLine }, i);
|
|
5403
5933
|
}) })
|
|
5404
5934
|
}
|
|
5405
5935
|
),
|
|
5406
|
-
/* @__PURE__ */
|
|
5407
|
-
/* @__PURE__ */
|
|
5408
|
-
/* @__PURE__ */
|
|
5409
|
-
/* @__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: [
|
|
5410
5940
|
"Showing last ",
|
|
5411
5941
|
visibleLogs.length,
|
|
5412
5942
|
" lines"
|
|
@@ -5416,14 +5946,14 @@ function LogsView({ context }) {
|
|
|
5416
5946
|
}
|
|
5417
5947
|
|
|
5418
5948
|
// src/components/DestroyView.tsx
|
|
5419
|
-
import { useState as
|
|
5420
|
-
import { useKeyboard as
|
|
5421
|
-
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";
|
|
5422
5952
|
function DestroyView({ context }) {
|
|
5423
|
-
const [viewState, setViewState] =
|
|
5424
|
-
const [selectedIndex, setSelectedIndex] =
|
|
5425
|
-
const [error, setError] =
|
|
5426
|
-
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("");
|
|
5427
5957
|
const deployments = context.deployments;
|
|
5428
5958
|
const destroyDeployment = async (name) => {
|
|
5429
5959
|
setViewState("destroying");
|
|
@@ -5468,7 +5998,7 @@ function DestroyView({ context }) {
|
|
|
5468
5998
|
}
|
|
5469
5999
|
};
|
|
5470
6000
|
const selectedDeployment = deployments[selectedIndex];
|
|
5471
|
-
|
|
6001
|
+
useKeyboard8((key) => {
|
|
5472
6002
|
if (deployments.length === 0) {
|
|
5473
6003
|
context.navigateTo("home");
|
|
5474
6004
|
return;
|
|
@@ -5493,31 +6023,31 @@ function DestroyView({ context }) {
|
|
|
5493
6023
|
}
|
|
5494
6024
|
});
|
|
5495
6025
|
if (deployments.length === 0) {
|
|
5496
|
-
return /* @__PURE__ */
|
|
5497
|
-
/* @__PURE__ */
|
|
5498
|
-
/* @__PURE__ */
|
|
5499
|
-
/* @__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" })
|
|
5500
6030
|
] }),
|
|
5501
|
-
/* @__PURE__ */
|
|
6031
|
+
/* @__PURE__ */ jsx9(
|
|
5502
6032
|
"box",
|
|
5503
6033
|
{
|
|
5504
6034
|
flexDirection: "column",
|
|
5505
6035
|
borderStyle: "single",
|
|
5506
6036
|
borderColor: t.border.default,
|
|
5507
6037
|
padding: 1,
|
|
5508
|
-
children: /* @__PURE__ */
|
|
6038
|
+
children: /* @__PURE__ */ jsx9("text", { fg: t.status.warning, children: "No deployments found!" })
|
|
5509
6039
|
}
|
|
5510
6040
|
),
|
|
5511
|
-
/* @__PURE__ */
|
|
6041
|
+
/* @__PURE__ */ jsx9("text", { fg: t.fg.muted, marginTop: 2, children: "Press any key to return to home" })
|
|
5512
6042
|
] });
|
|
5513
6043
|
}
|
|
5514
6044
|
if (viewState === "selecting") {
|
|
5515
|
-
return /* @__PURE__ */
|
|
5516
|
-
/* @__PURE__ */
|
|
5517
|
-
/* @__PURE__ */
|
|
5518
|
-
/* @__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" })
|
|
5519
6049
|
] }),
|
|
5520
|
-
/* @__PURE__ */
|
|
6050
|
+
/* @__PURE__ */ jsxs9(
|
|
5521
6051
|
"box",
|
|
5522
6052
|
{
|
|
5523
6053
|
flexDirection: "column",
|
|
@@ -5525,18 +6055,18 @@ function DestroyView({ context }) {
|
|
|
5525
6055
|
borderColor: t.status.error,
|
|
5526
6056
|
padding: 1,
|
|
5527
6057
|
children: [
|
|
5528
|
-
/* @__PURE__ */
|
|
6058
|
+
/* @__PURE__ */ jsx9("text", { fg: t.status.error, marginBottom: 1, children: "WARNING: This action cannot be undone!" }),
|
|
5529
6059
|
deployments.map((deployment, index) => {
|
|
5530
6060
|
const isSelected = index === selectedIndex;
|
|
5531
|
-
return /* @__PURE__ */
|
|
6061
|
+
return /* @__PURE__ */ jsxs9(
|
|
5532
6062
|
"box",
|
|
5533
6063
|
{
|
|
5534
6064
|
flexDirection: "row",
|
|
5535
6065
|
backgroundColor: isSelected ? t.selection.bg : void 0,
|
|
5536
6066
|
children: [
|
|
5537
|
-
/* @__PURE__ */
|
|
5538
|
-
/* @__PURE__ */
|
|
5539
|
-
/* @__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: [
|
|
5540
6070
|
"[",
|
|
5541
6071
|
deployment.state.status,
|
|
5542
6072
|
"]"
|
|
@@ -5549,13 +6079,13 @@ function DestroyView({ context }) {
|
|
|
5549
6079
|
]
|
|
5550
6080
|
}
|
|
5551
6081
|
),
|
|
5552
|
-
/* @__PURE__ */
|
|
6082
|
+
/* @__PURE__ */ jsx9("text", { fg: t.fg.muted, marginTop: 2, children: "Arrow keys to select | Enter to destroy | Esc to go back" })
|
|
5553
6083
|
] });
|
|
5554
6084
|
}
|
|
5555
6085
|
if (viewState === "confirming") {
|
|
5556
|
-
return /* @__PURE__ */
|
|
5557
|
-
/* @__PURE__ */
|
|
5558
|
-
/* @__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(
|
|
5559
6089
|
"box",
|
|
5560
6090
|
{
|
|
5561
6091
|
flexDirection: "column",
|
|
@@ -5563,26 +6093,26 @@ function DestroyView({ context }) {
|
|
|
5563
6093
|
borderColor: t.status.error,
|
|
5564
6094
|
padding: 1,
|
|
5565
6095
|
children: [
|
|
5566
|
-
/* @__PURE__ */
|
|
5567
|
-
/* @__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: [
|
|
5568
6098
|
"Deployment: ",
|
|
5569
6099
|
selectedDeployment.config.name
|
|
5570
6100
|
] }),
|
|
5571
|
-
selectedDeployment.state.serverIp && /* @__PURE__ */
|
|
6101
|
+
selectedDeployment.state.serverIp && /* @__PURE__ */ jsxs9("text", { fg: t.fg.primary, children: [
|
|
5572
6102
|
"Server IP: ",
|
|
5573
6103
|
selectedDeployment.state.serverIp
|
|
5574
6104
|
] }),
|
|
5575
|
-
/* @__PURE__ */
|
|
5576
|
-
/* @__PURE__ */
|
|
5577
|
-
/* @__PURE__ */
|
|
5578
|
-
/* @__PURE__ */
|
|
5579
|
-
/* @__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" })
|
|
5580
6110
|
]
|
|
5581
6111
|
}
|
|
5582
6112
|
),
|
|
5583
|
-
/* @__PURE__ */
|
|
5584
|
-
/* @__PURE__ */
|
|
5585
|
-
/* @__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(
|
|
5586
6116
|
"input",
|
|
5587
6117
|
{
|
|
5588
6118
|
value: confirmText,
|
|
@@ -5605,19 +6135,19 @@ function DestroyView({ context }) {
|
|
|
5605
6135
|
}
|
|
5606
6136
|
}
|
|
5607
6137
|
),
|
|
5608
|
-
error && /* @__PURE__ */
|
|
5609
|
-
/* @__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" })
|
|
5610
6140
|
] });
|
|
5611
6141
|
}
|
|
5612
6142
|
if (viewState === "destroying") {
|
|
5613
|
-
return /* @__PURE__ */
|
|
5614
|
-
/* @__PURE__ */
|
|
5615
|
-
/* @__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..." })
|
|
5616
6146
|
] });
|
|
5617
6147
|
}
|
|
5618
6148
|
if (viewState === "success") {
|
|
5619
|
-
return /* @__PURE__ */
|
|
5620
|
-
/* @__PURE__ */
|
|
6149
|
+
return /* @__PURE__ */ jsxs9("box", { flexDirection: "column", width: "100%", padding: 1, children: [
|
|
6150
|
+
/* @__PURE__ */ jsxs9(
|
|
5621
6151
|
"box",
|
|
5622
6152
|
{
|
|
5623
6153
|
flexDirection: "column",
|
|
@@ -5625,8 +6155,8 @@ function DestroyView({ context }) {
|
|
|
5625
6155
|
borderColor: t.status.success,
|
|
5626
6156
|
padding: 1,
|
|
5627
6157
|
children: [
|
|
5628
|
-
/* @__PURE__ */
|
|
5629
|
-
/* @__PURE__ */
|
|
6158
|
+
/* @__PURE__ */ jsx9("text", { fg: t.status.success, children: "Deployment Destroyed" }),
|
|
6159
|
+
/* @__PURE__ */ jsxs9("text", { fg: t.fg.primary, marginTop: 1, children: [
|
|
5630
6160
|
'The deployment "',
|
|
5631
6161
|
selectedDeployment.config.name,
|
|
5632
6162
|
'" has been permanently deleted.'
|
|
@@ -5634,12 +6164,12 @@ function DestroyView({ context }) {
|
|
|
5634
6164
|
]
|
|
5635
6165
|
}
|
|
5636
6166
|
),
|
|
5637
|
-
/* @__PURE__ */
|
|
6167
|
+
/* @__PURE__ */ jsx9("text", { fg: t.fg.muted, marginTop: 2, children: "Press any key to return to home" })
|
|
5638
6168
|
] });
|
|
5639
6169
|
}
|
|
5640
6170
|
if (viewState === "error") {
|
|
5641
|
-
return /* @__PURE__ */
|
|
5642
|
-
/* @__PURE__ */
|
|
6171
|
+
return /* @__PURE__ */ jsxs9("box", { flexDirection: "column", width: "100%", padding: 1, children: [
|
|
6172
|
+
/* @__PURE__ */ jsxs9(
|
|
5643
6173
|
"box",
|
|
5644
6174
|
{
|
|
5645
6175
|
flexDirection: "column",
|
|
@@ -5647,30 +6177,30 @@ function DestroyView({ context }) {
|
|
|
5647
6177
|
borderColor: t.status.error,
|
|
5648
6178
|
padding: 1,
|
|
5649
6179
|
children: [
|
|
5650
|
-
/* @__PURE__ */
|
|
5651
|
-
/* @__PURE__ */
|
|
6180
|
+
/* @__PURE__ */ jsx9("text", { fg: t.status.error, children: "Destruction Failed" }),
|
|
6181
|
+
/* @__PURE__ */ jsx9("text", { fg: t.fg.primary, marginTop: 1, children: error })
|
|
5652
6182
|
]
|
|
5653
6183
|
}
|
|
5654
6184
|
),
|
|
5655
|
-
/* @__PURE__ */
|
|
6185
|
+
/* @__PURE__ */ jsx9("text", { fg: t.fg.muted, marginTop: 2, children: "Press any key to go back" })
|
|
5656
6186
|
] });
|
|
5657
6187
|
}
|
|
5658
6188
|
return null;
|
|
5659
6189
|
}
|
|
5660
6190
|
|
|
5661
6191
|
// src/components/HelpView.tsx
|
|
5662
|
-
import { useKeyboard as
|
|
5663
|
-
import { jsx as
|
|
6192
|
+
import { useKeyboard as useKeyboard9 } from "@opentui/react";
|
|
6193
|
+
import { jsx as jsx10, jsxs as jsxs10 } from "@opentui/react/jsx-runtime";
|
|
5664
6194
|
function HelpView({ context }) {
|
|
5665
|
-
|
|
6195
|
+
useKeyboard9(() => {
|
|
5666
6196
|
context.navigateTo("home");
|
|
5667
6197
|
});
|
|
5668
|
-
return /* @__PURE__ */
|
|
5669
|
-
/* @__PURE__ */
|
|
5670
|
-
/* @__PURE__ */
|
|
5671
|
-
/* @__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" })
|
|
5672
6202
|
] }),
|
|
5673
|
-
/* @__PURE__ */
|
|
6203
|
+
/* @__PURE__ */ jsxs10(
|
|
5674
6204
|
"box",
|
|
5675
6205
|
{
|
|
5676
6206
|
flexDirection: "column",
|
|
@@ -5679,12 +6209,12 @@ function HelpView({ context }) {
|
|
|
5679
6209
|
padding: 1,
|
|
5680
6210
|
marginBottom: 1,
|
|
5681
6211
|
children: [
|
|
5682
|
-
/* @__PURE__ */
|
|
5683
|
-
/* @__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." })
|
|
5684
6214
|
]
|
|
5685
6215
|
}
|
|
5686
6216
|
),
|
|
5687
|
-
/* @__PURE__ */
|
|
6217
|
+
/* @__PURE__ */ jsxs10(
|
|
5688
6218
|
"box",
|
|
5689
6219
|
{
|
|
5690
6220
|
flexDirection: "column",
|
|
@@ -5693,48 +6223,48 @@ function HelpView({ context }) {
|
|
|
5693
6223
|
padding: 1,
|
|
5694
6224
|
marginBottom: 1,
|
|
5695
6225
|
children: [
|
|
5696
|
-
/* @__PURE__ */
|
|
5697
|
-
/* @__PURE__ */
|
|
5698
|
-
/* @__PURE__ */
|
|
5699
|
-
/* @__PURE__ */
|
|
5700
|
-
/* @__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" })
|
|
5701
6231
|
] }),
|
|
5702
|
-
/* @__PURE__ */
|
|
5703
|
-
/* @__PURE__ */
|
|
5704
|
-
/* @__PURE__ */
|
|
5705
|
-
/* @__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" })
|
|
5706
6236
|
] }),
|
|
5707
|
-
/* @__PURE__ */
|
|
5708
|
-
/* @__PURE__ */
|
|
5709
|
-
/* @__PURE__ */
|
|
5710
|
-
/* @__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" })
|
|
5711
6241
|
] }),
|
|
5712
|
-
/* @__PURE__ */
|
|
5713
|
-
/* @__PURE__ */
|
|
5714
|
-
/* @__PURE__ */
|
|
5715
|
-
/* @__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" })
|
|
5716
6246
|
] }),
|
|
5717
|
-
/* @__PURE__ */
|
|
5718
|
-
/* @__PURE__ */
|
|
5719
|
-
/* @__PURE__ */
|
|
5720
|
-
/* @__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" })
|
|
5721
6251
|
] }),
|
|
5722
|
-
/* @__PURE__ */
|
|
5723
|
-
/* @__PURE__ */
|
|
5724
|
-
/* @__PURE__ */
|
|
5725
|
-
/* @__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" })
|
|
5726
6256
|
] }),
|
|
5727
|
-
/* @__PURE__ */
|
|
5728
|
-
/* @__PURE__ */
|
|
5729
|
-
/* @__PURE__ */
|
|
5730
|
-
/* @__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" })
|
|
5731
6261
|
] }),
|
|
5732
|
-
/* @__PURE__ */
|
|
6262
|
+
/* @__PURE__ */ jsx10("text", { fg: t.fg.secondary, children: " View, fork, and use reusable deployment presets" })
|
|
5733
6263
|
] })
|
|
5734
6264
|
]
|
|
5735
6265
|
}
|
|
5736
6266
|
),
|
|
5737
|
-
/* @__PURE__ */
|
|
6267
|
+
/* @__PURE__ */ jsxs10(
|
|
5738
6268
|
"box",
|
|
5739
6269
|
{
|
|
5740
6270
|
flexDirection: "column",
|
|
@@ -5743,17 +6273,17 @@ function HelpView({ context }) {
|
|
|
5743
6273
|
padding: 1,
|
|
5744
6274
|
marginBottom: 1,
|
|
5745
6275
|
children: [
|
|
5746
|
-
/* @__PURE__ */
|
|
5747
|
-
/* @__PURE__ */
|
|
5748
|
-
/* @__PURE__ */
|
|
5749
|
-
/* @__PURE__ */
|
|
5750
|
-
/* @__PURE__ */
|
|
5751
|
-
/* @__PURE__ */
|
|
5752
|
-
/* @__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" })
|
|
5753
6283
|
]
|
|
5754
6284
|
}
|
|
5755
6285
|
),
|
|
5756
|
-
/* @__PURE__ */
|
|
6286
|
+
/* @__PURE__ */ jsxs10(
|
|
5757
6287
|
"box",
|
|
5758
6288
|
{
|
|
5759
6289
|
flexDirection: "column",
|
|
@@ -5762,36 +6292,36 @@ function HelpView({ context }) {
|
|
|
5762
6292
|
padding: 1,
|
|
5763
6293
|
marginBottom: 1,
|
|
5764
6294
|
children: [
|
|
5765
|
-
/* @__PURE__ */
|
|
5766
|
-
/* @__PURE__ */
|
|
5767
|
-
/* @__PURE__ */
|
|
5768
|
-
/* @__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)" })
|
|
5769
6299
|
] }),
|
|
5770
|
-
/* @__PURE__ */
|
|
5771
|
-
/* @__PURE__ */
|
|
5772
|
-
/* @__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)" })
|
|
5773
6303
|
] }),
|
|
5774
|
-
/* @__PURE__ */
|
|
5775
|
-
/* @__PURE__ */
|
|
5776
|
-
/* @__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" })
|
|
5777
6307
|
] })
|
|
5778
6308
|
]
|
|
5779
6309
|
}
|
|
5780
6310
|
),
|
|
5781
|
-
/* @__PURE__ */
|
|
5782
|
-
/* @__PURE__ */
|
|
5783
|
-
/* @__PURE__ */
|
|
5784
|
-
/* @__PURE__ */
|
|
5785
|
-
/* @__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/" })
|
|
5786
6316
|
] }),
|
|
5787
|
-
/* @__PURE__ */
|
|
6317
|
+
/* @__PURE__ */ jsx10("text", { fg: t.fg.muted, children: "Press any key to return to home" })
|
|
5788
6318
|
] });
|
|
5789
6319
|
}
|
|
5790
6320
|
|
|
5791
6321
|
// src/components/TemplatesView.tsx
|
|
5792
|
-
import { useState as
|
|
5793
|
-
import { useKeyboard as
|
|
5794
|
-
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";
|
|
5795
6325
|
var DO_DROPLET_SIZES2 = [
|
|
5796
6326
|
{ slug: "s-1vcpu-2gb", label: "1 vCPU, 2GB RAM, 50GB SSD", price: "$12/mo" },
|
|
5797
6327
|
{ slug: "s-2vcpu-2gb", label: "2 vCPU, 2GB RAM, 60GB SSD", price: "$18/mo" },
|
|
@@ -5800,20 +6330,20 @@ var DO_DROPLET_SIZES2 = [
|
|
|
5800
6330
|
{ slug: "s-8vcpu-16gb", label: "8 vCPU, 16GB RAM, 320GB SSD", price: "$96/mo" }
|
|
5801
6331
|
];
|
|
5802
6332
|
function TemplatesView({ context }) {
|
|
5803
|
-
const [viewState, setViewState] =
|
|
5804
|
-
const [templates, setTemplates] =
|
|
5805
|
-
const [selectedIndex, setSelectedIndex] =
|
|
5806
|
-
const [selectedTemplate, setSelectedTemplate] =
|
|
5807
|
-
const [error, setError] =
|
|
5808
|
-
const [forkStep, setForkStep] =
|
|
5809
|
-
const [forkName, setForkName] =
|
|
5810
|
-
const [forkProvider, setForkProvider] =
|
|
5811
|
-
const [forkProviderIndex, setForkProviderIndex] =
|
|
5812
|
-
const [forkDropletSizeIndex, setForkDropletSizeIndex] =
|
|
5813
|
-
const [forkAiProvider, setForkAiProvider] =
|
|
5814
|
-
const [forkAiProviderIndex, setForkAiProviderIndex] =
|
|
5815
|
-
const [forkModel, setForkModel] =
|
|
5816
|
-
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({
|
|
5817
6347
|
viewState,
|
|
5818
6348
|
selectedIndex,
|
|
5819
6349
|
selectedTemplate,
|
|
@@ -5927,7 +6457,7 @@ function TemplatesView({ context }) {
|
|
|
5927
6457
|
setViewState("viewing");
|
|
5928
6458
|
}
|
|
5929
6459
|
};
|
|
5930
|
-
|
|
6460
|
+
useKeyboard10((key) => {
|
|
5931
6461
|
const s = stateRef.current;
|
|
5932
6462
|
if (s.viewState === "listing") {
|
|
5933
6463
|
if (key.name === "up") {
|
|
@@ -6019,37 +6549,37 @@ function TemplatesView({ context }) {
|
|
|
6019
6549
|
}
|
|
6020
6550
|
}
|
|
6021
6551
|
});
|
|
6022
|
-
const renderListing = () => /* @__PURE__ */
|
|
6023
|
-
/* @__PURE__ */
|
|
6024
|
-
/* @__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(
|
|
6025
6555
|
"box",
|
|
6026
6556
|
{
|
|
6027
6557
|
flexDirection: "column",
|
|
6028
6558
|
borderStyle: "single",
|
|
6029
6559
|
borderColor: t.border.default,
|
|
6030
6560
|
padding: 1,
|
|
6031
|
-
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) => {
|
|
6032
6562
|
const isSelected = i === selectedIndex;
|
|
6033
6563
|
const badge = tmpl.builtIn ? "[built-in]" : "[custom]";
|
|
6034
6564
|
const badgeColor = tmpl.builtIn ? t.status.info : t.status.success;
|
|
6035
|
-
return /* @__PURE__ */
|
|
6036
|
-
/* @__PURE__ */
|
|
6037
|
-
/* @__PURE__ */
|
|
6038
|
-
/* @__PURE__ */
|
|
6039
|
-
/* @__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 })
|
|
6040
6570
|
] }, tmpl.id);
|
|
6041
6571
|
})
|
|
6042
6572
|
}
|
|
6043
6573
|
),
|
|
6044
|
-
/* @__PURE__ */
|
|
6574
|
+
/* @__PURE__ */ jsx11("text", { fg: t.fg.muted, marginTop: 1, children: "Enter to view, Esc to go back" })
|
|
6045
6575
|
] });
|
|
6046
6576
|
const renderViewing = () => {
|
|
6047
6577
|
if (!selectedTemplate) return null;
|
|
6048
6578
|
const tmpl = selectedTemplate;
|
|
6049
|
-
return /* @__PURE__ */
|
|
6050
|
-
/* @__PURE__ */
|
|
6051
|
-
/* @__PURE__ */
|
|
6052
|
-
/* @__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(
|
|
6053
6583
|
"box",
|
|
6054
6584
|
{
|
|
6055
6585
|
flexDirection: "column",
|
|
@@ -6058,89 +6588,89 @@ function TemplatesView({ context }) {
|
|
|
6058
6588
|
padding: 1,
|
|
6059
6589
|
marginBottom: 1,
|
|
6060
6590
|
children: [
|
|
6061
|
-
/* @__PURE__ */
|
|
6062
|
-
/* @__PURE__ */
|
|
6063
|
-
/* @__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" })
|
|
6064
6594
|
] }),
|
|
6065
|
-
/* @__PURE__ */
|
|
6066
|
-
/* @__PURE__ */
|
|
6067
|
-
/* @__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] })
|
|
6068
6598
|
] }),
|
|
6069
|
-
tmpl.hetzner && /* @__PURE__ */
|
|
6070
|
-
/* @__PURE__ */
|
|
6071
|
-
/* @__PURE__ */
|
|
6072
|
-
/* @__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 })
|
|
6073
6603
|
] }),
|
|
6074
|
-
/* @__PURE__ */
|
|
6075
|
-
/* @__PURE__ */
|
|
6076
|
-
/* @__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 })
|
|
6077
6607
|
] })
|
|
6078
6608
|
] }),
|
|
6079
|
-
tmpl.digitalocean && /* @__PURE__ */
|
|
6080
|
-
/* @__PURE__ */
|
|
6081
|
-
/* @__PURE__ */
|
|
6082
|
-
/* @__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 })
|
|
6083
6613
|
] }),
|
|
6084
|
-
/* @__PURE__ */
|
|
6085
|
-
/* @__PURE__ */
|
|
6086
|
-
/* @__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 })
|
|
6087
6617
|
] })
|
|
6088
6618
|
] }),
|
|
6089
|
-
/* @__PURE__ */
|
|
6090
|
-
/* @__PURE__ */
|
|
6091
|
-
/* @__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 })
|
|
6092
6622
|
] }),
|
|
6093
|
-
/* @__PURE__ */
|
|
6094
|
-
/* @__PURE__ */
|
|
6095
|
-
/* @__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 })
|
|
6096
6626
|
] }),
|
|
6097
|
-
/* @__PURE__ */
|
|
6098
|
-
/* @__PURE__ */
|
|
6099
|
-
/* @__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 })
|
|
6100
6630
|
] })
|
|
6101
6631
|
]
|
|
6102
6632
|
}
|
|
6103
6633
|
),
|
|
6104
|
-
/* @__PURE__ */
|
|
6105
|
-
/* @__PURE__ */
|
|
6106
|
-
/* @__PURE__ */
|
|
6107
|
-
/* @__PURE__ */
|
|
6108
|
-
/* @__PURE__ */
|
|
6109
|
-
!tmpl.builtIn && /* @__PURE__ */
|
|
6110
|
-
/* @__PURE__ */
|
|
6111
|
-
/* @__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 " })
|
|
6112
6642
|
] }),
|
|
6113
|
-
/* @__PURE__ */
|
|
6643
|
+
/* @__PURE__ */ jsx11("text", { fg: t.fg.muted, children: "[Esc] Back" })
|
|
6114
6644
|
] }),
|
|
6115
|
-
error && /* @__PURE__ */
|
|
6645
|
+
error && /* @__PURE__ */ jsx11("text", { fg: t.status.error, marginTop: 1, children: error })
|
|
6116
6646
|
] });
|
|
6117
6647
|
};
|
|
6118
|
-
const renderDeleteConfirm = () => /* @__PURE__ */
|
|
6119
|
-
/* @__PURE__ */
|
|
6120
|
-
/* @__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: [
|
|
6121
6651
|
'Are you sure you want to delete "',
|
|
6122
6652
|
selectedTemplate?.name,
|
|
6123
6653
|
'"?'
|
|
6124
6654
|
] }),
|
|
6125
|
-
/* @__PURE__ */
|
|
6126
|
-
/* @__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" })
|
|
6127
6657
|
] });
|
|
6128
|
-
const renderForkComplete = () => /* @__PURE__ */
|
|
6129
|
-
/* @__PURE__ */
|
|
6130
|
-
/* @__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: [
|
|
6131
6661
|
'Your template "',
|
|
6132
6662
|
forkName,
|
|
6133
6663
|
'" has been saved.'
|
|
6134
6664
|
] }),
|
|
6135
|
-
/* @__PURE__ */
|
|
6665
|
+
/* @__PURE__ */ jsx11("text", { fg: t.fg.muted, marginTop: 2, children: "Press any key to return to templates list" })
|
|
6136
6666
|
] });
|
|
6137
6667
|
const renderForking = () => {
|
|
6138
6668
|
switch (forkStep) {
|
|
6139
6669
|
case "fork_name":
|
|
6140
|
-
return /* @__PURE__ */
|
|
6141
|
-
/* @__PURE__ */
|
|
6142
|
-
/* @__PURE__ */
|
|
6143
|
-
/* @__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(
|
|
6144
6674
|
"input",
|
|
6145
6675
|
{
|
|
6146
6676
|
value: forkName,
|
|
@@ -6163,14 +6693,14 @@ function TemplatesView({ context }) {
|
|
|
6163
6693
|
}
|
|
6164
6694
|
}
|
|
6165
6695
|
),
|
|
6166
|
-
error && /* @__PURE__ */
|
|
6167
|
-
/* @__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" })
|
|
6168
6698
|
] });
|
|
6169
6699
|
case "fork_provider":
|
|
6170
|
-
return /* @__PURE__ */
|
|
6171
|
-
/* @__PURE__ */
|
|
6172
|
-
/* @__PURE__ */
|
|
6173
|
-
/* @__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(
|
|
6174
6704
|
"box",
|
|
6175
6705
|
{
|
|
6176
6706
|
flexDirection: "column",
|
|
@@ -6180,20 +6710,20 @@ function TemplatesView({ context }) {
|
|
|
6180
6710
|
padding: 1,
|
|
6181
6711
|
children: SUPPORTED_PROVIDERS.map((p, i) => {
|
|
6182
6712
|
const isSelected = i === forkProviderIndex;
|
|
6183
|
-
return /* @__PURE__ */
|
|
6184
|
-
/* @__PURE__ */
|
|
6185
|
-
/* @__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] })
|
|
6186
6716
|
] }, p);
|
|
6187
6717
|
})
|
|
6188
6718
|
}
|
|
6189
6719
|
),
|
|
6190
|
-
/* @__PURE__ */
|
|
6720
|
+
/* @__PURE__ */ jsx11("text", { fg: t.fg.muted, marginTop: 1, children: "Enter to select, Esc to go back" })
|
|
6191
6721
|
] });
|
|
6192
6722
|
case "fork_droplet_size":
|
|
6193
|
-
return /* @__PURE__ */
|
|
6194
|
-
/* @__PURE__ */
|
|
6195
|
-
/* @__PURE__ */
|
|
6196
|
-
/* @__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(
|
|
6197
6727
|
"box",
|
|
6198
6728
|
{
|
|
6199
6729
|
flexDirection: "column",
|
|
@@ -6203,21 +6733,21 @@ function TemplatesView({ context }) {
|
|
|
6203
6733
|
padding: 1,
|
|
6204
6734
|
children: DO_DROPLET_SIZES2.map((size, i) => {
|
|
6205
6735
|
const isSelected = i === forkDropletSizeIndex;
|
|
6206
|
-
return /* @__PURE__ */
|
|
6207
|
-
/* @__PURE__ */
|
|
6208
|
-
/* @__PURE__ */
|
|
6209
|
-
/* @__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 })
|
|
6210
6740
|
] }, size.slug);
|
|
6211
6741
|
})
|
|
6212
6742
|
}
|
|
6213
6743
|
),
|
|
6214
|
-
/* @__PURE__ */
|
|
6744
|
+
/* @__PURE__ */ jsx11("text", { fg: t.fg.muted, marginTop: 1, children: "Enter to select, Esc to go back" })
|
|
6215
6745
|
] });
|
|
6216
6746
|
case "fork_ai_provider":
|
|
6217
|
-
return /* @__PURE__ */
|
|
6218
|
-
/* @__PURE__ */
|
|
6219
|
-
/* @__PURE__ */
|
|
6220
|
-
/* @__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(
|
|
6221
6751
|
"box",
|
|
6222
6752
|
{
|
|
6223
6753
|
flexDirection: "column",
|
|
@@ -6227,21 +6757,21 @@ function TemplatesView({ context }) {
|
|
|
6227
6757
|
padding: 1,
|
|
6228
6758
|
children: AI_PROVIDERS.map((p, i) => {
|
|
6229
6759
|
const isSelected = i === forkAiProviderIndex;
|
|
6230
|
-
return /* @__PURE__ */
|
|
6231
|
-
/* @__PURE__ */
|
|
6232
|
-
/* @__PURE__ */
|
|
6233
|
-
/* @__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 })
|
|
6234
6764
|
] }, p.name);
|
|
6235
6765
|
})
|
|
6236
6766
|
}
|
|
6237
6767
|
),
|
|
6238
|
-
/* @__PURE__ */
|
|
6768
|
+
/* @__PURE__ */ jsx11("text", { fg: t.fg.muted, marginTop: 1, children: "Enter to select, Esc to go back" })
|
|
6239
6769
|
] });
|
|
6240
6770
|
case "fork_model":
|
|
6241
|
-
return /* @__PURE__ */
|
|
6242
|
-
/* @__PURE__ */
|
|
6243
|
-
/* @__PURE__ */
|
|
6244
|
-
/* @__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(
|
|
6245
6775
|
"input",
|
|
6246
6776
|
{
|
|
6247
6777
|
value: forkModel,
|
|
@@ -6264,13 +6794,13 @@ function TemplatesView({ context }) {
|
|
|
6264
6794
|
}
|
|
6265
6795
|
}
|
|
6266
6796
|
),
|
|
6267
|
-
error && /* @__PURE__ */
|
|
6268
|
-
/* @__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" })
|
|
6269
6799
|
] });
|
|
6270
6800
|
case "fork_confirm":
|
|
6271
|
-
return /* @__PURE__ */
|
|
6272
|
-
/* @__PURE__ */
|
|
6273
|
-
/* @__PURE__ */
|
|
6801
|
+
return /* @__PURE__ */ jsxs11("box", { flexDirection: "column", children: [
|
|
6802
|
+
/* @__PURE__ */ jsx11("text", { fg: t.accent, children: "Fork Template - Confirm" }),
|
|
6803
|
+
/* @__PURE__ */ jsxs11(
|
|
6274
6804
|
"box",
|
|
6275
6805
|
{
|
|
6276
6806
|
flexDirection: "column",
|
|
@@ -6279,35 +6809,35 @@ function TemplatesView({ context }) {
|
|
|
6279
6809
|
padding: 1,
|
|
6280
6810
|
marginTop: 1,
|
|
6281
6811
|
children: [
|
|
6282
|
-
/* @__PURE__ */
|
|
6283
|
-
/* @__PURE__ */
|
|
6284
|
-
/* @__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 })
|
|
6285
6815
|
] }),
|
|
6286
|
-
/* @__PURE__ */
|
|
6287
|
-
/* @__PURE__ */
|
|
6288
|
-
/* @__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] })
|
|
6289
6819
|
] }),
|
|
6290
|
-
forkProvider === "digitalocean" && /* @__PURE__ */
|
|
6291
|
-
/* @__PURE__ */
|
|
6292
|
-
/* @__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 })
|
|
6293
6823
|
] }),
|
|
6294
|
-
/* @__PURE__ */
|
|
6295
|
-
/* @__PURE__ */
|
|
6296
|
-
/* @__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 })
|
|
6297
6827
|
] }),
|
|
6298
|
-
/* @__PURE__ */
|
|
6299
|
-
/* @__PURE__ */
|
|
6300
|
-
/* @__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 })
|
|
6301
6831
|
] }),
|
|
6302
|
-
/* @__PURE__ */
|
|
6303
|
-
/* @__PURE__ */
|
|
6304
|
-
/* @__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" })
|
|
6305
6835
|
] })
|
|
6306
6836
|
]
|
|
6307
6837
|
}
|
|
6308
6838
|
),
|
|
6309
|
-
error && /* @__PURE__ */
|
|
6310
|
-
/* @__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" })
|
|
6311
6841
|
] });
|
|
6312
6842
|
}
|
|
6313
6843
|
};
|
|
@@ -6325,34 +6855,464 @@ function TemplatesView({ context }) {
|
|
|
6325
6855
|
return renderForking();
|
|
6326
6856
|
}
|
|
6327
6857
|
};
|
|
6328
|
-
return /* @__PURE__ */
|
|
6329
|
-
/* @__PURE__ */
|
|
6330
|
-
/* @__PURE__ */
|
|
6331
|
-
/* @__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" })
|
|
6332
6862
|
] }),
|
|
6333
6863
|
renderContent()
|
|
6334
6864
|
] });
|
|
6335
6865
|
}
|
|
6336
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
|
+
|
|
6337
7296
|
// src/App.tsx
|
|
6338
|
-
import { jsx as
|
|
7297
|
+
import { jsx as jsx13 } from "@opentui/react/jsx-runtime";
|
|
6339
7298
|
function App() {
|
|
6340
|
-
const renderer =
|
|
6341
|
-
const [currentView, setCurrentView] =
|
|
6342
|
-
const [selectedDeployment, setSelectedDeployment] =
|
|
6343
|
-
const [deployments, setDeployments] =
|
|
7299
|
+
const renderer = useRenderer2();
|
|
7300
|
+
const [currentView, setCurrentView] = useState12("home");
|
|
7301
|
+
const [selectedDeployment, setSelectedDeployment] = useState12(null);
|
|
7302
|
+
const [deployments, setDeployments] = useState12(() => {
|
|
6344
7303
|
try {
|
|
6345
7304
|
return getAllDeployments();
|
|
6346
7305
|
} catch {
|
|
6347
7306
|
return [];
|
|
6348
7307
|
}
|
|
6349
7308
|
});
|
|
6350
|
-
const [selectedTemplate, setSelectedTemplate] =
|
|
6351
|
-
const
|
|
6352
|
-
const
|
|
7309
|
+
const [selectedTemplate, setSelectedTemplate] = useState12(null);
|
|
7310
|
+
const [editingDeployment, setEditingDeployment] = useState12(null);
|
|
7311
|
+
const wasDraggingRef = useRef6(false);
|
|
7312
|
+
const handleMouseDrag = useCallback4(() => {
|
|
6353
7313
|
wasDraggingRef.current = true;
|
|
6354
7314
|
}, []);
|
|
6355
|
-
const handleMouseUp =
|
|
7315
|
+
const handleMouseUp = useCallback4(() => {
|
|
6356
7316
|
if (!wasDraggingRef.current) return;
|
|
6357
7317
|
wasDraggingRef.current = false;
|
|
6358
7318
|
const selection = renderer.getSelection();
|
|
@@ -6363,14 +7323,14 @@ function App() {
|
|
|
6363
7323
|
}
|
|
6364
7324
|
}
|
|
6365
7325
|
}, [renderer]);
|
|
6366
|
-
const refreshDeployments =
|
|
7326
|
+
const refreshDeployments = useCallback4(() => {
|
|
6367
7327
|
try {
|
|
6368
7328
|
setDeployments(getAllDeployments());
|
|
6369
7329
|
} catch {
|
|
6370
7330
|
setDeployments([]);
|
|
6371
7331
|
}
|
|
6372
7332
|
}, []);
|
|
6373
|
-
const navigateTo =
|
|
7333
|
+
const navigateTo = useCallback4((view, deployment) => {
|
|
6374
7334
|
if (deployment !== void 0) {
|
|
6375
7335
|
setSelectedDeployment(deployment);
|
|
6376
7336
|
}
|
|
@@ -6383,35 +7343,41 @@ function App() {
|
|
|
6383
7343
|
deployments,
|
|
6384
7344
|
refreshDeployments,
|
|
6385
7345
|
selectedTemplate,
|
|
6386
|
-
setSelectedTemplate
|
|
7346
|
+
setSelectedTemplate,
|
|
7347
|
+
editingDeployment,
|
|
7348
|
+
setEditingDeployment
|
|
6387
7349
|
};
|
|
6388
7350
|
const renderView = () => {
|
|
6389
7351
|
switch (currentView) {
|
|
6390
7352
|
case "home":
|
|
6391
|
-
return /* @__PURE__ */
|
|
7353
|
+
return /* @__PURE__ */ jsx13(Home, { context });
|
|
6392
7354
|
case "new":
|
|
6393
|
-
return /* @__PURE__ */
|
|
7355
|
+
return /* @__PURE__ */ jsx13(NewDeployment, { context });
|
|
7356
|
+
case "list":
|
|
7357
|
+
return /* @__PURE__ */ jsx13(ListView, { context });
|
|
6394
7358
|
case "deploy":
|
|
6395
|
-
return /* @__PURE__ */
|
|
7359
|
+
return /* @__PURE__ */ jsx13(DeployView, { context });
|
|
6396
7360
|
case "deploying":
|
|
6397
|
-
return /* @__PURE__ */
|
|
7361
|
+
return /* @__PURE__ */ jsx13(DeployingView, { context });
|
|
6398
7362
|
case "status":
|
|
6399
|
-
return /* @__PURE__ */
|
|
7363
|
+
return /* @__PURE__ */ jsx13(StatusView, { context });
|
|
6400
7364
|
case "ssh":
|
|
6401
|
-
return /* @__PURE__ */
|
|
7365
|
+
return /* @__PURE__ */ jsx13(SSHView, { context });
|
|
6402
7366
|
case "logs":
|
|
6403
|
-
return /* @__PURE__ */
|
|
7367
|
+
return /* @__PURE__ */ jsx13(LogsView, { context });
|
|
7368
|
+
case "dashboard":
|
|
7369
|
+
return /* @__PURE__ */ jsx13(DashboardView, { context });
|
|
6404
7370
|
case "destroy":
|
|
6405
|
-
return /* @__PURE__ */
|
|
7371
|
+
return /* @__PURE__ */ jsx13(DestroyView, { context });
|
|
6406
7372
|
case "help":
|
|
6407
|
-
return /* @__PURE__ */
|
|
7373
|
+
return /* @__PURE__ */ jsx13(HelpView, { context });
|
|
6408
7374
|
case "templates":
|
|
6409
|
-
return /* @__PURE__ */
|
|
7375
|
+
return /* @__PURE__ */ jsx13(TemplatesView, { context });
|
|
6410
7376
|
default:
|
|
6411
|
-
return /* @__PURE__ */
|
|
7377
|
+
return /* @__PURE__ */ jsx13(Home, { context });
|
|
6412
7378
|
}
|
|
6413
7379
|
};
|
|
6414
|
-
return /* @__PURE__ */
|
|
7380
|
+
return /* @__PURE__ */ jsx13(
|
|
6415
7381
|
"scrollbox",
|
|
6416
7382
|
{
|
|
6417
7383
|
width: "100%",
|
|
@@ -6438,12 +7404,40 @@ function App() {
|
|
|
6438
7404
|
}
|
|
6439
7405
|
|
|
6440
7406
|
// src/index.tsx
|
|
6441
|
-
import { jsx as
|
|
7407
|
+
import { jsx as jsx14 } from "@opentui/react/jsx-runtime";
|
|
7408
|
+
var args = process.argv.slice(2);
|
|
7409
|
+
if (args.includes("--version") || args.includes("-v")) {
|
|
7410
|
+
const require2 = createRequire(import.meta.url);
|
|
7411
|
+
const pkg = require2("../package.json");
|
|
7412
|
+
console.log(`clawcontrol v${pkg.version}`);
|
|
7413
|
+
process.exit(0);
|
|
7414
|
+
}
|
|
7415
|
+
if (args.includes("--help") || args.includes("-h")) {
|
|
7416
|
+
const require2 = createRequire(import.meta.url);
|
|
7417
|
+
const pkg = require2("../package.json");
|
|
7418
|
+
console.log(`
|
|
7419
|
+
clawcontrol v${pkg.version}
|
|
7420
|
+
${pkg.description}
|
|
7421
|
+
|
|
7422
|
+
Usage:
|
|
7423
|
+
clawcontrol Launch the interactive TUI
|
|
7424
|
+
clawcontrol --help Show this help message
|
|
7425
|
+
clawcontrol --version Show the version number
|
|
7426
|
+
|
|
7427
|
+
Options:
|
|
7428
|
+
-h, --help Show this help message
|
|
7429
|
+
-v, --version Show the version number
|
|
7430
|
+
|
|
7431
|
+
Documentation & source:
|
|
7432
|
+
https://github.com/ipenywis/clawcontrol
|
|
7433
|
+
`);
|
|
7434
|
+
process.exit(0);
|
|
7435
|
+
}
|
|
6442
7436
|
async function main() {
|
|
6443
7437
|
const renderer = await createCliRenderer({
|
|
6444
7438
|
useMouse: true
|
|
6445
7439
|
});
|
|
6446
7440
|
const root = createRoot(renderer);
|
|
6447
|
-
root.render(/* @__PURE__ */
|
|
7441
|
+
root.render(/* @__PURE__ */ jsx14(App, {}));
|
|
6448
7442
|
}
|
|
6449
7443
|
main().catch(console.error);
|