Add accountant/investor skills, Excel editor, email attachment fixes
Skills:
- Add accountant (CPA) and investor (CFA) skills with site: hints
- Add site: hints to lawyer, psychiatrist, musician skills
- Allow multiple skills active simultaneously (remove exclusive mode)
Tools:
- Add excel_read / excel_write tools (openpyxl-based)
- Fix python_eval packages: retry with --break-system-packages on PEP 668 failure
- Fix email_read: spaces→underscores in filenames, return /api/files/ links,
remove absolute savedPaths from data, send SSE 'files' event for attachments
Server:
- FILE_OP v2: fall through to primary when secondary model unavailable
- Add PUT /api/files/{*filePath} endpoint for xlsx editor save
UI:
- xlsx viewer: Luckysheet modal (lazy CDN load, ~3MB on first use)
with edit mode, 💾 save button, Nanum Gothic font
- Email attachment preview: SSE 'files' event appends file links to final reply
- ✏️ 편집 button (blue) for xlsx cards
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+7
-5
@@ -364,7 +364,7 @@ export const emailReadTool = {
|
||||
fs.mkdirSync(saveDir, { recursive: true });
|
||||
for (const att of parsed.attachments) {
|
||||
const filename = att.filename || `attachment-${savedPaths.length + 1}`;
|
||||
const safeName = filename.replace(/[/\\:*?"<>|]/g, '_');
|
||||
const safeName = filename.replace(/[/\\:*?"<>|]/g, '_').replace(/\s+/g, '_');
|
||||
const dest = path.join(saveDir, safeName);
|
||||
fs.writeFileSync(dest, att.content);
|
||||
savedPaths.push(dest);
|
||||
@@ -410,13 +410,14 @@ export const emailReadTool = {
|
||||
`날짜: ${r.date}`,
|
||||
r.attachments.length ? `첨부파일: ${r.attachments.join(', ')}` : '',
|
||||
r.savedRelPaths?.length
|
||||
? `✅ 첨부파일 저장 완료 — pdf_read/read 등에서 아래 경로를 그대로 사용하세요:\n${r.savedRelPaths.map((p: string) => ` "${p}"`).join('\n')}`
|
||||
? `✅ 첨부파일 저장 완료 — pdf_read 등에서 아래 상대경로를 그대로 사용하세요:\n${r.savedRelPaths.map((p: string) => ` - [${path.basename(p)}](/api/files/${p.replace(/\\/g, '/')})`).join('\n')}`
|
||||
: '',
|
||||
'',
|
||||
'─── 본문 ───',
|
||||
r.body,
|
||||
].filter((l: string) => l !== '');
|
||||
return { success: true, stdout: lines.join('\n'), data: { ...r, account: found.account } };
|
||||
const { savedPaths: _sp, ...safeData } = { ...r, account: found.account };
|
||||
return { success: true, stdout: lines.join('\n'), data: safeData };
|
||||
}
|
||||
|
||||
// No account specified — try all accounts
|
||||
@@ -438,13 +439,14 @@ export const emailReadTool = {
|
||||
`날짜: ${r.date}`,
|
||||
r.attachments.length ? `첨부파일: ${r.attachments.join(', ')}` : '',
|
||||
r.savedRelPaths?.length
|
||||
? `✅ 첨부파일 저장 완료 — pdf_read/read 등에서 아래 경로를 그대로 사용하세요:\n${r.savedRelPaths.map((p: string) => ` "${p}"`).join('\n')}`
|
||||
? `✅ 첨부파일 저장 완료 — pdf_read 등에서 아래 상대경로를 그대로 사용하세요:\n${r.savedRelPaths.map((p: string) => ` - [${path.basename(p)}](/api/files/${p.replace(/\\/g, '/')})`).join('\n')}`
|
||||
: '',
|
||||
'',
|
||||
'─── 본문 ───',
|
||||
r.body,
|
||||
].filter((l: string) => l !== '');
|
||||
return { success: true, stdout: lines.join('\n'), data: { ...r, account: found.account } };
|
||||
const { savedPaths: _sp, ...safeData } = { ...r, account: found.account };
|
||||
return { success: true, stdout: lines.join('\n'), data: safeData };
|
||||
}
|
||||
errors.push(acct.id);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user