clawcontrol 0.2.0 → 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.
Files changed (2) hide show
  1. package/dist/index.js +197 -56
  2. 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 useState12, useCallback as useCallback4, useRef as useRef6 } from "react";
8
+ import { useState as useState12, useCallback as useCallback4, useRef as useRef7 } 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 = {
@@ -155,27 +157,53 @@ var COMMANDS = [
155
157
  function Home({ context }) {
156
158
  const [inputValue, setInputValue] = useState("");
157
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 };
158
176
  const handleCommand = (command) => {
159
177
  const cmd = command.trim().toLowerCase();
160
178
  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
179
  if (viewMap[cmd]) {
174
180
  context.navigateTo(viewMap[cmd]);
175
181
  } else if (cmd.startsWith("/")) {
176
182
  setError(`Unknown command: ${cmd}. Type /help for available commands.`);
177
183
  }
178
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
+ });
179
207
  return /* @__PURE__ */ jsxs("box", { flexDirection: "column", width: "100%", height: "100%", children: [
180
208
  /* @__PURE__ */ jsx(
181
209
  "scrollbox",
@@ -278,9 +306,19 @@ function Home({ context }) {
278
306
  placeholder: "Type a command (e.g., /new)...",
279
307
  focused: true,
280
308
  width: "100%",
281
- onInput: (value) => setInputValue(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
+ },
282
314
  onSubmit: (value) => {
283
- if (typeof value === "string" && typeof value.trim === "function" && value.trim()) {
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()) {
284
322
  handleCommand(value);
285
323
  setInputValue("");
286
324
  }
@@ -290,14 +328,22 @@ function Home({ context }) {
290
328
  }
291
329
  )
292
330
  }
293
- )
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
+ }) })
294
340
  ] })
295
341
  ] });
296
342
  }
297
343
 
298
344
  // src/components/NewDeployment.tsx
299
- import { useState as useState2, useRef, useEffect } from "react";
300
- import { useKeyboard } from "@opentui/react";
345
+ import { useState as useState2, useRef as useRef2, useEffect } from "react";
346
+ import { useKeyboard as useKeyboard2 } from "@opentui/react";
301
347
  import { appendFileSync } from "fs";
302
348
  import { homedir as homedir3 } from "os";
303
349
  import { join as join4 } from "path";
@@ -1428,7 +1474,7 @@ function NewDeployment({ context }) {
1428
1474
  setSelectedSavedKeyIndex(0);
1429
1475
  }
1430
1476
  }, [step]);
1431
- const stateRef = useRef({
1477
+ const stateRef = useRef2({
1432
1478
  name,
1433
1479
  provider,
1434
1480
  apiKey,
@@ -1558,7 +1604,7 @@ function NewDeployment({ context }) {
1558
1604
  setError(`Failed to ${s.editMode === "edit" ? "update" : "create"} deployment: ${err instanceof Error ? err.message : String(err)}`);
1559
1605
  }
1560
1606
  };
1561
- useKeyboard((key) => {
1607
+ useKeyboard2((key) => {
1562
1608
  const currentState = stateRef.current;
1563
1609
  debugLog(`useKeyboard: key=${key.name}, step=${currentState.step}`);
1564
1610
  if (currentState.step === "template_choice") {
@@ -2811,8 +2857,8 @@ function NewDeployment({ context }) {
2811
2857
  }
2812
2858
 
2813
2859
  // src/components/ListView.tsx
2814
- import { useState as useState3, useRef as useRef2 } from "react";
2815
- import { useKeyboard as useKeyboard2 } from "@opentui/react";
2860
+ import { useState as useState3, useRef as useRef3 } from "react";
2861
+ import { useKeyboard as useKeyboard3 } from "@opentui/react";
2816
2862
  import { Fragment, jsx as jsx3, jsxs as jsxs3 } from "@opentui/react/jsx-runtime";
2817
2863
  function ListView({ context }) {
2818
2864
  const [viewState, setViewState] = useState3("listing");
@@ -2822,9 +2868,9 @@ function ListView({ context }) {
2822
2868
  const [deletedName, setDeletedName] = useState3("");
2823
2869
  const deployments = context.deployments;
2824
2870
  const selectedDeployment = deployments[selectedIndex];
2825
- const stateRef = useRef2({ viewState, selectedIndex });
2871
+ const stateRef = useRef3({ viewState, selectedIndex });
2826
2872
  stateRef.current = { viewState, selectedIndex };
2827
- useKeyboard2((key) => {
2873
+ useKeyboard3((key) => {
2828
2874
  const current = stateRef.current;
2829
2875
  if (deployments.length === 0) {
2830
2876
  context.navigateTo("home");
@@ -3122,7 +3168,7 @@ function ListView({ context }) {
3122
3168
 
3123
3169
  // src/components/DeployView.tsx
3124
3170
  import { useState as useState4 } from "react";
3125
- import { useKeyboard as useKeyboard3 } from "@opentui/react";
3171
+ import { useKeyboard as useKeyboard4 } from "@opentui/react";
3126
3172
  import { jsx as jsx4, jsxs as jsxs4 } from "@opentui/react/jsx-runtime";
3127
3173
  function DeployView({ context }) {
3128
3174
  const [selectedIndex, setSelectedIndex] = useState4(0);
@@ -3132,7 +3178,7 @@ function DeployView({ context }) {
3132
3178
  const deployed = deployments.filter((d) => d.state.status === "deployed");
3133
3179
  const allDeployments = [...notDeployed, ...deployed];
3134
3180
  const selectedDeployment = allDeployments[selectedIndex];
3135
- useKeyboard3((key) => {
3181
+ useKeyboard4((key) => {
3136
3182
  if (allDeployments.length === 0) {
3137
3183
  context.navigateTo("home");
3138
3184
  return;
@@ -3251,8 +3297,8 @@ function DeployView({ context }) {
3251
3297
  }
3252
3298
 
3253
3299
  // src/components/DeployingView.tsx
3254
- import { useState as useState5, useEffect as useEffect2, useCallback, useRef as useRef3 } from "react";
3255
- import { useKeyboard as useKeyboard4 } from "@opentui/react";
3300
+ import { useState as useState5, useEffect as useEffect2, useCallback, useRef as useRef4 } from "react";
3301
+ import { useKeyboard as useKeyboard5 } from "@opentui/react";
3256
3302
  import open from "open";
3257
3303
 
3258
3304
  // src/services/ssh.ts
@@ -4713,6 +4759,12 @@ function detectTerminal() {
4713
4759
  return { app: "konsole", canOpenTab: true };
4714
4760
  }
4715
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
+ }
4716
4768
  if (os === "darwin") {
4717
4769
  return { app: "terminal.app", canOpenTab: true };
4718
4770
  }
@@ -4747,11 +4799,19 @@ function openTerminalWithCommand(command) {
4747
4799
  return openKonsole(command);
4748
4800
  case "xfce4-terminal":
4749
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);
4750
4808
  default:
4751
4809
  if (os === "darwin") {
4752
4810
  return openTerminalApp(command);
4753
4811
  } else if (os === "linux") {
4754
4812
  return openLinuxFallback(command);
4813
+ } else if (os === "win32") {
4814
+ return openWindowsFallback(command);
4755
4815
  }
4756
4816
  return { success: false, error: `Unsupported terminal or OS: ${terminal.app}` };
4757
4817
  }
@@ -4947,6 +5007,9 @@ function openCursorTerminal(command) {
4947
5007
  }
4948
5008
  return openTerminalApp(command);
4949
5009
  }
5010
+ if (platform() === "win32") {
5011
+ return openWindowsFallback(command);
5012
+ }
4950
5013
  return openLinuxFallback(command);
4951
5014
  }
4952
5015
  function openVSCodeTerminal(command) {
@@ -5018,6 +5081,62 @@ function openLinuxFallback(command) {
5018
5081
  }
5019
5082
  return { success: false, error: "Could not find a supported terminal emulator" };
5020
5083
  }
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
+ }
5021
5140
  function getTerminalDisplayName(app) {
5022
5141
  const names = {
5023
5142
  "terminal.app": "Terminal.app",
@@ -5033,6 +5152,9 @@ function getTerminalDisplayName(app) {
5033
5152
  "konsole": "Konsole",
5034
5153
  "xfce4-terminal": "XFCE Terminal",
5035
5154
  "xterm": "XTerm",
5155
+ "windows-terminal": "Windows Terminal",
5156
+ "powershell": "PowerShell",
5157
+ "cmd": "Command Prompt",
5036
5158
  "unknown": "System Terminal"
5037
5159
  };
5038
5160
  return names[app];
@@ -5094,9 +5216,9 @@ function DeployingView({ context }) {
5094
5216
  setDeployState("deploying");
5095
5217
  addLog("Terminal session confirmed complete, continuing deployment...");
5096
5218
  }, [terminalResolve, addLog]);
5097
- const stateRef = useRef3({ deployState, terminalResolve });
5219
+ const stateRef = useRef4({ deployState, terminalResolve });
5098
5220
  stateRef.current = { deployState, terminalResolve };
5099
- useKeyboard4((key) => {
5221
+ useKeyboard5((key) => {
5100
5222
  const currentState = stateRef.current;
5101
5223
  if (currentState.deployState === "waiting_terminal") {
5102
5224
  if (key.name === "return") {
@@ -5344,7 +5466,7 @@ function DeployingView({ context }) {
5344
5466
 
5345
5467
  // src/components/StatusView.tsx
5346
5468
  import { useState as useState6 } from "react";
5347
- import { useKeyboard as useKeyboard5 } from "@opentui/react";
5469
+ import { useKeyboard as useKeyboard6 } from "@opentui/react";
5348
5470
  import { jsx as jsx6, jsxs as jsxs6 } from "@opentui/react/jsx-runtime";
5349
5471
  function StatusView({ context }) {
5350
5472
  const [selectedIndex, setSelectedIndex] = useState6(0);
@@ -5374,7 +5496,7 @@ function StatusView({ context }) {
5374
5496
  setHealthStatus((prev) => new Map(prev).set(name, health));
5375
5497
  setChecking(null);
5376
5498
  };
5377
- useKeyboard5((key) => {
5499
+ useKeyboard6((key) => {
5378
5500
  if (deployments.length === 0) {
5379
5501
  context.navigateTo("home");
5380
5502
  return;
@@ -5528,7 +5650,7 @@ function StatusView({ context }) {
5528
5650
 
5529
5651
  // src/components/SSHView.tsx
5530
5652
  import { useState as useState7, useCallback as useCallback2 } from "react";
5531
- import { useKeyboard as useKeyboard6 } from "@opentui/react";
5653
+ import { useKeyboard as useKeyboard7 } from "@opentui/react";
5532
5654
  import { jsx as jsx7, jsxs as jsxs7 } from "@opentui/react/jsx-runtime";
5533
5655
  function SSHView({ context }) {
5534
5656
  const [viewState, setViewState] = useState7("selecting");
@@ -5554,7 +5676,7 @@ function SSHView({ context }) {
5554
5676
  }
5555
5677
  }, []);
5556
5678
  const selectedDeployment = deployedDeployments[selectedIndex];
5557
- useKeyboard6((key) => {
5679
+ useKeyboard7((key) => {
5558
5680
  if (deployedDeployments.length === 0) {
5559
5681
  context.navigateTo("home");
5560
5682
  return;
@@ -5709,8 +5831,8 @@ function SSHView({ context }) {
5709
5831
  }
5710
5832
 
5711
5833
  // src/components/LogsView.tsx
5712
- import { useState as useState8, useEffect as useEffect3, useRef as useRef4 } from "react";
5713
- import { useKeyboard as useKeyboard7 } from "@opentui/react";
5834
+ import { useState as useState8, useEffect as useEffect3, useRef as useRef5 } from "react";
5835
+ import { useKeyboard as useKeyboard8 } from "@opentui/react";
5714
5836
  import { Fragment as Fragment2, jsx as jsx8, jsxs as jsxs8 } from "@opentui/react/jsx-runtime";
5715
5837
  function parseLogLine(line) {
5716
5838
  if (!line.trim()) return null;
@@ -5736,8 +5858,8 @@ function LogsView({ context }) {
5736
5858
  const [error, setError] = useState8(null);
5737
5859
  const [autoRefresh, setAutoRefresh] = useState8(false);
5738
5860
  const [lastFetched, setLastFetched] = useState8(null);
5739
- const sshRef = useRef4(null);
5740
- const refreshIntervalRef = useRef4(null);
5861
+ const sshRef = useRef5(null);
5862
+ const refreshIntervalRef = useRef5(null);
5741
5863
  const deployedDeployments = context.deployments.filter(
5742
5864
  (d) => d.state.status === "deployed" && d.state.serverIp
5743
5865
  );
@@ -5788,7 +5910,7 @@ function LogsView({ context }) {
5788
5910
  setLogs([]);
5789
5911
  };
5790
5912
  const selectedDeployment = deployedDeployments[selectedIndex];
5791
- useKeyboard7((key) => {
5913
+ useKeyboard8((key) => {
5792
5914
  if (deployedDeployments.length === 0) {
5793
5915
  context.navigateTo("home");
5794
5916
  return;
@@ -5947,7 +6069,7 @@ function LogsView({ context }) {
5947
6069
 
5948
6070
  // src/components/DestroyView.tsx
5949
6071
  import { useState as useState9 } from "react";
5950
- import { useKeyboard as useKeyboard8 } from "@opentui/react";
6072
+ import { useKeyboard as useKeyboard9 } from "@opentui/react";
5951
6073
  import { jsx as jsx9, jsxs as jsxs9 } from "@opentui/react/jsx-runtime";
5952
6074
  function DestroyView({ context }) {
5953
6075
  const [viewState, setViewState] = useState9("selecting");
@@ -5998,7 +6120,7 @@ function DestroyView({ context }) {
5998
6120
  }
5999
6121
  };
6000
6122
  const selectedDeployment = deployments[selectedIndex];
6001
- useKeyboard8((key) => {
6123
+ useKeyboard9((key) => {
6002
6124
  if (deployments.length === 0) {
6003
6125
  context.navigateTo("home");
6004
6126
  return;
@@ -6189,10 +6311,10 @@ function DestroyView({ context }) {
6189
6311
  }
6190
6312
 
6191
6313
  // src/components/HelpView.tsx
6192
- import { useKeyboard as useKeyboard9 } from "@opentui/react";
6314
+ import { useKeyboard as useKeyboard10 } from "@opentui/react";
6193
6315
  import { jsx as jsx10, jsxs as jsxs10 } from "@opentui/react/jsx-runtime";
6194
6316
  function HelpView({ context }) {
6195
- useKeyboard9(() => {
6317
+ useKeyboard10(() => {
6196
6318
  context.navigateTo("home");
6197
6319
  });
6198
6320
  return /* @__PURE__ */ jsxs10("box", { flexDirection: "column", width: "100%", padding: 1, children: [
@@ -6319,8 +6441,8 @@ function HelpView({ context }) {
6319
6441
  }
6320
6442
 
6321
6443
  // src/components/TemplatesView.tsx
6322
- import { useState as useState10, useRef as useRef5, useEffect as useEffect4 } from "react";
6323
- import { useKeyboard as useKeyboard10 } from "@opentui/react";
6444
+ import { useState as useState10, useRef as useRef6, useEffect as useEffect4 } from "react";
6445
+ import { useKeyboard as useKeyboard11 } from "@opentui/react";
6324
6446
  import { Fragment as Fragment3, jsx as jsx11, jsxs as jsxs11 } from "@opentui/react/jsx-runtime";
6325
6447
  var DO_DROPLET_SIZES2 = [
6326
6448
  { slug: "s-1vcpu-2gb", label: "1 vCPU, 2GB RAM, 50GB SSD", price: "$12/mo" },
@@ -6343,7 +6465,7 @@ function TemplatesView({ context }) {
6343
6465
  const [forkAiProvider, setForkAiProvider] = useState10("");
6344
6466
  const [forkAiProviderIndex, setForkAiProviderIndex] = useState10(0);
6345
6467
  const [forkModel, setForkModel] = useState10("");
6346
- const stateRef = useRef5({
6468
+ const stateRef = useRef6({
6347
6469
  viewState,
6348
6470
  selectedIndex,
6349
6471
  selectedTemplate,
@@ -6457,7 +6579,7 @@ function TemplatesView({ context }) {
6457
6579
  setViewState("viewing");
6458
6580
  }
6459
6581
  };
6460
- useKeyboard10((key) => {
6582
+ useKeyboard11((key) => {
6461
6583
  const s = stateRef.current;
6462
6584
  if (s.viewState === "listing") {
6463
6585
  if (key.name === "up") {
@@ -6866,7 +6988,7 @@ function TemplatesView({ context }) {
6866
6988
 
6867
6989
  // src/components/DashboardView.tsx
6868
6990
  import { useState as useState11, useCallback as useCallback3 } from "react";
6869
- import { useKeyboard as useKeyboard11, useRenderer } from "@opentui/react";
6991
+ import { useKeyboard as useKeyboard12, useRenderer } from "@opentui/react";
6870
6992
  import { spawnSync as spawnSync2 } from "child_process";
6871
6993
  import { platform as platform2 } from "os";
6872
6994
 
@@ -7035,7 +7157,7 @@ function DashboardView({ context }) {
7035
7157
  setViewState("error");
7036
7158
  }
7037
7159
  }, []);
7038
- useKeyboard11((key) => {
7160
+ useKeyboard12((key) => {
7039
7161
  if (viewState === "selecting") {
7040
7162
  if (deployedDeployments.length === 0) {
7041
7163
  if (key.name === "escape" || key.name === "return") {
@@ -7294,8 +7416,8 @@ function DashboardView({ context }) {
7294
7416
  }
7295
7417
 
7296
7418
  // src/App.tsx
7297
- import { jsx as jsx13 } from "@opentui/react/jsx-runtime";
7298
- function App() {
7419
+ import { jsx as jsx13, jsxs as jsxs13 } from "@opentui/react/jsx-runtime";
7420
+ function App({ lacksTrueColor: lacksTrueColor2 }) {
7299
7421
  const renderer = useRenderer2();
7300
7422
  const [currentView, setCurrentView] = useState12("home");
7301
7423
  const [selectedDeployment, setSelectedDeployment] = useState12(null);
@@ -7308,7 +7430,7 @@ function App() {
7308
7430
  });
7309
7431
  const [selectedTemplate, setSelectedTemplate] = useState12(null);
7310
7432
  const [editingDeployment, setEditingDeployment] = useState12(null);
7311
- const wasDraggingRef = useRef6(false);
7433
+ const wasDraggingRef = useRef7(false);
7312
7434
  const handleMouseDrag = useCallback4(() => {
7313
7435
  wasDraggingRef.current = true;
7314
7436
  }, []);
@@ -7377,7 +7499,7 @@ function App() {
7377
7499
  return /* @__PURE__ */ jsx13(Home, { context });
7378
7500
  }
7379
7501
  };
7380
- return /* @__PURE__ */ jsx13(
7502
+ return /* @__PURE__ */ jsxs13(
7381
7503
  "scrollbox",
7382
7504
  {
7383
7505
  width: "100%",
@@ -7398,7 +7520,23 @@ function App() {
7398
7520
  verticalScrollbarOptions: {
7399
7521
  showArrows: false
7400
7522
  },
7401
- children: renderView()
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
+ ]
7402
7540
  }
7403
7541
  );
7404
7542
  }
@@ -7433,11 +7571,14 @@ Documentation & source:
7433
7571
  `);
7434
7572
  process.exit(0);
7435
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;
7436
7577
  async function main() {
7437
7578
  const renderer = await createCliRenderer({
7438
7579
  useMouse: true
7439
7580
  });
7440
7581
  const root = createRoot(renderer);
7441
- root.render(/* @__PURE__ */ jsx14(App, {}));
7582
+ root.render(/* @__PURE__ */ jsx14(App, { lacksTrueColor }));
7442
7583
  }
7443
7584
  main().catch(console.error);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clawcontrol",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "description": "CLI tool for deploying and managing OpenClaw instances on VPS providers",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",