fix: handle sensitive, number-clear, and array-clear edge cases in plugin config TUI (#60640) (#60640)

- Skip sensitive fields with a note directing users to openclaw config set
  or the Web UI (WizardPrompter has no masked input)
- Clear number fields to undefined when input is empty instead of storing 0
- Allow clearing array fields to undefined via empty input
This commit is contained in:
George Zhang
2026-04-03 19:27:26 -07:00
committed by GitHub
parent 761bd3bbd0
commit 87885b948a
2 changed files with 44 additions and 10 deletions

View File

@@ -42,6 +42,21 @@ describe("discoverConfigurablePlugins", () => {
expect(result).toHaveLength(0);
});
it("excludes sensitive fields from promptable hints", () => {
const plugins = [
makeManifestPlugin("secret-plugin", {
endpoint: { label: "Endpoint" },
apiKey: { label: "API Key", sensitive: true },
}),
];
const result = discoverConfigurablePlugins({ manifestPlugins: plugins });
expect(result).toHaveLength(1);
// sensitive fields are still included in uiHints for discovery —
// they are skipped at prompt time, not at discovery time
expect(result[0].uiHints.endpoint).toBeDefined();
expect(result[0].uiHints.apiKey).toBeDefined();
});
it("excludes plugins where all fields are advanced", () => {
const plugins = [
makeManifestPlugin("all-advanced", {

View File

@@ -152,6 +152,16 @@ async function promptPluginFields(params: {
const label = hint.label ?? key;
const helpSuffix = hint.help ? `${hint.help}` : "";
// Skip sensitive fields — WizardPrompter has no masked input;
// direct users to openclaw config set or the Web UI instead.
if (hint.sensitive) {
await prompter.note(
`"${label}" is sensitive. Set it via:\n openclaw config set plugins.entries.${plugin.id}.config.${key} <value>\nor use the Web UI Settings page.`,
"Sensitive field",
);
continue;
}
// Handle enum fields with select
if (schemaProp?.enum && Array.isArray(schemaProp.enum)) {
const options = schemaProp.enum.map((v) => ({
@@ -193,17 +203,21 @@ async function promptPluginFields(params: {
if (schemaProp?.type === "array") {
const currentStr = Array.isArray(currentValue) ? (currentValue as unknown[]).join(", ") : "";
const input = await prompter.text({
message: `${label} (comma-separated)${helpSuffix}`,
message: `${label} (comma-separated, empty to clear)${helpSuffix}`,
initialValue: currentStr,
placeholder: hint.placeholder ?? "value1, value2",
});
const trimmed = input.trim();
if (trimmed) {
const values = trimmed
.split(",")
.map((v) => v.trim())
.filter(Boolean);
updatedConfig[key] = values;
if (trimmed !== currentStr) {
if (trimmed) {
const values = trimmed
.split(",")
.map((v) => v.trim())
.filter(Boolean);
updatedConfig[key] = values;
} else {
updatedConfig[key] = undefined;
}
changed = true;
}
continue;
@@ -220,10 +234,15 @@ async function promptPluginFields(params: {
if (trimmed !== currentStr) {
// Try to parse as number if schema says number
if (schemaProp?.type === "number") {
const parsed = Number(trimmed);
if (Number.isFinite(parsed)) {
updatedConfig[key] = parsed;
if (trimmed === "") {
updatedConfig[key] = undefined;
changed = true;
} else {
const parsed = Number(trimmed);
if (Number.isFinite(parsed)) {
updatedConfig[key] = parsed;
changed = true;
}
}
} else {
updatedConfig[key] = trimmed || undefined;