diff --git a/app/api/companies/[ws]/files/route.js b/app/api/companies/[ws]/files/route.js
index 4a7545e..dd2b081 100644
--- a/app/api/companies/[ws]/files/route.js
+++ b/app/api/companies/[ws]/files/route.js
@@ -26,10 +26,19 @@ export async function GET(req, { params }) {
const denied = await guardCompany(ws); if (denied) return denied;
const rel = new URL(req.url).searchParams.get('rel') ?? '';
// Windows normalize()는 백슬래시를 반환 — 슬래시로 통일해야 files/ 접두 검사가 통과한다
- const norm = normalize(rel).split('\\').join('/');
- // _imported/ = 옵시디언 임포트 리포트·미분류 보관 — 경로만 만들어 두고 서빙 허용 목록에서 빠지면
- // 사용자가 Finder로 찾아가야 한다(2026-07-20 신고와 같은 결함 계급 — 분리 검수 MED-5).
- if (!(norm.startsWith('files/') || norm.startsWith('projects/') || norm.startsWith('_imported/')) || norm.includes('..')) {
+ let norm = normalize(rel).split('\\').join('/').replace(/^\/+/, '');
+ if (norm.includes('..')) {
+ return new Response('잘못된 경로', { status: 400 });
+ }
+ const vault = paths(ws).vault;
+ // projects/ 또는 files/ 접두사가 누락된 경우 projects/ 접두사 붙여서 존재 여부 시도
+ if (!(norm.startsWith('files/') || norm.startsWith('projects/') || norm.startsWith('_imported/'))) {
+ try {
+ await realpath(join(vault, `projects/${norm}`));
+ norm = `projects/${norm}`;
+ } catch {}
+ }
+ if (!(norm.startsWith('files/') || norm.startsWith('projects/') || norm.startsWith('_imported/'))) {
return new Response('잘못된 경로', { status: 400 });
}
try {
diff --git a/app/c/[ws]/company-shell-context.jsx b/app/c/[ws]/company-shell-context.jsx
new file mode 100644
index 0000000..dec9d82
--- /dev/null
+++ b/app/c/[ws]/company-shell-context.jsx
@@ -0,0 +1,13 @@
+'use client';
+
+import { createContext, useContext } from 'react';
+
+const CompanyShellContext = createContext(null);
+
+export function CompanyShellProvider({ value, children }) {
+ return
{output || (starting ? t('crew.tools.terminal.starting') : '')}
+
+ {t('crew.tools.terminal.note')}
+ {error && ( +{t('crew.tools.browser.note')}
+