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 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 with OpenClaw-compatible control surface</strong>
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 OpenClaw/ClawHub compatibility.");
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: path.join(hovclawHome, "agents"),
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-Y6AqidKn.js");
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-D52M80De.js");
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, writeOpenClawMirror as U, 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 };
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: path.join(hovclawHome, "agents"),
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, U as writeOpenClawMirror, 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";
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({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hovclaw",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "description": "Multi-channel AI agent gateway",
5
5
  "license": "MIT",
6
6
  "type": "module",