| 1733 | // silently not-matching in the gate would look like channels are |
| 1734 | // "on" but nothing ever fires. |
| 1735 | const parseChannelEntries = (raw: string[], flag: string): ChannelEntry[] => { |
| 1736 | const entries: ChannelEntry[] = []; |
| 1737 | const bad: string[] = []; |
| 1738 | for (const c of raw) { |
| 1739 | if (c.startsWith('plugin:')) { |
| 1740 | const rest = c.slice(7); |
| 1741 | const at = rest.indexOf('@'); |
| 1742 | if (at <= 0 || at === rest.length - 1) { |
| 1743 | bad.push(c); |
| 1744 | } else { |
| 1745 | entries.push({ |
| 1746 | kind: 'plugin', |
| 1747 | name: rest.slice(0, at), |
| 1748 | marketplace: rest.slice(at + 1) |
| 1749 | }); |
| 1750 | } |
| 1751 | } else if (c.startsWith('server:') && c.length > 7) { |
| 1752 | entries.push({ |
| 1753 | kind: 'server', |
| 1754 | name: c.slice(7) |
| 1755 | }); |
| 1756 | } else { |
| 1757 | bad.push(c); |
| 1758 | } |
| 1759 | } |
| 1760 | if (bad.length > 0) { |
| 1761 | process.stderr.write(chalk.red(`${flag} entries must be tagged: ${bad.join(', ')}\n` + ` plugin:<name>@<marketplace> — plugin-provided channel (allowlist enforced)\n` + ` server:<name> — manually configured MCP server\n`)); |
| 1762 | process.exit(1); |
| 1763 | } |
| 1764 | return entries; |
| 1765 | }; |
| 1766 | const channelOpts = options as { |
| 1767 | channels?: string[]; |
| 1768 | dangerouslyLoadDevelopmentChannels?: string[]; |