@lisang233/pi-sync 0.1.2 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/extension.ts +60 -13
- package/src/status.ts +3 -3
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lisang233/pi-sync",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "Personal Pi extension that syncs Pi configuration through Git with background auto-sync and git-style fetch/merge conflict handling.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
package/src/extension.ts
CHANGED
|
@@ -3,7 +3,7 @@ import type {
|
|
|
3
3
|
ExtensionCommandContext,
|
|
4
4
|
ExtensionContext,
|
|
5
5
|
} from "@earendil-works/pi-coding-agent";
|
|
6
|
-
import { loadConfig } from "./config.js";
|
|
6
|
+
import { loadConfig, type SyncConfig } from "./config.js";
|
|
7
7
|
import { runConfigEditor } from "./config-ui.js";
|
|
8
8
|
import * as operations from "./operations.js";
|
|
9
9
|
import { syncBusyText } from "./status.js";
|
|
@@ -38,6 +38,7 @@ const USAGE = [
|
|
|
38
38
|
export default function sync(pi: ExtensionAPI): void {
|
|
39
39
|
let sessionAbort = new AbortController();
|
|
40
40
|
let backgroundSync: BackgroundSync | undefined;
|
|
41
|
+
let backgroundPush: BackgroundSync | undefined;
|
|
41
42
|
|
|
42
43
|
const startBackgroundSync = (ctx: ExtensionContext, signal: AbortSignal) => {
|
|
43
44
|
// While the automatic fetch is in flight the indicator shows the busy
|
|
@@ -55,15 +56,42 @@ export default function sync(pi: ExtensionAPI): void {
|
|
|
55
56
|
backgroundSync = { settled };
|
|
56
57
|
};
|
|
57
58
|
|
|
58
|
-
|
|
59
|
-
|
|
59
|
+
// Publish in the background so the TUI stays interactive while git talks
|
|
60
|
+
// to the remote (fetch + push can take many seconds). The indicator shows
|
|
61
|
+
// "pushing…"; operations.push notifies the outcome when it settles.
|
|
62
|
+
const startBackgroundPush = (
|
|
63
|
+
ctx: ExtensionCommandContext,
|
|
64
|
+
config: SyncConfig,
|
|
65
|
+
force: boolean,
|
|
66
|
+
signal: AbortSignal,
|
|
67
|
+
) => {
|
|
68
|
+
ctx.ui.setStatus(STATUS_KEY, syncBusyText("push"));
|
|
69
|
+
const settled = (async () => {
|
|
70
|
+
try {
|
|
71
|
+
await operations.push(ctx, config, { force });
|
|
72
|
+
} catch (error) {
|
|
73
|
+
if (signal.aborted) return;
|
|
74
|
+
ctx.ui.setStatus(STATUS_KEY, undefined);
|
|
75
|
+
ctx.ui.notify(`pi-sync push failed: ${errorMessage(error)}`, "error");
|
|
76
|
+
}
|
|
77
|
+
})();
|
|
78
|
+
backgroundPush = { settled };
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
const drainBackgroundTasks = async (signal?: AbortSignal): Promise<void> => {
|
|
82
|
+
const tasks = [backgroundSync, backgroundPush];
|
|
60
83
|
backgroundSync = undefined;
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
84
|
+
backgroundPush = undefined;
|
|
85
|
+
for (const current of tasks) {
|
|
86
|
+
if (!current) continue;
|
|
87
|
+
try {
|
|
88
|
+
await (signal
|
|
89
|
+
? Promise.race([current.settled, waitForAbort(signal)])
|
|
90
|
+
: current.settled);
|
|
91
|
+
} catch {
|
|
92
|
+
// The shutdown deadline or a replacement aborted while draining; the
|
|
93
|
+
// background task observes its own session signal and settles on its own.
|
|
94
|
+
}
|
|
67
95
|
}
|
|
68
96
|
};
|
|
69
97
|
|
|
@@ -84,7 +112,18 @@ export default function sync(pi: ExtensionAPI): void {
|
|
|
84
112
|
);
|
|
85
113
|
}
|
|
86
114
|
try {
|
|
87
|
-
await handleCommand(args, ctx)
|
|
115
|
+
await handleCommand(args, ctx, async (pushCtx, config, force) => {
|
|
116
|
+
if (backgroundPush) {
|
|
117
|
+
pushCtx.ui.notify("A push is already in progress.", "warning");
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
// Wait for the session-start automatic fetch so the two
|
|
121
|
+
// background tasks never contend on the mirror repo, then run
|
|
122
|
+
// the publish without blocking the TUI.
|
|
123
|
+
await drainBackgroundTasks();
|
|
124
|
+
if (sessionAbort.signal.aborted) return;
|
|
125
|
+
startBackgroundPush(pushCtx, config, force, sessionAbort.signal);
|
|
126
|
+
});
|
|
88
127
|
} catch (error) {
|
|
89
128
|
if (sessionAbort.signal.aborted) return;
|
|
90
129
|
ctx.ui.setStatus(STATUS_KEY, undefined);
|
|
@@ -98,7 +137,7 @@ export default function sync(pi: ExtensionAPI): void {
|
|
|
98
137
|
sessionAbort = new AbortController();
|
|
99
138
|
const signal = sessionAbort.signal;
|
|
100
139
|
ctx.ui.setStatus(STATUS_KEY, undefined);
|
|
101
|
-
await
|
|
140
|
+
await drainBackgroundTasks();
|
|
102
141
|
try {
|
|
103
142
|
const config = await loadConfig();
|
|
104
143
|
if (signal.aborted) return;
|
|
@@ -126,7 +165,15 @@ async function runAutomaticSync(ctx: ExtensionContext, signal: AbortSignal): Pro
|
|
|
126
165
|
await operations.fetch(ctx, config, { quiet: true });
|
|
127
166
|
}
|
|
128
167
|
|
|
129
|
-
async function handleCommand(
|
|
168
|
+
async function handleCommand(
|
|
169
|
+
rawArgs: string,
|
|
170
|
+
ctx: ExtensionCommandContext,
|
|
171
|
+
runPush: (
|
|
172
|
+
ctx: ExtensionCommandContext,
|
|
173
|
+
config: SyncConfig,
|
|
174
|
+
force: boolean,
|
|
175
|
+
) => Promise<void>,
|
|
176
|
+
): Promise<void> {
|
|
130
177
|
const [first = "", ...restTokens] = rawArgs.trim().split(/\s+/u);
|
|
131
178
|
const subcommand = normalizeSubcommand(first);
|
|
132
179
|
if (subcommand === undefined || subcommand === "help") {
|
|
@@ -159,7 +206,7 @@ async function handleCommand(rawArgs: string, ctx: ExtensionCommandContext): Pro
|
|
|
159
206
|
});
|
|
160
207
|
return;
|
|
161
208
|
case "push":
|
|
162
|
-
await
|
|
209
|
+
await runPush(ctx, config, force);
|
|
163
210
|
return;
|
|
164
211
|
case "pull":
|
|
165
212
|
await operations.pull(ctx, config, {
|
package/src/status.ts
CHANGED
|
@@ -49,9 +49,9 @@ export function deriveSyncStatus(
|
|
|
49
49
|
return { label: "up-to-date", ahead: 0, behind: 0, conflicts: 0 };
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
-
/**
|
|
53
|
-
export function syncBusyText(): string {
|
|
54
|
-
return "sync: fetching…";
|
|
52
|
+
/** Status-bar text while a background/foreground sync action is in flight. */
|
|
53
|
+
export function syncBusyText(action: "fetch" | "push" = "fetch"): string {
|
|
54
|
+
return action === "push" ? "sync: pushing…" : "sync: fetching…";
|
|
55
55
|
}
|
|
56
56
|
|
|
57
57
|
/** Short status-bar text for the sync indicator. */
|