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) => ( +