feat(settings): réglages Claude CLI (binaire + ~/.claude) & notif « disponible »
- Réglages → Claude CLI : override du chemin du binaire `claude` (effet à la prochaine session, fallback `which claude`), diagnostic de détection, et override de la racine ~/.claude (effet au redémarrage). Validateurs stricts. - Push : notifie aussi sur le front busy→idle (session redevenue disponible). - Renomme l'état affiché idle → « disponible » / « available » (web EN/FR + VS Code) ; l'enum SessionActivity du protocole reste inchangé.
This commit is contained in:
@@ -56,7 +56,7 @@ export default {
|
||||
},
|
||||
registryStatus: {
|
||||
busy: 'busy',
|
||||
idle: 'idle',
|
||||
idle: 'available',
|
||||
waiting: 'waiting',
|
||||
},
|
||||
dialog: {
|
||||
@@ -283,7 +283,7 @@ export default {
|
||||
live: 'Live',
|
||||
waiting: 'Waiting',
|
||||
busy: 'Busy',
|
||||
idle: 'Idle',
|
||||
idle: 'Available',
|
||||
exited: 'Exited',
|
||||
resumable: 'Resumable',
|
||||
},
|
||||
@@ -413,6 +413,20 @@ export default {
|
||||
removeRoot: 'Remove',
|
||||
scanInterval: 'Re-scan interval (minutes)',
|
||||
scanIntervalHint: '0 disables periodic scanning. Changes apply on the next daemon restart.',
|
||||
// Claude CLI
|
||||
claudeCli: 'Claude CLI',
|
||||
claudeCliHint: 'Where Arboretum finds the `claude` binary and its data. Handy when the daemon runs as a service with a minimal PATH that does not include ~/.local/bin.',
|
||||
claudeBinStatusOk: 'Detected',
|
||||
claudeBinStatusNotFound: 'Not found — sessions cannot start until this is resolved.',
|
||||
claudeBinStatusBadPath: 'The configured path is not an executable file.',
|
||||
claudeBinSourceConfigured: 'configured',
|
||||
claudeBinSourcePath: 'PATH',
|
||||
claudeBinPathLabel: 'Binary path override',
|
||||
claudeBinPathHint: 'Absolute path to the `claude` executable. Leave empty to auto-detect via PATH. Takes effect on the next session.',
|
||||
claudeBinPathPlaceholder: 'e.g. /home/you/.local/bin/claude',
|
||||
claudeHomeLabel: 'Claude home directory',
|
||||
claudeHomeHint: 'Override the ~/.claude location (transcripts & session registry). Applies on the next daemon restart.',
|
||||
claudeHomePlaceholder: 'e.g. /home/you/.claude',
|
||||
// Serveur (lecture seule)
|
||||
server: 'Server',
|
||||
serverHint: 'Set at startup via CLI flags — changing them requires restarting the daemon.',
|
||||
|
||||
@@ -58,7 +58,7 @@ const fr: typeof en = {
|
||||
},
|
||||
registryStatus: {
|
||||
busy: 'occupée',
|
||||
idle: 'inactive',
|
||||
idle: 'disponible',
|
||||
waiting: 'en attente',
|
||||
},
|
||||
dialog: {
|
||||
@@ -286,7 +286,7 @@ const fr: typeof en = {
|
||||
live: 'Active',
|
||||
waiting: 'En attente',
|
||||
busy: 'Occupée',
|
||||
idle: 'Au repos',
|
||||
idle: 'Disponible',
|
||||
exited: 'Terminée',
|
||||
resumable: 'Reprenable',
|
||||
},
|
||||
@@ -416,6 +416,20 @@ const fr: typeof en = {
|
||||
removeRoot: 'Retirer',
|
||||
scanInterval: 'Intervalle de re-scan (minutes)',
|
||||
scanIntervalHint: '0 désactive le scan périodique. Les changements prennent effet au prochain redémarrage du daemon.',
|
||||
// Claude CLI
|
||||
claudeCli: 'CLI Claude',
|
||||
claudeCliHint: 'Où Arboretum trouve le binaire `claude` et ses données. Utile quand le daemon tourne en service avec un PATH minimal qui n’inclut pas ~/.local/bin.',
|
||||
claudeBinStatusOk: 'Détecté',
|
||||
claudeBinStatusNotFound: 'Introuvable — impossible de lancer une session tant que ce n’est pas résolu.',
|
||||
claudeBinStatusBadPath: 'Le chemin configuré n’est pas un fichier exécutable.',
|
||||
claudeBinSourceConfigured: 'configuré',
|
||||
claudeBinSourcePath: 'PATH',
|
||||
claudeBinPathLabel: 'Chemin du binaire (override)',
|
||||
claudeBinPathHint: 'Chemin absolu vers l’exécutable `claude`. Laisser vide pour l’auto-détection via le PATH. Prend effet à la prochaine session.',
|
||||
claudeBinPathPlaceholder: 'ex. /home/vous/.local/bin/claude',
|
||||
claudeHomeLabel: 'Répertoire ~/.claude',
|
||||
claudeHomeHint: 'Surcharge l’emplacement de ~/.claude (transcripts & registre des sessions). Prend effet au redémarrage du daemon.',
|
||||
claudeHomePlaceholder: 'ex. /home/vous/.claude',
|
||||
// Serveur (lecture seule)
|
||||
server: 'Serveur',
|
||||
serverHint: 'Définis au démarrage via des flags CLI — les modifier nécessite de redémarrer le daemon.',
|
||||
|
||||
@@ -9,6 +9,8 @@ export const useSettingsStore = defineStore('settings', () => {
|
||||
const server = ref<ServerInfo | null>(null);
|
||||
const scanRoots = ref<string[]>([]);
|
||||
const scanIntervalMin = ref(0);
|
||||
const claudeBinPath = ref<string | null>(null);
|
||||
const claudeHome = ref<string | null>(null);
|
||||
const loaded = ref(false);
|
||||
const saving = ref(false);
|
||||
|
||||
@@ -16,6 +18,8 @@ export const useSettingsStore = defineStore('settings', () => {
|
||||
server.value = res.server;
|
||||
scanRoots.value = res.settings.scanRoots;
|
||||
scanIntervalMin.value = res.settings.scanIntervalMin;
|
||||
claudeBinPath.value = res.settings.claudeBinPath;
|
||||
claudeHome.value = res.settings.claudeHome;
|
||||
loaded.value = true;
|
||||
}
|
||||
|
||||
@@ -32,5 +36,5 @@ export const useSettingsStore = defineStore('settings', () => {
|
||||
}
|
||||
}
|
||||
|
||||
return { server, scanRoots, scanIntervalMin, loaded, saving, fetch, save };
|
||||
return { server, scanRoots, scanIntervalMin, claudeBinPath, claudeHome, loaded, saving, fetch, save };
|
||||
});
|
||||
|
||||
@@ -137,6 +137,44 @@
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Claude CLI -->
|
||||
<section class="card flex flex-col gap-3">
|
||||
<h2 class="flex items-center gap-2 text-sm font-semibold text-zinc-100">
|
||||
<Terminal :size="16" /> {{ t('settings.claudeCli') }}
|
||||
</h2>
|
||||
<p class="text-xs text-zinc-500">{{ t('settings.claudeCliHint') }}</p>
|
||||
|
||||
<!-- Diagnostic de détection -->
|
||||
<div v-if="settings.server" class="card-inset flex flex-wrap items-center gap-x-2 gap-y-1 text-xs">
|
||||
<component :is="claudeBin.ok ? CheckCircle2 : AlertCircle" :size="15" :class="claudeBin.ok ? 'text-emerald-400' : 'text-rose-400'" />
|
||||
<span :class="claudeBin.ok ? 'text-zinc-300' : 'text-rose-300'">
|
||||
{{ claudeBin.ok ? t('settings.claudeBinStatusOk') : (claudeBin.source === 'configured' ? t('settings.claudeBinStatusBadPath') : t('settings.claudeBinStatusNotFound')) }}
|
||||
</span>
|
||||
<code v-if="claudeBin.path" class="min-w-0 truncate font-mono text-zinc-200" :title="claudeBin.path">{{ claudeBin.path }}</code>
|
||||
<span v-if="claudeBin.source" class="badge bg-zinc-700/40 text-zinc-300">
|
||||
{{ claudeBin.source === 'configured' ? t('settings.claudeBinSourceConfigured') : t('settings.claudeBinSourcePath') }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<label class="flex flex-col gap-1">
|
||||
<span class="text-xs text-zinc-400">{{ t('settings.claudeBinPathLabel') }}</span>
|
||||
<input v-model.trim="claudeBinDraft" class="input font-mono" :placeholder="t('settings.claudeBinPathPlaceholder')" />
|
||||
<span class="text-xs text-zinc-500">{{ t('settings.claudeBinPathHint') }}</span>
|
||||
</label>
|
||||
|
||||
<label class="flex flex-col gap-1">
|
||||
<span class="text-xs text-zinc-400">{{ t('settings.claudeHomeLabel') }}</span>
|
||||
<input v-model.trim="claudeHomeDraft" class="input font-mono" :placeholder="t('settings.claudeHomePlaceholder')" />
|
||||
<span class="text-xs text-zinc-500">{{ t('settings.claudeHomeHint') }}</span>
|
||||
</label>
|
||||
|
||||
<div>
|
||||
<BaseButton variant="primary" :loading="settings.saving" :disabled="!claudeDirty" @click="saveClaude">
|
||||
{{ t('settings.save') }}
|
||||
</BaseButton>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Sécurité & conformité -->
|
||||
<section class="card flex flex-col gap-3">
|
||||
<h2 class="flex items-center gap-2 text-sm font-semibold text-zinc-100">
|
||||
@@ -202,7 +240,7 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted, ref } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { Bell, BellOff, Check, Coffee, Copy, Download, FolderOpen, KeyRound, Plus, RefreshCw, ScanSearch, Send, Server, Shield, SlidersHorizontal, Trash2, X } from '@lucide/vue';
|
||||
import { AlertCircle, Bell, BellOff, Check, CheckCircle2, Coffee, Copy, Download, FolderOpen, KeyRound, Plus, RefreshCw, ScanSearch, Send, Server, Shield, SlidersHorizontal, Terminal, Trash2, X } from '@lucide/vue';
|
||||
import type {
|
||||
AuditLogEntry,
|
||||
AuditLogsResponse,
|
||||
@@ -328,6 +366,27 @@ async function saveDiscovery(): Promise<void> {
|
||||
}
|
||||
}
|
||||
|
||||
// ---- Claude CLI ----
|
||||
const claudeBinDraft = ref('');
|
||||
const claudeHomeDraft = ref('');
|
||||
const claudeBin = computed(() => settings.server?.claudeBin ?? { path: null, source: null, ok: false });
|
||||
const claudeDirty = computed(
|
||||
() => claudeBinDraft.value !== (settings.claudeBinPath ?? '') || claudeHomeDraft.value !== (settings.claudeHome ?? ''),
|
||||
);
|
||||
function syncClaudeDraft(): void {
|
||||
claudeBinDraft.value = settings.claudeBinPath ?? '';
|
||||
claudeHomeDraft.value = settings.claudeHome ?? '';
|
||||
}
|
||||
async function saveClaude(): Promise<void> {
|
||||
try {
|
||||
await settings.save({ claudeBinPath: claudeBinDraft.value, claudeHome: claudeHomeDraft.value });
|
||||
syncClaudeDraft();
|
||||
toasts.success(t('settings.saved'));
|
||||
} catch (e) {
|
||||
toasts.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
// ---- Sécurité & conformité (audit / RGPD) ----
|
||||
const audit = ref<AuditLogEntry[]>([]);
|
||||
const auditing = ref(false);
|
||||
@@ -378,6 +437,7 @@ async function deleteMyData(): Promise<void> {
|
||||
onMounted(async () => {
|
||||
await Promise.all([loadTokens(), settings.loaded ? Promise.resolve() : settings.fetch()]);
|
||||
syncDiscoveryDraft();
|
||||
syncClaudeDraft();
|
||||
void push.refresh();
|
||||
});
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user