clawcontrol 0.1.6 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +1834 -728
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
// src/index.tsx
|
|
2
2
|
import { createRequire } from "module";
|
|
3
|
+
import { release } from "os";
|
|
3
4
|
import { createCliRenderer } from "@opentui/core";
|
|
4
5
|
import { createRoot } from "@opentui/react";
|
|
5
6
|
|
|
6
7
|
// src/App.tsx
|
|
7
|
-
import { useState as
|
|
8
|
-
import { useRenderer } from "@opentui/react";
|
|
8
|
+
import { useState as useState12, useCallback as useCallback4, useRef as useRef7 } from "react";
|
|
9
|
+
import { useRenderer as useRenderer2 } from "@opentui/react";
|
|
9
10
|
|
|
10
11
|
// src/components/Home.tsx
|
|
11
|
-
import { useState } from "react";
|
|
12
|
+
import { useState, useRef } from "react";
|
|
13
|
+
import { useKeyboard } from "@opentui/react";
|
|
12
14
|
|
|
13
15
|
// src/theme.ts
|
|
14
16
|
var palette = {
|
|
@@ -142,10 +144,12 @@ var LOGO = `
|
|
|
142
144
|
`;
|
|
143
145
|
var COMMANDS = [
|
|
144
146
|
{ name: "/new", description: "Initialize a new deployment" },
|
|
147
|
+
{ name: "/list", description: "List, edit, or fork deployments" },
|
|
145
148
|
{ name: "/deploy", description: "Deploy an initialized configuration" },
|
|
146
149
|
{ name: "/status", description: "View deployment status" },
|
|
147
150
|
{ name: "/ssh", description: "SSH into a deployment" },
|
|
148
151
|
{ name: "/logs", description: "View deployment logs" },
|
|
152
|
+
{ name: "/dashboard", description: "Open OpenClaw dashboard in browser" },
|
|
149
153
|
{ name: "/destroy", description: "Destroy a deployment" },
|
|
150
154
|
{ name: "/templates", description: "Manage deployment templates" },
|
|
151
155
|
{ name: "/help", description: "Show help" }
|
|
@@ -153,25 +157,53 @@ var COMMANDS = [
|
|
|
153
157
|
function Home({ context }) {
|
|
154
158
|
const [inputValue, setInputValue] = useState("");
|
|
155
159
|
const [error, setError] = useState(null);
|
|
160
|
+
const [selectedSuggestionIndex, setSelectedSuggestionIndex] = useState(-1);
|
|
161
|
+
const viewMap = {
|
|
162
|
+
"/new": "new",
|
|
163
|
+
"/list": "list",
|
|
164
|
+
"/deploy": "deploy",
|
|
165
|
+
"/status": "status",
|
|
166
|
+
"/ssh": "ssh",
|
|
167
|
+
"/logs": "logs",
|
|
168
|
+
"/dashboard": "dashboard",
|
|
169
|
+
"/destroy": "destroy",
|
|
170
|
+
"/templates": "templates",
|
|
171
|
+
"/help": "help"
|
|
172
|
+
};
|
|
173
|
+
const filteredCommands = inputValue.length >= 2 && inputValue.startsWith("/") ? COMMANDS.filter((cmd) => cmd.name.startsWith(inputValue.toLowerCase())) : [];
|
|
174
|
+
const stateRef = useRef({ selectedSuggestionIndex, filteredCommands, inputValue });
|
|
175
|
+
stateRef.current = { selectedSuggestionIndex, filteredCommands, inputValue };
|
|
156
176
|
const handleCommand = (command) => {
|
|
157
177
|
const cmd = command.trim().toLowerCase();
|
|
158
178
|
setError(null);
|
|
159
|
-
const viewMap = {
|
|
160
|
-
"/new": "new",
|
|
161
|
-
"/deploy": "deploy",
|
|
162
|
-
"/status": "status",
|
|
163
|
-
"/ssh": "ssh",
|
|
164
|
-
"/logs": "logs",
|
|
165
|
-
"/destroy": "destroy",
|
|
166
|
-
"/templates": "templates",
|
|
167
|
-
"/help": "help"
|
|
168
|
-
};
|
|
169
179
|
if (viewMap[cmd]) {
|
|
170
180
|
context.navigateTo(viewMap[cmd]);
|
|
171
181
|
} else if (cmd.startsWith("/")) {
|
|
172
182
|
setError(`Unknown command: ${cmd}. Type /help for available commands.`);
|
|
173
183
|
}
|
|
174
184
|
};
|
|
185
|
+
useKeyboard((key) => {
|
|
186
|
+
const s = stateRef.current;
|
|
187
|
+
if (s.filteredCommands.length === 0) return;
|
|
188
|
+
if (key.name === "down") {
|
|
189
|
+
setSelectedSuggestionIndex(
|
|
190
|
+
(prev) => prev < s.filteredCommands.length - 1 ? prev + 1 : 0
|
|
191
|
+
);
|
|
192
|
+
} else if (key.name === "up") {
|
|
193
|
+
setSelectedSuggestionIndex(
|
|
194
|
+
(prev) => prev > 0 ? prev - 1 : s.filteredCommands.length - 1
|
|
195
|
+
);
|
|
196
|
+
} else if (key.name === "tab") {
|
|
197
|
+
if (s.selectedSuggestionIndex >= 0 && s.selectedSuggestionIndex < s.filteredCommands.length) {
|
|
198
|
+
const cmd = s.filteredCommands[s.selectedSuggestionIndex].name;
|
|
199
|
+
setInputValue("");
|
|
200
|
+
setSelectedSuggestionIndex(-1);
|
|
201
|
+
handleCommand(cmd);
|
|
202
|
+
}
|
|
203
|
+
} else if (key.name === "escape") {
|
|
204
|
+
setSelectedSuggestionIndex(-1);
|
|
205
|
+
}
|
|
206
|
+
});
|
|
175
207
|
return /* @__PURE__ */ jsxs("box", { flexDirection: "column", width: "100%", height: "100%", children: [
|
|
176
208
|
/* @__PURE__ */ jsx(
|
|
177
209
|
"scrollbox",
|
|
@@ -223,7 +255,7 @@ function Home({ context }) {
|
|
|
223
255
|
children: [
|
|
224
256
|
/* @__PURE__ */ jsx("text", { fg: t.fg.primary, children: "Available Commands" }),
|
|
225
257
|
/* @__PURE__ */ jsx("box", { flexDirection: "column", marginTop: 1, children: COMMANDS.map((cmd) => /* @__PURE__ */ jsxs("box", { flexDirection: "row", children: [
|
|
226
|
-
/* @__PURE__ */ jsx("text", { fg: t.accent, width:
|
|
258
|
+
/* @__PURE__ */ jsx("text", { fg: t.accent, width: 14, children: cmd.name }),
|
|
227
259
|
/* @__PURE__ */ jsx("text", { fg: t.fg.secondary, children: cmd.description })
|
|
228
260
|
] }, cmd.name)) })
|
|
229
261
|
]
|
|
@@ -274,9 +306,19 @@ function Home({ context }) {
|
|
|
274
306
|
placeholder: "Type a command (e.g., /new)...",
|
|
275
307
|
focused: true,
|
|
276
308
|
width: "100%",
|
|
277
|
-
onInput: (value) =>
|
|
309
|
+
onInput: (value) => {
|
|
310
|
+
setInputValue(value);
|
|
311
|
+
const matches = value.length >= 2 && value.startsWith("/") ? COMMANDS.filter((cmd) => cmd.name.startsWith(value.toLowerCase())) : [];
|
|
312
|
+
setSelectedSuggestionIndex(matches.length > 0 ? 0 : -1);
|
|
313
|
+
},
|
|
278
314
|
onSubmit: (value) => {
|
|
279
|
-
|
|
315
|
+
const s = stateRef.current;
|
|
316
|
+
if (s.selectedSuggestionIndex >= 0 && s.selectedSuggestionIndex < s.filteredCommands.length) {
|
|
317
|
+
const cmd = s.filteredCommands[s.selectedSuggestionIndex].name;
|
|
318
|
+
setInputValue("");
|
|
319
|
+
setSelectedSuggestionIndex(-1);
|
|
320
|
+
handleCommand(cmd);
|
|
321
|
+
} else if (typeof value === "string" && typeof value.trim === "function" && value.trim()) {
|
|
280
322
|
handleCommand(value);
|
|
281
323
|
setInputValue("");
|
|
282
324
|
}
|
|
@@ -286,14 +328,22 @@ function Home({ context }) {
|
|
|
286
328
|
}
|
|
287
329
|
)
|
|
288
330
|
}
|
|
289
|
-
)
|
|
331
|
+
),
|
|
332
|
+
filteredCommands.length > 0 && /* @__PURE__ */ jsx("box", { flexDirection: "column", paddingLeft: 1, backgroundColor: t.bg.elevated, children: filteredCommands.map((cmd, i) => {
|
|
333
|
+
const selected = i === selectedSuggestionIndex;
|
|
334
|
+
return /* @__PURE__ */ jsxs("box", { flexDirection: "row", height: 1, overflow: "hidden", backgroundColor: selected ? t.selection.bg : t.bg.elevated, children: [
|
|
335
|
+
/* @__PURE__ */ jsx("text", { fg: selected ? t.selection.indicator : t.fg.muted, children: selected ? "> " : " " }),
|
|
336
|
+
/* @__PURE__ */ jsx("text", { fg: selected ? t.accent : t.fg.primary, width: 14, children: cmd.name }),
|
|
337
|
+
/* @__PURE__ */ jsx("text", { fg: t.fg.secondary, children: cmd.description })
|
|
338
|
+
] }, cmd.name);
|
|
339
|
+
}) })
|
|
290
340
|
] })
|
|
291
341
|
] });
|
|
292
342
|
}
|
|
293
343
|
|
|
294
344
|
// src/components/NewDeployment.tsx
|
|
295
|
-
import { useState as useState2, useRef, useEffect } from "react";
|
|
296
|
-
import { useKeyboard } from "@opentui/react";
|
|
345
|
+
import { useState as useState2, useRef as useRef2, useEffect } from "react";
|
|
346
|
+
import { useKeyboard as useKeyboard2 } from "@opentui/react";
|
|
297
347
|
import { appendFileSync } from "fs";
|
|
298
348
|
import { homedir as homedir3 } from "os";
|
|
299
349
|
import { join as join4 } from "path";
|
|
@@ -372,7 +422,8 @@ var DeploymentConfigSchema = z.object({
|
|
|
372
422
|
hetzner: HetznerConfigSchema.optional(),
|
|
373
423
|
digitalocean: DigitalOceanConfigSchema.optional(),
|
|
374
424
|
openclawConfig: OpenClawConfigSchema,
|
|
375
|
-
openclawAgent: OpenClawAgentConfigSchema.optional()
|
|
425
|
+
openclawAgent: OpenClawAgentConfigSchema.optional(),
|
|
426
|
+
skipTailscale: z.boolean().optional()
|
|
376
427
|
});
|
|
377
428
|
var DeploymentStateSchema = z.object({
|
|
378
429
|
status: DeploymentStatusSchema,
|
|
@@ -388,6 +439,7 @@ var DeploymentStateSchema = z.object({
|
|
|
388
439
|
retryCount: z.number()
|
|
389
440
|
})
|
|
390
441
|
),
|
|
442
|
+
gatewayToken: z.string().optional(),
|
|
391
443
|
lastError: z.string().optional(),
|
|
392
444
|
deployedAt: z.string().optional(),
|
|
393
445
|
updatedAt: z.string()
|
|
@@ -531,6 +583,14 @@ function deleteDeployment(name) {
|
|
|
531
583
|
}
|
|
532
584
|
rmSync(deploymentDir, { recursive: true, force: true });
|
|
533
585
|
}
|
|
586
|
+
function updateDeploymentConfig(name, config) {
|
|
587
|
+
const configPath = getConfigPath(name);
|
|
588
|
+
if (!existsSync(configPath)) {
|
|
589
|
+
throw new Error(`Deployment "${name}" not found`);
|
|
590
|
+
}
|
|
591
|
+
const validated = DeploymentConfigSchema.parse(config);
|
|
592
|
+
writeFileSync(configPath, JSON.stringify(validated, null, 2));
|
|
593
|
+
}
|
|
534
594
|
function validateDeploymentName(name) {
|
|
535
595
|
if (!name || name.trim() === "") {
|
|
536
596
|
return { valid: false, error: "Deployment name cannot be empty" };
|
|
@@ -1196,20 +1256,36 @@ var DO_DROPLET_SIZES = [
|
|
|
1196
1256
|
{ slug: "s-4vcpu-8gb", label: "4 vCPU, 8GB RAM, 160GB SSD", price: "$48/mo" },
|
|
1197
1257
|
{ slug: "s-8vcpu-16gb", label: "8 vCPU, 16GB RAM, 320GB SSD", price: "$96/mo" }
|
|
1198
1258
|
];
|
|
1199
|
-
function getStepList(provider, activeTemplate) {
|
|
1259
|
+
function getStepList(provider, activeTemplate, editMode) {
|
|
1260
|
+
if (editMode === "edit") {
|
|
1261
|
+
const steps = ["api_key_choose"];
|
|
1262
|
+
if (provider === "digitalocean") {
|
|
1263
|
+
steps.push("droplet_size");
|
|
1264
|
+
}
|
|
1265
|
+
steps.push("ai_api_key_choose", "model", "telegram_token_choose", "telegram_allow_choose", "confirm");
|
|
1266
|
+
return steps;
|
|
1267
|
+
}
|
|
1268
|
+
if (editMode === "fork") {
|
|
1269
|
+
const steps = ["name", "api_key_choose"];
|
|
1270
|
+
if (provider === "digitalocean") {
|
|
1271
|
+
steps.push("droplet_size");
|
|
1272
|
+
}
|
|
1273
|
+
steps.push("ai_api_key_choose", "model", "telegram_token_choose", "telegram_allow_choose", "tailscale", "confirm");
|
|
1274
|
+
return steps;
|
|
1275
|
+
}
|
|
1200
1276
|
const base = ["template_choice"];
|
|
1201
1277
|
if (!activeTemplate) {
|
|
1202
1278
|
base.push("name", "provider", "api_key_choose");
|
|
1203
1279
|
if (provider === "digitalocean") {
|
|
1204
1280
|
base.push("droplet_size");
|
|
1205
1281
|
}
|
|
1206
|
-
base.push("ai_provider", "ai_api_key_choose", "model", "telegram_token_choose", "telegram_allow_choose", "confirm");
|
|
1282
|
+
base.push("ai_provider", "ai_api_key_choose", "model", "telegram_token_choose", "telegram_allow_choose", "tailscale", "confirm");
|
|
1207
1283
|
} else {
|
|
1208
1284
|
base.push("name", "api_key_choose");
|
|
1209
1285
|
if (activeTemplate.provider === "digitalocean") {
|
|
1210
1286
|
base.push("droplet_size");
|
|
1211
1287
|
}
|
|
1212
|
-
base.push("ai_api_key_choose", "model", "telegram_token_choose", "telegram_allow_choose", "confirm");
|
|
1288
|
+
base.push("ai_api_key_choose", "model", "telegram_token_choose", "telegram_allow_choose", "tailscale", "confirm");
|
|
1213
1289
|
}
|
|
1214
1290
|
return base;
|
|
1215
1291
|
}
|
|
@@ -1236,19 +1312,39 @@ function resolveStepForProgress(s) {
|
|
|
1236
1312
|
}
|
|
1237
1313
|
}
|
|
1238
1314
|
function NewDeployment({ context }) {
|
|
1315
|
+
const [editingConfig, setEditingConfig] = useState2(null);
|
|
1316
|
+
const [editMode, setEditMode] = useState2(null);
|
|
1239
1317
|
const [templateChoices, setTemplateChoices] = useState2([]);
|
|
1240
1318
|
const [selectedTemplateIndex, setSelectedTemplateIndex] = useState2(0);
|
|
1241
1319
|
const [activeTemplate, setActiveTemplate] = useState2(null);
|
|
1242
1320
|
const [step, setStep] = useState2(() => {
|
|
1321
|
+
if (context.editingDeployment) {
|
|
1322
|
+
return context.editingDeployment.mode === "edit" ? "api_key_choose" : "name";
|
|
1323
|
+
}
|
|
1243
1324
|
if (context.selectedTemplate) return "name";
|
|
1244
1325
|
return "template_choice";
|
|
1245
1326
|
});
|
|
1246
|
-
const [name, setName] = useState2(
|
|
1327
|
+
const [name, setName] = useState2(() => {
|
|
1328
|
+
if (context.editingDeployment?.mode === "edit") return context.editingDeployment.config.name;
|
|
1329
|
+
return "";
|
|
1330
|
+
});
|
|
1247
1331
|
const [provider, setProvider] = useState2(() => {
|
|
1332
|
+
if (context.editingDeployment) return context.editingDeployment.config.provider;
|
|
1248
1333
|
return context.selectedTemplate?.provider ?? "hetzner";
|
|
1249
1334
|
});
|
|
1250
|
-
const [apiKey, setApiKey] = useState2(
|
|
1335
|
+
const [apiKey, setApiKey] = useState2(() => {
|
|
1336
|
+
if (context.editingDeployment) {
|
|
1337
|
+
const cfg = context.editingDeployment.config;
|
|
1338
|
+
if (cfg.provider === "hetzner" && cfg.hetzner) return cfg.hetzner.apiKey;
|
|
1339
|
+
if (cfg.provider === "digitalocean" && cfg.digitalocean) return cfg.digitalocean.apiKey;
|
|
1340
|
+
}
|
|
1341
|
+
return "";
|
|
1342
|
+
});
|
|
1251
1343
|
const [selectedDropletSizeIndex, setSelectedDropletSizeIndex] = useState2(() => {
|
|
1344
|
+
if (context.editingDeployment?.config.digitalocean) {
|
|
1345
|
+
const idx = DO_DROPLET_SIZES.findIndex((s) => s.slug === context.editingDeployment.config.digitalocean.size);
|
|
1346
|
+
return idx >= 0 ? idx : 0;
|
|
1347
|
+
}
|
|
1252
1348
|
if (context.selectedTemplate?.digitalocean) {
|
|
1253
1349
|
const idx = DO_DROPLET_SIZES.findIndex((s) => s.slug === context.selectedTemplate.digitalocean.size);
|
|
1254
1350
|
return idx >= 0 ? idx : 0;
|
|
@@ -1256,17 +1352,40 @@ function NewDeployment({ context }) {
|
|
|
1256
1352
|
return 0;
|
|
1257
1353
|
});
|
|
1258
1354
|
const [aiProvider, setAiProvider] = useState2(() => {
|
|
1355
|
+
if (context.editingDeployment?.config.openclawAgent) return context.editingDeployment.config.openclawAgent.aiProvider;
|
|
1259
1356
|
return context.selectedTemplate?.aiProvider ?? "";
|
|
1260
1357
|
});
|
|
1261
|
-
const [aiApiKey, setAiApiKey] = useState2(
|
|
1358
|
+
const [aiApiKey, setAiApiKey] = useState2(() => {
|
|
1359
|
+
if (context.editingDeployment?.config.openclawAgent) return context.editingDeployment.config.openclawAgent.aiApiKey;
|
|
1360
|
+
return "";
|
|
1361
|
+
});
|
|
1262
1362
|
const [model, setModel] = useState2(() => {
|
|
1363
|
+
if (context.editingDeployment?.config.openclawAgent) return context.editingDeployment.config.openclawAgent.model;
|
|
1263
1364
|
return context.selectedTemplate?.model ?? "";
|
|
1264
1365
|
});
|
|
1265
|
-
const [telegramBotToken, setTelegramBotToken] = useState2(
|
|
1266
|
-
|
|
1366
|
+
const [telegramBotToken, setTelegramBotToken] = useState2(() => {
|
|
1367
|
+
if (context.editingDeployment?.config.openclawAgent) return context.editingDeployment.config.openclawAgent.telegramBotToken;
|
|
1368
|
+
return "";
|
|
1369
|
+
});
|
|
1370
|
+
const [telegramAllowFrom, setTelegramAllowFrom] = useState2(() => {
|
|
1371
|
+
if (context.editingDeployment?.config.openclawAgent) return context.editingDeployment.config.openclawAgent.telegramAllowFrom ?? "";
|
|
1372
|
+
return "";
|
|
1373
|
+
});
|
|
1374
|
+
const [enableTailscale, setEnableTailscale] = useState2(() => {
|
|
1375
|
+
if (context.editingDeployment?.mode === "fork") return !context.editingDeployment.config.skipTailscale;
|
|
1376
|
+
return false;
|
|
1377
|
+
});
|
|
1378
|
+
const [selectedTailscaleIndex, setSelectedTailscaleIndex] = useState2(() => {
|
|
1379
|
+
if (context.editingDeployment?.mode === "fork" && !context.editingDeployment.config.skipTailscale) return 1;
|
|
1380
|
+
return 0;
|
|
1381
|
+
});
|
|
1267
1382
|
const [error, setError] = useState2(null);
|
|
1268
1383
|
const [isValidating, setIsValidating] = useState2(false);
|
|
1269
1384
|
const [selectedProviderIndex, setSelectedProviderIndex] = useState2(() => {
|
|
1385
|
+
if (context.editingDeployment) {
|
|
1386
|
+
const idx = SUPPORTED_PROVIDERS.indexOf(context.editingDeployment.config.provider);
|
|
1387
|
+
return idx >= 0 ? idx : 0;
|
|
1388
|
+
}
|
|
1270
1389
|
if (context.selectedTemplate) {
|
|
1271
1390
|
const idx = SUPPORTED_PROVIDERS.indexOf(context.selectedTemplate.provider);
|
|
1272
1391
|
return idx >= 0 ? idx : 0;
|
|
@@ -1274,6 +1393,10 @@ function NewDeployment({ context }) {
|
|
|
1274
1393
|
return 0;
|
|
1275
1394
|
});
|
|
1276
1395
|
const [selectedAiProviderIndex, setSelectedAiProviderIndex] = useState2(() => {
|
|
1396
|
+
if (context.editingDeployment?.config.openclawAgent) {
|
|
1397
|
+
const idx = AI_PROVIDERS.findIndex((p) => p.name === context.editingDeployment.config.openclawAgent.aiProvider);
|
|
1398
|
+
return idx >= 0 ? idx : 0;
|
|
1399
|
+
}
|
|
1277
1400
|
if (context.selectedTemplate) {
|
|
1278
1401
|
const idx = AI_PROVIDERS.findIndex((p) => p.name === context.selectedTemplate.aiProvider);
|
|
1279
1402
|
return idx >= 0 ? idx : 0;
|
|
@@ -1286,12 +1409,39 @@ function NewDeployment({ context }) {
|
|
|
1286
1409
|
const [savedTelegramUsers, setSavedTelegramUsers] = useState2([]);
|
|
1287
1410
|
const [selectedSavedKeyIndex, setSelectedSavedKeyIndex] = useState2(0);
|
|
1288
1411
|
const [newKeyName, setNewKeyName] = useState2("");
|
|
1289
|
-
const [apiKeyFromSaved, setApiKeyFromSaved] = useState2(
|
|
1290
|
-
const [aiApiKeyFromSaved, setAiApiKeyFromSaved] = useState2(
|
|
1291
|
-
const [telegramTokenFromSaved, setTelegramTokenFromSaved] = useState2(
|
|
1292
|
-
const [telegramAllowFromSaved, setTelegramAllowFromSaved] = useState2(
|
|
1412
|
+
const [apiKeyFromSaved, setApiKeyFromSaved] = useState2(() => !!context.editingDeployment);
|
|
1413
|
+
const [aiApiKeyFromSaved, setAiApiKeyFromSaved] = useState2(() => !!context.editingDeployment);
|
|
1414
|
+
const [telegramTokenFromSaved, setTelegramTokenFromSaved] = useState2(() => !!context.editingDeployment);
|
|
1415
|
+
const [telegramAllowFromSaved, setTelegramAllowFromSaved] = useState2(() => !!context.editingDeployment);
|
|
1293
1416
|
useEffect(() => {
|
|
1294
|
-
if (context.
|
|
1417
|
+
if (context.editingDeployment) {
|
|
1418
|
+
const ed = context.editingDeployment;
|
|
1419
|
+
setEditingConfig(ed.config);
|
|
1420
|
+
setEditMode(ed.mode);
|
|
1421
|
+
const syntheticTemplate = {
|
|
1422
|
+
id: "__editing__",
|
|
1423
|
+
name: ed.config.name,
|
|
1424
|
+
description: "Editing deployment",
|
|
1425
|
+
builtIn: false,
|
|
1426
|
+
createdAt: ed.config.createdAt,
|
|
1427
|
+
provider: ed.config.provider,
|
|
1428
|
+
hetzner: ed.config.hetzner ? {
|
|
1429
|
+
serverType: ed.config.hetzner.serverType,
|
|
1430
|
+
location: ed.config.hetzner.location,
|
|
1431
|
+
image: ed.config.hetzner.image
|
|
1432
|
+
} : void 0,
|
|
1433
|
+
digitalocean: ed.config.digitalocean ? {
|
|
1434
|
+
size: ed.config.digitalocean.size,
|
|
1435
|
+
region: ed.config.digitalocean.region,
|
|
1436
|
+
image: ed.config.digitalocean.image
|
|
1437
|
+
} : void 0,
|
|
1438
|
+
aiProvider: ed.config.openclawAgent?.aiProvider ?? "",
|
|
1439
|
+
model: ed.config.openclawAgent?.model ?? "",
|
|
1440
|
+
channel: ed.config.openclawAgent?.channel ?? "telegram"
|
|
1441
|
+
};
|
|
1442
|
+
setActiveTemplate(syntheticTemplate);
|
|
1443
|
+
context.setEditingDeployment(null);
|
|
1444
|
+
} else if (context.selectedTemplate) {
|
|
1295
1445
|
setActiveTemplate(context.selectedTemplate);
|
|
1296
1446
|
context.setSelectedTemplate(null);
|
|
1297
1447
|
}
|
|
@@ -1324,7 +1474,7 @@ function NewDeployment({ context }) {
|
|
|
1324
1474
|
setSelectedSavedKeyIndex(0);
|
|
1325
1475
|
}
|
|
1326
1476
|
}, [step]);
|
|
1327
|
-
const stateRef =
|
|
1477
|
+
const stateRef = useRef2({
|
|
1328
1478
|
name,
|
|
1329
1479
|
provider,
|
|
1330
1480
|
apiKey,
|
|
@@ -1336,6 +1486,8 @@ function NewDeployment({ context }) {
|
|
|
1336
1486
|
step,
|
|
1337
1487
|
selectedDropletSizeIndex,
|
|
1338
1488
|
activeTemplate,
|
|
1489
|
+
editingConfig,
|
|
1490
|
+
editMode,
|
|
1339
1491
|
savedProviderKeys,
|
|
1340
1492
|
savedAiKeys,
|
|
1341
1493
|
savedTelegramTokens,
|
|
@@ -1345,7 +1497,9 @@ function NewDeployment({ context }) {
|
|
|
1345
1497
|
apiKeyFromSaved,
|
|
1346
1498
|
aiApiKeyFromSaved,
|
|
1347
1499
|
telegramTokenFromSaved,
|
|
1348
|
-
telegramAllowFromSaved
|
|
1500
|
+
telegramAllowFromSaved,
|
|
1501
|
+
enableTailscale,
|
|
1502
|
+
selectedTailscaleIndex
|
|
1349
1503
|
});
|
|
1350
1504
|
stateRef.current = {
|
|
1351
1505
|
name,
|
|
@@ -1359,6 +1513,8 @@ function NewDeployment({ context }) {
|
|
|
1359
1513
|
step,
|
|
1360
1514
|
selectedDropletSizeIndex,
|
|
1361
1515
|
activeTemplate,
|
|
1516
|
+
editingConfig,
|
|
1517
|
+
editMode,
|
|
1362
1518
|
savedProviderKeys,
|
|
1363
1519
|
savedAiKeys,
|
|
1364
1520
|
savedTelegramTokens,
|
|
@@ -1368,10 +1524,12 @@ function NewDeployment({ context }) {
|
|
|
1368
1524
|
apiKeyFromSaved,
|
|
1369
1525
|
aiApiKeyFromSaved,
|
|
1370
1526
|
telegramTokenFromSaved,
|
|
1371
|
-
telegramAllowFromSaved
|
|
1527
|
+
telegramAllowFromSaved,
|
|
1528
|
+
enableTailscale,
|
|
1529
|
+
selectedTailscaleIndex
|
|
1372
1530
|
};
|
|
1373
1531
|
debugLog(`RENDER: step=${step}, apiKey.length=${apiKey?.length ?? "null"}`);
|
|
1374
|
-
const stepList = getStepList(provider, activeTemplate);
|
|
1532
|
+
const stepList = getStepList(provider, activeTemplate, editMode ?? void 0);
|
|
1375
1533
|
const handleConfirmFromRef = () => {
|
|
1376
1534
|
const s = stateRef.current;
|
|
1377
1535
|
debugLog(`handleConfirmFromRef CALLED: apiKey.length=${s.apiKey?.length ?? "null"}`);
|
|
@@ -1410,7 +1568,7 @@ function NewDeployment({ context }) {
|
|
|
1410
1568
|
const config = {
|
|
1411
1569
|
name: s.name,
|
|
1412
1570
|
provider: s.provider,
|
|
1413
|
-
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1571
|
+
createdAt: s.editingConfig?.createdAt ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
1414
1572
|
hetzner: s.provider === "hetzner" ? {
|
|
1415
1573
|
apiKey: s.apiKey,
|
|
1416
1574
|
serverType: tmpl?.hetzner?.serverType ?? "cpx11",
|
|
@@ -1424,6 +1582,7 @@ function NewDeployment({ context }) {
|
|
|
1424
1582
|
image: tmpl?.digitalocean?.image ?? "ubuntu-24-04-x64"
|
|
1425
1583
|
} : void 0,
|
|
1426
1584
|
openclawConfig: void 0,
|
|
1585
|
+
skipTailscale: !s.enableTailscale,
|
|
1427
1586
|
openclawAgent: {
|
|
1428
1587
|
aiProvider: s.aiProvider,
|
|
1429
1588
|
aiApiKey: s.aiApiKey,
|
|
@@ -1433,14 +1592,19 @@ function NewDeployment({ context }) {
|
|
|
1433
1592
|
telegramAllowFrom: s.telegramAllowFrom
|
|
1434
1593
|
}
|
|
1435
1594
|
};
|
|
1436
|
-
|
|
1595
|
+
if (s.editingConfig && s.editMode === "edit") {
|
|
1596
|
+
const updatedConfig = { ...config, name: s.editingConfig.name, createdAt: s.editingConfig.createdAt };
|
|
1597
|
+
updateDeploymentConfig(s.editingConfig.name, updatedConfig);
|
|
1598
|
+
} else {
|
|
1599
|
+
createDeployment(config);
|
|
1600
|
+
}
|
|
1437
1601
|
context.refreshDeployments();
|
|
1438
1602
|
setStep("complete");
|
|
1439
1603
|
} catch (err) {
|
|
1440
|
-
setError(`Failed to create deployment: ${err instanceof Error ? err.message : String(err)}`);
|
|
1604
|
+
setError(`Failed to ${s.editMode === "edit" ? "update" : "create"} deployment: ${err instanceof Error ? err.message : String(err)}`);
|
|
1441
1605
|
}
|
|
1442
1606
|
};
|
|
1443
|
-
|
|
1607
|
+
useKeyboard2((key) => {
|
|
1444
1608
|
const currentState = stateRef.current;
|
|
1445
1609
|
debugLog(`useKeyboard: key=${key.name}, step=${currentState.step}`);
|
|
1446
1610
|
if (currentState.step === "template_choice") {
|
|
@@ -1629,7 +1793,7 @@ function NewDeployment({ context }) {
|
|
|
1629
1793
|
setTelegramAllowFrom(saved.value);
|
|
1630
1794
|
setTelegramAllowFromSaved(true);
|
|
1631
1795
|
setError(null);
|
|
1632
|
-
setStep("confirm");
|
|
1796
|
+
setStep(currentState.editMode === "edit" ? "confirm" : "tailscale");
|
|
1633
1797
|
}
|
|
1634
1798
|
} else if (key.name === "escape") {
|
|
1635
1799
|
setStep("telegram_token_choose");
|
|
@@ -1666,13 +1830,26 @@ function NewDeployment({ context }) {
|
|
|
1666
1830
|
setStep("telegram_allow_name");
|
|
1667
1831
|
setNewKeyName("");
|
|
1668
1832
|
} else if (key.name === "n" || key.name === "escape") {
|
|
1833
|
+
setStep(currentState.editMode === "edit" ? "confirm" : "tailscale");
|
|
1834
|
+
}
|
|
1835
|
+
} else if (currentState.step === "tailscale") {
|
|
1836
|
+
const TAILSCALE_OPTIONS = ["Skip", "Configure Tailscale"];
|
|
1837
|
+
if (key.name === "up") {
|
|
1838
|
+
setSelectedTailscaleIndex((prev) => Math.max(0, prev - 1));
|
|
1839
|
+
} else if (key.name === "down") {
|
|
1840
|
+
setSelectedTailscaleIndex((prev) => Math.min(TAILSCALE_OPTIONS.length - 1, prev + 1));
|
|
1841
|
+
} else if (key.name === "return") {
|
|
1842
|
+
setEnableTailscale(currentState.selectedTailscaleIndex === 1);
|
|
1843
|
+
setError(null);
|
|
1669
1844
|
setStep("confirm");
|
|
1845
|
+
} else if (key.name === "escape") {
|
|
1846
|
+
setStep("telegram_allow_choose");
|
|
1670
1847
|
}
|
|
1671
1848
|
} else if (currentState.step === "confirm") {
|
|
1672
1849
|
if (key.name === "y" || key.name === "return") {
|
|
1673
1850
|
handleConfirmFromRef();
|
|
1674
1851
|
} else if (key.name === "n" || key.name === "escape") {
|
|
1675
|
-
setStep("telegram_allow_choose");
|
|
1852
|
+
setStep(currentState.editMode === "edit" ? "telegram_allow_choose" : "tailscale");
|
|
1676
1853
|
}
|
|
1677
1854
|
} else if (currentState.step === "complete") {
|
|
1678
1855
|
context.navigateTo("home");
|
|
@@ -1794,7 +1971,7 @@ function NewDeployment({ context }) {
|
|
|
1794
1971
|
}
|
|
1795
1972
|
setError(null);
|
|
1796
1973
|
if (telegramAllowFromSaved) {
|
|
1797
|
-
setStep("confirm");
|
|
1974
|
+
setStep(editMode === "edit" ? "confirm" : "tailscale");
|
|
1798
1975
|
} else {
|
|
1799
1976
|
setStep("telegram_allow_save");
|
|
1800
1977
|
}
|
|
@@ -2508,10 +2685,10 @@ function NewDeployment({ context }) {
|
|
|
2508
2685
|
setNewKeyName(value);
|
|
2509
2686
|
}
|
|
2510
2687
|
},
|
|
2511
|
-
onSubmit: () => handleSaveKeyName("telegram-user", telegramAllowFrom, "confirm"),
|
|
2688
|
+
onSubmit: () => handleSaveKeyName("telegram-user", telegramAllowFrom, editMode === "edit" ? "confirm" : "tailscale"),
|
|
2512
2689
|
onKeyDown: (e) => {
|
|
2513
2690
|
if (e.name === "escape") {
|
|
2514
|
-
setStep("confirm");
|
|
2691
|
+
setStep(editMode === "edit" ? "confirm" : "tailscale");
|
|
2515
2692
|
}
|
|
2516
2693
|
}
|
|
2517
2694
|
}
|
|
@@ -2519,6 +2696,41 @@ function NewDeployment({ context }) {
|
|
|
2519
2696
|
error && /* @__PURE__ */ jsx2("text", { fg: t.status.error, marginTop: 1, children: error }),
|
|
2520
2697
|
/* @__PURE__ */ jsx2("text", { fg: t.fg.muted, marginTop: 2, children: "Press Enter to save, Esc to skip" })
|
|
2521
2698
|
] });
|
|
2699
|
+
case "tailscale": {
|
|
2700
|
+
const tailscaleOptions = [
|
|
2701
|
+
{ label: "Skip", description: "Use SSH tunnel via /dashboard instead" },
|
|
2702
|
+
{ label: "Configure Tailscale", description: "Private VPN between your devices and the server" }
|
|
2703
|
+
];
|
|
2704
|
+
return /* @__PURE__ */ jsxs2("box", { flexDirection: "column", children: [
|
|
2705
|
+
/* @__PURE__ */ jsxs2("text", { fg: t.accent, children: [
|
|
2706
|
+
"Step ",
|
|
2707
|
+
currentStepNumber("tailscale"),
|
|
2708
|
+
": Tailscale Setup (Optional)"
|
|
2709
|
+
] }),
|
|
2710
|
+
/* @__PURE__ */ jsx2("text", { fg: t.fg.secondary, marginTop: 1, children: "Tailscale creates a private VPN between your devices and your OpenClaw server." }),
|
|
2711
|
+
/* @__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)." }),
|
|
2712
|
+
/* @__PURE__ */ jsx2(
|
|
2713
|
+
"box",
|
|
2714
|
+
{
|
|
2715
|
+
flexDirection: "column",
|
|
2716
|
+
borderStyle: "single",
|
|
2717
|
+
borderColor: t.border.default,
|
|
2718
|
+
marginTop: 1,
|
|
2719
|
+
padding: 1,
|
|
2720
|
+
children: tailscaleOptions.map((opt, i) => {
|
|
2721
|
+
const isSelected = i === selectedTailscaleIndex;
|
|
2722
|
+
return /* @__PURE__ */ jsxs2("box", { flexDirection: "row", backgroundColor: isSelected ? t.selection.bg : void 0, children: [
|
|
2723
|
+
/* @__PURE__ */ jsx2("text", { fg: isSelected ? t.selection.indicator : t.fg.primary, children: isSelected ? "> " : " " }),
|
|
2724
|
+
/* @__PURE__ */ jsx2("text", { fg: isSelected ? t.selection.fg : t.fg.primary, children: opt.label }),
|
|
2725
|
+
/* @__PURE__ */ jsx2("text", { fg: isSelected ? t.fg.primary : t.fg.secondary, children: " - " + opt.description })
|
|
2726
|
+
] }, opt.label);
|
|
2727
|
+
})
|
|
2728
|
+
}
|
|
2729
|
+
),
|
|
2730
|
+
error && /* @__PURE__ */ jsx2("text", { fg: t.status.error, marginTop: 1, children: error }),
|
|
2731
|
+
/* @__PURE__ */ jsx2("text", { fg: t.fg.muted, marginTop: 1, children: "Press Enter to select, Esc to go back" })
|
|
2732
|
+
] });
|
|
2733
|
+
}
|
|
2522
2734
|
case "confirm":
|
|
2523
2735
|
return /* @__PURE__ */ jsxs2("box", { flexDirection: "column", children: [
|
|
2524
2736
|
/* @__PURE__ */ jsxs2("text", { fg: t.accent, children: [
|
|
@@ -2578,6 +2790,10 @@ function NewDeployment({ context }) {
|
|
|
2578
2790
|
/* @__PURE__ */ jsxs2("box", { flexDirection: "row", children: [
|
|
2579
2791
|
/* @__PURE__ */ jsx2("text", { fg: t.fg.secondary, width: 20, children: "Allow From:" }),
|
|
2580
2792
|
/* @__PURE__ */ jsx2("text", { fg: t.fg.primary, children: telegramAllowFrom || "N/A" })
|
|
2793
|
+
] }),
|
|
2794
|
+
/* @__PURE__ */ jsxs2("box", { flexDirection: "row", children: [
|
|
2795
|
+
/* @__PURE__ */ jsx2("text", { fg: t.fg.secondary, width: 20, children: "Tailscale:" }),
|
|
2796
|
+
/* @__PURE__ */ jsx2("text", { fg: enableTailscale ? t.status.success : t.fg.muted, children: enableTailscale ? "Enabled" : "Skipped" })
|
|
2581
2797
|
] })
|
|
2582
2798
|
]
|
|
2583
2799
|
}
|
|
@@ -2587,7 +2803,7 @@ function NewDeployment({ context }) {
|
|
|
2587
2803
|
] });
|
|
2588
2804
|
case "complete":
|
|
2589
2805
|
return /* @__PURE__ */ jsxs2("box", { flexDirection: "column", children: [
|
|
2590
|
-
/* @__PURE__ */ jsx2("text", { fg: t.status.success, children: "Deployment Configuration Created!" }),
|
|
2806
|
+
/* @__PURE__ */ jsx2("text", { fg: t.status.success, children: editMode === "edit" ? "Deployment Updated Successfully!" : "Deployment Configuration Created!" }),
|
|
2591
2807
|
/* @__PURE__ */ jsxs2(
|
|
2592
2808
|
"box",
|
|
2593
2809
|
{
|
|
@@ -2597,14 +2813,10 @@ function NewDeployment({ context }) {
|
|
|
2597
2813
|
padding: 1,
|
|
2598
2814
|
marginTop: 1,
|
|
2599
2815
|
children: [
|
|
2600
|
-
/* @__PURE__ */
|
|
2601
|
-
'Your deployment "',
|
|
2602
|
-
name,
|
|
2603
|
-
'" has been initialized.'
|
|
2604
|
-
] }),
|
|
2816
|
+
/* @__PURE__ */ jsx2("text", { fg: t.fg.primary, children: editMode === "edit" ? `Your deployment "${editingConfig?.name ?? name}" has been updated.` : `Your deployment "${name}" has been initialized.` }),
|
|
2605
2817
|
/* @__PURE__ */ jsxs2("text", { fg: t.fg.secondary, marginTop: 1, children: [
|
|
2606
2818
|
"Configuration saved to: ~/.clawcontrol/deployments/",
|
|
2607
|
-
name,
|
|
2819
|
+
editingConfig?.name ?? name,
|
|
2608
2820
|
"/"
|
|
2609
2821
|
] }),
|
|
2610
2822
|
/* @__PURE__ */ jsxs2("text", { fg: t.fg.secondary, marginTop: 1, children: [
|
|
@@ -2621,15 +2833,15 @@ function NewDeployment({ context }) {
|
|
|
2621
2833
|
]
|
|
2622
2834
|
}
|
|
2623
2835
|
),
|
|
2624
|
-
/* @__PURE__ */ jsx2("text", { fg: t.accent, marginTop: 2, children: "Next step: Run /deploy to deploy this configuration" }),
|
|
2836
|
+
editMode !== "edit" && /* @__PURE__ */ jsx2("text", { fg: t.accent, marginTop: 2, children: "Next step: Run /deploy to deploy this configuration" }),
|
|
2625
2837
|
/* @__PURE__ */ jsx2("text", { fg: t.fg.muted, marginTop: 2, children: "Press any key to return to home" })
|
|
2626
2838
|
] });
|
|
2627
2839
|
}
|
|
2628
2840
|
};
|
|
2629
2841
|
return /* @__PURE__ */ jsxs2("box", { flexDirection: "column", width: "100%", padding: 1, children: [
|
|
2630
2842
|
/* @__PURE__ */ jsxs2("box", { flexDirection: "row", marginBottom: 2, children: [
|
|
2631
|
-
/* @__PURE__ */ jsx2("text", { fg: t.accent, children: "/new" }),
|
|
2632
|
-
/* @__PURE__ */ jsx2("text", { fg: t.fg.secondary, children: " - Initialize a new deployment" })
|
|
2843
|
+
/* @__PURE__ */ jsx2("text", { fg: t.accent, children: editMode === "edit" ? "/list" : "/new" }),
|
|
2844
|
+
/* @__PURE__ */ jsx2("text", { fg: t.fg.secondary, children: editMode === "edit" ? ` - Edit Deployment: ${editingConfig?.name ?? ""}` : editMode === "fork" ? " - Fork Deployment" : " - Initialize a new deployment" })
|
|
2633
2845
|
] }),
|
|
2634
2846
|
/* @__PURE__ */ jsx2("box", { flexDirection: "row", marginBottom: 2, children: stepList.map((s, i) => {
|
|
2635
2847
|
const resolvedStep = resolveStepForProgress(step);
|
|
@@ -2644,46 +2856,81 @@ function NewDeployment({ context }) {
|
|
|
2644
2856
|
] });
|
|
2645
2857
|
}
|
|
2646
2858
|
|
|
2647
|
-
// src/components/
|
|
2648
|
-
import { useState as useState3 } from "react";
|
|
2649
|
-
import { useKeyboard as
|
|
2650
|
-
import { jsx as jsx3, jsxs as jsxs3 } from "@opentui/react/jsx-runtime";
|
|
2651
|
-
function
|
|
2859
|
+
// src/components/ListView.tsx
|
|
2860
|
+
import { useState as useState3, useRef as useRef3 } from "react";
|
|
2861
|
+
import { useKeyboard as useKeyboard3 } from "@opentui/react";
|
|
2862
|
+
import { Fragment, jsx as jsx3, jsxs as jsxs3 } from "@opentui/react/jsx-runtime";
|
|
2863
|
+
function ListView({ context }) {
|
|
2864
|
+
const [viewState, setViewState] = useState3("listing");
|
|
2652
2865
|
const [selectedIndex, setSelectedIndex] = useState3(0);
|
|
2653
|
-
const [
|
|
2866
|
+
const [confirmText, setConfirmText] = useState3("");
|
|
2867
|
+
const [error, setError] = useState3(null);
|
|
2868
|
+
const [deletedName, setDeletedName] = useState3("");
|
|
2654
2869
|
const deployments = context.deployments;
|
|
2655
|
-
const
|
|
2656
|
-
const
|
|
2657
|
-
|
|
2658
|
-
|
|
2659
|
-
|
|
2660
|
-
if (
|
|
2870
|
+
const selectedDeployment = deployments[selectedIndex];
|
|
2871
|
+
const stateRef = useRef3({ viewState, selectedIndex });
|
|
2872
|
+
stateRef.current = { viewState, selectedIndex };
|
|
2873
|
+
useKeyboard3((key) => {
|
|
2874
|
+
const current = stateRef.current;
|
|
2875
|
+
if (deployments.length === 0) {
|
|
2661
2876
|
context.navigateTo("home");
|
|
2662
2877
|
return;
|
|
2663
2878
|
}
|
|
2664
|
-
if (
|
|
2665
|
-
if (key.name === "
|
|
2666
|
-
|
|
2667
|
-
} else if (key.name === "
|
|
2668
|
-
|
|
2669
|
-
}
|
|
2670
|
-
} else {
|
|
2671
|
-
if (key.name === "up" && selectedIndex > 0) {
|
|
2672
|
-
setSelectedIndex(selectedIndex - 1);
|
|
2673
|
-
} else if (key.name === "down" && selectedIndex < allDeployments.length - 1) {
|
|
2674
|
-
setSelectedIndex(selectedIndex + 1);
|
|
2879
|
+
if (current.viewState === "listing") {
|
|
2880
|
+
if (key.name === "up" && current.selectedIndex > 0) {
|
|
2881
|
+
setSelectedIndex(current.selectedIndex - 1);
|
|
2882
|
+
} else if (key.name === "down" && current.selectedIndex < deployments.length - 1) {
|
|
2883
|
+
setSelectedIndex(current.selectedIndex + 1);
|
|
2675
2884
|
} else if (key.name === "return") {
|
|
2676
|
-
|
|
2885
|
+
setViewState("detail");
|
|
2677
2886
|
} else if (key.name === "escape") {
|
|
2678
2887
|
context.navigateTo("home");
|
|
2679
2888
|
}
|
|
2889
|
+
} else if (current.viewState === "detail") {
|
|
2890
|
+
const dep = deployments[current.selectedIndex];
|
|
2891
|
+
if (!dep) return;
|
|
2892
|
+
if (key.name === "e" && dep.state.status === "initialized") {
|
|
2893
|
+
context.setEditingDeployment({ config: dep.config, mode: "edit" });
|
|
2894
|
+
context.navigateTo("new");
|
|
2895
|
+
} else if (key.name === "f") {
|
|
2896
|
+
context.setEditingDeployment({ config: dep.config, mode: "fork" });
|
|
2897
|
+
context.navigateTo("new");
|
|
2898
|
+
} else if (key.name === "d") {
|
|
2899
|
+
if (dep.state.status === "initialized") {
|
|
2900
|
+
setConfirmText("");
|
|
2901
|
+
setError(null);
|
|
2902
|
+
setViewState("delete_confirm");
|
|
2903
|
+
} else {
|
|
2904
|
+
setError("Deployed agents must be destroyed first using the /destroy command.");
|
|
2905
|
+
}
|
|
2906
|
+
} else if (key.name === "escape") {
|
|
2907
|
+
setError(null);
|
|
2908
|
+
setViewState("listing");
|
|
2909
|
+
}
|
|
2910
|
+
} else if (current.viewState === "success") {
|
|
2911
|
+
context.navigateTo("home");
|
|
2912
|
+
} else if (current.viewState === "error") {
|
|
2913
|
+
setError(null);
|
|
2914
|
+
setViewState("listing");
|
|
2680
2915
|
}
|
|
2681
2916
|
});
|
|
2682
|
-
|
|
2917
|
+
const handleDelete = (name) => {
|
|
2918
|
+
setViewState("deleting");
|
|
2919
|
+
try {
|
|
2920
|
+
deleteDeployment(name);
|
|
2921
|
+
setDeletedName(name);
|
|
2922
|
+
context.refreshDeployments();
|
|
2923
|
+
setViewState("success");
|
|
2924
|
+
} catch (err) {
|
|
2925
|
+
setError(err instanceof Error ? err.message : String(err));
|
|
2926
|
+
setViewState("error");
|
|
2927
|
+
}
|
|
2928
|
+
};
|
|
2929
|
+
if (deployments.length === 0) {
|
|
2683
2930
|
return /* @__PURE__ */ jsxs3("box", { flexDirection: "column", width: "100%", padding: 1, children: [
|
|
2684
2931
|
/* @__PURE__ */ jsxs3("box", { flexDirection: "row", marginBottom: 2, children: [
|
|
2685
|
-
/* @__PURE__ */ jsx3("text", { fg: t.accent, children: "/
|
|
2686
|
-
/* @__PURE__ */ jsx3("text", { fg: t.fg.secondary, children: " -
|
|
2932
|
+
/* @__PURE__ */ jsx3("text", { fg: t.accent, children: "/list" }),
|
|
2933
|
+
/* @__PURE__ */ jsx3("text", { fg: t.fg.secondary, children: " - Deployments" })
|
|
2687
2934
|
] }),
|
|
2688
2935
|
/* @__PURE__ */ jsxs3(
|
|
2689
2936
|
"box",
|
|
@@ -2694,135 +2941,410 @@ function DeployView({ context }) {
|
|
|
2694
2941
|
padding: 1,
|
|
2695
2942
|
children: [
|
|
2696
2943
|
/* @__PURE__ */ jsx3("text", { fg: t.status.warning, children: "No deployments found!" }),
|
|
2697
|
-
/* @__PURE__ */ jsx3("text", { fg: t.fg.secondary, marginTop: 1, children: "Run /new
|
|
2944
|
+
/* @__PURE__ */ jsx3("text", { fg: t.fg.secondary, marginTop: 1, children: "Run /new to create a deployment." })
|
|
2698
2945
|
]
|
|
2699
2946
|
}
|
|
2700
2947
|
),
|
|
2701
2948
|
/* @__PURE__ */ jsx3("text", { fg: t.fg.muted, marginTop: 2, children: "Press any key to return to home" })
|
|
2702
2949
|
] });
|
|
2703
2950
|
}
|
|
2704
|
-
if (
|
|
2951
|
+
if (viewState === "listing") {
|
|
2705
2952
|
return /* @__PURE__ */ jsxs3("box", { flexDirection: "column", width: "100%", padding: 1, children: [
|
|
2706
2953
|
/* @__PURE__ */ jsxs3("box", { flexDirection: "row", marginBottom: 2, children: [
|
|
2707
|
-
/* @__PURE__ */ jsx3("text", { fg: t.accent, children: "/
|
|
2708
|
-
/* @__PURE__ */
|
|
2954
|
+
/* @__PURE__ */ jsx3("text", { fg: t.accent, children: "/list" }),
|
|
2955
|
+
/* @__PURE__ */ jsxs3("text", { fg: t.fg.secondary, children: [
|
|
2956
|
+
" - Deployments (",
|
|
2957
|
+
deployments.length,
|
|
2958
|
+
")"
|
|
2959
|
+
] })
|
|
2709
2960
|
] }),
|
|
2710
|
-
/* @__PURE__ */
|
|
2961
|
+
/* @__PURE__ */ jsx3(
|
|
2711
2962
|
"box",
|
|
2712
2963
|
{
|
|
2713
2964
|
flexDirection: "column",
|
|
2714
2965
|
borderStyle: "single",
|
|
2715
2966
|
borderColor: t.border.default,
|
|
2716
2967
|
padding: 1,
|
|
2968
|
+
children: deployments.map((dep, index) => {
|
|
2969
|
+
const isSelected = index === selectedIndex;
|
|
2970
|
+
return /* @__PURE__ */ jsxs3(
|
|
2971
|
+
"box",
|
|
2972
|
+
{
|
|
2973
|
+
flexDirection: "row",
|
|
2974
|
+
backgroundColor: isSelected ? t.selection.bg : void 0,
|
|
2975
|
+
children: [
|
|
2976
|
+
/* @__PURE__ */ jsx3("text", { fg: isSelected ? t.selection.indicator : t.fg.primary, children: isSelected ? "> " : " " }),
|
|
2977
|
+
/* @__PURE__ */ jsx3("text", { fg: isSelected ? t.selection.fg : t.fg.primary, width: 25, children: dep.config.name }),
|
|
2978
|
+
/* @__PURE__ */ jsxs3("text", { fg: statusColor(dep.state.status), width: 16, children: [
|
|
2979
|
+
"[",
|
|
2980
|
+
dep.state.status,
|
|
2981
|
+
"]"
|
|
2982
|
+
] }),
|
|
2983
|
+
/* @__PURE__ */ jsx3("text", { fg: isSelected ? t.fg.secondary : t.fg.muted, children: PROVIDER_NAMES[dep.config.provider] })
|
|
2984
|
+
]
|
|
2985
|
+
},
|
|
2986
|
+
dep.config.name
|
|
2987
|
+
);
|
|
2988
|
+
})
|
|
2989
|
+
}
|
|
2990
|
+
),
|
|
2991
|
+
/* @__PURE__ */ jsx3("text", { fg: t.fg.muted, marginTop: 2, children: "Up/Down: Select | Enter: Details | Esc: Back" })
|
|
2992
|
+
] });
|
|
2993
|
+
}
|
|
2994
|
+
if (viewState === "detail" && selectedDeployment) {
|
|
2995
|
+
const dep = selectedDeployment;
|
|
2996
|
+
const isInitialized = dep.state.status === "initialized";
|
|
2997
|
+
return /* @__PURE__ */ jsxs3("box", { flexDirection: "column", width: "100%", padding: 1, children: [
|
|
2998
|
+
/* @__PURE__ */ jsxs3("box", { flexDirection: "row", marginBottom: 2, children: [
|
|
2999
|
+
/* @__PURE__ */ jsx3("text", { fg: t.accent, children: "/list" }),
|
|
3000
|
+
/* @__PURE__ */ jsxs3("text", { fg: t.fg.secondary, children: [
|
|
3001
|
+
" - ",
|
|
3002
|
+
dep.config.name
|
|
3003
|
+
] })
|
|
3004
|
+
] }),
|
|
3005
|
+
/* @__PURE__ */ jsxs3(
|
|
3006
|
+
"box",
|
|
3007
|
+
{
|
|
3008
|
+
flexDirection: "column",
|
|
3009
|
+
borderStyle: "single",
|
|
3010
|
+
borderColor: t.border.focus,
|
|
3011
|
+
padding: 1,
|
|
3012
|
+
marginBottom: 1,
|
|
2717
3013
|
children: [
|
|
2718
|
-
/* @__PURE__ */
|
|
2719
|
-
|
|
2720
|
-
|
|
2721
|
-
|
|
3014
|
+
/* @__PURE__ */ jsx3("text", { fg: t.accent, marginBottom: 1, children: "Deployment Details" }),
|
|
3015
|
+
/* @__PURE__ */ jsxs3("box", { flexDirection: "row", children: [
|
|
3016
|
+
/* @__PURE__ */ jsx3("text", { fg: t.fg.secondary, width: 20, children: "Name:" }),
|
|
3017
|
+
/* @__PURE__ */ jsx3("text", { fg: t.fg.primary, children: dep.config.name })
|
|
2722
3018
|
] }),
|
|
2723
|
-
/* @__PURE__ */
|
|
2724
|
-
|
|
2725
|
-
"
|
|
2726
|
-
|
|
3019
|
+
/* @__PURE__ */ jsxs3("box", { flexDirection: "row", children: [
|
|
3020
|
+
/* @__PURE__ */ jsx3("text", { fg: t.fg.secondary, width: 20, children: "Status:" }),
|
|
3021
|
+
/* @__PURE__ */ jsx3("text", { fg: statusColor(dep.state.status), children: dep.state.status })
|
|
3022
|
+
] }),
|
|
3023
|
+
/* @__PURE__ */ jsxs3("box", { flexDirection: "row", children: [
|
|
3024
|
+
/* @__PURE__ */ jsx3("text", { fg: t.fg.secondary, width: 20, children: "Provider:" }),
|
|
3025
|
+
/* @__PURE__ */ jsx3("text", { fg: t.fg.primary, children: PROVIDER_NAMES[dep.config.provider] })
|
|
3026
|
+
] }),
|
|
3027
|
+
/* @__PURE__ */ jsxs3("box", { flexDirection: "row", children: [
|
|
3028
|
+
/* @__PURE__ */ jsx3("text", { fg: t.fg.secondary, width: 20, children: "Created:" }),
|
|
3029
|
+
/* @__PURE__ */ jsx3("text", { fg: t.fg.primary, children: new Date(dep.config.createdAt).toLocaleString() })
|
|
2727
3030
|
] }),
|
|
2728
|
-
/* @__PURE__ */
|
|
2729
|
-
|
|
2730
|
-
|
|
3031
|
+
dep.state.serverIp && /* @__PURE__ */ jsxs3("box", { flexDirection: "row", children: [
|
|
3032
|
+
/* @__PURE__ */ jsx3("text", { fg: t.fg.secondary, width: 20, children: "Server IP:" }),
|
|
3033
|
+
/* @__PURE__ */ jsx3("text", { fg: t.accent, children: dep.state.serverIp })
|
|
3034
|
+
] }),
|
|
3035
|
+
dep.config.provider === "hetzner" && dep.config.hetzner && /* @__PURE__ */ jsxs3("box", { flexDirection: "row", children: [
|
|
3036
|
+
/* @__PURE__ */ jsx3("text", { fg: t.fg.secondary, width: 20, children: "Server Type:" }),
|
|
3037
|
+
/* @__PURE__ */ jsx3("text", { fg: t.fg.primary, children: dep.config.hetzner.serverType })
|
|
3038
|
+
] }),
|
|
3039
|
+
dep.config.provider === "digitalocean" && dep.config.digitalocean && /* @__PURE__ */ jsxs3("box", { flexDirection: "row", children: [
|
|
3040
|
+
/* @__PURE__ */ jsx3("text", { fg: t.fg.secondary, width: 20, children: "Droplet Size:" }),
|
|
3041
|
+
/* @__PURE__ */ jsx3("text", { fg: t.fg.primary, children: dep.config.digitalocean.size })
|
|
3042
|
+
] }),
|
|
3043
|
+
dep.config.openclawAgent && /* @__PURE__ */ jsxs3(Fragment, { children: [
|
|
3044
|
+
/* @__PURE__ */ jsxs3("box", { flexDirection: "row", children: [
|
|
3045
|
+
/* @__PURE__ */ jsx3("text", { fg: t.fg.secondary, width: 20, children: "AI Provider:" }),
|
|
3046
|
+
/* @__PURE__ */ jsx3("text", { fg: t.fg.primary, children: dep.config.openclawAgent.aiProvider })
|
|
3047
|
+
] }),
|
|
3048
|
+
/* @__PURE__ */ jsxs3("box", { flexDirection: "row", children: [
|
|
3049
|
+
/* @__PURE__ */ jsx3("text", { fg: t.fg.secondary, width: 20, children: "Model:" }),
|
|
3050
|
+
/* @__PURE__ */ jsx3("text", { fg: t.fg.primary, children: dep.config.openclawAgent.model })
|
|
3051
|
+
] }),
|
|
3052
|
+
/* @__PURE__ */ jsxs3("box", { flexDirection: "row", children: [
|
|
3053
|
+
/* @__PURE__ */ jsx3("text", { fg: t.fg.secondary, width: 20, children: "Channel:" }),
|
|
3054
|
+
/* @__PURE__ */ jsx3("text", { fg: t.fg.primary, children: dep.config.openclawAgent.channel })
|
|
3055
|
+
] })
|
|
3056
|
+
] })
|
|
2731
3057
|
]
|
|
2732
3058
|
}
|
|
2733
3059
|
),
|
|
2734
|
-
/* @__PURE__ */
|
|
3060
|
+
/* @__PURE__ */ jsxs3("box", { flexDirection: "row", marginBottom: 1, children: [
|
|
3061
|
+
isInitialized && /* @__PURE__ */ jsx3("text", { fg: t.accent, marginRight: 2, children: "[E]dit" }),
|
|
3062
|
+
/* @__PURE__ */ jsx3("text", { fg: t.accent, marginRight: 2, children: "[F]ork" }),
|
|
3063
|
+
isInitialized ? /* @__PURE__ */ jsx3("text", { fg: t.status.error, children: "[D]elete" }) : /* @__PURE__ */ jsx3("text", { fg: t.fg.muted, children: "[D]elete (use /destroy)" })
|
|
3064
|
+
] }),
|
|
3065
|
+
error && /* @__PURE__ */ jsx3("text", { fg: t.status.error, marginTop: 1, children: error }),
|
|
3066
|
+
/* @__PURE__ */ jsx3("text", { fg: t.fg.muted, marginTop: 1, children: "Esc: Back to list" })
|
|
2735
3067
|
] });
|
|
2736
3068
|
}
|
|
2737
|
-
|
|
2738
|
-
/* @__PURE__ */ jsxs3("box", { flexDirection: "
|
|
2739
|
-
/* @__PURE__ */ jsx3("text", { fg: t.
|
|
2740
|
-
/* @__PURE__ */
|
|
2741
|
-
|
|
2742
|
-
|
|
2743
|
-
|
|
2744
|
-
|
|
2745
|
-
|
|
2746
|
-
|
|
2747
|
-
|
|
2748
|
-
|
|
2749
|
-
|
|
2750
|
-
|
|
2751
|
-
|
|
2752
|
-
|
|
2753
|
-
"
|
|
2754
|
-
{
|
|
2755
|
-
|
|
2756
|
-
|
|
2757
|
-
|
|
2758
|
-
|
|
2759
|
-
|
|
2760
|
-
|
|
2761
|
-
|
|
2762
|
-
|
|
2763
|
-
|
|
2764
|
-
|
|
2765
|
-
|
|
2766
|
-
|
|
2767
|
-
|
|
2768
|
-
|
|
2769
|
-
|
|
2770
|
-
|
|
2771
|
-
|
|
2772
|
-
|
|
2773
|
-
|
|
2774
|
-
|
|
2775
|
-
|
|
2776
|
-
|
|
2777
|
-
|
|
2778
|
-
|
|
2779
|
-
|
|
2780
|
-
|
|
2781
|
-
|
|
2782
|
-
|
|
2783
|
-
|
|
2784
|
-
|
|
2785
|
-
|
|
2786
|
-
|
|
2787
|
-
|
|
2788
|
-
|
|
2789
|
-
|
|
2790
|
-
|
|
2791
|
-
|
|
2792
|
-
}
|
|
2793
|
-
|
|
2794
|
-
|
|
2795
|
-
|
|
2796
|
-
|
|
2797
|
-
|
|
2798
|
-
|
|
2799
|
-
|
|
2800
|
-
|
|
2801
|
-
|
|
2802
|
-
|
|
2803
|
-
|
|
2804
|
-
|
|
2805
|
-
|
|
2806
|
-
|
|
2807
|
-
|
|
2808
|
-
|
|
2809
|
-
|
|
2810
|
-
|
|
2811
|
-
|
|
2812
|
-
|
|
2813
|
-
|
|
2814
|
-
|
|
2815
|
-
|
|
2816
|
-
|
|
2817
|
-
|
|
2818
|
-
|
|
2819
|
-
|
|
2820
|
-
|
|
2821
|
-
|
|
2822
|
-
|
|
2823
|
-
|
|
2824
|
-
|
|
2825
|
-
|
|
3069
|
+
if (viewState === "delete_confirm" && selectedDeployment) {
|
|
3070
|
+
return /* @__PURE__ */ jsxs3("box", { flexDirection: "column", width: "100%", padding: 1, children: [
|
|
3071
|
+
/* @__PURE__ */ jsx3("box", { flexDirection: "row", marginBottom: 2, children: /* @__PURE__ */ jsx3("text", { fg: t.status.error, children: "Confirm Deletion" }) }),
|
|
3072
|
+
/* @__PURE__ */ jsxs3(
|
|
3073
|
+
"box",
|
|
3074
|
+
{
|
|
3075
|
+
flexDirection: "column",
|
|
3076
|
+
borderStyle: "double",
|
|
3077
|
+
borderColor: t.status.error,
|
|
3078
|
+
padding: 1,
|
|
3079
|
+
children: [
|
|
3080
|
+
/* @__PURE__ */ jsx3("text", { fg: t.status.error, children: "You are about to delete:" }),
|
|
3081
|
+
/* @__PURE__ */ jsxs3("text", { fg: t.fg.primary, marginTop: 1, children: [
|
|
3082
|
+
"Deployment: ",
|
|
3083
|
+
selectedDeployment.config.name
|
|
3084
|
+
] }),
|
|
3085
|
+
/* @__PURE__ */ jsx3("text", { fg: t.status.error, marginTop: 1, children: "This will permanently delete:" }),
|
|
3086
|
+
/* @__PURE__ */ jsx3("text", { fg: t.fg.secondary, children: " Local configuration files" }),
|
|
3087
|
+
/* @__PURE__ */ jsx3("text", { fg: t.fg.secondary, children: " SSH keys" })
|
|
3088
|
+
]
|
|
3089
|
+
}
|
|
3090
|
+
),
|
|
3091
|
+
/* @__PURE__ */ jsx3("text", { fg: t.status.warning, marginTop: 2, children: "Type the deployment name to confirm:" }),
|
|
3092
|
+
/* @__PURE__ */ jsx3("text", { fg: t.fg.primary, marginTop: 1, children: "Confirm:" }),
|
|
3093
|
+
/* @__PURE__ */ jsx3(
|
|
3094
|
+
"input",
|
|
3095
|
+
{
|
|
3096
|
+
value: confirmText,
|
|
3097
|
+
placeholder: selectedDeployment.config.name,
|
|
3098
|
+
focused: true,
|
|
3099
|
+
onInput: (value) => setConfirmText(value),
|
|
3100
|
+
onSubmit: (value) => {
|
|
3101
|
+
if (value === selectedDeployment.config.name) {
|
|
3102
|
+
handleDelete(selectedDeployment.config.name);
|
|
3103
|
+
} else {
|
|
3104
|
+
setError("Name does not match. Please type the exact deployment name.");
|
|
3105
|
+
}
|
|
3106
|
+
},
|
|
3107
|
+
onKeyDown: (e) => {
|
|
3108
|
+
if (e.name === "escape") {
|
|
3109
|
+
setViewState("detail");
|
|
3110
|
+
setConfirmText("");
|
|
3111
|
+
setError(null);
|
|
3112
|
+
}
|
|
3113
|
+
}
|
|
3114
|
+
}
|
|
3115
|
+
),
|
|
3116
|
+
error && /* @__PURE__ */ jsx3("text", { fg: t.status.error, marginTop: 1, children: error }),
|
|
3117
|
+
/* @__PURE__ */ jsx3("text", { fg: t.fg.muted, marginTop: 2, children: "Press Esc to cancel" })
|
|
3118
|
+
] });
|
|
3119
|
+
}
|
|
3120
|
+
if (viewState === "deleting") {
|
|
3121
|
+
return /* @__PURE__ */ jsxs3("box", { flexDirection: "column", width: "100%", padding: 1, children: [
|
|
3122
|
+
/* @__PURE__ */ jsx3("text", { fg: t.status.warning, children: "Deleting deployment..." }),
|
|
3123
|
+
/* @__PURE__ */ jsx3("text", { fg: t.fg.secondary, marginTop: 1, children: "Removing local configuration files..." })
|
|
3124
|
+
] });
|
|
3125
|
+
}
|
|
3126
|
+
if (viewState === "success") {
|
|
3127
|
+
return /* @__PURE__ */ jsxs3("box", { flexDirection: "column", width: "100%", padding: 1, children: [
|
|
3128
|
+
/* @__PURE__ */ jsxs3(
|
|
3129
|
+
"box",
|
|
3130
|
+
{
|
|
3131
|
+
flexDirection: "column",
|
|
3132
|
+
borderStyle: "single",
|
|
3133
|
+
borderColor: t.status.success,
|
|
3134
|
+
padding: 1,
|
|
3135
|
+
children: [
|
|
3136
|
+
/* @__PURE__ */ jsx3("text", { fg: t.status.success, children: "Deployment Deleted" }),
|
|
3137
|
+
/* @__PURE__ */ jsxs3("text", { fg: t.fg.primary, marginTop: 1, children: [
|
|
3138
|
+
'The deployment "',
|
|
3139
|
+
deletedName,
|
|
3140
|
+
'" has been permanently deleted.'
|
|
3141
|
+
] })
|
|
3142
|
+
]
|
|
3143
|
+
}
|
|
3144
|
+
),
|
|
3145
|
+
/* @__PURE__ */ jsx3("text", { fg: t.fg.muted, marginTop: 2, children: "Press any key to return to home" })
|
|
3146
|
+
] });
|
|
3147
|
+
}
|
|
3148
|
+
if (viewState === "error") {
|
|
3149
|
+
return /* @__PURE__ */ jsxs3("box", { flexDirection: "column", width: "100%", padding: 1, children: [
|
|
3150
|
+
/* @__PURE__ */ jsxs3(
|
|
3151
|
+
"box",
|
|
3152
|
+
{
|
|
3153
|
+
flexDirection: "column",
|
|
3154
|
+
borderStyle: "single",
|
|
3155
|
+
borderColor: t.status.error,
|
|
3156
|
+
padding: 1,
|
|
3157
|
+
children: [
|
|
3158
|
+
/* @__PURE__ */ jsx3("text", { fg: t.status.error, children: "Deletion Failed" }),
|
|
3159
|
+
/* @__PURE__ */ jsx3("text", { fg: t.fg.primary, marginTop: 1, children: error })
|
|
3160
|
+
]
|
|
3161
|
+
}
|
|
3162
|
+
),
|
|
3163
|
+
/* @__PURE__ */ jsx3("text", { fg: t.fg.muted, marginTop: 2, children: "Press any key to go back" })
|
|
3164
|
+
] });
|
|
3165
|
+
}
|
|
3166
|
+
return null;
|
|
3167
|
+
}
|
|
3168
|
+
|
|
3169
|
+
// src/components/DeployView.tsx
|
|
3170
|
+
import { useState as useState4 } from "react";
|
|
3171
|
+
import { useKeyboard as useKeyboard4 } from "@opentui/react";
|
|
3172
|
+
import { jsx as jsx4, jsxs as jsxs4 } from "@opentui/react/jsx-runtime";
|
|
3173
|
+
function DeployView({ context }) {
|
|
3174
|
+
const [selectedIndex, setSelectedIndex] = useState4(0);
|
|
3175
|
+
const [confirmMode, setConfirmMode] = useState4(false);
|
|
3176
|
+
const deployments = context.deployments;
|
|
3177
|
+
const notDeployed = deployments.filter((d) => d.state.status !== "deployed");
|
|
3178
|
+
const deployed = deployments.filter((d) => d.state.status === "deployed");
|
|
3179
|
+
const allDeployments = [...notDeployed, ...deployed];
|
|
3180
|
+
const selectedDeployment = allDeployments[selectedIndex];
|
|
3181
|
+
useKeyboard4((key) => {
|
|
3182
|
+
if (allDeployments.length === 0) {
|
|
3183
|
+
context.navigateTo("home");
|
|
3184
|
+
return;
|
|
3185
|
+
}
|
|
3186
|
+
if (confirmMode) {
|
|
3187
|
+
if (key.name === "y" || key.name === "return") {
|
|
3188
|
+
context.navigateTo("deploying", selectedDeployment.config.name);
|
|
3189
|
+
} else if (key.name === "n" || key.name === "escape") {
|
|
3190
|
+
setConfirmMode(false);
|
|
3191
|
+
}
|
|
3192
|
+
} else {
|
|
3193
|
+
if (key.name === "up" && selectedIndex > 0) {
|
|
3194
|
+
setSelectedIndex(selectedIndex - 1);
|
|
3195
|
+
} else if (key.name === "down" && selectedIndex < allDeployments.length - 1) {
|
|
3196
|
+
setSelectedIndex(selectedIndex + 1);
|
|
3197
|
+
} else if (key.name === "return") {
|
|
3198
|
+
setConfirmMode(true);
|
|
3199
|
+
} else if (key.name === "escape") {
|
|
3200
|
+
context.navigateTo("home");
|
|
3201
|
+
}
|
|
3202
|
+
}
|
|
3203
|
+
});
|
|
3204
|
+
if (allDeployments.length === 0) {
|
|
3205
|
+
return /* @__PURE__ */ jsxs4("box", { flexDirection: "column", width: "100%", padding: 1, children: [
|
|
3206
|
+
/* @__PURE__ */ jsxs4("box", { flexDirection: "row", marginBottom: 2, children: [
|
|
3207
|
+
/* @__PURE__ */ jsx4("text", { fg: t.accent, children: "/deploy" }),
|
|
3208
|
+
/* @__PURE__ */ jsx4("text", { fg: t.fg.secondary, children: " - Deploy a configuration" })
|
|
3209
|
+
] }),
|
|
3210
|
+
/* @__PURE__ */ jsxs4(
|
|
3211
|
+
"box",
|
|
3212
|
+
{
|
|
3213
|
+
flexDirection: "column",
|
|
3214
|
+
borderStyle: "single",
|
|
3215
|
+
borderColor: t.border.default,
|
|
3216
|
+
padding: 1,
|
|
3217
|
+
children: [
|
|
3218
|
+
/* @__PURE__ */ jsx4("text", { fg: t.status.warning, children: "No deployments found!" }),
|
|
3219
|
+
/* @__PURE__ */ jsx4("text", { fg: t.fg.secondary, marginTop: 1, children: "Run /new first to create a deployment configuration." })
|
|
3220
|
+
]
|
|
3221
|
+
}
|
|
3222
|
+
),
|
|
3223
|
+
/* @__PURE__ */ jsx4("text", { fg: t.fg.muted, marginTop: 2, children: "Press any key to return to home" })
|
|
3224
|
+
] });
|
|
3225
|
+
}
|
|
3226
|
+
if (confirmMode) {
|
|
3227
|
+
return /* @__PURE__ */ jsxs4("box", { flexDirection: "column", width: "100%", padding: 1, children: [
|
|
3228
|
+
/* @__PURE__ */ jsxs4("box", { flexDirection: "row", marginBottom: 2, children: [
|
|
3229
|
+
/* @__PURE__ */ jsx4("text", { fg: t.accent, children: "/deploy" }),
|
|
3230
|
+
/* @__PURE__ */ jsx4("text", { fg: t.fg.secondary, children: " - Confirm deployment" })
|
|
3231
|
+
] }),
|
|
3232
|
+
/* @__PURE__ */ jsxs4(
|
|
3233
|
+
"box",
|
|
3234
|
+
{
|
|
3235
|
+
flexDirection: "column",
|
|
3236
|
+
borderStyle: "single",
|
|
3237
|
+
borderColor: t.border.default,
|
|
3238
|
+
padding: 1,
|
|
3239
|
+
children: [
|
|
3240
|
+
/* @__PURE__ */ jsxs4("text", { fg: t.status.warning, children: [
|
|
3241
|
+
'Deploy "',
|
|
3242
|
+
selectedDeployment.config.name,
|
|
3243
|
+
'"?'
|
|
3244
|
+
] }),
|
|
3245
|
+
/* @__PURE__ */ jsx4("text", { fg: t.fg.secondary, marginTop: 1, children: "This will:" }),
|
|
3246
|
+
/* @__PURE__ */ jsxs4("text", { fg: t.fg.primary, children: [
|
|
3247
|
+
"\u2022 Create a VPS on ",
|
|
3248
|
+
selectedDeployment.config.provider
|
|
3249
|
+
] }),
|
|
3250
|
+
/* @__PURE__ */ jsx4("text", { fg: t.fg.primary, children: "\u2022 Install and configure OpenClaw" }),
|
|
3251
|
+
/* @__PURE__ */ jsx4("text", { fg: t.fg.primary, children: "\u2022 Set up Tailscale for secure access" }),
|
|
3252
|
+
/* @__PURE__ */ jsx4("text", { fg: t.fg.secondary, marginTop: 1, children: "Estimated cost: ~$4.99/month (Hetzner CPX11)" })
|
|
3253
|
+
]
|
|
3254
|
+
}
|
|
3255
|
+
),
|
|
3256
|
+
/* @__PURE__ */ jsx4("text", { fg: t.status.warning, marginTop: 2, children: "Press Y to confirm, N to cancel" })
|
|
3257
|
+
] });
|
|
3258
|
+
}
|
|
3259
|
+
return /* @__PURE__ */ jsxs4("box", { flexDirection: "column", width: "100%", padding: 1, children: [
|
|
3260
|
+
/* @__PURE__ */ jsxs4("box", { flexDirection: "row", marginBottom: 2, children: [
|
|
3261
|
+
/* @__PURE__ */ jsx4("text", { fg: t.accent, children: "/deploy" }),
|
|
3262
|
+
/* @__PURE__ */ jsx4("text", { fg: t.fg.secondary, children: " - Select a deployment to deploy" })
|
|
3263
|
+
] }),
|
|
3264
|
+
/* @__PURE__ */ jsx4("text", { fg: t.fg.secondary, marginBottom: 1, children: "Use arrow keys to select, Enter to deploy:" }),
|
|
3265
|
+
/* @__PURE__ */ jsx4(
|
|
3266
|
+
"box",
|
|
3267
|
+
{
|
|
3268
|
+
flexDirection: "column",
|
|
3269
|
+
borderStyle: "single",
|
|
3270
|
+
borderColor: t.border.default,
|
|
3271
|
+
padding: 1,
|
|
3272
|
+
children: allDeployments.map((deployment, index) => {
|
|
3273
|
+
const isSelected = index === selectedIndex;
|
|
3274
|
+
return /* @__PURE__ */ jsxs4(
|
|
3275
|
+
"box",
|
|
3276
|
+
{
|
|
3277
|
+
flexDirection: "row",
|
|
3278
|
+
backgroundColor: isSelected ? t.selection.bg : void 0,
|
|
3279
|
+
children: [
|
|
3280
|
+
/* @__PURE__ */ jsx4("text", { fg: isSelected ? t.selection.fg : t.fg.secondary, children: isSelected ? "> " : " " }),
|
|
3281
|
+
/* @__PURE__ */ jsx4("text", { fg: isSelected ? t.selection.fg : t.fg.secondary, width: 25, children: deployment.config.name }),
|
|
3282
|
+
/* @__PURE__ */ jsxs4("text", { fg: statusColor(deployment.state.status), width: 15, children: [
|
|
3283
|
+
"[",
|
|
3284
|
+
deployment.state.status,
|
|
3285
|
+
"]"
|
|
3286
|
+
] }),
|
|
3287
|
+
/* @__PURE__ */ jsx4("text", { fg: t.fg.secondary, children: deployment.config.provider })
|
|
3288
|
+
]
|
|
3289
|
+
},
|
|
3290
|
+
deployment.config.name
|
|
3291
|
+
);
|
|
3292
|
+
})
|
|
3293
|
+
}
|
|
3294
|
+
),
|
|
3295
|
+
/* @__PURE__ */ jsx4("text", { fg: t.fg.muted, marginTop: 2, children: "Press Esc to go back" })
|
|
3296
|
+
] });
|
|
3297
|
+
}
|
|
3298
|
+
|
|
3299
|
+
// src/components/DeployingView.tsx
|
|
3300
|
+
import { useState as useState5, useEffect as useEffect2, useCallback, useRef as useRef4 } from "react";
|
|
3301
|
+
import { useKeyboard as useKeyboard5 } from "@opentui/react";
|
|
3302
|
+
import open from "open";
|
|
3303
|
+
|
|
3304
|
+
// src/services/ssh.ts
|
|
3305
|
+
import { Client } from "ssh2";
|
|
3306
|
+
import { generateKeyPairSync, randomBytes } from "crypto";
|
|
3307
|
+
import { readFileSync as readFileSync4, writeFileSync as writeFileSync4, chmodSync, existsSync as existsSync4 } from "fs";
|
|
3308
|
+
import { mkdirSync as mkdirSync4 } from "fs";
|
|
3309
|
+
function generateSSHKeyPair(comment = "clawcontrol") {
|
|
3310
|
+
const { privateKey, publicKey } = generateKeyPairSync("ed25519", {
|
|
3311
|
+
publicKeyEncoding: {
|
|
3312
|
+
type: "spki",
|
|
3313
|
+
format: "pem"
|
|
3314
|
+
},
|
|
3315
|
+
privateKeyEncoding: {
|
|
3316
|
+
type: "pkcs8",
|
|
3317
|
+
format: "pem"
|
|
3318
|
+
}
|
|
3319
|
+
});
|
|
3320
|
+
const pubKeyDer = extractDERFromPEM(publicKey);
|
|
3321
|
+
const privKeyDer = extractDERFromPEM(privateKey);
|
|
3322
|
+
const publicKeyBytes = pubKeyDer.slice(-32);
|
|
3323
|
+
const privateKeyBytes = privKeyDer.slice(-32);
|
|
3324
|
+
const publicKeyOpenSSH = buildOpenSSHPublicKey(publicKeyBytes, comment);
|
|
3325
|
+
const privateKeyOpenSSH = buildOpenSSHPrivateKey(privateKeyBytes, publicKeyBytes, comment);
|
|
3326
|
+
return {
|
|
3327
|
+
privateKey: privateKeyOpenSSH,
|
|
3328
|
+
publicKey: publicKeyOpenSSH
|
|
3329
|
+
};
|
|
3330
|
+
}
|
|
3331
|
+
function extractDERFromPEM(pem) {
|
|
3332
|
+
const lines = pem.split("\n").filter((line) => !line.startsWith("-----") && line.trim() !== "");
|
|
3333
|
+
return Buffer.from(lines.join(""), "base64");
|
|
3334
|
+
}
|
|
3335
|
+
function buildOpenSSHPublicKey(publicKeyBytes, comment) {
|
|
3336
|
+
const keyType = Buffer.from("ssh-ed25519");
|
|
3337
|
+
const keyTypeLen = Buffer.alloc(4);
|
|
3338
|
+
keyTypeLen.writeUInt32BE(keyType.length);
|
|
3339
|
+
const keyLen = Buffer.alloc(4);
|
|
3340
|
+
keyLen.writeUInt32BE(publicKeyBytes.length);
|
|
3341
|
+
const opensshKey = Buffer.concat([keyTypeLen, keyType, keyLen, publicKeyBytes]);
|
|
3342
|
+
return `ssh-ed25519 ${opensshKey.toString("base64")} ${comment}`;
|
|
3343
|
+
}
|
|
3344
|
+
function buildOpenSSHPrivateKey(privateKeyBytes, publicKeyBytes, comment) {
|
|
3345
|
+
const AUTH_MAGIC = Buffer.from("openssh-key-v1\0");
|
|
3346
|
+
const cipherName = Buffer.from("none");
|
|
3347
|
+
const kdfName = Buffer.from("none");
|
|
2826
3348
|
const kdfOptions = Buffer.alloc(0);
|
|
2827
3349
|
const numKeys = 1;
|
|
2828
3350
|
const keyType = Buffer.from("ssh-ed25519");
|
|
@@ -3090,6 +3612,9 @@ async function waitForSSH(host, privateKey, timeoutMs = 18e4, pollIntervalMs = 5
|
|
|
3090
3612
|
throw new Error(`SSH not available after ${timeoutMs / 1e3} seconds`);
|
|
3091
3613
|
}
|
|
3092
3614
|
|
|
3615
|
+
// src/services/deployment.ts
|
|
3616
|
+
import { randomBytes as randomBytes2 } from "crypto";
|
|
3617
|
+
|
|
3093
3618
|
// src/services/setup/index.ts
|
|
3094
3619
|
async function execOrFail(ssh, command, errorMessage) {
|
|
3095
3620
|
const result = await ssh.exec(command);
|
|
@@ -3241,7 +3766,7 @@ async function installOpenClaw(ssh) {
|
|
|
3241
3766
|
throw new Error("OpenClaw installation verification failed");
|
|
3242
3767
|
}
|
|
3243
3768
|
}
|
|
3244
|
-
async function configureOpenClaw(ssh, customConfig, agentConfig) {
|
|
3769
|
+
async function configureOpenClaw(ssh, customConfig, agentConfig, gatewayToken) {
|
|
3245
3770
|
await ssh.exec("mkdir -p ~/.openclaw");
|
|
3246
3771
|
const config = {
|
|
3247
3772
|
browser: {
|
|
@@ -3263,6 +3788,7 @@ async function configureOpenClaw(ssh, customConfig, agentConfig) {
|
|
|
3263
3788
|
port: 18789,
|
|
3264
3789
|
mode: "local",
|
|
3265
3790
|
bind: "loopback",
|
|
3791
|
+
...gatewayToken ? { auth: { token: gatewayToken } } : {},
|
|
3266
3792
|
tailscale: {
|
|
3267
3793
|
mode: "serve",
|
|
3268
3794
|
resetOnExit: false
|
|
@@ -3510,6 +4036,41 @@ async function getOpenClawLogs(ssh, lines = 100) {
|
|
|
3510
4036
|
const result = await ssh.exec(`journalctl -u openclaw -n ${lines} --no-pager`);
|
|
3511
4037
|
return result.stdout;
|
|
3512
4038
|
}
|
|
4039
|
+
async function getDashboardUrl(ssh) {
|
|
4040
|
+
const nvmPrefix = "source ~/.nvm/nvm.sh &&";
|
|
4041
|
+
const dashResult = await ssh.exec(
|
|
4042
|
+
`${nvmPrefix} timeout 10 openclaw dashboard 2>&1 || true`
|
|
4043
|
+
);
|
|
4044
|
+
const rawOutput = dashResult.stdout + "\n" + dashResult.stderr;
|
|
4045
|
+
const output = rawOutput.replace(/\x1b\[[0-9;]*m/g, "");
|
|
4046
|
+
const urlMatch = output.match(/https?:\/\/[^\s\])'"<>]+/);
|
|
4047
|
+
if (urlMatch) {
|
|
4048
|
+
try {
|
|
4049
|
+
const parsed = new URL(urlMatch[0]);
|
|
4050
|
+
return { url: urlMatch[0], port: parseInt(parsed.port) || 18789 };
|
|
4051
|
+
} catch {
|
|
4052
|
+
}
|
|
4053
|
+
}
|
|
4054
|
+
const tokenResult = await ssh.exec(
|
|
4055
|
+
`${nvmPrefix} openclaw config get gateway.auth.token 2>/dev/null || true`
|
|
4056
|
+
);
|
|
4057
|
+
const token = tokenResult.stdout.trim().replace(/^["']|["']$/g, "");
|
|
4058
|
+
if (token) {
|
|
4059
|
+
return {
|
|
4060
|
+
url: `http://127.0.0.1:18789/?token=${encodeURIComponent(token)}`,
|
|
4061
|
+
port: 18789
|
|
4062
|
+
};
|
|
4063
|
+
}
|
|
4064
|
+
const statusResult = await ssh.exec(
|
|
4065
|
+
"systemctl is-active openclaw 2>/dev/null || true"
|
|
4066
|
+
);
|
|
4067
|
+
if (statusResult.stdout.trim() !== "active") {
|
|
4068
|
+
throw new Error("OpenClaw gateway is not running on this server");
|
|
4069
|
+
}
|
|
4070
|
+
throw new Error(
|
|
4071
|
+
"Could not retrieve dashboard URL. Try running 'openclaw dashboard' on the server manually."
|
|
4072
|
+
);
|
|
4073
|
+
}
|
|
3513
4074
|
|
|
3514
4075
|
// src/services/deployment.ts
|
|
3515
4076
|
var MAX_RETRIES = 3;
|
|
@@ -3608,6 +4169,7 @@ var DeploymentOrchestrator = class {
|
|
|
3608
4169
|
onConfirm;
|
|
3609
4170
|
onOpenUrl;
|
|
3610
4171
|
onSpawnTerminal;
|
|
4172
|
+
tailscaleSkipped = false;
|
|
3611
4173
|
constructor(deploymentName, onProgress, onConfirm, onOpenUrl, onSpawnTerminal) {
|
|
3612
4174
|
this.deploymentName = deploymentName;
|
|
3613
4175
|
this.deployment = readDeployment(deploymentName);
|
|
@@ -3991,18 +4553,28 @@ Would you like to retry from the beginning?`
|
|
|
3991
4553
|
const ssh = await this.ensureSSHConnected();
|
|
3992
4554
|
const customConfig = this.deployment.config.openclawConfig;
|
|
3993
4555
|
const agentConfig = this.deployment.config.openclawAgent;
|
|
3994
|
-
|
|
4556
|
+
const gatewayToken = randomBytes2(32).toString("hex");
|
|
4557
|
+
await configureOpenClaw(ssh, customConfig, agentConfig, gatewayToken);
|
|
4558
|
+
updateDeploymentState(this.deploymentName, { gatewayToken });
|
|
3995
4559
|
if (agentConfig) {
|
|
3996
4560
|
this.reportProgress("openclaw_configured", "Writing AI provider environment...");
|
|
3997
4561
|
await writeOpenClawEnvFile(ssh, agentConfig);
|
|
3998
4562
|
}
|
|
3999
4563
|
}
|
|
4000
4564
|
async installTailscale() {
|
|
4565
|
+
if (this.deployment.config.skipTailscale) {
|
|
4566
|
+
this.tailscaleSkipped = true;
|
|
4567
|
+
this.reportProgress("tailscale_installed", "Tailscale setup skipped.");
|
|
4568
|
+
return;
|
|
4569
|
+
}
|
|
4001
4570
|
const ssh = await this.ensureSSHConnected();
|
|
4002
4571
|
await installTailscale(ssh);
|
|
4003
4572
|
}
|
|
4004
4573
|
async authenticateTailscale() {
|
|
4574
|
+
if (this.tailscaleSkipped) return;
|
|
4005
4575
|
const ssh = await this.ensureSSHConnected();
|
|
4576
|
+
const check = await ssh.exec("which tailscale 2>/dev/null");
|
|
4577
|
+
if (check.code !== 0) return;
|
|
4006
4578
|
const authUrl = await getTailscaleAuthUrl(ssh);
|
|
4007
4579
|
if (authUrl) {
|
|
4008
4580
|
const confirmed = await this.onConfirm(
|
|
@@ -4025,7 +4597,10 @@ URL: ${authUrl}`
|
|
|
4025
4597
|
}
|
|
4026
4598
|
}
|
|
4027
4599
|
async configureTailscale() {
|
|
4600
|
+
if (this.tailscaleSkipped) return;
|
|
4028
4601
|
const ssh = await this.ensureSSHConnected();
|
|
4602
|
+
const check = await ssh.exec("which tailscale 2>/dev/null");
|
|
4603
|
+
if (check.code !== 0) return;
|
|
4029
4604
|
const tailscaleIp = await configureTailscaleServe(ssh);
|
|
4030
4605
|
updateDeploymentState(this.deploymentName, {
|
|
4031
4606
|
tailscaleIp
|
|
@@ -4184,6 +4759,12 @@ function detectTerminal() {
|
|
|
4184
4759
|
return { app: "konsole", canOpenTab: true };
|
|
4185
4760
|
}
|
|
4186
4761
|
}
|
|
4762
|
+
if (os === "win32") {
|
|
4763
|
+
if (env.WT_SESSION) {
|
|
4764
|
+
return { app: "windows-terminal", canOpenTab: true };
|
|
4765
|
+
}
|
|
4766
|
+
return { app: "powershell", canOpenTab: false };
|
|
4767
|
+
}
|
|
4187
4768
|
if (os === "darwin") {
|
|
4188
4769
|
return { app: "terminal.app", canOpenTab: true };
|
|
4189
4770
|
}
|
|
@@ -4218,11 +4799,19 @@ function openTerminalWithCommand(command) {
|
|
|
4218
4799
|
return openKonsole(command);
|
|
4219
4800
|
case "xfce4-terminal":
|
|
4220
4801
|
return openXfce4Terminal(command);
|
|
4802
|
+
case "windows-terminal":
|
|
4803
|
+
return openWindowsTerminal(command);
|
|
4804
|
+
case "powershell":
|
|
4805
|
+
return openPowerShell(command);
|
|
4806
|
+
case "cmd":
|
|
4807
|
+
return openCmd(command);
|
|
4221
4808
|
default:
|
|
4222
4809
|
if (os === "darwin") {
|
|
4223
4810
|
return openTerminalApp(command);
|
|
4224
4811
|
} else if (os === "linux") {
|
|
4225
4812
|
return openLinuxFallback(command);
|
|
4813
|
+
} else if (os === "win32") {
|
|
4814
|
+
return openWindowsFallback(command);
|
|
4226
4815
|
}
|
|
4227
4816
|
return { success: false, error: `Unsupported terminal or OS: ${terminal.app}` };
|
|
4228
4817
|
}
|
|
@@ -4418,6 +5007,9 @@ function openCursorTerminal(command) {
|
|
|
4418
5007
|
}
|
|
4419
5008
|
return openTerminalApp(command);
|
|
4420
5009
|
}
|
|
5010
|
+
if (platform() === "win32") {
|
|
5011
|
+
return openWindowsFallback(command);
|
|
5012
|
+
}
|
|
4421
5013
|
return openLinuxFallback(command);
|
|
4422
5014
|
}
|
|
4423
5015
|
function openVSCodeTerminal(command) {
|
|
@@ -4489,35 +5081,94 @@ function openLinuxFallback(command) {
|
|
|
4489
5081
|
}
|
|
4490
5082
|
return { success: false, error: "Could not find a supported terminal emulator" };
|
|
4491
5083
|
}
|
|
4492
|
-
function
|
|
4493
|
-
const
|
|
4494
|
-
|
|
4495
|
-
"
|
|
4496
|
-
|
|
4497
|
-
|
|
4498
|
-
"
|
|
4499
|
-
|
|
4500
|
-
|
|
4501
|
-
|
|
4502
|
-
|
|
5084
|
+
function createTempScriptWindows(command) {
|
|
5085
|
+
const scriptPath = join5(tmpdir(), `clawcontrol-${process.pid}-${Date.now()}.cmd`);
|
|
5086
|
+
const content = [
|
|
5087
|
+
"@echo off",
|
|
5088
|
+
command,
|
|
5089
|
+
`del "${scriptPath}"`,
|
|
5090
|
+
""
|
|
5091
|
+
].join("\r\n");
|
|
5092
|
+
writeFileSync5(scriptPath, content);
|
|
5093
|
+
return scriptPath;
|
|
5094
|
+
}
|
|
5095
|
+
function openWindowsTerminal(command) {
|
|
5096
|
+
const script = createTempScriptWindows(command);
|
|
5097
|
+
const proc = spawn("wt.exe", ["new-tab", "cmd", "/c", script], {
|
|
5098
|
+
stdio: "ignore",
|
|
5099
|
+
detached: true,
|
|
5100
|
+
shell: true
|
|
5101
|
+
});
|
|
5102
|
+
proc.unref();
|
|
5103
|
+
return { success: true };
|
|
5104
|
+
}
|
|
5105
|
+
function openPowerShell(command) {
|
|
5106
|
+
const proc = spawn("powershell.exe", [
|
|
5107
|
+
"-NoProfile",
|
|
5108
|
+
"Start-Process",
|
|
5109
|
+
"powershell",
|
|
5110
|
+
"-ArgumentList",
|
|
5111
|
+
`'-NoExit -Command "${command.replace(/"/g, '`"')}"'`
|
|
5112
|
+
], {
|
|
5113
|
+
stdio: "ignore",
|
|
5114
|
+
detached: true,
|
|
5115
|
+
shell: true
|
|
5116
|
+
});
|
|
5117
|
+
proc.unref();
|
|
5118
|
+
return { success: true };
|
|
5119
|
+
}
|
|
5120
|
+
function openCmd(command) {
|
|
5121
|
+
const script = createTempScriptWindows(command);
|
|
5122
|
+
const proc = spawn("cmd.exe", ["/c", "start", "cmd", "/k", script], {
|
|
5123
|
+
stdio: "ignore",
|
|
5124
|
+
detached: true,
|
|
5125
|
+
shell: true
|
|
5126
|
+
});
|
|
5127
|
+
proc.unref();
|
|
5128
|
+
return { success: true };
|
|
5129
|
+
}
|
|
5130
|
+
function openWindowsFallback(command) {
|
|
5131
|
+
try {
|
|
5132
|
+
const result = spawnSync("where", ["wt.exe"], { stdio: "pipe", timeout: 2e3 });
|
|
5133
|
+
if (result.status === 0) {
|
|
5134
|
+
return openWindowsTerminal(command);
|
|
5135
|
+
}
|
|
5136
|
+
} catch {
|
|
5137
|
+
}
|
|
5138
|
+
return openPowerShell(command);
|
|
5139
|
+
}
|
|
5140
|
+
function getTerminalDisplayName(app) {
|
|
5141
|
+
const names = {
|
|
5142
|
+
"terminal.app": "Terminal.app",
|
|
5143
|
+
"iterm2": "iTerm2",
|
|
5144
|
+
"ghostty": "Ghostty",
|
|
5145
|
+
"kitty": "Kitty",
|
|
5146
|
+
"alacritty": "Alacritty",
|
|
5147
|
+
"wezterm": "WezTerm",
|
|
5148
|
+
"hyper": "Hyper",
|
|
5149
|
+
"vscode": "VS Code",
|
|
5150
|
+
"cursor": "Cursor",
|
|
4503
5151
|
"gnome-terminal": "GNOME Terminal",
|
|
4504
5152
|
"konsole": "Konsole",
|
|
4505
5153
|
"xfce4-terminal": "XFCE Terminal",
|
|
4506
5154
|
"xterm": "XTerm",
|
|
5155
|
+
"windows-terminal": "Windows Terminal",
|
|
5156
|
+
"powershell": "PowerShell",
|
|
5157
|
+
"cmd": "Command Prompt",
|
|
4507
5158
|
"unknown": "System Terminal"
|
|
4508
5159
|
};
|
|
4509
5160
|
return names[app];
|
|
4510
5161
|
}
|
|
4511
5162
|
|
|
4512
5163
|
// src/components/DeployingView.tsx
|
|
4513
|
-
import { jsx as
|
|
5164
|
+
import { jsx as jsx5, jsxs as jsxs5 } from "@opentui/react/jsx-runtime";
|
|
4514
5165
|
function DeployingView({ context }) {
|
|
4515
|
-
const [deployState, setDeployState] =
|
|
4516
|
-
const [progress, setProgress] =
|
|
4517
|
-
const [logs, setLogs] =
|
|
4518
|
-
const [error, setError] =
|
|
4519
|
-
const [confirmPrompt, setConfirmPrompt] =
|
|
4520
|
-
const [terminalResolve, setTerminalResolve] =
|
|
5166
|
+
const [deployState, setDeployState] = useState5("deploying");
|
|
5167
|
+
const [progress, setProgress] = useState5(null);
|
|
5168
|
+
const [logs, setLogs] = useState5([]);
|
|
5169
|
+
const [error, setError] = useState5(null);
|
|
5170
|
+
const [confirmPrompt, setConfirmPrompt] = useState5(null);
|
|
5171
|
+
const [terminalResolve, setTerminalResolve] = useState5(null);
|
|
4521
5172
|
const deploymentName = context.selectedDeployment;
|
|
4522
5173
|
const addLog = useCallback((message) => {
|
|
4523
5174
|
setLogs((prev) => [...prev.slice(-20), `[${(/* @__PURE__ */ new Date()).toLocaleTimeString()}] ${message}`]);
|
|
@@ -4565,9 +5216,9 @@ function DeployingView({ context }) {
|
|
|
4565
5216
|
setDeployState("deploying");
|
|
4566
5217
|
addLog("Terminal session confirmed complete, continuing deployment...");
|
|
4567
5218
|
}, [terminalResolve, addLog]);
|
|
4568
|
-
const stateRef =
|
|
5219
|
+
const stateRef = useRef4({ deployState, terminalResolve });
|
|
4569
5220
|
stateRef.current = { deployState, terminalResolve };
|
|
4570
|
-
|
|
5221
|
+
useKeyboard5((key) => {
|
|
4571
5222
|
const currentState = stateRef.current;
|
|
4572
5223
|
if (currentState.deployState === "waiting_terminal") {
|
|
4573
5224
|
if (key.name === "return") {
|
|
@@ -4621,19 +5272,19 @@ function DeployingView({ context }) {
|
|
|
4621
5272
|
const width = 40;
|
|
4622
5273
|
const filled = Math.round(progress.progress / 100 * width);
|
|
4623
5274
|
const empty = width - filled;
|
|
4624
|
-
return /* @__PURE__ */
|
|
4625
|
-
/* @__PURE__ */
|
|
4626
|
-
/* @__PURE__ */
|
|
4627
|
-
/* @__PURE__ */
|
|
4628
|
-
/* @__PURE__ */
|
|
4629
|
-
/* @__PURE__ */
|
|
4630
|
-
/* @__PURE__ */
|
|
5275
|
+
return /* @__PURE__ */ jsxs5("box", { flexDirection: "column", marginBottom: 1, children: [
|
|
5276
|
+
/* @__PURE__ */ jsxs5("box", { flexDirection: "row", children: [
|
|
5277
|
+
/* @__PURE__ */ jsx5("text", { fg: t.fg.secondary, children: "[" }),
|
|
5278
|
+
/* @__PURE__ */ jsx5("text", { fg: t.status.success, children: "\u2588".repeat(filled) }),
|
|
5279
|
+
/* @__PURE__ */ jsx5("text", { fg: t.fg.muted, children: "\u2591".repeat(empty) }),
|
|
5280
|
+
/* @__PURE__ */ jsx5("text", { fg: t.fg.secondary, children: "]" }),
|
|
5281
|
+
/* @__PURE__ */ jsxs5("text", { fg: t.fg.primary, children: [
|
|
4631
5282
|
" ",
|
|
4632
5283
|
Math.round(progress.progress),
|
|
4633
5284
|
"%"
|
|
4634
5285
|
] })
|
|
4635
5286
|
] }),
|
|
4636
|
-
/* @__PURE__ */
|
|
5287
|
+
/* @__PURE__ */ jsxs5("text", { fg: t.accent, marginTop: 1, children: [
|
|
4637
5288
|
"Current: ",
|
|
4638
5289
|
progress.message
|
|
4639
5290
|
] })
|
|
@@ -4642,7 +5293,7 @@ function DeployingView({ context }) {
|
|
|
4642
5293
|
const renderConfirmDialog = () => {
|
|
4643
5294
|
if (!confirmPrompt) return null;
|
|
4644
5295
|
const lines = confirmPrompt.message.split("\n");
|
|
4645
|
-
return /* @__PURE__ */
|
|
5296
|
+
return /* @__PURE__ */ jsxs5(
|
|
4646
5297
|
"box",
|
|
4647
5298
|
{
|
|
4648
5299
|
flexDirection: "column",
|
|
@@ -4651,16 +5302,16 @@ function DeployingView({ context }) {
|
|
|
4651
5302
|
padding: 1,
|
|
4652
5303
|
marginBottom: 1,
|
|
4653
5304
|
children: [
|
|
4654
|
-
/* @__PURE__ */
|
|
4655
|
-
/* @__PURE__ */
|
|
4656
|
-
/* @__PURE__ */
|
|
5305
|
+
/* @__PURE__ */ jsx5("text", { fg: t.status.warning, children: "Confirmation Required" }),
|
|
5306
|
+
/* @__PURE__ */ jsx5("box", { flexDirection: "column", marginTop: 1, children: lines.map((line, i) => /* @__PURE__ */ jsx5("text", { fg: t.fg.primary, children: line }, i)) }),
|
|
5307
|
+
/* @__PURE__ */ jsx5("text", { fg: t.status.warning, marginTop: 1, children: "Press Y for Yes, N for No" })
|
|
4657
5308
|
]
|
|
4658
5309
|
}
|
|
4659
5310
|
);
|
|
4660
5311
|
};
|
|
4661
5312
|
const renderWaitingTerminal = () => {
|
|
4662
|
-
return /* @__PURE__ */
|
|
4663
|
-
/* @__PURE__ */
|
|
5313
|
+
return /* @__PURE__ */ jsxs5("box", { flexDirection: "column", flexGrow: 1, children: [
|
|
5314
|
+
/* @__PURE__ */ jsxs5(
|
|
4664
5315
|
"box",
|
|
4665
5316
|
{
|
|
4666
5317
|
flexDirection: "column",
|
|
@@ -4669,12 +5320,12 @@ function DeployingView({ context }) {
|
|
|
4669
5320
|
padding: 1,
|
|
4670
5321
|
marginBottom: 1,
|
|
4671
5322
|
children: [
|
|
4672
|
-
/* @__PURE__ */
|
|
4673
|
-
/* @__PURE__ */
|
|
5323
|
+
/* @__PURE__ */ jsx5("text", { fg: t.accent, children: "Interactive Setup" }),
|
|
5324
|
+
/* @__PURE__ */ jsx5("text", { fg: t.fg.primary, marginTop: 1, children: "A terminal window has been opened." })
|
|
4674
5325
|
]
|
|
4675
5326
|
}
|
|
4676
5327
|
),
|
|
4677
|
-
/* @__PURE__ */
|
|
5328
|
+
/* @__PURE__ */ jsxs5(
|
|
4678
5329
|
"box",
|
|
4679
5330
|
{
|
|
4680
5331
|
flexDirection: "column",
|
|
@@ -4683,32 +5334,32 @@ function DeployingView({ context }) {
|
|
|
4683
5334
|
padding: 1,
|
|
4684
5335
|
marginBottom: 1,
|
|
4685
5336
|
children: [
|
|
4686
|
-
/* @__PURE__ */
|
|
4687
|
-
/* @__PURE__ */
|
|
4688
|
-
/* @__PURE__ */
|
|
4689
|
-
/* @__PURE__ */
|
|
4690
|
-
/* @__PURE__ */
|
|
4691
|
-
/* @__PURE__ */
|
|
5337
|
+
/* @__PURE__ */ jsx5("text", { fg: t.status.warning, children: "Instructions:" }),
|
|
5338
|
+
/* @__PURE__ */ jsxs5("box", { flexDirection: "column", marginTop: 1, children: [
|
|
5339
|
+
/* @__PURE__ */ jsx5("text", { fg: t.fg.primary, children: "1. Complete the setup in the terminal window" }),
|
|
5340
|
+
/* @__PURE__ */ jsx5("text", { fg: t.fg.primary, children: "2. Follow the prompts shown in the terminal" }),
|
|
5341
|
+
/* @__PURE__ */ jsx5("text", { fg: t.fg.primary, children: "3. When done, close the terminal window" }),
|
|
5342
|
+
/* @__PURE__ */ jsx5("text", { fg: t.fg.primary, children: "4. Press Enter here to continue" })
|
|
4692
5343
|
] })
|
|
4693
5344
|
]
|
|
4694
5345
|
}
|
|
4695
5346
|
),
|
|
4696
|
-
/* @__PURE__ */
|
|
5347
|
+
/* @__PURE__ */ jsx5(
|
|
4697
5348
|
"box",
|
|
4698
5349
|
{
|
|
4699
5350
|
flexDirection: "column",
|
|
4700
5351
|
borderStyle: "single",
|
|
4701
5352
|
borderColor: t.status.success,
|
|
4702
5353
|
padding: 1,
|
|
4703
|
-
children: /* @__PURE__ */
|
|
5354
|
+
children: /* @__PURE__ */ jsx5("text", { fg: t.status.success, children: "Press Enter when you have completed the setup in the terminal" })
|
|
4704
5355
|
}
|
|
4705
5356
|
)
|
|
4706
5357
|
] });
|
|
4707
5358
|
};
|
|
4708
5359
|
const renderSuccess = () => {
|
|
4709
5360
|
const state = readDeploymentState(deploymentName);
|
|
4710
|
-
return /* @__PURE__ */
|
|
4711
|
-
/* @__PURE__ */
|
|
5361
|
+
return /* @__PURE__ */ jsxs5("box", { flexDirection: "column", children: [
|
|
5362
|
+
/* @__PURE__ */ jsxs5(
|
|
4712
5363
|
"box",
|
|
4713
5364
|
{
|
|
4714
5365
|
flexDirection: "column",
|
|
@@ -4717,12 +5368,12 @@ function DeployingView({ context }) {
|
|
|
4717
5368
|
padding: 1,
|
|
4718
5369
|
marginBottom: 1,
|
|
4719
5370
|
children: [
|
|
4720
|
-
/* @__PURE__ */
|
|
4721
|
-
/* @__PURE__ */
|
|
5371
|
+
/* @__PURE__ */ jsx5("text", { fg: t.status.success, children: "Deployment Successful!" }),
|
|
5372
|
+
/* @__PURE__ */ jsx5("text", { fg: t.status.success, marginTop: 1, children: "Your OpenClaw instance is now running." })
|
|
4722
5373
|
]
|
|
4723
5374
|
}
|
|
4724
5375
|
),
|
|
4725
|
-
/* @__PURE__ */
|
|
5376
|
+
/* @__PURE__ */ jsxs5(
|
|
4726
5377
|
"box",
|
|
4727
5378
|
{
|
|
4728
5379
|
flexDirection: "column",
|
|
@@ -4731,36 +5382,36 @@ function DeployingView({ context }) {
|
|
|
4731
5382
|
padding: 1,
|
|
4732
5383
|
marginBottom: 1,
|
|
4733
5384
|
children: [
|
|
4734
|
-
/* @__PURE__ */
|
|
4735
|
-
/* @__PURE__ */
|
|
4736
|
-
/* @__PURE__ */
|
|
4737
|
-
/* @__PURE__ */
|
|
5385
|
+
/* @__PURE__ */ jsx5("text", { fg: t.fg.primary, children: "Connection Details" }),
|
|
5386
|
+
/* @__PURE__ */ jsxs5("box", { flexDirection: "row", marginTop: 1, children: [
|
|
5387
|
+
/* @__PURE__ */ jsx5("text", { fg: t.fg.secondary, width: 15, children: "Server IP:" }),
|
|
5388
|
+
/* @__PURE__ */ jsx5("text", { fg: t.accent, children: state.serverIp || "N/A" })
|
|
4738
5389
|
] }),
|
|
4739
|
-
/* @__PURE__ */
|
|
4740
|
-
/* @__PURE__ */
|
|
4741
|
-
/* @__PURE__ */
|
|
5390
|
+
/* @__PURE__ */ jsxs5("box", { flexDirection: "row", children: [
|
|
5391
|
+
/* @__PURE__ */ jsx5("text", { fg: t.fg.secondary, width: 15, children: "Tailscale IP:" }),
|
|
5392
|
+
/* @__PURE__ */ jsx5("text", { fg: t.accent, children: state.tailscaleIp || "N/A" })
|
|
4742
5393
|
] }),
|
|
4743
|
-
/* @__PURE__ */
|
|
4744
|
-
/* @__PURE__ */
|
|
4745
|
-
/* @__PURE__ */
|
|
5394
|
+
/* @__PURE__ */ jsxs5("box", { flexDirection: "row", children: [
|
|
5395
|
+
/* @__PURE__ */ jsx5("text", { fg: t.fg.secondary, width: 15, children: "Gateway Port:" }),
|
|
5396
|
+
/* @__PURE__ */ jsx5("text", { fg: t.accent, children: "18789" })
|
|
4746
5397
|
] })
|
|
4747
5398
|
]
|
|
4748
5399
|
}
|
|
4749
5400
|
),
|
|
4750
|
-
/* @__PURE__ */
|
|
4751
|
-
/* @__PURE__ */
|
|
4752
|
-
/* @__PURE__ */
|
|
4753
|
-
/* @__PURE__ */
|
|
5401
|
+
/* @__PURE__ */ jsx5("text", { fg: t.status.success, children: "Next steps:" }),
|
|
5402
|
+
/* @__PURE__ */ jsx5("text", { fg: t.fg.primary, children: " /ssh - Connect to your server" }),
|
|
5403
|
+
/* @__PURE__ */ jsx5("text", { fg: t.fg.primary, children: " /logs - View OpenClaw logs" }),
|
|
5404
|
+
/* @__PURE__ */ jsxs5("text", { fg: t.fg.primary, children: [
|
|
4754
5405
|
" Gateway: http://",
|
|
4755
5406
|
state.tailscaleIp || state.serverIp,
|
|
4756
5407
|
":18789/"
|
|
4757
5408
|
] }),
|
|
4758
|
-
/* @__PURE__ */
|
|
5409
|
+
/* @__PURE__ */ jsx5("text", { fg: t.fg.muted, marginTop: 2, children: "Press any key to return to home" })
|
|
4759
5410
|
] });
|
|
4760
5411
|
};
|
|
4761
5412
|
const renderFailed = () => {
|
|
4762
|
-
return /* @__PURE__ */
|
|
4763
|
-
/* @__PURE__ */
|
|
5413
|
+
return /* @__PURE__ */ jsxs5("box", { flexDirection: "column", children: [
|
|
5414
|
+
/* @__PURE__ */ jsxs5(
|
|
4764
5415
|
"box",
|
|
4765
5416
|
{
|
|
4766
5417
|
flexDirection: "column",
|
|
@@ -4769,26 +5420,26 @@ function DeployingView({ context }) {
|
|
|
4769
5420
|
padding: 1,
|
|
4770
5421
|
marginBottom: 1,
|
|
4771
5422
|
children: [
|
|
4772
|
-
/* @__PURE__ */
|
|
4773
|
-
/* @__PURE__ */
|
|
4774
|
-
/* @__PURE__ */
|
|
5423
|
+
/* @__PURE__ */ jsx5("text", { fg: t.status.error, children: "Deployment Failed" }),
|
|
5424
|
+
/* @__PURE__ */ jsx5("text", { fg: t.fg.primary, marginTop: 1, children: "Something went wrong during deployment." }),
|
|
5425
|
+
/* @__PURE__ */ jsxs5("text", { fg: t.status.error, marginTop: 1, children: [
|
|
4775
5426
|
"Error: ",
|
|
4776
5427
|
error
|
|
4777
5428
|
] })
|
|
4778
5429
|
]
|
|
4779
5430
|
}
|
|
4780
5431
|
),
|
|
4781
|
-
/* @__PURE__ */
|
|
4782
|
-
/* @__PURE__ */
|
|
4783
|
-
/* @__PURE__ */
|
|
4784
|
-
/* @__PURE__ */
|
|
4785
|
-
/* @__PURE__ */
|
|
5432
|
+
/* @__PURE__ */ jsxs5("box", { flexDirection: "column", marginBottom: 1, children: [
|
|
5433
|
+
/* @__PURE__ */ jsx5("text", { fg: t.fg.primary, children: "What you can do:" }),
|
|
5434
|
+
/* @__PURE__ */ jsx5("text", { fg: t.fg.secondary, children: " 1. Run /deploy again - it will resume from the last successful step" }),
|
|
5435
|
+
/* @__PURE__ */ jsx5("text", { fg: t.fg.secondary, children: " 2. Run /status to check the current state of your deployment" }),
|
|
5436
|
+
/* @__PURE__ */ jsx5("text", { fg: t.fg.secondary, children: " 3. Run /destroy and /new to start fresh if the issue persists" })
|
|
4786
5437
|
] }),
|
|
4787
|
-
/* @__PURE__ */
|
|
5438
|
+
/* @__PURE__ */ jsx5("text", { fg: t.fg.muted, marginTop: 1, children: "Press any key to return to home" })
|
|
4788
5439
|
] });
|
|
4789
5440
|
};
|
|
4790
|
-
return /* @__PURE__ */
|
|
4791
|
-
/* @__PURE__ */
|
|
5441
|
+
return /* @__PURE__ */ jsxs5("box", { flexDirection: "column", width: "100%", padding: 1, children: [
|
|
5442
|
+
/* @__PURE__ */ jsx5("box", { flexDirection: "row", marginBottom: 2, children: /* @__PURE__ */ jsxs5("text", { fg: t.accent, children: [
|
|
4792
5443
|
"Deploying: ",
|
|
4793
5444
|
deploymentName
|
|
4794
5445
|
] }) }),
|
|
@@ -4797,7 +5448,7 @@ function DeployingView({ context }) {
|
|
|
4797
5448
|
deployState === "waiting_terminal" && renderWaitingTerminal(),
|
|
4798
5449
|
deployState === "success" && renderSuccess(),
|
|
4799
5450
|
deployState === "failed" && renderFailed(),
|
|
4800
|
-
/* @__PURE__ */
|
|
5451
|
+
/* @__PURE__ */ jsxs5(
|
|
4801
5452
|
"box",
|
|
4802
5453
|
{
|
|
4803
5454
|
flexDirection: "column",
|
|
@@ -4805,8 +5456,8 @@ function DeployingView({ context }) {
|
|
|
4805
5456
|
borderColor: t.border.default,
|
|
4806
5457
|
padding: 1,
|
|
4807
5458
|
children: [
|
|
4808
|
-
/* @__PURE__ */
|
|
4809
|
-
/* @__PURE__ */
|
|
5459
|
+
/* @__PURE__ */ jsx5("text", { fg: t.fg.secondary, children: "Deployment Log" }),
|
|
5460
|
+
/* @__PURE__ */ jsx5("box", { flexDirection: "column", marginTop: 1, children: logs.map((log, i) => /* @__PURE__ */ jsx5("text", { fg: t.fg.muted, children: log }, i)) })
|
|
4810
5461
|
]
|
|
4811
5462
|
}
|
|
4812
5463
|
)
|
|
@@ -4814,13 +5465,13 @@ function DeployingView({ context }) {
|
|
|
4814
5465
|
}
|
|
4815
5466
|
|
|
4816
5467
|
// src/components/StatusView.tsx
|
|
4817
|
-
import { useState as
|
|
4818
|
-
import { useKeyboard as
|
|
4819
|
-
import { jsx as
|
|
5468
|
+
import { useState as useState6 } from "react";
|
|
5469
|
+
import { useKeyboard as useKeyboard6 } from "@opentui/react";
|
|
5470
|
+
import { jsx as jsx6, jsxs as jsxs6 } from "@opentui/react/jsx-runtime";
|
|
4820
5471
|
function StatusView({ context }) {
|
|
4821
|
-
const [selectedIndex, setSelectedIndex] =
|
|
4822
|
-
const [healthStatus, setHealthStatus] =
|
|
4823
|
-
const [checking, setChecking] =
|
|
5472
|
+
const [selectedIndex, setSelectedIndex] = useState6(0);
|
|
5473
|
+
const [healthStatus, setHealthStatus] = useState6(/* @__PURE__ */ new Map());
|
|
5474
|
+
const [checking, setChecking] = useState6(null);
|
|
4824
5475
|
const deployments = context.deployments;
|
|
4825
5476
|
const checkHealth = async (deployment) => {
|
|
4826
5477
|
const name = deployment.config.name;
|
|
@@ -4845,7 +5496,7 @@ function StatusView({ context }) {
|
|
|
4845
5496
|
setHealthStatus((prev) => new Map(prev).set(name, health));
|
|
4846
5497
|
setChecking(null);
|
|
4847
5498
|
};
|
|
4848
|
-
|
|
5499
|
+
useKeyboard6((key) => {
|
|
4849
5500
|
if (deployments.length === 0) {
|
|
4850
5501
|
context.navigateTo("home");
|
|
4851
5502
|
return;
|
|
@@ -4861,12 +5512,12 @@ function StatusView({ context }) {
|
|
|
4861
5512
|
}
|
|
4862
5513
|
});
|
|
4863
5514
|
if (deployments.length === 0) {
|
|
4864
|
-
return /* @__PURE__ */
|
|
4865
|
-
/* @__PURE__ */
|
|
4866
|
-
/* @__PURE__ */
|
|
4867
|
-
/* @__PURE__ */
|
|
5515
|
+
return /* @__PURE__ */ jsxs6("box", { flexDirection: "column", width: "100%", padding: 1, children: [
|
|
5516
|
+
/* @__PURE__ */ jsxs6("box", { flexDirection: "row", marginBottom: 2, children: [
|
|
5517
|
+
/* @__PURE__ */ jsx6("text", { fg: t.accent, children: "/status" }),
|
|
5518
|
+
/* @__PURE__ */ jsx6("text", { fg: t.fg.secondary, children: " - Deployment Status" })
|
|
4868
5519
|
] }),
|
|
4869
|
-
/* @__PURE__ */
|
|
5520
|
+
/* @__PURE__ */ jsxs6(
|
|
4870
5521
|
"box",
|
|
4871
5522
|
{
|
|
4872
5523
|
flexDirection: "column",
|
|
@@ -4874,22 +5525,22 @@ function StatusView({ context }) {
|
|
|
4874
5525
|
borderColor: t.border.default,
|
|
4875
5526
|
padding: 1,
|
|
4876
5527
|
children: [
|
|
4877
|
-
/* @__PURE__ */
|
|
4878
|
-
/* @__PURE__ */
|
|
5528
|
+
/* @__PURE__ */ jsx6("text", { fg: t.status.warning, children: "No deployments found!" }),
|
|
5529
|
+
/* @__PURE__ */ jsx6("text", { fg: t.fg.secondary, marginTop: 1, children: "Run /new to create a deployment." })
|
|
4879
5530
|
]
|
|
4880
5531
|
}
|
|
4881
5532
|
),
|
|
4882
|
-
/* @__PURE__ */
|
|
5533
|
+
/* @__PURE__ */ jsx6("text", { fg: t.fg.muted, marginTop: 2, children: "Press any key to return to home" })
|
|
4883
5534
|
] });
|
|
4884
5535
|
}
|
|
4885
5536
|
const selectedDeployment = deployments[selectedIndex];
|
|
4886
5537
|
const selectedHealth = healthStatus.get(selectedDeployment.config.name);
|
|
4887
|
-
return /* @__PURE__ */
|
|
4888
|
-
/* @__PURE__ */
|
|
4889
|
-
/* @__PURE__ */
|
|
4890
|
-
/* @__PURE__ */
|
|
5538
|
+
return /* @__PURE__ */ jsxs6("box", { flexDirection: "column", width: "100%", padding: 1, children: [
|
|
5539
|
+
/* @__PURE__ */ jsxs6("box", { flexDirection: "row", marginBottom: 2, children: [
|
|
5540
|
+
/* @__PURE__ */ jsx6("text", { fg: t.accent, children: "/status" }),
|
|
5541
|
+
/* @__PURE__ */ jsx6("text", { fg: t.fg.secondary, children: " - Deployment Status" })
|
|
4891
5542
|
] }),
|
|
4892
|
-
/* @__PURE__ */
|
|
5543
|
+
/* @__PURE__ */ jsxs6(
|
|
4893
5544
|
"box",
|
|
4894
5545
|
{
|
|
4895
5546
|
flexDirection: "column",
|
|
@@ -4898,22 +5549,22 @@ function StatusView({ context }) {
|
|
|
4898
5549
|
padding: 1,
|
|
4899
5550
|
marginBottom: 1,
|
|
4900
5551
|
children: [
|
|
4901
|
-
/* @__PURE__ */
|
|
5552
|
+
/* @__PURE__ */ jsxs6("text", { fg: t.fg.primary, marginBottom: 1, children: [
|
|
4902
5553
|
"Deployments (",
|
|
4903
5554
|
deployments.length,
|
|
4904
5555
|
")"
|
|
4905
5556
|
] }),
|
|
4906
5557
|
deployments.map((deployment, index) => {
|
|
4907
5558
|
const isSelected = index === selectedIndex;
|
|
4908
|
-
return /* @__PURE__ */
|
|
5559
|
+
return /* @__PURE__ */ jsxs6(
|
|
4909
5560
|
"box",
|
|
4910
5561
|
{
|
|
4911
5562
|
flexDirection: "row",
|
|
4912
5563
|
backgroundColor: isSelected ? t.selection.bg : void 0,
|
|
4913
5564
|
children: [
|
|
4914
|
-
/* @__PURE__ */
|
|
4915
|
-
/* @__PURE__ */
|
|
4916
|
-
/* @__PURE__ */
|
|
5565
|
+
/* @__PURE__ */ jsx6("text", { fg: isSelected ? t.selection.fg : t.fg.secondary, children: isSelected ? "> " : " " }),
|
|
5566
|
+
/* @__PURE__ */ jsx6("text", { fg: isSelected ? t.selection.fg : t.fg.secondary, width: 25, children: deployment.config.name }),
|
|
5567
|
+
/* @__PURE__ */ jsxs6("text", { fg: statusColor(deployment.state.status), children: [
|
|
4917
5568
|
"[",
|
|
4918
5569
|
deployment.state.status,
|
|
4919
5570
|
"]"
|
|
@@ -4926,7 +5577,7 @@ function StatusView({ context }) {
|
|
|
4926
5577
|
]
|
|
4927
5578
|
}
|
|
4928
5579
|
),
|
|
4929
|
-
/* @__PURE__ */
|
|
5580
|
+
/* @__PURE__ */ jsxs6(
|
|
4930
5581
|
"box",
|
|
4931
5582
|
{
|
|
4932
5583
|
flexDirection: "column",
|
|
@@ -4935,78 +5586,78 @@ function StatusView({ context }) {
|
|
|
4935
5586
|
padding: 1,
|
|
4936
5587
|
marginBottom: 1,
|
|
4937
5588
|
children: [
|
|
4938
|
-
/* @__PURE__ */
|
|
5589
|
+
/* @__PURE__ */ jsxs6("text", { fg: t.accent, children: [
|
|
4939
5590
|
"Details: ",
|
|
4940
5591
|
selectedDeployment.config.name
|
|
4941
5592
|
] }),
|
|
4942
|
-
/* @__PURE__ */
|
|
4943
|
-
/* @__PURE__ */
|
|
4944
|
-
/* @__PURE__ */
|
|
5593
|
+
/* @__PURE__ */ jsxs6("box", { flexDirection: "row", marginTop: 1, children: [
|
|
5594
|
+
/* @__PURE__ */ jsx6("text", { fg: t.fg.secondary, width: 18, children: "Status:" }),
|
|
5595
|
+
/* @__PURE__ */ jsx6("text", { fg: statusColor(selectedDeployment.state.status), children: selectedDeployment.state.status })
|
|
4945
5596
|
] }),
|
|
4946
|
-
/* @__PURE__ */
|
|
4947
|
-
/* @__PURE__ */
|
|
4948
|
-
/* @__PURE__ */
|
|
5597
|
+
/* @__PURE__ */ jsxs6("box", { flexDirection: "row", children: [
|
|
5598
|
+
/* @__PURE__ */ jsx6("text", { fg: t.fg.secondary, width: 18, children: "Provider:" }),
|
|
5599
|
+
/* @__PURE__ */ jsx6("text", { fg: t.fg.primary, children: selectedDeployment.config.provider })
|
|
4949
5600
|
] }),
|
|
4950
|
-
/* @__PURE__ */
|
|
4951
|
-
/* @__PURE__ */
|
|
4952
|
-
/* @__PURE__ */
|
|
5601
|
+
/* @__PURE__ */ jsxs6("box", { flexDirection: "row", children: [
|
|
5602
|
+
/* @__PURE__ */ jsx6("text", { fg: t.fg.secondary, width: 18, children: "Server IP:" }),
|
|
5603
|
+
/* @__PURE__ */ jsx6("text", { fg: t.accent, children: selectedDeployment.state.serverIp || "Not deployed" })
|
|
4953
5604
|
] }),
|
|
4954
|
-
/* @__PURE__ */
|
|
4955
|
-
/* @__PURE__ */
|
|
4956
|
-
/* @__PURE__ */
|
|
5605
|
+
/* @__PURE__ */ jsxs6("box", { flexDirection: "row", children: [
|
|
5606
|
+
/* @__PURE__ */ jsx6("text", { fg: t.fg.secondary, width: 18, children: "Tailscale IP:" }),
|
|
5607
|
+
/* @__PURE__ */ jsx6("text", { fg: t.accent, children: selectedDeployment.state.tailscaleIp || "Not configured" })
|
|
4957
5608
|
] }),
|
|
4958
|
-
/* @__PURE__ */
|
|
4959
|
-
/* @__PURE__ */
|
|
4960
|
-
/* @__PURE__ */
|
|
5609
|
+
/* @__PURE__ */ jsxs6("box", { flexDirection: "row", children: [
|
|
5610
|
+
/* @__PURE__ */ jsx6("text", { fg: t.fg.secondary, width: 18, children: "Created:" }),
|
|
5611
|
+
/* @__PURE__ */ jsx6("text", { fg: t.fg.primary, children: new Date(selectedDeployment.config.createdAt).toLocaleString() })
|
|
4961
5612
|
] }),
|
|
4962
|
-
selectedDeployment.state.deployedAt && /* @__PURE__ */
|
|
4963
|
-
/* @__PURE__ */
|
|
4964
|
-
/* @__PURE__ */
|
|
5613
|
+
selectedDeployment.state.deployedAt && /* @__PURE__ */ jsxs6("box", { flexDirection: "row", children: [
|
|
5614
|
+
/* @__PURE__ */ jsx6("text", { fg: t.fg.secondary, width: 18, children: "Deployed:" }),
|
|
5615
|
+
/* @__PURE__ */ jsx6("text", { fg: t.fg.primary, children: new Date(selectedDeployment.state.deployedAt).toLocaleString() })
|
|
4965
5616
|
] }),
|
|
4966
|
-
selectedDeployment.state.lastError && /* @__PURE__ */
|
|
4967
|
-
/* @__PURE__ */
|
|
4968
|
-
/* @__PURE__ */
|
|
5617
|
+
selectedDeployment.state.lastError && /* @__PURE__ */ jsxs6("box", { flexDirection: "row", children: [
|
|
5618
|
+
/* @__PURE__ */ jsx6("text", { fg: t.fg.secondary, width: 18, children: "Last Error:" }),
|
|
5619
|
+
/* @__PURE__ */ jsx6("text", { fg: t.status.error, children: selectedDeployment.state.lastError })
|
|
4969
5620
|
] }),
|
|
4970
|
-
selectedHealth && /* @__PURE__ */
|
|
4971
|
-
/* @__PURE__ */
|
|
4972
|
-
/* @__PURE__ */
|
|
4973
|
-
/* @__PURE__ */
|
|
4974
|
-
/* @__PURE__ */
|
|
5621
|
+
selectedHealth && /* @__PURE__ */ jsxs6("box", { flexDirection: "column", marginTop: 1, children: [
|
|
5622
|
+
/* @__PURE__ */ jsx6("text", { fg: t.fg.primary, children: "Health Check:" }),
|
|
5623
|
+
/* @__PURE__ */ jsxs6("box", { flexDirection: "row", children: [
|
|
5624
|
+
/* @__PURE__ */ jsx6("text", { fg: t.fg.secondary, width: 18, children: "SSH:" }),
|
|
5625
|
+
/* @__PURE__ */ jsx6("text", { fg: selectedHealth.sshConnectable ? t.status.success : t.status.error, children: selectedHealth.sshConnectable ? "Connected" : "Unreachable" })
|
|
4975
5626
|
] }),
|
|
4976
|
-
/* @__PURE__ */
|
|
4977
|
-
/* @__PURE__ */
|
|
4978
|
-
/* @__PURE__ */
|
|
5627
|
+
/* @__PURE__ */ jsxs6("box", { flexDirection: "row", children: [
|
|
5628
|
+
/* @__PURE__ */ jsx6("text", { fg: t.fg.secondary, width: 18, children: "OpenClaw:" }),
|
|
5629
|
+
/* @__PURE__ */ jsx6("text", { fg: selectedHealth.openclawRunning ? t.status.success : t.status.error, children: selectedHealth.openclawRunning ? "Running" : "Not running" })
|
|
4979
5630
|
] }),
|
|
4980
|
-
/* @__PURE__ */
|
|
5631
|
+
/* @__PURE__ */ jsxs6("text", { fg: t.fg.muted, children: [
|
|
4981
5632
|
"Last checked: ",
|
|
4982
5633
|
selectedHealth.lastChecked.toLocaleTimeString()
|
|
4983
5634
|
] })
|
|
4984
5635
|
] }),
|
|
4985
|
-
checking === selectedDeployment.config.name && /* @__PURE__ */
|
|
5636
|
+
checking === selectedDeployment.config.name && /* @__PURE__ */ jsx6("text", { fg: t.status.warning, marginTop: 1, children: "Checking health..." })
|
|
4986
5637
|
]
|
|
4987
5638
|
}
|
|
4988
5639
|
),
|
|
4989
|
-
selectedDeployment.state.checkpoints.length > 0 && /* @__PURE__ */
|
|
4990
|
-
/* @__PURE__ */
|
|
4991
|
-
/* @__PURE__ */
|
|
5640
|
+
selectedDeployment.state.checkpoints.length > 0 && /* @__PURE__ */ jsxs6("box", { flexDirection: "row", marginBottom: 1, children: [
|
|
5641
|
+
/* @__PURE__ */ jsx6("text", { fg: t.fg.secondary, children: "Checkpoints: " }),
|
|
5642
|
+
/* @__PURE__ */ jsxs6("text", { fg: t.status.success, children: [
|
|
4992
5643
|
selectedDeployment.state.checkpoints.length,
|
|
4993
5644
|
" completed"
|
|
4994
5645
|
] })
|
|
4995
5646
|
] }),
|
|
4996
|
-
/* @__PURE__ */
|
|
5647
|
+
/* @__PURE__ */ jsx6("text", { fg: t.fg.muted, children: "Up/Down: Select | Enter: Health check | Esc: Back" })
|
|
4997
5648
|
] });
|
|
4998
5649
|
}
|
|
4999
5650
|
|
|
5000
5651
|
// src/components/SSHView.tsx
|
|
5001
|
-
import { useState as
|
|
5002
|
-
import { useKeyboard as
|
|
5003
|
-
import { jsx as
|
|
5652
|
+
import { useState as useState7, useCallback as useCallback2 } from "react";
|
|
5653
|
+
import { useKeyboard as useKeyboard7 } from "@opentui/react";
|
|
5654
|
+
import { jsx as jsx7, jsxs as jsxs7 } from "@opentui/react/jsx-runtime";
|
|
5004
5655
|
function SSHView({ context }) {
|
|
5005
|
-
const [viewState, setViewState] =
|
|
5006
|
-
const [selectedIndex, setSelectedIndex] =
|
|
5007
|
-
const [error, setError] =
|
|
5008
|
-
const [connectedDeployment, setConnectedDeployment] =
|
|
5009
|
-
const [terminalName, setTerminalName] =
|
|
5656
|
+
const [viewState, setViewState] = useState7("selecting");
|
|
5657
|
+
const [selectedIndex, setSelectedIndex] = useState7(0);
|
|
5658
|
+
const [error, setError] = useState7(null);
|
|
5659
|
+
const [connectedDeployment, setConnectedDeployment] = useState7(null);
|
|
5660
|
+
const [terminalName, setTerminalName] = useState7("");
|
|
5010
5661
|
const deployedDeployments = context.deployments.filter(
|
|
5011
5662
|
(d) => d.state.status === "deployed" && d.state.serverIp
|
|
5012
5663
|
);
|
|
@@ -5025,7 +5676,7 @@ function SSHView({ context }) {
|
|
|
5025
5676
|
}
|
|
5026
5677
|
}, []);
|
|
5027
5678
|
const selectedDeployment = deployedDeployments[selectedIndex];
|
|
5028
|
-
|
|
5679
|
+
useKeyboard7((key) => {
|
|
5029
5680
|
if (deployedDeployments.length === 0) {
|
|
5030
5681
|
context.navigateTo("home");
|
|
5031
5682
|
return;
|
|
@@ -5049,12 +5700,12 @@ function SSHView({ context }) {
|
|
|
5049
5700
|
}
|
|
5050
5701
|
});
|
|
5051
5702
|
if (deployedDeployments.length === 0) {
|
|
5052
|
-
return /* @__PURE__ */
|
|
5053
|
-
/* @__PURE__ */
|
|
5054
|
-
/* @__PURE__ */
|
|
5055
|
-
/* @__PURE__ */
|
|
5703
|
+
return /* @__PURE__ */ jsxs7("box", { flexDirection: "column", width: "100%", padding: 1, children: [
|
|
5704
|
+
/* @__PURE__ */ jsxs7("box", { flexDirection: "row", marginBottom: 2, children: [
|
|
5705
|
+
/* @__PURE__ */ jsx7("text", { fg: t.accent, children: "/ssh" }),
|
|
5706
|
+
/* @__PURE__ */ jsx7("text", { fg: t.fg.secondary, children: " - SSH into deployment" })
|
|
5056
5707
|
] }),
|
|
5057
|
-
/* @__PURE__ */
|
|
5708
|
+
/* @__PURE__ */ jsxs7(
|
|
5058
5709
|
"box",
|
|
5059
5710
|
{
|
|
5060
5711
|
flexDirection: "column",
|
|
@@ -5062,23 +5713,23 @@ function SSHView({ context }) {
|
|
|
5062
5713
|
borderColor: t.border.default,
|
|
5063
5714
|
padding: 1,
|
|
5064
5715
|
children: [
|
|
5065
|
-
/* @__PURE__ */
|
|
5066
|
-
/* @__PURE__ */
|
|
5716
|
+
/* @__PURE__ */ jsx7("text", { fg: t.status.warning, children: "No deployed instances found!" }),
|
|
5717
|
+
/* @__PURE__ */ jsx7("text", { fg: t.fg.secondary, marginTop: 1, children: "Deploy an instance first with /deploy" })
|
|
5067
5718
|
]
|
|
5068
5719
|
}
|
|
5069
5720
|
),
|
|
5070
|
-
/* @__PURE__ */
|
|
5721
|
+
/* @__PURE__ */ jsx7("text", { fg: t.fg.muted, marginTop: 2, children: "Press any key to return to home" })
|
|
5071
5722
|
] });
|
|
5072
5723
|
}
|
|
5073
5724
|
if (viewState === "selecting") {
|
|
5074
5725
|
const terminal = detectTerminal();
|
|
5075
5726
|
const terminalDisplayName = getTerminalDisplayName(terminal.app);
|
|
5076
|
-
return /* @__PURE__ */
|
|
5077
|
-
/* @__PURE__ */
|
|
5078
|
-
/* @__PURE__ */
|
|
5079
|
-
/* @__PURE__ */
|
|
5727
|
+
return /* @__PURE__ */ jsxs7("box", { flexDirection: "column", width: "100%", padding: 1, children: [
|
|
5728
|
+
/* @__PURE__ */ jsxs7("box", { flexDirection: "row", marginBottom: 2, children: [
|
|
5729
|
+
/* @__PURE__ */ jsx7("text", { fg: t.accent, children: "/ssh" }),
|
|
5730
|
+
/* @__PURE__ */ jsx7("text", { fg: t.fg.secondary, children: " - Select a deployment to connect" })
|
|
5080
5731
|
] }),
|
|
5081
|
-
/* @__PURE__ */
|
|
5732
|
+
/* @__PURE__ */ jsx7(
|
|
5082
5733
|
"box",
|
|
5083
5734
|
{
|
|
5084
5735
|
flexDirection: "column",
|
|
@@ -5088,15 +5739,15 @@ function SSHView({ context }) {
|
|
|
5088
5739
|
marginBottom: 1,
|
|
5089
5740
|
children: deployedDeployments.map((deployment, index) => {
|
|
5090
5741
|
const isSelected = index === selectedIndex;
|
|
5091
|
-
return /* @__PURE__ */
|
|
5742
|
+
return /* @__PURE__ */ jsxs7(
|
|
5092
5743
|
"box",
|
|
5093
5744
|
{
|
|
5094
5745
|
flexDirection: "row",
|
|
5095
5746
|
backgroundColor: isSelected ? t.selection.bg : void 0,
|
|
5096
5747
|
children: [
|
|
5097
|
-
/* @__PURE__ */
|
|
5098
|
-
/* @__PURE__ */
|
|
5099
|
-
/* @__PURE__ */
|
|
5748
|
+
/* @__PURE__ */ jsx7("text", { fg: isSelected ? t.selection.fg : t.fg.secondary, children: isSelected ? "> " : " " }),
|
|
5749
|
+
/* @__PURE__ */ jsx7("text", { fg: isSelected ? t.selection.fg : t.fg.secondary, width: 25, children: deployment.config.name }),
|
|
5750
|
+
/* @__PURE__ */ jsx7("text", { fg: t.accent, children: deployment.state.serverIp })
|
|
5100
5751
|
]
|
|
5101
5752
|
},
|
|
5102
5753
|
deployment.config.name
|
|
@@ -5104,24 +5755,24 @@ function SSHView({ context }) {
|
|
|
5104
5755
|
})
|
|
5105
5756
|
}
|
|
5106
5757
|
),
|
|
5107
|
-
/* @__PURE__ */
|
|
5108
|
-
/* @__PURE__ */
|
|
5109
|
-
/* @__PURE__ */
|
|
5110
|
-
/* @__PURE__ */
|
|
5758
|
+
/* @__PURE__ */ jsxs7("box", { flexDirection: "row", marginBottom: 1, children: [
|
|
5759
|
+
/* @__PURE__ */ jsx7("text", { fg: t.fg.secondary, children: "Terminal: " }),
|
|
5760
|
+
/* @__PURE__ */ jsx7("text", { fg: t.status.success, children: terminalDisplayName }),
|
|
5761
|
+
/* @__PURE__ */ jsx7("text", { fg: t.fg.secondary, children: " (will open in a new window)" })
|
|
5111
5762
|
] }),
|
|
5112
|
-
/* @__PURE__ */
|
|
5763
|
+
/* @__PURE__ */ jsx7("text", { fg: t.fg.muted, children: "Arrow keys to select | Enter to connect | Esc to go back" })
|
|
5113
5764
|
] });
|
|
5114
5765
|
}
|
|
5115
5766
|
if (viewState === "connected") {
|
|
5116
|
-
return /* @__PURE__ */
|
|
5117
|
-
/* @__PURE__ */
|
|
5118
|
-
/* @__PURE__ */
|
|
5119
|
-
/* @__PURE__ */
|
|
5767
|
+
return /* @__PURE__ */ jsxs7("box", { flexDirection: "column", width: "100%", padding: 1, children: [
|
|
5768
|
+
/* @__PURE__ */ jsxs7("box", { flexDirection: "row", marginBottom: 2, children: [
|
|
5769
|
+
/* @__PURE__ */ jsx7("text", { fg: t.status.success, children: "/ssh" }),
|
|
5770
|
+
/* @__PURE__ */ jsxs7("text", { fg: t.fg.secondary, children: [
|
|
5120
5771
|
" - Connected to ",
|
|
5121
5772
|
connectedDeployment
|
|
5122
5773
|
] })
|
|
5123
5774
|
] }),
|
|
5124
|
-
/* @__PURE__ */
|
|
5775
|
+
/* @__PURE__ */ jsxs7(
|
|
5125
5776
|
"box",
|
|
5126
5777
|
{
|
|
5127
5778
|
flexDirection: "column",
|
|
@@ -5130,8 +5781,8 @@ function SSHView({ context }) {
|
|
|
5130
5781
|
padding: 1,
|
|
5131
5782
|
marginBottom: 1,
|
|
5132
5783
|
children: [
|
|
5133
|
-
/* @__PURE__ */
|
|
5134
|
-
/* @__PURE__ */
|
|
5784
|
+
/* @__PURE__ */ jsx7("text", { fg: t.status.success, children: "SSH Session Opened" }),
|
|
5785
|
+
/* @__PURE__ */ jsxs7("text", { fg: t.fg.primary, marginTop: 1, children: [
|
|
5135
5786
|
"A new ",
|
|
5136
5787
|
terminalName,
|
|
5137
5788
|
" window/tab has been opened."
|
|
@@ -5139,7 +5790,7 @@ function SSHView({ context }) {
|
|
|
5139
5790
|
]
|
|
5140
5791
|
}
|
|
5141
5792
|
),
|
|
5142
|
-
/* @__PURE__ */
|
|
5793
|
+
/* @__PURE__ */ jsxs7(
|
|
5143
5794
|
"box",
|
|
5144
5795
|
{
|
|
5145
5796
|
flexDirection: "column",
|
|
@@ -5148,41 +5799,41 @@ function SSHView({ context }) {
|
|
|
5148
5799
|
padding: 1,
|
|
5149
5800
|
marginBottom: 1,
|
|
5150
5801
|
children: [
|
|
5151
|
-
/* @__PURE__ */
|
|
5802
|
+
/* @__PURE__ */ jsxs7("text", { fg: t.fg.primary, children: [
|
|
5152
5803
|
"Your SSH session is running in ",
|
|
5153
5804
|
terminalName,
|
|
5154
5805
|
"."
|
|
5155
5806
|
] }),
|
|
5156
|
-
/* @__PURE__ */
|
|
5807
|
+
/* @__PURE__ */ jsx7("text", { fg: t.fg.primary, marginTop: 1, children: "When you're done, type 'exit' or close the tab." })
|
|
5157
5808
|
]
|
|
5158
5809
|
}
|
|
5159
5810
|
),
|
|
5160
|
-
/* @__PURE__ */
|
|
5811
|
+
/* @__PURE__ */ jsx7("text", { fg: t.fg.muted, marginTop: 1, children: "Press Enter or Esc to return to ClawControl" })
|
|
5161
5812
|
] });
|
|
5162
5813
|
}
|
|
5163
5814
|
if (viewState === "error") {
|
|
5164
|
-
return /* @__PURE__ */
|
|
5165
|
-
/* @__PURE__ */
|
|
5166
|
-
/* @__PURE__ */
|
|
5815
|
+
return /* @__PURE__ */ jsxs7("box", { flexDirection: "column", width: "100%", padding: 1, children: [
|
|
5816
|
+
/* @__PURE__ */ jsx7("box", { flexDirection: "row", marginBottom: 2, children: /* @__PURE__ */ jsx7("text", { fg: t.status.error, children: "SSH Error" }) }),
|
|
5817
|
+
/* @__PURE__ */ jsx7(
|
|
5167
5818
|
"box",
|
|
5168
5819
|
{
|
|
5169
5820
|
flexDirection: "column",
|
|
5170
5821
|
borderStyle: "single",
|
|
5171
5822
|
borderColor: t.status.error,
|
|
5172
5823
|
padding: 1,
|
|
5173
|
-
children: /* @__PURE__ */
|
|
5824
|
+
children: /* @__PURE__ */ jsx7("text", { fg: t.status.error, children: error })
|
|
5174
5825
|
}
|
|
5175
5826
|
),
|
|
5176
|
-
/* @__PURE__ */
|
|
5827
|
+
/* @__PURE__ */ jsx7("text", { fg: t.fg.muted, marginTop: 2, children: "Press any key to return to home" })
|
|
5177
5828
|
] });
|
|
5178
5829
|
}
|
|
5179
5830
|
return null;
|
|
5180
5831
|
}
|
|
5181
5832
|
|
|
5182
5833
|
// src/components/LogsView.tsx
|
|
5183
|
-
import { useState as
|
|
5184
|
-
import { useKeyboard as
|
|
5185
|
-
import { Fragment, jsx as
|
|
5834
|
+
import { useState as useState8, useEffect as useEffect3, useRef as useRef5 } from "react";
|
|
5835
|
+
import { useKeyboard as useKeyboard8 } from "@opentui/react";
|
|
5836
|
+
import { Fragment as Fragment2, jsx as jsx8, jsxs as jsxs8 } from "@opentui/react/jsx-runtime";
|
|
5186
5837
|
function parseLogLine(line) {
|
|
5187
5838
|
if (!line.trim()) return null;
|
|
5188
5839
|
const match = line.match(/^(\w+\s+\d+\s+[\d:]+)\s+\S+\s+\S+:\s*(.*)$/);
|
|
@@ -5201,14 +5852,14 @@ function truncateLine(line, maxWidth = 120) {
|
|
|
5201
5852
|
return line.substring(0, maxWidth - 3) + "...";
|
|
5202
5853
|
}
|
|
5203
5854
|
function LogsView({ context }) {
|
|
5204
|
-
const [viewState, setViewState] =
|
|
5205
|
-
const [selectedIndex, setSelectedIndex] =
|
|
5206
|
-
const [logs, setLogs] =
|
|
5207
|
-
const [error, setError] =
|
|
5208
|
-
const [autoRefresh, setAutoRefresh] =
|
|
5209
|
-
const [lastFetched, setLastFetched] =
|
|
5210
|
-
const sshRef =
|
|
5211
|
-
const refreshIntervalRef =
|
|
5855
|
+
const [viewState, setViewState] = useState8("selecting");
|
|
5856
|
+
const [selectedIndex, setSelectedIndex] = useState8(0);
|
|
5857
|
+
const [logs, setLogs] = useState8([]);
|
|
5858
|
+
const [error, setError] = useState8(null);
|
|
5859
|
+
const [autoRefresh, setAutoRefresh] = useState8(false);
|
|
5860
|
+
const [lastFetched, setLastFetched] = useState8(null);
|
|
5861
|
+
const sshRef = useRef5(null);
|
|
5862
|
+
const refreshIntervalRef = useRef5(null);
|
|
5212
5863
|
const deployedDeployments = context.deployments.filter(
|
|
5213
5864
|
(d) => d.state.status === "deployed" && d.state.serverIp
|
|
5214
5865
|
);
|
|
@@ -5259,7 +5910,7 @@ function LogsView({ context }) {
|
|
|
5259
5910
|
setLogs([]);
|
|
5260
5911
|
};
|
|
5261
5912
|
const selectedDeployment = deployedDeployments[selectedIndex];
|
|
5262
|
-
|
|
5913
|
+
useKeyboard8((key) => {
|
|
5263
5914
|
if (deployedDeployments.length === 0) {
|
|
5264
5915
|
context.navigateTo("home");
|
|
5265
5916
|
return;
|
|
@@ -5292,12 +5943,12 @@ function LogsView({ context }) {
|
|
|
5292
5943
|
}
|
|
5293
5944
|
});
|
|
5294
5945
|
if (deployedDeployments.length === 0) {
|
|
5295
|
-
return /* @__PURE__ */
|
|
5296
|
-
/* @__PURE__ */
|
|
5297
|
-
/* @__PURE__ */
|
|
5298
|
-
/* @__PURE__ */
|
|
5946
|
+
return /* @__PURE__ */ jsxs8("box", { flexDirection: "column", width: "100%", padding: 1, children: [
|
|
5947
|
+
/* @__PURE__ */ jsxs8("box", { flexDirection: "row", marginBottom: 2, children: [
|
|
5948
|
+
/* @__PURE__ */ jsx8("text", { fg: t.accent, children: "/logs" }),
|
|
5949
|
+
/* @__PURE__ */ jsx8("text", { fg: t.fg.secondary, children: " - View deployment logs" })
|
|
5299
5950
|
] }),
|
|
5300
|
-
/* @__PURE__ */
|
|
5951
|
+
/* @__PURE__ */ jsxs8(
|
|
5301
5952
|
"box",
|
|
5302
5953
|
{
|
|
5303
5954
|
flexDirection: "column",
|
|
@@ -5305,21 +5956,21 @@ function LogsView({ context }) {
|
|
|
5305
5956
|
borderColor: t.border.default,
|
|
5306
5957
|
padding: 1,
|
|
5307
5958
|
children: [
|
|
5308
|
-
/* @__PURE__ */
|
|
5309
|
-
/* @__PURE__ */
|
|
5959
|
+
/* @__PURE__ */ jsx8("text", { fg: t.status.warning, children: "No deployed instances found!" }),
|
|
5960
|
+
/* @__PURE__ */ jsx8("text", { fg: t.fg.secondary, marginTop: 1, children: "Deploy an instance first with /deploy" })
|
|
5310
5961
|
]
|
|
5311
5962
|
}
|
|
5312
5963
|
),
|
|
5313
|
-
/* @__PURE__ */
|
|
5964
|
+
/* @__PURE__ */ jsx8("text", { fg: t.fg.muted, marginTop: 2, children: "Press any key to return to home" })
|
|
5314
5965
|
] });
|
|
5315
5966
|
}
|
|
5316
5967
|
if (viewState === "selecting") {
|
|
5317
|
-
return /* @__PURE__ */
|
|
5318
|
-
/* @__PURE__ */
|
|
5319
|
-
/* @__PURE__ */
|
|
5320
|
-
/* @__PURE__ */
|
|
5968
|
+
return /* @__PURE__ */ jsxs8("box", { flexDirection: "column", width: "100%", padding: 1, children: [
|
|
5969
|
+
/* @__PURE__ */ jsxs8("box", { flexDirection: "row", marginBottom: 2, children: [
|
|
5970
|
+
/* @__PURE__ */ jsx8("text", { fg: t.accent, children: "/logs" }),
|
|
5971
|
+
/* @__PURE__ */ jsx8("text", { fg: t.fg.secondary, children: " - Select a deployment" })
|
|
5321
5972
|
] }),
|
|
5322
|
-
/* @__PURE__ */
|
|
5973
|
+
/* @__PURE__ */ jsx8(
|
|
5323
5974
|
"box",
|
|
5324
5975
|
{
|
|
5325
5976
|
flexDirection: "column",
|
|
@@ -5328,15 +5979,15 @@ function LogsView({ context }) {
|
|
|
5328
5979
|
padding: 1,
|
|
5329
5980
|
children: deployedDeployments.map((deployment, index) => {
|
|
5330
5981
|
const isSelected = index === selectedIndex;
|
|
5331
|
-
return /* @__PURE__ */
|
|
5982
|
+
return /* @__PURE__ */ jsxs8(
|
|
5332
5983
|
"box",
|
|
5333
5984
|
{
|
|
5334
5985
|
flexDirection: "row",
|
|
5335
5986
|
backgroundColor: isSelected ? t.selection.bg : void 0,
|
|
5336
5987
|
children: [
|
|
5337
|
-
/* @__PURE__ */
|
|
5338
|
-
/* @__PURE__ */
|
|
5339
|
-
/* @__PURE__ */
|
|
5988
|
+
/* @__PURE__ */ jsx8("text", { fg: isSelected ? t.selection.fg : t.fg.secondary, children: isSelected ? "> " : " " }),
|
|
5989
|
+
/* @__PURE__ */ jsx8("text", { fg: isSelected ? t.selection.fg : t.fg.secondary, width: 25, children: deployment.config.name }),
|
|
5990
|
+
/* @__PURE__ */ jsx8("text", { fg: t.accent, children: deployment.state.serverIp })
|
|
5340
5991
|
]
|
|
5341
5992
|
},
|
|
5342
5993
|
deployment.config.name
|
|
@@ -5344,70 +5995,70 @@ function LogsView({ context }) {
|
|
|
5344
5995
|
})
|
|
5345
5996
|
}
|
|
5346
5997
|
),
|
|
5347
|
-
/* @__PURE__ */
|
|
5998
|
+
/* @__PURE__ */ jsx8("text", { fg: t.fg.muted, marginTop: 2, children: "Arrow keys to select | Enter to view logs | Esc to go back" })
|
|
5348
5999
|
] });
|
|
5349
6000
|
}
|
|
5350
6001
|
if (viewState === "loading") {
|
|
5351
|
-
return /* @__PURE__ */
|
|
5352
|
-
/* @__PURE__ */
|
|
5353
|
-
/* @__PURE__ */
|
|
6002
|
+
return /* @__PURE__ */ jsxs8("box", { flexDirection: "column", width: "100%", padding: 1, children: [
|
|
6003
|
+
/* @__PURE__ */ jsx8("text", { fg: t.accent, children: "Loading logs..." }),
|
|
6004
|
+
/* @__PURE__ */ jsx8("text", { fg: t.fg.secondary, marginTop: 1, children: "Fetching OpenClaw logs from server..." })
|
|
5354
6005
|
] });
|
|
5355
6006
|
}
|
|
5356
6007
|
if (viewState === "error") {
|
|
5357
|
-
return /* @__PURE__ */
|
|
5358
|
-
/* @__PURE__ */
|
|
5359
|
-
/* @__PURE__ */
|
|
6008
|
+
return /* @__PURE__ */ jsxs8("box", { flexDirection: "column", width: "100%", padding: 1, children: [
|
|
6009
|
+
/* @__PURE__ */ jsx8("box", { flexDirection: "row", marginBottom: 2, children: /* @__PURE__ */ jsx8("text", { fg: t.status.error, children: "Error Loading Logs" }) }),
|
|
6010
|
+
/* @__PURE__ */ jsx8(
|
|
5360
6011
|
"box",
|
|
5361
6012
|
{
|
|
5362
6013
|
flexDirection: "column",
|
|
5363
6014
|
borderStyle: "single",
|
|
5364
6015
|
borderColor: t.status.error,
|
|
5365
6016
|
padding: 1,
|
|
5366
|
-
children: /* @__PURE__ */
|
|
6017
|
+
children: /* @__PURE__ */ jsx8("text", { fg: t.status.error, children: error })
|
|
5367
6018
|
}
|
|
5368
6019
|
),
|
|
5369
|
-
/* @__PURE__ */
|
|
6020
|
+
/* @__PURE__ */ jsx8("text", { fg: t.fg.muted, marginTop: 2, children: "Press any key to go back" })
|
|
5370
6021
|
] });
|
|
5371
6022
|
}
|
|
5372
6023
|
const visibleLogs = logs;
|
|
5373
|
-
return /* @__PURE__ */
|
|
5374
|
-
/* @__PURE__ */
|
|
5375
|
-
/* @__PURE__ */
|
|
6024
|
+
return /* @__PURE__ */ jsxs8("box", { flexDirection: "column", width: "100%", padding: 1, children: [
|
|
6025
|
+
/* @__PURE__ */ jsxs8("box", { flexDirection: "row", marginBottom: 1, children: [
|
|
6026
|
+
/* @__PURE__ */ jsxs8("text", { fg: t.accent, children: [
|
|
5376
6027
|
"Logs: ",
|
|
5377
6028
|
selectedDeployment.config.name
|
|
5378
6029
|
] }),
|
|
5379
|
-
/* @__PURE__ */
|
|
5380
|
-
/* @__PURE__ */
|
|
6030
|
+
/* @__PURE__ */ jsx8("text", { fg: t.fg.muted, children: " | " }),
|
|
6031
|
+
/* @__PURE__ */ jsxs8("text", { fg: autoRefresh ? t.status.success : t.fg.muted, children: [
|
|
5381
6032
|
"Auto: ",
|
|
5382
6033
|
autoRefresh ? "ON (5s)" : "OFF"
|
|
5383
6034
|
] }),
|
|
5384
|
-
lastFetched && /* @__PURE__ */
|
|
5385
|
-
/* @__PURE__ */
|
|
5386
|
-
/* @__PURE__ */
|
|
6035
|
+
lastFetched && /* @__PURE__ */ jsxs8(Fragment2, { children: [
|
|
6036
|
+
/* @__PURE__ */ jsx8("text", { fg: t.fg.muted, children: " | " }),
|
|
6037
|
+
/* @__PURE__ */ jsxs8("text", { fg: t.fg.muted, children: [
|
|
5387
6038
|
"Fetched: ",
|
|
5388
6039
|
lastFetched.toLocaleTimeString()
|
|
5389
6040
|
] })
|
|
5390
6041
|
] })
|
|
5391
6042
|
] }),
|
|
5392
|
-
/* @__PURE__ */
|
|
6043
|
+
/* @__PURE__ */ jsx8(
|
|
5393
6044
|
"box",
|
|
5394
6045
|
{
|
|
5395
6046
|
flexDirection: "column",
|
|
5396
6047
|
borderStyle: "single",
|
|
5397
6048
|
borderColor: t.border.default,
|
|
5398
6049
|
padding: 1,
|
|
5399
|
-
children: /* @__PURE__ */
|
|
6050
|
+
children: /* @__PURE__ */ jsx8("box", { flexDirection: "column", children: visibleLogs.map((line, i) => {
|
|
5400
6051
|
const parsed = parseLogLine(line);
|
|
5401
6052
|
if (!parsed) return null;
|
|
5402
6053
|
const displayLine = parsed.timestamp ? `${parsed.timestamp} ${truncateLine(parsed.message, 100)}` : truncateLine(parsed.message, 120);
|
|
5403
|
-
return /* @__PURE__ */
|
|
6054
|
+
return /* @__PURE__ */ jsx8("text", { fg: logLevelColor(parsed.level), children: displayLine }, i);
|
|
5404
6055
|
}) })
|
|
5405
6056
|
}
|
|
5406
6057
|
),
|
|
5407
|
-
/* @__PURE__ */
|
|
5408
|
-
/* @__PURE__ */
|
|
5409
|
-
/* @__PURE__ */
|
|
5410
|
-
/* @__PURE__ */
|
|
6058
|
+
/* @__PURE__ */ jsxs8("box", { flexDirection: "row", marginTop: 1, children: [
|
|
6059
|
+
/* @__PURE__ */ jsx8("text", { fg: t.fg.muted, children: "R: Refresh | A: Toggle auto-refresh | Esc: Back" }),
|
|
6060
|
+
/* @__PURE__ */ jsx8("text", { fg: t.fg.muted, children: " | " }),
|
|
6061
|
+
/* @__PURE__ */ jsxs8("text", { fg: t.accent, children: [
|
|
5411
6062
|
"Showing last ",
|
|
5412
6063
|
visibleLogs.length,
|
|
5413
6064
|
" lines"
|
|
@@ -5417,14 +6068,14 @@ function LogsView({ context }) {
|
|
|
5417
6068
|
}
|
|
5418
6069
|
|
|
5419
6070
|
// src/components/DestroyView.tsx
|
|
5420
|
-
import { useState as
|
|
5421
|
-
import { useKeyboard as
|
|
5422
|
-
import { jsx as
|
|
6071
|
+
import { useState as useState9 } from "react";
|
|
6072
|
+
import { useKeyboard as useKeyboard9 } from "@opentui/react";
|
|
6073
|
+
import { jsx as jsx9, jsxs as jsxs9 } from "@opentui/react/jsx-runtime";
|
|
5423
6074
|
function DestroyView({ context }) {
|
|
5424
|
-
const [viewState, setViewState] =
|
|
5425
|
-
const [selectedIndex, setSelectedIndex] =
|
|
5426
|
-
const [error, setError] =
|
|
5427
|
-
const [confirmText, setConfirmText] =
|
|
6075
|
+
const [viewState, setViewState] = useState9("selecting");
|
|
6076
|
+
const [selectedIndex, setSelectedIndex] = useState9(0);
|
|
6077
|
+
const [error, setError] = useState9(null);
|
|
6078
|
+
const [confirmText, setConfirmText] = useState9("");
|
|
5428
6079
|
const deployments = context.deployments;
|
|
5429
6080
|
const destroyDeployment = async (name) => {
|
|
5430
6081
|
setViewState("destroying");
|
|
@@ -5469,7 +6120,7 @@ function DestroyView({ context }) {
|
|
|
5469
6120
|
}
|
|
5470
6121
|
};
|
|
5471
6122
|
const selectedDeployment = deployments[selectedIndex];
|
|
5472
|
-
|
|
6123
|
+
useKeyboard9((key) => {
|
|
5473
6124
|
if (deployments.length === 0) {
|
|
5474
6125
|
context.navigateTo("home");
|
|
5475
6126
|
return;
|
|
@@ -5494,31 +6145,31 @@ function DestroyView({ context }) {
|
|
|
5494
6145
|
}
|
|
5495
6146
|
});
|
|
5496
6147
|
if (deployments.length === 0) {
|
|
5497
|
-
return /* @__PURE__ */
|
|
5498
|
-
/* @__PURE__ */
|
|
5499
|
-
/* @__PURE__ */
|
|
5500
|
-
/* @__PURE__ */
|
|
6148
|
+
return /* @__PURE__ */ jsxs9("box", { flexDirection: "column", width: "100%", padding: 1, children: [
|
|
6149
|
+
/* @__PURE__ */ jsxs9("box", { flexDirection: "row", marginBottom: 2, children: [
|
|
6150
|
+
/* @__PURE__ */ jsx9("text", { fg: t.status.error, children: "/destroy" }),
|
|
6151
|
+
/* @__PURE__ */ jsx9("text", { fg: t.fg.secondary, children: " - Destroy deployment" })
|
|
5501
6152
|
] }),
|
|
5502
|
-
/* @__PURE__ */
|
|
6153
|
+
/* @__PURE__ */ jsx9(
|
|
5503
6154
|
"box",
|
|
5504
6155
|
{
|
|
5505
6156
|
flexDirection: "column",
|
|
5506
6157
|
borderStyle: "single",
|
|
5507
6158
|
borderColor: t.border.default,
|
|
5508
6159
|
padding: 1,
|
|
5509
|
-
children: /* @__PURE__ */
|
|
6160
|
+
children: /* @__PURE__ */ jsx9("text", { fg: t.status.warning, children: "No deployments found!" })
|
|
5510
6161
|
}
|
|
5511
6162
|
),
|
|
5512
|
-
/* @__PURE__ */
|
|
6163
|
+
/* @__PURE__ */ jsx9("text", { fg: t.fg.muted, marginTop: 2, children: "Press any key to return to home" })
|
|
5513
6164
|
] });
|
|
5514
6165
|
}
|
|
5515
6166
|
if (viewState === "selecting") {
|
|
5516
|
-
return /* @__PURE__ */
|
|
5517
|
-
/* @__PURE__ */
|
|
5518
|
-
/* @__PURE__ */
|
|
5519
|
-
/* @__PURE__ */
|
|
6167
|
+
return /* @__PURE__ */ jsxs9("box", { flexDirection: "column", width: "100%", padding: 1, children: [
|
|
6168
|
+
/* @__PURE__ */ jsxs9("box", { flexDirection: "row", marginBottom: 2, children: [
|
|
6169
|
+
/* @__PURE__ */ jsx9("text", { fg: t.status.error, children: "/destroy" }),
|
|
6170
|
+
/* @__PURE__ */ jsx9("text", { fg: t.fg.secondary, children: " - Select a deployment to destroy" })
|
|
5520
6171
|
] }),
|
|
5521
|
-
/* @__PURE__ */
|
|
6172
|
+
/* @__PURE__ */ jsxs9(
|
|
5522
6173
|
"box",
|
|
5523
6174
|
{
|
|
5524
6175
|
flexDirection: "column",
|
|
@@ -5526,18 +6177,18 @@ function DestroyView({ context }) {
|
|
|
5526
6177
|
borderColor: t.status.error,
|
|
5527
6178
|
padding: 1,
|
|
5528
6179
|
children: [
|
|
5529
|
-
/* @__PURE__ */
|
|
6180
|
+
/* @__PURE__ */ jsx9("text", { fg: t.status.error, marginBottom: 1, children: "WARNING: This action cannot be undone!" }),
|
|
5530
6181
|
deployments.map((deployment, index) => {
|
|
5531
6182
|
const isSelected = index === selectedIndex;
|
|
5532
|
-
return /* @__PURE__ */
|
|
6183
|
+
return /* @__PURE__ */ jsxs9(
|
|
5533
6184
|
"box",
|
|
5534
6185
|
{
|
|
5535
6186
|
flexDirection: "row",
|
|
5536
6187
|
backgroundColor: isSelected ? t.selection.bg : void 0,
|
|
5537
6188
|
children: [
|
|
5538
|
-
/* @__PURE__ */
|
|
5539
|
-
/* @__PURE__ */
|
|
5540
|
-
/* @__PURE__ */
|
|
6189
|
+
/* @__PURE__ */ jsx9("text", { fg: isSelected ? t.selection.fg : t.fg.secondary, children: isSelected ? "> " : " " }),
|
|
6190
|
+
/* @__PURE__ */ jsx9("text", { fg: isSelected ? t.selection.fg : t.fg.secondary, width: 25, children: deployment.config.name }),
|
|
6191
|
+
/* @__PURE__ */ jsxs9("text", { fg: isSelected ? t.selection.fg : t.fg.secondary, children: [
|
|
5541
6192
|
"[",
|
|
5542
6193
|
deployment.state.status,
|
|
5543
6194
|
"]"
|
|
@@ -5550,13 +6201,13 @@ function DestroyView({ context }) {
|
|
|
5550
6201
|
]
|
|
5551
6202
|
}
|
|
5552
6203
|
),
|
|
5553
|
-
/* @__PURE__ */
|
|
6204
|
+
/* @__PURE__ */ jsx9("text", { fg: t.fg.muted, marginTop: 2, children: "Arrow keys to select | Enter to destroy | Esc to go back" })
|
|
5554
6205
|
] });
|
|
5555
6206
|
}
|
|
5556
6207
|
if (viewState === "confirming") {
|
|
5557
|
-
return /* @__PURE__ */
|
|
5558
|
-
/* @__PURE__ */
|
|
5559
|
-
/* @__PURE__ */
|
|
6208
|
+
return /* @__PURE__ */ jsxs9("box", { flexDirection: "column", width: "100%", padding: 1, children: [
|
|
6209
|
+
/* @__PURE__ */ jsx9("box", { flexDirection: "row", marginBottom: 2, children: /* @__PURE__ */ jsx9("text", { fg: t.status.error, children: "Confirm Destruction" }) }),
|
|
6210
|
+
/* @__PURE__ */ jsxs9(
|
|
5560
6211
|
"box",
|
|
5561
6212
|
{
|
|
5562
6213
|
flexDirection: "column",
|
|
@@ -5564,26 +6215,26 @@ function DestroyView({ context }) {
|
|
|
5564
6215
|
borderColor: t.status.error,
|
|
5565
6216
|
padding: 1,
|
|
5566
6217
|
children: [
|
|
5567
|
-
/* @__PURE__ */
|
|
5568
|
-
/* @__PURE__ */
|
|
6218
|
+
/* @__PURE__ */ jsx9("text", { fg: t.status.error, children: "You are about to destroy:" }),
|
|
6219
|
+
/* @__PURE__ */ jsxs9("text", { fg: t.fg.primary, marginTop: 1, children: [
|
|
5569
6220
|
"Deployment: ",
|
|
5570
6221
|
selectedDeployment.config.name
|
|
5571
6222
|
] }),
|
|
5572
|
-
selectedDeployment.state.serverIp && /* @__PURE__ */
|
|
6223
|
+
selectedDeployment.state.serverIp && /* @__PURE__ */ jsxs9("text", { fg: t.fg.primary, children: [
|
|
5573
6224
|
"Server IP: ",
|
|
5574
6225
|
selectedDeployment.state.serverIp
|
|
5575
6226
|
] }),
|
|
5576
|
-
/* @__PURE__ */
|
|
5577
|
-
/* @__PURE__ */
|
|
5578
|
-
/* @__PURE__ */
|
|
5579
|
-
/* @__PURE__ */
|
|
5580
|
-
/* @__PURE__ */
|
|
6227
|
+
/* @__PURE__ */ jsx9("text", { fg: t.status.error, marginTop: 1, children: "This will permanently delete:" }),
|
|
6228
|
+
/* @__PURE__ */ jsx9("text", { fg: t.fg.secondary, children: "\u2022 The VPS server (if deployed)" }),
|
|
6229
|
+
/* @__PURE__ */ jsx9("text", { fg: t.fg.secondary, children: "\u2022 All data on the server" }),
|
|
6230
|
+
/* @__PURE__ */ jsx9("text", { fg: t.fg.secondary, children: "\u2022 Local configuration files" }),
|
|
6231
|
+
/* @__PURE__ */ jsx9("text", { fg: t.fg.secondary, children: "\u2022 SSH keys" })
|
|
5581
6232
|
]
|
|
5582
6233
|
}
|
|
5583
6234
|
),
|
|
5584
|
-
/* @__PURE__ */
|
|
5585
|
-
/* @__PURE__ */
|
|
5586
|
-
/* @__PURE__ */
|
|
6235
|
+
/* @__PURE__ */ jsx9("text", { fg: t.status.warning, marginTop: 2, children: "Type the deployment name to confirm:" }),
|
|
6236
|
+
/* @__PURE__ */ jsx9("text", { fg: t.fg.primary, marginTop: 1, children: "Confirm:" }),
|
|
6237
|
+
/* @__PURE__ */ jsx9(
|
|
5587
6238
|
"input",
|
|
5588
6239
|
{
|
|
5589
6240
|
value: confirmText,
|
|
@@ -5606,19 +6257,19 @@ function DestroyView({ context }) {
|
|
|
5606
6257
|
}
|
|
5607
6258
|
}
|
|
5608
6259
|
),
|
|
5609
|
-
error && /* @__PURE__ */
|
|
5610
|
-
/* @__PURE__ */
|
|
6260
|
+
error && /* @__PURE__ */ jsx9("text", { fg: t.status.error, marginTop: 1, children: error }),
|
|
6261
|
+
/* @__PURE__ */ jsx9("text", { fg: t.fg.muted, marginTop: 2, children: "Press Esc to cancel" })
|
|
5611
6262
|
] });
|
|
5612
6263
|
}
|
|
5613
6264
|
if (viewState === "destroying") {
|
|
5614
|
-
return /* @__PURE__ */
|
|
5615
|
-
/* @__PURE__ */
|
|
5616
|
-
/* @__PURE__ */
|
|
6265
|
+
return /* @__PURE__ */ jsxs9("box", { flexDirection: "column", width: "100%", padding: 1, children: [
|
|
6266
|
+
/* @__PURE__ */ jsx9("text", { fg: t.status.error, children: "Destroying deployment..." }),
|
|
6267
|
+
/* @__PURE__ */ jsx9("text", { fg: t.fg.secondary, marginTop: 1, children: "Deleting server and cleaning up resources..." })
|
|
5617
6268
|
] });
|
|
5618
6269
|
}
|
|
5619
6270
|
if (viewState === "success") {
|
|
5620
|
-
return /* @__PURE__ */
|
|
5621
|
-
/* @__PURE__ */
|
|
6271
|
+
return /* @__PURE__ */ jsxs9("box", { flexDirection: "column", width: "100%", padding: 1, children: [
|
|
6272
|
+
/* @__PURE__ */ jsxs9(
|
|
5622
6273
|
"box",
|
|
5623
6274
|
{
|
|
5624
6275
|
flexDirection: "column",
|
|
@@ -5626,8 +6277,8 @@ function DestroyView({ context }) {
|
|
|
5626
6277
|
borderColor: t.status.success,
|
|
5627
6278
|
padding: 1,
|
|
5628
6279
|
children: [
|
|
5629
|
-
/* @__PURE__ */
|
|
5630
|
-
/* @__PURE__ */
|
|
6280
|
+
/* @__PURE__ */ jsx9("text", { fg: t.status.success, children: "Deployment Destroyed" }),
|
|
6281
|
+
/* @__PURE__ */ jsxs9("text", { fg: t.fg.primary, marginTop: 1, children: [
|
|
5631
6282
|
'The deployment "',
|
|
5632
6283
|
selectedDeployment.config.name,
|
|
5633
6284
|
'" has been permanently deleted.'
|
|
@@ -5635,12 +6286,12 @@ function DestroyView({ context }) {
|
|
|
5635
6286
|
]
|
|
5636
6287
|
}
|
|
5637
6288
|
),
|
|
5638
|
-
/* @__PURE__ */
|
|
6289
|
+
/* @__PURE__ */ jsx9("text", { fg: t.fg.muted, marginTop: 2, children: "Press any key to return to home" })
|
|
5639
6290
|
] });
|
|
5640
6291
|
}
|
|
5641
6292
|
if (viewState === "error") {
|
|
5642
|
-
return /* @__PURE__ */
|
|
5643
|
-
/* @__PURE__ */
|
|
6293
|
+
return /* @__PURE__ */ jsxs9("box", { flexDirection: "column", width: "100%", padding: 1, children: [
|
|
6294
|
+
/* @__PURE__ */ jsxs9(
|
|
5644
6295
|
"box",
|
|
5645
6296
|
{
|
|
5646
6297
|
flexDirection: "column",
|
|
@@ -5648,30 +6299,30 @@ function DestroyView({ context }) {
|
|
|
5648
6299
|
borderColor: t.status.error,
|
|
5649
6300
|
padding: 1,
|
|
5650
6301
|
children: [
|
|
5651
|
-
/* @__PURE__ */
|
|
5652
|
-
/* @__PURE__ */
|
|
6302
|
+
/* @__PURE__ */ jsx9("text", { fg: t.status.error, children: "Destruction Failed" }),
|
|
6303
|
+
/* @__PURE__ */ jsx9("text", { fg: t.fg.primary, marginTop: 1, children: error })
|
|
5653
6304
|
]
|
|
5654
6305
|
}
|
|
5655
6306
|
),
|
|
5656
|
-
/* @__PURE__ */
|
|
6307
|
+
/* @__PURE__ */ jsx9("text", { fg: t.fg.muted, marginTop: 2, children: "Press any key to go back" })
|
|
5657
6308
|
] });
|
|
5658
6309
|
}
|
|
5659
6310
|
return null;
|
|
5660
6311
|
}
|
|
5661
6312
|
|
|
5662
6313
|
// src/components/HelpView.tsx
|
|
5663
|
-
import { useKeyboard as
|
|
5664
|
-
import { jsx as
|
|
6314
|
+
import { useKeyboard as useKeyboard10 } from "@opentui/react";
|
|
6315
|
+
import { jsx as jsx10, jsxs as jsxs10 } from "@opentui/react/jsx-runtime";
|
|
5665
6316
|
function HelpView({ context }) {
|
|
5666
|
-
|
|
6317
|
+
useKeyboard10(() => {
|
|
5667
6318
|
context.navigateTo("home");
|
|
5668
6319
|
});
|
|
5669
|
-
return /* @__PURE__ */
|
|
5670
|
-
/* @__PURE__ */
|
|
5671
|
-
/* @__PURE__ */
|
|
5672
|
-
/* @__PURE__ */
|
|
6320
|
+
return /* @__PURE__ */ jsxs10("box", { flexDirection: "column", width: "100%", padding: 1, children: [
|
|
6321
|
+
/* @__PURE__ */ jsxs10("box", { flexDirection: "row", marginBottom: 2, children: [
|
|
6322
|
+
/* @__PURE__ */ jsx10("text", { fg: t.accent, children: "/help" }),
|
|
6323
|
+
/* @__PURE__ */ jsx10("text", { fg: t.fg.secondary, children: " - ClawControl Help" })
|
|
5673
6324
|
] }),
|
|
5674
|
-
/* @__PURE__ */
|
|
6325
|
+
/* @__PURE__ */ jsxs10(
|
|
5675
6326
|
"box",
|
|
5676
6327
|
{
|
|
5677
6328
|
flexDirection: "column",
|
|
@@ -5680,12 +6331,12 @@ function HelpView({ context }) {
|
|
|
5680
6331
|
padding: 1,
|
|
5681
6332
|
marginBottom: 1,
|
|
5682
6333
|
children: [
|
|
5683
|
-
/* @__PURE__ */
|
|
5684
|
-
/* @__PURE__ */
|
|
6334
|
+
/* @__PURE__ */ jsx10("text", { fg: t.accent, children: "What is ClawControl?" }),
|
|
6335
|
+
/* @__PURE__ */ jsx10("text", { fg: t.fg.primary, marginTop: 1, children: "ClawControl is a CLI tool that simplifies deploying OpenClaw instances to cloud providers. It handles all the complex setup including VPS provisioning, Node.js installation, OpenClaw configuration, and Tailscale VPN setup." })
|
|
5685
6336
|
]
|
|
5686
6337
|
}
|
|
5687
6338
|
),
|
|
5688
|
-
/* @__PURE__ */
|
|
6339
|
+
/* @__PURE__ */ jsxs10(
|
|
5689
6340
|
"box",
|
|
5690
6341
|
{
|
|
5691
6342
|
flexDirection: "column",
|
|
@@ -5694,48 +6345,48 @@ function HelpView({ context }) {
|
|
|
5694
6345
|
padding: 1,
|
|
5695
6346
|
marginBottom: 1,
|
|
5696
6347
|
children: [
|
|
5697
|
-
/* @__PURE__ */
|
|
5698
|
-
/* @__PURE__ */
|
|
5699
|
-
/* @__PURE__ */
|
|
5700
|
-
/* @__PURE__ */
|
|
5701
|
-
/* @__PURE__ */
|
|
6348
|
+
/* @__PURE__ */ jsx10("text", { fg: t.fg.primary, children: "Available Commands" }),
|
|
6349
|
+
/* @__PURE__ */ jsxs10("box", { flexDirection: "column", marginTop: 1, children: [
|
|
6350
|
+
/* @__PURE__ */ jsxs10("box", { flexDirection: "row", children: [
|
|
6351
|
+
/* @__PURE__ */ jsx10("text", { fg: t.accent, width: 12, children: "/new" }),
|
|
6352
|
+
/* @__PURE__ */ jsx10("text", { fg: t.fg.primary, children: "Initialize a new deployment configuration" })
|
|
5702
6353
|
] }),
|
|
5703
|
-
/* @__PURE__ */
|
|
5704
|
-
/* @__PURE__ */
|
|
5705
|
-
/* @__PURE__ */
|
|
5706
|
-
/* @__PURE__ */
|
|
6354
|
+
/* @__PURE__ */ jsx10("text", { fg: t.fg.secondary, children: " Creates deployment config in ~/.clawcontrol/deployments/" }),
|
|
6355
|
+
/* @__PURE__ */ jsxs10("box", { flexDirection: "row", marginTop: 1, children: [
|
|
6356
|
+
/* @__PURE__ */ jsx10("text", { fg: t.accent, width: 12, children: "/deploy" }),
|
|
6357
|
+
/* @__PURE__ */ jsx10("text", { fg: t.fg.primary, children: "Deploy a configured instance to the cloud" })
|
|
5707
6358
|
] }),
|
|
5708
|
-
/* @__PURE__ */
|
|
5709
|
-
/* @__PURE__ */
|
|
5710
|
-
/* @__PURE__ */
|
|
5711
|
-
/* @__PURE__ */
|
|
6359
|
+
/* @__PURE__ */ jsx10("text", { fg: t.fg.secondary, children: " Provisions VPS, installs dependencies, configures OpenClaw" }),
|
|
6360
|
+
/* @__PURE__ */ jsxs10("box", { flexDirection: "row", marginTop: 1, children: [
|
|
6361
|
+
/* @__PURE__ */ jsx10("text", { fg: t.accent, width: 12, children: "/status" }),
|
|
6362
|
+
/* @__PURE__ */ jsx10("text", { fg: t.fg.primary, children: "View status of all deployments" })
|
|
5712
6363
|
] }),
|
|
5713
|
-
/* @__PURE__ */
|
|
5714
|
-
/* @__PURE__ */
|
|
5715
|
-
/* @__PURE__ */
|
|
5716
|
-
/* @__PURE__ */
|
|
6364
|
+
/* @__PURE__ */ jsx10("text", { fg: t.fg.secondary, children: " Shows deployment state, health checks, and connection info" }),
|
|
6365
|
+
/* @__PURE__ */ jsxs10("box", { flexDirection: "row", marginTop: 1, children: [
|
|
6366
|
+
/* @__PURE__ */ jsx10("text", { fg: t.accent, width: 12, children: "/ssh" }),
|
|
6367
|
+
/* @__PURE__ */ jsx10("text", { fg: t.fg.primary, children: "SSH into a deployed instance" })
|
|
5717
6368
|
] }),
|
|
5718
|
-
/* @__PURE__ */
|
|
5719
|
-
/* @__PURE__ */
|
|
5720
|
-
/* @__PURE__ */
|
|
5721
|
-
/* @__PURE__ */
|
|
6369
|
+
/* @__PURE__ */ jsx10("text", { fg: t.fg.secondary, children: " Opens interactive SSH session to your server" }),
|
|
6370
|
+
/* @__PURE__ */ jsxs10("box", { flexDirection: "row", marginTop: 1, children: [
|
|
6371
|
+
/* @__PURE__ */ jsx10("text", { fg: t.accent, width: 12, children: "/logs" }),
|
|
6372
|
+
/* @__PURE__ */ jsx10("text", { fg: t.fg.primary, children: "View OpenClaw logs from a deployment" })
|
|
5722
6373
|
] }),
|
|
5723
|
-
/* @__PURE__ */
|
|
5724
|
-
/* @__PURE__ */
|
|
5725
|
-
/* @__PURE__ */
|
|
5726
|
-
/* @__PURE__ */
|
|
6374
|
+
/* @__PURE__ */ jsx10("text", { fg: t.fg.secondary, children: " Streams logs from journalctl with auto-refresh option" }),
|
|
6375
|
+
/* @__PURE__ */ jsxs10("box", { flexDirection: "row", marginTop: 1, children: [
|
|
6376
|
+
/* @__PURE__ */ jsx10("text", { fg: t.status.error, width: 12, children: "/destroy" }),
|
|
6377
|
+
/* @__PURE__ */ jsx10("text", { fg: t.fg.primary, children: "Permanently delete a deployment" })
|
|
5727
6378
|
] }),
|
|
5728
|
-
/* @__PURE__ */
|
|
5729
|
-
/* @__PURE__ */
|
|
5730
|
-
/* @__PURE__ */
|
|
5731
|
-
/* @__PURE__ */
|
|
6379
|
+
/* @__PURE__ */ jsx10("text", { fg: t.fg.secondary, children: " Deletes VPS, SSH keys, and local configuration" }),
|
|
6380
|
+
/* @__PURE__ */ jsxs10("box", { flexDirection: "row", marginTop: 1, children: [
|
|
6381
|
+
/* @__PURE__ */ jsx10("text", { fg: t.accent, width: 12, children: "/templates" }),
|
|
6382
|
+
/* @__PURE__ */ jsx10("text", { fg: t.fg.primary, children: "Manage deployment templates" })
|
|
5732
6383
|
] }),
|
|
5733
|
-
/* @__PURE__ */
|
|
6384
|
+
/* @__PURE__ */ jsx10("text", { fg: t.fg.secondary, children: " View, fork, and use reusable deployment presets" })
|
|
5734
6385
|
] })
|
|
5735
6386
|
]
|
|
5736
6387
|
}
|
|
5737
6388
|
),
|
|
5738
|
-
/* @__PURE__ */
|
|
6389
|
+
/* @__PURE__ */ jsxs10(
|
|
5739
6390
|
"box",
|
|
5740
6391
|
{
|
|
5741
6392
|
flexDirection: "column",
|
|
@@ -5744,17 +6395,17 @@ function HelpView({ context }) {
|
|
|
5744
6395
|
padding: 1,
|
|
5745
6396
|
marginBottom: 1,
|
|
5746
6397
|
children: [
|
|
5747
|
-
/* @__PURE__ */
|
|
5748
|
-
/* @__PURE__ */
|
|
5749
|
-
/* @__PURE__ */
|
|
5750
|
-
/* @__PURE__ */
|
|
5751
|
-
/* @__PURE__ */
|
|
5752
|
-
/* @__PURE__ */
|
|
5753
|
-
/* @__PURE__ */
|
|
6398
|
+
/* @__PURE__ */ jsx10("text", { fg: t.status.success, children: "Typical Workflow" }),
|
|
6399
|
+
/* @__PURE__ */ jsx10("text", { fg: t.fg.primary, marginTop: 1, children: "1. Run /templates to browse or create reusable presets" }),
|
|
6400
|
+
/* @__PURE__ */ jsx10("text", { fg: t.fg.primary, children: "2. Run /new to create a deployment config (optionally from a template)" }),
|
|
6401
|
+
/* @__PURE__ */ jsx10("text", { fg: t.fg.primary, children: "3. Run /deploy to deploy to the cloud" }),
|
|
6402
|
+
/* @__PURE__ */ jsx10("text", { fg: t.fg.primary, children: "4. Authenticate Tailscale when prompted" }),
|
|
6403
|
+
/* @__PURE__ */ jsx10("text", { fg: t.fg.primary, children: "5. Complete OpenClaw onboarding via SSH" }),
|
|
6404
|
+
/* @__PURE__ */ jsx10("text", { fg: t.fg.primary, children: "6. Use /status and /logs to monitor" })
|
|
5754
6405
|
]
|
|
5755
6406
|
}
|
|
5756
6407
|
),
|
|
5757
|
-
/* @__PURE__ */
|
|
6408
|
+
/* @__PURE__ */ jsxs10(
|
|
5758
6409
|
"box",
|
|
5759
6410
|
{
|
|
5760
6411
|
flexDirection: "column",
|
|
@@ -5763,36 +6414,36 @@ function HelpView({ context }) {
|
|
|
5763
6414
|
padding: 1,
|
|
5764
6415
|
marginBottom: 1,
|
|
5765
6416
|
children: [
|
|
5766
|
-
/* @__PURE__ */
|
|
5767
|
-
/* @__PURE__ */
|
|
5768
|
-
/* @__PURE__ */
|
|
5769
|
-
/* @__PURE__ */
|
|
6417
|
+
/* @__PURE__ */ jsx10("text", { fg: t.fg.primary, children: "Supported Cloud Providers" }),
|
|
6418
|
+
/* @__PURE__ */ jsxs10("box", { flexDirection: "row", marginTop: 1, children: [
|
|
6419
|
+
/* @__PURE__ */ jsx10("text", { fg: t.status.success, children: "\u2713 " }),
|
|
6420
|
+
/* @__PURE__ */ jsx10("text", { fg: t.fg.primary, children: "Hetzner Cloud - ~$4.99/mo for CPX11 (US East)" })
|
|
5770
6421
|
] }),
|
|
5771
|
-
/* @__PURE__ */
|
|
5772
|
-
/* @__PURE__ */
|
|
5773
|
-
/* @__PURE__ */
|
|
6422
|
+
/* @__PURE__ */ jsxs10("box", { flexDirection: "row", children: [
|
|
6423
|
+
/* @__PURE__ */ jsx10("text", { fg: t.status.success, children: "\u2713 " }),
|
|
6424
|
+
/* @__PURE__ */ jsx10("text", { fg: t.fg.primary, children: "DigitalOcean - Starting at $12/mo (NYC1)" })
|
|
5774
6425
|
] }),
|
|
5775
|
-
/* @__PURE__ */
|
|
5776
|
-
/* @__PURE__ */
|
|
5777
|
-
/* @__PURE__ */
|
|
6426
|
+
/* @__PURE__ */ jsxs10("box", { flexDirection: "row", children: [
|
|
6427
|
+
/* @__PURE__ */ jsx10("text", { fg: t.fg.muted, children: "\u25CB " }),
|
|
6428
|
+
/* @__PURE__ */ jsx10("text", { fg: t.fg.secondary, children: "Vultr - Coming soon" })
|
|
5778
6429
|
] })
|
|
5779
6430
|
]
|
|
5780
6431
|
}
|
|
5781
6432
|
),
|
|
5782
|
-
/* @__PURE__ */
|
|
5783
|
-
/* @__PURE__ */
|
|
5784
|
-
/* @__PURE__ */
|
|
5785
|
-
/* @__PURE__ */
|
|
5786
|
-
/* @__PURE__ */
|
|
6433
|
+
/* @__PURE__ */ jsxs10("box", { flexDirection: "column", marginBottom: 1, children: [
|
|
6434
|
+
/* @__PURE__ */ jsx10("text", { fg: t.fg.primary, children: "Useful Links" }),
|
|
6435
|
+
/* @__PURE__ */ jsx10("text", { fg: t.fg.secondary, children: "\u2022 OpenClaw Docs: https://docs.openclaw.ai/" }),
|
|
6436
|
+
/* @__PURE__ */ jsx10("text", { fg: t.fg.secondary, children: "\u2022 Hetzner API: https://docs.hetzner.cloud/" }),
|
|
6437
|
+
/* @__PURE__ */ jsx10("text", { fg: t.fg.secondary, children: "\u2022 Tailscale: https://tailscale.com/" })
|
|
5787
6438
|
] }),
|
|
5788
|
-
/* @__PURE__ */
|
|
6439
|
+
/* @__PURE__ */ jsx10("text", { fg: t.fg.muted, children: "Press any key to return to home" })
|
|
5789
6440
|
] });
|
|
5790
6441
|
}
|
|
5791
6442
|
|
|
5792
6443
|
// src/components/TemplatesView.tsx
|
|
5793
|
-
import { useState as
|
|
5794
|
-
import { useKeyboard as
|
|
5795
|
-
import { Fragment as
|
|
6444
|
+
import { useState as useState10, useRef as useRef6, useEffect as useEffect4 } from "react";
|
|
6445
|
+
import { useKeyboard as useKeyboard11 } from "@opentui/react";
|
|
6446
|
+
import { Fragment as Fragment3, jsx as jsx11, jsxs as jsxs11 } from "@opentui/react/jsx-runtime";
|
|
5796
6447
|
var DO_DROPLET_SIZES2 = [
|
|
5797
6448
|
{ slug: "s-1vcpu-2gb", label: "1 vCPU, 2GB RAM, 50GB SSD", price: "$12/mo" },
|
|
5798
6449
|
{ slug: "s-2vcpu-2gb", label: "2 vCPU, 2GB RAM, 60GB SSD", price: "$18/mo" },
|
|
@@ -5801,20 +6452,20 @@ var DO_DROPLET_SIZES2 = [
|
|
|
5801
6452
|
{ slug: "s-8vcpu-16gb", label: "8 vCPU, 16GB RAM, 320GB SSD", price: "$96/mo" }
|
|
5802
6453
|
];
|
|
5803
6454
|
function TemplatesView({ context }) {
|
|
5804
|
-
const [viewState, setViewState] =
|
|
5805
|
-
const [templates, setTemplates] =
|
|
5806
|
-
const [selectedIndex, setSelectedIndex] =
|
|
5807
|
-
const [selectedTemplate, setSelectedTemplate] =
|
|
5808
|
-
const [error, setError] =
|
|
5809
|
-
const [forkStep, setForkStep] =
|
|
5810
|
-
const [forkName, setForkName] =
|
|
5811
|
-
const [forkProvider, setForkProvider] =
|
|
5812
|
-
const [forkProviderIndex, setForkProviderIndex] =
|
|
5813
|
-
const [forkDropletSizeIndex, setForkDropletSizeIndex] =
|
|
5814
|
-
const [forkAiProvider, setForkAiProvider] =
|
|
5815
|
-
const [forkAiProviderIndex, setForkAiProviderIndex] =
|
|
5816
|
-
const [forkModel, setForkModel] =
|
|
5817
|
-
const stateRef =
|
|
6455
|
+
const [viewState, setViewState] = useState10("listing");
|
|
6456
|
+
const [templates, setTemplates] = useState10([]);
|
|
6457
|
+
const [selectedIndex, setSelectedIndex] = useState10(0);
|
|
6458
|
+
const [selectedTemplate, setSelectedTemplate] = useState10(null);
|
|
6459
|
+
const [error, setError] = useState10(null);
|
|
6460
|
+
const [forkStep, setForkStep] = useState10("fork_name");
|
|
6461
|
+
const [forkName, setForkName] = useState10("");
|
|
6462
|
+
const [forkProvider, setForkProvider] = useState10("hetzner");
|
|
6463
|
+
const [forkProviderIndex, setForkProviderIndex] = useState10(0);
|
|
6464
|
+
const [forkDropletSizeIndex, setForkDropletSizeIndex] = useState10(0);
|
|
6465
|
+
const [forkAiProvider, setForkAiProvider] = useState10("");
|
|
6466
|
+
const [forkAiProviderIndex, setForkAiProviderIndex] = useState10(0);
|
|
6467
|
+
const [forkModel, setForkModel] = useState10("");
|
|
6468
|
+
const stateRef = useRef6({
|
|
5818
6469
|
viewState,
|
|
5819
6470
|
selectedIndex,
|
|
5820
6471
|
selectedTemplate,
|
|
@@ -5928,7 +6579,7 @@ function TemplatesView({ context }) {
|
|
|
5928
6579
|
setViewState("viewing");
|
|
5929
6580
|
}
|
|
5930
6581
|
};
|
|
5931
|
-
|
|
6582
|
+
useKeyboard11((key) => {
|
|
5932
6583
|
const s = stateRef.current;
|
|
5933
6584
|
if (s.viewState === "listing") {
|
|
5934
6585
|
if (key.name === "up") {
|
|
@@ -6020,37 +6671,37 @@ function TemplatesView({ context }) {
|
|
|
6020
6671
|
}
|
|
6021
6672
|
}
|
|
6022
6673
|
});
|
|
6023
|
-
const renderListing = () => /* @__PURE__ */
|
|
6024
|
-
/* @__PURE__ */
|
|
6025
|
-
/* @__PURE__ */
|
|
6674
|
+
const renderListing = () => /* @__PURE__ */ jsxs11("box", { flexDirection: "column", children: [
|
|
6675
|
+
/* @__PURE__ */ jsx11("text", { fg: t.fg.secondary, marginBottom: 1, children: "Select a template to view details. Use arrow keys and Enter." }),
|
|
6676
|
+
/* @__PURE__ */ jsx11(
|
|
6026
6677
|
"box",
|
|
6027
6678
|
{
|
|
6028
6679
|
flexDirection: "column",
|
|
6029
6680
|
borderStyle: "single",
|
|
6030
6681
|
borderColor: t.border.default,
|
|
6031
6682
|
padding: 1,
|
|
6032
|
-
children: templates.length === 0 ? /* @__PURE__ */
|
|
6683
|
+
children: templates.length === 0 ? /* @__PURE__ */ jsx11("text", { fg: t.fg.muted, children: "No templates found." }) : templates.map((tmpl, i) => {
|
|
6033
6684
|
const isSelected = i === selectedIndex;
|
|
6034
6685
|
const badge = tmpl.builtIn ? "[built-in]" : "[custom]";
|
|
6035
6686
|
const badgeColor = tmpl.builtIn ? t.status.info : t.status.success;
|
|
6036
|
-
return /* @__PURE__ */
|
|
6037
|
-
/* @__PURE__ */
|
|
6038
|
-
/* @__PURE__ */
|
|
6039
|
-
/* @__PURE__ */
|
|
6040
|
-
/* @__PURE__ */
|
|
6687
|
+
return /* @__PURE__ */ jsxs11("box", { flexDirection: "row", backgroundColor: isSelected ? t.selection.bg : void 0, children: [
|
|
6688
|
+
/* @__PURE__ */ jsx11("text", { fg: isSelected ? t.selection.indicator : t.fg.primary, children: isSelected ? "> " : " " }),
|
|
6689
|
+
/* @__PURE__ */ jsx11("text", { fg: isSelected ? t.selection.fg : t.fg.primary, children: tmpl.name }),
|
|
6690
|
+
/* @__PURE__ */ jsx11("text", { fg: badgeColor, children: " " + badge }),
|
|
6691
|
+
/* @__PURE__ */ jsx11("text", { fg: t.fg.muted, children: " \xB7 " + tmpl.channel })
|
|
6041
6692
|
] }, tmpl.id);
|
|
6042
6693
|
})
|
|
6043
6694
|
}
|
|
6044
6695
|
),
|
|
6045
|
-
/* @__PURE__ */
|
|
6696
|
+
/* @__PURE__ */ jsx11("text", { fg: t.fg.muted, marginTop: 1, children: "Enter to view, Esc to go back" })
|
|
6046
6697
|
] });
|
|
6047
6698
|
const renderViewing = () => {
|
|
6048
6699
|
if (!selectedTemplate) return null;
|
|
6049
6700
|
const tmpl = selectedTemplate;
|
|
6050
|
-
return /* @__PURE__ */
|
|
6051
|
-
/* @__PURE__ */
|
|
6052
|
-
/* @__PURE__ */
|
|
6053
|
-
/* @__PURE__ */
|
|
6701
|
+
return /* @__PURE__ */ jsxs11("box", { flexDirection: "column", children: [
|
|
6702
|
+
/* @__PURE__ */ jsx11("text", { fg: t.accent, marginBottom: 1, children: tmpl.name }),
|
|
6703
|
+
/* @__PURE__ */ jsx11("text", { fg: t.fg.secondary, marginBottom: 1, children: tmpl.description }),
|
|
6704
|
+
/* @__PURE__ */ jsxs11(
|
|
6054
6705
|
"box",
|
|
6055
6706
|
{
|
|
6056
6707
|
flexDirection: "column",
|
|
@@ -6059,89 +6710,89 @@ function TemplatesView({ context }) {
|
|
|
6059
6710
|
padding: 1,
|
|
6060
6711
|
marginBottom: 1,
|
|
6061
6712
|
children: [
|
|
6062
|
-
/* @__PURE__ */
|
|
6063
|
-
/* @__PURE__ */
|
|
6064
|
-
/* @__PURE__ */
|
|
6713
|
+
/* @__PURE__ */ jsxs11("box", { flexDirection: "row", children: [
|
|
6714
|
+
/* @__PURE__ */ jsx11("text", { fg: t.fg.secondary, width: 20, children: "Type:" }),
|
|
6715
|
+
/* @__PURE__ */ jsx11("text", { fg: tmpl.builtIn ? t.status.info : t.status.success, children: tmpl.builtIn ? "Built-in" : "Custom" })
|
|
6065
6716
|
] }),
|
|
6066
|
-
/* @__PURE__ */
|
|
6067
|
-
/* @__PURE__ */
|
|
6068
|
-
/* @__PURE__ */
|
|
6717
|
+
/* @__PURE__ */ jsxs11("box", { flexDirection: "row", children: [
|
|
6718
|
+
/* @__PURE__ */ jsx11("text", { fg: t.fg.secondary, width: 20, children: "Provider:" }),
|
|
6719
|
+
/* @__PURE__ */ jsx11("text", { fg: t.fg.primary, children: PROVIDER_NAMES[tmpl.provider] })
|
|
6069
6720
|
] }),
|
|
6070
|
-
tmpl.hetzner && /* @__PURE__ */
|
|
6071
|
-
/* @__PURE__ */
|
|
6072
|
-
/* @__PURE__ */
|
|
6073
|
-
/* @__PURE__ */
|
|
6721
|
+
tmpl.hetzner && /* @__PURE__ */ jsxs11(Fragment3, { children: [
|
|
6722
|
+
/* @__PURE__ */ jsxs11("box", { flexDirection: "row", children: [
|
|
6723
|
+
/* @__PURE__ */ jsx11("text", { fg: t.fg.secondary, width: 20, children: "Server Type:" }),
|
|
6724
|
+
/* @__PURE__ */ jsx11("text", { fg: t.fg.primary, children: tmpl.hetzner.serverType })
|
|
6074
6725
|
] }),
|
|
6075
|
-
/* @__PURE__ */
|
|
6076
|
-
/* @__PURE__ */
|
|
6077
|
-
/* @__PURE__ */
|
|
6726
|
+
/* @__PURE__ */ jsxs11("box", { flexDirection: "row", children: [
|
|
6727
|
+
/* @__PURE__ */ jsx11("text", { fg: t.fg.secondary, width: 20, children: "Location:" }),
|
|
6728
|
+
/* @__PURE__ */ jsx11("text", { fg: t.fg.primary, children: tmpl.hetzner.location })
|
|
6078
6729
|
] })
|
|
6079
6730
|
] }),
|
|
6080
|
-
tmpl.digitalocean && /* @__PURE__ */
|
|
6081
|
-
/* @__PURE__ */
|
|
6082
|
-
/* @__PURE__ */
|
|
6083
|
-
/* @__PURE__ */
|
|
6731
|
+
tmpl.digitalocean && /* @__PURE__ */ jsxs11(Fragment3, { children: [
|
|
6732
|
+
/* @__PURE__ */ jsxs11("box", { flexDirection: "row", children: [
|
|
6733
|
+
/* @__PURE__ */ jsx11("text", { fg: t.fg.secondary, width: 20, children: "Droplet Size:" }),
|
|
6734
|
+
/* @__PURE__ */ jsx11("text", { fg: t.fg.primary, children: tmpl.digitalocean.size })
|
|
6084
6735
|
] }),
|
|
6085
|
-
/* @__PURE__ */
|
|
6086
|
-
/* @__PURE__ */
|
|
6087
|
-
/* @__PURE__ */
|
|
6736
|
+
/* @__PURE__ */ jsxs11("box", { flexDirection: "row", children: [
|
|
6737
|
+
/* @__PURE__ */ jsx11("text", { fg: t.fg.secondary, width: 20, children: "Region:" }),
|
|
6738
|
+
/* @__PURE__ */ jsx11("text", { fg: t.fg.primary, children: tmpl.digitalocean.region })
|
|
6088
6739
|
] })
|
|
6089
6740
|
] }),
|
|
6090
|
-
/* @__PURE__ */
|
|
6091
|
-
/* @__PURE__ */
|
|
6092
|
-
/* @__PURE__ */
|
|
6741
|
+
/* @__PURE__ */ jsxs11("box", { flexDirection: "row", children: [
|
|
6742
|
+
/* @__PURE__ */ jsx11("text", { fg: t.fg.secondary, width: 20, children: "AI Provider:" }),
|
|
6743
|
+
/* @__PURE__ */ jsx11("text", { fg: t.fg.primary, children: tmpl.aiProvider })
|
|
6093
6744
|
] }),
|
|
6094
|
-
/* @__PURE__ */
|
|
6095
|
-
/* @__PURE__ */
|
|
6096
|
-
/* @__PURE__ */
|
|
6745
|
+
/* @__PURE__ */ jsxs11("box", { flexDirection: "row", children: [
|
|
6746
|
+
/* @__PURE__ */ jsx11("text", { fg: t.fg.secondary, width: 20, children: "Model:" }),
|
|
6747
|
+
/* @__PURE__ */ jsx11("text", { fg: t.fg.primary, children: tmpl.model })
|
|
6097
6748
|
] }),
|
|
6098
|
-
/* @__PURE__ */
|
|
6099
|
-
/* @__PURE__ */
|
|
6100
|
-
/* @__PURE__ */
|
|
6749
|
+
/* @__PURE__ */ jsxs11("box", { flexDirection: "row", children: [
|
|
6750
|
+
/* @__PURE__ */ jsx11("text", { fg: t.fg.secondary, width: 20, children: "Channel:" }),
|
|
6751
|
+
/* @__PURE__ */ jsx11("text", { fg: t.fg.primary, children: tmpl.channel })
|
|
6101
6752
|
] })
|
|
6102
6753
|
]
|
|
6103
6754
|
}
|
|
6104
6755
|
),
|
|
6105
|
-
/* @__PURE__ */
|
|
6106
|
-
/* @__PURE__ */
|
|
6107
|
-
/* @__PURE__ */
|
|
6108
|
-
/* @__PURE__ */
|
|
6109
|
-
/* @__PURE__ */
|
|
6110
|
-
!tmpl.builtIn && /* @__PURE__ */
|
|
6111
|
-
/* @__PURE__ */
|
|
6112
|
-
/* @__PURE__ */
|
|
6756
|
+
/* @__PURE__ */ jsxs11("box", { flexDirection: "row", children: [
|
|
6757
|
+
/* @__PURE__ */ jsx11("text", { fg: t.accent, children: "[F]" }),
|
|
6758
|
+
/* @__PURE__ */ jsx11("text", { fg: t.fg.primary, children: "ork " }),
|
|
6759
|
+
/* @__PURE__ */ jsx11("text", { fg: t.accent, children: "[U]" }),
|
|
6760
|
+
/* @__PURE__ */ jsx11("text", { fg: t.fg.primary, children: "se " }),
|
|
6761
|
+
!tmpl.builtIn && /* @__PURE__ */ jsxs11(Fragment3, { children: [
|
|
6762
|
+
/* @__PURE__ */ jsx11("text", { fg: t.status.error, children: "[D]" }),
|
|
6763
|
+
/* @__PURE__ */ jsx11("text", { fg: t.fg.primary, children: "elete " })
|
|
6113
6764
|
] }),
|
|
6114
|
-
/* @__PURE__ */
|
|
6765
|
+
/* @__PURE__ */ jsx11("text", { fg: t.fg.muted, children: "[Esc] Back" })
|
|
6115
6766
|
] }),
|
|
6116
|
-
error && /* @__PURE__ */
|
|
6767
|
+
error && /* @__PURE__ */ jsx11("text", { fg: t.status.error, marginTop: 1, children: error })
|
|
6117
6768
|
] });
|
|
6118
6769
|
};
|
|
6119
|
-
const renderDeleteConfirm = () => /* @__PURE__ */
|
|
6120
|
-
/* @__PURE__ */
|
|
6121
|
-
/* @__PURE__ */
|
|
6770
|
+
const renderDeleteConfirm = () => /* @__PURE__ */ jsxs11("box", { flexDirection: "column", children: [
|
|
6771
|
+
/* @__PURE__ */ jsx11("text", { fg: t.status.error, children: "Delete Template" }),
|
|
6772
|
+
/* @__PURE__ */ jsxs11("text", { fg: t.fg.primary, marginTop: 1, children: [
|
|
6122
6773
|
'Are you sure you want to delete "',
|
|
6123
6774
|
selectedTemplate?.name,
|
|
6124
6775
|
'"?'
|
|
6125
6776
|
] }),
|
|
6126
|
-
/* @__PURE__ */
|
|
6127
|
-
/* @__PURE__ */
|
|
6777
|
+
/* @__PURE__ */ jsx11("text", { fg: t.status.warning, marginTop: 1, children: "This action cannot be undone." }),
|
|
6778
|
+
/* @__PURE__ */ jsx11("text", { fg: t.fg.muted, marginTop: 2, children: "Press Y to confirm, N to cancel" })
|
|
6128
6779
|
] });
|
|
6129
|
-
const renderForkComplete = () => /* @__PURE__ */
|
|
6130
|
-
/* @__PURE__ */
|
|
6131
|
-
/* @__PURE__ */
|
|
6780
|
+
const renderForkComplete = () => /* @__PURE__ */ jsxs11("box", { flexDirection: "column", children: [
|
|
6781
|
+
/* @__PURE__ */ jsx11("text", { fg: t.status.success, children: "Template Created!" }),
|
|
6782
|
+
/* @__PURE__ */ jsxs11("text", { fg: t.fg.primary, marginTop: 1, children: [
|
|
6132
6783
|
'Your template "',
|
|
6133
6784
|
forkName,
|
|
6134
6785
|
'" has been saved.'
|
|
6135
6786
|
] }),
|
|
6136
|
-
/* @__PURE__ */
|
|
6787
|
+
/* @__PURE__ */ jsx11("text", { fg: t.fg.muted, marginTop: 2, children: "Press any key to return to templates list" })
|
|
6137
6788
|
] });
|
|
6138
6789
|
const renderForking = () => {
|
|
6139
6790
|
switch (forkStep) {
|
|
6140
6791
|
case "fork_name":
|
|
6141
|
-
return /* @__PURE__ */
|
|
6142
|
-
/* @__PURE__ */
|
|
6143
|
-
/* @__PURE__ */
|
|
6144
|
-
/* @__PURE__ */
|
|
6792
|
+
return /* @__PURE__ */ jsxs11("box", { flexDirection: "column", children: [
|
|
6793
|
+
/* @__PURE__ */ jsx11("text", { fg: t.accent, children: "Fork Template - Name" }),
|
|
6794
|
+
/* @__PURE__ */ jsx11("text", { fg: t.fg.secondary, marginTop: 1, children: "Enter a name for the new template:" }),
|
|
6795
|
+
/* @__PURE__ */ jsx11(
|
|
6145
6796
|
"input",
|
|
6146
6797
|
{
|
|
6147
6798
|
value: forkName,
|
|
@@ -6164,14 +6815,14 @@ function TemplatesView({ context }) {
|
|
|
6164
6815
|
}
|
|
6165
6816
|
}
|
|
6166
6817
|
),
|
|
6167
|
-
error && /* @__PURE__ */
|
|
6168
|
-
/* @__PURE__ */
|
|
6818
|
+
error && /* @__PURE__ */ jsx11("text", { fg: t.status.error, marginTop: 1, children: error }),
|
|
6819
|
+
/* @__PURE__ */ jsx11("text", { fg: t.fg.muted, marginTop: 2, children: "Press Enter to continue, Esc to go back" })
|
|
6169
6820
|
] });
|
|
6170
6821
|
case "fork_provider":
|
|
6171
|
-
return /* @__PURE__ */
|
|
6172
|
-
/* @__PURE__ */
|
|
6173
|
-
/* @__PURE__ */
|
|
6174
|
-
/* @__PURE__ */
|
|
6822
|
+
return /* @__PURE__ */ jsxs11("box", { flexDirection: "column", children: [
|
|
6823
|
+
/* @__PURE__ */ jsx11("text", { fg: t.accent, children: "Fork Template - Provider" }),
|
|
6824
|
+
/* @__PURE__ */ jsx11("text", { fg: t.fg.secondary, marginTop: 1, children: "Select cloud provider:" }),
|
|
6825
|
+
/* @__PURE__ */ jsx11(
|
|
6175
6826
|
"box",
|
|
6176
6827
|
{
|
|
6177
6828
|
flexDirection: "column",
|
|
@@ -6181,20 +6832,20 @@ function TemplatesView({ context }) {
|
|
|
6181
6832
|
padding: 1,
|
|
6182
6833
|
children: SUPPORTED_PROVIDERS.map((p, i) => {
|
|
6183
6834
|
const isSelected = i === forkProviderIndex;
|
|
6184
|
-
return /* @__PURE__ */
|
|
6185
|
-
/* @__PURE__ */
|
|
6186
|
-
/* @__PURE__ */
|
|
6835
|
+
return /* @__PURE__ */ jsxs11("box", { flexDirection: "row", backgroundColor: isSelected ? t.selection.bg : void 0, children: [
|
|
6836
|
+
/* @__PURE__ */ jsx11("text", { fg: isSelected ? t.selection.indicator : t.fg.primary, children: isSelected ? "> " : " " }),
|
|
6837
|
+
/* @__PURE__ */ jsx11("text", { fg: isSelected ? t.selection.fg : t.fg.primary, children: PROVIDER_NAMES[p] })
|
|
6187
6838
|
] }, p);
|
|
6188
6839
|
})
|
|
6189
6840
|
}
|
|
6190
6841
|
),
|
|
6191
|
-
/* @__PURE__ */
|
|
6842
|
+
/* @__PURE__ */ jsx11("text", { fg: t.fg.muted, marginTop: 1, children: "Enter to select, Esc to go back" })
|
|
6192
6843
|
] });
|
|
6193
6844
|
case "fork_droplet_size":
|
|
6194
|
-
return /* @__PURE__ */
|
|
6195
|
-
/* @__PURE__ */
|
|
6196
|
-
/* @__PURE__ */
|
|
6197
|
-
/* @__PURE__ */
|
|
6845
|
+
return /* @__PURE__ */ jsxs11("box", { flexDirection: "column", children: [
|
|
6846
|
+
/* @__PURE__ */ jsx11("text", { fg: t.accent, children: "Fork Template - Droplet Size" }),
|
|
6847
|
+
/* @__PURE__ */ jsx11("text", { fg: t.fg.secondary, marginTop: 1, children: "Select DigitalOcean droplet size:" }),
|
|
6848
|
+
/* @__PURE__ */ jsx11(
|
|
6198
6849
|
"box",
|
|
6199
6850
|
{
|
|
6200
6851
|
flexDirection: "column",
|
|
@@ -6204,21 +6855,21 @@ function TemplatesView({ context }) {
|
|
|
6204
6855
|
padding: 1,
|
|
6205
6856
|
children: DO_DROPLET_SIZES2.map((size, i) => {
|
|
6206
6857
|
const isSelected = i === forkDropletSizeIndex;
|
|
6207
|
-
return /* @__PURE__ */
|
|
6208
|
-
/* @__PURE__ */
|
|
6209
|
-
/* @__PURE__ */
|
|
6210
|
-
/* @__PURE__ */
|
|
6858
|
+
return /* @__PURE__ */ jsxs11("box", { flexDirection: "row", backgroundColor: isSelected ? t.selection.bg : void 0, children: [
|
|
6859
|
+
/* @__PURE__ */ jsx11("text", { fg: isSelected ? t.selection.indicator : t.fg.primary, children: isSelected ? "> " : " " }),
|
|
6860
|
+
/* @__PURE__ */ jsx11("text", { fg: isSelected ? t.selection.fg : t.fg.primary, children: size.slug }),
|
|
6861
|
+
/* @__PURE__ */ jsx11("text", { fg: isSelected ? t.fg.primary : t.fg.secondary, children: " - " + size.label + " - " + size.price })
|
|
6211
6862
|
] }, size.slug);
|
|
6212
6863
|
})
|
|
6213
6864
|
}
|
|
6214
6865
|
),
|
|
6215
|
-
/* @__PURE__ */
|
|
6866
|
+
/* @__PURE__ */ jsx11("text", { fg: t.fg.muted, marginTop: 1, children: "Enter to select, Esc to go back" })
|
|
6216
6867
|
] });
|
|
6217
6868
|
case "fork_ai_provider":
|
|
6218
|
-
return /* @__PURE__ */
|
|
6219
|
-
/* @__PURE__ */
|
|
6220
|
-
/* @__PURE__ */
|
|
6221
|
-
/* @__PURE__ */
|
|
6869
|
+
return /* @__PURE__ */ jsxs11("box", { flexDirection: "column", children: [
|
|
6870
|
+
/* @__PURE__ */ jsx11("text", { fg: t.accent, children: "Fork Template - AI Provider" }),
|
|
6871
|
+
/* @__PURE__ */ jsx11("text", { fg: t.fg.secondary, marginTop: 1, children: "Select AI provider:" }),
|
|
6872
|
+
/* @__PURE__ */ jsx11(
|
|
6222
6873
|
"box",
|
|
6223
6874
|
{
|
|
6224
6875
|
flexDirection: "column",
|
|
@@ -6228,21 +6879,21 @@ function TemplatesView({ context }) {
|
|
|
6228
6879
|
padding: 1,
|
|
6229
6880
|
children: AI_PROVIDERS.map((p, i) => {
|
|
6230
6881
|
const isSelected = i === forkAiProviderIndex;
|
|
6231
|
-
return /* @__PURE__ */
|
|
6232
|
-
/* @__PURE__ */
|
|
6233
|
-
/* @__PURE__ */
|
|
6234
|
-
/* @__PURE__ */
|
|
6882
|
+
return /* @__PURE__ */ jsxs11("box", { flexDirection: "row", backgroundColor: isSelected ? t.selection.bg : void 0, children: [
|
|
6883
|
+
/* @__PURE__ */ jsx11("text", { fg: isSelected ? t.selection.indicator : t.fg.primary, children: isSelected ? "> " : " " }),
|
|
6884
|
+
/* @__PURE__ */ jsx11("text", { fg: isSelected ? t.selection.fg : t.fg.primary, children: p.label }),
|
|
6885
|
+
/* @__PURE__ */ jsx11("text", { fg: isSelected ? t.fg.primary : t.fg.secondary, children: " - " + p.description })
|
|
6235
6886
|
] }, p.name);
|
|
6236
6887
|
})
|
|
6237
6888
|
}
|
|
6238
6889
|
),
|
|
6239
|
-
/* @__PURE__ */
|
|
6890
|
+
/* @__PURE__ */ jsx11("text", { fg: t.fg.muted, marginTop: 1, children: "Enter to select, Esc to go back" })
|
|
6240
6891
|
] });
|
|
6241
6892
|
case "fork_model":
|
|
6242
|
-
return /* @__PURE__ */
|
|
6243
|
-
/* @__PURE__ */
|
|
6244
|
-
/* @__PURE__ */
|
|
6245
|
-
/* @__PURE__ */
|
|
6893
|
+
return /* @__PURE__ */ jsxs11("box", { flexDirection: "column", children: [
|
|
6894
|
+
/* @__PURE__ */ jsx11("text", { fg: t.accent, children: "Fork Template - Model" }),
|
|
6895
|
+
/* @__PURE__ */ jsx11("text", { fg: t.fg.secondary, marginTop: 1, children: "Enter the model identifier:" }),
|
|
6896
|
+
/* @__PURE__ */ jsx11(
|
|
6246
6897
|
"input",
|
|
6247
6898
|
{
|
|
6248
6899
|
value: forkModel,
|
|
@@ -6265,13 +6916,13 @@ function TemplatesView({ context }) {
|
|
|
6265
6916
|
}
|
|
6266
6917
|
}
|
|
6267
6918
|
),
|
|
6268
|
-
error && /* @__PURE__ */
|
|
6269
|
-
/* @__PURE__ */
|
|
6919
|
+
error && /* @__PURE__ */ jsx11("text", { fg: t.status.error, marginTop: 1, children: error }),
|
|
6920
|
+
/* @__PURE__ */ jsx11("text", { fg: t.fg.muted, marginTop: 2, children: "Press Enter to continue, Esc to go back" })
|
|
6270
6921
|
] });
|
|
6271
6922
|
case "fork_confirm":
|
|
6272
|
-
return /* @__PURE__ */
|
|
6273
|
-
/* @__PURE__ */
|
|
6274
|
-
/* @__PURE__ */
|
|
6923
|
+
return /* @__PURE__ */ jsxs11("box", { flexDirection: "column", children: [
|
|
6924
|
+
/* @__PURE__ */ jsx11("text", { fg: t.accent, children: "Fork Template - Confirm" }),
|
|
6925
|
+
/* @__PURE__ */ jsxs11(
|
|
6275
6926
|
"box",
|
|
6276
6927
|
{
|
|
6277
6928
|
flexDirection: "column",
|
|
@@ -6280,35 +6931,35 @@ function TemplatesView({ context }) {
|
|
|
6280
6931
|
padding: 1,
|
|
6281
6932
|
marginTop: 1,
|
|
6282
6933
|
children: [
|
|
6283
|
-
/* @__PURE__ */
|
|
6284
|
-
/* @__PURE__ */
|
|
6285
|
-
/* @__PURE__ */
|
|
6934
|
+
/* @__PURE__ */ jsxs11("box", { flexDirection: "row", children: [
|
|
6935
|
+
/* @__PURE__ */ jsx11("text", { fg: t.fg.secondary, width: 20, children: "Name:" }),
|
|
6936
|
+
/* @__PURE__ */ jsx11("text", { fg: t.fg.primary, children: forkName })
|
|
6286
6937
|
] }),
|
|
6287
|
-
/* @__PURE__ */
|
|
6288
|
-
/* @__PURE__ */
|
|
6289
|
-
/* @__PURE__ */
|
|
6938
|
+
/* @__PURE__ */ jsxs11("box", { flexDirection: "row", children: [
|
|
6939
|
+
/* @__PURE__ */ jsx11("text", { fg: t.fg.secondary, width: 20, children: "Provider:" }),
|
|
6940
|
+
/* @__PURE__ */ jsx11("text", { fg: t.fg.primary, children: PROVIDER_NAMES[forkProvider] })
|
|
6290
6941
|
] }),
|
|
6291
|
-
forkProvider === "digitalocean" && /* @__PURE__ */
|
|
6292
|
-
/* @__PURE__ */
|
|
6293
|
-
/* @__PURE__ */
|
|
6942
|
+
forkProvider === "digitalocean" && /* @__PURE__ */ jsxs11("box", { flexDirection: "row", children: [
|
|
6943
|
+
/* @__PURE__ */ jsx11("text", { fg: t.fg.secondary, width: 20, children: "Droplet Size:" }),
|
|
6944
|
+
/* @__PURE__ */ jsx11("text", { fg: t.fg.primary, children: DO_DROPLET_SIZES2[forkDropletSizeIndex].slug })
|
|
6294
6945
|
] }),
|
|
6295
|
-
/* @__PURE__ */
|
|
6296
|
-
/* @__PURE__ */
|
|
6297
|
-
/* @__PURE__ */
|
|
6946
|
+
/* @__PURE__ */ jsxs11("box", { flexDirection: "row", children: [
|
|
6947
|
+
/* @__PURE__ */ jsx11("text", { fg: t.fg.secondary, width: 20, children: "AI Provider:" }),
|
|
6948
|
+
/* @__PURE__ */ jsx11("text", { fg: t.fg.primary, children: forkAiProvider })
|
|
6298
6949
|
] }),
|
|
6299
|
-
/* @__PURE__ */
|
|
6300
|
-
/* @__PURE__ */
|
|
6301
|
-
/* @__PURE__ */
|
|
6950
|
+
/* @__PURE__ */ jsxs11("box", { flexDirection: "row", children: [
|
|
6951
|
+
/* @__PURE__ */ jsx11("text", { fg: t.fg.secondary, width: 20, children: "Model:" }),
|
|
6952
|
+
/* @__PURE__ */ jsx11("text", { fg: t.fg.primary, children: forkModel })
|
|
6302
6953
|
] }),
|
|
6303
|
-
/* @__PURE__ */
|
|
6304
|
-
/* @__PURE__ */
|
|
6305
|
-
/* @__PURE__ */
|
|
6954
|
+
/* @__PURE__ */ jsxs11("box", { flexDirection: "row", children: [
|
|
6955
|
+
/* @__PURE__ */ jsx11("text", { fg: t.fg.secondary, width: 20, children: "Channel:" }),
|
|
6956
|
+
/* @__PURE__ */ jsx11("text", { fg: t.fg.primary, children: "telegram" })
|
|
6306
6957
|
] })
|
|
6307
6958
|
]
|
|
6308
6959
|
}
|
|
6309
6960
|
),
|
|
6310
|
-
error && /* @__PURE__ */
|
|
6311
|
-
/* @__PURE__ */
|
|
6961
|
+
error && /* @__PURE__ */ jsx11("text", { fg: t.status.error, marginTop: 1, children: error }),
|
|
6962
|
+
/* @__PURE__ */ jsx11("text", { fg: t.status.warning, marginTop: 2, children: "Press Y to confirm, N to go back" })
|
|
6312
6963
|
] });
|
|
6313
6964
|
}
|
|
6314
6965
|
};
|
|
@@ -6326,34 +6977,464 @@ function TemplatesView({ context }) {
|
|
|
6326
6977
|
return renderForking();
|
|
6327
6978
|
}
|
|
6328
6979
|
};
|
|
6329
|
-
return /* @__PURE__ */
|
|
6330
|
-
/* @__PURE__ */
|
|
6331
|
-
/* @__PURE__ */
|
|
6332
|
-
/* @__PURE__ */
|
|
6980
|
+
return /* @__PURE__ */ jsxs11("box", { flexDirection: "column", width: "100%", padding: 1, children: [
|
|
6981
|
+
/* @__PURE__ */ jsxs11("box", { flexDirection: "row", marginBottom: 2, children: [
|
|
6982
|
+
/* @__PURE__ */ jsx11("text", { fg: t.accent, children: "/templates" }),
|
|
6983
|
+
/* @__PURE__ */ jsx11("text", { fg: t.fg.secondary, children: " - Manage deployment templates" })
|
|
6333
6984
|
] }),
|
|
6334
6985
|
renderContent()
|
|
6335
6986
|
] });
|
|
6336
6987
|
}
|
|
6337
6988
|
|
|
6338
|
-
// src/
|
|
6339
|
-
import {
|
|
6340
|
-
|
|
6989
|
+
// src/components/DashboardView.tsx
|
|
6990
|
+
import { useState as useState11, useCallback as useCallback3 } from "react";
|
|
6991
|
+
import { useKeyboard as useKeyboard12, useRenderer } from "@opentui/react";
|
|
6992
|
+
import { spawnSync as spawnSync2 } from "child_process";
|
|
6993
|
+
import { platform as platform2 } from "os";
|
|
6994
|
+
|
|
6995
|
+
// src/services/tunnel.ts
|
|
6996
|
+
import { spawn as spawn2 } from "child_process";
|
|
6997
|
+
import { createServer } from "net";
|
|
6998
|
+
var activeTunnels = /* @__PURE__ */ new Map();
|
|
6999
|
+
function isPortAvailable(port) {
|
|
7000
|
+
return new Promise((resolve) => {
|
|
7001
|
+
const server = createServer();
|
|
7002
|
+
server.once("error", () => resolve(false));
|
|
7003
|
+
server.once("listening", () => {
|
|
7004
|
+
server.close(() => resolve(true));
|
|
7005
|
+
});
|
|
7006
|
+
server.listen(port, "127.0.0.1");
|
|
7007
|
+
});
|
|
7008
|
+
}
|
|
7009
|
+
async function findAvailablePort(startPort) {
|
|
7010
|
+
for (let port = startPort; port < startPort + 100; port++) {
|
|
7011
|
+
if (await isPortAvailable(port)) return port;
|
|
7012
|
+
}
|
|
7013
|
+
throw new Error(`No available port found in range ${startPort}\u2013${startPort + 99}`);
|
|
7014
|
+
}
|
|
7015
|
+
async function startTunnel(deploymentName, serverIp, remotePort = 18789) {
|
|
7016
|
+
const existing = getTunnel(deploymentName);
|
|
7017
|
+
if (existing) return existing;
|
|
7018
|
+
const sshKeyPath = getSSHKeyPath(deploymentName);
|
|
7019
|
+
const localPort = await findAvailablePort(remotePort);
|
|
7020
|
+
const proc = spawn2("ssh", [
|
|
7021
|
+
"-N",
|
|
7022
|
+
"-L",
|
|
7023
|
+
`${localPort}:127.0.0.1:${remotePort}`,
|
|
7024
|
+
"-i",
|
|
7025
|
+
sshKeyPath,
|
|
7026
|
+
"-o",
|
|
7027
|
+
"StrictHostKeyChecking=no",
|
|
7028
|
+
"-o",
|
|
7029
|
+
"UserKnownHostsFile=/dev/null",
|
|
7030
|
+
"-o",
|
|
7031
|
+
"ServerAliveInterval=15",
|
|
7032
|
+
"-o",
|
|
7033
|
+
"ServerAliveCountMax=3",
|
|
7034
|
+
"-o",
|
|
7035
|
+
"ExitOnForwardFailure=yes",
|
|
7036
|
+
`root@${serverIp}`
|
|
7037
|
+
], {
|
|
7038
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
7039
|
+
detached: false
|
|
7040
|
+
});
|
|
7041
|
+
const tunnel = {
|
|
7042
|
+
deploymentName,
|
|
7043
|
+
serverIp,
|
|
7044
|
+
localPort,
|
|
7045
|
+
remotePort,
|
|
7046
|
+
process: proc,
|
|
7047
|
+
dashboardUrl: null,
|
|
7048
|
+
startedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
7049
|
+
};
|
|
7050
|
+
activeTunnels.set(deploymentName, tunnel);
|
|
7051
|
+
proc.on("exit", () => {
|
|
7052
|
+
activeTunnels.delete(deploymentName);
|
|
7053
|
+
});
|
|
7054
|
+
await new Promise((resolve) => setTimeout(resolve, 2500));
|
|
7055
|
+
if (proc.exitCode !== null || proc.killed) {
|
|
7056
|
+
activeTunnels.delete(deploymentName);
|
|
7057
|
+
throw new Error("SSH tunnel failed to establish. Check that the server is reachable.");
|
|
7058
|
+
}
|
|
7059
|
+
return tunnel;
|
|
7060
|
+
}
|
|
7061
|
+
function stopTunnel(deploymentName) {
|
|
7062
|
+
const tunnel = activeTunnels.get(deploymentName);
|
|
7063
|
+
if (tunnel) {
|
|
7064
|
+
tunnel.process.kill();
|
|
7065
|
+
activeTunnels.delete(deploymentName);
|
|
7066
|
+
}
|
|
7067
|
+
}
|
|
7068
|
+
function getTunnel(deploymentName) {
|
|
7069
|
+
const tunnel = activeTunnels.get(deploymentName);
|
|
7070
|
+
if (tunnel && tunnel.process.exitCode === null && !tunnel.process.killed) {
|
|
7071
|
+
return tunnel;
|
|
7072
|
+
}
|
|
7073
|
+
if (tunnel) activeTunnels.delete(deploymentName);
|
|
7074
|
+
return null;
|
|
7075
|
+
}
|
|
7076
|
+
function getActiveTunnels() {
|
|
7077
|
+
for (const [name, tunnel] of activeTunnels) {
|
|
7078
|
+
if (tunnel.process.exitCode !== null || tunnel.process.killed) {
|
|
7079
|
+
activeTunnels.delete(name);
|
|
7080
|
+
}
|
|
7081
|
+
}
|
|
7082
|
+
return Array.from(activeTunnels.values());
|
|
7083
|
+
}
|
|
7084
|
+
|
|
7085
|
+
// src/components/DashboardView.tsx
|
|
7086
|
+
import { jsx as jsx12, jsxs as jsxs12 } from "@opentui/react/jsx-runtime";
|
|
7087
|
+
function openInBrowser(url) {
|
|
7088
|
+
try {
|
|
7089
|
+
if (platform2() === "darwin") {
|
|
7090
|
+
spawnSync2("open", [url]);
|
|
7091
|
+
} else {
|
|
7092
|
+
spawnSync2("xdg-open", [url]);
|
|
7093
|
+
}
|
|
7094
|
+
return true;
|
|
7095
|
+
} catch {
|
|
7096
|
+
return false;
|
|
7097
|
+
}
|
|
7098
|
+
}
|
|
7099
|
+
function DashboardView({ context }) {
|
|
6341
7100
|
const renderer = useRenderer();
|
|
6342
|
-
const [
|
|
6343
|
-
const [
|
|
6344
|
-
const [
|
|
7101
|
+
const [viewState, setViewState] = useState11("selecting");
|
|
7102
|
+
const [selectedIndex, setSelectedIndex] = useState11(0);
|
|
7103
|
+
const [error, setError] = useState11(null);
|
|
7104
|
+
const [activeTunnel, setActiveTunnel] = useState11(null);
|
|
7105
|
+
const [dashboardUrl, setDashboardUrl] = useState11(null);
|
|
7106
|
+
const [connectMessage, setConnectMessage] = useState11("");
|
|
7107
|
+
const [copied, setCopied] = useState11(false);
|
|
7108
|
+
const deployedDeployments = context.deployments.filter(
|
|
7109
|
+
(d) => d.state.status === "deployed" && d.state.serverIp
|
|
7110
|
+
);
|
|
7111
|
+
const connectToDashboard = useCallback3(async (deployment) => {
|
|
7112
|
+
setViewState("connecting");
|
|
7113
|
+
setError(null);
|
|
7114
|
+
setCopied(false);
|
|
7115
|
+
setConnectMessage("Establishing SSH tunnel...");
|
|
7116
|
+
try {
|
|
7117
|
+
const serverIp = deployment.state.serverIp;
|
|
7118
|
+
const remotePort = deployment.config.openclawConfig?.gateway?.port || 18789;
|
|
7119
|
+
const tunnel = await startTunnel(
|
|
7120
|
+
deployment.config.name,
|
|
7121
|
+
serverIp,
|
|
7122
|
+
remotePort
|
|
7123
|
+
);
|
|
7124
|
+
setActiveTunnel(tunnel);
|
|
7125
|
+
if (tunnel.dashboardUrl) {
|
|
7126
|
+
setDashboardUrl(tunnel.dashboardUrl);
|
|
7127
|
+
setViewState("active");
|
|
7128
|
+
return;
|
|
7129
|
+
}
|
|
7130
|
+
setConnectMessage("Retrieving dashboard URL...");
|
|
7131
|
+
const localToken = deployment.state.gatewayToken;
|
|
7132
|
+
if (localToken) {
|
|
7133
|
+
const localUrl = `http://127.0.0.1:${tunnel.localPort}/?token=${encodeURIComponent(localToken)}`;
|
|
7134
|
+
tunnel.dashboardUrl = localUrl;
|
|
7135
|
+
setDashboardUrl(localUrl);
|
|
7136
|
+
setViewState("active");
|
|
7137
|
+
return;
|
|
7138
|
+
}
|
|
7139
|
+
const ssh = await connectToDeployment(
|
|
7140
|
+
deployment.config.name,
|
|
7141
|
+
serverIp
|
|
7142
|
+
);
|
|
7143
|
+
try {
|
|
7144
|
+
const info = await getDashboardUrl(ssh);
|
|
7145
|
+
const parsed = new URL(info.url);
|
|
7146
|
+
parsed.hostname = "127.0.0.1";
|
|
7147
|
+
parsed.port = String(tunnel.localPort);
|
|
7148
|
+
const localUrl = parsed.toString();
|
|
7149
|
+
tunnel.dashboardUrl = localUrl;
|
|
7150
|
+
setDashboardUrl(localUrl);
|
|
7151
|
+
setViewState("active");
|
|
7152
|
+
} finally {
|
|
7153
|
+
ssh.disconnect();
|
|
7154
|
+
}
|
|
7155
|
+
} catch (err) {
|
|
7156
|
+
setError(err instanceof Error ? err.message : String(err));
|
|
7157
|
+
setViewState("error");
|
|
7158
|
+
}
|
|
7159
|
+
}, []);
|
|
7160
|
+
useKeyboard12((key) => {
|
|
7161
|
+
if (viewState === "selecting") {
|
|
7162
|
+
if (deployedDeployments.length === 0) {
|
|
7163
|
+
if (key.name === "escape" || key.name === "return") {
|
|
7164
|
+
context.navigateTo("home");
|
|
7165
|
+
}
|
|
7166
|
+
return;
|
|
7167
|
+
}
|
|
7168
|
+
if (key.name === "up" && selectedIndex > 0) {
|
|
7169
|
+
setSelectedIndex(selectedIndex - 1);
|
|
7170
|
+
} else if (key.name === "down" && selectedIndex < deployedDeployments.length - 1) {
|
|
7171
|
+
setSelectedIndex(selectedIndex + 1);
|
|
7172
|
+
} else if (key.name === "return") {
|
|
7173
|
+
const deployment = deployedDeployments[selectedIndex];
|
|
7174
|
+
const existing = getTunnel(deployment.config.name);
|
|
7175
|
+
if (existing && existing.dashboardUrl) {
|
|
7176
|
+
setActiveTunnel(existing);
|
|
7177
|
+
setDashboardUrl(existing.dashboardUrl);
|
|
7178
|
+
setViewState("active");
|
|
7179
|
+
return;
|
|
7180
|
+
}
|
|
7181
|
+
connectToDashboard(deployment);
|
|
7182
|
+
} else if (key.name === "escape") {
|
|
7183
|
+
context.navigateTo("home");
|
|
7184
|
+
}
|
|
7185
|
+
} else if (viewState === "active") {
|
|
7186
|
+
if (key.sequence === "o" || key.sequence === "O") {
|
|
7187
|
+
if (dashboardUrl) openInBrowser(dashboardUrl);
|
|
7188
|
+
} else if (key.sequence === "c" || key.sequence === "C") {
|
|
7189
|
+
if (dashboardUrl) {
|
|
7190
|
+
renderer.copyToClipboardOSC52(dashboardUrl);
|
|
7191
|
+
setCopied(true);
|
|
7192
|
+
setTimeout(() => setCopied(false), 3e3);
|
|
7193
|
+
}
|
|
7194
|
+
} else if (key.sequence === "d" || key.sequence === "D") {
|
|
7195
|
+
if (activeTunnel) {
|
|
7196
|
+
stopTunnel(activeTunnel.deploymentName);
|
|
7197
|
+
setActiveTunnel(null);
|
|
7198
|
+
setDashboardUrl(null);
|
|
7199
|
+
setViewState("selecting");
|
|
7200
|
+
}
|
|
7201
|
+
} else if (key.name === "escape") {
|
|
7202
|
+
setViewState("selecting");
|
|
7203
|
+
}
|
|
7204
|
+
} else if (viewState === "error") {
|
|
7205
|
+
if (key.name === "escape" || key.name === "return") {
|
|
7206
|
+
setViewState("selecting");
|
|
7207
|
+
}
|
|
7208
|
+
}
|
|
7209
|
+
});
|
|
7210
|
+
if (deployedDeployments.length === 0) {
|
|
7211
|
+
return /* @__PURE__ */ jsxs12("box", { flexDirection: "column", width: "100%", padding: 1, children: [
|
|
7212
|
+
/* @__PURE__ */ jsxs12("box", { flexDirection: "row", marginBottom: 2, children: [
|
|
7213
|
+
/* @__PURE__ */ jsx12("text", { fg: t.accent, children: "/dashboard" }),
|
|
7214
|
+
/* @__PURE__ */ jsx12("text", { fg: t.fg.secondary, children: " - Open OpenClaw dashboard" })
|
|
7215
|
+
] }),
|
|
7216
|
+
/* @__PURE__ */ jsxs12(
|
|
7217
|
+
"box",
|
|
7218
|
+
{
|
|
7219
|
+
flexDirection: "column",
|
|
7220
|
+
borderStyle: "single",
|
|
7221
|
+
borderColor: t.border.default,
|
|
7222
|
+
padding: 1,
|
|
7223
|
+
children: [
|
|
7224
|
+
/* @__PURE__ */ jsx12("text", { fg: t.status.warning, children: "No deployed instances found!" }),
|
|
7225
|
+
/* @__PURE__ */ jsx12("text", { fg: t.fg.secondary, marginTop: 1, children: "Deploy an instance first with /deploy" })
|
|
7226
|
+
]
|
|
7227
|
+
}
|
|
7228
|
+
),
|
|
7229
|
+
/* @__PURE__ */ jsx12("text", { fg: t.fg.muted, marginTop: 2, children: "Press any key to return to home" })
|
|
7230
|
+
] });
|
|
7231
|
+
}
|
|
7232
|
+
if (viewState === "selecting") {
|
|
7233
|
+
const tunnels = getActiveTunnels();
|
|
7234
|
+
return /* @__PURE__ */ jsxs12("box", { flexDirection: "column", width: "100%", padding: 1, children: [
|
|
7235
|
+
/* @__PURE__ */ jsxs12("box", { flexDirection: "row", marginBottom: 2, children: [
|
|
7236
|
+
/* @__PURE__ */ jsx12("text", { fg: t.accent, children: "/dashboard" }),
|
|
7237
|
+
/* @__PURE__ */ jsxs12("text", { fg: t.fg.secondary, children: [
|
|
7238
|
+
" ",
|
|
7239
|
+
"- Select a deployment to open its dashboard"
|
|
7240
|
+
] })
|
|
7241
|
+
] }),
|
|
7242
|
+
tunnels.length > 0 && /* @__PURE__ */ jsxs12(
|
|
7243
|
+
"box",
|
|
7244
|
+
{
|
|
7245
|
+
flexDirection: "column",
|
|
7246
|
+
borderStyle: "single",
|
|
7247
|
+
borderColor: t.status.success,
|
|
7248
|
+
padding: 1,
|
|
7249
|
+
marginBottom: 1,
|
|
7250
|
+
children: [
|
|
7251
|
+
/* @__PURE__ */ jsxs12("text", { fg: t.status.success, children: [
|
|
7252
|
+
tunnels.length,
|
|
7253
|
+
" active tunnel",
|
|
7254
|
+
tunnels.length > 1 ? "s" : ""
|
|
7255
|
+
] }),
|
|
7256
|
+
tunnels.map((tun) => /* @__PURE__ */ jsxs12("text", { fg: t.fg.secondary, children: [
|
|
7257
|
+
tun.deploymentName,
|
|
7258
|
+
": 127.0.0.1:",
|
|
7259
|
+
tun.localPort,
|
|
7260
|
+
" \u2192",
|
|
7261
|
+
" ",
|
|
7262
|
+
tun.serverIp,
|
|
7263
|
+
":",
|
|
7264
|
+
tun.remotePort
|
|
7265
|
+
] }, tun.deploymentName))
|
|
7266
|
+
]
|
|
7267
|
+
}
|
|
7268
|
+
),
|
|
7269
|
+
/* @__PURE__ */ jsx12(
|
|
7270
|
+
"box",
|
|
7271
|
+
{
|
|
7272
|
+
flexDirection: "column",
|
|
7273
|
+
borderStyle: "single",
|
|
7274
|
+
borderColor: t.border.default,
|
|
7275
|
+
padding: 1,
|
|
7276
|
+
marginBottom: 1,
|
|
7277
|
+
children: deployedDeployments.map((deployment, index) => {
|
|
7278
|
+
const isSelected = index === selectedIndex;
|
|
7279
|
+
const hasTunnel = getTunnel(deployment.config.name) !== null;
|
|
7280
|
+
return /* @__PURE__ */ jsxs12(
|
|
7281
|
+
"box",
|
|
7282
|
+
{
|
|
7283
|
+
flexDirection: "row",
|
|
7284
|
+
backgroundColor: isSelected ? t.selection.bg : void 0,
|
|
7285
|
+
children: [
|
|
7286
|
+
/* @__PURE__ */ jsx12("text", { fg: isSelected ? t.selection.fg : t.fg.secondary, children: isSelected ? "> " : " " }),
|
|
7287
|
+
/* @__PURE__ */ jsx12(
|
|
7288
|
+
"text",
|
|
7289
|
+
{
|
|
7290
|
+
fg: isSelected ? t.selection.fg : t.fg.primary,
|
|
7291
|
+
width: 25,
|
|
7292
|
+
children: deployment.config.name
|
|
7293
|
+
}
|
|
7294
|
+
),
|
|
7295
|
+
/* @__PURE__ */ jsx12("text", { fg: t.accent, width: 18, children: deployment.state.serverIp }),
|
|
7296
|
+
hasTunnel && /* @__PURE__ */ jsx12("text", { fg: t.status.success, children: "[tunnel active]" })
|
|
7297
|
+
]
|
|
7298
|
+
},
|
|
7299
|
+
deployment.config.name
|
|
7300
|
+
);
|
|
7301
|
+
})
|
|
7302
|
+
}
|
|
7303
|
+
),
|
|
7304
|
+
/* @__PURE__ */ jsx12("text", { fg: t.fg.muted, children: "Arrow keys to select | Enter to connect | Esc to go back" })
|
|
7305
|
+
] });
|
|
7306
|
+
}
|
|
7307
|
+
if (viewState === "connecting") {
|
|
7308
|
+
return /* @__PURE__ */ jsxs12("box", { flexDirection: "column", width: "100%", padding: 1, children: [
|
|
7309
|
+
/* @__PURE__ */ jsxs12("box", { flexDirection: "row", marginBottom: 2, children: [
|
|
7310
|
+
/* @__PURE__ */ jsx12("text", { fg: t.accent, children: "/dashboard" }),
|
|
7311
|
+
/* @__PURE__ */ jsx12("text", { fg: t.fg.secondary, children: " - Connecting..." })
|
|
7312
|
+
] }),
|
|
7313
|
+
/* @__PURE__ */ jsxs12(
|
|
7314
|
+
"box",
|
|
7315
|
+
{
|
|
7316
|
+
flexDirection: "column",
|
|
7317
|
+
borderStyle: "single",
|
|
7318
|
+
borderColor: t.border.focus,
|
|
7319
|
+
padding: 1,
|
|
7320
|
+
children: [
|
|
7321
|
+
/* @__PURE__ */ jsx12("text", { fg: t.status.info, children: connectMessage }),
|
|
7322
|
+
/* @__PURE__ */ jsx12("text", { fg: t.fg.muted, marginTop: 1, children: "This may take a few seconds..." })
|
|
7323
|
+
]
|
|
7324
|
+
}
|
|
7325
|
+
)
|
|
7326
|
+
] });
|
|
7327
|
+
}
|
|
7328
|
+
if (viewState === "active" && activeTunnel && dashboardUrl) {
|
|
7329
|
+
return /* @__PURE__ */ jsxs12("box", { flexDirection: "column", width: "100%", padding: 1, children: [
|
|
7330
|
+
/* @__PURE__ */ jsxs12("box", { flexDirection: "row", marginBottom: 2, children: [
|
|
7331
|
+
/* @__PURE__ */ jsx12("text", { fg: t.status.success, children: "/dashboard" }),
|
|
7332
|
+
/* @__PURE__ */ jsxs12("text", { fg: t.fg.secondary, children: [
|
|
7333
|
+
" ",
|
|
7334
|
+
"- ",
|
|
7335
|
+
activeTunnel.deploymentName
|
|
7336
|
+
] })
|
|
7337
|
+
] }),
|
|
7338
|
+
/* @__PURE__ */ jsxs12(
|
|
7339
|
+
"box",
|
|
7340
|
+
{
|
|
7341
|
+
flexDirection: "column",
|
|
7342
|
+
borderStyle: "double",
|
|
7343
|
+
borderColor: t.status.success,
|
|
7344
|
+
padding: 1,
|
|
7345
|
+
marginBottom: 1,
|
|
7346
|
+
children: [
|
|
7347
|
+
/* @__PURE__ */ jsx12("text", { fg: t.status.success, children: "Dashboard Ready" }),
|
|
7348
|
+
/* @__PURE__ */ jsxs12("text", { fg: t.fg.primary, marginTop: 1, children: [
|
|
7349
|
+
"Tunnel: 127.0.0.1:",
|
|
7350
|
+
activeTunnel.localPort,
|
|
7351
|
+
" \u2192",
|
|
7352
|
+
" ",
|
|
7353
|
+
activeTunnel.serverIp,
|
|
7354
|
+
":",
|
|
7355
|
+
activeTunnel.remotePort
|
|
7356
|
+
] })
|
|
7357
|
+
]
|
|
7358
|
+
}
|
|
7359
|
+
),
|
|
7360
|
+
/* @__PURE__ */ jsxs12(
|
|
7361
|
+
"box",
|
|
7362
|
+
{
|
|
7363
|
+
flexDirection: "column",
|
|
7364
|
+
borderStyle: "single",
|
|
7365
|
+
borderColor: t.accent,
|
|
7366
|
+
padding: 1,
|
|
7367
|
+
marginBottom: 1,
|
|
7368
|
+
children: [
|
|
7369
|
+
/* @__PURE__ */ jsx12("text", { fg: t.fg.secondary, children: "Dashboard URL:" }),
|
|
7370
|
+
/* @__PURE__ */ jsx12("text", { fg: t.accent, marginTop: 1, children: dashboardUrl })
|
|
7371
|
+
]
|
|
7372
|
+
}
|
|
7373
|
+
),
|
|
7374
|
+
/* @__PURE__ */ jsxs12("box", { flexDirection: "column", marginBottom: 1, children: [
|
|
7375
|
+
/* @__PURE__ */ jsxs12("box", { flexDirection: "row", children: [
|
|
7376
|
+
/* @__PURE__ */ jsx12("text", { fg: t.accent, width: 4, children: "O" }),
|
|
7377
|
+
/* @__PURE__ */ jsx12("text", { fg: t.fg.primary, children: "Open in browser" })
|
|
7378
|
+
] }),
|
|
7379
|
+
/* @__PURE__ */ jsxs12("box", { flexDirection: "row", children: [
|
|
7380
|
+
/* @__PURE__ */ jsx12("text", { fg: t.accent, width: 4, children: "C" }),
|
|
7381
|
+
/* @__PURE__ */ jsx12("text", { fg: t.fg.primary, children: "Copy URL to clipboard" })
|
|
7382
|
+
] }),
|
|
7383
|
+
/* @__PURE__ */ jsxs12("box", { flexDirection: "row", children: [
|
|
7384
|
+
/* @__PURE__ */ jsx12("text", { fg: t.accent, width: 4, children: "D" }),
|
|
7385
|
+
/* @__PURE__ */ jsx12("text", { fg: t.fg.primary, children: "Disconnect tunnel" })
|
|
7386
|
+
] }),
|
|
7387
|
+
/* @__PURE__ */ jsxs12("box", { flexDirection: "row", children: [
|
|
7388
|
+
/* @__PURE__ */ jsx12("text", { fg: t.accent, width: 4, children: "Esc" }),
|
|
7389
|
+
/* @__PURE__ */ jsx12("text", { fg: t.fg.primary, children: "Back to selection (tunnel stays active)" })
|
|
7390
|
+
] })
|
|
7391
|
+
] }),
|
|
7392
|
+
copied && /* @__PURE__ */ jsx12("text", { fg: t.status.success, children: "URL copied to clipboard!" })
|
|
7393
|
+
] });
|
|
7394
|
+
}
|
|
7395
|
+
if (viewState === "error") {
|
|
7396
|
+
return /* @__PURE__ */ jsxs12("box", { flexDirection: "column", width: "100%", padding: 1, children: [
|
|
7397
|
+
/* @__PURE__ */ jsxs12("box", { flexDirection: "row", marginBottom: 2, children: [
|
|
7398
|
+
/* @__PURE__ */ jsx12("text", { fg: t.status.error, children: "/dashboard" }),
|
|
7399
|
+
/* @__PURE__ */ jsx12("text", { fg: t.fg.secondary, children: " - Connection failed" })
|
|
7400
|
+
] }),
|
|
7401
|
+
/* @__PURE__ */ jsx12(
|
|
7402
|
+
"box",
|
|
7403
|
+
{
|
|
7404
|
+
flexDirection: "column",
|
|
7405
|
+
borderStyle: "single",
|
|
7406
|
+
borderColor: t.status.error,
|
|
7407
|
+
padding: 1,
|
|
7408
|
+
marginBottom: 1,
|
|
7409
|
+
children: /* @__PURE__ */ jsx12("text", { fg: t.status.error, children: error })
|
|
7410
|
+
}
|
|
7411
|
+
),
|
|
7412
|
+
/* @__PURE__ */ jsx12("text", { fg: t.fg.muted, children: "Press Enter or Esc to go back" })
|
|
7413
|
+
] });
|
|
7414
|
+
}
|
|
7415
|
+
return null;
|
|
7416
|
+
}
|
|
7417
|
+
|
|
7418
|
+
// src/App.tsx
|
|
7419
|
+
import { jsx as jsx13, jsxs as jsxs13 } from "@opentui/react/jsx-runtime";
|
|
7420
|
+
function App({ lacksTrueColor: lacksTrueColor2 }) {
|
|
7421
|
+
const renderer = useRenderer2();
|
|
7422
|
+
const [currentView, setCurrentView] = useState12("home");
|
|
7423
|
+
const [selectedDeployment, setSelectedDeployment] = useState12(null);
|
|
7424
|
+
const [deployments, setDeployments] = useState12(() => {
|
|
6345
7425
|
try {
|
|
6346
7426
|
return getAllDeployments();
|
|
6347
7427
|
} catch {
|
|
6348
7428
|
return [];
|
|
6349
7429
|
}
|
|
6350
7430
|
});
|
|
6351
|
-
const [selectedTemplate, setSelectedTemplate] =
|
|
6352
|
-
const
|
|
6353
|
-
const
|
|
7431
|
+
const [selectedTemplate, setSelectedTemplate] = useState12(null);
|
|
7432
|
+
const [editingDeployment, setEditingDeployment] = useState12(null);
|
|
7433
|
+
const wasDraggingRef = useRef7(false);
|
|
7434
|
+
const handleMouseDrag = useCallback4(() => {
|
|
6354
7435
|
wasDraggingRef.current = true;
|
|
6355
7436
|
}, []);
|
|
6356
|
-
const handleMouseUp =
|
|
7437
|
+
const handleMouseUp = useCallback4(() => {
|
|
6357
7438
|
if (!wasDraggingRef.current) return;
|
|
6358
7439
|
wasDraggingRef.current = false;
|
|
6359
7440
|
const selection = renderer.getSelection();
|
|
@@ -6364,14 +7445,14 @@ function App() {
|
|
|
6364
7445
|
}
|
|
6365
7446
|
}
|
|
6366
7447
|
}, [renderer]);
|
|
6367
|
-
const refreshDeployments =
|
|
7448
|
+
const refreshDeployments = useCallback4(() => {
|
|
6368
7449
|
try {
|
|
6369
7450
|
setDeployments(getAllDeployments());
|
|
6370
7451
|
} catch {
|
|
6371
7452
|
setDeployments([]);
|
|
6372
7453
|
}
|
|
6373
7454
|
}, []);
|
|
6374
|
-
const navigateTo =
|
|
7455
|
+
const navigateTo = useCallback4((view, deployment) => {
|
|
6375
7456
|
if (deployment !== void 0) {
|
|
6376
7457
|
setSelectedDeployment(deployment);
|
|
6377
7458
|
}
|
|
@@ -6384,35 +7465,41 @@ function App() {
|
|
|
6384
7465
|
deployments,
|
|
6385
7466
|
refreshDeployments,
|
|
6386
7467
|
selectedTemplate,
|
|
6387
|
-
setSelectedTemplate
|
|
7468
|
+
setSelectedTemplate,
|
|
7469
|
+
editingDeployment,
|
|
7470
|
+
setEditingDeployment
|
|
6388
7471
|
};
|
|
6389
7472
|
const renderView = () => {
|
|
6390
7473
|
switch (currentView) {
|
|
6391
7474
|
case "home":
|
|
6392
|
-
return /* @__PURE__ */
|
|
7475
|
+
return /* @__PURE__ */ jsx13(Home, { context });
|
|
6393
7476
|
case "new":
|
|
6394
|
-
return /* @__PURE__ */
|
|
7477
|
+
return /* @__PURE__ */ jsx13(NewDeployment, { context });
|
|
7478
|
+
case "list":
|
|
7479
|
+
return /* @__PURE__ */ jsx13(ListView, { context });
|
|
6395
7480
|
case "deploy":
|
|
6396
|
-
return /* @__PURE__ */
|
|
7481
|
+
return /* @__PURE__ */ jsx13(DeployView, { context });
|
|
6397
7482
|
case "deploying":
|
|
6398
|
-
return /* @__PURE__ */
|
|
7483
|
+
return /* @__PURE__ */ jsx13(DeployingView, { context });
|
|
6399
7484
|
case "status":
|
|
6400
|
-
return /* @__PURE__ */
|
|
7485
|
+
return /* @__PURE__ */ jsx13(StatusView, { context });
|
|
6401
7486
|
case "ssh":
|
|
6402
|
-
return /* @__PURE__ */
|
|
7487
|
+
return /* @__PURE__ */ jsx13(SSHView, { context });
|
|
6403
7488
|
case "logs":
|
|
6404
|
-
return /* @__PURE__ */
|
|
7489
|
+
return /* @__PURE__ */ jsx13(LogsView, { context });
|
|
7490
|
+
case "dashboard":
|
|
7491
|
+
return /* @__PURE__ */ jsx13(DashboardView, { context });
|
|
6405
7492
|
case "destroy":
|
|
6406
|
-
return /* @__PURE__ */
|
|
7493
|
+
return /* @__PURE__ */ jsx13(DestroyView, { context });
|
|
6407
7494
|
case "help":
|
|
6408
|
-
return /* @__PURE__ */
|
|
7495
|
+
return /* @__PURE__ */ jsx13(HelpView, { context });
|
|
6409
7496
|
case "templates":
|
|
6410
|
-
return /* @__PURE__ */
|
|
7497
|
+
return /* @__PURE__ */ jsx13(TemplatesView, { context });
|
|
6411
7498
|
default:
|
|
6412
|
-
return /* @__PURE__ */
|
|
7499
|
+
return /* @__PURE__ */ jsx13(Home, { context });
|
|
6413
7500
|
}
|
|
6414
7501
|
};
|
|
6415
|
-
return /* @__PURE__ */
|
|
7502
|
+
return /* @__PURE__ */ jsxs13(
|
|
6416
7503
|
"scrollbox",
|
|
6417
7504
|
{
|
|
6418
7505
|
width: "100%",
|
|
@@ -6433,13 +7520,29 @@ function App() {
|
|
|
6433
7520
|
verticalScrollbarOptions: {
|
|
6434
7521
|
showArrows: false
|
|
6435
7522
|
},
|
|
6436
|
-
children:
|
|
7523
|
+
children: [
|
|
7524
|
+
lacksTrueColor2 && /* @__PURE__ */ jsx13(
|
|
7525
|
+
"box",
|
|
7526
|
+
{
|
|
7527
|
+
width: "100%",
|
|
7528
|
+
style: {
|
|
7529
|
+
paddingLeft: 1,
|
|
7530
|
+
paddingRight: 1,
|
|
7531
|
+
paddingTop: 0,
|
|
7532
|
+
paddingBottom: 0,
|
|
7533
|
+
backgroundColor: t.status.warning
|
|
7534
|
+
},
|
|
7535
|
+
children: /* @__PURE__ */ jsx13("text", { fg: "#000000", children: "\u26A0 Your terminal does not support true color. Colors may look wrong. For full color support, use Ghostty, iTerm2, Kitty, or WezTerm \u2014 or upgrade to macOS 26+ for Terminal.app true color support." })
|
|
7536
|
+
}
|
|
7537
|
+
),
|
|
7538
|
+
renderView()
|
|
7539
|
+
]
|
|
6437
7540
|
}
|
|
6438
7541
|
);
|
|
6439
7542
|
}
|
|
6440
7543
|
|
|
6441
7544
|
// src/index.tsx
|
|
6442
|
-
import { jsx as
|
|
7545
|
+
import { jsx as jsx14 } from "@opentui/react/jsx-runtime";
|
|
6443
7546
|
var args = process.argv.slice(2);
|
|
6444
7547
|
if (args.includes("--version") || args.includes("-v")) {
|
|
6445
7548
|
const require2 = createRequire(import.meta.url);
|
|
@@ -6468,11 +7571,14 @@ Documentation & source:
|
|
|
6468
7571
|
`);
|
|
6469
7572
|
process.exit(0);
|
|
6470
7573
|
}
|
|
7574
|
+
var isAppleTerminal = process.env.TERM_PROGRAM === "Apple_Terminal";
|
|
7575
|
+
var darwinMajor = process.platform === "darwin" ? parseInt(release(), 10) : Infinity;
|
|
7576
|
+
var lacksTrueColor = isAppleTerminal && darwinMajor < 25;
|
|
6471
7577
|
async function main() {
|
|
6472
7578
|
const renderer = await createCliRenderer({
|
|
6473
7579
|
useMouse: true
|
|
6474
7580
|
});
|
|
6475
7581
|
const root = createRoot(renderer);
|
|
6476
|
-
root.render(/* @__PURE__ */
|
|
7582
|
+
root.render(/* @__PURE__ */ jsx14(App, { lacksTrueColor }));
|
|
6477
7583
|
}
|
|
6478
7584
|
main().catch(console.error);
|