hovclaw 0.1.1 → 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/README.md +2 -10
- package/dist/{doctor-D52M80De.js → doctor-DJHTvhli.js} +1 -1
- package/dist/hovclaw.js +16 -159
- package/dist/index.js +11 -98
- package/dist/{src-Y6AqidKn.js → src-qX1C6PLF.js} +2 -6
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<h1 align="center">HOVClaw</h1>
|
|
2
2
|
|
|
3
3
|
<p align="center">
|
|
4
|
-
<strong>Lean self-hosted AI agent gateway
|
|
4
|
+
<strong>Lean self-hosted AI agent gateway</strong>
|
|
5
5
|
</p>
|
|
6
6
|
|
|
7
7
|
<p align="center">
|
|
@@ -20,7 +20,6 @@ HOVClaw is built on a simple principle: **run your own AI agent infrastructure,
|
|
|
20
20
|
|
|
21
21
|
- **Self-hosted first** - Everything runs on your machine, no cloud dependency
|
|
22
22
|
- **Channel-native** - Talk to your agent via Telegram or Discord, not a custom app
|
|
23
|
-
- **OpenClaw-compatible** - Mirror config to `~/.openclaw` for ClawHub discovery and tooling interop
|
|
24
23
|
- **Gateway-first control** - WebSocket protocol v3 for programmatic access, with a built-in web UI for quick ops
|
|
25
24
|
|
|
26
25
|
## Features
|
|
@@ -59,12 +58,6 @@ HOVClaw is built on a simple principle: **run your own AI agent infrastructure,
|
|
|
59
58
|
- **Channel notifications** - Scheduled job results delivered to Telegram or Discord
|
|
60
59
|
- **Concurrent execution** - Configurable max concurrent jobs
|
|
61
60
|
|
|
62
|
-
### OpenClaw Compatibility
|
|
63
|
-
|
|
64
|
-
- **Mirror strategy** - HOVClaw is source of truth; mirror files written to `~/.openclaw`
|
|
65
|
-
- **ClawHub discovery** - `~/.openclaw/openclaw.json` + `~/.openclaw/skills` symlink
|
|
66
|
-
- **Compat CLI** - `hovclaw compat status --sync` to verify mirror state
|
|
67
|
-
|
|
68
61
|
## Installation
|
|
69
62
|
|
|
70
63
|
### Prerequisites
|
|
@@ -117,6 +110,7 @@ Security defaults in this release are intentionally strict:
|
|
|
117
110
|
|
|
118
111
|
Agent and skill definitions are loaded from:
|
|
119
112
|
- `~/.hovclaw/agents/<name>/agent.json` (`CLAUDE.md`, `cron.json`)
|
|
113
|
+
- missing `~/.hovclaw/agents/main/agent.json` is auto-bootstrapped with a minimal scaffold (`name`, `skills`)
|
|
120
114
|
- `~/.agents/skills/<name>/SKILL.md`
|
|
121
115
|
- legacy `~/.hovclaw/skills` content is copied once when shared skills are empty
|
|
122
116
|
|
|
@@ -251,8 +245,6 @@ hovclaw gateway open-ui
|
|
|
251
245
|
# Daemon
|
|
252
246
|
hovclaw daemon install|uninstall|start|stop|restart|status|logs
|
|
253
247
|
|
|
254
|
-
# Compatibility
|
|
255
|
-
hovclaw compat status [--sync] [--json]
|
|
256
248
|
```
|
|
257
249
|
|
|
258
250
|
## Gateway Methods (v3)
|
|
@@ -148,7 +148,7 @@ function runDoctorChecks(options, env = process.env) {
|
|
|
148
148
|
} else addFinding(findings, "telegram-webhook-secret", "fail", "Telegram webhook secret missing", `Accounts: ${webhookAccountsMissingSecret.join(", ")}`);
|
|
149
149
|
else addFinding(findings, "telegram-webhook-secret", "pass", "Telegram webhook secret policy", "OK");
|
|
150
150
|
}
|
|
151
|
-
if (!loadedConfig.gateway.enabled) addFinding(findings, "gateway-enabled", "warn", "Gateway is disabled", "Enable gateway for
|
|
151
|
+
if (!loadedConfig.gateway.enabled) addFinding(findings, "gateway-enabled", "warn", "Gateway is disabled", "Enable gateway for gateway RPC and built-in web UI access.");
|
|
152
152
|
else addFinding(findings, "gateway-enabled", "pass", "Gateway enabled", "OK");
|
|
153
153
|
if (loadedConfig.gateway.auth.allowUnauthenticated) addFinding(findings, "gateway-auth-mode", "warn", "Gateway unauthenticated mode is enabled", "Set gateway.auth.allowUnauthenticated=false for secure deployments.");
|
|
154
154
|
else if (!loadedConfig.gateway.auth.token.trim() && !loadedConfig.gateway.auth.password.trim()) if (options.repair) {
|
package/dist/hovclaw.js
CHANGED
|
@@ -4,11 +4,11 @@ import path from "node:path";
|
|
|
4
4
|
import { fileURLToPath } from "node:url";
|
|
5
5
|
import { log } from "@clack/prompts";
|
|
6
6
|
import { Command } from "commander";
|
|
7
|
+
import crypto, { randomUUID, timingSafeEqual } from "node:crypto";
|
|
8
|
+
import WebSocket from "ws";
|
|
7
9
|
import os from "node:os";
|
|
8
10
|
import dotenv from "dotenv";
|
|
9
11
|
import { z } from "zod";
|
|
10
|
-
import crypto, { randomUUID, timingSafeEqual } from "node:crypto";
|
|
11
|
-
import WebSocket from "ws";
|
|
12
12
|
import fs$1 from "node:fs/promises";
|
|
13
13
|
import { Agent } from "@mariozechner/pi-agent-core";
|
|
14
14
|
import { Type, getModel, getOAuthApiKey, getProviders } from "@mariozechner/pi-ai";
|
|
@@ -39,132 +39,6 @@ var __exportAll = (all, no_symbols) => {
|
|
|
39
39
|
return target;
|
|
40
40
|
};
|
|
41
41
|
|
|
42
|
-
//#endregion
|
|
43
|
-
//#region src/compat/openclaw-mirror.ts
|
|
44
|
-
function resolveOpenClawHome(env = process.env) {
|
|
45
|
-
const override = env.OPENCLAW_STATE_DIR?.trim();
|
|
46
|
-
if (override) return path.resolve(override.startsWith("~") ? path.join(os.homedir(), override.slice(1)) : override);
|
|
47
|
-
return path.join(os.homedir(), ".openclaw");
|
|
48
|
-
}
|
|
49
|
-
function resolveOpenClawConfigPath(openclawHome) {
|
|
50
|
-
return path.join(openclawHome, "openclaw.json");
|
|
51
|
-
}
|
|
52
|
-
function resolveOpenClawSharedSkillsPath(openclawHome) {
|
|
53
|
-
return path.join(openclawHome, "skills");
|
|
54
|
-
}
|
|
55
|
-
function readJsonIfExists(filePath) {
|
|
56
|
-
if (!fs.existsSync(filePath)) return null;
|
|
57
|
-
try {
|
|
58
|
-
return JSON.parse(fs.readFileSync(filePath, "utf8"));
|
|
59
|
-
} catch {
|
|
60
|
-
return null;
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
function buildMirrorConfig(config) {
|
|
64
|
-
const fallbackWorkspace = config.agents.defaults.workspace || path.join(config.hovclawHome, "workspace");
|
|
65
|
-
const agentList = config.agents.list.length > 0 ? config.agents.list : [{
|
|
66
|
-
id: "main",
|
|
67
|
-
name: "Main",
|
|
68
|
-
workspace: fallbackWorkspace,
|
|
69
|
-
default: true
|
|
70
|
-
}];
|
|
71
|
-
const extraDirs = /* @__PURE__ */ new Set();
|
|
72
|
-
extraDirs.add(config.skillsDir);
|
|
73
|
-
for (const agent of agentList) {
|
|
74
|
-
const workspace = (agent.workspace || fallbackWorkspace).trim();
|
|
75
|
-
if (!workspace) continue;
|
|
76
|
-
extraDirs.add(path.join(workspace, "skills"));
|
|
77
|
-
}
|
|
78
|
-
return {
|
|
79
|
-
agent: { workspace: fallbackWorkspace },
|
|
80
|
-
agents: {
|
|
81
|
-
defaults: { workspace: fallbackWorkspace },
|
|
82
|
-
list: agentList
|
|
83
|
-
},
|
|
84
|
-
skills: { load: { extraDirs: Array.from(extraDirs) } }
|
|
85
|
-
};
|
|
86
|
-
}
|
|
87
|
-
function ensureDir(dirPath) {
|
|
88
|
-
fs.mkdirSync(dirPath, {
|
|
89
|
-
recursive: true,
|
|
90
|
-
mode: 448
|
|
91
|
-
});
|
|
92
|
-
}
|
|
93
|
-
function syncSkillsDir(sharedSkillsPath, sourceSkillsPath) {
|
|
94
|
-
if (!fs.existsSync(sourceSkillsPath)) {
|
|
95
|
-
ensureDir(sharedSkillsPath);
|
|
96
|
-
return false;
|
|
97
|
-
}
|
|
98
|
-
try {
|
|
99
|
-
if (fs.lstatSync(sharedSkillsPath).isSymbolicLink()) {
|
|
100
|
-
const linkTarget = fs.readlinkSync(sharedSkillsPath);
|
|
101
|
-
if (path.resolve(path.dirname(sharedSkillsPath), linkTarget) === sourceSkillsPath) return true;
|
|
102
|
-
fs.unlinkSync(sharedSkillsPath);
|
|
103
|
-
}
|
|
104
|
-
} catch {}
|
|
105
|
-
if (!fs.existsSync(sharedSkillsPath)) try {
|
|
106
|
-
fs.symlinkSync(sourceSkillsPath, sharedSkillsPath, "dir");
|
|
107
|
-
return true;
|
|
108
|
-
} catch {}
|
|
109
|
-
ensureDir(sharedSkillsPath);
|
|
110
|
-
for (const entry of fs.readdirSync(sourceSkillsPath, { withFileTypes: true })) {
|
|
111
|
-
const src = path.join(sourceSkillsPath, entry.name);
|
|
112
|
-
const dst = path.join(sharedSkillsPath, entry.name);
|
|
113
|
-
if (entry.isDirectory()) {
|
|
114
|
-
fs.cpSync(src, dst, {
|
|
115
|
-
recursive: true,
|
|
116
|
-
force: true
|
|
117
|
-
});
|
|
118
|
-
continue;
|
|
119
|
-
}
|
|
120
|
-
if (entry.isFile()) fs.copyFileSync(src, dst);
|
|
121
|
-
}
|
|
122
|
-
return false;
|
|
123
|
-
}
|
|
124
|
-
function writeOpenClawMirror(config) {
|
|
125
|
-
const openclawHome = resolveOpenClawHome();
|
|
126
|
-
const configPath = resolveOpenClawConfigPath(openclawHome);
|
|
127
|
-
const sharedSkillsPath = resolveOpenClawSharedSkillsPath(openclawHome);
|
|
128
|
-
ensureDir(openclawHome);
|
|
129
|
-
const mirrorConfig = buildMirrorConfig(config);
|
|
130
|
-
fs.writeFileSync(configPath, `${JSON.stringify(mirrorConfig, null, 2)}\n`, {
|
|
131
|
-
encoding: "utf8",
|
|
132
|
-
mode: 384
|
|
133
|
-
});
|
|
134
|
-
fs.chmodSync(configPath, 384);
|
|
135
|
-
return {
|
|
136
|
-
openclawHome,
|
|
137
|
-
configPath,
|
|
138
|
-
sharedSkillsPath,
|
|
139
|
-
linkedSharedSkills: syncSkillsDir(sharedSkillsPath, config.skillsDir)
|
|
140
|
-
};
|
|
141
|
-
}
|
|
142
|
-
function getOpenClawMirrorStatus(config) {
|
|
143
|
-
const openclawHome = resolveOpenClawHome();
|
|
144
|
-
const configPath = resolveOpenClawConfigPath(openclawHome);
|
|
145
|
-
const sharedSkillsPath = resolveOpenClawSharedSkillsPath(openclawHome);
|
|
146
|
-
const expectedConfig = buildMirrorConfig(config);
|
|
147
|
-
const currentConfig = readJsonIfExists(configPath);
|
|
148
|
-
let linkedSharedSkills = false;
|
|
149
|
-
try {
|
|
150
|
-
if (fs.lstatSync(sharedSkillsPath).isSymbolicLink()) {
|
|
151
|
-
const target = fs.readlinkSync(sharedSkillsPath);
|
|
152
|
-
linkedSharedSkills = path.resolve(path.dirname(sharedSkillsPath), target) === path.resolve(config.skillsDir);
|
|
153
|
-
}
|
|
154
|
-
} catch {
|
|
155
|
-
linkedSharedSkills = false;
|
|
156
|
-
}
|
|
157
|
-
return {
|
|
158
|
-
openclawHome,
|
|
159
|
-
configPath,
|
|
160
|
-
sharedSkillsPath,
|
|
161
|
-
linkedSharedSkills,
|
|
162
|
-
configExists: fs.existsSync(configPath),
|
|
163
|
-
configInSync: JSON.stringify(currentConfig) === JSON.stringify(expectedConfig),
|
|
164
|
-
skillsPathExists: fs.existsSync(sharedSkillsPath)
|
|
165
|
-
};
|
|
166
|
-
}
|
|
167
|
-
|
|
168
42
|
//#endregion
|
|
169
43
|
//#region src/config.ts
|
|
170
44
|
dotenv.config();
|
|
@@ -673,6 +547,14 @@ function ensureHomeContentDirs(projectRoot, hovclawHome) {
|
|
|
673
547
|
});
|
|
674
548
|
}
|
|
675
549
|
}
|
|
550
|
+
function ensureMainAgentConfig(agentsDir) {
|
|
551
|
+
const mainAgentPath = path.join(agentsDir, "main", "agent.json");
|
|
552
|
+
if (fs.existsSync(mainAgentPath)) return;
|
|
553
|
+
writeJsonFile(mainAgentPath, {
|
|
554
|
+
name: "Main",
|
|
555
|
+
skills: []
|
|
556
|
+
});
|
|
557
|
+
}
|
|
676
558
|
function ensureHomeStateDirs(projectRoot, hovclawHome) {
|
|
677
559
|
for (const dirName of USER_STATE_DIRS) {
|
|
678
560
|
const destinationDir = path.join(hovclawHome, dirName);
|
|
@@ -1022,8 +904,10 @@ function isLegacyProjectRootWorkspace(workspacePath, projectRoot) {
|
|
|
1022
904
|
function loadConfig(env = process.env) {
|
|
1023
905
|
const merged = applyEnvOverrides(loadConfigFile(env), env);
|
|
1024
906
|
const hovclawHome = getHovclawHome(env);
|
|
907
|
+
const agentsDir = path.join(hovclawHome, "agents");
|
|
1025
908
|
ensureHomeContentDirsOnce(PROJECT_ROOT, hovclawHome);
|
|
1026
909
|
ensureHomeStateDirsOnce(PROJECT_ROOT, hovclawHome);
|
|
910
|
+
ensureMainAgentConfig(agentsDir);
|
|
1027
911
|
const sharedSkillsDir = ensureSharedSkillsDirOnce(hovclawHome, env);
|
|
1028
912
|
const defaultWorkspace = path.join(hovclawHome, "workspace");
|
|
1029
913
|
const configuredWorkspace = merged.agents.defaults.workspace.trim();
|
|
@@ -1034,7 +918,7 @@ function loadConfig(env = process.env) {
|
|
|
1034
918
|
hovclawHome,
|
|
1035
919
|
configPath: getConfigPath(env),
|
|
1036
920
|
credentialsPath: getCredentialsPath(env),
|
|
1037
|
-
agentsDir
|
|
921
|
+
agentsDir,
|
|
1038
922
|
skillsDir: sharedSkillsDir,
|
|
1039
923
|
storeDir: path.join(hovclawHome, "store"),
|
|
1040
924
|
dataDir: path.join(hovclawHome, "data"),
|
|
@@ -1241,31 +1125,6 @@ function getProviderApiKeyFromEnv(provider, env = process.env) {
|
|
|
1241
1125
|
}
|
|
1242
1126
|
const config = loadConfig();
|
|
1243
1127
|
|
|
1244
|
-
//#endregion
|
|
1245
|
-
//#region src/cli/compat.ts
|
|
1246
|
-
function renderStatus(status) {
|
|
1247
|
-
const lines = [];
|
|
1248
|
-
lines.push(`OpenClaw home: ${status.openclawHome}`);
|
|
1249
|
-
lines.push(`Config path: ${status.configPath}`);
|
|
1250
|
-
lines.push(`Config exists: ${status.configExists ? "yes" : "no"}`);
|
|
1251
|
-
lines.push(`Config in sync: ${status.configInSync ? "yes" : "no"}`);
|
|
1252
|
-
lines.push(`Skills path: ${status.sharedSkillsPath}`);
|
|
1253
|
-
lines.push(`Skills exists: ${status.skillsPathExists ? "yes" : "no"}`);
|
|
1254
|
-
lines.push(`Skills symlinked: ${status.linkedSharedSkills ? "yes" : "no"}`);
|
|
1255
|
-
return lines.join("\n");
|
|
1256
|
-
}
|
|
1257
|
-
function registerCompatCommands(program) {
|
|
1258
|
-
program.command("compat").description("Compatibility helpers").command("status").description("Show OpenClaw mirror compatibility status").option("--sync", "Rewrite mirror before reading status").option("--json", "Print JSON output").action((options) => {
|
|
1259
|
-
if (options.sync) writeOpenClawMirror(config);
|
|
1260
|
-
const status = getOpenClawMirrorStatus(config);
|
|
1261
|
-
if (options.json) {
|
|
1262
|
-
process.stdout.write(`${JSON.stringify(status, null, 2)}\n`);
|
|
1263
|
-
return;
|
|
1264
|
-
}
|
|
1265
|
-
process.stdout.write(`${renderStatus(status)}\n`);
|
|
1266
|
-
});
|
|
1267
|
-
}
|
|
1268
|
-
|
|
1269
1128
|
//#endregion
|
|
1270
1129
|
//#region src/gateway/protocol/schema.ts
|
|
1271
1130
|
const nonEmptyStringSchema = z.string().min(1);
|
|
@@ -4445,7 +4304,6 @@ function readFileConfigForCli(env = process.env) {
|
|
|
4445
4304
|
}
|
|
4446
4305
|
function writeFileConfigForCli(next, env = process.env) {
|
|
4447
4306
|
saveConfigFile(next, env);
|
|
4448
|
-
writeOpenClawMirror(loadConfig(env));
|
|
4449
4307
|
}
|
|
4450
4308
|
|
|
4451
4309
|
//#endregion
|
|
@@ -5621,7 +5479,7 @@ function registerGatewayCommands(program, deps = defaultGatewayCommandDeps) {
|
|
|
5621
5479
|
gateway.command("run").description("Run HOVClaw daemon with gateway in foreground").action(async () => {
|
|
5622
5480
|
if (!config.gateway.enabled) throw new Error("Gateway is disabled in config. Enable gateway.enabled first.");
|
|
5623
5481
|
process.stdout.write(`Starting HOVClaw gateway on ${config.gateway.host}:${config.gateway.port}...\n`);
|
|
5624
|
-
await import("./src-
|
|
5482
|
+
await import("./src-qX1C6PLF.js");
|
|
5625
5483
|
});
|
|
5626
5484
|
gateway.command("open-ui").description("Open the minimal gateway web UI in your default browser").option("--no-open", "Print URL but do not open browser").option("--json", "Print JSON output").action(async (options) => {
|
|
5627
5485
|
const url = resolveGatewayWebUiUrl();
|
|
@@ -5925,7 +5783,7 @@ function registerCoreCommands(program) {
|
|
|
5925
5783
|
await (await import("./login-BwvBMKdz.js")).main(provider ? [provider] : []);
|
|
5926
5784
|
});
|
|
5927
5785
|
program.command("doctor").description("Run installation and config health checks").option("--fix", "Attempt auto-repair").option("--repair", "Attempt auto-repair").option("--deep", "Run deep checks").option("--json", "Print JSON output").action(async (options) => {
|
|
5928
|
-
const module = await import("./doctor-
|
|
5786
|
+
const module = await import("./doctor-DJHTvhli.js");
|
|
5929
5787
|
const args = [];
|
|
5930
5788
|
if (options.fix || options.repair) args.push("--fix");
|
|
5931
5789
|
if (options.deep) args.push("--deep");
|
|
@@ -6041,7 +5899,6 @@ async function main() {
|
|
|
6041
5899
|
registerPairingCommands(program);
|
|
6042
5900
|
registerGatewayCommands(program);
|
|
6043
5901
|
registerSkillsCommands(program);
|
|
6044
|
-
registerCompatCommands(program);
|
|
6045
5902
|
await program.parseAsync(process.argv);
|
|
6046
5903
|
}
|
|
6047
5904
|
main().catch((error) => {
|
|
@@ -6050,4 +5907,4 @@ main().catch((error) => {
|
|
|
6050
5907
|
});
|
|
6051
5908
|
|
|
6052
5909
|
//#endregion
|
|
6053
|
-
export { ensureConfigFromLegacyEnv as A, resolveTelegramAccountConfig as B, resolveModelAlias as C, parseGatewayFrame as D, parseConnectParams as E, hasConfigFile as F, saveCredentials as H, hasCredentialsFile as I, loadConfig as L, getCredentialsPath as M, getDefaultFileConfig as N, config as O, getHovclawHome as P, loadCredentials as R, parseModelRef as S, PROTOCOL_VERSION as T,
|
|
5910
|
+
export { ensureConfigFromLegacyEnv as A, resolveTelegramAccountConfig as B, resolveModelAlias as C, parseGatewayFrame as D, parseConnectParams as E, hasConfigFile as F, saveCredentials as H, hasCredentialsFile as I, loadConfig as L, getCredentialsPath as M, getDefaultFileConfig as N, config as O, getHovclawHome as P, loadCredentials as R, parseModelRef as S, PROTOCOL_VERSION as T, saveConfigFile as V, extractAssistantText as _, LocalHostRuntime as a, loadSkill as b, redactSensitiveData as c, PiAgentManager as d, composeSessionKey as f, extractAssistantError as g, resolveAgentWorkspaceDir as h, createTools as i, getConfigPath as j, detectLegacyEnvConfig as k, TelegramChannel as l, ensureWorkspaceBootstrapForConfig as m, stopDaemon as n, ContainerRuntime as o, WORKSPACE_CONTEXT_FILE_ORDER as p, TelegramPairingStore as r, HovClawDb as s, requestDaemonRestartFromCurrentProcess as t, DiscordChannel as u, toUserFacingAssistantError as v, logger as w, listConfiguredModelRefs as x, listAvailableSkills as y, loadFileConfig as z };
|
package/dist/index.js
CHANGED
|
@@ -528,6 +528,14 @@ function ensureHomeContentDirs(projectRoot, hovclawHome) {
|
|
|
528
528
|
});
|
|
529
529
|
}
|
|
530
530
|
}
|
|
531
|
+
function ensureMainAgentConfig(agentsDir) {
|
|
532
|
+
const mainAgentPath = path.join(agentsDir, "main", "agent.json");
|
|
533
|
+
if (fs.existsSync(mainAgentPath)) return;
|
|
534
|
+
writeJsonFile(mainAgentPath, {
|
|
535
|
+
name: "Main",
|
|
536
|
+
skills: []
|
|
537
|
+
});
|
|
538
|
+
}
|
|
531
539
|
function ensureHomeStateDirs(projectRoot, hovclawHome) {
|
|
532
540
|
for (const dirName of USER_STATE_DIRS) {
|
|
533
541
|
const destinationDir = path.join(hovclawHome, dirName);
|
|
@@ -877,8 +885,10 @@ function isLegacyProjectRootWorkspace(workspacePath, projectRoot) {
|
|
|
877
885
|
function loadConfig(env = process.env) {
|
|
878
886
|
const merged = applyEnvOverrides(loadConfigFile(env), env);
|
|
879
887
|
const hovclawHome = getHovclawHome(env);
|
|
888
|
+
const agentsDir = path.join(hovclawHome, "agents");
|
|
880
889
|
ensureHomeContentDirsOnce(PROJECT_ROOT, hovclawHome);
|
|
881
890
|
ensureHomeStateDirsOnce(PROJECT_ROOT, hovclawHome);
|
|
891
|
+
ensureMainAgentConfig(agentsDir);
|
|
882
892
|
const sharedSkillsDir = ensureSharedSkillsDirOnce(hovclawHome, env);
|
|
883
893
|
const defaultWorkspace = path.join(hovclawHome, "workspace");
|
|
884
894
|
const configuredWorkspace = merged.agents.defaults.workspace.trim();
|
|
@@ -889,7 +899,7 @@ function loadConfig(env = process.env) {
|
|
|
889
899
|
hovclawHome,
|
|
890
900
|
configPath: getConfigPath(env),
|
|
891
901
|
credentialsPath: getCredentialsPath(env),
|
|
892
|
-
agentsDir
|
|
902
|
+
agentsDir,
|
|
893
903
|
skillsDir: sharedSkillsDir,
|
|
894
904
|
storeDir: path.join(hovclawHome, "store"),
|
|
895
905
|
dataDir: path.join(hovclawHome, "data"),
|
|
@@ -3074,99 +3084,6 @@ async function requestDaemonRestartFromCurrentProcess(env = process.env) {
|
|
|
3074
3084
|
};
|
|
3075
3085
|
}
|
|
3076
3086
|
|
|
3077
|
-
//#endregion
|
|
3078
|
-
//#region src/compat/openclaw-mirror.ts
|
|
3079
|
-
function resolveOpenClawHome(env = process.env) {
|
|
3080
|
-
const override = env.OPENCLAW_STATE_DIR?.trim();
|
|
3081
|
-
if (override) return path.resolve(override.startsWith("~") ? path.join(os.homedir(), override.slice(1)) : override);
|
|
3082
|
-
return path.join(os.homedir(), ".openclaw");
|
|
3083
|
-
}
|
|
3084
|
-
function resolveOpenClawConfigPath(openclawHome) {
|
|
3085
|
-
return path.join(openclawHome, "openclaw.json");
|
|
3086
|
-
}
|
|
3087
|
-
function resolveOpenClawSharedSkillsPath(openclawHome) {
|
|
3088
|
-
return path.join(openclawHome, "skills");
|
|
3089
|
-
}
|
|
3090
|
-
function buildMirrorConfig(config) {
|
|
3091
|
-
const fallbackWorkspace = config.agents.defaults.workspace || path.join(config.hovclawHome, "workspace");
|
|
3092
|
-
const agentList = config.agents.list.length > 0 ? config.agents.list : [{
|
|
3093
|
-
id: "main",
|
|
3094
|
-
name: "Main",
|
|
3095
|
-
workspace: fallbackWorkspace,
|
|
3096
|
-
default: true
|
|
3097
|
-
}];
|
|
3098
|
-
const extraDirs = /* @__PURE__ */ new Set();
|
|
3099
|
-
extraDirs.add(config.skillsDir);
|
|
3100
|
-
for (const agent of agentList) {
|
|
3101
|
-
const workspace = (agent.workspace || fallbackWorkspace).trim();
|
|
3102
|
-
if (!workspace) continue;
|
|
3103
|
-
extraDirs.add(path.join(workspace, "skills"));
|
|
3104
|
-
}
|
|
3105
|
-
return {
|
|
3106
|
-
agent: { workspace: fallbackWorkspace },
|
|
3107
|
-
agents: {
|
|
3108
|
-
defaults: { workspace: fallbackWorkspace },
|
|
3109
|
-
list: agentList
|
|
3110
|
-
},
|
|
3111
|
-
skills: { load: { extraDirs: Array.from(extraDirs) } }
|
|
3112
|
-
};
|
|
3113
|
-
}
|
|
3114
|
-
function ensureDir(dirPath) {
|
|
3115
|
-
fs.mkdirSync(dirPath, {
|
|
3116
|
-
recursive: true,
|
|
3117
|
-
mode: 448
|
|
3118
|
-
});
|
|
3119
|
-
}
|
|
3120
|
-
function syncSkillsDir(sharedSkillsPath, sourceSkillsPath) {
|
|
3121
|
-
if (!fs.existsSync(sourceSkillsPath)) {
|
|
3122
|
-
ensureDir(sharedSkillsPath);
|
|
3123
|
-
return false;
|
|
3124
|
-
}
|
|
3125
|
-
try {
|
|
3126
|
-
if (fs.lstatSync(sharedSkillsPath).isSymbolicLink()) {
|
|
3127
|
-
const linkTarget = fs.readlinkSync(sharedSkillsPath);
|
|
3128
|
-
if (path.resolve(path.dirname(sharedSkillsPath), linkTarget) === sourceSkillsPath) return true;
|
|
3129
|
-
fs.unlinkSync(sharedSkillsPath);
|
|
3130
|
-
}
|
|
3131
|
-
} catch {}
|
|
3132
|
-
if (!fs.existsSync(sharedSkillsPath)) try {
|
|
3133
|
-
fs.symlinkSync(sourceSkillsPath, sharedSkillsPath, "dir");
|
|
3134
|
-
return true;
|
|
3135
|
-
} catch {}
|
|
3136
|
-
ensureDir(sharedSkillsPath);
|
|
3137
|
-
for (const entry of fs.readdirSync(sourceSkillsPath, { withFileTypes: true })) {
|
|
3138
|
-
const src = path.join(sourceSkillsPath, entry.name);
|
|
3139
|
-
const dst = path.join(sharedSkillsPath, entry.name);
|
|
3140
|
-
if (entry.isDirectory()) {
|
|
3141
|
-
fs.cpSync(src, dst, {
|
|
3142
|
-
recursive: true,
|
|
3143
|
-
force: true
|
|
3144
|
-
});
|
|
3145
|
-
continue;
|
|
3146
|
-
}
|
|
3147
|
-
if (entry.isFile()) fs.copyFileSync(src, dst);
|
|
3148
|
-
}
|
|
3149
|
-
return false;
|
|
3150
|
-
}
|
|
3151
|
-
function writeOpenClawMirror(config) {
|
|
3152
|
-
const openclawHome = resolveOpenClawHome();
|
|
3153
|
-
const configPath = resolveOpenClawConfigPath(openclawHome);
|
|
3154
|
-
const sharedSkillsPath = resolveOpenClawSharedSkillsPath(openclawHome);
|
|
3155
|
-
ensureDir(openclawHome);
|
|
3156
|
-
const mirrorConfig = buildMirrorConfig(config);
|
|
3157
|
-
fs.writeFileSync(configPath, `${JSON.stringify(mirrorConfig, null, 2)}\n`, {
|
|
3158
|
-
encoding: "utf8",
|
|
3159
|
-
mode: 384
|
|
3160
|
-
});
|
|
3161
|
-
fs.chmodSync(configPath, 384);
|
|
3162
|
-
return {
|
|
3163
|
-
openclawHome,
|
|
3164
|
-
configPath,
|
|
3165
|
-
sharedSkillsPath,
|
|
3166
|
-
linkedSharedSkills: syncSkillsDir(sharedSkillsPath, config.skillsDir)
|
|
3167
|
-
};
|
|
3168
|
-
}
|
|
3169
|
-
|
|
3170
3087
|
//#endregion
|
|
3171
3088
|
//#region src/models/catalog.ts
|
|
3172
3089
|
function buildModelCatalog(aliases) {
|
|
@@ -3573,7 +3490,6 @@ function reloadRuntimeConfig() {
|
|
|
3573
3490
|
function persistFileConfig(next) {
|
|
3574
3491
|
saveConfigFile(next);
|
|
3575
3492
|
reloadRuntimeConfig();
|
|
3576
|
-
writeOpenClawMirror(config);
|
|
3577
3493
|
}
|
|
3578
3494
|
function parseConfigPath(raw) {
|
|
3579
3495
|
const pathValue = raw?.trim();
|
|
@@ -5073,14 +4989,12 @@ const configGetMethod = async (_params, context) => {
|
|
|
5073
4989
|
const configSetMethod = async (params, context) => {
|
|
5074
4990
|
const parsed = configSetParamsSchema.parse(params);
|
|
5075
4991
|
context.writeFileConfig(parsed.config);
|
|
5076
|
-
writeOpenClawMirror(loadConfig());
|
|
5077
4992
|
return { ok: true };
|
|
5078
4993
|
};
|
|
5079
4994
|
const configPatchMethod = async (params, context) => {
|
|
5080
4995
|
const parsed = configPatchParamsSchema.parse(params);
|
|
5081
4996
|
const merged = deepMerge(context.readFileConfig(), parsed.patch);
|
|
5082
4997
|
context.writeFileConfig(merged);
|
|
5083
|
-
writeOpenClawMirror(loadConfig());
|
|
5084
4998
|
return { ok: true };
|
|
5085
4999
|
};
|
|
5086
5000
|
|
|
@@ -7072,7 +6986,6 @@ async function main() {
|
|
|
7072
6986
|
} catch (error) {
|
|
7073
6987
|
logger.warn({ error }, "Workspace bootstrap failed");
|
|
7074
6988
|
}
|
|
7075
|
-
writeOpenClawMirror(config);
|
|
7076
6989
|
const db = new HovClawDb(path.join(config.storeDir, "hovclaw.db"));
|
|
7077
6990
|
db.ping();
|
|
7078
6991
|
const runtime = config.runtime.mode === "container" ? new ContainerRuntime({
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { A as ensureConfigFromLegacyEnv, B as resolveTelegramAccountConfig, C as resolveModelAlias, D as parseGatewayFrame, E as parseConnectParams, F as hasConfigFile, L as loadConfig, N as getDefaultFileConfig, O as config, S as parseModelRef, T as PROTOCOL_VERSION,
|
|
1
|
+
import { A as ensureConfigFromLegacyEnv, B as resolveTelegramAccountConfig, C as resolveModelAlias, D as parseGatewayFrame, E as parseConnectParams, F as hasConfigFile, L as loadConfig, N as getDefaultFileConfig, O as config, S as parseModelRef, T as PROTOCOL_VERSION, V as saveConfigFile, _ as extractAssistantText, a as LocalHostRuntime, b as loadSkill, c as redactSensitiveData, d as PiAgentManager, f as composeSessionKey, g as extractAssistantError, h as resolveAgentWorkspaceDir, i as createTools, l as TelegramChannel, m as ensureWorkspaceBootstrapForConfig, o as ContainerRuntime, p as WORKSPACE_CONTEXT_FILE_ORDER, r as TelegramPairingStore, s as HovClawDb, t as requestDaemonRestartFromCurrentProcess, u as DiscordChannel, v as toUserFacingAssistantError, w as logger, x as listConfiguredModelRefs, y as listAvailableSkills, z as loadFileConfig } from "./hovclaw.js";
|
|
2
2
|
import fs from "node:fs";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { fileURLToPath } from "node:url";
|
|
5
|
-
import { ZodError, z } from "zod";
|
|
6
5
|
import { randomUUID } from "node:crypto";
|
|
7
6
|
import WebSocket, { WebSocketServer } from "ws";
|
|
7
|
+
import { ZodError, z } from "zod";
|
|
8
8
|
import fs$1 from "node:fs/promises";
|
|
9
9
|
import { createServer } from "node:http";
|
|
10
10
|
import { spawnSync } from "node:child_process";
|
|
@@ -543,7 +543,6 @@ function reloadRuntimeConfig() {
|
|
|
543
543
|
function persistFileConfig(next) {
|
|
544
544
|
saveConfigFile(next);
|
|
545
545
|
reloadRuntimeConfig();
|
|
546
|
-
writeOpenClawMirror(config);
|
|
547
546
|
}
|
|
548
547
|
function parseConfigPath(raw) {
|
|
549
548
|
const pathValue = raw?.trim();
|
|
@@ -1483,14 +1482,12 @@ const configGetMethod = async (_params, context) => {
|
|
|
1483
1482
|
const configSetMethod = async (params, context) => {
|
|
1484
1483
|
const parsed = configSetParamsSchema.parse(params);
|
|
1485
1484
|
context.writeFileConfig(parsed.config);
|
|
1486
|
-
writeOpenClawMirror(loadConfig());
|
|
1487
1485
|
return { ok: true };
|
|
1488
1486
|
};
|
|
1489
1487
|
const configPatchMethod = async (params, context) => {
|
|
1490
1488
|
const parsed = configPatchParamsSchema.parse(params);
|
|
1491
1489
|
const merged = deepMerge(context.readFileConfig(), parsed.patch);
|
|
1492
1490
|
context.writeFileConfig(merged);
|
|
1493
|
-
writeOpenClawMirror(loadConfig());
|
|
1494
1491
|
return { ok: true };
|
|
1495
1492
|
};
|
|
1496
1493
|
|
|
@@ -2604,7 +2601,6 @@ async function main() {
|
|
|
2604
2601
|
} catch (error) {
|
|
2605
2602
|
logger.warn({ error }, "Workspace bootstrap failed");
|
|
2606
2603
|
}
|
|
2607
|
-
writeOpenClawMirror(config);
|
|
2608
2604
|
const db = new HovClawDb(path.join(config.storeDir, "hovclaw.db"));
|
|
2609
2605
|
db.ping();
|
|
2610
2606
|
const runtime = config.runtime.mode === "container" ? new ContainerRuntime({
|