clawcontrol 0.2.0 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/clawcontrol.js +116 -7
- package/dist/index.js +373 -75
- package/package.json +1 -1
package/bin/clawcontrol.js
CHANGED
|
@@ -8,32 +8,96 @@ import { execFileSync } from "node:child_process";
|
|
|
8
8
|
import { createRequire } from "node:module";
|
|
9
9
|
import { fileURLToPath } from "node:url";
|
|
10
10
|
import { dirname, resolve } from "node:path";
|
|
11
|
+
import { readFileSync } from "node:fs";
|
|
11
12
|
|
|
12
13
|
const args = process.argv.slice(2);
|
|
13
14
|
|
|
14
|
-
//
|
|
15
|
+
// ── Helpers ──────────────────────────────────────────────────────────
|
|
16
|
+
|
|
17
|
+
const require = createRequire(import.meta.url);
|
|
18
|
+
const pkg = require("../package.json");
|
|
19
|
+
const currentVersion = pkg.version;
|
|
20
|
+
|
|
21
|
+
/** Compare two semver strings. Returns -1, 0, or 1. */
|
|
22
|
+
function compareSemver(a, b) {
|
|
23
|
+
const pa = a.split(".").map(Number);
|
|
24
|
+
const pb = b.split(".").map(Number);
|
|
25
|
+
for (let i = 0; i < 3; i++) {
|
|
26
|
+
if (pa[i] < pb[i]) return -1;
|
|
27
|
+
if (pa[i] > pb[i]) return 1;
|
|
28
|
+
}
|
|
29
|
+
return 0;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** Fetch the latest version from the npm registry. Returns version string or null. */
|
|
33
|
+
async function fetchLatestVersion(timeoutMs = 3000) {
|
|
34
|
+
try {
|
|
35
|
+
const controller = new AbortController();
|
|
36
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
37
|
+
const res = await fetch("https://registry.npmjs.org/clawcontrol/latest", {
|
|
38
|
+
signal: controller.signal,
|
|
39
|
+
});
|
|
40
|
+
clearTimeout(timer);
|
|
41
|
+
if (!res.ok) return null;
|
|
42
|
+
const data = await res.json();
|
|
43
|
+
return data.version ?? null;
|
|
44
|
+
} catch {
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** Run bun add -g clawcontrol@latest. Returns true on success. */
|
|
50
|
+
function performUpdate() {
|
|
51
|
+
try {
|
|
52
|
+
execFileSync("bun", ["add", "-g", "clawcontrol@latest"], {
|
|
53
|
+
stdio: "pipe",
|
|
54
|
+
});
|
|
55
|
+
// Verify the new version by re-reading package.json from disk
|
|
56
|
+
const pkgPath = resolve(dirname(fileURLToPath(import.meta.url)), "../package.json");
|
|
57
|
+
const newPkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
|
|
58
|
+
console.log(`\x1b[32m[update] Updated to v${newPkg.version}\x1b[0m`);
|
|
59
|
+
return true;
|
|
60
|
+
} catch (e) {
|
|
61
|
+
if (e.code === "ENOENT") {
|
|
62
|
+
console.error(
|
|
63
|
+
"\x1b[31m[update] Update failed: bun is not installed.\x1b[0m\n" +
|
|
64
|
+
" Install it: curl -fsSL https://bun.sh/install | bash"
|
|
65
|
+
);
|
|
66
|
+
} else {
|
|
67
|
+
const detail = e.stderr ? e.stderr.toString().trim() : e.message;
|
|
68
|
+
console.error(`\x1b[31m[update] Update failed: ${detail}\x1b[0m`);
|
|
69
|
+
}
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// ── --version ────────────────────────────────────────────────────────
|
|
75
|
+
|
|
15
76
|
if (args.includes("--version") || args.includes("-v")) {
|
|
16
|
-
|
|
17
|
-
const pkg = require("../package.json");
|
|
18
|
-
console.log(`clawcontrol v${pkg.version}`);
|
|
77
|
+
console.log(`clawcontrol v${currentVersion}`);
|
|
19
78
|
process.exit(0);
|
|
20
79
|
}
|
|
21
80
|
|
|
81
|
+
// ── --help ───────────────────────────────────────────────────────────
|
|
82
|
+
|
|
22
83
|
if (args.includes("--help") || args.includes("-h")) {
|
|
23
|
-
const require = createRequire(import.meta.url);
|
|
24
|
-
const pkg = require("../package.json");
|
|
25
84
|
console.log(`
|
|
26
|
-
clawcontrol v${
|
|
85
|
+
clawcontrol v${currentVersion}
|
|
27
86
|
${pkg.description}
|
|
28
87
|
|
|
29
88
|
Usage:
|
|
30
89
|
clawcontrol Launch the interactive TUI
|
|
31
90
|
clawcontrol --help Show this help message
|
|
32
91
|
clawcontrol --version Show the version number
|
|
92
|
+
clawcontrol --update Update to the latest version
|
|
33
93
|
|
|
34
94
|
Options:
|
|
35
95
|
-h, --help Show this help message
|
|
36
96
|
-v, --version Show the version number
|
|
97
|
+
-u, --update Update clawcontrol to the latest version
|
|
98
|
+
|
|
99
|
+
Environment:
|
|
100
|
+
CLAWCONTROL_SKIP_UPDATE=1 Skip the automatic update check on startup
|
|
37
101
|
|
|
38
102
|
Documentation & source:
|
|
39
103
|
https://github.com/ipenywis/clawcontrol
|
|
@@ -41,6 +105,51 @@ Documentation & source:
|
|
|
41
105
|
process.exit(0);
|
|
42
106
|
}
|
|
43
107
|
|
|
108
|
+
// ── --update (manual) ────────────────────────────────────────────────
|
|
109
|
+
|
|
110
|
+
if (args.includes("--update") || args.includes("-u")) {
|
|
111
|
+
console.log("\x1b[36m[update] Checking for updates...\x1b[0m");
|
|
112
|
+
const latest = await fetchLatestVersion(10000); // generous 10s timeout for manual
|
|
113
|
+
if (latest === null) {
|
|
114
|
+
console.error("\x1b[31m[update] Could not reach npm registry.\x1b[0m");
|
|
115
|
+
process.exit(1);
|
|
116
|
+
}
|
|
117
|
+
if (compareSemver(currentVersion, latest) >= 0) {
|
|
118
|
+
console.log(`\x1b[32m[update] Already up to date (v${currentVersion})\x1b[0m`);
|
|
119
|
+
process.exit(0);
|
|
120
|
+
}
|
|
121
|
+
console.log(`\x1b[36m[update] New version available: v${currentVersion} -> v${latest}\x1b[0m`);
|
|
122
|
+
console.log("\x1b[36m[update] Updating clawcontrol...\x1b[0m");
|
|
123
|
+
const ok = performUpdate();
|
|
124
|
+
process.exit(ok ? 0 : 1);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// ── Auto-update on startup ───────────────────────────────────────────
|
|
128
|
+
|
|
129
|
+
if (process.env.CLAWCONTROL_SKIP_UPDATE !== "1") {
|
|
130
|
+
const latest = await fetchLatestVersion(3000);
|
|
131
|
+
if (latest !== null && compareSemver(currentVersion, latest) < 0) {
|
|
132
|
+
console.log(`\x1b[36m[update] New version available: v${currentVersion} -> v${latest}\x1b[0m`);
|
|
133
|
+
console.log("\x1b[36m[update] Updating clawcontrol...\x1b[0m");
|
|
134
|
+
const ok = performUpdate();
|
|
135
|
+
if (ok) {
|
|
136
|
+
console.log("\x1b[36m[update] Restarting with updated version...\x1b[0m");
|
|
137
|
+
try {
|
|
138
|
+
execFileSync(process.argv[0], process.argv.slice(1), {
|
|
139
|
+
stdio: "inherit",
|
|
140
|
+
env: { ...process.env, CLAWCONTROL_SKIP_UPDATE: "1" },
|
|
141
|
+
});
|
|
142
|
+
process.exit(0);
|
|
143
|
+
} catch (e) {
|
|
144
|
+
process.exit(e.status ?? 1);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
// If update failed, fall through and run the current version
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// ── Launch the TUI via bun ───────────────────────────────────────────
|
|
152
|
+
|
|
44
153
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
45
154
|
const script = resolve(__dirname, "../dist/index.js");
|
|
46
155
|
|
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 { useState as useState13, useCallback as useCallback4, useRef as useRef8 } from "react";
|
|
8
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 = {
|
|
@@ -149,33 +151,61 @@ var COMMANDS = [
|
|
|
149
151
|
{ name: "/logs", description: "View deployment logs" },
|
|
150
152
|
{ name: "/dashboard", description: "Open OpenClaw dashboard in browser" },
|
|
151
153
|
{ name: "/destroy", description: "Destroy a deployment" },
|
|
154
|
+
{ name: "/channels", description: "View configured channels" },
|
|
152
155
|
{ name: "/templates", description: "Manage deployment templates" },
|
|
153
156
|
{ name: "/help", description: "Show help" }
|
|
154
157
|
];
|
|
155
158
|
function Home({ context }) {
|
|
156
159
|
const [inputValue, setInputValue] = useState("");
|
|
157
160
|
const [error, setError] = useState(null);
|
|
161
|
+
const [selectedSuggestionIndex, setSelectedSuggestionIndex] = useState(-1);
|
|
162
|
+
const viewMap = {
|
|
163
|
+
"/new": "new",
|
|
164
|
+
"/list": "list",
|
|
165
|
+
"/deploy": "deploy",
|
|
166
|
+
"/status": "status",
|
|
167
|
+
"/ssh": "ssh",
|
|
168
|
+
"/logs": "logs",
|
|
169
|
+
"/dashboard": "dashboard",
|
|
170
|
+
"/destroy": "destroy",
|
|
171
|
+
"/channels": "channels",
|
|
172
|
+
"/templates": "templates",
|
|
173
|
+
"/help": "help"
|
|
174
|
+
};
|
|
175
|
+
const filteredCommands = inputValue.length >= 2 && inputValue.startsWith("/") ? COMMANDS.filter((cmd) => cmd.name.startsWith(inputValue.toLowerCase())) : [];
|
|
176
|
+
const stateRef = useRef({ selectedSuggestionIndex, filteredCommands, inputValue });
|
|
177
|
+
stateRef.current = { selectedSuggestionIndex, filteredCommands, inputValue };
|
|
158
178
|
const handleCommand = (command) => {
|
|
159
179
|
const cmd = command.trim().toLowerCase();
|
|
160
180
|
setError(null);
|
|
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
181
|
if (viewMap[cmd]) {
|
|
174
182
|
context.navigateTo(viewMap[cmd]);
|
|
175
183
|
} else if (cmd.startsWith("/")) {
|
|
176
184
|
setError(`Unknown command: ${cmd}. Type /help for available commands.`);
|
|
177
185
|
}
|
|
178
186
|
};
|
|
187
|
+
useKeyboard((key) => {
|
|
188
|
+
const s = stateRef.current;
|
|
189
|
+
if (s.filteredCommands.length === 0) return;
|
|
190
|
+
if (key.name === "down") {
|
|
191
|
+
setSelectedSuggestionIndex(
|
|
192
|
+
(prev) => prev < s.filteredCommands.length - 1 ? prev + 1 : 0
|
|
193
|
+
);
|
|
194
|
+
} else if (key.name === "up") {
|
|
195
|
+
setSelectedSuggestionIndex(
|
|
196
|
+
(prev) => prev > 0 ? prev - 1 : s.filteredCommands.length - 1
|
|
197
|
+
);
|
|
198
|
+
} else if (key.name === "tab") {
|
|
199
|
+
if (s.selectedSuggestionIndex >= 0 && s.selectedSuggestionIndex < s.filteredCommands.length) {
|
|
200
|
+
const cmd = s.filteredCommands[s.selectedSuggestionIndex].name;
|
|
201
|
+
setInputValue("");
|
|
202
|
+
setSelectedSuggestionIndex(-1);
|
|
203
|
+
handleCommand(cmd);
|
|
204
|
+
}
|
|
205
|
+
} else if (key.name === "escape") {
|
|
206
|
+
setSelectedSuggestionIndex(-1);
|
|
207
|
+
}
|
|
208
|
+
});
|
|
179
209
|
return /* @__PURE__ */ jsxs("box", { flexDirection: "column", width: "100%", height: "100%", children: [
|
|
180
210
|
/* @__PURE__ */ jsx(
|
|
181
211
|
"scrollbox",
|
|
@@ -278,9 +308,19 @@ function Home({ context }) {
|
|
|
278
308
|
placeholder: "Type a command (e.g., /new)...",
|
|
279
309
|
focused: true,
|
|
280
310
|
width: "100%",
|
|
281
|
-
onInput: (value) =>
|
|
311
|
+
onInput: (value) => {
|
|
312
|
+
setInputValue(value);
|
|
313
|
+
const matches = value.length >= 2 && value.startsWith("/") ? COMMANDS.filter((cmd) => cmd.name.startsWith(value.toLowerCase())) : [];
|
|
314
|
+
setSelectedSuggestionIndex(matches.length > 0 ? 0 : -1);
|
|
315
|
+
},
|
|
282
316
|
onSubmit: (value) => {
|
|
283
|
-
|
|
317
|
+
const s = stateRef.current;
|
|
318
|
+
if (s.selectedSuggestionIndex >= 0 && s.selectedSuggestionIndex < s.filteredCommands.length) {
|
|
319
|
+
const cmd = s.filteredCommands[s.selectedSuggestionIndex].name;
|
|
320
|
+
setInputValue("");
|
|
321
|
+
setSelectedSuggestionIndex(-1);
|
|
322
|
+
handleCommand(cmd);
|
|
323
|
+
} else if (typeof value === "string" && typeof value.trim === "function" && value.trim()) {
|
|
284
324
|
handleCommand(value);
|
|
285
325
|
setInputValue("");
|
|
286
326
|
}
|
|
@@ -290,14 +330,22 @@ function Home({ context }) {
|
|
|
290
330
|
}
|
|
291
331
|
)
|
|
292
332
|
}
|
|
293
|
-
)
|
|
333
|
+
),
|
|
334
|
+
filteredCommands.length > 0 && /* @__PURE__ */ jsx("box", { flexDirection: "column", paddingLeft: 1, backgroundColor: t.bg.elevated, children: filteredCommands.map((cmd, i) => {
|
|
335
|
+
const selected = i === selectedSuggestionIndex;
|
|
336
|
+
return /* @__PURE__ */ jsxs("box", { flexDirection: "row", height: 1, overflow: "hidden", backgroundColor: selected ? t.selection.bg : t.bg.elevated, children: [
|
|
337
|
+
/* @__PURE__ */ jsx("text", { fg: selected ? t.selection.indicator : t.fg.muted, children: selected ? "> " : " " }),
|
|
338
|
+
/* @__PURE__ */ jsx("text", { fg: selected ? t.accent : t.fg.primary, width: 14, children: cmd.name }),
|
|
339
|
+
/* @__PURE__ */ jsx("text", { fg: t.fg.secondary, children: cmd.description })
|
|
340
|
+
] }, cmd.name);
|
|
341
|
+
}) })
|
|
294
342
|
] })
|
|
295
343
|
] });
|
|
296
344
|
}
|
|
297
345
|
|
|
298
346
|
// src/components/NewDeployment.tsx
|
|
299
|
-
import { useState as useState2, useRef, useEffect } from "react";
|
|
300
|
-
import { useKeyboard } from "@opentui/react";
|
|
347
|
+
import { useState as useState2, useRef as useRef2, useEffect } from "react";
|
|
348
|
+
import { useKeyboard as useKeyboard2 } from "@opentui/react";
|
|
301
349
|
import { appendFileSync } from "fs";
|
|
302
350
|
import { homedir as homedir3 } from "os";
|
|
303
351
|
import { join as join4 } from "path";
|
|
@@ -1428,7 +1476,7 @@ function NewDeployment({ context }) {
|
|
|
1428
1476
|
setSelectedSavedKeyIndex(0);
|
|
1429
1477
|
}
|
|
1430
1478
|
}, [step]);
|
|
1431
|
-
const stateRef =
|
|
1479
|
+
const stateRef = useRef2({
|
|
1432
1480
|
name,
|
|
1433
1481
|
provider,
|
|
1434
1482
|
apiKey,
|
|
@@ -1558,7 +1606,7 @@ function NewDeployment({ context }) {
|
|
|
1558
1606
|
setError(`Failed to ${s.editMode === "edit" ? "update" : "create"} deployment: ${err instanceof Error ? err.message : String(err)}`);
|
|
1559
1607
|
}
|
|
1560
1608
|
};
|
|
1561
|
-
|
|
1609
|
+
useKeyboard2((key) => {
|
|
1562
1610
|
const currentState = stateRef.current;
|
|
1563
1611
|
debugLog(`useKeyboard: key=${key.name}, step=${currentState.step}`);
|
|
1564
1612
|
if (currentState.step === "template_choice") {
|
|
@@ -2811,8 +2859,8 @@ function NewDeployment({ context }) {
|
|
|
2811
2859
|
}
|
|
2812
2860
|
|
|
2813
2861
|
// src/components/ListView.tsx
|
|
2814
|
-
import { useState as useState3, useRef as
|
|
2815
|
-
import { useKeyboard as
|
|
2862
|
+
import { useState as useState3, useRef as useRef3 } from "react";
|
|
2863
|
+
import { useKeyboard as useKeyboard3 } from "@opentui/react";
|
|
2816
2864
|
import { Fragment, jsx as jsx3, jsxs as jsxs3 } from "@opentui/react/jsx-runtime";
|
|
2817
2865
|
function ListView({ context }) {
|
|
2818
2866
|
const [viewState, setViewState] = useState3("listing");
|
|
@@ -2822,9 +2870,9 @@ function ListView({ context }) {
|
|
|
2822
2870
|
const [deletedName, setDeletedName] = useState3("");
|
|
2823
2871
|
const deployments = context.deployments;
|
|
2824
2872
|
const selectedDeployment = deployments[selectedIndex];
|
|
2825
|
-
const stateRef =
|
|
2873
|
+
const stateRef = useRef3({ viewState, selectedIndex });
|
|
2826
2874
|
stateRef.current = { viewState, selectedIndex };
|
|
2827
|
-
|
|
2875
|
+
useKeyboard3((key) => {
|
|
2828
2876
|
const current = stateRef.current;
|
|
2829
2877
|
if (deployments.length === 0) {
|
|
2830
2878
|
context.navigateTo("home");
|
|
@@ -3122,7 +3170,7 @@ function ListView({ context }) {
|
|
|
3122
3170
|
|
|
3123
3171
|
// src/components/DeployView.tsx
|
|
3124
3172
|
import { useState as useState4 } from "react";
|
|
3125
|
-
import { useKeyboard as
|
|
3173
|
+
import { useKeyboard as useKeyboard4 } from "@opentui/react";
|
|
3126
3174
|
import { jsx as jsx4, jsxs as jsxs4 } from "@opentui/react/jsx-runtime";
|
|
3127
3175
|
function DeployView({ context }) {
|
|
3128
3176
|
const [selectedIndex, setSelectedIndex] = useState4(0);
|
|
@@ -3132,7 +3180,7 @@ function DeployView({ context }) {
|
|
|
3132
3180
|
const deployed = deployments.filter((d) => d.state.status === "deployed");
|
|
3133
3181
|
const allDeployments = [...notDeployed, ...deployed];
|
|
3134
3182
|
const selectedDeployment = allDeployments[selectedIndex];
|
|
3135
|
-
|
|
3183
|
+
useKeyboard4((key) => {
|
|
3136
3184
|
if (allDeployments.length === 0) {
|
|
3137
3185
|
context.navigateTo("home");
|
|
3138
3186
|
return;
|
|
@@ -3251,8 +3299,8 @@ function DeployView({ context }) {
|
|
|
3251
3299
|
}
|
|
3252
3300
|
|
|
3253
3301
|
// src/components/DeployingView.tsx
|
|
3254
|
-
import { useState as useState5, useEffect as useEffect2, useCallback, useRef as
|
|
3255
|
-
import { useKeyboard as
|
|
3302
|
+
import { useState as useState5, useEffect as useEffect2, useCallback, useRef as useRef4 } from "react";
|
|
3303
|
+
import { useKeyboard as useKeyboard5 } from "@opentui/react";
|
|
3256
3304
|
import open from "open";
|
|
3257
3305
|
|
|
3258
3306
|
// src/services/ssh.ts
|
|
@@ -4713,6 +4761,12 @@ function detectTerminal() {
|
|
|
4713
4761
|
return { app: "konsole", canOpenTab: true };
|
|
4714
4762
|
}
|
|
4715
4763
|
}
|
|
4764
|
+
if (os === "win32") {
|
|
4765
|
+
if (env.WT_SESSION) {
|
|
4766
|
+
return { app: "windows-terminal", canOpenTab: true };
|
|
4767
|
+
}
|
|
4768
|
+
return { app: "powershell", canOpenTab: false };
|
|
4769
|
+
}
|
|
4716
4770
|
if (os === "darwin") {
|
|
4717
4771
|
return { app: "terminal.app", canOpenTab: true };
|
|
4718
4772
|
}
|
|
@@ -4747,11 +4801,19 @@ function openTerminalWithCommand(command) {
|
|
|
4747
4801
|
return openKonsole(command);
|
|
4748
4802
|
case "xfce4-terminal":
|
|
4749
4803
|
return openXfce4Terminal(command);
|
|
4804
|
+
case "windows-terminal":
|
|
4805
|
+
return openWindowsTerminal(command);
|
|
4806
|
+
case "powershell":
|
|
4807
|
+
return openPowerShell(command);
|
|
4808
|
+
case "cmd":
|
|
4809
|
+
return openCmd(command);
|
|
4750
4810
|
default:
|
|
4751
4811
|
if (os === "darwin") {
|
|
4752
4812
|
return openTerminalApp(command);
|
|
4753
4813
|
} else if (os === "linux") {
|
|
4754
4814
|
return openLinuxFallback(command);
|
|
4815
|
+
} else if (os === "win32") {
|
|
4816
|
+
return openWindowsFallback(command);
|
|
4755
4817
|
}
|
|
4756
4818
|
return { success: false, error: `Unsupported terminal or OS: ${terminal.app}` };
|
|
4757
4819
|
}
|
|
@@ -4947,6 +5009,9 @@ function openCursorTerminal(command) {
|
|
|
4947
5009
|
}
|
|
4948
5010
|
return openTerminalApp(command);
|
|
4949
5011
|
}
|
|
5012
|
+
if (platform() === "win32") {
|
|
5013
|
+
return openWindowsFallback(command);
|
|
5014
|
+
}
|
|
4950
5015
|
return openLinuxFallback(command);
|
|
4951
5016
|
}
|
|
4952
5017
|
function openVSCodeTerminal(command) {
|
|
@@ -5018,6 +5083,62 @@ function openLinuxFallback(command) {
|
|
|
5018
5083
|
}
|
|
5019
5084
|
return { success: false, error: "Could not find a supported terminal emulator" };
|
|
5020
5085
|
}
|
|
5086
|
+
function createTempScriptWindows(command) {
|
|
5087
|
+
const scriptPath = join5(tmpdir(), `clawcontrol-${process.pid}-${Date.now()}.cmd`);
|
|
5088
|
+
const content = [
|
|
5089
|
+
"@echo off",
|
|
5090
|
+
command,
|
|
5091
|
+
`del "${scriptPath}"`,
|
|
5092
|
+
""
|
|
5093
|
+
].join("\r\n");
|
|
5094
|
+
writeFileSync5(scriptPath, content);
|
|
5095
|
+
return scriptPath;
|
|
5096
|
+
}
|
|
5097
|
+
function openWindowsTerminal(command) {
|
|
5098
|
+
const script = createTempScriptWindows(command);
|
|
5099
|
+
const proc = spawn("wt.exe", ["new-tab", "cmd", "/c", script], {
|
|
5100
|
+
stdio: "ignore",
|
|
5101
|
+
detached: true,
|
|
5102
|
+
shell: true
|
|
5103
|
+
});
|
|
5104
|
+
proc.unref();
|
|
5105
|
+
return { success: true };
|
|
5106
|
+
}
|
|
5107
|
+
function openPowerShell(command) {
|
|
5108
|
+
const proc = spawn("powershell.exe", [
|
|
5109
|
+
"-NoProfile",
|
|
5110
|
+
"Start-Process",
|
|
5111
|
+
"powershell",
|
|
5112
|
+
"-ArgumentList",
|
|
5113
|
+
`'-NoExit -Command "${command.replace(/"/g, '`"')}"'`
|
|
5114
|
+
], {
|
|
5115
|
+
stdio: "ignore",
|
|
5116
|
+
detached: true,
|
|
5117
|
+
shell: true
|
|
5118
|
+
});
|
|
5119
|
+
proc.unref();
|
|
5120
|
+
return { success: true };
|
|
5121
|
+
}
|
|
5122
|
+
function openCmd(command) {
|
|
5123
|
+
const script = createTempScriptWindows(command);
|
|
5124
|
+
const proc = spawn("cmd.exe", ["/c", "start", "cmd", "/k", script], {
|
|
5125
|
+
stdio: "ignore",
|
|
5126
|
+
detached: true,
|
|
5127
|
+
shell: true
|
|
5128
|
+
});
|
|
5129
|
+
proc.unref();
|
|
5130
|
+
return { success: true };
|
|
5131
|
+
}
|
|
5132
|
+
function openWindowsFallback(command) {
|
|
5133
|
+
try {
|
|
5134
|
+
const result = spawnSync("where", ["wt.exe"], { stdio: "pipe", timeout: 2e3 });
|
|
5135
|
+
if (result.status === 0) {
|
|
5136
|
+
return openWindowsTerminal(command);
|
|
5137
|
+
}
|
|
5138
|
+
} catch {
|
|
5139
|
+
}
|
|
5140
|
+
return openPowerShell(command);
|
|
5141
|
+
}
|
|
5021
5142
|
function getTerminalDisplayName(app) {
|
|
5022
5143
|
const names = {
|
|
5023
5144
|
"terminal.app": "Terminal.app",
|
|
@@ -5033,6 +5154,9 @@ function getTerminalDisplayName(app) {
|
|
|
5033
5154
|
"konsole": "Konsole",
|
|
5034
5155
|
"xfce4-terminal": "XFCE Terminal",
|
|
5035
5156
|
"xterm": "XTerm",
|
|
5157
|
+
"windows-terminal": "Windows Terminal",
|
|
5158
|
+
"powershell": "PowerShell",
|
|
5159
|
+
"cmd": "Command Prompt",
|
|
5036
5160
|
"unknown": "System Terminal"
|
|
5037
5161
|
};
|
|
5038
5162
|
return names[app];
|
|
@@ -5094,9 +5218,9 @@ function DeployingView({ context }) {
|
|
|
5094
5218
|
setDeployState("deploying");
|
|
5095
5219
|
addLog("Terminal session confirmed complete, continuing deployment...");
|
|
5096
5220
|
}, [terminalResolve, addLog]);
|
|
5097
|
-
const stateRef =
|
|
5221
|
+
const stateRef = useRef4({ deployState, terminalResolve });
|
|
5098
5222
|
stateRef.current = { deployState, terminalResolve };
|
|
5099
|
-
|
|
5223
|
+
useKeyboard5((key) => {
|
|
5100
5224
|
const currentState = stateRef.current;
|
|
5101
5225
|
if (currentState.deployState === "waiting_terminal") {
|
|
5102
5226
|
if (key.name === "return") {
|
|
@@ -5344,7 +5468,7 @@ function DeployingView({ context }) {
|
|
|
5344
5468
|
|
|
5345
5469
|
// src/components/StatusView.tsx
|
|
5346
5470
|
import { useState as useState6 } from "react";
|
|
5347
|
-
import { useKeyboard as
|
|
5471
|
+
import { useKeyboard as useKeyboard6 } from "@opentui/react";
|
|
5348
5472
|
import { jsx as jsx6, jsxs as jsxs6 } from "@opentui/react/jsx-runtime";
|
|
5349
5473
|
function StatusView({ context }) {
|
|
5350
5474
|
const [selectedIndex, setSelectedIndex] = useState6(0);
|
|
@@ -5374,7 +5498,7 @@ function StatusView({ context }) {
|
|
|
5374
5498
|
setHealthStatus((prev) => new Map(prev).set(name, health));
|
|
5375
5499
|
setChecking(null);
|
|
5376
5500
|
};
|
|
5377
|
-
|
|
5501
|
+
useKeyboard6((key) => {
|
|
5378
5502
|
if (deployments.length === 0) {
|
|
5379
5503
|
context.navigateTo("home");
|
|
5380
5504
|
return;
|
|
@@ -5528,7 +5652,7 @@ function StatusView({ context }) {
|
|
|
5528
5652
|
|
|
5529
5653
|
// src/components/SSHView.tsx
|
|
5530
5654
|
import { useState as useState7, useCallback as useCallback2 } from "react";
|
|
5531
|
-
import { useKeyboard as
|
|
5655
|
+
import { useKeyboard as useKeyboard7 } from "@opentui/react";
|
|
5532
5656
|
import { jsx as jsx7, jsxs as jsxs7 } from "@opentui/react/jsx-runtime";
|
|
5533
5657
|
function SSHView({ context }) {
|
|
5534
5658
|
const [viewState, setViewState] = useState7("selecting");
|
|
@@ -5554,7 +5678,7 @@ function SSHView({ context }) {
|
|
|
5554
5678
|
}
|
|
5555
5679
|
}, []);
|
|
5556
5680
|
const selectedDeployment = deployedDeployments[selectedIndex];
|
|
5557
|
-
|
|
5681
|
+
useKeyboard7((key) => {
|
|
5558
5682
|
if (deployedDeployments.length === 0) {
|
|
5559
5683
|
context.navigateTo("home");
|
|
5560
5684
|
return;
|
|
@@ -5709,8 +5833,8 @@ function SSHView({ context }) {
|
|
|
5709
5833
|
}
|
|
5710
5834
|
|
|
5711
5835
|
// src/components/LogsView.tsx
|
|
5712
|
-
import { useState as useState8, useEffect as useEffect3, useRef as
|
|
5713
|
-
import { useKeyboard as
|
|
5836
|
+
import { useState as useState8, useEffect as useEffect3, useRef as useRef5 } from "react";
|
|
5837
|
+
import { useKeyboard as useKeyboard8 } from "@opentui/react";
|
|
5714
5838
|
import { Fragment as Fragment2, jsx as jsx8, jsxs as jsxs8 } from "@opentui/react/jsx-runtime";
|
|
5715
5839
|
function parseLogLine(line) {
|
|
5716
5840
|
if (!line.trim()) return null;
|
|
@@ -5736,8 +5860,8 @@ function LogsView({ context }) {
|
|
|
5736
5860
|
const [error, setError] = useState8(null);
|
|
5737
5861
|
const [autoRefresh, setAutoRefresh] = useState8(false);
|
|
5738
5862
|
const [lastFetched, setLastFetched] = useState8(null);
|
|
5739
|
-
const sshRef =
|
|
5740
|
-
const refreshIntervalRef =
|
|
5863
|
+
const sshRef = useRef5(null);
|
|
5864
|
+
const refreshIntervalRef = useRef5(null);
|
|
5741
5865
|
const deployedDeployments = context.deployments.filter(
|
|
5742
5866
|
(d) => d.state.status === "deployed" && d.state.serverIp
|
|
5743
5867
|
);
|
|
@@ -5788,7 +5912,7 @@ function LogsView({ context }) {
|
|
|
5788
5912
|
setLogs([]);
|
|
5789
5913
|
};
|
|
5790
5914
|
const selectedDeployment = deployedDeployments[selectedIndex];
|
|
5791
|
-
|
|
5915
|
+
useKeyboard8((key) => {
|
|
5792
5916
|
if (deployedDeployments.length === 0) {
|
|
5793
5917
|
context.navigateTo("home");
|
|
5794
5918
|
return;
|
|
@@ -5947,7 +6071,7 @@ function LogsView({ context }) {
|
|
|
5947
6071
|
|
|
5948
6072
|
// src/components/DestroyView.tsx
|
|
5949
6073
|
import { useState as useState9 } from "react";
|
|
5950
|
-
import { useKeyboard as
|
|
6074
|
+
import { useKeyboard as useKeyboard9 } from "@opentui/react";
|
|
5951
6075
|
import { jsx as jsx9, jsxs as jsxs9 } from "@opentui/react/jsx-runtime";
|
|
5952
6076
|
function DestroyView({ context }) {
|
|
5953
6077
|
const [viewState, setViewState] = useState9("selecting");
|
|
@@ -5998,7 +6122,7 @@ function DestroyView({ context }) {
|
|
|
5998
6122
|
}
|
|
5999
6123
|
};
|
|
6000
6124
|
const selectedDeployment = deployments[selectedIndex];
|
|
6001
|
-
|
|
6125
|
+
useKeyboard9((key) => {
|
|
6002
6126
|
if (deployments.length === 0) {
|
|
6003
6127
|
context.navigateTo("home");
|
|
6004
6128
|
return;
|
|
@@ -6189,10 +6313,10 @@ function DestroyView({ context }) {
|
|
|
6189
6313
|
}
|
|
6190
6314
|
|
|
6191
6315
|
// src/components/HelpView.tsx
|
|
6192
|
-
import { useKeyboard as
|
|
6316
|
+
import { useKeyboard as useKeyboard10 } from "@opentui/react";
|
|
6193
6317
|
import { jsx as jsx10, jsxs as jsxs10 } from "@opentui/react/jsx-runtime";
|
|
6194
6318
|
function HelpView({ context }) {
|
|
6195
|
-
|
|
6319
|
+
useKeyboard10(() => {
|
|
6196
6320
|
context.navigateTo("home");
|
|
6197
6321
|
});
|
|
6198
6322
|
return /* @__PURE__ */ jsxs10("box", { flexDirection: "column", width: "100%", padding: 1, children: [
|
|
@@ -6319,8 +6443,8 @@ function HelpView({ context }) {
|
|
|
6319
6443
|
}
|
|
6320
6444
|
|
|
6321
6445
|
// src/components/TemplatesView.tsx
|
|
6322
|
-
import { useState as useState10, useRef as
|
|
6323
|
-
import { useKeyboard as
|
|
6446
|
+
import { useState as useState10, useRef as useRef6, useEffect as useEffect4 } from "react";
|
|
6447
|
+
import { useKeyboard as useKeyboard11 } from "@opentui/react";
|
|
6324
6448
|
import { Fragment as Fragment3, jsx as jsx11, jsxs as jsxs11 } from "@opentui/react/jsx-runtime";
|
|
6325
6449
|
var DO_DROPLET_SIZES2 = [
|
|
6326
6450
|
{ slug: "s-1vcpu-2gb", label: "1 vCPU, 2GB RAM, 50GB SSD", price: "$12/mo" },
|
|
@@ -6343,7 +6467,7 @@ function TemplatesView({ context }) {
|
|
|
6343
6467
|
const [forkAiProvider, setForkAiProvider] = useState10("");
|
|
6344
6468
|
const [forkAiProviderIndex, setForkAiProviderIndex] = useState10(0);
|
|
6345
6469
|
const [forkModel, setForkModel] = useState10("");
|
|
6346
|
-
const stateRef =
|
|
6470
|
+
const stateRef = useRef6({
|
|
6347
6471
|
viewState,
|
|
6348
6472
|
selectedIndex,
|
|
6349
6473
|
selectedTemplate,
|
|
@@ -6457,7 +6581,7 @@ function TemplatesView({ context }) {
|
|
|
6457
6581
|
setViewState("viewing");
|
|
6458
6582
|
}
|
|
6459
6583
|
};
|
|
6460
|
-
|
|
6584
|
+
useKeyboard11((key) => {
|
|
6461
6585
|
const s = stateRef.current;
|
|
6462
6586
|
if (s.viewState === "listing") {
|
|
6463
6587
|
if (key.name === "up") {
|
|
@@ -6866,7 +6990,7 @@ function TemplatesView({ context }) {
|
|
|
6866
6990
|
|
|
6867
6991
|
// src/components/DashboardView.tsx
|
|
6868
6992
|
import { useState as useState11, useCallback as useCallback3 } from "react";
|
|
6869
|
-
import { useKeyboard as
|
|
6993
|
+
import { useKeyboard as useKeyboard12, useRenderer } from "@opentui/react";
|
|
6870
6994
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
6871
6995
|
import { platform as platform2 } from "os";
|
|
6872
6996
|
|
|
@@ -7035,7 +7159,7 @@ function DashboardView({ context }) {
|
|
|
7035
7159
|
setViewState("error");
|
|
7036
7160
|
}
|
|
7037
7161
|
}, []);
|
|
7038
|
-
|
|
7162
|
+
useKeyboard12((key) => {
|
|
7039
7163
|
if (viewState === "selecting") {
|
|
7040
7164
|
if (deployedDeployments.length === 0) {
|
|
7041
7165
|
if (key.name === "escape" || key.name === "return") {
|
|
@@ -7293,22 +7417,175 @@ function DashboardView({ context }) {
|
|
|
7293
7417
|
return null;
|
|
7294
7418
|
}
|
|
7295
7419
|
|
|
7420
|
+
// src/components/ChannelsView.tsx
|
|
7421
|
+
import { useState as useState12, useRef as useRef7 } from "react";
|
|
7422
|
+
import { useKeyboard as useKeyboard13 } from "@opentui/react";
|
|
7423
|
+
import { Fragment as Fragment4, jsx as jsx13, jsxs as jsxs13 } from "@opentui/react/jsx-runtime";
|
|
7424
|
+
function maskToken(token) {
|
|
7425
|
+
if (token.length <= 10) return "****";
|
|
7426
|
+
return token.slice(0, 6) + "..." + token.slice(-4);
|
|
7427
|
+
}
|
|
7428
|
+
function ChannelsView({ context }) {
|
|
7429
|
+
const [viewState, setViewState] = useState12("listing");
|
|
7430
|
+
const [selectedIndex, setSelectedIndex] = useState12(0);
|
|
7431
|
+
const [revealed, setRevealed] = useState12(false);
|
|
7432
|
+
const deployments = context.deployments;
|
|
7433
|
+
const selectedDeployment = deployments[selectedIndex];
|
|
7434
|
+
const stateRef = useRef7({ viewState, selectedIndex });
|
|
7435
|
+
stateRef.current = { viewState, selectedIndex };
|
|
7436
|
+
useKeyboard13((key) => {
|
|
7437
|
+
const current = stateRef.current;
|
|
7438
|
+
if (current.viewState === "listing") {
|
|
7439
|
+
if (deployments.length === 0) {
|
|
7440
|
+
if (key.name === "escape") {
|
|
7441
|
+
context.navigateTo("home");
|
|
7442
|
+
}
|
|
7443
|
+
return;
|
|
7444
|
+
}
|
|
7445
|
+
if (key.name === "up" && current.selectedIndex > 0) {
|
|
7446
|
+
setSelectedIndex(current.selectedIndex - 1);
|
|
7447
|
+
} else if (key.name === "down" && current.selectedIndex < deployments.length - 1) {
|
|
7448
|
+
setSelectedIndex(current.selectedIndex + 1);
|
|
7449
|
+
} else if (key.name === "return") {
|
|
7450
|
+
setRevealed(false);
|
|
7451
|
+
setViewState("detail");
|
|
7452
|
+
} else if (key.name === "escape") {
|
|
7453
|
+
context.navigateTo("home");
|
|
7454
|
+
}
|
|
7455
|
+
} else if (current.viewState === "detail") {
|
|
7456
|
+
if (key.name === "r") {
|
|
7457
|
+
setRevealed((prev) => !prev);
|
|
7458
|
+
} else if (key.name === "escape") {
|
|
7459
|
+
setRevealed(false);
|
|
7460
|
+
setViewState("listing");
|
|
7461
|
+
}
|
|
7462
|
+
}
|
|
7463
|
+
});
|
|
7464
|
+
if (deployments.length === 0) {
|
|
7465
|
+
return /* @__PURE__ */ jsxs13("box", { flexDirection: "column", width: "100%", padding: 1, children: [
|
|
7466
|
+
/* @__PURE__ */ jsxs13("box", { flexDirection: "row", marginBottom: 2, children: [
|
|
7467
|
+
/* @__PURE__ */ jsx13("text", { fg: t.accent, children: "/channels" }),
|
|
7468
|
+
/* @__PURE__ */ jsx13("text", { fg: t.fg.secondary, children: " - Communication Channels" })
|
|
7469
|
+
] }),
|
|
7470
|
+
/* @__PURE__ */ jsxs13(
|
|
7471
|
+
"box",
|
|
7472
|
+
{
|
|
7473
|
+
flexDirection: "column",
|
|
7474
|
+
borderStyle: "single",
|
|
7475
|
+
borderColor: t.border.default,
|
|
7476
|
+
padding: 1,
|
|
7477
|
+
children: [
|
|
7478
|
+
/* @__PURE__ */ jsx13("text", { fg: t.status.warning, children: "No deployments found!" }),
|
|
7479
|
+
/* @__PURE__ */ jsx13("text", { fg: t.fg.secondary, marginTop: 1, children: "Run /new to create a deployment first." })
|
|
7480
|
+
]
|
|
7481
|
+
}
|
|
7482
|
+
),
|
|
7483
|
+
/* @__PURE__ */ jsx13("text", { fg: t.fg.muted, marginTop: 2, children: "Esc: Back" })
|
|
7484
|
+
] });
|
|
7485
|
+
}
|
|
7486
|
+
if (viewState === "detail" && selectedDeployment) {
|
|
7487
|
+
const dep = selectedDeployment;
|
|
7488
|
+
const agent = dep.config.openclawAgent;
|
|
7489
|
+
return /* @__PURE__ */ jsxs13("box", { flexDirection: "column", width: "100%", padding: 1, children: [
|
|
7490
|
+
/* @__PURE__ */ jsxs13("box", { flexDirection: "row", marginBottom: 2, children: [
|
|
7491
|
+
/* @__PURE__ */ jsx13("text", { fg: t.accent, children: "/channels" }),
|
|
7492
|
+
/* @__PURE__ */ jsxs13("text", { fg: t.fg.secondary, children: [
|
|
7493
|
+
" - ",
|
|
7494
|
+
dep.config.name
|
|
7495
|
+
] })
|
|
7496
|
+
] }),
|
|
7497
|
+
/* @__PURE__ */ jsxs13(
|
|
7498
|
+
"box",
|
|
7499
|
+
{
|
|
7500
|
+
flexDirection: "column",
|
|
7501
|
+
borderStyle: "single",
|
|
7502
|
+
borderColor: t.border.focus,
|
|
7503
|
+
padding: 1,
|
|
7504
|
+
marginBottom: 1,
|
|
7505
|
+
children: [
|
|
7506
|
+
/* @__PURE__ */ jsx13("text", { fg: t.accent, marginBottom: 1, children: "Channel Details" }),
|
|
7507
|
+
/* @__PURE__ */ jsxs13("box", { flexDirection: "row", children: [
|
|
7508
|
+
/* @__PURE__ */ jsx13("text", { fg: t.fg.secondary, width: 20, children: "Deployment:" }),
|
|
7509
|
+
/* @__PURE__ */ jsx13("text", { fg: t.fg.primary, children: dep.config.name })
|
|
7510
|
+
] }),
|
|
7511
|
+
agent ? /* @__PURE__ */ jsxs13(Fragment4, { children: [
|
|
7512
|
+
/* @__PURE__ */ jsxs13("box", { flexDirection: "row", children: [
|
|
7513
|
+
/* @__PURE__ */ jsx13("text", { fg: t.fg.secondary, width: 20, children: "Channel:" }),
|
|
7514
|
+
/* @__PURE__ */ jsx13("text", { fg: t.fg.primary, children: agent.channel })
|
|
7515
|
+
] }),
|
|
7516
|
+
/* @__PURE__ */ jsxs13("box", { flexDirection: "row", children: [
|
|
7517
|
+
/* @__PURE__ */ jsx13("text", { fg: t.fg.secondary, width: 20, children: "Bot Token:" }),
|
|
7518
|
+
/* @__PURE__ */ jsx13("text", { fg: t.fg.primary, children: revealed ? agent.telegramBotToken : maskToken(agent.telegramBotToken) })
|
|
7519
|
+
] }),
|
|
7520
|
+
/* @__PURE__ */ jsxs13("box", { flexDirection: "row", children: [
|
|
7521
|
+
/* @__PURE__ */ jsx13("text", { fg: t.fg.secondary, width: 20, children: "Allowed Users:" }),
|
|
7522
|
+
/* @__PURE__ */ jsx13("text", { fg: t.fg.primary, children: agent.telegramAllowFrom || "Any" })
|
|
7523
|
+
] })
|
|
7524
|
+
] }) : /* @__PURE__ */ jsx13("box", { flexDirection: "row", marginTop: 1, children: /* @__PURE__ */ jsx13("text", { fg: t.fg.muted, children: "No channel configured for this deployment." }) })
|
|
7525
|
+
]
|
|
7526
|
+
}
|
|
7527
|
+
),
|
|
7528
|
+
/* @__PURE__ */ jsx13("text", { fg: t.fg.muted, marginTop: 1, children: agent ? "R: Reveal/hide secrets | Esc: Back to list" : "Esc: Back to list" })
|
|
7529
|
+
] });
|
|
7530
|
+
}
|
|
7531
|
+
return /* @__PURE__ */ jsxs13("box", { flexDirection: "column", width: "100%", padding: 1, children: [
|
|
7532
|
+
/* @__PURE__ */ jsxs13("box", { flexDirection: "row", marginBottom: 2, children: [
|
|
7533
|
+
/* @__PURE__ */ jsx13("text", { fg: t.accent, children: "/channels" }),
|
|
7534
|
+
/* @__PURE__ */ jsxs13("text", { fg: t.fg.secondary, children: [
|
|
7535
|
+
" - Communication Channels (",
|
|
7536
|
+
deployments.length,
|
|
7537
|
+
")"
|
|
7538
|
+
] })
|
|
7539
|
+
] }),
|
|
7540
|
+
/* @__PURE__ */ jsx13(
|
|
7541
|
+
"box",
|
|
7542
|
+
{
|
|
7543
|
+
flexDirection: "column",
|
|
7544
|
+
borderStyle: "single",
|
|
7545
|
+
borderColor: t.border.default,
|
|
7546
|
+
padding: 1,
|
|
7547
|
+
children: deployments.map((dep, index) => {
|
|
7548
|
+
const isSelected = index === selectedIndex;
|
|
7549
|
+
const agent = dep.config.openclawAgent;
|
|
7550
|
+
return /* @__PURE__ */ jsxs13(
|
|
7551
|
+
"box",
|
|
7552
|
+
{
|
|
7553
|
+
flexDirection: "row",
|
|
7554
|
+
backgroundColor: isSelected ? t.selection.bg : void 0,
|
|
7555
|
+
children: [
|
|
7556
|
+
/* @__PURE__ */ jsx13("text", { fg: isSelected ? t.selection.indicator : t.fg.primary, children: isSelected ? "> " : " " }),
|
|
7557
|
+
/* @__PURE__ */ jsx13("text", { fg: isSelected ? t.selection.fg : t.fg.primary, width: 22, children: dep.config.name }),
|
|
7558
|
+
agent ? /* @__PURE__ */ jsxs13(Fragment4, { children: [
|
|
7559
|
+
/* @__PURE__ */ jsx13("text", { fg: isSelected ? t.fg.primary : t.fg.secondary, width: 12, children: agent.channel }),
|
|
7560
|
+
/* @__PURE__ */ jsx13("text", { fg: t.fg.muted, children: agent.telegramAllowFrom ? `users: ${agent.telegramAllowFrom}` : "" })
|
|
7561
|
+
] }) : /* @__PURE__ */ jsx13("text", { fg: t.fg.muted, children: "No channel" })
|
|
7562
|
+
]
|
|
7563
|
+
},
|
|
7564
|
+
dep.config.name
|
|
7565
|
+
);
|
|
7566
|
+
})
|
|
7567
|
+
}
|
|
7568
|
+
),
|
|
7569
|
+
/* @__PURE__ */ jsx13("text", { fg: t.fg.muted, marginTop: 2, children: "Up/Down: Select | Enter: Details | Esc: Back" })
|
|
7570
|
+
] });
|
|
7571
|
+
}
|
|
7572
|
+
|
|
7296
7573
|
// src/App.tsx
|
|
7297
|
-
import { jsx as
|
|
7298
|
-
function App() {
|
|
7574
|
+
import { jsx as jsx14, jsxs as jsxs14 } from "@opentui/react/jsx-runtime";
|
|
7575
|
+
function App({ lacksTrueColor: lacksTrueColor2 }) {
|
|
7299
7576
|
const renderer = useRenderer2();
|
|
7300
|
-
const [currentView, setCurrentView] =
|
|
7301
|
-
const [selectedDeployment, setSelectedDeployment] =
|
|
7302
|
-
const [deployments, setDeployments] =
|
|
7577
|
+
const [currentView, setCurrentView] = useState13("home");
|
|
7578
|
+
const [selectedDeployment, setSelectedDeployment] = useState13(null);
|
|
7579
|
+
const [deployments, setDeployments] = useState13(() => {
|
|
7303
7580
|
try {
|
|
7304
7581
|
return getAllDeployments();
|
|
7305
7582
|
} catch {
|
|
7306
7583
|
return [];
|
|
7307
7584
|
}
|
|
7308
7585
|
});
|
|
7309
|
-
const [selectedTemplate, setSelectedTemplate] =
|
|
7310
|
-
const [editingDeployment, setEditingDeployment] =
|
|
7311
|
-
const wasDraggingRef =
|
|
7586
|
+
const [selectedTemplate, setSelectedTemplate] = useState13(null);
|
|
7587
|
+
const [editingDeployment, setEditingDeployment] = useState13(null);
|
|
7588
|
+
const wasDraggingRef = useRef8(false);
|
|
7312
7589
|
const handleMouseDrag = useCallback4(() => {
|
|
7313
7590
|
wasDraggingRef.current = true;
|
|
7314
7591
|
}, []);
|
|
@@ -7350,34 +7627,36 @@ function App() {
|
|
|
7350
7627
|
const renderView = () => {
|
|
7351
7628
|
switch (currentView) {
|
|
7352
7629
|
case "home":
|
|
7353
|
-
return /* @__PURE__ */
|
|
7630
|
+
return /* @__PURE__ */ jsx14(Home, { context });
|
|
7354
7631
|
case "new":
|
|
7355
|
-
return /* @__PURE__ */
|
|
7632
|
+
return /* @__PURE__ */ jsx14(NewDeployment, { context });
|
|
7356
7633
|
case "list":
|
|
7357
|
-
return /* @__PURE__ */
|
|
7634
|
+
return /* @__PURE__ */ jsx14(ListView, { context });
|
|
7358
7635
|
case "deploy":
|
|
7359
|
-
return /* @__PURE__ */
|
|
7636
|
+
return /* @__PURE__ */ jsx14(DeployView, { context });
|
|
7360
7637
|
case "deploying":
|
|
7361
|
-
return /* @__PURE__ */
|
|
7638
|
+
return /* @__PURE__ */ jsx14(DeployingView, { context });
|
|
7362
7639
|
case "status":
|
|
7363
|
-
return /* @__PURE__ */
|
|
7640
|
+
return /* @__PURE__ */ jsx14(StatusView, { context });
|
|
7364
7641
|
case "ssh":
|
|
7365
|
-
return /* @__PURE__ */
|
|
7642
|
+
return /* @__PURE__ */ jsx14(SSHView, { context });
|
|
7366
7643
|
case "logs":
|
|
7367
|
-
return /* @__PURE__ */
|
|
7644
|
+
return /* @__PURE__ */ jsx14(LogsView, { context });
|
|
7368
7645
|
case "dashboard":
|
|
7369
|
-
return /* @__PURE__ */
|
|
7646
|
+
return /* @__PURE__ */ jsx14(DashboardView, { context });
|
|
7370
7647
|
case "destroy":
|
|
7371
|
-
return /* @__PURE__ */
|
|
7648
|
+
return /* @__PURE__ */ jsx14(DestroyView, { context });
|
|
7372
7649
|
case "help":
|
|
7373
|
-
return /* @__PURE__ */
|
|
7650
|
+
return /* @__PURE__ */ jsx14(HelpView, { context });
|
|
7374
7651
|
case "templates":
|
|
7375
|
-
return /* @__PURE__ */
|
|
7652
|
+
return /* @__PURE__ */ jsx14(TemplatesView, { context });
|
|
7653
|
+
case "channels":
|
|
7654
|
+
return /* @__PURE__ */ jsx14(ChannelsView, { context });
|
|
7376
7655
|
default:
|
|
7377
|
-
return /* @__PURE__ */
|
|
7656
|
+
return /* @__PURE__ */ jsx14(Home, { context });
|
|
7378
7657
|
}
|
|
7379
7658
|
};
|
|
7380
|
-
return /* @__PURE__ */
|
|
7659
|
+
return /* @__PURE__ */ jsxs14(
|
|
7381
7660
|
"scrollbox",
|
|
7382
7661
|
{
|
|
7383
7662
|
width: "100%",
|
|
@@ -7398,13 +7677,29 @@ function App() {
|
|
|
7398
7677
|
verticalScrollbarOptions: {
|
|
7399
7678
|
showArrows: false
|
|
7400
7679
|
},
|
|
7401
|
-
children:
|
|
7680
|
+
children: [
|
|
7681
|
+
lacksTrueColor2 && /* @__PURE__ */ jsx14(
|
|
7682
|
+
"box",
|
|
7683
|
+
{
|
|
7684
|
+
width: "100%",
|
|
7685
|
+
style: {
|
|
7686
|
+
paddingLeft: 1,
|
|
7687
|
+
paddingRight: 1,
|
|
7688
|
+
paddingTop: 0,
|
|
7689
|
+
paddingBottom: 0,
|
|
7690
|
+
backgroundColor: t.status.warning
|
|
7691
|
+
},
|
|
7692
|
+
children: /* @__PURE__ */ jsx14("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." })
|
|
7693
|
+
}
|
|
7694
|
+
),
|
|
7695
|
+
renderView()
|
|
7696
|
+
]
|
|
7402
7697
|
}
|
|
7403
7698
|
);
|
|
7404
7699
|
}
|
|
7405
7700
|
|
|
7406
7701
|
// src/index.tsx
|
|
7407
|
-
import { jsx as
|
|
7702
|
+
import { jsx as jsx15 } from "@opentui/react/jsx-runtime";
|
|
7408
7703
|
var args = process.argv.slice(2);
|
|
7409
7704
|
if (args.includes("--version") || args.includes("-v")) {
|
|
7410
7705
|
const require2 = createRequire(import.meta.url);
|
|
@@ -7433,11 +7728,14 @@ Documentation & source:
|
|
|
7433
7728
|
`);
|
|
7434
7729
|
process.exit(0);
|
|
7435
7730
|
}
|
|
7731
|
+
var isAppleTerminal = process.env.TERM_PROGRAM === "Apple_Terminal";
|
|
7732
|
+
var darwinMajor = process.platform === "darwin" ? parseInt(release(), 10) : Infinity;
|
|
7733
|
+
var lacksTrueColor = isAppleTerminal && darwinMajor < 25;
|
|
7436
7734
|
async function main() {
|
|
7437
7735
|
const renderer = await createCliRenderer({
|
|
7438
7736
|
useMouse: true
|
|
7439
7737
|
});
|
|
7440
7738
|
const root = createRoot(renderer);
|
|
7441
|
-
root.render(/* @__PURE__ */
|
|
7739
|
+
root.render(/* @__PURE__ */ jsx15(App, { lacksTrueColor }));
|
|
7442
7740
|
}
|
|
7443
7741
|
main().catch(console.error);
|