This commit is contained in:
Team3
2026-10-07 01:08:18 +02:00
parent f3ef3861a9
commit 59f39729fc

View File

@@ -28,6 +28,8 @@ const SOON_MS = 500;
// "files" in sync.json: false schaltet ab, ein Pfad ersetzt ~/.claude/projects.
const FILES_FIRST_SECONDS = 10;
const FILES_BATCH_BYTES = 16 * 1024 * 1024;
// So lange über das Soup-Timeout hinaus warten wir auf eine Antwort, siehe request()
const REQUEST_GRACE_SECONDS = 10;
// Von außen anstoßbar: make sync-files ruft diese Methode über D-Bus auf
const DBUS_IFACE = `
@@ -201,18 +203,7 @@ export class Sync {
const message = Soup.Message.new('POST', `${this._config.url}/api/sync`);
message.request_headers.append('Authorization', `Bearer ${this._config.token}`);
message.set_request_body_from_bytes('application/json', new GLib.Bytes(new TextEncoder().encode(JSON.stringify(payload))));
return new Promise((resolve, reject) => {
session.send_and_read_async(message, GLib.PRIORITY_DEFAULT, null, (session, result) => {
try {
const bytes = session.send_and_read_finish(result);
if (message.get_status() !== Soup.Status.OK)
throw new Error(`HTTP ${message.get_status()} ${message.get_reason_phrase()}`);
resolve(JSON.parse(new TextDecoder().decode(bytes.get_data())));
} catch (e) {
reject(e);
}
});
});
return request(session, message, bytes => JSON.parse(new TextDecoder().decode(bytes.get_data())));
}
// Antwort einarbeiten. Was der Server zurückschickt und wir gerade hochgeladen haben, kennt er jetzt.
@@ -375,18 +366,10 @@ export class Sync {
const path = id.split('/').map(encodeURIComponent).join('/');
const message = Soup.Message.new('GET', `${this._config.url}/api/files/${path}?from=${from}&hash=${hash}`);
message.request_headers.append('Authorization', `Bearer ${this._config.token}`);
return new Promise((resolve, reject) => {
this._filesSession.send_and_read_async(message, GLib.PRIORITY_DEFAULT, null, (session, result) => {
try {
const bytes = session.send_and_read_finish(result);
if (message.get_status() !== Soup.Status.OK)
throw new Error(`HTTP ${message.get_status()} ${message.get_reason_phrase()}`);
resolve({bytes: bytes.get_data() ?? new Uint8Array(0), from: Number(message.get_response_headers().get_one('X-From'))});
} catch (e) {
reject(e);
}
});
});
return request(this._filesSession, message, bytes => ({
bytes: bytes.get_data() ?? new Uint8Array(0),
from: Number(message.get_response_headers().get_one('X-From')),
}));
}
_fail(message) {
@@ -423,6 +406,33 @@ function scanFiles(root) {
return local;
}
// Anfrage schicken und die Antwort lesen. GJS verwirft den Soup-Callback gelegentlich, wenn er
// während der Garbage Collection fällig wird. Dann käme nie eine Antwort, der Lauf bliebe für immer
// "beschäftigt" und der Takt stünde still. Deshalb eine eigene Frist über dem Soup-Timeout:
// danach gilt die Anfrage als gescheitert, der nächste Lauf kommt wie geplant.
function request(session, message, read) {
return new Promise((resolve, reject) => {
let watchdog = GLib.timeout_add_seconds(GLib.PRIORITY_DEFAULT, session.timeout + REQUEST_GRACE_SECONDS, () => {
watchdog = null;
reject(new Error('keine Antwort, Anfrage verworfen'));
return GLib.SOURCE_REMOVE;
});
session.send_and_read_async(message, GLib.PRIORITY_DEFAULT, null, (_session, result) => {
if (watchdog)
GLib.source_remove(watchdog);
watchdog = null;
try {
const bytes = session.send_and_read_finish(result);
if (message.get_status() !== Soup.Status.OK)
throw new Error(`HTTP ${message.get_status()} ${message.get_reason_phrase()}`);
resolve(read(bytes));
} catch (e) {
reject(e);
}
});
});
}
// Datei als Bytes lesen. null, wenn sie fehlt.
function readBytes(path) {
try {
@@ -434,6 +444,8 @@ function readBytes(path) {
}
function appendBytes(path, bytes) {
if (!bytes.length)
return;
const stream = Gio.File.new_for_path(path).append_to(Gio.FileCreateFlags.NONE, null);
stream.write_bytes(new GLib.Bytes(bytes), null);
stream.close(null);