feat: anythingllm-devtools service, builazoo project, ssh-config example, docs
- Add services/anythingllm-devtools HTTP API (repos + AnythingLLM + RAG) - Rename gitea-issues to git-issues across smart_ide agents and docs - Add projects/builazoo, builazoo README, cron fragment, ssh-config.example - Add ensure-ia-dev-project-link.sh; wrapper delegates smart_ide id - Bump ia_dev submodule (git-issues rename, project symlinks) - Align 4nkaiignore templates; update API index and project docs
This commit is contained in:
parent
49767c8f9a
commit
b21ac2cf64
7
.gitignore
vendored
7
.gitignore
vendored
@ -16,10 +16,17 @@ logs/**/*.log
|
||||
projects/*
|
||||
!projects/README.md
|
||||
!projects/active-project.json.example
|
||||
!projects/ssh-config.example
|
||||
!projects/enso/
|
||||
!projects/enso/**
|
||||
!projects/smart_ide/
|
||||
!projects/smart_ide/**
|
||||
!projects/builazoo/
|
||||
!projects/builazoo/**
|
||||
|
||||
# Clone applicatif builazoo sous la racine monorepo (hors Git sauf README)
|
||||
builazoo/*
|
||||
!builazoo/README.md
|
||||
|
||||
# Projet IDE actif (copie locale de active-project.json.example)
|
||||
projects/active-project.json
|
||||
|
||||
@ -9,7 +9,7 @@ is_background: false
|
||||
|
||||
- **Identifiant projet ia_dev :** `smart_ide` (conf : `projects/smart_ide/` à la racine ; lien sous `ia_dev/projects/smart_ide`).
|
||||
- **Environnement cible :** `test`, `pprod` ou `prod`. Le reprendre dans le message utilisateur ; **si absent, le demander** avant d'exécuter des scripts dépendants du env.
|
||||
- **Racine des scripts ia_dev :** le dossier `ia_dev/` à la racine du workspace **smart_ide**. Pour `deploy/`, `gitea-issues/`, etc. : se placer dans ce répertoire avant d'exécuter.
|
||||
- **Racine des scripts ia_dev :** le dossier `ia_dev/` à la racine du workspace **smart_ide**. Pour `deploy/`, `git-issues/`, etc. : se placer dans ce répertoire avant d'exécuter.
|
||||
- **Variables / arguments :** `IA_PROJECT_ID=smart_ide` et/ou `--project smart_ide` selon le script ; référence : `docs/repo/ia-dev-project-conf-schema.md`.
|
||||
- **MAIL_TO (ticketing / mails) :** exporter `MAIL_TO` avec l'adresse pour l'environnement choisi, lue dans `projects/smart_ide/conf.json` (racine workspace) → `tickets.authorized_emails.to` : test → `AI.SMART_IDE.TEST@4nkweb.com`, pprod → `AI.SMART_IDE.PPROD@4nkweb.com`, prod → `AI.SMART_IDE.PROD@4nkweb.com`.
|
||||
- **Dépôt applicatif :** racine du workspace smart_ide ; `project_path` dans `conf.json` doit y pointer. Doc principale du monorepo : `docs/` à la racine (`projects/smart_ide/docs` sous ia_dev souvent absent).
|
||||
|
||||
@ -9,7 +9,7 @@ is_background: false
|
||||
|
||||
- **Identifiant projet ia_dev :** `smart_ide` (conf : `projects/smart_ide/` à la racine ; lien sous `ia_dev/projects/smart_ide`).
|
||||
- **Environnement cible :** `test`, `pprod` ou `prod`. Le reprendre dans le message utilisateur ; **si absent, le demander** avant d'exécuter des scripts dépendants du env.
|
||||
- **Racine des scripts ia_dev :** le dossier `ia_dev/` à la racine du workspace **smart_ide**. Pour `deploy/`, `gitea-issues/`, etc. : se placer dans ce répertoire avant d'exécuter.
|
||||
- **Racine des scripts ia_dev :** le dossier `ia_dev/` à la racine du workspace **smart_ide**. Pour `deploy/`, `git-issues/`, etc. : se placer dans ce répertoire avant d'exécuter.
|
||||
- **Variables / arguments :** `IA_PROJECT_ID=smart_ide` et/ou `--project smart_ide` selon le script ; référence : `docs/repo/ia-dev-project-conf-schema.md`.
|
||||
- **MAIL_TO (ticketing / mails) :** exporter `MAIL_TO` avec l'adresse pour l'environnement choisi, lue dans `projects/smart_ide/conf.json` (racine workspace) → `tickets.authorized_emails.to` : test → `AI.SMART_IDE.TEST@4nkweb.com`, pprod → `AI.SMART_IDE.PPROD@4nkweb.com`, prod → `AI.SMART_IDE.PROD@4nkweb.com`.
|
||||
- **Dépôt applicatif :** racine du workspace smart_ide ; `project_path` dans `conf.json` doit y pointer. Doc principale du monorepo : `docs/` à la racine (`projects/smart_ide/docs` sous ia_dev souvent absent).
|
||||
|
||||
@ -9,7 +9,7 @@ is_background: false
|
||||
|
||||
- **Identifiant projet ia_dev :** `smart_ide` (conf : `projects/smart_ide/` à la racine ; lien sous `ia_dev/projects/smart_ide`).
|
||||
- **Environnement cible :** `test`, `pprod` ou `prod`. Le reprendre dans le message utilisateur ; **si absent, le demander** avant d'exécuter des scripts dépendants du env.
|
||||
- **Racine des scripts ia_dev :** le dossier `ia_dev/` à la racine du workspace **smart_ide**. Pour `deploy/`, `gitea-issues/`, etc. : se placer dans ce répertoire avant d'exécuter.
|
||||
- **Racine des scripts ia_dev :** le dossier `ia_dev/` à la racine du workspace **smart_ide**. Pour `deploy/`, `git-issues/`, etc. : se placer dans ce répertoire avant d'exécuter.
|
||||
- **Variables / arguments :** `IA_PROJECT_ID=smart_ide` et/ou `--project smart_ide` selon le script ; référence : `docs/repo/ia-dev-project-conf-schema.md`.
|
||||
- **MAIL_TO (ticketing / mails) :** exporter `MAIL_TO` avec l'adresse pour l'environnement choisi, lue dans `projects/smart_ide/conf.json` (racine workspace) → `tickets.authorized_emails.to` : test → `AI.SMART_IDE.TEST@4nkweb.com`, pprod → `AI.SMART_IDE.PPROD@4nkweb.com`, prod → `AI.SMART_IDE.PROD@4nkweb.com`.
|
||||
- **Dépôt applicatif :** racine du workspace smart_ide ; `project_path` dans `conf.json` doit y pointer. Doc principale du monorepo : `docs/` à la racine (`projects/smart_ide/docs` sous ia_dev souvent absent).
|
||||
|
||||
@ -9,7 +9,7 @@ is_background: false
|
||||
|
||||
- **Identifiant projet ia_dev :** `smart_ide` (conf : `projects/smart_ide/` à la racine ; lien sous `ia_dev/projects/smart_ide`).
|
||||
- **Environnement cible :** `test`, `pprod` ou `prod`. Le reprendre dans le message utilisateur ; **si absent, le demander** avant d'exécuter des scripts dépendants du env.
|
||||
- **Racine des scripts ia_dev :** le dossier `ia_dev/` à la racine du workspace **smart_ide**. Pour `deploy/`, `gitea-issues/`, etc. : se placer dans ce répertoire avant d'exécuter.
|
||||
- **Racine des scripts ia_dev :** le dossier `ia_dev/` à la racine du workspace **smart_ide**. Pour `deploy/`, `git-issues/`, etc. : se placer dans ce répertoire avant d'exécuter.
|
||||
- **Variables / arguments :** `IA_PROJECT_ID=smart_ide` et/ou `--project smart_ide` selon le script ; référence : `docs/repo/ia-dev-project-conf-schema.md`.
|
||||
- **MAIL_TO (ticketing / mails) :** exporter `MAIL_TO` avec l'adresse pour l'environnement choisi, lue dans `projects/smart_ide/conf.json` (racine workspace) → `tickets.authorized_emails.to` : test → `AI.SMART_IDE.TEST@4nkweb.com`, pprod → `AI.SMART_IDE.PPROD@4nkweb.com`, prod → `AI.SMART_IDE.PROD@4nkweb.com`.
|
||||
- **Dépôt applicatif :** racine du workspace smart_ide ; `project_path` dans `conf.json` doit y pointer. Doc principale du monorepo : `docs/` à la racine (`projects/smart_ide/docs` sous ia_dev souvent absent).
|
||||
|
||||
@ -9,7 +9,7 @@ is_background: false
|
||||
|
||||
- **Identifiant projet ia_dev :** `smart_ide` (conf : `projects/smart_ide/` à la racine ; lien sous `ia_dev/projects/smart_ide`).
|
||||
- **Environnement cible :** `test`, `pprod` ou `prod`. Le reprendre dans le message utilisateur ; **si absent, le demander** avant d'exécuter des scripts dépendants du env.
|
||||
- **Racine des scripts ia_dev :** le dossier `ia_dev/` à la racine du workspace **smart_ide**. Pour `deploy/`, `gitea-issues/`, etc. : se placer dans ce répertoire avant d'exécuter.
|
||||
- **Racine des scripts ia_dev :** le dossier `ia_dev/` à la racine du workspace **smart_ide**. Pour `deploy/`, `git-issues/`, etc. : se placer dans ce répertoire avant d'exécuter.
|
||||
- **Variables / arguments :** `IA_PROJECT_ID=smart_ide` et/ou `--project smart_ide` selon le script ; référence : `docs/repo/ia-dev-project-conf-schema.md`.
|
||||
- **MAIL_TO (ticketing / mails) :** exporter `MAIL_TO` avec l'adresse pour l'environnement choisi, lue dans `projects/smart_ide/conf.json` (racine workspace) → `tickets.authorized_emails.to` : test → `AI.SMART_IDE.TEST@4nkweb.com`, pprod → `AI.SMART_IDE.PPROD@4nkweb.com`, prod → `AI.SMART_IDE.PROD@4nkweb.com`.
|
||||
- **Dépôt applicatif :** racine du workspace smart_ide ; `project_path` dans `conf.json` doit y pointer. Doc principale du monorepo : `docs/` à la racine (`projects/smart_ide/docs` sous ia_dev souvent absent).
|
||||
|
||||
@ -9,7 +9,7 @@ is_background: false
|
||||
|
||||
- **Identifiant projet ia_dev :** `smart_ide` (conf : `projects/smart_ide/` à la racine ; lien sous `ia_dev/projects/smart_ide`).
|
||||
- **Environnement cible :** `test`, `pprod` ou `prod`. Le reprendre dans le message utilisateur ; **si absent, le demander** avant d'exécuter des scripts dépendants du env.
|
||||
- **Racine des scripts ia_dev :** le dossier `ia_dev/` à la racine du workspace **smart_ide**. Pour `deploy/`, `gitea-issues/`, etc. : se placer dans ce répertoire avant d'exécuter.
|
||||
- **Racine des scripts ia_dev :** le dossier `ia_dev/` à la racine du workspace **smart_ide**. Pour `deploy/`, `git-issues/`, etc. : se placer dans ce répertoire avant d'exécuter.
|
||||
- **Variables / arguments :** `IA_PROJECT_ID=smart_ide` et/ou `--project smart_ide` selon le script ; référence : `docs/repo/ia-dev-project-conf-schema.md`.
|
||||
- **MAIL_TO (ticketing / mails) :** exporter `MAIL_TO` avec l'adresse pour l'environnement choisi, lue dans `projects/smart_ide/conf.json` (racine workspace) → `tickets.authorized_emails.to` : test → `AI.SMART_IDE.TEST@4nkweb.com`, pprod → `AI.SMART_IDE.PPROD@4nkweb.com`, prod → `AI.SMART_IDE.PROD@4nkweb.com`.
|
||||
- **Dépôt applicatif :** racine du workspace smart_ide ; `project_path` dans `conf.json` doit y pointer. Doc principale du monorepo : `docs/` à la racine (`projects/smart_ide/docs` sous ia_dev souvent absent).
|
||||
|
||||
@ -9,7 +9,7 @@ is_background: false
|
||||
|
||||
- **Identifiant projet ia_dev :** `smart_ide` (conf : `projects/smart_ide/` à la racine ; lien sous `ia_dev/projects/smart_ide`).
|
||||
- **Environnement cible :** `test`, `pprod` ou `prod`. Le reprendre dans le message utilisateur ; **si absent, le demander** avant d'exécuter des scripts dépendants du env.
|
||||
- **Racine des scripts ia_dev :** le dossier `ia_dev/` à la racine du workspace **smart_ide**. Pour `deploy/`, `gitea-issues/`, etc. : se placer dans ce répertoire avant d'exécuter.
|
||||
- **Racine des scripts ia_dev :** le dossier `ia_dev/` à la racine du workspace **smart_ide**. Pour `deploy/`, `git-issues/`, etc. : se placer dans ce répertoire avant d'exécuter.
|
||||
- **Variables / arguments :** `IA_PROJECT_ID=smart_ide` et/ou `--project smart_ide` selon le script ; référence : `docs/repo/ia-dev-project-conf-schema.md`.
|
||||
- **MAIL_TO (ticketing / mails) :** exporter `MAIL_TO` avec l'adresse pour l'environnement choisi, lue dans `projects/smart_ide/conf.json` (racine workspace) → `tickets.authorized_emails.to` : test → `AI.SMART_IDE.TEST@4nkweb.com`, pprod → `AI.SMART_IDE.PPROD@4nkweb.com`, prod → `AI.SMART_IDE.PROD@4nkweb.com`.
|
||||
- **Dépôt applicatif :** racine du workspace smart_ide ; `project_path` dans `conf.json` doit y pointer. Doc principale du monorepo : `docs/` à la racine (`projects/smart_ide/docs` sous ia_dev souvent absent).
|
||||
|
||||
@ -9,7 +9,7 @@ is_background: false
|
||||
|
||||
- **Identifiant projet ia_dev :** `smart_ide` (conf : `projects/smart_ide/` à la racine ; lien sous `ia_dev/projects/smart_ide`).
|
||||
- **Environnement cible :** `test`, `pprod` ou `prod`. Le reprendre dans le message utilisateur ; **si absent, le demander** avant d'exécuter des scripts dépendants du env.
|
||||
- **Racine des scripts ia_dev :** le dossier `ia_dev/` à la racine du workspace **smart_ide**. Pour `deploy/`, `gitea-issues/`, etc. : se placer dans ce répertoire avant d'exécuter.
|
||||
- **Racine des scripts ia_dev :** le dossier `ia_dev/` à la racine du workspace **smart_ide**. Pour `deploy/`, `git-issues/`, etc. : se placer dans ce répertoire avant d'exécuter.
|
||||
- **Variables / arguments :** `IA_PROJECT_ID=smart_ide` et/ou `--project smart_ide` selon le script ; référence : `docs/repo/ia-dev-project-conf-schema.md`.
|
||||
- **MAIL_TO (ticketing / mails) :** exporter `MAIL_TO` avec l'adresse pour l'environnement choisi, lue dans `projects/smart_ide/conf.json` (racine workspace) → `tickets.authorized_emails.to` : test → `AI.SMART_IDE.TEST@4nkweb.com`, pprod → `AI.SMART_IDE.PPROD@4nkweb.com`, prod → `AI.SMART_IDE.PROD@4nkweb.com`.
|
||||
- **Dépôt applicatif :** racine du workspace smart_ide ; `project_path` dans `conf.json` doit y pointer. Doc principale du monorepo : `docs/` à la racine (`projects/smart_ide/docs` sous ia_dev souvent absent).
|
||||
|
||||
@ -9,7 +9,7 @@ is_background: false
|
||||
|
||||
- **Identifiant projet ia_dev :** `smart_ide` (conf : `projects/smart_ide/` à la racine ; lien sous `ia_dev/projects/smart_ide`).
|
||||
- **Environnement cible :** `test`, `pprod` ou `prod`. Le reprendre dans le message utilisateur ; **si absent, le demander** avant d'exécuter des scripts dépendants du env.
|
||||
- **Racine des scripts ia_dev :** le dossier `ia_dev/` à la racine du workspace **smart_ide**. Pour `deploy/`, `gitea-issues/`, etc. : se placer dans ce répertoire avant d'exécuter.
|
||||
- **Racine des scripts ia_dev :** le dossier `ia_dev/` à la racine du workspace **smart_ide**. Pour `deploy/`, `git-issues/`, etc. : se placer dans ce répertoire avant d'exécuter.
|
||||
- **Variables / arguments :** `IA_PROJECT_ID=smart_ide` et/ou `--project smart_ide` selon le script ; référence : `docs/repo/ia-dev-project-conf-schema.md`.
|
||||
- **MAIL_TO (ticketing / mails) :** exporter `MAIL_TO` avec l'adresse pour l'environnement choisi, lue dans `projects/smart_ide/conf.json` (racine workspace) → `tickets.authorized_emails.to` : test → `AI.SMART_IDE.TEST@4nkweb.com`, pprod → `AI.SMART_IDE.PPROD@4nkweb.com`, prod → `AI.SMART_IDE.PROD@4nkweb.com`.
|
||||
- **Dépôt applicatif :** racine du workspace smart_ide ; `project_path` dans `conf.json` doit y pointer. Doc principale du monorepo : `docs/` à la racine (`projects/smart_ide/docs` sous ia_dev souvent absent).
|
||||
|
||||
@ -9,7 +9,7 @@ is_background: false
|
||||
|
||||
- **Identifiant projet ia_dev :** `smart_ide` (conf : `projects/smart_ide/` à la racine ; lien sous `ia_dev/projects/smart_ide`).
|
||||
- **Environnement cible :** `test`, `pprod` ou `prod`. Le reprendre dans le message utilisateur ; **si absent, le demander** avant d'exécuter des scripts dépendants du env.
|
||||
- **Racine des scripts ia_dev :** le dossier `ia_dev/` à la racine du workspace **smart_ide**. Pour `deploy/`, `gitea-issues/`, etc. : se placer dans ce répertoire avant d'exécuter.
|
||||
- **Racine des scripts ia_dev :** le dossier `ia_dev/` à la racine du workspace **smart_ide**. Pour `deploy/`, `git-issues/`, etc. : se placer dans ce répertoire avant d'exécuter.
|
||||
- **Variables / arguments :** `IA_PROJECT_ID=smart_ide` et/ou `--project smart_ide` selon le script ; référence : `docs/repo/ia-dev-project-conf-schema.md`.
|
||||
- **MAIL_TO (ticketing / mails) :** exporter `MAIL_TO` avec l'adresse pour l'environnement choisi, lue dans `projects/smart_ide/conf.json` (racine workspace) → `tickets.authorized_emails.to` : test → `AI.SMART_IDE.TEST@4nkweb.com`, pprod → `AI.SMART_IDE.PPROD@4nkweb.com`, prod → `AI.SMART_IDE.PROD@4nkweb.com`.
|
||||
- **Dépôt applicatif :** racine du workspace smart_ide ; `project_path` dans `conf.json` doit y pointer. Doc principale du monorepo : `docs/` à la racine (`projects/smart_ide/docs` sous ia_dev souvent absent).
|
||||
|
||||
@ -9,7 +9,7 @@ is_background: false
|
||||
|
||||
- **Identifiant projet ia_dev :** `smart_ide` (conf : `projects/smart_ide/` à la racine ; lien sous `ia_dev/projects/smart_ide`).
|
||||
- **Environnement cible :** `test`, `pprod` ou `prod`. Le reprendre dans le message utilisateur ; **si absent, le demander** avant d'exécuter des scripts dépendants du env.
|
||||
- **Racine des scripts ia_dev :** le dossier `ia_dev/` à la racine du workspace **smart_ide**. Pour `deploy/`, `gitea-issues/`, etc. : se placer dans ce répertoire avant d'exécuter.
|
||||
- **Racine des scripts ia_dev :** le dossier `ia_dev/` à la racine du workspace **smart_ide**. Pour `deploy/`, `git-issues/`, etc. : se placer dans ce répertoire avant d'exécuter.
|
||||
- **Variables / arguments :** `IA_PROJECT_ID=smart_ide` et/ou `--project smart_ide` selon le script ; référence : `docs/repo/ia-dev-project-conf-schema.md`.
|
||||
- **MAIL_TO (ticketing / mails) :** exporter `MAIL_TO` avec l'adresse pour l'environnement choisi, lue dans `projects/smart_ide/conf.json` (racine workspace) → `tickets.authorized_emails.to` : test → `AI.SMART_IDE.TEST@4nkweb.com`, pprod → `AI.SMART_IDE.PPROD@4nkweb.com`, prod → `AI.SMART_IDE.PROD@4nkweb.com`.
|
||||
- **Dépôt applicatif :** racine du workspace smart_ide ; `project_path` dans `conf.json` doit y pointer. Doc principale du monorepo : `docs/` à la racine (`projects/smart_ide/docs` sous ia_dev souvent absent).
|
||||
|
||||
@ -9,7 +9,7 @@ is_background: false
|
||||
|
||||
- **Identifiant projet ia_dev :** `smart_ide` (conf : `projects/smart_ide/` à la racine ; lien sous `ia_dev/projects/smart_ide`).
|
||||
- **Environnement cible :** `test`, `pprod` ou `prod`. Le reprendre dans le message utilisateur ; **si absent, le demander** avant d'exécuter des scripts dépendants du env.
|
||||
- **Racine des scripts ia_dev :** le dossier `ia_dev/` à la racine du workspace **smart_ide**. Pour `deploy/`, `gitea-issues/`, etc. : se placer dans ce répertoire avant d'exécuter.
|
||||
- **Racine des scripts ia_dev :** le dossier `ia_dev/` à la racine du workspace **smart_ide**. Pour `deploy/`, `git-issues/`, etc. : se placer dans ce répertoire avant d'exécuter.
|
||||
- **Variables / arguments :** `IA_PROJECT_ID=smart_ide` et/ou `--project smart_ide` selon le script ; référence : `docs/repo/ia-dev-project-conf-schema.md`.
|
||||
- **MAIL_TO (ticketing / mails) :** exporter `MAIL_TO` avec l'adresse pour l'environnement choisi, lue dans `projects/smart_ide/conf.json` (racine workspace) → `tickets.authorized_emails.to` : test → `AI.SMART_IDE.TEST@4nkweb.com`, pprod → `AI.SMART_IDE.PPROD@4nkweb.com`, prod → `AI.SMART_IDE.PROD@4nkweb.com`.
|
||||
- **Dépôt applicatif :** racine du workspace smart_ide ; `project_path` dans `conf.json` doit y pointer. Doc principale du monorepo : `docs/` à la racine (`projects/smart_ide/docs` sous ia_dev souvent absent).
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
---
|
||||
name: ia-dev-gitea-issues-process
|
||||
name: ia-dev-git-issues-process
|
||||
description: Traitement issues Gitea smart_ide via ia_dev. Indiquer l'environnement.
|
||||
model: inherit
|
||||
is_background: false
|
||||
@ -9,7 +9,7 @@ is_background: false
|
||||
|
||||
- **Identifiant projet ia_dev :** `smart_ide` (conf : `projects/smart_ide/` à la racine ; lien sous `ia_dev/projects/smart_ide`).
|
||||
- **Environnement cible :** `test`, `pprod` ou `prod`. Le reprendre dans le message utilisateur ; **si absent, le demander** avant d'exécuter des scripts dépendants du env.
|
||||
- **Racine des scripts ia_dev :** le dossier `ia_dev/` à la racine du workspace **smart_ide**. Pour `deploy/`, `gitea-issues/`, etc. : se placer dans ce répertoire avant d'exécuter.
|
||||
- **Racine des scripts ia_dev :** le dossier `ia_dev/` à la racine du workspace **smart_ide**. Pour `deploy/`, `git-issues/`, etc. : se placer dans ce répertoire avant d'exécuter.
|
||||
- **Variables / arguments :** `IA_PROJECT_ID=smart_ide` et/ou `--project smart_ide` selon le script ; référence : `docs/repo/ia-dev-project-conf-schema.md`.
|
||||
- **MAIL_TO (ticketing / mails) :** exporter `MAIL_TO` avec l'adresse pour l'environnement choisi, lue dans `projects/smart_ide/conf.json` (racine workspace) → `tickets.authorized_emails.to` : test → `AI.SMART_IDE.TEST@4nkweb.com`, pprod → `AI.SMART_IDE.PPROD@4nkweb.com`, prod → `AI.SMART_IDE.PROD@4nkweb.com`.
|
||||
- **Dépôt applicatif :** racine du workspace smart_ide ; `project_path` dans `conf.json` doit y pointer. Doc principale du monorepo : `docs/` à la racine (`projects/smart_ide/docs` sous ia_dev souvent absent).
|
||||
@ -18,6 +18,6 @@ is_background: false
|
||||
|
||||
## Délégation
|
||||
|
||||
Lire le fichier **`ia_dev/.smartIde/agents/gitea-issues-process.md`** et appliquer **intégralement** sa procédure et ses contraintes, en respectant le contexte ci-dessus.
|
||||
Lire le fichier **`ia_dev/.smartIde/agents/git-issues-process.md`** et appliquer **intégralement** sa procédure et ses contraintes, en respectant le contexte ci-dessus.
|
||||
|
||||
**Référence résolution projet / env :** `docs/repo/ia-dev-project-conf-schema.md`.
|
||||
@ -9,7 +9,7 @@ is_background: false
|
||||
|
||||
- **Identifiant projet ia_dev :** `smart_ide` (conf : `projects/smart_ide/` à la racine ; lien sous `ia_dev/projects/smart_ide`).
|
||||
- **Environnement cible :** `test`, `pprod` ou `prod`. Le reprendre dans le message utilisateur ; **si absent, le demander** avant d'exécuter des scripts dépendants du env.
|
||||
- **Racine des scripts ia_dev :** le dossier `ia_dev/` à la racine du workspace **smart_ide**. Pour `deploy/`, `gitea-issues/`, etc. : se placer dans ce répertoire avant d'exécuter.
|
||||
- **Racine des scripts ia_dev :** le dossier `ia_dev/` à la racine du workspace **smart_ide**. Pour `deploy/`, `git-issues/`, etc. : se placer dans ce répertoire avant d'exécuter.
|
||||
- **Variables / arguments :** `IA_PROJECT_ID=smart_ide` et/ou `--project smart_ide` selon le script ; référence : `docs/repo/ia-dev-project-conf-schema.md`.
|
||||
- **MAIL_TO (ticketing / mails) :** exporter `MAIL_TO` avec l'adresse pour l'environnement choisi, lue dans `projects/smart_ide/conf.json` (racine workspace) → `tickets.authorized_emails.to` : test → `AI.SMART_IDE.TEST@4nkweb.com`, pprod → `AI.SMART_IDE.PPROD@4nkweb.com`, prod → `AI.SMART_IDE.PROD@4nkweb.com`.
|
||||
- **Dépôt applicatif :** racine du workspace smart_ide ; `project_path` dans `conf.json` doit y pointer. Doc principale du monorepo : `docs/` à la racine (`projects/smart_ide/docs` sous ia_dev souvent absent).
|
||||
|
||||
@ -9,7 +9,7 @@ is_background: false
|
||||
|
||||
- **Identifiant projet ia_dev :** `smart_ide` (conf : `projects/smart_ide/` à la racine ; lien sous `ia_dev/projects/smart_ide`).
|
||||
- **Environnement cible :** `test`, `pprod` ou `prod`. Le reprendre dans le message utilisateur ; **si absent, le demander** avant d'exécuter des scripts dépendants du env.
|
||||
- **Racine des scripts ia_dev :** le dossier `ia_dev/` à la racine du workspace **smart_ide**. Pour `deploy/`, `gitea-issues/`, etc. : se placer dans ce répertoire avant d'exécuter.
|
||||
- **Racine des scripts ia_dev :** le dossier `ia_dev/` à la racine du workspace **smart_ide**. Pour `deploy/`, `git-issues/`, etc. : se placer dans ce répertoire avant d'exécuter.
|
||||
- **Variables / arguments :** `IA_PROJECT_ID=smart_ide` et/ou `--project smart_ide` selon le script ; référence : `docs/repo/ia-dev-project-conf-schema.md`.
|
||||
- **MAIL_TO (ticketing / mails) :** exporter `MAIL_TO` avec l'adresse pour l'environnement choisi, lue dans `projects/smart_ide/conf.json` (racine workspace) → `tickets.authorized_emails.to` : test → `AI.SMART_IDE.TEST@4nkweb.com`, pprod → `AI.SMART_IDE.PPROD@4nkweb.com`, prod → `AI.SMART_IDE.PROD@4nkweb.com`.
|
||||
- **Dépôt applicatif :** racine du workspace smart_ide ; `project_path` dans `conf.json` doit y pointer. Doc principale du monorepo : `docs/` à la racine (`projects/smart_ide/docs` sous ia_dev souvent absent).
|
||||
|
||||
@ -9,7 +9,7 @@ is_background: false
|
||||
|
||||
- **Identifiant projet ia_dev :** `smart_ide` (conf : `projects/smart_ide/` à la racine ; lien sous `ia_dev/projects/smart_ide`).
|
||||
- **Environnement cible :** `test`, `pprod` ou `prod`. Le reprendre dans le message utilisateur ; **si absent, le demander** avant d'exécuter des scripts dépendants du env.
|
||||
- **Racine des scripts ia_dev :** le dossier `ia_dev/` à la racine du workspace **smart_ide**. Pour `deploy/`, `gitea-issues/`, etc. : se placer dans ce répertoire avant d'exécuter.
|
||||
- **Racine des scripts ia_dev :** le dossier `ia_dev/` à la racine du workspace **smart_ide**. Pour `deploy/`, `git-issues/`, etc. : se placer dans ce répertoire avant d'exécuter.
|
||||
- **Variables / arguments :** `IA_PROJECT_ID=smart_ide` et/ou `--project smart_ide` selon le script ; référence : `docs/repo/ia-dev-project-conf-schema.md`.
|
||||
- **MAIL_TO (ticketing / mails) :** exporter `MAIL_TO` avec l'adresse pour l'environnement choisi, lue dans `projects/smart_ide/conf.json` (racine workspace) → `tickets.authorized_emails.to` : test → `AI.SMART_IDE.TEST@4nkweb.com`, pprod → `AI.SMART_IDE.PPROD@4nkweb.com`, prod → `AI.SMART_IDE.PROD@4nkweb.com`.
|
||||
- **Dépôt applicatif :** racine du workspace smart_ide ; `project_path` dans `conf.json` doit y pointer. Doc principale du monorepo : `docs/` à la racine (`projects/smart_ide/docs` sous ia_dev souvent absent).
|
||||
|
||||
@ -9,7 +9,7 @@ is_background: false
|
||||
|
||||
- **Identifiant projet ia_dev :** `smart_ide` (conf : `projects/smart_ide/` à la racine ; lien sous `ia_dev/projects/smart_ide`).
|
||||
- **Environnement cible :** `test`, `pprod` ou `prod`. Le reprendre dans le message utilisateur ; **si absent, le demander** avant d'exécuter des scripts dépendants du env.
|
||||
- **Racine des scripts ia_dev :** le dossier `ia_dev/` à la racine du workspace **smart_ide**. Pour `deploy/`, `gitea-issues/`, etc. : se placer dans ce répertoire avant d'exécuter.
|
||||
- **Racine des scripts ia_dev :** le dossier `ia_dev/` à la racine du workspace **smart_ide**. Pour `deploy/`, `git-issues/`, etc. : se placer dans ce répertoire avant d'exécuter.
|
||||
- **Variables / arguments :** `IA_PROJECT_ID=smart_ide` et/ou `--project smart_ide` selon le script ; référence : `docs/repo/ia-dev-project-conf-schema.md`.
|
||||
- **MAIL_TO (ticketing / mails) :** exporter `MAIL_TO` avec l'adresse pour l'environnement choisi, lue dans `projects/smart_ide/conf.json` (racine workspace) → `tickets.authorized_emails.to` : test → `AI.SMART_IDE.TEST@4nkweb.com`, pprod → `AI.SMART_IDE.PPROD@4nkweb.com`, prod → `AI.SMART_IDE.PROD@4nkweb.com`.
|
||||
- **Dépôt applicatif :** racine du workspace smart_ide ; `project_path` dans `conf.json` doit y pointer. Doc principale du monorepo : `docs/` à la racine (`projects/smart_ide/docs` sous ia_dev souvent absent).
|
||||
|
||||
@ -11,7 +11,7 @@ Quand le périmètre touche ce dépôt et le module **`ia_dev/`** :
|
||||
- **Projet courant (IDE / smart_ide) :** lire dans l’ordre : fichier local **`projects/active-project.json`** (champ **`id`**, non versionné — copier depuis `projects/active-project.json.example`) ; sinon variable **`SMART_IDE_PROJECT_ID`** ; sinon paramètre workspace **`smartIde.activeProjectId`** dans le fichier **`.code-workspace`** ou **`.vscode/settings.json`** ; sinon défaut **`smart_ide`**. La conf versionnée du projet est **`projects/<id>/conf.json`** ([docs/repo/projects-directory.md](../../docs/repo/projects-directory.md)). Données déployées et SSH : [docs/features/remote-deployed-data-ssh.md](../../docs/features/remote-deployed-data-ssh.md).
|
||||
- **Lien ia_dev :** pour l’id choisi, `ia_dev/projects/<id>` doit être un **lien** vers `../../projects/<id>` lorsque les scripts ia_dev résolvent ce chemin (script `scripts/ensure-ia-dev-smart-ide-project-link.sh` pour `smart_ide`).
|
||||
- **Environnement :** `test` | `pprod` | `prod`. Le reprendre depuis le message utilisateur ou depuis **`default_env`** dans `projects/active-project.json` ; **demander** s’il manque avant des actions dépendantes du env.
|
||||
- **Exécution des scripts ia_dev :** répertoire courant = racine **`ia_dev/`** (dans le workspace smart_ide). Pas de mélange avec la racine smart_ide pour `deploy/`, `gitea-issues/`, etc.
|
||||
- **Exécution des scripts ia_dev :** répertoire courant = racine **`ia_dev/`** (dans le workspace smart_ide). Pas de mélange avec la racine smart_ide pour `deploy/`, `git-issues/`, etc.
|
||||
- **Sélection projet pour les scripts :** `IA_PROJECT_ID=<id>` et/ou `--project <id>` selon le script ; détail : [docs/repo/ia-dev-project-conf-schema.md](../../docs/repo/ia-dev-project-conf-schema.md).
|
||||
- **MAIL_TO (ticketing / mails) :** lire `projects/<id>/conf.json` → `tickets.authorized_emails.to` pour l’env choisi.
|
||||
- **Code et doc applicative :** pour le monorepo smart_ide, racine du workspace et `docs/` ; pour un autre id, suivre **`project_path`** dans `conf.json`.
|
||||
|
||||
3
builazoo/README.md
Normal file
3
builazoo/README.md
Normal file
@ -0,0 +1,3 @@
|
||||
# builazoo
|
||||
|
||||
Racine du dépôt applicatif **builazoo** (clone Git ici). Configuration **smart_ide** / **ia_dev** : [`../projects/builazoo/conf.json`](../projects/builazoo/conf.json).
|
||||
4
cron/fragments/builazoo.cron
Normal file
4
cron/fragments/builazoo.cron
Normal file
@ -0,0 +1,4 @@
|
||||
# Projet builazoo — préférer cron/config.env (PULL_SYNC_MODE=project, PULL_SYNC_PROJECT_ID=builazoo)
|
||||
# puis ./cron/git-pull-wrapper.sh depuis crontab, ou systemd (install-git-pull-systemd-user.sh).
|
||||
#
|
||||
# */15 * * * * USER cd SMART_IDE_ROOT && ./cron/git-pull-wrapper.sh
|
||||
@ -11,6 +11,7 @@ Documentation des **API HTTP** exposées par les services sous [`services/`](../
|
||||
| **local-office** | `X-API-Key` | `8000` (exemple run) | [local-office.md](./local-office.md) |
|
||||
| **ia-dev-gateway** | Bearer | `37144` (spécification) | [ia-dev-gateway.md](./ia-dev-gateway.md) |
|
||||
| **smart_ide-orchestrator** | Bearer (spécification) | `37145` (spécification) | [orchestrator.md](./orchestrator.md) |
|
||||
| **anythingllm-devtools** | Bearer | `37146` | [anythingllm-devtools-api.md](./anythingllm-devtools-api.md) |
|
||||
| **docv** (externe) | selon dépôt Enso | selon déploiement | [docv.md](./docv.md) |
|
||||
|
||||
**OpenAPI** : FastAPI expose une spec interactive pour **langextract-api** (`/docs`) et **local-office** (`/docs`) une fois le service démarré.
|
||||
|
||||
66
docs/API/anythingllm-devtools-api.md
Normal file
66
docs/API/anythingllm-devtools-api.md
Normal file
@ -0,0 +1,66 @@
|
||||
# anythingllm-devtools — API HTTP
|
||||
|
||||
Service sous [`services/anythingllm-devtools/`](../../services/anythingllm-devtools/). Écoute par défaut sur **`127.0.0.1:37146`**.
|
||||
|
||||
## Authentification
|
||||
|
||||
Toutes les routes sauf **`GET /health`** exigent :
|
||||
|
||||
```http
|
||||
Authorization: Bearer <ANYTHINGLLM_DEVTOOLS_TOKEN>
|
||||
```
|
||||
|
||||
## Routes
|
||||
|
||||
### `GET /health`
|
||||
|
||||
Sans auth. Corps JSON : `{ "ok": true, "service": "anythingllm-devtools" }`.
|
||||
|
||||
### `GET /v1/workspaces`
|
||||
|
||||
Liste les workspaces AnythingLLM (`GET …/api/v1/workspaces` amont).
|
||||
|
||||
Réponse `200` :
|
||||
|
||||
```json
|
||||
{ "workspaces": [ { "id": 1, "name": "…", "slug": "…" } ] }
|
||||
```
|
||||
|
||||
Erreurs `503` si `ANYTHINGLLM_BASE_URL` ou `ANYTHINGLLM_API_KEY` manquent.
|
||||
|
||||
### `POST /v1/devtools/run`
|
||||
|
||||
Exécute une ou plusieurs lignes de commandes (même grammaire que l’ancien panneau extension : `/repos-clone`, `/repos-clone-sync`, `repos-list`, `/repos-load`, `/repos-load-sync`, `/workspace-load`, `/workspace-sync`, `help`).
|
||||
|
||||
Corps JSON (une des deux formes) :
|
||||
|
||||
```json
|
||||
{ "script": "/repos-list\n/workspace-sync my-repo" }
|
||||
```
|
||||
|
||||
```json
|
||||
{ "lines": [ "/repos-list", "/workspace-sync my-repo" ] }
|
||||
```
|
||||
|
||||
Réponse `200` :
|
||||
|
||||
```json
|
||||
{
|
||||
"ok": true,
|
||||
"output": "…",
|
||||
"actions": [
|
||||
{ "type": "openFolder", "path": "/abs/path/to/repo" },
|
||||
{ "type": "openWorkspaceUrl", "slug": "my-slug", "url": "https://…/workspace/my-slug" }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Le serveur n’ouvre ni dossier ni navigateur : `actions` indique ce que le client peut faire localement.
|
||||
|
||||
Erreurs `400` : corps JSON invalide ou message d’erreur métier (ex. repo introuvable).
|
||||
|
||||
## Voir aussi
|
||||
|
||||
- [anythingllm-workspaces.md](../anythingllm-workspaces.md)
|
||||
- [extension-anythingllm-workspaces.md](../repo/extension-anythingllm-workspaces.md) (extension dépréciée côté surface IDE)
|
||||
- [repos-devtools-server.md](./repos-devtools-server.md)
|
||||
@ -22,7 +22,8 @@ Vue d’ensemble et index complet : **[repo/README.md](./repo/README.md)**. Règ
|
||||
| [repo/ia-dev-shared-lib.md](./repo/ia-dev-shared-lib.md) | `ia_dev/lib/project_config.sh` |
|
||||
| [repo/service-*.md](./repo/README.md) | Exploitation de chaque micro-service (voir index `repo/README`) |
|
||||
| [repo/script-anythingllm-pull-sync.md](./repo/script-anythingllm-pull-sync.md) | Hook post-merge → AnythingLLM |
|
||||
| [repo/extension-anythingllm-workspaces.md](./repo/extension-anythingllm-workspaces.md) | Extension VS Code / Cursor |
|
||||
| [repo/service-anythingllm-devtools.md](./repo/service-anythingllm-devtools.md) | Service HTTP AnythingLLM + devtools |
|
||||
| [repo/extension-anythingllm-workspaces.md](./repo/extension-anythingllm-workspaces.md) | Extension VS Code / Cursor (héritée) |
|
||||
|
||||
Les fichiers **`README.md`** sous `services/*/`, `cron/`, `projects/`, etc. ne font que **renvoyer** vers ces pages.
|
||||
|
||||
|
||||
@ -13,5 +13,6 @@
|
||||
## Exploitation
|
||||
|
||||
- Instance Docker décrite dans [services.md](./services.md) : stockage hôte typiquement sous `$HOME/anythingllm` sur l’**hôte qui exécute le conteneur** — en **première cible de déploiement**, cet hôte est le **serveur distant** (SSH), pas obligatoirement le poste Linux client ; la création de **plusieurs workspaces** se fait dans l’UI AnythingLLM (ou via API) en conservant la convention « un workspace = un projet ».
|
||||
- Orchestration locale (clone, chargement dépôt, création workspace, upload RAG initial) : service HTTP **`anythingllm-devtools`** — [API/anythingllm-devtools-api.md](./API/anythingllm-devtools-api.md), [services/anythingllm-devtools/README.md](../services/anythingllm-devtools/README.md).
|
||||
- L’orchestrateur IDE décide **quand** interroger AnythingLLM (voir [system-architecture.md](./system-architecture.md)). L’URL vue depuis le client peut exiger un **tunnel SSH** ou un rebond réseau : [deployment-target.md](./deployment-target.md).
|
||||
- Stratégie d’ensemble (Git, hooks, scripts de synchro) : [ecosystem-architecture-and-sync.md](./ecosystem-architecture-and-sync.md).
|
||||
|
||||
@ -90,7 +90,7 @@ Objectif : après un changement **tracé dans Git**, les systèmes en aval (Anyt
|
||||
### 4.1 Ordre de référence (nouvelle machine ou post-clone)
|
||||
|
||||
1. Cloner **smart_ide** : `git clone …` (le répertoire **`ia_dev/`** suit le même historique Git que le monorepo).
|
||||
2. Recréer le lien conf projet si besoin : `./scripts/ensure-ia-dev-smart-ide-project-link.sh` ([repo/projects-directory.md](./repo/projects-directory.md)).
|
||||
2. Recréer les liens `ia_dev/projects/<id>` si besoin : `./scripts/ensure-ia-dev-project-link.sh smart_ide` (ou le wrapper `ensure-ia-dev-smart-ide-project-link.sh`) — voir [repo/projects-directory.md](./repo/projects-directory.md).
|
||||
3. Cloner les **projets applicatifs** à l’emplacement convenu (ex. `../projects/<id>/`) et vérifier les chemins **absolus** dans `projects/<id>/conf.json` si `ia_dev` doit les piloter.
|
||||
4. Démarrer **Ollama** et **AnythingLLM** sur l’hôte ([services.md](./services.md)) ; créer les **workspaces** et noter les **slugs**.
|
||||
5. Configurer l’environnement de synchro AnythingLLM : `~/.config/4nk/anythingllm-sync.env` (URL, clé API) — ne pas commiter les secrets.
|
||||
@ -105,7 +105,7 @@ Objectif : après un changement **tracé dans Git**, les systèmes en aval (Anyt
|
||||
### 4.3 Cycle de travail sur smart_ide
|
||||
|
||||
1. `git pull` sur smart_ide (inclut les mises à jour sous **`ia_dev/`**).
|
||||
2. Réexécuter **`ensure-ia-dev-smart-ide-project-link.sh`** si le répertoire `ia_dev/projects/` a été réinitialisé.
|
||||
2. Réexécuter **`ensure-ia-dev-project-link.sh`** pour chaque id versionné (`smart_ide`, `enso`, `builazoo`, …) si `ia_dev/projects/` a été réinitialisé.
|
||||
3. Option : installer le même hook **post-merge** sur le dépôt **smart_ide** si un workspace AnythingLLM est dédié au monorepo (fichier `.anythingllm.json` + slug).
|
||||
|
||||
### 4.4 Agents, déploiement, ticketing (`ia_dev`)
|
||||
|
||||
@ -14,7 +14,7 @@ Pour chaque **projet logique** (ex. périmètre docv, autre produit) :
|
||||
|
||||
1. **Clone Git** : le dépôt applicatif doit être **déjà cloné** au même titre que les autres projets de l’espace de travail, en général sous une **racine de clones** **distincte** du dossier `./projects/` du monorepo (voir [repo/projects-directory.md](../repo/projects-directory.md)) — convention fréquente : répertoire **frère** `../projects/<nom>/` par rapport à la racine `smart_ide`.
|
||||
2. **AnythingLLM** : le projet doit être **rattaché à un workspace** AnythingLLM (un workspace par projet). L’alimentation du workspace repose sur un corpus **aligné sur les données déployées** : récupération via **SSH** depuis test / pprod / prod puis pipeline de synchro (voir [remote-deployed-data-ssh.md](./remote-deployed-data-ssh.md), [anythingllm-workspaces.md](../anythingllm-workspaces.md), scripts sous `scripts/`).
|
||||
3. **Configuration ia_dev** : lorsqu’un id projet est enregistré pour les agents ou le ticketing, un `conf.json` peut être versionné sous **`smart_ide/projects/<id>/conf.json`** ; les scripts `ia_dev` y accèdent via le lien `ia_dev/projects/<id>` lorsque le script [`ensure-ia-dev-smart-ide-project-link.sh`](../../scripts/ensure-ia-dev-smart-ide-project-link.sh) (ou équivalent) a été exécuté.
|
||||
3. **Configuration ia_dev** : lorsqu’un id projet est enregistré pour les agents ou le ticketing, un `conf.json` peut être versionné sous **`smart_ide/projects/<id>/conf.json`** ; les scripts `ia_dev` y accèdent via le lien `ia_dev/projects/<id>` lorsque le script [`ensure-ia-dev-project-link.sh`](../../scripts/ensure-ia-dev-project-link.sh) `<id>` (ou le wrapper `ensure-ia-dev-smart-ide-project-link.sh` pour `smart_ide`) a été exécuté.
|
||||
|
||||
## Flux cible (vue simplifiée)
|
||||
|
||||
|
||||
@ -9,15 +9,16 @@
|
||||
## Comportement
|
||||
|
||||
1. **Serveur `repos-devtools-server`** : après `git clone` réussi, copie **`templates/4nkaiignore.default`** vers **`<repo>/.4nkaiignore`** si absent.
|
||||
2. **Extension 0.3.0** : après `/repos-clone-sync`, `/repos-load-sync`, ou sur **`/workspace-sync <nom>`**, si l’option **`anythingllm.initialSyncAfterClone`** n’est pas à `false` :
|
||||
- assure **`.4nkaiignore`** depuis le template bundlé si toujours absent ;
|
||||
2. **Service `anythingllm-devtools`** (ou extension héritée) : après `/repos-clone-sync`, `/repos-load-sync`, ou sur **`/workspace-sync <nom>`**, si la synchro initiale n’est pas désactivée (`ANYTHINGLLM_INITIAL_SYNC_AFTER_CLONE` côté service, ou `anythingllm.initialSyncAfterClone` côté extension) :
|
||||
- assure **`.4nkaiignore`** depuis le template si toujours absent ;
|
||||
- parcourt le dépôt, applique règles de base + `.4nkaiignore` ;
|
||||
- envoie chaque fichier accepté via **`POST /api/v1/document/upload`** avec **`addToWorkspaces`** = slug du workspace.
|
||||
|
||||
## Fichier type
|
||||
|
||||
- **`extensions/anythingllm-workspaces/templates/4nkaiignore.default`**
|
||||
- **`services/repos-devtools-server/templates/4nkaiignore.default`** (même contenu ; à maintenir en parité).
|
||||
- **`services/anythingllm-devtools/templates/4nkaiignore.default`** (référence)
|
||||
- **`services/repos-devtools-server/templates/4nkaiignore.default`** (même contenu ; à maintenir en parité)
|
||||
- **`extensions/anythingllm-workspaces/templates/4nkaiignore.default`** (copie héritée)
|
||||
|
||||
L’utilisateur renomme / copie en **`.4nkaiignore`** à la racine du projet et adapte les règles.
|
||||
|
||||
@ -32,4 +33,4 @@ Le **collecteur / processeur de documents** doit être joignable par l’instanc
|
||||
|
||||
## Modalités de déploiement
|
||||
|
||||
- Rebuild et redémarrage de **repos-devtools-server** ; repackaging / réinstallation de l’extension **0.3.0+**.
|
||||
- Rebuild et redémarrage de **repos-devtools-server** et de **anythingllm-devtools** ; repackaging / réinstallation de l’extension uniquement si vous conservez encore la surface IDE.
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
# Portage AnythingLLM Workspaces → Lapce (`core_ide/`)
|
||||
|
||||
L’extension [extensions/anythingllm-workspaces/](../../extensions/anythingllm-workspaces/) cible **VS Code / Cursor** (`vscode` API). Lapce utilise un **modèle de plugins** distinct (Volt / WASI, RPC). Ce document découpe le travail en **phases** pour une interface cohérente avec [platform-target.md](../platform-target.md).
|
||||
L’orchestration AnythingLLM + repos-devtools est exposée en **service HTTP** [`services/anythingllm-devtools/`](../../services/anythingllm-devtools/) ; l’extension [extensions/anythingllm-workspaces/](../../extensions/anythingllm-workspaces/) reste une surface **VS Code / Cursor** héritée. Lapce utilise un **modèle de plugins** distinct (Volt / WASI, RPC). Ce document découpe le travail en **phases** pour une interface cohérente avec [platform-target.md](../platform-target.md).
|
||||
|
||||
## Phase 1 — Connectivité sans webview
|
||||
|
||||
@ -8,13 +8,13 @@ L’extension [extensions/anythingllm-workspaces/](../../extensions/anythingllm-
|
||||
- Commandes palette :
|
||||
- Lister les workspaces AnythingLLM → ouvrir URL dans le **navigateur système**.
|
||||
- Ouvrir l’UI web AnythingLLM.
|
||||
- Client HTTP vers `repos-devtools-server` et API AnythingLLM (réutiliser la logique des fichiers TypeScript comme **spécification** ; implémenter en Rust dans Lapce ou via petit binaire Node invoqué — choix d’équipe).
|
||||
- Pas de panneau Dev tools ; pas de sync RAG initiale depuis l’IDE.
|
||||
- Client HTTP vers **`anythingllm-devtools`** (qui appelle `repos-devtools-server` et AnythingLLM) — voir [API/anythingllm-devtools-api.md](../API/anythingllm-devtools-api.md) ; alternative : appels directs aux deux backends si politique projet l’exige.
|
||||
- Pas de panneau Dev tools embarqué obligatoire ; la sync RAG initiale peut être déclenchée via **`POST /v1/devtools/run`** sur l’hôte accessible.
|
||||
|
||||
## Phase 2 — Parité « Dev tools » et sync RAG
|
||||
|
||||
- Panneau ou vue dédiée : saisie des lignes de commande (`/repos-clone-sync`, `/workspace-sync`, …) comme décrit dans [repo/extension-anythingllm-workspaces.md](../repo/extension-anythingllm-workspaces.md).
|
||||
- Réimplémenter **initialRagSync** + `.4nkaiignore` (crate `ignore` ou équivalent Rust).
|
||||
- Panneau ou vue dédiée **ou** proxy vers le service : mêmes lignes de commande que `POST /v1/devtools/run` — [repo/extension-anythingllm-workspaces.md](../repo/extension-anythingllm-workspaces.md), [repo/service-anythingllm-devtools.md](../repo/service-anythingllm-devtools.md).
|
||||
- Réutiliser le service Node existant **ou** réimplémenter **initialRagSync** + `.4nkaiignore` (crate `ignore` ou équivalent Rust).
|
||||
- Ouvrir le dossier dépôt dans Lapce après clone (API workspace Lapce).
|
||||
|
||||
## Phase 3 — Orchestrateur
|
||||
|
||||
@ -1,13 +1,17 @@
|
||||
# Module `ia_dev` dans smart_ide
|
||||
|
||||
Le répertoire **`./ia_dev`** à la racine du monorepo **smart_ide** contient l’**équipe d’agents** (définitions sous `.smartIde/agents/`, `.smartIde/rules/`), **`deploy/`**, **`gitea-issues/`**, etc. Il est **versionné dans ce dépôt** (plus de sous-module Git séparé pour le checkout standard).
|
||||
Le répertoire **`./ia_dev`** à la racine du monorepo **smart_ide** contient l’**équipe d’agents** (définitions sous `.smartIde/agents/`, `.smartIde/rules/`), **`deploy/`**, **`git-issues/`**, etc. Il est **versionné dans ce dépôt** (plus de sous-module Git séparé pour le checkout standard).
|
||||
|
||||
Les **`conf.json` par projet** pour ce monorepo restent sous **`./projects/<id>/`** à la racine de **smart_ide** (voir [repo/projects-directory.md](./repo/projects-directory.md)), pas mélangés avec les clones applicatifs (`../projects/` ou autre). Un **lien symbolique** `ia_dev/projects/smart_ide` → `../../projects/smart_ide` permet aux scripts `ia_dev` de résoudre `projects/smart_ide/conf.json`. Après un clone neuf ou une réorganisation des dossiers, exécuter si besoin :
|
||||
Les **`conf.json` par projet** pour ce monorepo restent sous **`./projects/<id>/`** à la racine de **smart_ide** (voir [repo/projects-directory.md](./repo/projects-directory.md)), pas mélangés avec les clones applicatifs (`../projects/` ou autre). Des **liens symboliques** `ia_dev/projects/<id>` → `../../projects/<id>` (pour chaque id versionné, ex. `smart_ide`, `enso`, `builazoo`) permettent aux scripts `ia_dev` de résoudre les `conf.json` sous `projects/`. Après un clone neuf ou une réorganisation des dossiers :
|
||||
|
||||
```bash
|
||||
./scripts/ensure-ia-dev-smart-ide-project-link.sh
|
||||
./scripts/ensure-ia-dev-project-link.sh smart_ide
|
||||
./scripts/ensure-ia-dev-project-link.sh enso
|
||||
./scripts/ensure-ia-dev-project-link.sh builazoo
|
||||
```
|
||||
|
||||
Le script `ensure-ia-dev-smart-ide-project-link.sh` appelle `ensure-ia-dev-project-link.sh smart_ide`.
|
||||
|
||||
L’exécution des scripts reste **depuis la racine `ia_dev/`**, comme dans la documentation amont du dépôt historique [4nk/ia_dev](https://git.4nkweb.com/4nk/ia_dev.git) ; **smart_ide** fournit l’environnement IDE, les scripts hôte, les unités systemd et les journaux sous `logs/` ([repo/logs-directory.md](./repo/logs-directory.md)).
|
||||
|
||||
## Journaux
|
||||
|
||||
@ -5,7 +5,7 @@ Le dépôt **smart_ide** est enregistré dans le sous-module **`ia_dev`** sous l
|
||||
## Fichier de configuration
|
||||
|
||||
- **Source de vérité (versionnée dans ce monorepo) :** [`projects/smart_ide/conf.json`](../projects/smart_ide/conf.json) — chemins machine (`project_path`), URLs wiki et issues (`https://git.4nkweb.com/4nk/smart_ide/...`), boîtes mail autorisées pour le ticketing (envs test / pprod / prod).
|
||||
- **Sous-module `ia_dev` :** le chemin `ia_dev/projects/smart_ide/conf.json` doit résoudre le même fichier via le lien créé par [`scripts/ensure-ia-dev-smart-ide-project-link.sh`](../scripts/ensure-ia-dev-smart-ide-project-link.sh).
|
||||
- **Sous-module `ia_dev` :** le chemin `ia_dev/projects/smart_ide/conf.json` doit résoudre le même fichier via le lien créé par [`scripts/ensure-ia-dev-project-link.sh`](../scripts/ensure-ia-dev-project-link.sh) `smart_ide` (wrapper : [`ensure-ia-dev-smart-ide-project-link.sh`](../scripts/ensure-ia-dev-smart-ide-project-link.sh)).
|
||||
|
||||
Adapter **`project_path`** (et champs dérivés si vous ajoutez `build_dirs` / `deploy`) sur chaque poste ou serveur où `ia_dev` exécute des commandes sur ce dépôt. Les **clones** d’autres apps (docv, etc.) ne vont **pas** dans `./projects/` : voir [repo/projects-directory.md](./repo/projects-directory.md).
|
||||
|
||||
@ -27,7 +27,7 @@ Dans ce dépôt, les définitions **Cursor** sous [`.smartIde/agents/`](../.smar
|
||||
| `ia-dev-branch-align-by-script-from-test` | `branch-align-by-script-from-test.md` |
|
||||
| `ia-dev-change-to-all-branches` | `change-to-all-branches.md` |
|
||||
| `ia-dev-agent-loop` | `agent-loop.md` |
|
||||
| `ia-dev-gitea-issues-process` | `gitea-issues-process.md` |
|
||||
| `ia-dev-git-issues-process` | `git-issues-process.md` |
|
||||
| `ia-dev-setup-host` | `setup-host.md` |
|
||||
| `ia-dev-notary-ai-loop` | `notary-ai-loop.md` |
|
||||
| `ia-dev-notary-ai-process` | `notary-ai-process.md` |
|
||||
|
||||
@ -28,6 +28,7 @@ Toute la documentation **opérationnelle** qui vivait auparavant sous des `READM
|
||||
| [ia-dev-shared-lib.md](./ia-dev-shared-lib.md) | `ia_dev/lib/project_config.sh` et résolution projet |
|
||||
| **Services HTTP (exploitation)** | |
|
||||
| [service-repos-devtools.md](./service-repos-devtools.md) | Clone / liste / load Git sous racine contrôlée |
|
||||
| [service-anythingllm-devtools.md](./service-anythingllm-devtools.md) | AnythingLLM + repos-devtools + RAG initial (HTTP) |
|
||||
| [service-local-office.md](./service-local-office.md) | API Office (docx, …) |
|
||||
| [service-smart-ide-orchestrator.md](./service-smart-ide-orchestrator.md) | Routeur d’intentions HTTP |
|
||||
| [service-ia-dev-gateway.md](./service-ia-dev-gateway.md) | Gateway ia_dev (agents, runs, SSE) |
|
||||
@ -36,6 +37,6 @@ Toute la documentation **opérationnelle** qui vivait auparavant sous des `READM
|
||||
| [service-langextract.md](./service-langextract.md) | Wrapper LangExtract |
|
||||
| **Scripts et extensions** | |
|
||||
| [script-anythingllm-pull-sync.md](./script-anythingllm-pull-sync.md) | Hook post-merge → upload AnythingLLM |
|
||||
| [extension-anythingllm-workspaces.md](./extension-anythingllm-workspaces.md) | Extension VS Code / Cursor AnythingLLM |
|
||||
| [extension-anythingllm-workspaces.md](./extension-anythingllm-workspaces.md) | Extension VS Code / Cursor AnythingLLM (héritée) |
|
||||
|
||||
Les **spécifications** détaillées (contrats HTTP, sécurité, orchestration) restent dans [../API/README.md](../API/README.md) et [../features/](../features/).
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
# Extension AnythingLLM (`extensions/anythingllm-workspaces/`)
|
||||
|
||||
Extension **VS Code / Cursor** : API développeur AnythingLLM (workspaces, documents), **repos-devtools-server** optionnel, panneau **Dev tools**, upload RAG initial après clone/load via **`.4nkaiignore`**.
|
||||
**Surface préférée** : le service HTTP [`services/anythingllm-devtools/`](../../services/anythingllm-devtools/) — même orchestration (repos-devtools, workspaces AnythingLLM, upload RAG initial `.4nkaiignore`) ; l’orchestrateur, les agents et les scripts appellent ce service plutôt que l’IDE.
|
||||
|
||||
Extension **VS Code / Cursor** (héritée) : API développeur AnythingLLM, **repos-devtools-server** optionnel, panneau **Dev tools**, mêmes commandes que `POST /v1/devtools/run` côté service.
|
||||
|
||||
## Prérequis
|
||||
|
||||
@ -26,7 +28,7 @@ List workspaces, ouvrir UI web, panneau Dev tools, lignes de commande scriptées
|
||||
|
||||
## `.4nkaiignore`
|
||||
|
||||
- Modèle : `extensions/anythingllm-workspaces/templates/4nkaiignore.default` (aligné avec repos-devtools-server).
|
||||
- Modèle canonique : `services/anythingllm-devtools/templates/4nkaiignore.default` (aligné avec repos-devtools-server) ; copie sous l’extension conservée pour compatibilité.
|
||||
- À la racine du dépôt cible : fichier **`.4nkaiignore`**.
|
||||
- Filtrage : paquet **`ignore`** (sémantique gitignore) + règles de base (`.git/`, `node_modules/`, …).
|
||||
|
||||
|
||||
@ -42,7 +42,7 @@ Les agents ne modifient pas `projects/<id>/conf.json` sans validation humaine ex
|
||||
|
||||
Détail ticketing : `ia_dev/projects/ia_dev/docs/TICKETS_SPOOL_FORMAT.md`.
|
||||
|
||||
**.secrets à la racine ia_dev** : `token`, `gitea-issues/agent-loop.env`, `gitea-issues/imap-bridge.env`.
|
||||
**.secrets à la racine ia_dev** : `token`, `git-issues/agent-loop.env`, `git-issues/imap-bridge.env`.
|
||||
|
||||
## Exemple minimal
|
||||
|
||||
|
||||
@ -7,7 +7,7 @@ Dépôt de pilotage par l'IA pour les projets : **équipe d'agents IA** dont le
|
||||
## Usage (standalone)
|
||||
|
||||
- **Racine d'exécution** : tous les scripts sont lancés depuis la **racine de ia_dev** (ce dépôt). L'id projet est résolu par **MAIL_TO**, **AI_AGENT_TOKEN**, **`IA_PROJECT_ID`**, **`--project`**, ou premier argument selon le script (voir [ia-dev-project-conf-schema.md](./ia-dev-project-conf-schema.md)).
|
||||
- **Config** : dans `projects/<id>/conf.json`, les chemins vers les dépôts projet peuvent être **absolus** ou **relatifs à la racine du monorepo smart_ide** lorsque ia_dev y est intégré (`lib/conf_path_resolve.sh`). Les champs `mail.imap_bridge_env` et `git.token_file` sont **relatifs à la racine de ia_dev**. Le répertoire `.secrets` à la racine de ia_dev contient `token` et `gitea-issues/agent-loop.env`, `gitea-issues/imap-bridge.env`.
|
||||
- **Config** : dans `projects/<id>/conf.json`, les chemins vers les dépôts projet peuvent être **absolus** ou **relatifs à la racine du monorepo smart_ide** lorsque ia_dev y est intégré (`lib/conf_path_resolve.sh`). Les champs `mail.imap_bridge_env` et `git.token_file` sont **relatifs à la racine de ia_dev**. Le répertoire `.secrets` à la racine de ia_dev contient `token` et `git-issues/agent-loop.env`, `git-issues/imap-bridge.env`.
|
||||
|
||||
## Agents et domaines
|
||||
|
||||
@ -17,21 +17,21 @@ Chaque agent indique où se trouve la doc : **projets gérés** → `projects/<i
|
||||
|
||||
| Domaine | Agents / composants |
|
||||
|---------|---------------------|
|
||||
| **Doc** | `docupdate` ; `ia_dev/projects/ia_dev/docs/` ; migration wiki (`gitea-issues/wiki-migrate-docs.sh`). |
|
||||
| **Doc** | `docupdate` ; `ia_dev/projects/ia_dev/docs/` ; migration wiki (`git-issues/wiki-migrate-docs.sh`). |
|
||||
| **Code** | `fix`, `evol`, `code`, `fix-search` ; workflow correctifs/évolutions. |
|
||||
| **Ticketing** | `gitea-issues-process`, `agent-loop` ; spooler `projects/<id>/data/issues` ; scripts `gitea-issues/`. |
|
||||
| **Ticketing** | `git-issues-process`, `agent-loop` ; spooler `projects/<id>/data/issues` ; scripts `git-issues/`. |
|
||||
| **IA notaire (ai_working_help)** | `notary-ai-loop`, `notary-ai-process` ; API `ai_working_help/server.js` ; spooler `projects/<id>/data/notary-ai/{pending,responded}`. |
|
||||
| **DevOps** | `push-by-script`, `deploy-by-script`, `deploy-pprod-or-prod`, `branch-align-by-script-from-test`, `change-to-all-branches` ; scripts `deploy/`. |
|
||||
| **Sécurité / Qualité** | Règles `.smartIde/rules/` ; pas de secrets en dur ; `fix-lint` ; clôture obligatoire (`.smartIde/rules/cloture-evolution.mdc`). |
|
||||
|
||||
Référence détaillée : `ia_dev/projects/ia_dev/docs/GITEA_ISSUES_SCRIPTS_AGENTS.md`. Index : `ia_dev/projects/ia_dev/docs/README.md`.
|
||||
Référence détaillée : `ia_dev/projects/ia_dev/docs/GIT_ISSUES_SCRIPTS_AGENTS.md`. Index : `ia_dev/projects/ia_dev/docs/README.md`.
|
||||
|
||||
## Répertoire d'exécution (standalone)
|
||||
|
||||
Tous les scripts sont invoqués depuis la **racine de ia_dev**.
|
||||
|
||||
- **deploy/** : déploient les **projets configurés**, pas ia_dev.
|
||||
- **gitea-issues/** : logs et data par projet sous `projects/<id>/logs/` et `projects/<id>/data/issues/`.
|
||||
- **git-issues/** : logs et data par projet sous `projects/<id>/logs/` et `projects/<id>/data/issues/`.
|
||||
- **ai_working_help/** : API et scripts `notary-ai/` ; doc `ai_working_help/docs/notary-ai-api.md`.
|
||||
|
||||
## Scripts centralisés (`deploy/`)
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
|
||||
## `project_config.sh`
|
||||
|
||||
Sourcé par les scripts **deploy** et **gitea-issues** pour résoudre l’**id** projet et le chemin vers son JSON.
|
||||
Sourcé par les scripts **deploy** et **git-issues** pour résoudre l’**id** projet et le chemin vers son JSON.
|
||||
|
||||
**Usage standalone** : exécution depuis la racine **ia_dev** ; définir **`IA_DEV_ROOT`** avant source si besoin.
|
||||
|
||||
|
||||
@ -4,7 +4,7 @@ Le répertoire **`ia_dev/`** à la racine du monorepo **smart_ide** est le **mod
|
||||
|
||||
## Rôle
|
||||
|
||||
- Scripts **`ia_dev/deploy/`**, **`ia_dev/gitea-issues/`**, outillage **`ia_dev/tools/`**, définitions **`ia_dev/.smartIde/`**.
|
||||
- Scripts **`ia_dev/deploy/`**, **`ia_dev/git-issues/`**, outillage **`ia_dev/tools/`**, définitions **`ia_dev/.smartIde/`**.
|
||||
- Résolution des projets via **`projects/<id>/conf.json`** à la racine **smart_ide** et liens sous `ia_dev/projects/` (voir [projects-directory.md](./projects-directory.md), [ia_dev-module.md](../ia_dev-module.md)).
|
||||
|
||||
## Journaux smart_ide
|
||||
|
||||
@ -14,14 +14,23 @@ Les dépôts sources des produits (ex. backend **docv** sous un chemin du type `
|
||||
## `smart_ide`
|
||||
|
||||
- Fichier : `projects/smart_ide/conf.json`
|
||||
- Après clone ou réorganisation des dossiers, exécuter si besoin `scripts/ensure-ia-dev-smart-ide-project-link.sh` pour recréer le lien `ia_dev/projects/smart_ide` → `../../projects/smart_ide`.
|
||||
- Après clone ou réorganisation des dossiers : `./scripts/ensure-ia-dev-project-link.sh smart_ide` (ou le wrapper `scripts/ensure-ia-dev-smart-ide-project-link.sh`) pour recréer le lien `ia_dev/projects/smart_ide` → `../../projects/smart_ide`.
|
||||
|
||||
## `enso`
|
||||
|
||||
- Fichier : `projects/enso/conf.json` — clone **enso** frère du monorepo (`project_path` typique `../enso`), déploiement `deploy/scripts_v2`, forge **4nk/enso** (wiki / issues), mails ticketing `AI.ENSO.*@4nkweb.com`.
|
||||
- Chemins **absolus sur les serveurs** sous **`smart_ide.remote_data_access`** : alignés sur **`ENSO_REMOTE_ROOT`**, **`ENSO_SSH_HOST`** et **`data/dossiers-permanents`** (dépôt enso, `enso-deploy.env`). Valeurs réelles : fichiers **`enso-deploy.env`** non versionnés.
|
||||
- Cron fragment : `cron/fragments/enso.cron`
|
||||
- Pour **`ia_dev`** : lien symbolique `ia_dev/projects/enso` → `../../projects/enso`
|
||||
- Pour **`ia_dev`** : lien symbolique `ia_dev/projects/enso` → `../../projects/enso` (recréer avec `./scripts/ensure-ia-dev-project-link.sh enso`).
|
||||
|
||||
## `builazoo`
|
||||
|
||||
- Fichier : `projects/builazoo/conf.json` — dépôt **sous la racine monorepo** (`project_path` : `builazoo`, soit `smart_ide/builazoo/`), forge **4nk/builazoo** (wiki / issues à ajuster si le dépôt diffère), mails ticketing `AI.BUILAZOO.*@4nkweb.com`.
|
||||
- **`smart_ide.remote_data_access`** : alias SSH `builazoo-test` / `builazoo-pprod` / `builazoo-prod` (à déclarer dans `~/.ssh/config` comme pour les autres ids).
|
||||
- Workspace : `projects/builazoo/smart_ide.code-workspace`
|
||||
- Cron fragment : `cron/fragments/builazoo.cron`
|
||||
- Pour **`ia_dev`** : `./scripts/ensure-ia-dev-project-link.sh builazoo`
|
||||
- Le répertoire **`builazoo/`** à la racine du monorepo est partiellement ignoré par Git (sauf `builazoo/README.md`) : y placer le clone ou les sources.
|
||||
|
||||
## Synchronisation Git planifiée
|
||||
|
||||
|
||||
15
docs/repo/service-anythingllm-devtools.md
Normal file
15
docs/repo/service-anythingllm-devtools.md
Normal file
@ -0,0 +1,15 @@
|
||||
# anythingllm-devtools (`services/anythingllm-devtools/`)
|
||||
|
||||
Service HTTP local : **AnythingLLM** (workspaces, upload documents) + **repos-devtools-server** + **RAG initial** selon **`.4nkaiignore`**. Surface préférée pour l’orchestration auparavant exposée par l’extension [extension-anythingllm-workspaces.md](./extension-anythingllm-workspaces.md).
|
||||
|
||||
## Exploitation
|
||||
|
||||
Voir le [README du service](../../services/anythingllm-devtools/README.md) (build, variables d’environnement, démarrage).
|
||||
|
||||
## Spécification HTTP
|
||||
|
||||
[API/anythingllm-devtools-api.md](../API/anythingllm-devtools-api.md)
|
||||
|
||||
## Template `.4nkaiignore`
|
||||
|
||||
Canonique : **`services/anythingllm-devtools/templates/4nkaiignore.default`** — à maintenir en parité avec **`services/repos-devtools-server/templates/4nkaiignore.default`**.
|
||||
@ -33,7 +33,7 @@ Unité systemd utilisateur possible : `systemctl --user daemon-reload && systemc
|
||||
|
||||
## Templates
|
||||
|
||||
Maintenir **`templates/4nkaiignore.default`** aligné avec `extensions/anythingllm-workspaces/templates/4nkaiignore.default`.
|
||||
Maintenir **`templates/4nkaiignore.default`** aligné avec `services/anythingllm-devtools/templates/4nkaiignore.default` (et, pour compatibilité, `extensions/anythingllm-workspaces/templates/4nkaiignore.default`).
|
||||
|
||||
## Spécification HTTP
|
||||
|
||||
|
||||
@ -34,7 +34,7 @@ Ce document décrit les **services logiciels** typiques sur l’**hôte** (serve
|
||||
|
||||
## Micro-services HTTP sous `services/`
|
||||
|
||||
Services d’appoint sur **`127.0.0.1`** (souvent auth **Bearer**) : Git devtools, LangExtract, recherche regex, proxy claw, **`ia-dev-gateway`** (agents / runs stub), **`smart-ide-orchestrator`** (routage intentions) — voir tableau dans [system-architecture.md](./system-architecture.md), la **référence API** dans [`API/README.md`](./API/README.md), et l’index d’exploitation [repo/README.md](./repo/README.md) (fichiers `repo/service-*.md`).
|
||||
Services d’appoint sur **`127.0.0.1`** (souvent auth **Bearer**) : Git devtools, **anythingllm-devtools** (AnythingLLM + RAG initial), LangExtract, recherche regex, proxy claw, **`ia-dev-gateway`** (agents / runs stub), **`smart-ide-orchestrator`** (routage intentions) — voir tableau dans [system-architecture.md](./system-architecture.md), la **référence API** dans [`API/README.md`](./API/README.md), et l’index d’exploitation [repo/README.md](./repo/README.md) (fichiers `repo/service-*.md`).
|
||||
|
||||
## Documentation liée
|
||||
|
||||
|
||||
@ -32,7 +32,8 @@ Conséquences :
|
||||
| `services/claw-harness-api/` | **Harnais** optionnel multi-fournisseur (amont claw-code) + proxy ; gabarits **sans Anthropic** |
|
||||
| `services/repos-devtools-server/` | **Outillage Git** HTTP local (clone, liste, chargement de dépôts sous racine contrôlée) |
|
||||
| `core_ide/` | **Sources Lapce** — socle applicatif (build éditeur, personnalisations) — clone amont, hors index du parent |
|
||||
| `extensions/anythingllm-workspaces/` | Outils / modèles alignés AnythingLLM et workspaces par projet |
|
||||
| `services/anythingllm-devtools/` | HTTP : AnythingLLM + repos-devtools + RAG initial (`.4nkaiignore`) — [API/anythingllm-devtools-api.md](./API/anythingllm-devtools-api.md) |
|
||||
| `extensions/anythingllm-workspaces/` | Extension VS Code / Cursor (héritée) ; surface préférée : service **anythingllm-devtools** |
|
||||
| `scripts/` , `setup/` , `systemd/` | Installation hôte, scripts d’exploitation, unités utilisateur pour services |
|
||||
| `cron/` | Pull **Git** planifié des clones décrits par `projects/<id>/conf.json` (`project_path`) — [repo/cron-git-pull.md](./repo/cron-git-pull.md) |
|
||||
| `services/local-office/` | **API REST** Office (upload, commandes docx, stockage SQLite + fichiers) ; complément programmatique à ONLYOFFICE |
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
# AnythingLLM Workspaces (extension)
|
||||
# AnythingLLM Workspaces (extension, héritée)
|
||||
|
||||
Surface préférée : **[`services/anythingllm-devtools/`](../../services/anythingllm-devtools/)** (HTTP, orchestration identique).
|
||||
|
||||
**[docs/repo/extension-anythingllm-workspaces.md](../../docs/repo/extension-anythingllm-workspaces.md)**
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
# .4nkaiignore — same rules as .gitignore (see gitignore(5))
|
||||
# Used by the AnythingLLM Workspaces extension to filter the initial document upload
|
||||
# after clone or /repos-load-sync. Copy or rename to `.4nkaiignore` at the repo root.
|
||||
# Used by anythingllm-devtools and repos-devtools-server (post-clone) to filter uploads
|
||||
# and by the legacy VS Code extension after /repos-load-sync. Copy or rename to `.4nkaiignore`.
|
||||
|
||||
# VCS
|
||||
.git/
|
||||
|
||||
2
ia_dev
2
ia_dev
@ -1 +1 @@
|
||||
Subproject commit 285e72039eda3b27684ad4c67620350a9150d30f
|
||||
Subproject commit b4ce81858cf4ba10b5a5ac16ea4ebc6f94e85a5d
|
||||
@ -1,5 +1,52 @@
|
||||
{
|
||||
"id": "smart_ide",
|
||||
"default_env": "test",
|
||||
"notes": "Copy this file to projects/active-project.json (gitignored). Field id must match an existing projects/<id>/conf.json. Field default_env is one of: test, pprod, prod — used by agents when the environment is not specified."
|
||||
"id": "<name>",
|
||||
"<name>": "<name>",
|
||||
"cron": {
|
||||
"git_pull": true
|
||||
},
|
||||
"project_path": ".",
|
||||
"build_dirs": [],
|
||||
"deploy": {},
|
||||
"version": {
|
||||
"package_json_paths": [],
|
||||
"splash_app_<name>": "<name>"
|
||||
},
|
||||
"mail": {
|
||||
"imap_bridge_env": ".secrets/git-issues/imap-bridge.env"
|
||||
},
|
||||
"git": {
|
||||
"wiki_url": "https://git.<domain>/4nk/<name>/wiki",
|
||||
"token_file": ".secrets/git-issues/token"
|
||||
},
|
||||
"tickets": {
|
||||
"ticketing_url": "https://git.<domain>/4nk/<name>/issues",
|
||||
"authorized_emails": {
|
||||
"to": [
|
||||
{
|
||||
"test": "AI.<name>.TEST@<domain>",
|
||||
"pprod": "AI.<name>.PPROD@<domain>",
|
||||
"prod": "AI.<name>.PROD@<domain>"
|
||||
}
|
||||
],
|
||||
"from": ["<name>@<domain>"]
|
||||
}
|
||||
},
|
||||
"smart_ide": {
|
||||
"remote_data_access": {
|
||||
"environments": {
|
||||
"test": {
|
||||
"ssh_host_alias": "<name>-test",
|
||||
"remote_data_directories": []
|
||||
},
|
||||
"pprod": {
|
||||
"ssh_host_alias": "<name>-pprod",
|
||||
"remote_data_directories": []
|
||||
},
|
||||
"prod": {
|
||||
"ssh_host_alias": "<name>-prod",
|
||||
"remote_data_directories": []
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
52
projects/builazoo/conf.json
Normal file
52
projects/builazoo/conf.json
Normal file
@ -0,0 +1,52 @@
|
||||
{
|
||||
"id": "builazoo",
|
||||
"name": "builazoo",
|
||||
"cron": {
|
||||
"git_pull": true
|
||||
},
|
||||
"project_path": "builazoo",
|
||||
"build_dirs": [],
|
||||
"deploy": {},
|
||||
"version": {
|
||||
"package_json_paths": [],
|
||||
"splash_app_name": "builazoo"
|
||||
},
|
||||
"mail": {
|
||||
"imap_bridge_env": ".secrets/git-issues/imap-bridge.env"
|
||||
},
|
||||
"git": {
|
||||
"wiki_url": "https://git.4nkweb.com/4nk/builazoo/wiki",
|
||||
"token_file": ".secrets/git-issues/token"
|
||||
},
|
||||
"tickets": {
|
||||
"ticketing_url": "https://git.4nkweb.com/4nk/builazoo/issues",
|
||||
"authorized_emails": {
|
||||
"to": [
|
||||
{
|
||||
"test": "AI.BUILAZOO.TEST@4nkweb.com",
|
||||
"pprod": "AI.BUILAZOO.PPROD@4nkweb.com",
|
||||
"prod": "AI.BUILAZOO.PROD@4nkweb.com"
|
||||
}
|
||||
],
|
||||
"from": ["nicolas.4nk@pm.me"]
|
||||
}
|
||||
},
|
||||
"smart_ide": {
|
||||
"remote_data_access": {
|
||||
"environments": {
|
||||
"test": {
|
||||
"ssh_host_alias": "builazoo-test",
|
||||
"remote_data_directories": []
|
||||
},
|
||||
"pprod": {
|
||||
"ssh_host_alias": "builazoo-pprod",
|
||||
"remote_data_directories": []
|
||||
},
|
||||
"prod": {
|
||||
"ssh_host_alias": "builazoo-prod",
|
||||
"remote_data_directories": []
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
13
projects/builazoo/smart_ide.code-workspace
Normal file
13
projects/builazoo/smart_ide.code-workspace
Normal file
@ -0,0 +1,13 @@
|
||||
{
|
||||
"folders": [
|
||||
{
|
||||
"path": "../.."
|
||||
},
|
||||
{
|
||||
"path": "../../builazoo"
|
||||
}
|
||||
],
|
||||
"settings": {
|
||||
"smartIde.activeProjectId": "builazoo"
|
||||
}
|
||||
}
|
||||
@ -24,11 +24,11 @@
|
||||
"splash_app_name": "enso"
|
||||
},
|
||||
"mail": {
|
||||
"imap_bridge_env": ".secrets/gitea-issues/imap-bridge.env"
|
||||
"imap_bridge_env": ".secrets/git-issues/imap-bridge.env"
|
||||
},
|
||||
"git": {
|
||||
"wiki_url": "https://git.4nkweb.com/4nk/enso/wiki",
|
||||
"token_file": ".secrets/gitea-issues/token"
|
||||
"token_file": ".secrets/git-issues/token"
|
||||
},
|
||||
"tickets": {
|
||||
"ticketing_url": "https://git.4nkweb.com/4nk/enso/issues",
|
||||
|
||||
@ -12,20 +12,20 @@
|
||||
"splash_app_name": "smart_ide"
|
||||
},
|
||||
"mail": {
|
||||
"imap_bridge_env": ".secrets/gitea-issues/imap-bridge.env"
|
||||
"imap_bridge_env": ".secrets/git-issues/imap-bridge.env"
|
||||
},
|
||||
"git": {
|
||||
"wiki_url": "https://git.4nkweb.com/4nk/smart_ide/wiki",
|
||||
"token_file": ".secrets/gitea-issues/token"
|
||||
"token_file": ".secrets/git-issues/token"
|
||||
},
|
||||
"tickets": {
|
||||
"ticketing_url": "https://git.4nkweb.com/4nk/smart_ide/issues",
|
||||
"authorized_emails": {
|
||||
"to": [
|
||||
{
|
||||
"test": "AI.SMART_IDE.TEST@4nkweb.com",
|
||||
"pprod": "AI.SMART_IDE.PPROD@4nkweb.com",
|
||||
"prod": "AI.SMART_IDE.PROD@4nkweb.com"
|
||||
"test": "AI.TEST@4nkweb.com",
|
||||
"pprod": "AI.PPROD@4nkweb.com",
|
||||
"prod": "AI.PROD@4nkweb.com"
|
||||
}
|
||||
],
|
||||
"from": ["nicolas.4nk@pm.me"]
|
||||
|
||||
75
projects/ssh-config.example
Normal file
75
projects/ssh-config.example
Normal file
@ -0,0 +1,75 @@
|
||||
# Exemple de configuration client OpenSSH (~/.ssh/config).
|
||||
# Copier vers ~/.ssh/config ou inclure avec : Include /chemin/vers/smart_ide/projects/ssh-config.local
|
||||
# (ne pas versionner de fichier contenant des données personnelles non nécessaires).
|
||||
#
|
||||
# Alignement avec le dépôt :
|
||||
# - projects/<id>/conf.json → smart_ide.remote_data_access.environments.*.ssh_host_alias
|
||||
# - projects/active-project.json.example → alias <name>-test | <name>-pprod | <name>-prod
|
||||
#
|
||||
# Remplacer <SSH_USER>, <LAN_IP_*>, <forge_host>, <key_file> selon le poste.
|
||||
# Bastion optionnel par Host : ProxyJump <SSH_USER>@<bastion_host>
|
||||
|
||||
Host git.<forge_host>
|
||||
HostName git.<forge_host>
|
||||
User git
|
||||
IdentityFile ~/.ssh/<key_file>
|
||||
IdentitiesOnly yes
|
||||
|
||||
# smart_ide — projects/*/conf.json → ssh_host_alias
|
||||
# HostName : mapping LAN type ia_dev/deploy/_lib/env-map.sh (test/pprod/prod → .101/.102/.103) si applicable.
|
||||
|
||||
Host test
|
||||
HostName <LAN_IP_TEST>
|
||||
User <SSH_USER>
|
||||
IdentityFile ~/.ssh/<key_file>
|
||||
IdentitiesOnly yes
|
||||
|
||||
Host pprod
|
||||
HostName <LAN_IP_PPROD>
|
||||
User <SSH_USER>
|
||||
IdentityFile ~/.ssh/<key_file>
|
||||
IdentitiesOnly yes
|
||||
|
||||
Host prod
|
||||
HostName <LAN_IP_PROD>
|
||||
User <SSH_USER>
|
||||
IdentityFile ~/.ssh/<key_file>
|
||||
IdentitiesOnly yes
|
||||
|
||||
Host smart-ide-test
|
||||
HostName <LAN_IP_TEST>
|
||||
User <SSH_USER>
|
||||
IdentityFile ~/.ssh/<key_file>
|
||||
IdentitiesOnly yes
|
||||
|
||||
Host smart-ide-pprod
|
||||
HostName <LAN_IP_PPROD>
|
||||
User <SSH_USER>
|
||||
IdentityFile ~/.ssh/<key_file>
|
||||
IdentitiesOnly yes
|
||||
|
||||
Host smart-ide-prod
|
||||
HostName <LAN_IP_PROD>
|
||||
User <SSH_USER>
|
||||
IdentityFile ~/.ssh/<key_file>
|
||||
IdentitiesOnly yes
|
||||
|
||||
Host builazoo-test
|
||||
HostName <LAN_IP_TEST>
|
||||
User <SSH_USER>
|
||||
IdentityFile ~/.ssh/<key_file>
|
||||
IdentitiesOnly yes
|
||||
|
||||
Host builazoo-pprod
|
||||
HostName <LAN_IP_PPROD>
|
||||
User <SSH_USER>
|
||||
IdentityFile ~/.ssh/<key_file>
|
||||
IdentitiesOnly yes
|
||||
|
||||
Host builazoo-prod
|
||||
HostName <LAN_IP_PROD>
|
||||
User <SSH_USER>
|
||||
IdentityFile ~/.ssh/<key_file>
|
||||
IdentitiesOnly yes
|
||||
|
||||
# Autres ids : dupliquer le trio <id>-test / <id>-pprod / <id>-prod comme dans projects/<id>/conf.json.
|
||||
23
scripts/ensure-ia-dev-project-link.sh
Executable file
23
scripts/ensure-ia-dev-project-link.sh
Executable file
@ -0,0 +1,23 @@
|
||||
#!/usr/bin/env bash
|
||||
# Create ia_dev/projects/<id> -> ../../projects/<id> so ia_dev scripts resolve conf.json
|
||||
# from the monorepo versioned projects/<id>/.
|
||||
set -euo pipefail
|
||||
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
PROJECT_ID="${1:?usage: $(basename "$0") <project_id>}"
|
||||
|
||||
CONF="${ROOT}/projects/${PROJECT_ID}/conf.json"
|
||||
if [[ ! -f "${CONF}" ]]; then
|
||||
echo "Missing ${CONF}" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
LINK_PARENT="${ROOT}/ia_dev/projects"
|
||||
TARGET="../../projects/${PROJECT_ID}"
|
||||
LINK_NAME="${LINK_PARENT}/${PROJECT_ID}"
|
||||
mkdir -p "${LINK_PARENT}"
|
||||
if [[ -e "${LINK_NAME}" && ! -L "${LINK_NAME}" ]]; then
|
||||
echo "Refusing to replace non-symlink: ${LINK_NAME}" >&2
|
||||
exit 1
|
||||
fi
|
||||
ln -sfn "${TARGET}" "${LINK_NAME}"
|
||||
echo "OK: ${LINK_NAME} -> ${TARGET}"
|
||||
@ -1,15 +1,5 @@
|
||||
#!/usr/bin/env bash
|
||||
# Create ia_dev/projects/smart_ide -> ../../projects/smart_ide so scripts under ia_dev
|
||||
# resolve projects/smart_ide/conf.json to this monorepo's versioned conf.
|
||||
# Backward-compatible wrapper: see ensure-ia-dev-project-link.sh
|
||||
set -euo pipefail
|
||||
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
LINK_PARENT="${ROOT}/ia_dev/projects"
|
||||
TARGET="../../projects/smart_ide"
|
||||
LINK_NAME="${LINK_PARENT}/smart_ide"
|
||||
mkdir -p "${LINK_PARENT}"
|
||||
if [[ -e "${LINK_NAME}" && ! -L "${LINK_NAME}" ]]; then
|
||||
echo "Refusing to replace non-symlink: ${LINK_NAME}" >&2
|
||||
exit 1
|
||||
fi
|
||||
ln -sfn "${TARGET}" "${LINK_NAME}"
|
||||
echo "OK: ${LINK_NAME} -> ${TARGET}"
|
||||
exec "${ROOT}/scripts/ensure-ia-dev-project-link.sh" smart_ide
|
||||
|
||||
22
services/anythingllm-devtools/.env.example
Normal file
22
services/anythingllm-devtools/.env.example
Normal file
@ -0,0 +1,22 @@
|
||||
# Bind (default 127.0.0.1:37146)
|
||||
ANYTHINGLLM_DEVTOOLS_HOST=127.0.0.1
|
||||
ANYTHINGLLM_DEVTOOLS_PORT=37146
|
||||
|
||||
# Required: Bearer token for this service (Authorization: Bearer …)
|
||||
ANYTHINGLLM_DEVTOOLS_TOKEN=
|
||||
|
||||
# AnythingLLM HTTP API (same as former extension settings)
|
||||
ANYTHINGLLM_BASE_URL=http://127.0.0.1:3001
|
||||
ANYTHINGLLM_API_KEY=
|
||||
|
||||
# repos-devtools-server
|
||||
REPOS_DEVTOOLS_URL=http://127.0.0.1:37140
|
||||
REPOS_DEVTOOLS_TOKEN=
|
||||
|
||||
# Initial RAG after clone/load-sync (default true)
|
||||
ANYTHINGLLM_INITIAL_SYNC_AFTER_CLONE=true
|
||||
ANYTHINGLLM_INITIAL_SYNC_MAX_FILES=400
|
||||
ANYTHINGLLM_INITIAL_SYNC_MAX_FILE_BYTES=5242880
|
||||
|
||||
# Optional: override path to 4nkaiignore template (default: ../templates/4nkaiignore.default from dist)
|
||||
# ANYTHINGLLM_DEVTOOLS_TEMPLATE_PATH=
|
||||
2
services/anythingllm-devtools/.gitignore
vendored
Normal file
2
services/anythingllm-devtools/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
node_modules/
|
||||
dist/
|
||||
40
services/anythingllm-devtools/README.md
Normal file
40
services/anythingllm-devtools/README.md
Normal file
@ -0,0 +1,40 @@
|
||||
# anythingllm-devtools
|
||||
|
||||
Service HTTP local : orchestration **repos-devtools-server** + API **AnythingLLM** (workspaces, upload documents initiaux selon `.4nkaiignore`). Remplace l’usage principal de l’extension VS Code / Cursor `extensions/anythingllm-workspaces` (voir [extension-anythingllm-workspaces.md](../../docs/repo/extension-anythingllm-workspaces.md)).
|
||||
|
||||
## Prérequis
|
||||
|
||||
- Node ≥ 20
|
||||
- **repos-devtools-server** démarré (`REPOS_DEVTOOLS_URL`, `REPOS_DEVTOOLS_TOKEN`)
|
||||
- AnythingLLM joignable (`ANYTHINGLLM_BASE_URL`, `ANYTHINGLLM_API_KEY`)
|
||||
|
||||
## Configuration
|
||||
|
||||
Copier `.env.example` vers un fichier d’environnement chargé par votre gestionnaire de processus (le dépôt ne versionne pas les secrets). Variables principales :
|
||||
|
||||
| Variable | Rôle |
|
||||
|----------|------|
|
||||
| `ANYTHINGLLM_DEVTOOLS_TOKEN` | Secret Bearer pour ce service (obligatoire) |
|
||||
| `ANYTHINGLLM_DEVTOOLS_HOST` / `ANYTHINGLLM_DEVTOOLS_PORT` | Écoute (défaut `127.0.0.1:37146`) |
|
||||
| `ANYTHINGLLM_BASE_URL` / `ANYTHINGLLM_API_KEY` | API AnythingLLM |
|
||||
| `REPOS_DEVTOOLS_URL` / `REPOS_DEVTOOLS_TOKEN` | API repos-devtools-server |
|
||||
| `ANYTHINGLLM_INITIAL_SYNC_*` | Limites upload RAG initial |
|
||||
|
||||
Modèle `.4nkaiignore` par défaut : `templates/4nkaiignore.default` (surcharge possible avec `ANYTHINGLLM_DEVTOOLS_TEMPLATE_PATH`).
|
||||
|
||||
## Build et exécution
|
||||
|
||||
```bash
|
||||
cd services/anythingllm-devtools
|
||||
npm install
|
||||
npm run build
|
||||
ANYTHINGLLM_DEVTOOLS_TOKEN=… ANYTHINGLLM_BASE_URL=… ANYTHINGLLM_API_KEY=… REPOS_DEVTOOLS_URL=… REPOS_DEVTOOLS_TOKEN=… npm start
|
||||
```
|
||||
|
||||
## API
|
||||
|
||||
Voir [docs/API/anythingllm-devtools-api.md](../../docs/API/anythingllm-devtools-api.md).
|
||||
|
||||
## Intégration
|
||||
|
||||
Les clients (orchestrateur, scripts, agents via **ia-dev-gateway**) appellent ce service au lieu d’embarquer la logique dans l’IDE. Les actions `openFolder` / ouverture navigateur ne sont pas exécutées côté serveur : la réponse JSON contient une liste `actions` que le client peut appliquer.
|
||||
63
services/anythingllm-devtools/package-lock.json
generated
Normal file
63
services/anythingllm-devtools/package-lock.json
generated
Normal file
@ -0,0 +1,63 @@
|
||||
{
|
||||
"name": "@4nk/anythingllm-devtools",
|
||||
"version": "0.1.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@4nk/anythingllm-devtools",
|
||||
"version": "0.1.0",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ignore": "^5.3.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^20.11.0",
|
||||
"typescript": "^5.3.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "20.19.39",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.39.tgz",
|
||||
"integrity": "sha512-orrrD74MBUyK8jOAD/r0+lfa1I2MO6I+vAkmAWzMYbCcgrN4lCrmK52gRFQq/JRxfYPfonkr4b0jcY7Olqdqbw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"undici-types": "~6.21.0"
|
||||
}
|
||||
},
|
||||
"node_modules/ignore": {
|
||||
"version": "5.3.2",
|
||||
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
|
||||
"integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 4"
|
||||
}
|
||||
},
|
||||
"node_modules/typescript": {
|
||||
"version": "5.9.3",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
|
||||
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
"tsserver": "bin/tsserver"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.17"
|
||||
}
|
||||
},
|
||||
"node_modules/undici-types": {
|
||||
"version": "6.21.0",
|
||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
|
||||
"integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
}
|
||||
}
|
||||
}
|
||||
23
services/anythingllm-devtools/package.json
Normal file
23
services/anythingllm-devtools/package.json
Normal file
@ -0,0 +1,23 @@
|
||||
{
|
||||
"name": "@4nk/anythingllm-devtools",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"description": "Local HTTP API: AnythingLLM workspace ensure + repos-devtools orchestration + initial RAG (.4nkaiignore).",
|
||||
"license": "MIT",
|
||||
"type": "module",
|
||||
"main": "dist/server.js",
|
||||
"scripts": {
|
||||
"build": "tsc -p ./",
|
||||
"start": "node dist/server.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20"
|
||||
},
|
||||
"dependencies": {
|
||||
"ignore": "^5.3.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^20.11.0",
|
||||
"typescript": "^5.3.3"
|
||||
}
|
||||
}
|
||||
122
services/anythingllm-devtools/src/anythingllmClient.ts
Normal file
122
services/anythingllm-devtools/src/anythingllmClient.ts
Normal file
@ -0,0 +1,122 @@
|
||||
import type { AnythingWorkspace } from "./types.js";
|
||||
|
||||
const trimTrailingSlashes = (value: string): string => value.replace(/\/+$/, "");
|
||||
|
||||
export const normalizeAnythingLlmBaseUrl = (raw: string): string => {
|
||||
const trimmed = raw.trim();
|
||||
if (trimmed.length === 0) {
|
||||
throw new Error("ANYTHINGLLM_BASE_URL is empty");
|
||||
}
|
||||
return trimTrailingSlashes(trimmed);
|
||||
};
|
||||
|
||||
const parseJson = (text: string): unknown => {
|
||||
try {
|
||||
return JSON.parse(text) as unknown;
|
||||
} catch (cause) {
|
||||
throw new Error("Invalid JSON from AnythingLLM API", { cause });
|
||||
}
|
||||
};
|
||||
|
||||
const isRecord = (value: unknown): value is Record<string, unknown> =>
|
||||
typeof value === "object" && value !== null && !Array.isArray(value);
|
||||
|
||||
const isWorkspace = (value: unknown): value is AnythingWorkspace => {
|
||||
if (!isRecord(value)) {
|
||||
return false;
|
||||
}
|
||||
const id = value.id;
|
||||
const name = value.name;
|
||||
const slug = value.slug;
|
||||
return typeof id === "number" && typeof name === "string" && typeof slug === "string";
|
||||
};
|
||||
|
||||
const parseListWorkspaces = (payload: unknown): readonly AnythingWorkspace[] => {
|
||||
if (!isRecord(payload)) {
|
||||
throw new Error("AnythingLLM API: expected object body");
|
||||
}
|
||||
const list = payload.workspaces;
|
||||
if (!Array.isArray(list)) {
|
||||
throw new Error("AnythingLLM API: missing workspaces array");
|
||||
}
|
||||
const workspaces: AnythingWorkspace[] = [];
|
||||
for (const item of list) {
|
||||
if (!isWorkspace(item)) {
|
||||
throw new Error("AnythingLLM API: invalid workspace entry");
|
||||
}
|
||||
workspaces.push(item);
|
||||
}
|
||||
return workspaces;
|
||||
};
|
||||
|
||||
const normalizeApiSecret = (raw: string): string => {
|
||||
const trimmed = raw.trim();
|
||||
const bearerPrefix = /^Bearer\s+/i;
|
||||
return bearerPrefix.test(trimmed) ? trimmed.replace(bearerPrefix, "").trim() : trimmed;
|
||||
};
|
||||
|
||||
const parseWorkspaceEnvelope = (payload: unknown): AnythingWorkspace => {
|
||||
if (!isRecord(payload)) {
|
||||
throw new Error("AnythingLLM API: expected object body");
|
||||
}
|
||||
const ws = payload.workspace;
|
||||
if (!isWorkspace(ws)) {
|
||||
throw new Error("AnythingLLM API: missing workspace in response");
|
||||
}
|
||||
return ws;
|
||||
};
|
||||
|
||||
export const listWorkspaces = async (
|
||||
baseUrl: string,
|
||||
apiKey: string,
|
||||
): Promise<readonly AnythingWorkspace[]> => {
|
||||
const normalized = normalizeAnythingLlmBaseUrl(baseUrl);
|
||||
const key = normalizeApiSecret(apiKey);
|
||||
if (key.length === 0) {
|
||||
throw new Error("ANYTHINGLLM_API_KEY is empty");
|
||||
}
|
||||
const url = `${normalized}/api/v1/workspaces`;
|
||||
const response = await fetch(url, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
Accept: "application/json",
|
||||
Authorization: `Bearer ${key}`,
|
||||
},
|
||||
});
|
||||
const text = await response.text();
|
||||
if (!response.ok) {
|
||||
throw new Error(`AnythingLLM API ${response.status}: ${text.slice(0, 500)}`);
|
||||
}
|
||||
return parseListWorkspaces(parseJson(text));
|
||||
};
|
||||
|
||||
export const createWorkspace = async (
|
||||
baseUrl: string,
|
||||
apiKey: string,
|
||||
name: string,
|
||||
): Promise<AnythingWorkspace> => {
|
||||
const normalized = normalizeAnythingLlmBaseUrl(baseUrl);
|
||||
const key = normalizeApiSecret(apiKey);
|
||||
if (key.length === 0) {
|
||||
throw new Error("ANYTHINGLLM_API_KEY is empty");
|
||||
}
|
||||
const label = name.trim();
|
||||
if (label.length === 0) {
|
||||
throw new Error("workspace name is empty");
|
||||
}
|
||||
const url = `${normalized}/api/v1/workspace/new`;
|
||||
const response = await fetch(url, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
Accept: "application/json",
|
||||
"Content-Type": "application/json",
|
||||
Authorization: `Bearer ${key}`,
|
||||
},
|
||||
body: JSON.stringify({ name: label }),
|
||||
});
|
||||
const text = await response.text();
|
||||
if (!response.ok) {
|
||||
throw new Error(`AnythingLLM API ${response.status}: ${text.slice(0, 500)}`);
|
||||
}
|
||||
return parseWorkspaceEnvelope(parseJson(text));
|
||||
};
|
||||
56
services/anythingllm-devtools/src/anythingllmDocumentApi.ts
Normal file
56
services/anythingllm-devtools/src/anythingllmDocumentApi.ts
Normal file
@ -0,0 +1,56 @@
|
||||
import * as fs from "node:fs/promises";
|
||||
import { normalizeAnythingLlmBaseUrl } from "./anythingllmClient.js";
|
||||
|
||||
const normalizeApiSecret = (raw: string): string => {
|
||||
const trimmed = raw.trim();
|
||||
const bearerPrefix = /^Bearer\s+/i;
|
||||
return bearerPrefix.test(trimmed) ? trimmed.replace(bearerPrefix, "").trim() : trimmed;
|
||||
};
|
||||
|
||||
const isRecord = (value: unknown): value is Record<string, unknown> =>
|
||||
typeof value === "object" && value !== null && !Array.isArray(value);
|
||||
|
||||
export const uploadLocalFileToWorkspace = async (
|
||||
baseUrl: string,
|
||||
apiKey: string,
|
||||
workspaceSlug: string,
|
||||
absoluteFilePath: string,
|
||||
uploadFileName: string,
|
||||
): Promise<void> => {
|
||||
const normalized = normalizeAnythingLlmBaseUrl(baseUrl);
|
||||
const key = normalizeApiSecret(apiKey);
|
||||
if (key.length === 0) {
|
||||
throw new Error("ANYTHINGLLM_API_KEY is empty");
|
||||
}
|
||||
const buf = await fs.readFile(absoluteFilePath);
|
||||
const body = new FormData();
|
||||
body.append("file", new Blob([buf]), uploadFileName);
|
||||
body.append("addToWorkspaces", workspaceSlug);
|
||||
const url = `${normalized}/api/v1/document/upload`;
|
||||
const response = await fetch(url, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
Authorization: `Bearer ${key}`,
|
||||
},
|
||||
body,
|
||||
});
|
||||
const text = await response.text();
|
||||
let parsed: unknown;
|
||||
try {
|
||||
parsed = JSON.parse(text) as unknown;
|
||||
} catch {
|
||||
throw new Error(`document upload: non-JSON response ${response.status}: ${text.slice(0, 300)}`);
|
||||
}
|
||||
if (!response.ok) {
|
||||
throw new Error(`document upload ${response.status}: ${text.slice(0, 500)}`);
|
||||
}
|
||||
if (!isRecord(parsed)) {
|
||||
throw new Error("document upload: invalid JSON body");
|
||||
}
|
||||
const success = parsed.success;
|
||||
const err = parsed.error;
|
||||
if (success !== true) {
|
||||
const msg = typeof err === "string" ? err : JSON.stringify(err);
|
||||
throw new Error(`document upload failed: ${msg}`);
|
||||
}
|
||||
};
|
||||
21
services/anythingllm-devtools/src/auth.ts
Normal file
21
services/anythingllm-devtools/src/auth.ts
Normal file
@ -0,0 +1,21 @@
|
||||
import type { IncomingMessage, ServerResponse } from "node:http";
|
||||
|
||||
export const readExpectedToken = (): string => {
|
||||
return process.env.ANYTHINGLLM_DEVTOOLS_TOKEN?.trim() ?? "";
|
||||
};
|
||||
|
||||
export const requireBearer = (
|
||||
req: IncomingMessage,
|
||||
res: ServerResponse,
|
||||
expected: string,
|
||||
): boolean => {
|
||||
const h = req.headers.authorization ?? "";
|
||||
const match = /^Bearer\s+(.+)$/i.exec(h);
|
||||
const got = match?.[1]?.trim() ?? "";
|
||||
if (got !== expected) {
|
||||
res.writeHead(401, { "Content-Type": "application/json; charset=utf-8" });
|
||||
res.end(JSON.stringify({ error: "Unauthorized" }));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
57
services/anythingllm-devtools/src/commandParser.ts
Normal file
57
services/anythingllm-devtools/src/commandParser.ts
Normal file
@ -0,0 +1,57 @@
|
||||
export type ParsedDevCommand =
|
||||
| { readonly kind: "repos-clone"; readonly url: string; readonly sync: boolean }
|
||||
| { readonly kind: "repos-list" }
|
||||
| { readonly kind: "repos-load"; readonly name: string; readonly sync: boolean }
|
||||
| { readonly kind: "workspace-load"; readonly name: string }
|
||||
| { readonly kind: "workspace-sync-repo"; readonly name: string }
|
||||
| { readonly kind: "help" }
|
||||
| { readonly kind: "unknown"; readonly raw: string };
|
||||
|
||||
export const parseDevCommandLine = (line: string): ParsedDevCommand => {
|
||||
const trimmed = line.trim();
|
||||
if (trimmed.length === 0) {
|
||||
return { kind: "unknown", raw: line };
|
||||
}
|
||||
const parts = trimmed.split(/\s+/);
|
||||
const cmd = parts[0];
|
||||
const argRest = parts.slice(1).join(" ").trim();
|
||||
if (cmd === "/repos-clone-sync") {
|
||||
return { kind: "repos-clone", url: argRest, sync: true };
|
||||
}
|
||||
if (cmd === "/repos-clone") {
|
||||
return { kind: "repos-clone", url: argRest, sync: false };
|
||||
}
|
||||
if (cmd === "repos-list" || cmd === "/repos-list") {
|
||||
return { kind: "repos-list" };
|
||||
}
|
||||
if (cmd === "/repos-load-sync") {
|
||||
return { kind: "repos-load", name: argRest, sync: true };
|
||||
}
|
||||
if (cmd === "/repos-load") {
|
||||
return { kind: "repos-load", name: argRest, sync: false };
|
||||
}
|
||||
if (cmd === "/workspace-load") {
|
||||
return { kind: "workspace-load", name: argRest };
|
||||
}
|
||||
if (cmd === "/workspace-sync") {
|
||||
return { kind: "workspace-sync-repo", name: argRest };
|
||||
}
|
||||
if (cmd === "help" || cmd === "/help") {
|
||||
return { kind: "help" };
|
||||
}
|
||||
return { kind: "unknown", raw: trimmed };
|
||||
};
|
||||
|
||||
export const devCommandsHelpText = (): string => {
|
||||
return [
|
||||
"Commands (one per line):",
|
||||
" /repos-clone <git-url> — clone (branch test)",
|
||||
" /repos-clone-sync <url> — clone + workspace + suggested open-folder + initial RAG (.4nkaiignore)",
|
||||
" repos-list — list git repos under REPOS_DEVTOOLS_ROOT",
|
||||
" /repos-load <name> — verify repo + suggested open folder",
|
||||
" /repos-load-sync <name> — suggested open folder + workspace + optional initial RAG",
|
||||
" /workspace-load <name> — ensure workspace + suggested workspace URL",
|
||||
" /workspace-sync <name> — ensure workspace + initial RAG (repo must exist under root)",
|
||||
" help — this list",
|
||||
].join("\n");
|
||||
};
|
||||
238
services/anythingllm-devtools/src/devRunner.ts
Normal file
238
services/anythingllm-devtools/src/devRunner.ts
Normal file
@ -0,0 +1,238 @@
|
||||
import {
|
||||
devCommandsHelpText,
|
||||
parseDevCommandLine,
|
||||
type ParsedDevCommand,
|
||||
} from "./commandParser.js";
|
||||
import { normalizeAnythingLlmBaseUrl } from "./anythingllmClient.js";
|
||||
import { reposApiClone, reposApiList, reposApiLoad } from "./reposApiClient.js";
|
||||
import { ensureWorkspaceForRepoName } from "./workspaceEnsure.js";
|
||||
import { runInitialRagImportFromRepo } from "./initialRagSync.js";
|
||||
import type { AnythingllmDevtoolsConfig } from "./env.js";
|
||||
|
||||
const DEFAULT_BRANCH = "test";
|
||||
|
||||
export type DevClientAction =
|
||||
| { readonly type: "openFolder"; readonly path: string }
|
||||
| { readonly type: "openWorkspaceUrl"; readonly url: string; readonly slug: string };
|
||||
|
||||
const fmt = (value: unknown): string => {
|
||||
if (typeof value === "string") {
|
||||
return value;
|
||||
}
|
||||
try {
|
||||
return JSON.stringify(value, null, 2);
|
||||
} catch {
|
||||
return String(value);
|
||||
}
|
||||
};
|
||||
|
||||
export interface DevRunnerContext extends AnythingllmDevtoolsConfig {
|
||||
readonly pushOpenFolder: (fsPath: string) => void;
|
||||
readonly pushOpenWorkspaceUrl: (slug: string) => void;
|
||||
}
|
||||
|
||||
const assertReposConfig = (ctx: DevRunnerContext): void => {
|
||||
if (ctx.reposApiBaseUrl.trim().length === 0) {
|
||||
throw new Error("Set REPOS_DEVTOOLS_URL (repos-devtools-server URL).");
|
||||
}
|
||||
if (ctx.reposApiToken.trim().length === 0) {
|
||||
throw new Error("Set REPOS_DEVTOOLS_TOKEN (same value as repos-devtools-server expects).");
|
||||
}
|
||||
};
|
||||
|
||||
const assertAnythingConfig = (ctx: DevRunnerContext): void => {
|
||||
if (ctx.anythingApiKey.trim().length === 0) {
|
||||
throw new Error("Set ANYTHINGLLM_API_KEY for workspace operations.");
|
||||
}
|
||||
};
|
||||
|
||||
const appendInitialRag = async (
|
||||
ctx: DevRunnerContext,
|
||||
repoRoot: string,
|
||||
workspaceSlug: string,
|
||||
): Promise<string> => {
|
||||
if (!ctx.initialSyncAfterClone) {
|
||||
return "";
|
||||
}
|
||||
assertAnythingConfig(ctx);
|
||||
const res = await runInitialRagImportFromRepo({
|
||||
baseUrl: ctx.anythingBaseUrl,
|
||||
apiKey: ctx.anythingApiKey,
|
||||
workspaceSlug,
|
||||
repoRoot,
|
||||
templateFsPath: ctx.default4nkaiignoreTemplateFsPath,
|
||||
maxFiles: ctx.initialSyncMaxFiles,
|
||||
maxFileBytes: ctx.initialSyncMaxFileBytes,
|
||||
});
|
||||
return `\n---\nInitial RAG sync: ${fmt(res)}`;
|
||||
};
|
||||
|
||||
const workspaceUrlForSlug = (baseUrl: string, slug: string): string => {
|
||||
const root = normalizeAnythingLlmBaseUrl(baseUrl);
|
||||
return `${root}/workspace/${encodeURIComponent(slug)}`;
|
||||
};
|
||||
|
||||
const runOne = async (cmd: ParsedDevCommand, ctx: DevRunnerContext): Promise<string> => {
|
||||
if (cmd.kind === "help") {
|
||||
return devCommandsHelpText();
|
||||
}
|
||||
if (cmd.kind === "unknown") {
|
||||
return `Unknown command: ${cmd.raw}\n${devCommandsHelpText()}`;
|
||||
}
|
||||
if (cmd.kind === "repos-list") {
|
||||
assertReposConfig(ctx);
|
||||
const data = await reposApiList(ctx.reposApiBaseUrl, ctx.reposApiToken);
|
||||
return fmt(data);
|
||||
}
|
||||
if (cmd.kind === "repos-clone") {
|
||||
assertReposConfig(ctx);
|
||||
if (cmd.url.length === 0) {
|
||||
throw new Error("/repos-clone requires a git URL.");
|
||||
}
|
||||
const data = await reposApiClone(
|
||||
ctx.reposApiBaseUrl,
|
||||
ctx.reposApiToken,
|
||||
cmd.url,
|
||||
DEFAULT_BRANCH,
|
||||
);
|
||||
let out = fmt(data);
|
||||
if (cmd.sync) {
|
||||
assertAnythingConfig(ctx);
|
||||
const rec = data as Record<string, unknown>;
|
||||
const name = rec.name;
|
||||
const fsPath = rec.path;
|
||||
if (typeof name !== "string") {
|
||||
throw new Error("clone response missing name");
|
||||
}
|
||||
if (typeof fsPath !== "string" || fsPath.length === 0) {
|
||||
throw new Error("clone response missing path");
|
||||
}
|
||||
const ensured = await ensureWorkspaceForRepoName(
|
||||
ctx.anythingBaseUrl,
|
||||
ctx.anythingApiKey,
|
||||
name,
|
||||
);
|
||||
out += `\n---\nAnythingLLM workspace: ${fmt({
|
||||
slug: ensured.workspace.slug,
|
||||
name: ensured.workspace.name,
|
||||
workspaceCreatedByApi: ensured.created,
|
||||
})}`;
|
||||
out += await appendInitialRag(ctx, fsPath, ensured.workspace.slug);
|
||||
ctx.pushOpenFolder(fsPath);
|
||||
ctx.pushOpenWorkspaceUrl(ensured.workspace.slug);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
if (cmd.kind === "repos-load") {
|
||||
assertReposConfig(ctx);
|
||||
if (cmd.name.length === 0) {
|
||||
throw new Error("/repos-load requires a repository folder name.");
|
||||
}
|
||||
const loaded = await reposApiLoad(ctx.reposApiBaseUrl, ctx.reposApiToken, cmd.name);
|
||||
let out = fmt(loaded);
|
||||
ctx.pushOpenFolder(loaded.path);
|
||||
if (cmd.sync) {
|
||||
assertAnythingConfig(ctx);
|
||||
const ensured = await ensureWorkspaceForRepoName(
|
||||
ctx.anythingBaseUrl,
|
||||
ctx.anythingApiKey,
|
||||
loaded.name,
|
||||
);
|
||||
out += `\n---\nAnythingLLM workspace: ${fmt({
|
||||
slug: ensured.workspace.slug,
|
||||
name: ensured.workspace.name,
|
||||
workspaceCreatedByApi: ensured.created,
|
||||
})}`;
|
||||
out += await appendInitialRag(ctx, loaded.path, ensured.workspace.slug);
|
||||
ctx.pushOpenWorkspaceUrl(ensured.workspace.slug);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
if (cmd.kind === "workspace-load") {
|
||||
assertAnythingConfig(ctx);
|
||||
if (cmd.name.length === 0) {
|
||||
throw new Error("/workspace-load requires a workspace name.");
|
||||
}
|
||||
const ensured = await ensureWorkspaceForRepoName(
|
||||
ctx.anythingBaseUrl,
|
||||
ctx.anythingApiKey,
|
||||
cmd.name,
|
||||
);
|
||||
ctx.pushOpenWorkspaceUrl(ensured.workspace.slug);
|
||||
return fmt({
|
||||
slug: ensured.workspace.slug,
|
||||
name: ensured.workspace.name,
|
||||
workspaceCreatedByApi: ensured.created,
|
||||
});
|
||||
}
|
||||
if (cmd.kind === "workspace-sync-repo") {
|
||||
assertReposConfig(ctx);
|
||||
assertAnythingConfig(ctx);
|
||||
if (cmd.name.length === 0) {
|
||||
throw new Error("/workspace-sync requires a repository folder name.");
|
||||
}
|
||||
const loaded = await reposApiLoad(ctx.reposApiBaseUrl, ctx.reposApiToken, cmd.name);
|
||||
const ensured = await ensureWorkspaceForRepoName(
|
||||
ctx.anythingBaseUrl,
|
||||
ctx.anythingApiKey,
|
||||
loaded.name,
|
||||
);
|
||||
let out = fmt({
|
||||
repoPath: loaded.path,
|
||||
slug: ensured.workspace.slug,
|
||||
name: ensured.workspace.name,
|
||||
workspaceCreatedByApi: ensured.created,
|
||||
});
|
||||
out += await appendInitialRag(ctx, loaded.path, ensured.workspace.slug);
|
||||
return out;
|
||||
}
|
||||
return `Unhandled: ${JSON.stringify(cmd)}`;
|
||||
};
|
||||
|
||||
export interface DevRunAggregate {
|
||||
readonly text: string;
|
||||
readonly actions: readonly DevClientAction[];
|
||||
}
|
||||
|
||||
export const runDevToolsScript = async (
|
||||
text: string,
|
||||
base: AnythingllmDevtoolsConfig,
|
||||
): Promise<DevRunAggregate> => {
|
||||
const actions: DevClientAction[] = [];
|
||||
const ctx: DevRunnerContext = {
|
||||
...base,
|
||||
pushOpenFolder: (p: string) => {
|
||||
if (p.length > 0) {
|
||||
actions.push({ type: "openFolder", path: p });
|
||||
}
|
||||
},
|
||||
pushOpenWorkspaceUrl: (slug: string) => {
|
||||
if (slug.length > 0) {
|
||||
actions.push({
|
||||
type: "openWorkspaceUrl",
|
||||
slug,
|
||||
url: workspaceUrlForSlug(base.anythingBaseUrl, slug),
|
||||
});
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
const lines = text
|
||||
.split("\n")
|
||||
.map((l) => l.trim())
|
||||
.filter((l) => l.length > 0);
|
||||
if (lines.length === 0) {
|
||||
return { text: devCommandsHelpText(), actions: [] };
|
||||
}
|
||||
const parts: string[] = [];
|
||||
for (const line of lines) {
|
||||
const parsed = parseDevCommandLine(line);
|
||||
try {
|
||||
parts.push(await runOne(parsed, ctx));
|
||||
} catch (e) {
|
||||
const msg = e instanceof Error ? e.message : String(e);
|
||||
parts.push(`ERROR: ${msg}`);
|
||||
}
|
||||
}
|
||||
return { text: parts.join("\n---\n"), actions };
|
||||
};
|
||||
67
services/anythingllm-devtools/src/env.ts
Normal file
67
services/anythingllm-devtools/src/env.ts
Normal file
@ -0,0 +1,67 @@
|
||||
import * as fs from "node:fs";
|
||||
import * as path from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
|
||||
const defaultTemplatePath = (): string => {
|
||||
const fromDist = path.join(__dirname, "..", "templates", "4nkaiignore.default");
|
||||
if (fs.existsSync(fromDist)) {
|
||||
return fromDist;
|
||||
}
|
||||
return path.join(__dirname, "..", "..", "templates", "4nkaiignore.default");
|
||||
};
|
||||
|
||||
const readBool = (raw: string | undefined, fallback: boolean): boolean => {
|
||||
if (raw === undefined || raw.trim().length === 0) {
|
||||
return fallback;
|
||||
}
|
||||
const v = raw.trim().toLowerCase();
|
||||
if (v === "1" || v === "true" || v === "yes") {
|
||||
return true;
|
||||
}
|
||||
if (v === "0" || v === "false" || v === "no") {
|
||||
return false;
|
||||
}
|
||||
return fallback;
|
||||
};
|
||||
|
||||
const readPositiveInt = (raw: string | undefined, fallback: number): number => {
|
||||
if (raw === undefined || raw.trim().length === 0) {
|
||||
return fallback;
|
||||
}
|
||||
const n = Number.parseInt(raw.trim(), 10);
|
||||
if (!Number.isFinite(n) || n <= 0) {
|
||||
return fallback;
|
||||
}
|
||||
return n;
|
||||
};
|
||||
|
||||
export interface AnythingllmDevtoolsConfig {
|
||||
readonly anythingBaseUrl: string;
|
||||
readonly anythingApiKey: string;
|
||||
readonly reposApiBaseUrl: string;
|
||||
readonly reposApiToken: string;
|
||||
readonly initialSyncAfterClone: boolean;
|
||||
readonly initialSyncMaxFiles: number;
|
||||
readonly initialSyncMaxFileBytes: number;
|
||||
readonly default4nkaiignoreTemplateFsPath: string;
|
||||
}
|
||||
|
||||
export const loadConfig = (): AnythingllmDevtoolsConfig => {
|
||||
const override = process.env.ANYTHINGLLM_DEVTOOLS_TEMPLATE_PATH?.trim() ?? "";
|
||||
const templateFsPath = override.length > 0 ? override : defaultTemplatePath();
|
||||
return {
|
||||
anythingBaseUrl: process.env.ANYTHINGLLM_BASE_URL?.trim() ?? "",
|
||||
anythingApiKey: process.env.ANYTHINGLLM_API_KEY?.trim() ?? "",
|
||||
reposApiBaseUrl: process.env.REPOS_DEVTOOLS_URL?.trim() ?? "",
|
||||
reposApiToken: process.env.REPOS_DEVTOOLS_TOKEN?.trim() ?? "",
|
||||
initialSyncAfterClone: readBool(process.env.ANYTHINGLLM_INITIAL_SYNC_AFTER_CLONE, true),
|
||||
initialSyncMaxFiles: readPositiveInt(process.env.ANYTHINGLLM_INITIAL_SYNC_MAX_FILES, 400),
|
||||
initialSyncMaxFileBytes: readPositiveInt(
|
||||
process.env.ANYTHINGLLM_INITIAL_SYNC_MAX_FILE_BYTES,
|
||||
5_242_880,
|
||||
),
|
||||
default4nkaiignoreTemplateFsPath: templateFsPath,
|
||||
};
|
||||
};
|
||||
25
services/anythingllm-devtools/src/httpUtil.ts
Normal file
25
services/anythingllm-devtools/src/httpUtil.ts
Normal file
@ -0,0 +1,25 @@
|
||||
import type { IncomingMessage } from "node:http";
|
||||
|
||||
const MAX_BODY = 1_048_576;
|
||||
|
||||
export const readJsonBody = async (req: IncomingMessage): Promise<unknown> => {
|
||||
const chunks: Buffer[] = [];
|
||||
let total = 0;
|
||||
for await (const chunk of req) {
|
||||
const buf = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
|
||||
total += buf.length;
|
||||
if (total > MAX_BODY) {
|
||||
throw new Error("Request body too large");
|
||||
}
|
||||
chunks.push(buf);
|
||||
}
|
||||
const raw = Buffer.concat(chunks).toString("utf8").trim();
|
||||
if (raw.length === 0) {
|
||||
return {};
|
||||
}
|
||||
try {
|
||||
return JSON.parse(raw) as unknown;
|
||||
} catch (cause) {
|
||||
throw new Error("Invalid JSON body", { cause });
|
||||
}
|
||||
};
|
||||
140
services/anythingllm-devtools/src/initialRagSync.ts
Normal file
140
services/anythingllm-devtools/src/initialRagSync.ts
Normal file
@ -0,0 +1,140 @@
|
||||
import { createRequire } from "node:module";
|
||||
import * as fs from "node:fs/promises";
|
||||
import * as path from "node:path";
|
||||
import { uploadLocalFileToWorkspace } from "./anythingllmDocumentApi.js";
|
||||
import type { Ignore, Options } from "ignore";
|
||||
|
||||
const require = createRequire(import.meta.url);
|
||||
const createIgnore = require("ignore") as (options?: Options) => Ignore;
|
||||
|
||||
const ALWAYS_IGNORE = [".git/", "node_modules/", "**/node_modules/"].join("\n");
|
||||
|
||||
export interface InitialRagImportResult {
|
||||
readonly uploaded: number;
|
||||
readonly skipped: number;
|
||||
readonly errors: readonly string[];
|
||||
readonly dotfileCreated: boolean;
|
||||
readonly capped: boolean;
|
||||
}
|
||||
|
||||
export const ensureDot4nkaiignoreFromTemplate = async (
|
||||
repoRoot: string,
|
||||
templateFsPath: string,
|
||||
): Promise<{ created: boolean }> => {
|
||||
const target = path.join(repoRoot, ".4nkaiignore");
|
||||
try {
|
||||
await fs.access(target);
|
||||
return { created: false };
|
||||
} catch {
|
||||
const tmpl = await fs.readFile(templateFsPath, "utf8");
|
||||
await fs.writeFile(target, tmpl, "utf8");
|
||||
return { created: true };
|
||||
}
|
||||
};
|
||||
|
||||
const walkFiles = async (dir: string): Promise<string[]> => {
|
||||
const out: string[] = [];
|
||||
const scan = async (d: string): Promise<void> => {
|
||||
const entries = await fs.readdir(d, { withFileTypes: true });
|
||||
for (const e of entries) {
|
||||
const p = path.join(d, e.name);
|
||||
if (e.isSymbolicLink()) {
|
||||
continue;
|
||||
}
|
||||
if (e.isDirectory()) {
|
||||
await scan(p);
|
||||
continue;
|
||||
}
|
||||
if (e.isFile()) {
|
||||
out.push(p);
|
||||
}
|
||||
}
|
||||
};
|
||||
await scan(dir);
|
||||
return out;
|
||||
};
|
||||
|
||||
const toPosixRel = (root: string, abs: string): string => {
|
||||
const rel = path.relative(root, abs);
|
||||
return rel.split(path.sep).join("/");
|
||||
};
|
||||
|
||||
const uploadNameForRel = (rel: string): string => {
|
||||
return rel.split("/").join("__");
|
||||
};
|
||||
|
||||
export const runInitialRagImportFromRepo = async (opts: {
|
||||
readonly baseUrl: string;
|
||||
readonly apiKey: string;
|
||||
readonly workspaceSlug: string;
|
||||
readonly repoRoot: string;
|
||||
readonly templateFsPath: string;
|
||||
readonly maxFiles: number;
|
||||
readonly maxFileBytes: number;
|
||||
}): Promise<InitialRagImportResult> => {
|
||||
const dot = await ensureDot4nkaiignoreFromTemplate(opts.repoRoot, opts.templateFsPath);
|
||||
const ignorePath = path.join(opts.repoRoot, ".4nkaiignore");
|
||||
let userRules = "";
|
||||
try {
|
||||
userRules = await fs.readFile(ignorePath, "utf8");
|
||||
} catch {
|
||||
userRules = "";
|
||||
}
|
||||
const ig = createIgnore();
|
||||
ig.add(ALWAYS_IGNORE);
|
||||
ig.add(userRules);
|
||||
|
||||
const absFiles = await walkFiles(opts.repoRoot);
|
||||
const candidates: string[] = [];
|
||||
for (const abs of absFiles) {
|
||||
const rel = toPosixRel(opts.repoRoot, abs);
|
||||
if (rel.length === 0 || rel.startsWith("..")) {
|
||||
continue;
|
||||
}
|
||||
if (ig.ignores(rel)) {
|
||||
continue;
|
||||
}
|
||||
candidates.push(abs);
|
||||
}
|
||||
|
||||
let uploaded = 0;
|
||||
let skipped = 0;
|
||||
const errors: string[] = [];
|
||||
let capped = false;
|
||||
|
||||
for (const abs of candidates) {
|
||||
if (uploaded >= opts.maxFiles) {
|
||||
capped = true;
|
||||
skipped += 1;
|
||||
continue;
|
||||
}
|
||||
const st = await fs.stat(abs);
|
||||
if (st.size > opts.maxFileBytes) {
|
||||
skipped += 1;
|
||||
continue;
|
||||
}
|
||||
const rel = toPosixRel(opts.repoRoot, abs);
|
||||
const uploadName = uploadNameForRel(rel);
|
||||
try {
|
||||
await uploadLocalFileToWorkspace(
|
||||
opts.baseUrl,
|
||||
opts.apiKey,
|
||||
opts.workspaceSlug,
|
||||
abs,
|
||||
uploadName,
|
||||
);
|
||||
uploaded += 1;
|
||||
} catch (e) {
|
||||
const m = e instanceof Error ? e.message : String(e);
|
||||
errors.push(`${rel}: ${m}`);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
uploaded,
|
||||
skipped,
|
||||
errors,
|
||||
dotfileCreated: dot.created,
|
||||
capped,
|
||||
};
|
||||
};
|
||||
96
services/anythingllm-devtools/src/reposApiClient.ts
Normal file
96
services/anythingllm-devtools/src/reposApiClient.ts
Normal file
@ -0,0 +1,96 @@
|
||||
const trimSlash = (u: string): string => u.replace(/\/+$/, "");
|
||||
|
||||
const normalizeReposToken = (raw: string): string => {
|
||||
const trimmed = raw.trim();
|
||||
const bearerPrefix = /^Bearer\s+/i;
|
||||
return bearerPrefix.test(trimmed) ? trimmed.replace(bearerPrefix, "").trim() : trimmed;
|
||||
};
|
||||
|
||||
const authHeaders = (token: string): Record<string, string> => {
|
||||
const key = normalizeReposToken(token);
|
||||
return {
|
||||
Accept: "application/json",
|
||||
"Content-Type": "application/json",
|
||||
Authorization: `Bearer ${key}`,
|
||||
};
|
||||
};
|
||||
|
||||
export const reposApiClone = async (
|
||||
baseUrl: string,
|
||||
token: string,
|
||||
url: string,
|
||||
branch: string,
|
||||
): Promise<unknown> => {
|
||||
const root = trimSlash(baseUrl.trim());
|
||||
if (root.length === 0) {
|
||||
throw new Error("REPOS_DEVTOOLS_URL is empty");
|
||||
}
|
||||
const res = await fetch(`${root}/repos-clone`, {
|
||||
method: "POST",
|
||||
headers: authHeaders(token),
|
||||
body: JSON.stringify({ url, branch }),
|
||||
});
|
||||
const text = await res.text();
|
||||
let body: unknown = text;
|
||||
try {
|
||||
body = JSON.parse(text) as unknown;
|
||||
} catch {
|
||||
/* keep text */
|
||||
}
|
||||
if (!res.ok) {
|
||||
throw new Error(
|
||||
`repos API ${res.status}: ${typeof body === "string" ? body : JSON.stringify(body)}`,
|
||||
);
|
||||
}
|
||||
return body;
|
||||
};
|
||||
|
||||
export const reposApiList = async (baseUrl: string, token: string): Promise<unknown> => {
|
||||
const root = trimSlash(baseUrl.trim());
|
||||
if (root.length === 0) {
|
||||
throw new Error("REPOS_DEVTOOLS_URL is empty");
|
||||
}
|
||||
const res = await fetch(`${root}/repos-list`, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
Accept: "application/json",
|
||||
Authorization: `Bearer ${normalizeReposToken(token)}`,
|
||||
},
|
||||
});
|
||||
const text = await res.text();
|
||||
if (!res.ok) {
|
||||
throw new Error(`repos API ${res.status}: ${text}`);
|
||||
}
|
||||
return JSON.parse(text) as unknown;
|
||||
};
|
||||
|
||||
export const reposApiLoad = async (
|
||||
baseUrl: string,
|
||||
token: string,
|
||||
name: string,
|
||||
): Promise<{ path: string; name: string }> => {
|
||||
const root = trimSlash(baseUrl.trim());
|
||||
if (root.length === 0) {
|
||||
throw new Error("REPOS_DEVTOOLS_URL is empty");
|
||||
}
|
||||
const res = await fetch(`${root}/repos-load`, {
|
||||
method: "POST",
|
||||
headers: authHeaders(token),
|
||||
body: JSON.stringify({ name }),
|
||||
});
|
||||
const text = await res.text();
|
||||
const body = JSON.parse(text) as unknown;
|
||||
if (!res.ok) {
|
||||
throw new Error(`repos API ${res.status}: ${text}`);
|
||||
}
|
||||
if (typeof body !== "object" || body === null) {
|
||||
throw new Error("repos-load: invalid response");
|
||||
}
|
||||
const rec = body as Record<string, unknown>;
|
||||
const p = rec.path;
|
||||
const n = rec.name;
|
||||
if (typeof p !== "string" || typeof n !== "string") {
|
||||
throw new Error("repos-load: missing path or name");
|
||||
}
|
||||
return { path: p, name: n };
|
||||
};
|
||||
91
services/anythingllm-devtools/src/server.ts
Normal file
91
services/anythingllm-devtools/src/server.ts
Normal file
@ -0,0 +1,91 @@
|
||||
import * as http from "node:http";
|
||||
import { readExpectedToken, requireBearer } from "./auth.js";
|
||||
import { readJsonBody } from "./httpUtil.js";
|
||||
import { loadConfig } from "./env.js";
|
||||
import { listWorkspaces } from "./anythingllmClient.js";
|
||||
import { runDevToolsScript } from "./devRunner.js";
|
||||
|
||||
const HOST = process.env.ANYTHINGLLM_DEVTOOLS_HOST ?? "127.0.0.1";
|
||||
const PORT = Number(process.env.ANYTHINGLLM_DEVTOOLS_PORT ?? "37146");
|
||||
|
||||
const json = (res: http.ServerResponse, status: number, body: unknown): void => {
|
||||
res.writeHead(status, { "Content-Type": "application/json; charset=utf-8" });
|
||||
res.end(JSON.stringify(body));
|
||||
};
|
||||
|
||||
const isRecord = (v: unknown): v is Record<string, unknown> =>
|
||||
typeof v === "object" && v !== null && !Array.isArray(v);
|
||||
|
||||
const scriptFromBody = (body: unknown): string => {
|
||||
if (!isRecord(body)) {
|
||||
throw new Error("Expected JSON object body");
|
||||
}
|
||||
const script = body.script;
|
||||
if (typeof script === "string") {
|
||||
return script;
|
||||
}
|
||||
const lines = body.lines;
|
||||
if (Array.isArray(lines) && lines.every((x) => typeof x === "string")) {
|
||||
return (lines as string[]).join("\n");
|
||||
}
|
||||
throw new Error('Body must include "script" (string) or "lines" (string[])');
|
||||
};
|
||||
|
||||
const main = (): void => {
|
||||
const token = readExpectedToken();
|
||||
if (token.length === 0) {
|
||||
console.error("anythingllm-devtools: set ANYTHINGLLM_DEVTOOLS_TOKEN (non-empty secret).");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const server = http.createServer(async (req, res) => {
|
||||
try {
|
||||
const url = req.url ?? "/";
|
||||
const method = req.method ?? "GET";
|
||||
|
||||
if (method === "GET" && (url === "/health" || url === "/health/")) {
|
||||
json(res, 200, { ok: true, service: "anythingllm-devtools" });
|
||||
return;
|
||||
}
|
||||
|
||||
if (!requireBearer(req, res, token)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (method === "GET" && (url === "/v1/workspaces" || url === "/v1/workspaces/")) {
|
||||
const cfg = loadConfig();
|
||||
if (cfg.anythingBaseUrl.trim().length === 0) {
|
||||
json(res, 503, { error: "ANYTHINGLLM_BASE_URL not configured" });
|
||||
return;
|
||||
}
|
||||
if (cfg.anythingApiKey.trim().length === 0) {
|
||||
json(res, 503, { error: "ANYTHINGLLM_API_KEY not configured" });
|
||||
return;
|
||||
}
|
||||
const workspaces = await listWorkspaces(cfg.anythingBaseUrl, cfg.anythingApiKey);
|
||||
json(res, 200, { workspaces });
|
||||
return;
|
||||
}
|
||||
|
||||
if (method === "POST" && (url === "/v1/devtools/run" || url === "/v1/devtools/run/")) {
|
||||
const body = await readJsonBody(req);
|
||||
const script = scriptFromBody(body);
|
||||
const cfg = loadConfig();
|
||||
const { text, actions } = await runDevToolsScript(script, cfg);
|
||||
json(res, 200, { ok: true, output: text, actions });
|
||||
return;
|
||||
}
|
||||
|
||||
json(res, 404, { error: "Not found" });
|
||||
} catch (e) {
|
||||
const msg = e instanceof Error ? e.message : String(e);
|
||||
json(res, 400, { error: msg });
|
||||
}
|
||||
});
|
||||
|
||||
server.listen(PORT, HOST, () => {
|
||||
console.error(`anythingllm-devtools listening on http://${HOST}:${PORT}`);
|
||||
});
|
||||
};
|
||||
|
||||
main();
|
||||
7
services/anythingllm-devtools/src/types.ts
Normal file
7
services/anythingllm-devtools/src/types.ts
Normal file
@ -0,0 +1,7 @@
|
||||
export interface AnythingWorkspace {
|
||||
readonly id: number;
|
||||
readonly name: string;
|
||||
readonly slug: string;
|
||||
readonly createdAt?: string;
|
||||
readonly lastUpdatedAt?: string;
|
||||
}
|
||||
27
services/anythingllm-devtools/src/workspaceEnsure.ts
Normal file
27
services/anythingllm-devtools/src/workspaceEnsure.ts
Normal file
@ -0,0 +1,27 @@
|
||||
import { createWorkspace, listWorkspaces } from "./anythingllmClient.js";
|
||||
import type { AnythingWorkspace } from "./types.js";
|
||||
|
||||
export interface EnsuredWorkspace {
|
||||
readonly workspace: AnythingWorkspace;
|
||||
readonly created: boolean;
|
||||
}
|
||||
|
||||
export const ensureWorkspaceForRepoName = async (
|
||||
baseUrl: string,
|
||||
apiKey: string,
|
||||
repoName: string,
|
||||
): Promise<EnsuredWorkspace> => {
|
||||
const key = repoName.trim();
|
||||
if (key.length === 0) {
|
||||
throw new Error("repo/workspace name is empty");
|
||||
}
|
||||
const all = await listWorkspaces(baseUrl, apiKey);
|
||||
const byName = all.find((w) => w.name === key);
|
||||
const bySlug = all.find((w) => w.slug === key);
|
||||
const found = byName ?? bySlug;
|
||||
if (found) {
|
||||
return { workspace: found, created: false };
|
||||
}
|
||||
const created = await createWorkspace(baseUrl, apiKey, key);
|
||||
return { workspace: created, created: true };
|
||||
};
|
||||
54
services/anythingllm-devtools/templates/4nkaiignore.default
Normal file
54
services/anythingllm-devtools/templates/4nkaiignore.default
Normal file
@ -0,0 +1,54 @@
|
||||
# .4nkaiignore — same rules as .gitignore (see gitignore(5))
|
||||
# Used by anythingllm-devtools and repos-devtools-server (post-clone) to filter uploads
|
||||
# and by the legacy VS Code extension after /repos-load-sync. Copy or rename to `.4nkaiignore`.
|
||||
|
||||
# VCS
|
||||
.git/
|
||||
|
||||
# Dependencies & build outputs
|
||||
node_modules/
|
||||
**/node_modules/
|
||||
dist/
|
||||
out/
|
||||
build/
|
||||
.next/
|
||||
.turbo/
|
||||
coverage/
|
||||
.nyc_output/
|
||||
target/
|
||||
|
||||
# IDE / OS
|
||||
.idea/
|
||||
.vscode/
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
|
||||
# Secrets & local env (never embed)
|
||||
.env
|
||||
.env.*
|
||||
!.env.example
|
||||
|
||||
# Large or binary artifacts (remove a line if your project should embed that type)
|
||||
*.png
|
||||
*.jpg
|
||||
*.jpeg
|
||||
*.gif
|
||||
*.webp
|
||||
*.ico
|
||||
*.pdf
|
||||
*.zip
|
||||
*.tar
|
||||
*.gz
|
||||
*.7z
|
||||
*.wasm
|
||||
*.so
|
||||
*.dylib
|
||||
*.dll
|
||||
*.exe
|
||||
*.mp4
|
||||
*.mp3
|
||||
|
||||
# Minified bundles (often redundant with sources)
|
||||
*.min.js
|
||||
*.min.css
|
||||
*.map
|
||||
17
services/anythingllm-devtools/tsconfig.json
Normal file
17
services/anythingllm-devtools/tsconfig.json
Normal file
@ -0,0 +1,17 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2022",
|
||||
"module": "NodeNext",
|
||||
"moduleResolution": "NodeNext",
|
||||
"outDir": "dist",
|
||||
"rootDir": "src",
|
||||
"strict": true,
|
||||
"esModuleInterop": true,
|
||||
"skipLibCheck": true,
|
||||
"noImplicitReturns": true,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"declaration": false
|
||||
},
|
||||
"include": ["src/**/*.ts"]
|
||||
}
|
||||
@ -1,6 +1,6 @@
|
||||
# .4nkaiignore — same rules as .gitignore (see gitignore(5))
|
||||
# Used to filter the initial document upload to AnythingLLM (extension).
|
||||
# Copy or rename to `.4nkaiignore` at the repo root and adjust per project.
|
||||
# Used by anythingllm-devtools and repos-devtools-server (post-clone) to filter uploads
|
||||
# and by the legacy VS Code extension after /repos-load-sync. Copy or rename to `.4nkaiignore`.
|
||||
|
||||
# VCS
|
||||
.git/
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user