@moxt-ai/mobius 0.0.422 → 0.0.424
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/{chunk-LYXXD335.js → chunk-PWNESRGO.js} +2 -2
- package/dist/{chunk-LYXXD335.js.map → chunk-PWNESRGO.js.map} +1 -1
- package/dist/{chunk-KZSWPDB2.js → chunk-RSYDC4H6.js} +3 -2
- package/dist/{cli-AT2J5IM3.js → cli-RWGALMSD.js} +70 -15
- package/dist/cli-RWGALMSD.js.map +7 -0
- package/dist/cli.js +2 -2
- package/dist/engine.js +2 -2
- package/dist/storage/daemon/control-token.d.ts +1 -1
- package/dist/storage/daemon/control-token.d.ts.map +1 -1
- package/dist/storage/daemon/control-token.js +3 -2
- package/dist/storage/daemon/control-token.js.map +1 -1
- package/dist/storage/daemon/daemon-lock.d.ts +2 -5
- package/dist/storage/daemon/daemon-lock.d.ts.map +1 -1
- package/dist/storage/daemon/daemon-lock.js +78 -8
- package/dist/storage/daemon/daemon-lock.js.map +1 -1
- package/package.json +1 -1
- package/dist/cli-AT2J5IM3.js.map +0 -7
- /package/dist/{chunk-KZSWPDB2.js.map → chunk-RSYDC4H6.js.map} +0 -0
package/dist/cli.js
CHANGED
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
__commonJS,
|
|
4
4
|
__toESM,
|
|
5
5
|
package_default
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-PWNESRGO.js";
|
|
7
7
|
|
|
8
8
|
// ../../node_modules/.pnpm/semver@7.8.5/node_modules/semver/internal/constants.js
|
|
9
9
|
var require_constants = __commonJS({
|
|
@@ -2008,7 +2008,7 @@ if (!(0, import_semver.satisfies)(process.versions.node, package_default.engines
|
|
|
2008
2008
|
);
|
|
2009
2009
|
process.exitCode = 1;
|
|
2010
2010
|
} else {
|
|
2011
|
-
const { runCLI } = await import("./cli-
|
|
2011
|
+
const { runCLI } = await import("./cli-RWGALMSD.js");
|
|
2012
2012
|
await runCLI();
|
|
2013
2013
|
}
|
|
2014
2014
|
//# sourceMappingURL=cli.js.map
|
package/dist/engine.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type InstallationDirectory } from "../../engine/storage/installation/installation-files.ts";
|
|
2
2
|
/** The secret local control clients present to the daemon, created on first use with owner-only permissions. */
|
|
3
3
|
export declare function readOrCreateControlToken(stateDirectory: InstallationDirectory): Promise<string>;
|
|
4
4
|
//# sourceMappingURL=control-token.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"control-token.d.ts","sourceRoot":"","sources":["../../../src/storage/daemon/control-token.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"control-token.d.ts","sourceRoot":"","sources":["../../../src/storage/daemon/control-token.ts"],"names":[],"mappings":"AAGA,OAAO,EAEH,KAAK,qBAAqB,EAC7B,MAAM,yDAAyD,CAAC;AAOjE,gHAAgH;AAChH,wBAAsB,wBAAwB,CAAC,cAAc,EAAE,qBAAqB,GAAG,OAAO,CAAC,MAAM,CAAC,CAmBrG"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { randomBytes } from "node:crypto";
|
|
2
|
-
import { readFile
|
|
2
|
+
import { readFile } from "node:fs/promises";
|
|
3
3
|
import { join } from "node:path";
|
|
4
|
+
import { createInstallationFile, } from "../../engine/storage/installation/installation-files.js";
|
|
4
5
|
import { isNodeError } from "../../engine/util/node-error.js";
|
|
5
6
|
function controlTokenPath(stateDirectory) {
|
|
6
7
|
return join(stateDirectory, "manager.token");
|
|
@@ -18,7 +19,7 @@ export async function readOrCreateControlToken(stateDirectory) {
|
|
|
18
19
|
}
|
|
19
20
|
const token = randomBytes(32).toString("base64url");
|
|
20
21
|
try {
|
|
21
|
-
await
|
|
22
|
+
await createInstallationFile(path, `${token}\n`);
|
|
22
23
|
return token;
|
|
23
24
|
}
|
|
24
25
|
catch (cause) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"control-token.js","sourceRoot":"","sources":["../../../src/storage/daemon/control-token.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,QAAQ,EAAE,
|
|
1
|
+
{"version":3,"file":"control-token.js","sourceRoot":"","sources":["../../../src/storage/daemon/control-token.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EACH,sBAAsB,GAEzB,MAAM,yDAAyD,CAAC;AACjE,OAAO,EAAE,WAAW,EAAE,MAAM,iCAAiC,CAAC;AAE9D,SAAS,gBAAgB,CAAC,cAAqC;IAC3D,OAAO,IAAI,CAAC,cAAc,EAAE,eAAe,CAAC,CAAC;AACjD,CAAC;AAED,gHAAgH;AAChH,MAAM,CAAC,KAAK,UAAU,wBAAwB,CAAC,cAAqC;IAChF,MAAM,IAAI,GAAG,gBAAgB,CAAC,cAAc,CAAC,CAAC;IAC9C,IAAI,CAAC;QACD,OAAO,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IACjD,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACb,IAAI,CAAC,WAAW,CAAC,KAAK,EAAE,QAAQ,CAAC,EAAE,CAAC;YAChC,MAAM,KAAK,CAAC;QAChB,CAAC;IACL,CAAC;IACD,MAAM,KAAK,GAAG,WAAW,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;IACpD,IAAI,CAAC;QACD,MAAM,sBAAsB,CAAC,IAAI,EAAE,GAAG,KAAK,IAAI,CAAC,CAAC;QACjD,OAAO,KAAK,CAAC;IACjB,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACb,IAAI,CAAC,WAAW,CAAC,KAAK,EAAE,QAAQ,CAAC,EAAE,CAAC;YAChC,MAAM,KAAK,CAAC;QAChB,CAAC;QACD,OAAO,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IACjD,CAAC;AACL,CAAC"}
|
|
@@ -1,8 +1,5 @@
|
|
|
1
|
-
import type
|
|
2
|
-
/**
|
|
3
|
-
* Takes the lock file that makes this process the only daemon. A lock left by a
|
|
4
|
-
* process that is gone is replaced. Returns the release function, or null when a live process holds it.
|
|
5
|
-
*/
|
|
1
|
+
import { type InstallationDirectory } from "../../engine/storage/installation/installation-files.ts";
|
|
2
|
+
/** Makes this process the only daemon. Returns the release function, or null when another daemon holds the lock. */
|
|
6
3
|
export declare function acquireDaemonLock(stateDirectory: InstallationDirectory, filename?: "manager.lock" | "worker.lock"): Promise<(() => Promise<void>) | null>;
|
|
7
4
|
/** Keep the released lock filename; a dead owner does not keep the daemon alive. */
|
|
8
5
|
export declare function daemonLockExists(stateDirectory: InstallationDirectory, filename?: "manager.lock" | "worker.lock"): Promise<boolean>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"daemon-lock.d.ts","sourceRoot":"","sources":["../../../src/storage/daemon/daemon-lock.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"daemon-lock.d.ts","sourceRoot":"","sources":["../../../src/storage/daemon/daemon-lock.ts"],"names":[],"mappings":"AAIA,OAAO,EAEH,KAAK,qBAAqB,EAC7B,MAAM,yDAAyD,CAAC;AAmGjE,oHAAoH;AACpH,wBAAsB,iBAAiB,CACnC,cAAc,EAAE,qBAAqB,EACrC,QAAQ,GAAE,cAAc,GAAG,aAA8B,GAC1D,OAAO,CAAC,CAAC,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,CAsBvC;AAED,oFAAoF;AACpF,wBAAsB,gBAAgB,CAClC,cAAc,EAAE,qBAAqB,EACrC,QAAQ,GAAE,cAAc,GAAG,aAA8B,GAC1D,OAAO,CAAC,OAAO,CAAC,CA6BlB"}
|
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
|
-
import { readFile, rename, unlink
|
|
2
|
+
import { readFile, rename, unlink } from "node:fs/promises";
|
|
3
3
|
import { join } from "node:path";
|
|
4
|
+
import { DatabaseSync } from "node:sqlite";
|
|
5
|
+
import { createInstallationFile, } from "../../engine/storage/installation/installation-files.js";
|
|
4
6
|
import { isNodeError } from "../../engine/util/node-error.js";
|
|
7
|
+
const SQLITE_BUSY = 5;
|
|
8
|
+
const OWNERSHIP_CONTENTION_MILLISECONDS = 2_000;
|
|
5
9
|
/**
|
|
6
10
|
* Frees the lock name in one step. On Windows a file that is being unlinked stays
|
|
7
11
|
* in place until the deleting handle closes, and opening or recreating it fails
|
|
@@ -20,14 +24,53 @@ async function removeLockFile(path) {
|
|
|
20
24
|
await unlink(released).catch(() => undefined);
|
|
21
25
|
}
|
|
22
26
|
/**
|
|
23
|
-
*
|
|
24
|
-
*
|
|
27
|
+
* The lock that makes one process the daemon among releases that take it: SQLite's exclusive lock on `<lock>.sqlite`,
|
|
28
|
+
* which the operating system holds for the process and frees when it exits, however it exits. Its holder's PID is
|
|
29
|
+
* recorded, so the next holder knows which lock file a crashed holder left, even after that PID was reused.
|
|
25
30
|
*/
|
|
26
|
-
|
|
27
|
-
const
|
|
31
|
+
function holdDaemonOwnership(path) {
|
|
32
|
+
const database = new DatabaseSync(path);
|
|
33
|
+
try {
|
|
34
|
+
// Contenders starting together briefly hold the file's other locks; waiting out that moment lets one of them
|
|
35
|
+
// win instead of all of them failing. A held daemon lock outlasts the wait, so later contenders give up.
|
|
36
|
+
database.exec(`PRAGMA busy_timeout = ${OWNERSHIP_CONTENTION_MILLISECONDS.toString()}`);
|
|
37
|
+
database.exec("BEGIN EXCLUSIVE");
|
|
38
|
+
// Only the winner keeps its locks: the exclusive locking mode retains the lock after the transaction commits,
|
|
39
|
+
// until the connection closes. Set before winning, it would also keep a loser's read lock and block the winner.
|
|
40
|
+
database.exec("PRAGMA locking_mode = EXCLUSIVE");
|
|
41
|
+
}
|
|
42
|
+
catch (cause) {
|
|
43
|
+
database.close();
|
|
44
|
+
if (cause instanceof Error && "errcode" in cause && cause.errcode === SQLITE_BUSY) {
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
throw cause;
|
|
48
|
+
}
|
|
49
|
+
try {
|
|
50
|
+
database.exec("CREATE TABLE IF NOT EXISTS daemon_owner (singleton INTEGER PRIMARY KEY CHECK (singleton = 1), pid INTEGER NOT NULL)");
|
|
51
|
+
const previous = database.prepare("SELECT pid FROM daemon_owner").get();
|
|
52
|
+
database.prepare("INSERT OR REPLACE INTO daemon_owner (singleton, pid) VALUES (1, ?)").run(process.pid);
|
|
53
|
+
database.exec("COMMIT");
|
|
54
|
+
return {
|
|
55
|
+
previousOwner: typeof previous?.["pid"] === "number" ? previous["pid"] : null,
|
|
56
|
+
release: () => database.close(),
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
catch (cause) {
|
|
60
|
+
database.close();
|
|
61
|
+
throw cause;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Takes the lock file that earlier releases take and read. Only the ownership holder calls this, so contenders never
|
|
66
|
+
* take each other's lock file: one a crashed holder left is replaced, and one a live process of an earlier release holds
|
|
67
|
+
* is kept. Returns the release function, or null when that process holds it.
|
|
68
|
+
*/
|
|
69
|
+
async function takeLockFile(path, previousOwner) {
|
|
28
70
|
for (let attempt = 0; attempt < 2; attempt += 1) {
|
|
29
71
|
try {
|
|
30
|
-
|
|
72
|
+
// A contender never reads an empty lock and mistakes its live owner for a dead one.
|
|
73
|
+
await createInstallationFile(path, `${process.pid.toString()}\n`);
|
|
31
74
|
return async () => await removeLockFile(path);
|
|
32
75
|
}
|
|
33
76
|
catch (cause) {
|
|
@@ -36,7 +79,9 @@ export async function acquireDaemonLock(stateDirectory, filename = "manager.lock
|
|
|
36
79
|
}
|
|
37
80
|
const encoded = await readFile(path, "utf8").catch(() => "");
|
|
38
81
|
const pid = Number(encoded.trim());
|
|
39
|
-
|
|
82
|
+
// The previous ownership holder no longer holds the lock, so its lock file is stale whatever its PID names
|
|
83
|
+
// now. Any other PID was written by an earlier release, whose daemon holds the lock while it is alive.
|
|
84
|
+
if (Number.isSafeInteger(pid) && pid > 1 && pid !== previousOwner) {
|
|
40
85
|
try {
|
|
41
86
|
process.kill(pid, 0);
|
|
42
87
|
return null;
|
|
@@ -52,11 +97,36 @@ export async function acquireDaemonLock(stateDirectory, filename = "manager.lock
|
|
|
52
97
|
}
|
|
53
98
|
return null;
|
|
54
99
|
}
|
|
100
|
+
/** Makes this process the only daemon. Returns the release function, or null when another daemon holds the lock. */
|
|
101
|
+
export async function acquireDaemonLock(stateDirectory, filename = "manager.lock") {
|
|
102
|
+
const path = join(stateDirectory, filename);
|
|
103
|
+
const ownership = holdDaemonOwnership(`${path}.sqlite`);
|
|
104
|
+
if (ownership === null) {
|
|
105
|
+
return null;
|
|
106
|
+
}
|
|
107
|
+
let releaseLockFile = null;
|
|
108
|
+
try {
|
|
109
|
+
releaseLockFile = await takeLockFile(path, ownership.previousOwner);
|
|
110
|
+
}
|
|
111
|
+
finally {
|
|
112
|
+
if (releaseLockFile === null) {
|
|
113
|
+
ownership.release();
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
if (releaseLockFile === null) {
|
|
117
|
+
return null;
|
|
118
|
+
}
|
|
119
|
+
const release = releaseLockFile;
|
|
120
|
+
return async () => {
|
|
121
|
+
await release();
|
|
122
|
+
ownership.release();
|
|
123
|
+
};
|
|
124
|
+
}
|
|
55
125
|
/** Keep the released lock filename; a dead owner does not keep the daemon alive. */
|
|
56
126
|
export async function daemonLockExists(stateDirectory, filename = "manager.lock") {
|
|
57
127
|
try {
|
|
58
128
|
const pid = Number((await readFile(join(stateDirectory, filename), "utf8")).trim());
|
|
59
|
-
//
|
|
129
|
+
// An earlier release can expose the exclusive file before it finishes writing the PID.
|
|
60
130
|
if (!Number.isSafeInteger(pid) || pid <= 1) {
|
|
61
131
|
return true;
|
|
62
132
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"daemon-lock.js","sourceRoot":"","sources":["../../../src/storage/daemon/daemon-lock.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,
|
|
1
|
+
{"version":3,"file":"daemon-lock.js","sourceRoot":"","sources":["../../../src/storage/daemon/daemon-lock.ts"],"names":[],"mappings":"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"}
|