This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import { useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useQueries, useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
|
||||
import { FileTree, type SubdirId, type FileEntry, FILE_SUBDIRS } from './FileTree';
|
||||
import { MonacoFileEditor } from './MonacoFileEditor';
|
||||
@@ -17,91 +18,20 @@ import { MemoryPanel } from './MemoryPanel';
|
||||
/** All subdirs shown in the tree — both real file-based and virtual. */
|
||||
const ALL_SUBDIRS: SubdirId[] = ['agents-md', 'browser-macros', 'recordings', 'notes', 'subscribed-notes', 'pets', 'browser-sessions', 'mcp', 'skills', 'ssh-connections', 'trash', 'memory'];
|
||||
|
||||
const SUBDIR_INFO: { id: SubdirId; icon: string; title: string; desc: string; agency: string }[] = [
|
||||
{
|
||||
id: 'agents-md',
|
||||
icon: '📖',
|
||||
title: 'AGENTS.md',
|
||||
desc: 'タスク起動時に system prompt へ自動注入される、ユーザー専用の永続的な指示。 「常に丁寧な日本語で答える」「Tailwind を優先」等、毎タスクで覚えて欲しい好み・ルールを書く。 最大 64KB。 ファイル形式は markdown。',
|
||||
agency: 'ユーザー編集 / エージェントが自動参照',
|
||||
},
|
||||
{
|
||||
id: 'browser-macros',
|
||||
icon: '🤖',
|
||||
title: 'browser-macros/',
|
||||
desc: 'Playwright ベースのブラウザマクロ。recordings/ から "Save as Script" で生成、または UI で手書き。RunUserScript ツールで実行。main({ context, params }) シグネチャで context は Playwright BrowserContext。session_profile_id で保存済みログインを利用可能。.next.js は self-healing 失敗時の自動パッチ候補で、Diff レビュー後に accept/reject。雛形・手順書は Skills へ、アドホックなコード実行は Bash ツールへ。',
|
||||
agency: 'エージェント実行 / UI で recordings → スクリプト化',
|
||||
},
|
||||
{
|
||||
id: 'recordings',
|
||||
icon: '🎬',
|
||||
title: 'recordings/',
|
||||
desc: 'BrowseWeb 操作トレース (JSON)。BrowseWeb 呼び出しで recordTo パラメータを指定すると、成功したアクションがバッファされ、タスク終了時にここへ書き出される。"Save as Script" でブラウザマクロに変換できる (browser-macros/ へ保存)。',
|
||||
agency: 'エージェントが記録 / UI でスクリプト化',
|
||||
},
|
||||
{
|
||||
id: 'pets',
|
||||
icon: '◉',
|
||||
title: 'pets/',
|
||||
desc: 'Codex Pets 互換のキャラクターをユーザーごとに import する場所。Chat 画面右下に表示され、タスク状態やツール呼び出しに応じて小さく反応する。',
|
||||
agency: 'ユーザー管理 / Chat UI が参照',
|
||||
},
|
||||
{
|
||||
id: 'browser-sessions',
|
||||
icon: '🌐',
|
||||
title: 'browser-sessions/',
|
||||
desc: 'ブラウザのログインプロファイル管理。CAPTCHA や 2FA の壁を越えて取得した cookie/storage を user-scoped に暗号化保存し、browser-macros から session_profile_id で参照する。',
|
||||
agency: 'ユーザー管理 (noVNC でログイン → save) / browser-macros が利用',
|
||||
},
|
||||
{
|
||||
id: 'trash',
|
||||
icon: '🗑',
|
||||
title: 'trash/',
|
||||
desc: '削除ファイルの退避先。ハードデリートはせず、`{YYYYMMDD-HHMMSS}-{rand4hex}-{name}` 命名で着地する。script の accept/reject で旧版もここへ。閲覧は read-only、復元したい場合は内容コピーで元 subdir に PUT。30 日経過したファイルはサーバ起動時 / 24h 毎に自動削除 (`tools.trash_retention_days` で変更可)。',
|
||||
agency: 'ソフト削除 / read-only / 30 日自動 cleanup',
|
||||
},
|
||||
{
|
||||
id: 'memory',
|
||||
icon: '🧠',
|
||||
title: 'memory/',
|
||||
desc: 'エージェントの永続事実置き場。`MEMORY.md` (index) がタスク起動時に system prompt へ自動注入 (32 KB cap)。エントリの閲覧・編集・削除はこのパネルで行う。reflection の実行履歴・revert は Settings → Reflection 履歴。',
|
||||
agency: 'エージェント管理 + ユーザー編集(このパネル)',
|
||||
},
|
||||
{
|
||||
id: 'mcp',
|
||||
icon: '🔌',
|
||||
title: 'mcp/',
|
||||
desc: 'MCP サーバーの登録・接続管理・設定変更をまとめて行えます。OAuth / API key 認証、ツール一覧の取得、接続状態の確認がここで完結します。credentials は AES-256-GCM で暗号化して保存されます。',
|
||||
agency: 'ユーザー管理 / 管理者は global サーバーも追加可能',
|
||||
},
|
||||
{
|
||||
id: 'skills',
|
||||
icon: '📚',
|
||||
title: 'skills/',
|
||||
desc: 'エージェントのスキル (参照知識・手順書) を管理します。URL からインストール、手動作成、編集、削除が可能。スキルはタスク実行時にエージェントへコンテキストとして注入されます。',
|
||||
agency: 'ユーザー管理 / エージェントが ReadSkill で参照',
|
||||
},
|
||||
{
|
||||
id: 'ssh-connections',
|
||||
icon: '🔐',
|
||||
title: 'ssh-connections/',
|
||||
desc: 'エージェントの SshExec / SshUpload / SshDownload ツールが利用する SSH 接続を管理します。秘密鍵は envelope encryption (AES-256-GCM + per-user DEK) で保存され、ホストキーは TOFU でユーザー確認後に固定されます。グローバル接続は管理者が登録し、ピースごとに grant を付与した時のみ参照可能です。',
|
||||
agency: 'ユーザー管理 / グローバル接続は管理者が登録',
|
||||
},
|
||||
{
|
||||
id: 'notes',
|
||||
icon: '📝',
|
||||
title: 'notes/',
|
||||
desc: '他のエージェントや他のユーザーと共有したい情報を Markdown で書く場所です。visibility を設定して公開範囲を制御できます。SearchNotes / ReadNote / WriteNote ツールでエージェントがアクセスできます。',
|
||||
agency: 'ユーザー作成 / エージェントが SearchNotes / ReadNote / WriteNote で利用',
|
||||
},
|
||||
{
|
||||
id: 'subscribed-notes',
|
||||
icon: '🔔',
|
||||
title: 'Subscribed Notes',
|
||||
desc: '他のユーザーが公開している notes フォルダーを購読・発見します。search モードは SearchNotes ツールで横断検索でき、inject モードは LLM コンテキストに自動注入します。',
|
||||
agency: 'ユーザー管理 / エージェントが自動参照 (inject モード)',
|
||||
},
|
||||
// title/desc/agency are i18n keys under root.subdirs.<key>, translated at render.
|
||||
const SUBDIR_INFO: { id: SubdirId; icon: string; key: string }[] = [
|
||||
{ id: 'agents-md', icon: '📖', key: 'agentsMd' },
|
||||
{ id: 'browser-macros', icon: '🤖', key: 'browserMacros' },
|
||||
{ id: 'recordings', icon: '🎬', key: 'recordings' },
|
||||
{ id: 'pets', icon: '◉', key: 'pets' },
|
||||
{ id: 'browser-sessions', icon: '🌐', key: 'browserSessions' },
|
||||
{ id: 'trash', icon: '🗑', key: 'trash' },
|
||||
{ id: 'memory', icon: '🧠', key: 'memory' },
|
||||
{ id: 'mcp', icon: '🔌', key: 'mcp' },
|
||||
{ id: 'skills', icon: '📚', key: 'skills' },
|
||||
{ id: 'ssh-connections', icon: '🔐', key: 'sshConnections' },
|
||||
{ id: 'notes', icon: '📝', key: 'notes' },
|
||||
{ id: 'subscribed-notes', icon: '🔔', key: 'subscribedNotes' },
|
||||
];
|
||||
|
||||
interface FolderListResponse {
|
||||
@@ -182,6 +112,7 @@ interface UserFolderTabProps {
|
||||
}
|
||||
|
||||
export function UserFolderTab({ showToast }: UserFolderTabProps = {}) {
|
||||
const { t } = useTranslation('userfolder');
|
||||
const [selectedSubdir, setSelectedSubdir] = useState<SubdirId | null>('browser-macros');
|
||||
const [selectedFile, setSelectedFile] = useState<string | null>(null);
|
||||
const [editorDirty, setEditorDirty] = useState(false);
|
||||
@@ -264,7 +195,7 @@ export function UserFolderTab({ showToast }: UserFolderTabProps = {}) {
|
||||
},
|
||||
onError: (err, { subdir, file }) => {
|
||||
const msg = err instanceof Error ? err.message : 'Unknown error';
|
||||
const label = `${subdir}/${file} の削除に失敗`;
|
||||
const label = t('delete.failed', { path: `${subdir}/${file}` });
|
||||
if (showToast) showToast(`${label}: ${msg}`, 'error');
|
||||
else console.error(`${label}: ${msg}`);
|
||||
},
|
||||
@@ -456,14 +387,14 @@ export function UserFolderTab({ showToast }: UserFolderTabProps = {}) {
|
||||
{info.icon}
|
||||
</span>
|
||||
<div className="flex-1 min-w-0">
|
||||
<h2 className="text-base font-semibold text-slate-900">{info.title}</h2>
|
||||
<p className="text-[13px] text-slate-500 mt-1 leading-relaxed">{info.desc}</p>
|
||||
<p className="text-2xs text-slate-400 mt-1 uppercase tracking-wide">{info.agency}</p>
|
||||
<h2 className="text-base font-semibold text-slate-900">{t(`root.subdirs.${info.key}.title`)}</h2>
|
||||
<p className="text-[13px] text-slate-500 mt-1 leading-relaxed">{t(`root.subdirs.${info.key}.desc`)}</p>
|
||||
<p className="text-2xs text-slate-400 mt-1 uppercase tracking-wide">{t(`root.subdirs.${info.key}.agency`)}</p>
|
||||
</div>
|
||||
</div>
|
||||
{files.length > 0 && (
|
||||
<div className="mb-4 text-xs text-slate-500">
|
||||
現在 {files.length} 件のファイルがあります。左のツリーから選択して編集できます。
|
||||
{t('root.fileCount', { count: files.length })}
|
||||
</div>
|
||||
)}
|
||||
<NewFileForm
|
||||
@@ -482,22 +413,21 @@ export function UserFolderTab({ showToast }: UserFolderTabProps = {}) {
|
||||
/* Full overview when no subdir is selected (or non-writable subdir selected without a file) */
|
||||
<>
|
||||
<div className="mb-6">
|
||||
<h2 className="text-base font-semibold text-slate-900 mb-1">User Folder</h2>
|
||||
<h2 className="text-base font-semibold text-slate-900 mb-1">{t('root.title')}</h2>
|
||||
<p className="text-[13px] text-slate-500 leading-relaxed">
|
||||
セッションをまたいで永続化される、ユーザーごとの作業空間です。
|
||||
左のツリーから subdirectory を開き、ファイルを選択して編集できます。
|
||||
{t('root.intro')}
|
||||
</p>
|
||||
</div>
|
||||
<ul className="space-y-5">
|
||||
{SUBDIR_INFO.map(({ id, icon, title, desc, agency }) => (
|
||||
{SUBDIR_INFO.map(({ id, icon, key }) => (
|
||||
<li key={id} className="flex gap-3">
|
||||
<span className="text-xl leading-none mt-0.5 select-none" aria-hidden>
|
||||
{icon}
|
||||
</span>
|
||||
<div className="flex-1 min-w-0">
|
||||
<div className="text-[13px] font-semibold text-slate-900">{title}</div>
|
||||
<p className="text-[13px] text-slate-600 mt-1 leading-relaxed">{desc}</p>
|
||||
<p className="text-2xs text-slate-400 mt-1 uppercase tracking-wide">{agency}</p>
|
||||
<div className="text-[13px] font-semibold text-slate-900">{t(`root.subdirs.${key}.title`)}</div>
|
||||
<p className="text-[13px] text-slate-600 mt-1 leading-relaxed">{t(`root.subdirs.${key}.desc`)}</p>
|
||||
<p className="text-2xs text-slate-400 mt-1 uppercase tracking-wide">{t(`root.subdirs.${key}.agency`)}</p>
|
||||
</div>
|
||||
</li>
|
||||
))}
|
||||
|
||||
Reference in New Issue
Block a user