@narumitw/pi-sync 0.30.0 → 0.34.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 +159 -236
- package/package.json +14 -6
- package/src/backend-factory.ts +20 -0
- package/src/command.ts +39 -39
- package/src/config-errors.ts +7 -0
- package/src/config-file.ts +64 -8
- package/src/config.ts +605 -476
- package/src/file-selection.ts +121 -140
- package/src/git-backend.ts +832 -0
- package/src/git-config.ts +136 -0
- package/src/git-runner.ts +276 -0
- package/src/git-storage.ts +288 -0
- package/src/git-ui.ts +307 -0
- package/src/manager-helpers.ts +64 -0
- package/src/manager-ui.ts +537 -542
- package/src/paths.ts +4 -1
- package/src/s3-backend.ts +498 -0
- package/src/s3-client.ts +147 -64
- package/src/s3-credentials-ui.ts +92 -0
- package/src/secret-input.ts +199 -0
- package/src/settings-management.ts +191 -277
- package/src/settings-ui.ts +94 -115
- package/src/setup-switch.ts +150 -0
- package/src/snapshot-apply.ts +13 -7
- package/src/snapshot-codec.ts +48 -0
- package/src/snapshot-transaction.ts +3 -1
- package/src/snapshot.ts +63 -32
- package/src/storage-connections-ui.ts +308 -0
- package/src/sync-backend.ts +97 -0
- package/src/sync-format.ts +31 -33
- package/src/sync-operations.ts +912 -0
- package/src/sync-policy.ts +137 -61
- package/src/sync-setups-ui.ts +177 -0
- package/src/sync-state.ts +48 -48
- package/src/sync.ts +180 -739
- package/src/types.ts +177 -53
- package/src/webdav-backend.ts +678 -0
- package/src/webdav-client.ts +390 -0
- package/src/webdav-config.ts +92 -0
- package/src/webdav-ui.ts +421 -0
- package/src/target-switch.ts +0 -119
package/src/sync-format.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { createHash } from "node:crypto";
|
|
2
1
|
import { agentDir } from "./config.js";
|
|
2
|
+
import type { PublicationCapability, RemoteHead } from "./sync-backend.js";
|
|
3
3
|
import { fileHashMap } from "./sync-state.js";
|
|
4
|
-
import type {
|
|
4
|
+
import type { CommonSyncConfig, Snapshot } from "./types.js";
|
|
5
5
|
|
|
6
6
|
export function formatDiff(local: Snapshot, remote: Snapshot) {
|
|
7
7
|
const localMap = fileHashMap(local);
|
|
@@ -36,19 +36,20 @@ export function formatSnapshotOnlyDiff(title: string, snapshot: Snapshot) {
|
|
|
36
36
|
}
|
|
37
37
|
|
|
38
38
|
export function formatPushSummary(
|
|
39
|
-
config:
|
|
39
|
+
config: CommonSyncConfig,
|
|
40
|
+
destination: string,
|
|
40
41
|
upload: Snapshot,
|
|
41
|
-
|
|
42
|
+
head: RemoteHead | undefined,
|
|
42
43
|
preservedRemoteFileCount = 0,
|
|
43
44
|
remote?: Snapshot,
|
|
44
45
|
) {
|
|
45
46
|
return [
|
|
46
|
-
`
|
|
47
|
-
`
|
|
47
|
+
`Sync setup: ${safeTerminalText(config.setupName)}`,
|
|
48
|
+
`Storage location: ${safeTerminalText(destination)}`,
|
|
48
49
|
`Upload ${upload.files.length} files from ${safeTerminalText(agentDir())}.`,
|
|
49
50
|
`Sessions: ${upload.syncSessions ? "included — may contain private conversations" : "not included"}`,
|
|
50
|
-
|
|
51
|
-
"Publication effect:
|
|
51
|
+
head ? `Remote latest: ${head.snapshotId}` : "Remote latest: empty",
|
|
52
|
+
"Publication effect: the backend's active head will reference the new immutable snapshot.",
|
|
52
53
|
formatPublicationPreview(remote, upload),
|
|
53
54
|
preservedRemoteFileCount > 0
|
|
54
55
|
? `Possible secrets in locally managed files were scanned before this prompt; ${preservedRemoteFileCount} preserved remote file(s) were not rescanned.`
|
|
@@ -65,14 +66,15 @@ export function formatApplyPreview(local: Snapshot, remote: Snapshot) {
|
|
|
65
66
|
}
|
|
66
67
|
|
|
67
68
|
export function formatPullSummary(
|
|
68
|
-
config:
|
|
69
|
+
config: CommonSyncConfig,
|
|
70
|
+
destination: string,
|
|
69
71
|
local: Snapshot,
|
|
70
72
|
remote: Snapshot,
|
|
71
73
|
protectedSessionCount: number,
|
|
72
74
|
) {
|
|
73
75
|
return [
|
|
74
|
-
`
|
|
75
|
-
`
|
|
76
|
+
`Sync setup: ${safeTerminalText(config.setupName)}`,
|
|
77
|
+
`Storage location: ${safeTerminalText(destination)}`,
|
|
76
78
|
`Snapshot: ${safeTerminalText(remote.id)}`,
|
|
77
79
|
`Sessions: ${remote.syncSessions ? "included — may contain private conversations" : "not included"}`,
|
|
78
80
|
`Protected live sessions: ${protectedSessionCount || "none"}`,
|
|
@@ -82,20 +84,21 @@ export function formatPullSummary(
|
|
|
82
84
|
}
|
|
83
85
|
|
|
84
86
|
export function formatRollbackSummary(
|
|
85
|
-
config:
|
|
87
|
+
config: CommonSyncConfig,
|
|
88
|
+
destination: string,
|
|
86
89
|
local: Snapshot,
|
|
87
90
|
remote: Snapshot,
|
|
88
91
|
requestedSnapshot: string,
|
|
89
92
|
protectedSessionCount: number,
|
|
90
93
|
) {
|
|
91
94
|
return [
|
|
92
|
-
`
|
|
93
|
-
`
|
|
95
|
+
`Sync setup: ${safeTerminalText(config.setupName)}`,
|
|
96
|
+
`Storage location: ${safeTerminalText(destination)}`,
|
|
94
97
|
`Snapshot: ${safeTerminalText(requestedSnapshot)}`,
|
|
95
98
|
`Sessions: ${remote.syncSessions ? "included — may contain private conversations" : "not included"}`,
|
|
96
99
|
`Protected live sessions: ${protectedSessionCount || "none"}`,
|
|
97
100
|
formatApplyPreview(local, remote),
|
|
98
|
-
"A local backup is created before applying; the
|
|
101
|
+
"A local backup is created before applying; the backend's active head will change.",
|
|
99
102
|
].join("\n");
|
|
100
103
|
}
|
|
101
104
|
|
|
@@ -131,21 +134,24 @@ function formatDirectionalChanges(
|
|
|
131
134
|
return lines.join("\n");
|
|
132
135
|
}
|
|
133
136
|
|
|
134
|
-
export function formatDestination(config: SyncConfig) {
|
|
135
|
-
let host = config.endpoint;
|
|
136
|
-
try {
|
|
137
|
-
host = new URL(config.endpoint).hostname;
|
|
138
|
-
} catch {
|
|
139
|
-
// Preserve a sanitized invalid endpoint for actionable repair output.
|
|
140
|
-
}
|
|
141
|
-
return safeTerminalText(`${host} · ${config.bucket}/${config.prefix}/profiles/${config.profile}`);
|
|
142
|
-
}
|
|
143
|
-
|
|
144
137
|
export function countPreservedRemoteFiles(local: Snapshot, upload: Snapshot) {
|
|
145
138
|
const localPaths = new Set(local.files.map((file) => file.path));
|
|
146
139
|
return upload.files.filter((file) => !localPaths.has(file.path)).length;
|
|
147
140
|
}
|
|
148
141
|
|
|
142
|
+
export function publicationCapabilityDescription(capability: PublicationCapability) {
|
|
143
|
+
switch (capability) {
|
|
144
|
+
case "lease-protected":
|
|
145
|
+
return "lease-protected (exact expected-ref update)";
|
|
146
|
+
case "atomic-conditional":
|
|
147
|
+
return "atomic-conditional (verified atomic precondition)";
|
|
148
|
+
case "conditional-required":
|
|
149
|
+
return "conditional-required (read-only until atomic preconditions are verified)";
|
|
150
|
+
case "read-check-write-verify":
|
|
151
|
+
return "read-check-write-verify (visible races rejected; simultaneous unseen races remain possible)";
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
149
155
|
export function safeTerminalText(value: string) {
|
|
150
156
|
// biome-ignore lint/suspicious/noControlCharactersInRegex: Escape untrusted terminal controls.
|
|
151
157
|
return value.replace(/[\u0000-\u001f\u007f-\u009f]/gu, "?");
|
|
@@ -155,14 +161,6 @@ export function redact(value: string) {
|
|
|
155
161
|
return value.length <= 8 ? "configured" : `${value.slice(0, 4)}…${value.slice(-4)}`;
|
|
156
162
|
}
|
|
157
163
|
|
|
158
|
-
export function remoteIdentity(remote: RemoteObject<LatestPointer>) {
|
|
159
|
-
return remote.missing ? "missing" : (remote.value?.snapshot ?? "unknown");
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
export function sha256(value: Buffer) {
|
|
163
|
-
return createHash("sha256").update(value).digest("hex");
|
|
164
|
-
}
|
|
165
|
-
|
|
166
164
|
export function errorMessage(error: unknown) {
|
|
167
165
|
return safeTerminalText(error instanceof Error ? error.message : String(error));
|
|
168
166
|
}
|