@relayfile/sdk 0.10.56 → 0.10.57
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/dist/mount-launcher.js +78 -7
- package/dist/sync.js +1 -1
- package/dist/types.d.ts +1 -1
- package/package.json +6 -6
package/dist/mount-launcher.js
CHANGED
|
@@ -14,6 +14,9 @@ const LOG_ROTATION_FILES = 3;
|
|
|
14
14
|
const DEFAULT_CHECKPOINT_TIMEOUT_MS = 30_000;
|
|
15
15
|
const MAX_CHECKPOINT_OUTPUT_BYTES = 1024 * 1024;
|
|
16
16
|
const FUSE_UNAVAILABLE_SIGNATURE = "fuse mode is not available in this build";
|
|
17
|
+
// relayfile-mount uses EX_TEMPFAIL only for the typed, resumable --once
|
|
18
|
+
// bootstrap outcome. All other nonzero exits remain fatal.
|
|
19
|
+
const INITIAL_BOOTSTRAP_INCOMPLETE_EXIT_CODE = 75;
|
|
17
20
|
export const defaultMountLauncher = createDefaultMountLauncher();
|
|
18
21
|
export function createDefaultMountLauncher(options = {}) {
|
|
19
22
|
return {
|
|
@@ -92,7 +95,6 @@ async function startRelayfileMount(input, options) {
|
|
|
92
95
|
});
|
|
93
96
|
}
|
|
94
97
|
class RelayfileMountProcessInstance {
|
|
95
|
-
pid;
|
|
96
98
|
ready;
|
|
97
99
|
child;
|
|
98
100
|
logStream;
|
|
@@ -107,6 +109,7 @@ class RelayfileMountProcessInstance {
|
|
|
107
109
|
cwd;
|
|
108
110
|
spawnImpl;
|
|
109
111
|
exited = false;
|
|
112
|
+
exitCode = null;
|
|
110
113
|
stopping;
|
|
111
114
|
readyResolved = false;
|
|
112
115
|
checkpointPromise;
|
|
@@ -124,14 +127,14 @@ class RelayfileMountProcessInstance {
|
|
|
124
127
|
this.effectiveEnv = input.effectiveEnv;
|
|
125
128
|
this.cwd = input.cwd;
|
|
126
129
|
this.spawnImpl = input.spawnImpl;
|
|
127
|
-
this.pid = input.child.pid ?? undefined;
|
|
128
130
|
this.now = input.now;
|
|
129
131
|
this.readyPollIntervalMs = input.readyPollIntervalMs;
|
|
130
|
-
this.child
|
|
131
|
-
this.exited = true;
|
|
132
|
-
});
|
|
132
|
+
this.attachExitListener(input.child);
|
|
133
133
|
this.ready = this.waitForReady();
|
|
134
134
|
}
|
|
135
|
+
get pid() {
|
|
136
|
+
return this.child.pid ?? undefined;
|
|
137
|
+
}
|
|
135
138
|
async status() {
|
|
136
139
|
const status = await readMountedWorkspaceStatus({
|
|
137
140
|
localDir: this.localDir,
|
|
@@ -146,7 +149,13 @@ class RelayfileMountProcessInstance {
|
|
|
146
149
|
suggestedRefreshAt: null,
|
|
147
150
|
pid: this.pid
|
|
148
151
|
});
|
|
149
|
-
|
|
152
|
+
// A foreground --once child is expected to exit after publishing its
|
|
153
|
+
// final state. Preserve that ready state; daemon exits still make an
|
|
154
|
+
// otherwise-stale state unready.
|
|
155
|
+
return this.exited &&
|
|
156
|
+
(this.input.background !== false || this.exitCode !== 0)
|
|
157
|
+
? { ...status, ready: false }
|
|
158
|
+
: status;
|
|
150
159
|
}
|
|
151
160
|
async stop() {
|
|
152
161
|
if (!this.stopping) {
|
|
@@ -203,14 +212,43 @@ class RelayfileMountProcessInstance {
|
|
|
203
212
|
throw new CloudAbortError("mountWorkspace");
|
|
204
213
|
}
|
|
205
214
|
const status = await this.status();
|
|
206
|
-
|
|
215
|
+
// Foreground --once is authoritative only after the child exits 0. A
|
|
216
|
+
// prior run may have left a fresh-looking public state file behind, so
|
|
217
|
+
// accepting it while this attempt is still running (or after exit 75)
|
|
218
|
+
// would skip the resumable checkpoint retry contract.
|
|
219
|
+
if (!this.stopping &&
|
|
220
|
+
status.ready &&
|
|
221
|
+
(this.input.background !== false || (this.exited && this.exitCode === 0))) {
|
|
207
222
|
this.readyResolved = true;
|
|
208
223
|
return;
|
|
209
224
|
}
|
|
210
225
|
if (this.isFuseUnavailable()) {
|
|
211
226
|
throw new MountModeUnavailableError("fuse");
|
|
212
227
|
}
|
|
228
|
+
// A typed foreground yield is retryable only inside the caller's
|
|
229
|
+
// readiness budget. Once that budget is exhausted, report the public
|
|
230
|
+
// timeout contract and run normal shutdown cleanup instead of falling
|
|
231
|
+
// through to the generic early-exit error.
|
|
232
|
+
if (!this.stopping &&
|
|
233
|
+
this.isResumableOnceExit() &&
|
|
234
|
+
this.now() >= timeoutAt) {
|
|
235
|
+
const error = new MountReadyTimeoutError(this.localDir, this.input.readyTimeoutMs);
|
|
236
|
+
await this.stop();
|
|
237
|
+
throw error;
|
|
238
|
+
}
|
|
213
239
|
if (this.exited) {
|
|
240
|
+
if (this.isResumableOnceExit() &&
|
|
241
|
+
!this.stopping &&
|
|
242
|
+
this.now() < timeoutAt) {
|
|
243
|
+
await delay(this.readyPollIntervalMs);
|
|
244
|
+
if (this.stopping ||
|
|
245
|
+
this.input.signal?.aborted ||
|
|
246
|
+
this.now() >= timeoutAt) {
|
|
247
|
+
continue;
|
|
248
|
+
}
|
|
249
|
+
await this.restartOnceMount();
|
|
250
|
+
continue;
|
|
251
|
+
}
|
|
214
252
|
throw this.buildEarlyExitError();
|
|
215
253
|
}
|
|
216
254
|
if (this.now() >= timeoutAt) {
|
|
@@ -231,6 +269,39 @@ class RelayfileMountProcessInstance {
|
|
|
231
269
|
return (normalizeMountMode(this.input.env.RELAYFILE_MOUNT_MODE) === "fuse" &&
|
|
232
270
|
this.outputBuffer.join("").includes(FUSE_UNAVAILABLE_SIGNATURE));
|
|
233
271
|
}
|
|
272
|
+
isResumableOnceExit() {
|
|
273
|
+
return (this.input.background === false &&
|
|
274
|
+
this.exitCode === INITIAL_BOOTSTRAP_INCOMPLETE_EXIT_CODE);
|
|
275
|
+
}
|
|
276
|
+
async restartOnceMount() {
|
|
277
|
+
const child = this.spawnImpl(this.command, ["--once"], {
|
|
278
|
+
cwd: this.cwd,
|
|
279
|
+
env: this.effectiveEnv,
|
|
280
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
281
|
+
});
|
|
282
|
+
pipeChildOutput(child, this.logStream, this.outputBuffer, this.input);
|
|
283
|
+
this.child = child;
|
|
284
|
+
this.exited = child.exitCode !== null;
|
|
285
|
+
this.exitCode = child.exitCode;
|
|
286
|
+
this.attachExitListener(child);
|
|
287
|
+
if (typeof child.pid === "number" && child.pid > 0) {
|
|
288
|
+
await writeAtomicFile(this.pidPath, `${child.pid}\n`);
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
attachExitListener(child) {
|
|
292
|
+
if (child.exitCode !== null) {
|
|
293
|
+
this.exited = true;
|
|
294
|
+
this.exitCode = child.exitCode;
|
|
295
|
+
}
|
|
296
|
+
child.once("exit", (code) => {
|
|
297
|
+
// A late event from a previous resumable attempt must not mark its
|
|
298
|
+
// replacement as exited.
|
|
299
|
+
if (this.child !== child)
|
|
300
|
+
return;
|
|
301
|
+
this.exited = true;
|
|
302
|
+
this.exitCode = code;
|
|
303
|
+
});
|
|
304
|
+
}
|
|
234
305
|
async performStop() {
|
|
235
306
|
if (!this.exited && typeof this.child.pid === "number") {
|
|
236
307
|
this.child.kill("SIGTERM");
|
package/dist/sync.js
CHANGED
|
@@ -743,7 +743,7 @@ export class RelayFileSync {
|
|
|
743
743
|
this.emitFilesystemEvent(normalized);
|
|
744
744
|
}
|
|
745
745
|
emitFilesystemEvent(event) {
|
|
746
|
-
if (!pathMatchesAnyFilter(this.paths, event.path)) {
|
|
746
|
+
if (event.type !== "sync.reconcile" && !pathMatchesAnyFilter(this.paths, event.path)) {
|
|
747
747
|
return;
|
|
748
748
|
}
|
|
749
749
|
this.emit("event", event);
|
package/dist/types.d.ts
CHANGED
|
@@ -284,7 +284,7 @@ export interface WriteQueuedResponse {
|
|
|
284
284
|
state?: WritebackState;
|
|
285
285
|
};
|
|
286
286
|
}
|
|
287
|
-
export type FilesystemEventType = "file.created" | "file.updated" | "file.deleted" | "dir.created" | "dir.deleted" | "sync.error" | "sync.ignored" | "sync.suppressed" | "sync.stale" | "writeback.failed" | "writeback.succeeded";
|
|
287
|
+
export type FilesystemEventType = "file.created" | "file.updated" | "file.deleted" | "dir.created" | "dir.deleted" | "sync.error" | "sync.ignored" | "sync.suppressed" | "sync.stale" | "sync.reconcile" | "writeback.failed" | "writeback.succeeded";
|
|
288
288
|
export type EventOrigin = "provider_sync" | "agent_write" | "system";
|
|
289
289
|
export interface FilesystemEvent {
|
|
290
290
|
eventId: string;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@relayfile/sdk",
|
|
3
|
-
"version": "0.10.
|
|
3
|
+
"version": "0.10.57",
|
|
4
4
|
"description": "TypeScript SDK for relayfile — real-time filesystem for humans and agents",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -59,15 +59,15 @@
|
|
|
59
59
|
"prepublishOnly": "npm run build"
|
|
60
60
|
},
|
|
61
61
|
"dependencies": {
|
|
62
|
-
"@relayfile/core": "0.10.
|
|
62
|
+
"@relayfile/core": "0.10.57",
|
|
63
63
|
"ignore": "^7.0.5",
|
|
64
64
|
"tar": "^7.5.10"
|
|
65
65
|
},
|
|
66
66
|
"optionalDependencies": {
|
|
67
|
-
"@relayfile/mount-darwin-arm64": "0.10.
|
|
68
|
-
"@relayfile/mount-darwin-x64": "0.10.
|
|
69
|
-
"@relayfile/mount-linux-arm64": "0.10.
|
|
70
|
-
"@relayfile/mount-linux-x64": "0.10.
|
|
67
|
+
"@relayfile/mount-darwin-arm64": "0.10.57",
|
|
68
|
+
"@relayfile/mount-darwin-x64": "0.10.57",
|
|
69
|
+
"@relayfile/mount-linux-arm64": "0.10.57",
|
|
70
|
+
"@relayfile/mount-linux-x64": "0.10.57"
|
|
71
71
|
},
|
|
72
72
|
"devDependencies": {
|
|
73
73
|
"typescript": "^5.7.3",
|