mirror of
http://10.0.2.1:3031/sauer/claude-code.git
synced 2026-06-30 15:06:57 +10:00
266 lines
29 KiB
TypeScript
266 lines
29 KiB
TypeScript
|
|
import { c as _c } from "react/compiler-runtime";
|
||
|
|
// Conditionally require()'d in LogoV2.tsx behind feature('KAIROS') ||
|
||
|
|
// feature('KAIROS_CHANNELS'). No feature() guard here — the whole file
|
||
|
|
// tree-shakes via the require pattern when both flags are false (see
|
||
|
|
// docs/feature-gating.md). Do NOT import this module statically from
|
||
|
|
// unguarded code.
|
||
|
|
|
||
|
|
import * as React from 'react';
|
||
|
|
import { useState } from 'react';
|
||
|
|
import { type ChannelEntry, getAllowedChannels, getHasDevChannels } from '../../bootstrap/state.js';
|
||
|
|
import { Box, Text } from '../../ink.js';
|
||
|
|
import { isChannelsEnabled } from '../../services/mcp/channelAllowlist.js';
|
||
|
|
import { getEffectiveChannelAllowlist } from '../../services/mcp/channelNotification.js';
|
||
|
|
import { getMcpConfigsByScope } from '../../services/mcp/config.js';
|
||
|
|
import { getClaudeAIOAuthTokens, getSubscriptionType } from '../../utils/auth.js';
|
||
|
|
import { loadInstalledPluginsV2 } from '../../utils/plugins/installedPluginsManager.js';
|
||
|
|
import { getSettingsForSource } from '../../utils/settings/settings.js';
|
||
|
|
export function ChannelsNotice() {
|
||
|
|
const $ = _c(32);
|
||
|
|
const [t0] = useState(_temp);
|
||
|
|
const {
|
||
|
|
channels,
|
||
|
|
disabled,
|
||
|
|
noAuth,
|
||
|
|
policyBlocked,
|
||
|
|
list,
|
||
|
|
unmatched
|
||
|
|
} = t0;
|
||
|
|
if (channels.length === 0) {
|
||
|
|
return null;
|
||
|
|
}
|
||
|
|
const hasNonDev = channels.some(_temp2);
|
||
|
|
const flag = getHasDevChannels() && hasNonDev ? "Channels" : getHasDevChannels() ? "--dangerously-load-development-channels" : "--channels";
|
||
|
|
if (disabled) {
|
||
|
|
let t1;
|
||
|
|
if ($[0] !== flag || $[1] !== list) {
|
||
|
|
t1 = <Text color="error">{flag} ignored ({list})</Text>;
|
||
|
|
$[0] = flag;
|
||
|
|
$[1] = list;
|
||
|
|
$[2] = t1;
|
||
|
|
} else {
|
||
|
|
t1 = $[2];
|
||
|
|
}
|
||
|
|
let t2;
|
||
|
|
if ($[3] === Symbol.for("react.memo_cache_sentinel")) {
|
||
|
|
t2 = <Text dimColor={true}>Channels are not currently available</Text>;
|
||
|
|
$[3] = t2;
|
||
|
|
} else {
|
||
|
|
t2 = $[3];
|
||
|
|
}
|
||
|
|
let t3;
|
||
|
|
if ($[4] !== t1) {
|
||
|
|
t3 = <Box paddingLeft={2} flexDirection="column">{t1}{t2}</Box>;
|
||
|
|
$[4] = t1;
|
||
|
|
$[5] = t3;
|
||
|
|
} else {
|
||
|
|
t3 = $[5];
|
||
|
|
}
|
||
|
|
return t3;
|
||
|
|
}
|
||
|
|
if (noAuth) {
|
||
|
|
let t1;
|
||
|
|
if ($[6] !== flag || $[7] !== list) {
|
||
|
|
t1 = <Text color="error">{flag} ignored ({list})</Text>;
|
||
|
|
$[6] = flag;
|
||
|
|
$[7] = list;
|
||
|
|
$[8] = t1;
|
||
|
|
} else {
|
||
|
|
t1 = $[8];
|
||
|
|
}
|
||
|
|
let t2;
|
||
|
|
if ($[9] === Symbol.for("react.memo_cache_sentinel")) {
|
||
|
|
t2 = <Text dimColor={true}>Channels require claude.ai authentication · run /login, then restart</Text>;
|
||
|
|
$[9] = t2;
|
||
|
|
} else {
|
||
|
|
t2 = $[9];
|
||
|
|
}
|
||
|
|
let t3;
|
||
|
|
if ($[10] !== t1) {
|
||
|
|
t3 = <Box paddingLeft={2} flexDirection="column">{t1}{t2}</Box>;
|
||
|
|
$[10] = t1;
|
||
|
|
$[11] = t3;
|
||
|
|
} else {
|
||
|
|
t3 = $[11];
|
||
|
|
}
|
||
|
|
return t3;
|
||
|
|
}
|
||
|
|
if (policyBlocked) {
|
||
|
|
let t1;
|
||
|
|
if ($[12] !== flag || $[13] !== list) {
|
||
|
|
t1 = <Text color="error">{flag} blocked by org policy ({list})</Text>;
|
||
|
|
$[12] = flag;
|
||
|
|
$[13] = list;
|
||
|
|
$[14] = t1;
|
||
|
|
} else {
|
||
|
|
t1 = $[14];
|
||
|
|
}
|
||
|
|
let t2;
|
||
|
|
let t3;
|
||
|
|
if ($[15] === Symbol.for("react.memo_cache_sentinel")) {
|
||
|
|
t2 = <Text dimColor={true}>Inbound messages will be silently dropped</Text>;
|
||
|
|
t3 = <Text dimColor={true}>Have an administrator set channelsEnabled: true in managed settings to enable</Text>;
|
||
|
|
$[15] = t2;
|
||
|
|
$[16] = t3;
|
||
|
|
} else {
|
||
|
|
t2 = $[15];
|
||
|
|
t3 = $[16];
|
||
|
|
}
|
||
|
|
let t4;
|
||
|
|
if ($[17] !== unmatched) {
|
||
|
|
t4 = unmatched.map(_temp3);
|
||
|
|
$[17] = unmatched;
|
||
|
|
$[18] = t4;
|
||
|
|
} else {
|
||
|
|
t4 = $[18];
|
||
|
|
}
|
||
|
|
let t5;
|
||
|
|
if ($[19] !== t1 || $[20] !== t4) {
|
||
|
|
t5 = <Box paddingLeft={2} flexDirection="column">{t1}{t2}{t3}{t4}</Box>;
|
||
|
|
$[19] = t1;
|
||
|
|
$[20] = t4;
|
||
|
|
$[21] = t5;
|
||
|
|
} else {
|
||
|
|
t5 = $[21];
|
||
|
|
}
|
||
|
|
return t5;
|
||
|
|
}
|
||
|
|
let t1;
|
||
|
|
if ($[22] !== list) {
|
||
|
|
t1 = <Text color="error">Listening for channel messages from: {list}</Text>;
|
||
|
|
$[22] = list;
|
||
|
|
$[23] = t1;
|
||
|
|
} else {
|
||
|
|
t1 = $[23];
|
||
|
|
}
|
||
|
|
let t2;
|
||
|
|
if ($[24] !== flag) {
|
||
|
|
t2 = <Text dimColor={true}>Experimental · inbound messages will be pushed into this session, this carries prompt injection risks. Restart Claude Code without {flag} to disable.</Text>;
|
||
|
|
$[24] = flag;
|
||
|
|
$[25] = t2;
|
||
|
|
} else {
|
||
|
|
t2 = $[25];
|
||
|
|
}
|
||
|
|
let t3;
|
||
|
|
if ($[26] !== unmatched) {
|
||
|
|
t3 = unmatched.map(_temp4);
|
||
|
|
$[26] = unmatched;
|
||
|
|
$[27] = t3;
|
||
|
|
} else {
|
||
|
|
t3 = $[27];
|
||
|
|
}
|
||
|
|
let t4;
|
||
|
|
if ($[28] !== t1 || $[29] !== t2 || $[30] !== t3) {
|
||
|
|
t4 = <Box paddingLeft={2} flexDirection="column">{t1}{t2}{t3}</Box>;
|
||
|
|
$[28] = t1;
|
||
|
|
$[29] = t2;
|
||
|
|
$[30] = t3;
|
||
|
|
$[31] = t4;
|
||
|
|
} else {
|
||
|
|
t4 = $[31];
|
||
|
|
}
|
||
|
|
return t4;
|
||
|
|
}
|
||
|
|
function _temp4(u_0) {
|
||
|
|
return <Text key={`${formatEntry(u_0.entry)}:${u_0.why}`} color="warning">{formatEntry(u_0.entry)} · {u_0.why}</Text>;
|
||
|
|
}
|
||
|
|
function _temp3(u) {
|
||
|
|
return <Text key={`${formatEntry(u.entry)}:${u.why}`} color="warning">{formatEntry(u.entry)} · {u.why}</Text>;
|
||
|
|
}
|
||
|
|
function _temp2(c) {
|
||
|
|
return !c.dev;
|
||
|
|
}
|
||
|
|
function _temp() {
|
||
|
|
const ch = getAllowedChannels();
|
||
|
|
if (ch.length === 0) {
|
||
|
|
return {
|
||
|
|
channels: ch,
|
||
|
|
disabled: false,
|
||
|
|
noAuth: false,
|
||
|
|
policyBlocked: false,
|
||
|
|
list: "",
|
||
|
|
unmatched: [] as Unmatched[]
|
||
|
|
};
|
||
|
|
}
|
||
|
|
const l = ch.map(formatEntry).join(", ");
|
||
|
|
const sub = getSubscriptionType();
|
||
|
|
const managed = sub === "team" || sub === "enterprise";
|
||
|
|
const policy = getSettingsForSource("policySettings");
|
||
|
|
const allowlist = getEffectiveChannelAllowlist(sub, policy?.allowedChannelPlugins);
|
||
|
|
return {
|
||
|
|
channels: ch,
|
||
|
|
disabled: !isChannelsEnabled(),
|
||
|
|
noAuth: !getClaudeAIOAuthTokens()?.accessToken,
|
||
|
|
policyBlocked: managed && policy?.channelsEnabled !== true,
|
||
|
|
list: l,
|
||
|
|
unmatched: findUnmatched(ch, allowlist)
|
||
|
|
};
|
||
|
|
}
|
||
|
|
function formatEntry(c: ChannelEntry): string {
|
||
|
|
return c.kind === 'plugin' ? `plugin:${c.name}@${c.marketplace}` : `server:${c.name}`;
|
||
|
|
}
|
||
|
|
type Unmatched = {
|
||
|
|
entry: ChannelEntry;
|
||
|
|
why: string;
|
||
|
|
};
|
||
|
|
function findUnmatched(entries: readonly ChannelEntry[], allowlist: ReturnType<typeof getEffectiveChannelAllowlist>): Unmatched[] {
|
||
|
|
// Server-kind: build one Set from all scopes up front. getMcpConfigsByScope
|
||
|
|
// is not cached (project scope walks the dir tree); getMcpConfigByName would
|
||
|
|
// redo that walk per entry.
|
||
|
|
const scopes = ['enterprise', 'user', 'project', 'local'] as const;
|
||
|
|
const configured = new Set<string>();
|
||
|
|
for (const scope of scopes) {
|
||
|
|
for (const name of Object.keys(getMcpConfigsByScope(scope).servers)) {
|
||
|
|
configured.add(name);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Plugin-kind installed check: installed_plugins.json keys are
|
||
|
|
// `name@marketplace`. loadInstalledPluginsV2 is cached.
|
||
|
|
const installedPluginIds = new Set(Object.keys(loadInstalledPluginsV2().plugins));
|
||
|
|
|
||
|
|
// Plugin-kind allowlist check: same {marketplace, plugin} test as the
|
||
|
|
// gate at channelNotification.ts. entry.dev bypasses (dev flag opts out
|
||
|
|
// of the allowlist). Org list replaces ledger when set (team/enterprise).
|
||
|
|
// GrowthBook _CACHED_MAY_BE_STALE — cold cache yields [] so every plugin
|
||
|
|
// entry warns; same tradeoff the gate already accepts.
|
||
|
|
const {
|
||
|
|
entries: allowed,
|
||
|
|
source
|
||
|
|
} = allowlist;
|
||
|
|
|
||
|
|
// Independent ifs — a plugin entry that's both uninstalled AND
|
||
|
|
// unlisted shows two lines. Server kind checks config + dev flag.
|
||
|
|
const out: Unmatched[] = [];
|
||
|
|
for (const entry of entries) {
|
||
|
|
if (entry.kind === 'server') {
|
||
|
|
if (!configured.has(entry.name)) {
|
||
|
|
out.push({
|
||
|
|
entry,
|
||
|
|
why: 'no MCP server configured with that name'
|
||
|
|
});
|
||
|
|
}
|
||
|
|
if (!entry.dev) {
|
||
|
|
out.push({
|
||
|
|
entry,
|
||
|
|
why: 'server: entries need --dangerously-load-development-channels'
|
||
|
|
});
|
||
|
|
}
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
if (!installedPluginIds.has(`${entry.name}@${entry.marketplace}`)) {
|
||
|
|
out.push({
|
||
|
|
entry,
|
||
|
|
why: 'plugin not installed'
|
||
|
|
});
|
||
|
|
}
|
||
|
|
if (!entry.dev && !allowed.some(e => e.plugin === entry.name && e.marketplace === entry.marketplace)) {
|
||
|
|
out.push({
|
||
|
|
entry,
|
||
|
|
why: source === 'org' ? "not on your org's approved channels list" : 'not on the approved channels allowlist'
|
||
|
|
});
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return out;
|
||
|
|
}
|
||
|
|
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJSZWFjdCIsInVzZVN0YXRlIiwiQ2hhbm5lbEVudHJ5IiwiZ2V0QWxsb3dlZENoYW5uZWxzIiwiZ2V0SGFzRGV2Q2hhbm5lbHMiLCJCb3giLCJUZXh0IiwiaXNDaGFubmVsc0VuYWJsZWQiLCJnZXRFZmZlY3RpdmVDaGFubmVsQWxsb3dsaXN0IiwiZ2V0TWNwQ29uZmlnc0J5U2NvcGUiLCJnZXRDbGF1ZGVBSU9BdXRoVG9rZW5zIiwiZ2V0U3Vic2NyaXB0aW9uVHlwZSIsImxvYWRJbnN0YWxsZWRQbHVnaW5zVjIiLCJnZXRTZXR0aW5nc0ZvclNvdXJjZSIsIkNoYW5uZWxzTm90aWNlIiwiJCIsIl9jIiwidDAiLCJfdGVtcCIsImNoYW5uZWxzIiwiZGlzYWJsZWQiLCJub0F1dGgiLCJwb2xpY3lCbG9ja2VkIiwibGlzdCIsInVubWF0Y2hlZCIsImxlbmd0aCIsImhhc05vbkRldiIsInNvbWUiLCJfdGVtcDIiLCJmbGFnIiwidDEiLCJ0MiIsIlN5bWJvbCIsImZvciIsInQzIiwidDQiLCJtYXAiLCJfdGVtcDMiLCJ0NSIsIl90ZW1wNCIsInVfMCIsImZvcm1hdEVudHJ5IiwidSIsImVudHJ5Iiwid2h5IiwiYyIsImRldiIsImNoIiwiVW5tYXRjaGVkIiwibCIsImpvaW4iLCJzdWIiLCJtYW5hZ2VkIiwicG9saWN5IiwiYWxsb3dsaXN0IiwiYWxsb3dlZENoYW5uZWxQbHVnaW5zIiwiYWNjZXNzVG9rZW4iLCJjaGFubmVsc0VuYWJsZWQiLCJmaW5kVW5tYXRjaGVkIiwia2luZCIsIm5hbWUiLCJtYXJrZXRwbGFjZSIsImVudHJpZXMiLCJSZXR1cm5UeXBlIiwic2NvcGVzIiwiY29uc3QiLCJjb25maWd1cmVkIiwiU2V0Iiwic2NvcGUiLCJPYmplY3QiLCJrZXlzIiwic2VydmVycyIsImFkZCIsImluc3RhbGxlZFBsdWdpbklkcyIsInBsdWdpbnMiLCJhbGxvd2VkIiwic291cmNlIiwib3V0IiwiaGFzIiwicHVzaCIsImUiLCJwbHVnaW4iXSwic291cmNlcyI6WyJDaGFubmVsc05vdGljZS50c3giXSwic291cmNlc0NvbnRlbnQiOlsiLy8gQ29uZGl0aW9uYWxseSByZXF1aXJlKCknZCBpbiBMb2dvVjIudHN4IGJlaGluZCBmZWF0dXJlKCdLQUlST1MnKSB8fFxuLy8gZmVhdHVyZSgnS0FJUk9TX0NIQU5ORUxTJykuIE5vIGZlYXR1cmUoKSBndWFyZCBoZXJlIOKAlCB0aGUgd2hvbGUgZmlsZVxuLy8gdHJlZS1zaGFrZXMgdmlhIHRoZSByZXF1aXJlIHBhdHRlcm4gd2hlbiBib3RoIGZsYWdzIGFyZSBmYWxzZSAoc2VlXG4vLyBkb2NzL2ZlYXR1cmUtZ2F0aW5nLm1kKS4gRG8gTk9UIGltcG9ydCB0aGlzIG1vZHVsZSBzdGF0aWNhbGx5IGZyb21cbi8vIHVuZ3VhcmRlZCBjb2RlLlxuXG5pbXBvcnQgKiBhcyBSZWFjdCBmcm9tICdyZWFjdCdcbmltcG9ydCB7IHVzZVN0YXRlIH0gZnJvbSAncmVhY3QnXG5pbXBvcnQge1xuICB0eXBlIENoYW5uZWxFbnRyeSxcbiAgZ2V0QWxsb3dlZENoYW5uZWxzLFxuICBnZXRIYXNEZXZDaGFubmVscyxcbn0gZnJvbSAnLi4vLi4vYm9vdHN0cmFwL3N0YXRlLmpzJ1xuaW1wb3J0IHsgQm94LCBUZXh0IH0gZnJvbSAnLi4vLi4vaW5rLmpzJ1xuaW1wb3J0IHsgaXNDaGFubmVsc0VuYWJsZWQgfSBmcm9tICcuLi8uLi9zZXJ2aWNlcy9tY3AvY2hhbm5lbEFsbG93bGlzdC5qcydcbmltcG9ydCB7IGdldEVmZmVjdGl2ZUNoYW5uZWxBbGxvd2xpc3QgfSBmcm9tICcuLi8uLi9zZXJ2aWNlcy9tY3AvY2hhbm5lbE5vdGlmaWNhdGlvbi5qcydcbmltcG9ydCB7IGdldE1jcENvbmZpZ3NCeVNjb3BlIH0gZnJvbSAnLi4vLi4vc2VydmljZXMvbWNwL2NvbmZpZy5qcydcbmltcG9ydCB7XG4gIGdldENsYXVkZUFJT0F1dGhUb2tlbnMsXG4gIGdldFN1YnNjcmlwdGlvblR5cGUsXG59IGZyb20gJy4uLy4uL3V0aWxzL2F1dGguanMnXG5pbXBvcnQgeyBsb2FkSW5zdGFsbGVkUGx1Z2luc1YyIH0gZnJvbSAnLi4vLi4vdXRpbHMvcGx1Z2lucy9pbnN0YWxsZWRQbHVnaW5zTWFuYWdlci5qcydcbmltcG9ydCB7IGdldFNldHRpbmdzRm9yU291cmNlIH0gZnJvbSAnLi4vLi4vdXRpbHMvc2V0dGluZ3Mvc2V0dGluZ3MuanMnXG5cbmV4cG9ydCBmdW5jdGlvbiBDaGFubmVsc05vdGljZSgpOiBSZWFjdC5SZWFjdE5vZGUge1xuICAvLyBTbmFwc2hvdCBhbGwgcmVhZHMgYXQgbW91bnQuIFRoaXMgbm90aWNlIGVudGVycyBzY3JvbGxiYWNrIGltbWVkaWF0ZWx5XG4gIC8vIGFmdGVyIHRoZSBsb2dvOyBhbnkgcmUtcmVuZGVyIHBhc3QgdGhhdCBwb2ludCBmb3JjZXMgYSBmdWxsIHRlcm1pbmFsXG4gIC8vIHJlc2V0LiBnZXRBbGxvd2VkQ2hhbm5lbHMgKGJvb3RzdHJhcCBzdGF0ZSksIGdldFNldHRpbmdzRm9yU291cmNlXG4gIC8vIChzZXNzaW9uIGNhY2hlIHVwZGF0ZWQgYnkgYmFja2dyb3VuZCBwb2xsaW5nIC8gL2xvZ2luKSwgYW5kXG4gIC8vIGlzQ2hhbm5lbHNFbmFibGVkIChHcm93dGhCb29rIDUtbWluIHJlZnJlc2gpIG11c3QgYmUgY2FwdHVyZWQgb25jZVxuICAvLyBzbyBhIGxhdGVyIHJlLXJlbmRlciBjYW5ub3QgZmxpcCBicmFuY2hlcy5cbiAgY29uc3QgW3sgY2hhbm5lbHMsIGRpc2FibGVkLCBub0F1dGgsIHBvbGljeUJsb2NrZWQsIGxpc3QsIHVubWF0Y2hlZCB9XSA9XG4gICAgdXNlU3RhdGUoKCkgPT4ge1xuICAgICAgY29uc3QgY2ggPSBnZXRBbGxvd2VkQ2hhbm5lbHMoKVxuICAgICAgaWYgKGNoLmxlbmd0aCA9PT0gMClcbiAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICBjaGFubmVsczogY2gsXG4gICAgICAgICAgZGlzYWJsZWQ6IGZhbHNlLFxuICAgICAgICAgIG5vQXV0aDogZmFsc2UsXG4gICAgICAgICAgcG9saWN5QmxvY2tlZDogZmFsc2UsXG4gICAgICAgICAgbGlzdDogJycsXG4gICAgICAgICAgdW5tYXRjaGVkOiBbXSBhcyBVbm1hdGNoZWRbXSxcbiAgICAgICAgfVxuICAgICAgY29uc3QgbCA9IGNoLm1hcChmb3JtYXRFbnRyeSkuam9pbignLCAnKVxuICAgICAgY29uc3Qgc3ViID0gZ2V0U3Vic2NyaXB0aW9uVHlwZSgpXG4gICAgICBjb25zdCBtYW5hZ2VkID0gc3ViID09PSAndGVhbScgfHwgc3ViID09PSAnZW50ZXJwcmlzZSdcbiAgICAgIGNvbnN0IHBvbGljeSA9IGdldFNldHRpbmdzRm9yU291cmNlKCdwb2xpY3lTZXR0aW5ncycpXG4gICAgICBjb25zdCBhbGxvd2x
|