mirror of
https://github.com/mudler/LocalAI.git
synced 2026-11-06 20:43:27 -05:00
A connection now has a write mode, chosen on the add-on page: off (the default), propose (each change waits for the person to confirm) or auto (a chat of the person makes it at once). The host enforces it for every write tool of an add-on that holds a credential, before the add-on is called, and holds the call to the repository list of the connection. Host: - Binding.WriteMode, stored in both stores and in the connector API (write_mode). Absent means off, so existing bindings stay read only. - A credentialed add-on may declare write tools. They name the resource with the credential slot argument, never resourceId. A tool can set approval "always", a ceiling that keeps a proposal even in auto. - The proposal broker fetches the credential for the preview and again for the apply, so it is never stored. A mode lowered between the proposal and the confirmation refuses the apply. A proposal that awaits a person cannot be confirmed by a chat turn. - New capability name tools.write for the roles a connector kind declares. Add-on, tools (kind write): comment-issue, comment-pr, create-issue, edit-issue (title, close or reopen), add-labels, review-comment-pr. They preview plain facts, refuse a stale target, cap and screen the text for secrets, and keep a receipt journal plus a hidden marker in the text, so a change whose answer was lost is not sent twice. Assisted-by: Claude:claude-sonnet-5-5 go gofmt ginkgo node
66 lines
2.7 KiB
JavaScript
66 lines
2.7 KiB
JavaScript
// Helpers of the GitHub page. No React, so a node test drives them.
|
|
|
|
// A fine-grained token. A classic token (ghp_) reaches every repository of
|
|
// the account, so the page does not take it.
|
|
export const TOKEN_PREFIX = 'github_pat_'
|
|
|
|
// What is wrong with the typed token, as a key of the page's words, or ''.
|
|
// Nothing typed is no problem: an update without a token keeps the stored one.
|
|
export function tokenProblem(form) {
|
|
const token = (form.token || '').trim()
|
|
if (!token) return ''
|
|
if (/\s/.test(token)) return 'tokenSpace'
|
|
if (!token.startsWith(TOKEN_PREFIX)) return 'tokenShape'
|
|
return ''
|
|
}
|
|
|
|
// The secret the field makes: the token, or '' when not typed or not right.
|
|
export function githubSecret(form) {
|
|
return tokenProblem(form) ? '' : (form.token || '').trim()
|
|
}
|
|
|
|
export const MAX_REPOS = 50
|
|
const REPO = /^[A-Za-z0-9_.-]{1,100}\/[A-Za-z0-9_.-]{1,100}$/
|
|
|
|
// The repositories of a text box: one per line, or separated by commas or
|
|
// spaces. Blank entries and repeats (any case) are dropped.
|
|
export function parseRepos(text) {
|
|
const seen = new Set()
|
|
const out = []
|
|
for (const part of String(text || '').split(/[\s,]+/)) {
|
|
const r = part.trim()
|
|
if (!r || seen.has(r.toLowerCase())) continue
|
|
seen.add(r.toLowerCase())
|
|
out.push(r)
|
|
}
|
|
return out
|
|
}
|
|
|
|
// What is wrong with the typed list, as a key of the page's words, or ''.
|
|
export function reposProblem(text) {
|
|
const list = parseRepos(text)
|
|
if (list.length > MAX_REPOS) return 'reposMany'
|
|
if (list.some((r) => !REPO.test(r) || r.includes('..'))) return 'reposShape'
|
|
return ''
|
|
}
|
|
|
|
// What the agent may change on GitHub with this connection. The host keeps it
|
|
// on the binding and enforces it: off runs no write tool, propose stores each
|
|
// change for you to confirm, auto lets a chat of yours make it at once.
|
|
export const WRITE_MODES = ['off', 'propose', 'auto']
|
|
|
|
// The mode of a saved binding, off for one that has none or an unknown one.
|
|
export const writeModeOf = (m) => (WRITE_MODES.includes(m) ? m : 'off')
|
|
|
|
// The fields this page adds to a binding: its form values, the request body
|
|
// they make, and whether they allow saving.
|
|
export const extraForm = (c) => ({ repos_text: (c.resources || []).join('\n'), write_mode: writeModeOf(c.write_mode) })
|
|
export const extraBody = (form) => ({ resources: parseRepos(form.repos_text), write_mode: writeModeOf(form.write_mode) })
|
|
export const extraValid = (form) => !reposProblem(form.repos_text)
|
|
|
|
// The line the Test button adds for each repository, from the check tool.
|
|
export function checkLine(r, t) {
|
|
if (!r || r.reachable === false) return t('check.unreachable', { repo: r?.repo || '' })
|
|
return t(r.canWrite ? 'check.readWrite' : 'check.readOnly', { repo: r.fullName || r.repo })
|
|
}
|