my-durable 0.0.5 → 0.0.7

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.
@@ -11,6 +11,7 @@ import { chmodSync, closeSync, fsyncSync, openSync, readFileSync, readdirSync, r
11
11
  import { dirname, join } from "node:path";
12
12
  import { randomBytes } from "node:crypto";
13
13
  import { ensurePrivateDir } from "../../dpi-sdk/src/node/private-dir.js";
14
+ import { fsyncReadOnlySupported } from "../../dpi-sdk/src/node/registry-migration.js";
14
15
 
15
16
  export { ensurePrivateDir };
16
17
 
@@ -32,12 +33,15 @@ export function replaceFileAtomic(path: string, contents: string): void {
32
33
  closeSync(fd);
33
34
  fd = undefined;
34
35
  renameSync(temp, path);
35
- // fsync the directory so the rename itself is durable.
36
- const dirFd = openSync(dir, "r");
37
- try {
38
- fsyncSync(dirFd);
39
- } finally {
40
- closeSync(dirFd);
36
+ // fsync the directory so the rename itself is durable (POSIX only: Windows
37
+ // directory fds cannot be fsynced — EPERM; NTFS journaling carries it there).
38
+ if (fsyncReadOnlySupported) {
39
+ const dirFd = openSync(dir, "r");
40
+ try {
41
+ fsyncSync(dirFd);
42
+ } finally {
43
+ closeSync(dirFd);
44
+ }
41
45
  }
42
46
  } catch (e) {
43
47
  if (fd !== undefined) {
@@ -388,7 +388,15 @@ function preflightV0(db: DatabaseSync): void {
388
388
  }
389
389
  }
390
390
 
391
+ /**
392
+ * Windows cannot fsync read-only or directory handles (FlushFileBuffers needs a write
393
+ * handle; directory handles cannot be flushed at all — EPERM, errno -4048). POSIX-only
394
+ * durability steps are skipped there: NTFS journaling plus the atomic rename carry it.
395
+ */
396
+ export const fsyncReadOnlySupported = process.platform !== "win32";
397
+
391
398
  function syncFile(file: string): void {
399
+ if (!fsyncReadOnlySupported) return;
392
400
  const fd = openSync(file, "r");
393
401
  try {
394
402
  fsyncSync(fd);
@@ -405,6 +413,7 @@ function syncFile(file: string): void {
405
413
  * Exported for a focused unit test — the fsync itself is not observable from JS.
406
414
  */
407
415
  export function syncDirectory(dir: string): void {
416
+ if (!fsyncReadOnlySupported) return; // Windows: directory fds cannot be fsynced (EPERM)
408
417
  const fd = openSync(dir, "r");
409
418
  try {
410
419
  if (!fstatSync(fd).isDirectory()) throw new Error(`not a directory: ${dir}`);
@@ -147,7 +147,27 @@ if (startup.kind === "welcome") {
147
147
  await start(device, client, info);
148
148
  } else {
149
149
  const known = registry.list().map((d) => d.id);
150
- if (!deviceRef) fail(`${USAGE}\n\nKnown devices: ${known.join(", ") || "none"}`);
150
+ if (!deviceRef && (values.pair !== undefined || values.hub !== undefined)) {
151
+ // Fresh-install flow: an explicit --pair (a pasted code, pair URL or offer link IS
152
+ // the whole configuration) must never die on usage text — pair this machine's LOCAL
153
+ // backend first, then the app opens on it. Mirrors the welcome screen's [p] action.
154
+ const { applyBackendEnv } = await import("./welcome.ts");
155
+ applyBackendEnv();
156
+ const { LocalPairing } = await import("./pairing.ts");
157
+ try {
158
+ LocalPairing.fromEnv();
159
+ } catch (e) {
160
+ fail((e as Error).message);
161
+ }
162
+ const port = Number(process.env.DPI_PORT ?? "4780");
163
+ const url = `http://127.0.0.1:${port}`;
164
+ const device: DeviceRef = { id: "local", name: "This machine", url, token: process.env.DPI_TOKEN ?? "" };
165
+ const client = new DpiClient(url, device.token);
166
+ const info = await client.device({ timeoutMs: 8000 }).catch((e: unknown) =>
167
+ fail(`no local dpi-backend on ${url} — start one with: dpi device (${(e as Error).message})`),
168
+ );
169
+ await start(device, client, info);
170
+ } else if (!deviceRef) fail(`${USAGE}\n\nKnown devices: ${known.join(", ") || "none"}`);
151
171
  const row = registry.get(deviceRef);
152
172
  if (!row) fail(`Unknown device "${deviceRef}". Known: ${known.join(", ") || "none (add one to dpi-network.yaml)"}`);
153
173
  if (!row.token) fail(`No token for ${row.id}. Run: just net register ${row.id}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "my-durable",
3
- "version": "0.0.5",
3
+ "version": "0.0.7",
4
4
  "description": "Durable Pi — the durable agent network: hub web UI, device backend daemon, TUI admin and the shared SDK, packaged as one runnable monorepo install",
5
5
  "license": "MIT",
6
6
  "repository": {