added support for todos
This commit is contained in:
124
chat/server.js
124
chat/server.js
@@ -105,6 +105,10 @@ const WORKSPACES_ROOT = path.join(DATA_ROOT, 'apps');
|
||||
const STATIC_ROOT = path.join(__dirname, 'public');
|
||||
const UPLOADS_DIR = path.join(STATE_DIR, 'uploads');
|
||||
const REPO_ROOT = process.env.CHAT_REPO_ROOT || process.cwd();
|
||||
const OPENCODE_REPO_ROOT = path.join(REPO_ROOT, 'opencode');
|
||||
const OPENCODE_REPO_CLI = path.join(OPENCODE_REPO_ROOT, 'packages', 'opencode', 'bin', 'opencode');
|
||||
const OPENCODE_PROMPT_DIR = path.join(OPENCODE_REPO_ROOT, 'packages', 'opencode', 'src', 'session', 'prompt');
|
||||
const OPENCODE_REQUIRE_REPO = process.env.OPENCODE_REQUIRE_REPO !== 'false';
|
||||
const DEFAULT_OPENROUTER_API_URL = 'https://openrouter.ai/api/v1/chat/completions';
|
||||
const OPENROUTER_API_URL = process.env.OPENROUTER_API_URL || DEFAULT_OPENROUTER_API_URL;
|
||||
const OPENROUTER_API_KEY = process.env.OPENROUTER_API_KEY || process.env.OPENROUTER_API_TOKEN || '';
|
||||
@@ -5279,6 +5283,7 @@ function resolveCliCommand(cli) {
|
||||
const normalized = normalizeCli(cli);
|
||||
const binDir = process.env.OPENCODE_BIN_DIR || '/root/.opencode/bin';
|
||||
const candidates = [];
|
||||
if (OPENCODE_REPO_CLI) candidates.push(OPENCODE_REPO_CLI);
|
||||
if (binDir) candidates.push(path.join(binDir, normalized));
|
||||
candidates.push(`/usr/local/bin/${normalized}`);
|
||||
candidates.push(normalized);
|
||||
@@ -5292,6 +5297,85 @@ function resolveCliCommand(cli) {
|
||||
return normalized;
|
||||
}
|
||||
|
||||
const opencodeVerificationCache = {
|
||||
checked: false,
|
||||
ok: false,
|
||||
error: null,
|
||||
cliPath: null,
|
||||
};
|
||||
|
||||
function verifyOpencodePrompts() {
|
||||
const promptFiles = [
|
||||
'codex_header.txt',
|
||||
'beast.txt',
|
||||
'gemini.txt',
|
||||
'anthropic.txt',
|
||||
'qwen.txt',
|
||||
'trinity.txt',
|
||||
'wordpress-plugin.txt',
|
||||
'wordpress-plugin-subsequent.txt'
|
||||
];
|
||||
const missing = [];
|
||||
const nonWordPress = [];
|
||||
|
||||
for (const file of promptFiles) {
|
||||
const fullPath = path.join(OPENCODE_PROMPT_DIR, file);
|
||||
if (!fsSync.existsSync(fullPath)) {
|
||||
missing.push(fullPath);
|
||||
continue;
|
||||
}
|
||||
const content = fsSync.readFileSync(fullPath, 'utf8');
|
||||
if (!/wordpress/i.test(content)) {
|
||||
nonWordPress.push(fullPath);
|
||||
}
|
||||
}
|
||||
|
||||
if (missing.length) {
|
||||
throw new Error(`OpenCode prompt verification failed: missing prompt files: ${missing.join(', ')}`);
|
||||
}
|
||||
if (nonWordPress.length) {
|
||||
throw new Error(`OpenCode prompt verification failed: prompts are not WordPress-specific: ${nonWordPress.join(', ')}`);
|
||||
}
|
||||
}
|
||||
|
||||
function verifyOpencodeCli(cliCommand) {
|
||||
if (!OPENCODE_REQUIRE_REPO) return;
|
||||
const repoRootResolved = fsSync.existsSync(OPENCODE_REPO_ROOT)
|
||||
? fsSync.realpathSync(OPENCODE_REPO_ROOT)
|
||||
: null;
|
||||
if (!repoRootResolved) {
|
||||
throw new Error('OpenCode repo root not found; cannot verify CLI build source.');
|
||||
}
|
||||
let cliResolved = null;
|
||||
try {
|
||||
cliResolved = fsSync.realpathSync(cliCommand);
|
||||
} catch (_) {
|
||||
cliResolved = null;
|
||||
}
|
||||
if (!cliResolved || !cliResolved.startsWith(repoRootResolved)) {
|
||||
throw new Error(`OpenCode CLI is not using the repo build. Expected CLI under ${repoRootResolved} but got ${cliCommand}.`);
|
||||
}
|
||||
}
|
||||
|
||||
function verifyOpencodeSetup(cliCommand) {
|
||||
if (opencodeVerificationCache.checked) {
|
||||
if (opencodeVerificationCache.error) throw opencodeVerificationCache.error;
|
||||
return;
|
||||
}
|
||||
try {
|
||||
verifyOpencodeCli(cliCommand);
|
||||
verifyOpencodePrompts();
|
||||
opencodeVerificationCache.checked = true;
|
||||
opencodeVerificationCache.ok = true;
|
||||
opencodeVerificationCache.cliPath = cliCommand;
|
||||
} catch (err) {
|
||||
opencodeVerificationCache.checked = true;
|
||||
opencodeVerificationCache.ok = false;
|
||||
opencodeVerificationCache.error = err;
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
async function ensureStateFile() {
|
||||
try {
|
||||
await fs.mkdir(STATE_DIR, { recursive: true });
|
||||
@@ -8898,6 +8982,7 @@ async function sendToOpencode({ session, model, content, message, cli, streamCal
|
||||
const workspaceDir = session.workspaceDir;
|
||||
const cliName = normalizeCli(cli || session?.cli);
|
||||
const cliCommand = resolveCliCommand(cliName);
|
||||
verifyOpencodeSetup(cliCommand);
|
||||
|
||||
// Ensure model is properly resolved
|
||||
const resolvedModel = model || session.model;
|
||||
@@ -9117,6 +9202,36 @@ async function sendToOpencode({ session, model, content, message, cli, streamCal
|
||||
}
|
||||
});
|
||||
}
|
||||
persistState().catch(() => { });
|
||||
}
|
||||
}
|
||||
|
||||
if (event.type === 'todo.updated' && event.properties?.todos) {
|
||||
const todos = event.properties.todos;
|
||||
if (message) {
|
||||
message.todos = todos;
|
||||
log('Captured todos from todo.updated event', {
|
||||
messageId: messageKey,
|
||||
todoCount: todos.length,
|
||||
todos: todos.map(t => ({ id: t.id, content: t.content?.substring(0, 50), status: t.status }))
|
||||
});
|
||||
|
||||
if (messageKey && activeStreams.has(messageKey)) {
|
||||
const streams = activeStreams.get(messageKey);
|
||||
const data = JSON.stringify({
|
||||
type: 'todos',
|
||||
todos: todos,
|
||||
timestamp: new Date().toISOString()
|
||||
});
|
||||
streams.forEach(res => {
|
||||
try {
|
||||
res.write(`data: ${data}\n\n`);
|
||||
} catch (err) {
|
||||
log('SSE todos write error', { err: String(err) });
|
||||
}
|
||||
});
|
||||
}
|
||||
persistState().catch(() => { });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9213,6 +9328,9 @@ async function sendToOpencode({ session, model, content, message, cli, streamCal
|
||||
if (session) {
|
||||
session.updatedAt = message.finishedAt;
|
||||
}
|
||||
if (message.todos && Array.isArray(message.todos) && message.todos.length) {
|
||||
message.todos = [];
|
||||
}
|
||||
}
|
||||
|
||||
// The reply is the final accumulated output
|
||||
@@ -10590,6 +10708,9 @@ async function processMessage(sessionId, message) {
|
||||
|
||||
message.status = 'done';
|
||||
message.reply = reply;
|
||||
if (message.todos && Array.isArray(message.todos) && message.todos.length) {
|
||||
message.todos = [];
|
||||
}
|
||||
message.finishedAt = new Date().toISOString();
|
||||
session.updatedAt = message.finishedAt;
|
||||
session.cli = activeCli;
|
||||
@@ -10599,6 +10720,9 @@ async function processMessage(sessionId, message) {
|
||||
} catch (error) {
|
||||
// Provide helpful and parseable error details in the message
|
||||
message.status = 'error';
|
||||
if (message.todos && Array.isArray(message.todos) && message.todos.length) {
|
||||
message.todos = [];
|
||||
}
|
||||
const details = [];
|
||||
if (error.code) details.push(`code: ${error.code}`);
|
||||
if (error.stderr) details.push(`stderr: ${error.stderr.trim()}`);
|
||||
|
||||
Reference in New Issue
Block a user