@jskit-ai/kernel 0.1.222 → 0.1.224

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jskit-ai/kernel",
3
- "version": "0.1.222",
3
+ "version": "0.1.224",
4
4
  "type": "module",
5
5
  "dependencies": {
6
6
  "json-rest-schema": "^1.0.17"
@@ -1,3 +1,4 @@
1
1
  export { createCapabilityHttpRuntime } from "./capabilityHttpRuntime.js";
2
2
  export { HttpProvider } from "./HttpProvider.js";
3
+ export { attachDirectRequestActionExecutor } from "./directRequestActionExecutor.js";
3
4
  export { createCachedResponseSerializerFactory } from "./responseSerializerFactory.js";
@@ -0,0 +1,123 @@
1
+ import { spawn } from "node:child_process";
2
+ import { constants as fsConstants } from "node:fs";
3
+ import { open } from "node:fs/promises";
4
+ import path from "node:path";
5
+
6
+ const FILE_LOCK_ERROR_CODE = "file_lock_failed";
7
+
8
+ function fileLockError(message = "", code = FILE_LOCK_ERROR_CODE) {
9
+ const error = new Error(message || "Exclusive file lock failed.");
10
+ error.code = code;
11
+ return error;
12
+ }
13
+
14
+ function normalizedAbsolutePath(value = "", label = "path", errorCode = FILE_LOCK_ERROR_CODE) {
15
+ const normalized = String(value || "").trim();
16
+ if (!normalized || !path.isAbsolute(normalized) || path.normalize(normalized) !== normalized) {
17
+ throw fileLockError(`${label} must be a normalized absolute path.`, errorCode);
18
+ }
19
+ return normalized;
20
+ }
21
+
22
+ async function tryAcquireExclusiveFileLock(lockPath = "", {
23
+ cwd = "",
24
+ errorCode = FILE_LOCK_ERROR_CODE
25
+ } = {}) {
26
+ const normalizedLockPath = normalizedAbsolutePath(lockPath, "lockPath", errorCode);
27
+ const normalizedCwd = cwd
28
+ ? normalizedAbsolutePath(cwd, "cwd", errorCode)
29
+ : path.dirname(normalizedLockPath);
30
+ const lockHandle = await open(
31
+ normalizedLockPath,
32
+ fsConstants.O_CREAT | fsConstants.O_NOFOLLOW | fsConstants.O_RDWR,
33
+ 0o660
34
+ );
35
+ let child = null;
36
+ let stderr = "";
37
+ let closed = Promise.resolve({ code: null, signal: null });
38
+ try {
39
+ child = spawn(
40
+ "/bin/sh",
41
+ [
42
+ "-c",
43
+ "/usr/bin/flock --exclusive --nonblock 3 || exit 1; " +
44
+ "printf 'locked\\n'; IFS= read -r _ || true"
45
+ ],
46
+ {
47
+ cwd: normalizedCwd,
48
+ env: { PATH: "/usr/bin:/bin" },
49
+ stdio: ["pipe", "pipe", "pipe", lockHandle.fd]
50
+ }
51
+ );
52
+ closed = new Promise((resolve) => {
53
+ child.once("close", (code, signal) => resolve({ code, signal }));
54
+ });
55
+ const acquired = await new Promise((resolve, reject) => {
56
+ let output = "";
57
+ let settled = false;
58
+ const settle = (callback, value) => {
59
+ if (settled) {
60
+ return;
61
+ }
62
+ settled = true;
63
+ callback(value);
64
+ };
65
+ child.once("error", (error) => settle(reject, error));
66
+ child.stderr.on("data", (chunk) => {
67
+ stderr = `${stderr}${String(chunk || "")}`.slice(-4096);
68
+ });
69
+ child.stdout.on("data", (chunk) => {
70
+ output += String(chunk || "");
71
+ if (output.includes("\n")) {
72
+ settle(resolve, true);
73
+ }
74
+ });
75
+ child.once("exit", (code) => {
76
+ if (code === 1) {
77
+ settle(resolve, false);
78
+ return;
79
+ }
80
+ settle(reject, fileLockError(
81
+ `Exclusive file lock process failed${stderr ? `: ${stderr.trim()}` : "."}`, errorCode
82
+ ));
83
+ });
84
+ });
85
+ if (!acquired) {
86
+ await lockHandle.close();
87
+ return null;
88
+ }
89
+
90
+ let released = false;
91
+ return async () => {
92
+ if (released) {
93
+ return;
94
+ }
95
+ released = true;
96
+ try {
97
+ child.stdin.end("release\n");
98
+ const result = await closed;
99
+ if (result.code !== 0) {
100
+ throw fileLockError(
101
+ `Exclusive file lock process exited unexpectedly${stderr ? `: ${stderr.trim()}` : "."}`, errorCode
102
+ );
103
+ }
104
+ } finally {
105
+ await lockHandle.close();
106
+ }
107
+ };
108
+ } catch (error) {
109
+ child?.stdin?.destroy();
110
+ child?.kill?.("SIGTERM");
111
+ await closed;
112
+ await lockHandle.close().catch(() => null);
113
+ if (error?.code === errorCode) {
114
+ throw error;
115
+ }
116
+ throw fileLockError(error?.message || String(error || "Exclusive file lock failed."), errorCode);
117
+ }
118
+ }
119
+
120
+ export {
121
+ FILE_LOCK_ERROR_CODE,
122
+ tryAcquireExclusiveFileLock
123
+ };
@@ -28,3 +28,4 @@ export {
28
28
  resolveSemanticPlacementTargetFromApp,
29
29
  resolveShellOutletPlacementTargetFromApp
30
30
  } from "./shellOutlets.js";
31
+ export { tryAcquireExclusiveFileLock } from "./fileLock.js";
@@ -131,6 +131,7 @@ const BARREL_EXPECTATIONS = Object.freeze([
131
131
  filePath: path.join(REPO_ROOT, "packages", "kernel", "server", "http", "index.js"),
132
132
  expectedExports: Object.freeze([
133
133
  "HttpProvider",
134
+ "attachDirectRequestActionExecutor",
134
135
  "createCachedResponseSerializerFactory",
135
136
  "createCapabilityHttpRuntime"
136
137
  ])