From 3bbb40b6ff4bcb9be0aba6ec0ec091ec99194b3a Mon Sep 17 00:00:00 2001 From: Ettore Di Giacinto Date: Tue, 3 Nov 2026 14:02:32 +0000 Subject: [PATCH] feat(addons): GitHub settings page, registration and docs Add the GitHub connection page (fine-grained token, repository list, read-only note, Test that checks each listed repository), let the shared connector page hide messaging settings for a kind that carries no messages, register the add-on in the package matrix and the catalog, and document the credential slot and the GitHub add-on. Assisted-by: Claude:claude-sonnet-5-5 [go node] --- .github/workflows/addons-package.yml | 4 +- addons/github/ui/.gitignore | 2 + addons/github/ui/build.mjs | 15 +++ addons/github/ui/package.json | 8 ++ addons/github/ui/src/addon.jsx | 92 ++++++++++++++ addons/github/ui/src/i18n/github.en.json | 49 ++++++++ addons/github/ui/src/index.jsx | 14 +++ addons/github/ui/src/repos.js | 57 +++++++++ addons/github/ui/test/github.test.mjs | 52 ++++++++ addons/github/ui/test/run.mjs | 38 ++++++ .../connector-ui/src/ConnectorPage.jsx | 47 ++++++-- addons/internal/connector-ui/src/connector.js | 6 +- core/services/addons/catalog/official_test.go | 2 +- docs/content/features/addons.md | 39 ++++++ docs/content/features/chats-connectors.md | 16 ++- gallery/addons/README.md | 2 +- gallery/addons/github.yaml.tmpl | 18 +++ gallery/addons/index.yaml | 112 ++++++++++++++++++ pkg/addons/packaging/packaging_test.go | 8 +- 19 files changed, 557 insertions(+), 24 deletions(-) create mode 100644 addons/github/ui/.gitignore create mode 100644 addons/github/ui/build.mjs create mode 100644 addons/github/ui/package.json create mode 100644 addons/github/ui/src/addon.jsx create mode 100644 addons/github/ui/src/i18n/github.en.json create mode 100644 addons/github/ui/src/index.jsx create mode 100644 addons/github/ui/src/repos.js create mode 100644 addons/github/ui/test/github.test.mjs create mode 100644 addons/github/ui/test/run.mjs create mode 100644 gallery/addons/github.yaml.tmpl diff --git a/.github/workflows/addons-package.yml b/.github/workflows/addons-package.yml index 8108ab437f..d5f1cccd35 100644 --- a/.github/workflows/addons-package.yml +++ b/.github/workflows/addons-package.yml @@ -107,7 +107,7 @@ jobs: strategy: fail-fast: true matrix: - addon: [tables, voice, face, fine-tuning, quantization, studio, talk, telegram, slack, office, sandbox, web, memory] + addon: [tables, voice, face, fine-tuning, quantization, studio, talk, telegram, slack, github, office, sandbox, web, memory] steps: - uses: actions/checkout@v7 with: @@ -226,7 +226,7 @@ jobs: # event it fails the plan. A missing or malformed platform file, or # any other error, fails the plan on every event. include='[]' - for id in tables voice face fine-tuning quantization studio talk telegram slack office sandbox web memory; do + for id in tables voice face fine-tuning quantization studio talk telegram slack github office sandbox web memory; do cfg="addons/$id/package/platforms.yaml" err="matrix-$id.err" if rows=$(go run ./scripts/addons/cmd/addonpkg matrix --config "$cfg" 2>"$err"); then diff --git a/addons/github/ui/.gitignore b/addons/github/ui/.gitignore new file mode 100644 index 0000000000..1225aa2fae --- /dev/null +++ b/addons/github/ui/.gitignore @@ -0,0 +1,2 @@ +# Written by build.mjs; the package build makes it. +/dist/ diff --git a/addons/github/ui/build.mjs b/addons/github/ui/build.mjs new file mode 100644 index 0000000000..b167979f6f --- /dev/null +++ b/addons/github/ui/build.mjs @@ -0,0 +1,15 @@ +import { fileURLToPath } from 'node:url' +import { dirname, resolve } from 'node:path' +import { buildAddonUI } from '../../internal/uibuild/uibuild.mjs' + +const here = dirname(fileURLToPath(import.meta.url)) + +export async function build() { + // The page keeps the host module names it was written with; the compat + // directory answers them through the SDK (addons/internal/host-compat). + return buildAddonUI({ root: here, name: 'github', rgb: [36, 41, 47], compat: resolve(here, '../../internal/host-compat/src') }) +} + +if (process.argv[1] === fileURLToPath(import.meta.url)) { + build().then((dir) => console.log(`built ${dir}`), (e) => { console.error(e); process.exit(1) }) +} diff --git a/addons/github/ui/package.json b/addons/github/ui/package.json new file mode 100644 index 0000000000..f2086e0656 --- /dev/null +++ b/addons/github/ui/package.json @@ -0,0 +1,8 @@ +{ + "name": "localai-github-ui", + "private": true, + "version": "0.1.0", + "description": "Native UI of the GitHub add-on: a standalone ES module that the host loads after its own build.", + "type": "module", + "scripts": { "build": "node build.mjs", "test": "node test/run.mjs" } +} diff --git a/addons/github/ui/src/addon.jsx b/addons/github/ui/src/addon.jsx new file mode 100644 index 0000000000..c2b6b5046f --- /dev/null +++ b/addons/github/ui/src/addon.jsx @@ -0,0 +1,92 @@ +/* eslint-disable no-unused-vars -- components used only inside JSX look unused to this config, which has no eslint-plugin-react */ +import { RouteProvider } from 'react-router-dom' +import { getSdk, } from '../../../internal/host-compat/src/runtime.js' +import { registerLocales } from '../../../internal/host-compat/src/i18n.js' +import ConnectorPage from '../../../internal/connector-ui/src/ConnectorPage' +import { Field, FieldError, Notice, TextInput } from '../../../internal/connector-ui/src/controls' +import en from './i18n/github.en.json' +import { checkLine, extraBody, extraForm, extraValid, githubSecret, parseRepos, reposProblem, tokenProblem } from './repos' + +registerLocales({ github: { en } }) + +// The fine-grained token. It is typed only to replace it. +function GitHubToken({ form, setForm, t }) { + const problem = tokenProblem(form) + return ( + + {(p) => ( + setForm({ ...form, token: e.target.value })} /> + )} + + ) +} + +// The repositories the agent may read. The host refuses any other one before +// the add-on is called. +function GitHubFields({ form, setForm, t }) { + const problem = reposProblem(form.repos_text) + const count = parseRepos(form.repos_text).length + return ( + <> + {tokenProblem(form) && {t(tokenProblem(form))}} + + {(p) => ( +