mirror of
https://github.com/claude-code-best/claude-code.git
synced 2026-06-17 22:05:50 +00:00
更新大量 tsx 原始文件; 已经迁移 login panel; 部分 (#121)
* style(B1-1): 格式化 ink/buddy/cli/context/screens/tasks/services/keybindings/state (43 files) 纯格式化:移除分号、React Compiler import、import 多行展开。 修复了 Box.tsx 和 ScrollBox.tsx 中无效的 global.d.ts import。 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * style(B1-2): 格式化 commands (79 files) 纯格式化:移除分号、React Compiler import、import 多行展开。 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * style(B1-3): 格式化 components/messages,permissions,mcp,sandbox,shell (104 files) 纯格式化:移除分号、React Compiler import、import 多行展开。 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * style(B1-4): 格式化 components/PromptInput,FeedbackSurvey,tasks,agents,skills,design-system,wizard (73 files) 纯格式化:移除分号、React Compiler import、import 多行展开。 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * style(B1-5): 格式化 components其余 + hooks + tools (232 files) 纯格式化:移除分号、React Compiler import、import 多行展开。 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * style(B1-6): 格式化 main/entrypoints/utils/moreright (21 files) 纯格式化:移除分号、React Compiler import、import 多行展开。 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * docs: 更新 README,新增 Run.ps1/TODO.md,删除 V6.md - README.md: 大幅重写,更详细版本历史和配置示例 - Run.ps1: 新增 Windows 启动脚本 - TODO.md: 新增包完成清单 - V6.md: 删除(架构重构规划已不适用) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: 修复以前的问题 * fix: 修复 login 面板的问题 --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,230 +1,152 @@
|
||||
import { c as _c } from "react/compiler-runtime";
|
||||
import React, { useCallback, useState } from 'react';
|
||||
import TextInput from '../../components/TextInput.js';
|
||||
import { useTerminalSize } from '../../hooks/useTerminalSize.js';
|
||||
import { Box, color, Text, useTheme } from '../../ink.js';
|
||||
import { useKeybindings } from '../../keybindings/useKeybinding.js';
|
||||
import React, { useCallback, useState } from 'react'
|
||||
import TextInput from '../../components/TextInput.js'
|
||||
import { useTerminalSize } from '../../hooks/useTerminalSize.js'
|
||||
import { Box, color, Text, useTheme } from '../../ink.js'
|
||||
import { useKeybindings } from '../../keybindings/useKeybinding.js'
|
||||
|
||||
interface ApiKeyStepProps {
|
||||
existingApiKey: string | null;
|
||||
useExistingKey: boolean;
|
||||
apiKeyOrOAuthToken: string;
|
||||
onApiKeyChange: (value: string) => void;
|
||||
onToggleUseExistingKey: (useExisting: boolean) => void;
|
||||
onSubmit: () => void;
|
||||
onCreateOAuthToken?: () => void;
|
||||
selectedOption?: 'existing' | 'new' | 'oauth';
|
||||
onSelectOption?: (option: 'existing' | 'new' | 'oauth') => void;
|
||||
existingApiKey: string | null
|
||||
useExistingKey: boolean
|
||||
apiKeyOrOAuthToken: string
|
||||
onApiKeyChange: (value: string) => void
|
||||
onToggleUseExistingKey: (useExisting: boolean) => void
|
||||
onSubmit: () => void
|
||||
onCreateOAuthToken?: () => void
|
||||
selectedOption?: 'existing' | 'new' | 'oauth'
|
||||
onSelectOption?: (option: 'existing' | 'new' | 'oauth') => void
|
||||
}
|
||||
export function ApiKeyStep(t0) {
|
||||
const $ = _c(55);
|
||||
const {
|
||||
existingApiKey,
|
||||
apiKeyOrOAuthToken,
|
||||
onApiKeyChange,
|
||||
onSubmit,
|
||||
onToggleUseExistingKey,
|
||||
|
||||
export function ApiKeyStep({
|
||||
existingApiKey,
|
||||
apiKeyOrOAuthToken,
|
||||
onApiKeyChange,
|
||||
onSubmit,
|
||||
onToggleUseExistingKey,
|
||||
onCreateOAuthToken,
|
||||
selectedOption = existingApiKey
|
||||
? 'existing'
|
||||
: onCreateOAuthToken
|
||||
? 'oauth'
|
||||
: 'new',
|
||||
onSelectOption,
|
||||
}: ApiKeyStepProps) {
|
||||
const [cursorOffset, setCursorOffset] = useState(0)
|
||||
const terminalSize = useTerminalSize()
|
||||
const [theme] = useTheme()
|
||||
|
||||
const handlePrevious = useCallback(() => {
|
||||
if (selectedOption === 'new' && onCreateOAuthToken) {
|
||||
// From 'new' go up to 'oauth'
|
||||
onSelectOption?.('oauth')
|
||||
} else if (selectedOption === 'oauth' && existingApiKey) {
|
||||
// From 'oauth' go up to 'existing' (only if it exists)
|
||||
onSelectOption?.('existing')
|
||||
onToggleUseExistingKey(true)
|
||||
}
|
||||
}, [
|
||||
selectedOption,
|
||||
onCreateOAuthToken,
|
||||
selectedOption: t1,
|
||||
onSelectOption
|
||||
} = t0;
|
||||
const selectedOption = t1 === undefined ? existingApiKey ? "existing" : onCreateOAuthToken ? "oauth" : "new" : t1;
|
||||
const [cursorOffset, setCursorOffset] = useState(0);
|
||||
const terminalSize = useTerminalSize();
|
||||
const [theme] = useTheme();
|
||||
let t2;
|
||||
if ($[0] !== existingApiKey || $[1] !== onCreateOAuthToken || $[2] !== onSelectOption || $[3] !== onToggleUseExistingKey || $[4] !== selectedOption) {
|
||||
t2 = () => {
|
||||
if (selectedOption === "new" && onCreateOAuthToken) {
|
||||
onSelectOption?.("oauth");
|
||||
} else {
|
||||
if (selectedOption === "oauth" && existingApiKey) {
|
||||
onSelectOption?.("existing");
|
||||
onToggleUseExistingKey(true);
|
||||
}
|
||||
}
|
||||
};
|
||||
$[0] = existingApiKey;
|
||||
$[1] = onCreateOAuthToken;
|
||||
$[2] = onSelectOption;
|
||||
$[3] = onToggleUseExistingKey;
|
||||
$[4] = selectedOption;
|
||||
$[5] = t2;
|
||||
} else {
|
||||
t2 = $[5];
|
||||
}
|
||||
const handlePrevious = t2;
|
||||
let t3;
|
||||
if ($[6] !== onCreateOAuthToken || $[7] !== onSelectOption || $[8] !== onToggleUseExistingKey || $[9] !== selectedOption) {
|
||||
t3 = () => {
|
||||
if (selectedOption === "existing") {
|
||||
onSelectOption?.(onCreateOAuthToken ? "oauth" : "new");
|
||||
onToggleUseExistingKey(false);
|
||||
} else {
|
||||
if (selectedOption === "oauth") {
|
||||
onSelectOption?.("new");
|
||||
}
|
||||
}
|
||||
};
|
||||
$[6] = onCreateOAuthToken;
|
||||
$[7] = onSelectOption;
|
||||
$[8] = onToggleUseExistingKey;
|
||||
$[9] = selectedOption;
|
||||
$[10] = t3;
|
||||
} else {
|
||||
t3 = $[10];
|
||||
}
|
||||
const handleNext = t3;
|
||||
let t4;
|
||||
if ($[11] !== onCreateOAuthToken || $[12] !== onSubmit || $[13] !== selectedOption) {
|
||||
t4 = () => {
|
||||
if (selectedOption === "oauth" && onCreateOAuthToken) {
|
||||
onCreateOAuthToken();
|
||||
} else {
|
||||
onSubmit();
|
||||
}
|
||||
};
|
||||
$[11] = onCreateOAuthToken;
|
||||
$[12] = onSubmit;
|
||||
$[13] = selectedOption;
|
||||
$[14] = t4;
|
||||
} else {
|
||||
t4 = $[14];
|
||||
}
|
||||
const handleConfirm = t4;
|
||||
const isTextInputVisible = selectedOption === "new";
|
||||
let t5;
|
||||
if ($[15] !== handleConfirm || $[16] !== handleNext || $[17] !== handlePrevious) {
|
||||
t5 = {
|
||||
"confirm:previous": handlePrevious,
|
||||
"confirm:next": handleNext,
|
||||
"confirm:yes": handleConfirm
|
||||
};
|
||||
$[15] = handleConfirm;
|
||||
$[16] = handleNext;
|
||||
$[17] = handlePrevious;
|
||||
$[18] = t5;
|
||||
} else {
|
||||
t5 = $[18];
|
||||
}
|
||||
const t6 = !isTextInputVisible;
|
||||
let t7;
|
||||
if ($[19] !== t6) {
|
||||
t7 = {
|
||||
context: "Confirmation",
|
||||
isActive: t6
|
||||
};
|
||||
$[19] = t6;
|
||||
$[20] = t7;
|
||||
} else {
|
||||
t7 = $[20];
|
||||
}
|
||||
useKeybindings(t5, t7);
|
||||
let t8;
|
||||
if ($[21] !== handleNext || $[22] !== handlePrevious) {
|
||||
t8 = {
|
||||
"confirm:previous": handlePrevious,
|
||||
"confirm:next": handleNext
|
||||
};
|
||||
$[21] = handleNext;
|
||||
$[22] = handlePrevious;
|
||||
$[23] = t8;
|
||||
} else {
|
||||
t8 = $[23];
|
||||
}
|
||||
let t9;
|
||||
if ($[24] !== isTextInputVisible) {
|
||||
t9 = {
|
||||
context: "Confirmation",
|
||||
isActive: isTextInputVisible
|
||||
};
|
||||
$[24] = isTextInputVisible;
|
||||
$[25] = t9;
|
||||
} else {
|
||||
t9 = $[25];
|
||||
}
|
||||
useKeybindings(t8, t9);
|
||||
let t10;
|
||||
if ($[26] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t10 = <Box flexDirection="column" marginBottom={1}><Text bold={true}>Install GitHub App</Text><Text dimColor={true}>Choose API key</Text></Box>;
|
||||
$[26] = t10;
|
||||
} else {
|
||||
t10 = $[26];
|
||||
}
|
||||
let t11;
|
||||
if ($[27] !== existingApiKey || $[28] !== selectedOption || $[29] !== theme) {
|
||||
t11 = existingApiKey && <Box marginBottom={1}><Text>{selectedOption === "existing" ? color("success", theme)("> ") : " "}Use your existing Claude Code API key</Text></Box>;
|
||||
$[27] = existingApiKey;
|
||||
$[28] = selectedOption;
|
||||
$[29] = theme;
|
||||
$[30] = t11;
|
||||
} else {
|
||||
t11 = $[30];
|
||||
}
|
||||
let t12;
|
||||
if ($[31] !== onCreateOAuthToken || $[32] !== selectedOption || $[33] !== theme) {
|
||||
t12 = onCreateOAuthToken && <Box marginBottom={1}><Text>{selectedOption === "oauth" ? color("success", theme)("> ") : " "}Create a long-lived token with your Claude subscription</Text></Box>;
|
||||
$[31] = onCreateOAuthToken;
|
||||
$[32] = selectedOption;
|
||||
$[33] = theme;
|
||||
$[34] = t12;
|
||||
} else {
|
||||
t12 = $[34];
|
||||
}
|
||||
let t13;
|
||||
if ($[35] !== selectedOption || $[36] !== theme) {
|
||||
t13 = selectedOption === "new" ? color("success", theme)("> ") : " ";
|
||||
$[35] = selectedOption;
|
||||
$[36] = theme;
|
||||
$[37] = t13;
|
||||
} else {
|
||||
t13 = $[37];
|
||||
}
|
||||
let t14;
|
||||
if ($[38] !== t13) {
|
||||
t14 = <Box marginBottom={1}><Text>{t13}Enter a new API key</Text></Box>;
|
||||
$[38] = t13;
|
||||
$[39] = t14;
|
||||
} else {
|
||||
t14 = $[39];
|
||||
}
|
||||
let t15;
|
||||
if ($[40] !== apiKeyOrOAuthToken || $[41] !== cursorOffset || $[42] !== onApiKeyChange || $[43] !== onSubmit || $[44] !== selectedOption || $[45] !== terminalSize) {
|
||||
t15 = selectedOption === "new" && <TextInput value={apiKeyOrOAuthToken} onChange={onApiKeyChange} onSubmit={onSubmit} onPaste={onApiKeyChange} focus={true} placeholder={"sk-ant\u2026 (Create a new key at https://platform.claude.com/settings/keys)"} mask="*" columns={terminalSize.columns} cursorOffset={cursorOffset} onChangeCursorOffset={setCursorOffset} showCursor={true} />;
|
||||
$[40] = apiKeyOrOAuthToken;
|
||||
$[41] = cursorOffset;
|
||||
$[42] = onApiKeyChange;
|
||||
$[43] = onSubmit;
|
||||
$[44] = selectedOption;
|
||||
$[45] = terminalSize;
|
||||
$[46] = t15;
|
||||
} else {
|
||||
t15 = $[46];
|
||||
}
|
||||
let t16;
|
||||
if ($[47] !== t11 || $[48] !== t12 || $[49] !== t14 || $[50] !== t15) {
|
||||
t16 = <Box flexDirection="column" borderStyle="round" paddingX={1}>{t10}{t11}{t12}{t14}{t15}</Box>;
|
||||
$[47] = t11;
|
||||
$[48] = t12;
|
||||
$[49] = t14;
|
||||
$[50] = t15;
|
||||
$[51] = t16;
|
||||
} else {
|
||||
t16 = $[51];
|
||||
}
|
||||
let t17;
|
||||
if ($[52] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t17 = <Box marginLeft={3}><Text dimColor={true}>↑/↓ to select · Enter to continue</Text></Box>;
|
||||
$[52] = t17;
|
||||
} else {
|
||||
t17 = $[52];
|
||||
}
|
||||
let t18;
|
||||
if ($[53] !== t16) {
|
||||
t18 = <>{t16}{t17}</>;
|
||||
$[53] = t16;
|
||||
$[54] = t18;
|
||||
} else {
|
||||
t18 = $[54];
|
||||
}
|
||||
return t18;
|
||||
existingApiKey,
|
||||
onSelectOption,
|
||||
onToggleUseExistingKey,
|
||||
])
|
||||
|
||||
const handleNext = useCallback(() => {
|
||||
if (selectedOption === 'existing') {
|
||||
// From 'existing' go down to 'oauth' (if available) or 'new'
|
||||
onSelectOption?.(onCreateOAuthToken ? 'oauth' : 'new')
|
||||
onToggleUseExistingKey(false)
|
||||
} else if (selectedOption === 'oauth') {
|
||||
// From 'oauth' go down to 'new'
|
||||
onSelectOption?.('new')
|
||||
}
|
||||
}, [
|
||||
selectedOption,
|
||||
onCreateOAuthToken,
|
||||
onSelectOption,
|
||||
onToggleUseExistingKey,
|
||||
])
|
||||
|
||||
const handleConfirm = useCallback(() => {
|
||||
if (selectedOption === 'oauth' && onCreateOAuthToken) {
|
||||
onCreateOAuthToken()
|
||||
} else {
|
||||
onSubmit()
|
||||
}
|
||||
}, [selectedOption, onCreateOAuthToken, onSubmit])
|
||||
|
||||
// When the text input is visible, omit confirm:yes so bare 'y' passes
|
||||
// through to the input instead of submitting. TextInput's onSubmit handles
|
||||
// Enter. Keep the Confirmation context (not Settings) to avoid j/k bindings.
|
||||
const isTextInputVisible = selectedOption === 'new'
|
||||
useKeybindings(
|
||||
{
|
||||
'confirm:previous': handlePrevious,
|
||||
'confirm:next': handleNext,
|
||||
'confirm:yes': handleConfirm,
|
||||
},
|
||||
{ context: 'Confirmation', isActive: !isTextInputVisible },
|
||||
)
|
||||
useKeybindings(
|
||||
{
|
||||
'confirm:previous': handlePrevious,
|
||||
'confirm:next': handleNext,
|
||||
},
|
||||
{ context: 'Confirmation', isActive: isTextInputVisible },
|
||||
)
|
||||
|
||||
return (
|
||||
<>
|
||||
<Box flexDirection="column" borderStyle="round" paddingX={1}>
|
||||
<Box flexDirection="column" marginBottom={1}>
|
||||
<Text bold>Install GitHub App</Text>
|
||||
<Text dimColor>Choose API key</Text>
|
||||
</Box>
|
||||
{existingApiKey && (
|
||||
<Box marginBottom={1}>
|
||||
<Text>
|
||||
{selectedOption === 'existing'
|
||||
? color('success', theme)('> ')
|
||||
: ' '}
|
||||
Use your existing Claude Code API key
|
||||
</Text>
|
||||
</Box>
|
||||
)}
|
||||
{onCreateOAuthToken && (
|
||||
<Box marginBottom={1}>
|
||||
<Text>
|
||||
{selectedOption === 'oauth'
|
||||
? color('success', theme)('> ')
|
||||
: ' '}
|
||||
Create a long-lived token with your Claude subscription
|
||||
</Text>
|
||||
</Box>
|
||||
)}
|
||||
<Box marginBottom={1}>
|
||||
<Text>
|
||||
{selectedOption === 'new' ? color('success', theme)('> ') : ' '}
|
||||
Enter a new API key
|
||||
</Text>
|
||||
</Box>
|
||||
{selectedOption === 'new' && (
|
||||
<TextInput
|
||||
value={apiKeyOrOAuthToken}
|
||||
onChange={onApiKeyChange}
|
||||
onSubmit={onSubmit}
|
||||
onPaste={onApiKeyChange}
|
||||
focus={true}
|
||||
placeholder="sk-ant… (Create a new key at https://platform.claude.com/settings/keys)"
|
||||
mask="*"
|
||||
columns={terminalSize.columns}
|
||||
cursorOffset={cursorOffset}
|
||||
onChangeCursorOffset={setCursorOffset}
|
||||
showCursor={true}
|
||||
/>
|
||||
)}
|
||||
</Box>
|
||||
<Box marginLeft={3}>
|
||||
<Text dimColor>↑/↓ to select · Enter to continue</Text>
|
||||
</Box>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,189 +1,106 @@
|
||||
import { c as _c } from "react/compiler-runtime";
|
||||
import React, { useCallback, useState } from 'react';
|
||||
import TextInput from '../../components/TextInput.js';
|
||||
import { useTerminalSize } from '../../hooks/useTerminalSize.js';
|
||||
import { Box, color, Text, useTheme } from '../../ink.js';
|
||||
import { useKeybindings } from '../../keybindings/useKeybinding.js';
|
||||
import React, { useCallback, useState } from 'react'
|
||||
import TextInput from '../../components/TextInput.js'
|
||||
import { useTerminalSize } from '../../hooks/useTerminalSize.js'
|
||||
import { Box, color, Text, useTheme } from '../../ink.js'
|
||||
import { useKeybindings } from '../../keybindings/useKeybinding.js'
|
||||
|
||||
interface CheckExistingSecretStepProps {
|
||||
useExistingSecret: boolean;
|
||||
secretName: string;
|
||||
onToggleUseExistingSecret: (useExisting: boolean) => void;
|
||||
onSecretNameChange: (value: string) => void;
|
||||
onSubmit: () => void;
|
||||
useExistingSecret: boolean
|
||||
secretName: string
|
||||
onToggleUseExistingSecret: (useExisting: boolean) => void
|
||||
onSecretNameChange: (value: string) => void
|
||||
onSubmit: () => void
|
||||
}
|
||||
export function CheckExistingSecretStep(t0) {
|
||||
const $ = _c(42);
|
||||
const {
|
||||
useExistingSecret,
|
||||
secretName,
|
||||
onToggleUseExistingSecret,
|
||||
onSecretNameChange,
|
||||
onSubmit
|
||||
} = t0;
|
||||
const [cursorOffset, setCursorOffset] = useState(0);
|
||||
const terminalSize = useTerminalSize();
|
||||
const [theme] = useTheme();
|
||||
let t1;
|
||||
if ($[0] !== onToggleUseExistingSecret) {
|
||||
t1 = () => onToggleUseExistingSecret(true);
|
||||
$[0] = onToggleUseExistingSecret;
|
||||
$[1] = t1;
|
||||
} else {
|
||||
t1 = $[1];
|
||||
}
|
||||
const handlePrevious = t1;
|
||||
let t2;
|
||||
if ($[2] !== onToggleUseExistingSecret) {
|
||||
t2 = () => onToggleUseExistingSecret(false);
|
||||
$[2] = onToggleUseExistingSecret;
|
||||
$[3] = t2;
|
||||
} else {
|
||||
t2 = $[3];
|
||||
}
|
||||
const handleNext = t2;
|
||||
let t3;
|
||||
if ($[4] !== handleNext || $[5] !== handlePrevious || $[6] !== onSubmit) {
|
||||
t3 = {
|
||||
"confirm:previous": handlePrevious,
|
||||
"confirm:next": handleNext,
|
||||
"confirm:yes": onSubmit
|
||||
};
|
||||
$[4] = handleNext;
|
||||
$[5] = handlePrevious;
|
||||
$[6] = onSubmit;
|
||||
$[7] = t3;
|
||||
} else {
|
||||
t3 = $[7];
|
||||
}
|
||||
let t4;
|
||||
if ($[8] !== useExistingSecret) {
|
||||
t4 = {
|
||||
context: "Confirmation",
|
||||
isActive: useExistingSecret
|
||||
};
|
||||
$[8] = useExistingSecret;
|
||||
$[9] = t4;
|
||||
} else {
|
||||
t4 = $[9];
|
||||
}
|
||||
useKeybindings(t3, t4);
|
||||
let t5;
|
||||
if ($[10] !== handleNext || $[11] !== handlePrevious) {
|
||||
t5 = {
|
||||
"confirm:previous": handlePrevious,
|
||||
"confirm:next": handleNext
|
||||
};
|
||||
$[10] = handleNext;
|
||||
$[11] = handlePrevious;
|
||||
$[12] = t5;
|
||||
} else {
|
||||
t5 = $[12];
|
||||
}
|
||||
const t6 = !useExistingSecret;
|
||||
let t7;
|
||||
if ($[13] !== t6) {
|
||||
t7 = {
|
||||
context: "Confirmation",
|
||||
isActive: t6
|
||||
};
|
||||
$[13] = t6;
|
||||
$[14] = t7;
|
||||
} else {
|
||||
t7 = $[14];
|
||||
}
|
||||
useKeybindings(t5, t7);
|
||||
let t8;
|
||||
if ($[15] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t8 = <Box flexDirection="column" marginBottom={1}><Text bold={true}>Install GitHub App</Text><Text dimColor={true}>Setup API key secret</Text></Box>;
|
||||
$[15] = t8;
|
||||
} else {
|
||||
t8 = $[15];
|
||||
}
|
||||
let t9;
|
||||
if ($[16] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t9 = <Box marginBottom={1}><Text color="warning">ANTHROPIC_API_KEY already exists in repository secrets!</Text></Box>;
|
||||
$[16] = t9;
|
||||
} else {
|
||||
t9 = $[16];
|
||||
}
|
||||
let t10;
|
||||
if ($[17] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t10 = <Box marginBottom={1}><Text>Would you like to:</Text></Box>;
|
||||
$[17] = t10;
|
||||
} else {
|
||||
t10 = $[17];
|
||||
}
|
||||
let t11;
|
||||
if ($[18] !== theme || $[19] !== useExistingSecret) {
|
||||
t11 = useExistingSecret ? color("success", theme)("> ") : " ";
|
||||
$[18] = theme;
|
||||
$[19] = useExistingSecret;
|
||||
$[20] = t11;
|
||||
} else {
|
||||
t11 = $[20];
|
||||
}
|
||||
let t12;
|
||||
if ($[21] !== t11) {
|
||||
t12 = <Box marginBottom={1}><Text>{t11}Use the existing API key</Text></Box>;
|
||||
$[21] = t11;
|
||||
$[22] = t12;
|
||||
} else {
|
||||
t12 = $[22];
|
||||
}
|
||||
let t13;
|
||||
if ($[23] !== theme || $[24] !== useExistingSecret) {
|
||||
t13 = !useExistingSecret ? color("success", theme)("> ") : " ";
|
||||
$[23] = theme;
|
||||
$[24] = useExistingSecret;
|
||||
$[25] = t13;
|
||||
} else {
|
||||
t13 = $[25];
|
||||
}
|
||||
let t14;
|
||||
if ($[26] !== t13) {
|
||||
t14 = <Box marginBottom={1}><Text>{t13}Create a new secret with a different name</Text></Box>;
|
||||
$[26] = t13;
|
||||
$[27] = t14;
|
||||
} else {
|
||||
t14 = $[27];
|
||||
}
|
||||
let t15;
|
||||
if ($[28] !== cursorOffset || $[29] !== onSecretNameChange || $[30] !== onSubmit || $[31] !== secretName || $[32] !== terminalSize || $[33] !== useExistingSecret) {
|
||||
t15 = !useExistingSecret && <><Box marginBottom={1}><Text>Enter new secret name (alphanumeric with underscores):</Text></Box><TextInput value={secretName} onChange={onSecretNameChange} onSubmit={onSubmit} focus={true} placeholder="e.g., CLAUDE_API_KEY" columns={terminalSize.columns} cursorOffset={cursorOffset} onChangeCursorOffset={setCursorOffset} showCursor={true} /></>;
|
||||
$[28] = cursorOffset;
|
||||
$[29] = onSecretNameChange;
|
||||
$[30] = onSubmit;
|
||||
$[31] = secretName;
|
||||
$[32] = terminalSize;
|
||||
$[33] = useExistingSecret;
|
||||
$[34] = t15;
|
||||
} else {
|
||||
t15 = $[34];
|
||||
}
|
||||
let t16;
|
||||
if ($[35] !== t12 || $[36] !== t14 || $[37] !== t15) {
|
||||
t16 = <Box flexDirection="column" borderStyle="round" paddingX={1}>{t8}{t9}{t10}{t12}{t14}{t15}</Box>;
|
||||
$[35] = t12;
|
||||
$[36] = t14;
|
||||
$[37] = t15;
|
||||
$[38] = t16;
|
||||
} else {
|
||||
t16 = $[38];
|
||||
}
|
||||
let t17;
|
||||
if ($[39] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t17 = <Box marginLeft={3}><Text dimColor={true}>↑/↓ to select · Enter to continue</Text></Box>;
|
||||
$[39] = t17;
|
||||
} else {
|
||||
t17 = $[39];
|
||||
}
|
||||
let t18;
|
||||
if ($[40] !== t16) {
|
||||
t18 = <>{t16}{t17}</>;
|
||||
$[40] = t16;
|
||||
$[41] = t18;
|
||||
} else {
|
||||
t18 = $[41];
|
||||
}
|
||||
return t18;
|
||||
|
||||
export function CheckExistingSecretStep({
|
||||
useExistingSecret,
|
||||
secretName,
|
||||
onToggleUseExistingSecret,
|
||||
onSecretNameChange,
|
||||
onSubmit,
|
||||
}: CheckExistingSecretStepProps) {
|
||||
const [cursorOffset, setCursorOffset] = useState(0)
|
||||
const terminalSize = useTerminalSize()
|
||||
const [theme] = useTheme()
|
||||
|
||||
// When the text input is visible, omit confirm:yes so bare 'y' passes
|
||||
// through to the input instead of submitting. TextInput's onSubmit handles
|
||||
// Enter. Keep the Confirmation context (not Settings) to avoid j/k bindings.
|
||||
const handlePrevious = useCallback(
|
||||
() => onToggleUseExistingSecret(true),
|
||||
[onToggleUseExistingSecret],
|
||||
)
|
||||
const handleNext = useCallback(
|
||||
() => onToggleUseExistingSecret(false),
|
||||
[onToggleUseExistingSecret],
|
||||
)
|
||||
useKeybindings(
|
||||
{
|
||||
'confirm:previous': handlePrevious,
|
||||
'confirm:next': handleNext,
|
||||
'confirm:yes': onSubmit,
|
||||
},
|
||||
{ context: 'Confirmation', isActive: useExistingSecret },
|
||||
)
|
||||
useKeybindings(
|
||||
{
|
||||
'confirm:previous': handlePrevious,
|
||||
'confirm:next': handleNext,
|
||||
},
|
||||
{ context: 'Confirmation', isActive: !useExistingSecret },
|
||||
)
|
||||
|
||||
return (
|
||||
<>
|
||||
<Box flexDirection="column" borderStyle="round" paddingX={1}>
|
||||
<Box flexDirection="column" marginBottom={1}>
|
||||
<Text bold>Install GitHub App</Text>
|
||||
<Text dimColor>Setup API key secret</Text>
|
||||
</Box>
|
||||
<Box marginBottom={1}>
|
||||
<Text color="warning">
|
||||
ANTHROPIC_API_KEY already exists in repository secrets!
|
||||
</Text>
|
||||
</Box>
|
||||
<Box marginBottom={1}>
|
||||
<Text>Would you like to:</Text>
|
||||
</Box>
|
||||
<Box marginBottom={1}>
|
||||
<Text>
|
||||
{useExistingSecret ? color('success', theme)('> ') : ' '}
|
||||
Use the existing API key
|
||||
</Text>
|
||||
</Box>
|
||||
<Box marginBottom={1}>
|
||||
<Text>
|
||||
{!useExistingSecret ? color('success', theme)('> ') : ' '}
|
||||
Create a new secret with a different name
|
||||
</Text>
|
||||
</Box>
|
||||
{!useExistingSecret && (
|
||||
<>
|
||||
<Box marginBottom={1}>
|
||||
<Text>
|
||||
Enter new secret name (alphanumeric with underscores):
|
||||
</Text>
|
||||
</Box>
|
||||
<TextInput
|
||||
value={secretName}
|
||||
onChange={onSecretNameChange}
|
||||
onSubmit={onSubmit}
|
||||
focus={true}
|
||||
placeholder="e.g., CLAUDE_API_KEY"
|
||||
columns={terminalSize.columns}
|
||||
cursorOffset={cursorOffset}
|
||||
onChangeCursorOffset={setCursorOffset}
|
||||
showCursor={true}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</Box>
|
||||
<Box marginLeft={3}>
|
||||
<Text dimColor>↑/↓ to select · Enter to continue</Text>
|
||||
</Box>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,14 +1,6 @@
|
||||
import { c as _c } from "react/compiler-runtime";
|
||||
import React from 'react';
|
||||
import { Text } from '../../ink.js';
|
||||
import React from 'react'
|
||||
import { Text } from '../../ink.js'
|
||||
|
||||
export function CheckGitHubStep() {
|
||||
const $ = _c(1);
|
||||
let t0;
|
||||
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t0 = <Text>Checking GitHub CLI installation…</Text>;
|
||||
$[0] = t0;
|
||||
} else {
|
||||
t0 = $[0];
|
||||
}
|
||||
return t0;
|
||||
return <Text>Checking GitHub CLI installation…</Text>
|
||||
}
|
||||
|
||||
@@ -1,210 +1,125 @@
|
||||
import { c as _c } from "react/compiler-runtime";
|
||||
import React, { useCallback, useState } from 'react';
|
||||
import TextInput from '../../components/TextInput.js';
|
||||
import { useTerminalSize } from '../../hooks/useTerminalSize.js';
|
||||
import { Box, Text } from '../../ink.js';
|
||||
import { useKeybindings } from '../../keybindings/useKeybinding.js';
|
||||
import React, { useCallback, useState } from 'react'
|
||||
import TextInput from '../../components/TextInput.js'
|
||||
import { useTerminalSize } from '../../hooks/useTerminalSize.js'
|
||||
import { Box, Text } from '../../ink.js'
|
||||
import { useKeybindings } from '../../keybindings/useKeybinding.js'
|
||||
|
||||
interface ChooseRepoStepProps {
|
||||
currentRepo: string | null;
|
||||
useCurrentRepo: boolean;
|
||||
repoUrl: string;
|
||||
onRepoUrlChange: (value: string) => void;
|
||||
onToggleUseCurrentRepo: (useCurrentRepo: boolean) => void;
|
||||
onSubmit: () => void;
|
||||
currentRepo: string | null
|
||||
useCurrentRepo: boolean
|
||||
repoUrl: string
|
||||
onRepoUrlChange: (value: string) => void
|
||||
onToggleUseCurrentRepo: (useCurrentRepo: boolean) => void
|
||||
onSubmit: () => void
|
||||
}
|
||||
export function ChooseRepoStep(t0) {
|
||||
const $ = _c(49);
|
||||
const {
|
||||
currentRepo,
|
||||
useCurrentRepo,
|
||||
repoUrl,
|
||||
onRepoUrlChange,
|
||||
onSubmit,
|
||||
onToggleUseCurrentRepo
|
||||
} = t0;
|
||||
const [cursorOffset, setCursorOffset] = useState(0);
|
||||
const [showEmptyError, setShowEmptyError] = useState(false);
|
||||
const terminalSize = useTerminalSize();
|
||||
const textInputColumns = terminalSize.columns;
|
||||
let t1;
|
||||
if ($[0] !== currentRepo || $[1] !== onSubmit || $[2] !== repoUrl || $[3] !== useCurrentRepo) {
|
||||
t1 = () => {
|
||||
const repoName = useCurrentRepo ? currentRepo : repoUrl;
|
||||
if (!repoName?.trim()) {
|
||||
setShowEmptyError(true);
|
||||
return;
|
||||
}
|
||||
onSubmit();
|
||||
};
|
||||
$[0] = currentRepo;
|
||||
$[1] = onSubmit;
|
||||
$[2] = repoUrl;
|
||||
$[3] = useCurrentRepo;
|
||||
$[4] = t1;
|
||||
} else {
|
||||
t1 = $[4];
|
||||
}
|
||||
const handleSubmit = t1;
|
||||
const isTextInputVisible = !useCurrentRepo || !currentRepo;
|
||||
let t2;
|
||||
if ($[5] !== onToggleUseCurrentRepo) {
|
||||
t2 = () => {
|
||||
onToggleUseCurrentRepo(true);
|
||||
setShowEmptyError(false);
|
||||
};
|
||||
$[5] = onToggleUseCurrentRepo;
|
||||
$[6] = t2;
|
||||
} else {
|
||||
t2 = $[6];
|
||||
}
|
||||
const handlePrevious = t2;
|
||||
let t3;
|
||||
if ($[7] !== onToggleUseCurrentRepo) {
|
||||
t3 = () => {
|
||||
onToggleUseCurrentRepo(false);
|
||||
setShowEmptyError(false);
|
||||
};
|
||||
$[7] = onToggleUseCurrentRepo;
|
||||
$[8] = t3;
|
||||
} else {
|
||||
t3 = $[8];
|
||||
}
|
||||
const handleNext = t3;
|
||||
let t4;
|
||||
if ($[9] !== handleNext || $[10] !== handlePrevious || $[11] !== handleSubmit) {
|
||||
t4 = {
|
||||
"confirm:previous": handlePrevious,
|
||||
"confirm:next": handleNext,
|
||||
"confirm:yes": handleSubmit
|
||||
};
|
||||
$[9] = handleNext;
|
||||
$[10] = handlePrevious;
|
||||
$[11] = handleSubmit;
|
||||
$[12] = t4;
|
||||
} else {
|
||||
t4 = $[12];
|
||||
}
|
||||
const t5 = !isTextInputVisible;
|
||||
let t6;
|
||||
if ($[13] !== t5) {
|
||||
t6 = {
|
||||
context: "Confirmation",
|
||||
isActive: t5
|
||||
};
|
||||
$[13] = t5;
|
||||
$[14] = t6;
|
||||
} else {
|
||||
t6 = $[14];
|
||||
}
|
||||
useKeybindings(t4, t6);
|
||||
let t7;
|
||||
if ($[15] !== handleNext || $[16] !== handlePrevious) {
|
||||
t7 = {
|
||||
"confirm:previous": handlePrevious,
|
||||
"confirm:next": handleNext
|
||||
};
|
||||
$[15] = handleNext;
|
||||
$[16] = handlePrevious;
|
||||
$[17] = t7;
|
||||
} else {
|
||||
t7 = $[17];
|
||||
}
|
||||
let t8;
|
||||
if ($[18] !== isTextInputVisible) {
|
||||
t8 = {
|
||||
context: "Confirmation",
|
||||
isActive: isTextInputVisible
|
||||
};
|
||||
$[18] = isTextInputVisible;
|
||||
$[19] = t8;
|
||||
} else {
|
||||
t8 = $[19];
|
||||
}
|
||||
useKeybindings(t7, t8);
|
||||
let t9;
|
||||
if ($[20] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t9 = <Box flexDirection="column" marginBottom={1}><Text bold={true}>Install GitHub App</Text><Text dimColor={true}>Select GitHub repository</Text></Box>;
|
||||
$[20] = t9;
|
||||
} else {
|
||||
t9 = $[20];
|
||||
}
|
||||
let t10;
|
||||
if ($[21] !== currentRepo || $[22] !== useCurrentRepo) {
|
||||
t10 = currentRepo && <Box marginBottom={1}><Text bold={useCurrentRepo} color={useCurrentRepo ? "permission" : undefined}>{useCurrentRepo ? "> " : " "}Use current repository: {currentRepo}</Text></Box>;
|
||||
$[21] = currentRepo;
|
||||
$[22] = useCurrentRepo;
|
||||
$[23] = t10;
|
||||
} else {
|
||||
t10 = $[23];
|
||||
}
|
||||
const t11 = !useCurrentRepo || !currentRepo;
|
||||
const t12 = !useCurrentRepo || !currentRepo ? "permission" : undefined;
|
||||
const t13 = !useCurrentRepo || !currentRepo ? "> " : " ";
|
||||
const t14 = currentRepo ? "Enter a different repository" : "Enter repository";
|
||||
let t15;
|
||||
if ($[24] !== t11 || $[25] !== t12 || $[26] !== t13 || $[27] !== t14) {
|
||||
t15 = <Box marginBottom={1}><Text bold={t11} color={t12}>{t13}{t14}</Text></Box>;
|
||||
$[24] = t11;
|
||||
$[25] = t12;
|
||||
$[26] = t13;
|
||||
$[27] = t14;
|
||||
$[28] = t15;
|
||||
} else {
|
||||
t15 = $[28];
|
||||
}
|
||||
let t16;
|
||||
if ($[29] !== currentRepo || $[30] !== cursorOffset || $[31] !== handleSubmit || $[32] !== onRepoUrlChange || $[33] !== repoUrl || $[34] !== textInputColumns || $[35] !== useCurrentRepo) {
|
||||
t16 = (!useCurrentRepo || !currentRepo) && <Box marginLeft={2} marginBottom={1}><TextInput value={repoUrl} onChange={value => {
|
||||
onRepoUrlChange(value);
|
||||
setShowEmptyError(false);
|
||||
}} onSubmit={handleSubmit} focus={true} placeholder={"Enter a repo as owner/repo or https://github.com/owner/repo\u2026"} columns={textInputColumns} cursorOffset={cursorOffset} onChangeCursorOffset={setCursorOffset} showCursor={true} /></Box>;
|
||||
$[29] = currentRepo;
|
||||
$[30] = cursorOffset;
|
||||
$[31] = handleSubmit;
|
||||
$[32] = onRepoUrlChange;
|
||||
$[33] = repoUrl;
|
||||
$[34] = textInputColumns;
|
||||
$[35] = useCurrentRepo;
|
||||
$[36] = t16;
|
||||
} else {
|
||||
t16 = $[36];
|
||||
}
|
||||
let t17;
|
||||
if ($[37] !== t10 || $[38] !== t15 || $[39] !== t16) {
|
||||
t17 = <Box flexDirection="column" borderStyle="round" paddingX={1}>{t9}{t10}{t15}{t16}</Box>;
|
||||
$[37] = t10;
|
||||
$[38] = t15;
|
||||
$[39] = t16;
|
||||
$[40] = t17;
|
||||
} else {
|
||||
t17 = $[40];
|
||||
}
|
||||
let t18;
|
||||
if ($[41] !== showEmptyError) {
|
||||
t18 = showEmptyError && <Box marginLeft={3} marginBottom={1}><Text color="error">Please enter a repository name to continue</Text></Box>;
|
||||
$[41] = showEmptyError;
|
||||
$[42] = t18;
|
||||
} else {
|
||||
t18 = $[42];
|
||||
}
|
||||
const t19 = currentRepo ? "\u2191/\u2193 to select \xB7 " : "";
|
||||
let t20;
|
||||
if ($[43] !== t19) {
|
||||
t20 = <Box marginLeft={3}><Text dimColor={true}>{t19}Enter to continue</Text></Box>;
|
||||
$[43] = t19;
|
||||
$[44] = t20;
|
||||
} else {
|
||||
t20 = $[44];
|
||||
}
|
||||
let t21;
|
||||
if ($[45] !== t17 || $[46] !== t18 || $[47] !== t20) {
|
||||
t21 = <>{t17}{t18}{t20}</>;
|
||||
$[45] = t17;
|
||||
$[46] = t18;
|
||||
$[47] = t20;
|
||||
$[48] = t21;
|
||||
} else {
|
||||
t21 = $[48];
|
||||
}
|
||||
return t21;
|
||||
|
||||
export function ChooseRepoStep({
|
||||
currentRepo,
|
||||
useCurrentRepo,
|
||||
repoUrl,
|
||||
onRepoUrlChange,
|
||||
onSubmit,
|
||||
onToggleUseCurrentRepo,
|
||||
}: ChooseRepoStepProps) {
|
||||
const [cursorOffset, setCursorOffset] = useState(0)
|
||||
const [showEmptyError, setShowEmptyError] = useState(false)
|
||||
const terminalSize = useTerminalSize()
|
||||
const textInputColumns = terminalSize.columns
|
||||
|
||||
const handleSubmit = useCallback(() => {
|
||||
const repoName = useCurrentRepo ? currentRepo : repoUrl
|
||||
if (!repoName?.trim()) {
|
||||
setShowEmptyError(true)
|
||||
return
|
||||
}
|
||||
onSubmit()
|
||||
}, [useCurrentRepo, currentRepo, repoUrl, onSubmit])
|
||||
|
||||
// When the text input is visible, omit confirm:yes so bare 'y' passes
|
||||
// through to the input instead of submitting. TextInput's onSubmit handles
|
||||
// Enter. Keep the Confirmation context (not Settings) to avoid j/k bindings.
|
||||
const isTextInputVisible = !useCurrentRepo || !currentRepo
|
||||
const handlePrevious = useCallback(() => {
|
||||
onToggleUseCurrentRepo(true)
|
||||
setShowEmptyError(false)
|
||||
}, [onToggleUseCurrentRepo])
|
||||
const handleNext = useCallback(() => {
|
||||
onToggleUseCurrentRepo(false)
|
||||
setShowEmptyError(false)
|
||||
}, [onToggleUseCurrentRepo])
|
||||
|
||||
useKeybindings(
|
||||
{
|
||||
'confirm:previous': handlePrevious,
|
||||
'confirm:next': handleNext,
|
||||
'confirm:yes': handleSubmit,
|
||||
},
|
||||
{ context: 'Confirmation', isActive: !isTextInputVisible },
|
||||
)
|
||||
useKeybindings(
|
||||
{
|
||||
'confirm:previous': handlePrevious,
|
||||
'confirm:next': handleNext,
|
||||
},
|
||||
{ context: 'Confirmation', isActive: isTextInputVisible },
|
||||
)
|
||||
|
||||
return (
|
||||
<>
|
||||
<Box flexDirection="column" borderStyle="round" paddingX={1}>
|
||||
<Box flexDirection="column" marginBottom={1}>
|
||||
<Text bold>Install GitHub App</Text>
|
||||
<Text dimColor>Select GitHub repository</Text>
|
||||
</Box>
|
||||
{currentRepo && (
|
||||
<Box marginBottom={1}>
|
||||
<Text
|
||||
bold={useCurrentRepo}
|
||||
color={useCurrentRepo ? 'permission' : undefined}
|
||||
>
|
||||
{useCurrentRepo ? '> ' : ' '}
|
||||
Use current repository: {currentRepo}
|
||||
</Text>
|
||||
</Box>
|
||||
)}
|
||||
<Box marginBottom={1}>
|
||||
<Text
|
||||
bold={!useCurrentRepo || !currentRepo}
|
||||
color={!useCurrentRepo || !currentRepo ? 'permission' : undefined}
|
||||
>
|
||||
{!useCurrentRepo || !currentRepo ? '> ' : ' '}
|
||||
{currentRepo ? 'Enter a different repository' : 'Enter repository'}
|
||||
</Text>
|
||||
</Box>
|
||||
{(!useCurrentRepo || !currentRepo) && (
|
||||
<Box marginLeft={2} marginBottom={1}>
|
||||
<TextInput
|
||||
value={repoUrl}
|
||||
onChange={value => {
|
||||
onRepoUrlChange(value)
|
||||
setShowEmptyError(false)
|
||||
}}
|
||||
onSubmit={handleSubmit}
|
||||
focus={true}
|
||||
placeholder="Enter a repo as owner/repo or https://github.com/owner/repo…"
|
||||
columns={textInputColumns}
|
||||
cursorOffset={cursorOffset}
|
||||
onChangeCursorOffset={setCursorOffset}
|
||||
showCursor={true}
|
||||
/>
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
{showEmptyError && (
|
||||
<Box marginLeft={3} marginBottom={1}>
|
||||
<Text color="error">Please enter a repository name to continue</Text>
|
||||
</Box>
|
||||
)}
|
||||
<Box marginLeft={3}>
|
||||
<Text dimColor>
|
||||
{currentRepo ? '↑/↓ to select · ' : ''}Enter to continue
|
||||
</Text>
|
||||
</Box>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,64 +1,78 @@
|
||||
import { c as _c } from "react/compiler-runtime";
|
||||
import React from 'react';
|
||||
import { Box, Text } from '../../ink.js';
|
||||
import type { Workflow } from './types.js';
|
||||
import React from 'react'
|
||||
import { Box, Text } from '../../ink.js'
|
||||
import type { Workflow } from './types.js'
|
||||
|
||||
interface CreatingStepProps {
|
||||
currentWorkflowInstallStep: number;
|
||||
secretExists: boolean;
|
||||
useExistingSecret: boolean;
|
||||
secretName: string;
|
||||
skipWorkflow?: boolean;
|
||||
selectedWorkflows: Workflow[];
|
||||
currentWorkflowInstallStep: number
|
||||
secretExists: boolean
|
||||
useExistingSecret: boolean
|
||||
secretName: string
|
||||
skipWorkflow?: boolean
|
||||
selectedWorkflows: Workflow[]
|
||||
}
|
||||
export function CreatingStep(t0) {
|
||||
const $ = _c(10);
|
||||
const {
|
||||
currentWorkflowInstallStep,
|
||||
secretExists,
|
||||
useExistingSecret,
|
||||
secretName,
|
||||
skipWorkflow: t1,
|
||||
selectedWorkflows
|
||||
} = t0;
|
||||
const skipWorkflow = t1 === undefined ? false : t1;
|
||||
let t2;
|
||||
if ($[0] !== secretExists || $[1] !== secretName || $[2] !== selectedWorkflows || $[3] !== skipWorkflow || $[4] !== useExistingSecret) {
|
||||
t2 = skipWorkflow ? ["Getting repository information", secretExists && useExistingSecret ? "Using existing API key secret" : `Setting up ${secretName} secret`] : ["Getting repository information", "Creating branch", selectedWorkflows.length > 1 ? "Creating workflow files" : "Creating workflow file", secretExists && useExistingSecret ? "Using existing API key secret" : `Setting up ${secretName} secret`, "Opening pull request page"];
|
||||
$[0] = secretExists;
|
||||
$[1] = secretName;
|
||||
$[2] = selectedWorkflows;
|
||||
$[3] = skipWorkflow;
|
||||
$[4] = useExistingSecret;
|
||||
$[5] = t2;
|
||||
} else {
|
||||
t2 = $[5];
|
||||
}
|
||||
const progressSteps = t2;
|
||||
let t3;
|
||||
if ($[6] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t3 = <Box flexDirection="column" marginBottom={1}><Text bold={true}>Install GitHub App</Text><Text dimColor={true}>Create GitHub Actions workflow</Text></Box>;
|
||||
$[6] = t3;
|
||||
} else {
|
||||
t3 = $[6];
|
||||
}
|
||||
let t4;
|
||||
if ($[7] !== currentWorkflowInstallStep || $[8] !== progressSteps) {
|
||||
t4 = <><Box flexDirection="column" borderStyle="round" paddingX={1}>{t3}{progressSteps.map((stepText, index) => {
|
||||
let status = "pending";
|
||||
|
||||
export function CreatingStep({
|
||||
currentWorkflowInstallStep,
|
||||
secretExists,
|
||||
useExistingSecret,
|
||||
secretName,
|
||||
skipWorkflow = false,
|
||||
selectedWorkflows,
|
||||
}: CreatingStepProps) {
|
||||
const progressSteps = skipWorkflow
|
||||
? [
|
||||
'Getting repository information',
|
||||
secretExists && useExistingSecret
|
||||
? 'Using existing API key secret'
|
||||
: `Setting up ${secretName} secret`,
|
||||
]
|
||||
: [
|
||||
'Getting repository information',
|
||||
'Creating branch',
|
||||
selectedWorkflows.length > 1
|
||||
? 'Creating workflow files'
|
||||
: 'Creating workflow file',
|
||||
secretExists && useExistingSecret
|
||||
? 'Using existing API key secret'
|
||||
: `Setting up ${secretName} secret`,
|
||||
'Opening pull request page',
|
||||
]
|
||||
|
||||
return (
|
||||
<>
|
||||
<Box flexDirection="column" borderStyle="round" paddingX={1}>
|
||||
<Box flexDirection="column" marginBottom={1}>
|
||||
<Text bold>Install GitHub App</Text>
|
||||
<Text dimColor>Create GitHub Actions workflow</Text>
|
||||
</Box>
|
||||
{progressSteps.map((stepText, index) => {
|
||||
let status: 'completed' | 'in-progress' | 'pending' = 'pending'
|
||||
|
||||
if (index < currentWorkflowInstallStep) {
|
||||
status = "completed";
|
||||
} else {
|
||||
if (index === currentWorkflowInstallStep) {
|
||||
status = "in-progress";
|
||||
}
|
||||
status = 'completed'
|
||||
} else if (index === currentWorkflowInstallStep) {
|
||||
status = 'in-progress'
|
||||
}
|
||||
return <Box key={index}><Text color={status === "completed" ? "success" : status === "in-progress" ? "warning" : undefined}>{status === "completed" ? "\u2713 " : ""}{stepText}{status === "in-progress" ? "\u2026" : ""}</Text></Box>;
|
||||
})}</Box></>;
|
||||
$[7] = currentWorkflowInstallStep;
|
||||
$[8] = progressSteps;
|
||||
$[9] = t4;
|
||||
} else {
|
||||
t4 = $[9];
|
||||
}
|
||||
return t4;
|
||||
|
||||
return (
|
||||
<Box key={index}>
|
||||
<Text
|
||||
color={
|
||||
status === 'completed'
|
||||
? 'success'
|
||||
: status === 'in-progress'
|
||||
? 'warning'
|
||||
: undefined
|
||||
}
|
||||
>
|
||||
{status === 'completed' ? '✓ ' : ''}
|
||||
{stepText}
|
||||
{status === 'in-progress' ? '…' : ''}
|
||||
</Text>
|
||||
</Box>
|
||||
)
|
||||
})}
|
||||
</Box>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,84 +1,51 @@
|
||||
import { c as _c } from "react/compiler-runtime";
|
||||
import React from 'react';
|
||||
import { GITHUB_ACTION_SETUP_DOCS_URL } from '../../constants/github-app.js';
|
||||
import { Box, Text } from '../../ink.js';
|
||||
import React from 'react'
|
||||
import { GITHUB_ACTION_SETUP_DOCS_URL } from '../../constants/github-app.js'
|
||||
import { Box, Text } from '../../ink.js'
|
||||
|
||||
interface ErrorStepProps {
|
||||
error: string | undefined;
|
||||
errorReason?: string;
|
||||
errorInstructions?: string[];
|
||||
error: string | undefined
|
||||
errorReason?: string
|
||||
errorInstructions?: string[]
|
||||
}
|
||||
export function ErrorStep(t0) {
|
||||
const $ = _c(15);
|
||||
const {
|
||||
error,
|
||||
errorReason,
|
||||
errorInstructions
|
||||
} = t0;
|
||||
let t1;
|
||||
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t1 = <Box flexDirection="column" marginBottom={1}><Text bold={true}>Install GitHub App</Text></Box>;
|
||||
$[0] = t1;
|
||||
} else {
|
||||
t1 = $[0];
|
||||
}
|
||||
let t2;
|
||||
if ($[1] !== error) {
|
||||
t2 = <Text color="error">Error: {error}</Text>;
|
||||
$[1] = error;
|
||||
$[2] = t2;
|
||||
} else {
|
||||
t2 = $[2];
|
||||
}
|
||||
let t3;
|
||||
if ($[3] !== errorReason) {
|
||||
t3 = errorReason && <Box marginTop={1}><Text dimColor={true}>Reason: {errorReason}</Text></Box>;
|
||||
$[3] = errorReason;
|
||||
$[4] = t3;
|
||||
} else {
|
||||
t3 = $[4];
|
||||
}
|
||||
let t4;
|
||||
if ($[5] !== errorInstructions) {
|
||||
t4 = errorInstructions && errorInstructions.length > 0 && <Box flexDirection="column" marginTop={1}><Text dimColor={true}>How to fix:</Text>{errorInstructions.map(_temp)}</Box>;
|
||||
$[5] = errorInstructions;
|
||||
$[6] = t4;
|
||||
} else {
|
||||
t4 = $[6];
|
||||
}
|
||||
let t5;
|
||||
if ($[7] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t5 = <Box marginTop={1}><Text dimColor={true}>For manual setup instructions, see:{" "}<Text color="claude">{GITHUB_ACTION_SETUP_DOCS_URL}</Text></Text></Box>;
|
||||
$[7] = t5;
|
||||
} else {
|
||||
t5 = $[7];
|
||||
}
|
||||
let t6;
|
||||
if ($[8] !== t2 || $[9] !== t3 || $[10] !== t4) {
|
||||
t6 = <Box flexDirection="column" borderStyle="round" paddingX={1}>{t1}{t2}{t3}{t4}{t5}</Box>;
|
||||
$[8] = t2;
|
||||
$[9] = t3;
|
||||
$[10] = t4;
|
||||
$[11] = t6;
|
||||
} else {
|
||||
t6 = $[11];
|
||||
}
|
||||
let t7;
|
||||
if ($[12] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t7 = <Box marginLeft={3}><Text dimColor={true}>Press any key to exit</Text></Box>;
|
||||
$[12] = t7;
|
||||
} else {
|
||||
t7 = $[12];
|
||||
}
|
||||
let t8;
|
||||
if ($[13] !== t6) {
|
||||
t8 = <>{t6}{t7}</>;
|
||||
$[13] = t6;
|
||||
$[14] = t8;
|
||||
} else {
|
||||
t8 = $[14];
|
||||
}
|
||||
return t8;
|
||||
}
|
||||
function _temp(instruction, index) {
|
||||
return <Box key={index} marginLeft={2}><Text dimColor={true}>• </Text><Text>{instruction}</Text></Box>;
|
||||
|
||||
export function ErrorStep({
|
||||
error,
|
||||
errorReason,
|
||||
errorInstructions,
|
||||
}: ErrorStepProps) {
|
||||
return (
|
||||
<>
|
||||
<Box flexDirection="column" borderStyle="round" paddingX={1}>
|
||||
<Box flexDirection="column" marginBottom={1}>
|
||||
<Text bold>Install GitHub App</Text>
|
||||
</Box>
|
||||
<Text color="error">Error: {error}</Text>
|
||||
{errorReason && (
|
||||
<Box marginTop={1}>
|
||||
<Text dimColor>Reason: {errorReason}</Text>
|
||||
</Box>
|
||||
)}
|
||||
{errorInstructions && errorInstructions.length > 0 && (
|
||||
<Box flexDirection="column" marginTop={1}>
|
||||
<Text dimColor>How to fix:</Text>
|
||||
{errorInstructions.map((instruction, index) => (
|
||||
<Box key={index} marginLeft={2}>
|
||||
<Text dimColor>• </Text>
|
||||
<Text>{instruction}</Text>
|
||||
</Box>
|
||||
))}
|
||||
</Box>
|
||||
)}
|
||||
<Box marginTop={1}>
|
||||
<Text dimColor>
|
||||
For manual setup instructions, see:{' '}
|
||||
<Text color="claude">{GITHUB_ACTION_SETUP_DOCS_URL}</Text>
|
||||
</Text>
|
||||
</Box>
|
||||
</Box>
|
||||
<Box marginLeft={3}>
|
||||
<Text dimColor>Press any key to exit</Text>
|
||||
</Box>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,102 +1,70 @@
|
||||
import { c as _c } from "react/compiler-runtime";
|
||||
import React from 'react';
|
||||
import { Select } from 'src/components/CustomSelect/index.js';
|
||||
import { Box, Text } from '../../ink.js';
|
||||
import React from 'react'
|
||||
import { Select } from 'src/components/CustomSelect/index.js'
|
||||
import { Box, Text } from '../../ink.js'
|
||||
|
||||
interface ExistingWorkflowStepProps {
|
||||
repoName: string;
|
||||
onSelectAction: (action: 'update' | 'skip' | 'exit') => void;
|
||||
repoName: string
|
||||
onSelectAction: (action: 'update' | 'skip' | 'exit') => void
|
||||
}
|
||||
export function ExistingWorkflowStep(t0) {
|
||||
const $ = _c(16);
|
||||
const {
|
||||
repoName,
|
||||
onSelectAction
|
||||
} = t0;
|
||||
let t1;
|
||||
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t1 = [{
|
||||
label: "Update workflow file with latest version",
|
||||
value: "update"
|
||||
}, {
|
||||
label: "Skip workflow update (configure secrets only)",
|
||||
value: "skip"
|
||||
}, {
|
||||
label: "Exit without making changes",
|
||||
value: "exit"
|
||||
}];
|
||||
$[0] = t1;
|
||||
} else {
|
||||
t1 = $[0];
|
||||
|
||||
export function ExistingWorkflowStep({
|
||||
repoName,
|
||||
onSelectAction,
|
||||
}: ExistingWorkflowStepProps) {
|
||||
const options = [
|
||||
{
|
||||
label: 'Update workflow file with latest version',
|
||||
value: 'update',
|
||||
},
|
||||
{
|
||||
label: 'Skip workflow update (configure secrets only)',
|
||||
value: 'skip',
|
||||
},
|
||||
{
|
||||
label: 'Exit without making changes',
|
||||
value: 'exit',
|
||||
},
|
||||
]
|
||||
|
||||
const handleSelect = (value: string) => {
|
||||
onSelectAction(value as 'update' | 'skip' | 'exit')
|
||||
}
|
||||
const options = t1;
|
||||
let t2;
|
||||
if ($[1] !== onSelectAction) {
|
||||
t2 = value => {
|
||||
onSelectAction(value as 'update' | 'skip' | 'exit');
|
||||
};
|
||||
$[1] = onSelectAction;
|
||||
$[2] = t2;
|
||||
} else {
|
||||
t2 = $[2];
|
||||
|
||||
const handleCancel = () => {
|
||||
onSelectAction('exit')
|
||||
}
|
||||
const handleSelect = t2;
|
||||
let t3;
|
||||
if ($[3] !== onSelectAction) {
|
||||
t3 = () => {
|
||||
onSelectAction("exit");
|
||||
};
|
||||
$[3] = onSelectAction;
|
||||
$[4] = t3;
|
||||
} else {
|
||||
t3 = $[4];
|
||||
}
|
||||
const handleCancel = t3;
|
||||
let t4;
|
||||
if ($[5] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t4 = <Text bold={true}>Existing Workflow Found</Text>;
|
||||
$[5] = t4;
|
||||
} else {
|
||||
t4 = $[5];
|
||||
}
|
||||
let t5;
|
||||
if ($[6] !== repoName) {
|
||||
t5 = <Box flexDirection="column" marginBottom={1}>{t4}<Text dimColor={true}>Repository: {repoName}</Text></Box>;
|
||||
$[6] = repoName;
|
||||
$[7] = t5;
|
||||
} else {
|
||||
t5 = $[7];
|
||||
}
|
||||
let t6;
|
||||
if ($[8] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t6 = <Box flexDirection="column" marginBottom={1}><Text>A Claude workflow file already exists at{" "}<Text color="claude">.github/workflows/claude.yml</Text></Text><Text dimColor={true}>What would you like to do?</Text></Box>;
|
||||
$[8] = t6;
|
||||
} else {
|
||||
t6 = $[8];
|
||||
}
|
||||
let t7;
|
||||
if ($[9] !== handleCancel || $[10] !== handleSelect) {
|
||||
t7 = <Box flexDirection="column"><Select options={options} onChange={handleSelect} onCancel={handleCancel} /></Box>;
|
||||
$[9] = handleCancel;
|
||||
$[10] = handleSelect;
|
||||
$[11] = t7;
|
||||
} else {
|
||||
t7 = $[11];
|
||||
}
|
||||
let t8;
|
||||
if ($[12] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t8 = <Box marginTop={1}><Text dimColor={true}>View the latest workflow template at:{" "}<Text color="claude">https://github.com/anthropics/claude-code-action/blob/main/examples/claude.yml</Text></Text></Box>;
|
||||
$[12] = t8;
|
||||
} else {
|
||||
t8 = $[12];
|
||||
}
|
||||
let t9;
|
||||
if ($[13] !== t5 || $[14] !== t7) {
|
||||
t9 = <Box flexDirection="column" borderStyle="round" borderDimColor={true} paddingX={1}>{t5}{t6}{t7}{t8}</Box>;
|
||||
$[13] = t5;
|
||||
$[14] = t7;
|
||||
$[15] = t9;
|
||||
} else {
|
||||
t9 = $[15];
|
||||
}
|
||||
return t9;
|
||||
|
||||
return (
|
||||
<Box flexDirection="column" borderStyle="round" borderDimColor paddingX={1}>
|
||||
<Box flexDirection="column" marginBottom={1}>
|
||||
<Text bold>Existing Workflow Found</Text>
|
||||
<Text dimColor>Repository: {repoName}</Text>
|
||||
</Box>
|
||||
|
||||
<Box flexDirection="column" marginBottom={1}>
|
||||
<Text>
|
||||
A Claude workflow file already exists at{' '}
|
||||
<Text color="claude">.github/workflows/claude.yml</Text>
|
||||
</Text>
|
||||
<Text dimColor>What would you like to do?</Text>
|
||||
</Box>
|
||||
|
||||
<Box flexDirection="column">
|
||||
<Select
|
||||
options={options}
|
||||
onChange={handleSelect}
|
||||
onCancel={handleCancel}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
<Box marginTop={1}>
|
||||
<Text dimColor>
|
||||
View the latest workflow template at:{' '}
|
||||
<Text color="claude">
|
||||
https://github.com/anthropics/claude-code-action/blob/main/examples/claude.yml
|
||||
</Text>
|
||||
</Text>
|
||||
</Box>
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,93 +1,53 @@
|
||||
import { c as _c } from "react/compiler-runtime";
|
||||
import figures from 'figures';
|
||||
import React from 'react';
|
||||
import { GITHUB_ACTION_SETUP_DOCS_URL } from '../../constants/github-app.js';
|
||||
import { Box, Text } from '../../ink.js';
|
||||
import { useKeybinding } from '../../keybindings/useKeybinding.js';
|
||||
import figures from 'figures'
|
||||
import React from 'react'
|
||||
import { GITHUB_ACTION_SETUP_DOCS_URL } from '../../constants/github-app.js'
|
||||
import { Box, Text } from '../../ink.js'
|
||||
import { useKeybinding } from '../../keybindings/useKeybinding.js'
|
||||
|
||||
interface InstallAppStepProps {
|
||||
repoUrl: string;
|
||||
onSubmit: () => void;
|
||||
repoUrl: string
|
||||
onSubmit: () => void
|
||||
}
|
||||
export function InstallAppStep(t0) {
|
||||
const $ = _c(12);
|
||||
const {
|
||||
repoUrl,
|
||||
onSubmit
|
||||
} = t0;
|
||||
let t1;
|
||||
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t1 = {
|
||||
context: "Confirmation"
|
||||
};
|
||||
$[0] = t1;
|
||||
} else {
|
||||
t1 = $[0];
|
||||
}
|
||||
useKeybinding("confirm:yes", onSubmit, t1);
|
||||
let t2;
|
||||
if ($[1] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t2 = <Box flexDirection="column" marginBottom={1}><Text bold={true}>Install the Claude GitHub App</Text></Box>;
|
||||
$[1] = t2;
|
||||
} else {
|
||||
t2 = $[1];
|
||||
}
|
||||
let t3;
|
||||
if ($[2] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t3 = <Box marginBottom={1}><Text>Opening browser to install the Claude GitHub App…</Text></Box>;
|
||||
$[2] = t3;
|
||||
} else {
|
||||
t3 = $[2];
|
||||
}
|
||||
let t4;
|
||||
if ($[3] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t4 = <Box marginBottom={1}><Text>If your browser doesn't open automatically, visit:</Text></Box>;
|
||||
$[3] = t4;
|
||||
} else {
|
||||
t4 = $[3];
|
||||
}
|
||||
let t5;
|
||||
if ($[4] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t5 = <Box marginBottom={1}><Text underline={true}>https://github.com/apps/claude</Text></Box>;
|
||||
$[4] = t5;
|
||||
} else {
|
||||
t5 = $[4];
|
||||
}
|
||||
let t6;
|
||||
if ($[5] !== repoUrl) {
|
||||
t6 = <Box marginBottom={1}><Text>Please install the app for repository: <Text bold={true}>{repoUrl}</Text></Text></Box>;
|
||||
$[5] = repoUrl;
|
||||
$[6] = t6;
|
||||
} else {
|
||||
t6 = $[6];
|
||||
}
|
||||
let t7;
|
||||
if ($[7] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t7 = <Box marginBottom={1}><Text dimColor={true}>Important: Make sure to grant access to this specific repository</Text></Box>;
|
||||
$[7] = t7;
|
||||
} else {
|
||||
t7 = $[7];
|
||||
}
|
||||
let t8;
|
||||
if ($[8] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t8 = <Box><Text bold={true} color="permission">Press Enter once you've installed the app{figures.ellipsis}</Text></Box>;
|
||||
$[8] = t8;
|
||||
} else {
|
||||
t8 = $[8];
|
||||
}
|
||||
let t9;
|
||||
if ($[9] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t9 = <Box marginTop={1}><Text dimColor={true}>Having trouble? See manual setup instructions at:{" "}<Text color="claude">{GITHUB_ACTION_SETUP_DOCS_URL}</Text></Text></Box>;
|
||||
$[9] = t9;
|
||||
} else {
|
||||
t9 = $[9];
|
||||
}
|
||||
let t10;
|
||||
if ($[10] !== t6) {
|
||||
t10 = <Box flexDirection="column" borderStyle="round" borderDimColor={true} paddingX={1}>{t2}{t3}{t4}{t5}{t6}{t7}{t8}{t9}</Box>;
|
||||
$[10] = t6;
|
||||
$[11] = t10;
|
||||
} else {
|
||||
t10 = $[11];
|
||||
}
|
||||
return t10;
|
||||
|
||||
export function InstallAppStep({ repoUrl, onSubmit }: InstallAppStepProps) {
|
||||
// Enter to submit
|
||||
useKeybinding('confirm:yes', onSubmit, { context: 'Confirmation' })
|
||||
|
||||
return (
|
||||
<Box flexDirection="column" borderStyle="round" borderDimColor paddingX={1}>
|
||||
<Box flexDirection="column" marginBottom={1}>
|
||||
<Text bold>Install the Claude GitHub App</Text>
|
||||
</Box>
|
||||
<Box marginBottom={1}>
|
||||
<Text>Opening browser to install the Claude GitHub App…</Text>
|
||||
</Box>
|
||||
<Box marginBottom={1}>
|
||||
<Text>If your browser doesn't open automatically, visit:</Text>
|
||||
</Box>
|
||||
<Box marginBottom={1}>
|
||||
<Text underline>https://github.com/apps/claude</Text>
|
||||
</Box>
|
||||
<Box marginBottom={1}>
|
||||
<Text>
|
||||
Please install the app for repository: <Text bold>{repoUrl}</Text>
|
||||
</Text>
|
||||
</Box>
|
||||
<Box marginBottom={1}>
|
||||
<Text dimColor>
|
||||
Important: Make sure to grant access to this specific repository
|
||||
</Text>
|
||||
</Box>
|
||||
<Box>
|
||||
<Text bold color="permission">
|
||||
Press Enter once you've installed the app{figures.ellipsis}
|
||||
</Text>
|
||||
</Box>
|
||||
<Box marginTop={1}>
|
||||
<Text dimColor>
|
||||
Having trouble? See manual setup instructions at:{' '}
|
||||
<Text color="claude">{GITHUB_ACTION_SETUP_DOCS_URL}</Text>
|
||||
</Text>
|
||||
</Box>
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,275 +1,343 @@
|
||||
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import { type AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, logEvent } from 'src/services/analytics/index.js';
|
||||
import { KeyboardShortcutHint } from '../../components/design-system/KeyboardShortcutHint.js';
|
||||
import { Spinner } from '../../components/Spinner.js';
|
||||
import TextInput from '../../components/TextInput.js';
|
||||
import { useTerminalSize } from '../../hooks/useTerminalSize.js';
|
||||
import type { KeyboardEvent } from '../../ink/events/keyboard-event.js';
|
||||
import { setClipboard } from '../../ink/termio/osc.js';
|
||||
import { Box, Link, Text } from '../../ink.js';
|
||||
import { OAuthService } from '../../services/oauth/index.js';
|
||||
import { saveOAuthTokensIfNeeded } from '../../utils/auth.js';
|
||||
import { logError } from '../../utils/log.js';
|
||||
import React, { useCallback, useEffect, useRef, useState } from 'react'
|
||||
import {
|
||||
type AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
|
||||
logEvent,
|
||||
} from 'src/services/analytics/index.js'
|
||||
import { KeyboardShortcutHint } from '../../components/design-system/KeyboardShortcutHint.js'
|
||||
import { Spinner } from '../../components/Spinner.js'
|
||||
import TextInput from '../../components/TextInput.js'
|
||||
import { useTerminalSize } from '../../hooks/useTerminalSize.js'
|
||||
import type { KeyboardEvent } from '../../ink/events/keyboard-event.js'
|
||||
import { setClipboard } from '../../ink/termio/osc.js'
|
||||
import { Box, Link, Text } from '../../ink.js'
|
||||
import { OAuthService } from '../../services/oauth/index.js'
|
||||
import { saveOAuthTokensIfNeeded } from '../../utils/auth.js'
|
||||
import { logError } from '../../utils/log.js'
|
||||
|
||||
interface OAuthFlowStepProps {
|
||||
onSuccess: (token: string) => void;
|
||||
onCancel: () => void;
|
||||
onSuccess: (token: string) => void
|
||||
onCancel: () => void
|
||||
}
|
||||
type OAuthStatus = {
|
||||
state: 'starting';
|
||||
} | {
|
||||
state: 'waiting_for_login';
|
||||
url: string;
|
||||
} | {
|
||||
state: 'processing';
|
||||
} | {
|
||||
state: 'success';
|
||||
token: string;
|
||||
} | {
|
||||
state: 'error';
|
||||
message: string;
|
||||
toRetry?: OAuthStatus;
|
||||
} | {
|
||||
state: 'about_to_retry';
|
||||
nextState: OAuthStatus;
|
||||
};
|
||||
const PASTE_HERE_MSG = 'Paste code here if prompted > ';
|
||||
|
||||
type OAuthStatus =
|
||||
| { state: 'starting' }
|
||||
| { state: 'waiting_for_login'; url: string }
|
||||
| { state: 'processing' }
|
||||
| { state: 'success'; token: string }
|
||||
| { state: 'error'; message: string; toRetry?: OAuthStatus }
|
||||
| { state: 'about_to_retry'; nextState: OAuthStatus }
|
||||
|
||||
const PASTE_HERE_MSG = 'Paste code here if prompted > '
|
||||
|
||||
export function OAuthFlowStep({
|
||||
onSuccess,
|
||||
onCancel
|
||||
onCancel,
|
||||
}: OAuthFlowStepProps): React.ReactNode {
|
||||
const [oauthStatus, setOAuthStatus] = useState<OAuthStatus>({
|
||||
state: 'starting'
|
||||
});
|
||||
const [oauthService] = useState(() => new OAuthService());
|
||||
const [pastedCode, setPastedCode] = useState('');
|
||||
const [cursorOffset, setCursorOffset] = useState(0);
|
||||
const [showPastePrompt, setShowPastePrompt] = useState(false);
|
||||
const [urlCopied, setUrlCopied] = useState(false);
|
||||
const timersRef = useRef<Set<NodeJS.Timeout>>(new Set());
|
||||
state: 'starting',
|
||||
})
|
||||
const [oauthService] = useState(() => new OAuthService())
|
||||
const [pastedCode, setPastedCode] = useState('')
|
||||
const [cursorOffset, setCursorOffset] = useState(0)
|
||||
const [showPastePrompt, setShowPastePrompt] = useState(false)
|
||||
const [urlCopied, setUrlCopied] = useState(false)
|
||||
const timersRef = useRef<Set<NodeJS.Timeout>>(new Set())
|
||||
// Separate ref so startOAuth's timer clear doesn't cancel the urlCopied reset
|
||||
const urlCopiedTimerRef = useRef<NodeJS.Timeout | undefined>(undefined);
|
||||
const terminalSize = useTerminalSize();
|
||||
const textInputColumns = Math.max(50, terminalSize.columns - PASTE_HERE_MSG.length - 4);
|
||||
const urlCopiedTimerRef = useRef<NodeJS.Timeout | undefined>(undefined)
|
||||
|
||||
const terminalSize = useTerminalSize()
|
||||
const textInputColumns = Math.max(
|
||||
50,
|
||||
terminalSize.columns - PASTE_HERE_MSG.length - 4,
|
||||
)
|
||||
|
||||
function handleKeyDown(e: KeyboardEvent): void {
|
||||
if (oauthStatus.state !== 'error') return;
|
||||
e.preventDefault();
|
||||
if (oauthStatus.state !== 'error') return
|
||||
e.preventDefault()
|
||||
if (e.key === 'return' && oauthStatus.toRetry) {
|
||||
setPastedCode('');
|
||||
setCursorOffset(0);
|
||||
setPastedCode('')
|
||||
setCursorOffset(0)
|
||||
setOAuthStatus({
|
||||
state: 'about_to_retry',
|
||||
nextState: oauthStatus.toRetry
|
||||
});
|
||||
nextState: oauthStatus.toRetry,
|
||||
})
|
||||
} else {
|
||||
onCancel();
|
||||
onCancel()
|
||||
}
|
||||
}
|
||||
|
||||
async function handleSubmitCode(value: string, url: string) {
|
||||
try {
|
||||
// Expecting format "authorizationCode#state" from the authorization callback URL
|
||||
const [authorizationCode, state] = value.split('#');
|
||||
const [authorizationCode, state] = value.split('#')
|
||||
|
||||
if (!authorizationCode || !state) {
|
||||
setOAuthStatus({
|
||||
state: 'error',
|
||||
message: 'Invalid code. Please make sure the full code was copied',
|
||||
toRetry: {
|
||||
state: 'waiting_for_login',
|
||||
url
|
||||
}
|
||||
});
|
||||
return;
|
||||
toRetry: { state: 'waiting_for_login', url },
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// Track which path the user is taking (manual code entry)
|
||||
logEvent('tengu_oauth_manual_entry', {});
|
||||
logEvent('tengu_oauth_manual_entry', {})
|
||||
oauthService.handleManualAuthCodeInput({
|
||||
authorizationCode,
|
||||
state
|
||||
});
|
||||
state,
|
||||
})
|
||||
} catch (err: unknown) {
|
||||
logError(err);
|
||||
logError(err)
|
||||
setOAuthStatus({
|
||||
state: 'error',
|
||||
message: (err as Error).message,
|
||||
toRetry: {
|
||||
state: 'waiting_for_login',
|
||||
url
|
||||
}
|
||||
});
|
||||
toRetry: { state: 'waiting_for_login', url },
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const startOAuth = useCallback(async () => {
|
||||
// Clear any existing timers when starting new OAuth flow
|
||||
timersRef.current.forEach(timer => clearTimeout(timer));
|
||||
timersRef.current.clear();
|
||||
timersRef.current.forEach(timer => clearTimeout(timer))
|
||||
timersRef.current.clear()
|
||||
|
||||
try {
|
||||
const result = await oauthService.startOAuthFlow(async url_0 => {
|
||||
setOAuthStatus({
|
||||
state: 'waiting_for_login',
|
||||
url: url_0
|
||||
});
|
||||
const timer_0 = setTimeout(setShowPastePrompt, 3000, true);
|
||||
timersRef.current.add(timer_0);
|
||||
}, {
|
||||
loginWithClaudeAi: true,
|
||||
// Always use Claude AI for subscription tokens
|
||||
inferenceOnly: true,
|
||||
expiresIn: 365 * 24 * 60 * 60 // 1 year
|
||||
});
|
||||
const result = await oauthService.startOAuthFlow(
|
||||
async url => {
|
||||
setOAuthStatus({ state: 'waiting_for_login', url })
|
||||
const timer = setTimeout(setShowPastePrompt, 3000, true)
|
||||
timersRef.current.add(timer)
|
||||
},
|
||||
{
|
||||
loginWithClaudeAi: true, // Always use Claude AI for subscription tokens
|
||||
inferenceOnly: true,
|
||||
expiresIn: 365 * 24 * 60 * 60, // 1 year
|
||||
},
|
||||
)
|
||||
|
||||
// Show processing state
|
||||
setOAuthStatus({
|
||||
state: 'processing'
|
||||
});
|
||||
setOAuthStatus({ state: 'processing' })
|
||||
|
||||
// OAuthFlowStep creates inference-only tokens for GitHub Actions, not a
|
||||
// replacement login. Use saveOAuthTokensIfNeeded directly to avoid
|
||||
// performLogout which would destroy the user's existing auth session.
|
||||
saveOAuthTokensIfNeeded(result);
|
||||
saveOAuthTokensIfNeeded(result)
|
||||
|
||||
// For OAuth flow, the access token can be used as an API key
|
||||
const timer1 = setTimeout((setOAuthStatus_0, accessToken, onSuccess_0, timersRef_0) => {
|
||||
setOAuthStatus_0({
|
||||
state: 'success',
|
||||
token: accessToken
|
||||
});
|
||||
// Auto-continue after brief delay to show success
|
||||
const timer2 = setTimeout(onSuccess_0, 1000, accessToken);
|
||||
timersRef_0.current.add(timer2 as ReturnType<typeof setTimeout>);
|
||||
}, 100, setOAuthStatus, result.accessToken, onSuccess, timersRef);
|
||||
timersRef.current.add(timer1);
|
||||
} catch (err_0) {
|
||||
const errorMessage = (err_0 as Error).message;
|
||||
const timer1 = setTimeout(
|
||||
(setOAuthStatus, accessToken, onSuccess, timersRef) => {
|
||||
setOAuthStatus({ state: 'success', token: accessToken })
|
||||
// Auto-continue after brief delay to show success
|
||||
const timer2 = setTimeout(onSuccess, 1000, accessToken)
|
||||
timersRef.current.add(timer2)
|
||||
},
|
||||
100,
|
||||
setOAuthStatus,
|
||||
result.accessToken,
|
||||
onSuccess,
|
||||
timersRef,
|
||||
)
|
||||
timersRef.current.add(timer1)
|
||||
} catch (err) {
|
||||
const errorMessage = (err as Error).message
|
||||
setOAuthStatus({
|
||||
state: 'error',
|
||||
message: errorMessage,
|
||||
toRetry: {
|
||||
state: 'starting'
|
||||
} // Allow retry by starting fresh OAuth flow
|
||||
});
|
||||
logError(err_0);
|
||||
toRetry: { state: 'starting' }, // Allow retry by starting fresh OAuth flow
|
||||
})
|
||||
logError(err)
|
||||
logEvent('tengu_oauth_error', {
|
||||
error: errorMessage as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS
|
||||
});
|
||||
error:
|
||||
errorMessage as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
|
||||
})
|
||||
}
|
||||
}, [oauthService, onSuccess]);
|
||||
}, [oauthService, onSuccess])
|
||||
|
||||
useEffect(() => {
|
||||
if (oauthStatus.state === 'starting') {
|
||||
void startOAuth();
|
||||
void startOAuth()
|
||||
}
|
||||
}, [oauthStatus.state, startOAuth]);
|
||||
}, [oauthStatus.state, startOAuth])
|
||||
|
||||
// Retry logic
|
||||
useEffect(() => {
|
||||
if (oauthStatus.state === 'about_to_retry') {
|
||||
const timer_1 = setTimeout((nextState, setShowPastePrompt_0, setOAuthStatus_1) => {
|
||||
// Only show paste prompt when retrying to waiting_for_login
|
||||
setShowPastePrompt_0(nextState.state === 'waiting_for_login');
|
||||
setOAuthStatus_1(nextState);
|
||||
}, 500, oauthStatus.nextState, setShowPastePrompt, setOAuthStatus);
|
||||
timersRef.current.add(timer_1);
|
||||
const timer = setTimeout(
|
||||
(nextState, setShowPastePrompt, setOAuthStatus) => {
|
||||
// Only show paste prompt when retrying to waiting_for_login
|
||||
setShowPastePrompt(nextState.state === 'waiting_for_login')
|
||||
setOAuthStatus(nextState)
|
||||
},
|
||||
500,
|
||||
oauthStatus.nextState,
|
||||
setShowPastePrompt,
|
||||
setOAuthStatus,
|
||||
)
|
||||
timersRef.current.add(timer)
|
||||
}
|
||||
}, [oauthStatus]);
|
||||
}, [oauthStatus])
|
||||
|
||||
useEffect(() => {
|
||||
if (pastedCode === 'c' && oauthStatus.state === 'waiting_for_login' && showPastePrompt && !urlCopied) {
|
||||
if (
|
||||
pastedCode === 'c' &&
|
||||
oauthStatus.state === 'waiting_for_login' &&
|
||||
showPastePrompt &&
|
||||
!urlCopied
|
||||
) {
|
||||
void setClipboard(oauthStatus.url).then(raw => {
|
||||
if (raw) process.stdout.write(raw);
|
||||
setUrlCopied(true);
|
||||
clearTimeout(urlCopiedTimerRef.current);
|
||||
urlCopiedTimerRef.current = setTimeout(setUrlCopied, 2000, false);
|
||||
});
|
||||
setPastedCode('');
|
||||
if (raw) process.stdout.write(raw)
|
||||
setUrlCopied(true)
|
||||
clearTimeout(urlCopiedTimerRef.current)
|
||||
urlCopiedTimerRef.current = setTimeout(setUrlCopied, 2000, false)
|
||||
})
|
||||
setPastedCode('')
|
||||
}
|
||||
}, [pastedCode, oauthStatus, showPastePrompt, urlCopied]);
|
||||
}, [pastedCode, oauthStatus, showPastePrompt, urlCopied])
|
||||
|
||||
// Cleanup OAuth service and timers when component unmounts
|
||||
useEffect(() => {
|
||||
const timers = timersRef.current;
|
||||
const timers = timersRef.current
|
||||
return () => {
|
||||
oauthService.cleanup();
|
||||
oauthService.cleanup()
|
||||
// Clear all timers
|
||||
timers.forEach(timer_2 => clearTimeout(timer_2));
|
||||
timers.clear();
|
||||
clearTimeout(urlCopiedTimerRef.current);
|
||||
};
|
||||
}, [oauthService]);
|
||||
timers.forEach(timer => clearTimeout(timer))
|
||||
timers.clear()
|
||||
clearTimeout(urlCopiedTimerRef.current)
|
||||
}
|
||||
}, [oauthService])
|
||||
|
||||
// Helper function to render the appropriate status message
|
||||
function renderStatusMessage(): React.ReactNode {
|
||||
switch (oauthStatus.state) {
|
||||
case 'starting':
|
||||
return <Box>
|
||||
return (
|
||||
<Box>
|
||||
<Spinner />
|
||||
<Text>Starting authentication…</Text>
|
||||
</Box>;
|
||||
</Box>
|
||||
)
|
||||
|
||||
case 'waiting_for_login':
|
||||
return <Box flexDirection="column" gap={1}>
|
||||
{!showPastePrompt && <Box>
|
||||
return (
|
||||
<Box flexDirection="column" gap={1}>
|
||||
{!showPastePrompt && (
|
||||
<Box>
|
||||
<Spinner />
|
||||
<Text>
|
||||
Opening browser to sign in with your Claude account…
|
||||
</Text>
|
||||
</Box>}
|
||||
</Box>
|
||||
)}
|
||||
|
||||
{showPastePrompt && <Box>
|
||||
{showPastePrompt && (
|
||||
<Box>
|
||||
<Text>{PASTE_HERE_MSG}</Text>
|
||||
<TextInput value={pastedCode} onChange={setPastedCode} onSubmit={(value_0: string) => handleSubmitCode(value_0, oauthStatus.url)} cursorOffset={cursorOffset} onChangeCursorOffset={setCursorOffset} columns={textInputColumns} />
|
||||
</Box>}
|
||||
</Box>;
|
||||
<TextInput
|
||||
value={pastedCode}
|
||||
onChange={setPastedCode}
|
||||
onSubmit={(value: string) =>
|
||||
handleSubmitCode(value, oauthStatus.url)
|
||||
}
|
||||
cursorOffset={cursorOffset}
|
||||
onChangeCursorOffset={setCursorOffset}
|
||||
columns={textInputColumns}
|
||||
/>
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
)
|
||||
|
||||
case 'processing':
|
||||
return <Box>
|
||||
return (
|
||||
<Box>
|
||||
<Spinner />
|
||||
<Text>Processing authentication…</Text>
|
||||
</Box>;
|
||||
</Box>
|
||||
)
|
||||
|
||||
case 'success':
|
||||
return <Box flexDirection="column" gap={1}>
|
||||
return (
|
||||
<Box flexDirection="column" gap={1}>
|
||||
<Text color="success">
|
||||
✓ Authentication token created successfully!
|
||||
</Text>
|
||||
<Text dimColor>Using token for GitHub Actions setup…</Text>
|
||||
</Box>;
|
||||
</Box>
|
||||
)
|
||||
|
||||
case 'error':
|
||||
return <Box flexDirection="column" gap={1}>
|
||||
return (
|
||||
<Box flexDirection="column" gap={1}>
|
||||
<Text color="error">OAuth error: {oauthStatus.message}</Text>
|
||||
{oauthStatus.toRetry ? <Text dimColor>
|
||||
{oauthStatus.toRetry ? (
|
||||
<Text dimColor>
|
||||
Press Enter to try again, or any other key to cancel
|
||||
</Text> : <Text dimColor>Press any key to return to API key selection</Text>}
|
||||
</Box>;
|
||||
</Text>
|
||||
) : (
|
||||
<Text dimColor>Press any key to return to API key selection</Text>
|
||||
)}
|
||||
</Box>
|
||||
)
|
||||
|
||||
case 'about_to_retry':
|
||||
return <Box flexDirection="column" gap={1}>
|
||||
return (
|
||||
<Box flexDirection="column" gap={1}>
|
||||
<Text color="permission">Retrying…</Text>
|
||||
</Box>;
|
||||
</Box>
|
||||
)
|
||||
|
||||
default:
|
||||
return null;
|
||||
return null
|
||||
}
|
||||
}
|
||||
return <Box flexDirection="column" gap={1} tabIndex={0} autoFocus onKeyDown={handleKeyDown}>
|
||||
|
||||
return (
|
||||
<Box
|
||||
flexDirection="column"
|
||||
gap={1}
|
||||
tabIndex={0}
|
||||
autoFocus
|
||||
onKeyDown={handleKeyDown}
|
||||
>
|
||||
{/* Show header inline only for initial starting state */}
|
||||
{oauthStatus.state === 'starting' && <Box flexDirection="column" gap={1} paddingBottom={1}>
|
||||
{oauthStatus.state === 'starting' && (
|
||||
<Box flexDirection="column" gap={1} paddingBottom={1}>
|
||||
<Text bold>Create Authentication Token</Text>
|
||||
<Text dimColor>Creating a long-lived token for GitHub Actions</Text>
|
||||
</Box>}
|
||||
</Box>
|
||||
)}
|
||||
{/* Show header for non-starting states (to avoid duplicate with inline header)*/}
|
||||
{oauthStatus.state !== 'success' && oauthStatus.state !== 'starting' && oauthStatus.state !== 'processing' && <Box key="header" flexDirection="column" gap={1} paddingBottom={1}>
|
||||
{oauthStatus.state !== 'success' &&
|
||||
oauthStatus.state !== 'starting' &&
|
||||
oauthStatus.state !== 'processing' && (
|
||||
<Box key="header" flexDirection="column" gap={1} paddingBottom={1}>
|
||||
<Text bold>Create Authentication Token</Text>
|
||||
<Text dimColor>Creating a long-lived token for GitHub Actions</Text>
|
||||
</Box>}
|
||||
</Box>
|
||||
)}
|
||||
{/* Show URL when paste prompt is visible */}
|
||||
{oauthStatus.state === 'waiting_for_login' && showPastePrompt && <Box flexDirection="column" key="urlToCopy" gap={1} paddingBottom={1}>
|
||||
{oauthStatus.state === 'waiting_for_login' && showPastePrompt && (
|
||||
<Box flexDirection="column" key="urlToCopy" gap={1} paddingBottom={1}>
|
||||
<Box paddingX={1}>
|
||||
<Text dimColor>
|
||||
Browser didn't open? Use the url below to sign in{' '}
|
||||
</Text>
|
||||
{urlCopied ? <Text color="success">(Copied!)</Text> : <Text dimColor>
|
||||
{urlCopied ? (
|
||||
<Text color="success">(Copied!)</Text>
|
||||
) : (
|
||||
<Text dimColor>
|
||||
<KeyboardShortcutHint shortcut="c" action="copy" parens />
|
||||
</Text>}
|
||||
</Text>
|
||||
)}
|
||||
</Box>
|
||||
<Link url={oauthStatus.url}>
|
||||
<Text dimColor>{oauthStatus.url}</Text>
|
||||
</Link>
|
||||
</Box>}
|
||||
</Box>
|
||||
)}
|
||||
<Box paddingLeft={1} flexDirection="column" gap={1}>
|
||||
{renderStatusMessage()}
|
||||
</Box>
|
||||
</Box>;
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,95 +1,65 @@
|
||||
import { c as _c } from "react/compiler-runtime";
|
||||
import React from 'react';
|
||||
import { Box, Text } from '../../ink.js';
|
||||
import React from 'react'
|
||||
import { Box, Text } from '../../ink.js'
|
||||
|
||||
type SuccessStepProps = {
|
||||
secretExists: boolean;
|
||||
useExistingSecret: boolean;
|
||||
secretName: string;
|
||||
skipWorkflow?: boolean;
|
||||
};
|
||||
export function SuccessStep(t0) {
|
||||
const $ = _c(21);
|
||||
const {
|
||||
secretExists,
|
||||
useExistingSecret,
|
||||
secretName,
|
||||
skipWorkflow: t1
|
||||
} = t0;
|
||||
const skipWorkflow = t1 === undefined ? false : t1;
|
||||
let t2;
|
||||
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t2 = <Box flexDirection="column" marginBottom={1}><Text bold={true}>Install GitHub App</Text><Text dimColor={true}>Success</Text></Box>;
|
||||
$[0] = t2;
|
||||
} else {
|
||||
t2 = $[0];
|
||||
}
|
||||
let t3;
|
||||
if ($[1] !== skipWorkflow) {
|
||||
t3 = !skipWorkflow && <Text color="success">✓ GitHub Actions workflow created!</Text>;
|
||||
$[1] = skipWorkflow;
|
||||
$[2] = t3;
|
||||
} else {
|
||||
t3 = $[2];
|
||||
}
|
||||
let t4;
|
||||
if ($[3] !== secretExists || $[4] !== useExistingSecret) {
|
||||
t4 = secretExists && useExistingSecret && <Box marginTop={1}><Text color="success">✓ Using existing ANTHROPIC_API_KEY secret</Text></Box>;
|
||||
$[3] = secretExists;
|
||||
$[4] = useExistingSecret;
|
||||
$[5] = t4;
|
||||
} else {
|
||||
t4 = $[5];
|
||||
}
|
||||
let t5;
|
||||
if ($[6] !== secretExists || $[7] !== secretName || $[8] !== useExistingSecret) {
|
||||
t5 = (!secretExists || !useExistingSecret) && <Box marginTop={1}><Text color="success">✓ API key saved as {secretName} secret</Text></Box>;
|
||||
$[6] = secretExists;
|
||||
$[7] = secretName;
|
||||
$[8] = useExistingSecret;
|
||||
$[9] = t5;
|
||||
} else {
|
||||
t5 = $[9];
|
||||
}
|
||||
let t6;
|
||||
if ($[10] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t6 = <Box marginTop={1}><Text>Next steps:</Text></Box>;
|
||||
$[10] = t6;
|
||||
} else {
|
||||
t6 = $[10];
|
||||
}
|
||||
let t7;
|
||||
if ($[11] !== skipWorkflow) {
|
||||
t7 = skipWorkflow ? <><Text>1. Install the Claude GitHub App if you haven't already</Text><Text>2. Your workflow file was kept unchanged</Text><Text>3. API key is configured and ready to use</Text></> : <><Text>1. A pre-filled PR page has been created</Text><Text>2. Install the Claude GitHub App if you haven't already</Text><Text>3. Merge the PR to enable Claude PR assistance</Text></>;
|
||||
$[11] = skipWorkflow;
|
||||
$[12] = t7;
|
||||
} else {
|
||||
t7 = $[12];
|
||||
}
|
||||
let t8;
|
||||
if ($[13] !== t3 || $[14] !== t4 || $[15] !== t5 || $[16] !== t7) {
|
||||
t8 = <Box flexDirection="column" borderStyle="round" paddingX={1}>{t2}{t3}{t4}{t5}{t6}{t7}</Box>;
|
||||
$[13] = t3;
|
||||
$[14] = t4;
|
||||
$[15] = t5;
|
||||
$[16] = t7;
|
||||
$[17] = t8;
|
||||
} else {
|
||||
t8 = $[17];
|
||||
}
|
||||
let t9;
|
||||
if ($[18] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t9 = <Box marginLeft={3}><Text dimColor={true}>Press any key to exit</Text></Box>;
|
||||
$[18] = t9;
|
||||
} else {
|
||||
t9 = $[18];
|
||||
}
|
||||
let t10;
|
||||
if ($[19] !== t8) {
|
||||
t10 = <>{t8}{t9}</>;
|
||||
$[19] = t8;
|
||||
$[20] = t10;
|
||||
} else {
|
||||
t10 = $[20];
|
||||
}
|
||||
return t10;
|
||||
secretExists: boolean
|
||||
useExistingSecret: boolean
|
||||
secretName: string
|
||||
skipWorkflow?: boolean
|
||||
}
|
||||
|
||||
export function SuccessStep({
|
||||
secretExists,
|
||||
useExistingSecret,
|
||||
secretName,
|
||||
skipWorkflow = false,
|
||||
}: SuccessStepProps): React.ReactNode {
|
||||
return (
|
||||
<>
|
||||
<Box flexDirection="column" borderStyle="round" paddingX={1}>
|
||||
<Box flexDirection="column" marginBottom={1}>
|
||||
<Text bold>Install GitHub App</Text>
|
||||
<Text dimColor>Success</Text>
|
||||
</Box>
|
||||
{!skipWorkflow && (
|
||||
<Text color="success">✓ GitHub Actions workflow created!</Text>
|
||||
)}
|
||||
{secretExists && useExistingSecret && (
|
||||
<Box marginTop={1}>
|
||||
<Text color="success">
|
||||
✓ Using existing ANTHROPIC_API_KEY secret
|
||||
</Text>
|
||||
</Box>
|
||||
)}
|
||||
{(!secretExists || !useExistingSecret) && (
|
||||
<Box marginTop={1}>
|
||||
<Text color="success">✓ API key saved as {secretName} secret</Text>
|
||||
</Box>
|
||||
)}
|
||||
<Box marginTop={1}>
|
||||
<Text>Next steps:</Text>
|
||||
</Box>
|
||||
{skipWorkflow ? (
|
||||
<>
|
||||
<Text>
|
||||
1. Install the Claude GitHub App if you haven't already
|
||||
</Text>
|
||||
<Text>2. Your workflow file was kept unchanged</Text>
|
||||
<Text>3. API key is configured and ready to use</Text>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Text>1. A pre-filled PR page has been created</Text>
|
||||
<Text>
|
||||
2. Install the Claude GitHub App if you haven't already
|
||||
</Text>
|
||||
<Text>3. Merge the PR to enable Claude PR assistance</Text>
|
||||
</>
|
||||
)}
|
||||
</Box>
|
||||
<Box marginLeft={3}>
|
||||
<Text dimColor>Press any key to exit</Text>
|
||||
</Box>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,72 +1,59 @@
|
||||
import { c as _c } from "react/compiler-runtime";
|
||||
import figures from 'figures';
|
||||
import React from 'react';
|
||||
import { GITHUB_ACTION_SETUP_DOCS_URL } from '../../constants/github-app.js';
|
||||
import { Box, Text } from '../../ink.js';
|
||||
import { useKeybinding } from '../../keybindings/useKeybinding.js';
|
||||
import type { Warning } from './types.js';
|
||||
import figures from 'figures'
|
||||
import React from 'react'
|
||||
import { GITHUB_ACTION_SETUP_DOCS_URL } from '../../constants/github-app.js'
|
||||
import { Box, Text } from '../../ink.js'
|
||||
import { useKeybinding } from '../../keybindings/useKeybinding.js'
|
||||
import type { Warning } from './types.js'
|
||||
|
||||
interface WarningsStepProps {
|
||||
warnings: Warning[];
|
||||
onContinue: () => void;
|
||||
warnings: Warning[]
|
||||
onContinue: () => void
|
||||
}
|
||||
export function WarningsStep(t0) {
|
||||
const $ = _c(8);
|
||||
const {
|
||||
warnings,
|
||||
onContinue
|
||||
} = t0;
|
||||
let t1;
|
||||
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t1 = {
|
||||
context: "Confirmation"
|
||||
};
|
||||
$[0] = t1;
|
||||
} else {
|
||||
t1 = $[0];
|
||||
}
|
||||
useKeybinding("confirm:yes", onContinue, t1);
|
||||
let t2;
|
||||
if ($[1] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t2 = <Box flexDirection="column" marginBottom={1}><Text bold={true}>{figures.warning} Setup Warnings</Text><Text dimColor={true}>We found some potential issues, but you can continue anyway</Text></Box>;
|
||||
$[1] = t2;
|
||||
} else {
|
||||
t2 = $[1];
|
||||
}
|
||||
let t3;
|
||||
if ($[2] !== warnings) {
|
||||
t3 = warnings.map(_temp2);
|
||||
$[2] = warnings;
|
||||
$[3] = t3;
|
||||
} else {
|
||||
t3 = $[3];
|
||||
}
|
||||
let t4;
|
||||
if ($[4] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t4 = <Box marginTop={1}><Text bold={true} color="permission">Press Enter to continue anyway, or Ctrl+C to exit and fix issues</Text></Box>;
|
||||
$[4] = t4;
|
||||
} else {
|
||||
t4 = $[4];
|
||||
}
|
||||
let t5;
|
||||
if ($[5] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t5 = <Box marginTop={1}><Text dimColor={true}>You can also try the manual setup steps if needed:{" "}<Text color="claude">{GITHUB_ACTION_SETUP_DOCS_URL}</Text></Text></Box>;
|
||||
$[5] = t5;
|
||||
} else {
|
||||
t5 = $[5];
|
||||
}
|
||||
let t6;
|
||||
if ($[6] !== t3) {
|
||||
t6 = <><Box flexDirection="column" borderStyle="round" paddingX={1}>{t2}{t3}{t4}{t5}</Box></>;
|
||||
$[6] = t3;
|
||||
$[7] = t6;
|
||||
} else {
|
||||
t6 = $[7];
|
||||
}
|
||||
return t6;
|
||||
}
|
||||
function _temp2(warning, index) {
|
||||
return <Box key={index} flexDirection="column" marginBottom={1}><Text color="warning" bold={true}>{warning.title}</Text><Text>{warning.message}</Text>{warning.instructions.length > 0 && <Box flexDirection="column" marginLeft={2} marginTop={1}>{warning.instructions.map(_temp)}</Box>}</Box>;
|
||||
}
|
||||
function _temp(instruction, i) {
|
||||
return <Text key={i} dimColor={true}>• {instruction}</Text>;
|
||||
|
||||
export function WarningsStep({ warnings, onContinue }: WarningsStepProps) {
|
||||
// Enter to continue
|
||||
useKeybinding('confirm:yes', onContinue, { context: 'Confirmation' })
|
||||
|
||||
return (
|
||||
<>
|
||||
<Box flexDirection="column" borderStyle="round" paddingX={1}>
|
||||
<Box flexDirection="column" marginBottom={1}>
|
||||
<Text bold>{figures.warning} Setup Warnings</Text>
|
||||
<Text dimColor>
|
||||
We found some potential issues, but you can continue anyway
|
||||
</Text>
|
||||
</Box>
|
||||
|
||||
{warnings.map((warning, index) => (
|
||||
<Box key={index} flexDirection="column" marginBottom={1}>
|
||||
<Text color="warning" bold>
|
||||
{warning.title}
|
||||
</Text>
|
||||
<Text>{warning.message}</Text>
|
||||
{warning.instructions.length > 0 && (
|
||||
<Box flexDirection="column" marginLeft={2} marginTop={1}>
|
||||
{warning.instructions.map((instruction, i) => (
|
||||
<Text key={i} dimColor>
|
||||
• {instruction}
|
||||
</Text>
|
||||
))}
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
))}
|
||||
|
||||
<Box marginTop={1}>
|
||||
<Text bold color="permission">
|
||||
Press Enter to continue anyway, or Ctrl+C to exit and fix issues
|
||||
</Text>
|
||||
</Box>
|
||||
<Box marginTop={1}>
|
||||
<Text dimColor>
|
||||
You can also try the manual setup steps if needed:{' '}
|
||||
<Text color="claude">{GITHUB_ACTION_SETUP_DOCS_URL}</Text>
|
||||
</Text>
|
||||
</Box>
|
||||
</Box>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user