codexmobile-relay / server /mobile-session-index.js
Codex
deploy: CodexMobile Relay
90f0300
Raw
History Blame Contribute Delete
4.52 kB
import fs from 'node:fs/promises';
import path from 'node:path';
import { CODEXMOBILE_STATE_DIR, statePath } from './runtime-paths.js';
const STATE_DIR = CODEXMOBILE_STATE_DIR;
const INDEX_PATH = statePath('mobile-sessions.json');
const MAX_SESSIONS = 300;
async function readIndexFile() {
try {
const raw = await fs.readFile(INDEX_PATH, 'utf8');
const parsed = JSON.parse(raw);
return Array.isArray(parsed.sessions) ? parsed.sessions : [];
} catch (error) {
if (error.code !== 'ENOENT') {
console.warn('[mobile-sessions] Failed to read index:', error.message);
}
return [];
}
}
async function writeIndexFile(sessions) {
await fs.mkdir(STATE_DIR, { recursive: true });
const trimmed = sessions
.filter((session) => session?.id && session?.projectPath)
.sort((a, b) => new Date(b.updatedAt || 0) - new Date(a.updatedAt || 0))
.slice(0, MAX_SESSIONS);
await fs.writeFile(
INDEX_PATH,
JSON.stringify({ version: 1, sessions: trimmed }, null, 2),
'utf8'
);
}
function fallbackTitle(title, summary) {
const value = String(title || summary || '').trim();
return value ? value.slice(0, 52) : '新对话';
}
export async function readMobileSessionIndex() {
const sessions = await readIndexFile();
return new Map(
sessions
.filter((session) => session?.id)
.map((session) => [session.id, session])
);
}
export async function readMobileSessions() {
return readIndexFile();
}
export async function readMobileSessionMessages(sessionId) {
const sessions = await readIndexFile();
const session = sessions.find((item) => item.id === sessionId);
return Array.isArray(session?.messages) ? session.messages : [];
}
export async function deleteMobileSession(sessionId) {
const id = String(sessionId || '').trim();
if (!id) {
return false;
}
const sessions = await readIndexFile();
const next = sessions.filter((session) => session.id !== id);
if (next.length === sessions.length) {
return false;
}
await writeIndexFile(next);
return true;
}
export async function registerMobileSession({ id, projectPath, title, summary, updatedAt, messages }) {
if (!id || !projectPath || String(id).startsWith('draft-') || String(id).startsWith('codex-')) {
return null;
}
const now = updatedAt || new Date().toISOString();
const sessions = await readIndexFile();
const existingIndex = sessions.findIndex((session) => session.id === id);
const existing = existingIndex >= 0 ? sessions[existingIndex] : null;
const next = {
...(existing || {}),
id,
projectPath: path.resolve(projectPath),
title: existing?.title || fallbackTitle(title, summary),
titleLocked: true,
summary: summary || title || existing?.summary || 'CodexMobile 对话',
updatedAt: now,
source: existing?.source || 'codexmobile'
};
if (Array.isArray(messages)) {
const seenMessageIds = new Set();
next.messages = messages
.filter((message) => message && message.role && typeof message.content === 'string')
.map((message) => ({
id: String(message.id || `${id}-${Date.now()}`),
role: message.role === 'assistant' ? 'assistant' : 'user',
content: message.content,
timestamp: message.timestamp || now
}))
.filter((message) => {
if (seenMessageIds.has(message.id)) {
return false;
}
seenMessageIds.add(message.id);
return true;
});
}
if (existingIndex >= 0) {
sessions[existingIndex] = next;
} else {
sessions.push(next);
}
await writeIndexFile(sessions);
return next;
}
export async function renameMobileSession({ id, projectPath, title, updatedAt }) {
const sessionId = String(id || '').trim();
if (!sessionId || !projectPath || sessionId.startsWith('draft-')) {
return null;
}
const sessions = await readIndexFile();
const existingIndex = sessions.findIndex((session) => session.id === sessionId);
const existing = existingIndex >= 0 ? sessions[existingIndex] : null;
const next = {
...(existing || {}),
id: sessionId,
projectPath: path.resolve(projectPath),
title: fallbackTitle(title),
titleLocked: true,
summary: existing?.summary,
updatedAt: existing?.updatedAt || updatedAt || new Date().toISOString(),
source: existing?.source || 'codexmobile'
};
if (existingIndex >= 0) {
sessions[existingIndex] = next;
} else {
sessions.push(next);
}
await writeIndexFile(sessions);
return next;
}