docuking-mcp 2.13.0 → 2.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/handlers/sync.js +36 -93
  2. package/package.json +1 -1
package/handlers/sync.js CHANGED
@@ -806,82 +806,23 @@ docuking_init을 먼저 실행하세요.`,
806
806
  }
807
807
 
808
808
  // ========================================
809
- // 4. 서버에만 있고 로컬에 없는 파일 soft-delete (deleted_at 기반)
809
+ // 4. 자동 삭제 비활성화 (2026-01-17 버그 수정)
810
+ // ========================================
811
+ // 이전 로직: "서버에 있고 로컬에 없으면 삭제"
812
+ // 문제: 다른 프로젝트에서 Pull하면 서버 파일이 삭제됨
813
+ // 해결: 자동 삭제 제거, docuking_delete로만 명시적 삭제
814
+ //
815
+ // 삭제는 명시적으로만:
816
+ // - 사용자가 "이 파일 삭제해줘" 요청 시
817
+ // - docuking_delete MCP 도구 사용
810
818
  // ========================================
811
- // 단, 협업자 폴더(yy_Coworker_*)는 삭제하지 않음 (오너가 협업자 파일을 삭제하면 안됨)
812
- // source: 'web' 파일은 MCP에서 삭제 시도하지 않음 (클라이언트에서 필터링)
813
- // 중요: localPathsBeforePull 사용 (Pull 전에 수집한 목록)
814
- // processedLocalPaths는 Pull로 내려받은 파일도 포함하므로 사용 금지
815
819
  let deleted = 0;
816
820
  const deletedFilePaths = [];
817
- let protectedFiles = []; // source: 'web' 파일 (보호됨)
818
- const webProtectedPaths = []; // MCP에서 미리 필터링한 웹 파일
819
-
820
- if (serverAllPaths.length > 0 && !isCoworker) {
821
- // 오너만 삭제 수행
822
- // yy_Coworker_*로 시작하는 경로는 삭제 대상에서 제외
823
- // localPathsBeforePull: Pull 전에 수집한 로컬 파일 목록 (좀비 파일 방지)
824
- //
825
- // ★ 핵심: source: 'web' 파일은 삭제 대상에서 미리 제외!
826
- // 이렇게 하면 웹에서 생성한 파일이 절대 삭제되지 않음
827
- const pathsToDelete = serverAllPaths.filter(p => {
828
- // 1. 로컬에 있으면 제외 (삭제 대상 아님)
829
- if (localPathsBeforePull.has(p)) return false;
830
-
831
- // 2. 협업자 폴더는 제외
832
- if (p.startsWith('yy_Coworker_')) return false;
833
-
834
- // 3. source: 'web' 파일은 제외 (웹에서 생성된 파일 보호)
835
- const meta = serverPathToMeta[p];
836
- if (meta && meta.source === 'web') {
837
- webProtectedPaths.push(p);
838
- return false;
839
- }
840
-
841
- return true;
842
- });
843
-
844
- // 웹 보호 파일 로그
845
- if (webProtectedPaths.length > 0) {
846
- console.error(`[DocuKing] source:web 파일 ${webProtectedPaths.length}개 삭제에서 제외:`, webProtectedPaths.slice(0, 5));
847
- }
821
+ let protectedFiles = [];
848
822
 
849
- console.error(`[DocuKing] soft-delete 대상: ${pathsToDelete.length}개 (서버에만 있고 Pull 로컬에 없던 파일, source:local만)`);
850
-
851
- if (pathsToDelete.length > 0) {
852
- try {
853
- // soft-delete API 호출 (deleted_at 설정, 3일 후 hard delete)
854
- const deleteResponse = await fetch(`${API_ENDPOINT}/files/soft-delete`, {
855
- method: 'POST',
856
- headers: {
857
- 'Content-Type': 'application/json',
858
- 'Authorization': `Bearer ${apiKey}`,
859
- },
860
- body: JSON.stringify({
861
- projectId,
862
- paths: pathsToDelete,
863
- }),
864
- });
865
-
866
- if (deleteResponse.ok) {
867
- const deleteResult = await deleteResponse.json();
868
- deleted = deleteResult.deleted || 0;
869
- deletedFilePaths.push(...(deleteResult.deletedPaths || []));
870
- protectedFiles = deleteResult.protected || [];
871
-
872
- // 보호된 파일이 있으면 로그 출력 (Backend에서 추가로 보호한 파일)
873
- if (protectedFiles.length > 0) {
874
- console.error(`[DocuKing] Backend에서 추가 보호된 파일 ${protectedFiles.length}개:`, protectedFiles.slice(0, 5));
875
- }
876
- }
877
- } catch (e) {
878
- console.error('[DocuKing] 파일 soft-delete 실패:', e.message);
879
- }
880
- }
881
-
882
- // MCP에서 미리 제외한 웹 파일도 보호 목록에 추가 (결과 표시용)
883
- protectedFiles = [...protectedFiles, ...webProtectedPaths];
884
- }
823
+ // 자동 삭제 로직 비활성화 - 아래 코드는 주석 처리
824
+ // "로컬에 없으면 삭제" 로직은 위험하므로 제거
825
+ console.error(`[DocuKing] 자동 삭제 비활성화됨 (명시적 삭제만 허용: docuking_delete 사용)`);
885
826
 
886
827
  // 5. 빈 폴더 생성
887
828
  let createdEmptyFolders = 0;
@@ -1300,11 +1241,24 @@ export async function handlePullInternal(args) {
1300
1241
  current++;
1301
1242
  continue;
1302
1243
  }
1244
+
1245
+ // ========================================
1246
+ // [버그 수정] 로컬 파일 우선 정책
1247
+ // 해시가 다르면 → 로컬 수정이 있다는 뜻
1248
+ // 서버 파일로 덮어쓰지 않고 로컬 유지
1249
+ // 다음 Push에서 로컬 → 서버로 업로드됨
1250
+ // ========================================
1251
+ console.error(`[DocuKing] 로컬 우선: ${file.path} (해시 다름 → 로컬 유지, Push 시 업로드됨)`);
1252
+ results.push({ type: 'skip', path: file.path, reason: 'local-modified' });
1253
+ skipped++;
1254
+ current++;
1255
+ continue;
1303
1256
  } catch (e) {
1304
1257
  // 해시 계산 실패 시 다운로드 대상
1305
1258
  }
1306
1259
  }
1307
1260
 
1261
+ // 로컬에 파일이 없는 경우만 다운로드
1308
1262
  filesToDownload.push({ ...file, fullPath });
1309
1263
  }
1310
1264
 
@@ -1437,29 +1391,18 @@ export async function handlePullInternal(args) {
1437
1391
  }
1438
1392
 
1439
1393
  // ========================================
1440
- // mark-as-pulled 호출 (source: 'web' → 'local' 변경)
1441
- // Pull된 웹 파일/폴더는 이제 로컬이 관리권한을 가짐
1394
+ // mark-as-pulled 비활성화 (2026-01-17 정책 변경)
1395
+ // ========================================
1396
+ // 이전 로직: source를 'web' → 'local'로 변경
1397
+ // 문제: source는 "출생신고"로, 한 번 정해지면 불변이어야 함
1398
+ // 변경하면 "출처 위조"가 됨
1399
+ //
1400
+ // 새 정책: source는 절대 변경하지 않음
1401
+ // - web에서 태어난 파일은 Pull해도 source: 'web' 유지
1402
+ // - local에서 태어난 파일은 Push해도 source: 'local' 유지
1442
1403
  // ========================================
1443
1404
  if (filesToMarkAsPulled.length > 0) {
1444
- try {
1445
- const markResponse = await fetch(`${API_ENDPOINT}/files/mark-as-pulled`, {
1446
- method: 'POST',
1447
- headers: {
1448
- 'Content-Type': 'application/json',
1449
- 'Authorization': `Bearer ${apiKey}`,
1450
- },
1451
- body: JSON.stringify({
1452
- projectId,
1453
- paths: filesToMarkAsPulled,
1454
- }),
1455
- });
1456
-
1457
- if (markResponse.ok) {
1458
- console.error(`[DocuKing] mark-as-pulled 완료: ${filesToMarkAsPulled.length}개`);
1459
- }
1460
- } catch (e) {
1461
- console.error('[DocuKing] mark-as-pulled 실패:', e.message);
1462
- }
1405
+ console.error(`[DocuKing] mark-as-pulled 비활성화됨 (source 불변 정책): ${filesToMarkAsPulled.length}개 파일의 source 유지`);
1463
1406
  }
1464
1407
 
1465
1408
  // ========================================
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "docuking-mcp",
3
- "version": "2.13.0",
3
+ "version": "2.14.0",
4
4
  "description": "DocuKing MCP Server - AI 시대의 문서 협업 플랫폼",
5
5
  "type": "module",
6
6
  "main": "index.js",