@kal-elsam/kairo-runtime 0.5.0 → 0.5.1
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": "@kal-elsam/kairo-runtime",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.1",
|
|
4
4
|
"description": "Kairo Runtime — local agent operating system for Codex, Cursor, Claude, Gemini, Copilot, Engram, and Graphify.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"homepage": "https://github.com/Kal-elSam/harness#readme",
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { existsSync } from "node:fs";
|
|
2
|
-
import { mkdir, readFile
|
|
2
|
+
import { mkdir, readFile } from "node:fs/promises";
|
|
3
3
|
import { runPaths } from "../paths.js";
|
|
4
|
+
import { writeAtomicJson } from "./write-atomic-json.js";
|
|
4
5
|
|
|
5
6
|
function cancelSignalPath(homeDir, runId) {
|
|
6
7
|
return `${runPaths(homeDir, runId).runDir}/cancel.signal.json`;
|
|
@@ -9,7 +10,7 @@ function cancelSignalPath(homeDir, runId) {
|
|
|
9
10
|
export async function writeCancelSignal(homeDir, runId, payload) {
|
|
10
11
|
const { runDir } = runPaths(homeDir, runId);
|
|
11
12
|
await mkdir(runDir, { recursive: true });
|
|
12
|
-
await
|
|
13
|
+
await writeAtomicJson(cancelSignalPath(homeDir, runId), payload);
|
|
13
14
|
}
|
|
14
15
|
|
|
15
16
|
export async function readCancelSignal(homeDir, runId) {
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { existsSync } from "node:fs";
|
|
2
|
-
import { appendFile, mkdir, readdir, readFile
|
|
2
|
+
import { appendFile, mkdir, readdir, readFile } from "node:fs/promises";
|
|
3
3
|
import { harnessHomePaths, runPaths } from "../paths.js";
|
|
4
4
|
import { RUN_STATES, isActiveRunState } from "./run-types.js";
|
|
5
5
|
import { createRunEvent } from "./run-events.js";
|
|
6
|
+
import { writeAtomicJson } from "./write-atomic-json.js";
|
|
6
7
|
|
|
7
8
|
const writeLocks = new Map();
|
|
8
9
|
|
|
@@ -13,7 +14,7 @@ export function getRunsDir(homeDir) {
|
|
|
13
14
|
export async function createRunRecord(homeDir, metadata) {
|
|
14
15
|
const { runDir, statePath } = runPaths(homeDir, metadata.runId);
|
|
15
16
|
await mkdir(runDir, { recursive: true });
|
|
16
|
-
await
|
|
17
|
+
await writeAtomicJson(statePath, metadata);
|
|
17
18
|
return metadata;
|
|
18
19
|
}
|
|
19
20
|
|
|
@@ -34,7 +35,7 @@ export async function writeRunState(homeDir, metadata) {
|
|
|
34
35
|
const next = previous.then(async () => {
|
|
35
36
|
const { statePath, runDir } = runPaths(homeDir, metadata.runId);
|
|
36
37
|
await mkdir(runDir, { recursive: true });
|
|
37
|
-
await
|
|
38
|
+
await writeAtomicJson(statePath, metadata);
|
|
38
39
|
return metadata;
|
|
39
40
|
});
|
|
40
41
|
writeLocks.set(key, next.catch(() => {}));
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { existsSync } from "node:fs";
|
|
2
|
-
import { mkdir, readFile
|
|
2
|
+
import { mkdir, readFile } from "node:fs/promises";
|
|
3
3
|
import { runPaths } from "../paths.js";
|
|
4
|
+
import { writeAtomicJson } from "./write-atomic-json.js";
|
|
4
5
|
|
|
5
6
|
function lockPath(homeDir, runId) {
|
|
6
7
|
return `${runPaths(homeDir, runId).runDir}/supervisor.lock.json`;
|
|
@@ -9,7 +10,7 @@ function lockPath(homeDir, runId) {
|
|
|
9
10
|
export async function writeSupervisorLock(homeDir, runId, lock) {
|
|
10
11
|
const { runDir } = runPaths(homeDir, runId);
|
|
11
12
|
await mkdir(runDir, { recursive: true });
|
|
12
|
-
await
|
|
13
|
+
await writeAtomicJson(lockPath(homeDir, runId), lock);
|
|
13
14
|
}
|
|
14
15
|
|
|
15
16
|
export async function readSupervisorLock(homeDir, runId) {
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { open as fsOpen, rename as fsRename, unlink as fsUnlink } from "node:fs/promises";
|
|
2
|
+
import { constants } from "node:fs";
|
|
3
|
+
import { basename, dirname, join } from "node:path";
|
|
4
|
+
import { randomBytes } from "node:crypto";
|
|
5
|
+
|
|
6
|
+
function defaultTempPath(targetPath) {
|
|
7
|
+
const id = randomBytes(8).toString("hex");
|
|
8
|
+
return join(
|
|
9
|
+
dirname(targetPath),
|
|
10
|
+
`.${basename(targetPath)}.${process.pid}.${id}.tmp`
|
|
11
|
+
);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Atomically replace targetPath with pretty-printed JSON.
|
|
16
|
+
* Creates a unique temp in the same directory (O_EXCL), writes + fsync,
|
|
17
|
+
* renames over the destination, and deletes the temp on any failure.
|
|
18
|
+
*/
|
|
19
|
+
export async function writeAtomicJson(targetPath, value, deps = {}) {
|
|
20
|
+
const open = deps.open ?? fsOpen;
|
|
21
|
+
const rename = deps.rename ?? fsRename;
|
|
22
|
+
const unlink = deps.unlink ?? fsUnlink;
|
|
23
|
+
const createTempPath = deps.createTempPath ?? defaultTempPath;
|
|
24
|
+
|
|
25
|
+
const payload = `${JSON.stringify(value, null, 2)}\n`;
|
|
26
|
+
const tempPath = createTempPath(targetPath);
|
|
27
|
+
let handle;
|
|
28
|
+
|
|
29
|
+
try {
|
|
30
|
+
handle = await open(tempPath, constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL, 0o644);
|
|
31
|
+
await handle.writeFile(payload, "utf8");
|
|
32
|
+
await handle.sync();
|
|
33
|
+
await handle.close();
|
|
34
|
+
handle = undefined;
|
|
35
|
+
await rename(tempPath, targetPath);
|
|
36
|
+
} catch (error) {
|
|
37
|
+
if (handle) {
|
|
38
|
+
try {
|
|
39
|
+
await handle.close();
|
|
40
|
+
} catch {
|
|
41
|
+
// Best-effort close before temp cleanup.
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
try {
|
|
45
|
+
await unlink(tempPath);
|
|
46
|
+
} catch {
|
|
47
|
+
// Temp may not exist yet or already renamed.
|
|
48
|
+
}
|
|
49
|
+
throw error;
|
|
50
|
+
}
|
|
51
|
+
}
|