feat(console): Cloud Console Skills management view
Tests / Test passed: 855

Adds SkillsView.vue (cloud-skill CRUD, per-host entitlement grant/revoke,
read-only host local-skill inventory), skill API client methods + types,
and wires it into App.vue behind the skills:admin scope. Console
typecheck/build/tests green (20 passed).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-07-15 08:06:49 +08:00
co-authored by Claude Opus 4.6
parent 8300c3b6b7
commit fd0ea3a066
5 changed files with 431 additions and 4 deletions
+10 -1
View File
@@ -3,6 +3,7 @@ import { computed, onMounted, onUnmounted, ref } from "vue";
import type { Component } from "vue";
import {
Boxes,
BookOpen,
ListChecks,
LogOut,
MonitorSmartphone,
@@ -24,8 +25,9 @@ import DevicesView from "./views/DevicesView.vue";
import PluginsView from "./views/PluginsView.vue";
import UsersView from "./views/UsersView.vue";
import LlmProvidersView from "./views/LlmProvidersView.vue";
import SkillsView from "./views/SkillsView.vue";
type ViewId = "tasks" | "devices" | "plugins" | "users" | "providers";
type ViewId = "tasks" | "devices" | "plugins" | "users" | "providers" | "skills";
const activeView = ref<ViewId>("tasks");
const currentUser = ref<CloudUser | null>(null);
@@ -53,6 +55,7 @@ const canAdminGovernance = computed(
currentUser.value?.scopes.includes("governance:admin")),
);
const canAdminProviders = computed(() => hasScope(currentUser.value, "llm-providers:admin"));
const canAdminSkills = computed(() => hasScope(currentUser.value, "skills:admin"));
const isAuthenticated = computed(() => currentUser.value !== null);
const currentUserLabel = computed(() =>
currentUser.value ? `${currentUser.value.display_name} (${currentUser.value.role})` : "",
@@ -70,6 +73,9 @@ const navItems = computed<{ id: ViewId; label: string; icon: Component }[]>(() =
if (canAdminProviders.value) {
items.push({ id: "providers", label: "LLM providers", icon: SlidersHorizontal });
}
if (canAdminSkills.value) {
items.push({ id: "skills", label: "Skills", icon: BookOpen });
}
return items;
});
@@ -128,6 +134,8 @@ const activeComponent = computed(() => {
return UsersView;
case "providers":
return LlmProvidersView;
case "skills":
return SkillsView;
default:
return TasksView;
}
@@ -165,6 +173,7 @@ const activeComponent = computed(() => {
:can-admin-governance="canAdminGovernance"
/>
<LlmProvidersView v-else-if="activeView === 'providers'" :can-admin="canAdminProviders" />
<SkillsView v-else-if="activeView === 'skills'" :can-admin="canAdminSkills" />
<component v-else :is="activeComponent" :can-submit="canSubmitTasks" />
</main>
</div>