feat(channels): add resolve command + defaults

This commit is contained in:
Peter Steinberger
2026-01-18 00:41:57 +00:00
parent b543339373
commit c7ea47e886
60 changed files with 4418 additions and 101 deletions

View File

@@ -6,9 +6,11 @@ import {
resolveDefaultSlackAccountId,
resolveSlackAccount,
} from "../../../slack/accounts.js";
import { resolveSlackChannelAllowlist } from "../../../slack/resolve-channels.js";
import { formatDocsLink } from "../../../terminal/links.js";
import type { WizardPrompter } from "../../../wizard/prompts.js";
import type { ChannelOnboardingAdapter, ChannelOnboardingDmPolicy } from "../onboarding-types.js";
import { promptChannelAccessConfig } from "./channel-access.js";
import { addWildcardAllowFrom, promptAccountId } from "./helpers.js";
const channel = "slack" as const;
@@ -121,6 +123,85 @@ async function noteSlackTokenHelp(prompter: WizardPrompter, botName: string): Pr
);
}
function setSlackGroupPolicy(
cfg: ClawdbotConfig,
accountId: string,
groupPolicy: "open" | "allowlist" | "disabled",
): ClawdbotConfig {
if (accountId === DEFAULT_ACCOUNT_ID) {
return {
...cfg,
channels: {
...cfg.channels,
slack: {
...cfg.channels?.slack,
enabled: true,
groupPolicy,
},
},
};
}
return {
...cfg,
channels: {
...cfg.channels,
slack: {
...cfg.channels?.slack,
enabled: true,
accounts: {
...cfg.channels?.slack?.accounts,
[accountId]: {
...cfg.channels?.slack?.accounts?.[accountId],
enabled: cfg.channels?.slack?.accounts?.[accountId]?.enabled ?? true,
groupPolicy,
},
},
},
},
};
}
function setSlackChannelAllowlist(
cfg: ClawdbotConfig,
accountId: string,
channelKeys: string[],
): ClawdbotConfig {
const channels = Object.fromEntries(
channelKeys.map((key) => [key, { allow: true }]),
);
if (accountId === DEFAULT_ACCOUNT_ID) {
return {
...cfg,
channels: {
...cfg.channels,
slack: {
...cfg.channels?.slack,
enabled: true,
channels,
},
},
};
}
return {
...cfg,
channels: {
...cfg.channels,
slack: {
...cfg.channels?.slack,
enabled: true,
accounts: {
...cfg.channels?.slack?.accounts,
[accountId]: {
...cfg.channels?.slack?.accounts?.[accountId],
enabled: cfg.channels?.slack?.accounts?.[accountId]?.enabled ?? true,
channels,
},
},
},
},
};
}
const dmPolicy: ChannelOnboardingDmPolicy = {
label: "Slack",
channel,
@@ -284,6 +365,68 @@ export const slackOnboardingAdapter: ChannelOnboardingAdapter = {
}
}
const accessConfig = await promptChannelAccessConfig({
prompter,
label: "Slack channels",
currentPolicy: resolvedAccount.config.groupPolicy ?? "allowlist",
currentEntries: Object.entries(resolvedAccount.config.channels ?? {})
.filter(([, value]) => value?.allow !== false && value?.enabled !== false)
.map(([key]) => key),
placeholder: "#general, #private, C123",
updatePrompt: Boolean(resolvedAccount.config.channels),
});
if (accessConfig) {
if (accessConfig.policy !== "allowlist") {
next = setSlackGroupPolicy(next, slackAccountId, accessConfig.policy);
} else {
let keys = accessConfig.entries;
const accountWithTokens = resolveSlackAccount({
cfg: next,
accountId: slackAccountId,
});
if (accountWithTokens.botToken && accessConfig.entries.length > 0) {
try {
const resolved = await resolveSlackChannelAllowlist({
token: accountWithTokens.botToken,
entries: accessConfig.entries,
});
const resolvedKeys = resolved
.filter((entry) => entry.resolved && entry.id)
.map((entry) => entry.id as string);
const unresolved = resolved
.filter((entry) => !entry.resolved)
.map((entry) => entry.input);
keys = [
...resolvedKeys,
...unresolved.map((entry) => entry.trim()).filter(Boolean),
];
if (resolvedKeys.length > 0 || unresolved.length > 0) {
await prompter.note(
[
resolvedKeys.length > 0
? `Resolved: ${resolvedKeys.join(", ")}`
: undefined,
unresolved.length > 0
? `Unresolved (kept as typed): ${unresolved.join(", ")}`
: undefined,
]
.filter(Boolean)
.join("\n"),
"Slack channels",
);
}
} catch (err) {
await prompter.note(
`Channel lookup failed; keeping entries as typed. ${String(err)}`,
"Slack channels",
);
}
}
next = setSlackGroupPolicy(next, slackAccountId, "allowlist");
next = setSlackChannelAllowlist(next, slackAccountId, keys);
}
}
return { cfg: next, accountId: slackAccountId };
},
dmPolicy,