From b21ac2cf641a1f69abde640c4e18edba76a69445 Mon Sep 17 00:00:00 2001 From: Nicolas Cantu Date: Fri, 3 Apr 2026 19:06:19 +0200 Subject: [PATCH] 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 --- .gitignore | 7 + .smartIde/agents/ia-dev-agent-loop.md | 2 +- ...ia-dev-branch-align-by-script-from-test.md | 2 +- .../agents/ia-dev-change-to-all-branches.md | 2 +- .../ia-dev-closure-point-7-justification.md | 2 +- .smartIde/agents/ia-dev-code.md | 2 +- .smartIde/agents/ia-dev-deploy-by-script.md | 2 +- .../agents/ia-dev-deploy-pprod-or-prod.md | 2 +- .smartIde/agents/ia-dev-docupdate.md | 2 +- .smartIde/agents/ia-dev-evol.md | 2 +- .smartIde/agents/ia-dev-fix-lint.md | 2 +- .smartIde/agents/ia-dev-fix-search.md | 2 +- .smartIde/agents/ia-dev-fix.md | 2 +- ...rocess.md => ia-dev-git-issues-process.md} | 6 +- .smartIde/agents/ia-dev-notary-ai-loop.md | 2 +- .smartIde/agents/ia-dev-notary-ai-process.md | 2 +- .smartIde/agents/ia-dev-push-by-script.md | 2 +- .smartIde/agents/ia-dev-setup-host.md | 2 +- .smartIde/rules/smart-ide-ia-dev-bridge.mdc | 2 +- builazoo/README.md | 3 + cron/fragments/builazoo.cron | 4 + docs/API/README.md | 1 + docs/API/anythingllm-devtools-api.md | 66 +++++ docs/README.md | 3 +- docs/anythingllm-workspaces.md | 1 + docs/ecosystem-architecture-and-sync.md | 4 +- docs/features/docv-ai-integration.md | 2 +- docs/features/initial-rag-sync-4nkaiignore.md | 11 +- docs/features/lapce-porting-roadmap.md | 10 +- docs/ia_dev-module.md | 10 +- docs/ia_dev-project-smart_ide.md | 4 +- docs/repo/README.md | 3 +- docs/repo/extension-anythingllm-workspaces.md | 6 +- docs/repo/ia-dev-project-conf-schema.md | 2 +- docs/repo/ia-dev-repository-overview.md | 10 +- docs/repo/ia-dev-shared-lib.md | 2 +- docs/repo/ia-dev-smart-ide-integration.md | 2 +- docs/repo/projects-directory.md | 13 +- docs/repo/service-anythingllm-devtools.md | 15 ++ docs/repo/service-repos-devtools.md | 2 +- docs/services.md | 2 +- docs/system-architecture.md | 3 +- extensions/anythingllm-workspaces/README.md | 4 +- .../templates/4nkaiignore.default | 4 +- ia_dev | 2 +- projects/active-project.json.example | 53 +++- projects/builazoo/conf.json | 52 ++++ projects/builazoo/smart_ide.code-workspace | 13 + projects/enso/conf.json | 4 +- projects/smart_ide/conf.json | 10 +- projects/ssh-config.example | 75 ++++++ scripts/ensure-ia-dev-project-link.sh | 23 ++ .../ensure-ia-dev-smart-ide-project-link.sh | 14 +- services/anythingllm-devtools/.env.example | 22 ++ services/anythingllm-devtools/.gitignore | 2 + services/anythingllm-devtools/README.md | 40 +++ .../anythingllm-devtools/package-lock.json | 63 +++++ services/anythingllm-devtools/package.json | 23 ++ .../src/anythingllmClient.ts | 122 +++++++++ .../src/anythingllmDocumentApi.ts | 56 +++++ services/anythingllm-devtools/src/auth.ts | 21 ++ .../anythingllm-devtools/src/commandParser.ts | 57 +++++ .../anythingllm-devtools/src/devRunner.ts | 238 ++++++++++++++++++ services/anythingllm-devtools/src/env.ts | 67 +++++ services/anythingllm-devtools/src/httpUtil.ts | 25 ++ .../src/initialRagSync.ts | 140 +++++++++++ .../src/reposApiClient.ts | 96 +++++++ services/anythingllm-devtools/src/server.ts | 91 +++++++ services/anythingllm-devtools/src/types.ts | 7 + .../src/workspaceEnsure.ts | 27 ++ .../templates/4nkaiignore.default | 54 ++++ services/anythingllm-devtools/tsconfig.json | 17 ++ .../templates/4nkaiignore.default | 4 +- 73 files changed, 1569 insertions(+), 83 deletions(-) rename .smartIde/agents/{ia-dev-gitea-issues-process.md => ia-dev-git-issues-process.md} (83%) create mode 100644 builazoo/README.md create mode 100644 cron/fragments/builazoo.cron create mode 100644 docs/API/anythingllm-devtools-api.md create mode 100644 docs/repo/service-anythingllm-devtools.md create mode 100644 projects/builazoo/conf.json create mode 100644 projects/builazoo/smart_ide.code-workspace create mode 100644 projects/ssh-config.example create mode 100755 scripts/ensure-ia-dev-project-link.sh create mode 100644 services/anythingllm-devtools/.env.example create mode 100644 services/anythingllm-devtools/.gitignore create mode 100644 services/anythingllm-devtools/README.md create mode 100644 services/anythingllm-devtools/package-lock.json create mode 100644 services/anythingllm-devtools/package.json create mode 100644 services/anythingllm-devtools/src/anythingllmClient.ts create mode 100644 services/anythingllm-devtools/src/anythingllmDocumentApi.ts create mode 100644 services/anythingllm-devtools/src/auth.ts create mode 100644 services/anythingllm-devtools/src/commandParser.ts create mode 100644 services/anythingllm-devtools/src/devRunner.ts create mode 100644 services/anythingllm-devtools/src/env.ts create mode 100644 services/anythingllm-devtools/src/httpUtil.ts create mode 100644 services/anythingllm-devtools/src/initialRagSync.ts create mode 100644 services/anythingllm-devtools/src/reposApiClient.ts create mode 100644 services/anythingllm-devtools/src/server.ts create mode 100644 services/anythingllm-devtools/src/types.ts create mode 100644 services/anythingllm-devtools/src/workspaceEnsure.ts create mode 100644 services/anythingllm-devtools/templates/4nkaiignore.default create mode 100644 services/anythingllm-devtools/tsconfig.json diff --git a/.gitignore b/.gitignore index 6b5a7b4..466a2fa 100644 --- a/.gitignore +++ b/.gitignore @@ -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 diff --git a/.smartIde/agents/ia-dev-agent-loop.md b/.smartIde/agents/ia-dev-agent-loop.md index 7a5f0c7..a9a2eae 100644 --- a/.smartIde/agents/ia-dev-agent-loop.md +++ b/.smartIde/agents/ia-dev-agent-loop.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). diff --git a/.smartIde/agents/ia-dev-branch-align-by-script-from-test.md b/.smartIde/agents/ia-dev-branch-align-by-script-from-test.md index cf3f257..b270fcd 100644 --- a/.smartIde/agents/ia-dev-branch-align-by-script-from-test.md +++ b/.smartIde/agents/ia-dev-branch-align-by-script-from-test.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). diff --git a/.smartIde/agents/ia-dev-change-to-all-branches.md b/.smartIde/agents/ia-dev-change-to-all-branches.md index c61a836..f036ec3 100644 --- a/.smartIde/agents/ia-dev-change-to-all-branches.md +++ b/.smartIde/agents/ia-dev-change-to-all-branches.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). diff --git a/.smartIde/agents/ia-dev-closure-point-7-justification.md b/.smartIde/agents/ia-dev-closure-point-7-justification.md index 8b2f4a0..4ade9f4 100644 --- a/.smartIde/agents/ia-dev-closure-point-7-justification.md +++ b/.smartIde/agents/ia-dev-closure-point-7-justification.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). diff --git a/.smartIde/agents/ia-dev-code.md b/.smartIde/agents/ia-dev-code.md index b8c2aa0..82ecc96 100644 --- a/.smartIde/agents/ia-dev-code.md +++ b/.smartIde/agents/ia-dev-code.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). diff --git a/.smartIde/agents/ia-dev-deploy-by-script.md b/.smartIde/agents/ia-dev-deploy-by-script.md index fb6e2a0..d425f6b 100644 --- a/.smartIde/agents/ia-dev-deploy-by-script.md +++ b/.smartIde/agents/ia-dev-deploy-by-script.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). diff --git a/.smartIde/agents/ia-dev-deploy-pprod-or-prod.md b/.smartIde/agents/ia-dev-deploy-pprod-or-prod.md index 20a8188..78d0a03 100644 --- a/.smartIde/agents/ia-dev-deploy-pprod-or-prod.md +++ b/.smartIde/agents/ia-dev-deploy-pprod-or-prod.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). diff --git a/.smartIde/agents/ia-dev-docupdate.md b/.smartIde/agents/ia-dev-docupdate.md index 0ba7597..16f693f 100644 --- a/.smartIde/agents/ia-dev-docupdate.md +++ b/.smartIde/agents/ia-dev-docupdate.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). diff --git a/.smartIde/agents/ia-dev-evol.md b/.smartIde/agents/ia-dev-evol.md index 6eff9e2..029fca7 100644 --- a/.smartIde/agents/ia-dev-evol.md +++ b/.smartIde/agents/ia-dev-evol.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). diff --git a/.smartIde/agents/ia-dev-fix-lint.md b/.smartIde/agents/ia-dev-fix-lint.md index 9d21120..524de9c 100644 --- a/.smartIde/agents/ia-dev-fix-lint.md +++ b/.smartIde/agents/ia-dev-fix-lint.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). diff --git a/.smartIde/agents/ia-dev-fix-search.md b/.smartIde/agents/ia-dev-fix-search.md index 5f9ffb3..8f0632c 100644 --- a/.smartIde/agents/ia-dev-fix-search.md +++ b/.smartIde/agents/ia-dev-fix-search.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). diff --git a/.smartIde/agents/ia-dev-fix.md b/.smartIde/agents/ia-dev-fix.md index 675a473..60d0675 100644 --- a/.smartIde/agents/ia-dev-fix.md +++ b/.smartIde/agents/ia-dev-fix.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). diff --git a/.smartIde/agents/ia-dev-gitea-issues-process.md b/.smartIde/agents/ia-dev-git-issues-process.md similarity index 83% rename from .smartIde/agents/ia-dev-gitea-issues-process.md rename to .smartIde/agents/ia-dev-git-issues-process.md index 2732ff6..d1fd045 100644 --- a/.smartIde/agents/ia-dev-gitea-issues-process.md +++ b/.smartIde/agents/ia-dev-git-issues-process.md @@ -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`. diff --git a/.smartIde/agents/ia-dev-notary-ai-loop.md b/.smartIde/agents/ia-dev-notary-ai-loop.md index b9f4b14..7f10b35 100644 --- a/.smartIde/agents/ia-dev-notary-ai-loop.md +++ b/.smartIde/agents/ia-dev-notary-ai-loop.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). diff --git a/.smartIde/agents/ia-dev-notary-ai-process.md b/.smartIde/agents/ia-dev-notary-ai-process.md index e31d93d..7d0e6af 100644 --- a/.smartIde/agents/ia-dev-notary-ai-process.md +++ b/.smartIde/agents/ia-dev-notary-ai-process.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). diff --git a/.smartIde/agents/ia-dev-push-by-script.md b/.smartIde/agents/ia-dev-push-by-script.md index 130a676..0bcd3e5 100644 --- a/.smartIde/agents/ia-dev-push-by-script.md +++ b/.smartIde/agents/ia-dev-push-by-script.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). diff --git a/.smartIde/agents/ia-dev-setup-host.md b/.smartIde/agents/ia-dev-setup-host.md index 89f554d..dd6ad11 100644 --- a/.smartIde/agents/ia-dev-setup-host.md +++ b/.smartIde/agents/ia-dev-setup-host.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). diff --git a/.smartIde/rules/smart-ide-ia-dev-bridge.mdc b/.smartIde/rules/smart-ide-ia-dev-bridge.mdc index 4242342..38894e6 100644 --- a/.smartIde/rules/smart-ide-ia-dev-bridge.mdc +++ b/.smartIde/rules/smart-ide-ia-dev-bridge.mdc @@ -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//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/` doit être un **lien** vers `../../projects/` 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=` et/ou `--project ` 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//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`. diff --git a/builazoo/README.md b/builazoo/README.md new file mode 100644 index 0000000..7fbf03a --- /dev/null +++ b/builazoo/README.md @@ -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). diff --git a/cron/fragments/builazoo.cron b/cron/fragments/builazoo.cron new file mode 100644 index 0000000..694f872 --- /dev/null +++ b/cron/fragments/builazoo.cron @@ -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 diff --git a/docs/API/README.md b/docs/API/README.md index 17ad7b6..f515b3e 100644 --- a/docs/API/README.md +++ b/docs/API/README.md @@ -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é. diff --git a/docs/API/anythingllm-devtools-api.md b/docs/API/anythingllm-devtools-api.md new file mode 100644 index 0000000..eec5986 --- /dev/null +++ b/docs/API/anythingllm-devtools-api.md @@ -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 +``` + +## 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) diff --git a/docs/README.md b/docs/README.md index 31f5eb7..7d51087 100644 --- a/docs/README.md +++ b/docs/README.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. diff --git a/docs/anythingllm-workspaces.md b/docs/anythingllm-workspaces.md index f578ebd..299254e 100644 --- a/docs/anythingllm-workspaces.md +++ b/docs/anythingllm-workspaces.md @@ -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). diff --git a/docs/ecosystem-architecture-and-sync.md b/docs/ecosystem-architecture-and-sync.md index f4bf46b..ca57716 100644 --- a/docs/ecosystem-architecture-and-sync.md +++ b/docs/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/` 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//`) et vérifier les chemins **absolus** dans `projects//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`) diff --git a/docs/features/docv-ai-integration.md b/docs/features/docv-ai-integration.md index f41107b..2e3a69b 100644 --- a/docs/features/docv-ai-integration.md +++ b/docs/features/docv-ai-integration.md @@ -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//` 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//conf.json`** ; les scripts `ia_dev` y accèdent via le lien `ia_dev/projects/` 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//conf.json`** ; les scripts `ia_dev` y accèdent via le lien `ia_dev/projects/` lorsque le script [`ensure-ia-dev-project-link.sh`](../../scripts/ensure-ia-dev-project-link.sh) `` (ou le wrapper `ensure-ia-dev-smart-ide-project-link.sh` pour `smart_ide`) a été exécuté. ## Flux cible (vue simplifiée) diff --git a/docs/features/initial-rag-sync-4nkaiignore.md b/docs/features/initial-rag-sync-4nkaiignore.md index 455766e..da056aa 100644 --- a/docs/features/initial-rag-sync-4nkaiignore.md +++ b/docs/features/initial-rag-sync-4nkaiignore.md @@ -9,15 +9,16 @@ ## Comportement 1. **Serveur `repos-devtools-server`** : après `git clone` réussi, copie **`templates/4nkaiignore.default`** vers **`/.4nkaiignore`** si absent. -2. **Extension 0.3.0** : après `/repos-clone-sync`, `/repos-load-sync`, ou sur **`/workspace-sync `**, 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 `**, 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. diff --git a/docs/features/lapce-porting-roadmap.md b/docs/features/lapce-porting-roadmap.md index bf49408..9df4219 100644 --- a/docs/features/lapce-porting-roadmap.md +++ b/docs/features/lapce-porting-roadmap.md @@ -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 diff --git a/docs/ia_dev-module.md b/docs/ia_dev-module.md index 24c9340..64d931d 100644 --- a/docs/ia_dev-module.md +++ b/docs/ia_dev-module.md @@ -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//`** à 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//`** à 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/` → `../../projects/` (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 diff --git a/docs/ia_dev-project-smart_ide.md b/docs/ia_dev-project-smart_ide.md index 0dc00c0..fdd8df3 100644 --- a/docs/ia_dev-project-smart_ide.md +++ b/docs/ia_dev-project-smart_ide.md @@ -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` | diff --git a/docs/repo/README.md b/docs/repo/README.md index 0e09aea..4c5996b 100644 --- a/docs/repo/README.md +++ b/docs/repo/README.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/). diff --git a/docs/repo/extension-anythingllm-workspaces.md b/docs/repo/extension-anythingllm-workspaces.md index 55c5c83..e9fda2d 100644 --- a/docs/repo/extension-anythingllm-workspaces.md +++ b/docs/repo/extension-anythingllm-workspaces.md @@ -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/`, …). diff --git a/docs/repo/ia-dev-project-conf-schema.md b/docs/repo/ia-dev-project-conf-schema.md index ffadf1a..ab10be1 100644 --- a/docs/repo/ia-dev-project-conf-schema.md +++ b/docs/repo/ia-dev-project-conf-schema.md @@ -42,7 +42,7 @@ Les agents ne modifient pas `projects//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 diff --git a/docs/repo/ia-dev-repository-overview.md b/docs/repo/ia-dev-repository-overview.md index 05cabfc..3905160 100644 --- a/docs/repo/ia-dev-repository-overview.md +++ b/docs/repo/ia-dev-repository-overview.md @@ -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//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//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//data/issues` ; scripts `gitea-issues/`. | +| **Ticketing** | `git-issues-process`, `agent-loop` ; spooler `projects//data/issues` ; scripts `git-issues/`. | | **IA notaire (ai_working_help)** | `notary-ai-loop`, `notary-ai-process` ; API `ai_working_help/server.js` ; spooler `projects//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//logs/` et `projects//data/issues/`. +- **git-issues/** : logs et data par projet sous `projects//logs/` et `projects//data/issues/`. - **ai_working_help/** : API et scripts `notary-ai/` ; doc `ai_working_help/docs/notary-ai-api.md`. ## Scripts centralisés (`deploy/`) diff --git a/docs/repo/ia-dev-shared-lib.md b/docs/repo/ia-dev-shared-lib.md index c5409f8..9a48263 100644 --- a/docs/repo/ia-dev-shared-lib.md +++ b/docs/repo/ia-dev-shared-lib.md @@ -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. diff --git a/docs/repo/ia-dev-smart-ide-integration.md b/docs/repo/ia-dev-smart-ide-integration.md index 7894d4d..3b480e7 100644 --- a/docs/repo/ia-dev-smart-ide-integration.md +++ b/docs/repo/ia-dev-smart-ide-integration.md @@ -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//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 diff --git a/docs/repo/projects-directory.md b/docs/repo/projects-directory.md index 53bad7c..c9fae44 100644 --- a/docs/repo/projects-directory.md +++ b/docs/repo/projects-directory.md @@ -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 diff --git a/docs/repo/service-anythingllm-devtools.md b/docs/repo/service-anythingllm-devtools.md new file mode 100644 index 0000000..7fc7576 --- /dev/null +++ b/docs/repo/service-anythingllm-devtools.md @@ -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`**. diff --git a/docs/repo/service-repos-devtools.md b/docs/repo/service-repos-devtools.md index 07bf7ea..584fdd8 100644 --- a/docs/repo/service-repos-devtools.md +++ b/docs/repo/service-repos-devtools.md @@ -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 diff --git a/docs/services.md b/docs/services.md index b61e973..6966631 100644 --- a/docs/services.md +++ b/docs/services.md @@ -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 diff --git a/docs/system-architecture.md b/docs/system-architecture.md index 3dc886a..e54b433 100644 --- a/docs/system-architecture.md +++ b/docs/system-architecture.md @@ -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//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 | diff --git a/extensions/anythingllm-workspaces/README.md b/extensions/anythingllm-workspaces/README.md index abee19d..7bae8b6 100644 --- a/extensions/anythingllm-workspaces/README.md +++ b/extensions/anythingllm-workspaces/README.md @@ -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)** diff --git a/extensions/anythingllm-workspaces/templates/4nkaiignore.default b/extensions/anythingllm-workspaces/templates/4nkaiignore.default index 87a678a..be598ec 100644 --- a/extensions/anythingllm-workspaces/templates/4nkaiignore.default +++ b/extensions/anythingllm-workspaces/templates/4nkaiignore.default @@ -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/ diff --git a/ia_dev b/ia_dev index 285e720..b4ce818 160000 --- a/ia_dev +++ b/ia_dev @@ -1 +1 @@ -Subproject commit 285e72039eda3b27684ad4c67620350a9150d30f +Subproject commit b4ce81858cf4ba10b5a5ac16ea4ebc6f94e85a5d diff --git a/projects/active-project.json.example b/projects/active-project.json.example index e7c73f6..7a0ce78 100644 --- a/projects/active-project.json.example +++ b/projects/active-project.json.example @@ -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//conf.json. Field default_env is one of: test, pprod, prod — used by agents when the environment is not specified." + "id": "", + "": "", + "cron": { + "git_pull": true + }, + "project_path": ".", + "build_dirs": [], + "deploy": {}, + "version": { + "package_json_paths": [], + "splash_app_": "" + }, + "mail": { + "imap_bridge_env": ".secrets/git-issues/imap-bridge.env" + }, + "git": { + "wiki_url": "https://git./4nk//wiki", + "token_file": ".secrets/git-issues/token" + }, + "tickets": { + "ticketing_url": "https://git./4nk//issues", + "authorized_emails": { + "to": [ + { + "test": "AI..TEST@", + "pprod": "AI..PPROD@", + "prod": "AI..PROD@" + } + ], + "from": ["@"] + } + }, + "smart_ide": { + "remote_data_access": { + "environments": { + "test": { + "ssh_host_alias": "-test", + "remote_data_directories": [] + }, + "pprod": { + "ssh_host_alias": "-pprod", + "remote_data_directories": [] + }, + "prod": { + "ssh_host_alias": "-prod", + "remote_data_directories": [] + } + } + } + } } diff --git a/projects/builazoo/conf.json b/projects/builazoo/conf.json new file mode 100644 index 0000000..0e7aee5 --- /dev/null +++ b/projects/builazoo/conf.json @@ -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": [] + } + } + } + } +} diff --git a/projects/builazoo/smart_ide.code-workspace b/projects/builazoo/smart_ide.code-workspace new file mode 100644 index 0000000..fcb240c --- /dev/null +++ b/projects/builazoo/smart_ide.code-workspace @@ -0,0 +1,13 @@ +{ + "folders": [ + { + "path": "../.." + }, + { + "path": "../../builazoo" + } + ], + "settings": { + "smartIde.activeProjectId": "builazoo" + } +} diff --git a/projects/enso/conf.json b/projects/enso/conf.json index 913fc4b..b40a53b 100644 --- a/projects/enso/conf.json +++ b/projects/enso/conf.json @@ -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", diff --git a/projects/smart_ide/conf.json b/projects/smart_ide/conf.json index 1e7567a..41e77bf 100644 --- a/projects/smart_ide/conf.json +++ b/projects/smart_ide/conf.json @@ -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"] diff --git a/projects/ssh-config.example b/projects/ssh-config.example new file mode 100644 index 0000000..43cbbaf --- /dev/null +++ b/projects/ssh-config.example @@ -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//conf.json → smart_ide.remote_data_access.environments.*.ssh_host_alias +# - projects/active-project.json.example → alias -test | -pprod | -prod +# +# Remplacer , , , selon le poste. +# Bastion optionnel par Host : ProxyJump @ + +Host git. + HostName git. + User git + IdentityFile ~/.ssh/ + 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 + User + IdentityFile ~/.ssh/ + IdentitiesOnly yes + +Host pprod + HostName + User + IdentityFile ~/.ssh/ + IdentitiesOnly yes + +Host prod + HostName + User + IdentityFile ~/.ssh/ + IdentitiesOnly yes + +Host smart-ide-test + HostName + User + IdentityFile ~/.ssh/ + IdentitiesOnly yes + +Host smart-ide-pprod + HostName + User + IdentityFile ~/.ssh/ + IdentitiesOnly yes + +Host smart-ide-prod + HostName + User + IdentityFile ~/.ssh/ + IdentitiesOnly yes + +Host builazoo-test + HostName + User + IdentityFile ~/.ssh/ + IdentitiesOnly yes + +Host builazoo-pprod + HostName + User + IdentityFile ~/.ssh/ + IdentitiesOnly yes + +Host builazoo-prod + HostName + User + IdentityFile ~/.ssh/ + IdentitiesOnly yes + +# Autres ids : dupliquer le trio -test / -pprod / -prod comme dans projects//conf.json. diff --git a/scripts/ensure-ia-dev-project-link.sh b/scripts/ensure-ia-dev-project-link.sh new file mode 100755 index 0000000..484e9d9 --- /dev/null +++ b/scripts/ensure-ia-dev-project-link.sh @@ -0,0 +1,23 @@ +#!/usr/bin/env bash +# Create ia_dev/projects/ -> ../../projects/ so ia_dev scripts resolve conf.json +# from the monorepo versioned projects//. +set -euo pipefail +ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +PROJECT_ID="${1:?usage: $(basename "$0") }" + +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}" diff --git a/scripts/ensure-ia-dev-smart-ide-project-link.sh b/scripts/ensure-ia-dev-smart-ide-project-link.sh index b44f240..79e6016 100755 --- a/scripts/ensure-ia-dev-smart-ide-project-link.sh +++ b/scripts/ensure-ia-dev-smart-ide-project-link.sh @@ -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 diff --git a/services/anythingllm-devtools/.env.example b/services/anythingllm-devtools/.env.example new file mode 100644 index 0000000..1eacdf2 --- /dev/null +++ b/services/anythingllm-devtools/.env.example @@ -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= diff --git a/services/anythingllm-devtools/.gitignore b/services/anythingllm-devtools/.gitignore new file mode 100644 index 0000000..b947077 --- /dev/null +++ b/services/anythingllm-devtools/.gitignore @@ -0,0 +1,2 @@ +node_modules/ +dist/ diff --git a/services/anythingllm-devtools/README.md b/services/anythingllm-devtools/README.md new file mode 100644 index 0000000..b4c28bd --- /dev/null +++ b/services/anythingllm-devtools/README.md @@ -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. diff --git a/services/anythingllm-devtools/package-lock.json b/services/anythingllm-devtools/package-lock.json new file mode 100644 index 0000000..8b8f223 --- /dev/null +++ b/services/anythingllm-devtools/package-lock.json @@ -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" + } + } +} diff --git a/services/anythingllm-devtools/package.json b/services/anythingllm-devtools/package.json new file mode 100644 index 0000000..532ad77 --- /dev/null +++ b/services/anythingllm-devtools/package.json @@ -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" + } +} diff --git a/services/anythingllm-devtools/src/anythingllmClient.ts b/services/anythingllm-devtools/src/anythingllmClient.ts new file mode 100644 index 0000000..4e01a8e --- /dev/null +++ b/services/anythingllm-devtools/src/anythingllmClient.ts @@ -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 => + 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 => { + 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 => { + 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)); +}; diff --git a/services/anythingllm-devtools/src/anythingllmDocumentApi.ts b/services/anythingllm-devtools/src/anythingllmDocumentApi.ts new file mode 100644 index 0000000..7b82695 --- /dev/null +++ b/services/anythingllm-devtools/src/anythingllmDocumentApi.ts @@ -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 => + typeof value === "object" && value !== null && !Array.isArray(value); + +export const uploadLocalFileToWorkspace = async ( + baseUrl: string, + apiKey: string, + workspaceSlug: string, + absoluteFilePath: string, + uploadFileName: string, +): Promise => { + 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}`); + } +}; diff --git a/services/anythingllm-devtools/src/auth.ts b/services/anythingllm-devtools/src/auth.ts new file mode 100644 index 0000000..509f9f8 --- /dev/null +++ b/services/anythingllm-devtools/src/auth.ts @@ -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; +}; diff --git a/services/anythingllm-devtools/src/commandParser.ts b/services/anythingllm-devtools/src/commandParser.ts new file mode 100644 index 0000000..9c26b80 --- /dev/null +++ b/services/anythingllm-devtools/src/commandParser.ts @@ -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 — clone (branch test)", + " /repos-clone-sync — clone + workspace + suggested open-folder + initial RAG (.4nkaiignore)", + " repos-list — list git repos under REPOS_DEVTOOLS_ROOT", + " /repos-load — verify repo + suggested open folder", + " /repos-load-sync — suggested open folder + workspace + optional initial RAG", + " /workspace-load — ensure workspace + suggested workspace URL", + " /workspace-sync — ensure workspace + initial RAG (repo must exist under root)", + " help — this list", + ].join("\n"); +}; diff --git a/services/anythingllm-devtools/src/devRunner.ts b/services/anythingllm-devtools/src/devRunner.ts new file mode 100644 index 0000000..fc9ca36 --- /dev/null +++ b/services/anythingllm-devtools/src/devRunner.ts @@ -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 => { + 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 => { + 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; + 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 => { + 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 }; +}; diff --git a/services/anythingllm-devtools/src/env.ts b/services/anythingllm-devtools/src/env.ts new file mode 100644 index 0000000..bd32a82 --- /dev/null +++ b/services/anythingllm-devtools/src/env.ts @@ -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, + }; +}; diff --git a/services/anythingllm-devtools/src/httpUtil.ts b/services/anythingllm-devtools/src/httpUtil.ts new file mode 100644 index 0000000..698210e --- /dev/null +++ b/services/anythingllm-devtools/src/httpUtil.ts @@ -0,0 +1,25 @@ +import type { IncomingMessage } from "node:http"; + +const MAX_BODY = 1_048_576; + +export const readJsonBody = async (req: IncomingMessage): Promise => { + 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 }); + } +}; diff --git a/services/anythingllm-devtools/src/initialRagSync.ts b/services/anythingllm-devtools/src/initialRagSync.ts new file mode 100644 index 0000000..ec132c3 --- /dev/null +++ b/services/anythingllm-devtools/src/initialRagSync.ts @@ -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 => { + const out: string[] = []; + const scan = async (d: string): Promise => { + 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 => { + 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, + }; +}; diff --git a/services/anythingllm-devtools/src/reposApiClient.ts b/services/anythingllm-devtools/src/reposApiClient.ts new file mode 100644 index 0000000..65c6ec1 --- /dev/null +++ b/services/anythingllm-devtools/src/reposApiClient.ts @@ -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 => { + 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 => { + 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 => { + 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; + 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 }; +}; diff --git a/services/anythingllm-devtools/src/server.ts b/services/anythingllm-devtools/src/server.ts new file mode 100644 index 0000000..bd4a322 --- /dev/null +++ b/services/anythingllm-devtools/src/server.ts @@ -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 => + 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(); diff --git a/services/anythingllm-devtools/src/types.ts b/services/anythingllm-devtools/src/types.ts new file mode 100644 index 0000000..08ffa7d --- /dev/null +++ b/services/anythingllm-devtools/src/types.ts @@ -0,0 +1,7 @@ +export interface AnythingWorkspace { + readonly id: number; + readonly name: string; + readonly slug: string; + readonly createdAt?: string; + readonly lastUpdatedAt?: string; +} diff --git a/services/anythingllm-devtools/src/workspaceEnsure.ts b/services/anythingllm-devtools/src/workspaceEnsure.ts new file mode 100644 index 0000000..94fb596 --- /dev/null +++ b/services/anythingllm-devtools/src/workspaceEnsure.ts @@ -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 => { + 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 }; +}; diff --git a/services/anythingllm-devtools/templates/4nkaiignore.default b/services/anythingllm-devtools/templates/4nkaiignore.default new file mode 100644 index 0000000..be598ec --- /dev/null +++ b/services/anythingllm-devtools/templates/4nkaiignore.default @@ -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 diff --git a/services/anythingllm-devtools/tsconfig.json b/services/anythingllm-devtools/tsconfig.json new file mode 100644 index 0000000..524cdcb --- /dev/null +++ b/services/anythingllm-devtools/tsconfig.json @@ -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"] +} diff --git a/services/repos-devtools-server/templates/4nkaiignore.default b/services/repos-devtools-server/templates/4nkaiignore.default index 1a33234..be598ec 100644 --- a/services/repos-devtools-server/templates/4nkaiignore.default +++ b/services/repos-devtools-server/templates/4nkaiignore.default @@ -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/