claw-control-center 0.1.11 → 0.1.13

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.cjs CHANGED
@@ -16403,7 +16403,7 @@ var isDownKey = (key, keybindings2 = []) => (
16403
16403
  keybindings2.includes("vim") && key.name === "j" || // Emacs keybinding: Ctrl+N means "next" in Emacs navigation conventions
16404
16404
  keybindings2.includes("emacs") && key.ctrl && key.name === "n"
16405
16405
  );
16406
- var isSpaceKey = (key) => key.name === "space";
16406
+ var isBackspaceKey = (key) => key.name === "backspace";
16407
16407
  var isNumberKey = (key) => "1234567890".includes(key.name);
16408
16408
  var isEnterKey = (key) => key.name === "enter" || key.name === "return";
16409
16409
 
@@ -17906,23 +17906,17 @@ var Separator = class {
17906
17906
  }
17907
17907
  };
17908
17908
 
17909
- // ../node_modules/.pnpm/@inquirer+checkbox@5.2.1_@types+node@24.12.4/node_modules/@inquirer/checkbox/dist/index.js
17909
+ // ../node_modules/.pnpm/@inquirer+select@5.2.1_@types+node@24.12.4/node_modules/@inquirer/select/dist/index.js
17910
17910
  var import_node_util4 = require("util");
17911
- var checkboxTheme = {
17912
- icon: {
17913
- checked: (0, import_node_util4.styleText)("green", dist_default.circleFilled),
17914
- unchecked: dist_default.circle,
17915
- cursor: dist_default.pointer,
17916
- disabledChecked: (0, import_node_util4.styleText)("green", dist_default.circleDouble),
17917
- disabledUnchecked: "-"
17918
- },
17911
+ var selectTheme = {
17912
+ icon: { cursor: dist_default.pointer },
17919
17913
  style: {
17920
17914
  disabled: (text) => (0, import_node_util4.styleText)("dim", text),
17921
- renderSelectedChoices: (selectedChoices) => selectedChoices.map((choice) => choice.short).join(", "),
17922
17915
  description: (text) => (0, import_node_util4.styleText)("cyan", text),
17923
17916
  keysHelpTip: (keys) => keys.map(([key, action]) => `${(0, import_node_util4.styleText)("bold", key)} ${(0, import_node_util4.styleText)("dim", action)}`).join((0, import_node_util4.styleText)("dim", " \u2022 "))
17924
17917
  },
17925
- i18n: { disabledError: "This option is disabled and cannot be toggled." }
17918
+ i18n: { disabledError: "This option is disabled and cannot be selected." },
17919
+ indexMode: "hidden"
17926
17920
  };
17927
17921
  function isSelectable(item) {
17928
17922
  return !Separator.isSeparator(item) && !item.disabled;
@@ -17930,17 +17924,6 @@ function isSelectable(item) {
17930
17924
  function isNavigable(item) {
17931
17925
  return !Separator.isSeparator(item);
17932
17926
  }
17933
- function isChecked(item) {
17934
- return !Separator.isSeparator(item) && item.checked;
17935
- }
17936
- function toggle(item) {
17937
- return isSelectable(item) ? { ...item, checked: !item.checked } : item;
17938
- }
17939
- function check(checked) {
17940
- return function(item) {
17941
- return isSelectable(item) ? { ...item, checked } : item;
17942
- };
17943
- }
17944
17927
  function normalizeChoices(choices) {
17945
17928
  return choices.map((choice) => {
17946
17929
  if (Separator.isSeparator(choice))
@@ -17951,9 +17934,7 @@ function normalizeChoices(choices) {
17951
17934
  value: choice,
17952
17935
  name: name2,
17953
17936
  short: name2,
17954
- checkedName: name2,
17955
- disabled: false,
17956
- checked: false
17937
+ disabled: false
17957
17938
  };
17958
17939
  }
17959
17940
  const name = choice.name ?? String(choice.value);
@@ -17961,9 +17942,7 @@ function normalizeChoices(choices) {
17961
17942
  value: choice.value,
17962
17943
  name,
17963
17944
  short: choice.short ?? name,
17964
- checkedName: choice.checkedName ?? name,
17965
- disabled: choice.disabled ?? false,
17966
- checked: choice.checked ?? false
17945
+ disabled: choice.disabled ?? false
17967
17946
  };
17968
17947
  if (choice.description) {
17969
17948
  normalizedChoice.description = choice.description;
@@ -17972,39 +17951,47 @@ function normalizeChoices(choices) {
17972
17951
  });
17973
17952
  }
17974
17953
  var dist_default4 = createPrompt((config, done) => {
17975
- const { pageSize = 7, loop = true, required, validate = () => true } = config;
17976
- const shortcuts = { all: "a", invert: "i", ...config.shortcuts };
17977
- const theme = makeTheme(checkboxTheme, config.theme);
17954
+ const { loop = true, pageSize = 7 } = config;
17955
+ const theme = makeTheme(selectTheme, config.theme);
17978
17956
  const { keybindings: keybindings2 } = theme;
17979
17957
  const [status, setStatus] = useState("idle");
17980
17958
  const prefix = usePrefix({ status, theme });
17981
- const [items, setItems] = useState(normalizeChoices(config.choices));
17959
+ const searchTimeoutRef = useRef();
17960
+ const searchEnabled = !keybindings2.includes("vim");
17961
+ const items = useMemo(() => normalizeChoices(config.choices), [config.choices]);
17982
17962
  const bounds = useMemo(() => {
17983
17963
  const first = items.findIndex(isNavigable);
17984
17964
  const last = items.findLastIndex(isNavigable);
17985
17965
  if (first === -1) {
17986
- throw new ValidationError("[checkbox prompt] No selectable choices. All choices are disabled.");
17966
+ throw new ValidationError("[select prompt] No selectable choices. All choices are disabled.");
17987
17967
  }
17988
17968
  return { first, last };
17989
17969
  }, [items]);
17990
- const [active, setActive] = useState(bounds.first);
17970
+ const defaultItemIndex = useMemo(() => {
17971
+ if (!("default" in config))
17972
+ return -1;
17973
+ return items.findIndex((item) => isSelectable(item) && item.value === config.default);
17974
+ }, [config.default, items]);
17975
+ const [active, setActive] = useState(defaultItemIndex === -1 ? bounds.first : defaultItemIndex);
17976
+ const selectedChoice = items[active];
17977
+ if (selectedChoice == null || Separator.isSeparator(selectedChoice)) {
17978
+ throw new Error("Active index does not point to a choice");
17979
+ }
17991
17980
  const [errorMsg, setError] = useState();
17992
- useKeypress(async (key) => {
17981
+ useKeypress((key, rl) => {
17982
+ clearTimeout(searchTimeoutRef.current);
17983
+ if (errorMsg) {
17984
+ setError(void 0);
17985
+ }
17993
17986
  if (isEnterKey(key)) {
17994
- const selection = items.filter(isChecked);
17995
- const isValid = await validate([...selection]);
17996
- if (required && !selection.length) {
17997
- setError("At least one choice must be selected");
17998
- } else if (isValid === true) {
17999
- setStatus("done");
18000
- done(selection.map((choice) => choice.value));
17987
+ if (selectedChoice.disabled) {
17988
+ setError(theme.i18n.disabledError);
18001
17989
  } else {
18002
- setError(isValid || "You must select a valid value");
17990
+ setStatus("done");
17991
+ done(selectedChoice.value);
18003
17992
  }
18004
17993
  } else if (isUpKey(key, keybindings2) || isDownKey(key, keybindings2)) {
18005
- if (errorMsg) {
18006
- setError(void 0);
18007
- }
17994
+ rl.clearLine(0);
18008
17995
  if (loop || isUpKey(key, keybindings2) && active !== bounds.first || isDownKey(key, keybindings2) && active !== bounds.last) {
18009
17996
  const offset = isUpKey(key, keybindings2) ? -1 : 1;
18010
17997
  let next = active;
@@ -18013,78 +18000,73 @@ var dist_default4 = createPrompt((config, done) => {
18013
18000
  } while (!isNavigable(items[next]));
18014
18001
  setActive(next);
18015
18002
  }
18016
- } else if (isSpaceKey(key)) {
18017
- const activeItem = items[active];
18018
- if (activeItem && !Separator.isSeparator(activeItem)) {
18019
- if (activeItem.disabled) {
18020
- setError(theme.i18n.disabledError);
18021
- } else {
18022
- setError(void 0);
18023
- setItems(items.map((choice, i) => i === active ? toggle(choice) : choice));
18024
- }
18025
- }
18026
- } else if (key.name === shortcuts.all) {
18027
- const selectAll = items.some((choice) => isSelectable(choice) && !choice.checked);
18028
- setItems(items.map(check(selectAll)));
18029
- } else if (key.name === shortcuts.invert) {
18030
- setItems(items.map(toggle));
18031
- } else if (isNumberKey(key)) {
18032
- const selectedIndex = Number(key.name) - 1;
18003
+ } else if (isNumberKey(key) && !Number.isNaN(Number(rl.line))) {
18004
+ const selectedIndex = Number(rl.line) - 1;
18033
18005
  let selectableIndex = -1;
18034
- const position = items.findIndex((item) => {
18035
- if (Separator.isSeparator(item))
18006
+ const position = items.findIndex((item2) => {
18007
+ if (Separator.isSeparator(item2))
18036
18008
  return false;
18037
18009
  selectableIndex++;
18038
18010
  return selectableIndex === selectedIndex;
18039
18011
  });
18040
- const selectedItem = items[position];
18041
- if (selectedItem && isSelectable(selectedItem)) {
18012
+ const item = items[position];
18013
+ if (item != null && isSelectable(item)) {
18042
18014
  setActive(position);
18043
- setItems(items.map((choice, i) => i === position ? toggle(choice) : choice));
18044
18015
  }
18016
+ searchTimeoutRef.current = setTimeout(() => {
18017
+ rl.clearLine(0);
18018
+ }, 700);
18019
+ } else if (isBackspaceKey(key)) {
18020
+ rl.clearLine(0);
18021
+ } else if (searchEnabled) {
18022
+ const searchTerm = rl.line.toLowerCase();
18023
+ const matchIndex = items.findIndex((item) => {
18024
+ if (Separator.isSeparator(item) || !isSelectable(item))
18025
+ return false;
18026
+ return item.name.toLowerCase().startsWith(searchTerm);
18027
+ });
18028
+ if (matchIndex !== -1) {
18029
+ setActive(matchIndex);
18030
+ }
18031
+ searchTimeoutRef.current = setTimeout(() => {
18032
+ rl.clearLine(0);
18033
+ }, 700);
18045
18034
  }
18046
18035
  });
18036
+ useEffect(() => () => {
18037
+ clearTimeout(searchTimeoutRef.current);
18038
+ }, []);
18047
18039
  const message = theme.style.message(config.message, status);
18048
- let description;
18040
+ const helpLine = theme.style.keysHelpTip([
18041
+ ["\u2191\u2193", "navigate"],
18042
+ ["\u23CE", "select"]
18043
+ ]);
18044
+ let separatorCount = 0;
18049
18045
  const page = usePagination({
18050
18046
  items,
18051
18047
  active,
18052
- renderItem({ item, isActive }) {
18048
+ renderItem({ item, isActive, index }) {
18053
18049
  if (Separator.isSeparator(item)) {
18050
+ separatorCount++;
18054
18051
  return ` ${item.separator}`;
18055
18052
  }
18056
18053
  const cursor = isActive ? theme.icon.cursor : " ";
18054
+ const indexLabel = theme.indexMode === "number" ? `${index + 1 - separatorCount}. ` : "";
18057
18055
  if (item.disabled) {
18058
18056
  const disabledLabel = typeof item.disabled === "string" ? item.disabled : "(disabled)";
18059
- const checkbox2 = item.checked ? theme.icon.disabledChecked : theme.icon.disabledUnchecked;
18060
- return theme.style.disabled(`${cursor}${checkbox2} ${item.name} ${disabledLabel}`);
18057
+ const disabledCursor = isActive ? theme.icon.cursor : "-";
18058
+ return theme.style.disabled(`${disabledCursor} ${indexLabel}${item.name} ${disabledLabel}`);
18061
18059
  }
18062
- if (isActive) {
18063
- description = item.description;
18064
- }
18065
- const checkbox = item.checked ? theme.icon.checked : theme.icon.unchecked;
18066
- const name = item.checked ? item.checkedName : item.name;
18067
18060
  const color = isActive ? theme.style.highlight : (x) => x;
18068
- return color(`${cursor}${checkbox} ${name}`);
18061
+ return color(`${cursor} ${indexLabel}${item.name}`);
18069
18062
  },
18070
18063
  pageSize,
18071
18064
  loop
18072
18065
  });
18073
18066
  if (status === "done") {
18074
- const selection = items.filter(isChecked);
18075
- const answer = theme.style.answer(theme.style.renderSelectedChoices(selection, items));
18076
- return [prefix, message, answer].filter(Boolean).join(" ");
18067
+ return [prefix, message, theme.style.answer(selectedChoice.short)].filter(Boolean).join(" ");
18077
18068
  }
18078
- const keys = [
18079
- ["\u2191\u2193", "navigate"],
18080
- ["space", "select"]
18081
- ];
18082
- if (shortcuts.all)
18083
- keys.push([shortcuts.all, "all"]);
18084
- if (shortcuts.invert)
18085
- keys.push([shortcuts.invert, "invert"]);
18086
- keys.push(["\u23CE", "submit"]);
18087
- const helpLine = theme.style.keysHelpTip(keys);
18069
+ const { description } = selectedChoice;
18088
18070
  const lines = [
18089
18071
  [prefix, message].filter(Boolean).join(" "),
18090
18072
  page,
@@ -18275,7 +18257,7 @@ async function runInstallCommand(input) {
18275
18257
  hostDefinitions: input.hostDefinitions,
18276
18258
  selectHosts: input.selectHosts,
18277
18259
  selectHost: input.selectHost,
18278
- promptSelectHosts: input.promptSelectHosts,
18260
+ promptSelectHost: input.promptSelectHost,
18279
18261
  ttyPath: input.ttyPath
18280
18262
  });
18281
18263
  for (const destination of destinations) {
@@ -18327,12 +18309,9 @@ async function resolveInstallDestinations(args, options = {}) {
18327
18309
  const detected = detectInstallHosts(options.hostDefinitions ?? getDefaultHostDefinitions());
18328
18310
  const compatible = detected;
18329
18311
  const incompatible = [];
18330
- if (compatible.length === 1) {
18331
- return [toInstallDestination(compatible[0])];
18332
- }
18333
- if (compatible.length > 1) {
18334
- const selected = options.selectHosts ? await options.selectHosts(compatible, incompatible) : options.selectHost ? [await options.selectHost(compatible)] : await (options.promptSelectHosts ?? promptForInstallHosts)(compatible, incompatible);
18335
- return validateSelectedHosts(selected, compatible).map(toInstallDestination);
18312
+ if (compatible.length > 0) {
18313
+ const selected = options.selectHosts ? validateSingleSelectedHost(await options.selectHosts(compatible, incompatible), compatible) : options.selectHost ? validateSingleSelectedHost(await options.selectHost(compatible), compatible) : validateSingleSelectedHost(await (options.promptSelectHost ?? promptForInstallHost)(compatible, incompatible), compatible);
18314
+ return [toInstallDestination(selected)];
18336
18315
  }
18337
18316
  throw new Error(
18338
18317
  [
@@ -18382,7 +18361,7 @@ function detectInstallHosts(hosts) {
18382
18361
  return true;
18383
18362
  });
18384
18363
  }
18385
- async function promptForInstallHosts(hosts, incompatibleHosts) {
18364
+ async function promptForInstallHost(hosts, incompatibleHosts) {
18386
18365
  try {
18387
18366
  if (!process.stdin.isTTY || !process.stdout.isTTY) {
18388
18367
  throw new Error("interactive terminal is required");
@@ -18405,14 +18384,10 @@ Extensions: ${host.extensionsDir}`
18405
18384
  }))
18406
18385
  ];
18407
18386
  return await dist_default4({
18408
- message: "Choose one or more Claw installations to install claw-control-center:",
18387
+ message: "Choose the Claw installation to connect with this 53AIHub agent:",
18409
18388
  choices,
18410
18389
  pageSize: Math.min(Math.max(choices.length, 5), 12),
18411
- required: true,
18412
- shortcuts: {
18413
- all: "a",
18414
- invert: "i"
18415
- }
18390
+ loop: true
18416
18391
  });
18417
18392
  } catch (error) {
18418
18393
  throw new Error(
@@ -18435,20 +18410,18 @@ function toInstallDestination(host) {
18435
18410
  installKind: host.installKind ?? "openclaw"
18436
18411
  };
18437
18412
  }
18438
- function validateSelectedHosts(selected, compatible) {
18413
+ function validateSingleSelectedHost(selected, compatible) {
18414
+ if (Array.isArray(selected)) {
18415
+ if (selected.length !== 1) {
18416
+ throw new Error("select exactly one Claw host for this 53AIHub agent");
18417
+ }
18418
+ return validateSingleSelectedHost(selected[0], compatible);
18419
+ }
18439
18420
  const compatibleIds = new Set(compatible.map((host) => host.id));
18440
- const invalid = selected.find((host) => !compatibleIds.has(host.id));
18441
- if (invalid) {
18442
- throw new Error(`selected host is not installable: ${invalid.label}`);
18421
+ if (!compatibleIds.has(selected.id)) {
18422
+ throw new Error(`selected host is not installable: ${selected.label}`);
18443
18423
  }
18444
- const seen = /* @__PURE__ */ new Set();
18445
- return selected.filter((host) => {
18446
- if (seen.has(host.id)) {
18447
- return false;
18448
- }
18449
- seen.add(host.id);
18450
- return true;
18451
- });
18424
+ return selected;
18452
18425
  }
18453
18426
  function formatHostList(hosts) {
18454
18427
  return hosts.flatMap((host) => [
@@ -7562,7 +7562,7 @@ var isDownKey = (key, keybindings2 = []) => (
7562
7562
  keybindings2.includes("vim") && key.name === "j" || // Emacs keybinding: Ctrl+N means "next" in Emacs navigation conventions
7563
7563
  keybindings2.includes("emacs") && key.ctrl && key.name === "n"
7564
7564
  );
7565
- var isSpaceKey = (key) => key.name === "space";
7565
+ var isBackspaceKey = (key) => key.name === "backspace";
7566
7566
  var isNumberKey = (key) => "1234567890".includes(key.name);
7567
7567
  var isEnterKey = (key) => key.name === "enter" || key.name === "return";
7568
7568
 
@@ -9065,23 +9065,17 @@ var Separator = class {
9065
9065
  }
9066
9066
  };
9067
9067
 
9068
- // ../node_modules/.pnpm/@inquirer+checkbox@5.2.1_@types+node@24.12.4/node_modules/@inquirer/checkbox/dist/index.js
9068
+ // ../node_modules/.pnpm/@inquirer+select@5.2.1_@types+node@24.12.4/node_modules/@inquirer/select/dist/index.js
9069
9069
  var import_node_util4 = require("util");
9070
- var checkboxTheme = {
9071
- icon: {
9072
- checked: (0, import_node_util4.styleText)("green", dist_default.circleFilled),
9073
- unchecked: dist_default.circle,
9074
- cursor: dist_default.pointer,
9075
- disabledChecked: (0, import_node_util4.styleText)("green", dist_default.circleDouble),
9076
- disabledUnchecked: "-"
9077
- },
9070
+ var selectTheme = {
9071
+ icon: { cursor: dist_default.pointer },
9078
9072
  style: {
9079
9073
  disabled: (text) => (0, import_node_util4.styleText)("dim", text),
9080
- renderSelectedChoices: (selectedChoices) => selectedChoices.map((choice) => choice.short).join(", "),
9081
9074
  description: (text) => (0, import_node_util4.styleText)("cyan", text),
9082
9075
  keysHelpTip: (keys) => keys.map(([key, action]) => `${(0, import_node_util4.styleText)("bold", key)} ${(0, import_node_util4.styleText)("dim", action)}`).join((0, import_node_util4.styleText)("dim", " \u2022 "))
9083
9076
  },
9084
- i18n: { disabledError: "This option is disabled and cannot be toggled." }
9077
+ i18n: { disabledError: "This option is disabled and cannot be selected." },
9078
+ indexMode: "hidden"
9085
9079
  };
9086
9080
  function isSelectable(item) {
9087
9081
  return !Separator.isSeparator(item) && !item.disabled;
@@ -9089,17 +9083,6 @@ function isSelectable(item) {
9089
9083
  function isNavigable(item) {
9090
9084
  return !Separator.isSeparator(item);
9091
9085
  }
9092
- function isChecked(item) {
9093
- return !Separator.isSeparator(item) && item.checked;
9094
- }
9095
- function toggle(item) {
9096
- return isSelectable(item) ? { ...item, checked: !item.checked } : item;
9097
- }
9098
- function check(checked) {
9099
- return function(item) {
9100
- return isSelectable(item) ? { ...item, checked } : item;
9101
- };
9102
- }
9103
9086
  function normalizeChoices(choices) {
9104
9087
  return choices.map((choice) => {
9105
9088
  if (Separator.isSeparator(choice))
@@ -9110,9 +9093,7 @@ function normalizeChoices(choices) {
9110
9093
  value: choice,
9111
9094
  name: name2,
9112
9095
  short: name2,
9113
- checkedName: name2,
9114
- disabled: false,
9115
- checked: false
9096
+ disabled: false
9116
9097
  };
9117
9098
  }
9118
9099
  const name = choice.name ?? String(choice.value);
@@ -9120,9 +9101,7 @@ function normalizeChoices(choices) {
9120
9101
  value: choice.value,
9121
9102
  name,
9122
9103
  short: choice.short ?? name,
9123
- checkedName: choice.checkedName ?? name,
9124
- disabled: choice.disabled ?? false,
9125
- checked: choice.checked ?? false
9104
+ disabled: choice.disabled ?? false
9126
9105
  };
9127
9106
  if (choice.description) {
9128
9107
  normalizedChoice.description = choice.description;
@@ -9131,39 +9110,47 @@ function normalizeChoices(choices) {
9131
9110
  });
9132
9111
  }
9133
9112
  var dist_default4 = createPrompt((config, done) => {
9134
- const { pageSize = 7, loop = true, required, validate = () => true } = config;
9135
- const shortcuts = { all: "a", invert: "i", ...config.shortcuts };
9136
- const theme = makeTheme(checkboxTheme, config.theme);
9113
+ const { loop = true, pageSize = 7 } = config;
9114
+ const theme = makeTheme(selectTheme, config.theme);
9137
9115
  const { keybindings: keybindings2 } = theme;
9138
9116
  const [status, setStatus] = useState("idle");
9139
9117
  const prefix = usePrefix({ status, theme });
9140
- const [items, setItems] = useState(normalizeChoices(config.choices));
9118
+ const searchTimeoutRef = useRef();
9119
+ const searchEnabled = !keybindings2.includes("vim");
9120
+ const items = useMemo(() => normalizeChoices(config.choices), [config.choices]);
9141
9121
  const bounds = useMemo(() => {
9142
9122
  const first = items.findIndex(isNavigable);
9143
9123
  const last = items.findLastIndex(isNavigable);
9144
9124
  if (first === -1) {
9145
- throw new ValidationError("[checkbox prompt] No selectable choices. All choices are disabled.");
9125
+ throw new ValidationError("[select prompt] No selectable choices. All choices are disabled.");
9146
9126
  }
9147
9127
  return { first, last };
9148
9128
  }, [items]);
9149
- const [active, setActive] = useState(bounds.first);
9129
+ const defaultItemIndex = useMemo(() => {
9130
+ if (!("default" in config))
9131
+ return -1;
9132
+ return items.findIndex((item) => isSelectable(item) && item.value === config.default);
9133
+ }, [config.default, items]);
9134
+ const [active, setActive] = useState(defaultItemIndex === -1 ? bounds.first : defaultItemIndex);
9135
+ const selectedChoice = items[active];
9136
+ if (selectedChoice == null || Separator.isSeparator(selectedChoice)) {
9137
+ throw new Error("Active index does not point to a choice");
9138
+ }
9150
9139
  const [errorMsg, setError] = useState();
9151
- useKeypress(async (key) => {
9140
+ useKeypress((key, rl) => {
9141
+ clearTimeout(searchTimeoutRef.current);
9142
+ if (errorMsg) {
9143
+ setError(void 0);
9144
+ }
9152
9145
  if (isEnterKey(key)) {
9153
- const selection = items.filter(isChecked);
9154
- const isValid = await validate([...selection]);
9155
- if (required && !selection.length) {
9156
- setError("At least one choice must be selected");
9157
- } else if (isValid === true) {
9158
- setStatus("done");
9159
- done(selection.map((choice) => choice.value));
9146
+ if (selectedChoice.disabled) {
9147
+ setError(theme.i18n.disabledError);
9160
9148
  } else {
9161
- setError(isValid || "You must select a valid value");
9149
+ setStatus("done");
9150
+ done(selectedChoice.value);
9162
9151
  }
9163
9152
  } else if (isUpKey(key, keybindings2) || isDownKey(key, keybindings2)) {
9164
- if (errorMsg) {
9165
- setError(void 0);
9166
- }
9153
+ rl.clearLine(0);
9167
9154
  if (loop || isUpKey(key, keybindings2) && active !== bounds.first || isDownKey(key, keybindings2) && active !== bounds.last) {
9168
9155
  const offset = isUpKey(key, keybindings2) ? -1 : 1;
9169
9156
  let next = active;
@@ -9172,78 +9159,73 @@ var dist_default4 = createPrompt((config, done) => {
9172
9159
  } while (!isNavigable(items[next]));
9173
9160
  setActive(next);
9174
9161
  }
9175
- } else if (isSpaceKey(key)) {
9176
- const activeItem = items[active];
9177
- if (activeItem && !Separator.isSeparator(activeItem)) {
9178
- if (activeItem.disabled) {
9179
- setError(theme.i18n.disabledError);
9180
- } else {
9181
- setError(void 0);
9182
- setItems(items.map((choice, i) => i === active ? toggle(choice) : choice));
9183
- }
9184
- }
9185
- } else if (key.name === shortcuts.all) {
9186
- const selectAll = items.some((choice) => isSelectable(choice) && !choice.checked);
9187
- setItems(items.map(check(selectAll)));
9188
- } else if (key.name === shortcuts.invert) {
9189
- setItems(items.map(toggle));
9190
- } else if (isNumberKey(key)) {
9191
- const selectedIndex = Number(key.name) - 1;
9162
+ } else if (isNumberKey(key) && !Number.isNaN(Number(rl.line))) {
9163
+ const selectedIndex = Number(rl.line) - 1;
9192
9164
  let selectableIndex = -1;
9193
- const position = items.findIndex((item) => {
9194
- if (Separator.isSeparator(item))
9165
+ const position = items.findIndex((item2) => {
9166
+ if (Separator.isSeparator(item2))
9195
9167
  return false;
9196
9168
  selectableIndex++;
9197
9169
  return selectableIndex === selectedIndex;
9198
9170
  });
9199
- const selectedItem = items[position];
9200
- if (selectedItem && isSelectable(selectedItem)) {
9171
+ const item = items[position];
9172
+ if (item != null && isSelectable(item)) {
9201
9173
  setActive(position);
9202
- setItems(items.map((choice, i) => i === position ? toggle(choice) : choice));
9203
9174
  }
9175
+ searchTimeoutRef.current = setTimeout(() => {
9176
+ rl.clearLine(0);
9177
+ }, 700);
9178
+ } else if (isBackspaceKey(key)) {
9179
+ rl.clearLine(0);
9180
+ } else if (searchEnabled) {
9181
+ const searchTerm = rl.line.toLowerCase();
9182
+ const matchIndex = items.findIndex((item) => {
9183
+ if (Separator.isSeparator(item) || !isSelectable(item))
9184
+ return false;
9185
+ return item.name.toLowerCase().startsWith(searchTerm);
9186
+ });
9187
+ if (matchIndex !== -1) {
9188
+ setActive(matchIndex);
9189
+ }
9190
+ searchTimeoutRef.current = setTimeout(() => {
9191
+ rl.clearLine(0);
9192
+ }, 700);
9204
9193
  }
9205
9194
  });
9195
+ useEffect(() => () => {
9196
+ clearTimeout(searchTimeoutRef.current);
9197
+ }, []);
9206
9198
  const message = theme.style.message(config.message, status);
9207
- let description;
9199
+ const helpLine = theme.style.keysHelpTip([
9200
+ ["\u2191\u2193", "navigate"],
9201
+ ["\u23CE", "select"]
9202
+ ]);
9203
+ let separatorCount = 0;
9208
9204
  const page = usePagination({
9209
9205
  items,
9210
9206
  active,
9211
- renderItem({ item, isActive }) {
9207
+ renderItem({ item, isActive, index }) {
9212
9208
  if (Separator.isSeparator(item)) {
9209
+ separatorCount++;
9213
9210
  return ` ${item.separator}`;
9214
9211
  }
9215
9212
  const cursor = isActive ? theme.icon.cursor : " ";
9213
+ const indexLabel = theme.indexMode === "number" ? `${index + 1 - separatorCount}. ` : "";
9216
9214
  if (item.disabled) {
9217
9215
  const disabledLabel = typeof item.disabled === "string" ? item.disabled : "(disabled)";
9218
- const checkbox2 = item.checked ? theme.icon.disabledChecked : theme.icon.disabledUnchecked;
9219
- return theme.style.disabled(`${cursor}${checkbox2} ${item.name} ${disabledLabel}`);
9216
+ const disabledCursor = isActive ? theme.icon.cursor : "-";
9217
+ return theme.style.disabled(`${disabledCursor} ${indexLabel}${item.name} ${disabledLabel}`);
9220
9218
  }
9221
- if (isActive) {
9222
- description = item.description;
9223
- }
9224
- const checkbox = item.checked ? theme.icon.checked : theme.icon.unchecked;
9225
- const name = item.checked ? item.checkedName : item.name;
9226
9219
  const color = isActive ? theme.style.highlight : (x) => x;
9227
- return color(`${cursor}${checkbox} ${name}`);
9220
+ return color(`${cursor} ${indexLabel}${item.name}`);
9228
9221
  },
9229
9222
  pageSize,
9230
9223
  loop
9231
9224
  });
9232
9225
  if (status === "done") {
9233
- const selection = items.filter(isChecked);
9234
- const answer = theme.style.answer(theme.style.renderSelectedChoices(selection, items));
9235
- return [prefix, message, answer].filter(Boolean).join(" ");
9226
+ return [prefix, message, theme.style.answer(selectedChoice.short)].filter(Boolean).join(" ");
9236
9227
  }
9237
- const keys = [
9238
- ["\u2191\u2193", "navigate"],
9239
- ["space", "select"]
9240
- ];
9241
- if (shortcuts.all)
9242
- keys.push([shortcuts.all, "all"]);
9243
- if (shortcuts.invert)
9244
- keys.push([shortcuts.invert, "invert"]);
9245
- keys.push(["\u23CE", "submit"]);
9246
- const helpLine = theme.style.keysHelpTip(keys);
9228
+ const { description } = selectedChoice;
9247
9229
  const lines = [
9248
9230
  [prefix, message].filter(Boolean).join(" "),
9249
9231
  page,
@@ -9434,7 +9416,7 @@ async function runInstallCommand(input) {
9434
9416
  hostDefinitions: input.hostDefinitions,
9435
9417
  selectHosts: input.selectHosts,
9436
9418
  selectHost: input.selectHost,
9437
- promptSelectHosts: input.promptSelectHosts,
9419
+ promptSelectHost: input.promptSelectHost,
9438
9420
  ttyPath: input.ttyPath
9439
9421
  });
9440
9422
  for (const destination of destinations) {
@@ -9486,12 +9468,9 @@ async function resolveInstallDestinations(args, options = {}) {
9486
9468
  const detected = detectInstallHosts(options.hostDefinitions ?? getDefaultHostDefinitions());
9487
9469
  const compatible = detected;
9488
9470
  const incompatible = [];
9489
- if (compatible.length === 1) {
9490
- return [toInstallDestination(compatible[0])];
9491
- }
9492
- if (compatible.length > 1) {
9493
- const selected = options.selectHosts ? await options.selectHosts(compatible, incompatible) : options.selectHost ? [await options.selectHost(compatible)] : await (options.promptSelectHosts ?? promptForInstallHosts)(compatible, incompatible);
9494
- return validateSelectedHosts(selected, compatible).map(toInstallDestination);
9471
+ if (compatible.length > 0) {
9472
+ const selected = options.selectHosts ? validateSingleSelectedHost(await options.selectHosts(compatible, incompatible), compatible) : options.selectHost ? validateSingleSelectedHost(await options.selectHost(compatible), compatible) : validateSingleSelectedHost(await (options.promptSelectHost ?? promptForInstallHost)(compatible, incompatible), compatible);
9473
+ return [toInstallDestination(selected)];
9495
9474
  }
9496
9475
  throw new Error(
9497
9476
  [
@@ -9541,7 +9520,7 @@ function detectInstallHosts(hosts) {
9541
9520
  return true;
9542
9521
  });
9543
9522
  }
9544
- async function promptForInstallHosts(hosts, incompatibleHosts) {
9523
+ async function promptForInstallHost(hosts, incompatibleHosts) {
9545
9524
  try {
9546
9525
  if (!process.stdin.isTTY || !process.stdout.isTTY) {
9547
9526
  throw new Error("interactive terminal is required");
@@ -9564,14 +9543,10 @@ Extensions: ${host.extensionsDir}`
9564
9543
  }))
9565
9544
  ];
9566
9545
  return await dist_default4({
9567
- message: "Choose one or more Claw installations to install claw-control-center:",
9546
+ message: "Choose the Claw installation to connect with this 53AIHub agent:",
9568
9547
  choices,
9569
9548
  pageSize: Math.min(Math.max(choices.length, 5), 12),
9570
- required: true,
9571
- shortcuts: {
9572
- all: "a",
9573
- invert: "i"
9574
- }
9549
+ loop: true
9575
9550
  });
9576
9551
  } catch (error) {
9577
9552
  throw new Error(
@@ -9594,20 +9569,18 @@ function toInstallDestination(host) {
9594
9569
  installKind: host.installKind ?? "openclaw"
9595
9570
  };
9596
9571
  }
9597
- function validateSelectedHosts(selected, compatible) {
9572
+ function validateSingleSelectedHost(selected, compatible) {
9573
+ if (Array.isArray(selected)) {
9574
+ if (selected.length !== 1) {
9575
+ throw new Error("select exactly one Claw host for this 53AIHub agent");
9576
+ }
9577
+ return validateSingleSelectedHost(selected[0], compatible);
9578
+ }
9598
9579
  const compatibleIds = new Set(compatible.map((host) => host.id));
9599
- const invalid = selected.find((host) => !compatibleIds.has(host.id));
9600
- if (invalid) {
9601
- throw new Error(`selected host is not installable: ${invalid.label}`);
9580
+ if (!compatibleIds.has(selected.id)) {
9581
+ throw new Error(`selected host is not installable: ${selected.label}`);
9602
9582
  }
9603
- const seen = /* @__PURE__ */ new Set();
9604
- return selected.filter((host) => {
9605
- if (seen.has(host.id)) {
9606
- return false;
9607
- }
9608
- seen.add(host.id);
9609
- return true;
9610
- });
9583
+ return selected;
9611
9584
  }
9612
9585
  function formatHostList(hosts) {
9613
9586
  return hosts.flatMap((host) => [
@@ -22,7 +22,7 @@ type HostDefinition = {
22
22
  installKind?: "openclaw" | "hermes";
23
23
  incompatibilityReason?: string;
24
24
  };
25
- type PromptSelectHosts = (hosts: HostDefinition[], incompatibleHosts: HostDefinition[]) => Promise<HostDefinition[]>;
25
+ type PromptSelectHost = (hosts: HostDefinition[], incompatibleHosts: HostDefinition[]) => Promise<HostDefinition>;
26
26
  declare function installIntoQClaw(input: InstallInput): Promise<{
27
27
  configPath: string;
28
28
  extensionsDir: string;
@@ -52,8 +52,8 @@ declare function runInstallCommand(input: {
52
52
  hostDefinitions?: HostDefinition[];
53
53
  selectHosts?: (hosts: HostDefinition[], incompatibleHosts: HostDefinition[]) => Promise<HostDefinition[]>;
54
54
  selectHost?: (hosts: HostDefinition[]) => Promise<HostDefinition>;
55
- promptSelectHosts?: PromptSelectHosts;
55
+ promptSelectHost?: PromptSelectHost;
56
56
  ttyPath?: string;
57
57
  }): Promise<void>;
58
58
 
59
- export { type HostDefinition, type PromptSelectHosts, installIntoHermes, installIntoOpenClaw, installIntoQClaw, runInstallCommand };
59
+ export { type HostDefinition, type PromptSelectHost, installIntoHermes, installIntoOpenClaw, installIntoQClaw, runInstallCommand };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claw-control-center",
3
- "version": "0.1.11",
3
+ "version": "0.1.13",
4
4
  "type": "module",
5
5
  "main": "dist/index.cjs",
6
6
  "types": "dist/index.d.ts",