@floomhq/signaldash 0.13.0 → 0.22.0

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.
@@ -0,0 +1,114 @@
1
+ import {
2
+ chmodSync,
3
+ closeSync,
4
+ fsyncSync,
5
+ mkdirSync,
6
+ openSync,
7
+ readFileSync,
8
+ renameSync,
9
+ unlinkSync,
10
+ writeFileSync,
11
+ } from "node:fs";
12
+ import path from "node:path";
13
+ import { randomBytes } from "node:crypto";
14
+
15
+
16
+ const WAIT_ARRAY = new Int32Array(new SharedArrayBuffer(4));
17
+ const LOCK_TIMEOUT_MS = 5_000;
18
+
19
+
20
+ function wait(milliseconds) {
21
+ Atomics.wait(WAIT_ARRAY, 0, 0, milliseconds);
22
+ }
23
+
24
+
25
+ function acquireLock(lockFile) {
26
+ const deadline = Date.now() + LOCK_TIMEOUT_MS;
27
+ for (;;) {
28
+ try {
29
+ return openSync(lockFile, "wx", 0o600);
30
+ } catch (error) {
31
+ if (error.code !== "EEXIST") throw error;
32
+ if (Date.now() >= deadline) {
33
+ throw new Error(
34
+ `timed out waiting for SignalDash config lock: ${lockFile}; ` +
35
+ "the lock is never stolen automatically because that could corrupt the session",
36
+ );
37
+ }
38
+ wait(25);
39
+ }
40
+ }
41
+ }
42
+
43
+
44
+ function releaseLock(lockFile, descriptor) {
45
+ try {
46
+ closeSync(descriptor);
47
+ } finally {
48
+ try {
49
+ unlinkSync(lockFile);
50
+ } catch (error) {
51
+ if (error.code !== "ENOENT") throw error;
52
+ }
53
+ }
54
+ }
55
+
56
+
57
+ export function readConfigFile(file, { optional = false } = {}) {
58
+ try {
59
+ return JSON.parse(readFileSync(file, "utf8"));
60
+ } catch (error) {
61
+ if (error.code === "ENOENT" && optional) return {};
62
+ throw error;
63
+ }
64
+ }
65
+
66
+
67
+ export function updateConfigFile(file, update) {
68
+ const directory = path.dirname(file);
69
+ mkdirSync(directory, { recursive: true, mode: 0o700 });
70
+ chmodSync(directory, 0o700);
71
+ const lockFile = `${file}.lock`;
72
+ const descriptor = acquireLock(lockFile);
73
+ let temporary;
74
+ try {
75
+ const current = readConfigFile(file, { optional: true });
76
+ const next = update({ ...current });
77
+ if (!next || typeof next !== "object" || Array.isArray(next)) {
78
+ throw new TypeError("SignalDash config update must return an object");
79
+ }
80
+ temporary = `${file}.${process.pid}.${randomBytes(6).toString("hex")}.tmp`;
81
+ writeFileSync(temporary, `${JSON.stringify(next, null, 2)}\n`, {
82
+ mode: 0o600,
83
+ flag: "wx",
84
+ });
85
+ const temporaryDescriptor = openSync(temporary, "r");
86
+ try {
87
+ fsyncSync(temporaryDescriptor);
88
+ } finally {
89
+ closeSync(temporaryDescriptor);
90
+ }
91
+ renameSync(temporary, file);
92
+ temporary = undefined;
93
+ chmodSync(file, 0o600);
94
+ const directoryDescriptor = openSync(directory, "r");
95
+ try {
96
+ fsyncSync(directoryDescriptor);
97
+ } finally {
98
+ closeSync(directoryDescriptor);
99
+ }
100
+ return next;
101
+ } finally {
102
+ try {
103
+ if (temporary) {
104
+ try {
105
+ unlinkSync(temporary);
106
+ } catch (error) {
107
+ if (error.code !== "ENOENT") throw error;
108
+ }
109
+ }
110
+ } finally {
111
+ releaseLock(lockFile, descriptor);
112
+ }
113
+ }
114
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@floomhq/signaldash",
3
- "version": "0.13.0",
3
+ "version": "0.22.0",
4
4
  "description": "Secure LinkedIn, WhatsApp, and email access for AI agents",
5
5
  "type": "module",
6
6
  "bin": {
@@ -18,7 +18,7 @@
18
18
  },
19
19
  "scripts": {
20
20
  "test": "node --test",
21
- "check": "node --check bin/sd.mjs && node --check server/server.cjs && node --check bin/signaldash.js && node --check lib/cli.js && node --check lib/mcp.js && node --check lib/rate-guard.js && node --check lib/secrets.js && node --check lib/unipile.js"
21
+ "check": "node --check bin/sd.mjs && node --check server/server.cjs && node --check server/write-control.cjs && node --check bin/signaldash.js && node --check lib/cli.js && node --check lib/config-file.js && node --check lib/mcp.js && node --check lib/rate-guard.js && node --check lib/secrets.js && node --check lib/unipile.js"
22
22
  },
23
23
  "keywords": [
24
24
  "mcp",