im-nobsidian 0.3.0 → 0.3.2
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.
- package/dist/index.js +312 -73
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
// src/index.ts
|
|
4
4
|
import { createRequire } from "module";
|
|
5
|
-
import { Command as
|
|
5
|
+
import { Command as Command11 } from "commander";
|
|
6
6
|
|
|
7
7
|
// src/commands/init.ts
|
|
8
8
|
import { Command } from "commander";
|
|
@@ -52,6 +52,12 @@ function summary(created, updated, deleted) {
|
|
|
52
52
|
if (parts.length === 0) parts.push(chalk.dim("no changes"));
|
|
53
53
|
return parts.join(" ");
|
|
54
54
|
}
|
|
55
|
+
function completionHeader(label, failedCount) {
|
|
56
|
+
return failedCount > 0 ? header(chalk.yellow(`${label} finished with errors`)) : header(chalk.green(`${label} complete`));
|
|
57
|
+
}
|
|
58
|
+
function failedCountText(count) {
|
|
59
|
+
return count > 0 ? chalk.red(`${count} failed`) : chalk.dim("0 failed");
|
|
60
|
+
}
|
|
55
61
|
function duration(ms) {
|
|
56
62
|
return chalk.dim(`Done in ${(ms / 1e3).toFixed(1)}s`);
|
|
57
63
|
}
|
|
@@ -177,7 +183,6 @@ import {
|
|
|
177
183
|
SyncOrchestrator,
|
|
178
184
|
NodeVaultFS
|
|
179
185
|
} from "@im-nobsidian/core";
|
|
180
|
-
import chalk3 from "chalk";
|
|
181
186
|
var pushCommand = new Command2("push").description("\uB85C\uCEEC \uBCC0\uACBD\uC0AC\uD56D\uC744 Notion\uC5D0 \uBC18\uC601").option("--dry-run", "\uC2E4\uC81C \uBC18\uC601 \uC5C6\uC774 \uBCC0\uACBD\uC0AC\uD56D\uB9CC \uD45C\uC2DC").option("-p, --path <paths...>", "\uD2B9\uC815 \uACBD\uB85C\uB9CC push").action(async (options) => {
|
|
182
187
|
const cwd = process.cwd();
|
|
183
188
|
const configManager = new ConfigManager2(cwd);
|
|
@@ -201,17 +206,19 @@ ${header(" Pushing to Notion...")}`);
|
|
|
201
206
|
console.log(` ${icon} ${item.path} ${prog} ${label}`);
|
|
202
207
|
}
|
|
203
208
|
});
|
|
209
|
+
const failedCount = result.failed.length;
|
|
204
210
|
console.log(`
|
|
205
|
-
${
|
|
211
|
+
${completionHeader("Push", failedCount)}`);
|
|
206
212
|
console.log(
|
|
207
|
-
` ${summary(result.created, result.updated, result.deleted)} ${
|
|
213
|
+
` ${summary(result.created, result.updated, result.deleted)} ${failedCountText(failedCount)}`
|
|
208
214
|
);
|
|
209
215
|
console.log(` ${duration(result.duration)}`);
|
|
210
|
-
if (
|
|
216
|
+
if (failedCount > 0) {
|
|
211
217
|
console.log("");
|
|
212
218
|
for (const f of result.failed) {
|
|
213
219
|
failedItem(f.path, f.error);
|
|
214
220
|
}
|
|
221
|
+
process.exitCode = 1;
|
|
215
222
|
}
|
|
216
223
|
} finally {
|
|
217
224
|
stateDb.close();
|
|
@@ -227,7 +234,7 @@ import {
|
|
|
227
234
|
SyncOrchestrator as SyncOrchestrator2,
|
|
228
235
|
NodeVaultFS as NodeVaultFS2
|
|
229
236
|
} from "@im-nobsidian/core";
|
|
230
|
-
import
|
|
237
|
+
import chalk3 from "chalk";
|
|
231
238
|
var pullCommand = new Command3("pull").description("Notion \uBCC0\uACBD\uC0AC\uD56D\uC744 \uB85C\uCEEC\uC5D0 \uBC18\uC601").option("--dry-run", "\uC2E4\uC81C \uBC18\uC601 \uC5C6\uC774 \uBCC0\uACBD\uC0AC\uD56D\uB9CC \uD45C\uC2DC").option("-p, --path <paths...>", "\uD2B9\uC815 \uACBD\uB85C\uB9CC pull").option(
|
|
232
239
|
"--force",
|
|
233
240
|
"\uC99D\uBD84 \uAC10\uC9C0\uB97C \uAC74\uB108\uB6F0\uACE0 \uC804\uCCB4 \uC2A4\uCE94 (search \uC778\uB371\uC2F1 \uC9C0\uC5F0\uC73C\uB85C \uB204\uB77D\uB41C \uC2E0\uADDC \uD398\uC774\uC9C0\xB7DB \uBCF5\uAD6C)"
|
|
@@ -267,23 +274,28 @@ ${header(" Pulling from Notion...")}`);
|
|
|
267
274
|
if (result.linkCount > 0) {
|
|
268
275
|
console.log(` ${icons.success} ${result.linkCount} links resolved`);
|
|
269
276
|
}
|
|
277
|
+
const failedCount = result.failed.length;
|
|
270
278
|
console.log(`
|
|
271
|
-
${
|
|
279
|
+
${completionHeader("Pull", failedCount)}`);
|
|
272
280
|
console.log(
|
|
273
|
-
` ${summary(result.created, result.updated, result.deleted)} ${
|
|
281
|
+
` ${summary(result.created, result.updated, result.deleted)} ${failedCountText(failedCount)}`
|
|
274
282
|
);
|
|
283
|
+
if (result.restored > 0) {
|
|
284
|
+
console.log(` ${icons.success} ${chalk3.yellow(`${result.restored} restored`)}`);
|
|
285
|
+
}
|
|
275
286
|
console.log(` ${duration(result.duration)}`);
|
|
276
287
|
if (result.conflicts.length > 0) {
|
|
277
288
|
console.log(
|
|
278
289
|
`
|
|
279
|
-
${icons.conflict} ${
|
|
290
|
+
${icons.conflict} ${chalk3.magenta(`${result.conflicts.length} conflicts`)} \u2014 run ${chalk3.cyan("nobsi resolve")}`
|
|
280
291
|
);
|
|
281
292
|
}
|
|
282
|
-
if (
|
|
293
|
+
if (failedCount > 0) {
|
|
283
294
|
console.log("");
|
|
284
295
|
for (const f of result.failed) {
|
|
285
296
|
failedItem(f.path, f.error);
|
|
286
297
|
}
|
|
298
|
+
process.exitCode = 1;
|
|
287
299
|
}
|
|
288
300
|
} finally {
|
|
289
301
|
stateDb.close();
|
|
@@ -299,7 +311,7 @@ import {
|
|
|
299
311
|
SyncOrchestrator as SyncOrchestrator3,
|
|
300
312
|
NodeVaultFS as NodeVaultFS3
|
|
301
313
|
} from "@im-nobsidian/core";
|
|
302
|
-
import
|
|
314
|
+
import chalk4 from "chalk";
|
|
303
315
|
var syncCommand = new Command4("sync").description("\uC591\uBC29\uD5A5 \uB3D9\uAE30\uD654 (Pull \u2192 Push)").option("--dry-run", "\uC2E4\uC81C \uBC18\uC601 \uC5C6\uC774 \uBCC0\uACBD\uC0AC\uD56D\uB9CC \uD45C\uC2DC").action(async (options) => {
|
|
304
316
|
const cwd = process.cwd();
|
|
305
317
|
const configManager = new ConfigManager4(cwd);
|
|
@@ -314,7 +326,7 @@ ${header(" Bidirectional Sync")}`);
|
|
|
314
326
|
console.log(` ${separator()}`);
|
|
315
327
|
if (options.dryRun) console.log(dimText(" (dry-run mode)"));
|
|
316
328
|
console.log(`
|
|
317
|
-
${header(
|
|
329
|
+
${header(chalk4.blue("\u25BC Pull"))} ${dimText("(Notion \u2192 Obsidian)")}`);
|
|
318
330
|
const pullResult = await orchestrator.pull({
|
|
319
331
|
dryRun: options.dryRun,
|
|
320
332
|
onProgress: (_current, _total, item) => {
|
|
@@ -330,7 +342,7 @@ ${header(" Bidirectional Sync")}`);
|
|
|
330
342
|
console.log(` ${icons.success} ${pullResult.linkCount} links resolved`);
|
|
331
343
|
}
|
|
332
344
|
console.log(`
|
|
333
|
-
${header(
|
|
345
|
+
${header(chalk4.magenta("\u25B2 Push"))} ${dimText("(Obsidian \u2192 Notion)")}`);
|
|
334
346
|
const pushResult = await orchestrator.push({
|
|
335
347
|
dryRun: options.dryRun,
|
|
336
348
|
onProgress: (_current, _total, item) => {
|
|
@@ -340,21 +352,30 @@ ${header(" Bidirectional Sync")}`);
|
|
|
340
352
|
}
|
|
341
353
|
});
|
|
342
354
|
const totalDuration = pullResult.duration + pushResult.duration;
|
|
355
|
+
const pullFailed = pullResult.failed.length;
|
|
356
|
+
const pushFailed = pushResult.failed.length;
|
|
343
357
|
console.log(`
|
|
344
|
-
${
|
|
358
|
+
${completionHeader("Sync", pullFailed + pushFailed)}`);
|
|
345
359
|
console.log(
|
|
346
|
-
` ${
|
|
360
|
+
` ${chalk4.blue("Pull:")} ${summary(pullResult.created, pullResult.updated, pullResult.deleted)}` + // 사라졌던 파일이 되살아난 경우에만 덧붙인다 — 평상시 요약을 어지럽히지 않는다.
|
|
361
|
+
(pullResult.restored > 0 ? ` ${chalk4.yellow(`${pullResult.restored} restored`)}` : "") + (pullFailed > 0 ? ` ${chalk4.red(`${pullFailed} failed`)}` : "")
|
|
347
362
|
);
|
|
348
363
|
console.log(
|
|
349
|
-
` ${
|
|
364
|
+
` ${chalk4.magenta("Push:")} ${summary(pushResult.created, pushResult.updated, pushResult.deleted)}` + (pushFailed > 0 ? ` ${chalk4.red(`${pushFailed} failed`)}` : "")
|
|
350
365
|
);
|
|
351
366
|
console.log(` ${duration(totalDuration)}`);
|
|
352
367
|
if (pullResult.conflicts.length > 0) {
|
|
353
368
|
console.log(
|
|
354
369
|
`
|
|
355
|
-
${icons.conflict} ${
|
|
370
|
+
${icons.conflict} ${chalk4.magenta(`${pullResult.conflicts.length} conflicts`)} \u2014 run ${chalk4.cyan("nobsi resolve")}`
|
|
356
371
|
);
|
|
357
372
|
}
|
|
373
|
+
if (pullFailed + pushFailed > 0) {
|
|
374
|
+
console.log("");
|
|
375
|
+
for (const f of pullResult.failed) failedItem(`\u25BC ${f.path}`, f.error);
|
|
376
|
+
for (const f of pushResult.failed) failedItem(`\u25B2 ${f.path}`, f.error);
|
|
377
|
+
process.exitCode = 1;
|
|
378
|
+
}
|
|
358
379
|
} finally {
|
|
359
380
|
stateDb.close();
|
|
360
381
|
}
|
|
@@ -369,7 +390,7 @@ import {
|
|
|
369
390
|
SyncOrchestrator as SyncOrchestrator4,
|
|
370
391
|
NodeVaultFS as NodeVaultFS4
|
|
371
392
|
} from "@im-nobsidian/core";
|
|
372
|
-
import
|
|
393
|
+
import chalk5 from "chalk";
|
|
373
394
|
var statusCommand = new Command5("status").description("\uB3D9\uAE30\uD654 \uC0C1\uD0DC \uD655\uC778 (--full: \uC6D0\uACA9 \uBCC0\uACBD \uD3EC\uD568)").option("--full", "Notion \uC6D0\uACA9 \uBCC0\uACBD\uAE4C\uC9C0 \uC591\uBC29\uD5A5 \uD655\uC778 (API \uD638\uCD9C, \uB290\uB9BC)").action(async (opts) => {
|
|
374
395
|
const cwd = process.cwd();
|
|
375
396
|
const configManager = new ConfigManager5(cwd);
|
|
@@ -392,35 +413,48 @@ ${header(" Sync Status")}`);
|
|
|
392
413
|
const iso = date.getFullYear() + "-" + String(date.getMonth() + 1).padStart(2, "0") + "-" + String(date.getDate()).padStart(2, "0") + " " + String(date.getHours()).padStart(2, "0") + ":" + String(date.getMinutes()).padStart(2, "0") + ":" + String(date.getSeconds()).padStart(2, "0");
|
|
393
414
|
console.log(` ${dimText("Last sync:")} ${iso}`);
|
|
394
415
|
} else {
|
|
395
|
-
console.log(` ${dimText("Last sync:")} ${
|
|
416
|
+
console.log(` ${dimText("Last sync:")} ${chalk5.yellow("never")}`);
|
|
396
417
|
}
|
|
397
418
|
const allRecords = stateDb.getAll();
|
|
398
419
|
const total = allRecords.length;
|
|
399
|
-
const synced = allRecords.filter((r) => r.status === "synced").length;
|
|
400
420
|
const conflictCount = status.conflictRecords.length;
|
|
401
421
|
const createdChanges = status.localChanges.filter((c) => c.type === "created");
|
|
402
422
|
const modifiedChanges = status.localChanges.filter((c) => c.type === "modified");
|
|
423
|
+
const deletedChanges = status.localChanges.filter((c) => c.type === "deleted");
|
|
424
|
+
const dirtyPaths = /* @__PURE__ */ new Set([
|
|
425
|
+
...modifiedChanges.map((c) => c.path),
|
|
426
|
+
...deletedChanges.map((c) => c.path),
|
|
427
|
+
...status.conflictRecords.map((r) => r.obsidianPath)
|
|
428
|
+
]);
|
|
429
|
+
const synced = allRecords.filter(
|
|
430
|
+
(r) => r.status === "synced" && !dirtyPaths.has(r.obsidianPath)
|
|
431
|
+
).length;
|
|
403
432
|
const remoteCreated = status.remoteChanges.filter((c) => c.type === "created");
|
|
404
433
|
const remoteModified = status.remoteChanges.filter((c) => c.type === "modified");
|
|
405
434
|
const remoteDeleted = status.remoteChanges.filter((c) => c.type === "deleted");
|
|
406
435
|
console.log(`
|
|
407
436
|
${header(`Tracked files: ${total}`)}`);
|
|
408
|
-
console.log(` ${icons.synced} ${
|
|
437
|
+
console.log(` ${icons.synced} ${chalk5.green("synced")} ${synced}`);
|
|
409
438
|
if (modifiedChanges.length > 0) {
|
|
410
|
-
console.log(` ${icons.modified} ${
|
|
439
|
+
console.log(` ${icons.modified} ${chalk5.yellow("modified")} ${modifiedChanges.length}`);
|
|
411
440
|
}
|
|
412
|
-
if (
|
|
413
|
-
console.log(` ${
|
|
441
|
+
if (deletedChanges.length > 0) {
|
|
442
|
+
console.log(` ${chalk5.red("-")} ${chalk5.red("deleted")} ${deletedChanges.length}`);
|
|
414
443
|
}
|
|
415
444
|
if (conflictCount > 0) {
|
|
416
|
-
console.log(` ${icons.conflictDot} ${
|
|
445
|
+
console.log(` ${icons.conflictDot} ${chalk5.magenta("conflict")} ${conflictCount}`);
|
|
446
|
+
}
|
|
447
|
+
if (createdChanges.length > 0) {
|
|
448
|
+
console.log(
|
|
449
|
+
` ${icons.newFile} ${chalk5.cyan("new")} ${createdChanges.length} ${dimText("(untracked)")}`
|
|
450
|
+
);
|
|
417
451
|
}
|
|
418
452
|
if (modifiedChanges.length > 0) {
|
|
419
453
|
console.log(`
|
|
420
454
|
${header("Modified files:")}`);
|
|
421
455
|
for (const c of modifiedChanges.slice(0, 10)) {
|
|
422
456
|
console.log(
|
|
423
|
-
` ${
|
|
457
|
+
` ${chalk5.yellow("~")} ${c.path.padEnd(30)} ${dimText("(local changed)")}`
|
|
424
458
|
);
|
|
425
459
|
}
|
|
426
460
|
if (modifiedChanges.length > 10) {
|
|
@@ -431,7 +465,7 @@ ${header(" Sync Status")}`);
|
|
|
431
465
|
console.log(`
|
|
432
466
|
${header("New files:")}`);
|
|
433
467
|
for (const c of createdChanges.slice(0, 10)) {
|
|
434
|
-
console.log(` ${
|
|
468
|
+
console.log(` ${chalk5.cyan("+")} ${c.path.padEnd(30)} ${dimText("(untracked)")}`);
|
|
435
469
|
}
|
|
436
470
|
if (createdChanges.length > 10) {
|
|
437
471
|
console.log(dimText(` ... and ${createdChanges.length - 10} more`));
|
|
@@ -441,18 +475,18 @@ ${header(" Sync Status")}`);
|
|
|
441
475
|
console.log(`
|
|
442
476
|
${header(`Remote changes (Notion): ${status.remoteChanges.length}`)}`);
|
|
443
477
|
if (remoteCreated.length > 0) {
|
|
444
|
-
console.log(` ${
|
|
478
|
+
console.log(` ${chalk5.cyan("+")} ${chalk5.cyan("new")} ${remoteCreated.length}`);
|
|
445
479
|
}
|
|
446
480
|
if (remoteModified.length > 0) {
|
|
447
481
|
console.log(
|
|
448
|
-
` ${
|
|
482
|
+
` ${chalk5.yellow("~")} ${chalk5.yellow("modified")} ${remoteModified.length}`
|
|
449
483
|
);
|
|
450
484
|
}
|
|
451
485
|
if (remoteDeleted.length > 0) {
|
|
452
|
-
console.log(` ${
|
|
486
|
+
console.log(` ${chalk5.red("-")} ${chalk5.red("deleted")} ${remoteDeleted.length}`);
|
|
453
487
|
}
|
|
454
488
|
for (const c of status.remoteChanges.slice(0, 10)) {
|
|
455
|
-
const icon = c.type === "created" ?
|
|
489
|
+
const icon = c.type === "created" ? chalk5.cyan("+") : c.type === "modified" ? chalk5.yellow("~") : chalk5.red("-");
|
|
456
490
|
console.log(` ${icon} ${c.pageId.slice(0, 8)}... ${dimText(`(${c.type})`)}`);
|
|
457
491
|
}
|
|
458
492
|
if (status.remoteChanges.length > 10) {
|
|
@@ -461,7 +495,7 @@ ${header(" Sync Status")}`);
|
|
|
461
495
|
}
|
|
462
496
|
if (conflictCount > 0) {
|
|
463
497
|
console.log(`
|
|
464
|
-
${header(
|
|
498
|
+
${header(chalk5.magenta("Conflicts:"))}`);
|
|
465
499
|
for (const r of status.conflictRecords) {
|
|
466
500
|
console.log(
|
|
467
501
|
` ${icons.conflict} ${r.obsidianPath.padEnd(30)} ${dimText("(both sides changed)")}`
|
|
@@ -473,20 +507,20 @@ ${header(" Sync Status")}`);
|
|
|
473
507
|
console.log("");
|
|
474
508
|
if (conflictCount > 0) {
|
|
475
509
|
console.log(
|
|
476
|
-
` ${dimText("Run")} ${
|
|
510
|
+
` ${dimText("Run")} ${chalk5.cyan("nobsi resolve")} ${dimText("to resolve conflicts")}`
|
|
477
511
|
);
|
|
478
512
|
}
|
|
479
513
|
console.log(
|
|
480
|
-
` ${dimText("Run")} ${
|
|
514
|
+
` ${dimText("Run")} ${chalk5.cyan("nobsi sync")} ${dimText("to push/pull changes")}`
|
|
481
515
|
);
|
|
482
516
|
} else {
|
|
483
517
|
console.log(`
|
|
484
|
-
${
|
|
518
|
+
${chalk5.green("Everything up to date")} \u2713`);
|
|
485
519
|
}
|
|
486
520
|
if (!opts.full) {
|
|
487
521
|
console.log(
|
|
488
522
|
`
|
|
489
|
-
${dimText("Run")} ${
|
|
523
|
+
${dimText("Run")} ${chalk5.cyan("nobsi status --full")} ${dimText("to check Notion remote changes")}`
|
|
490
524
|
);
|
|
491
525
|
}
|
|
492
526
|
} finally {
|
|
@@ -501,10 +535,13 @@ import {
|
|
|
501
535
|
StateDB as StateDB5,
|
|
502
536
|
NotionClient as NotionClient6,
|
|
503
537
|
SyncOrchestrator as SyncOrchestrator5,
|
|
504
|
-
NodeVaultFS as NodeVaultFS5
|
|
538
|
+
NodeVaultFS as NodeVaultFS5,
|
|
539
|
+
matchesPathScope
|
|
505
540
|
} from "@im-nobsidian/core";
|
|
506
541
|
import { createTwoFilesPatch } from "diff";
|
|
507
|
-
var diffCommand = new Command6("diff").description(
|
|
542
|
+
var diffCommand = new Command6("diff").description(
|
|
543
|
+
"\uB85C\uCEEC \uBCC0\uACBD\uC0AC\uD56D \uD45C\uC2DC (\uAE30\uBCF8: \uB9C8\uC9C0\uB9C9 \uB3D9\uAE30\uD654 \uC2DC\uC810\uACFC \uBE44\uAD50, --remote: Notion \uD604\uC7AC\uBCF8\uACFC \uBE44\uAD50)"
|
|
544
|
+
).argument("[path]", "\uD2B9\uC815 \uD30C\uC77C \uACBD\uB85C (\uC0DD\uB7B5 \uC2DC \uBCC0\uACBD\uB41C \uBAA8\uB4E0 \uD30C\uC77C)").option("--color", "\uCEEC\uB7EC \uCD9C\uB825", true).option("--remote", "Notion \uD604\uC7AC \uBCF8\uBB38\uACFC \uBE44\uAD50 (API \uD638\uCD9C, \uB290\uB9BC)").action(async (targetPath, options) => {
|
|
508
545
|
const cwd = process.cwd();
|
|
509
546
|
const configManager = new ConfigManager6(cwd);
|
|
510
547
|
const config = await configManager.load();
|
|
@@ -514,13 +551,33 @@ var diffCommand = new Command6("diff").description("\uB85C\uCEEC\uACFC Notion \u
|
|
|
514
551
|
const vaultFs = new NodeVaultFS5(cwd, config.paths);
|
|
515
552
|
const orchestrator = new SyncOrchestrator5(config, stateDb, client, vaultFs);
|
|
516
553
|
const status = await orchestrator.statusLocal();
|
|
517
|
-
const changes = targetPath ? status.localChanges.filter((c) => c.path
|
|
554
|
+
const changes = targetPath ? status.localChanges.filter((c) => matchesPathScope(c.path, targetPath)) : status.localChanges;
|
|
518
555
|
if (changes.length === 0) {
|
|
519
556
|
console.log("\uBCC0\uACBD\uC0AC\uD56D \uC5C6\uC74C");
|
|
520
557
|
return;
|
|
521
558
|
}
|
|
522
559
|
for (const change of changes) {
|
|
523
560
|
const record = stateDb.getByPath(change.path);
|
|
561
|
+
if (options.remote && change.type !== "deleted") {
|
|
562
|
+
const remote = await orchestrator.renderRemoteSnapshot(change.path);
|
|
563
|
+
if (remote === null) {
|
|
564
|
+
console.log(`--- (\uC6D0\uACA9 \uC5C6\uC74C) ${change.path}`);
|
|
565
|
+
console.log(`+++ b/${change.path}`);
|
|
566
|
+
console.log(`(\uC544\uC9C1 Notion \uC5D0 \uC5C6\uB294 \uD30C\uC77C\uC785\uB2C8\uB2E4)`);
|
|
567
|
+
console.log("");
|
|
568
|
+
continue;
|
|
569
|
+
}
|
|
570
|
+
const currentContent = await vaultFs.readFile(change.path);
|
|
571
|
+
const patch = createTwoFilesPatch(
|
|
572
|
+
`notion/${change.path}`,
|
|
573
|
+
`b/${change.path}`,
|
|
574
|
+
remote,
|
|
575
|
+
currentContent
|
|
576
|
+
);
|
|
577
|
+
printPatch(patch, options.color, `notion/${change.path}`, `b/${change.path}`);
|
|
578
|
+
console.log("");
|
|
579
|
+
continue;
|
|
580
|
+
}
|
|
524
581
|
if (change.type === "created") {
|
|
525
582
|
const content = await vaultFs.readFile(change.path);
|
|
526
583
|
const patch = createTwoFilesPatch("/dev/null", `b/${change.path}`, "", content);
|
|
@@ -589,7 +646,7 @@ import {
|
|
|
589
646
|
SyncOrchestrator as SyncOrchestrator6,
|
|
590
647
|
NodeVaultFS as NodeVaultFS6
|
|
591
648
|
} from "@im-nobsidian/core";
|
|
592
|
-
import
|
|
649
|
+
import chalk6 from "chalk";
|
|
593
650
|
var fetchCommand = new Command7("fetch").description("Notion \uB9AC\uBAA8\uD2B8 \uC0C1\uD0DC \uC2A4\uCE94 (\uC0AD\uC81C \uAC10\uC9C0 \uD3EC\uD568)").action(async () => {
|
|
594
651
|
const cwd = process.cwd();
|
|
595
652
|
const configManager = new ConfigManager7(cwd);
|
|
@@ -605,21 +662,21 @@ var fetchCommand = new Command7("fetch").description("Notion \uB9AC\uBAA8\uD2B8
|
|
|
605
662
|
${header(" Remote Scan")}`);
|
|
606
663
|
console.log(` ${separator(40)}`);
|
|
607
664
|
if (result.newPages > 0) {
|
|
608
|
-
console.log(` ${
|
|
665
|
+
console.log(` ${chalk6.cyan("+")} New pages: ${result.newPages}`);
|
|
609
666
|
}
|
|
610
667
|
if (result.modifiedPages > 0) {
|
|
611
|
-
console.log(` ${
|
|
668
|
+
console.log(` ${chalk6.yellow("~")} Modified pages: ${result.modifiedPages}`);
|
|
612
669
|
}
|
|
613
670
|
if (result.deletedPages > 0) {
|
|
614
|
-
console.log(` ${
|
|
671
|
+
console.log(` ${chalk6.red("-")} Deleted pages: ${result.deletedPages}`);
|
|
615
672
|
}
|
|
616
673
|
const total = result.newPages + result.modifiedPages + result.deletedPages;
|
|
617
674
|
if (total === 0) {
|
|
618
|
-
console.log(` ${
|
|
675
|
+
console.log(` ${chalk6.green("Remote is up to date")} \u2713`);
|
|
619
676
|
} else {
|
|
620
677
|
console.log(
|
|
621
678
|
`
|
|
622
|
-
${dimText("Run")} ${
|
|
679
|
+
${dimText("Run")} ${chalk6.cyan("nobsi pull")} ${dimText("to apply remote changes")}`
|
|
623
680
|
);
|
|
624
681
|
}
|
|
625
682
|
console.log(dimText(`
|
|
@@ -639,53 +696,99 @@ import {
|
|
|
639
696
|
SyncOrchestrator as SyncOrchestrator7,
|
|
640
697
|
NodeVaultFS as NodeVaultFS7
|
|
641
698
|
} from "@im-nobsidian/core";
|
|
642
|
-
var
|
|
699
|
+
var STRATEGIES = ["local-first", "remote-first", "duplicate"];
|
|
700
|
+
function isInteractive() {
|
|
701
|
+
return process.stdin.isTTY === true && process.stdout.isTTY === true;
|
|
702
|
+
}
|
|
703
|
+
var resolveCommand = new Command8("resolve").description("\uCDA9\uB3CC\uC744 \uD574\uACB0\uD569\uB2C8\uB2E4").option("-s, --strategy <strategy>", `\uC790\uB3D9 \uD574\uACB0 \uC804\uB7B5 (${STRATEGIES.join(" | ")})`).option("-y, --yes", "\uD655\uC778 \uD504\uB86C\uD504\uD2B8 \uC5C6\uC774 \uC9C4\uD589 (\uBE44\uB300\uD654\uD615/CI)").action(async (options) => {
|
|
704
|
+
if (options.strategy && !STRATEGIES.includes(options.strategy)) {
|
|
705
|
+
console.error(`\uC54C \uC218 \uC5C6\uB294 \uC804\uB7B5 '${options.strategy}' \u2014 \uC0AC\uC6A9 \uAC00\uB2A5: ${STRATEGIES.join(", ")}`);
|
|
706
|
+
process.exitCode = 1;
|
|
707
|
+
return;
|
|
708
|
+
}
|
|
643
709
|
const cwd = process.cwd();
|
|
644
710
|
const configManager = new ConfigManager8(cwd);
|
|
711
|
+
let stateDb;
|
|
645
712
|
try {
|
|
646
713
|
const config = await configManager.load();
|
|
647
|
-
|
|
714
|
+
stateDb = StateDB7.open(configManager.dbPath);
|
|
648
715
|
const client = NotionClient8.fromConfig(config);
|
|
649
716
|
const vaultFs = new NodeVaultFS7(cwd, config.paths);
|
|
650
717
|
const orchestrator = new SyncOrchestrator7(config, stateDb, client, vaultFs);
|
|
651
|
-
await orchestrator.
|
|
652
|
-
|
|
653
|
-
if (conflictRecords.length === 0) {
|
|
718
|
+
const conflicts = await orchestrator.listConflicts();
|
|
719
|
+
if (conflicts.length === 0) {
|
|
654
720
|
console.log("\uCDA9\uB3CC\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.");
|
|
655
|
-
|
|
721
|
+
return;
|
|
722
|
+
}
|
|
723
|
+
const cleared = new Set(orchestrator.clearStaleConflicts(conflicts));
|
|
724
|
+
for (const path of cleared) {
|
|
725
|
+
console.log(` \x1B[32m\u2713\x1B[0m ${path} \u2192 \uC591\uCABD \uB3D9\uC77C, \uCDA9\uB3CC \uD45C\uC2DC\uB9CC \uD574\uC81C`);
|
|
726
|
+
}
|
|
727
|
+
const pending = conflicts.filter((c) => !cleared.has(c.syncRecord.obsidianPath));
|
|
728
|
+
if (pending.length === 0) {
|
|
729
|
+
console.log(`
|
|
730
|
+
${cleared.size}\uAC74 \uBAA8\uB450 \uC774\uBBF8 \uD574\uC18C\uB41C \uC0C1\uD0DC\uC600\uC2B5\uB2C8\uB2E4 \u2014 \uC0C1\uD0DC\uB9CC \uC815\uB9AC\uD588\uC2B5\uB2C8\uB2E4.`);
|
|
656
731
|
return;
|
|
657
732
|
}
|
|
658
733
|
console.log(`
|
|
659
|
-
\uCDA9\uB3CC ${
|
|
734
|
+
\uCDA9\uB3CC ${pending.length}\uAC74 \uBC1C\uACAC
|
|
660
735
|
`);
|
|
661
736
|
if (options.strategy) {
|
|
662
737
|
const strategy = options.strategy;
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
738
|
+
if (!options.yes) {
|
|
739
|
+
if (!isInteractive()) {
|
|
740
|
+
console.error(
|
|
741
|
+
`\uBE44\uB300\uD654\uD615 \uD658\uACBD\uC785\uB2C8\uB2E4 \u2014 '${strategy}' \uC804\uB7B5\uC73C\uB85C ${pending.length}\uAC74\uC744 \uC77C\uAD04 \uD574\uACB0\uD558\uB824\uBA74 --yes \uB97C \uD568\uAED8 \uC9C0\uC815\uD558\uC138\uC694.`
|
|
742
|
+
);
|
|
743
|
+
process.exitCode = 1;
|
|
744
|
+
return;
|
|
745
|
+
}
|
|
746
|
+
const shouldProceed = await confirm2({
|
|
747
|
+
message: `${pending.length}\uAC74\uC758 \uCDA9\uB3CC\uC744 '${strategy}' \uC804\uB7B5\uC73C\uB85C \uC77C\uAD04 \uD574\uACB0\uD560\uAE4C\uC694?`
|
|
748
|
+
});
|
|
749
|
+
if (!shouldProceed) return;
|
|
669
750
|
}
|
|
670
|
-
const
|
|
671
|
-
|
|
751
|
+
const results = await orchestrator.resolveAllConflicts(pending, strategy);
|
|
752
|
+
let failed2 = 0;
|
|
672
753
|
for (const result of results) {
|
|
754
|
+
if (!result.success) failed2++;
|
|
673
755
|
const icon = result.success ? "\x1B[32m\u2713\x1B[0m" : "\x1B[31m\u2717\x1B[0m";
|
|
674
756
|
console.log(` ${icon} ${result.path} \u2192 ${result.choice}`);
|
|
675
757
|
}
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
for (const conflict of pullResult.conflicts) {
|
|
679
|
-
await resolveInteractive(conflict, orchestrator);
|
|
680
|
-
}
|
|
758
|
+
report(results.length - failed2, failed2);
|
|
759
|
+
return;
|
|
681
760
|
}
|
|
682
|
-
|
|
683
|
-
|
|
761
|
+
if (!isInteractive()) {
|
|
762
|
+
console.error(
|
|
763
|
+
"\uBE44\uB300\uD654\uD615 \uD658\uACBD\uC5D0\uC11C\uB294 \uB300\uD654\uD615 \uD574\uACB0\uC744 \uC4F8 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4 \u2014 --strategy <local-first|remote-first|duplicate> --yes \uB97C \uC9C0\uC815\uD558\uC138\uC694."
|
|
764
|
+
);
|
|
765
|
+
process.exitCode = 1;
|
|
766
|
+
return;
|
|
767
|
+
}
|
|
768
|
+
let resolved = 0;
|
|
769
|
+
let failed = 0;
|
|
770
|
+
for (const conflict of pending) {
|
|
771
|
+
if (await resolveInteractive(conflict, orchestrator)) resolved++;
|
|
772
|
+
else failed++;
|
|
773
|
+
}
|
|
774
|
+
report(resolved, failed);
|
|
684
775
|
} catch (error) {
|
|
685
776
|
console.error("\uC624\uB958:", error instanceof Error ? error.message : String(error));
|
|
686
|
-
process.
|
|
777
|
+
process.exitCode = 1;
|
|
778
|
+
} finally {
|
|
779
|
+
stateDb?.close();
|
|
687
780
|
}
|
|
688
781
|
});
|
|
782
|
+
function report(resolved, failed) {
|
|
783
|
+
if (failed === 0) {
|
|
784
|
+
console.log(`
|
|
785
|
+
\uCDA9\uB3CC \uD574\uACB0 \uC644\uB8CC \u2014 ${resolved}\uAC74`);
|
|
786
|
+
return;
|
|
787
|
+
}
|
|
788
|
+
console.log(`
|
|
789
|
+
\x1B[33m\uCDA9\uB3CC ${resolved}\uAC74 \uD574\uACB0 \xB7 ${failed}\uAC74 \uBBF8\uD574\uACB0\x1B[0m`);
|
|
790
|
+
process.exitCode = 1;
|
|
791
|
+
}
|
|
689
792
|
async function resolveInteractive(conflict, orchestrator) {
|
|
690
793
|
const path = conflict.syncRecord.obsidianPath;
|
|
691
794
|
console.log(`
|
|
@@ -717,11 +820,14 @@ ${"\u2500".repeat(60)}`);
|
|
|
717
820
|
const result = await orchestrator.resolveConflict(conflict, choice);
|
|
718
821
|
if (result.success) {
|
|
719
822
|
console.log(`\x1B[32m\u2713 ${path} \u2192 ${choice}\uB85C \uD574\uACB0\x1B[0m`);
|
|
720
|
-
|
|
823
|
+
return true;
|
|
824
|
+
}
|
|
825
|
+
if (result.mergeHadConflicts) {
|
|
721
826
|
console.log(`\x1B[33m\u26A0 ${path} \u2192 \uC790\uB3D9 \uBCD1\uD569 \uC644\uB8CC (\uC218\uB3D9 \uD655\uC778 \uD544\uC694\uD55C \uCDA9\uB3CC \uB9C8\uCEE4 \uC788\uC74C)\x1B[0m`);
|
|
722
|
-
|
|
723
|
-
console.log(`\x1B[31m\u2717 ${path} \u2192 \uD574\uACB0 \uC2E4\uD328\x1B[0m`);
|
|
827
|
+
return false;
|
|
724
828
|
}
|
|
829
|
+
console.log(`\x1B[31m\u2717 ${path} \u2192 \uD574\uACB0 \uC2E4\uD328\x1B[0m`);
|
|
830
|
+
return false;
|
|
725
831
|
}
|
|
726
832
|
|
|
727
833
|
// src/commands/watch.ts
|
|
@@ -803,10 +909,142 @@ var watchCommand = new Command9("watch").description("\uD30C\uC77C \uBCC0\uACBD
|
|
|
803
909
|
});
|
|
804
910
|
});
|
|
805
911
|
|
|
912
|
+
// src/commands/verify.ts
|
|
913
|
+
import { Command as Command10 } from "commander";
|
|
914
|
+
import {
|
|
915
|
+
ConfigManager as ConfigManager10,
|
|
916
|
+
StateDB as StateDB9,
|
|
917
|
+
NotionClient as NotionClient10,
|
|
918
|
+
SyncOrchestrator as SyncOrchestrator9,
|
|
919
|
+
NodeVaultFS as NodeVaultFS9
|
|
920
|
+
} from "@im-nobsidian/core";
|
|
921
|
+
import chalk7 from "chalk";
|
|
922
|
+
var SAMPLE_LIMIT = 10;
|
|
923
|
+
function printIdSample(label, ids, color) {
|
|
924
|
+
console.log(` ${color(label)} ${ids.length}`);
|
|
925
|
+
for (const id of ids.slice(0, SAMPLE_LIMIT)) {
|
|
926
|
+
console.log(` ${dimText(id)}`);
|
|
927
|
+
}
|
|
928
|
+
if (ids.length > SAMPLE_LIMIT) {
|
|
929
|
+
console.log(dimText(` ... and ${ids.length - SAMPLE_LIMIT} more`));
|
|
930
|
+
}
|
|
931
|
+
}
|
|
932
|
+
function printDatabaseSection(report2) {
|
|
933
|
+
console.log(`
|
|
934
|
+
${header(" DB Completeness")}`);
|
|
935
|
+
console.log(` ${separator(50)}`);
|
|
936
|
+
console.log(` ${dimText("Databases:")} ${report2.databases.length}`);
|
|
937
|
+
console.log(` ${dimText("Remote rows:")} ${report2.remoteTotal}`);
|
|
938
|
+
console.log(` ${dimText("Vault rows:")} ${report2.localTotal}`);
|
|
939
|
+
const broken = report2.databases.filter((d) => d.missingIds.length > 0 || d.extraIds.length > 0);
|
|
940
|
+
if (broken.length > 0) {
|
|
941
|
+
console.log(`
|
|
942
|
+
${header(chalk7.red(`Incomplete databases: ${broken.length}`))}`);
|
|
943
|
+
for (const db of broken) {
|
|
944
|
+
console.log(
|
|
945
|
+
` ${icons.fail} ${db.databaseId} ${dimText(`remote ${db.remoteRows} / vault ${db.localRows}`)}`
|
|
946
|
+
);
|
|
947
|
+
if (db.missingIds.length > 0) printIdSample("missing", db.missingIds, chalk7.red);
|
|
948
|
+
if (db.extraIds.length > 0) printIdSample("extra", db.extraIds, chalk7.yellow);
|
|
949
|
+
}
|
|
950
|
+
}
|
|
951
|
+
if (report2.failures.length > 0) {
|
|
952
|
+
console.log(`
|
|
953
|
+
${header(chalk7.yellow(`Query failures: ${report2.failures.length}`))}`);
|
|
954
|
+
for (const failure of report2.failures) {
|
|
955
|
+
console.log(` ${icons.fail} ${failure.databaseId} ${dimText(failure.error)}`);
|
|
956
|
+
}
|
|
957
|
+
}
|
|
958
|
+
if (report2.complete) {
|
|
959
|
+
console.log(`
|
|
960
|
+
${chalk7.green("All database rows present in vault")} ${icons.success}`);
|
|
961
|
+
}
|
|
962
|
+
}
|
|
963
|
+
function printPageSection(report2) {
|
|
964
|
+
console.log(`
|
|
965
|
+
${header(" Page Completeness")}`);
|
|
966
|
+
console.log(` ${separator(50)}`);
|
|
967
|
+
console.log(` ${dimText("Remote pages:")} ${report2.remotePages}`);
|
|
968
|
+
console.log(` ${dimText("Vault pages:")} ${report2.vaultPages}`);
|
|
969
|
+
if (report2.error) {
|
|
970
|
+
console.log(`
|
|
971
|
+
${icons.fail} ${chalk7.yellow("Page enumeration failed")}`);
|
|
972
|
+
console.log(` ${dimText(report2.error)}`);
|
|
973
|
+
return;
|
|
974
|
+
}
|
|
975
|
+
if (report2.missingIds.length > 0) {
|
|
976
|
+
console.log(
|
|
977
|
+
`
|
|
978
|
+
${header(chalk7.red(`Pages missing from vault: ${report2.missingIds.length}`))}`
|
|
979
|
+
);
|
|
980
|
+
printIdSample("missing", report2.missingIds, chalk7.red);
|
|
981
|
+
}
|
|
982
|
+
if (report2.localOnlyIds.length > 0) {
|
|
983
|
+
console.log(
|
|
984
|
+
`
|
|
985
|
+
${dimText(`local-only pages: ${report2.localOnlyIds.length} (informational)`)}`
|
|
986
|
+
);
|
|
987
|
+
printIdSample("local-only", report2.localOnlyIds, dimText);
|
|
988
|
+
}
|
|
989
|
+
if (report2.complete) {
|
|
990
|
+
console.log(`
|
|
991
|
+
${chalk7.green("All remote pages present in vault")} ${icons.success}`);
|
|
992
|
+
}
|
|
993
|
+
}
|
|
994
|
+
function printReport(report2) {
|
|
995
|
+
printDatabaseSection(report2.databases);
|
|
996
|
+
if (report2.pages) {
|
|
997
|
+
printPageSection(report2.pages);
|
|
998
|
+
} else {
|
|
999
|
+
console.log(`
|
|
1000
|
+
${dimText("Page completeness: skipped (database mode)")}`);
|
|
1001
|
+
}
|
|
1002
|
+
if (!report2.complete) {
|
|
1003
|
+
console.log(
|
|
1004
|
+
`
|
|
1005
|
+
${dimText("Run")} ${chalk7.cyan("nobsi pull")} ${dimText("to fetch what is missing")}`
|
|
1006
|
+
);
|
|
1007
|
+
}
|
|
1008
|
+
}
|
|
1009
|
+
var verifyCommand = new Command10("verify").description("\uC644\uACB0\uC131 \uAC80\uC99D \u2014 \uC6D0\uACA9 DB \uD589\xB7\uD398\uC774\uC9C0\uAC00 \uBCFC\uD2B8\uC5D0 \uBE60\uC9D0\uC5C6\uC774 \uC788\uB294\uC9C0 \uB300\uC870 (API \uD638\uCD9C)").option("--json", "\uACB0\uACFC\uB97C JSON \uC73C\uB85C \uCD9C\uB825").action(async (opts) => {
|
|
1010
|
+
const cwd = process.cwd();
|
|
1011
|
+
const configManager = new ConfigManager10(cwd);
|
|
1012
|
+
const config = await configManager.load();
|
|
1013
|
+
const stateDb = StateDB9.open(configManager.dbPath);
|
|
1014
|
+
try {
|
|
1015
|
+
const client = NotionClient10.fromConfig(config);
|
|
1016
|
+
const vaultFs = new NodeVaultFS9(cwd, config.paths);
|
|
1017
|
+
const orchestrator = new SyncOrchestrator9(config, stateDb, client, vaultFs);
|
|
1018
|
+
const report2 = await orchestrator.verifyCompleteness();
|
|
1019
|
+
if (opts.json) {
|
|
1020
|
+
console.log(JSON.stringify(report2, null, 2));
|
|
1021
|
+
} else {
|
|
1022
|
+
printReport(report2);
|
|
1023
|
+
}
|
|
1024
|
+
if (!report2.complete) process.exitCode = 1;
|
|
1025
|
+
} finally {
|
|
1026
|
+
stateDb.close();
|
|
1027
|
+
}
|
|
1028
|
+
});
|
|
1029
|
+
|
|
1030
|
+
// src/utils/exit.ts
|
|
1031
|
+
var FORCED_EXIT_DELAY_MS = 100;
|
|
1032
|
+
function resolveExitCode(exitCode) {
|
|
1033
|
+
if (typeof exitCode === "number") return exitCode;
|
|
1034
|
+
if (typeof exitCode === "string") {
|
|
1035
|
+
const parsed = Number(exitCode);
|
|
1036
|
+
return Number.isFinite(parsed) ? parsed : 0;
|
|
1037
|
+
}
|
|
1038
|
+
return 0;
|
|
1039
|
+
}
|
|
1040
|
+
function scheduleForcedExit(proc, schedule = setTimeout) {
|
|
1041
|
+
schedule(() => proc.exit(resolveExitCode(proc.exitCode)), FORCED_EXIT_DELAY_MS);
|
|
1042
|
+
}
|
|
1043
|
+
|
|
806
1044
|
// src/index.ts
|
|
807
1045
|
var requireJson = createRequire(import.meta.url);
|
|
808
1046
|
var { version } = requireJson("../package.json");
|
|
809
|
-
var program = new
|
|
1047
|
+
var program = new Command11();
|
|
810
1048
|
program.name("im-nobsidian").description("Im-Nobsidian: Obsidian \u2194 Notion \uC591\uBC29\uD5A5 \uB3D9\uAE30\uD654 CLI").version(version).option("--verbose", "\uC0C1\uC138 \uB85C\uADF8 \uCD9C\uB825").option("--quiet", "\uCD5C\uC18C \uCD9C\uB825");
|
|
811
1049
|
program.addCommand(initCommand);
|
|
812
1050
|
program.addCommand(pushCommand);
|
|
@@ -817,7 +1055,8 @@ program.addCommand(diffCommand);
|
|
|
817
1055
|
program.addCommand(fetchCommand);
|
|
818
1056
|
program.addCommand(resolveCommand);
|
|
819
1057
|
program.addCommand(watchCommand);
|
|
1058
|
+
program.addCommand(verifyCommand);
|
|
820
1059
|
program.parseAsync().then(() => {
|
|
821
|
-
|
|
1060
|
+
scheduleForcedExit(process);
|
|
822
1061
|
});
|
|
823
1062
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/commands/init.ts","../src/utils/format.ts","../src/commands/push.ts","../src/commands/pull.ts","../src/commands/sync.ts","../src/commands/status.ts","../src/commands/diff.ts","../src/commands/fetch.ts","../src/commands/resolve.ts","../src/commands/watch.ts"],"sourcesContent":["import { createRequire } from \"node:module\";\nimport { Command } from \"commander\";\nimport { initCommand } from \"./commands/init.js\";\nimport { pushCommand } from \"./commands/push.js\";\nimport { pullCommand } from \"./commands/pull.js\";\nimport { syncCommand } from \"./commands/sync.js\";\nimport { statusCommand } from \"./commands/status.js\";\nimport { diffCommand } from \"./commands/diff.js\";\nimport { fetchCommand } from \"./commands/fetch.js\";\nimport { resolveCommand } from \"./commands/resolve.js\";\nimport { watchCommand } from \"./commands/watch.js\";\n\nconst requireJson = createRequire(import.meta.url);\nconst { version } = requireJson(\"../package.json\") as { version: string };\n\nconst program = new Command();\n\nprogram\n .name(\"im-nobsidian\")\n .description(\"Im-Nobsidian: Obsidian ↔ Notion 양방향 동기화 CLI\")\n .version(version)\n .option(\"--verbose\", \"상세 로그 출력\")\n .option(\"--quiet\", \"최소 출력\");\n\nprogram.addCommand(initCommand);\nprogram.addCommand(pushCommand);\nprogram.addCommand(pullCommand);\nprogram.addCommand(syncCommand);\nprogram.addCommand(statusCommand);\nprogram.addCommand(diffCommand);\nprogram.addCommand(fetchCommand);\nprogram.addCommand(resolveCommand);\nprogram.addCommand(watchCommand);\n\nprogram.parseAsync().then(() => {\n setTimeout(() => process.exit(0), 100);\n});\n","import { Command } from \"commander\";\nimport { ConfigManager, NotionClient } from \"@im-nobsidian/core\";\nimport { input, confirm } from \"@inquirer/prompts\";\nimport ora from \"ora\";\nimport chalk from \"chalk\";\nimport { header, separator, icons, dimText } from \"../utils/format.js\";\n\nexport const initCommand = new Command(\"init\")\n .description(\"Im-Nobsidian 초기 설정\")\n .option(\"--token <token>\", \"Notion Integration Token\")\n .option(\"--root-page-id <id>\", \"루트 페이지 ID\")\n .option(\"--non-interactive\", \"비대화형 모드\")\n .action(async (options: { token?: string; rootPageId?: string; nonInteractive?: boolean }) => {\n const cwd = process.cwd();\n const configManager = new ConfigManager(cwd);\n\n console.log(`\\n${header(chalk.cyan(\" Im-Nobsidian Setup\"))}`);\n console.log(` ${separator()}`);\n\n if (options.nonInteractive) {\n if (!options.token || !options.rootPageId) {\n console.error(\"비대화형 모드에서는 --token과 --root-page-id가 필수입니다.\");\n process.exitCode = 1;\n return;\n }\n\n if (!options.token.startsWith(\"ntn_\")) {\n console.error(\"토큰은 ntn_으로 시작해야 합니다.\");\n process.exitCode = 1;\n return;\n }\n\n const spinner = ora(\"Notion 연결 확인 중...\").start();\n try {\n const client = new NotionClient({ token: options.token });\n await client.search({ pageSize: 1, filter: { property: \"object\", value: \"page\" } });\n spinner.stop();\n console.log(` ${icons.success} Token validated`);\n\n await configManager.init({ token: options.token, rootPageId: options.rootPageId });\n console.log(`\\n ${icons.success} Config saved to ${dimText(configManager.configPath)}`);\n console.log(` ${icons.success} State DB initialized`);\n console.log(\n `\\n ${header(chalk.green(\"Ready!\"))} Run ${chalk.cyan(\"nobsi sync\")} to start syncing.`,\n );\n } catch (error) {\n spinner.fail(\"Notion 연결 실패\");\n console.error(error instanceof Error ? error.message : error);\n process.exitCode = 1;\n }\n return;\n }\n\n const isInit = await configManager.isInitialized();\n if (isInit) {\n const overwrite = await confirm({\n message: \"이미 초기화되어 있습니다. 다시 설정할까요?\",\n default: false,\n });\n if (!overwrite) return;\n }\n\n const token = await input({\n message: \"Notion Integration Token:\",\n validate: (v) => v.startsWith(\"ntn_\") || \"ntn_으로 시작하는 토큰을 입력하세요\",\n });\n\n const spinner = ora(\"Notion 연결 확인 중...\").start();\n\n try {\n const client = new NotionClient({ token });\n const result = await client.search({\n pageSize: 10,\n filter: { property: \"object\", value: \"page\" },\n });\n spinner.stop();\n console.log(` ${icons.success} Token validated`);\n\n if (result.results.length === 0) {\n console.log(\n `\\n ${chalk.yellow(\"⚠\")} 접근 가능한 페이지가 없습니다. Integration에 페이지를 공유해주세요.`,\n );\n return;\n }\n\n console.log(`\\n Select root page:`);\n for (const [i, page] of result.results.entries()) {\n const title = extractPageTitle(page);\n console.log(` ${dimText(`${i + 1}.`)} ${title}`);\n }\n\n const rootPageId = await input({\n message: \"루트 페이지 ID (위에서 선택하거나 직접 입력):\",\n default: result.results[0]?.id,\n });\n\n await configManager.init({ token, rootPageId });\n console.log(`\\n ${icons.success} Config saved to ${dimText(configManager.configPath)}`);\n console.log(` ${icons.success} State DB initialized`);\n console.log(\n `\\n ${header(chalk.green(\"Ready!\"))} Run ${chalk.cyan(\"nobsi sync\")} to start syncing.`,\n );\n } catch (error) {\n spinner.fail(\"Notion 연결 실패\");\n console.error(error instanceof Error ? error.message : error);\n process.exitCode = 1;\n }\n });\n\nfunction extractPageTitle(page: Record<string, unknown>): string {\n const properties = page[\"properties\"] as Record<string, unknown> | undefined;\n if (!properties) return \"(제목 없음)\";\n\n for (const prop of Object.values(properties)) {\n const p = prop as { type?: string; title?: Array<{ plain_text?: string }> };\n if (p.type === \"title\" && p.title?.[0]?.plain_text) {\n return p.title[0].plain_text;\n }\n }\n return \"(제목 없음)\";\n}\n","import chalk from \"chalk\";\n\nexport const icons = {\n create: chalk.green(\"+\"),\n update: chalk.yellow(\"~\"),\n delete: chalk.red(\"-\"),\n success: chalk.green(\"✓\"),\n fail: chalk.red(\"✕\"),\n conflict: chalk.magenta(\"!\"),\n synced: chalk.green(\"●\"),\n modified: chalk.yellow(\"●\"),\n newFile: chalk.cyan(\"●\"),\n conflictDot: chalk.magenta(\"●\"),\n} as const;\n\nexport function header(text: string): string {\n return chalk.bold(text);\n}\n\nexport function separator(width = 35): string {\n return chalk.dim(\"━\".repeat(width));\n}\n\nexport function operationIcon(op: \"create\" | \"update\" | \"delete\"): string {\n return icons[op];\n}\n\nexport function operationLabel(op: \"create\" | \"update\" | \"delete\"): string {\n switch (op) {\n case \"create\":\n return chalk.green(\"created\");\n case \"update\":\n return chalk.yellow(\"updated\");\n case \"delete\":\n return chalk.red(\"deleted\");\n }\n}\n\nexport function summary(created: number, updated: number, deleted: number): string {\n const parts: string[] = [];\n if (created > 0) parts.push(chalk.green(`${created} created`));\n if (updated > 0) parts.push(chalk.yellow(`${updated} updated`));\n if (deleted > 0) parts.push(chalk.red(`${deleted} deleted`));\n if (parts.length === 0) parts.push(chalk.dim(\"no changes\"));\n return parts.join(\" \");\n}\n\nexport function duration(ms: number): string {\n return chalk.dim(`Done in ${(ms / 1000).toFixed(1)}s`);\n}\n\nexport function progress(current: number, total: number): string {\n return chalk.dim(`[${current}/${total}]`);\n}\n\nexport function dimText(text: string): string {\n return chalk.dim(text);\n}\n\nexport function failedItem(path: string, error: string): void {\n console.log(` ${icons.fail} ${path}: ${chalk.red(error)}`);\n}\n","import { Command } from \"commander\";\nimport {\n ConfigManager,\n StateDB,\n NotionClient,\n SyncOrchestrator,\n NodeVaultFS,\n} from \"@im-nobsidian/core\";\nimport chalk from \"chalk\";\nimport {\n header,\n operationIcon,\n operationLabel,\n progress,\n summary,\n duration,\n failedItem,\n dimText,\n} from \"../utils/format.js\";\n\nexport const pushCommand = new Command(\"push\")\n .description(\"로컬 변경사항을 Notion에 반영\")\n .option(\"--dry-run\", \"실제 반영 없이 변경사항만 표시\")\n .option(\"-p, --path <paths...>\", \"특정 경로만 push\")\n .action(async (options) => {\n const cwd = process.cwd();\n const configManager = new ConfigManager(cwd);\n const config = await configManager.load();\n const stateDb = StateDB.open(configManager.dbPath);\n\n try {\n const client = NotionClient.fromConfig(config);\n const vaultFs = new NodeVaultFS(cwd, config.paths);\n const orchestrator = new SyncOrchestrator(config, stateDb, client, vaultFs);\n\n console.log(`\\n${header(\" Pushing to Notion...\")}`);\n if (options.dryRun) console.log(dimText(\" (dry-run mode)\"));\n console.log(\"\");\n\n const result = await orchestrator.push({\n dryRun: options.dryRun,\n paths: options.path,\n onProgress: (current, total, item) => {\n const icon = operationIcon(item.operation);\n const label = operationLabel(item.operation);\n const prog = progress(current, total);\n console.log(` ${icon} ${item.path} ${prog} ${label}`);\n },\n });\n\n console.log(`\\n ${header(chalk.green(\"Push complete\"))}`);\n console.log(\n ` ${summary(result.created, result.updated, result.deleted)} ${dimText(`${result.failed.length} failed`)}`,\n );\n console.log(` ${duration(result.duration)}`);\n\n if (result.failed.length > 0) {\n console.log(\"\");\n for (const f of result.failed) {\n failedItem(f.path, f.error);\n }\n }\n } finally {\n stateDb.close();\n }\n });\n","import { Command } from \"commander\";\nimport {\n ConfigManager,\n StateDB,\n NotionClient,\n SyncOrchestrator,\n NodeVaultFS,\n} from \"@im-nobsidian/core\";\nimport chalk from \"chalk\";\nimport {\n header,\n operationIcon,\n operationLabel,\n progress,\n summary,\n duration,\n icons,\n failedItem,\n dimText,\n} from \"../utils/format.js\";\n\nexport const pullCommand = new Command(\"pull\")\n .description(\"Notion 변경사항을 로컬에 반영\")\n .option(\"--dry-run\", \"실제 반영 없이 변경사항만 표시\")\n .option(\"-p, --path <paths...>\", \"특정 경로만 pull\")\n .option(\n \"--force\",\n \"증분 감지를 건너뛰고 전체 스캔 (search 인덱싱 지연으로 누락된 신규 페이지·DB 복구)\",\n )\n .action(async (options) => {\n const cwd = process.cwd();\n const configManager = new ConfigManager(cwd);\n const config = await configManager.load();\n const stateDb = StateDB.open(configManager.dbPath);\n\n try {\n const client = NotionClient.fromConfig(config);\n const vaultFs = new NodeVaultFS(cwd, config.paths);\n const orchestrator = new SyncOrchestrator(config, stateDb, client, vaultFs);\n\n console.log(`\\n${header(\" Pulling from Notion...\")}`);\n if (options.dryRun) console.log(dimText(\" (dry-run mode)\"));\n console.log(\"\");\n\n const result = await orchestrator.pull({\n dryRun: options.dryRun,\n paths: options.path,\n force: options.force,\n onProgress: (current, total, item) => {\n const icon = operationIcon(item.operation);\n const label = operationLabel(item.operation);\n const prog = progress(current, total);\n console.log(` ${icon} ${item.path} ${prog} ${label}`);\n },\n });\n\n if (result.imageCount > 0) {\n console.log(\n `\\n ${icons.success} ${result.imageCount} images → ${dimText(\"attachments/\")}`,\n );\n }\n if (result.fileCount > 0) {\n console.log(` ${icons.success} ${result.fileCount} files → ${dimText(\"attachments/\")}`);\n }\n if (result.linkCount > 0) {\n console.log(` ${icons.success} ${result.linkCount} links resolved`);\n }\n\n console.log(`\\n ${header(chalk.green(\"Pull complete\"))}`);\n console.log(\n ` ${summary(result.created, result.updated, result.deleted)} ${dimText(`${result.failed.length} failed`)}`,\n );\n console.log(` ${duration(result.duration)}`);\n\n if (result.conflicts.length > 0) {\n console.log(\n `\\n ${icons.conflict} ${chalk.magenta(`${result.conflicts.length} conflicts`)} — run ${chalk.cyan(\"nobsi resolve\")}`,\n );\n }\n\n if (result.failed.length > 0) {\n console.log(\"\");\n for (const f of result.failed) {\n failedItem(f.path, f.error);\n }\n }\n } finally {\n stateDb.close();\n }\n });\n","import { Command } from \"commander\";\nimport {\n ConfigManager,\n StateDB,\n NotionClient,\n SyncOrchestrator,\n NodeVaultFS,\n} from \"@im-nobsidian/core\";\nimport chalk from \"chalk\";\nimport {\n header,\n separator,\n operationIcon,\n operationLabel,\n summary,\n duration,\n icons,\n dimText,\n} from \"../utils/format.js\";\n\nexport const syncCommand = new Command(\"sync\")\n .description(\"양방향 동기화 (Pull → Push)\")\n .option(\"--dry-run\", \"실제 반영 없이 변경사항만 표시\")\n .action(async (options) => {\n const cwd = process.cwd();\n const configManager = new ConfigManager(cwd);\n const config = await configManager.load();\n const stateDb = StateDB.open(configManager.dbPath);\n\n try {\n const client = NotionClient.fromConfig(config);\n const vaultFs = new NodeVaultFS(cwd, config.paths);\n const orchestrator = new SyncOrchestrator(config, stateDb, client, vaultFs);\n\n console.log(`\\n${header(\" Bidirectional Sync\")}`);\n console.log(` ${separator()}`);\n if (options.dryRun) console.log(dimText(\" (dry-run mode)\"));\n\n // Pull phase\n console.log(`\\n ${header(chalk.blue(\"▼ Pull\"))} ${dimText(\"(Notion → Obsidian)\")}`);\n\n const pullResult = await orchestrator.pull({\n dryRun: options.dryRun,\n onProgress: (_current, _total, item) => {\n const icon = operationIcon(item.operation);\n const label = operationLabel(item.operation);\n console.log(` ${icon} ${item.path} ${label}`);\n },\n });\n\n if (pullResult.imageCount > 0) {\n console.log(` ${icons.success} ${pullResult.imageCount} images downloaded`);\n }\n if (pullResult.linkCount > 0) {\n console.log(` ${icons.success} ${pullResult.linkCount} links resolved`);\n }\n\n // Push phase\n console.log(`\\n ${header(chalk.magenta(\"▲ Push\"))} ${dimText(\"(Obsidian → Notion)\")}`);\n\n const pushResult = await orchestrator.push({\n dryRun: options.dryRun,\n onProgress: (_current, _total, item) => {\n const icon = operationIcon(item.operation);\n const label = operationLabel(item.operation);\n console.log(` ${icon} ${item.path} ${label}`);\n },\n });\n\n // Summary\n const totalDuration = pullResult.duration + pushResult.duration;\n console.log(`\\n ${header(chalk.green(\"Sync complete\"))}`);\n console.log(\n ` ${chalk.blue(\"Pull:\")} ${summary(pullResult.created, pullResult.updated, pullResult.deleted)}`,\n );\n console.log(\n ` ${chalk.magenta(\"Push:\")} ${summary(pushResult.created, pushResult.updated, pushResult.deleted)}`,\n );\n console.log(` ${duration(totalDuration)}`);\n\n if (pullResult.conflicts.length > 0) {\n console.log(\n `\\n ${icons.conflict} ${chalk.magenta(`${pullResult.conflicts.length} conflicts`)} — run ${chalk.cyan(\"nobsi resolve\")}`,\n );\n }\n } finally {\n stateDb.close();\n }\n });\n","import { Command } from \"commander\";\nimport {\n ConfigManager,\n StateDB,\n NotionClient,\n SyncOrchestrator,\n NodeVaultFS,\n} from \"@im-nobsidian/core\";\nimport chalk from \"chalk\";\nimport { header, separator, icons, dimText } from \"../utils/format.js\";\n\nexport const statusCommand = new Command(\"status\")\n .description(\"동기화 상태 확인 (--full: 원격 변경 포함)\")\n .option(\"--full\", \"Notion 원격 변경까지 양방향 확인 (API 호출, 느림)\")\n .action(async (opts: { full?: boolean }) => {\n const cwd = process.cwd();\n const configManager = new ConfigManager(cwd);\n const config = await configManager.load();\n const stateDb = StateDB.open(configManager.dbPath);\n\n try {\n const client = NotionClient.fromConfig(config);\n const vaultFs = new NodeVaultFS(cwd, config.paths);\n const orchestrator = new SyncOrchestrator(config, stateDb, client, vaultFs);\n\n const status = opts.full ? await orchestrator.status() : await orchestrator.statusLocal();\n\n console.log(`\\n${header(\" Sync Status\")}`);\n console.log(` ${separator(50)}`);\n\n const rootId = config.notion.rootPageId;\n const direction = config.sync.direction === \"both\" ? \"bidirectional\" : config.sync.direction;\n console.log(` ${dimText(\"Root page:\")} ${rootId.slice(0, 8)}...`);\n console.log(` ${dimText(\"Direction:\")} ${direction}`);\n if (status.lastSyncAt) {\n const date = new Date(status.lastSyncAt);\n const iso =\n date.getFullYear() +\n \"-\" +\n String(date.getMonth() + 1).padStart(2, \"0\") +\n \"-\" +\n String(date.getDate()).padStart(2, \"0\") +\n \" \" +\n String(date.getHours()).padStart(2, \"0\") +\n \":\" +\n String(date.getMinutes()).padStart(2, \"0\") +\n \":\" +\n String(date.getSeconds()).padStart(2, \"0\");\n console.log(` ${dimText(\"Last sync:\")} ${iso}`);\n } else {\n console.log(` ${dimText(\"Last sync:\")} ${chalk.yellow(\"never\")}`);\n }\n\n // Tracked files summary\n const allRecords = stateDb.getAll();\n const total = allRecords.length;\n const synced = allRecords.filter((r) => r.status === \"synced\").length;\n const conflictCount = status.conflictRecords.length;\n const createdChanges = status.localChanges.filter((c) => c.type === \"created\");\n const modifiedChanges = status.localChanges.filter((c) => c.type === \"modified\");\n const remoteCreated = status.remoteChanges.filter((c) => c.type === \"created\");\n const remoteModified = status.remoteChanges.filter((c) => c.type === \"modified\");\n const remoteDeleted = status.remoteChanges.filter((c) => c.type === \"deleted\");\n\n console.log(`\\n ${header(`Tracked files: ${total}`)}`);\n console.log(` ${icons.synced} ${chalk.green(\"synced\")} ${synced}`);\n if (modifiedChanges.length > 0) {\n console.log(` ${icons.modified} ${chalk.yellow(\"modified\")} ${modifiedChanges.length}`);\n }\n if (createdChanges.length > 0) {\n console.log(` ${icons.newFile} ${chalk.cyan(\"new\")} ${createdChanges.length}`);\n }\n if (conflictCount > 0) {\n console.log(` ${icons.conflictDot} ${chalk.magenta(\"conflict\")} ${conflictCount}`);\n }\n\n // Modified files\n if (modifiedChanges.length > 0) {\n console.log(`\\n ${header(\"Modified files:\")}`);\n for (const c of modifiedChanges.slice(0, 10)) {\n console.log(\n ` ${chalk.yellow(\"~\")} ${c.path.padEnd(30)} ${dimText(\"(local changed)\")}`,\n );\n }\n if (modifiedChanges.length > 10) {\n console.log(dimText(` ... and ${modifiedChanges.length - 10} more`));\n }\n }\n\n // New files\n if (createdChanges.length > 0) {\n console.log(`\\n ${header(\"New files:\")}`);\n for (const c of createdChanges.slice(0, 10)) {\n console.log(` ${chalk.cyan(\"+\")} ${c.path.padEnd(30)} ${dimText(\"(untracked)\")}`);\n }\n if (createdChanges.length > 10) {\n console.log(dimText(` ... and ${createdChanges.length - 10} more`));\n }\n }\n\n // Remote changes (Notion-side)\n if (status.remoteChanges.length > 0) {\n console.log(`\\n ${header(`Remote changes (Notion): ${status.remoteChanges.length}`)}`);\n if (remoteCreated.length > 0) {\n console.log(` ${chalk.cyan(\"+\")} ${chalk.cyan(\"new\")} ${remoteCreated.length}`);\n }\n if (remoteModified.length > 0) {\n console.log(\n ` ${chalk.yellow(\"~\")} ${chalk.yellow(\"modified\")} ${remoteModified.length}`,\n );\n }\n if (remoteDeleted.length > 0) {\n console.log(` ${chalk.red(\"-\")} ${chalk.red(\"deleted\")} ${remoteDeleted.length}`);\n }\n for (const c of status.remoteChanges.slice(0, 10)) {\n const icon =\n c.type === \"created\"\n ? chalk.cyan(\"+\")\n : c.type === \"modified\"\n ? chalk.yellow(\"~\")\n : chalk.red(\"-\");\n console.log(` ${icon} ${c.pageId.slice(0, 8)}... ${dimText(`(${c.type})`)}`);\n }\n if (status.remoteChanges.length > 10) {\n console.log(dimText(` ... and ${status.remoteChanges.length - 10} more`));\n }\n }\n\n // Conflicts\n if (conflictCount > 0) {\n console.log(`\\n ${header(chalk.magenta(\"Conflicts:\"))}`);\n for (const r of status.conflictRecords) {\n console.log(\n ` ${icons.conflict} ${r.obsidianPath.padEnd(30)} ${dimText(\"(both sides changed)\")}`,\n );\n }\n }\n\n // Helpful commands\n const hasChanges =\n status.localChanges.length > 0 || status.remoteChanges.length > 0 || conflictCount > 0;\n if (hasChanges) {\n console.log(\"\");\n if (conflictCount > 0) {\n console.log(\n ` ${dimText(\"Run\")} ${chalk.cyan(\"nobsi resolve\")} ${dimText(\"to resolve conflicts\")}`,\n );\n }\n console.log(\n ` ${dimText(\"Run\")} ${chalk.cyan(\"nobsi sync\")} ${dimText(\"to push/pull changes\")}`,\n );\n } else {\n console.log(`\\n ${chalk.green(\"Everything up to date\")} ✓`);\n }\n if (!opts.full) {\n console.log(\n `\\n ${dimText(\"Run\")} ${chalk.cyan(\"nobsi status --full\")} ${dimText(\"to check Notion remote changes\")}`,\n );\n }\n } finally {\n stateDb.close();\n }\n });\n","import { Command } from \"commander\";\nimport {\n ConfigManager,\n StateDB,\n NotionClient,\n SyncOrchestrator,\n NodeVaultFS,\n} from \"@im-nobsidian/core\";\nimport { createTwoFilesPatch } from \"diff\";\n\nexport const diffCommand = new Command(\"diff\")\n .description(\"로컬과 Notion 간의 차이 표시\")\n .argument(\"[path]\", \"특정 파일 경로 (생략 시 변경된 모든 파일)\")\n .option(\"--color\", \"컬러 출력\", true)\n .action(async (targetPath: string | undefined, options) => {\n const cwd = process.cwd();\n const configManager = new ConfigManager(cwd);\n const config = await configManager.load();\n const stateDb = StateDB.open(configManager.dbPath);\n\n try {\n const client = NotionClient.fromConfig(config);\n const vaultFs = new NodeVaultFS(cwd, config.paths);\n const orchestrator = new SyncOrchestrator(config, stateDb, client, vaultFs);\n\n const status = await orchestrator.statusLocal();\n\n const changes = targetPath\n ? status.localChanges.filter((c) => c.path === targetPath || c.path.startsWith(targetPath))\n : status.localChanges;\n\n if (changes.length === 0) {\n console.log(\"변경사항 없음\");\n return;\n }\n\n for (const change of changes) {\n const record = stateDb.getByPath(change.path);\n\n if (change.type === \"created\") {\n const content = await vaultFs.readFile(change.path);\n const patch = createTwoFilesPatch(\"/dev/null\", `b/${change.path}`, \"\", content);\n printPatch(patch, options.color, \"/dev/null\", `b/${change.path}`);\n } else if (change.type === \"deleted\") {\n if (record?.baseSnapshot) {\n const oldContent = record.baseSnapshot.toString(\"utf-8\");\n const patch = createTwoFilesPatch(`a/${change.path}`, \"/dev/null\", oldContent, \"\");\n printPatch(patch, options.color, `a/${change.path}`, \"/dev/null\");\n } else {\n console.log(`--- a/${change.path}`);\n console.log(`+++ /dev/null`);\n console.log(`(이전 스냅샷 없음)`);\n }\n } else if (change.type === \"modified\" && record?.baseSnapshot) {\n const currentContent = await vaultFs.readFile(change.path);\n const baseContent = record.baseSnapshot.toString(\"utf-8\");\n const patch = createTwoFilesPatch(\n `a/${change.path}`,\n `b/${change.path}`,\n baseContent,\n currentContent,\n );\n printPatch(patch, options.color, `a/${change.path}`, `b/${change.path}`);\n }\n\n console.log(\"\");\n }\n } finally {\n stateDb.close();\n }\n });\n\nfunction printPatch(patch: string, color: boolean, oldPath: string, newPath: string): void {\n for (const line of patch.split(\"\\n\")) {\n let output = line;\n if (line.startsWith(\"===\")) {\n output = \"===================================================================\";\n } else if (line.startsWith(\"--- \")) {\n output = `--- ${oldPath}`;\n } else if (line.startsWith(\"+++ \")) {\n output = `+++ ${newPath}`;\n }\n\n if (!color) {\n console.log(output);\n continue;\n }\n\n if (output.startsWith(\"+++\") || output.startsWith(\"---\")) {\n console.log(`\\x1b[1m${output}\\x1b[0m`);\n } else if (output.startsWith(\"+\")) {\n console.log(`\\x1b[32m${output}\\x1b[0m`);\n } else if (output.startsWith(\"-\")) {\n console.log(`\\x1b[31m${output}\\x1b[0m`);\n } else if (output.startsWith(\"@@\")) {\n console.log(`\\x1b[36m${output}\\x1b[0m`);\n } else {\n console.log(output);\n }\n }\n}\n","import { Command } from \"commander\";\nimport {\n ConfigManager,\n StateDB,\n NotionClient,\n SyncOrchestrator,\n NodeVaultFS,\n} from \"@im-nobsidian/core\";\nimport chalk from \"chalk\";\nimport { header, separator, dimText } from \"../utils/format.js\";\n\nexport const fetchCommand = new Command(\"fetch\")\n .description(\"Notion 리모트 상태 스캔 (삭제 감지 포함)\")\n .action(async () => {\n const cwd = process.cwd();\n const configManager = new ConfigManager(cwd);\n const config = await configManager.load();\n const stateDb = StateDB.open(configManager.dbPath);\n\n try {\n const client = NotionClient.fromConfig(config);\n const vaultFs = new NodeVaultFS(cwd, config.paths);\n const orchestrator = new SyncOrchestrator(config, stateDb, client, vaultFs);\n\n console.log(dimText(\" Scanning remote pages...\"));\n\n const result = await orchestrator.fetch();\n\n console.log(`\\n${header(\" Remote Scan\")}`);\n console.log(` ${separator(40)}`);\n\n if (result.newPages > 0) {\n console.log(` ${chalk.cyan(\"+\")} New pages: ${result.newPages}`);\n }\n if (result.modifiedPages > 0) {\n console.log(` ${chalk.yellow(\"~\")} Modified pages: ${result.modifiedPages}`);\n }\n if (result.deletedPages > 0) {\n console.log(` ${chalk.red(\"-\")} Deleted pages: ${result.deletedPages}`);\n }\n\n const total = result.newPages + result.modifiedPages + result.deletedPages;\n if (total === 0) {\n console.log(` ${chalk.green(\"Remote is up to date\")} ✓`);\n } else {\n console.log(\n `\\n ${dimText(\"Run\")} ${chalk.cyan(\"nobsi pull\")} ${dimText(\"to apply remote changes\")}`,\n );\n }\n\n console.log(dimText(`\\n Completed in ${(result.duration / 1000).toFixed(1)}s`));\n } finally {\n stateDb.close();\n }\n });\n","import { Command } from \"commander\";\nimport { select, confirm } from \"@inquirer/prompts\";\nimport {\n ConfigManager,\n StateDB,\n NotionClient,\n SyncOrchestrator,\n NodeVaultFS,\n} from \"@im-nobsidian/core\";\nimport type { Conflict, ResolutionChoice } from \"@im-nobsidian/core\";\n\nexport const resolveCommand = new Command(\"resolve\")\n .description(\"충돌을 해결합니다\")\n .option(\"-s, --strategy <strategy>\", \"자동 해결 전략 (local-first, remote-first, duplicate)\")\n .action(async (options: { strategy?: string }) => {\n const cwd = process.cwd();\n const configManager = new ConfigManager(cwd);\n\n try {\n const config = await configManager.load();\n const stateDb = StateDB.open(configManager.dbPath);\n const client = NotionClient.fromConfig(config);\n const vaultFs = new NodeVaultFS(cwd, config.paths);\n\n const orchestrator = new SyncOrchestrator(config, stateDb, client, vaultFs);\n\n await orchestrator.status();\n const conflictRecords = stateDb.getByStatus(\"conflict\");\n\n if (conflictRecords.length === 0) {\n console.log(\"충돌이 없습니다.\");\n stateDb.close();\n return;\n }\n\n console.log(`\\n충돌 ${conflictRecords.length}건 발견\\n`);\n\n if (options.strategy) {\n const strategy = options.strategy as \"local-first\" | \"remote-first\" | \"duplicate\";\n const shouldProceed = await confirm({\n message: `${conflictRecords.length}건의 충돌을 '${strategy}' 전략으로 일괄 해결할까요?`,\n });\n\n if (!shouldProceed) {\n stateDb.close();\n return;\n }\n\n const pullResult = await orchestrator.pull();\n const results = await orchestrator.resolveAllConflicts(pullResult.conflicts, strategy);\n\n for (const result of results) {\n const icon = result.success ? \"\\x1b[32m✓\\x1b[0m\" : \"\\x1b[31m✗\\x1b[0m\";\n console.log(` ${icon} ${result.path} → ${result.choice}`);\n }\n } else {\n const pullResult = await orchestrator.pull();\n\n for (const conflict of pullResult.conflicts) {\n await resolveInteractive(conflict, orchestrator);\n }\n }\n\n stateDb.close();\n console.log(\"\\n충돌 해결 완료\");\n } catch (error) {\n console.error(\"오류:\", error instanceof Error ? error.message : String(error));\n process.exit(1);\n }\n });\n\nasync function resolveInteractive(\n conflict: Conflict,\n orchestrator: SyncOrchestrator,\n): Promise<void> {\n const path = conflict.syncRecord.obsidianPath;\n\n console.log(`\\n${\"─\".repeat(60)}`);\n console.log(`파일: \\x1b[1m${path}\\x1b[0m`);\n console.log(`${\"─\".repeat(60)}`);\n\n const diff = orchestrator.generateConflictDiff(conflict);\n const diffLines = diff.split(\"\\n\");\n for (const line of diffLines) {\n if (line.startsWith(\"- \")) {\n console.log(`\\x1b[31m${line}\\x1b[0m`);\n } else if (line.startsWith(\"+ \")) {\n console.log(`\\x1b[32m${line}\\x1b[0m`);\n } else if (line.startsWith(\"---\") || line.startsWith(\"+++\")) {\n console.log(`\\x1b[1m${line}\\x1b[0m`);\n } else {\n console.log(line);\n }\n }\n\n const choice = await select<ResolutionChoice>({\n message: \"어떻게 해결할까요?\",\n choices: [\n { name: \"로컬 유지 (Obsidian 버전 유지)\", value: \"local\" },\n { name: \"원격 유지 (Notion 버전으로 덮어쓰기)\", value: \"remote\" },\n { name: \"자동 병합 (3-way merge)\", value: \"merge\" },\n { name: \"복제 (원본 유지 + .conflict 파일 생성)\", value: \"duplicate\" },\n ],\n });\n\n const result = await orchestrator.resolveConflict(conflict, choice);\n\n if (result.success) {\n console.log(`\\x1b[32m✓ ${path} → ${choice}로 해결\\x1b[0m`);\n } else if (result.mergeHadConflicts) {\n console.log(`\\x1b[33m⚠ ${path} → 자동 병합 완료 (수동 확인 필요한 충돌 마커 있음)\\x1b[0m`);\n } else {\n console.log(`\\x1b[31m✗ ${path} → 해결 실패\\x1b[0m`);\n }\n}\n","import { Command } from \"commander\";\nimport {\n ConfigManager,\n StateDB,\n NotionClient,\n SyncOrchestrator,\n NodeVaultFS,\n WatchSyncService,\n} from \"@im-nobsidian/core\";\nimport type { SyncResult } from \"@im-nobsidian/core\";\n\nfunction formatResult(result: SyncResult): string {\n const pull = `Pull: +${result.pull.created} ~${result.pull.updated} -${result.pull.deleted}`;\n const push = `Push: +${result.push.created} ~${result.push.updated} -${result.push.deleted}`;\n const parts = [pull, push];\n\n if (result.conflicts.length > 0) {\n parts.push(`충돌 ${result.conflicts.length}건`);\n }\n\n return parts.join(\" | \");\n}\n\nfunction timestamp(): string {\n return new Date().toLocaleTimeString(\"ko-KR\", { hour12: false });\n}\n\nexport const watchCommand = new Command(\"watch\")\n .description(\"파일 변경 감지 + 자동 동기화\")\n .option(\"-d, --debounce <ms>\", \"디바운스 대기 시간(ms)\", \"2000\")\n .option(\"-i, --interval <seconds>\", \"풀 동기화 주기(초, 0이면 비활성)\", \"0\")\n .action(async (options: { debounce: string; interval: string }) => {\n const cwd = process.cwd();\n const configManager = new ConfigManager(cwd);\n const config = await configManager.load();\n const stateDb = StateDB.open(configManager.dbPath);\n\n const client = NotionClient.fromConfig(config);\n const vaultFs = new NodeVaultFS(cwd, config.paths);\n const orchestrator = new SyncOrchestrator(config, stateDb, client, vaultFs);\n\n const debounceMs = parseInt(options.debounce, 10);\n const intervalSec = parseInt(options.interval, 10);\n\n const service = new WatchSyncService(cwd, orchestrator, {\n debounceMs,\n onFileChange: (event, path) => {\n console.log(`[${timestamp()}] ${event}: ${path}`);\n },\n onSyncStart: () => {\n console.log(`[${timestamp()}] 동기화 시작...`);\n },\n onSyncComplete: (result) => {\n console.log(`[${timestamp()}] 동기화 완료 — ${formatResult(result)}`);\n },\n onSyncError: (error) => {\n console.error(`[${timestamp()}] 동기화 실패: ${error.message}`);\n },\n });\n\n let pullTimer: ReturnType<typeof setInterval> | null = null;\n\n const shutdown = async (): Promise<void> => {\n console.log(`\\n[${timestamp()}] 감시 종료 중...`);\n if (pullTimer) clearInterval(pullTimer);\n await service.stop();\n stateDb.close();\n console.log(`[${timestamp()}] 종료 완료`);\n process.exit(0);\n };\n\n process.on(\"SIGINT\", () => void shutdown());\n process.on(\"SIGTERM\", () => void shutdown());\n\n service.start();\n console.log(`[${timestamp()}] 파일 감시 시작 (debounce: ${debounceMs}ms)`);\n\n if (intervalSec > 0) {\n console.log(`[${timestamp()}] 풀 동기화 주기: ${intervalSec}초`);\n pullTimer = setInterval(async () => {\n if (service.isSyncing()) return;\n try {\n console.log(`[${timestamp()}] 주기적 동기화 시작...`);\n const result = await orchestrator.sync();\n console.log(`[${timestamp()}] 주기적 동기화 완료 — ${formatResult(result)}`);\n } catch (error) {\n const msg = error instanceof Error ? error.message : String(error);\n console.error(`[${timestamp()}] 주기적 동기화 실패: ${msg}`);\n }\n }, intervalSec * 1000);\n }\n\n await new Promise(() => {});\n });\n"],"mappings":"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","names":["Command","chalk","chalk","spinner","Command","ConfigManager","NotionClient","chalk","Command","ConfigManager","NotionClient","chalk","Command","ConfigManager","StateDB","NotionClient","SyncOrchestrator","NodeVaultFS","chalk","Command","ConfigManager","StateDB","NotionClient","NodeVaultFS","SyncOrchestrator","chalk","Command","ConfigManager","StateDB","NotionClient","SyncOrchestrator","NodeVaultFS","chalk","Command","ConfigManager","StateDB","NotionClient","NodeVaultFS","SyncOrchestrator","chalk","Command","ConfigManager","StateDB","NotionClient","SyncOrchestrator","NodeVaultFS","chalk","Command","ConfigManager","StateDB","NotionClient","NodeVaultFS","SyncOrchestrator","chalk","Command","ConfigManager","StateDB","NotionClient","SyncOrchestrator","NodeVaultFS","Command","ConfigManager","StateDB","NotionClient","SyncOrchestrator","NodeVaultFS","chalk","Command","ConfigManager","StateDB","NotionClient","NodeVaultFS","SyncOrchestrator","chalk","Command","confirm","ConfigManager","StateDB","NotionClient","SyncOrchestrator","NodeVaultFS","Command","ConfigManager","StateDB","NotionClient","SyncOrchestrator","NodeVaultFS","Command"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/commands/init.ts","../src/utils/format.ts","../src/commands/push.ts","../src/commands/pull.ts","../src/commands/sync.ts","../src/commands/status.ts","../src/commands/diff.ts","../src/commands/fetch.ts","../src/commands/resolve.ts","../src/commands/watch.ts","../src/commands/verify.ts","../src/utils/exit.ts"],"sourcesContent":["import { createRequire } from \"node:module\";\nimport { Command } from \"commander\";\nimport { initCommand } from \"./commands/init.js\";\nimport { pushCommand } from \"./commands/push.js\";\nimport { pullCommand } from \"./commands/pull.js\";\nimport { syncCommand } from \"./commands/sync.js\";\nimport { statusCommand } from \"./commands/status.js\";\nimport { diffCommand } from \"./commands/diff.js\";\nimport { fetchCommand } from \"./commands/fetch.js\";\nimport { resolveCommand } from \"./commands/resolve.js\";\nimport { watchCommand } from \"./commands/watch.js\";\nimport { verifyCommand } from \"./commands/verify.js\";\nimport { scheduleForcedExit } from \"./utils/exit.js\";\n\nconst requireJson = createRequire(import.meta.url);\nconst { version } = requireJson(\"../package.json\") as { version: string };\n\nconst program = new Command();\n\nprogram\n .name(\"im-nobsidian\")\n .description(\"Im-Nobsidian: Obsidian ↔ Notion 양방향 동기화 CLI\")\n .version(version)\n .option(\"--verbose\", \"상세 로그 출력\")\n .option(\"--quiet\", \"최소 출력\");\n\nprogram.addCommand(initCommand);\nprogram.addCommand(pushCommand);\nprogram.addCommand(pullCommand);\nprogram.addCommand(syncCommand);\nprogram.addCommand(statusCommand);\nprogram.addCommand(diffCommand);\nprogram.addCommand(fetchCommand);\nprogram.addCommand(resolveCommand);\nprogram.addCommand(watchCommand);\nprogram.addCommand(verifyCommand);\n\n// 강제 종료하되 명령이 정한 종료 코드를 보존한다 — 근거는 utils/exit.ts 참조(R11-C).\nprogram.parseAsync().then(() => {\n scheduleForcedExit(process);\n});\n","import { Command } from \"commander\";\nimport { ConfigManager, NotionClient } from \"@im-nobsidian/core\";\nimport { input, confirm } from \"@inquirer/prompts\";\nimport ora from \"ora\";\nimport chalk from \"chalk\";\nimport { header, separator, icons, dimText } from \"../utils/format.js\";\n\nexport const initCommand = new Command(\"init\")\n .description(\"Im-Nobsidian 초기 설정\")\n .option(\"--token <token>\", \"Notion Integration Token\")\n .option(\"--root-page-id <id>\", \"루트 페이지 ID\")\n .option(\"--non-interactive\", \"비대화형 모드\")\n .action(async (options: { token?: string; rootPageId?: string; nonInteractive?: boolean }) => {\n const cwd = process.cwd();\n const configManager = new ConfigManager(cwd);\n\n console.log(`\\n${header(chalk.cyan(\" Im-Nobsidian Setup\"))}`);\n console.log(` ${separator()}`);\n\n if (options.nonInteractive) {\n if (!options.token || !options.rootPageId) {\n console.error(\"비대화형 모드에서는 --token과 --root-page-id가 필수입니다.\");\n process.exitCode = 1;\n return;\n }\n\n if (!options.token.startsWith(\"ntn_\")) {\n console.error(\"토큰은 ntn_으로 시작해야 합니다.\");\n process.exitCode = 1;\n return;\n }\n\n const spinner = ora(\"Notion 연결 확인 중...\").start();\n try {\n const client = new NotionClient({ token: options.token });\n await client.search({ pageSize: 1, filter: { property: \"object\", value: \"page\" } });\n spinner.stop();\n console.log(` ${icons.success} Token validated`);\n\n await configManager.init({ token: options.token, rootPageId: options.rootPageId });\n console.log(`\\n ${icons.success} Config saved to ${dimText(configManager.configPath)}`);\n console.log(` ${icons.success} State DB initialized`);\n console.log(\n `\\n ${header(chalk.green(\"Ready!\"))} Run ${chalk.cyan(\"nobsi sync\")} to start syncing.`,\n );\n } catch (error) {\n spinner.fail(\"Notion 연결 실패\");\n console.error(error instanceof Error ? error.message : error);\n process.exitCode = 1;\n }\n return;\n }\n\n const isInit = await configManager.isInitialized();\n if (isInit) {\n const overwrite = await confirm({\n message: \"이미 초기화되어 있습니다. 다시 설정할까요?\",\n default: false,\n });\n if (!overwrite) return;\n }\n\n const token = await input({\n message: \"Notion Integration Token:\",\n validate: (v) => v.startsWith(\"ntn_\") || \"ntn_으로 시작하는 토큰을 입력하세요\",\n });\n\n const spinner = ora(\"Notion 연결 확인 중...\").start();\n\n try {\n const client = new NotionClient({ token });\n const result = await client.search({\n pageSize: 10,\n filter: { property: \"object\", value: \"page\" },\n });\n spinner.stop();\n console.log(` ${icons.success} Token validated`);\n\n if (result.results.length === 0) {\n console.log(\n `\\n ${chalk.yellow(\"⚠\")} 접근 가능한 페이지가 없습니다. Integration에 페이지를 공유해주세요.`,\n );\n return;\n }\n\n console.log(`\\n Select root page:`);\n for (const [i, page] of result.results.entries()) {\n const title = extractPageTitle(page);\n console.log(` ${dimText(`${i + 1}.`)} ${title}`);\n }\n\n const rootPageId = await input({\n message: \"루트 페이지 ID (위에서 선택하거나 직접 입력):\",\n default: result.results[0]?.id,\n });\n\n await configManager.init({ token, rootPageId });\n console.log(`\\n ${icons.success} Config saved to ${dimText(configManager.configPath)}`);\n console.log(` ${icons.success} State DB initialized`);\n console.log(\n `\\n ${header(chalk.green(\"Ready!\"))} Run ${chalk.cyan(\"nobsi sync\")} to start syncing.`,\n );\n } catch (error) {\n spinner.fail(\"Notion 연결 실패\");\n console.error(error instanceof Error ? error.message : error);\n process.exitCode = 1;\n }\n });\n\nfunction extractPageTitle(page: Record<string, unknown>): string {\n const properties = page[\"properties\"] as Record<string, unknown> | undefined;\n if (!properties) return \"(제목 없음)\";\n\n for (const prop of Object.values(properties)) {\n const p = prop as { type?: string; title?: Array<{ plain_text?: string }> };\n if (p.type === \"title\" && p.title?.[0]?.plain_text) {\n return p.title[0].plain_text;\n }\n }\n return \"(제목 없음)\";\n}\n","import chalk from \"chalk\";\n\nexport const icons = {\n create: chalk.green(\"+\"),\n update: chalk.yellow(\"~\"),\n delete: chalk.red(\"-\"),\n success: chalk.green(\"✓\"),\n fail: chalk.red(\"✕\"),\n conflict: chalk.magenta(\"!\"),\n synced: chalk.green(\"●\"),\n modified: chalk.yellow(\"●\"),\n newFile: chalk.cyan(\"●\"),\n conflictDot: chalk.magenta(\"●\"),\n} as const;\n\nexport function header(text: string): string {\n return chalk.bold(text);\n}\n\nexport function separator(width = 35): string {\n return chalk.dim(\"━\".repeat(width));\n}\n\nexport function operationIcon(op: \"create\" | \"update\" | \"delete\"): string {\n return icons[op];\n}\n\nexport function operationLabel(op: \"create\" | \"update\" | \"delete\"): string {\n switch (op) {\n case \"create\":\n return chalk.green(\"created\");\n case \"update\":\n return chalk.yellow(\"updated\");\n case \"delete\":\n return chalk.red(\"deleted\");\n }\n}\n\nexport function summary(created: number, updated: number, deleted: number): string {\n const parts: string[] = [];\n if (created > 0) parts.push(chalk.green(`${created} created`));\n if (updated > 0) parts.push(chalk.yellow(`${updated} updated`));\n if (deleted > 0) parts.push(chalk.red(`${deleted} deleted`));\n if (parts.length === 0) parts.push(chalk.dim(\"no changes\"));\n return parts.join(\" \");\n}\n\n/**\n * 완료 헤더 — 실패가 하나라도 있으면 초록 \"complete\" 로 끝내지 않는다.\n *\n * pull/push/sync 가 모두 결과와 무관하게 초록 `... complete` 를 찍었다. 수십 건이 실패해도\n * 화면 맨 아래는 성공으로 읽혀, 사람은 물론 로그를 훑는 스크립트까지 넘어갔다.\n * 헤더 문구·색을 결과에서 파생시켜 세 명령이 같은 판정을 공유한다.\n */\nexport function completionHeader(label: string, failedCount: number): string {\n return failedCount > 0\n ? header(chalk.yellow(`${label} finished with errors`))\n : header(chalk.green(`${label} complete`));\n}\n\n/** 실패 건수 표기. dim 은 \"없는 셈\" 으로 읽히므로 0 이 아닐 때만 dim 을 벗는다. */\nexport function failedCountText(count: number): string {\n return count > 0 ? chalk.red(`${count} failed`) : chalk.dim(\"0 failed\");\n}\n\nexport function duration(ms: number): string {\n return chalk.dim(`Done in ${(ms / 1000).toFixed(1)}s`);\n}\n\nexport function progress(current: number, total: number): string {\n return chalk.dim(`[${current}/${total}]`);\n}\n\nexport function dimText(text: string): string {\n return chalk.dim(text);\n}\n\nexport function failedItem(path: string, error: string): void {\n console.log(` ${icons.fail} ${path}: ${chalk.red(error)}`);\n}\n","import { Command } from \"commander\";\nimport {\n ConfigManager,\n StateDB,\n NotionClient,\n SyncOrchestrator,\n NodeVaultFS,\n} from \"@im-nobsidian/core\";\nimport {\n header,\n operationIcon,\n operationLabel,\n progress,\n summary,\n duration,\n failedItem,\n dimText,\n completionHeader,\n failedCountText,\n} from \"../utils/format.js\";\n\nexport const pushCommand = new Command(\"push\")\n .description(\"로컬 변경사항을 Notion에 반영\")\n .option(\"--dry-run\", \"실제 반영 없이 변경사항만 표시\")\n .option(\"-p, --path <paths...>\", \"특정 경로만 push\")\n .action(async (options) => {\n const cwd = process.cwd();\n const configManager = new ConfigManager(cwd);\n const config = await configManager.load();\n const stateDb = StateDB.open(configManager.dbPath);\n\n try {\n const client = NotionClient.fromConfig(config);\n const vaultFs = new NodeVaultFS(cwd, config.paths);\n const orchestrator = new SyncOrchestrator(config, stateDb, client, vaultFs);\n\n console.log(`\\n${header(\" Pushing to Notion...\")}`);\n if (options.dryRun) console.log(dimText(\" (dry-run mode)\"));\n console.log(\"\");\n\n const result = await orchestrator.push({\n dryRun: options.dryRun,\n paths: options.path,\n onProgress: (current, total, item) => {\n const icon = operationIcon(item.operation);\n const label = operationLabel(item.operation);\n const prog = progress(current, total);\n console.log(` ${icon} ${item.path} ${prog} ${label}`);\n },\n });\n\n const failedCount = result.failed.length;\n console.log(`\\n ${completionHeader(\"Push\", failedCount)}`);\n console.log(\n ` ${summary(result.created, result.updated, result.deleted)} ${failedCountText(failedCount)}`,\n );\n console.log(` ${duration(result.duration)}`);\n\n if (failedCount > 0) {\n console.log(\"\");\n for (const f of result.failed) {\n failedItem(f.path, f.error);\n }\n // 종료 코드까지 실패로 남긴다 — push 가 반쯤 실패한 걸 자동화가 성공으로 보면 안 된다.\n process.exitCode = 1;\n }\n } finally {\n stateDb.close();\n }\n });\n","import { Command } from \"commander\";\nimport {\n ConfigManager,\n StateDB,\n NotionClient,\n SyncOrchestrator,\n NodeVaultFS,\n} from \"@im-nobsidian/core\";\nimport chalk from \"chalk\";\nimport {\n header,\n operationIcon,\n operationLabel,\n progress,\n summary,\n duration,\n icons,\n failedItem,\n dimText,\n completionHeader,\n failedCountText,\n} from \"../utils/format.js\";\n\nexport const pullCommand = new Command(\"pull\")\n .description(\"Notion 변경사항을 로컬에 반영\")\n .option(\"--dry-run\", \"실제 반영 없이 변경사항만 표시\")\n .option(\"-p, --path <paths...>\", \"특정 경로만 pull\")\n .option(\n \"--force\",\n \"증분 감지를 건너뛰고 전체 스캔 (search 인덱싱 지연으로 누락된 신규 페이지·DB 복구)\",\n )\n .action(async (options) => {\n const cwd = process.cwd();\n const configManager = new ConfigManager(cwd);\n const config = await configManager.load();\n const stateDb = StateDB.open(configManager.dbPath);\n\n try {\n const client = NotionClient.fromConfig(config);\n const vaultFs = new NodeVaultFS(cwd, config.paths);\n const orchestrator = new SyncOrchestrator(config, stateDb, client, vaultFs);\n\n console.log(`\\n${header(\" Pulling from Notion...\")}`);\n if (options.dryRun) console.log(dimText(\" (dry-run mode)\"));\n console.log(\"\");\n\n const result = await orchestrator.pull({\n dryRun: options.dryRun,\n paths: options.path,\n force: options.force,\n onProgress: (current, total, item) => {\n const icon = operationIcon(item.operation);\n const label = operationLabel(item.operation);\n const prog = progress(current, total);\n console.log(` ${icon} ${item.path} ${prog} ${label}`);\n },\n });\n\n if (result.imageCount > 0) {\n console.log(\n `\\n ${icons.success} ${result.imageCount} images → ${dimText(\"attachments/\")}`,\n );\n }\n if (result.fileCount > 0) {\n console.log(` ${icons.success} ${result.fileCount} files → ${dimText(\"attachments/\")}`);\n }\n if (result.linkCount > 0) {\n console.log(` ${icons.success} ${result.linkCount} links resolved`);\n }\n\n const failedCount = result.failed.length;\n console.log(`\\n ${completionHeader(\"Pull\", failedCount)}`);\n console.log(\n ` ${summary(result.created, result.updated, result.deleted)} ${failedCountText(failedCount)}`,\n );\n // 복원은 별도 줄로 알린다 — 볼트에서 파일이 사라졌었다는 사실은 조용히 넘길 일이 아니다.\n if (result.restored > 0) {\n console.log(` ${icons.success} ${chalk.yellow(`${result.restored} restored`)}`);\n }\n console.log(` ${duration(result.duration)}`);\n\n if (result.conflicts.length > 0) {\n console.log(\n `\\n ${icons.conflict} ${chalk.magenta(`${result.conflicts.length} conflicts`)} — run ${chalk.cyan(\"nobsi resolve\")}`,\n );\n }\n\n if (failedCount > 0) {\n console.log(\"\");\n for (const f of result.failed) {\n failedItem(f.path, f.error);\n }\n // 종료 코드까지 실패로 남긴다 — 자동화가 pull 실패를 성공으로 집계하면 안 된다.\n process.exitCode = 1;\n }\n } finally {\n stateDb.close();\n }\n });\n","import { Command } from \"commander\";\nimport {\n ConfigManager,\n StateDB,\n NotionClient,\n SyncOrchestrator,\n NodeVaultFS,\n} from \"@im-nobsidian/core\";\nimport chalk from \"chalk\";\nimport {\n header,\n separator,\n operationIcon,\n operationLabel,\n summary,\n duration,\n icons,\n dimText,\n completionHeader,\n failedItem,\n} from \"../utils/format.js\";\n\nexport const syncCommand = new Command(\"sync\")\n .description(\"양방향 동기화 (Pull → Push)\")\n .option(\"--dry-run\", \"실제 반영 없이 변경사항만 표시\")\n .action(async (options) => {\n const cwd = process.cwd();\n const configManager = new ConfigManager(cwd);\n const config = await configManager.load();\n const stateDb = StateDB.open(configManager.dbPath);\n\n try {\n const client = NotionClient.fromConfig(config);\n const vaultFs = new NodeVaultFS(cwd, config.paths);\n const orchestrator = new SyncOrchestrator(config, stateDb, client, vaultFs);\n\n console.log(`\\n${header(\" Bidirectional Sync\")}`);\n console.log(` ${separator()}`);\n if (options.dryRun) console.log(dimText(\" (dry-run mode)\"));\n\n // Pull phase\n console.log(`\\n ${header(chalk.blue(\"▼ Pull\"))} ${dimText(\"(Notion → Obsidian)\")}`);\n\n const pullResult = await orchestrator.pull({\n dryRun: options.dryRun,\n onProgress: (_current, _total, item) => {\n const icon = operationIcon(item.operation);\n const label = operationLabel(item.operation);\n console.log(` ${icon} ${item.path} ${label}`);\n },\n });\n\n if (pullResult.imageCount > 0) {\n console.log(` ${icons.success} ${pullResult.imageCount} images downloaded`);\n }\n if (pullResult.linkCount > 0) {\n console.log(` ${icons.success} ${pullResult.linkCount} links resolved`);\n }\n\n // Push phase\n console.log(`\\n ${header(chalk.magenta(\"▲ Push\"))} ${dimText(\"(Obsidian → Notion)\")}`);\n\n const pushResult = await orchestrator.push({\n dryRun: options.dryRun,\n onProgress: (_current, _total, item) => {\n const icon = operationIcon(item.operation);\n const label = operationLabel(item.operation);\n console.log(` ${icon} ${item.path} ${label}`);\n },\n });\n\n // Summary\n // 예전엔 양쪽 failed 를 아예 찍지 않아, 전 건 실패한 sync 도 화면 끝은 초록 \"complete\"\n // 였다 — 실패를 헤더와 목록 양쪽에 드러낸다.\n const totalDuration = pullResult.duration + pushResult.duration;\n const pullFailed = pullResult.failed.length;\n const pushFailed = pushResult.failed.length;\n console.log(`\\n ${completionHeader(\"Sync\", pullFailed + pushFailed)}`);\n console.log(\n ` ${chalk.blue(\"Pull:\")} ${summary(pullResult.created, pullResult.updated, pullResult.deleted)}` +\n // 사라졌던 파일이 되살아난 경우에만 덧붙인다 — 평상시 요약을 어지럽히지 않는다.\n (pullResult.restored > 0 ? ` ${chalk.yellow(`${pullResult.restored} restored`)}` : \"\") +\n (pullFailed > 0 ? ` ${chalk.red(`${pullFailed} failed`)}` : \"\"),\n );\n console.log(\n ` ${chalk.magenta(\"Push:\")} ${summary(pushResult.created, pushResult.updated, pushResult.deleted)}` +\n (pushFailed > 0 ? ` ${chalk.red(`${pushFailed} failed`)}` : \"\"),\n );\n console.log(` ${duration(totalDuration)}`);\n\n if (pullResult.conflicts.length > 0) {\n console.log(\n `\\n ${icons.conflict} ${chalk.magenta(`${pullResult.conflicts.length} conflicts`)} — run ${chalk.cyan(\"nobsi resolve\")}`,\n );\n }\n\n if (pullFailed + pushFailed > 0) {\n console.log(\"\");\n for (const f of pullResult.failed) failedItem(`▼ ${f.path}`, f.error);\n for (const f of pushResult.failed) failedItem(`▲ ${f.path}`, f.error);\n process.exitCode = 1;\n }\n } finally {\n stateDb.close();\n }\n });\n","import { Command } from \"commander\";\nimport {\n ConfigManager,\n StateDB,\n NotionClient,\n SyncOrchestrator,\n NodeVaultFS,\n} from \"@im-nobsidian/core\";\nimport chalk from \"chalk\";\nimport { header, separator, icons, dimText } from \"../utils/format.js\";\n\nexport const statusCommand = new Command(\"status\")\n .description(\"동기화 상태 확인 (--full: 원격 변경 포함)\")\n .option(\"--full\", \"Notion 원격 변경까지 양방향 확인 (API 호출, 느림)\")\n .action(async (opts: { full?: boolean }) => {\n const cwd = process.cwd();\n const configManager = new ConfigManager(cwd);\n const config = await configManager.load();\n const stateDb = StateDB.open(configManager.dbPath);\n\n try {\n const client = NotionClient.fromConfig(config);\n const vaultFs = new NodeVaultFS(cwd, config.paths);\n const orchestrator = new SyncOrchestrator(config, stateDb, client, vaultFs);\n\n const status = opts.full ? await orchestrator.status() : await orchestrator.statusLocal();\n\n console.log(`\\n${header(\" Sync Status\")}`);\n console.log(` ${separator(50)}`);\n\n const rootId = config.notion.rootPageId;\n const direction = config.sync.direction === \"both\" ? \"bidirectional\" : config.sync.direction;\n console.log(` ${dimText(\"Root page:\")} ${rootId.slice(0, 8)}...`);\n console.log(` ${dimText(\"Direction:\")} ${direction}`);\n if (status.lastSyncAt) {\n const date = new Date(status.lastSyncAt);\n const iso =\n date.getFullYear() +\n \"-\" +\n String(date.getMonth() + 1).padStart(2, \"0\") +\n \"-\" +\n String(date.getDate()).padStart(2, \"0\") +\n \" \" +\n String(date.getHours()).padStart(2, \"0\") +\n \":\" +\n String(date.getMinutes()).padStart(2, \"0\") +\n \":\" +\n String(date.getSeconds()).padStart(2, \"0\");\n console.log(` ${dimText(\"Last sync:\")} ${iso}`);\n } else {\n console.log(` ${dimText(\"Last sync:\")} ${chalk.yellow(\"never\")}`);\n }\n\n // Tracked files summary\n const allRecords = stateDb.getAll();\n const total = allRecords.length;\n const conflictCount = status.conflictRecords.length;\n const createdChanges = status.localChanges.filter((c) => c.type === \"created\");\n const modifiedChanges = status.localChanges.filter((c) => c.type === \"modified\");\n const deletedChanges = status.localChanges.filter((c) => c.type === \"deleted\");\n\n // 추적 파일 내역은 서로 겹치지 않아야 한다. DB 의 status 는 push/pull 시점에만 갱신되므로\n // 방금 고친 파일도 레코드상으론 여전히 'synced' 다 — 그대로 세면 같은 파일이 synced 와\n // modified 양쪽에 잡혀 내역 합이 총계를 넘는 표가 나온다(무엇이 밀렸는지 못 읽는다).\n // 실제 변경이 걸린 경로를 먼저 덜어 내고 남은 것만 synced 로 센다.\n const dirtyPaths = new Set([\n ...modifiedChanges.map((c) => c.path),\n ...deletedChanges.map((c) => c.path),\n ...status.conflictRecords.map((r) => r.obsidianPath),\n ]);\n const synced = allRecords.filter(\n (r) => r.status === \"synced\" && !dirtyPaths.has(r.obsidianPath),\n ).length;\n const remoteCreated = status.remoteChanges.filter((c) => c.type === \"created\");\n const remoteModified = status.remoteChanges.filter((c) => c.type === \"modified\");\n const remoteDeleted = status.remoteChanges.filter((c) => c.type === \"deleted\");\n\n console.log(`\\n ${header(`Tracked files: ${total}`)}`);\n console.log(` ${icons.synced} ${chalk.green(\"synced\")} ${synced}`);\n if (modifiedChanges.length > 0) {\n console.log(` ${icons.modified} ${chalk.yellow(\"modified\")} ${modifiedChanges.length}`);\n }\n if (deletedChanges.length > 0) {\n console.log(` ${chalk.red(\"-\")} ${chalk.red(\"deleted\")} ${deletedChanges.length}`);\n }\n if (conflictCount > 0) {\n console.log(` ${icons.conflictDot} ${chalk.magenta(\"conflict\")} ${conflictCount}`);\n }\n // new 는 아직 추적 레코드가 없는 파일이라 위 총계(Tracked files)에 들어가지 않는다.\n // 같은 블록에 숫자만 늘어놓으면 합이 안 맞아 보이므로 그 사실을 함께 적는다.\n if (createdChanges.length > 0) {\n console.log(\n ` ${icons.newFile} ${chalk.cyan(\"new\")} ${createdChanges.length} ${dimText(\"(untracked)\")}`,\n );\n }\n\n // Modified files\n if (modifiedChanges.length > 0) {\n console.log(`\\n ${header(\"Modified files:\")}`);\n for (const c of modifiedChanges.slice(0, 10)) {\n console.log(\n ` ${chalk.yellow(\"~\")} ${c.path.padEnd(30)} ${dimText(\"(local changed)\")}`,\n );\n }\n if (modifiedChanges.length > 10) {\n console.log(dimText(` ... and ${modifiedChanges.length - 10} more`));\n }\n }\n\n // New files\n if (createdChanges.length > 0) {\n console.log(`\\n ${header(\"New files:\")}`);\n for (const c of createdChanges.slice(0, 10)) {\n console.log(` ${chalk.cyan(\"+\")} ${c.path.padEnd(30)} ${dimText(\"(untracked)\")}`);\n }\n if (createdChanges.length > 10) {\n console.log(dimText(` ... and ${createdChanges.length - 10} more`));\n }\n }\n\n // Remote changes (Notion-side)\n if (status.remoteChanges.length > 0) {\n console.log(`\\n ${header(`Remote changes (Notion): ${status.remoteChanges.length}`)}`);\n if (remoteCreated.length > 0) {\n console.log(` ${chalk.cyan(\"+\")} ${chalk.cyan(\"new\")} ${remoteCreated.length}`);\n }\n if (remoteModified.length > 0) {\n console.log(\n ` ${chalk.yellow(\"~\")} ${chalk.yellow(\"modified\")} ${remoteModified.length}`,\n );\n }\n if (remoteDeleted.length > 0) {\n console.log(` ${chalk.red(\"-\")} ${chalk.red(\"deleted\")} ${remoteDeleted.length}`);\n }\n for (const c of status.remoteChanges.slice(0, 10)) {\n const icon =\n c.type === \"created\"\n ? chalk.cyan(\"+\")\n : c.type === \"modified\"\n ? chalk.yellow(\"~\")\n : chalk.red(\"-\");\n console.log(` ${icon} ${c.pageId.slice(0, 8)}... ${dimText(`(${c.type})`)}`);\n }\n if (status.remoteChanges.length > 10) {\n console.log(dimText(` ... and ${status.remoteChanges.length - 10} more`));\n }\n }\n\n // Conflicts\n if (conflictCount > 0) {\n console.log(`\\n ${header(chalk.magenta(\"Conflicts:\"))}`);\n for (const r of status.conflictRecords) {\n console.log(\n ` ${icons.conflict} ${r.obsidianPath.padEnd(30)} ${dimText(\"(both sides changed)\")}`,\n );\n }\n }\n\n // Helpful commands\n const hasChanges =\n status.localChanges.length > 0 || status.remoteChanges.length > 0 || conflictCount > 0;\n if (hasChanges) {\n console.log(\"\");\n if (conflictCount > 0) {\n console.log(\n ` ${dimText(\"Run\")} ${chalk.cyan(\"nobsi resolve\")} ${dimText(\"to resolve conflicts\")}`,\n );\n }\n console.log(\n ` ${dimText(\"Run\")} ${chalk.cyan(\"nobsi sync\")} ${dimText(\"to push/pull changes\")}`,\n );\n } else {\n console.log(`\\n ${chalk.green(\"Everything up to date\")} ✓`);\n }\n if (!opts.full) {\n console.log(\n `\\n ${dimText(\"Run\")} ${chalk.cyan(\"nobsi status --full\")} ${dimText(\"to check Notion remote changes\")}`,\n );\n }\n } finally {\n stateDb.close();\n }\n });\n","import { Command } from \"commander\";\nimport {\n ConfigManager,\n StateDB,\n NotionClient,\n SyncOrchestrator,\n NodeVaultFS,\n matchesPathScope,\n} from \"@im-nobsidian/core\";\nimport { createTwoFilesPatch } from \"diff\";\n\n// 기본 비교 대상은 **마지막 동기화 스냅샷**이다(로컬에서 무엇을 고쳤는지). Notion 현재\n// 본문과의 비교는 API 호출이 필요하므로 `--remote` 로 명시할 때만 한다 — 예전 설명은\n// \"로컬과 Notion 간의 차이\"라고만 적어 두고 실제로는 원격을 한 번도 읽지 않았다.\nexport const diffCommand = new Command(\"diff\")\n .description(\n \"로컬 변경사항 표시 (기본: 마지막 동기화 시점과 비교, --remote: Notion 현재본과 비교)\",\n )\n .argument(\"[path]\", \"특정 파일 경로 (생략 시 변경된 모든 파일)\")\n .option(\"--color\", \"컬러 출력\", true)\n .option(\"--remote\", \"Notion 현재 본문과 비교 (API 호출, 느림)\")\n .action(async (targetPath: string | undefined, options) => {\n const cwd = process.cwd();\n const configManager = new ConfigManager(cwd);\n const config = await configManager.load();\n const stateDb = StateDB.open(configManager.dbPath);\n\n try {\n const client = NotionClient.fromConfig(config);\n const vaultFs = new NodeVaultFS(cwd, config.paths);\n const orchestrator = new SyncOrchestrator(config, stateDb, client, vaultFs);\n\n const status = await orchestrator.statusLocal();\n\n const changes = targetPath\n ? status.localChanges.filter((c) => matchesPathScope(c.path, targetPath))\n : status.localChanges;\n\n if (changes.length === 0) {\n console.log(\"변경사항 없음\");\n return;\n }\n\n for (const change of changes) {\n const record = stateDb.getByPath(change.path);\n\n // --remote: 마지막 스냅샷이 아니라 Notion 의 현재 본문을 기준선으로 삼는다.\n // 원격이 그 사이 바뀌었을 때 \"내가 뭘 덮어쓰게 되는지\"는 이 비교로만 보인다.\n if (options.remote && change.type !== \"deleted\") {\n const remote = await orchestrator.renderRemoteSnapshot(change.path);\n if (remote === null) {\n console.log(`--- (원격 없음) ${change.path}`);\n console.log(`+++ b/${change.path}`);\n console.log(`(아직 Notion 에 없는 파일입니다)`);\n console.log(\"\");\n continue;\n }\n const currentContent = await vaultFs.readFile(change.path);\n const patch = createTwoFilesPatch(\n `notion/${change.path}`,\n `b/${change.path}`,\n remote,\n currentContent,\n );\n printPatch(patch, options.color, `notion/${change.path}`, `b/${change.path}`);\n console.log(\"\");\n continue;\n }\n\n if (change.type === \"created\") {\n const content = await vaultFs.readFile(change.path);\n const patch = createTwoFilesPatch(\"/dev/null\", `b/${change.path}`, \"\", content);\n printPatch(patch, options.color, \"/dev/null\", `b/${change.path}`);\n } else if (change.type === \"deleted\") {\n if (record?.baseSnapshot) {\n const oldContent = record.baseSnapshot.toString(\"utf-8\");\n const patch = createTwoFilesPatch(`a/${change.path}`, \"/dev/null\", oldContent, \"\");\n printPatch(patch, options.color, `a/${change.path}`, \"/dev/null\");\n } else {\n console.log(`--- a/${change.path}`);\n console.log(`+++ /dev/null`);\n console.log(`(이전 스냅샷 없음)`);\n }\n } else if (change.type === \"modified\" && record?.baseSnapshot) {\n const currentContent = await vaultFs.readFile(change.path);\n const baseContent = record.baseSnapshot.toString(\"utf-8\");\n const patch = createTwoFilesPatch(\n `a/${change.path}`,\n `b/${change.path}`,\n baseContent,\n currentContent,\n );\n printPatch(patch, options.color, `a/${change.path}`, `b/${change.path}`);\n }\n\n console.log(\"\");\n }\n } finally {\n stateDb.close();\n }\n });\n\nfunction printPatch(patch: string, color: boolean, oldPath: string, newPath: string): void {\n for (const line of patch.split(\"\\n\")) {\n let output = line;\n if (line.startsWith(\"===\")) {\n output = \"===================================================================\";\n } else if (line.startsWith(\"--- \")) {\n output = `--- ${oldPath}`;\n } else if (line.startsWith(\"+++ \")) {\n output = `+++ ${newPath}`;\n }\n\n if (!color) {\n console.log(output);\n continue;\n }\n\n if (output.startsWith(\"+++\") || output.startsWith(\"---\")) {\n console.log(`\\x1b[1m${output}\\x1b[0m`);\n } else if (output.startsWith(\"+\")) {\n console.log(`\\x1b[32m${output}\\x1b[0m`);\n } else if (output.startsWith(\"-\")) {\n console.log(`\\x1b[31m${output}\\x1b[0m`);\n } else if (output.startsWith(\"@@\")) {\n console.log(`\\x1b[36m${output}\\x1b[0m`);\n } else {\n console.log(output);\n }\n }\n}\n","import { Command } from \"commander\";\nimport {\n ConfigManager,\n StateDB,\n NotionClient,\n SyncOrchestrator,\n NodeVaultFS,\n} from \"@im-nobsidian/core\";\nimport chalk from \"chalk\";\nimport { header, separator, dimText } from \"../utils/format.js\";\n\nexport const fetchCommand = new Command(\"fetch\")\n .description(\"Notion 리모트 상태 스캔 (삭제 감지 포함)\")\n .action(async () => {\n const cwd = process.cwd();\n const configManager = new ConfigManager(cwd);\n const config = await configManager.load();\n const stateDb = StateDB.open(configManager.dbPath);\n\n try {\n const client = NotionClient.fromConfig(config);\n const vaultFs = new NodeVaultFS(cwd, config.paths);\n const orchestrator = new SyncOrchestrator(config, stateDb, client, vaultFs);\n\n console.log(dimText(\" Scanning remote pages...\"));\n\n const result = await orchestrator.fetch();\n\n console.log(`\\n${header(\" Remote Scan\")}`);\n console.log(` ${separator(40)}`);\n\n if (result.newPages > 0) {\n console.log(` ${chalk.cyan(\"+\")} New pages: ${result.newPages}`);\n }\n if (result.modifiedPages > 0) {\n console.log(` ${chalk.yellow(\"~\")} Modified pages: ${result.modifiedPages}`);\n }\n if (result.deletedPages > 0) {\n console.log(` ${chalk.red(\"-\")} Deleted pages: ${result.deletedPages}`);\n }\n\n const total = result.newPages + result.modifiedPages + result.deletedPages;\n if (total === 0) {\n console.log(` ${chalk.green(\"Remote is up to date\")} ✓`);\n } else {\n console.log(\n `\\n ${dimText(\"Run\")} ${chalk.cyan(\"nobsi pull\")} ${dimText(\"to apply remote changes\")}`,\n );\n }\n\n console.log(dimText(`\\n Completed in ${(result.duration / 1000).toFixed(1)}s`));\n } finally {\n stateDb.close();\n }\n });\n","import { Command } from \"commander\";\nimport { select, confirm } from \"@inquirer/prompts\";\nimport {\n ConfigManager,\n StateDB,\n NotionClient,\n SyncOrchestrator,\n NodeVaultFS,\n} from \"@im-nobsidian/core\";\nimport type { Conflict, ResolutionChoice } from \"@im-nobsidian/core\";\n\n/** 허용 전략 — 오타를 조용히 흘리지 않도록 실행 전에 한 번 검증한다. */\nconst STRATEGIES = [\"local-first\", \"remote-first\", \"duplicate\"] as const;\ntype Strategy = (typeof STRATEGIES)[number];\n\n/**\n * 대화형 프롬프트를 띄울 수 있는 환경인지.\n *\n * 파이프·cron·CI 처럼 TTY 가 없는 곳에서 @inquirer 프롬프트를 띄우면 원인을 알 수 없는\n * 오류로 죽거나 입력을 기다리며 멎는다. 먼저 판정해 무엇을 어떻게 하라고 말해 준다.\n */\nfunction isInteractive(): boolean {\n return process.stdin.isTTY === true && process.stdout.isTTY === true;\n}\n\nexport const resolveCommand = new Command(\"resolve\")\n .description(\"충돌을 해결합니다\")\n .option(\"-s, --strategy <strategy>\", `자동 해결 전략 (${STRATEGIES.join(\" | \")})`)\n .option(\"-y, --yes\", \"확인 프롬프트 없이 진행 (비대화형/CI)\")\n .action(async (options: { strategy?: string; yes?: boolean }) => {\n if (options.strategy && !STRATEGIES.includes(options.strategy as Strategy)) {\n console.error(`알 수 없는 전략 '${options.strategy}' — 사용 가능: ${STRATEGIES.join(\", \")}`);\n process.exitCode = 1;\n return;\n }\n\n const cwd = process.cwd();\n const configManager = new ConfigManager(cwd);\n let stateDb: StateDB | undefined;\n\n try {\n const config = await configManager.load();\n stateDb = StateDB.open(configManager.dbPath);\n const client = NotionClient.fromConfig(config);\n const vaultFs = new NodeVaultFS(cwd, config.paths);\n const orchestrator = new SyncOrchestrator(config, stateDb, client, vaultFs);\n\n // 충돌 목록은 **충돌로 표시된 페이지만** 읽어 온다. 예전엔 목록을 얻겠다고 전체 pull 을\n // 돌렸는데, 그건 해소하기도 전에 볼트를 원격으로 덮어쓰는 짓이라 순서가 거꾸로였다.\n const conflicts = await orchestrator.listConflicts();\n\n if (conflicts.length === 0) {\n console.log(\"충돌이 없습니다.\");\n return;\n }\n\n // 양쪽이 이미 같아진 항목은 물어볼 게 없다. 그대로 두면 push 가 그 파일을 영영\n // 건너뛰어(충돌 상태는 push 제외) 이후 편집이 조용히 정체하므로 먼저 걷어낸다.\n const cleared = new Set(orchestrator.clearStaleConflicts(conflicts));\n for (const path of cleared) {\n console.log(` \\x1b[32m✓\\x1b[0m ${path} → 양쪽 동일, 충돌 표시만 해제`);\n }\n\n const pending = conflicts.filter((c) => !cleared.has(c.syncRecord.obsidianPath));\n if (pending.length === 0) {\n console.log(`\\n${cleared.size}건 모두 이미 해소된 상태였습니다 — 상태만 정리했습니다.`);\n return;\n }\n\n console.log(`\\n충돌 ${pending.length}건 발견\\n`);\n\n if (options.strategy) {\n const strategy = options.strategy as Strategy;\n\n if (!options.yes) {\n if (!isInteractive()) {\n console.error(\n `비대화형 환경입니다 — '${strategy}' 전략으로 ${pending.length}건을 일괄 해결하려면 --yes 를 함께 지정하세요.`,\n );\n process.exitCode = 1;\n return;\n }\n const shouldProceed = await confirm({\n message: `${pending.length}건의 충돌을 '${strategy}' 전략으로 일괄 해결할까요?`,\n });\n if (!shouldProceed) return;\n }\n\n const results = await orchestrator.resolveAllConflicts(pending, strategy);\n let failed = 0;\n for (const result of results) {\n if (!result.success) failed++;\n const icon = result.success ? \"\\x1b[32m✓\\x1b[0m\" : \"\\x1b[31m✗\\x1b[0m\";\n console.log(` ${icon} ${result.path} → ${result.choice}`);\n }\n report(results.length - failed, failed);\n return;\n }\n\n if (!isInteractive()) {\n console.error(\n \"비대화형 환경에서는 대화형 해결을 쓸 수 없습니다 — --strategy <local-first|remote-first|duplicate> --yes 를 지정하세요.\",\n );\n process.exitCode = 1;\n return;\n }\n\n let resolved = 0;\n let failed = 0;\n for (const conflict of pending) {\n if (await resolveInteractive(conflict, orchestrator)) resolved++;\n else failed++;\n }\n report(resolved, failed);\n } catch (error) {\n console.error(\"오류:\", error instanceof Error ? error.message : String(error));\n process.exitCode = 1;\n } finally {\n // 예전엔 분기마다 close 를 흩뿌려 두고 catch 경로에선 아예 닫지 않았다 — 잠금이 남는다.\n stateDb?.close();\n }\n });\n\n/** 결과를 사실대로 알린다 — 실패가 하나라도 있으면 \"완료\"라고 말하지 않는다. */\nfunction report(resolved: number, failed: number): void {\n if (failed === 0) {\n console.log(`\\n충돌 해결 완료 — ${resolved}건`);\n return;\n }\n console.log(`\\n\\x1b[33m충돌 ${resolved}건 해결 · ${failed}건 미해결\\x1b[0m`);\n process.exitCode = 1;\n}\n\n/** @returns 해소에 성공했으면 true. 병합 마커가 남은 경우는 미해결로 센다. */\nasync function resolveInteractive(\n conflict: Conflict,\n orchestrator: SyncOrchestrator,\n): Promise<boolean> {\n const path = conflict.syncRecord.obsidianPath;\n\n console.log(`\\n${\"─\".repeat(60)}`);\n console.log(`파일: \\x1b[1m${path}\\x1b[0m`);\n console.log(`${\"─\".repeat(60)}`);\n\n const diff = orchestrator.generateConflictDiff(conflict);\n const diffLines = diff.split(\"\\n\");\n for (const line of diffLines) {\n if (line.startsWith(\"- \")) {\n console.log(`\\x1b[31m${line}\\x1b[0m`);\n } else if (line.startsWith(\"+ \")) {\n console.log(`\\x1b[32m${line}\\x1b[0m`);\n } else if (line.startsWith(\"---\") || line.startsWith(\"+++\")) {\n console.log(`\\x1b[1m${line}\\x1b[0m`);\n } else {\n console.log(line);\n }\n }\n\n const choice = await select<ResolutionChoice>({\n message: \"어떻게 해결할까요?\",\n choices: [\n { name: \"로컬 유지 (Obsidian 버전 유지)\", value: \"local\" },\n { name: \"원격 유지 (Notion 버전으로 덮어쓰기)\", value: \"remote\" },\n { name: \"자동 병합 (3-way merge)\", value: \"merge\" },\n { name: \"복제 (원본 유지 + .conflict 파일 생성)\", value: \"duplicate\" },\n ],\n });\n\n const result = await orchestrator.resolveConflict(conflict, choice);\n\n if (result.success) {\n console.log(`\\x1b[32m✓ ${path} → ${choice}로 해결\\x1b[0m`);\n return true;\n }\n if (result.mergeHadConflicts) {\n console.log(`\\x1b[33m⚠ ${path} → 자동 병합 완료 (수동 확인 필요한 충돌 마커 있음)\\x1b[0m`);\n return false;\n }\n console.log(`\\x1b[31m✗ ${path} → 해결 실패\\x1b[0m`);\n return false;\n}\n","import { Command } from \"commander\";\nimport {\n ConfigManager,\n StateDB,\n NotionClient,\n SyncOrchestrator,\n NodeVaultFS,\n WatchSyncService,\n} from \"@im-nobsidian/core\";\nimport type { SyncResult } from \"@im-nobsidian/core\";\n\nfunction formatResult(result: SyncResult): string {\n const pull = `Pull: +${result.pull.created} ~${result.pull.updated} -${result.pull.deleted}`;\n const push = `Push: +${result.push.created} ~${result.push.updated} -${result.push.deleted}`;\n const parts = [pull, push];\n\n if (result.conflicts.length > 0) {\n parts.push(`충돌 ${result.conflicts.length}건`);\n }\n\n return parts.join(\" | \");\n}\n\nfunction timestamp(): string {\n return new Date().toLocaleTimeString(\"ko-KR\", { hour12: false });\n}\n\nexport const watchCommand = new Command(\"watch\")\n .description(\"파일 변경 감지 + 자동 동기화\")\n .option(\"-d, --debounce <ms>\", \"디바운스 대기 시간(ms)\", \"2000\")\n .option(\"-i, --interval <seconds>\", \"풀 동기화 주기(초, 0이면 비활성)\", \"0\")\n .action(async (options: { debounce: string; interval: string }) => {\n const cwd = process.cwd();\n const configManager = new ConfigManager(cwd);\n const config = await configManager.load();\n const stateDb = StateDB.open(configManager.dbPath);\n\n const client = NotionClient.fromConfig(config);\n const vaultFs = new NodeVaultFS(cwd, config.paths);\n const orchestrator = new SyncOrchestrator(config, stateDb, client, vaultFs);\n\n const debounceMs = parseInt(options.debounce, 10);\n const intervalSec = parseInt(options.interval, 10);\n\n const service = new WatchSyncService(cwd, orchestrator, {\n debounceMs,\n onFileChange: (event, path) => {\n console.log(`[${timestamp()}] ${event}: ${path}`);\n },\n onSyncStart: () => {\n console.log(`[${timestamp()}] 동기화 시작...`);\n },\n onSyncComplete: (result) => {\n console.log(`[${timestamp()}] 동기화 완료 — ${formatResult(result)}`);\n },\n onSyncError: (error) => {\n console.error(`[${timestamp()}] 동기화 실패: ${error.message}`);\n },\n });\n\n let pullTimer: ReturnType<typeof setInterval> | null = null;\n\n const shutdown = async (): Promise<void> => {\n console.log(`\\n[${timestamp()}] 감시 종료 중...`);\n if (pullTimer) clearInterval(pullTimer);\n await service.stop();\n stateDb.close();\n console.log(`[${timestamp()}] 종료 완료`);\n process.exit(0);\n };\n\n process.on(\"SIGINT\", () => void shutdown());\n process.on(\"SIGTERM\", () => void shutdown());\n\n service.start();\n console.log(`[${timestamp()}] 파일 감시 시작 (debounce: ${debounceMs}ms)`);\n\n if (intervalSec > 0) {\n console.log(`[${timestamp()}] 풀 동기화 주기: ${intervalSec}초`);\n pullTimer = setInterval(async () => {\n if (service.isSyncing()) return;\n try {\n console.log(`[${timestamp()}] 주기적 동기화 시작...`);\n const result = await orchestrator.sync();\n console.log(`[${timestamp()}] 주기적 동기화 완료 — ${formatResult(result)}`);\n } catch (error) {\n const msg = error instanceof Error ? error.message : String(error);\n console.error(`[${timestamp()}] 주기적 동기화 실패: ${msg}`);\n }\n }, intervalSec * 1000);\n }\n\n await new Promise(() => {});\n });\n","import { Command } from \"commander\";\nimport {\n ConfigManager,\n StateDB,\n NotionClient,\n SyncOrchestrator,\n NodeVaultFS,\n} from \"@im-nobsidian/core\";\nimport type {\n CompletenessReport,\n PageCompletenessReport,\n VaultCompletenessReport,\n} from \"@im-nobsidian/core\";\nimport chalk from \"chalk\";\nimport { header, separator, icons, dimText } from \"../utils/format.js\";\n\n/** 화면에 늘어놓을 id 최대 개수 — 나머지는 건수로 접는다. */\nconst SAMPLE_LIMIT = 10;\n\nfunction printIdSample(label: string, ids: readonly string[], color: (s: string) => string): void {\n console.log(` ${color(label)} ${ids.length}`);\n for (const id of ids.slice(0, SAMPLE_LIMIT)) {\n console.log(` ${dimText(id)}`);\n }\n if (ids.length > SAMPLE_LIMIT) {\n console.log(dimText(` ... and ${ids.length - SAMPLE_LIMIT} more`));\n }\n}\n\nfunction printDatabaseSection(report: CompletenessReport): void {\n console.log(`\\n${header(\" DB Completeness\")}`);\n console.log(` ${separator(50)}`);\n console.log(` ${dimText(\"Databases:\")} ${report.databases.length}`);\n console.log(` ${dimText(\"Remote rows:\")} ${report.remoteTotal}`);\n console.log(` ${dimText(\"Vault rows:\")} ${report.localTotal}`);\n\n const broken = report.databases.filter((d) => d.missingIds.length > 0 || d.extraIds.length > 0);\n\n if (broken.length > 0) {\n console.log(`\\n ${header(chalk.red(`Incomplete databases: ${broken.length}`))}`);\n for (const db of broken) {\n console.log(\n ` ${icons.fail} ${db.databaseId} ${dimText(`remote ${db.remoteRows} / vault ${db.localRows}`)}`,\n );\n if (db.missingIds.length > 0) printIdSample(\"missing\", db.missingIds, chalk.red);\n if (db.extraIds.length > 0) printIdSample(\"extra\", db.extraIds, chalk.yellow);\n }\n }\n\n if (report.failures.length > 0) {\n console.log(`\\n ${header(chalk.yellow(`Query failures: ${report.failures.length}`))}`);\n for (const failure of report.failures) {\n console.log(` ${icons.fail} ${failure.databaseId} ${dimText(failure.error)}`);\n }\n }\n\n if (report.complete) {\n console.log(`\\n ${chalk.green(\"All database rows present in vault\")} ${icons.success}`);\n }\n}\n\n/**\n * 페이지 대조 출력 (R12-C).\n *\n * `local-only` 는 실패가 아니라 정보다 — 아직 push 하지 않은 로컬 노트·root 페이지 자신·\n * search 색인 지연이 전부 여기에 정상적으로 들어온다. 색을 달리 줘서 \"고쳐야 할 것\"과\n * \"그냥 알아 둘 것\"이 화면에서 섞이지 않게 한다.\n */\nfunction printPageSection(report: PageCompletenessReport): void {\n console.log(`\\n${header(\" Page Completeness\")}`);\n console.log(` ${separator(50)}`);\n console.log(` ${dimText(\"Remote pages:\")} ${report.remotePages}`);\n console.log(` ${dimText(\"Vault pages:\")} ${report.vaultPages}`);\n\n if (report.error) {\n console.log(`\\n ${icons.fail} ${chalk.yellow(\"Page enumeration failed\")}`);\n console.log(` ${dimText(report.error)}`);\n return;\n }\n\n if (report.missingIds.length > 0) {\n console.log(\n `\\n ${header(chalk.red(`Pages missing from vault: ${report.missingIds.length}`))}`,\n );\n printIdSample(\"missing\", report.missingIds, chalk.red);\n }\n\n if (report.localOnlyIds.length > 0) {\n console.log(\n `\\n ${dimText(`local-only pages: ${report.localOnlyIds.length} (informational)`)}`,\n );\n printIdSample(\"local-only\", report.localOnlyIds, dimText);\n }\n\n if (report.complete) {\n console.log(`\\n ${chalk.green(\"All remote pages present in vault\")} ${icons.success}`);\n }\n}\n\nfunction printReport(report: VaultCompletenessReport): void {\n printDatabaseSection(report.databases);\n\n if (report.pages) {\n printPageSection(report.pages);\n } else {\n // DB 모드에는 root 서브트리가 없어 대조할 페이지 자체가 없다 — 생략을 명시한다.\n console.log(`\\n ${dimText(\"Page completeness: skipped (database mode)\")}`);\n }\n\n if (!report.complete) {\n console.log(\n `\\n ${dimText(\"Run\")} ${chalk.cyan(\"nobsi pull\")} ${dimText(\"to fetch what is missing\")}`,\n );\n }\n}\n\nexport const verifyCommand = new Command(\"verify\")\n .description(\"완결성 검증 — 원격 DB 행·페이지가 볼트에 빠짐없이 있는지 대조 (API 호출)\")\n .option(\"--json\", \"결과를 JSON 으로 출력\")\n .action(async (opts: { json?: boolean }) => {\n const cwd = process.cwd();\n const configManager = new ConfigManager(cwd);\n const config = await configManager.load();\n const stateDb = StateDB.open(configManager.dbPath);\n\n try {\n const client = NotionClient.fromConfig(config);\n const vaultFs = new NodeVaultFS(cwd, config.paths);\n const orchestrator = new SyncOrchestrator(config, stateDb, client, vaultFs);\n\n const report = await orchestrator.verifyCompleteness();\n\n if (opts.json) {\n console.log(JSON.stringify(report, null, 2));\n } else {\n printReport(report);\n }\n\n // 미발견·잔재·조회 실패가 있으면 실패 종료한다 — CI·E2E 하니스가 이 코드로 판정한다.\n if (!report.complete) process.exitCode = 1;\n } finally {\n stateDb.close();\n }\n });\n","/**\n * CLI 종료 처리 — 남은 핸들 때문에 강제 종료하되, **명령이 정한 종료 코드는 보존한다**.\n *\n * better-sqlite3 핸들과 rate limiter 타이머가 이벤트 루프를 붙잡고 있어 명령이 끝나도\n * 프로세스가 자연 종료되지 않는다. 그래서 짧은 지연 뒤 강제 종료하는데, 여기서 코드를\n * 0 으로 못박으면 각 명령이 실패마다 세워 둔 `process.exitCode = 1`\n * (init·pull·push·sync·resolve·verify)이 **전부 지워진다** — 실패한 실행도 셸과 CI 에는\n * 성공으로 보이고, 종료 코드로 판정하는 스크립트는 아무것도 못 잡는다(R11-C).\n *\n * 종료 코드 결정을 이 함수 하나에 두어, \"강제 종료한다\"와 \"성공으로 종료한다\"가 다시\n * 한 줄에 섞이지 않게 한다.\n */\n\n/** 강제 종료까지의 지연 — 마지막 stdout flush 여유. */\nexport const FORCED_EXIT_DELAY_MS = 100;\n\n/**\n * {@link scheduleForcedExit} 가 쓰는 프로세스 표면(테스트 대역 주입용).\n *\n * `exitCode` 는 Node 의 `process.exitCode` 를 그대로 받는다 — 명시적으로 지운 경우\n * `null` 이 될 수 있어 그 유니온까지 포함해야 실제 `process` 를 넘길 수 있다.\n */\nexport interface ExitableProcess {\n exitCode?: number | string | null | undefined;\n exit(code?: number): void;\n}\n\n/** `process.exitCode` 를 종료 코드로 정규화한다. 숫자가 아니면 성공(0). */\nexport function resolveExitCode(exitCode: number | string | null | undefined): number {\n if (typeof exitCode === \"number\") return exitCode;\n if (typeof exitCode === \"string\") {\n const parsed = Number(exitCode);\n return Number.isFinite(parsed) ? parsed : 0;\n }\n return 0;\n}\n\n/** 지연 후 강제 종료를 예약한다 — 종료 코드는 명령이 세운 값을 그대로 쓴다. */\nexport function scheduleForcedExit(\n proc: ExitableProcess,\n schedule: (fn: () => void, ms: number) => unknown = setTimeout,\n): void {\n schedule(() => proc.exit(resolveExitCode(proc.exitCode)), FORCED_EXIT_DELAY_MS);\n}\n"],"mappings":"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","names":["Command","chalk","chalk","spinner","Command","ConfigManager","NotionClient","Command","ConfigManager","NotionClient","Command","ConfigManager","StateDB","NotionClient","SyncOrchestrator","NodeVaultFS","chalk","Command","ConfigManager","StateDB","NotionClient","NodeVaultFS","SyncOrchestrator","chalk","Command","ConfigManager","StateDB","NotionClient","SyncOrchestrator","NodeVaultFS","chalk","Command","ConfigManager","StateDB","NotionClient","NodeVaultFS","SyncOrchestrator","chalk","Command","ConfigManager","StateDB","NotionClient","SyncOrchestrator","NodeVaultFS","chalk","Command","ConfigManager","StateDB","NotionClient","NodeVaultFS","SyncOrchestrator","chalk","Command","ConfigManager","StateDB","NotionClient","SyncOrchestrator","NodeVaultFS","Command","ConfigManager","StateDB","NotionClient","SyncOrchestrator","NodeVaultFS","chalk","Command","ConfigManager","StateDB","NotionClient","NodeVaultFS","SyncOrchestrator","chalk","Command","confirm","ConfigManager","StateDB","NotionClient","SyncOrchestrator","NodeVaultFS","failed","Command","ConfigManager","StateDB","NotionClient","SyncOrchestrator","NodeVaultFS","Command","ConfigManager","StateDB","NotionClient","SyncOrchestrator","NodeVaultFS","chalk","report","chalk","Command","ConfigManager","StateDB","NotionClient","NodeVaultFS","SyncOrchestrator","Command"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "im-nobsidian",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.2",
|
|
4
4
|
"description": "CLI tool for Obsidian ↔ Notion bidirectional sync",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
"commander": "^15.0.0",
|
|
17
17
|
"diff": "^9.0.0",
|
|
18
18
|
"ora": "^9.4.1",
|
|
19
|
-
"@im-nobsidian/core": "0.3.
|
|
19
|
+
"@im-nobsidian/core": "0.3.2"
|
|
20
20
|
},
|
|
21
21
|
"devDependencies": {
|
|
22
22
|
"tsup": "^8.0.0",
|