@narumitw/pi-sync 0.49.15 → 0.50.1
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 +49 -16
- package/dist/chunks/attention-widget-Z7G3SYYJ.ts +9 -0
- package/dist/chunks/attention-widget-Z7G3SYYJ.ts.map +7 -0
- package/dist/chunks/chunk-2B2ELUF5.ts +29 -0
- package/dist/chunks/chunk-2B2ELUF5.ts.map +7 -0
- package/dist/chunks/{chunk-ARN2672F.ts → chunk-4CHTZMEO.ts} +23 -55
- package/dist/chunks/chunk-4CHTZMEO.ts.map +7 -0
- package/dist/chunks/chunk-6LP3MCR2.ts +115 -0
- package/dist/chunks/chunk-6LP3MCR2.ts.map +7 -0
- package/dist/chunks/{chunk-JF6MZCT4.ts → chunk-AUSDLD6F.ts} +4 -4
- package/dist/chunks/chunk-AUSDLD6F.ts.map +7 -0
- package/dist/chunks/{chunk-5T442OP3.ts → chunk-C7XY5NF5.ts} +210 -373
- package/dist/chunks/chunk-C7XY5NF5.ts.map +7 -0
- package/dist/chunks/chunk-CCSN3F47.ts +51 -0
- package/dist/chunks/chunk-CCSN3F47.ts.map +7 -0
- package/dist/chunks/{chunk-4U6AFLJB.ts → chunk-CHH26D4F.ts} +3 -2
- package/dist/chunks/chunk-CHH26D4F.ts.map +7 -0
- package/dist/chunks/chunk-CPEHGXJV.ts +50 -0
- package/dist/chunks/chunk-CPEHGXJV.ts.map +7 -0
- package/dist/chunks/{chunk-5MQ76BK5.ts → chunk-D5X27LCA.ts} +2 -2
- package/dist/chunks/chunk-D5X27LCA.ts.map +7 -0
- package/dist/chunks/{chunk-WSOBYHIZ.ts → chunk-ETTRJDNP.ts} +4 -4
- package/dist/chunks/chunk-ETTRJDNP.ts.map +7 -0
- package/dist/chunks/{chunk-44LPGQTI.ts → chunk-G4RNQQZS.ts} +9 -7
- package/dist/chunks/chunk-G4RNQQZS.ts.map +7 -0
- package/dist/chunks/{chunk-5LZALPCD.ts → chunk-HUDQATCL.ts} +25 -25
- package/dist/chunks/chunk-HUDQATCL.ts.map +7 -0
- package/dist/chunks/{chunk-X7SQ2EQM.ts → chunk-IJ6FJFA5.ts} +9 -9
- package/dist/chunks/chunk-IJ6FJFA5.ts.map +7 -0
- package/dist/chunks/{chunk-YQ6UW7IF.ts → chunk-JAAKLL6M.ts} +2 -2
- package/dist/chunks/chunk-JAAKLL6M.ts.map +7 -0
- package/dist/chunks/chunk-KW7YQEZN.ts +1255 -0
- package/dist/chunks/chunk-KW7YQEZN.ts.map +7 -0
- package/dist/chunks/{chunk-DFXBKHGN.ts → chunk-LCRI3ABW.ts} +21 -8
- package/dist/chunks/chunk-LCRI3ABW.ts.map +7 -0
- package/dist/chunks/chunk-LTBWGSBV.ts +12 -0
- package/dist/chunks/chunk-LTBWGSBV.ts.map +7 -0
- package/dist/chunks/chunk-NJFUIVYL.ts +17 -0
- package/dist/chunks/chunk-NJFUIVYL.ts.map +7 -0
- package/dist/chunks/{chunk-HVUSHMVL.ts → chunk-OEX4ABY6.ts} +39 -37
- package/dist/chunks/chunk-OEX4ABY6.ts.map +7 -0
- package/dist/chunks/{chunk-HGV24P4T.ts → chunk-Q2TUM5X6.ts} +3 -3
- package/dist/chunks/chunk-Q2TUM5X6.ts.map +7 -0
- package/dist/chunks/chunk-S7YBZR42.ts +146 -0
- package/dist/chunks/chunk-S7YBZR42.ts.map +7 -0
- package/dist/chunks/{chunk-GLQLN5GV.ts → chunk-SL64RLXR.ts} +8 -6
- package/dist/chunks/chunk-SL64RLXR.ts.map +7 -0
- package/dist/chunks/chunk-SZWBGPID.ts +57 -0
- package/dist/chunks/chunk-SZWBGPID.ts.map +7 -0
- package/dist/chunks/chunk-T2B5IU74.ts +25 -0
- package/dist/chunks/chunk-T2B5IU74.ts.map +7 -0
- package/dist/chunks/chunk-T3HH3UEJ.ts +96 -0
- package/dist/chunks/chunk-T3HH3UEJ.ts.map +7 -0
- package/dist/chunks/chunk-TEN2ZBJV.ts +101 -0
- package/dist/chunks/chunk-TEN2ZBJV.ts.map +7 -0
- package/dist/chunks/chunk-THMF4BVL.ts +18 -0
- package/dist/chunks/chunk-THMF4BVL.ts.map +7 -0
- package/dist/chunks/chunk-Z4DQF6L3.ts +12 -0
- package/dist/chunks/chunk-Z4DQF6L3.ts.map +7 -0
- package/dist/chunks/{chunk-F5QL2GPY.ts → chunk-ZI5MXMEX.ts} +5 -99
- package/dist/chunks/chunk-ZI5MXMEX.ts.map +7 -0
- package/dist/chunks/{file-selection-RKEHALNQ.ts → file-selection-FTTKAOYX.ts} +10 -7
- package/dist/chunks/file-selection-FTTKAOYX.ts.map +7 -0
- package/dist/chunks/{git-backend-E3UOUUOM.ts → git-backend-GMVS4PD4.ts} +39 -14
- package/dist/chunks/git-backend-GMVS4PD4.ts.map +7 -0
- package/dist/chunks/manager-result-dispatcher-FKIBM3GO.ts +22 -0
- package/dist/chunks/manager-ui-R3XPUFFS.ts +1680 -0
- package/dist/chunks/manager-ui-R3XPUFFS.ts.map +7 -0
- package/dist/chunks/remote-selection-ui-D7RBRJNL.ts +21 -0
- package/dist/chunks/{s3-backend-552LXHE4.ts → s3-backend-66O4FGBH.ts} +94 -16
- package/dist/chunks/s3-backend-66O4FGBH.ts.map +7 -0
- package/dist/chunks/{setup-switch-5YDG5YGJ.ts → setup-switch-IS2PEZ3J.ts} +9 -9
- package/dist/chunks/setup-wizard-IUB6ESEU.ts +89 -0
- package/dist/chunks/setup-wizard-IUB6ESEU.ts.map +7 -0
- package/dist/chunks/{snapshot-2RYBUDRA.ts → snapshot-HKEWCVK6.ts} +7 -6
- package/dist/chunks/sync-inspection-CST2KL65.ts +20 -0
- package/dist/chunks/{sync-operations-3LLIHWZO.ts → sync-operations-LBX3GKCM.ts} +369 -289
- package/dist/chunks/sync-operations-LBX3GKCM.ts.map +7 -0
- package/dist/chunks/{sync-state-SRN6YRBK.ts → sync-state-Q2FTXTW6.ts} +6 -6
- package/dist/chunks/sync-state-Q2FTXTW6.ts.map +7 -0
- package/dist/chunks/{webdav-backend-ZD5RHRIU.ts → webdav-backend-ECIKD7SO.ts} +26 -13
- package/dist/chunks/webdav-backend-ECIKD7SO.ts.map +7 -0
- package/dist/index.ts +476 -341
- package/dist/index.ts.map +4 -4
- package/docs/settings.md +30 -6
- package/package.json +1 -1
- package/src/{backend-factory.ts → backends/backend-factory.ts} +4 -4
- package/src/backends/backend-identity.ts +40 -0
- package/src/backends/backend-types.ts +81 -0
- package/src/{git-backend.ts → backends/git/git-backend.ts} +43 -19
- package/src/{git-config.ts → backends/git/git-config.ts} +1 -0
- package/src/{git-storage.ts → backends/git/git-storage.ts} +2 -2
- package/src/{s3-backend.ts → backends/s3/s3-backend.ts} +45 -9
- package/src/{s3-client.ts → backends/s3/s3-client.ts} +56 -8
- package/src/{sync-backend.ts → backends/sync-backend.ts} +1 -1
- package/src/{webdav-backend.ts → backends/webdav/webdav-backend.ts} +22 -10
- package/src/{webdav-client.ts → backends/webdav/webdav-client.ts} +1 -1
- package/src/{webdav-config.ts → backends/webdav/webdav-config.ts} +1 -0
- package/src/commands/command-execution.ts +330 -0
- package/src/commands/command-handler.ts +201 -0
- package/src/commands/command-types.ts +18 -0
- package/src/{command.ts → commands/command.ts} +2 -1
- package/src/commands/setup-completions.ts +6 -0
- package/src/settings/config.ts +238 -0
- package/src/{settings-management.ts → settings/settings-management.ts} +39 -14
- package/src/settings/settings-store.ts +109 -0
- package/src/settings/settings-types.ts +128 -0
- package/src/settings/settings-validation.ts +443 -0
- package/src/snapshot/session-paths.ts +84 -0
- package/src/{snapshot-apply.ts → snapshot/snapshot-apply.ts} +3 -3
- package/src/{snapshot-codec.ts → snapshot/snapshot-codec.ts} +2 -2
- package/src/{snapshot-transaction.ts → snapshot/snapshot-transaction.ts} +5 -4
- package/src/snapshot/snapshot-types.ts +38 -0
- package/src/{snapshot.ts → snapshot/snapshot.ts} +39 -20
- package/src/state/json-file.ts +20 -0
- package/src/{lock.ts → state/lock.ts} +3 -2
- package/src/{operation-availability.ts → state/operation-availability.ts} +2 -2
- package/src/state/state-types.ts +20 -0
- package/src/state/sync-state-store.ts +62 -0
- package/src/sync/automatic-sync.ts +103 -0
- package/src/{remote-snapshot.ts → sync/remote-snapshot.ts} +27 -4
- package/src/{setup-switch.ts → sync/setup-switch.ts} +6 -7
- package/src/sync/signals.ts +10 -0
- package/src/sync/startup-check.ts +128 -0
- package/src/{sync-decision.ts → sync/sync-decision.ts} +6 -3
- package/src/sync/sync-error-guidance.ts +53 -0
- package/src/sync/sync-inspection.ts +51 -0
- package/src/sync/sync-loaders.ts +54 -0
- package/src/{sync-operations.ts → sync/sync-mutations.ts} +34 -325
- package/src/sync/sync-operations.ts +12 -0
- package/src/{sync-policy.ts → sync/sync-policy.ts} +2 -2
- package/src/sync/sync-queries.ts +235 -0
- package/src/{sync-state.ts → sync/sync-state.ts} +5 -4
- package/src/sync-extension.ts +64 -708
- package/src/sync.ts +8 -8
- package/src/types.ts +47 -286
- package/src/ui/attention-widget.ts +1 -0
- package/src/ui/automatic-sync-summary.ts +6 -0
- package/src/{cancellable-operation.ts → ui/cancellable-operation.ts} +4 -4
- package/src/{file-selection.ts → ui/file-selection.ts} +5 -3
- package/src/{manager-attention.ts → ui/manager-attention.ts} +5 -1
- package/src/{manager-recovery.ts → ui/manager-recovery.ts} +6 -2
- package/src/{manager-state.ts → ui/manager-state.ts} +35 -14
- package/src/ui/manager-ui.ts +386 -0
- package/src/{remote-selection-ui.ts → ui/remote-selection-ui.ts} +21 -13
- package/src/{secret-input.ts → ui/secret-input.ts} +22 -4
- package/src/{settings-ui.ts → ui/settings-ui.ts} +17 -13
- package/src/ui/setup/git-ui.ts +293 -0
- package/src/{s3-credentials-ui.ts → ui/setup/s3-credentials-ui.ts} +13 -21
- package/src/ui/setup/s3-ui.ts +217 -0
- package/src/ui/setup/setup-actions.ts +127 -0
- package/src/ui/setup/setup-location-ui.ts +167 -0
- package/src/ui/setup/setup-name-ui.ts +34 -0
- package/src/ui/setup/setup-prompts.ts +92 -0
- package/src/ui/setup/setup-review.ts +70 -0
- package/src/ui/setup/setup-switcher.ts +181 -0
- package/src/ui/setup/setup-wizard.ts +39 -0
- package/src/ui/setup/text-input.ts +78 -0
- package/src/ui/setup/webdav-ui.ts +330 -0
- package/src/{storage-connections-ui.ts → ui/storage-connections-ui.ts} +99 -69
- package/src/ui/sync-attention.ts +187 -0
- package/src/{sync-format.ts → ui/sync-format.ts} +7 -13
- package/src/{sync-resolution-ui.ts → ui/sync-resolution-ui.ts} +4 -3
- package/src/{sync-setups-ui.ts → ui/sync-setups-ui.ts} +23 -10
- package/src/ui/terminal-text.ts +4 -0
- package/dist/chunks/chunk-44LPGQTI.ts.map +0 -7
- package/dist/chunks/chunk-4U6AFLJB.ts.map +0 -7
- package/dist/chunks/chunk-5LZALPCD.ts.map +0 -7
- package/dist/chunks/chunk-5MQ76BK5.ts.map +0 -7
- package/dist/chunks/chunk-5T442OP3.ts.map +0 -7
- package/dist/chunks/chunk-ARN2672F.ts.map +0 -7
- package/dist/chunks/chunk-C7LXZ3UH.ts +0 -167
- package/dist/chunks/chunk-C7LXZ3UH.ts.map +0 -7
- package/dist/chunks/chunk-DFXBKHGN.ts.map +0 -7
- package/dist/chunks/chunk-E4COSV32.ts +0 -82
- package/dist/chunks/chunk-E4COSV32.ts.map +0 -7
- package/dist/chunks/chunk-F5QL2GPY.ts.map +0 -7
- package/dist/chunks/chunk-GLQLN5GV.ts.map +0 -7
- package/dist/chunks/chunk-HGV24P4T.ts.map +0 -7
- package/dist/chunks/chunk-HVUSHMVL.ts.map +0 -7
- package/dist/chunks/chunk-JF6MZCT4.ts.map +0 -7
- package/dist/chunks/chunk-WSOBYHIZ.ts.map +0 -7
- package/dist/chunks/chunk-X7SQ2EQM.ts.map +0 -7
- package/dist/chunks/chunk-YQ6UW7IF.ts.map +0 -7
- package/dist/chunks/file-selection-RKEHALNQ.ts.map +0 -7
- package/dist/chunks/git-backend-E3UOUUOM.ts.map +0 -7
- package/dist/chunks/manager-result-dispatcher-LWPYJAD5.ts +0 -18
- package/dist/chunks/manager-ui-NF4RUHBY.ts +0 -2785
- package/dist/chunks/manager-ui-NF4RUHBY.ts.map +0 -7
- package/dist/chunks/remote-selection-ui-NUF5P4ZJ.ts +0 -17
- package/dist/chunks/s3-backend-552LXHE4.ts.map +0 -7
- package/dist/chunks/sync-operations-3LLIHWZO.ts.map +0 -7
- package/dist/chunks/webdav-backend-ZD5RHRIU.ts.map +0 -7
- package/src/config.ts +0 -923
- package/src/git-ui.ts +0 -307
- package/src/manager-helpers.ts +0 -64
- package/src/manager-ui.ts +0 -997
- package/src/sync-attention.ts +0 -100
- package/src/webdav-ui.ts +0 -421
- /package/dist/chunks/{manager-result-dispatcher-LWPYJAD5.ts.map → manager-result-dispatcher-FKIBM3GO.ts.map} +0 -0
- /package/dist/chunks/{remote-selection-ui-NUF5P4ZJ.ts.map → remote-selection-ui-D7RBRJNL.ts.map} +0 -0
- /package/dist/chunks/{setup-switch-5YDG5YGJ.ts.map → setup-switch-IS2PEZ3J.ts.map} +0 -0
- /package/dist/chunks/{snapshot-2RYBUDRA.ts.map → snapshot-HKEWCVK6.ts.map} +0 -0
- /package/dist/chunks/{sync-state-SRN6YRBK.ts.map → sync-inspection-CST2KL65.ts.map} +0 -0
- /package/src/{git-runner.ts → backends/git/git-runner.ts} +0 -0
- /package/src/{config-errors.ts → settings/config-errors.ts} +0 -0
- /package/src/{config-file.ts → settings/config-file.ts} +0 -0
- /package/src/{snapshot-paths.ts → snapshot/snapshot-paths.ts} +0 -0
- /package/src/{lock-policy.ts → state/lock-policy.ts} +0 -0
- /package/src/{lockfile-fs.ts → state/lockfile-fs.ts} +0 -0
- /package/src/{state-directory.ts → state/state-directory.ts} +0 -0
- /package/src/{sync-errors.ts → sync/sync-errors.ts} +0 -0
- /package/src/{manager-result-dispatcher.ts → ui/manager-result-dispatcher.ts} +0 -0
|
@@ -1,17 +1,24 @@
|
|
|
1
1
|
// @generated by scripts/build-runtime.mjs; do not edit.
|
|
2
2
|
// @ts-nocheck -- generated JavaScript uses a .ts extension for Pi's Jiti loader.
|
|
3
|
+
import {
|
|
4
|
+
backendIdentityCoordinates,
|
|
5
|
+
normalizeEndpointIdentity,
|
|
6
|
+
normalizeWebDavIdentityUrl,
|
|
7
|
+
normalizeWebDavPath,
|
|
8
|
+
normalizeWebDavUrl,
|
|
9
|
+
validateWebDavCredentials
|
|
10
|
+
} from "./chunk-6LP3MCR2.ts";
|
|
3
11
|
import {
|
|
4
12
|
normalizeGitBranch,
|
|
5
13
|
normalizeGitDirectory,
|
|
6
14
|
normalizeGitRemote,
|
|
7
|
-
normalizeGitRemoteIdentity
|
|
8
|
-
|
|
9
|
-
} from "./chunk-F5QL2GPY.ts";
|
|
15
|
+
normalizeGitRemoteIdentity
|
|
16
|
+
} from "./chunk-TEN2ZBJV.ts";
|
|
10
17
|
import {
|
|
11
18
|
normalizeSyncInclude
|
|
12
|
-
} from "./chunk-
|
|
19
|
+
} from "./chunk-JAAKLL6M.ts";
|
|
13
20
|
|
|
14
|
-
// src/config-file.ts
|
|
21
|
+
// src/settings/config-file.ts
|
|
15
22
|
import { randomUUID } from "node:crypto";
|
|
16
23
|
import {
|
|
17
24
|
mkdir,
|
|
@@ -72,6 +79,15 @@ function consumeLocalConfigMigrationNotice() {
|
|
|
72
79
|
configMigrationNotices.delete(configPath);
|
|
73
80
|
return notice;
|
|
74
81
|
}
|
|
82
|
+
async function readActiveLocalConfigDocumentForRepair() {
|
|
83
|
+
const canonicalPath = localConfigPath();
|
|
84
|
+
const legacyPath = legacyLocalConfigPath();
|
|
85
|
+
return withLocalConfigReadLockIfNeeded(async () => {
|
|
86
|
+
const filePath = await pathExists(canonicalPath) ? canonicalPath : legacyPath;
|
|
87
|
+
const snapshot = await readConfigSnapshotIfExists(filePath);
|
|
88
|
+
return snapshot ? { path: filePath, ...snapshot } : void 0;
|
|
89
|
+
});
|
|
90
|
+
}
|
|
75
91
|
async function readMigratingLocalConfigDocument(validateForMigration) {
|
|
76
92
|
return withLocalConfigReadLockIfNeeded(async () => {
|
|
77
93
|
const configPath = await prepareLocalConfigPath(validateForMigration);
|
|
@@ -482,169 +498,9 @@ function recordConfigMigrationNotice(configPath, notice) {
|
|
|
482
498
|
if (!configMigrationNotices.has(configPath)) configMigrationNotices.set(configPath, notice);
|
|
483
499
|
}
|
|
484
500
|
|
|
485
|
-
// src/
|
|
486
|
-
import { createHash, randomUUID as randomUUID2 } from "node:crypto";
|
|
487
|
-
import fs2 from "node:fs/promises";
|
|
488
|
-
import os from "node:os";
|
|
501
|
+
// src/settings/settings-validation.ts
|
|
489
502
|
import path2 from "node:path";
|
|
490
|
-
import {
|
|
491
|
-
getAgentDir as getAgentDir2
|
|
492
|
-
} from "@earendil-works/pi-coding-agent";
|
|
493
|
-
|
|
494
|
-
// src/webdav-config.ts
|
|
495
|
-
var DEFAULT_PATH = "pi-sync";
|
|
496
|
-
function normalizeWebDavIdentityUrl(value) {
|
|
497
|
-
try {
|
|
498
|
-
const url = new URL(value.trim());
|
|
499
|
-
url.username = "";
|
|
500
|
-
url.password = "";
|
|
501
|
-
url.search = "";
|
|
502
|
-
url.hash = "";
|
|
503
|
-
url.pathname = `${url.pathname.replace(/\/+$/u, "")}/`;
|
|
504
|
-
return url.toString();
|
|
505
|
-
} catch {
|
|
506
|
-
return value.trim();
|
|
507
|
-
}
|
|
508
|
-
}
|
|
509
|
-
function normalizeWebDavUrl(value) {
|
|
510
|
-
const normalized = normalizeOptionalString(value);
|
|
511
|
-
if (!normalized) return void 0;
|
|
512
|
-
let url;
|
|
513
|
-
try {
|
|
514
|
-
url = new URL(normalized);
|
|
515
|
-
} catch {
|
|
516
|
-
throw new Error("Invalid pi-sync WebDAV URL.");
|
|
517
|
-
}
|
|
518
|
-
if (url.username || url.password || url.search || url.hash) {
|
|
519
|
-
throw new Error(
|
|
520
|
-
"Invalid pi-sync WebDAV URL: credentials, query, and fragment are not allowed."
|
|
521
|
-
);
|
|
522
|
-
}
|
|
523
|
-
const loopback = url.hostname === "127.0.0.1" || url.hostname === "localhost" || url.hostname === "[::1]" || url.hostname === "::1";
|
|
524
|
-
if (url.protocol !== "https:" && !(url.protocol === "http:" && loopback)) {
|
|
525
|
-
throw new Error("Invalid pi-sync WebDAV URL: HTTPS is required except for loopback.");
|
|
526
|
-
}
|
|
527
|
-
url.pathname = `${url.pathname.replace(/\/+$/u, "")}/`;
|
|
528
|
-
return url.toString();
|
|
529
|
-
}
|
|
530
|
-
function normalizeWebDavPath(value) {
|
|
531
|
-
const normalized = trimSlashes(normalizeOptionalString(value) ?? DEFAULT_PATH);
|
|
532
|
-
if (!normalized || normalized.includes("\\") || hasControlCharacter(normalized) || normalized.split("/").some((segment) => !segment || segment === "." || segment === "..")) {
|
|
533
|
-
throw new Error("Invalid pi-sync WebDAV path.");
|
|
534
|
-
}
|
|
535
|
-
return normalized;
|
|
536
|
-
}
|
|
537
|
-
function validateWebDavNamespace(value) {
|
|
538
|
-
if (value === "." || value === ".." || value.includes("/") || value.includes("\\") || hasControlCharacter(value)) {
|
|
539
|
-
throw new Error("Invalid pi-sync WebDAV namespace.");
|
|
540
|
-
}
|
|
541
|
-
}
|
|
542
|
-
function validateWebDavCredentials(username, password) {
|
|
543
|
-
if (username.includes(":") || hasControlCharacter(username) || password !== void 0 && hasControlCharacter(password)) {
|
|
544
|
-
throw new Error("Invalid pi-sync WebDAV credentials.");
|
|
545
|
-
}
|
|
546
|
-
}
|
|
547
|
-
function normalizeOptionalString(value) {
|
|
548
|
-
const normalized = value?.trim();
|
|
549
|
-
return normalized || void 0;
|
|
550
|
-
}
|
|
551
|
-
function trimSlashes(value) {
|
|
552
|
-
return value.replace(/^\/+|\/+$/gu, "");
|
|
553
|
-
}
|
|
554
|
-
function hasControlCharacter(value) {
|
|
555
|
-
return [...value].some((character) => {
|
|
556
|
-
const code = character.codePointAt(0) ?? 0;
|
|
557
|
-
return code < 32 || code >= 127 && code <= 159;
|
|
558
|
-
});
|
|
559
|
-
}
|
|
560
|
-
|
|
561
|
-
// src/config-errors.ts
|
|
562
|
-
function isMissingConfigError(error) {
|
|
563
|
-
return error instanceof Error && (error.message.startsWith("Missing pi-sync settings.") || error.message === "No sync setups are configured.");
|
|
564
|
-
}
|
|
565
|
-
|
|
566
|
-
// src/config.ts
|
|
567
|
-
var STATE_VERSION = 2;
|
|
568
503
|
var DEFAULT_ON_SWITCH = "ask-before-pull";
|
|
569
|
-
function sessionDirFromContext(ctx) {
|
|
570
|
-
const manager = ctx.sessionManager;
|
|
571
|
-
if (manager.usesDefaultSessionDir?.call(manager)) return void 0;
|
|
572
|
-
return typeof manager.getSessionDir === "function" ? manager.getSessionDir.call(manager) : void 0;
|
|
573
|
-
}
|
|
574
|
-
async function loadConfig(setupName) {
|
|
575
|
-
const settings = await requireSettings();
|
|
576
|
-
const selectedName = setupName ?? settings.activeSyncSetup;
|
|
577
|
-
if (!selectedName) throw new Error("No sync setups are configured.");
|
|
578
|
-
validateConfigName(selectedName, "sync setup");
|
|
579
|
-
const setup = ownObject(settings.syncSetups, selectedName);
|
|
580
|
-
if (!setup)
|
|
581
|
-
throw new Error(`Invalid pi-sync settings: sync setup \u201C${selectedName}\u201D was not found.`);
|
|
582
|
-
const connectionName = setup.storage.connection;
|
|
583
|
-
const connection = ownObject(
|
|
584
|
-
settings.storageConnections,
|
|
585
|
-
connectionName
|
|
586
|
-
);
|
|
587
|
-
if (!connection) {
|
|
588
|
-
throw new Error(
|
|
589
|
-
`Invalid pi-sync settings: sync setup \u201C${selectedName}\u201D references missing storage connection \u201C${connectionName}\u201D.`
|
|
590
|
-
);
|
|
591
|
-
}
|
|
592
|
-
return resolveSyncConfig(
|
|
593
|
-
selectedName,
|
|
594
|
-
setup,
|
|
595
|
-
connectionName,
|
|
596
|
-
connection,
|
|
597
|
-
settings.onSwitch,
|
|
598
|
-
settings.skipSecretScan ?? false
|
|
599
|
-
);
|
|
600
|
-
}
|
|
601
|
-
async function loadPartialConfig(setupName) {
|
|
602
|
-
const config = await loadConfig(setupName);
|
|
603
|
-
return {
|
|
604
|
-
setupName: config.setupName,
|
|
605
|
-
...storageReviewFromConfig(config),
|
|
606
|
-
include: [...config.include],
|
|
607
|
-
automatic: config.automatic,
|
|
608
|
-
onSwitch: config.onSwitch
|
|
609
|
-
};
|
|
610
|
-
}
|
|
611
|
-
function syncSetupStorageReview(setupName, setup, connectionName, connection) {
|
|
612
|
-
return storageReviewFromConfig(
|
|
613
|
-
resolveSyncConfig(setupName, setup, connectionName, connection, DEFAULT_ON_SWITCH, false)
|
|
614
|
-
);
|
|
615
|
-
}
|
|
616
|
-
function syncSetupReviewIdentity(setupName, setup, connectionName, connection) {
|
|
617
|
-
return syncConfigReviewIdentity(
|
|
618
|
-
resolveSyncConfig(setupName, setup, connectionName, connection, DEFAULT_ON_SWITCH, false)
|
|
619
|
-
);
|
|
620
|
-
}
|
|
621
|
-
function syncConfigReviewIdentity(config) {
|
|
622
|
-
return JSON.stringify([
|
|
623
|
-
config.setupName,
|
|
624
|
-
config.connectionName,
|
|
625
|
-
backendIdentityCoordinates(config),
|
|
626
|
-
config.include,
|
|
627
|
-
config.automatic
|
|
628
|
-
]);
|
|
629
|
-
}
|
|
630
|
-
function syncConfigReviewFingerprint(config) {
|
|
631
|
-
return createHash("sha256").update(syncConfigReviewIdentity(config)).digest("hex");
|
|
632
|
-
}
|
|
633
|
-
function storageReviewFromConfig(config) {
|
|
634
|
-
return {
|
|
635
|
-
connectionName: config.connectionName,
|
|
636
|
-
storageKind: config.backend.type,
|
|
637
|
-
storagePath: config.storagePath,
|
|
638
|
-
...config.backend.type === "s3" ? { bucket: config.backend.destination.bucket } : config.backend.type === "git" ? { branch: config.backend.destination.branch } : {}
|
|
639
|
-
};
|
|
640
|
-
}
|
|
641
|
-
async function configuredSyncSetupNames() {
|
|
642
|
-
const settings = await readLocalConfigObject();
|
|
643
|
-
return settings ? Object.keys(settings.syncSetups).sort((left, right) => left.localeCompare(right)) : [];
|
|
644
|
-
}
|
|
645
|
-
async function loadOnSwitch() {
|
|
646
|
-
return (await requireSettings()).onSwitch;
|
|
647
|
-
}
|
|
648
504
|
function normalizeOnSwitch(value) {
|
|
649
505
|
if (value === "ask-before-pull" || value === "pull-after-switch" || value === "switch-only") {
|
|
650
506
|
return value;
|
|
@@ -653,76 +509,6 @@ function normalizeOnSwitch(value) {
|
|
|
653
509
|
'Invalid pi-sync settings: onSwitch must be "ask-before-pull", "pull-after-switch", or "switch-only".'
|
|
654
510
|
);
|
|
655
511
|
}
|
|
656
|
-
function resolveSyncConfig(setupName, setup, connectionName, connection, onSwitch, skipSecretScan) {
|
|
657
|
-
const storagePath = normalizeStoragePath(setup.storage.path);
|
|
658
|
-
const namespace = storagePath.slice(storagePath.lastIndexOf("/") + 1);
|
|
659
|
-
const include = normalizeSyncInclude(setup.sync.include);
|
|
660
|
-
const common = {
|
|
661
|
-
setupName,
|
|
662
|
-
connectionName,
|
|
663
|
-
storagePath,
|
|
664
|
-
snapshotIdentity: namespace,
|
|
665
|
-
include,
|
|
666
|
-
automatic: setup.sync.automatic,
|
|
667
|
-
onSwitch,
|
|
668
|
-
skipSecretScan
|
|
669
|
-
};
|
|
670
|
-
if (connection.type === "git") {
|
|
671
|
-
return {
|
|
672
|
-
...common,
|
|
673
|
-
backend: {
|
|
674
|
-
type: "git",
|
|
675
|
-
profile: { kind: "git", remote: normalizeGitRemote(connection.remote) },
|
|
676
|
-
destination: {
|
|
677
|
-
branch: normalizeGitBranch(setup.storage.branch),
|
|
678
|
-
directory: normalizeGitDirectory(storagePath),
|
|
679
|
-
namespace
|
|
680
|
-
}
|
|
681
|
-
}
|
|
682
|
-
};
|
|
683
|
-
}
|
|
684
|
-
if (connection.type === "webdav") {
|
|
685
|
-
return {
|
|
686
|
-
...common,
|
|
687
|
-
backend: {
|
|
688
|
-
type: "webdav",
|
|
689
|
-
profile: {
|
|
690
|
-
kind: "webdav",
|
|
691
|
-
url: normalizeWebDavUrl(connection.url),
|
|
692
|
-
username: connection.credentials.username,
|
|
693
|
-
password: connection.credentials.password
|
|
694
|
-
},
|
|
695
|
-
destination: { path: normalizeWebDavPath(storagePath), namespace }
|
|
696
|
-
}
|
|
697
|
-
};
|
|
698
|
-
}
|
|
699
|
-
return {
|
|
700
|
-
...common,
|
|
701
|
-
backend: {
|
|
702
|
-
type: "s3",
|
|
703
|
-
profile: {
|
|
704
|
-
kind: isCloudflareR2Endpoint(connection.endpoint) ? "r2" : "s3-compatible",
|
|
705
|
-
endpoint: normalizeS3Endpoint(connection.endpoint),
|
|
706
|
-
region: requiredString(connection.region, "S3 region"),
|
|
707
|
-
accessKeyId: connection.credentials.accessKeyId,
|
|
708
|
-
secretAccessKey: connection.credentials.secretAccessKey,
|
|
709
|
-
sessionToken: optionalString(connection.credentials.sessionToken, "S3 session token")
|
|
710
|
-
},
|
|
711
|
-
destination: {
|
|
712
|
-
bucket: normalizeS3Bucket(setup.storage.bucket),
|
|
713
|
-
prefix: storagePath,
|
|
714
|
-
namespace
|
|
715
|
-
}
|
|
716
|
-
}
|
|
717
|
-
};
|
|
718
|
-
}
|
|
719
|
-
async function requireSettings() {
|
|
720
|
-
const settings = await readLocalConfigObject();
|
|
721
|
-
if (!settings) {
|
|
722
|
-
throw new Error(`Missing pi-sync settings. Use /sync setup or create ${localConfigPath()}.`);
|
|
723
|
-
}
|
|
724
|
-
return settings;
|
|
725
|
-
}
|
|
726
512
|
function validateSettingsDocument(value) {
|
|
727
513
|
if (value.version !== 3) {
|
|
728
514
|
throw new Error(
|
|
@@ -977,12 +763,12 @@ function requireRecord(value, field) {
|
|
|
977
763
|
return value;
|
|
978
764
|
}
|
|
979
765
|
function validateConfigName(value, field) {
|
|
980
|
-
if (!value.trim() || value !== value.trim() || value.length > 100 || value === "__proto__" || value === "prototype" || value === "constructor" ||
|
|
766
|
+
if (!value.trim() || value !== value.trim() || value.length > 100 || value === "__proto__" || value === "prototype" || value === "constructor" || hasControlCharacter(value)) {
|
|
981
767
|
throw new Error(`Invalid pi-sync settings: invalid ${field} name.`);
|
|
982
768
|
}
|
|
983
769
|
}
|
|
984
770
|
function requiredString(value, field) {
|
|
985
|
-
if (typeof value !== "string" || !value.trim() ||
|
|
771
|
+
if (typeof value !== "string" || !value.trim() || hasControlCharacter(value)) {
|
|
986
772
|
throw new Error(`Invalid pi-sync settings: ${field} must be a non-empty string.`);
|
|
987
773
|
}
|
|
988
774
|
return value.trim();
|
|
@@ -1002,21 +788,22 @@ function optionalCanonicalReference(value, field) {
|
|
|
1002
788
|
return normalized;
|
|
1003
789
|
}
|
|
1004
790
|
function requiredSecret(value, field) {
|
|
1005
|
-
if (typeof value !== "string" || !value ||
|
|
791
|
+
if (typeof value !== "string" || !value || hasControlCharacter(value)) {
|
|
1006
792
|
throw new Error(`Invalid pi-sync settings: ${field} must be configured.`);
|
|
1007
793
|
}
|
|
1008
794
|
return value;
|
|
1009
795
|
}
|
|
1010
796
|
function optionalString(value, field) {
|
|
1011
797
|
if (value === void 0) return void 0;
|
|
1012
|
-
if (typeof value !== "string" ||
|
|
798
|
+
if (typeof value !== "string" || hasControlCharacter(value)) {
|
|
1013
799
|
throw new Error(`Invalid pi-sync settings: ${field} must be a string.`);
|
|
1014
800
|
}
|
|
1015
801
|
return value.trim() || void 0;
|
|
1016
802
|
}
|
|
1017
803
|
function normalizeStoragePath(value) {
|
|
804
|
+
if (value.trim() === "." || value.trim() === "./") return "./";
|
|
1018
805
|
const normalized = value.trim().replace(/^\/+|\/+$/gu, "");
|
|
1019
|
-
if (!normalized || normalized.length > 1024 || normalized.startsWith("-") || normalized.includes("\\") ||
|
|
806
|
+
if (!normalized || normalized.length > 1024 || normalized.startsWith("-") || normalized.includes("\\") || hasControlCharacter(normalized) || normalized.split("/").some((segment) => !segment || segment === "." || segment === "..")) {
|
|
1020
807
|
throw new Error("Invalid pi-sync settings: storage.path must be a safe relative path.");
|
|
1021
808
|
}
|
|
1022
809
|
return normalized;
|
|
@@ -1042,93 +829,40 @@ function normalizeS3Bucket(value) {
|
|
|
1042
829
|
}
|
|
1043
830
|
return bucket;
|
|
1044
831
|
}
|
|
1045
|
-
function
|
|
832
|
+
function hasControlCharacter(value) {
|
|
833
|
+
return /[\u0000-\u001f\u007f-\u009f]/u.test(value);
|
|
834
|
+
}
|
|
835
|
+
function isCloudflareR2Endpoint(endpoint) {
|
|
836
|
+
const value = endpoint?.trim();
|
|
837
|
+
if (!value) return false;
|
|
1046
838
|
try {
|
|
1047
|
-
const
|
|
1048
|
-
|
|
1049
|
-
url.pathname = url.pathname.replace(/\/+$/gu, "");
|
|
1050
|
-
return url.toString().replace(/\/$/u, "");
|
|
839
|
+
const hostname = new URL(value).hostname.toLowerCase();
|
|
840
|
+
return hostname === "r2.cloudflarestorage.com" || hostname.endsWith(".r2.cloudflarestorage.com");
|
|
1051
841
|
} catch {
|
|
1052
|
-
return
|
|
842
|
+
return false;
|
|
1053
843
|
}
|
|
1054
844
|
}
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
path2.join(agentDir(), "settings.json")
|
|
1058
|
-
);
|
|
1059
|
-
return settings?.sessionDir ? expandHome(settings.sessionDir) : void 0;
|
|
1060
|
-
}
|
|
1061
|
-
async function sessionDirForApply(ctx, snapshot) {
|
|
1062
|
-
const contextSessionDir = sessionDirFromContext(ctx);
|
|
1063
|
-
const localSessionDir = await configuredSessionDir();
|
|
1064
|
-
if (contextSessionDir && path2.resolve(contextSessionDir) !== path2.resolve(localSessionDir ?? "")) {
|
|
1065
|
-
return contextSessionDir;
|
|
1066
|
-
}
|
|
1067
|
-
return sessionDirFromSnapshot(snapshot) ?? contextSessionDir;
|
|
845
|
+
function ownRecord(value) {
|
|
846
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
1068
847
|
}
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
848
|
+
|
|
849
|
+
// src/settings/settings-store.ts
|
|
850
|
+
var configUpdateQueue = Promise.resolve();
|
|
851
|
+
async function requireSettings() {
|
|
852
|
+
const settings = await readLocalConfigObject();
|
|
853
|
+
if (!settings) {
|
|
854
|
+
throw new Error(
|
|
855
|
+
`Missing pi-sync settings. Open /sync and choose Set up sync, or create ${localConfigPath()}.`
|
|
1075
856
|
);
|
|
1076
|
-
return settings.sessionDir ? expandHome(settings.sessionDir) : void 0;
|
|
1077
|
-
} catch {
|
|
1078
|
-
return void 0;
|
|
1079
|
-
}
|
|
1080
|
-
}
|
|
1081
|
-
function decodeBase64Strict(value, filePath) {
|
|
1082
|
-
if (!/^[A-Za-z0-9+/]*={0,2}$/.test(value) || value.length % 4 !== 0) {
|
|
1083
|
-
throw new Error(`Invalid base64 content in snapshot file: ${filePath}`);
|
|
1084
857
|
}
|
|
1085
|
-
return
|
|
858
|
+
return settings;
|
|
1086
859
|
}
|
|
1087
|
-
async function
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
profile: config.snapshotIdentity,
|
|
1091
|
-
lastFileHashes: {}
|
|
1092
|
-
};
|
|
860
|
+
async function configuredSyncSetupNames() {
|
|
861
|
+
const settings = await readLocalConfigObject();
|
|
862
|
+
return settings ? Object.keys(settings.syncSetups).sort((left, right) => left.localeCompare(right)) : [];
|
|
1093
863
|
}
|
|
1094
|
-
async function
|
|
1095
|
-
await
|
|
1096
|
-
}
|
|
1097
|
-
function statePathForConfig(config) {
|
|
1098
|
-
const identity = backendIdentityCoordinates(config);
|
|
1099
|
-
const hash = createHash("sha256").update(identity).digest("hex").slice(0, 16);
|
|
1100
|
-
return path2.join(stateDir(), "setups", `${config.backend.type}-${hash}.state.json`);
|
|
1101
|
-
}
|
|
1102
|
-
function backendIdentityCoordinates(config) {
|
|
1103
|
-
switch (config.backend.type) {
|
|
1104
|
-
case "s3":
|
|
1105
|
-
return JSON.stringify([
|
|
1106
|
-
"s3",
|
|
1107
|
-
normalizeEndpointIdentity(config.backend.profile.endpoint),
|
|
1108
|
-
config.backend.destination.bucket,
|
|
1109
|
-
config.storagePath
|
|
1110
|
-
]);
|
|
1111
|
-
case "git":
|
|
1112
|
-
return JSON.stringify([
|
|
1113
|
-
"git",
|
|
1114
|
-
normalizeGitRemoteIdentity(config.backend.profile.remote),
|
|
1115
|
-
config.backend.destination.branch,
|
|
1116
|
-
config.storagePath
|
|
1117
|
-
]);
|
|
1118
|
-
case "webdav":
|
|
1119
|
-
return JSON.stringify([
|
|
1120
|
-
"webdav",
|
|
1121
|
-
normalizeWebDavIdentityUrl(config.backend.profile.url),
|
|
1122
|
-
config.backend.profile.username,
|
|
1123
|
-
config.storagePath
|
|
1124
|
-
]);
|
|
1125
|
-
}
|
|
1126
|
-
}
|
|
1127
|
-
function agentDir() {
|
|
1128
|
-
return getAgentDir2();
|
|
1129
|
-
}
|
|
1130
|
-
function expandHome(value) {
|
|
1131
|
-
return value === "~" || value.startsWith("~/") ? path2.join(os.homedir(), value.slice(2)) : value;
|
|
864
|
+
async function loadOnSwitch() {
|
|
865
|
+
return (await requireSettings()).onSwitch;
|
|
1132
866
|
}
|
|
1133
867
|
function localConfigTemplate() {
|
|
1134
868
|
return {
|
|
@@ -1149,7 +883,6 @@ async function readLocalConfigDocument() {
|
|
|
1149
883
|
async function readLocalConfigObject() {
|
|
1150
884
|
return (await readLocalConfigDocument())?.parsed;
|
|
1151
885
|
}
|
|
1152
|
-
var configUpdateQueue = Promise.resolve();
|
|
1153
886
|
function updateLocalConfig(update, signal) {
|
|
1154
887
|
const operation = configUpdateQueue.then(() => {
|
|
1155
888
|
signal?.throwIfAborted();
|
|
@@ -1164,27 +897,149 @@ function updateLocalConfig(update, signal) {
|
|
|
1164
897
|
async function performLocalConfigUpdate(update, signal) {
|
|
1165
898
|
return updateLocalConfigDocument(localConfigTemplate(), update, validateSettingsDocument, signal);
|
|
1166
899
|
}
|
|
1167
|
-
|
|
1168
|
-
|
|
900
|
+
|
|
901
|
+
// src/settings/config.ts
|
|
902
|
+
import { createHash } from "node:crypto";
|
|
903
|
+
async function loadConfig(setupName) {
|
|
904
|
+
return configFromSettings(await requireSettings(), setupName);
|
|
1169
905
|
}
|
|
1170
|
-
async function
|
|
1171
|
-
|
|
906
|
+
async function loadConfigForCheck() {
|
|
907
|
+
const document = await readActiveLocalConfigDocumentForRepair();
|
|
908
|
+
if (!document) throw new Error(`Missing pi-sync settings: ${localConfigPath()}`);
|
|
909
|
+
validateSettingsDocument(document.parsed);
|
|
910
|
+
return configFromSettings(document.parsed);
|
|
1172
911
|
}
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
912
|
+
function configFromSettings(settings, setupName) {
|
|
913
|
+
const selectedName = setupName ?? settings.activeSyncSetup;
|
|
914
|
+
if (!selectedName) throw new Error("No sync setups are configured.");
|
|
915
|
+
validateConfigName(selectedName, "sync setup");
|
|
916
|
+
const setup = ownObject(settings.syncSetups, selectedName);
|
|
917
|
+
if (!setup)
|
|
918
|
+
throw new Error(`Invalid pi-sync settings: sync setup \u201C${selectedName}\u201D was not found.`);
|
|
919
|
+
const connectionName = setup.storage.connection;
|
|
920
|
+
const connection = ownObject(
|
|
921
|
+
settings.storageConnections,
|
|
922
|
+
connectionName
|
|
923
|
+
);
|
|
924
|
+
if (!connection) {
|
|
925
|
+
throw new Error(
|
|
926
|
+
`Invalid pi-sync settings: sync setup \u201C${selectedName}\u201D references missing storage connection \u201C${connectionName}\u201D.`
|
|
927
|
+
);
|
|
1179
928
|
}
|
|
929
|
+
return resolveSyncConfig(
|
|
930
|
+
selectedName,
|
|
931
|
+
setup,
|
|
932
|
+
connectionName,
|
|
933
|
+
connection,
|
|
934
|
+
settings.onSwitch,
|
|
935
|
+
settings.skipSecretScan ?? false
|
|
936
|
+
);
|
|
937
|
+
}
|
|
938
|
+
async function loadPartialConfig(setupName) {
|
|
939
|
+
const config = await loadConfig(setupName);
|
|
940
|
+
return {
|
|
941
|
+
setupName: config.setupName,
|
|
942
|
+
...storageReviewFromConfig(config),
|
|
943
|
+
include: [...config.include],
|
|
944
|
+
automatic: config.automatic,
|
|
945
|
+
onSwitch: config.onSwitch
|
|
946
|
+
};
|
|
947
|
+
}
|
|
948
|
+
function syncSetupStorageReview(setupName, setup, connectionName, connection) {
|
|
949
|
+
return storageReviewFromConfig(
|
|
950
|
+
resolveSyncConfig(setupName, setup, connectionName, connection, DEFAULT_ON_SWITCH, false)
|
|
951
|
+
);
|
|
952
|
+
}
|
|
953
|
+
function syncSetupReviewIdentity(setupName, setup, connectionName, connection) {
|
|
954
|
+
return syncConfigReviewIdentity(
|
|
955
|
+
resolveSyncConfig(setupName, setup, connectionName, connection, DEFAULT_ON_SWITCH, false)
|
|
956
|
+
);
|
|
957
|
+
}
|
|
958
|
+
function syncConfigReviewIdentity(config) {
|
|
959
|
+
return JSON.stringify([
|
|
960
|
+
config.setupName,
|
|
961
|
+
config.connectionName,
|
|
962
|
+
backendIdentityCoordinates(config),
|
|
963
|
+
config.include,
|
|
964
|
+
config.automatic
|
|
965
|
+
]);
|
|
1180
966
|
}
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
967
|
+
function syncConfigReviewFingerprint(config) {
|
|
968
|
+
return createHash("sha256").update(syncConfigReviewIdentity(config)).digest("hex");
|
|
969
|
+
}
|
|
970
|
+
function syncCheckConfigFingerprint(config) {
|
|
971
|
+
return createHash("sha256").update(JSON.stringify(config)).digest("hex");
|
|
972
|
+
}
|
|
973
|
+
function storageReviewFromConfig(config) {
|
|
974
|
+
return {
|
|
975
|
+
connectionName: config.connectionName,
|
|
976
|
+
storageKind: config.backend.type,
|
|
977
|
+
storagePath: config.storagePath,
|
|
978
|
+
...config.backend.type === "s3" ? { bucket: config.backend.destination.bucket } : config.backend.type === "git" ? { branch: config.backend.destination.branch } : {}
|
|
979
|
+
};
|
|
980
|
+
}
|
|
981
|
+
function resolveSyncConfig(setupName, setup, connectionName, connection, onSwitch, skipSecretScan) {
|
|
982
|
+
const storagePath = normalizeStoragePath(setup.storage.path);
|
|
983
|
+
const namespace = storagePath === "./" ? "root" : storagePath.slice(storagePath.lastIndexOf("/") + 1);
|
|
984
|
+
const include = normalizeSyncInclude(setup.sync.include);
|
|
985
|
+
const common = {
|
|
986
|
+
setupName,
|
|
987
|
+
connectionName,
|
|
988
|
+
storagePath,
|
|
989
|
+
snapshotIdentity: namespace,
|
|
990
|
+
include,
|
|
991
|
+
automatic: setup.sync.automatic,
|
|
992
|
+
onSwitch,
|
|
993
|
+
skipSecretScan
|
|
994
|
+
};
|
|
995
|
+
if (connection.type === "git") {
|
|
996
|
+
return {
|
|
997
|
+
...common,
|
|
998
|
+
backend: {
|
|
999
|
+
type: "git",
|
|
1000
|
+
profile: { kind: "git", remote: normalizeGitRemote(connection.remote) },
|
|
1001
|
+
destination: {
|
|
1002
|
+
branch: normalizeGitBranch(setup.storage.branch),
|
|
1003
|
+
directory: normalizeGitDirectory(storagePath),
|
|
1004
|
+
namespace
|
|
1005
|
+
}
|
|
1006
|
+
}
|
|
1007
|
+
};
|
|
1008
|
+
}
|
|
1009
|
+
if (connection.type === "webdav") {
|
|
1010
|
+
return {
|
|
1011
|
+
...common,
|
|
1012
|
+
backend: {
|
|
1013
|
+
type: "webdav",
|
|
1014
|
+
profile: {
|
|
1015
|
+
kind: "webdav",
|
|
1016
|
+
url: normalizeWebDavUrl(connection.url),
|
|
1017
|
+
username: connection.credentials.username,
|
|
1018
|
+
password: connection.credentials.password
|
|
1019
|
+
},
|
|
1020
|
+
destination: { path: normalizeWebDavPath(storagePath), namespace }
|
|
1021
|
+
}
|
|
1022
|
+
};
|
|
1023
|
+
}
|
|
1024
|
+
return {
|
|
1025
|
+
...common,
|
|
1026
|
+
backend: {
|
|
1027
|
+
type: "s3",
|
|
1028
|
+
profile: {
|
|
1029
|
+
kind: isCloudflareR2Endpoint(connection.endpoint) ? "r2" : "s3-compatible",
|
|
1030
|
+
endpoint: normalizeS3Endpoint(connection.endpoint),
|
|
1031
|
+
region: requiredString(connection.region, "S3 region"),
|
|
1032
|
+
accessKeyId: connection.credentials.accessKeyId,
|
|
1033
|
+
secretAccessKey: connection.credentials.secretAccessKey,
|
|
1034
|
+
sessionToken: optionalString(connection.credentials.sessionToken, "S3 session token")
|
|
1035
|
+
},
|
|
1036
|
+
destination: {
|
|
1037
|
+
bucket: normalizeS3Bucket(setup.storage.bucket),
|
|
1038
|
+
prefix: storagePath,
|
|
1039
|
+
namespace
|
|
1040
|
+
}
|
|
1041
|
+
}
|
|
1042
|
+
};
|
|
1188
1043
|
}
|
|
1189
1044
|
function sessionTokenWarnings(config) {
|
|
1190
1045
|
if (!isCloudflareR2Endpoint(config.endpoint) || !config.sessionToken) return [];
|
|
@@ -1198,53 +1053,35 @@ function syncSessionsWarnings(config) {
|
|
|
1198
1053
|
"sessions: included; Pi session JSONL can contain prompts, tool output, file paths, images, and secrets. Sync sessions only to storage you trust."
|
|
1199
1054
|
];
|
|
1200
1055
|
}
|
|
1201
|
-
function isCloudflareR2Endpoint(endpoint) {
|
|
1202
|
-
const value = endpoint?.trim();
|
|
1203
|
-
if (!value) return false;
|
|
1204
|
-
try {
|
|
1205
|
-
const hostname = new URL(value).hostname.toLowerCase();
|
|
1206
|
-
return hostname === "r2.cloudflarestorage.com" || hostname.endsWith(".r2.cloudflarestorage.com");
|
|
1207
|
-
} catch {
|
|
1208
|
-
return false;
|
|
1209
|
-
}
|
|
1210
|
-
}
|
|
1211
|
-
function hasControlCharacter2(value) {
|
|
1212
|
-
return /[\u0000-\u001f\u007f-\u009f]/u.test(value);
|
|
1213
|
-
}
|
|
1214
1056
|
|
|
1215
1057
|
export {
|
|
1216
1058
|
localConfigPath,
|
|
1217
1059
|
activeLocalConfigPath,
|
|
1218
1060
|
consumeLocalConfigMigrationNotice,
|
|
1219
1061
|
createLocalConfigDocument,
|
|
1220
|
-
normalizeWebDavUrl,
|
|
1221
|
-
normalizeWebDavPath,
|
|
1222
|
-
validateWebDavNamespace,
|
|
1223
|
-
validateWebDavCredentials,
|
|
1224
|
-
isMissingConfigError,
|
|
1225
|
-
loadConfig,
|
|
1226
|
-
loadPartialConfig,
|
|
1227
|
-
syncSetupStorageReview,
|
|
1228
|
-
syncSetupReviewIdentity,
|
|
1229
|
-
syncConfigReviewIdentity,
|
|
1230
|
-
syncConfigReviewFingerprint,
|
|
1231
|
-
configuredSyncSetupNames,
|
|
1232
|
-
loadOnSwitch,
|
|
1233
1062
|
normalizeOnSwitch,
|
|
1234
1063
|
effectiveSyncSetupRemoteIdentity,
|
|
1235
1064
|
validateConfigName,
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1065
|
+
requiredString,
|
|
1066
|
+
normalizeStoragePath,
|
|
1067
|
+
normalizeS3Endpoint,
|
|
1068
|
+
normalizeS3Bucket,
|
|
1069
|
+
isCloudflareR2Endpoint,
|
|
1070
|
+
ownRecord,
|
|
1071
|
+
configuredSyncSetupNames,
|
|
1072
|
+
loadOnSwitch,
|
|
1241
1073
|
localConfigTemplate,
|
|
1242
1074
|
readLocalConfigObject,
|
|
1243
1075
|
updateLocalConfig,
|
|
1244
|
-
|
|
1245
|
-
|
|
1076
|
+
loadConfig,
|
|
1077
|
+
loadConfigForCheck,
|
|
1078
|
+
loadPartialConfig,
|
|
1079
|
+
syncSetupStorageReview,
|
|
1080
|
+
syncSetupReviewIdentity,
|
|
1081
|
+
syncConfigReviewIdentity,
|
|
1082
|
+
syncConfigReviewFingerprint,
|
|
1083
|
+
syncCheckConfigFingerprint,
|
|
1246
1084
|
sessionTokenWarnings,
|
|
1247
|
-
syncSessionsWarnings
|
|
1248
|
-
isCloudflareR2Endpoint
|
|
1085
|
+
syncSessionsWarnings
|
|
1249
1086
|
};
|
|
1250
|
-
//# sourceMappingURL=chunk-
|
|
1087
|
+
//# sourceMappingURL=chunk-C7XY5NF5.ts.map
|