| 1648 | // silently not-matching in the gate would look like channels are |
| 1649 | // "on" but nothing ever fires. |
| 1650 | const parseChannelEntries = (raw: string[], flag: string): ChannelEntry[] => { |
| 1651 | const entries: ChannelEntry[] = []; |
| 1652 | const bad: string[] = []; |
| 1653 | for (const c of raw) { |
| 1654 | if (c.startsWith('plugin:')) { |
| 1655 | const rest = c.slice(7); |
| 1656 | const at = rest.indexOf('@'); |
| 1657 | if (at <= 0 || at === rest.length - 1) { |
| 1658 | bad.push(c); |
| 1659 | } else { |
| 1660 | entries.push({ |
| 1661 | kind: 'plugin', |
| 1662 | name: rest.slice(0, at), |
| 1663 | marketplace: rest.slice(at + 1) |
| 1664 | }); |
| 1665 | } |
| 1666 | } else if (c.startsWith('server:') && c.length > 7) { |
| 1667 | entries.push({ |
| 1668 | kind: 'server', |
| 1669 | name: c.slice(7) |
| 1670 | }); |
| 1671 | } else { |
| 1672 | bad.push(c); |
| 1673 | } |
| 1674 | } |
| 1675 | if (bad.length > 0) { |
| 1676 | 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`)); |
| 1677 | process.exit(1); |
| 1678 | } |
| 1679 | return entries; |
| 1680 | }; |
| 1681 | const channelOpts = options as { |
| 1682 | channels?: string[]; |
| 1683 | dangerouslyLoadDevelopmentChannels?: string[]; |