im-nobsidian 0.4.1 โ 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +147 -91
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -2,12 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
// src/index.ts
|
|
4
4
|
import { createRequire } from "module";
|
|
5
|
-
import { Command as
|
|
5
|
+
import { Command as Command13 } from "commander";
|
|
6
6
|
|
|
7
7
|
// src/commands/init.ts
|
|
8
8
|
import { Command } from "commander";
|
|
9
9
|
import {
|
|
10
|
-
ConfigManager,
|
|
10
|
+
ConfigManager as ConfigManager2,
|
|
11
11
|
NOTION_TOKEN_PREFIX,
|
|
12
12
|
NotionClient,
|
|
13
13
|
notionIdFromInput
|
|
@@ -111,14 +111,69 @@ function failedItem(path, error, direction) {
|
|
|
111
111
|
console.log(` ${icons.fail} ${where}: ${chalk.red(error)}`);
|
|
112
112
|
}
|
|
113
113
|
|
|
114
|
+
// src/utils/vault.ts
|
|
115
|
+
import { existsSync } from "fs";
|
|
116
|
+
import { dirname, join, relative } from "path";
|
|
117
|
+
import {
|
|
118
|
+
ConfigManager,
|
|
119
|
+
NodeVaultFS,
|
|
120
|
+
StateDB,
|
|
121
|
+
matchesPathScope,
|
|
122
|
+
ScopedVaultFS,
|
|
123
|
+
findConnectionFolders,
|
|
124
|
+
OBSIDIAN_CONFIG_DIR
|
|
125
|
+
} from "@im-nobsidian/core";
|
|
126
|
+
async function nestedConnections(cwd) {
|
|
127
|
+
return (await findConnectionFolders(cwd)).filter((folder) => folder !== "");
|
|
128
|
+
}
|
|
129
|
+
async function openVaultFs(cwd, paths) {
|
|
130
|
+
const base = new NodeVaultFS(cwd, paths);
|
|
131
|
+
const nested = await nestedConnections(cwd);
|
|
132
|
+
return nested.length === 0 ? base : new ScopedVaultFS(base, "", ["", ...nested]);
|
|
133
|
+
}
|
|
134
|
+
function obsidianConfigDir(cwd) {
|
|
135
|
+
for (let dir = cwd; ; dir = dirname(dir)) {
|
|
136
|
+
if (existsSync(join(dir, OBSIDIAN_CONFIG_DIR))) {
|
|
137
|
+
return relative(cwd, join(dir, OBSIDIAN_CONFIG_DIR)).split("\\").join("/");
|
|
138
|
+
}
|
|
139
|
+
if (dirname(dir) === dir) return OBSIDIAN_CONFIG_DIR;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
function outerConnectionTracking(cwd) {
|
|
143
|
+
for (let dir = dirname(cwd); ; dir = dirname(dir)) {
|
|
144
|
+
const manager = new ConfigManager(dir);
|
|
145
|
+
if (existsSync(manager.dbPath)) {
|
|
146
|
+
const scope = relative(dir, cwd).split("\\").join("/");
|
|
147
|
+
const db = StateDB.open(manager.dbPath);
|
|
148
|
+
try {
|
|
149
|
+
const notes = db.getAll().filter(
|
|
150
|
+
(r) => r.obsidianPath !== scope && matchesPathScope(r.obsidianPath, scope)
|
|
151
|
+
).length;
|
|
152
|
+
return notes > 0 ? { folder: dir, notes } : null;
|
|
153
|
+
} finally {
|
|
154
|
+
db.close();
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
if (dirname(dir) === dir) return null;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
114
161
|
// src/commands/init.ts
|
|
115
162
|
var ROOT_PAGE_UNREADABLE = "\uB8E8\uD2B8 \uD398\uC774\uC9C0\uC5D0\uC11C Notion \uD398\uC774\uC9C0 ID(32\uC790\uB9AC)\uB97C \uCC3E\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4 \u2014 \uD398\uC774\uC9C0 \uC8FC\uC18C\uB098 ID \uB97C \uB123\uC73C\uC138\uC694.";
|
|
116
163
|
var initCommand = new Command("init").description("Im-Nobsidian \uCD08\uAE30 \uC124\uC815").option("--token <token>", "Notion Integration Token").option("--root-page-id <id>", "\uB8E8\uD2B8 \uD398\uC774\uC9C0 ID \uB610\uB294 \uD398\uC774\uC9C0 \uC8FC\uC18C").option("--non-interactive", "\uBE44\uB300\uD654\uD615 \uBAA8\uB4DC").action(async (options) => {
|
|
117
164
|
const cwd = process.cwd();
|
|
118
|
-
const configManager = new
|
|
165
|
+
const configManager = new ConfigManager2(cwd);
|
|
119
166
|
console.log(`
|
|
120
167
|
${header(chalk2.cyan(" Im-Nobsidian Setup"))}`);
|
|
121
168
|
console.log(` ${separator()}`);
|
|
169
|
+
const outer = outerConnectionTracking(cwd);
|
|
170
|
+
if (outer) {
|
|
171
|
+
console.error(
|
|
172
|
+
`\uC774 \uD3F4\uB354\uB294 \uC774\uBBF8 ${outer.folder} \uC758 \uC5F0\uACB0\uC774 \uB178\uD2B8 ${outer.notes}\uAC1C\uB97C \uB3D9\uAE30\uD654\uD558\uACE0 \uC788\uC5B4 \uB530\uB85C \uC5F0\uACB0\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.`
|
|
173
|
+
);
|
|
174
|
+
process.exitCode = 1;
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
122
177
|
if (options.nonInteractive) {
|
|
123
178
|
if (!options.token || !options.rootPageId) {
|
|
124
179
|
console.error("\uBE44\uB300\uD654\uD615 \uBAA8\uB4DC\uC5D0\uC11C\uB294 --token\uACFC --root-page-id\uAC00 \uD544\uC218\uC785\uB2C8\uB2E4.");
|
|
@@ -223,13 +278,7 @@ function extractPageTitle(page) {
|
|
|
223
278
|
|
|
224
279
|
// src/commands/push.ts
|
|
225
280
|
import { Command as Command2 } from "commander";
|
|
226
|
-
import {
|
|
227
|
-
ConfigManager as ConfigManager2,
|
|
228
|
-
StateDB,
|
|
229
|
-
NotionClient as NotionClient2,
|
|
230
|
-
SyncOrchestrator,
|
|
231
|
-
NodeVaultFS
|
|
232
|
-
} from "@im-nobsidian/core";
|
|
281
|
+
import { ConfigManager as ConfigManager3, StateDB as StateDB2, NotionClient as NotionClient2, SyncOrchestrator } from "@im-nobsidian/core";
|
|
233
282
|
|
|
234
283
|
// src/utils/json-output.ts
|
|
235
284
|
import { setLogger } from "@im-nobsidian/core";
|
|
@@ -244,7 +293,8 @@ function pushJson(result) {
|
|
|
244
293
|
updated: result.updated,
|
|
245
294
|
deleted: result.deleted,
|
|
246
295
|
moved: result.moved,
|
|
247
|
-
churn: result.created + result.updated + result.deleted + result.moved,
|
|
296
|
+
churn: result.created + result.updated + result.deleted + result.moved + (result.createdDatabases ?? 0),
|
|
297
|
+
createdDatabases: result.createdDatabases ?? 0,
|
|
248
298
|
failed: result.failed,
|
|
249
299
|
durationMs: result.duration
|
|
250
300
|
};
|
|
@@ -276,12 +326,12 @@ var pushCommand = new Command2("push").description("\uB85C\uCEEC \uBCC0\uACBD\uC
|
|
|
276
326
|
const json = options.json === true;
|
|
277
327
|
if (json) enableJsonMode();
|
|
278
328
|
const cwd = process.cwd();
|
|
279
|
-
const configManager = new
|
|
329
|
+
const configManager = new ConfigManager3(cwd);
|
|
280
330
|
const config = await configManager.load();
|
|
281
|
-
const stateDb =
|
|
331
|
+
const stateDb = StateDB2.open(configManager.dbPath);
|
|
282
332
|
try {
|
|
283
333
|
const client = NotionClient2.fromConfig(config);
|
|
284
|
-
const vaultFs =
|
|
334
|
+
const vaultFs = await openVaultFs(cwd, config.paths);
|
|
285
335
|
const orchestrator = new SyncOrchestrator(config, stateDb, client, vaultFs);
|
|
286
336
|
if (!json) {
|
|
287
337
|
console.log(`
|
|
@@ -310,6 +360,9 @@ ${header(" Pushing to Notion...")}`);
|
|
|
310
360
|
console.log(
|
|
311
361
|
` ${summary(result.created, result.updated, result.deleted, result.moved)} ${failedCountText(failedCount)}`
|
|
312
362
|
);
|
|
363
|
+
if (result.createdDatabases) {
|
|
364
|
+
console.log(` ${options.dryRun ? "\uB9CC\uB4E4 DB" : "\uB9CC\uB4E0 DB"} ${result.createdDatabases}`);
|
|
365
|
+
}
|
|
313
366
|
console.log(` ${duration(result.duration)}`);
|
|
314
367
|
if (failedCount > 0) {
|
|
315
368
|
console.log("");
|
|
@@ -325,13 +378,7 @@ ${header(" Pushing to Notion...")}`);
|
|
|
325
378
|
|
|
326
379
|
// src/commands/pull.ts
|
|
327
380
|
import { Command as Command3, Option } from "commander";
|
|
328
|
-
import {
|
|
329
|
-
ConfigManager as ConfigManager3,
|
|
330
|
-
StateDB as StateDB2,
|
|
331
|
-
NotionClient as NotionClient3,
|
|
332
|
-
SyncOrchestrator as SyncOrchestrator2,
|
|
333
|
-
NodeVaultFS as NodeVaultFS2
|
|
334
|
-
} from "@im-nobsidian/core";
|
|
381
|
+
import { ConfigManager as ConfigManager4, StateDB as StateDB3, NotionClient as NotionClient3, SyncOrchestrator as SyncOrchestrator2 } from "@im-nobsidian/core";
|
|
335
382
|
import chalk3 from "chalk";
|
|
336
383
|
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(
|
|
337
384
|
"--force",
|
|
@@ -345,12 +392,12 @@ var pullCommand = new Command3("pull").description("Notion \uBCC0\uACBD\uC0AC\uD
|
|
|
345
392
|
const json = options.json === true;
|
|
346
393
|
if (json) enableJsonMode();
|
|
347
394
|
const cwd = process.cwd();
|
|
348
|
-
const configManager = new
|
|
395
|
+
const configManager = new ConfigManager4(cwd);
|
|
349
396
|
const config = await configManager.load();
|
|
350
|
-
const stateDb =
|
|
397
|
+
const stateDb = StateDB3.open(configManager.dbPath);
|
|
351
398
|
try {
|
|
352
399
|
const client = NotionClient3.fromConfig(config);
|
|
353
|
-
const vaultFs =
|
|
400
|
+
const vaultFs = await openVaultFs(cwd, config.paths);
|
|
354
401
|
const orchestrator = new SyncOrchestrator2(config, stateDb, client, vaultFs);
|
|
355
402
|
if (!json) {
|
|
356
403
|
console.log(`
|
|
@@ -423,13 +470,7 @@ ${header(" Pulling from Notion...")}`);
|
|
|
423
470
|
|
|
424
471
|
// src/commands/sync.ts
|
|
425
472
|
import { Command as Command4 } from "commander";
|
|
426
|
-
import {
|
|
427
|
-
ConfigManager as ConfigManager4,
|
|
428
|
-
StateDB as StateDB3,
|
|
429
|
-
NotionClient as NotionClient4,
|
|
430
|
-
SyncOrchestrator as SyncOrchestrator3,
|
|
431
|
-
NodeVaultFS as NodeVaultFS3
|
|
432
|
-
} from "@im-nobsidian/core";
|
|
473
|
+
import { ConfigManager as ConfigManager5, StateDB as StateDB4, NotionClient as NotionClient4, SyncOrchestrator as SyncOrchestrator3 } from "@im-nobsidian/core";
|
|
433
474
|
import chalk4 from "chalk";
|
|
434
475
|
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").option("--json", "\uACB0\uACFC\uB97C JSON \uD55C \uC904\uB85C \uCD9C\uB825 (\uC790\uB3D9\uD654\uC6A9 \u2014 \uB85C\uADF8\uB294 stderr)").action(async (options) => {
|
|
435
476
|
const json = options.json === true;
|
|
@@ -437,12 +478,12 @@ var syncCommand = new Command4("sync").description("\uC591\uBC29\uD5A5 \uB3D9\uA
|
|
|
437
478
|
const log = json ? () => {
|
|
438
479
|
} : (line) => console.log(line);
|
|
439
480
|
const cwd = process.cwd();
|
|
440
|
-
const configManager = new
|
|
481
|
+
const configManager = new ConfigManager5(cwd);
|
|
441
482
|
const config = await configManager.load();
|
|
442
|
-
const stateDb =
|
|
483
|
+
const stateDb = StateDB4.open(configManager.dbPath);
|
|
443
484
|
try {
|
|
444
485
|
const client = NotionClient4.fromConfig(config);
|
|
445
|
-
const vaultFs =
|
|
486
|
+
const vaultFs = await openVaultFs(cwd, config.paths);
|
|
446
487
|
const orchestrator = new SyncOrchestrator3(config, stateDb, client, vaultFs);
|
|
447
488
|
log(`
|
|
448
489
|
${header(" Bidirectional Sync")}`);
|
|
@@ -523,22 +564,21 @@ ${header(" Bidirectional Sync")}`);
|
|
|
523
564
|
// src/commands/status.ts
|
|
524
565
|
import { Command as Command5 } from "commander";
|
|
525
566
|
import {
|
|
526
|
-
ConfigManager as
|
|
527
|
-
StateDB as
|
|
567
|
+
ConfigManager as ConfigManager6,
|
|
568
|
+
StateDB as StateDB5,
|
|
528
569
|
NotionClient as NotionClient5,
|
|
529
570
|
SyncOrchestrator as SyncOrchestrator4,
|
|
530
|
-
NodeVaultFS as NodeVaultFS4,
|
|
531
571
|
isRemoteDeletion
|
|
532
572
|
} from "@im-nobsidian/core";
|
|
533
573
|
import chalk5 from "chalk";
|
|
534
574
|
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) => {
|
|
535
575
|
const cwd = process.cwd();
|
|
536
|
-
const configManager = new
|
|
576
|
+
const configManager = new ConfigManager6(cwd);
|
|
537
577
|
const config = await configManager.load();
|
|
538
|
-
const stateDb =
|
|
578
|
+
const stateDb = StateDB5.open(configManager.dbPath);
|
|
539
579
|
try {
|
|
540
580
|
const client = NotionClient5.fromConfig(config);
|
|
541
|
-
const vaultFs =
|
|
581
|
+
const vaultFs = await openVaultFs(cwd, config.paths);
|
|
542
582
|
const orchestrator = new SyncOrchestrator4(config, stateDb, client, vaultFs);
|
|
543
583
|
const status = opts.full ? await orchestrator.status() : await orchestrator.statusLocal();
|
|
544
584
|
console.log(`
|
|
@@ -554,6 +594,11 @@ ${header(" Sync Status")}`);
|
|
|
554
594
|
console.log(` ${dimText("Last sync:")} ${chalk5.yellow("never")}`);
|
|
555
595
|
}
|
|
556
596
|
console.log(` ${dimText("Full scan:")} ${lastFullScanText(status.lastFullScanAt)}`);
|
|
597
|
+
for (const folder of await nestedConnections(cwd)) {
|
|
598
|
+
console.log(
|
|
599
|
+
` ${dimText("Connection:")} ${folder}/ ${dimText(`(separate \u2014 cd ${folder} && nobsi status)`)}`
|
|
600
|
+
);
|
|
601
|
+
}
|
|
557
602
|
const allRecords = stateDb.getAll();
|
|
558
603
|
const total = allRecords.length;
|
|
559
604
|
const conflictCount = status.conflictRecords.length;
|
|
@@ -716,14 +761,13 @@ ${header(" Sync Status")}`);
|
|
|
716
761
|
// src/commands/diff.ts
|
|
717
762
|
import { Command as Command6 } from "commander";
|
|
718
763
|
import {
|
|
719
|
-
ConfigManager as
|
|
720
|
-
StateDB as
|
|
764
|
+
ConfigManager as ConfigManager7,
|
|
765
|
+
StateDB as StateDB6,
|
|
721
766
|
NotionClient as NotionClient6,
|
|
722
767
|
SyncOrchestrator as SyncOrchestrator5,
|
|
723
|
-
NodeVaultFS as NodeVaultFS5,
|
|
724
768
|
formatUnifiedDiff,
|
|
725
769
|
lineDiff,
|
|
726
|
-
matchesPathScope
|
|
770
|
+
matchesPathScope as matchesPathScope2
|
|
727
771
|
} from "@im-nobsidian/core";
|
|
728
772
|
|
|
729
773
|
// src/utils/diff-output.ts
|
|
@@ -750,15 +794,15 @@ var diffCommand = new Command6("diff").description(
|
|
|
750
794
|
"\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)"
|
|
751
795
|
).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) => {
|
|
752
796
|
const cwd = process.cwd();
|
|
753
|
-
const configManager = new
|
|
797
|
+
const configManager = new ConfigManager7(cwd);
|
|
754
798
|
const config = await configManager.load();
|
|
755
|
-
const stateDb =
|
|
799
|
+
const stateDb = StateDB6.open(configManager.dbPath);
|
|
756
800
|
try {
|
|
757
801
|
const client = NotionClient6.fromConfig(config);
|
|
758
|
-
const vaultFs =
|
|
802
|
+
const vaultFs = await openVaultFs(cwd, config.paths);
|
|
759
803
|
const orchestrator = new SyncOrchestrator5(config, stateDb, client, vaultFs);
|
|
760
804
|
const status = await orchestrator.statusLocal();
|
|
761
|
-
const changes = targetPath ? status.localChanges.filter((c) =>
|
|
805
|
+
const changes = targetPath ? status.localChanges.filter((c) => matchesPathScope2(c.path, targetPath)) : status.localChanges;
|
|
762
806
|
if (changes.length === 0) {
|
|
763
807
|
console.log("\uBCC0\uACBD\uC0AC\uD56D \uC5C6\uC74C");
|
|
764
808
|
return;
|
|
@@ -824,21 +868,15 @@ function printChangeDiff(diff, color) {
|
|
|
824
868
|
|
|
825
869
|
// src/commands/discard.ts
|
|
826
870
|
import { Command as Command7 } from "commander";
|
|
827
|
-
import {
|
|
828
|
-
ConfigManager as ConfigManager7,
|
|
829
|
-
StateDB as StateDB6,
|
|
830
|
-
NotionClient as NotionClient7,
|
|
831
|
-
SyncOrchestrator as SyncOrchestrator6,
|
|
832
|
-
NodeVaultFS as NodeVaultFS6
|
|
833
|
-
} from "@im-nobsidian/core";
|
|
871
|
+
import { ConfigManager as ConfigManager8, StateDB as StateDB7, NotionClient as NotionClient7, SyncOrchestrator as SyncOrchestrator6 } from "@im-nobsidian/core";
|
|
834
872
|
var discardCommand = new Command7("discard").description("\uB85C\uCEEC \uBCC0\uACBD\uC744 \uC9C0\uB09C \uB3D9\uAE30\uD654 \uB54C\uC758 \uAE00\uB85C \uB418\uB3CC\uB9AC\uAE30 (Notion \uC740 \uADF8\uB300\uB85C)").argument("<paths...>", "\uB418\uB3CC\uB9B4 \uB178\uD2B8 \uACBD\uB85C \u2014 status \uAC00 \uBCF4\uC778 \uBCFC\uD2B8 \uAE30\uC900 \uACBD\uB85C").action(async (paths) => {
|
|
835
873
|
const cwd = process.cwd();
|
|
836
|
-
const configManager = new
|
|
874
|
+
const configManager = new ConfigManager8(cwd);
|
|
837
875
|
const config = await configManager.load();
|
|
838
|
-
const stateDb =
|
|
876
|
+
const stateDb = StateDB7.open(configManager.dbPath);
|
|
839
877
|
try {
|
|
840
878
|
const client = NotionClient7.fromConfig(config);
|
|
841
|
-
const vaultFs =
|
|
879
|
+
const vaultFs = await openVaultFs(cwd, config.paths);
|
|
842
880
|
const orchestrator = new SyncOrchestrator6(config, stateDb, client, vaultFs);
|
|
843
881
|
let failed = 0;
|
|
844
882
|
for (const path of paths) {
|
|
@@ -858,22 +896,16 @@ var discardCommand = new Command7("discard").description("\uB85C\uCEEC \uBCC0\uA
|
|
|
858
896
|
|
|
859
897
|
// src/commands/fetch.ts
|
|
860
898
|
import { Command as Command8 } from "commander";
|
|
861
|
-
import {
|
|
862
|
-
ConfigManager as ConfigManager8,
|
|
863
|
-
StateDB as StateDB7,
|
|
864
|
-
NotionClient as NotionClient8,
|
|
865
|
-
SyncOrchestrator as SyncOrchestrator7,
|
|
866
|
-
NodeVaultFS as NodeVaultFS7
|
|
867
|
-
} from "@im-nobsidian/core";
|
|
899
|
+
import { ConfigManager as ConfigManager9, StateDB as StateDB8, NotionClient as NotionClient8, SyncOrchestrator as SyncOrchestrator7 } from "@im-nobsidian/core";
|
|
868
900
|
import chalk6 from "chalk";
|
|
869
901
|
var fetchCommand = new Command8("fetch").description("Notion \uB9AC\uBAA8\uD2B8 \uC0C1\uD0DC \uC2A4\uCE94 (\uC0AD\uC81C \uAC10\uC9C0 \uD3EC\uD568)").action(async () => {
|
|
870
902
|
const cwd = process.cwd();
|
|
871
|
-
const configManager = new
|
|
903
|
+
const configManager = new ConfigManager9(cwd);
|
|
872
904
|
const config = await configManager.load();
|
|
873
|
-
const stateDb =
|
|
905
|
+
const stateDb = StateDB8.open(configManager.dbPath);
|
|
874
906
|
try {
|
|
875
907
|
const client = NotionClient8.fromConfig(config);
|
|
876
|
-
const vaultFs =
|
|
908
|
+
const vaultFs = await openVaultFs(cwd, config.paths);
|
|
877
909
|
const orchestrator = new SyncOrchestrator7(config, stateDb, client, vaultFs);
|
|
878
910
|
console.log(dimText(" Scanning remote pages..."));
|
|
879
911
|
const result = await orchestrator.fetch();
|
|
@@ -909,11 +941,10 @@ ${header(" Remote Scan")}`);
|
|
|
909
941
|
import { Command as Command9 } from "commander";
|
|
910
942
|
import { select, confirm as confirm2 } from "@inquirer/prompts";
|
|
911
943
|
import {
|
|
912
|
-
ConfigManager as
|
|
913
|
-
StateDB as
|
|
944
|
+
ConfigManager as ConfigManager10,
|
|
945
|
+
StateDB as StateDB9,
|
|
914
946
|
NotionClient as NotionClient9,
|
|
915
947
|
SyncOrchestrator as SyncOrchestrator8,
|
|
916
|
-
NodeVaultFS as NodeVaultFS8,
|
|
917
948
|
applicableChoices,
|
|
918
949
|
isRemoteDeletion as isRemoteDeletion2
|
|
919
950
|
} from "@im-nobsidian/core";
|
|
@@ -960,13 +991,13 @@ var resolveCommand = new Command9("resolve").description("\uCDA9\uB3CC\uC744 \uD
|
|
|
960
991
|
return;
|
|
961
992
|
}
|
|
962
993
|
const cwd = process.cwd();
|
|
963
|
-
const configManager = new
|
|
994
|
+
const configManager = new ConfigManager10(cwd);
|
|
964
995
|
let stateDb;
|
|
965
996
|
try {
|
|
966
997
|
const config = await configManager.load();
|
|
967
|
-
stateDb =
|
|
998
|
+
stateDb = StateDB9.open(configManager.dbPath);
|
|
968
999
|
const client = NotionClient9.fromConfig(config);
|
|
969
|
-
const vaultFs =
|
|
1000
|
+
const vaultFs = await openVaultFs(cwd, config.paths);
|
|
970
1001
|
const orchestrator = new SyncOrchestrator8(config, stateDb, client, vaultFs);
|
|
971
1002
|
const conflicts = await orchestrator.listConflicts();
|
|
972
1003
|
if (conflicts.length === 0) {
|
|
@@ -1093,11 +1124,10 @@ ${"\u2500".repeat(60)}`);
|
|
|
1093
1124
|
// src/commands/watch.ts
|
|
1094
1125
|
import { Command as Command10 } from "commander";
|
|
1095
1126
|
import {
|
|
1096
|
-
ConfigManager as
|
|
1097
|
-
StateDB as
|
|
1127
|
+
ConfigManager as ConfigManager11,
|
|
1128
|
+
StateDB as StateDB10,
|
|
1098
1129
|
NotionClient as NotionClient10,
|
|
1099
1130
|
SyncOrchestrator as SyncOrchestrator9,
|
|
1100
|
-
NodeVaultFS as NodeVaultFS9,
|
|
1101
1131
|
WatchSyncService,
|
|
1102
1132
|
DEFAULT_WATCH_DEBOUNCE_MS
|
|
1103
1133
|
} from "@im-nobsidian/core";
|
|
@@ -1131,11 +1161,11 @@ var watchCommand = new Command10("watch").description("\uD30C\uC77C \uBCC0\uACBD
|
|
|
1131
1161
|
return;
|
|
1132
1162
|
}
|
|
1133
1163
|
const cwd = process.cwd();
|
|
1134
|
-
const configManager = new
|
|
1164
|
+
const configManager = new ConfigManager11(cwd);
|
|
1135
1165
|
const config = await configManager.load();
|
|
1136
|
-
const stateDb =
|
|
1166
|
+
const stateDb = StateDB10.open(configManager.dbPath);
|
|
1137
1167
|
const client = NotionClient10.fromConfig(config);
|
|
1138
|
-
const vaultFs =
|
|
1168
|
+
const vaultFs = await openVaultFs(cwd, config.paths);
|
|
1139
1169
|
const orchestrator = new SyncOrchestrator9(config, stateDb, client, vaultFs);
|
|
1140
1170
|
const service = new WatchSyncService(cwd, orchestrator, {
|
|
1141
1171
|
debounceMs,
|
|
@@ -1187,13 +1217,7 @@ var watchCommand = new Command10("watch").description("\uD30C\uC77C \uBCC0\uACBD
|
|
|
1187
1217
|
|
|
1188
1218
|
// src/commands/verify.ts
|
|
1189
1219
|
import { Command as Command11 } from "commander";
|
|
1190
|
-
import {
|
|
1191
|
-
ConfigManager as ConfigManager11,
|
|
1192
|
-
StateDB as StateDB10,
|
|
1193
|
-
NotionClient as NotionClient11,
|
|
1194
|
-
SyncOrchestrator as SyncOrchestrator10,
|
|
1195
|
-
NodeVaultFS as NodeVaultFS10
|
|
1196
|
-
} from "@im-nobsidian/core";
|
|
1220
|
+
import { ConfigManager as ConfigManager12, StateDB as StateDB11, NotionClient as NotionClient11, SyncOrchestrator as SyncOrchestrator10 } from "@im-nobsidian/core";
|
|
1197
1221
|
import chalk7 from "chalk";
|
|
1198
1222
|
var SAMPLE_LIMIT = 10;
|
|
1199
1223
|
function printIdSample(label, ids, color) {
|
|
@@ -1284,12 +1308,12 @@ function printReport(report2) {
|
|
|
1284
1308
|
}
|
|
1285
1309
|
var verifyCommand = new Command11("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) => {
|
|
1286
1310
|
const cwd = process.cwd();
|
|
1287
|
-
const configManager = new
|
|
1311
|
+
const configManager = new ConfigManager12(cwd);
|
|
1288
1312
|
const config = await configManager.load();
|
|
1289
|
-
const stateDb =
|
|
1313
|
+
const stateDb = StateDB11.open(configManager.dbPath);
|
|
1290
1314
|
try {
|
|
1291
1315
|
const client = NotionClient11.fromConfig(config);
|
|
1292
|
-
const vaultFs =
|
|
1316
|
+
const vaultFs = await openVaultFs(cwd, config.paths);
|
|
1293
1317
|
const orchestrator = new SyncOrchestrator10(config, stateDb, client, vaultFs);
|
|
1294
1318
|
const report2 = await orchestrator.verifyCompleteness();
|
|
1295
1319
|
if (opts.json) {
|
|
@@ -1303,6 +1327,37 @@ var verifyCommand = new Command11("verify").description("\uC644\uACB0\uC131 \uAC
|
|
|
1303
1327
|
}
|
|
1304
1328
|
});
|
|
1305
1329
|
|
|
1330
|
+
// src/commands/prepare-db.ts
|
|
1331
|
+
import { Command as Command12 } from "commander";
|
|
1332
|
+
import { ConfigManager as ConfigManager13, StateDB as StateDB12, NotionClient as NotionClient12, SyncOrchestrator as SyncOrchestrator11 } from "@im-nobsidian/core";
|
|
1333
|
+
import chalk8 from "chalk";
|
|
1334
|
+
var prepareDbCommand = new Command12("prepare-db").description("\uBCFC\uD2B8\uC758 \uB178\uD2B8 \uD3F4\uB354\uB97C \uB2E4\uC74C push \uC5D0\uC11C Notion DB \uB85C \uB9CC\uB4E4\uB3C4\uB85D \uC900\uBE44 (Notion \uC740 \uADF8\uB300\uB85C)").argument("<folder>", "DB \uB85C \uB9CC\uB4E4 \uD3F4\uB354 \u2014 \uBCFC\uD2B8 \uAE30\uC900 \uACBD\uB85C. \uBC14\uB85C \uC544\uB798 \uB178\uD2B8\uAC00 \uD589\uC774 \uB41C\uB2E4").action(async (folder) => {
|
|
1335
|
+
const cwd = process.cwd();
|
|
1336
|
+
const configManager = new ConfigManager13(cwd);
|
|
1337
|
+
const config = await configManager.load();
|
|
1338
|
+
const stateDb = StateDB12.open(configManager.dbPath);
|
|
1339
|
+
try {
|
|
1340
|
+
const client = NotionClient12.fromConfig(config);
|
|
1341
|
+
const vaultFs = await openVaultFs(cwd, config.paths);
|
|
1342
|
+
const orchestrator = new SyncOrchestrator11(config, stateDb, client, vaultFs);
|
|
1343
|
+
const report2 = await orchestrator.prepareFolderDatabase(folder, obsidianConfigDir(cwd));
|
|
1344
|
+
console.log(` ${icons.success} ${report2.sidecarPath} \u2014 \uD589 ${report2.rows}\uAC1C`);
|
|
1345
|
+
for (const property of report2.properties) {
|
|
1346
|
+
console.log(` ${property.name}: ${property.type} ${chalk8.dim(`(${property.reason})`)}`);
|
|
1347
|
+
}
|
|
1348
|
+
if (report2.views.length > 0) console.log(` \uBCF4\uAE30: ${report2.views.join(", ")}`);
|
|
1349
|
+
for (const skip of report2.skipped) {
|
|
1350
|
+
console.log(chalk8.dim(` \uC62E\uAE30\uC9C0 \uC54A\uC74C \u2014 ${skip.what}: ${skip.reason}`));
|
|
1351
|
+
}
|
|
1352
|
+
console.log(chalk8.dim(" \uD0C0\uC785\uC744 \uBC14\uAFB8\uB824\uBA74 \uC0AC\uC774\uB4DC\uCE74\uB97C \uACE0\uCE5C \uB4A4 push \uD558\uC138\uC694."));
|
|
1353
|
+
} catch (error) {
|
|
1354
|
+
failedItem(folder, error instanceof Error ? error.message : String(error));
|
|
1355
|
+
process.exitCode = 1;
|
|
1356
|
+
} finally {
|
|
1357
|
+
stateDb.close();
|
|
1358
|
+
}
|
|
1359
|
+
});
|
|
1360
|
+
|
|
1306
1361
|
// src/utils/exit.ts
|
|
1307
1362
|
var FORCED_EXIT_DELAY_MS = 100;
|
|
1308
1363
|
function resolveExitCode(exitCode) {
|
|
@@ -1320,7 +1375,7 @@ function scheduleForcedExit(proc, schedule = setTimeout) {
|
|
|
1320
1375
|
// src/index.ts
|
|
1321
1376
|
var requireJson = createRequire(import.meta.url);
|
|
1322
1377
|
var { version } = requireJson("../package.json");
|
|
1323
|
-
var program = new
|
|
1378
|
+
var program = new Command13();
|
|
1324
1379
|
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");
|
|
1325
1380
|
program.addCommand(initCommand);
|
|
1326
1381
|
program.addCommand(pushCommand);
|
|
@@ -1333,6 +1388,7 @@ program.addCommand(fetchCommand);
|
|
|
1333
1388
|
program.addCommand(resolveCommand);
|
|
1334
1389
|
program.addCommand(watchCommand);
|
|
1335
1390
|
program.addCommand(verifyCommand);
|
|
1391
|
+
program.addCommand(prepareDbCommand);
|
|
1336
1392
|
program.parseAsync().then(
|
|
1337
1393
|
() => {
|
|
1338
1394
|
scheduleForcedExit(process);
|
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/utils/json-output.ts","../src/commands/pull.ts","../src/commands/sync.ts","../src/commands/status.ts","../src/commands/diff.ts","../src/utils/diff-output.ts","../src/commands/discard.ts","../src/commands/fetch.ts","../src/commands/resolve.ts","../src/utils/state-db-failure.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 { discardCommand } from \"./commands/discard.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\";\nimport { reportStateDbFailure } from \"./utils/state-db-failure.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(discardCommand);\nprogram.addCommand(fetchCommand);\nprogram.addCommand(resolveCommand);\nprogram.addCommand(watchCommand);\nprogram.addCommand(verifyCommand);\n\n// ๊ฐ์ ์ข
๋ฃํ๋ ๋ช
๋ น์ด ์ ํ ์ข
๋ฃ ์ฝ๋๋ฅผ ๋ณด์กดํ๋ค โ ๊ทผ๊ฑฐ๋ utils/exit.ts ์ฐธ์กฐ(R11-C).\n// ์ํ DB ๋ฅผ ์ธ ์ ์์ด ๋ช
๋ น์ด ๋์ก์ผ๋ฉด ์ด์ ์ ํ ์ผ์ ๋ณด์ด๊ณ 1 ๋ก ๋๋ธ๋ค โ utils/state-db-failure.ts.\nprogram.parseAsync().then(\n () => {\n scheduleForcedExit(process);\n },\n (error: unknown) => {\n reportStateDbFailure(error, process);\n scheduleForcedExit(process);\n },\n);\n","import { Command } from \"commander\";\nimport {\n ConfigManager,\n NOTION_TOKEN_PREFIX,\n NotionClient,\n notionIdFromInput,\n} 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\n/** ๋ฃจํธ ํ์ด์ง์์ ID ๋ฅผ ๋ชป ์ฐพ์์ ๋ โ ์ฃผ์๋ฅผ ๊ทธ๋๋ก ๋ถ์ฌ ๋ฃ์ด๋ ๋๋ค๊ณ ๋งํ๋ค. */\nconst ROOT_PAGE_UNREADABLE =\n \"๋ฃจํธ ํ์ด์ง์์ Notion ํ์ด์ง ID(32์๋ฆฌ)๋ฅผ ์ฐพ์ง ๋ชปํ์ต๋๋ค โ ํ์ด์ง ์ฃผ์๋ ID ๋ฅผ ๋ฃ์ผ์ธ์.\";\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(NOTION_TOKEN_PREFIX)) {\n console.error(`ํ ํฐ์ ${NOTION_TOKEN_PREFIX}์ผ๋ก ์์ํด์ผ ํฉ๋๋ค.`);\n process.exitCode = 1;\n return;\n }\n\n const rootPageId = notionIdFromInput(options.rootPageId);\n if (rootPageId === null) {\n console.error(ROOT_PAGE_UNREADABLE);\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 });\n console.log(`\\n ${icons.success} Config saved to ${dimText(configManager.configPath)}`);\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) =>\n v.startsWith(NOTION_TOKEN_PREFIX) ||\n `${NOTION_TOKEN_PREFIX}์ผ๋ก ์์ํ๋ ํ ํฐ์ ์
๋ ฅํ์ธ์`,\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 rootPageInput = await input({\n message: \"๋ฃจํธ ํ์ด์ง ID ๋๋ ์ฃผ์ (์์์ ์ ํํ๊ฑฐ๋ ์ง์ ์
๋ ฅ):\",\n default: result.results[0]?.id,\n validate: (value) => notionIdFromInput(value) !== null || ROOT_PAGE_UNREADABLE,\n });\n const rootPageId = notionIdFromInput(rootPageInput)!;\n\n await configManager.init({ token, rootPageId });\n console.log(`\\n ${icons.success} Config saved to ${dimText(configManager.configPath)}`);\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\";\nimport type { FullScanReason, ProgressItem, RemoteScanInfo } from \"@im-nobsidian/core\";\n\nexport const icons = {\n create: chalk.green(\"+\"),\n update: chalk.yellow(\"~\"),\n delete: chalk.red(\"-\"),\n move: chalk.cyan(\"โ\"),\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: ProgressItem[\"operation\"]): string {\n return icons[op];\n}\n\nexport function operationLabel(op: ProgressItem[\"operation\"]): 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 case \"move\":\n return chalk.cyan(\"moved\");\n }\n}\n\n/** ๊ฒฐ๊ณผ ์ ํ ์ค. `moved` ๋ push ๋ง ์ผ๋ค โ ์ฎ๊ธด ๋
ธํธ ยท ํด๋(๋ณธ๋ฌธ์ ํจ๊ป ๊ณ ์น ๊ฒ๋). */\nexport function summary(created: number, updated: number, deleted: number, moved = 0): 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 (moved > 0) parts.push(chalk.cyan(`${moved} moved`));\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\n/** ๋ก์ปฌ ์๊ฐ `YYYY-MM-DD HH:MM:SS`. ์ฝ์ ์ ์๋ ๊ฐ์ ๊ทธ๋๋ก ๋ณด์ธ๋ค โ NaN ์ผ๋ก ๋ฐ๊พธ์ง ์๋๋ค. */\nexport function localDateTime(iso: string): string {\n const date = new Date(iso);\n if (Number.isNaN(date.getTime())) return iso;\n const pad = (n: number): string => String(n).padStart(2, \"0\");\n return (\n `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())} ` +\n `${pad(date.getHours())}:${pad(date.getMinutes())}:${pad(date.getSeconds())}`\n );\n}\n\n/** ์ ์ฒด ๋์กฐํ ์ด์ . ์ฝ์ด์ ์ด์ ์ฝ๋๊ฐ ๋๋ฉด ์ฌ๊ธฐ๊ฐ ์ปดํ์ผ๋์ง ์๋๋ค โ ๋น ์ง ๋ง์ ์กฐ์ฉํ ๋์ง ์๋๋ค. */\nconst FULL_SCAN_REASON: Record<FullScanReason, string> = {\n first: \"first pull\",\n forced: \"--force\",\n \"database-mode\": \"database mode\",\n \"every-pull\": \"every pull\",\n due: \"scheduled\",\n};\n\n/**\n * ์ด๋ฒ์ ์๊ฒฉ์ ์ผ๋ง๋ ํ์๋ (ADR-027). ๋ฐ๋ ๊ฒ๋ง ์ฐพ์์ผ๋ฉด Notion ์์ ์ง์ด ๊ฒ์ ์์ง ๋ณผํธ์ ๋ฐ์๋์ง\n * ์์์ ์ ์๋ค โ ๊ทธ ์ฌ์ค๊ณผ ์ธ์ ๋ฐ์๋๋์ง๋ฅผ ํจ๊ป ์ ๋๋ค. ์ ์ง ์์ผ๋ฉด ยซNotion ์์ ์ง์ ๋๋ฐ ๋ณผํธ์\n * ๋จ์๋คยป ๋ฅผ ๊ฒฐํจ์ผ๋ก ์ฝ๋๋ค. ์๊ฒฉ ์ญ์ ๋ฅผ ๋ฐ์ํ์ง ์๋ ์ค์ (`sync.deleteSync` ๊บผ์ง)์ด๋ฉด ์ ์ง ์๋๋ค.\n */\nexport function remoteScanLines(scan: RemoteScanInfo): string[] {\n if (scan.kind === \"full\") {\n return [dimText(scan.reason ? `Full scan (${FULL_SCAN_REASON[scan.reason]})` : \"Full scan\")];\n }\n const skipped = scan.skippedDatabases ?? 0;\n const skippedText =\n skipped > 0\n ? ` ยท ${skipped} unchanged ${skipped === 1 ? \"database\" : \"databases\"} skipped`\n : \"\";\n const lines = [dimText(`Changes-only scan${skippedText}`)];\n if (scan.deletionsDeferred) lines.push(remoteDeletionHint(scan));\n return lines;\n}\n\n/** ๋ฏธ๋ฃฌ ์๊ฒฉ ์ญ์ ๊ฐ ์ธ์ ๋ฐ์๋๋ โ {@link RemoteScanInfo.deletionsDeferred} ์ผ ๋ ์ด๋ค. */\nexport function remoteDeletionHint(scan: RemoteScanInfo): string {\n const when = scan.nextFullAt\n ? `the next full scan (${localDateTime(scan.nextFullAt)})`\n : \"the next pull (full scan due)\";\n return `${dimText(`Deletions in Notion apply at ${when} โ or run`)} ${chalk.cyan(\"nobsi pull --force\")}`;\n}\n\n/** ๋ง์ง๋ง ์ ์ฒด ๋์กฐ ์๊ฐ โ ํ ๋ฒ๋ ์์ผ๋ฉด never. */\nexport function lastFullScanText(lastFullScanAt: string | null): string {\n return lastFullScanAt ? localDateTime(lastFullScanAt) : chalk.yellow(\"never\");\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\n/**\n * ์คํจ ํ ์ค. ๋ณผํธ ์ต์์์ ์คํจ(DB ์๋ ๋ฐ๊ฒฌ ๋ฑ)๋ ๋น ๊ฒฝ๋ก๋ก ์ค๋ฏ๋ก `/` ๋ก ๋ณด์ธ๋ค. `direction` ์\n * sync ๊ฐ ๋ฐ๊ธฐ(โผ) ยท ์ฌ๋ฆฌ๊ธฐ(โฒ)๋ฅผ ๊ฐ๋ฅผ ๋ ์ด๋ค.\n */\nexport function failedItem(path: string, error: string, direction?: string): void {\n const where = `${direction ? `${direction} ` : \"\"}${path || \"/\"}`;\n console.log(` ${icons.fail} ${where}: ${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\";\nimport { enableJsonMode, printJson, pushJson } from \"../utils/json-output.js\";\n\nexport const pushCommand = new Command(\"push\")\n .description(\"๋ก์ปฌ ๋ณ๊ฒฝ์ฌํญ์ Notion์ ๋ฐ์\")\n .option(\"--dry-run\", \"์ค์ ๋ฐ์ ์์ด ๋ณ๊ฒฝ์ฌํญ๋ง ํ์\")\n .option(\"-p, --path <paths...>\", \"ํน์ ๊ฒฝ๋ก๋ง push\")\n .option(\"--json\", \"๊ฒฐ๊ณผ๋ฅผ JSON ํ ์ค๋ก ์ถ๋ ฅ (์๋ํ์ฉ โ ๋ก๊ทธ๋ stderr)\")\n .action(async (options) => {\n const json = options.json === true;\n if (json) enableJsonMode();\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 if (!json) {\n console.log(`\\n${header(\" Pushing to Notion...\")}`);\n if (options.dryRun) console.log(dimText(\" (dry-run mode)\"));\n console.log(\"\");\n }\n\n const result = await orchestrator.push({\n dryRun: options.dryRun,\n paths: options.path,\n onProgress: json\n ? undefined\n : (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 (json) {\n printJson(pushJson(result));\n if (result.failed.length > 0) process.exitCode = 1;\n return;\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, result.moved)} ${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 { setLogger } from \"@im-nobsidian/core\";\nimport type { FailedOperation, PullResult, PushResult, RemoteScanInfo } from \"@im-nobsidian/core\";\n\n/**\n * `--json` ๋ชจ๋ โ ์๋ํ(E2E ํ๋์ค ยท CI)๊ฐ ์ฌ๋์ฉ ๋ฌธ๊ตฌ๋ฅผ grep ํ์ง ์๊ณ ๊ฒฐ๊ณผ๋ฅผ ์ฝ๊ฒ ํ๋ค.\n *\n * ๋ฌธ๊ตฌ grep ์ ์กฐ์ฉํ ํ๋ ธ๋ค. sync ๋ก๊ทธ์ Pull ์ชฝ \"no changes\" ๊ฐ ํ ์ค๋ง ์์ด๋ Push ์ชฝ\n * ๋ณ๊ฒฝ๊ณผ ๋ฌด๊ดํ๊ฒ churn 0 ์ผ๋ก ํ์ ํ๊ณ , restored ยท deleted ๋ ์์ ์ธ์ง ์์๋ค.\n *\n * ๊ณ์ฝ: stdout ์๋ JSON ํ ์ค๋ง ์ด๋ค. ์ฝ์ด ๋ก๊ทธ๋ ์ ๋ถ stderr ๋ก ๋๋ฆฐ๋ค.\n */\nexport function enableJsonMode(): void {\n const toStderr = (message: string, ...args: unknown[]): void => console.error(message, ...args);\n setLogger({ warn: toStderr, error: toStderr, info: toStderr, debug: () => {} });\n}\n\nexport interface PushJson {\n readonly created: number;\n readonly updated: number;\n readonly deleted: number;\n /** ์ฎ๊น์ ๋ฐ์ํ ๋
ธํธ ยท ํด๋ ์ โ updated ์ ๊ฒน์น์ง ์๋๋ค. */\n readonly moved: number;\n /** ์๊ฒฉ์ ์ค์ ๋ก ์ด(๋๋ dry-run ์ด๋ฉด ์ธ) ํญ๋ชฉ ์. ๋ฉฑ๋ฑ ํ์ ์ ๊ธฐ์ค๊ฐ. */\n readonly churn: number;\n readonly failed: readonly FailedOperation[];\n readonly durationMs: number;\n}\n\nexport interface PullJson {\n readonly created: number;\n readonly updated: number;\n readonly deleted: number;\n readonly restored: number;\n /** ๋ณผํธ์ ์ค์ ๋ก ์ด ํญ๋ชฉ ์ โ ๋ณต์๋ ์ฐ๊ธฐ๋ค. ๋ฉฑ๋ฑ ํ์ ์ ๊ธฐ์ค๊ฐ. */\n readonly churn: number;\n readonly conflicts: number;\n readonly failed: readonly FailedOperation[];\n readonly writtenPaths: number;\n readonly images: number;\n readonly files: number;\n readonly links: number;\n readonly durationMs: number;\n /** ์๊ฒฉ์ ์ผ๋ง๋ ํ์๋ โ ๋ฐ๋ ๊ฒ๋ง ์ฐพ์์ผ๋ฉด Notion ์์ ์ง์ด ๊ฒ์ ๋ค์ ์ ์ฒด ๋์กฐ๊ฐ ๋ฐ์ํ๋ค. */\n readonly remoteScan: RemoteScanInfo | null;\n}\n\nexport function pushJson(result: PushResult): PushJson {\n return {\n created: result.created,\n updated: result.updated,\n deleted: result.deleted,\n moved: result.moved,\n churn: result.created + result.updated + result.deleted + result.moved,\n failed: result.failed,\n durationMs: result.duration,\n };\n}\n\nexport function pullJson(result: PullResult): PullJson {\n return {\n created: result.created,\n updated: result.updated,\n deleted: result.deleted,\n restored: result.restored,\n churn: result.created + result.updated + result.deleted + result.restored,\n conflicts: result.conflicts.length,\n failed: result.failed,\n writtenPaths: result.writtenPaths.length,\n images: result.imageCount,\n files: result.fileCount,\n links: result.linkCount,\n durationMs: result.duration,\n remoteScan: result.remoteScan ?? null,\n };\n}\n\nexport function printJson(value: unknown): void {\n process.stdout.write(`${JSON.stringify(value)}\\n`);\n}\n","import { Command, Option } 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 remoteScanLines,\n} from \"../utils/format.js\";\nimport { enableJsonMode, printJson, pullJson } from \"../utils/json-output.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 // ๋ค์ ๊ทธ๋ ค ๋ด์ผ ๋ฐ๋๋์ง ์๋ค โ ๋ฏธ๋ฆฌ ๋ณด๊ธฐ๋ก๋ ๋ณด์ผ ๊ฒ์ด ์๋ค(B14).\n .addOption(\n new Option(\n \"--rerender\",\n \"Notion ์์ ๋ฐ๋์ง ์์ ๋ฐ์ ๋ ๋
ธํธ๋ ์ง๊ธ ๋ณํ๊ธฐ๋ก ๋ค์ ๊ทธ๋ฆผ (๋ณผํธ์์ ๊ณ ์น ๋
ธํธ๋ ๊ทธ๋๋ก)\",\n ).conflicts(\"dryRun\"),\n )\n .option(\"--json\", \"๊ฒฐ๊ณผ๋ฅผ JSON ํ ์ค๋ก ์ถ๋ ฅ (์๋ํ์ฉ โ ๋ก๊ทธ๋ stderr)\")\n .action(async (options) => {\n const json = options.json === true;\n if (json) enableJsonMode();\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 if (!json) {\n console.log(`\\n${header(\" Pulling from Notion...\")}`);\n if (options.dryRun) console.log(dimText(\" (dry-run mode)\"));\n console.log(\"\");\n }\n\n const result = await orchestrator.pull({\n dryRun: options.dryRun,\n paths: options.path,\n force: options.force,\n rerender: options.rerender,\n onProgress: json\n ? undefined\n : (current, total, item) => {\n // ๋ฐ์ ๋ดค์ง๋ง ๋ฐ๋์ง ์์ ๋
ธํธ โ ๋ค์ ๊ทธ๋ฆฌ๊ธฐ๋ ๋ฐ์ ๋ ๋
ธํธ๋ฅผ ๋ชจ๋ ๋ฐ์ ๋ณธ๋ค(B14).\n if (item.unchanged) return;\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 (json) {\n printJson(pullJson(result));\n if (result.failed.length > 0) process.exitCode = 1;\n return;\n }\n\n if (result.imageCount > 0) {\n console.log(\n `\\n ${icons.success} ${result.imageCount} images โ ${dimText(`${config.paths.attachments}/`)}`,\n );\n }\n if (result.fileCount > 0) {\n console.log(\n ` ${icons.success} ${result.fileCount} files โ ${dimText(`${config.paths.attachments}/`)}`,\n );\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 if (result.remoteScan) {\n for (const line of remoteScanLines(result.remoteScan)) console.log(` ${line}`);\n }\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 remoteScanLines,\n} from \"../utils/format.js\";\nimport { enableJsonMode, printJson, pullJson, pushJson } from \"../utils/json-output.js\";\n\nexport const syncCommand = new Command(\"sync\")\n .description(\"์๋ฐฉํฅ ๋๊ธฐํ (Pull โ Push)\")\n .option(\"--dry-run\", \"์ค์ ๋ฐ์ ์์ด ๋ณ๊ฒฝ์ฌํญ๋ง ํ์\")\n .option(\"--json\", \"๊ฒฐ๊ณผ๋ฅผ JSON ํ ์ค๋ก ์ถ๋ ฅ (์๋ํ์ฉ โ ๋ก๊ทธ๋ stderr)\")\n .action(async (options) => {\n const json = options.json === true;\n if (json) enableJsonMode();\n // ์งํ ๋ก๊ทธ๋ ์ฌ๋์ฉ์ด๋ค โ JSON ๋ชจ๋์์๋ stdout ๊ณ์ฝ(JSON ํ ์ค)์ ์งํค๋ ค๊ณ ๋๋ค.\n const log = json ? () => {} : (line: string) => console.log(line);\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 log(`\\n${header(\" Bidirectional Sync\")}`);\n log(` ${separator()}`);\n if (options.dryRun) log(dimText(\" (dry-run mode)\"));\n\n // ๋ฐ๊ธฐ ยท ์ฌ๋ฆฌ๊ธฐ๋ฅผ ํ ๋ฒ์ ๋๋ค โ ํ๋ฌ๊ทธ์ธ ยท watch ์ ๊ฐ์ sync ๋ค. push ๋ pull ์ด ๋ฐฉ๊ธ ์ด ๋
ธํธ๋ฅผ\n // ๋ค์ ์ฌ๋ฆฌ์ง ์๋๋ค(F-j). ์์ ์๋ pull ยท push ๋ฅผ ๋ฐ๋ก ๋ถ๋ฌ ๋ ยซsyncยป ๊ฐ ๋ฐ๋ก ์๋๋ค.\n log(`\\n ${header(chalk.blue(\"โผ Pull\"))} ${dimText(\"(Notion โ Obsidian)\")}`);\n let pushShown = false;\n const showPush = (): void => {\n if (pushShown) return;\n pushShown = true;\n log(`\\n ${header(chalk.magenta(\"โฒ Push\"))} ${dimText(\"(Obsidian โ Notion)\")}`);\n };\n\n const result = await orchestrator.sync({\n dryRun: options.dryRun,\n onProgress: (_current, _total, item) => {\n if (item.direction === \"push\") showPush();\n if (item.unchanged) return;\n const icon = operationIcon(item.operation);\n const label = operationLabel(item.operation);\n log(` ${icon} ${item.path} ${label}`);\n },\n });\n showPush();\n const { pull: pullResult, push: pushResult } = result;\n\n if (json) {\n const pull = pullJson(pullResult);\n const push = pushJson(pushResult);\n printJson({\n pull,\n push,\n churn: pull.churn + push.churn,\n conflicts: pull.conflicts,\n durationMs: result.duration,\n });\n if (pullResult.failed.length + pushResult.failed.length > 0) process.exitCode = 1;\n return;\n }\n\n // Summary\n // ์์ ์ ์์ชฝ failed ๋ฅผ ์์ ์ฐ์ง ์์, ์ ๊ฑด ์คํจํ sync ๋ ํ๋ฉด ๋์ ์ด๋ก \"complete\"\n // ์๋ค โ ์คํจ๋ฅผ ํค๋์ ๋ชฉ๋ก ์์ชฝ์ ๋๋ฌ๋ธ๋ค.\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 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 console.log(\n ` ${chalk.magenta(\"Push:\")} ${summary(pushResult.created, pushResult.updated, pushResult.deleted, pushResult.moved)}` +\n (pushFailed > 0 ? ` ${chalk.red(`${pushFailed} failed`)}` : \"\"),\n );\n console.log(` ${duration(result.duration)}`);\n if (pullResult.remoteScan) {\n for (const line of remoteScanLines(pullResult.remoteScan)) console.log(` ${line}`);\n }\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 (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 isRemoteDeletion,\n} from \"@im-nobsidian/core\";\nimport chalk from \"chalk\";\nimport {\n header,\n separator,\n icons,\n dimText,\n localDateTime,\n lastFullScanText,\n remoteDeletionHint,\n} 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 console.log(` ${dimText(\"Last sync:\")} ${localDateTime(status.lastSyncAt)}`);\n } else {\n console.log(` ${dimText(\"Last sync:\")} ${chalk.yellow(\"never\")}`);\n }\n // Notion ์์ ์ง์ด ๋
ธํธ๋ ์ ์ฒด ๋์กฐ ๋ ๋ณผํธ์ ๋ฐ์๋๋ค โ ๊ทธ๋๋ฅผ ํจ๊ป ๋ณด์ธ๋ค.\n console.log(` ${dimText(\"Full scan:\")} ${lastFullScanText(status.lastFullScanAt)}`);\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 const movedChanges = status.localChanges.filter((c) => c.type === \"moved\");\n\n // ์ถ์ ํ์ผ ๋ด์ญ์ ์๋ก ๊ฒน์น์ง ์์์ผ ํ๋ค. DB ์ status ๋ push/pull ์์ ์๋ง ๊ฐฑ์ ๋๋ฏ๋ก\n // ๋ฐฉ๊ธ ๊ณ ์น ํ์ผ๋ ๋ ์ฝ๋์์ผ๋ก ์ฌ์ ํ 'synced' ๋ค โ ๊ทธ๋๋ก ์ธ๋ฉด ๊ฐ์ ํ์ผ์ด synced ์\n // modified ์์ชฝ์ ์กํ ๋ด์ญ ํฉ์ด ์ด๊ณ๋ฅผ ๋๋ ํ๊ฐ ๋์จ๋ค(๋ฌด์์ด ๋ฐ๋ ธ๋์ง ๋ชป ์ฝ๋๋ค).\n // ์ค์ ๋ณ๊ฒฝ์ด ๊ฑธ๋ฆฐ ๊ฒฝ๋ก๋ฅผ ๋จผ์ ๋์ด ๋ด๊ณ ๋จ์ ๊ฒ๋ง synced ๋ก ์ผ๋ค.\n // ์ฎ๊ธด ๋
ธํธ์ ๋ ์ฝ๋๋ ์์ง ์ ๊ฒฝ๋ก(movedFrom)๋ฅผ ์ ๊ณ ์๋ค โ ๋ ๊ฒฝ๋ก๋ฅผ ๋ค ๋์ด ๋ธ๋ค.\n const dirtyPaths = new Set([\n ...modifiedChanges.map((c) => c.path),\n ...deletedChanges.map((c) => c.path),\n ...movedChanges.flatMap((c) => [c.path, c.movedFrom ?? 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 const remoteMoved = status.remoteChanges.filter((c) => c.type === \"moved\");\n const { folderMoves } = status;\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 (movedChanges.length > 0) {\n console.log(` ${icons.move} ${chalk.cyan(\"moved\")} ${movedChanges.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 // Moved โ ํด๋๋ฅผ ๋จผ์ ๋ณด์ธ๋ค. ๊ทธ ์์ ๋
ธํธ๋ ์๋์ ๋
ธํธ๋ง๋ค ๋ฐ๋ก ์๋ค.\n if (folderMoves.length + movedChanges.length > 0) {\n console.log(`\\n ${header(\"Moved:\")}`);\n for (const move of folderMoves) {\n console.log(` ${icons.move} ${move.from}/ โ ${move.to}/ ${dimText(\"(folder)\")}`);\n }\n for (const c of movedChanges.slice(0, 10)) {\n console.log(` ${icons.move} ${c.movedFrom ?? \"?\"} โ ${c.path}`);\n }\n if (movedChanges.length > 10) {\n console.log(dimText(` ... and ${movedChanges.length - 10} more`));\n }\n }\n\n // Deleted files โ ์๋ง ๋ณด์ด๋ฉด ๋ฌด์์ด Notion ์์ ์ง์์ง์ง ๋ชจ๋ฅธ๋ค.\n if (deletedChanges.length > 0) {\n console.log(`\\n ${header(\"Deleted files:\")}`);\n for (const c of deletedChanges.slice(0, 10)) {\n console.log(` ${chalk.red(\"-\")} ${c.path}`);\n }\n if (deletedChanges.length > 10) {\n console.log(dimText(` ... and ${deletedChanges.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 (remoteMoved.length > 0) {\n console.log(` ${icons.move} ${chalk.cyan(\"moved\")} ${remoteMoved.length}`);\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 : c.type === \"moved\"\n ? icons.move\n : chalk.red(\"-\");\n // ๋ณผํธ์ ์๋ ๋
ธํธ๋ ๊ฒฝ๋ก, ์์ง ์๋ ์ ํ์ด์ง๋ Notion ์ ๋ชฉ โ ๋ด๋ถ id ๋ ๋ ๋ค ์์ ๋๋ง.\n const name = c.path ?? (c.title ? `${c.title} ${dimText(\"(new page)\")}` : undefined);\n console.log(\n ` ${icon} ${name ?? `${c.pageId.slice(0, 8)}...`} ${dimText(`(${c.type})`)}`,\n );\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 // ์๊ฒฉ์ ์ฝ์ ์ํ(--full)๋ง Notion ์์ ์ง์ด ๋
ธํธ๋ฅผ ๊ฐ๋ฅธ๋ค.\n const remoteDeleted = new Set(\n status.conflicts.filter(isRemoteDeletion).map((c) => c.syncRecord.obsidianPath),\n );\n for (const r of status.conflictRecords) {\n const reason = remoteDeleted.has(r.obsidianPath)\n ? \"(deleted in Notion, local edits not pushed)\"\n : \"(both sides changed)\";\n console.log(` ${icons.conflict} ${r.obsidianPath.padEnd(30)} ${dimText(reason)}`);\n }\n }\n\n // Helpful commands\n const hasChanges =\n status.localChanges.length > 0 ||\n folderMoves.length > 0 ||\n status.remoteChanges.length > 0 ||\n 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 // ๋๋๋ฆด ์ ์๋ ๊ฒ(๊ณ ์น ยท ์ง์ด ๋
ธํธ)์ด ์์ ๋๋ง โ git status ์ `git restore` ์๋ด์ ๊ฐ๋ค.\n if (modifiedChanges.length + deletedChanges.length > 0) {\n console.log(\n ` ${dimText(\"Run\")} ${chalk.cyan(\"nobsi discard <path>\")} ${dimText(\"to discard local edits\")}`,\n );\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 } else if (status.remoteScan?.deletionsDeferred) {\n // ๋ฐ๋ ๊ฒ๋ง ์ฐพ์๋ค โ ์ ์๊ฒฉ ๋ณ๊ฒฝ์๋ Notion ์์ ์ง์ด ๊ฒ์ด ์์ง ์๋ค.\n console.log(`\\n ${remoteDeletionHint(status.remoteScan)}`);\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 formatUnifiedDiff,\n lineDiff,\n matchesPathScope,\n} from \"@im-nobsidian/core\";\nimport type { ChangeDiff } from \"@im-nobsidian/core\";\nimport { printUnifiedDiff } from \"../utils/diff-output.js\";\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 let failed = 0;\n for (const change of changes) {\n // --remote: ๋ง์ง๋ง ์ค๋
์ท์ด ์๋๋ผ Notion ์ ํ์ฌ ๋ณธ๋ฌธ์ ๊ธฐ์ค์ ์ผ๋ก ์ผ๋๋ค.\n // ์๊ฒฉ์ด ๊ทธ ์ฌ์ด ๋ฐ๋์์ ๋ \"๋ด๊ฐ ๋ญ ๋ฎ์ด์ฐ๊ฒ ๋๋์ง\"๋ ์ด ๋น๊ต๋ก๋ง ๋ณด์ธ๋ค.\n if (options.remote && change.type !== \"deleted\") {\n // ์ฎ๊ธด ๋
ธํธ๋ Notion ์ ๋ฐ์ํ๊ธฐ ์ ๊น์ง ์ถ์ ๊ธฐ๋ก์ด ์ ์๋ฆฌ์ ์๋ค โ Notion ์ชฝ ์ด๋ฆ๋ ๊ทธ ์๋ฆฌ๋ค.\n let remotePath = change.path;\n let remote = await orchestrator.renderRemoteSnapshot(remotePath);\n if (remote === null && change.movedFrom) {\n remotePath = change.movedFrom;\n remote = await orchestrator.renderRemoteSnapshot(remotePath);\n }\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 hunks = lineDiff(remote, currentContent);\n const lines = formatUnifiedDiff(hunks, `notion/${remotePath}`, `b/${change.path}`);\n // ์ด๋ฆ ์ค๋ง ์ฐ๊ณ ๋๋๋ฉด ๊ฐ์ ๊ฒ์ธ์ง ๋ชป ์ฝ์ ๊ฒ์ธ์ง ์ ์ ์๋ค โ ๊ฐ๋ค๊ณ ์ ๋๋ค.\n if (hunks.length === 0) lines.push(\"(Notion ๊ณผ ๋ด์ฉ์ด ๊ฐ์ต๋๋ค)\");\n printUnifiedDiff(lines, options.color);\n console.log(\"\");\n continue;\n }\n\n // ์ง๋ ๋๊ธฐํ ๋์ ๊ธ๊ณผ ์ง๊ธ ๊ธ โ ํ๋ฌ๊ทธ์ธ ๋ณ๊ฒฝ ํจ๋์ ๋น๊ต์ ๊ฐ์ ๋ ๊ธ์ด๋ค.\n let diff: ChangeDiff;\n try {\n diff = await orchestrator.localChangeDiff(change);\n } catch (error) {\n failed++;\n console.log(`--- a/${change.path}`);\n console.log(`(${error instanceof Error ? error.message : String(error)})`);\n console.log(\"\");\n continue;\n }\n printChangeDiff(diff, options.color);\n console.log(\"\");\n }\n // ๋ณด์ด์ง ๋ชปํ ๋น๊ต๊ฐ ์์ผ๋ฉด ์คํจ๋ก ๋๋ธ๋ค โ ์คํฌ๋ฆฝํธ๊ฐ ๋น ์ง ๊ฒ์ ์ ์ ์๊ฒ.\n if (failed > 0) process.exitCode = 1;\n } finally {\n stateDb.close();\n }\n });\n\n/** ์ฎ๊ธด ๋
ธํธ๋ Git ์ฒ๋ผ ์ ์๋ฆฌ ยท ์ ์๋ฆฌ๋ฅผ ๋จผ์ ์ ๋๋ค. ๋ด์ฉ์ด ๊ฐ์ผ๋ฉด ๋น๊ต ๋์ ๊ทธ๋ ๋ค๊ณ ์๋ฆฐ๋ค. */\nfunction printChangeDiff(diff: ChangeDiff, color: boolean): void {\n const oldPath = diff.before === null ? \"/dev/null\" : `a/${diff.movedFrom ?? diff.path}`;\n const newPath = diff.after === null ? \"/dev/null\" : `b/${diff.path}`;\n if (diff.movedFrom) {\n console.log(`rename from ${diff.movedFrom}`);\n console.log(`rename to ${diff.path}`);\n if (diff.before === diff.after) {\n console.log(\"(๋ด์ฉ์ ๊ทธ๋๋ก์
๋๋ค โ ์๋ฆฌ๋ง ์ฎ๊ฒผ์ต๋๋ค)\");\n return;\n }\n }\n printUnifiedDiff(\n formatUnifiedDiff(lineDiff(diff.before ?? \"\", diff.after ?? \"\"), oldPath, newPath),\n color,\n );\n}\n","import { DIFF_SIGN } from \"@im-nobsidian/core\";\n\nconst BOLD = \"\\x1b[1m\";\nconst RED = \"\\x1b[31m\";\nconst GREEN = \"\\x1b[32m\";\nconst CYAN = \"\\x1b[36m\";\nconst RESET = \"\\x1b[0m\";\n\n/** ์ค ์ฒซ ๊ธ์๋ง๋ค์ ์ โ ๋ํ ์ค ยท ์ง์ด ์ค์ ๋ถํธ๋ core ์ ๊ฒ์ ๋ฐ๋ฅธ๋ค. `@` ๋ ๋ฌถ์ ๋จธ๋ฆฌ. */\nconst LINE_COLOR: Readonly<Record<string, string>> = {\n [DIFF_SIGN.added]: GREEN,\n [DIFF_SIGN.removed]: RED,\n \"@\": CYAN,\n};\n\n/**\n * ํตํฉ diff ์ค(core `formatUnifiedDiff`)์ ์ฐ๋๋ค โ `diff` ยท `resolve` ๊ฐ ๊ฐ์ ๋ชจ์ ยท ๊ฐ์ ์์ผ๋ก ๋ณด์ธ๋ค.\n *\n * ์ ๊ธ ยท ์ ๊ธ ์ด๋ฆ ์ค์ ์ฒซ ๊ธ์๊ฐ ์๋๋ผ ์๋ฆฌ(์ ๋ ์ค)๋ก ๊ฐ๋ฅธ๋ค โ ๊ตฌ๋ถ์ (`---`)์ ์ง์ด ์ค์ `----` ๋ก\n * ์์ํด ์ฒซ ๊ธ์๋ก๋ ์ด๋ฆ ์ค๊ณผ ๊ตฌ๋ณ๋์ง ์๋๋ค.\n */\nexport function printUnifiedDiff(lines: readonly string[], color: boolean): void {\n lines.forEach((line, index) => {\n const code = index < 2 ? BOLD : LINE_COLOR[line.charAt(0)];\n console.log(color && code ? `${code}${line}${RESET}` : line);\n });\n}\n","import { Command } from \"commander\";\nimport {\n ConfigManager,\n StateDB,\n NotionClient,\n SyncOrchestrator,\n NodeVaultFS,\n} from \"@im-nobsidian/core\";\nimport { icons, failedItem } from \"../utils/format.js\";\n\n// ๋ณ๊ฒฝ ๋ชฉ๋ก(status)์์ ๊ณ ๋ฅธ ๋
ธํธ๋ฅผ ์ง๋ ๋๊ธฐํ ๋์ ๊ธ๋ก ๋๋๋ฆฐ๋ค โ Git ์ `restore` ์ ๊ฐ๋ค.\n// ํ๋ฌ๊ทธ์ธ ๋ณ๊ฒฝ ํจ๋์ โบ ์ ๊ฐ์ ๋์(orchestrator.discardLocalChange)์ด๊ณ Notion ์ ๊ฑด๋๋ฆฌ์ง ์๋๋ค.\n// ํ๋๋ฅผ ๋๋๋ฆฌ์ง ๋ชปํด๋ ๋๋จธ์ง๋ ๋๋๋ฆฐ๋ค โ ๋ฌด์์ด ์ ์ ๋๋์ง ๊ฒฝ๋ก๋ง๋ค ์๋ฆฌ๊ณ ์ข
๋ฃ ์ฝ๋๋ก ๋จ๊ธด๋ค.\nexport const discardCommand = new Command(\"discard\")\n .description(\"๋ก์ปฌ ๋ณ๊ฒฝ์ ์ง๋ ๋๊ธฐํ ๋์ ๊ธ๋ก ๋๋๋ฆฌ๊ธฐ (Notion ์ ๊ทธ๋๋ก)\")\n .argument(\"<paths...>\", \"๋๋๋ฆด ๋
ธํธ ๊ฒฝ๋ก โ status ๊ฐ ๋ณด์ธ ๋ณผํธ ๊ธฐ์ค ๊ฒฝ๋ก\")\n .action(async (paths: 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 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 let failed = 0;\n for (const path of paths) {\n try {\n await orchestrator.discardLocalChange(path);\n console.log(` ${icons.success} ${path}`);\n } catch (error) {\n failed++;\n failedItem(path, error instanceof Error ? error.message : String(error));\n }\n }\n if (failed > 0) process.exitCode = 1;\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, 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 applicableChoices,\n isRemoteDeletion,\n} from \"@im-nobsidian/core\";\nimport type { Conflict, ResolutionChoice } from \"@im-nobsidian/core\";\nimport { printUnifiedDiff } from \"../utils/diff-output.js\";\nimport { describeFailure } from \"../utils/state-db-failure.js\";\n\n/** ํ์ฉ ์ ๋ต โ ์คํ๋ฅผ ์กฐ์ฉํ ํ๋ฆฌ์ง ์๋๋ก ์คํ ์ ์ ํ ๋ฒ ๊ฒ์ฆํ๋ค. */\nconst STRATEGIES = [\"local-first\", \"remote-first\", \"duplicate\"] as const;\ntype Strategy = (typeof STRATEGIES)[number];\n\nconst CHOICE_NAMES: Record<ResolutionChoice, string> = {\n local: \"๋ก์ปฌ ์ ์ง (Obsidian ๋ฒ์ ์ ์ง)\",\n remote: \"์๊ฒฉ ์ ์ง (Notion ๋ฒ์ ์ผ๋ก ๋ฎ์ด์ฐ๊ธฐ)\",\n merge: \"์๋ ๋ณํฉ (3-way merge)\",\n duplicate: \"๋ณต์ (์๋ณธ ์ ์ง + ์ฌ๋ฆฌ์ง ์๋ .conflict ์ฌ๋ณธ ์์ฑ)\",\n};\n\n/** Notion ์์ ์ง์ด ๋
ธํธ๋ ๊ฐ์ ์ ํ์ด ๋ค๋ฅธ ์ผ์ ํ๋ค โ ๋ก์ปฌ ์ ์ง๋ ๋ค์ ๋ง๋ค๊ณ , ์๊ฒฉ ์ ์ง๋ ์ง์ด๋ค. */\nconst REMOTE_DELETION_CHOICE_NAMES: Partial<Record<ResolutionChoice, string>> = {\n local: \"๋ก์ปฌ ์ ์ง (Notion ์ ์ ํ์ด์ง๋ก ๋ค์ ๋ง๋ค๊ธฐ)\",\n remote: \"์ญ์ ๋ฐ๋ฅด๊ธฐ (Obsidian ํ์ผ๋ ์ง์ฐ๊ธฐ)\",\n};\n\n/** ๋ฐ๊ธฐ ์ ์ฉ(direction: pull)์ ํด๊ฒฐ ๊ฒฐ๊ณผ๋ฅผ Notion ์ ์ฌ๋ฆฌ์ง ์๋๋ค โ ๋ค์ ๋ง๋ค์ง๋ ์๋๋ค. */\nconst PULL_ONLY_REMOTE_DELETION_CHOICE_NAMES: Partial<Record<ResolutionChoice, string>> = {\n local: \"๋ก์ปฌ ์ ์ง (Obsidian ํ์ผ๋ง ๋๊ธฐ โ ๋ฐ๊ธฐ ์ ์ฉ์ด๋ผ Notion ์ ๋ง๋ค์ง ์์)\",\n};\n\nfunction choiceName(conflict: Conflict, choice: ResolutionChoice, uploads: boolean): string {\n const deleted = isRemoteDeletion(conflict);\n return (\n (deleted && !uploads ? PULL_ONLY_REMOTE_DELETION_CHOICE_NAMES[choice] : undefined) ??\n (deleted ? REMOTE_DELETION_CHOICE_NAMES[choice] : undefined) ??\n CHOICE_NAMES[choice] ??\n choice\n );\n}\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 const uploads = config.sync.direction !== \"pull\";\n if (!uploads) {\n console.log(\n \" ๋ฐ๊ธฐ ์ ์ฉ(direction: pull) โ ํด๊ฒฐ ๊ฒฐ๊ณผ๋ Obsidian ์๋ง ๋๊ณ Notion ์๋ ์ฌ๋ฆฌ์ง ์์ต๋๋ค\\n\",\n );\n }\n\n if (options.strategy) {\n const strategy = options.strategy as Strategy;\n // ์๊ฒฉ์์ ์ง์ด ๋
ธํธ๋ ์ ๋ต์ด ํ์ผ์ ์ง์ฐ๊ฑฐ๋ ํ์ด์ง๋ฅผ ์๋ก ๋ง๋ ๋ค โ ๋ฌป๊ธฐ ์ ์ ์๋ฆฐ๋ค.\n const deletions = pending.filter(isRemoteDeletion).length;\n if (deletions > 0) {\n console.log(\n ` Notion ์์ ์ญ์ ๋ ๋
ธํธ ${deletions}๊ฑด โ ${\n strategy === \"remote-first\"\n ? \"Obsidian ํ์ผ๋ ์ง์๋๋ค\"\n : uploads\n ? \"Notion ์ ์ ํ์ด์ง๋ก ๋ค์ ๋ง๋ญ๋๋ค\"\n : \"Obsidian ํ์ผ์ ๊ทธ๋๋ก ๋ก๋๋ค\"\n }`,\n );\n }\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 [index, result] of results.entries()) {\n if (!result.success) failed++;\n const icon = result.success ? \"\\x1b[32mโ\\x1b[0m\" : \"\\x1b[31mโ\\x1b[0m\";\n const reason = result.error ? ` โ ${result.error}` : \"\";\n console.log(\n ` ${icon} ${result.path} โ ${choiceName(pending[index]!, result.choice, uploads)}${reason}`,\n );\n }\n report(results.length - failed, failed);\n return;\n }\n\n if (!isInteractive()) {\n console.error(\n `๋น๋ํํ ํ๊ฒฝ์์๋ ๋ํํ ํด๊ฒฐ์ ์ธ ์ ์์ต๋๋ค โ --strategy <${STRATEGIES.join(\"|\")}> --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, uploads)) resolved++;\n else failed++;\n }\n report(resolved, failed);\n } catch (error) {\n console.error(\"์ค๋ฅ:\", describeFailure(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 uploads: boolean,\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 if (isRemoteDeletion(conflict)) {\n // ๊ฒฌ์ค ์๊ฒฉ ๋ณธ๋ฌธ์ด ์๋ค โ ์ค ๋น๊ต๋ ๋ก์ปฌ ์ ๋ถ๋ฅผ ์ง์ด ๊ฒ์ผ๋ก ๋ณด์ฌ ์ค๋ค.\n console.log(\n \"\\x1b[33mNotion ์์ ์ญ์ ๋ ๋
ธํธ์
๋๋ค โ Notion ์ ์ฌ๋ฆฌ์ง ์์ ๋ก์ปฌ ํธ์ง์ด ๋จ์ ์์ต๋๋ค.\\x1b[0m\",\n );\n } else {\n printUnifiedDiff(orchestrator.generateConflictDiff(conflict).split(\"\\n\"), true);\n }\n\n const choice = await select<ResolutionChoice>({\n message: \"์ด๋ป๊ฒ ํด๊ฒฐํ ๊น์?\",\n choices: applicableChoices(conflict).map((value) => ({\n name: choiceName(conflict, value, uploads),\n value,\n })),\n });\n\n const result = await orchestrator.resolveConflict(conflict, choice);\n\n if (result.success) {\n console.log(`\\x1b[32mโ ${path} โ ${choiceName(conflict, choice, uploads)}\\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","/**\n * ์ํ DB ๋ฅผ ์ธ ์ ์์ ๋์ CLI ์ถ๋ ฅ โ ์ด์ ๋ค์ ํ ์ผ์ ๋ถ์ธ๋ค.\n *\n * ๋ช
๋ น์ ์ฒ์์ ์ํ DB ๋ฅผ ์ฐ๋ค(`StateDB.open`). ํ์ผ์ด ๋น์๊ฑฐ๋ ์๋ ธ๊ฑฐ๋(`SavedStateDbError`), ํ๋ฌ๊ทธ์ธ์ด ๊ฐ์\n * ๋ณผํธ๋ฅผ ์ฐ๋ ์ค์ด๋ฉด(`StateDbLockedError`) ๋์ง๋๋ฐ, ๋ช
๋ น ๋ฐ์ผ๋ก ๋์ค๋ฉด Node ๊ฐ ์ฝ๋ ์์น(์คํ)์ ํจ๊ป ์ด์ ๋ง\n * ๋ณด์๋ค โ ์ฌ์ฉ์๊ฐ ํ ์ผ์ ์ด๋์๋ ์์๋ค.\n */\nimport { StateDbUnavailableError } from \"@im-nobsidian/core\";\nimport type { ExitableProcess } from \"./exit.js\";\n\n/** ์คํจ๋ฅผ ๋ณด์ผ ๋ง โ ์ํ DB ๋ฅผ ์ธ ์ ์๋ ๊ฒ์ด๋ฉด ํ ์ผ์ ๋ถ์ด๊ณ , ๋ค๋ฅธ ์คํจ๋ ๊ทธ ๋ง ๊ทธ๋๋ก๋ค. */\nexport function describeFailure(error: unknown): string {\n const message = error instanceof Error ? error.message : String(error);\n if (!(error instanceof StateDbUnavailableError)) return message;\n return `${message} โ ${error.guidance(\"๋ช
๋ น์ ๋ค์ ์คํํ์ธ์\")}`;\n}\n\n/**\n * ๋ช
๋ น ๋ฐ์ผ๋ก ๋์จ ์คํจ๋ฅผ ์๋ฆฐ๋ค. ์ํ DB ๋ฅผ ์ธ ์ ์๋ ๊ฒ์ ์ฌ์ฉ์๊ฐ ํ ์ผ์ด๋ผ ์ด์ ์ ํ ์ผ๋ง ๋ณด์ด๊ณ ์คํจ(1)๋ก\n * ๋๋ธ๋ค. ๋ค๋ฅธ ์คํจ๋ ๊ทธ๋๋ก ๋ค์ ๋์ง๋ค โ ์ง๊ธ๊น์ง์ฒ๋ผ Node ๊ฐ ์คํ๊ณผ ํจ๊ป ๋ณด์ด๊ณ 1 ๋ก ๋๋๋ค.\n */\nexport function reportStateDbFailure(error: unknown, proc: ExitableProcess): void {\n if (!(error instanceof StateDbUnavailableError)) throw error;\n console.error(`์ค๋ฅ: ${describeFailure(error)}`);\n proc.exitCode = 1;\n}\n","import { Command } from \"commander\";\nimport {\n ConfigManager,\n StateDB,\n NotionClient,\n SyncOrchestrator,\n NodeVaultFS,\n WatchSyncService,\n DEFAULT_WATCH_DEBOUNCE_MS,\n} from \"@im-nobsidian/core\";\nimport type { SyncResult, WatchSyncScope } 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 moved = result.push.moved > 0 ? ` โ${result.push.moved}` : \"\";\n const push = `Push: +${result.push.created} ~${result.push.updated} -${result.push.deleted}${moved}`;\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\n/** ๋ก๊ทธ์ sync ์ด๋ฆ โ ์ฃผ๊ธฐ sync ๋ ๋ณผํธ ์ ์ฒด๋ฅผ ๋ณธ๋ค. */\nfunction syncLabel(scope: WatchSyncScope): string {\n return scope === \"full\" ? \"์ฃผ๊ธฐ์ ๋๊ธฐํ\" : \"๋๊ธฐํ\";\n}\n\nexport const watchCommand = new Command(\"watch\")\n .description(\"ํ์ผ ๋ณ๊ฒฝ ๊ฐ์ง + ์๋ ๋๊ธฐํ\")\n .option(\"-d, --debounce <ms>\", \"๋๋ฐ์ด์ค ๋๊ธฐ ์๊ฐ(ms)\", String(DEFAULT_WATCH_DEBOUNCE_MS))\n .option(\"-i, --interval <seconds>\", \"ํ ๋๊ธฐํ ์ฃผ๊ธฐ(์ด, 0์ด๋ฉด ๋นํ์ฑ)\", \"0\")\n .action(async (options: { debounce: string; interval: string }) => {\n // ์ซ์๊ฐ ์๋๋ฉด ๋๋ฐ์ด์ค๊ฐ 0 ์ด ๋๊ฑฐ๋ ์ฃผ๊ธฐ ๋๊ธฐํ๊ฐ ๋ง์์ด ๊บผ์ง๋ค โ ์์ํ๊ธฐ ์ ์ ๊ฑฐ์ ํ๋ค.\n const debounceMs = Number(options.debounce);\n const intervalSec = Number(options.interval);\n if (!Number.isInteger(debounceMs) || debounceMs < 0) {\n console.error(`--debounce ๋ 0 ์ด์์ ์ ์(ms)์ฌ์ผ ํฉ๋๋ค: ${options.debounce}`);\n process.exitCode = 1;\n return;\n }\n if (!Number.isInteger(intervalSec) || intervalSec < 0) {\n console.error(`--interval ์ 0 ์ด์์ ์ ์(์ด)์ฌ์ผ ํฉ๋๋ค: ${options.interval}`);\n process.exitCode = 1;\n return;\n }\n\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 service = new WatchSyncService(cwd, orchestrator, {\n debounceMs,\n onFileChange: (event, path) => {\n console.log(`[${timestamp()}] ${event}: ${path}`);\n },\n onSyncStart: (scope) => {\n console.log(`[${timestamp()}] ${syncLabel(scope)} ์์...`);\n },\n onSyncComplete: (result, scope) => {\n console.log(`[${timestamp()}] ${syncLabel(scope)} ์๋ฃ โ ${formatResult(result)}`);\n },\n onSyncCancelled: (result, scope) => {\n console.log(\n `[${timestamp()}] ${syncLabel(scope)} ์ทจ์ โ ๋ฉ์ถ๊ธฐ ์ ๊น์ง ${formatResult(result)}`,\n );\n },\n onSyncError: (error, scope) => {\n console.error(`[${timestamp()}] ${syncLabel(scope)} ์คํจ: ${error.message}`);\n },\n });\n\n let pullTimer: ReturnType<typeof setInterval> | null = null;\n let shuttingDown = false;\n\n const shutdown = async (): Promise<void> => {\n // ๋๋ sync ๋ฅผ ๊ธฐ๋ค๋ฆฌ๋ ์ค์ ํ ๋ฒ ๋ ๋๋ฅด๋ฉด ๊ธฐ๋ค๋ฆฌ์ง ์๊ณ ๋๋ธ๋ค.\n if (shuttingDown) {\n console.log(`[${timestamp()}] ๊ฐ์ ์ข
๋ฃ`);\n return process.exit(1);\n }\n shuttingDown = true;\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 // ํ์ผ ๋ณ๊ฒฝ sync ์ ๊ฐ์ ์ค์ ์ธ์ด๋ค โ ๋๋ sync ๊ฐ ์์ผ๋ฉด ๋๋ ๋ค ํ ๋ฒ ๋๋ค(S-09).\n pullTimer = setInterval(() => service.requestFullSync(), 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","remoteDeleted","Command","ConfigManager","StateDB","NotionClient","SyncOrchestrator","NodeVaultFS","Command","ConfigManager","StateDB","NotionClient","NodeVaultFS","SyncOrchestrator","Command","ConfigManager","StateDB","NotionClient","SyncOrchestrator","NodeVaultFS","Command","ConfigManager","StateDB","NotionClient","NodeVaultFS","SyncOrchestrator","Command","ConfigManager","StateDB","NotionClient","SyncOrchestrator","NodeVaultFS","chalk","Command","ConfigManager","StateDB","NotionClient","NodeVaultFS","SyncOrchestrator","chalk","Command","confirm","ConfigManager","StateDB","NotionClient","SyncOrchestrator","NodeVaultFS","isRemoteDeletion","isRemoteDeletion","Command","ConfigManager","StateDB","NotionClient","NodeVaultFS","SyncOrchestrator","confirm","failed","Command","ConfigManager","StateDB","NotionClient","SyncOrchestrator","NodeVaultFS","Command","ConfigManager","StateDB","NotionClient","SyncOrchestrator","NodeVaultFS","chalk","report","chalk","Command","ConfigManager","StateDB","NotionClient","NodeVaultFS","SyncOrchestrator","Command"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/commands/init.ts","../src/utils/format.ts","../src/utils/vault.ts","../src/commands/push.ts","../src/utils/json-output.ts","../src/commands/pull.ts","../src/commands/sync.ts","../src/commands/status.ts","../src/commands/diff.ts","../src/utils/diff-output.ts","../src/commands/discard.ts","../src/commands/fetch.ts","../src/commands/resolve.ts","../src/utils/state-db-failure.ts","../src/commands/watch.ts","../src/commands/verify.ts","../src/commands/prepare-db.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 { discardCommand } from \"./commands/discard.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 { prepareDbCommand } from \"./commands/prepare-db.js\";\nimport { scheduleForcedExit } from \"./utils/exit.js\";\nimport { reportStateDbFailure } from \"./utils/state-db-failure.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(discardCommand);\nprogram.addCommand(fetchCommand);\nprogram.addCommand(resolveCommand);\nprogram.addCommand(watchCommand);\nprogram.addCommand(verifyCommand);\nprogram.addCommand(prepareDbCommand);\n\n// ๊ฐ์ ์ข
๋ฃํ๋ ๋ช
๋ น์ด ์ ํ ์ข
๋ฃ ์ฝ๋๋ฅผ ๋ณด์กดํ๋ค โ ๊ทผ๊ฑฐ๋ utils/exit.ts ์ฐธ์กฐ(R11-C).\n// ์ํ DB ๋ฅผ ์ธ ์ ์์ด ๋ช
๋ น์ด ๋์ก์ผ๋ฉด ์ด์ ์ ํ ์ผ์ ๋ณด์ด๊ณ 1 ๋ก ๋๋ธ๋ค โ utils/state-db-failure.ts.\nprogram.parseAsync().then(\n () => {\n scheduleForcedExit(process);\n },\n (error: unknown) => {\n reportStateDbFailure(error, process);\n scheduleForcedExit(process);\n },\n);\n","import { Command } from \"commander\";\nimport {\n ConfigManager,\n NOTION_TOKEN_PREFIX,\n NotionClient,\n notionIdFromInput,\n} 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\";\nimport { outerConnectionTracking } from \"../utils/vault.js\";\n\n/** ๋ฃจํธ ํ์ด์ง์์ ID ๋ฅผ ๋ชป ์ฐพ์์ ๋ โ ์ฃผ์๋ฅผ ๊ทธ๋๋ก ๋ถ์ฌ ๋ฃ์ด๋ ๋๋ค๊ณ ๋งํ๋ค. */\nconst ROOT_PAGE_UNREADABLE =\n \"๋ฃจํธ ํ์ด์ง์์ Notion ํ์ด์ง ID(32์๋ฆฌ)๋ฅผ ์ฐพ์ง ๋ชปํ์ต๋๋ค โ ํ์ด์ง ์ฃผ์๋ ID ๋ฅผ ๋ฃ์ผ์ธ์.\";\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 // ๋ฐ๊นฅ ์ฐ๊ฒฐ์ด ์ด๋ฏธ ์ด ํด๋๋ฅผ ๋๊ธฐํํ๋ฉด ๋ฐ๋ก ์ฐ๊ฒฐํ์ง ์๋๋ค(ADR-038) โ ๋ฐ๊นฅ ์ฐ๊ฒฐ์ด ๋ฉ์ถ๋ค.\n const outer = outerConnectionTracking(cwd);\n if (outer) {\n console.error(\n `์ด ํด๋๋ ์ด๋ฏธ ${outer.folder} ์ ์ฐ๊ฒฐ์ด ๋
ธํธ ${outer.notes}๊ฐ๋ฅผ ๋๊ธฐํํ๊ณ ์์ด ๋ฐ๋ก ์ฐ๊ฒฐํ ์ ์์ต๋๋ค.`,\n );\n process.exitCode = 1;\n return;\n }\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(NOTION_TOKEN_PREFIX)) {\n console.error(`ํ ํฐ์ ${NOTION_TOKEN_PREFIX}์ผ๋ก ์์ํด์ผ ํฉ๋๋ค.`);\n process.exitCode = 1;\n return;\n }\n\n const rootPageId = notionIdFromInput(options.rootPageId);\n if (rootPageId === null) {\n console.error(ROOT_PAGE_UNREADABLE);\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 });\n console.log(`\\n ${icons.success} Config saved to ${dimText(configManager.configPath)}`);\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) =>\n v.startsWith(NOTION_TOKEN_PREFIX) ||\n `${NOTION_TOKEN_PREFIX}์ผ๋ก ์์ํ๋ ํ ํฐ์ ์
๋ ฅํ์ธ์`,\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 rootPageInput = await input({\n message: \"๋ฃจํธ ํ์ด์ง ID ๋๋ ์ฃผ์ (์์์ ์ ํํ๊ฑฐ๋ ์ง์ ์
๋ ฅ):\",\n default: result.results[0]?.id,\n validate: (value) => notionIdFromInput(value) !== null || ROOT_PAGE_UNREADABLE,\n });\n const rootPageId = notionIdFromInput(rootPageInput)!;\n\n await configManager.init({ token, rootPageId });\n console.log(`\\n ${icons.success} Config saved to ${dimText(configManager.configPath)}`);\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\";\nimport type { FullScanReason, ProgressItem, RemoteScanInfo } from \"@im-nobsidian/core\";\n\nexport const icons = {\n create: chalk.green(\"+\"),\n update: chalk.yellow(\"~\"),\n delete: chalk.red(\"-\"),\n move: chalk.cyan(\"โ\"),\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: ProgressItem[\"operation\"]): string {\n return icons[op];\n}\n\nexport function operationLabel(op: ProgressItem[\"operation\"]): 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 case \"move\":\n return chalk.cyan(\"moved\");\n }\n}\n\n/** ๊ฒฐ๊ณผ ์ ํ ์ค. `moved` ๋ push ๋ง ์ผ๋ค โ ์ฎ๊ธด ๋
ธํธ ยท ํด๋(๋ณธ๋ฌธ์ ํจ๊ป ๊ณ ์น ๊ฒ๋). */\nexport function summary(created: number, updated: number, deleted: number, moved = 0): 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 (moved > 0) parts.push(chalk.cyan(`${moved} moved`));\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\n/** ๋ก์ปฌ ์๊ฐ `YYYY-MM-DD HH:MM:SS`. ์ฝ์ ์ ์๋ ๊ฐ์ ๊ทธ๋๋ก ๋ณด์ธ๋ค โ NaN ์ผ๋ก ๋ฐ๊พธ์ง ์๋๋ค. */\nexport function localDateTime(iso: string): string {\n const date = new Date(iso);\n if (Number.isNaN(date.getTime())) return iso;\n const pad = (n: number): string => String(n).padStart(2, \"0\");\n return (\n `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())} ` +\n `${pad(date.getHours())}:${pad(date.getMinutes())}:${pad(date.getSeconds())}`\n );\n}\n\n/** ์ ์ฒด ๋์กฐํ ์ด์ . ์ฝ์ด์ ์ด์ ์ฝ๋๊ฐ ๋๋ฉด ์ฌ๊ธฐ๊ฐ ์ปดํ์ผ๋์ง ์๋๋ค โ ๋น ์ง ๋ง์ ์กฐ์ฉํ ๋์ง ์๋๋ค. */\nconst FULL_SCAN_REASON: Record<FullScanReason, string> = {\n first: \"first pull\",\n forced: \"--force\",\n \"database-mode\": \"database mode\",\n \"every-pull\": \"every pull\",\n due: \"scheduled\",\n};\n\n/**\n * ์ด๋ฒ์ ์๊ฒฉ์ ์ผ๋ง๋ ํ์๋ (ADR-027). ๋ฐ๋ ๊ฒ๋ง ์ฐพ์์ผ๋ฉด Notion ์์ ์ง์ด ๊ฒ์ ์์ง ๋ณผํธ์ ๋ฐ์๋์ง\n * ์์์ ์ ์๋ค โ ๊ทธ ์ฌ์ค๊ณผ ์ธ์ ๋ฐ์๋๋์ง๋ฅผ ํจ๊ป ์ ๋๋ค. ์ ์ง ์์ผ๋ฉด ยซNotion ์์ ์ง์ ๋๋ฐ ๋ณผํธ์\n * ๋จ์๋คยป ๋ฅผ ๊ฒฐํจ์ผ๋ก ์ฝ๋๋ค. ์๊ฒฉ ์ญ์ ๋ฅผ ๋ฐ์ํ์ง ์๋ ์ค์ (`sync.deleteSync` ๊บผ์ง)์ด๋ฉด ์ ์ง ์๋๋ค.\n */\nexport function remoteScanLines(scan: RemoteScanInfo): string[] {\n if (scan.kind === \"full\") {\n return [dimText(scan.reason ? `Full scan (${FULL_SCAN_REASON[scan.reason]})` : \"Full scan\")];\n }\n const skipped = scan.skippedDatabases ?? 0;\n const skippedText =\n skipped > 0\n ? ` ยท ${skipped} unchanged ${skipped === 1 ? \"database\" : \"databases\"} skipped`\n : \"\";\n const lines = [dimText(`Changes-only scan${skippedText}`)];\n if (scan.deletionsDeferred) lines.push(remoteDeletionHint(scan));\n return lines;\n}\n\n/** ๋ฏธ๋ฃฌ ์๊ฒฉ ์ญ์ ๊ฐ ์ธ์ ๋ฐ์๋๋ โ {@link RemoteScanInfo.deletionsDeferred} ์ผ ๋ ์ด๋ค. */\nexport function remoteDeletionHint(scan: RemoteScanInfo): string {\n const when = scan.nextFullAt\n ? `the next full scan (${localDateTime(scan.nextFullAt)})`\n : \"the next pull (full scan due)\";\n return `${dimText(`Deletions in Notion apply at ${when} โ or run`)} ${chalk.cyan(\"nobsi pull --force\")}`;\n}\n\n/** ๋ง์ง๋ง ์ ์ฒด ๋์กฐ ์๊ฐ โ ํ ๋ฒ๋ ์์ผ๋ฉด never. */\nexport function lastFullScanText(lastFullScanAt: string | null): string {\n return lastFullScanAt ? localDateTime(lastFullScanAt) : chalk.yellow(\"never\");\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\n/**\n * ์คํจ ํ ์ค. ๋ณผํธ ์ต์์์ ์คํจ(DB ์๋ ๋ฐ๊ฒฌ ๋ฑ)๋ ๋น ๊ฒฝ๋ก๋ก ์ค๋ฏ๋ก `/` ๋ก ๋ณด์ธ๋ค. `direction` ์\n * sync ๊ฐ ๋ฐ๊ธฐ(โผ) ยท ์ฌ๋ฆฌ๊ธฐ(โฒ)๋ฅผ ๊ฐ๋ฅผ ๋ ์ด๋ค.\n */\nexport function failedItem(path: string, error: string, direction?: string): void {\n const where = `${direction ? `${direction} ` : \"\"}${path || \"/\"}`;\n console.log(` ${icons.fail} ${where}: ${chalk.red(error)}`);\n}\n","import { existsSync } from \"node:fs\";\nimport { dirname, join, relative } from \"node:path\";\nimport {\n ConfigManager,\n NodeVaultFS,\n StateDB,\n matchesPathScope,\n ScopedVaultFS,\n findConnectionFolders,\n OBSIDIAN_CONFIG_DIR,\n type PathFilterConfig,\n type VaultFS,\n} from \"@im-nobsidian/core\";\n\n/** ์ด ํด๋ ์์ชฝ์ ๋ค๋ฅธ ์ฐ๊ฒฐ ํด๋(ADR-038) โ ์ด ํด๋์ ๋๊ธฐํ๊ฐ ๋น์ผ ๊ฐ๋ค. */\nexport async function nestedConnections(cwd: string): Promise<string[]> {\n return (await findConnectionFolders(cwd)).filter((folder) => folder !== \"\");\n}\n\n/**\n * ํ์ฌ ํด๋๋ฅผ ๋ณผํธ๋ก ๋ค๋ฃจ๋ VaultFS. ์์ชฝ์ ๋ค๋ฅธ ์ฐ๊ฒฐ ํด๋๊ฐ ์์ผ๋ฉด ๊ทธ ํด๋๋ ์ฌ๋ฆฌ์ง๋ ๋ฎ์ด์ฐ์ง๋ ์๋๋ค โ\n * ๊ทธ ํด๋๋ ์๊ธฐ ์ค์ ยท Notion ์ผ๋ก ๋ฐ๋ก ๋๊ธฐํํ๋ค(`cd <ํด๋> && nobsi sync`).\n */\nexport async function openVaultFs(cwd: string, paths: PathFilterConfig): Promise<VaultFS> {\n const base = new NodeVaultFS(cwd, paths);\n const nested = await nestedConnections(cwd);\n return nested.length === 0 ? base : new ScopedVaultFS(base, \"\", [\"\", ...nested]);\n}\n\n/**\n * Obsidian ์ค์ ํด๋๊น์ง์ ์๋ ๊ฒฝ๋ก โ ์ฐ๊ฒฐ ํด๋๊ฐ ๋ณผํธ ์์ชฝ์ด๋ฉด ์๋ก ์ฌ๋ผ๊ฐ ์ฐพ๋๋ค. ๋ชป ์ฐพ์ผ๋ฉด `.obsidian`\n * (์์ผ๋ฉด ์์ฑ ํ์
์ ๊ฐ์ผ๋ก ์ ํ๋ค).\n */\nexport function obsidianConfigDir(cwd: string): string {\n for (let dir = cwd; ; dir = dirname(dir)) {\n if (existsSync(join(dir, OBSIDIAN_CONFIG_DIR))) {\n return relative(cwd, join(dir, OBSIDIAN_CONFIG_DIR)).split(\"\\\\\").join(\"/\");\n }\n if (dirname(dir) === dir) return OBSIDIAN_CONFIG_DIR;\n }\n}\n\n/**\n * ์ด ํด๋๋ฅผ ์ด๋ฏธ ๋๊ธฐํํ๋ ๋ฐ๊นฅ ์ฐ๊ฒฐ โ ๊ทธ ์ฐ๊ฒฐ ํด๋์ ์ด ํด๋ ์์์ ์ถ์ ํ๋ ๋
ธํธ ์. ์์ผ๋ฉด null.\n *\n * ์ด๋ฏธ ๋๊ธฐํํ๋ ํด๋๋ฅผ ๋ฐ๋ก ์ฐ๊ฒฐํ๋ฉด ๋ฐ๊นฅ ์ฐ๊ฒฐ์ด ๋ฉ์ถ๋ค(ConnectionScopeError) โ init ์ด ๋จผ์ ๋ง๋๋ค.\n */\nexport function outerConnectionTracking(cwd: string): { folder: string; notes: number } | null {\n for (let dir = dirname(cwd); ; dir = dirname(dir)) {\n const manager = new ConfigManager(dir);\n if (existsSync(manager.dbPath)) {\n const scope = relative(dir, cwd).split(\"\\\\\").join(\"/\");\n const db = StateDB.open(manager.dbPath);\n try {\n // ํด๋ ์์ ์ ๋ ์ฝ๋(ํด๋ ํ์ด์ง)๋ ๋นผ๊ณ ๊ทธ ์์ ๊ฒ๋ง ์ผ๋ค.\n const notes = db\n .getAll()\n .filter(\n (r) => r.obsidianPath !== scope && matchesPathScope(r.obsidianPath, scope),\n ).length;\n return notes > 0 ? { folder: dir, notes } : null;\n } finally {\n db.close();\n }\n }\n if (dirname(dir) === dir) return null;\n }\n}\n","import { Command } from \"commander\";\nimport { ConfigManager, StateDB, NotionClient, SyncOrchestrator } 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\";\nimport { enableJsonMode, printJson, pushJson } from \"../utils/json-output.js\";\nimport { openVaultFs } from \"../utils/vault.js\";\n\nexport const pushCommand = new Command(\"push\")\n .description(\"๋ก์ปฌ ๋ณ๊ฒฝ์ฌํญ์ Notion์ ๋ฐ์\")\n .option(\"--dry-run\", \"์ค์ ๋ฐ์ ์์ด ๋ณ๊ฒฝ์ฌํญ๋ง ํ์\")\n .option(\"-p, --path <paths...>\", \"ํน์ ๊ฒฝ๋ก๋ง push\")\n .option(\"--json\", \"๊ฒฐ๊ณผ๋ฅผ JSON ํ ์ค๋ก ์ถ๋ ฅ (์๋ํ์ฉ โ ๋ก๊ทธ๋ stderr)\")\n .action(async (options) => {\n const json = options.json === true;\n if (json) enableJsonMode();\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 = await openVaultFs(cwd, config.paths);\n const orchestrator = new SyncOrchestrator(config, stateDb, client, vaultFs);\n\n if (!json) {\n console.log(`\\n${header(\" Pushing to Notion...\")}`);\n if (options.dryRun) console.log(dimText(\" (dry-run mode)\"));\n console.log(\"\");\n }\n\n const result = await orchestrator.push({\n dryRun: options.dryRun,\n paths: options.path,\n onProgress: json\n ? undefined\n : (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 (json) {\n printJson(pushJson(result));\n if (result.failed.length > 0) process.exitCode = 1;\n return;\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, result.moved)} ${failedCountText(failedCount)}`,\n );\n if (result.createdDatabases) {\n console.log(` ${options.dryRun ? \"๋ง๋ค DB\" : \"๋ง๋ DB\"} ${result.createdDatabases}`);\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 { setLogger } from \"@im-nobsidian/core\";\nimport type { FailedOperation, PullResult, PushResult, RemoteScanInfo } from \"@im-nobsidian/core\";\n\n/**\n * `--json` ๋ชจ๋ โ ์๋ํ(E2E ํ๋์ค ยท CI)๊ฐ ์ฌ๋์ฉ ๋ฌธ๊ตฌ๋ฅผ grep ํ์ง ์๊ณ ๊ฒฐ๊ณผ๋ฅผ ์ฝ๊ฒ ํ๋ค.\n *\n * ๋ฌธ๊ตฌ grep ์ ์กฐ์ฉํ ํ๋ ธ๋ค. sync ๋ก๊ทธ์ Pull ์ชฝ \"no changes\" ๊ฐ ํ ์ค๋ง ์์ด๋ Push ์ชฝ\n * ๋ณ๊ฒฝ๊ณผ ๋ฌด๊ดํ๊ฒ churn 0 ์ผ๋ก ํ์ ํ๊ณ , restored ยท deleted ๋ ์์ ์ธ์ง ์์๋ค.\n *\n * ๊ณ์ฝ: stdout ์๋ JSON ํ ์ค๋ง ์ด๋ค. ์ฝ์ด ๋ก๊ทธ๋ ์ ๋ถ stderr ๋ก ๋๋ฆฐ๋ค.\n */\nexport function enableJsonMode(): void {\n const toStderr = (message: string, ...args: unknown[]): void => console.error(message, ...args);\n setLogger({ warn: toStderr, error: toStderr, info: toStderr, debug: () => {} });\n}\n\nexport interface PushJson {\n readonly created: number;\n readonly updated: number;\n readonly deleted: number;\n /** ์ฎ๊น์ ๋ฐ์ํ ๋
ธํธ ยท ํด๋ ์ โ updated ์ ๊ฒน์น์ง ์๋๋ค. */\n readonly moved: number;\n /** ์๊ฒฉ์ ์ค์ ๋ก ์ด(๋๋ dry-run ์ด๋ฉด ์ธ) ํญ๋ชฉ ์. ๋ฉฑ๋ฑ ํ์ ์ ๊ธฐ์ค๊ฐ. */\n readonly churn: number;\n /** ์ฌ์ด๋์นด๋ก ๋ง๋ (dry-run ์ ๋ง๋ค) Notion DB ์ โ churn ์๋ ๋ ๋ค. */\n readonly createdDatabases: number;\n readonly failed: readonly FailedOperation[];\n readonly durationMs: number;\n}\n\nexport interface PullJson {\n readonly created: number;\n readonly updated: number;\n readonly deleted: number;\n readonly restored: number;\n /** ๋ณผํธ์ ์ค์ ๋ก ์ด ํญ๋ชฉ ์ โ ๋ณต์๋ ์ฐ๊ธฐ๋ค. ๋ฉฑ๋ฑ ํ์ ์ ๊ธฐ์ค๊ฐ. */\n readonly churn: number;\n readonly conflicts: number;\n readonly failed: readonly FailedOperation[];\n readonly writtenPaths: number;\n readonly images: number;\n readonly files: number;\n readonly links: number;\n readonly durationMs: number;\n /** ์๊ฒฉ์ ์ผ๋ง๋ ํ์๋ โ ๋ฐ๋ ๊ฒ๋ง ์ฐพ์์ผ๋ฉด Notion ์์ ์ง์ด ๊ฒ์ ๋ค์ ์ ์ฒด ๋์กฐ๊ฐ ๋ฐ์ํ๋ค. */\n readonly remoteScan: RemoteScanInfo | null;\n}\n\nexport function pushJson(result: PushResult): PushJson {\n return {\n created: result.created,\n updated: result.updated,\n deleted: result.deleted,\n moved: result.moved,\n churn:\n result.created +\n result.updated +\n result.deleted +\n result.moved +\n (result.createdDatabases ?? 0),\n createdDatabases: result.createdDatabases ?? 0,\n failed: result.failed,\n durationMs: result.duration,\n };\n}\n\nexport function pullJson(result: PullResult): PullJson {\n return {\n created: result.created,\n updated: result.updated,\n deleted: result.deleted,\n restored: result.restored,\n churn: result.created + result.updated + result.deleted + result.restored,\n conflicts: result.conflicts.length,\n failed: result.failed,\n writtenPaths: result.writtenPaths.length,\n images: result.imageCount,\n files: result.fileCount,\n links: result.linkCount,\n durationMs: result.duration,\n remoteScan: result.remoteScan ?? null,\n };\n}\n\nexport function printJson(value: unknown): void {\n process.stdout.write(`${JSON.stringify(value)}\\n`);\n}\n","import { Command, Option } from \"commander\";\nimport { ConfigManager, StateDB, NotionClient, SyncOrchestrator } 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 remoteScanLines,\n} from \"../utils/format.js\";\nimport { enableJsonMode, printJson, pullJson } from \"../utils/json-output.js\";\nimport { openVaultFs } from \"../utils/vault.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 // ๋ค์ ๊ทธ๋ ค ๋ด์ผ ๋ฐ๋๋์ง ์๋ค โ ๋ฏธ๋ฆฌ ๋ณด๊ธฐ๋ก๋ ๋ณด์ผ ๊ฒ์ด ์๋ค(B14).\n .addOption(\n new Option(\n \"--rerender\",\n \"Notion ์์ ๋ฐ๋์ง ์์ ๋ฐ์ ๋ ๋
ธํธ๋ ์ง๊ธ ๋ณํ๊ธฐ๋ก ๋ค์ ๊ทธ๋ฆผ (๋ณผํธ์์ ๊ณ ์น ๋
ธํธ๋ ๊ทธ๋๋ก)\",\n ).conflicts(\"dryRun\"),\n )\n .option(\"--json\", \"๊ฒฐ๊ณผ๋ฅผ JSON ํ ์ค๋ก ์ถ๋ ฅ (์๋ํ์ฉ โ ๋ก๊ทธ๋ stderr)\")\n .action(async (options) => {\n const json = options.json === true;\n if (json) enableJsonMode();\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 = await openVaultFs(cwd, config.paths);\n const orchestrator = new SyncOrchestrator(config, stateDb, client, vaultFs);\n\n if (!json) {\n console.log(`\\n${header(\" Pulling from Notion...\")}`);\n if (options.dryRun) console.log(dimText(\" (dry-run mode)\"));\n console.log(\"\");\n }\n\n const result = await orchestrator.pull({\n dryRun: options.dryRun,\n paths: options.path,\n force: options.force,\n rerender: options.rerender,\n onProgress: json\n ? undefined\n : (current, total, item) => {\n // ๋ฐ์ ๋ดค์ง๋ง ๋ฐ๋์ง ์์ ๋
ธํธ โ ๋ค์ ๊ทธ๋ฆฌ๊ธฐ๋ ๋ฐ์ ๋ ๋
ธํธ๋ฅผ ๋ชจ๋ ๋ฐ์ ๋ณธ๋ค(B14).\n if (item.unchanged) return;\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 (json) {\n printJson(pullJson(result));\n if (result.failed.length > 0) process.exitCode = 1;\n return;\n }\n\n if (result.imageCount > 0) {\n console.log(\n `\\n ${icons.success} ${result.imageCount} images โ ${dimText(`${config.paths.attachments}/`)}`,\n );\n }\n if (result.fileCount > 0) {\n console.log(\n ` ${icons.success} ${result.fileCount} files โ ${dimText(`${config.paths.attachments}/`)}`,\n );\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 if (result.remoteScan) {\n for (const line of remoteScanLines(result.remoteScan)) console.log(` ${line}`);\n }\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 { ConfigManager, StateDB, NotionClient, SyncOrchestrator } 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 remoteScanLines,\n} from \"../utils/format.js\";\nimport { enableJsonMode, printJson, pullJson, pushJson } from \"../utils/json-output.js\";\nimport { openVaultFs } from \"../utils/vault.js\";\n\nexport const syncCommand = new Command(\"sync\")\n .description(\"์๋ฐฉํฅ ๋๊ธฐํ (Pull โ Push)\")\n .option(\"--dry-run\", \"์ค์ ๋ฐ์ ์์ด ๋ณ๊ฒฝ์ฌํญ๋ง ํ์\")\n .option(\"--json\", \"๊ฒฐ๊ณผ๋ฅผ JSON ํ ์ค๋ก ์ถ๋ ฅ (์๋ํ์ฉ โ ๋ก๊ทธ๋ stderr)\")\n .action(async (options) => {\n const json = options.json === true;\n if (json) enableJsonMode();\n // ์งํ ๋ก๊ทธ๋ ์ฌ๋์ฉ์ด๋ค โ JSON ๋ชจ๋์์๋ stdout ๊ณ์ฝ(JSON ํ ์ค)์ ์งํค๋ ค๊ณ ๋๋ค.\n const log = json ? () => {} : (line: string) => console.log(line);\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 = await openVaultFs(cwd, config.paths);\n const orchestrator = new SyncOrchestrator(config, stateDb, client, vaultFs);\n\n log(`\\n${header(\" Bidirectional Sync\")}`);\n log(` ${separator()}`);\n if (options.dryRun) log(dimText(\" (dry-run mode)\"));\n\n // ๋ฐ๊ธฐ ยท ์ฌ๋ฆฌ๊ธฐ๋ฅผ ํ ๋ฒ์ ๋๋ค โ ํ๋ฌ๊ทธ์ธ ยท watch ์ ๊ฐ์ sync ๋ค. push ๋ pull ์ด ๋ฐฉ๊ธ ์ด ๋
ธํธ๋ฅผ\n // ๋ค์ ์ฌ๋ฆฌ์ง ์๋๋ค(F-j). ์์ ์๋ pull ยท push ๋ฅผ ๋ฐ๋ก ๋ถ๋ฌ ๋ ยซsyncยป ๊ฐ ๋ฐ๋ก ์๋๋ค.\n log(`\\n ${header(chalk.blue(\"โผ Pull\"))} ${dimText(\"(Notion โ Obsidian)\")}`);\n let pushShown = false;\n const showPush = (): void => {\n if (pushShown) return;\n pushShown = true;\n log(`\\n ${header(chalk.magenta(\"โฒ Push\"))} ${dimText(\"(Obsidian โ Notion)\")}`);\n };\n\n const result = await orchestrator.sync({\n dryRun: options.dryRun,\n onProgress: (_current, _total, item) => {\n if (item.direction === \"push\") showPush();\n if (item.unchanged) return;\n const icon = operationIcon(item.operation);\n const label = operationLabel(item.operation);\n log(` ${icon} ${item.path} ${label}`);\n },\n });\n showPush();\n const { pull: pullResult, push: pushResult } = result;\n\n if (json) {\n const pull = pullJson(pullResult);\n const push = pushJson(pushResult);\n printJson({\n pull,\n push,\n churn: pull.churn + push.churn,\n conflicts: pull.conflicts,\n durationMs: result.duration,\n });\n if (pullResult.failed.length + pushResult.failed.length > 0) process.exitCode = 1;\n return;\n }\n\n // Summary\n // ์์ ์ ์์ชฝ failed ๋ฅผ ์์ ์ฐ์ง ์์, ์ ๊ฑด ์คํจํ sync ๋ ํ๋ฉด ๋์ ์ด๋ก \"complete\"\n // ์๋ค โ ์คํจ๋ฅผ ํค๋์ ๋ชฉ๋ก ์์ชฝ์ ๋๋ฌ๋ธ๋ค.\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 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 console.log(\n ` ${chalk.magenta(\"Push:\")} ${summary(pushResult.created, pushResult.updated, pushResult.deleted, pushResult.moved)}` +\n (pushFailed > 0 ? ` ${chalk.red(`${pushFailed} failed`)}` : \"\"),\n );\n console.log(` ${duration(result.duration)}`);\n if (pullResult.remoteScan) {\n for (const line of remoteScanLines(pullResult.remoteScan)) console.log(` ${line}`);\n }\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 (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 isRemoteDeletion,\n} from \"@im-nobsidian/core\";\nimport chalk from \"chalk\";\nimport {\n header,\n separator,\n icons,\n dimText,\n localDateTime,\n lastFullScanText,\n remoteDeletionHint,\n} from \"../utils/format.js\";\nimport { nestedConnections, openVaultFs } from \"../utils/vault.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 = await openVaultFs(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 console.log(` ${dimText(\"Last sync:\")} ${localDateTime(status.lastSyncAt)}`);\n } else {\n console.log(` ${dimText(\"Last sync:\")} ${chalk.yellow(\"never\")}`);\n }\n // Notion ์์ ์ง์ด ๋
ธํธ๋ ์ ์ฒด ๋์กฐ ๋ ๋ณผํธ์ ๋ฐ์๋๋ค โ ๊ทธ๋๋ฅผ ํจ๊ป ๋ณด์ธ๋ค.\n console.log(` ${dimText(\"Full scan:\")} ${lastFullScanText(status.lastFullScanAt)}`);\n // ์์ชฝ ์ฐ๊ฒฐ ํด๋(ADR-038)๋ ์ฌ๊ธฐ์ ๋ณด์ง ์๋๋ค โ ๊ทธ ํด๋์์ ๋ฐ๋ก ๋ณธ๋ค.\n for (const folder of await nestedConnections(cwd)) {\n console.log(\n ` ${dimText(\"Connection:\")} ${folder}/ ${dimText(`(separate โ cd ${folder} && nobsi status)`)}`,\n );\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 const movedChanges = status.localChanges.filter((c) => c.type === \"moved\");\n\n // ์ถ์ ํ์ผ ๋ด์ญ์ ์๋ก ๊ฒน์น์ง ์์์ผ ํ๋ค. DB ์ status ๋ push/pull ์์ ์๋ง ๊ฐฑ์ ๋๋ฏ๋ก\n // ๋ฐฉ๊ธ ๊ณ ์น ํ์ผ๋ ๋ ์ฝ๋์์ผ๋ก ์ฌ์ ํ 'synced' ๋ค โ ๊ทธ๋๋ก ์ธ๋ฉด ๊ฐ์ ํ์ผ์ด synced ์\n // modified ์์ชฝ์ ์กํ ๋ด์ญ ํฉ์ด ์ด๊ณ๋ฅผ ๋๋ ํ๊ฐ ๋์จ๋ค(๋ฌด์์ด ๋ฐ๋ ธ๋์ง ๋ชป ์ฝ๋๋ค).\n // ์ค์ ๋ณ๊ฒฝ์ด ๊ฑธ๋ฆฐ ๊ฒฝ๋ก๋ฅผ ๋จผ์ ๋์ด ๋ด๊ณ ๋จ์ ๊ฒ๋ง synced ๋ก ์ผ๋ค.\n // ์ฎ๊ธด ๋
ธํธ์ ๋ ์ฝ๋๋ ์์ง ์ ๊ฒฝ๋ก(movedFrom)๋ฅผ ์ ๊ณ ์๋ค โ ๋ ๊ฒฝ๋ก๋ฅผ ๋ค ๋์ด ๋ธ๋ค.\n const dirtyPaths = new Set([\n ...modifiedChanges.map((c) => c.path),\n ...deletedChanges.map((c) => c.path),\n ...movedChanges.flatMap((c) => [c.path, c.movedFrom ?? 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 const remoteMoved = status.remoteChanges.filter((c) => c.type === \"moved\");\n const { folderMoves } = status;\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 (movedChanges.length > 0) {\n console.log(` ${icons.move} ${chalk.cyan(\"moved\")} ${movedChanges.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 // Moved โ ํด๋๋ฅผ ๋จผ์ ๋ณด์ธ๋ค. ๊ทธ ์์ ๋
ธํธ๋ ์๋์ ๋
ธํธ๋ง๋ค ๋ฐ๋ก ์๋ค.\n if (folderMoves.length + movedChanges.length > 0) {\n console.log(`\\n ${header(\"Moved:\")}`);\n for (const move of folderMoves) {\n console.log(` ${icons.move} ${move.from}/ โ ${move.to}/ ${dimText(\"(folder)\")}`);\n }\n for (const c of movedChanges.slice(0, 10)) {\n console.log(` ${icons.move} ${c.movedFrom ?? \"?\"} โ ${c.path}`);\n }\n if (movedChanges.length > 10) {\n console.log(dimText(` ... and ${movedChanges.length - 10} more`));\n }\n }\n\n // Deleted files โ ์๋ง ๋ณด์ด๋ฉด ๋ฌด์์ด Notion ์์ ์ง์์ง์ง ๋ชจ๋ฅธ๋ค.\n if (deletedChanges.length > 0) {\n console.log(`\\n ${header(\"Deleted files:\")}`);\n for (const c of deletedChanges.slice(0, 10)) {\n console.log(` ${chalk.red(\"-\")} ${c.path}`);\n }\n if (deletedChanges.length > 10) {\n console.log(dimText(` ... and ${deletedChanges.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 (remoteMoved.length > 0) {\n console.log(` ${icons.move} ${chalk.cyan(\"moved\")} ${remoteMoved.length}`);\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 : c.type === \"moved\"\n ? icons.move\n : chalk.red(\"-\");\n // ๋ณผํธ์ ์๋ ๋
ธํธ๋ ๊ฒฝ๋ก, ์์ง ์๋ ์ ํ์ด์ง๋ Notion ์ ๋ชฉ โ ๋ด๋ถ id ๋ ๋ ๋ค ์์ ๋๋ง.\n const name = c.path ?? (c.title ? `${c.title} ${dimText(\"(new page)\")}` : undefined);\n console.log(\n ` ${icon} ${name ?? `${c.pageId.slice(0, 8)}...`} ${dimText(`(${c.type})`)}`,\n );\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 // ์๊ฒฉ์ ์ฝ์ ์ํ(--full)๋ง Notion ์์ ์ง์ด ๋
ธํธ๋ฅผ ๊ฐ๋ฅธ๋ค.\n const remoteDeleted = new Set(\n status.conflicts.filter(isRemoteDeletion).map((c) => c.syncRecord.obsidianPath),\n );\n for (const r of status.conflictRecords) {\n const reason = remoteDeleted.has(r.obsidianPath)\n ? \"(deleted in Notion, local edits not pushed)\"\n : \"(both sides changed)\";\n console.log(` ${icons.conflict} ${r.obsidianPath.padEnd(30)} ${dimText(reason)}`);\n }\n }\n\n // Helpful commands\n const hasChanges =\n status.localChanges.length > 0 ||\n folderMoves.length > 0 ||\n status.remoteChanges.length > 0 ||\n 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 // ๋๋๋ฆด ์ ์๋ ๊ฒ(๊ณ ์น ยท ์ง์ด ๋
ธํธ)์ด ์์ ๋๋ง โ git status ์ `git restore` ์๋ด์ ๊ฐ๋ค.\n if (modifiedChanges.length + deletedChanges.length > 0) {\n console.log(\n ` ${dimText(\"Run\")} ${chalk.cyan(\"nobsi discard <path>\")} ${dimText(\"to discard local edits\")}`,\n );\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 } else if (status.remoteScan?.deletionsDeferred) {\n // ๋ฐ๋ ๊ฒ๋ง ์ฐพ์๋ค โ ์ ์๊ฒฉ ๋ณ๊ฒฝ์๋ Notion ์์ ์ง์ด ๊ฒ์ด ์์ง ์๋ค.\n console.log(`\\n ${remoteDeletionHint(status.remoteScan)}`);\n }\n } finally {\n stateDb.close();\n }\n });\n","import { Command } from \"commander\";\nimport {\n ConfigManager,\n StateDB,\n NotionClient,\n SyncOrchestrator,\n formatUnifiedDiff,\n lineDiff,\n matchesPathScope,\n} from \"@im-nobsidian/core\";\nimport type { ChangeDiff } from \"@im-nobsidian/core\";\nimport { printUnifiedDiff } from \"../utils/diff-output.js\";\nimport { openVaultFs } from \"../utils/vault.js\";\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 = await openVaultFs(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 let failed = 0;\n for (const change of changes) {\n // --remote: ๋ง์ง๋ง ์ค๋
์ท์ด ์๋๋ผ Notion ์ ํ์ฌ ๋ณธ๋ฌธ์ ๊ธฐ์ค์ ์ผ๋ก ์ผ๋๋ค.\n // ์๊ฒฉ์ด ๊ทธ ์ฌ์ด ๋ฐ๋์์ ๋ \"๋ด๊ฐ ๋ญ ๋ฎ์ด์ฐ๊ฒ ๋๋์ง\"๋ ์ด ๋น๊ต๋ก๋ง ๋ณด์ธ๋ค.\n if (options.remote && change.type !== \"deleted\") {\n // ์ฎ๊ธด ๋
ธํธ๋ Notion ์ ๋ฐ์ํ๊ธฐ ์ ๊น์ง ์ถ์ ๊ธฐ๋ก์ด ์ ์๋ฆฌ์ ์๋ค โ Notion ์ชฝ ์ด๋ฆ๋ ๊ทธ ์๋ฆฌ๋ค.\n let remotePath = change.path;\n let remote = await orchestrator.renderRemoteSnapshot(remotePath);\n if (remote === null && change.movedFrom) {\n remotePath = change.movedFrom;\n remote = await orchestrator.renderRemoteSnapshot(remotePath);\n }\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 hunks = lineDiff(remote, currentContent);\n const lines = formatUnifiedDiff(hunks, `notion/${remotePath}`, `b/${change.path}`);\n // ์ด๋ฆ ์ค๋ง ์ฐ๊ณ ๋๋๋ฉด ๊ฐ์ ๊ฒ์ธ์ง ๋ชป ์ฝ์ ๊ฒ์ธ์ง ์ ์ ์๋ค โ ๊ฐ๋ค๊ณ ์ ๋๋ค.\n if (hunks.length === 0) lines.push(\"(Notion ๊ณผ ๋ด์ฉ์ด ๊ฐ์ต๋๋ค)\");\n printUnifiedDiff(lines, options.color);\n console.log(\"\");\n continue;\n }\n\n // ์ง๋ ๋๊ธฐํ ๋์ ๊ธ๊ณผ ์ง๊ธ ๊ธ โ ํ๋ฌ๊ทธ์ธ ๋ณ๊ฒฝ ํจ๋์ ๋น๊ต์ ๊ฐ์ ๋ ๊ธ์ด๋ค.\n let diff: ChangeDiff;\n try {\n diff = await orchestrator.localChangeDiff(change);\n } catch (error) {\n failed++;\n console.log(`--- a/${change.path}`);\n console.log(`(${error instanceof Error ? error.message : String(error)})`);\n console.log(\"\");\n continue;\n }\n printChangeDiff(diff, options.color);\n console.log(\"\");\n }\n // ๋ณด์ด์ง ๋ชปํ ๋น๊ต๊ฐ ์์ผ๋ฉด ์คํจ๋ก ๋๋ธ๋ค โ ์คํฌ๋ฆฝํธ๊ฐ ๋น ์ง ๊ฒ์ ์ ์ ์๊ฒ.\n if (failed > 0) process.exitCode = 1;\n } finally {\n stateDb.close();\n }\n });\n\n/** ์ฎ๊ธด ๋
ธํธ๋ Git ์ฒ๋ผ ์ ์๋ฆฌ ยท ์ ์๋ฆฌ๋ฅผ ๋จผ์ ์ ๋๋ค. ๋ด์ฉ์ด ๊ฐ์ผ๋ฉด ๋น๊ต ๋์ ๊ทธ๋ ๋ค๊ณ ์๋ฆฐ๋ค. */\nfunction printChangeDiff(diff: ChangeDiff, color: boolean): void {\n const oldPath = diff.before === null ? \"/dev/null\" : `a/${diff.movedFrom ?? diff.path}`;\n const newPath = diff.after === null ? \"/dev/null\" : `b/${diff.path}`;\n if (diff.movedFrom) {\n console.log(`rename from ${diff.movedFrom}`);\n console.log(`rename to ${diff.path}`);\n if (diff.before === diff.after) {\n console.log(\"(๋ด์ฉ์ ๊ทธ๋๋ก์
๋๋ค โ ์๋ฆฌ๋ง ์ฎ๊ฒผ์ต๋๋ค)\");\n return;\n }\n }\n printUnifiedDiff(\n formatUnifiedDiff(lineDiff(diff.before ?? \"\", diff.after ?? \"\"), oldPath, newPath),\n color,\n );\n}\n","import { DIFF_SIGN } from \"@im-nobsidian/core\";\n\nconst BOLD = \"\\x1b[1m\";\nconst RED = \"\\x1b[31m\";\nconst GREEN = \"\\x1b[32m\";\nconst CYAN = \"\\x1b[36m\";\nconst RESET = \"\\x1b[0m\";\n\n/** ์ค ์ฒซ ๊ธ์๋ง๋ค์ ์ โ ๋ํ ์ค ยท ์ง์ด ์ค์ ๋ถํธ๋ core ์ ๊ฒ์ ๋ฐ๋ฅธ๋ค. `@` ๋ ๋ฌถ์ ๋จธ๋ฆฌ. */\nconst LINE_COLOR: Readonly<Record<string, string>> = {\n [DIFF_SIGN.added]: GREEN,\n [DIFF_SIGN.removed]: RED,\n \"@\": CYAN,\n};\n\n/**\n * ํตํฉ diff ์ค(core `formatUnifiedDiff`)์ ์ฐ๋๋ค โ `diff` ยท `resolve` ๊ฐ ๊ฐ์ ๋ชจ์ ยท ๊ฐ์ ์์ผ๋ก ๋ณด์ธ๋ค.\n *\n * ์ ๊ธ ยท ์ ๊ธ ์ด๋ฆ ์ค์ ์ฒซ ๊ธ์๊ฐ ์๋๋ผ ์๋ฆฌ(์ ๋ ์ค)๋ก ๊ฐ๋ฅธ๋ค โ ๊ตฌ๋ถ์ (`---`)์ ์ง์ด ์ค์ `----` ๋ก\n * ์์ํด ์ฒซ ๊ธ์๋ก๋ ์ด๋ฆ ์ค๊ณผ ๊ตฌ๋ณ๋์ง ์๋๋ค.\n */\nexport function printUnifiedDiff(lines: readonly string[], color: boolean): void {\n lines.forEach((line, index) => {\n const code = index < 2 ? BOLD : LINE_COLOR[line.charAt(0)];\n console.log(color && code ? `${code}${line}${RESET}` : line);\n });\n}\n","import { Command } from \"commander\";\nimport { ConfigManager, StateDB, NotionClient, SyncOrchestrator } from \"@im-nobsidian/core\";\nimport { icons, failedItem } from \"../utils/format.js\";\nimport { openVaultFs } from \"../utils/vault.js\";\n\n// ๋ณ๊ฒฝ ๋ชฉ๋ก(status)์์ ๊ณ ๋ฅธ ๋
ธํธ๋ฅผ ์ง๋ ๋๊ธฐํ ๋์ ๊ธ๋ก ๋๋๋ฆฐ๋ค โ Git ์ `restore` ์ ๊ฐ๋ค.\n// ํ๋ฌ๊ทธ์ธ ๋ณ๊ฒฝ ํจ๋์ โบ ์ ๊ฐ์ ๋์(orchestrator.discardLocalChange)์ด๊ณ Notion ์ ๊ฑด๋๋ฆฌ์ง ์๋๋ค.\n// ํ๋๋ฅผ ๋๋๋ฆฌ์ง ๋ชปํด๋ ๋๋จธ์ง๋ ๋๋๋ฆฐ๋ค โ ๋ฌด์์ด ์ ์ ๋๋์ง ๊ฒฝ๋ก๋ง๋ค ์๋ฆฌ๊ณ ์ข
๋ฃ ์ฝ๋๋ก ๋จ๊ธด๋ค.\nexport const discardCommand = new Command(\"discard\")\n .description(\"๋ก์ปฌ ๋ณ๊ฒฝ์ ์ง๋ ๋๊ธฐํ ๋์ ๊ธ๋ก ๋๋๋ฆฌ๊ธฐ (Notion ์ ๊ทธ๋๋ก)\")\n .argument(\"<paths...>\", \"๋๋๋ฆด ๋
ธํธ ๊ฒฝ๋ก โ status ๊ฐ ๋ณด์ธ ๋ณผํธ ๊ธฐ์ค ๊ฒฝ๋ก\")\n .action(async (paths: 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 try {\n const client = NotionClient.fromConfig(config);\n const vaultFs = await openVaultFs(cwd, config.paths);\n const orchestrator = new SyncOrchestrator(config, stateDb, client, vaultFs);\n\n let failed = 0;\n for (const path of paths) {\n try {\n await orchestrator.discardLocalChange(path);\n console.log(` ${icons.success} ${path}`);\n } catch (error) {\n failed++;\n failedItem(path, error instanceof Error ? error.message : String(error));\n }\n }\n if (failed > 0) process.exitCode = 1;\n } finally {\n stateDb.close();\n }\n });\n","import { Command } from \"commander\";\nimport { ConfigManager, StateDB, NotionClient, SyncOrchestrator } from \"@im-nobsidian/core\";\nimport chalk from \"chalk\";\nimport { header, separator, dimText } from \"../utils/format.js\";\nimport { openVaultFs } from \"../utils/vault.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 = await openVaultFs(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 applicableChoices,\n isRemoteDeletion,\n} from \"@im-nobsidian/core\";\nimport type { Conflict, ResolutionChoice } from \"@im-nobsidian/core\";\nimport { printUnifiedDiff } from \"../utils/diff-output.js\";\nimport { describeFailure } from \"../utils/state-db-failure.js\";\nimport { openVaultFs } from \"../utils/vault.js\";\n\n/** ํ์ฉ ์ ๋ต โ ์คํ๋ฅผ ์กฐ์ฉํ ํ๋ฆฌ์ง ์๋๋ก ์คํ ์ ์ ํ ๋ฒ ๊ฒ์ฆํ๋ค. */\nconst STRATEGIES = [\"local-first\", \"remote-first\", \"duplicate\"] as const;\ntype Strategy = (typeof STRATEGIES)[number];\n\nconst CHOICE_NAMES: Record<ResolutionChoice, string> = {\n local: \"๋ก์ปฌ ์ ์ง (Obsidian ๋ฒ์ ์ ์ง)\",\n remote: \"์๊ฒฉ ์ ์ง (Notion ๋ฒ์ ์ผ๋ก ๋ฎ์ด์ฐ๊ธฐ)\",\n merge: \"์๋ ๋ณํฉ (3-way merge)\",\n duplicate: \"๋ณต์ (์๋ณธ ์ ์ง + ์ฌ๋ฆฌ์ง ์๋ .conflict ์ฌ๋ณธ ์์ฑ)\",\n};\n\n/** Notion ์์ ์ง์ด ๋
ธํธ๋ ๊ฐ์ ์ ํ์ด ๋ค๋ฅธ ์ผ์ ํ๋ค โ ๋ก์ปฌ ์ ์ง๋ ๋ค์ ๋ง๋ค๊ณ , ์๊ฒฉ ์ ์ง๋ ์ง์ด๋ค. */\nconst REMOTE_DELETION_CHOICE_NAMES: Partial<Record<ResolutionChoice, string>> = {\n local: \"๋ก์ปฌ ์ ์ง (Notion ์ ์ ํ์ด์ง๋ก ๋ค์ ๋ง๋ค๊ธฐ)\",\n remote: \"์ญ์ ๋ฐ๋ฅด๊ธฐ (Obsidian ํ์ผ๋ ์ง์ฐ๊ธฐ)\",\n};\n\n/** ๋ฐ๊ธฐ ์ ์ฉ(direction: pull)์ ํด๊ฒฐ ๊ฒฐ๊ณผ๋ฅผ Notion ์ ์ฌ๋ฆฌ์ง ์๋๋ค โ ๋ค์ ๋ง๋ค์ง๋ ์๋๋ค. */\nconst PULL_ONLY_REMOTE_DELETION_CHOICE_NAMES: Partial<Record<ResolutionChoice, string>> = {\n local: \"๋ก์ปฌ ์ ์ง (Obsidian ํ์ผ๋ง ๋๊ธฐ โ ๋ฐ๊ธฐ ์ ์ฉ์ด๋ผ Notion ์ ๋ง๋ค์ง ์์)\",\n};\n\nfunction choiceName(conflict: Conflict, choice: ResolutionChoice, uploads: boolean): string {\n const deleted = isRemoteDeletion(conflict);\n return (\n (deleted && !uploads ? PULL_ONLY_REMOTE_DELETION_CHOICE_NAMES[choice] : undefined) ??\n (deleted ? REMOTE_DELETION_CHOICE_NAMES[choice] : undefined) ??\n CHOICE_NAMES[choice] ??\n choice\n );\n}\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 = await openVaultFs(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 const uploads = config.sync.direction !== \"pull\";\n if (!uploads) {\n console.log(\n \" ๋ฐ๊ธฐ ์ ์ฉ(direction: pull) โ ํด๊ฒฐ ๊ฒฐ๊ณผ๋ Obsidian ์๋ง ๋๊ณ Notion ์๋ ์ฌ๋ฆฌ์ง ์์ต๋๋ค\\n\",\n );\n }\n\n if (options.strategy) {\n const strategy = options.strategy as Strategy;\n // ์๊ฒฉ์์ ์ง์ด ๋
ธํธ๋ ์ ๋ต์ด ํ์ผ์ ์ง์ฐ๊ฑฐ๋ ํ์ด์ง๋ฅผ ์๋ก ๋ง๋ ๋ค โ ๋ฌป๊ธฐ ์ ์ ์๋ฆฐ๋ค.\n const deletions = pending.filter(isRemoteDeletion).length;\n if (deletions > 0) {\n console.log(\n ` Notion ์์ ์ญ์ ๋ ๋
ธํธ ${deletions}๊ฑด โ ${\n strategy === \"remote-first\"\n ? \"Obsidian ํ์ผ๋ ์ง์๋๋ค\"\n : uploads\n ? \"Notion ์ ์ ํ์ด์ง๋ก ๋ค์ ๋ง๋ญ๋๋ค\"\n : \"Obsidian ํ์ผ์ ๊ทธ๋๋ก ๋ก๋๋ค\"\n }`,\n );\n }\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 [index, result] of results.entries()) {\n if (!result.success) failed++;\n const icon = result.success ? \"\\x1b[32mโ\\x1b[0m\" : \"\\x1b[31mโ\\x1b[0m\";\n const reason = result.error ? ` โ ${result.error}` : \"\";\n console.log(\n ` ${icon} ${result.path} โ ${choiceName(pending[index]!, result.choice, uploads)}${reason}`,\n );\n }\n report(results.length - failed, failed);\n return;\n }\n\n if (!isInteractive()) {\n console.error(\n `๋น๋ํํ ํ๊ฒฝ์์๋ ๋ํํ ํด๊ฒฐ์ ์ธ ์ ์์ต๋๋ค โ --strategy <${STRATEGIES.join(\"|\")}> --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, uploads)) resolved++;\n else failed++;\n }\n report(resolved, failed);\n } catch (error) {\n console.error(\"์ค๋ฅ:\", describeFailure(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 uploads: boolean,\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 if (isRemoteDeletion(conflict)) {\n // ๊ฒฌ์ค ์๊ฒฉ ๋ณธ๋ฌธ์ด ์๋ค โ ์ค ๋น๊ต๋ ๋ก์ปฌ ์ ๋ถ๋ฅผ ์ง์ด ๊ฒ์ผ๋ก ๋ณด์ฌ ์ค๋ค.\n console.log(\n \"\\x1b[33mNotion ์์ ์ญ์ ๋ ๋
ธํธ์
๋๋ค โ Notion ์ ์ฌ๋ฆฌ์ง ์์ ๋ก์ปฌ ํธ์ง์ด ๋จ์ ์์ต๋๋ค.\\x1b[0m\",\n );\n } else {\n printUnifiedDiff(orchestrator.generateConflictDiff(conflict).split(\"\\n\"), true);\n }\n\n const choice = await select<ResolutionChoice>({\n message: \"์ด๋ป๊ฒ ํด๊ฒฐํ ๊น์?\",\n choices: applicableChoices(conflict).map((value) => ({\n name: choiceName(conflict, value, uploads),\n value,\n })),\n });\n\n const result = await orchestrator.resolveConflict(conflict, choice);\n\n if (result.success) {\n console.log(`\\x1b[32mโ ${path} โ ${choiceName(conflict, choice, uploads)}\\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","/**\n * ์ํ DB ๋ฅผ ์ธ ์ ์์ ๋์ CLI ์ถ๋ ฅ โ ์ด์ ๋ค์ ํ ์ผ์ ๋ถ์ธ๋ค.\n *\n * ๋ช
๋ น์ ์ฒ์์ ์ํ DB ๋ฅผ ์ฐ๋ค(`StateDB.open`). ํ์ผ์ด ๋น์๊ฑฐ๋ ์๋ ธ๊ฑฐ๋(`SavedStateDbError`), ํ๋ฌ๊ทธ์ธ์ด ๊ฐ์\n * ๋ณผํธ๋ฅผ ์ฐ๋ ์ค์ด๋ฉด(`StateDbLockedError`) ๋์ง๋๋ฐ, ๋ช
๋ น ๋ฐ์ผ๋ก ๋์ค๋ฉด Node ๊ฐ ์ฝ๋ ์์น(์คํ)์ ํจ๊ป ์ด์ ๋ง\n * ๋ณด์๋ค โ ์ฌ์ฉ์๊ฐ ํ ์ผ์ ์ด๋์๋ ์์๋ค.\n */\nimport { StateDbUnavailableError } from \"@im-nobsidian/core\";\nimport type { ExitableProcess } from \"./exit.js\";\n\n/** ์คํจ๋ฅผ ๋ณด์ผ ๋ง โ ์ํ DB ๋ฅผ ์ธ ์ ์๋ ๊ฒ์ด๋ฉด ํ ์ผ์ ๋ถ์ด๊ณ , ๋ค๋ฅธ ์คํจ๋ ๊ทธ ๋ง ๊ทธ๋๋ก๋ค. */\nexport function describeFailure(error: unknown): string {\n const message = error instanceof Error ? error.message : String(error);\n if (!(error instanceof StateDbUnavailableError)) return message;\n return `${message} โ ${error.guidance(\"๋ช
๋ น์ ๋ค์ ์คํํ์ธ์\")}`;\n}\n\n/**\n * ๋ช
๋ น ๋ฐ์ผ๋ก ๋์จ ์คํจ๋ฅผ ์๋ฆฐ๋ค. ์ํ DB ๋ฅผ ์ธ ์ ์๋ ๊ฒ์ ์ฌ์ฉ์๊ฐ ํ ์ผ์ด๋ผ ์ด์ ์ ํ ์ผ๋ง ๋ณด์ด๊ณ ์คํจ(1)๋ก\n * ๋๋ธ๋ค. ๋ค๋ฅธ ์คํจ๋ ๊ทธ๋๋ก ๋ค์ ๋์ง๋ค โ ์ง๊ธ๊น์ง์ฒ๋ผ Node ๊ฐ ์คํ๊ณผ ํจ๊ป ๋ณด์ด๊ณ 1 ๋ก ๋๋๋ค.\n */\nexport function reportStateDbFailure(error: unknown, proc: ExitableProcess): void {\n if (!(error instanceof StateDbUnavailableError)) throw error;\n console.error(`์ค๋ฅ: ${describeFailure(error)}`);\n proc.exitCode = 1;\n}\n","import { Command } from \"commander\";\nimport {\n ConfigManager,\n StateDB,\n NotionClient,\n SyncOrchestrator,\n WatchSyncService,\n DEFAULT_WATCH_DEBOUNCE_MS,\n} from \"@im-nobsidian/core\";\nimport type { SyncResult, WatchSyncScope } from \"@im-nobsidian/core\";\nimport { openVaultFs } from \"../utils/vault.js\";\n\nfunction formatResult(result: SyncResult): string {\n const pull = `Pull: +${result.pull.created} ~${result.pull.updated} -${result.pull.deleted}`;\n const moved = result.push.moved > 0 ? ` โ${result.push.moved}` : \"\";\n const push = `Push: +${result.push.created} ~${result.push.updated} -${result.push.deleted}${moved}`;\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\n/** ๋ก๊ทธ์ sync ์ด๋ฆ โ ์ฃผ๊ธฐ sync ๋ ๋ณผํธ ์ ์ฒด๋ฅผ ๋ณธ๋ค. */\nfunction syncLabel(scope: WatchSyncScope): string {\n return scope === \"full\" ? \"์ฃผ๊ธฐ์ ๋๊ธฐํ\" : \"๋๊ธฐํ\";\n}\n\nexport const watchCommand = new Command(\"watch\")\n .description(\"ํ์ผ ๋ณ๊ฒฝ ๊ฐ์ง + ์๋ ๋๊ธฐํ\")\n .option(\"-d, --debounce <ms>\", \"๋๋ฐ์ด์ค ๋๊ธฐ ์๊ฐ(ms)\", String(DEFAULT_WATCH_DEBOUNCE_MS))\n .option(\"-i, --interval <seconds>\", \"ํ ๋๊ธฐํ ์ฃผ๊ธฐ(์ด, 0์ด๋ฉด ๋นํ์ฑ)\", \"0\")\n .action(async (options: { debounce: string; interval: string }) => {\n // ์ซ์๊ฐ ์๋๋ฉด ๋๋ฐ์ด์ค๊ฐ 0 ์ด ๋๊ฑฐ๋ ์ฃผ๊ธฐ ๋๊ธฐํ๊ฐ ๋ง์์ด ๊บผ์ง๋ค โ ์์ํ๊ธฐ ์ ์ ๊ฑฐ์ ํ๋ค.\n const debounceMs = Number(options.debounce);\n const intervalSec = Number(options.interval);\n if (!Number.isInteger(debounceMs) || debounceMs < 0) {\n console.error(`--debounce ๋ 0 ์ด์์ ์ ์(ms)์ฌ์ผ ํฉ๋๋ค: ${options.debounce}`);\n process.exitCode = 1;\n return;\n }\n if (!Number.isInteger(intervalSec) || intervalSec < 0) {\n console.error(`--interval ์ 0 ์ด์์ ์ ์(์ด)์ฌ์ผ ํฉ๋๋ค: ${options.interval}`);\n process.exitCode = 1;\n return;\n }\n\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 = await openVaultFs(cwd, config.paths);\n const orchestrator = new SyncOrchestrator(config, stateDb, client, vaultFs);\n\n const service = new WatchSyncService(cwd, orchestrator, {\n debounceMs,\n onFileChange: (event, path) => {\n console.log(`[${timestamp()}] ${event}: ${path}`);\n },\n onSyncStart: (scope) => {\n console.log(`[${timestamp()}] ${syncLabel(scope)} ์์...`);\n },\n onSyncComplete: (result, scope) => {\n console.log(`[${timestamp()}] ${syncLabel(scope)} ์๋ฃ โ ${formatResult(result)}`);\n },\n onSyncCancelled: (result, scope) => {\n console.log(\n `[${timestamp()}] ${syncLabel(scope)} ์ทจ์ โ ๋ฉ์ถ๊ธฐ ์ ๊น์ง ${formatResult(result)}`,\n );\n },\n onSyncError: (error, scope) => {\n console.error(`[${timestamp()}] ${syncLabel(scope)} ์คํจ: ${error.message}`);\n },\n });\n\n let pullTimer: ReturnType<typeof setInterval> | null = null;\n let shuttingDown = false;\n\n const shutdown = async (): Promise<void> => {\n // ๋๋ sync ๋ฅผ ๊ธฐ๋ค๋ฆฌ๋ ์ค์ ํ ๋ฒ ๋ ๋๋ฅด๋ฉด ๊ธฐ๋ค๋ฆฌ์ง ์๊ณ ๋๋ธ๋ค.\n if (shuttingDown) {\n console.log(`[${timestamp()}] ๊ฐ์ ์ข
๋ฃ`);\n return process.exit(1);\n }\n shuttingDown = true;\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 // ํ์ผ ๋ณ๊ฒฝ sync ์ ๊ฐ์ ์ค์ ์ธ์ด๋ค โ ๋๋ sync ๊ฐ ์์ผ๋ฉด ๋๋ ๋ค ํ ๋ฒ ๋๋ค(S-09).\n pullTimer = setInterval(() => service.requestFullSync(), intervalSec * 1000);\n }\n\n await new Promise(() => {});\n });\n","import { Command } from \"commander\";\nimport { ConfigManager, StateDB, NotionClient, SyncOrchestrator } 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\";\nimport { openVaultFs } from \"../utils/vault.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 = await openVaultFs(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","import { Command } from \"commander\";\nimport { ConfigManager, StateDB, NotionClient, SyncOrchestrator } from \"@im-nobsidian/core\";\nimport chalk from \"chalk\";\nimport { icons, failedItem } from \"../utils/format.js\";\nimport { obsidianConfigDir, openVaultFs } from \"../utils/vault.js\";\n\n// ๋ณผํธ์์ ๋ง๋ ๋
ธํธ ํด๋๋ฅผ Notion DB ๋ก ์ฌ๋ฆด ์ค๋น(#75) โ frontmatter ยท `.base` ๋ก ์ฌ์ด๋์นด๋ฅผ ์จ ๋๋ค.\n// Notion ์๋ ์ฐ์ง ์๋๋ค. ์ฌ์ด๋์นด๋ JSON ์ด๋ผ push ์ ์ ๊ณ ์น ์ ์๋ค โ ๋ฌด์์ ์ ๊ทธ๋ ๊ฒ ์ ํ๋์ง ๋ณด์ธ๋ค.\nexport const prepareDbCommand = new Command(\"prepare-db\")\n .description(\"๋ณผํธ์ ๋
ธํธ ํด๋๋ฅผ ๋ค์ push ์์ Notion DB ๋ก ๋ง๋ค๋๋ก ์ค๋น (Notion ์ ๊ทธ๋๋ก)\")\n .argument(\"<folder>\", \"DB ๋ก ๋ง๋ค ํด๋ โ ๋ณผํธ ๊ธฐ์ค ๊ฒฝ๋ก. ๋ฐ๋ก ์๋ ๋
ธํธ๊ฐ ํ์ด ๋๋ค\")\n .action(async (folder: 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 try {\n const client = NotionClient.fromConfig(config);\n const vaultFs = await openVaultFs(cwd, config.paths);\n const orchestrator = new SyncOrchestrator(config, stateDb, client, vaultFs);\n const report = await orchestrator.prepareFolderDatabase(folder, obsidianConfigDir(cwd));\n\n console.log(` ${icons.success} ${report.sidecarPath} โ ํ ${report.rows}๊ฐ`);\n for (const property of report.properties) {\n console.log(` ${property.name}: ${property.type} ${chalk.dim(`(${property.reason})`)}`);\n }\n if (report.views.length > 0) console.log(` ๋ณด๊ธฐ: ${report.views.join(\", \")}`);\n for (const skip of report.skipped) {\n console.log(chalk.dim(` ์ฎ๊ธฐ์ง ์์ โ ${skip.what}: ${skip.reason}`));\n }\n console.log(chalk.dim(\" ํ์
์ ๋ฐ๊พธ๋ ค๋ฉด ์ฌ์ด๋์นด๋ฅผ ๊ณ ์น ๋ค push ํ์ธ์.\"));\n } catch (error) {\n failedItem(folder, error instanceof Error ? error.message : String(error));\n 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","ConfigManager","chalk","ConfigManager","chalk","spinner","Command","ConfigManager","StateDB","NotionClient","Command","ConfigManager","StateDB","NotionClient","Command","ConfigManager","StateDB","NotionClient","SyncOrchestrator","chalk","Command","ConfigManager","StateDB","NotionClient","SyncOrchestrator","chalk","Command","ConfigManager","StateDB","NotionClient","SyncOrchestrator","chalk","Command","ConfigManager","StateDB","NotionClient","SyncOrchestrator","chalk","Command","ConfigManager","StateDB","NotionClient","SyncOrchestrator","chalk","Command","ConfigManager","StateDB","NotionClient","SyncOrchestrator","chalk","remoteDeleted","Command","ConfigManager","StateDB","NotionClient","SyncOrchestrator","matchesPathScope","Command","ConfigManager","StateDB","NotionClient","SyncOrchestrator","matchesPathScope","Command","ConfigManager","StateDB","NotionClient","SyncOrchestrator","Command","ConfigManager","StateDB","NotionClient","SyncOrchestrator","Command","ConfigManager","StateDB","NotionClient","SyncOrchestrator","chalk","Command","ConfigManager","StateDB","NotionClient","SyncOrchestrator","chalk","Command","confirm","ConfigManager","StateDB","NotionClient","SyncOrchestrator","isRemoteDeletion","isRemoteDeletion","Command","ConfigManager","StateDB","NotionClient","SyncOrchestrator","confirm","failed","Command","ConfigManager","StateDB","NotionClient","SyncOrchestrator","Command","ConfigManager","StateDB","NotionClient","SyncOrchestrator","Command","ConfigManager","StateDB","NotionClient","SyncOrchestrator","chalk","report","chalk","Command","ConfigManager","StateDB","NotionClient","SyncOrchestrator","Command","ConfigManager","StateDB","NotionClient","SyncOrchestrator","chalk","Command","ConfigManager","StateDB","NotionClient","SyncOrchestrator","report","chalk","Command"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "im-nobsidian",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"description": "CLI tool for Obsidian โ Notion bidirectional sync",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
"chalk": "^5.6.2",
|
|
16
16
|
"commander": "^15.0.0",
|
|
17
17
|
"ora": "^9.4.1",
|
|
18
|
-
"@im-nobsidian/core": "0.
|
|
18
|
+
"@im-nobsidian/core": "0.9.0"
|
|
19
19
|
},
|
|
20
20
|
"devDependencies": {
|
|
21
21
|
"tsup": "^8.0.0",
|