@wrongstack/governance 1.0.1 → 1.0.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/dist/index.js +29 -36
- package/dist/project-daemon.js +29 -36
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -511,6 +511,7 @@ import * as path3 from "node:path";
|
|
|
511
511
|
import * as fs from "node:fs/promises";
|
|
512
512
|
import * as net from "node:net";
|
|
513
513
|
import * as path2 from "node:path";
|
|
514
|
+
import { atomicWrite, restrictFilePermissions } from "@wrongstack/persistence";
|
|
514
515
|
|
|
515
516
|
// src/ipc-endpoint.ts
|
|
516
517
|
import { createHash as createHash3 } from "node:crypto";
|
|
@@ -601,18 +602,30 @@ async function ensurePrivateDirectory(projectRoot) {
|
|
|
601
602
|
if (process.platform !== "win32") await fs.chmod(directory, 448);
|
|
602
603
|
return directory;
|
|
603
604
|
}
|
|
605
|
+
var TRANSIENT_READ_CODES = /* @__PURE__ */ new Set(["EPERM", "EBUSY"]);
|
|
606
|
+
var READ_RETRY_DELAYS_MS = [15, 40, 100, 250];
|
|
604
607
|
async function readBounded(pathname) {
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
608
|
+
let attempt = 0;
|
|
609
|
+
for (; ; ) {
|
|
610
|
+
try {
|
|
611
|
+
const value = await fs.readFile(pathname);
|
|
612
|
+
if (value.byteLength > GOVERNANCE_DAEMON_METADATA_MAX_BYTES) {
|
|
613
|
+
throw new Error(
|
|
614
|
+
`Governance daemon metadata exceeds ${GOVERNANCE_DAEMON_METADATA_MAX_BYTES} bytes.`
|
|
615
|
+
);
|
|
616
|
+
}
|
|
617
|
+
return value.toString("utf8");
|
|
618
|
+
} catch (error) {
|
|
619
|
+
const code = errorCode(error);
|
|
620
|
+
if (code === "ENOENT") return null;
|
|
621
|
+
const delayMs = READ_RETRY_DELAYS_MS[attempt];
|
|
622
|
+
if (process.platform === "win32" && code !== void 0 && TRANSIENT_READ_CODES.has(code) && delayMs !== void 0) {
|
|
623
|
+
await delay(delayMs);
|
|
624
|
+
attempt++;
|
|
625
|
+
continue;
|
|
626
|
+
}
|
|
627
|
+
throw error;
|
|
611
628
|
}
|
|
612
|
-
return value.toString("utf8");
|
|
613
|
-
} catch (error) {
|
|
614
|
-
if (errorCode(error) === "ENOENT") return null;
|
|
615
|
-
throw error;
|
|
616
629
|
}
|
|
617
630
|
}
|
|
618
631
|
function parseMetadata(raw, projectRoot) {
|
|
@@ -747,19 +760,9 @@ async function writeGovernanceDaemonAttachmentBroker(projectRoot, broker) {
|
|
|
747
760
|
const expected = validated.broker;
|
|
748
761
|
await ensurePrivateDirectory(projectRoot);
|
|
749
762
|
const pathname = governanceDaemonAttachmentBrokerPath(projectRoot);
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
encoding: "utf8",
|
|
754
|
-
mode: 384
|
|
755
|
-
});
|
|
756
|
-
if (process.platform !== "win32") await fs.chmod(temporary, 384);
|
|
757
|
-
try {
|
|
758
|
-
await fs.rename(temporary, pathname);
|
|
759
|
-
} catch (error) {
|
|
760
|
-
await fs.rm(temporary, { force: true }).catch(() => void 0);
|
|
761
|
-
throw error;
|
|
762
|
-
}
|
|
763
|
+
await atomicWrite(pathname, `${JSON.stringify(expected, null, 2)}
|
|
764
|
+
`, { mode: 384 });
|
|
765
|
+
await restrictFilePermissions(pathname).catch(() => void 0);
|
|
763
766
|
}
|
|
764
767
|
async function readGovernanceDaemonAttachmentBroker(projectRoot) {
|
|
765
768
|
const raw = await readBounded(governanceDaemonAttachmentBrokerPath(projectRoot));
|
|
@@ -799,19 +802,9 @@ async function writeGovernanceDaemonMetadata(projectRoot, metadata) {
|
|
|
799
802
|
const expected = createGovernanceDaemonMetadata(metadata);
|
|
800
803
|
await ensurePrivateDirectory(projectRoot);
|
|
801
804
|
const pathname = governanceDaemonMetadataPath(projectRoot);
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
encoding: "utf8",
|
|
806
|
-
mode: 384
|
|
807
|
-
});
|
|
808
|
-
try {
|
|
809
|
-
await fs.rename(temporary, pathname);
|
|
810
|
-
} catch {
|
|
811
|
-
await fs.rm(pathname, { force: true });
|
|
812
|
-
await fs.rename(temporary, pathname);
|
|
813
|
-
}
|
|
814
|
-
if (process.platform !== "win32") await fs.chmod(pathname, 384);
|
|
805
|
+
await atomicWrite(pathname, `${JSON.stringify(expected, null, 2)}
|
|
806
|
+
`, { mode: 384 });
|
|
807
|
+
await restrictFilePermissions(pathname).catch(() => void 0);
|
|
815
808
|
}
|
|
816
809
|
async function readGovernanceDaemonMetadata(projectRoot) {
|
|
817
810
|
const raw = await readBounded(governanceDaemonMetadataPath(projectRoot));
|
package/dist/project-daemon.js
CHANGED
|
@@ -8,6 +8,7 @@ import { fileURLToPath } from "node:url";
|
|
|
8
8
|
import * as fs from "node:fs/promises";
|
|
9
9
|
import * as net from "node:net";
|
|
10
10
|
import * as path2 from "node:path";
|
|
11
|
+
import { atomicWrite, restrictFilePermissions } from "@wrongstack/persistence";
|
|
11
12
|
|
|
12
13
|
// src/ipc-endpoint.ts
|
|
13
14
|
import { createHash } from "node:crypto";
|
|
@@ -121,18 +122,30 @@ async function ensurePrivateDirectory(projectRoot) {
|
|
|
121
122
|
if (process.platform !== "win32") await fs.chmod(directory, 448);
|
|
122
123
|
return directory;
|
|
123
124
|
}
|
|
125
|
+
var TRANSIENT_READ_CODES = /* @__PURE__ */ new Set(["EPERM", "EBUSY"]);
|
|
126
|
+
var READ_RETRY_DELAYS_MS = [15, 40, 100, 250];
|
|
124
127
|
async function readBounded(pathname) {
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
128
|
+
let attempt = 0;
|
|
129
|
+
for (; ; ) {
|
|
130
|
+
try {
|
|
131
|
+
const value = await fs.readFile(pathname);
|
|
132
|
+
if (value.byteLength > GOVERNANCE_DAEMON_METADATA_MAX_BYTES) {
|
|
133
|
+
throw new Error(
|
|
134
|
+
`Governance daemon metadata exceeds ${GOVERNANCE_DAEMON_METADATA_MAX_BYTES} bytes.`
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
return value.toString("utf8");
|
|
138
|
+
} catch (error) {
|
|
139
|
+
const code = errorCode(error);
|
|
140
|
+
if (code === "ENOENT") return null;
|
|
141
|
+
const delayMs = READ_RETRY_DELAYS_MS[attempt];
|
|
142
|
+
if (process.platform === "win32" && code !== void 0 && TRANSIENT_READ_CODES.has(code) && delayMs !== void 0) {
|
|
143
|
+
await delay(delayMs);
|
|
144
|
+
attempt++;
|
|
145
|
+
continue;
|
|
146
|
+
}
|
|
147
|
+
throw error;
|
|
131
148
|
}
|
|
132
|
-
return value.toString("utf8");
|
|
133
|
-
} catch (error) {
|
|
134
|
-
if (errorCode(error) === "ENOENT") return null;
|
|
135
|
-
throw error;
|
|
136
149
|
}
|
|
137
150
|
}
|
|
138
151
|
function parseMetadata(raw, projectRoot) {
|
|
@@ -267,19 +280,9 @@ async function writeGovernanceDaemonAttachmentBroker(projectRoot, broker) {
|
|
|
267
280
|
const expected = validated.broker;
|
|
268
281
|
await ensurePrivateDirectory(projectRoot);
|
|
269
282
|
const pathname = governanceDaemonAttachmentBrokerPath(projectRoot);
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
encoding: "utf8",
|
|
274
|
-
mode: 384
|
|
275
|
-
});
|
|
276
|
-
if (process.platform !== "win32") await fs.chmod(temporary, 384);
|
|
277
|
-
try {
|
|
278
|
-
await fs.rename(temporary, pathname);
|
|
279
|
-
} catch (error) {
|
|
280
|
-
await fs.rm(temporary, { force: true }).catch(() => void 0);
|
|
281
|
-
throw error;
|
|
282
|
-
}
|
|
283
|
+
await atomicWrite(pathname, `${JSON.stringify(expected, null, 2)}
|
|
284
|
+
`, { mode: 384 });
|
|
285
|
+
await restrictFilePermissions(pathname).catch(() => void 0);
|
|
283
286
|
}
|
|
284
287
|
async function removeOwnedGovernanceDaemonAttachmentBroker(projectRoot, owner) {
|
|
285
288
|
const pathname = governanceDaemonAttachmentBrokerPath(projectRoot);
|
|
@@ -315,19 +318,9 @@ async function writeGovernanceDaemonMetadata(projectRoot, metadata) {
|
|
|
315
318
|
const expected = createGovernanceDaemonMetadata(metadata);
|
|
316
319
|
await ensurePrivateDirectory(projectRoot);
|
|
317
320
|
const pathname = governanceDaemonMetadataPath(projectRoot);
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
encoding: "utf8",
|
|
322
|
-
mode: 384
|
|
323
|
-
});
|
|
324
|
-
try {
|
|
325
|
-
await fs.rename(temporary, pathname);
|
|
326
|
-
} catch {
|
|
327
|
-
await fs.rm(pathname, { force: true });
|
|
328
|
-
await fs.rename(temporary, pathname);
|
|
329
|
-
}
|
|
330
|
-
if (process.platform !== "win32") await fs.chmod(pathname, 384);
|
|
321
|
+
await atomicWrite(pathname, `${JSON.stringify(expected, null, 2)}
|
|
322
|
+
`, { mode: 384 });
|
|
323
|
+
await restrictFilePermissions(pathname).catch(() => void 0);
|
|
331
324
|
}
|
|
332
325
|
async function readGovernanceDaemonMetadata(projectRoot) {
|
|
333
326
|
const raw = await readBounded(governanceDaemonMetadataPath(projectRoot));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wrongstack/governance",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.3",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "Deterministic workflow contracts and transition policy for WrongStack orchestration.",
|
|
6
6
|
"repository": {
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
"README.md"
|
|
28
28
|
],
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"@wrongstack/persistence": "1.0.
|
|
30
|
+
"@wrongstack/persistence": "1.0.3"
|
|
31
31
|
},
|
|
32
32
|
"devDependencies": {
|
|
33
33
|
"@types/node": "^26.2.0",
|