@slock-ai/daemon 0.41.0 → 0.41.1-alpha.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.
- package/dist/{chunk-KFVDXO5Y.js → chunk-JAB3HALZ.js} +145 -14
- package/dist/core.js +1 -1
- package/dist/index.js +7 -2
- package/package.json +1 -1
|
@@ -4,8 +4,8 @@ import {
|
|
|
4
4
|
} from "./chunk-JG7ONJZ6.js";
|
|
5
5
|
|
|
6
6
|
// src/core.ts
|
|
7
|
-
import
|
|
8
|
-
import
|
|
7
|
+
import path12 from "path";
|
|
8
|
+
import os5 from "os";
|
|
9
9
|
import { createRequire } from "module";
|
|
10
10
|
import { accessSync } from "fs";
|
|
11
11
|
import { fileURLToPath } from "url";
|
|
@@ -1097,7 +1097,7 @@ function normalizeExecOutput(raw) {
|
|
|
1097
1097
|
return Buffer.isBuffer(raw) ? raw.toString("utf8") : String(raw ?? "");
|
|
1098
1098
|
}
|
|
1099
1099
|
function resolveCommandOnWindows(command, env, execFileSyncFn) {
|
|
1100
|
-
const script = "$cmd = Get-Command -Name $args[0] -ErrorAction Stop | Select-Object -First 1; if ($cmd.Path) { $cmd.Path } elseif ($cmd.Source) { $cmd.Source } elseif ($cmd.Definition) { $cmd.Definition }";
|
|
1100
|
+
const script = "& {$cmd = Get-Command -Name $args[0] -ErrorAction Stop | Select-Object -First 1; if ($cmd.Path) { $cmd.Path } elseif ($cmd.Source) { $cmd.Source } elseif ($cmd.Definition) { $cmd.Definition } }";
|
|
1101
1101
|
try {
|
|
1102
1102
|
const output = normalizeExecOutput(execFileSyncFn("powershell.exe", [
|
|
1103
1103
|
"-NoProfile",
|
|
@@ -4585,6 +4585,112 @@ var ReminderCache = class {
|
|
|
4585
4585
|
}
|
|
4586
4586
|
};
|
|
4587
4587
|
|
|
4588
|
+
// src/machineLock.ts
|
|
4589
|
+
import { createHash, randomUUID as randomUUID2 } from "crypto";
|
|
4590
|
+
import { mkdirSync as mkdirSync4, readFileSync as readFileSync3, rmSync, statSync, writeFileSync as writeFileSync7 } from "fs";
|
|
4591
|
+
import os4 from "os";
|
|
4592
|
+
import path11 from "path";
|
|
4593
|
+
var DEFAULT_MACHINE_STATE_ROOT = path11.join(os4.homedir(), ".slock", "machines");
|
|
4594
|
+
var INCOMPLETE_LOCK_STALE_MS = 3e4;
|
|
4595
|
+
var DaemonMachineLockConflictError = class extends Error {
|
|
4596
|
+
code = "DAEMON_MACHINE_LOCK_HELD";
|
|
4597
|
+
constructor(lockDir, owner) {
|
|
4598
|
+
const ownerText = owner ? `pid=${owner.pid}, startedAt=${owner.startedAt}, host=${owner.hostname}` : "unknown owner";
|
|
4599
|
+
super(
|
|
4600
|
+
`Another Slock daemon is already running for this machine key (${ownerText}). Lock: ${lockDir}. Stop the existing daemon first, or use a different machine key/state directory.`
|
|
4601
|
+
);
|
|
4602
|
+
this.name = "DaemonMachineLockConflictError";
|
|
4603
|
+
}
|
|
4604
|
+
};
|
|
4605
|
+
function apiKeyFingerprint(apiKey) {
|
|
4606
|
+
return createHash("sha256").update(apiKey).digest("hex");
|
|
4607
|
+
}
|
|
4608
|
+
function getDaemonMachineLockId(apiKey) {
|
|
4609
|
+
return `machine-${apiKeyFingerprint(apiKey).slice(0, 16)}`;
|
|
4610
|
+
}
|
|
4611
|
+
function ownerPath(lockDir) {
|
|
4612
|
+
return path11.join(lockDir, "owner.json");
|
|
4613
|
+
}
|
|
4614
|
+
function readOwner(lockDir) {
|
|
4615
|
+
try {
|
|
4616
|
+
return JSON.parse(readFileSync3(ownerPath(lockDir), "utf8"));
|
|
4617
|
+
} catch {
|
|
4618
|
+
return null;
|
|
4619
|
+
}
|
|
4620
|
+
}
|
|
4621
|
+
function lockAgeMs(lockDir) {
|
|
4622
|
+
try {
|
|
4623
|
+
return Date.now() - statSync(lockDir).mtimeMs;
|
|
4624
|
+
} catch {
|
|
4625
|
+
return null;
|
|
4626
|
+
}
|
|
4627
|
+
}
|
|
4628
|
+
function isProcessAlive(pid) {
|
|
4629
|
+
if (!Number.isInteger(pid) || pid <= 0) return false;
|
|
4630
|
+
try {
|
|
4631
|
+
process.kill(pid, 0);
|
|
4632
|
+
return true;
|
|
4633
|
+
} catch (err) {
|
|
4634
|
+
const code = typeof err === "object" && err && "code" in err ? err.code : void 0;
|
|
4635
|
+
return code !== "ESRCH";
|
|
4636
|
+
}
|
|
4637
|
+
}
|
|
4638
|
+
function acquireDaemonMachineLock(options) {
|
|
4639
|
+
const rootDir = options.rootDir ?? DEFAULT_MACHINE_STATE_ROOT;
|
|
4640
|
+
const fingerprint = apiKeyFingerprint(options.apiKey);
|
|
4641
|
+
const lockId = getDaemonMachineLockId(options.apiKey);
|
|
4642
|
+
const machineDir = path11.join(rootDir, lockId);
|
|
4643
|
+
const lockDir = path11.join(machineDir, "daemon.lock");
|
|
4644
|
+
const token = randomUUID2();
|
|
4645
|
+
mkdirSync4(machineDir, { recursive: true });
|
|
4646
|
+
for (let attempt = 0; attempt < 2; attempt += 1) {
|
|
4647
|
+
try {
|
|
4648
|
+
mkdirSync4(lockDir);
|
|
4649
|
+
const owner = {
|
|
4650
|
+
pid: process.pid,
|
|
4651
|
+
token,
|
|
4652
|
+
hostname: os4.hostname(),
|
|
4653
|
+
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4654
|
+
serverUrl: options.serverUrl,
|
|
4655
|
+
apiKeyFingerprint: fingerprint.slice(0, 16)
|
|
4656
|
+
};
|
|
4657
|
+
try {
|
|
4658
|
+
writeFileSync7(ownerPath(lockDir), `${JSON.stringify(owner, null, 2)}
|
|
4659
|
+
`, { mode: 384 });
|
|
4660
|
+
} catch (err) {
|
|
4661
|
+
rmSync(lockDir, { recursive: true, force: true });
|
|
4662
|
+
throw err;
|
|
4663
|
+
}
|
|
4664
|
+
return {
|
|
4665
|
+
lockId,
|
|
4666
|
+
machineDir,
|
|
4667
|
+
lockDir,
|
|
4668
|
+
release: () => {
|
|
4669
|
+
const currentOwner = readOwner(lockDir);
|
|
4670
|
+
if (currentOwner?.pid === process.pid && currentOwner.token === token) {
|
|
4671
|
+
rmSync(lockDir, { recursive: true, force: true });
|
|
4672
|
+
}
|
|
4673
|
+
}
|
|
4674
|
+
};
|
|
4675
|
+
} catch (err) {
|
|
4676
|
+
const code = typeof err === "object" && err && "code" in err ? err.code : void 0;
|
|
4677
|
+
if (code !== "EEXIST") throw err;
|
|
4678
|
+
const owner = readOwner(lockDir);
|
|
4679
|
+
if (owner?.pid && isProcessAlive(owner.pid)) {
|
|
4680
|
+
throw new DaemonMachineLockConflictError(lockDir, owner);
|
|
4681
|
+
}
|
|
4682
|
+
if (!owner) {
|
|
4683
|
+
const ageMs = lockAgeMs(lockDir);
|
|
4684
|
+
if (ageMs === null || ageMs < INCOMPLETE_LOCK_STALE_MS) {
|
|
4685
|
+
throw new DaemonMachineLockConflictError(lockDir, null);
|
|
4686
|
+
}
|
|
4687
|
+
}
|
|
4688
|
+
rmSync(lockDir, { recursive: true, force: true });
|
|
4689
|
+
}
|
|
4690
|
+
}
|
|
4691
|
+
throw new DaemonMachineLockConflictError(lockDir, readOwner(lockDir));
|
|
4692
|
+
}
|
|
4693
|
+
|
|
4588
4694
|
// src/core.ts
|
|
4589
4695
|
var DAEMON_CLI_USAGE = "Usage: slock-daemon --server-url <url> --api-key <key>";
|
|
4590
4696
|
function parseDaemonCliArgs(args) {
|
|
@@ -4606,23 +4712,23 @@ function readDaemonVersion(moduleUrl = import.meta.url) {
|
|
|
4606
4712
|
}
|
|
4607
4713
|
}
|
|
4608
4714
|
function resolveChatBridgePath(moduleUrl = import.meta.url) {
|
|
4609
|
-
const dirname =
|
|
4610
|
-
const jsPath =
|
|
4715
|
+
const dirname = path12.dirname(fileURLToPath(moduleUrl));
|
|
4716
|
+
const jsPath = path12.resolve(dirname, "chat-bridge.js");
|
|
4611
4717
|
try {
|
|
4612
4718
|
accessSync(jsPath);
|
|
4613
4719
|
return jsPath;
|
|
4614
4720
|
} catch {
|
|
4615
|
-
return
|
|
4721
|
+
return path12.resolve(dirname, "chat-bridge.ts");
|
|
4616
4722
|
}
|
|
4617
4723
|
}
|
|
4618
4724
|
function resolveSlockCliPath(moduleUrl = import.meta.url) {
|
|
4619
|
-
const thisDir =
|
|
4620
|
-
const bundledDistPath =
|
|
4725
|
+
const thisDir = path12.dirname(fileURLToPath(moduleUrl));
|
|
4726
|
+
const bundledDistPath = path12.resolve(thisDir, "cli", "index.js");
|
|
4621
4727
|
try {
|
|
4622
4728
|
accessSync(bundledDistPath);
|
|
4623
4729
|
return bundledDistPath;
|
|
4624
4730
|
} catch {
|
|
4625
|
-
const workspaceDistPath =
|
|
4731
|
+
const workspaceDistPath = path12.resolve(thisDir, "..", "..", "cli", "dist", "index.js");
|
|
4626
4732
|
accessSync(workspaceDistPath);
|
|
4627
4733
|
return workspaceDistPath;
|
|
4628
4734
|
}
|
|
@@ -4701,6 +4807,7 @@ var DaemonCore = class {
|
|
|
4701
4807
|
connection;
|
|
4702
4808
|
reminderCache;
|
|
4703
4809
|
tracer;
|
|
4810
|
+
machineLock = null;
|
|
4704
4811
|
constructor(options) {
|
|
4705
4812
|
this.options = options;
|
|
4706
4813
|
this.daemonVersion = options.daemonVersion ?? readDaemonVersion();
|
|
@@ -4731,15 +4838,39 @@ var DaemonCore = class {
|
|
|
4731
4838
|
});
|
|
4732
4839
|
this.connection = connection;
|
|
4733
4840
|
}
|
|
4841
|
+
resolveMachineStateRoot() {
|
|
4842
|
+
if (this.options.machineStateDir) return this.options.machineStateDir;
|
|
4843
|
+
if (this.options.dataDir) return path12.join(path12.dirname(this.options.dataDir), "machines");
|
|
4844
|
+
return DEFAULT_MACHINE_STATE_ROOT;
|
|
4845
|
+
}
|
|
4734
4846
|
start() {
|
|
4735
4847
|
logger.info("[Slock Daemon] Starting...");
|
|
4736
|
-
this.
|
|
4848
|
+
if (!this.machineLock) {
|
|
4849
|
+
this.machineLock = acquireDaemonMachineLock({
|
|
4850
|
+
apiKey: this.options.apiKey,
|
|
4851
|
+
serverUrl: this.options.serverUrl,
|
|
4852
|
+
rootDir: this.resolveMachineStateRoot()
|
|
4853
|
+
});
|
|
4854
|
+
logger.info(`[Slock Daemon] Acquired machine lock: ${this.machineLock.lockDir}`);
|
|
4855
|
+
}
|
|
4856
|
+
try {
|
|
4857
|
+
this.connection.connect();
|
|
4858
|
+
} catch (err) {
|
|
4859
|
+
this.machineLock.release();
|
|
4860
|
+
this.machineLock = null;
|
|
4861
|
+
throw err;
|
|
4862
|
+
}
|
|
4737
4863
|
}
|
|
4738
4864
|
async stop() {
|
|
4739
4865
|
logger.info("[Slock Daemon] Shutting down...");
|
|
4740
4866
|
this.reminderCache.clear();
|
|
4741
|
-
|
|
4742
|
-
|
|
4867
|
+
try {
|
|
4868
|
+
await this.agentManager.stopAll();
|
|
4869
|
+
} finally {
|
|
4870
|
+
this.connection.disconnect();
|
|
4871
|
+
this.machineLock?.release();
|
|
4872
|
+
this.machineLock = null;
|
|
4873
|
+
}
|
|
4743
4874
|
}
|
|
4744
4875
|
get connected() {
|
|
4745
4876
|
return this.connection.connected;
|
|
@@ -4900,8 +5031,8 @@ var DaemonCore = class {
|
|
|
4900
5031
|
capabilities: ["agent:start", "agent:stop", "agent:deliver", "workspace:files"],
|
|
4901
5032
|
runtimes,
|
|
4902
5033
|
runningAgents: this.agentManager.getRunningAgentIds(),
|
|
4903
|
-
hostname: this.options.hostname ??
|
|
4904
|
-
os: this.options.osDescription ?? `${
|
|
5034
|
+
hostname: this.options.hostname ?? os5.hostname(),
|
|
5035
|
+
os: this.options.osDescription ?? `${os5.platform()} ${os5.arch()}`,
|
|
4905
5036
|
daemonVersion: this.daemonVersion
|
|
4906
5037
|
});
|
|
4907
5038
|
for (const agentId of this.agentManager.getRunningAgentIds()) {
|
package/dist/core.js
CHANGED
package/dist/index.js
CHANGED
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
DAEMON_CLI_USAGE,
|
|
4
4
|
DaemonCore,
|
|
5
5
|
parseDaemonCliArgs
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-JAB3HALZ.js";
|
|
7
7
|
import "./chunk-JG7ONJZ6.js";
|
|
8
8
|
|
|
9
9
|
// src/index.ts
|
|
@@ -13,7 +13,12 @@ if (!parsedArgs) {
|
|
|
13
13
|
process.exit(1);
|
|
14
14
|
}
|
|
15
15
|
var daemon = new DaemonCore(parsedArgs);
|
|
16
|
-
|
|
16
|
+
try {
|
|
17
|
+
daemon.start();
|
|
18
|
+
} catch (err) {
|
|
19
|
+
console.error(err instanceof Error ? err.message : err);
|
|
20
|
+
process.exit(1);
|
|
21
|
+
}
|
|
17
22
|
var shutdown = async () => {
|
|
18
23
|
await daemon.stop();
|
|
19
24
|
process.exit(0);
|