|
|
@@ -32,14 +32,7 @@ import { codexTextKey } from '../utils/codexVisualRegistry';
|
|
|
|
|
|
const SESSIONS_KEY = 'codex.sessions.v1';
|
|
|
const ACTIVE_KEY = 'codex.active-thread.v1';
|
|
|
-const CWD_KEY = 'codex.workspace-dir.v1';
|
|
|
-const CWD_OVERRIDE_KEY = 'codex.workspace-dir.v2';
|
|
|
const PROMPTS_KEY = 'codex.prompts.v1';
|
|
|
-const RECENT_CWD_KEY = 'codex.recent-cwds.v1';
|
|
|
-/** 最近工作目录保留条数 */
|
|
|
-const MAX_RECENT_CWDS = 10;
|
|
|
-/** 无案件时覆盖项的键 */
|
|
|
-const NO_CASE_KEY = 'no-case';
|
|
|
|
|
|
/** 单个会话缓存的事件上限 */
|
|
|
const MAX_EVENTS_PER_THREAD = 2000;
|
|
|
@@ -143,75 +136,6 @@ function writeSessions(sessions: CodexSessionMeta[]) {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-/** 目录去重用的规范化键:Windows 下大小写不敏感 */
|
|
|
-function cwdKey(value: string): string {
|
|
|
- return /^[A-Za-z]:/.test(value) ? value.toLowerCase() : value;
|
|
|
-}
|
|
|
-
|
|
|
-function readRecentCwds(): string[] {
|
|
|
- try {
|
|
|
- const raw = window.localStorage.getItem(RECENT_CWD_KEY);
|
|
|
- if (!raw) return [];
|
|
|
- const parsed = JSON.parse(raw);
|
|
|
- if (!Array.isArray(parsed)) return [];
|
|
|
- const seen = new Set<string>();
|
|
|
- const result: string[] = [];
|
|
|
- for (const item of parsed) {
|
|
|
- if (typeof item !== 'string' || !item.trim()) continue;
|
|
|
- const key = cwdKey(item);
|
|
|
- if (seen.has(key)) continue;
|
|
|
- seen.add(key);
|
|
|
- result.push(item);
|
|
|
- }
|
|
|
- return result.slice(0, MAX_RECENT_CWDS);
|
|
|
- } catch {
|
|
|
- return [];
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-function writeRecentCwds(list: string[]) {
|
|
|
- try {
|
|
|
- window.localStorage.setItem(RECENT_CWD_KEY, JSON.stringify(list.slice(0, MAX_RECENT_CWDS)));
|
|
|
- } catch {
|
|
|
- // 只影响持久化
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-/**
|
|
|
- * 用户显式指定的工作目录,**按案件分别记忆**。
|
|
|
- *
|
|
|
- * 为什么不存一个全局值:进案件后工作目录应优先跟着案件工作空间走,
|
|
|
- * 若把用户的手选目录存成全局值,下一个案件就会被上一个案件的目录污染。
|
|
|
- */
|
|
|
-function readCwdOverrides(): Record<string, string> {
|
|
|
- try {
|
|
|
- const raw = window.localStorage.getItem(CWD_OVERRIDE_KEY);
|
|
|
- if (raw) {
|
|
|
- const parsed = JSON.parse(raw);
|
|
|
- if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
|
|
|
- const result: Record<string, string> = {};
|
|
|
- for (const [key, value] of Object.entries(parsed as Record<string, unknown>)) {
|
|
|
- if (typeof value === 'string' && value.trim()) result[key] = value;
|
|
|
- }
|
|
|
- return result;
|
|
|
- }
|
|
|
- }
|
|
|
- // 迁移旧版全局值:挂到「无案件」下,避免升级后丢用户选过的目录
|
|
|
- const legacy = window.localStorage.getItem(CWD_KEY);
|
|
|
- return legacy ? { [NO_CASE_KEY]: legacy } : {};
|
|
|
- } catch {
|
|
|
- return {};
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-function writeCwdOverrides(map: Record<string, string>) {
|
|
|
- try {
|
|
|
- window.localStorage.setItem(CWD_OVERRIDE_KEY, JSON.stringify(map));
|
|
|
- } catch {
|
|
|
- /* 只影响持久化 */
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
/** 每个会话最多留存的提问条数 */
|
|
|
const MAX_PROMPTS_PER_THREAD = 200;
|
|
|
/** 单条提问落盘上限 */
|
|
|
@@ -274,14 +198,8 @@ export const useCodexChatStore = defineStore('codex-chat', {
|
|
|
diagnostics: [] as CodexDiagnosticItem[],
|
|
|
/** 主进程默认工作目录(用户没选目录时用) */
|
|
|
defaultCwd: '',
|
|
|
- /** 当前案件 ID(用于按案件区分工作目录覆盖项) */
|
|
|
- caseId: '',
|
|
|
/** 当前案件的工作空间(后端 /case/open 回传,见 plat/api/case/caseApi.ts 的 workspacePath) */
|
|
|
caseWorkspace: '',
|
|
|
- /** 用户显式指定的工作目录,按案件 key 记 */
|
|
|
- cwdOverrides: {} as Record<string, string>,
|
|
|
- /** 最近用过的工作目录(最新在前) */
|
|
|
- recentCwds: [] as string[],
|
|
|
/** 新会话继承的默认运行参数(沙箱 / 审批策略) */
|
|
|
runDefaults: { ...DEFAULT_RUN_OPTIONS } as CodexRunOptions,
|
|
|
/** 看板:钉住的块(只存内容哈希) */
|
|
|
@@ -330,32 +248,24 @@ export const useCodexChatStore = defineStore('codex-chat', {
|
|
|
return state.runningThreads.length;
|
|
|
},
|
|
|
/**
|
|
|
- * 当前会话的工作目录。
|
|
|
+ * 当前会话的工作目录(**由代码决定,界面不可改**)。
|
|
|
*
|
|
|
* 优先级:**会话自身 cwd(Codex 在 threadStart 时已固定,不可改)**
|
|
|
- * → 本案件的显式覆盖(用户手选)
|
|
|
- * → 案件工作空间(后端回传,进案件后默认走它)
|
|
|
+ * → 案件工作空间(后端回传,进案件后就是它)
|
|
|
* → 客户端默认目录
|
|
|
*/
|
|
|
activeCwd(state): string {
|
|
|
// getter 里只能读 state:activeSession 本身也是 getter,不能相互引用
|
|
|
const session = state.sessions.find((item) => item.threadId === state.activeThreadId);
|
|
|
if (session?.cwd) return session.cwd;
|
|
|
- const override = state.cwdOverrides[state.caseId || NO_CASE_KEY];
|
|
|
- if (override) return override;
|
|
|
return state.caseWorkspace || state.defaultCwd;
|
|
|
},
|
|
|
- /** 当前工作目录的来源(UI 用来说明「为什么会用这个目录」) */
|
|
|
- activeCwdSource(state): 'session' | 'override' | 'case' | 'default' {
|
|
|
+ /** 当前工作目录的来源(UI 只读展示,让用户知道「为什么会用这个目录」) */
|
|
|
+ activeCwdSource(state): 'session' | 'case' | 'default' {
|
|
|
const session = state.sessions.find((item) => item.threadId === state.activeThreadId);
|
|
|
if (session?.cwd) return 'session';
|
|
|
- if (state.cwdOverrides[state.caseId || NO_CASE_KEY]) return 'override';
|
|
|
return state.caseWorkspace ? 'case' : 'default';
|
|
|
},
|
|
|
- /** 当前案件下用户显式指定的工作目录(空串 = 没指定) */
|
|
|
- workspaceOverride(state): string {
|
|
|
- return state.cwdOverrides[state.caseId || NO_CASE_KEY] || '';
|
|
|
- },
|
|
|
/** 最新一轮回合的文件变更(产物栏「本轮变更」用) */
|
|
|
artifactChanges(state): CodexArtifactChange[] {
|
|
|
const threadId = state.activeThreadId;
|
|
|
@@ -407,8 +317,6 @@ export const useCodexChatStore = defineStore('codex-chat', {
|
|
|
|
|
|
this.sessions = readSessions();
|
|
|
this.prompts = readPrompts();
|
|
|
- this.recentCwds = readRecentCwds();
|
|
|
- this.cwdOverrides = readCwdOverrides();
|
|
|
this.runDefaults = readRunDefaults();
|
|
|
this.board = readBoard();
|
|
|
this.syncCaseContext();
|
|
|
@@ -475,7 +383,7 @@ export const useCodexChatStore = defineStore('codex-chat', {
|
|
|
},
|
|
|
|
|
|
/**
|
|
|
- * 从案件缓存同步「当前案件 + 案件工作空间」。
|
|
|
+ * 从案件缓存同步案件工作空间。
|
|
|
*
|
|
|
* 后端在 `/case/open`、`/case/current`、`/case/list` 返回里带 `workspacePath`
|
|
|
* (见 ai-server `CaseInfoService.fillWorkspacePath`),前端由 `useUserStore` 缓存。
|
|
|
@@ -483,49 +391,10 @@ export const useCodexChatStore = defineStore('codex-chat', {
|
|
|
*/
|
|
|
syncCaseContext() {
|
|
|
const caseInfo = useUserStore().getCaseInfo || {};
|
|
|
- const id = caseInfo?.id ? String(caseInfo.id) : '';
|
|
|
const workspace = String(caseInfo?.workspacePath || '').trim();
|
|
|
- if (id !== this.caseId) this.caseId = id;
|
|
|
if (workspace !== this.caseWorkspace) this.caseWorkspace = workspace;
|
|
|
},
|
|
|
|
|
|
- /** 该案件是否已有用户显式指定的目录 */
|
|
|
- hasWorkspaceOverride(): boolean {
|
|
|
- return !!this.cwdOverrides[this.caseId || NO_CASE_KEY];
|
|
|
- },
|
|
|
-
|
|
|
- /**
|
|
|
- * 显式指定工作目录(只影响**新建**的会话)。
|
|
|
- *
|
|
|
- * 按当前案件记忆;传空串表示清除覆盖,回落到案件工作空间 / 客户端默认目录。
|
|
|
- */
|
|
|
- setWorkspaceDir(cwd: string) {
|
|
|
- const key = this.caseId || NO_CASE_KEY;
|
|
|
- const value = String(cwd ?? '').trim();
|
|
|
- const next = { ...this.cwdOverrides };
|
|
|
- if (value) next[key] = value;
|
|
|
- else delete next[key];
|
|
|
- this.cwdOverrides = next;
|
|
|
- writeCwdOverrides(next);
|
|
|
- if (value) this.rememberCwd(value);
|
|
|
- },
|
|
|
-
|
|
|
- /** 记进「最近目录」(去重、最新在前、上限 10) */
|
|
|
- rememberCwd(cwd: string) {
|
|
|
- const value = String(cwd ?? '').trim();
|
|
|
- if (!value) return;
|
|
|
- const key = cwdKey(value);
|
|
|
- const rest = this.recentCwds.filter((item) => cwdKey(item) !== key);
|
|
|
- this.recentCwds = [value, ...rest].slice(0, MAX_RECENT_CWDS);
|
|
|
- writeRecentCwds(this.recentCwds);
|
|
|
- },
|
|
|
-
|
|
|
- removeRecentCwd(cwd: string) {
|
|
|
- const key = cwdKey(String(cwd ?? ''));
|
|
|
- this.recentCwds = this.recentCwds.filter((item) => cwdKey(item) !== key);
|
|
|
- writeRecentCwds(this.recentCwds);
|
|
|
- },
|
|
|
-
|
|
|
/** 该会话是否正在跑回合(多会话并行下用于按钮态与侧栏标识) */
|
|
|
isThreadRunning(threadId: string): boolean {
|
|
|
return !!threadId && this.runningThreads.includes(threadId);
|
|
|
@@ -749,7 +618,6 @@ export const useCodexChatStore = defineStore('codex-chat', {
|
|
|
this.activeThreadId = meta.threadId;
|
|
|
this.events[meta.threadId] = [];
|
|
|
this.prompts[meta.threadId] = [];
|
|
|
- this.rememberCwd(meta.cwd);
|
|
|
this.persistSessions();
|
|
|
await this.reloadArtifacts();
|
|
|
return meta;
|
|
|
@@ -911,8 +779,6 @@ export const useCodexChatStore = defineStore('codex-chat', {
|
|
|
resolveCwd(): string {
|
|
|
const session = this.sessions.find((item) => item.threadId === this.activeThreadId);
|
|
|
if (session?.cwd) return session.cwd;
|
|
|
- const override = this.cwdOverrides[this.caseId || NO_CASE_KEY];
|
|
|
- if (override) return override;
|
|
|
return this.caseWorkspace || this.defaultCwd;
|
|
|
},
|
|
|
|