crosssync 0.1.1 → 0.1.3
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/README.md +16 -0
- package/dist/index.js +359 -33
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -77,9 +77,25 @@ crosssync sync --bookmarks --force # 浏览器运行中也强制写回
|
|
|
77
77
|
crosssync sync --strategy export # 完全只读
|
|
78
78
|
crosssync sync --browser edge # 只处理 Edge
|
|
79
79
|
crosssync sync --profile all # 处理所有 Chromium profile
|
|
80
|
+
crosssync sync --profile "Default,Profile 11" # 同时处理多个 profile
|
|
80
81
|
crosssync sync --json # 机器可读报告
|
|
81
82
|
```
|
|
82
83
|
|
|
84
|
+
如果配置的 profile(默认 `Default`)在某个浏览器中不存在,交互式终端会列出可用的
|
|
85
|
+
profile 让你按序号选择(支持多选),并自动保存选择。列表会附带 `Local State` 中记录的
|
|
86
|
+
本地名称和已登录账号(如 `Profile 11 Roe <roe@example.com>`),便于区分 profile。
|
|
87
|
+
|
|
88
|
+
### `crosssync profiles`
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
crosssync profiles # 列出可用 profile 和当前选择
|
|
92
|
+
crosssync profiles --set "Default,Profile 11,Profile 12"
|
|
93
|
+
crosssync profiles --add "Profile 9"
|
|
94
|
+
crosssync profiles --remove "Profile 11"
|
|
95
|
+
crosssync profiles --all # 选择所有 profile
|
|
96
|
+
crosssync profiles --reset # 恢复为 Default
|
|
97
|
+
```
|
|
98
|
+
|
|
83
99
|
### `crosssync backup`
|
|
84
100
|
|
|
85
101
|
```bash
|
package/dist/index.js
CHANGED
|
@@ -3036,7 +3036,7 @@ var require_constants = __commonJS({
|
|
|
3036
3036
|
"../shared/dist/constants.js"(exports2) {
|
|
3037
3037
|
"use strict";
|
|
3038
3038
|
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
3039
|
-
exports2.DEFAULT_CRON_SCHEDULE = exports2.DEFAULT_EXPORT_RETENTION = exports2.CONFIG_VERSION = exports2.DEFAULT_LOG_LEVEL = exports2.DEFAULT_PROFILE = exports2.PROFILE_NAME_PATTERN = exports2.HISTORY_FILE_NAME = exports2.BOOKMARKS_FILE_NAME = exports2.LOG_FILE_NAME = exports2.CONFIG_FILE_NAME = exports2.LOG_DIR_NAME = exports2.EXPORT_DIR_NAME = exports2.BACKUP_DIR_NAME = exports2.CROSSSYNC_DIR_NAME = exports2.CHROMIUM_EPOCH_OFFSET_MS = exports2.BROWSERS = exports2.LOG_LEVELS = exports2.SHARED_VERSION = exports2.SHARED_PACKAGE = void 0;
|
|
3039
|
+
exports2.DEFAULT_CRON_SCHEDULE = exports2.DEFAULT_EXPORT_RETENTION = exports2.CONFIG_VERSION = exports2.DEFAULT_LOG_LEVEL = exports2.DEFAULT_PROFILE = exports2.PROFILE_NAME_PATTERN = exports2.LOCAL_STATE_FILE_NAME = exports2.HISTORY_FILE_NAME = exports2.BOOKMARKS_FILE_NAME = exports2.LOG_FILE_NAME = exports2.CONFIG_FILE_NAME = exports2.LOG_DIR_NAME = exports2.EXPORT_DIR_NAME = exports2.BACKUP_DIR_NAME = exports2.CROSSSYNC_DIR_NAME = exports2.CHROMIUM_EPOCH_OFFSET_MS = exports2.BROWSERS = exports2.LOG_LEVELS = exports2.SHARED_VERSION = exports2.SHARED_PACKAGE = void 0;
|
|
3040
3040
|
exports2.SHARED_PACKAGE = "@crosssync/shared";
|
|
3041
3041
|
exports2.SHARED_VERSION = "0.1.0";
|
|
3042
3042
|
exports2.LOG_LEVELS = ["debug", "info", "warn", "error"];
|
|
@@ -3050,6 +3050,7 @@ var require_constants = __commonJS({
|
|
|
3050
3050
|
exports2.LOG_FILE_NAME = "crosssync.log";
|
|
3051
3051
|
exports2.BOOKMARKS_FILE_NAME = "Bookmarks";
|
|
3052
3052
|
exports2.HISTORY_FILE_NAME = "History";
|
|
3053
|
+
exports2.LOCAL_STATE_FILE_NAME = "Local State";
|
|
3053
3054
|
exports2.PROFILE_NAME_PATTERN = /^(Default|Profile \d+)$/;
|
|
3054
3055
|
exports2.DEFAULT_PROFILE = "Default";
|
|
3055
3056
|
exports2.DEFAULT_LOG_LEVEL = "info";
|
|
@@ -3126,12 +3127,36 @@ var require_config = __commonJS({
|
|
|
3126
3127
|
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
3127
3128
|
exports2.SYNC_STRATEGIES = void 0;
|
|
3128
3129
|
exports2.resolveExportRetention = resolveExportRetention3;
|
|
3130
|
+
exports2.parseProfileList = parseProfileList2;
|
|
3131
|
+
exports2.addProfilesToSelection = addProfilesToSelection3;
|
|
3129
3132
|
exports2.createDefaultConfig = createDefaultConfig3;
|
|
3130
3133
|
var constants_1 = require_constants();
|
|
3131
3134
|
exports2.SYNC_STRATEGIES = ["merge", "export"];
|
|
3132
3135
|
function resolveExportRetention3(config) {
|
|
3133
3136
|
return config.exports?.keep ?? constants_1.DEFAULT_EXPORT_RETENTION;
|
|
3134
3137
|
}
|
|
3138
|
+
function parseProfileList2(value) {
|
|
3139
|
+
const names = [];
|
|
3140
|
+
for (const part of value.split(",")) {
|
|
3141
|
+
const name = part.trim();
|
|
3142
|
+
if (name.length > 0 && !names.includes(name)) {
|
|
3143
|
+
names.push(name);
|
|
3144
|
+
}
|
|
3145
|
+
}
|
|
3146
|
+
return names;
|
|
3147
|
+
}
|
|
3148
|
+
function addProfilesToSelection3(current, additions) {
|
|
3149
|
+
if (current.trim() === "all") {
|
|
3150
|
+
return "all";
|
|
3151
|
+
}
|
|
3152
|
+
const names = parseProfileList2(current);
|
|
3153
|
+
for (const name of additions) {
|
|
3154
|
+
if (name.trim().length > 0 && !names.includes(name.trim())) {
|
|
3155
|
+
names.push(name.trim());
|
|
3156
|
+
}
|
|
3157
|
+
}
|
|
3158
|
+
return names.join(",");
|
|
3159
|
+
}
|
|
3135
3160
|
function createDefaultConfig3(options) {
|
|
3136
3161
|
return {
|
|
3137
3162
|
version: constants_1.CONFIG_VERSION,
|
|
@@ -3182,7 +3207,7 @@ var require_utils = __commonJS({
|
|
|
3182
3207
|
exports2.toChromiumTime = toChromiumTime;
|
|
3183
3208
|
exports2.resolveHomeDir = resolveHomeDir3;
|
|
3184
3209
|
exports2.resolveBrowserDataDir = resolveBrowserDataDir;
|
|
3185
|
-
exports2.listProfiles =
|
|
3210
|
+
exports2.listProfiles = listProfiles2;
|
|
3186
3211
|
exports2.resolveProfileDir = resolveProfileDir;
|
|
3187
3212
|
exports2.resolveCrossSyncDirs = resolveCrossSyncDirs2;
|
|
3188
3213
|
exports2.loadSqliteDriver = loadSqliteDriver;
|
|
@@ -3194,7 +3219,7 @@ var require_utils = __commonJS({
|
|
|
3194
3219
|
exports2.isBookmarkFile = isBookmarkFile;
|
|
3195
3220
|
exports2.isHistoryEntry = isHistoryEntry;
|
|
3196
3221
|
exports2.isLogLevel = isLogLevel2;
|
|
3197
|
-
exports2.isBrowserName =
|
|
3222
|
+
exports2.isBrowserName = isBrowserName3;
|
|
3198
3223
|
exports2.isCliConfig = isCliConfig3;
|
|
3199
3224
|
var fs_1 = __importDefault(require("fs"));
|
|
3200
3225
|
var os_1 = __importDefault(require("os"));
|
|
@@ -3314,7 +3339,7 @@ var require_utils = __commonJS({
|
|
|
3314
3339
|
const key = platform === "darwin" ? "darwin" : "linux";
|
|
3315
3340
|
return path_1.default.join(home, ...segments[key]);
|
|
3316
3341
|
}
|
|
3317
|
-
function
|
|
3342
|
+
function listProfiles2(dataDir) {
|
|
3318
3343
|
let entries;
|
|
3319
3344
|
try {
|
|
3320
3345
|
entries = fs_1.default.readdirSync(dataDir, { withFileTypes: true });
|
|
@@ -3419,7 +3444,7 @@ var require_utils = __commonJS({
|
|
|
3419
3444
|
function isLogLevel2(value) {
|
|
3420
3445
|
return typeof value === "string" && constants_1.LOG_LEVELS.includes(value);
|
|
3421
3446
|
}
|
|
3422
|
-
function
|
|
3447
|
+
function isBrowserName3(value) {
|
|
3423
3448
|
return typeof value === "string" && constants_1.BROWSERS.includes(value);
|
|
3424
3449
|
}
|
|
3425
3450
|
function isCliConfig3(value) {
|
|
@@ -3785,11 +3810,40 @@ var require_chromium = __commonJS({
|
|
|
3785
3810
|
return mod && mod.__esModule ? mod : { "default": mod };
|
|
3786
3811
|
};
|
|
3787
3812
|
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
3813
|
+
exports2.readProfileInfos = readProfileInfos;
|
|
3788
3814
|
exports2.toHistoryEntry = toHistoryEntry;
|
|
3789
3815
|
exports2.countBookmarkFileNodes = countBookmarkFileNodes;
|
|
3790
|
-
exports2.createBrowserReader =
|
|
3816
|
+
exports2.createBrowserReader = createBrowserReader3;
|
|
3791
3817
|
var path_1 = __importDefault(require("path"));
|
|
3792
3818
|
var shared_1 = require_dist();
|
|
3819
|
+
function readProfileInfos(dataDir) {
|
|
3820
|
+
const localState = (0, shared_1.readJsonFile)(path_1.default.join(dataDir, shared_1.LOCAL_STATE_FILE_NAME));
|
|
3821
|
+
const cache = localState?.profile?.info_cache ?? {};
|
|
3822
|
+
return (0, shared_1.listProfiles)(dataDir).map((profile) => toProfileInfo(profile, cache[profile]));
|
|
3823
|
+
}
|
|
3824
|
+
function toProfileInfo(profile, raw) {
|
|
3825
|
+
const info = { profile };
|
|
3826
|
+
const name = readString(raw, "name");
|
|
3827
|
+
const userName = readString(raw, "user_name");
|
|
3828
|
+
const gaiaName = readString(raw, "gaia_name");
|
|
3829
|
+
if (name !== void 0) {
|
|
3830
|
+
info.name = name;
|
|
3831
|
+
}
|
|
3832
|
+
if (userName !== void 0) {
|
|
3833
|
+
info.userName = userName;
|
|
3834
|
+
}
|
|
3835
|
+
if (gaiaName !== void 0) {
|
|
3836
|
+
info.gaiaName = gaiaName;
|
|
3837
|
+
}
|
|
3838
|
+
if (typeof raw?.is_using_default_name === "boolean") {
|
|
3839
|
+
info.isUsingDefaultName = raw.is_using_default_name;
|
|
3840
|
+
}
|
|
3841
|
+
return info;
|
|
3842
|
+
}
|
|
3843
|
+
function readString(raw, key) {
|
|
3844
|
+
const value = raw?.[key];
|
|
3845
|
+
return typeof value === "string" && value.trim().length > 0 ? value.trim() : void 0;
|
|
3846
|
+
}
|
|
3793
3847
|
function toHistoryEntry(row) {
|
|
3794
3848
|
return {
|
|
3795
3849
|
id: typeof row.id === "number" ? row.id : void 0,
|
|
@@ -3810,17 +3864,20 @@ var require_chromium = __commonJS({
|
|
|
3810
3864
|
}
|
|
3811
3865
|
return total;
|
|
3812
3866
|
}
|
|
3813
|
-
function
|
|
3867
|
+
function createBrowserReader3(browser, options = {}) {
|
|
3814
3868
|
const dataDir = options.dataDir ?? (0, shared_1.resolveBrowserDataDir)(browser, { platform: options.platform, env: options.env });
|
|
3815
3869
|
const bookmarksPath = (profile) => path_1.default.join((0, shared_1.resolveProfileDir)(dataDir, profile), shared_1.BOOKMARKS_FILE_NAME);
|
|
3816
3870
|
const historyPath = (profile) => path_1.default.join((0, shared_1.resolveProfileDir)(dataDir, profile), shared_1.HISTORY_FILE_NAME);
|
|
3817
3871
|
const profiles = () => (0, shared_1.listProfiles)(dataDir);
|
|
3872
|
+
const profileInfos = () => readProfileInfos(dataDir);
|
|
3873
|
+
const profileInfo = (profile) => profileInfos().find((info) => info.profile === profile) ?? null;
|
|
3818
3874
|
const resolveSelectedProfiles = (profile) => {
|
|
3819
3875
|
const found = profiles();
|
|
3820
|
-
if (profile === "all") {
|
|
3876
|
+
if (profile.trim() === "all") {
|
|
3821
3877
|
return found;
|
|
3822
3878
|
}
|
|
3823
|
-
|
|
3879
|
+
const requested = (0, shared_1.parseProfileList)(profile);
|
|
3880
|
+
return requested.filter((name) => found.includes(name));
|
|
3824
3881
|
};
|
|
3825
3882
|
const readBookmarks = (profile) => {
|
|
3826
3883
|
const filePath = bookmarksPath(profile);
|
|
@@ -3843,6 +3900,8 @@ var require_chromium = __commonJS({
|
|
|
3843
3900
|
browser,
|
|
3844
3901
|
dataDir,
|
|
3845
3902
|
profiles,
|
|
3903
|
+
profileInfos,
|
|
3904
|
+
profileInfo,
|
|
3846
3905
|
resolveProfiles: resolveSelectedProfiles,
|
|
3847
3906
|
bookmarksPath,
|
|
3848
3907
|
historyPath,
|
|
@@ -3886,12 +3945,12 @@ var require_readers = __commonJS({
|
|
|
3886
3945
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports3, p)) __createBinding(exports3, m, p);
|
|
3887
3946
|
};
|
|
3888
3947
|
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
3889
|
-
exports2.createConfiguredBrowserReaders =
|
|
3948
|
+
exports2.createConfiguredBrowserReaders = createConfiguredBrowserReaders5;
|
|
3890
3949
|
exports2.supportedBrowsers = supportedBrowsers;
|
|
3891
3950
|
var shared_1 = require_dist();
|
|
3892
3951
|
var chromium_1 = require_chromium();
|
|
3893
3952
|
__exportStar(require_chromium(), exports2);
|
|
3894
|
-
function
|
|
3953
|
+
function createConfiguredBrowserReaders5(config, options = {}) {
|
|
3895
3954
|
const readers = [];
|
|
3896
3955
|
for (const browser of shared_1.BROWSERS) {
|
|
3897
3956
|
const browserConfig = config.browsers[browser];
|
|
@@ -4197,13 +4256,16 @@ var require_sync = __commonJS({
|
|
|
4197
4256
|
for (const reader of readers) {
|
|
4198
4257
|
const profiles = reader.resolveProfiles(config.profile);
|
|
4199
4258
|
if (profiles.length === 0) {
|
|
4200
|
-
|
|
4259
|
+
const available = reader.profiles();
|
|
4260
|
+
const suffix = available.length > 0 ? ` Available: ${available.join(", ")}.` : "";
|
|
4261
|
+
warnings.push(`No ${reader.browser} profile found under ${reader.dataDir}.${suffix}`);
|
|
4201
4262
|
continue;
|
|
4202
4263
|
}
|
|
4203
4264
|
for (const profile of profiles) {
|
|
4204
4265
|
const outcome = {
|
|
4205
4266
|
browser: reader.browser,
|
|
4206
4267
|
profile,
|
|
4268
|
+
profileInfo: reader.profileInfo(profile),
|
|
4207
4269
|
bookmarksRead: 0,
|
|
4208
4270
|
historyRead: 0,
|
|
4209
4271
|
writtenBack: false,
|
|
@@ -4368,7 +4430,7 @@ var require_dist2 = __commonJS({
|
|
|
4368
4430
|
"../core/dist/index.js"(exports2) {
|
|
4369
4431
|
"use strict";
|
|
4370
4432
|
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
4371
|
-
exports2.writeBackBookmarks = exports2.runSync = exports2.pruneBackups = exports2.listBackups = exports2.formatBackupId = exports2.createBackup = exports2.backupFileName = exports2.BACKUP_MANIFEST_FILE = exports2.mergeHistory = exports2.mergeBookmarkFiles = exports2.toHistoryEntry = exports2.supportedBrowsers = exports2.createConfiguredBrowserReaders = exports2.createBrowserReader = exports2.countBookmarkFileNodes = exports2.parseProcessOutput = exports2.isBrowserRunning = exports2.getRunningBrowsers = exports2.getRunningBrowserNames = exports2.getProcessName = exports2.buildProcessQuery = exports2.readLogEntries = exports2.clearLogFile = exports2.Logger = exports2.MAX_BUFFERED_LOG_ENTRIES = exports2.DEFAULT_LOG_LIMIT = exports2.DEFAULT_BACKUP_RETENTION = exports2.BROWSER_PROCESS_NAMES = exports2.VERSION = exports2.PACKAGE = void 0;
|
|
4433
|
+
exports2.writeBackBookmarks = exports2.runSync = exports2.pruneBackups = exports2.listBackups = exports2.formatBackupId = exports2.createBackup = exports2.backupFileName = exports2.BACKUP_MANIFEST_FILE = exports2.mergeHistory = exports2.mergeBookmarkFiles = exports2.toHistoryEntry = exports2.supportedBrowsers = exports2.readProfileInfos = exports2.createConfiguredBrowserReaders = exports2.createBrowserReader = exports2.countBookmarkFileNodes = exports2.parseProcessOutput = exports2.isBrowserRunning = exports2.getRunningBrowsers = exports2.getRunningBrowserNames = exports2.getProcessName = exports2.buildProcessQuery = exports2.readLogEntries = exports2.clearLogFile = exports2.Logger = exports2.MAX_BUFFERED_LOG_ENTRIES = exports2.DEFAULT_LOG_LIMIT = exports2.DEFAULT_BACKUP_RETENTION = exports2.BROWSER_PROCESS_NAMES = exports2.VERSION = exports2.PACKAGE = void 0;
|
|
4372
4434
|
var constants_1 = require_constants2();
|
|
4373
4435
|
Object.defineProperty(exports2, "PACKAGE", { enumerable: true, get: function() {
|
|
4374
4436
|
return constants_1.CORE_PACKAGE;
|
|
@@ -4428,6 +4490,9 @@ var require_dist2 = __commonJS({
|
|
|
4428
4490
|
Object.defineProperty(exports2, "createConfiguredBrowserReaders", { enumerable: true, get: function() {
|
|
4429
4491
|
return readers_1.createConfiguredBrowserReaders;
|
|
4430
4492
|
} });
|
|
4493
|
+
Object.defineProperty(exports2, "readProfileInfos", { enumerable: true, get: function() {
|
|
4494
|
+
return readers_1.readProfileInfos;
|
|
4495
|
+
} });
|
|
4431
4496
|
Object.defineProperty(exports2, "supportedBrowsers", { enumerable: true, get: function() {
|
|
4432
4497
|
return readers_1.supportedBrowsers;
|
|
4433
4498
|
} });
|
|
@@ -4501,7 +4566,7 @@ var {
|
|
|
4501
4566
|
} = import_index.default;
|
|
4502
4567
|
|
|
4503
4568
|
// src/constants.ts
|
|
4504
|
-
var CLI_VERSION = "0.1.
|
|
4569
|
+
var CLI_VERSION = "0.1.3";
|
|
4505
4570
|
var CLI_NAME = "crosssync";
|
|
4506
4571
|
var CLI_DESCRIPTION = "Cross-browser bookmark & history sync CLI (Chrome + Edge)";
|
|
4507
4572
|
var WINDOWS_TASK_NAME = "CrossSync";
|
|
@@ -4573,7 +4638,9 @@ function readMergedData(config, options = {}) {
|
|
|
4573
4638
|
}
|
|
4574
4639
|
const profiles = reader.resolveProfiles(options.profile ?? config.profile);
|
|
4575
4640
|
if (profiles.length === 0) {
|
|
4576
|
-
|
|
4641
|
+
const available = reader.profiles();
|
|
4642
|
+
const suffix = available.length > 0 ? ` Available: ${available.join(", ")}.` : "";
|
|
4643
|
+
warnings.push(`No ${reader.browser} profile found under ${reader.dataDir}.${suffix}`);
|
|
4577
4644
|
continue;
|
|
4578
4645
|
}
|
|
4579
4646
|
for (const profile of profiles) {
|
|
@@ -5450,8 +5517,27 @@ function pruneExports(exportDir, keep) {
|
|
|
5450
5517
|
}
|
|
5451
5518
|
|
|
5452
5519
|
// src/commands/sync.ts
|
|
5520
|
+
var readline = __toESM(require("readline/promises"));
|
|
5453
5521
|
var import_shared10 = __toESM(require_dist());
|
|
5454
5522
|
var import_core4 = __toESM(require_dist2());
|
|
5523
|
+
|
|
5524
|
+
// src/profile-info.ts
|
|
5525
|
+
function formatProfileAccount(info) {
|
|
5526
|
+
if (!info) {
|
|
5527
|
+
return null;
|
|
5528
|
+
}
|
|
5529
|
+
const displayName = info.isUsingDefaultName ? info.gaiaName ?? info.name : info.name ?? info.gaiaName;
|
|
5530
|
+
const parts = [];
|
|
5531
|
+
if (displayName) {
|
|
5532
|
+
parts.push(displayName);
|
|
5533
|
+
}
|
|
5534
|
+
if (info.userName && info.userName !== displayName) {
|
|
5535
|
+
parts.push(`<${info.userName}>`);
|
|
5536
|
+
}
|
|
5537
|
+
return parts.length > 0 ? parts.join(" ") : null;
|
|
5538
|
+
}
|
|
5539
|
+
|
|
5540
|
+
// src/commands/sync.ts
|
|
5455
5541
|
function resolveTargets(options) {
|
|
5456
5542
|
if (options.all) {
|
|
5457
5543
|
return { bookmarks: true, history: true };
|
|
@@ -5498,9 +5584,117 @@ function buildSyncConfig(config, options) {
|
|
|
5498
5584
|
}
|
|
5499
5585
|
};
|
|
5500
5586
|
}
|
|
5501
|
-
function
|
|
5587
|
+
async function ensureProfilesSelected(context, options) {
|
|
5588
|
+
if (options.profile !== void 0 || options.json) {
|
|
5589
|
+
return;
|
|
5590
|
+
}
|
|
5591
|
+
const interactive = options.selectProfiles !== void 0 || process.stdin.isTTY === true && process.stdout.isTTY === true;
|
|
5592
|
+
if (!interactive) {
|
|
5593
|
+
return;
|
|
5594
|
+
}
|
|
5595
|
+
const effective = buildSyncConfig(context.config, options);
|
|
5596
|
+
const readers = (0, import_core4.createConfiguredBrowserReaders)(effective, { platform: options.platform });
|
|
5597
|
+
const select = options.selectProfiles ?? promptForProfiles;
|
|
5598
|
+
let changed = false;
|
|
5599
|
+
for (const reader of readers) {
|
|
5600
|
+
if (reader.resolveProfiles(context.config.profile).length > 0) {
|
|
5601
|
+
continue;
|
|
5602
|
+
}
|
|
5603
|
+
const available = reader.profiles();
|
|
5604
|
+
if (available.length === 0) {
|
|
5605
|
+
continue;
|
|
5606
|
+
}
|
|
5607
|
+
const selected = await select({
|
|
5608
|
+
browser: reader.browser,
|
|
5609
|
+
dataDir: reader.dataDir,
|
|
5610
|
+
available,
|
|
5611
|
+
infos: reader.profileInfos(),
|
|
5612
|
+
current: context.config.profile
|
|
5613
|
+
});
|
|
5614
|
+
if (!selected || selected.length === 0) {
|
|
5615
|
+
continue;
|
|
5616
|
+
}
|
|
5617
|
+
const valid = selected.filter((name) => available.includes(name));
|
|
5618
|
+
if (valid.length === 0) {
|
|
5619
|
+
continue;
|
|
5620
|
+
}
|
|
5621
|
+
context.config.profile = (0, import_shared10.addProfilesToSelection)(context.config.profile, valid);
|
|
5622
|
+
changed = true;
|
|
5623
|
+
}
|
|
5624
|
+
if (changed) {
|
|
5625
|
+
saveConfigFile(context.dirs.configFile, context.config);
|
|
5626
|
+
context.io.out(`Profile selection saved: ${context.config.profile}`);
|
|
5627
|
+
}
|
|
5628
|
+
}
|
|
5629
|
+
async function promptForProfiles(request) {
|
|
5630
|
+
const output = process.stdout;
|
|
5631
|
+
output.write(`
|
|
5632
|
+
No ${request.browser} profile matched "${request.current}".
|
|
5633
|
+
`);
|
|
5634
|
+
output.write(`Available ${request.browser} profiles in ${request.dataDir}:
|
|
5635
|
+
`);
|
|
5636
|
+
const infos = new Map((request.infos ?? []).map((info) => [info.profile, info]));
|
|
5637
|
+
request.available.forEach((name, index) => {
|
|
5638
|
+
const account = formatProfileAccount(infos.get(name));
|
|
5639
|
+
output.write(` ${index + 1}) ${name}${account ? ` ${account}` : ""}
|
|
5640
|
+
`);
|
|
5641
|
+
});
|
|
5642
|
+
const rl = readline.createInterface({ input: process.stdin, output });
|
|
5643
|
+
try {
|
|
5644
|
+
for (; ; ) {
|
|
5645
|
+
const answer = await rl.question(
|
|
5646
|
+
'Select profiles (comma-separated numbers or names, "all", empty to skip): '
|
|
5647
|
+
);
|
|
5648
|
+
const parsed = parseProfileAnswer(answer, request.available);
|
|
5649
|
+
if (parsed !== void 0) {
|
|
5650
|
+
return parsed;
|
|
5651
|
+
}
|
|
5652
|
+
output.write("Invalid selection; please try again.\n");
|
|
5653
|
+
}
|
|
5654
|
+
} catch {
|
|
5655
|
+
return null;
|
|
5656
|
+
} finally {
|
|
5657
|
+
rl.close();
|
|
5658
|
+
}
|
|
5659
|
+
}
|
|
5660
|
+
function parseProfileAnswer(answer, available) {
|
|
5661
|
+
const trimmed = answer.trim();
|
|
5662
|
+
if (trimmed.length === 0) {
|
|
5663
|
+
return null;
|
|
5664
|
+
}
|
|
5665
|
+
if (trimmed.toLowerCase() === "all") {
|
|
5666
|
+
return [...available];
|
|
5667
|
+
}
|
|
5668
|
+
const selected = [];
|
|
5669
|
+
for (const token of trimmed.split(",")) {
|
|
5670
|
+
const value = token.trim();
|
|
5671
|
+
if (value.length === 0) {
|
|
5672
|
+
continue;
|
|
5673
|
+
}
|
|
5674
|
+
if (/^\d+$/.test(value)) {
|
|
5675
|
+
const index = Number(value) - 1;
|
|
5676
|
+
if (index < 0 || index >= available.length) {
|
|
5677
|
+
return void 0;
|
|
5678
|
+
}
|
|
5679
|
+
const name = available[index];
|
|
5680
|
+
if (!selected.includes(name)) {
|
|
5681
|
+
selected.push(name);
|
|
5682
|
+
}
|
|
5683
|
+
continue;
|
|
5684
|
+
}
|
|
5685
|
+
if (!available.includes(value)) {
|
|
5686
|
+
return void 0;
|
|
5687
|
+
}
|
|
5688
|
+
if (!selected.includes(value)) {
|
|
5689
|
+
selected.push(value);
|
|
5690
|
+
}
|
|
5691
|
+
}
|
|
5692
|
+
return selected.length > 0 ? selected : void 0;
|
|
5693
|
+
}
|
|
5694
|
+
async function runSyncCommand(context, options = {}) {
|
|
5502
5695
|
const targets = resolveTargets(options);
|
|
5503
5696
|
const strategy = resolveStrategy(options.strategy) ?? context.config.sync.strategy;
|
|
5697
|
+
await ensureProfilesSelected(context, options);
|
|
5504
5698
|
const config = buildSyncConfig(context.config, options);
|
|
5505
5699
|
const report = (0, import_core4.runSync)({
|
|
5506
5700
|
config,
|
|
@@ -5537,7 +5731,8 @@ function formatSyncReport(report) {
|
|
|
5537
5731
|
].filter((kind) => kind !== null).join(" + ");
|
|
5538
5732
|
lines.push(`CrossSync sync (${report.strategy}, ${kinds})${report.dryRun ? " [dry run]" : ""}`);
|
|
5539
5733
|
for (const outcome of report.browsers) {
|
|
5540
|
-
const
|
|
5734
|
+
const account = formatProfileAccount(outcome.profileInfo);
|
|
5735
|
+
const parts = [`${outcome.browser}/${outcome.profile}${account ? ` \u2014 ${account}` : ""}`];
|
|
5541
5736
|
if (report.targets.bookmarks) {
|
|
5542
5737
|
parts.push(`${outcome.bookmarksRead} bookmark nodes`);
|
|
5543
5738
|
}
|
|
@@ -5694,14 +5889,136 @@ function formatLogEntry(entry) {
|
|
|
5694
5889
|
return `${timestamp} [${entry.level.toUpperCase()}] ${entry.message}${meta}`;
|
|
5695
5890
|
}
|
|
5696
5891
|
|
|
5697
|
-
// src/commands/
|
|
5698
|
-
var import_path7 = __toESM(require("path"));
|
|
5892
|
+
// src/commands/profiles.ts
|
|
5699
5893
|
var import_shared13 = __toESM(require_dist());
|
|
5700
5894
|
var import_core7 = __toESM(require_dist2());
|
|
5895
|
+
function parseProfileNames(value) {
|
|
5896
|
+
if (value.trim().toLowerCase() === "all") {
|
|
5897
|
+
return "all";
|
|
5898
|
+
}
|
|
5899
|
+
const names = (0, import_shared13.parseProfileList)(value);
|
|
5900
|
+
if (names.length === 0) {
|
|
5901
|
+
throw new Error("Profile list is empty.");
|
|
5902
|
+
}
|
|
5903
|
+
for (const name of names) {
|
|
5904
|
+
if (!import_shared13.PROFILE_NAME_PATTERN.test(name)) {
|
|
5905
|
+
throw new Error(`Invalid profile name "${name}". Use "Default" or "Profile N".`);
|
|
5906
|
+
}
|
|
5907
|
+
}
|
|
5908
|
+
return names;
|
|
5909
|
+
}
|
|
5910
|
+
function listProfiles(context, options) {
|
|
5911
|
+
const filter = options.browser ?? "all";
|
|
5912
|
+
if (filter !== "all" && !(0, import_shared13.isBrowserName)(filter)) {
|
|
5913
|
+
context.io.err(`Unknown browser "${filter}". Use one of: ${import_shared13.BROWSERS.join(", ")}, all.`);
|
|
5914
|
+
return 1;
|
|
5915
|
+
}
|
|
5916
|
+
const selection = context.config.profile;
|
|
5917
|
+
const selected = selection.trim() === "all" ? null : (0, import_shared13.parseProfileList)(selection);
|
|
5918
|
+
for (const browser of import_shared13.BROWSERS) {
|
|
5919
|
+
if (filter !== "all" && browser !== filter) {
|
|
5920
|
+
continue;
|
|
5921
|
+
}
|
|
5922
|
+
const browserConfig = context.config.browsers[browser];
|
|
5923
|
+
const reader = (0, import_core7.createBrowserReader)(browser, {
|
|
5924
|
+
dataDir: browserConfig.dataDir,
|
|
5925
|
+
platform: options.platform,
|
|
5926
|
+
env: options.env
|
|
5927
|
+
});
|
|
5928
|
+
const available = reader.profiles();
|
|
5929
|
+
context.io.out(`${browser}: ${reader.dataDir}${browserConfig.enabled ? "" : " [disabled]"}`);
|
|
5930
|
+
if (available.length === 0) {
|
|
5931
|
+
context.io.out(" (no profiles found)");
|
|
5932
|
+
continue;
|
|
5933
|
+
}
|
|
5934
|
+
const infos = new Map(reader.profileInfos().map((info) => [info.profile, info]));
|
|
5935
|
+
for (const name of available) {
|
|
5936
|
+
const isSelected = selected === null || selected.includes(name);
|
|
5937
|
+
const account = formatProfileAccount(infos.get(name));
|
|
5938
|
+
context.io.out(
|
|
5939
|
+
` ${name}${account ? ` ${account}` : ""}${isSelected ? " [selected]" : ""}`
|
|
5940
|
+
);
|
|
5941
|
+
}
|
|
5942
|
+
const missing = selected?.filter((name) => !available.includes(name)) ?? [];
|
|
5943
|
+
if (missing.length > 0) {
|
|
5944
|
+
context.io.out(` (configured but not found: ${missing.join(", ")})`);
|
|
5945
|
+
}
|
|
5946
|
+
}
|
|
5947
|
+
context.io.out(`Selected profile(s): ${selection}`);
|
|
5948
|
+
return 0;
|
|
5949
|
+
}
|
|
5950
|
+
function expandAllProfiles(context, options) {
|
|
5951
|
+
const names = [];
|
|
5952
|
+
const readers = (0, import_core7.createConfiguredBrowserReaders)(context.config, {
|
|
5953
|
+
platform: options.platform,
|
|
5954
|
+
env: options.env
|
|
5955
|
+
});
|
|
5956
|
+
for (const reader of readers) {
|
|
5957
|
+
for (const name of reader.profiles()) {
|
|
5958
|
+
if (!names.includes(name)) {
|
|
5959
|
+
names.push(name);
|
|
5960
|
+
}
|
|
5961
|
+
}
|
|
5962
|
+
}
|
|
5963
|
+
return names;
|
|
5964
|
+
}
|
|
5965
|
+
function removeProfiles(context, current, names, options) {
|
|
5966
|
+
if (names === "all") {
|
|
5967
|
+
throw new Error("Refusing to remove every profile; use --reset or --set instead.");
|
|
5968
|
+
}
|
|
5969
|
+
const base = current.trim() === "all" ? expandAllProfiles(context, options).join(",") : current;
|
|
5970
|
+
const remaining = (0, import_shared13.parseProfileList)(base).filter((name) => !names.includes(name));
|
|
5971
|
+
if (remaining.length === (0, import_shared13.parseProfileList)(base).length) {
|
|
5972
|
+
throw new Error(`None of the selected profiles matched: ${names.join(", ")}.`);
|
|
5973
|
+
}
|
|
5974
|
+
if (remaining.length === 0) {
|
|
5975
|
+
throw new Error("Refusing to remove every profile; use --reset or --set instead.");
|
|
5976
|
+
}
|
|
5977
|
+
return remaining.join(",");
|
|
5978
|
+
}
|
|
5979
|
+
function runProfilesCommand(context, options = {}) {
|
|
5980
|
+
const mutating = options.set !== void 0 || options.add !== void 0 || options.remove !== void 0 || options.all === true || options.reset === true;
|
|
5981
|
+
if (!mutating) {
|
|
5982
|
+
return listProfiles(context, options);
|
|
5983
|
+
}
|
|
5984
|
+
if (options.browser !== void 0 && options.browser !== "all") {
|
|
5985
|
+
context.io.err("--browser only applies when listing; profile changes are global.");
|
|
5986
|
+
return 1;
|
|
5987
|
+
}
|
|
5988
|
+
try {
|
|
5989
|
+
let next = context.config.profile;
|
|
5990
|
+
if (options.reset) {
|
|
5991
|
+
next = import_shared13.DEFAULT_PROFILE;
|
|
5992
|
+
} else if (options.all) {
|
|
5993
|
+
next = "all";
|
|
5994
|
+
} else if (options.set !== void 0) {
|
|
5995
|
+
const names = parseProfileNames(options.set);
|
|
5996
|
+
next = names === "all" ? "all" : names.join(",");
|
|
5997
|
+
} else if (options.add !== void 0) {
|
|
5998
|
+
const names = parseProfileNames(options.add);
|
|
5999
|
+
next = names === "all" ? "all" : (0, import_shared13.addProfilesToSelection)(next, names);
|
|
6000
|
+
} else if (options.remove !== void 0) {
|
|
6001
|
+
next = removeProfiles(context, next, parseProfileNames(options.remove), options);
|
|
6002
|
+
}
|
|
6003
|
+
const nextConfig = { ...context.config, profile: next };
|
|
6004
|
+
saveConfigFile(context.dirs.configFile, nextConfig);
|
|
6005
|
+
context.config = nextConfig;
|
|
6006
|
+
context.io.out(`profile = ${JSON.stringify(next)}`);
|
|
6007
|
+
return 0;
|
|
6008
|
+
} catch (error) {
|
|
6009
|
+
context.io.err(error instanceof Error ? error.message : String(error));
|
|
6010
|
+
return 1;
|
|
6011
|
+
}
|
|
6012
|
+
}
|
|
6013
|
+
|
|
6014
|
+
// src/commands/status.ts
|
|
6015
|
+
var import_path7 = __toESM(require("path"));
|
|
6016
|
+
var import_shared14 = __toESM(require_dist());
|
|
6017
|
+
var import_core8 = __toESM(require_dist2());
|
|
5701
6018
|
function buildStatusReport(context, options = {}) {
|
|
5702
6019
|
const { config, dirs } = context;
|
|
5703
6020
|
const running = new Map(
|
|
5704
|
-
(0,
|
|
6021
|
+
(0, import_core8.getRunningBrowsers)({ platform: options.platform }).map((status) => [
|
|
5705
6022
|
status.browser,
|
|
5706
6023
|
status.running
|
|
5707
6024
|
])
|
|
@@ -5716,14 +6033,14 @@ function buildStatusReport(context, options = {}) {
|
|
|
5716
6033
|
for (const profile of reader.resolveProfiles(config.profile)) {
|
|
5717
6034
|
profiles.push({
|
|
5718
6035
|
profile,
|
|
5719
|
-
bookmarks: (0,
|
|
5720
|
-
history: (0,
|
|
6036
|
+
bookmarks: (0, import_shared14.pathExists)(reader.bookmarksPath(profile)),
|
|
6037
|
+
history: (0, import_shared14.pathExists)(reader.historyPath(profile))
|
|
5721
6038
|
});
|
|
5722
6039
|
}
|
|
5723
6040
|
browsers.push({
|
|
5724
6041
|
browser,
|
|
5725
6042
|
dataDir: reader.dataDir,
|
|
5726
|
-
exists: (0,
|
|
6043
|
+
exists: (0, import_shared14.pathExists)(reader.dataDir),
|
|
5727
6044
|
running: running.get(browser) ?? false,
|
|
5728
6045
|
profiles
|
|
5729
6046
|
});
|
|
@@ -5737,14 +6054,14 @@ function buildStatusReport(context, options = {}) {
|
|
|
5737
6054
|
});
|
|
5738
6055
|
}
|
|
5739
6056
|
}
|
|
5740
|
-
const backups = (0,
|
|
6057
|
+
const backups = (0, import_core8.listBackups)(config.backupDir);
|
|
5741
6058
|
const exports2 = listExports(config.exportDir, 1);
|
|
5742
6059
|
return {
|
|
5743
6060
|
version: CLI_VERSION,
|
|
5744
6061
|
configFile: dirs.configFile,
|
|
5745
6062
|
dataDir: config.dataDir,
|
|
5746
6063
|
logLevel: config.logLevel,
|
|
5747
|
-
logFile: import_path7.default.join(config.logDir,
|
|
6064
|
+
logFile: import_path7.default.join(config.logDir, import_shared14.LOG_FILE_NAME),
|
|
5748
6065
|
profile: config.profile,
|
|
5749
6066
|
schedule: { ...config.schedule },
|
|
5750
6067
|
browsers,
|
|
@@ -5757,7 +6074,7 @@ function buildStatusReport(context, options = {}) {
|
|
|
5757
6074
|
};
|
|
5758
6075
|
}
|
|
5759
6076
|
function createReader(browser, dataDir, options) {
|
|
5760
|
-
return (0,
|
|
6077
|
+
return (0, import_core8.createBrowserReader)(browser, {
|
|
5761
6078
|
dataDir,
|
|
5762
6079
|
platform: options.platform,
|
|
5763
6080
|
env: options.env
|
|
@@ -5806,11 +6123,16 @@ function createCliContext(options = {}) {
|
|
|
5806
6123
|
function buildProgram(contextFactory = createCliContext) {
|
|
5807
6124
|
const program2 = new Command();
|
|
5808
6125
|
program2.name(CLI_NAME).description(CLI_DESCRIPTION).version(CLI_VERSION, "-v, --version", "print the CrossSync version").option("--home-dir <path>", "use an alternate home directory").option("--verbose", "enable debug logging").option("--quiet", "only log errors");
|
|
5809
|
-
const withContext = (action) => () => {
|
|
6126
|
+
const withContext = (action) => async () => {
|
|
5810
6127
|
const context = contextFactory(program2.opts());
|
|
5811
|
-
|
|
6128
|
+
try {
|
|
6129
|
+
process.exitCode = await action(context);
|
|
6130
|
+
} catch (error) {
|
|
6131
|
+
context.io.err(error instanceof Error ? error.message : String(error));
|
|
6132
|
+
process.exitCode = 1;
|
|
6133
|
+
}
|
|
5812
6134
|
};
|
|
5813
|
-
const syncCommand = program2.command("sync").description("read, merge and write back browser bookmarks/history").option("--bookmarks", "sync bookmarks only").option("--history", "sync history only").option("--all", "sync bookmarks and history (default)").option("--browser <name>", "chrome, edge or all", "all").option("--profile <
|
|
6135
|
+
const syncCommand = program2.command("sync").description("read, merge and write back browser bookmarks/history").option("--bookmarks", "sync bookmarks only").option("--history", "sync history only").option("--all", "sync bookmarks and history (default)").option("--browser <name>", "chrome, edge or all", "all").option("--profile <names>", 'profile name(s), comma-separated, or "all"').option("--strategy <name>", "merge (write back) or export (read only)").option("--dry-run", "show what would happen without touching any file").option("--force", "write bookmarks back even while the browser is running").option("--json", "print the report as JSON");
|
|
5814
6136
|
syncCommand.action(withContext((context) => runSyncCommand(context, syncCommand.opts())));
|
|
5815
6137
|
const backupCommand = program2.command("backup").description("back up browser bookmark and history files").option("--list", "list existing backups").option("--prune", "delete old backups").option("--keep <count>", "how many backups to keep when pruning");
|
|
5816
6138
|
backupCommand.action(
|
|
@@ -5823,10 +6145,14 @@ function buildProgram(contextFactory = createCliContext) {
|
|
|
5823
6145
|
});
|
|
5824
6146
|
})
|
|
5825
6147
|
);
|
|
5826
|
-
const exportCommand = program2.command("export").description("export merged bookmarks/history as JSON").option("--format <format>", "output format (json)", "json").option("--out <dir>", "directory to write into").option("--browser <name>", "chrome, edge or all", "all").option("--profile <
|
|
6148
|
+
const exportCommand = program2.command("export").description("export merged bookmarks/history as JSON").option("--format <format>", "output format (json)", "json").option("--out <dir>", "directory to write into").option("--browser <name>", "chrome, edge or all", "all").option("--profile <names>", 'profile name(s), comma-separated, or "all"').option("--stdout", "print the export instead of writing a file");
|
|
5827
6149
|
exportCommand.action(withContext((context) => runExportCommand(context, exportCommand.opts())));
|
|
5828
6150
|
const statusCommand = program2.command("status").description("show browsers, profiles, backups and schedule state").option("--json", "print the status as JSON");
|
|
5829
6151
|
statusCommand.action(withContext((context) => runStatusCommand(context, statusCommand.opts())));
|
|
6152
|
+
const profilesCommand = program2.command("profiles").description("list available Chromium profiles or change the synced selection").option("--list", "list profiles and the current selection (default)").option("--set <names>", 'replace the selection, e.g. "Default,Profile 11" or "all"').option("--add <names>", "add profiles to the selection").option("--remove <names>", "remove profiles from the selection").option("--all", "select every available profile").option("--reset", 'reset the selection to "Default"').option("--browser <name>", "limit listing to chrome, edge or all", "all");
|
|
6153
|
+
profilesCommand.action(
|
|
6154
|
+
withContext((context) => runProfilesCommand(context, profilesCommand.opts()))
|
|
6155
|
+
);
|
|
5830
6156
|
const logsCommand = program2.command("logs").description("print CrossSync log entries").option("--limit <count>", "number of entries to show", "50").option("--level <level>", "debug, info, warn or error").option("--clear", "delete the log file").option("--json", "print the entries as JSON");
|
|
5831
6157
|
logsCommand.action(
|
|
5832
6158
|
withContext((context) => {
|
|
@@ -5851,13 +6177,13 @@ function buildProgram(contextFactory = createCliContext) {
|
|
|
5851
6177
|
configCommand.action(withContext((context) => runConfigCommand(context, configCommand.opts())));
|
|
5852
6178
|
return program2;
|
|
5853
6179
|
}
|
|
5854
|
-
function run(argv = process.argv) {
|
|
5855
|
-
buildProgram().
|
|
6180
|
+
async function run(argv = process.argv) {
|
|
6181
|
+
await buildProgram().parseAsync(argv);
|
|
5856
6182
|
}
|
|
5857
6183
|
var PACKAGE = "crosssync";
|
|
5858
6184
|
var VERSION = CLI_VERSION;
|
|
5859
6185
|
if (require.main === module) {
|
|
5860
|
-
run();
|
|
6186
|
+
void run();
|
|
5861
6187
|
}
|
|
5862
6188
|
// Annotate the CommonJS export names for ESM import in node:
|
|
5863
6189
|
0 && (module.exports = {
|