mirror of
https://github.com/tiennm99/DocsGPT.git
synced 2026-10-13 18:15:02 +00:00
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.
This commit is contained in:
1 parent
12ad4f5e85
commit
34dc74c0ad
16 files changed
+346
-13
No files matched your search
@@ -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<HTMLButtonElement>('#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();
|
||||
|
||||
@@ -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>).
|
||||
</AlertDescription>
|
||||
</Alert>
|
||||
)}
|
||||
@@ -259,7 +266,10 @@ export default function Connectors() {
|
||||
// older one arriving last would otherwise put back a stale policy.
|
||||
const saveQueue = useRef<Promise<void>>(Promise.resolve());
|
||||
const save = (body: {
|
||||
policies?: Record<string, { enabled?: boolean; credential_mode?: Policy }>;
|
||||
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() {
|
||||
</TableContainer>
|
||||
</section>
|
||||
|
||||
{writable.length > 0 && (
|
||||
<section className="flex flex-col gap-3">
|
||||
<SectionHeader
|
||||
title="Write access"
|
||||
description="Members can let agents make changes through these connectors, one connection at a time. Each change asks first unless the member allows it."
|
||||
/>
|
||||
<SettingRows>
|
||||
{writable.map((connector) => (
|
||||
<SettingRow
|
||||
key={connector.key}
|
||||
label={`Let agents make changes through ${connector.name}`}
|
||||
description={
|
||||
connector.key === 'github'
|
||||
? 'Create issues, comments and pull requests. Off keeps every GitHub tool read-only, including ones already set up for changes.'
|
||||
: `Off keeps every ${connector.name} tool read-only.`
|
||||
}
|
||||
htmlFor={`allow-writes-${connector.key}`}
|
||||
alignStart
|
||||
>
|
||||
<Switch
|
||||
id={`allow-writes-${connector.key}`}
|
||||
checked={connector.allow_writes === true}
|
||||
onCheckedChange={(checked) =>
|
||||
save({
|
||||
policies: {
|
||||
[connector.key]: { allow_writes: checked === true },
|
||||
},
|
||||
})
|
||||
}
|
||||
/>
|
||||
</SettingRow>
|
||||
))}
|
||||
</SettingRows>
|
||||
</section>
|
||||
)}
|
||||
|
||||
{detail && (
|
||||
<Sheet open onOpenChange={(open) => !open && setDetailKey(null)}>
|
||||
<SheetContent side="right" size="detail" closeLabel="Close">
|
||||
|
||||
@@ -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`,
|
||||
|
||||
@@ -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<string, 'always' | 'ask' | 'off'>;
|
||||
sync?: {
|
||||
items: Record<string, unknown>;
|
||||
@@ -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,
|
||||
|
||||
@@ -609,6 +609,49 @@ describe('ConnectWizard', () => {
|
||||
});
|
||||
});
|
||||
|
||||
const writesSwitch = () =>
|
||||
document.body.querySelector<HTMLButtonElement>('#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<HTMLButtonElement>('#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,
|
||||
|
||||
@@ -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<string | null>(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<ConnectorAuthKind | null>(
|
||||
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)}
|
||||
/>
|
||||
</SettingRow>
|
||||
{offerWrites && (
|
||||
<SettingRow
|
||||
label={t('settings.connectors.github.writes')}
|
||||
description={t('settings.connectors.github.writesDescription')}
|
||||
htmlFor={`tools-${connector.key}-writes`}
|
||||
alignStart
|
||||
>
|
||||
<Switch
|
||||
id={`tools-${connector.key}-writes`}
|
||||
checked={writesOn}
|
||||
onCheckedChange={(checked) => setWritesOn(checked === true)}
|
||||
/>
|
||||
</SettingRow>
|
||||
)}
|
||||
</SettingRows>
|
||||
)}
|
||||
{isRepoPicker && connectionId ? (
|
||||
|
||||
@@ -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<HTMLButtonElement>('#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<typeof selectActionToast>[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);
|
||||
|
||||
@@ -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<void>;
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
const [pending, setPending] = useState(false);
|
||||
const id = `writes-${detail.id}`;
|
||||
return (
|
||||
<SettingRows>
|
||||
<SettingRow
|
||||
label={t('settings.connectors.github.writes')}
|
||||
description={t('settings.connectors.github.writesDescription')}
|
||||
htmlFor={id}
|
||||
alignStart
|
||||
>
|
||||
<Switch
|
||||
id={id}
|
||||
checked={!!detail.writes}
|
||||
disabled={pending || detail.status !== 'connected'}
|
||||
onCheckedChange={(checked) => {
|
||||
setPending(true);
|
||||
onSwitch(detail, checked === true).finally(() => setPending(false));
|
||||
}}
|
||||
/>
|
||||
</SettingRow>
|
||||
</SettingRows>
|
||||
);
|
||||
}
|
||||
|
||||
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<void>;
|
||||
/** Lets its tool make changes or only read (GitHub's). */
|
||||
onSwitchWrites: (detail: ConnectionDetail, allow: boolean) => Promise<void>;
|
||||
}) {
|
||||
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={<ToolSwitch tool={tool} onToggle={onToggleTool} />}
|
||||
/>
|
||||
{canSwitchWrites && tool.name === 'mcp_tool' && (
|
||||
<WritesSwitch detail={detail} onSwitch={onSwitchWrites} />
|
||||
)}
|
||||
<ToolPermissions
|
||||
connectionId={detail.id}
|
||||
tool={tool}
|
||||
@@ -769,6 +817,29 @@ export default function ConnectionDrawer({
|
||||
refresh();
|
||||
};
|
||||
|
||||
const switchWrites = async (detail: ConnectionDetail, allow: boolean) => {
|
||||
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}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
@@ -955,6 +1027,7 @@ export default function ConnectionDrawer({
|
||||
onToggleTool={toggleTool}
|
||||
onSyncNow={syncNow}
|
||||
onAddTools={addTools}
|
||||
onSwitchWrites={switchWrites}
|
||||
/>
|
||||
))}
|
||||
</section>
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
@@ -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.",
|
||||
|
||||
@@ -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.",
|
||||
|
||||
@@ -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.",
|
||||
|
||||
@@ -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 で選択してください。",
|
||||
|
||||
@@ -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, какие ему можно читать.",
|
||||
|
||||
@@ -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 上選擇允許它讀取的儲存庫。",
|
||||
|
||||
@@ -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 上选择允许它读取的仓库。",
|
||||
|
||||
Reference in new issue
Block a user