@narumitw/pi-sync 0.31.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.
@@ -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 { LatestPointer, RemoteObject, Snapshot, SyncConfig } from "./types.js";
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: SyncConfig,
39
+ config: CommonSyncConfig,
40
+ destination: string,
40
41
  upload: Snapshot,
41
- latest: RemoteObject<LatestPointer>,
42
+ head: RemoteHead | undefined,
42
43
  preservedRemoteFileCount = 0,
43
44
  remote?: Snapshot,
44
45
  ) {
45
46
  return [
46
- `Target: ${safeTerminalText(config.target ?? "default")}`,
47
- `Destination: ${formatDestination(config)}`,
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
- latest.value ? `Remote latest: ${latest.value.snapshot}` : "Remote latest: empty",
51
- "Publication effect: latest.json will point to the new immutable snapshot.",
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: SyncConfig,
69
+ config: CommonSyncConfig,
70
+ destination: string,
69
71
  local: Snapshot,
70
72
  remote: Snapshot,
71
73
  protectedSessionCount: number,
72
74
  ) {
73
75
  return [
74
- `Target: ${safeTerminalText(config.target ?? "default")}`,
75
- `Destination: ${formatDestination(config)}`,
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: SyncConfig,
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
- `Target: ${safeTerminalText(config.target ?? "default")}`,
93
- `Destination: ${formatDestination(config)}`,
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 remote latest pointer will change.",
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
  }