bingocode 1.0.21 → 1.0.23
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/bingo +12 -0
- package/bin/bingo-win.cjs +28 -0
- package/bin/bingocode-win.cjs +26 -0
- package/bin/claude +12 -0
- package/bin/claude-win.cjs +24 -0
- package/config/bingo-defaults/settings.json +6 -0
- package/package.json +1 -1
- package/src/components/Onboarding.tsx +6 -2
- package/src/manager/CliMenuManager.tsx +125 -52
- package/src/utils/auth.ts +1 -1
package/bin/bingo
CHANGED
|
@@ -4,6 +4,18 @@ set -euo pipefail
|
|
|
4
4
|
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
5
5
|
export CALLER_DIR="${CALLER_DIR:-$(pwd -W 2>/dev/null || pwd)}"
|
|
6
6
|
|
|
7
|
+
# ── 首次部署:将默认 bingo 配置复制到 ~/.claude/bingo/ ──
|
|
8
|
+
_BINGO_CONFIG_DIR="${CLAUDE_CONFIG_DIR:-$HOME/.claude}"
|
|
9
|
+
_BINGO_DIR="$_BINGO_CONFIG_DIR/bingo"
|
|
10
|
+
_BINGO_TARGET="$_BINGO_DIR/settings.json"
|
|
11
|
+
_BINGO_SRC="$ROOT_DIR/config/bingo-defaults/settings.json"
|
|
12
|
+
|
|
13
|
+
if [ ! -f "$_BINGO_TARGET" ] && [ -f "$_BINGO_SRC" ]; then
|
|
14
|
+
mkdir -p "$_BINGO_DIR"
|
|
15
|
+
cp "$_BINGO_SRC" "$_BINGO_TARGET" 2>/dev/null && \
|
|
16
|
+
echo "[bingo] 首次启动:已部署默认配置到 $_BINGO_TARGET" || true
|
|
17
|
+
fi
|
|
18
|
+
|
|
7
19
|
cd "$ROOT_DIR"
|
|
8
20
|
|
|
9
21
|
if [[ -f .env ]]; then
|
package/bin/bingo-win.cjs
CHANGED
|
@@ -7,6 +7,34 @@ const fs = require('fs');
|
|
|
7
7
|
|
|
8
8
|
process.env.NoDefaultCurrentDirectoryInExePath = '1';
|
|
9
9
|
|
|
10
|
+
// ── 首次部署:将默认 bingo 配置复制到 ~/.claude/bingo/ ──
|
|
11
|
+
// 确保新电脑首次启动时 settings.json 存在(含占位符 ANTHROPIC_AUTH_TOKEN),
|
|
12
|
+
// 这样 isOfficialMode() 返回 false,子进程不会走 OAuth 流程。
|
|
13
|
+
// 用户通过 ProviderPanel 激活 provider 后 syncToSettings() 会覆盖此文件。
|
|
14
|
+
(function deployBingoDefaults() {
|
|
15
|
+
const configDir = process.env.CLAUDE_CONFIG_DIR || path.join(os.homedir(), '.claude');
|
|
16
|
+
const bingoDir = path.join(configDir, 'bingo');
|
|
17
|
+
const targetSettings = path.join(bingoDir, 'settings.json');
|
|
18
|
+
|
|
19
|
+
// 只在 settings.json 不存在时才部署(不覆盖已有配置)
|
|
20
|
+
if (!fs.existsSync(targetSettings)) {
|
|
21
|
+
const defaultsDir = path.join(__dirname, '..', 'config', 'bingo-defaults');
|
|
22
|
+
const srcSettings = path.join(defaultsDir, 'settings.json');
|
|
23
|
+
|
|
24
|
+
if (fs.existsSync(srcSettings)) {
|
|
25
|
+
try {
|
|
26
|
+
if (!fs.existsSync(bingoDir)) {
|
|
27
|
+
fs.mkdirSync(bingoDir, { recursive: true });
|
|
28
|
+
}
|
|
29
|
+
fs.copyFileSync(srcSettings, targetSettings);
|
|
30
|
+
console.log('[bingo] 首次启动:已部署默认配置到', targetSettings);
|
|
31
|
+
} catch (err) {
|
|
32
|
+
console.warn('[bingo] 部署默认配置失败:', err.message);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
})();
|
|
37
|
+
|
|
10
38
|
// 自动定位 bun 路径
|
|
11
39
|
const bunPath =
|
|
12
40
|
process.env.BUN_PATH ||
|
package/bin/bingocode-win.cjs
CHANGED
|
@@ -7,6 +7,32 @@ const fs = require('fs');
|
|
|
7
7
|
|
|
8
8
|
process.env.NoDefaultCurrentDirectoryInExePath = '1';
|
|
9
9
|
|
|
10
|
+
// ── 首次部署:将默认 bingo 配置复制到 ~/.claude/bingo/ ──
|
|
11
|
+
// 确保新电脑首次启动时 settings.json 存在(含占位符 ANTHROPIC_AUTH_TOKEN),
|
|
12
|
+
// 这样 isAnthropicAuthEnabled() 返回 false,跳过 OAuth 流程。
|
|
13
|
+
(function deployBingoDefaults() {
|
|
14
|
+
const configDir = process.env.CLAUDE_CONFIG_DIR || path.join(os.homedir(), '.claude');
|
|
15
|
+
const bingoDir = path.join(configDir, 'bingo');
|
|
16
|
+
const targetSettings = path.join(bingoDir, 'settings.json');
|
|
17
|
+
|
|
18
|
+
if (!fs.existsSync(targetSettings)) {
|
|
19
|
+
const defaultsDir = path.join(__dirname, '..', 'config', 'bingo-defaults');
|
|
20
|
+
const srcSettings = path.join(defaultsDir, 'settings.json');
|
|
21
|
+
|
|
22
|
+
if (fs.existsSync(srcSettings)) {
|
|
23
|
+
try {
|
|
24
|
+
if (!fs.existsSync(bingoDir)) {
|
|
25
|
+
fs.mkdirSync(bingoDir, { recursive: true });
|
|
26
|
+
}
|
|
27
|
+
fs.copyFileSync(srcSettings, targetSettings);
|
|
28
|
+
console.log('[bingocode] 首次启动:已部署默认配置到', targetSettings);
|
|
29
|
+
} catch (err) {
|
|
30
|
+
console.warn('[bingocode] 部署默认配置失败:', err.message);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
})();
|
|
35
|
+
|
|
10
36
|
// 自动定位 bun 路径
|
|
11
37
|
const bun =
|
|
12
38
|
process.env.BUN_PATH ||
|
package/bin/claude
CHANGED
|
@@ -5,6 +5,18 @@ ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
|
5
5
|
#Get your current working directory and export it as an environment variable.
|
|
6
6
|
export CALLER_DIR="${CALLER_DIR:-$(pwd -W 2>/dev/null || pwd)}"
|
|
7
7
|
|
|
8
|
+
# ── 首次部署:将默认 bingo 配置复制到 ~/.claude/bingo/ ──
|
|
9
|
+
_BINGO_CONFIG_DIR="${CLAUDE_CONFIG_DIR:-$HOME/.claude}"
|
|
10
|
+
_BINGO_DIR="$_BINGO_CONFIG_DIR/bingo"
|
|
11
|
+
_BINGO_TARGET="$_BINGO_DIR/settings.json"
|
|
12
|
+
_BINGO_SRC="$ROOT_DIR/config/bingo-defaults/settings.json"
|
|
13
|
+
|
|
14
|
+
if [ ! -f "$_BINGO_TARGET" ] && [ -f "$_BINGO_SRC" ]; then
|
|
15
|
+
mkdir -p "$_BINGO_DIR"
|
|
16
|
+
cp "$_BINGO_SRC" "$_BINGO_TARGET" 2>/dev/null && \
|
|
17
|
+
echo "[claude] 首次启动:已部署默认配置到 $_BINGO_TARGET" || true
|
|
18
|
+
fi
|
|
19
|
+
|
|
8
20
|
cd "$ROOT_DIR"
|
|
9
21
|
|
|
10
22
|
# When spawned by the desktop/web server as a child CLI process,
|
package/bin/claude-win.cjs
CHANGED
|
@@ -8,6 +8,30 @@ const fs = require('fs');
|
|
|
8
8
|
// 使 Windows bun/spawn 更可靠
|
|
9
9
|
process.env.NoDefaultCurrentDirectoryInExePath = '1';
|
|
10
10
|
|
|
11
|
+
// ── 首次部署:将默认 bingo 配置复制到 ~/.claude/bingo/ ──
|
|
12
|
+
(function deployBingoDefaults() {
|
|
13
|
+
const configDir = process.env.CLAUDE_CONFIG_DIR || path.join(os.homedir(), '.claude');
|
|
14
|
+
const bingoDir = path.join(configDir, 'bingo');
|
|
15
|
+
const targetSettings = path.join(bingoDir, 'settings.json');
|
|
16
|
+
|
|
17
|
+
if (!fs.existsSync(targetSettings)) {
|
|
18
|
+
const defaultsDir = path.join(__dirname, '..', 'config', 'bingo-defaults');
|
|
19
|
+
const srcSettings = path.join(defaultsDir, 'settings.json');
|
|
20
|
+
|
|
21
|
+
if (fs.existsSync(srcSettings)) {
|
|
22
|
+
try {
|
|
23
|
+
if (!fs.existsSync(bingoDir)) {
|
|
24
|
+
fs.mkdirSync(bingoDir, { recursive: true });
|
|
25
|
+
}
|
|
26
|
+
fs.copyFileSync(srcSettings, targetSettings);
|
|
27
|
+
console.log('[claude] 首次启动:已部署默认配置到', targetSettings);
|
|
28
|
+
} catch (err) {
|
|
29
|
+
console.warn('[claude] 部署默认配置失败:', err.message);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
})();
|
|
34
|
+
|
|
11
35
|
// 自动定位 bun 路径
|
|
12
36
|
const bun =
|
|
13
37
|
process.env.BUN_PATH ||
|
package/package.json
CHANGED
|
@@ -5,7 +5,7 @@ import { setupTerminal, shouldOfferTerminalSetup } from '../commands/terminalSet
|
|
|
5
5
|
import { useExitOnCtrlCDWithKeybindings } from '../hooks/useExitOnCtrlCDWithKeybindings.js';
|
|
6
6
|
import { Box, Link, Newline, Text, useTheme } from '../ink.js';
|
|
7
7
|
import { useKeybindings } from '../keybindings/useKeybinding.js';
|
|
8
|
-
import { isAnthropicAuthEnabled } from '../utils/auth.js';
|
|
8
|
+
import { isAnthropicAuthEnabled, isManagedOAuthContext } from '../utils/auth.js';
|
|
9
9
|
import { normalizeApiKeyForConfig } from '../utils/authPortable.js';
|
|
10
10
|
import { getCustomApiKeyStatus } from '../utils/config.js';
|
|
11
11
|
import { env } from '../utils/env.js';
|
|
@@ -114,7 +114,11 @@ export function Onboarding({
|
|
|
114
114
|
goToNextStep();
|
|
115
115
|
}
|
|
116
116
|
const steps: OnboardingStep[] = [];
|
|
117
|
-
|
|
117
|
+
// Skip preflight in managed OAuth context (claude-desktop / CCR):
|
|
118
|
+
// The desktop app already verifies local server connectivity,
|
|
119
|
+
// and the Anthropic /api/hello endpoint may return 400 before OAuth login,
|
|
120
|
+
// which would incorrectly kill the onboarding flow.
|
|
121
|
+
if (oauthEnabled && !isManagedOAuthContext()) {
|
|
118
122
|
steps.push({
|
|
119
123
|
id: 'preflight',
|
|
120
124
|
component: preflightStep
|
|
@@ -17,6 +17,9 @@ import { TopToolbar } from '../manager/TopToolbar.tsx';
|
|
|
17
17
|
|
|
18
18
|
// 主题切换(Hook)
|
|
19
19
|
import { useTheme } from '../components/design-system/ThemeProvider.js';
|
|
20
|
+
// Markdown 渲染(纯函数,不依赖 AppStateProvider context)
|
|
21
|
+
import { applyMarkdown } from '../utils/markdown.js';
|
|
22
|
+
import { Ansi } from '../ink/Ansi.js';
|
|
20
23
|
|
|
21
24
|
// 配置相关(仅使用可用接口)
|
|
22
25
|
import { getGlobalConfig, saveGlobalConfig } from '../utils/config.ts';
|
|
@@ -132,21 +135,57 @@ function loadMarkedSessionIds(): Set<string> {
|
|
|
132
135
|
}
|
|
133
136
|
}
|
|
134
137
|
|
|
135
|
-
//@C:F ID=F.CM.saveMarkedSessionIds;K=F;V=1.
|
|
138
|
+
//@C:F ID=F.CM.saveMarkedSessionIds;K=F;V=1.1;P=save marked ids;D=CLI;M=cli;S=persist;In=Set<string>;Out=void
|
|
136
139
|
function saveMarkedSessionIds(set: Set<string>) {
|
|
137
140
|
try {
|
|
141
|
+
const dir = path.dirname(MARKED_FILE);
|
|
142
|
+
if (!fs.existsSync(dir)) {
|
|
143
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
144
|
+
}
|
|
138
145
|
fs.writeFileSync(MARKED_FILE, JSON.stringify([...set]), 'utf-8');
|
|
139
|
-
} catch {
|
|
146
|
+
} catch (err) {
|
|
147
|
+
console.error('[saveMarkedSessionIds] 写入失败:', err);
|
|
148
|
+
}
|
|
140
149
|
}
|
|
141
150
|
|
|
142
|
-
//
|
|
143
|
-
type
|
|
151
|
+
// 消息条目(与后端 MessageEntry 对齐)
|
|
152
|
+
type MessageEntry = {
|
|
144
153
|
id: string;
|
|
145
|
-
|
|
146
|
-
content: string
|
|
147
|
-
|
|
154
|
+
type: 'user' | 'assistant' | 'system' | 'tool_use' | 'tool_result';
|
|
155
|
+
content: unknown; // string 或 ContentBlock[]
|
|
156
|
+
timestamp: string;
|
|
157
|
+
model?: string;
|
|
158
|
+
parentUuid?: string;
|
|
159
|
+
parentToolUseId?: string;
|
|
160
|
+
isSidechain?: boolean;
|
|
148
161
|
};
|
|
149
162
|
|
|
163
|
+
/** 从 MessageEntry.content 提取纯文本 */
|
|
164
|
+
function extractTextFromContent(content: unknown): string {
|
|
165
|
+
if (typeof content === 'string') return content;
|
|
166
|
+
if (Array.isArray(content)) {
|
|
167
|
+
return content
|
|
168
|
+
.map((block: any) => {
|
|
169
|
+
if (block.type === 'text' && typeof block.text === 'string') return block.text;
|
|
170
|
+
if (block.type === 'tool_use') return `[Tool: ${block.name || 'unknown'}]`;
|
|
171
|
+
if (block.type === 'tool_result') {
|
|
172
|
+
if (typeof block.content === 'string') return block.content;
|
|
173
|
+
if (Array.isArray(block.content)) {
|
|
174
|
+
return block.content
|
|
175
|
+
.filter((b: any) => b.type === 'text')
|
|
176
|
+
.map((b: any) => b.text)
|
|
177
|
+
.join('\n');
|
|
178
|
+
}
|
|
179
|
+
return '[Tool Result]';
|
|
180
|
+
}
|
|
181
|
+
return '';
|
|
182
|
+
})
|
|
183
|
+
.filter(Boolean)
|
|
184
|
+
.join('\n');
|
|
185
|
+
}
|
|
186
|
+
return String(content ?? '');
|
|
187
|
+
}
|
|
188
|
+
|
|
150
189
|
//@C:F ID=F.CM.CliMenuManager;K=F;V=1.5;P=CLI 主菜单;D=CLI;M=cli;S=main;In=;Out=JSX.Element
|
|
151
190
|
export const CliMenuManager: React.FC = () => {
|
|
152
191
|
const { stdout } = useStdout();
|
|
@@ -208,8 +247,8 @@ export const CliMenuManager: React.FC = () => {
|
|
|
208
247
|
const [historyMenuStage, setHistoryMenuStage] = useState<'list'|'window'|'deleteConfirm'>('list');
|
|
209
248
|
const [selectedHistory, setSelectedHistory] = useState<any|null>(null);
|
|
210
249
|
|
|
211
|
-
//
|
|
212
|
-
const [sessionMessages, setSessionMessages] = useState<
|
|
250
|
+
// 历史-消息内容
|
|
251
|
+
const [sessionMessages, setSessionMessages] = useState<MessageEntry[]>([]);
|
|
213
252
|
const [loadingMsgs, setLoadingMsgs] = useState(false);
|
|
214
253
|
const [msgsErr, setMsgsErr] = useState<string | null>(null);
|
|
215
254
|
const [msgsPage, setMsgsPage] = useState(0); // 0=最新页(底部),1=向上翻一页
|
|
@@ -380,9 +419,26 @@ export const CliMenuManager: React.FC = () => {
|
|
|
380
419
|
|
|
381
420
|
// 会话消息获取
|
|
382
421
|
useEffect(() => {
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
422
|
+
if (page === 'history' && historyMenuStage === 'window' && selectedHistory && apiUrl) {
|
|
423
|
+
let cancelled = false;
|
|
424
|
+
setLoadingMsgs(true);
|
|
425
|
+
setMsgsErr(null);
|
|
426
|
+
setSessionMessages([]);
|
|
427
|
+
setMsgsPage(0);
|
|
428
|
+
(async () => {
|
|
429
|
+
try {
|
|
430
|
+
const resp = await axios.get(`${apiUrl}/api/sessions/${selectedHistory.id}/messages`);
|
|
431
|
+
if (!cancelled) {
|
|
432
|
+
const msgs: MessageEntry[] = resp.data?.messages ?? [];
|
|
433
|
+
setSessionMessages(msgs);
|
|
434
|
+
}
|
|
435
|
+
} catch (e: any) {
|
|
436
|
+
if (!cancelled) setMsgsErr(e.message || '消息加载失败');
|
|
437
|
+
} finally {
|
|
438
|
+
if (!cancelled) setLoadingMsgs(false);
|
|
439
|
+
}
|
|
440
|
+
})();
|
|
441
|
+
return () => { cancelled = true; };
|
|
386
442
|
} else {
|
|
387
443
|
setSessionMessages([]);
|
|
388
444
|
setLoadingMsgs(false);
|
|
@@ -531,6 +587,15 @@ export const CliMenuManager: React.FC = () => {
|
|
|
531
587
|
handleHistoryMenuAction('__back');
|
|
532
588
|
return;
|
|
533
589
|
}
|
|
590
|
+
// 消息滚动:↑/k 向上翻页,↓/j 向下翻页
|
|
591
|
+
if (key.upArrow || input === 'k') {
|
|
592
|
+
setMsgsPage(p => Math.max(0, p - 1));
|
|
593
|
+
return;
|
|
594
|
+
}
|
|
595
|
+
if (key.downArrow || input === 'j') {
|
|
596
|
+
setMsgsPage(p => p + 1);
|
|
597
|
+
return;
|
|
598
|
+
}
|
|
534
599
|
|
|
535
600
|
} else if (historyMenuStage === 'deleteConfirm') {
|
|
536
601
|
if (input === 'q') {
|
|
@@ -860,54 +925,62 @@ export const CliMenuManager: React.FC = () => {
|
|
|
860
925
|
}
|
|
861
926
|
|
|
862
927
|
if (historyMenuStage === 'window' && selectedHistory) {
|
|
863
|
-
const halfH = Math.floor(MID_H / 2);
|
|
864
928
|
const isMarked = markedSessionIds.has(selectedHistory.id);
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
} else if (item.value === '__delete') {
|
|
881
|
-
setHistoryMenuStage('deleteConfirm');
|
|
882
|
-
} else if (item.value === '__toggle_mark' && selectedHistory) {
|
|
883
|
-
toggleMarkSession(selectedHistory.id);
|
|
884
|
-
}
|
|
885
|
-
}
|
|
886
|
-
};
|
|
929
|
+
|
|
930
|
+
// ── 信息栏高度固定 3 行(标题 + 元信息 + 提示) ──
|
|
931
|
+
const INFO_H = 3;
|
|
932
|
+
const MSGS_H = Math.max(3, MID_H - INFO_H);
|
|
933
|
+
|
|
934
|
+
// ── 过滤出可展示的消息(user / assistant / system,跳过 tool_use / tool_result) ──
|
|
935
|
+
const displayMsgs = sessionMessages.filter(
|
|
936
|
+
m => m.type === 'user' || m.type === 'assistant' || m.type === 'system'
|
|
937
|
+
);
|
|
938
|
+
|
|
939
|
+
// ── 分页:每页 MSGS_PAGE_SIZE 条消息 ──
|
|
940
|
+
const totalPages = Math.max(1, Math.ceil(displayMsgs.length / MSGS_PAGE_SIZE));
|
|
941
|
+
const safePage = Math.min(msgsPage, totalPages - 1);
|
|
942
|
+
const pageStart = safePage * MSGS_PAGE_SIZE;
|
|
943
|
+
const pageMsgs = displayMsgs.slice(pageStart, pageStart + MSGS_PAGE_SIZE);
|
|
887
944
|
|
|
888
945
|
return (
|
|
889
946
|
<Box width={VIEW_W} height={MID_H} flexDirection="column">
|
|
890
|
-
{/*
|
|
891
|
-
<Box height={
|
|
947
|
+
{/* ── 顶部信息栏 ── */}
|
|
948
|
+
<Box height={INFO_H} flexDirection="column">
|
|
892
949
|
<Text color={isMarked ? 'yellow' : 'cyan'}>
|
|
893
|
-
{isMarked ? '★ ' : ''}
|
|
950
|
+
{isMarked ? '★ ' : ''}{selectedHistory.title || 'Untitled'}
|
|
951
|
+
<Text dimColor> {selectedHistory.createdAt?.slice(0, 16).replace('T', ' ') || ''} · {displayMsgs.length} msgs</Text>
|
|
894
952
|
</Text>
|
|
895
|
-
<
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
<Text>已标记: {isMarked ? '是' : '否'}</Text>
|
|
900
|
-
<Hint>m 标记/取消 · c 继续 · d 删除 · q 返回</Hint>
|
|
953
|
+
<Hint>
|
|
954
|
+
j/↓ 下翻 · k/↑ 上翻 · m 标记 · c 继续 · d 删除 · q 返回
|
|
955
|
+
{displayMsgs.length > MSGS_PAGE_SIZE ? ` [${safePage + 1}/${totalPages}]` : ''}
|
|
956
|
+
</Hint>
|
|
901
957
|
</Box>
|
|
902
958
|
|
|
903
|
-
{/*
|
|
904
|
-
<Box height={
|
|
905
|
-
<Text>
|
|
906
|
-
<
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
959
|
+
{/* ── 消息区 ── */}
|
|
960
|
+
<Box height={MSGS_H} flexDirection="column" overflow="hidden">
|
|
961
|
+
{loadingMsgs && <Text color="yellow">加载消息中...</Text>}
|
|
962
|
+
{msgsErr && <Text color="red">错误: {msgsErr}</Text>}
|
|
963
|
+
{!loadingMsgs && !msgsErr && displayMsgs.length === 0 && (
|
|
964
|
+
<Text dimColor>无消息记录</Text>
|
|
965
|
+
)}
|
|
966
|
+
{pageMsgs.map((msg) => {
|
|
967
|
+
const text = extractTextFromContent(msg.content);
|
|
968
|
+
if (!text.trim()) return null;
|
|
969
|
+
const isUser = msg.type === 'user';
|
|
970
|
+
const isSystem = msg.type === 'system';
|
|
971
|
+
const roleLabel = isUser ? '👤 You' : isSystem ? '⚙ System' : '🤖 Assistant';
|
|
972
|
+
const roleColor = isUser ? 'green' : isSystem ? 'gray' : 'cyan';
|
|
973
|
+
return (
|
|
974
|
+
<Box key={msg.id} flexDirection="column" marginBottom={1}>
|
|
975
|
+
<Text color={roleColor} bold>{roleLabel}</Text>
|
|
976
|
+
{isUser ? (
|
|
977
|
+
<Text>{text}</Text>
|
|
978
|
+
) : (
|
|
979
|
+
<Ansi>{applyMarkdown(text, theme)}</Ansi>
|
|
980
|
+
)}
|
|
981
|
+
</Box>
|
|
982
|
+
);
|
|
983
|
+
})}
|
|
911
984
|
</Box>
|
|
912
985
|
</Box>
|
|
913
986
|
);
|
package/src/utils/auth.ts
CHANGED
|
@@ -88,7 +88,7 @@ const DEFAULT_API_KEY_HELPER_TTL = 5 * 60 * 1000
|
|
|
88
88
|
* who runs `claude` in their terminal with an API key sees every CCD session
|
|
89
89
|
* also use that key — and fail if it's stale/wrong-org.
|
|
90
90
|
*/
|
|
91
|
-
function isManagedOAuthContext(): boolean {
|
|
91
|
+
export function isManagedOAuthContext(): boolean {
|
|
92
92
|
return (
|
|
93
93
|
isEnvTruthy(process.env.CLAUDE_CODE_REMOTE) ||
|
|
94
94
|
process.env.CLAUDE_CODE_ENTRYPOINT === 'claude-desktop'
|