@narumitw/pi-sync 0.49.14 → 0.50.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/README.md +134 -241
- package/dist/chunks/chunk-2B2ELUF5.ts +29 -0
- package/dist/chunks/chunk-2B2ELUF5.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-6UHKGALE.ts +134 -0
- package/dist/chunks/chunk-6UHKGALE.ts.map +7 -0
- package/dist/chunks/{chunk-RGJ6SOYR.ts → chunk-AUSDLD6F.ts} +4 -4
- package/dist/chunks/chunk-AUSDLD6F.ts.map +7 -0
- package/dist/chunks/{chunk-3NDD5QPM.ts → chunk-AXXS2BPA.ts} +9 -9
- package/dist/chunks/chunk-AXXS2BPA.ts.map +7 -0
- package/dist/chunks/{chunk-APZVBSM6.ts → chunk-C7XY5NF5.ts} +214 -365
- package/dist/chunks/chunk-C7XY5NF5.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-LJDSHNS3.ts → chunk-GWMHAQG4.ts} +4 -4
- package/dist/chunks/chunk-GWMHAQG4.ts.map +7 -0
- package/dist/chunks/{chunk-FR6CVXAL.ts → chunk-HUDQATCL.ts} +25 -25
- package/dist/chunks/chunk-HUDQATCL.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-JSYMGJQQ.ts +51 -0
- package/dist/chunks/chunk-JSYMGJQQ.ts.map +7 -0
- package/dist/chunks/chunk-L7TBO32M.ts +25 -0
- package/dist/chunks/chunk-L7TBO32M.ts.map +7 -0
- package/dist/chunks/{chunk-4F3W4CSC.ts → chunk-LCRI3ABW.ts} +22 -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-EWX3TPLQ.ts → chunk-OEX4ABY6.ts} +39 -37
- package/dist/chunks/chunk-OEX4ABY6.ts.map +7 -0
- package/dist/chunks/chunk-OUHGBFYF.ts +1255 -0
- package/dist/chunks/chunk-OUHGBFYF.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-BHJUISB4.ts → chunk-SL64RLXR.ts} +8 -6
- package/dist/chunks/chunk-SL64RLXR.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-LONPFFNP.ts → chunk-X5OM6GXT.ts} +9 -7
- package/dist/chunks/chunk-X5OM6GXT.ts.map +7 -0
- package/dist/chunks/chunk-XBX4HBLG.ts +57 -0
- package/dist/chunks/chunk-XBX4HBLG.ts.map +7 -0
- package/dist/chunks/{chunk-5WJNWFTG.ts → chunk-YKJHPTGO.ts} +23 -55
- package/dist/chunks/chunk-YKJHPTGO.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-4UABOP44.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-OQW46G2X.ts} +42 -17
- package/dist/chunks/git-backend-OQW46G2X.ts.map +7 -0
- package/dist/chunks/manager-result-dispatcher-U23ODD32.ts +22 -0
- package/dist/chunks/manager-ui-ABATHC36.ts +1680 -0
- package/dist/chunks/manager-ui-ABATHC36.ts.map +7 -0
- package/dist/chunks/remote-selection-ui-ZE3TY67S.ts +21 -0
- package/dist/chunks/{s3-backend-3QZGO2EF.ts → s3-backend-3P33LQHU.ts} +96 -18
- package/dist/chunks/s3-backend-3P33LQHU.ts.map +7 -0
- package/dist/chunks/{setup-switch-X3W3FALZ.ts → setup-switch-IS2PEZ3J.ts} +9 -9
- package/dist/chunks/setup-wizard-VB3VDOV5.ts +89 -0
- package/dist/chunks/setup-wizard-VB3VDOV5.ts.map +7 -0
- package/dist/chunks/{snapshot-WBSV4MQG.ts → snapshot-HKEWCVK6.ts} +7 -6
- package/dist/chunks/sync-inspection-FEP4L5TR.ts +20 -0
- package/dist/chunks/{sync-operations-MPAFVVHA.ts → sync-operations-ONJ27QRL.ts} +381 -299
- package/dist/chunks/sync-operations-ONJ27QRL.ts.map +7 -0
- package/dist/chunks/{sync-state-36H7ULG2.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 +477 -342
- package/dist/index.ts.map +4 -4
- package/docs/settings.md +211 -0
- package/package.json +6 -8
- 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} +40 -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} +41 -330
- 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 -284
- 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} +42 -11
- 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/{sync-attention.ts → ui/sync-attention.ts} +75 -6
- 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-3NDD5QPM.ts.map +0 -7
- package/dist/chunks/chunk-4F3W4CSC.ts.map +0 -7
- package/dist/chunks/chunk-4U6AFLJB.ts.map +0 -7
- package/dist/chunks/chunk-5MQ76BK5.ts.map +0 -7
- package/dist/chunks/chunk-5WJNWFTG.ts.map +0 -7
- package/dist/chunks/chunk-APZVBSM6.ts.map +0 -7
- package/dist/chunks/chunk-BHJUISB4.ts.map +0 -7
- package/dist/chunks/chunk-EWX3TPLQ.ts.map +0 -7
- package/dist/chunks/chunk-F5QL2GPY.ts.map +0 -7
- package/dist/chunks/chunk-FR6CVXAL.ts.map +0 -7
- package/dist/chunks/chunk-HGV24P4T.ts.map +0 -7
- package/dist/chunks/chunk-JNOOSPEW.ts +0 -82
- package/dist/chunks/chunk-JNOOSPEW.ts.map +0 -7
- package/dist/chunks/chunk-LJDSHNS3.ts.map +0 -7
- package/dist/chunks/chunk-LONPFFNP.ts.map +0 -7
- package/dist/chunks/chunk-RGJ6SOYR.ts.map +0 -7
- package/dist/chunks/chunk-W64NEHXT.ts +0 -167
- package/dist/chunks/chunk-W64NEHXT.ts.map +0 -7
- package/dist/chunks/chunk-YQ6UW7IF.ts.map +0 -7
- package/dist/chunks/file-selection-4UABOP44.ts.map +0 -7
- package/dist/chunks/git-backend-E3UOUUOM.ts.map +0 -7
- package/dist/chunks/manager-result-dispatcher-RNRQA3PW.ts +0 -18
- package/dist/chunks/manager-ui-QO2P4V3Z.ts +0 -2757
- package/dist/chunks/manager-ui-QO2P4V3Z.ts.map +0 -7
- package/dist/chunks/remote-selection-ui-LHSAGKGA.ts +0 -17
- package/dist/chunks/s3-backend-3QZGO2EF.ts.map +0 -7
- package/dist/chunks/sync-operations-MPAFVVHA.ts.map +0 -7
- package/dist/chunks/webdav-backend-ZD5RHRIU.ts.map +0 -7
- package/src/config.ts +0 -910
- package/src/git-ui.ts +0 -307
- package/src/manager-helpers.ts +0 -64
- package/src/manager-ui.ts +0 -997
- package/src/webdav-ui.ts +0 -421
- /package/dist/chunks/{manager-result-dispatcher-RNRQA3PW.ts.map → manager-result-dispatcher-U23ODD32.ts.map} +0 -0
- /package/dist/chunks/{remote-selection-ui-LHSAGKGA.ts.map → remote-selection-ui-ZE3TY67S.ts.map} +0 -0
- /package/dist/chunks/{setup-switch-X3W3FALZ.ts.map → setup-switch-IS2PEZ3J.ts.map} +0 -0
- /package/dist/chunks/{snapshot-WBSV4MQG.ts.map → snapshot-HKEWCVK6.ts.map} +0 -0
- /package/dist/chunks/{sync-state-36H7ULG2.ts.map → sync-inspection-FEP4L5TR.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,162 +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(selectedName, setup, connectionName, connection, settings.onSwitch);
|
|
593
|
-
}
|
|
594
|
-
async function loadPartialConfig(setupName) {
|
|
595
|
-
const config = await loadConfig(setupName);
|
|
596
|
-
return {
|
|
597
|
-
setupName: config.setupName,
|
|
598
|
-
...storageReviewFromConfig(config),
|
|
599
|
-
include: [...config.include],
|
|
600
|
-
automatic: config.automatic,
|
|
601
|
-
onSwitch: config.onSwitch
|
|
602
|
-
};
|
|
603
|
-
}
|
|
604
|
-
function syncSetupStorageReview(setupName, setup, connectionName, connection) {
|
|
605
|
-
return storageReviewFromConfig(
|
|
606
|
-
resolveSyncConfig(setupName, setup, connectionName, connection, DEFAULT_ON_SWITCH)
|
|
607
|
-
);
|
|
608
|
-
}
|
|
609
|
-
function syncSetupReviewIdentity(setupName, setup, connectionName, connection) {
|
|
610
|
-
return syncConfigReviewIdentity(
|
|
611
|
-
resolveSyncConfig(setupName, setup, connectionName, connection, DEFAULT_ON_SWITCH)
|
|
612
|
-
);
|
|
613
|
-
}
|
|
614
|
-
function syncConfigReviewIdentity(config) {
|
|
615
|
-
return JSON.stringify([
|
|
616
|
-
config.setupName,
|
|
617
|
-
config.connectionName,
|
|
618
|
-
backendIdentityCoordinates(config),
|
|
619
|
-
config.include,
|
|
620
|
-
config.automatic
|
|
621
|
-
]);
|
|
622
|
-
}
|
|
623
|
-
function syncConfigReviewFingerprint(config) {
|
|
624
|
-
return createHash("sha256").update(syncConfigReviewIdentity(config)).digest("hex");
|
|
625
|
-
}
|
|
626
|
-
function storageReviewFromConfig(config) {
|
|
627
|
-
return {
|
|
628
|
-
connectionName: config.connectionName,
|
|
629
|
-
storageKind: config.backend.type,
|
|
630
|
-
storagePath: config.storagePath,
|
|
631
|
-
...config.backend.type === "s3" ? { bucket: config.backend.destination.bucket } : config.backend.type === "git" ? { branch: config.backend.destination.branch } : {}
|
|
632
|
-
};
|
|
633
|
-
}
|
|
634
|
-
async function configuredSyncSetupNames() {
|
|
635
|
-
const settings = await readLocalConfigObject();
|
|
636
|
-
return settings ? Object.keys(settings.syncSetups).sort((left, right) => left.localeCompare(right)) : [];
|
|
637
|
-
}
|
|
638
|
-
async function loadOnSwitch() {
|
|
639
|
-
return (await requireSettings()).onSwitch;
|
|
640
|
-
}
|
|
641
504
|
function normalizeOnSwitch(value) {
|
|
642
505
|
if (value === "ask-before-pull" || value === "pull-after-switch" || value === "switch-only") {
|
|
643
506
|
return value;
|
|
@@ -646,75 +509,6 @@ function normalizeOnSwitch(value) {
|
|
|
646
509
|
'Invalid pi-sync settings: onSwitch must be "ask-before-pull", "pull-after-switch", or "switch-only".'
|
|
647
510
|
);
|
|
648
511
|
}
|
|
649
|
-
function resolveSyncConfig(setupName, setup, connectionName, connection, onSwitch) {
|
|
650
|
-
const storagePath = normalizeStoragePath(setup.storage.path);
|
|
651
|
-
const namespace = storagePath.slice(storagePath.lastIndexOf("/") + 1);
|
|
652
|
-
const include = normalizeSyncInclude(setup.sync.include);
|
|
653
|
-
const common = {
|
|
654
|
-
setupName,
|
|
655
|
-
connectionName,
|
|
656
|
-
storagePath,
|
|
657
|
-
snapshotIdentity: namespace,
|
|
658
|
-
include,
|
|
659
|
-
automatic: setup.sync.automatic,
|
|
660
|
-
onSwitch
|
|
661
|
-
};
|
|
662
|
-
if (connection.type === "git") {
|
|
663
|
-
return {
|
|
664
|
-
...common,
|
|
665
|
-
backend: {
|
|
666
|
-
type: "git",
|
|
667
|
-
profile: { kind: "git", remote: normalizeGitRemote(connection.remote) },
|
|
668
|
-
destination: {
|
|
669
|
-
branch: normalizeGitBranch(setup.storage.branch),
|
|
670
|
-
directory: normalizeGitDirectory(storagePath),
|
|
671
|
-
namespace
|
|
672
|
-
}
|
|
673
|
-
}
|
|
674
|
-
};
|
|
675
|
-
}
|
|
676
|
-
if (connection.type === "webdav") {
|
|
677
|
-
return {
|
|
678
|
-
...common,
|
|
679
|
-
backend: {
|
|
680
|
-
type: "webdav",
|
|
681
|
-
profile: {
|
|
682
|
-
kind: "webdav",
|
|
683
|
-
url: normalizeWebDavUrl(connection.url),
|
|
684
|
-
username: connection.credentials.username,
|
|
685
|
-
password: connection.credentials.password
|
|
686
|
-
},
|
|
687
|
-
destination: { path: normalizeWebDavPath(storagePath), namespace }
|
|
688
|
-
}
|
|
689
|
-
};
|
|
690
|
-
}
|
|
691
|
-
return {
|
|
692
|
-
...common,
|
|
693
|
-
backend: {
|
|
694
|
-
type: "s3",
|
|
695
|
-
profile: {
|
|
696
|
-
kind: isCloudflareR2Endpoint(connection.endpoint) ? "r2" : "s3-compatible",
|
|
697
|
-
endpoint: normalizeS3Endpoint(connection.endpoint),
|
|
698
|
-
region: requiredString(connection.region, "S3 region"),
|
|
699
|
-
accessKeyId: connection.credentials.accessKeyId,
|
|
700
|
-
secretAccessKey: connection.credentials.secretAccessKey,
|
|
701
|
-
sessionToken: optionalString(connection.credentials.sessionToken, "S3 session token")
|
|
702
|
-
},
|
|
703
|
-
destination: {
|
|
704
|
-
bucket: normalizeS3Bucket(setup.storage.bucket),
|
|
705
|
-
prefix: storagePath,
|
|
706
|
-
namespace
|
|
707
|
-
}
|
|
708
|
-
}
|
|
709
|
-
};
|
|
710
|
-
}
|
|
711
|
-
async function requireSettings() {
|
|
712
|
-
const settings = await readLocalConfigObject();
|
|
713
|
-
if (!settings) {
|
|
714
|
-
throw new Error(`Missing pi-sync settings. Use /sync setup or create ${localConfigPath()}.`);
|
|
715
|
-
}
|
|
716
|
-
return settings;
|
|
717
|
-
}
|
|
718
512
|
function validateSettingsDocument(value) {
|
|
719
513
|
if (value.version !== 3) {
|
|
720
514
|
throw new Error(
|
|
@@ -744,6 +538,9 @@ function validateSettingsDocument(value) {
|
|
|
744
538
|
"top level"
|
|
745
539
|
);
|
|
746
540
|
normalizeOnSwitch(value.onSwitch);
|
|
541
|
+
if (value.skipSecretScan !== void 0 && typeof value.skipSecretScan !== "boolean") {
|
|
542
|
+
throw new Error("Invalid pi-sync settings: skipSecretScan must be boolean.");
|
|
543
|
+
}
|
|
747
544
|
const storageConnections = requireNamedObjectMap(
|
|
748
545
|
value.storageConnections,
|
|
749
546
|
"storageConnections",
|
|
@@ -966,12 +763,12 @@ function requireRecord(value, field) {
|
|
|
966
763
|
return value;
|
|
967
764
|
}
|
|
968
765
|
function validateConfigName(value, field) {
|
|
969
|
-
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)) {
|
|
970
767
|
throw new Error(`Invalid pi-sync settings: invalid ${field} name.`);
|
|
971
768
|
}
|
|
972
769
|
}
|
|
973
770
|
function requiredString(value, field) {
|
|
974
|
-
if (typeof value !== "string" || !value.trim() ||
|
|
771
|
+
if (typeof value !== "string" || !value.trim() || hasControlCharacter(value)) {
|
|
975
772
|
throw new Error(`Invalid pi-sync settings: ${field} must be a non-empty string.`);
|
|
976
773
|
}
|
|
977
774
|
return value.trim();
|
|
@@ -991,21 +788,22 @@ function optionalCanonicalReference(value, field) {
|
|
|
991
788
|
return normalized;
|
|
992
789
|
}
|
|
993
790
|
function requiredSecret(value, field) {
|
|
994
|
-
if (typeof value !== "string" || !value ||
|
|
791
|
+
if (typeof value !== "string" || !value || hasControlCharacter(value)) {
|
|
995
792
|
throw new Error(`Invalid pi-sync settings: ${field} must be configured.`);
|
|
996
793
|
}
|
|
997
794
|
return value;
|
|
998
795
|
}
|
|
999
796
|
function optionalString(value, field) {
|
|
1000
797
|
if (value === void 0) return void 0;
|
|
1001
|
-
if (typeof value !== "string" ||
|
|
798
|
+
if (typeof value !== "string" || hasControlCharacter(value)) {
|
|
1002
799
|
throw new Error(`Invalid pi-sync settings: ${field} must be a string.`);
|
|
1003
800
|
}
|
|
1004
801
|
return value.trim() || void 0;
|
|
1005
802
|
}
|
|
1006
803
|
function normalizeStoragePath(value) {
|
|
804
|
+
if (value.trim() === "." || value.trim() === "./") return "./";
|
|
1007
805
|
const normalized = value.trim().replace(/^\/+|\/+$/gu, "");
|
|
1008
|
-
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 === "..")) {
|
|
1009
807
|
throw new Error("Invalid pi-sync settings: storage.path must be a safe relative path.");
|
|
1010
808
|
}
|
|
1011
809
|
return normalized;
|
|
@@ -1031,98 +829,46 @@ function normalizeS3Bucket(value) {
|
|
|
1031
829
|
}
|
|
1032
830
|
return bucket;
|
|
1033
831
|
}
|
|
1034
|
-
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;
|
|
1035
838
|
try {
|
|
1036
|
-
const
|
|
1037
|
-
|
|
1038
|
-
url.pathname = url.pathname.replace(/\/+$/gu, "");
|
|
1039
|
-
return url.toString().replace(/\/$/u, "");
|
|
839
|
+
const hostname = new URL(value).hostname.toLowerCase();
|
|
840
|
+
return hostname === "r2.cloudflarestorage.com" || hostname.endsWith(".r2.cloudflarestorage.com");
|
|
1040
841
|
} catch {
|
|
1041
|
-
return
|
|
842
|
+
return false;
|
|
1042
843
|
}
|
|
1043
844
|
}
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
path2.join(agentDir(), "settings.json")
|
|
1047
|
-
);
|
|
1048
|
-
return settings?.sessionDir ? expandHome(settings.sessionDir) : void 0;
|
|
1049
|
-
}
|
|
1050
|
-
async function sessionDirForApply(ctx, snapshot) {
|
|
1051
|
-
const contextSessionDir = sessionDirFromContext(ctx);
|
|
1052
|
-
const localSessionDir = await configuredSessionDir();
|
|
1053
|
-
if (contextSessionDir && path2.resolve(contextSessionDir) !== path2.resolve(localSessionDir ?? "")) {
|
|
1054
|
-
return contextSessionDir;
|
|
1055
|
-
}
|
|
1056
|
-
return sessionDirFromSnapshot(snapshot) ?? contextSessionDir;
|
|
845
|
+
function ownRecord(value) {
|
|
846
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
1057
847
|
}
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
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()}.`
|
|
1064
856
|
);
|
|
1065
|
-
return settings.sessionDir ? expandHome(settings.sessionDir) : void 0;
|
|
1066
|
-
} catch {
|
|
1067
|
-
return void 0;
|
|
1068
857
|
}
|
|
858
|
+
return settings;
|
|
1069
859
|
}
|
|
1070
|
-
function
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
}
|
|
1074
|
-
return Buffer.from(value, "base64");
|
|
1075
|
-
}
|
|
1076
|
-
async function readStateForConfig(config) {
|
|
1077
|
-
return await readJsonIfExists(statePathForConfig(config)) ?? {
|
|
1078
|
-
version: STATE_VERSION,
|
|
1079
|
-
profile: config.snapshotIdentity,
|
|
1080
|
-
lastFileHashes: {}
|
|
1081
|
-
};
|
|
860
|
+
async function configuredSyncSetupNames() {
|
|
861
|
+
const settings = await readLocalConfigObject();
|
|
862
|
+
return settings ? Object.keys(settings.syncSetups).sort((left, right) => left.localeCompare(right)) : [];
|
|
1082
863
|
}
|
|
1083
|
-
async function
|
|
1084
|
-
await
|
|
1085
|
-
}
|
|
1086
|
-
function statePathForConfig(config) {
|
|
1087
|
-
const identity = backendIdentityCoordinates(config);
|
|
1088
|
-
const hash = createHash("sha256").update(identity).digest("hex").slice(0, 16);
|
|
1089
|
-
return path2.join(stateDir(), "setups", `${config.backend.type}-${hash}.state.json`);
|
|
1090
|
-
}
|
|
1091
|
-
function backendIdentityCoordinates(config) {
|
|
1092
|
-
switch (config.backend.type) {
|
|
1093
|
-
case "s3":
|
|
1094
|
-
return JSON.stringify([
|
|
1095
|
-
"s3",
|
|
1096
|
-
normalizeEndpointIdentity(config.backend.profile.endpoint),
|
|
1097
|
-
config.backend.destination.bucket,
|
|
1098
|
-
config.storagePath
|
|
1099
|
-
]);
|
|
1100
|
-
case "git":
|
|
1101
|
-
return JSON.stringify([
|
|
1102
|
-
"git",
|
|
1103
|
-
normalizeGitRemoteIdentity(config.backend.profile.remote),
|
|
1104
|
-
config.backend.destination.branch,
|
|
1105
|
-
config.storagePath
|
|
1106
|
-
]);
|
|
1107
|
-
case "webdav":
|
|
1108
|
-
return JSON.stringify([
|
|
1109
|
-
"webdav",
|
|
1110
|
-
normalizeWebDavIdentityUrl(config.backend.profile.url),
|
|
1111
|
-
config.backend.profile.username,
|
|
1112
|
-
config.storagePath
|
|
1113
|
-
]);
|
|
1114
|
-
}
|
|
1115
|
-
}
|
|
1116
|
-
function agentDir() {
|
|
1117
|
-
return getAgentDir2();
|
|
1118
|
-
}
|
|
1119
|
-
function expandHome(value) {
|
|
1120
|
-
return value === "~" || value.startsWith("~/") ? path2.join(os.homedir(), value.slice(2)) : value;
|
|
864
|
+
async function loadOnSwitch() {
|
|
865
|
+
return (await requireSettings()).onSwitch;
|
|
1121
866
|
}
|
|
1122
867
|
function localConfigTemplate() {
|
|
1123
868
|
return {
|
|
1124
869
|
version: 3,
|
|
1125
870
|
onSwitch: DEFAULT_ON_SWITCH,
|
|
871
|
+
skipSecretScan: false,
|
|
1126
872
|
storageConnections: {},
|
|
1127
873
|
syncSetups: {}
|
|
1128
874
|
};
|
|
@@ -1137,7 +883,6 @@ async function readLocalConfigDocument() {
|
|
|
1137
883
|
async function readLocalConfigObject() {
|
|
1138
884
|
return (await readLocalConfigDocument())?.parsed;
|
|
1139
885
|
}
|
|
1140
|
-
var configUpdateQueue = Promise.resolve();
|
|
1141
886
|
function updateLocalConfig(update, signal) {
|
|
1142
887
|
const operation = configUpdateQueue.then(() => {
|
|
1143
888
|
signal?.throwIfAborted();
|
|
@@ -1152,27 +897,149 @@ function updateLocalConfig(update, signal) {
|
|
|
1152
897
|
async function performLocalConfigUpdate(update, signal) {
|
|
1153
898
|
return updateLocalConfigDocument(localConfigTemplate(), update, validateSettingsDocument, signal);
|
|
1154
899
|
}
|
|
1155
|
-
|
|
1156
|
-
|
|
900
|
+
|
|
901
|
+
// src/settings/config.ts
|
|
902
|
+
import { createHash } from "node:crypto";
|
|
903
|
+
async function loadConfig(setupName) {
|
|
904
|
+
return configFromSettings(await requireSettings(), setupName);
|
|
1157
905
|
}
|
|
1158
|
-
async function
|
|
1159
|
-
|
|
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);
|
|
1160
911
|
}
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
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
|
+
);
|
|
1167
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
|
+
);
|
|
1168
952
|
}
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
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
|
+
]);
|
|
966
|
+
}
|
|
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
|
+
};
|
|
1176
1043
|
}
|
|
1177
1044
|
function sessionTokenWarnings(config) {
|
|
1178
1045
|
if (!isCloudflareR2Endpoint(config.endpoint) || !config.sessionToken) return [];
|
|
@@ -1186,53 +1053,35 @@ function syncSessionsWarnings(config) {
|
|
|
1186
1053
|
"sessions: included; Pi session JSONL can contain prompts, tool output, file paths, images, and secrets. Sync sessions only to storage you trust."
|
|
1187
1054
|
];
|
|
1188
1055
|
}
|
|
1189
|
-
function isCloudflareR2Endpoint(endpoint) {
|
|
1190
|
-
const value = endpoint?.trim();
|
|
1191
|
-
if (!value) return false;
|
|
1192
|
-
try {
|
|
1193
|
-
const hostname = new URL(value).hostname.toLowerCase();
|
|
1194
|
-
return hostname === "r2.cloudflarestorage.com" || hostname.endsWith(".r2.cloudflarestorage.com");
|
|
1195
|
-
} catch {
|
|
1196
|
-
return false;
|
|
1197
|
-
}
|
|
1198
|
-
}
|
|
1199
|
-
function hasControlCharacter2(value) {
|
|
1200
|
-
return /[\u0000-\u001f\u007f-\u009f]/u.test(value);
|
|
1201
|
-
}
|
|
1202
1056
|
|
|
1203
1057
|
export {
|
|
1204
1058
|
localConfigPath,
|
|
1205
1059
|
activeLocalConfigPath,
|
|
1206
1060
|
consumeLocalConfigMigrationNotice,
|
|
1207
1061
|
createLocalConfigDocument,
|
|
1208
|
-
normalizeWebDavUrl,
|
|
1209
|
-
normalizeWebDavPath,
|
|
1210
|
-
validateWebDavNamespace,
|
|
1211
|
-
validateWebDavCredentials,
|
|
1212
|
-
isMissingConfigError,
|
|
1213
|
-
loadConfig,
|
|
1214
|
-
loadPartialConfig,
|
|
1215
|
-
syncSetupStorageReview,
|
|
1216
|
-
syncSetupReviewIdentity,
|
|
1217
|
-
syncConfigReviewIdentity,
|
|
1218
|
-
syncConfigReviewFingerprint,
|
|
1219
|
-
configuredSyncSetupNames,
|
|
1220
|
-
loadOnSwitch,
|
|
1221
1062
|
normalizeOnSwitch,
|
|
1222
1063
|
effectiveSyncSetupRemoteIdentity,
|
|
1223
1064
|
validateConfigName,
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1065
|
+
requiredString,
|
|
1066
|
+
normalizeStoragePath,
|
|
1067
|
+
normalizeS3Endpoint,
|
|
1068
|
+
normalizeS3Bucket,
|
|
1069
|
+
isCloudflareR2Endpoint,
|
|
1070
|
+
ownRecord,
|
|
1071
|
+
configuredSyncSetupNames,
|
|
1072
|
+
loadOnSwitch,
|
|
1229
1073
|
localConfigTemplate,
|
|
1230
1074
|
readLocalConfigObject,
|
|
1231
1075
|
updateLocalConfig,
|
|
1232
|
-
|
|
1233
|
-
|
|
1076
|
+
loadConfig,
|
|
1077
|
+
loadConfigForCheck,
|
|
1078
|
+
loadPartialConfig,
|
|
1079
|
+
syncSetupStorageReview,
|
|
1080
|
+
syncSetupReviewIdentity,
|
|
1081
|
+
syncConfigReviewIdentity,
|
|
1082
|
+
syncConfigReviewFingerprint,
|
|
1083
|
+
syncCheckConfigFingerprint,
|
|
1234
1084
|
sessionTokenWarnings,
|
|
1235
|
-
syncSessionsWarnings
|
|
1236
|
-
isCloudflareR2Endpoint
|
|
1085
|
+
syncSessionsWarnings
|
|
1237
1086
|
};
|
|
1238
|
-
//# sourceMappingURL=chunk-
|
|
1087
|
+
//# sourceMappingURL=chunk-C7XY5NF5.ts.map
|