mirror of
https://github.com/aljazceru/claude-code-viewer.git
synced 2026-01-02 13:14:22 +01:00
chore: disable permisseion setting if feature unavailable
This commit is contained in:
@@ -10,6 +10,7 @@ import {
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from "@/components/ui/select";
|
||||
import { useFeatureFlags } from "@/hooks/useFeatureFlags";
|
||||
import { useTheme } from "@/hooks/useTheme";
|
||||
import { projectDetailQuery, projectListQuery } from "../lib/api/queries";
|
||||
import type { SupportedLocale } from "../lib/i18n/schema";
|
||||
@@ -36,6 +37,9 @@ export const SettingsControls: FC<SettingsControlsProps> = ({
|
||||
const queryClient = useQueryClient();
|
||||
const { theme } = useTheme();
|
||||
const { i18n } = useLingui();
|
||||
const { isFlagEnabled } = useFeatureFlags();
|
||||
|
||||
const isToolApprovalAvailable = isFlagEnabled("tool-approval");
|
||||
|
||||
const handleHideNoUserMessageChange = async () => {
|
||||
const newConfig = {
|
||||
@@ -222,6 +226,7 @@ export const SettingsControls: FC<SettingsControlsProps> = ({
|
||||
<Select
|
||||
value={config?.permissionMode || "default"}
|
||||
onValueChange={handlePermissionModeChange}
|
||||
disabled={!isToolApprovalAvailable}
|
||||
>
|
||||
<SelectTrigger id={permissionModeId} className="w-full">
|
||||
<SelectValue placeholder={i18n._("Select permission mode")} />
|
||||
@@ -253,7 +258,7 @@ export const SettingsControls: FC<SettingsControlsProps> = ({
|
||||
</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
{showDescriptions && (
|
||||
{showDescriptions && isToolApprovalAvailable && (
|
||||
<p className="text-xs text-muted-foreground mt-1">
|
||||
<Trans
|
||||
id="settings.permission.mode.description"
|
||||
@@ -261,6 +266,14 @@ export const SettingsControls: FC<SettingsControlsProps> = ({
|
||||
/>
|
||||
</p>
|
||||
)}
|
||||
{showDescriptions && !isToolApprovalAvailable && (
|
||||
<p className="text-xs text-destructive mt-1">
|
||||
<Trans
|
||||
id="settings.permission.mode.unavailable"
|
||||
message="This feature is not available in your Claude Code version. All tools will be automatically approved."
|
||||
/>
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
|
||||
@@ -2,11 +2,8 @@ import { Trans } from "@lingui/react";
|
||||
import { useSuspenseQuery } from "@tanstack/react-query";
|
||||
import { CheckCircle2, ChevronDown, ChevronRight, XCircle } from "lucide-react";
|
||||
import { type FC, type ReactNode, useState } from "react";
|
||||
import {
|
||||
claudeCodeFeaturesQuery,
|
||||
claudeCodeMetaQuery,
|
||||
systemVersionQuery,
|
||||
} from "@/lib/api/queries";
|
||||
import { useFeatureFlags } from "@/hooks/useFeatureFlags";
|
||||
import { claudeCodeMetaQuery, systemVersionQuery } from "@/lib/api/queries";
|
||||
import { Badge } from "./ui/badge";
|
||||
import {
|
||||
Collapsible,
|
||||
@@ -27,48 +24,33 @@ interface FeatureInfo {
|
||||
|
||||
const getFeatureInfo = (featureName: string): FeatureInfo => {
|
||||
switch (featureName) {
|
||||
case "canUseTool":
|
||||
case "tool-approval":
|
||||
return {
|
||||
title: (
|
||||
<Trans
|
||||
id="system_info.feature.can_use_tool.title"
|
||||
message="Tool Use Permission Control"
|
||||
id="system_info.feature.tool_approval.title"
|
||||
message="Tool Execution Approval"
|
||||
/>
|
||||
),
|
||||
description: (
|
||||
<Trans
|
||||
id="system_info.feature.can_use_tool.description"
|
||||
message="Dynamically control tool usage permissions and request user approval before tool execution (v1.0.82+)"
|
||||
id="system_info.feature.tool_approval.description"
|
||||
message="Allows you to approve or reject tool executions before Claude runs them, giving you full control over actions"
|
||||
/>
|
||||
),
|
||||
};
|
||||
case "uuidOnSDKMessage":
|
||||
return {
|
||||
title: (
|
||||
<Trans
|
||||
id="system_info.feature.uuid_on_sdk_message.title"
|
||||
message="Message UUID Support"
|
||||
/>
|
||||
),
|
||||
description: (
|
||||
<Trans
|
||||
id="system_info.feature.uuid_on_sdk_message.description"
|
||||
message="Adds unique identifiers to SDK messages for better tracking (v1.0.86+)"
|
||||
/>
|
||||
),
|
||||
};
|
||||
case "agentSdk":
|
||||
case "agent-sdk":
|
||||
return {
|
||||
title: (
|
||||
<Trans
|
||||
id="system_info.feature.agent_sdk.title"
|
||||
message="Claude Agent SDK"
|
||||
message="Enhanced Agent Mode"
|
||||
/>
|
||||
),
|
||||
description: (
|
||||
<Trans
|
||||
id="system_info.feature.agent_sdk.description"
|
||||
message="Uses Claude Agent SDK instead of Claude Code SDK (v1.0.125+)"
|
||||
message="Uses @anthropic-ai/claude-agent-sdk instead of @anthropic-ai/claude-code for enhanced capabilities"
|
||||
/>
|
||||
),
|
||||
};
|
||||
@@ -96,9 +78,7 @@ export const SystemInfoCard: FC = () => {
|
||||
...claudeCodeMetaQuery,
|
||||
});
|
||||
|
||||
const { data: claudeCodeFeaturesData } = useSuspenseQuery({
|
||||
...claudeCodeFeaturesQuery,
|
||||
});
|
||||
const { flags } = useFeatureFlags();
|
||||
|
||||
return (
|
||||
<div className="h-full flex flex-col">
|
||||
@@ -185,7 +165,7 @@ export const SystemInfoCard: FC = () => {
|
||||
<CollapsibleContent className="pt-3">
|
||||
<TooltipProvider>
|
||||
<ul className="space-y-2 pl-2">
|
||||
{claudeCodeFeaturesData?.features.map(({ name, enabled }) => {
|
||||
{flags.map(({ name, enabled }) => {
|
||||
const featureInfo = getFeatureInfo(name);
|
||||
return (
|
||||
<li key={name} className="flex items-start gap-2">
|
||||
|
||||
29
src/hooks/useFeatureFlags.ts
Normal file
29
src/hooks/useFeatureFlags.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
import { useSuspenseQuery } from "@tanstack/react-query";
|
||||
import { useCallback, useMemo } from "react";
|
||||
import { featureFlagsQuery } from "../lib/api/queries";
|
||||
import type { FlagName } from "../server/core/feature-flag/models/flag";
|
||||
|
||||
export const useFeatureFlags = () => {
|
||||
const { data } = useSuspenseQuery({
|
||||
queryKey: featureFlagsQuery.queryKey,
|
||||
queryFn: featureFlagsQuery.queryFn,
|
||||
});
|
||||
|
||||
const enabledFlags = useMemo(() => {
|
||||
return new Set(
|
||||
data.flags.filter((flag) => flag.enabled).map((flag) => flag.name),
|
||||
);
|
||||
}, [data.flags]);
|
||||
|
||||
const isFlagEnabled = useCallback(
|
||||
(flagName: FlagName) => {
|
||||
return enabledFlags.has(flagName);
|
||||
},
|
||||
[enabledFlags],
|
||||
);
|
||||
|
||||
return {
|
||||
flags: data.flags,
|
||||
isFlagEnabled,
|
||||
} as const;
|
||||
};
|
||||
@@ -262,3 +262,16 @@ export const schedulerJobsQuery = {
|
||||
return await response.json();
|
||||
},
|
||||
} as const;
|
||||
|
||||
export const featureFlagsQuery = {
|
||||
queryKey: ["flags"],
|
||||
queryFn: async () => {
|
||||
const response = await honoClient.api.flags.$get();
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`Failed to fetch feature flags: ${response.statusText}`);
|
||||
}
|
||||
|
||||
return await response.json();
|
||||
},
|
||||
} as const;
|
||||
|
||||
@@ -368,11 +368,11 @@
|
||||
"translation": "Choose your preferred language"
|
||||
},
|
||||
"system_info.feature.agent_sdk.title": {
|
||||
"message": "Claude Agent SDK",
|
||||
"message": "Enhanced Agent Mode",
|
||||
"placeholders": {},
|
||||
"comments": [],
|
||||
"origin": [["src/components/SystemInfoCard.tsx", 65]],
|
||||
"translation": "Claude Agent SDK"
|
||||
"origin": [["src/components/SystemInfoCard.tsx", 49]],
|
||||
"translation": "Enhanced Agent Mode"
|
||||
},
|
||||
"system_info.claude_code": {
|
||||
"message": "Claude Code",
|
||||
@@ -486,6 +486,13 @@
|
||||
"origin": [["src/components/SettingsControls.tsx", 255]],
|
||||
"translation": "Control how Claude Code handles permission requests for file operations"
|
||||
},
|
||||
"settings.permission.mode.unavailable": {
|
||||
"message": "This feature is not available in your Claude Code version. All tools will be automatically approved.",
|
||||
"placeholders": {},
|
||||
"comments": [],
|
||||
"origin": [["src/components/SettingsControls.tsx", 272]],
|
||||
"translation": "This feature is not available in your Claude Code version. All tools will be automatically approved."
|
||||
},
|
||||
"session.conversation.in.progress": {
|
||||
"message": "Conversation is in progress...",
|
||||
"placeholders": {},
|
||||
@@ -599,13 +606,6 @@
|
||||
"origin": [["src/components/GlobalSidebar.tsx", 56]],
|
||||
"translation": "Display and behavior preferences"
|
||||
},
|
||||
"system_info.feature.can_use_tool.description": {
|
||||
"message": "Dynamically control tool usage permissions and request user approval before tool execution (v1.0.82+)",
|
||||
"placeholders": {},
|
||||
"comments": [],
|
||||
"origin": [["src/components/SystemInfoCard.tsx", 41]],
|
||||
"translation": "Dynamically control tool usage permissions and request user approval before tool execution (v1.0.82+)"
|
||||
},
|
||||
"settings.locale.en": {
|
||||
"message": "English",
|
||||
"placeholders": {},
|
||||
@@ -1506,13 +1506,6 @@
|
||||
],
|
||||
"translation": "Tool Result"
|
||||
},
|
||||
"system_info.feature.can_use_tool.title": {
|
||||
"message": "Tool Use Permission Control",
|
||||
"placeholders": {},
|
||||
"comments": [],
|
||||
"origin": [["src/components/SystemInfoCard.tsx", 35]],
|
||||
"translation": "Tool Use Permission Control"
|
||||
},
|
||||
"sessions.total": {
|
||||
"message": "total",
|
||||
"placeholders": {},
|
||||
@@ -1629,11 +1622,25 @@
|
||||
"translation": "Document type not supported for display"
|
||||
},
|
||||
"system_info.feature.agent_sdk.description": {
|
||||
"message": "Uses Claude Agent SDK instead of Claude Code SDK (v1.0.125+)",
|
||||
"message": "Uses @anthropic-ai/claude-agent-sdk instead of @anthropic-ai/claude-code for enhanced capabilities",
|
||||
"placeholders": {},
|
||||
"comments": [],
|
||||
"origin": [["src/components/SystemInfoCard.tsx", 71]],
|
||||
"translation": "Uses Claude Agent SDK instead of Claude Code SDK (v1.0.125+)"
|
||||
"origin": [["src/components/SystemInfoCard.tsx", 53]],
|
||||
"translation": "Uses @anthropic-ai/claude-agent-sdk instead of @anthropic-ai/claude-code for enhanced capabilities"
|
||||
},
|
||||
"system_info.feature.tool_approval.title": {
|
||||
"message": "Tool Execution Approval",
|
||||
"placeholders": {},
|
||||
"comments": [],
|
||||
"origin": [["src/components/SystemInfoCard.tsx", 35]],
|
||||
"translation": "Tool Execution Approval"
|
||||
},
|
||||
"system_info.feature.tool_approval.description": {
|
||||
"message": "Allows you to approve or reject tool executions before Claude runs them, giving you full control over actions",
|
||||
"placeholders": {},
|
||||
"comments": [],
|
||||
"origin": [["src/components/SystemInfoCard.tsx", 41]],
|
||||
"translation": "Allows you to approve or reject tool executions before Claude runs them, giving you full control over actions"
|
||||
},
|
||||
"system_info.version_label": {
|
||||
"message": "Version",
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -368,11 +368,11 @@
|
||||
"translation": "お好みの言語を選択"
|
||||
},
|
||||
"system_info.feature.agent_sdk.title": {
|
||||
"message": "Claude Agent SDK",
|
||||
"message": "Enhanced Agent Mode",
|
||||
"placeholders": {},
|
||||
"comments": [],
|
||||
"origin": [["src/components/SystemInfoCard.tsx", 65]],
|
||||
"translation": "Claude Agent SDK"
|
||||
"origin": [["src/components/SystemInfoCard.tsx", 49]],
|
||||
"translation": "拡張エージェントモード"
|
||||
},
|
||||
"system_info.claude_code": {
|
||||
"message": "Claude Code",
|
||||
@@ -486,6 +486,13 @@
|
||||
"origin": [["src/components/SettingsControls.tsx", 255]],
|
||||
"translation": "ファイル操作の権限リクエストの処理方法を制御"
|
||||
},
|
||||
"settings.permission.mode.unavailable": {
|
||||
"message": "This feature is not available in your Claude Code version. All tools will be automatically approved.",
|
||||
"placeholders": {},
|
||||
"comments": [],
|
||||
"origin": [["src/components/SettingsControls.tsx", 272]],
|
||||
"translation": "お使いのClaude Codeバージョンではこの機能は利用できません。すべてのツールは自動で承認されます。"
|
||||
},
|
||||
"session.conversation.in.progress": {
|
||||
"message": "Conversation is in progress...",
|
||||
"placeholders": {},
|
||||
@@ -599,13 +606,6 @@
|
||||
"origin": [["src/components/GlobalSidebar.tsx", 56]],
|
||||
"translation": "表示と動作の設定"
|
||||
},
|
||||
"system_info.feature.can_use_tool.description": {
|
||||
"message": "Dynamically control tool usage permissions and request user approval before tool execution (v1.0.82+)",
|
||||
"placeholders": {},
|
||||
"comments": [],
|
||||
"origin": [["src/components/SystemInfoCard.tsx", 41]],
|
||||
"translation": "動的にツールの使用許可を制御し、ツール実行前にユーザーの承認を求めることができます (v1.0.82+)"
|
||||
},
|
||||
"settings.locale.en": {
|
||||
"message": "English",
|
||||
"placeholders": {},
|
||||
@@ -1506,13 +1506,6 @@
|
||||
],
|
||||
"translation": "ツール実行結果"
|
||||
},
|
||||
"system_info.feature.can_use_tool.title": {
|
||||
"message": "Tool Use Permission Control",
|
||||
"placeholders": {},
|
||||
"comments": [],
|
||||
"origin": [["src/components/SystemInfoCard.tsx", 35]],
|
||||
"translation": "ツール使用権限制御"
|
||||
},
|
||||
"sessions.total": {
|
||||
"message": "total",
|
||||
"placeholders": {},
|
||||
@@ -1629,11 +1622,25 @@
|
||||
"translation": "文書タイプは表示がサポートされていません"
|
||||
},
|
||||
"system_info.feature.agent_sdk.description": {
|
||||
"message": "Uses Claude Agent SDK instead of Claude Code SDK (v1.0.125+)",
|
||||
"message": "Uses @anthropic-ai/claude-agent-sdk instead of @anthropic-ai/claude-code for enhanced capabilities",
|
||||
"placeholders": {},
|
||||
"comments": [],
|
||||
"origin": [["src/components/SystemInfoCard.tsx", 71]],
|
||||
"translation": "Claude Code SDKではなくClaude Agent SDKを使用 (v1.0.125+)"
|
||||
"origin": [["src/components/SystemInfoCard.tsx", 53]],
|
||||
"translation": "@anthropic-ai/claude-code ではなく @anthropic-ai/claude-agent-sdk を利用します"
|
||||
},
|
||||
"system_info.feature.tool_approval.title": {
|
||||
"message": "Tool Execution Approval",
|
||||
"placeholders": {},
|
||||
"comments": [],
|
||||
"origin": [["src/components/SystemInfoCard.tsx", 35]],
|
||||
"translation": "ツール実行承認"
|
||||
},
|
||||
"system_info.feature.tool_approval.description": {
|
||||
"message": "Allows you to approve or reject tool executions before Claude runs them, giving you full control over actions",
|
||||
"placeholders": {},
|
||||
"comments": [],
|
||||
"origin": [["src/components/SystemInfoCard.tsx", 41]],
|
||||
"translation": "Claude がツールを実行する前に承認または拒否でき、アクションを完全にコントロールできます"
|
||||
},
|
||||
"system_info.version_label": {
|
||||
"message": "Version",
|
||||
|
||||
File diff suppressed because one or more lines are too long
11
src/server/core/feature-flag/models/flag.ts
Normal file
11
src/server/core/feature-flag/models/flag.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
export type Flag =
|
||||
| {
|
||||
name: "tool-approval";
|
||||
enabled: boolean;
|
||||
}
|
||||
| {
|
||||
name: "agent-sdk";
|
||||
enabled: boolean;
|
||||
};
|
||||
|
||||
export type FlagName = Flag["name"];
|
||||
@@ -0,0 +1,43 @@
|
||||
import { Context, Effect, Layer } from "effect";
|
||||
import type { ControllerResponse } from "../../../lib/effect/toEffectResponse";
|
||||
import type { InferEffect } from "../../../lib/effect/types";
|
||||
import { ClaudeCodeService } from "../../claude-code/services/ClaudeCodeService";
|
||||
import type { Flag } from "../models/flag";
|
||||
|
||||
const LayerImpl = Effect.gen(function* () {
|
||||
const claudeCodeService = yield* ClaudeCodeService;
|
||||
|
||||
const getFlags = () =>
|
||||
Effect.gen(function* () {
|
||||
const claudeCodeFeatures =
|
||||
yield* claudeCodeService.getAvailableFeatures();
|
||||
|
||||
return {
|
||||
response: {
|
||||
flags: [
|
||||
{
|
||||
name: "tool-approval",
|
||||
enabled: claudeCodeFeatures.canUseTool,
|
||||
},
|
||||
{
|
||||
name: "agent-sdk",
|
||||
enabled: claudeCodeFeatures.agentSdk,
|
||||
},
|
||||
] satisfies Flag[],
|
||||
},
|
||||
status: 200,
|
||||
} as const satisfies ControllerResponse;
|
||||
});
|
||||
|
||||
return {
|
||||
getFlags,
|
||||
};
|
||||
});
|
||||
|
||||
export type IFeatureFlagController = InferEffect<typeof LayerImpl>;
|
||||
export class FeatureFlagController extends Context.Tag("FeatureFlagController")<
|
||||
FeatureFlagController,
|
||||
IFeatureFlagController
|
||||
>() {
|
||||
static Live = Layer.effect(this, LayerImpl);
|
||||
}
|
||||
@@ -13,6 +13,7 @@ import { userMessageInputSchema } from "../core/claude-code/schema";
|
||||
import { ClaudeCodeLifeCycleService } from "../core/claude-code/services/ClaudeCodeLifeCycleService";
|
||||
import { TypeSafeSSE } from "../core/events/functions/typeSafeSSE";
|
||||
import { SSEController } from "../core/events/presentation/SSEController";
|
||||
import { FeatureFlagController } from "../core/feature-flag/presentation/FeatureFlagController";
|
||||
import { FileSystemController } from "../core/file-system/presentation/FileSystemController";
|
||||
import { GitController } from "../core/git/presentation/GitController";
|
||||
import { CommitRequestSchema, PushRequestSchema } from "../core/git/schema";
|
||||
@@ -49,6 +50,7 @@ export const routes = (app: HonoAppType) =>
|
||||
const fileSystemController = yield* FileSystemController;
|
||||
const claudeCodeController = yield* ClaudeCodeController;
|
||||
const schedulerController = yield* SchedulerController;
|
||||
const featureFlagController = yield* FeatureFlagController;
|
||||
|
||||
// services
|
||||
const envService = yield* EnvService;
|
||||
@@ -553,6 +555,18 @@ export const routes = (app: HonoAppType) =>
|
||||
return response;
|
||||
},
|
||||
)
|
||||
|
||||
/**
|
||||
* FeatureFlagController Routes
|
||||
*/
|
||||
.get("/api/flags", async (c) => {
|
||||
const response = await effectToResponse(
|
||||
c,
|
||||
featureFlagController.getFlags().pipe(Effect.provide(runtime)),
|
||||
);
|
||||
|
||||
return response;
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ import { ClaudeCodeService } from "./core/claude-code/services/ClaudeCodeService
|
||||
import { ClaudeCodeSessionProcessService } from "./core/claude-code/services/ClaudeCodeSessionProcessService";
|
||||
import { SSEController } from "./core/events/presentation/SSEController";
|
||||
import { FileWatcherService } from "./core/events/services/fileWatcher";
|
||||
import { FeatureFlagController } from "./core/feature-flag/presentation/FeatureFlagController";
|
||||
import { FileSystemController } from "./core/file-system/presentation/FileSystemController";
|
||||
import { GitController } from "./core/git/presentation/GitController";
|
||||
import { GitService } from "./core/git/services/GitService";
|
||||
@@ -68,6 +69,7 @@ const program = routes(honoApp)
|
||||
Effect.provide(FileSystemController.Live),
|
||||
Effect.provide(SSEController.Live),
|
||||
Effect.provide(SchedulerController.Live),
|
||||
Effect.provide(FeatureFlagController.Live),
|
||||
)
|
||||
.pipe(
|
||||
/** Application */
|
||||
|
||||
Reference in New Issue
Block a user