mtech-ai-local-runner 0.1.2 → 0.1.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/src/cli.js
CHANGED
|
@@ -184,7 +184,7 @@ export async function main(argv, env, io = {}) {
|
|
|
184
184
|
const fetchFn = io.fetchFn || fetch;
|
|
185
185
|
if (isServiceConfig(config)) {
|
|
186
186
|
const localStore = new FileLocalConfigStore(config.stateRoot);
|
|
187
|
-
const launchd = io.launchdService ?? new LaunchdService({ home: env.HOME ?? "", uid: process.getuid?.() ?? 0 });
|
|
187
|
+
const launchd = io.launchdService ?? new LaunchdService({ home: env.HOME ?? "", uid: process.getuid?.() ?? 0, path: env.PATH });
|
|
188
188
|
try {
|
|
189
189
|
if (config.mode === "setup") {
|
|
190
190
|
if (process.platform !== "darwin")
|
|
@@ -203,7 +203,7 @@ export async function main(argv, env, io = {}) {
|
|
|
203
203
|
const authenticated = await runFile(binary, ["auth", "status"], { encoding: "utf8" }).then(() => true).catch(() => false);
|
|
204
204
|
const registered = await gateway.registerLocalRunner({
|
|
205
205
|
deviceFingerprint: createHash("sha256").update(`${process.getuid?.() ?? 0}|${runnerDisplayName()}|${config.gatewayMcpUrl}`).digest("hex"),
|
|
206
|
-
displayName: runnerDisplayName(), packageVersion: "0.1.
|
|
206
|
+
displayName: runnerDisplayName(), packageVersion: "0.1.3",
|
|
207
207
|
capabilities: [{ adapter: "claude_code_cli", version: "1", capabilities: ["repository_read", "repository_edit", "shell_test", "git_diff"], authenticated }],
|
|
208
208
|
});
|
|
209
209
|
await localStore.save({ schemaVersion: 1, gatewayMcpUrl: config.gatewayMcpUrl, runnerId: registered.runner.id, workspaceRoot: config.workspaceRoot, concurrency: 1 });
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { mkdir, open, rm } from "node:fs/promises";
|
|
1
|
+
import { mkdir, open, rm, readFile, unlink } from "node:fs/promises";
|
|
2
2
|
import { dirname } from "node:path";
|
|
3
3
|
export class ProcessLock {
|
|
4
4
|
path;
|
|
@@ -8,15 +8,41 @@ export class ProcessLock {
|
|
|
8
8
|
}
|
|
9
9
|
async acquire() {
|
|
10
10
|
await mkdir(dirname(this.path), { recursive: true, mode: 0o700 });
|
|
11
|
+
const guard = `${this.path}.acquire`;
|
|
11
12
|
try {
|
|
12
|
-
|
|
13
|
+
await mkdir(guard, { mode: 0o700 });
|
|
13
14
|
}
|
|
14
15
|
catch (error) {
|
|
15
16
|
if (error.code === "EEXIST")
|
|
16
17
|
throw new Error("local runner daemon is already running");
|
|
17
18
|
throw error;
|
|
18
19
|
}
|
|
19
|
-
|
|
20
|
+
try {
|
|
21
|
+
try {
|
|
22
|
+
const stored = (await readFile(this.path, "utf8")).trim();
|
|
23
|
+
const pid = Number(stored);
|
|
24
|
+
if (!/^\d+$/.test(stored) || !Number.isSafeInteger(pid) || pid <= 0)
|
|
25
|
+
throw new Error("local runner daemon lock has an invalid owner");
|
|
26
|
+
try {
|
|
27
|
+
process.kill(pid, 0);
|
|
28
|
+
throw new Error("local runner daemon is already running");
|
|
29
|
+
}
|
|
30
|
+
catch (error) {
|
|
31
|
+
if (error.code !== "ESRCH")
|
|
32
|
+
throw error;
|
|
33
|
+
}
|
|
34
|
+
await unlink(this.path);
|
|
35
|
+
}
|
|
36
|
+
catch (error) {
|
|
37
|
+
if (error.code !== "ENOENT")
|
|
38
|
+
throw error;
|
|
39
|
+
}
|
|
40
|
+
this.handle = await open(this.path, "wx", 0o600);
|
|
41
|
+
await this.handle.writeFile(String(process.pid));
|
|
42
|
+
}
|
|
43
|
+
finally {
|
|
44
|
+
await rm(guard, { recursive: true });
|
|
45
|
+
}
|
|
20
46
|
}
|
|
21
47
|
async release() {
|
|
22
48
|
await this.handle?.close();
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { execFile } from "node:child_process";
|
|
2
2
|
import { promisify } from "node:util";
|
|
3
3
|
import { hostname } from "node:os";
|
|
4
|
+
import { createHash } from "node:crypto";
|
|
4
5
|
import { join } from "node:path";
|
|
5
6
|
import { mkdirSync, writeFileSync, chmodSync } from "node:fs";
|
|
6
7
|
import { McpGatewayClient } from "../gateway-client.js";
|
|
@@ -72,6 +73,12 @@ export async function buildDaemon(input) {
|
|
|
72
73
|
state,
|
|
73
74
|
});
|
|
74
75
|
const poller = new JobPoller({
|
|
76
|
+
heartbeat: () => gateway.heartbeatLocalRunner({
|
|
77
|
+
deviceFingerprint: createHash("sha256").update(`${process.getuid?.() ?? 0}|${runnerDisplayName()}|${input.gatewayMcpUrl}`).digest("hex"),
|
|
78
|
+
displayName: runnerDisplayName(), packageVersion: "0.1.3",
|
|
79
|
+
capabilities: probes.map((probe) => ({ adapter: probe.id, version: probe.version,
|
|
80
|
+
capabilities: probe.capabilities, authenticated: probe.available && probe.authenticated })),
|
|
81
|
+
}).then(() => undefined),
|
|
75
82
|
wait: async () => {
|
|
76
83
|
const claim = await gateway.waitForLocalJob(versions, 25);
|
|
77
84
|
if (claim.status !== "claimed" || !claim.job || !claim.task_package || !claim.lease_token || !claim.launch_grant)
|
|
@@ -403,6 +403,17 @@ export class McpGatewayClient {
|
|
|
403
403
|
capabilities: input.capabilities,
|
|
404
404
|
});
|
|
405
405
|
}
|
|
406
|
+
async heartbeatLocalRunner(input) {
|
|
407
|
+
return this.callTool("local_agent_runner_heartbeat", {
|
|
408
|
+
runner_id: this.runnerId,
|
|
409
|
+
runner: {
|
|
410
|
+
device_fingerprint: input.deviceFingerprint,
|
|
411
|
+
display_name: input.displayName,
|
|
412
|
+
package_version: input.packageVersion,
|
|
413
|
+
capabilities: input.capabilities,
|
|
414
|
+
},
|
|
415
|
+
});
|
|
416
|
+
}
|
|
406
417
|
async localRunnerStatus() {
|
|
407
418
|
return this.callTool("local_agent_runner_status", { runner_id: this.runnerId });
|
|
408
419
|
}
|
|
@@ -31,6 +31,7 @@ export class LaunchdService {
|
|
|
31
31
|
<plist version="1.0"><dict>
|
|
32
32
|
<key>Label</key><string>${LAUNCHD_LABEL}</string>
|
|
33
33
|
<key>ProgramArguments</key><array><string>${xml(process.execPath)}</string><string>${xml(executablePath)}</string><string>run</string><string>--state-root=${xml(stateRoot)}</string></array>
|
|
34
|
+
<key>EnvironmentVariables</key><dict><key>PATH</key><string>${xml(this.deps.path ?? process.env.PATH ?? "/usr/bin:/bin:/usr/sbin:/sbin")}</string></dict>
|
|
34
35
|
<key>RunAtLoad</key><true/><key>KeepAlive</key><true/>
|
|
35
36
|
<key>ProcessType</key><string>Background</string>
|
|
36
37
|
</dict></plist>
|
package/package.json
CHANGED
|
@@ -1,12 +1,20 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mtech-ai-local-runner",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"private": false,
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
|
-
"bin": {
|
|
7
|
-
|
|
6
|
+
"bin": {
|
|
7
|
+
"mtec-ai-local-runner": "dist/src/cli.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"dist/src",
|
|
11
|
+
"README.md",
|
|
12
|
+
"LICENSE"
|
|
13
|
+
],
|
|
8
14
|
"type": "module",
|
|
9
|
-
"engines": {
|
|
15
|
+
"engines": {
|
|
16
|
+
"node": ">=20"
|
|
17
|
+
},
|
|
10
18
|
"scripts": {
|
|
11
19
|
"build": "tsc",
|
|
12
20
|
"prepack": "npm run build && chmod 755 dist/src/cli.js",
|