From 34dc74c0ad60ec80e0620f7ac81cdfe9fffcc688 Mon Sep 17 00:00:00 2001 From: arc53-machine <232052973+arc53-machine@users.noreply.github.com> Date: Tue, 29 Sep 2026 11:16:13 +0100 Subject: [PATCH] Offer GitHub write tools in the connect wizard, drawer and admin page The GitHub tools step gets a second switch under Let agents use GitHub: Also let agents make changes (issues, comments, pull requests), off by default. A connection's page has the same switch on its GitHub tool, which re-reads the tool's actions from the other endpoint. Both are hidden when an admin turns changes off in the new Write access section of Admin > Connectors. The token hint and the GitHub App setup note name the permissions changes need. --- frontend/src/admin/Connectors.test.tsx | 38 ++++++++++ frontend/src/admin/Connectors.tsx | 56 +++++++++++++- frontend/src/api/endpoints.ts | 2 + .../src/api/services/connectorsService.ts | 11 +++ .../src/connectors/ConnectWizard.test.tsx | 43 +++++++++++ frontend/src/connectors/ConnectWizard.tsx | 30 +++++++- .../src/connectors/ConnectionDrawer.test.tsx | 55 ++++++++++++++ frontend/src/connectors/ConnectionDrawer.tsx | 73 +++++++++++++++++++ frontend/src/connectors/types.ts | 9 +++ frontend/src/locale/de.json | 6 +- frontend/src/locale/en.json | 6 +- frontend/src/locale/es.json | 6 +- frontend/src/locale/jp.json | 6 +- frontend/src/locale/ru.json | 6 +- frontend/src/locale/zh-TW.json | 6 +- frontend/src/locale/zh.json | 6 +- 16 files changed, 346 insertions(+), 13 deletions(-) diff --git a/frontend/src/admin/Connectors.test.tsx b/frontend/src/admin/Connectors.test.tsx index cf190634..0d4ac1eb 100644 --- a/frontend/src/admin/Connectors.test.tsx +++ b/frontend/src/admin/Connectors.test.tsx @@ -312,6 +312,44 @@ describe('Admin Connectors', () => { expect(mcp().getAttribute('aria-checked')).toBe('false'); }); + describe('write access', () => { + const GITHUB = connector({ + key: 'github', + name: 'GitHub', + icon: 'github', + auth_kind: 'api_key', + capabilities: ['sync', 'read'], + configured: true, + required_settings: [], + allow_writes: true, + }); + const writes = () => + container.querySelector('#allow-writes-github'); + + it('lets an admin forbid changes through GitHub', async () => { + getAdmin.mockResolvedValue(payload({ connectors: [GITHUB, MCP_ROW] })); + updateAdmin.mockResolvedValue( + payload({ + connectors: [{ ...GITHUB, allow_writes: false }, MCP_ROW], + }), + ); + await render(); + expect(writes()!.getAttribute('aria-checked')).toBe('true'); + await act(async () => writes()!.click()); + expect(updateAdmin).toHaveBeenCalledWith( + { policies: { github: { allow_writes: false } } }, + null, + ); + expect(writes()!.getAttribute('aria-checked')).toBe('false'); + }); + + it('has no write access section without such a connector', async () => { + getAdmin.mockResolvedValue(payload()); + await render(); + expect(container.textContent).not.toContain('Write access'); + }); + }); + it('offers a retry when loading fails', async () => { getAdmin.mockRejectedValue(new Error('offline')); await render(); diff --git a/frontend/src/admin/Connectors.tsx b/frontend/src/admin/Connectors.tsx index 36c27a52..ea629c6c 100644 --- a/frontend/src/admin/Connectors.tsx +++ b/frontend/src/admin/Connectors.tsx @@ -71,6 +71,11 @@ type AdminConnector = { connection_count: number; docs_url: string | null; mcp_url: string | null; + /** + * Whether members may let agents make changes through it (GitHub's write + * tools); null where the connector offers no such choice. + */ + allow_writes?: boolean | null; }; type AdminConnectorsData = { @@ -195,8 +200,10 @@ function SetupGuide({ In the GitHub App, give repository permissions Contents and Metadata read-only access, and turn on Request user authorization (OAuth) during installation so choosing repositories returns to - DocsGPT. GITHUB_APP_SLUG is the name in the app's public link - (github.com/apps/<slug>). + DocsGPT. For agents to make changes, also give Issues and Pull + requests read and write access (Contents read and write only if + agents should edit files). GITHUB_APP_SLUG is the name in the + app's public link (github.com/apps/<slug>). )} @@ -259,7 +266,10 @@ export default function Connectors() { // older one arriving last would otherwise put back a stale policy. const saveQueue = useRef>(Promise.resolve()); const save = (body: { - policies?: Record; + policies?: Record< + string, + { enabled?: boolean; credential_mode?: Policy; allow_writes?: boolean } + >; allow_custom_mcp?: boolean; }) => { const failed = () => @@ -287,6 +297,10 @@ export default function Connectors() { // The sheet (phones) always shows the connector's latest saved state. const detail = data.connectors.find((c) => c.key === detailKey) ?? null; + const writable = data.connectors.filter( + (connector) => + connector.allow_writes !== null && connector.allow_writes !== undefined, + ); // The custom MCP row is the one switch for members' own MCP servers. const isCustomMcp = (connector: AdminConnector) => @@ -504,6 +518,42 @@ export default function Connectors() { + {writable.length > 0 && ( +
+ + + {writable.map((connector) => ( + + + save({ + policies: { + [connector.key]: { allow_writes: checked === true }, + }, + }) + } + /> + + ))} + +
+ )} + {detail && ( !open && setDetailKey(null)}> diff --git a/frontend/src/api/endpoints.ts b/frontend/src/api/endpoints.ts index 4f83a7c6..c6a7aeed 100644 --- a/frontend/src/api/endpoints.ts +++ b/frontend/src/api/endpoints.ts @@ -125,6 +125,8 @@ const endpoints = { `/api/connections/${encodeURIComponent(id)}/repositories`, CONNECTION_TOOL_PARAMETERS: (id: string, toolId: string) => `/api/connections/${encodeURIComponent(id)}/tools/${encodeURIComponent(toolId)}/parameters`, + CONNECTION_WRITES: (id: string) => + `/api/connections/${encodeURIComponent(id)}/writes`, CONNECTIONS_CLAIM: '/api/connections/claim', TOOL_CREDENTIAL_MODE: (toolId: string) => `/api/connections/tools/${encodeURIComponent(toolId)}/credential-mode`, diff --git a/frontend/src/api/services/connectorsService.ts b/frontend/src/api/services/connectorsService.ts index 2b7b2a62..8c3ddd11 100644 --- a/frontend/src/api/services/connectorsService.ts +++ b/frontend/src/api/services/connectorsService.ts @@ -13,6 +13,8 @@ const json = async (response: Response) => { export type ConnectionSetupBody = { create_tools?: boolean; + /** GitHub: let agents make changes, not only read. */ + allow_writes?: boolean; tool_permissions?: Record; sync?: { items: Record; @@ -101,6 +103,15 @@ const connectorsService = { token, ), ), + /** GitHub: let agents make changes through a connection, or only read. */ + setWrites: async (id: string, allow: boolean, token: string | null) => + json( + await apiClient.put( + endpoints.USER.CONNECTION_WRITES(id), + { allow }, + token, + ), + ), setToolPermissions: async ( id: string, toolId: string, diff --git a/frontend/src/connectors/ConnectWizard.test.tsx b/frontend/src/connectors/ConnectWizard.test.tsx index 8c4296df..d04aceca 100644 --- a/frontend/src/connectors/ConnectWizard.test.tsx +++ b/frontend/src/connectors/ConnectWizard.test.tsx @@ -609,6 +609,49 @@ describe('ConnectWizard', () => { }); }); + const writesSwitch = () => + document.body.querySelector('#tools-github-writes'); + + it('offers changes as a second choice, read only by default', async () => { + service.setup.mockResolvedValue({ + success: true, + tools: [], + sources: [], + }); + await render({ ...github, writes_opt_in: true, writes_allowed: true }); + await connectWithToken(); + expect(document.body.textContent).toContain( + 'settings.connectors.github.writes', + ); + expect(writesSwitch()!.getAttribute('aria-checked')).toBe('false'); + await act(async () => writesSwitch()!.click()); + await click('settings.connectors.wizard.continue'); + expect(service.setup.mock.calls[0][1]).toEqual({ + create_tools: true, + allow_writes: true, + }); + }); + + it('drops the choice with the tools', async () => { + await render({ ...github, writes_opt_in: true, writes_allowed: true }); + await connectWithToken(); + await act(async () => + document.body + .querySelector('#tools-github')! + .click(), + ); + expect(writesSwitch()).toBeNull(); + }); + + it('hides the choice when an admin turned changes off', async () => { + await render({ ...github, writes_opt_in: true, writes_allowed: false }); + await connectWithToken(); + expect(writesSwitch()).toBeNull(); + expect(document.body.textContent).not.toContain( + 'settings.connectors.github.writes', + ); + }); + it('shows why the tools could not be added', async () => { service.setup.mockResolvedValue({ success: false, diff --git a/frontend/src/connectors/ConnectWizard.tsx b/frontend/src/connectors/ConnectWizard.tsx index 04eb8e02..b8cd065e 100644 --- a/frontend/src/connectors/ConnectWizard.tsx +++ b/frontend/src/connectors/ConnectWizard.tsx @@ -66,7 +66,8 @@ const PICKER_CONNECTORS = new Set([ 'share_point', 'confluence', ]); -// Where a GitHub user makes a fine-grained token (Contents and Metadata: read). +// Where a GitHub user makes a fine-grained token (Contents and Metadata: read; +// Issues and Pull requests: read and write when agents may make changes). const GITHUB_TOKEN_URL = 'https://github.com/settings/personal-access-tokens/new'; @@ -119,6 +120,8 @@ export default function ConnectWizard({ const [selectedRepo, setSelectedRepo] = useState(null); // A connector that asks about its tools (GitHub) offers them switched on. const [toolsOn, setToolsOn] = useState(true); + // Changes (GitHub's issues, comments, pull requests) are an opt-in on top. + const [writesOn, setWritesOn] = useState(false); const [chosenMethod, setChosenMethod] = useState( null, ); @@ -319,6 +322,8 @@ export default function ConnectWizard({ ? !!selectedRepo : credentialsComplete(connector.setup_fields, setupValues); const wantsTools = offerTools && toolsOn; + const offerWrites = wantsTools && !!connector.writes_allowed; + const wantsWrites = offerWrites && writesOn; const canAddSource = hasSyncSelection || wantsTools; const addSource = async () => { @@ -330,6 +335,7 @@ export default function ConnectWizard({ connectionId, { create_tools: wantsTools, + ...(wantsWrites && { allow_writes: true }), ...(hasSyncSelection && { sync: { items: syncItems(), @@ -348,7 +354,9 @@ export default function ConnectWizard({ name, interpolation: { escapeValue: false }, }) - : data?.error || t('settings.connectors.wizard.syncFailed'), + : data?.code === 'writes_forbidden' + ? t('settings.connectors.github.writesForbidden') + : data?.error || t('settings.connectors.wizard.syncFailed'), ); return; } @@ -545,7 +553,9 @@ export default function ConnectWizard({ })} description={t( `settings.connectors.wizard.addToolsDescription.${ - connector.capabilities.includes('write') ? 'readWrite' : 'read' + connector.capabilities.includes('write') || wantsWrites + ? 'readWrite' + : 'read' }`, )} htmlFor={`tools-${connector.key}`} @@ -557,6 +567,20 @@ export default function ConnectWizard({ onCheckedChange={(checked) => setToolsOn(checked === true)} /> + {offerWrites && ( + + setWritesOn(checked === true)} + /> + + )} )} {isRepoPicker && connectionId ? ( diff --git a/frontend/src/connectors/ConnectionDrawer.test.tsx b/frontend/src/connectors/ConnectionDrawer.test.tsx index 7f7a126e..af6c6268 100644 --- a/frontend/src/connectors/ConnectionDrawer.test.tsx +++ b/frontend/src/connectors/ConnectionDrawer.test.tsx @@ -13,6 +13,7 @@ const connectors = vi.hoisted(() => ({ listConnections: vi.fn(async () => ({ success: true, connections: [] })), setup: vi.fn(), renameConnection: vi.fn(), + setWrites: vi.fn(), })); vi.mock('../api/services/connectorsService', () => ({ default: connectors })); @@ -295,6 +296,7 @@ describe('ConnectionDrawer', () => { beforeEach(() => { connectors.setup.mockReset(); + connectors.setWrites.mockReset(); connectors.getConnection.mockResolvedValue({ success: true, connection: TOKEN_DETAIL, @@ -311,6 +313,59 @@ describe('ConnectionDrawer', () => { ); }); + const GITHUB_TOOL = { + id: 'tool-gh', + name: 'mcp_tool', + display_name: 'GitHub', + status: true, + credential_mode: 'owner', + actions: [], + }; + const writesSwitch = () => + document.body.querySelector('#writes-conn-1'); + + it('switches a connection between reading and making changes', async () => { + connectors.getConnection.mockResolvedValue({ + success: true, + connection: { ...TOKEN_DETAIL, tools: [GITHUB_TOOL], writes: false }, + }); + connectors.setWrites.mockResolvedValue({ success: true, writes: true }); + await render({ ...GITHUB, writes_allowed: true }); + expect(writesSwitch()!.getAttribute('aria-checked')).toBe('false'); + const loads = connectors.getConnection.mock.calls.length; + await act(async () => writesSwitch()!.click()); + expect(connectors.setWrites).toHaveBeenCalledWith('conn-1', true, null); + // The page reloads the connection, with the new endpoint's actions. + expect(connectors.getConnection.mock.calls.length).toBeGreaterThan(loads); + }); + + it('says when switching failed', async () => { + connectors.getConnection.mockResolvedValue({ + success: true, + connection: { ...TOKEN_DETAIL, tools: [GITHUB_TOOL], writes: false }, + }); + connectors.setWrites.mockResolvedValue({ + success: false, + code: 'writes_forbidden', + }); + await render({ ...GITHUB, writes_allowed: true }); + await act(async () => writesSwitch()!.click()); + const toast = selectActionToast( + store.getState() as Parameters[0], + ); + expect(toast?.variant).toBe('destructive'); + expect(toast?.message).toBe('settings.connectors.github.writesForbidden'); + }); + + it('hides the switch when an admin turned changes off', async () => { + connectors.getConnection.mockResolvedValue({ + success: true, + connection: { ...TOKEN_DETAIL, tools: [GITHUB_TOOL], writes: false }, + }); + await render({ ...GITHUB, writes_allowed: false }); + expect(writesSwitch()).toBeNull(); + }); + it('adds the tools a connection skipped during setup', async () => { connectors.setup.mockResolvedValue({ success: true, tools: [] }); await render(GITHUB); diff --git a/frontend/src/connectors/ConnectionDrawer.tsx b/frontend/src/connectors/ConnectionDrawer.tsx index 9b3a2dbc..38007f81 100644 --- a/frontend/src/connectors/ConnectionDrawer.tsx +++ b/frontend/src/connectors/ConnectionDrawer.tsx @@ -26,6 +26,7 @@ import { ListRow, ListRows } from '../components/ui/list-row'; import { LoadingState } from '../components/ui/loading-state'; import { Modal, ModalActions } from '../components/ui/modal'; import { SectionHeader } from '../components/ui/section-header'; +import { SettingRow, SettingRows } from '../components/ui/setting-row'; import { Switch } from '../components/ui/switch'; import { Sheet, @@ -317,6 +318,42 @@ function ToolSwitch({ ); } +/** + * Lets agents make changes through a connection (GitHub's issues, comments + * and pull requests) or only read. Switching re-reads the tool's actions. + */ +function WritesSwitch({ + detail, + onSwitch, +}: { + detail: ConnectionDetail; + onSwitch: (detail: ConnectionDetail, allow: boolean) => Promise; +}) { + const { t } = useTranslation(); + const [pending, setPending] = useState(false); + const id = `writes-${detail.id}`; + return ( + + + { + setPending(true); + onSwitch(detail, checked === true).finally(() => setPending(false)); + }} + /> + + + ); +} + function AccountSection({ connector, detail, @@ -329,6 +366,7 @@ function AccountSection({ onToggleTool, onSyncNow, onAddTools, + onSwitchWrites, }: { connector: ConnectorDefinition; detail: ConnectionDetail; @@ -343,6 +381,8 @@ function AccountSection({ onSyncNow: (source: ConnectionSource) => void; /** Creates the tools a connection skipped while it was set up (GitHub's). */ onAddTools: (detail: ConnectionDetail) => Promise; + /** Lets its tool make changes or only read (GitHub's). */ + onSwitchWrites: (detail: ConnectionDetail, allow: boolean) => Promise; }) { const { t } = useTranslation(); const accountTitle = useAccountTitle(); @@ -357,6 +397,11 @@ function AccountSection({ detail.status === 'connected'; const canSync = connector.setup.sync !== 'off' && !!connector.sync_ingestor; const isMcp = detail.tools.some((tool) => tool.name === 'mcp_tool'); + // Hidden once an admin forbids changes; its tool then only reads anyway. + const canSwitchWrites = + !!connector.writes_allowed && + detail.writes !== null && + detail.writes !== undefined; const menu: MenuOption[] = [ { icon: Pencil, @@ -579,6 +624,9 @@ function AccountSection({ title={tool.display_name} actions={} /> + {canSwitchWrites && tool.name === 'mcp_tool' && ( + + )} { + const data = await connectorsService + .setWrites(detail.id, allow, token) + .catch(() => null); + if (!data?.success) { + dispatch( + showActionToast({ + variant: 'destructive', + message: + data?.code === 'writes_forbidden' + ? t('settings.connectors.github.writesForbidden') + : data?.code === 'tools_unavailable' + ? t('settings.connectors.wizard.toolsUnavailable', { + name: connector ? connectorName(t, connector) : detail.name, + interpolation: { escapeValue: false }, + }) + : t('settings.connectors.github.writesFailed'), + }), + ); + } + refresh(); + }; + const refreshTools = async (detail: ConnectionDetail) => { const data = await connectorsService.refreshTools(detail.id, token); dispatch( @@ -908,6 +979,7 @@ export default function ConnectionDrawer({ onToggleTool={toggleTool} onSyncNow={syncNow} onAddTools={addTools} + onSwitchWrites={switchWrites} /> ))} @@ -955,6 +1027,7 @@ export default function ConnectionDrawer({ onToggleTool={toggleTool} onSyncNow={syncNow} onAddTools={addTools} + onSwitchWrites={switchWrites} /> ))} diff --git a/frontend/src/connectors/types.ts b/frontend/src/connectors/types.ts index 603b98fc..50257a6f 100644 --- a/frontend/src/connectors/types.ts +++ b/frontend/src/connectors/types.ts @@ -54,6 +54,8 @@ export type ConnectorDefinition = { tool_templates: string[]; setup: { tools: 'auto' | 'ask' | 'off'; sync: 'auto' | 'ask' | 'off' }; mcp_url: string | null; + /** Its tool can also make changes, when a connection opts in (GitHub). */ + writes_opt_in?: boolean; publisher: 'built_in' | 'preset' | 'custom'; docs_url: string | null; oauth_scopes: string[]; @@ -73,6 +75,8 @@ export type ConnectorDefinition = { status: ConnectionStatus | null; state: ConnectorCardState; credential_policy: 'choose' | CredentialMode; + /** A connection may opt into changes: offered, and no admin forbade it. */ + writes_allowed?: boolean; }; export type Connection = { @@ -149,4 +153,9 @@ export type GitHubRepository = { export type ConnectionDetail = Connection & { sources: ConnectionSource[]; tools: ConnectionTool[]; + /** + * Whether its tool can make changes (GitHub's write endpoint); null when + * the connector offers no such choice or the tool is not added yet. + */ + writes?: boolean | null; }; diff --git a/frontend/src/locale/de.json b/frontend/src/locale/de.json index fa4bd842..2ec4053e 100644 --- a/frontend/src/locale/de.json +++ b/frontend/src/locale/de.json @@ -1339,7 +1339,11 @@ }, "manageConnection": "Verbindung verwalten", "github": { - "tokenHint": "Erstelle ein feingranulares Token mit Lesezugriff auf „Contents“ und „Metadata“ für die Repositorys, die du nutzen möchtest.", + "tokenHint": "Erstelle ein feingranulares Token mit Lesezugriff auf „Contents“ und „Metadata“ für die Repositorys, die du nutzen möchtest. Damit Agenten Änderungen vornehmen können, gib ihm außerdem Lese- und Schreibzugriff auf „Issues“ und „Pull requests“.", + "writes": "Agenten auch Änderungen erlauben (Issues, Kommentare, Pull Requests)", + "writesDescription": "Änderungen fragen dich standardmäßig zuerst. Dein Token oder die GitHub App braucht Lese- und Schreibzugriff auf „Issues“ und „Pull requests“.", + "writesForbidden": "Ein Admin hat Änderungen über GitHub ausgeschaltet.", + "writesFailed": "Es konnte nicht geändert werden, was Agenten auf GitHub dürfen.", "createToken": "Token auf GitHub erstellen", "chooseRepositories": "Repositorys auf GitHub auswählen", "noAppRepositories": "DocsGPT sieht noch keine Repositorys. Wähle auf GitHub aus, welche es lesen darf.", diff --git a/frontend/src/locale/en.json b/frontend/src/locale/en.json index 16ff04e8..65d2c403 100644 --- a/frontend/src/locale/en.json +++ b/frontend/src/locale/en.json @@ -1345,7 +1345,11 @@ }, "manageConnection": "Manage connection", "github": { - "tokenHint": "Create a fine-grained token with read access to Contents and Metadata on the repositories you want to use.", + "tokenHint": "Create a fine-grained token with read access to Contents and Metadata on the repositories you want to use. For agents to make changes, also give it read and write access to Issues and Pull requests.", + "writes": "Also let agents make changes (issues, comments, pull requests)", + "writesDescription": "Changes ask you first by default. Your token, or the GitHub App, needs read and write access to Issues and Pull requests.", + "writesForbidden": "An admin turned off changes through GitHub.", + "writesFailed": "Could not change what agents may do on GitHub.", "createToken": "Create a token on GitHub", "chooseRepositories": "Choose repositories on GitHub", "noAppRepositories": "DocsGPT cannot see any repositories yet. Choose which ones it may read on GitHub.", diff --git a/frontend/src/locale/es.json b/frontend/src/locale/es.json index c1be3359..be9e68fb 100644 --- a/frontend/src/locale/es.json +++ b/frontend/src/locale/es.json @@ -1339,7 +1339,11 @@ }, "manageConnection": "Gestionar conexión", "github": { - "tokenHint": "Crea un token detallado con acceso de lectura a Contents y Metadata en los repositorios que quieras usar.", + "tokenHint": "Crea un token detallado con acceso de lectura a Contents y Metadata en los repositorios que quieras usar. Para que los agentes hagan cambios, dale también acceso de lectura y escritura a Issues y Pull requests.", + "writes": "Permitir también que los agentes hagan cambios (issues, comentarios, pull requests)", + "writesDescription": "Por defecto, cada cambio te pide aprobación primero. Tu token, o la GitHub App, necesita acceso de lectura y escritura a Issues y Pull requests.", + "writesForbidden": "Un administrador desactivó los cambios a través de GitHub.", + "writesFailed": "No se pudo cambiar lo que los agentes pueden hacer en GitHub.", "createToken": "Crear un token en GitHub", "chooseRepositories": "Elegir repositorios en GitHub", "noAppRepositories": "DocsGPT aún no ve ningún repositorio. Elige en GitHub cuáles puede leer.", diff --git a/frontend/src/locale/jp.json b/frontend/src/locale/jp.json index 01ad25a7..0fa45dca 100644 --- a/frontend/src/locale/jp.json +++ b/frontend/src/locale/jp.json @@ -1321,7 +1321,11 @@ }, "manageConnection": "接続を管理", "github": { - "tokenHint": "使用するリポジトリに対して Contents と Metadata の読み取り権限を持つ fine-grained トークンを作成してください。", + "tokenHint": "使用するリポジトリに対して Contents と Metadata の読み取り権限を持つ fine-grained トークンを作成してください。エージェントに変更を許可する場合は、Issues と Pull requests の読み取りと書き込みの権限も付与してください。", + "writes": "エージェントによる変更も許可する(Issue、コメント、プルリクエスト)", + "writesDescription": "変更は既定で先にあなたの承認を求めます。トークンまたは GitHub App に Issues と Pull requests の読み取りと書き込みの権限が必要です。", + "writesForbidden": "管理者が GitHub での変更をオフにしています。", + "writesFailed": "GitHub でエージェントに許可する操作を変更できませんでした。", "createToken": "GitHub でトークンを作成", "chooseRepositories": "GitHub でリポジトリを選択", "noAppRepositories": "DocsGPT からはまだリポジトリが見えません。読み取りを許可するリポジトリを GitHub で選択してください。", diff --git a/frontend/src/locale/ru.json b/frontend/src/locale/ru.json index 513f6828..2da1190a 100644 --- a/frontend/src/locale/ru.json +++ b/frontend/src/locale/ru.json @@ -1413,7 +1413,11 @@ }, "manageConnection": "Управлять подключением", "github": { - "tokenHint": "Создайте детализированный токен с доступом на чтение к Contents и Metadata для нужных репозиториев.", + "tokenHint": "Создайте детализированный токен с доступом на чтение к Contents и Metadata для нужных репозиториев. Чтобы агенты могли вносить изменения, дайте ему также доступ на чтение и запись к Issues и Pull requests.", + "writes": "Разрешить агентам также вносить изменения (задачи, комментарии, пул-реквесты)", + "writesDescription": "По умолчанию каждое изменение сначала требует вашего одобрения. Токену или GitHub App нужен доступ на чтение и запись к Issues и Pull requests.", + "writesForbidden": "Администратор отключил изменения через GitHub.", + "writesFailed": "Не удалось изменить, что агентам можно делать в GitHub.", "createToken": "Создать токен на GitHub", "chooseRepositories": "Выбрать репозитории на GitHub", "noAppRepositories": "DocsGPT пока не видит ни одного репозитория. Выберите на GitHub, какие ему можно читать.", diff --git a/frontend/src/locale/zh-TW.json b/frontend/src/locale/zh-TW.json index a408a0fb..97e8d3e4 100644 --- a/frontend/src/locale/zh-TW.json +++ b/frontend/src/locale/zh-TW.json @@ -1321,7 +1321,11 @@ }, "manageConnection": "管理連線", "github": { - "tokenHint": "為要使用的儲存庫建立細部權杖,並授予 Contents 和 Metadata 的讀取權限。", + "tokenHint": "為要使用的儲存庫建立細部權杖,並授予 Contents 和 Metadata 的讀取權限。如要讓代理進行變更,還需授予 Issues 和 Pull requests 的讀寫權限。", + "writes": "也允許代理進行變更(議題、留言、提取要求)", + "writesDescription": "預設情況下,每次變更都會先徵求你的同意。你的權杖或 GitHub App 需要 Issues 和 Pull requests 的讀寫權限。", + "writesForbidden": "管理員已關閉透過 GitHub 進行變更。", + "writesFailed": "無法變更代理在 GitHub 上可執行的操作。", "createToken": "在 GitHub 上建立權杖", "chooseRepositories": "在 GitHub 上選擇儲存庫", "noAppRepositories": "DocsGPT 還看不到任何儲存庫。請在 GitHub 上選擇允許它讀取的儲存庫。", diff --git a/frontend/src/locale/zh.json b/frontend/src/locale/zh.json index 50737a75..ad86c1f1 100644 --- a/frontend/src/locale/zh.json +++ b/frontend/src/locale/zh.json @@ -1321,7 +1321,11 @@ }, "manageConnection": "管理连接", "github": { - "tokenHint": "为要使用的仓库创建一个细粒度令牌,并授予 Contents 和 Metadata 的读取权限。", + "tokenHint": "为要使用的仓库创建一个细粒度令牌,并授予 Contents 和 Metadata 的读取权限。如需让智能体进行更改,还需授予 Issues 和 Pull requests 的读写权限。", + "writes": "也允许智能体进行更改(议题、评论、拉取请求)", + "writesDescription": "默认情况下,每次更改都会先征求你的同意。你的令牌或 GitHub App 需要 Issues 和 Pull requests 的读写权限。", + "writesForbidden": "管理员已关闭通过 GitHub 进行更改。", + "writesFailed": "无法更改智能体在 GitHub 上可执行的操作。", "createToken": "在 GitHub 上创建令牌", "chooseRepositories": "在 GitHub 上选择仓库", "noAppRepositories": "DocsGPT 还看不到任何仓库。请在 GitHub 上选择允许它读取的仓库。",