nextclaw-core 0.4.3 → 0.4.5

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.
Files changed (3) hide show
  1. package/dist/index.d.ts +177 -149
  2. package/dist/index.js +582 -162
  3. package/package.json +3 -2
package/dist/index.js CHANGED
@@ -1,15 +1,16 @@
1
1
  // src/agent/context.ts
2
- import { readFileSync as readFileSync3, existsSync as existsSync4 } from "fs";
2
+ import { readFileSync as readFileSync4, existsSync as existsSync4 } from "fs";
3
3
  import { join as join3, extname } from "path";
4
4
 
5
5
  // src/agent/memory.ts
6
- import { readFileSync, writeFileSync, existsSync as existsSync2, readdirSync } from "fs";
6
+ import { readFileSync as readFileSync2, writeFileSync, existsSync as existsSync2, readdirSync } from "fs";
7
7
  import { join } from "path";
8
8
 
9
9
  // src/utils/helpers.ts
10
- import { existsSync, mkdirSync } from "fs";
10
+ import { existsSync, mkdirSync, readFileSync } from "fs";
11
11
  import { homedir } from "os";
12
12
  import { resolve } from "path";
13
+ import { fileURLToPath } from "url";
13
14
 
14
15
  // src/config/brand.ts
15
16
  var ENV_APP_NAME_KEY = "NEXTCLAW_APP_NAME";
@@ -85,6 +86,17 @@ function expandHome(value) {
85
86
  }
86
87
  return value;
87
88
  }
89
+ function getPackageVersion() {
90
+ try {
91
+ const dir = resolve(fileURLToPath(new URL(".", import.meta.url)));
92
+ const pkgPath = resolve(dir, "..", "..", "package.json");
93
+ const raw = readFileSync(pkgPath, "utf-8");
94
+ const parsed = JSON.parse(raw);
95
+ return typeof parsed.version === "string" ? parsed.version : "0.0.0";
96
+ } catch {
97
+ return "0.0.0";
98
+ }
99
+ }
88
100
 
89
101
  // src/agent/memory.ts
90
102
  var MemoryStore = class {
@@ -103,7 +115,7 @@ var MemoryStore = class {
103
115
  readToday() {
104
116
  const todayFile = this.getTodayFile();
105
117
  if (existsSync2(todayFile)) {
106
- return readFileSync(todayFile, "utf-8");
118
+ return readFileSync2(todayFile, "utf-8");
107
119
  }
108
120
  return "";
109
121
  }
@@ -111,7 +123,7 @@ var MemoryStore = class {
111
123
  const todayFile = this.getTodayFile();
112
124
  let nextContent = content;
113
125
  if (existsSync2(todayFile)) {
114
- const existing = readFileSync(todayFile, "utf-8");
126
+ const existing = readFileSync2(todayFile, "utf-8");
115
127
  nextContent = `${existing}
116
128
  ${content}`;
117
129
  } else {
@@ -124,13 +136,13 @@ ${content}`;
124
136
  }
125
137
  readLongTerm() {
126
138
  if (existsSync2(this.memoryFile)) {
127
- return readFileSync(this.memoryFile, "utf-8");
139
+ return readFileSync2(this.memoryFile, "utf-8");
128
140
  }
129
141
  return "";
130
142
  }
131
143
  readWorkspaceMemory() {
132
144
  if (existsSync2(this.workspaceMemoryFile)) {
133
- return readFileSync(this.workspaceMemoryFile, "utf-8");
145
+ return readFileSync2(this.workspaceMemoryFile, "utf-8");
134
146
  }
135
147
  return "";
136
148
  }
@@ -146,7 +158,7 @@ ${content}`;
146
158
  const dateStr = date.toISOString().slice(0, 10);
147
159
  const path = join(this.memoryDir, `${dateStr}.md`);
148
160
  if (existsSync2(path)) {
149
- memories.push(readFileSync(path, "utf-8"));
161
+ memories.push(readFileSync2(path, "utf-8"));
150
162
  }
151
163
  }
152
164
  return memories.length ? memories.join("\n\n---\n\n") : "";
@@ -179,10 +191,10 @@ ${today}`);
179
191
  };
180
192
 
181
193
  // src/agent/skills.ts
182
- import { readFileSync as readFileSync2, existsSync as existsSync3, readdirSync as readdirSync2 } from "fs";
194
+ import { readFileSync as readFileSync3, existsSync as existsSync3, readdirSync as readdirSync2 } from "fs";
183
195
  import { join as join2 } from "path";
184
- import { fileURLToPath } from "url";
185
- var BUILTIN_SKILLS_DIR = join2(fileURLToPath(new URL(".", import.meta.url)), "skills");
196
+ import { fileURLToPath as fileURLToPath2 } from "url";
197
+ var BUILTIN_SKILLS_DIR = join2(fileURLToPath2(new URL(".", import.meta.url)), "skills");
186
198
  var SkillsLoader = class {
187
199
  constructor(workspace, builtinSkillsDir) {
188
200
  this.workspace = workspace;
@@ -223,11 +235,11 @@ var SkillsLoader = class {
223
235
  loadSkill(name) {
224
236
  const workspaceSkill = join2(this.workspaceSkills, name, "SKILL.md");
225
237
  if (existsSync3(workspaceSkill)) {
226
- return readFileSync2(workspaceSkill, "utf-8");
238
+ return readFileSync3(workspaceSkill, "utf-8");
227
239
  }
228
240
  const builtinSkill = join2(this.builtinSkills, name, "SKILL.md");
229
241
  if (existsSync3(builtinSkill)) {
230
- return readFileSync2(builtinSkill, "utf-8");
242
+ return readFileSync3(builtinSkill, "utf-8");
231
243
  }
232
244
  return null;
233
245
  }
@@ -601,7 +613,7 @@ Your workspace is at: ${this.workspace}
601
613
  for (const filename of fileList) {
602
614
  const filePath = join3(this.workspace, filename);
603
615
  if (existsSync4(filePath)) {
604
- const raw = readFileSync3(filePath, "utf-8").trim();
616
+ const raw = readFileSync4(filePath, "utf-8").trim();
605
617
  if (!raw) {
606
618
  continue;
607
619
  }
@@ -674,7 +686,7 @@ ${truncated}`;
674
686
  continue;
675
687
  }
676
688
  try {
677
- const b64 = readFileSync3(path).toString("base64");
689
+ const b64 = readFileSync4(path).toString("base64");
678
690
  images.push({ type: "image_url", image_url: { url: `data:${mime};base64,${b64}` } });
679
691
  } catch {
680
692
  continue;
@@ -746,7 +758,7 @@ var ToolRegistry = class {
746
758
  };
747
759
 
748
760
  // src/agent/tools/filesystem.ts
749
- import { readFileSync as readFileSync4, writeFileSync as writeFileSync2, existsSync as existsSync5, readdirSync as readdirSync3, statSync } from "fs";
761
+ import { readFileSync as readFileSync5, writeFileSync as writeFileSync2, existsSync as existsSync5, readdirSync as readdirSync3, statSync } from "fs";
750
762
  import { resolve as resolve2, dirname } from "path";
751
763
 
752
764
  // src/agent/tools/base.ts
@@ -861,7 +873,7 @@ var ReadFileTool = class extends Tool {
861
873
  if (!existsSync5(path)) {
862
874
  return `Error: File not found: ${path}`;
863
875
  }
864
- return readFileSync4(path, "utf-8");
876
+ return readFileSync5(path, "utf-8");
865
877
  }
866
878
  };
867
879
  var WriteFileTool = class extends Tool {
@@ -925,7 +937,7 @@ var EditFileTool = class extends Tool {
925
937
  }
926
938
  const oldText = String(params.oldText ?? "");
927
939
  const newText = String(params.newText ?? "");
928
- const content = readFileSync4(path, "utf-8");
940
+ const content = readFileSync5(path, "utf-8");
929
941
  if (!content.includes(oldText)) {
930
942
  return "Error: Text to replace not found";
931
943
  }
@@ -982,16 +994,17 @@ var ExecTool = class extends Tool {
982
994
  "\\brm\\s+-[rf]{1,2}\\b",
983
995
  "\\bdel\\s+/[fq]\\b",
984
996
  "\\brmdir\\s+/s\\b",
985
- "\\b(format|mkfs|diskpart)\\b",
986
997
  "\\bdd\\s+if=",
987
998
  ">\\s*/dev/sd",
988
999
  "\\b(shutdown|reboot|poweroff)\\b",
989
1000
  ":\\(\\)\\s*\\{.*\\};\\s*:"
990
1001
  ]).map((pattern) => new RegExp(pattern, "i"));
991
1002
  this.allowPatterns = (options.allowPatterns ?? []).map((pattern) => new RegExp(pattern, "i"));
1003
+ this.dangerousCommands = ["format", "diskpart", "mkfs"];
992
1004
  }
993
1005
  denyPatterns;
994
1006
  allowPatterns;
1007
+ dangerousCommands;
995
1008
  get name() {
996
1009
  return "exec";
997
1010
  }
@@ -1037,6 +1050,9 @@ ${stderr}`);
1037
1050
  }
1038
1051
  guardCommand(command, cwd) {
1039
1052
  const normalized = command.trim().toLowerCase();
1053
+ if (this.isDangerousCommand(normalized)) {
1054
+ return "Error: Command blocked by safety guard (dangerous pattern detected)";
1055
+ }
1040
1056
  for (const pattern of this.denyPatterns) {
1041
1057
  if (pattern.test(normalized)) {
1042
1058
  return "Error: Command blocked by safety guard (dangerous pattern detected)";
@@ -1062,6 +1078,26 @@ ${stderr}`);
1062
1078
  }
1063
1079
  return null;
1064
1080
  }
1081
+ isDangerousCommand(command) {
1082
+ const segments = command.split(/\s*(?:\|\||&&|;|\|)\s*/);
1083
+ for (const segment2 of segments) {
1084
+ const match = segment2.trim().match(/^(?:sudo\s+)?([^\s]+)/i);
1085
+ if (!match) {
1086
+ continue;
1087
+ }
1088
+ const token = match[1]?.toLowerCase() ?? "";
1089
+ if (!token) {
1090
+ continue;
1091
+ }
1092
+ if (this.dangerousCommands.includes(token)) {
1093
+ return true;
1094
+ }
1095
+ if (token.startsWith("mkfs")) {
1096
+ return true;
1097
+ }
1098
+ }
1099
+ return false;
1100
+ }
1065
1101
  };
1066
1102
  function truncateOutput(result, maxLen = 1e4) {
1067
1103
  if (result.length <= maxLen) {
@@ -1646,7 +1682,7 @@ var SessionsSendTool = class extends Tool {
1646
1682
  };
1647
1683
 
1648
1684
  // src/agent/tools/memory.ts
1649
- import { existsSync as existsSync6, readFileSync as readFileSync5, readdirSync as readdirSync4 } from "fs";
1685
+ import { existsSync as existsSync6, readFileSync as readFileSync6, readdirSync as readdirSync4 } from "fs";
1650
1686
  import { join as join4, resolve as resolve4 } from "path";
1651
1687
  var DEFAULT_LIMIT2 = 20;
1652
1688
  var DEFAULT_CONTEXT_LINES = 0;
@@ -1711,7 +1747,7 @@ var MemorySearchTool = class extends Tool {
1711
1747
  const results = [];
1712
1748
  const files = getMemoryFiles(this.workspace);
1713
1749
  for (const filePath of files) {
1714
- const content = readFileSync5(filePath, "utf-8");
1750
+ const content = readFileSync6(filePath, "utf-8");
1715
1751
  const lines = content.split("\n");
1716
1752
  for (let i = 0; i < lines.length; i += 1) {
1717
1753
  if (lines[i].toLowerCase().includes(lowerQuery)) {
@@ -1775,7 +1811,7 @@ var MemoryGetTool = class extends Tool {
1775
1811
  if (!existsSync6(resolvedPath)) {
1776
1812
  return `Error: file not found: ${resolvedPath}`;
1777
1813
  }
1778
- const content = readFileSync5(resolvedPath, "utf-8");
1814
+ const content = readFileSync6(resolvedPath, "utf-8");
1779
1815
  const lines = content.split("\n");
1780
1816
  const startLine = toInt2(params.from ?? params.startLine, 1);
1781
1817
  const requestedLines = toInt2(params.lines ?? params.endLine, Math.max(lines.length - startLine + 1, 1));
@@ -2244,7 +2280,7 @@ When you have completed the task, provide a clear summary of your findings or ac
2244
2280
  };
2245
2281
 
2246
2282
  // src/session/manager.ts
2247
- import { readFileSync as readFileSync6, writeFileSync as writeFileSync3, existsSync as existsSync7, readdirSync as readdirSync5, unlinkSync } from "fs";
2283
+ import { readFileSync as readFileSync7, writeFileSync as writeFileSync3, existsSync as existsSync7, readdirSync as readdirSync5, unlinkSync } from "fs";
2248
2284
  import { join as join5 } from "path";
2249
2285
  var SessionManager = class {
2250
2286
  constructor(workspace) {
@@ -2326,7 +2362,7 @@ var SessionManager = class {
2326
2362
  return null;
2327
2363
  }
2328
2364
  try {
2329
- const lines = readFileSync6(path, "utf-8").split("\n").filter(Boolean);
2365
+ const lines = readFileSync7(path, "utf-8").split("\n").filter(Boolean);
2330
2366
  const messages = [];
2331
2367
  let metadata = {};
2332
2368
  let createdAt = /* @__PURE__ */ new Date();
@@ -2385,7 +2421,7 @@ var SessionManager = class {
2385
2421
  continue;
2386
2422
  }
2387
2423
  const path = join5(this.sessionsDir, entry.name);
2388
- const firstLine = readFileSync6(path, "utf-8").split("\n")[0];
2424
+ const firstLine = readFileSync7(path, "utf-8").split("\n")[0];
2389
2425
  if (!firstLine) {
2390
2426
  continue;
2391
2427
  }
@@ -3610,7 +3646,7 @@ function parseMdTable(tableText) {
3610
3646
  import { io } from "socket.io-client";
3611
3647
  import { fetch as fetch5 } from "undici";
3612
3648
  import { join as join8 } from "path";
3613
- import { mkdirSync as mkdirSync4, existsSync as existsSync9, readFileSync as readFileSync7, writeFileSync as writeFileSync5 } from "fs";
3649
+ import { mkdirSync as mkdirSync4, existsSync as existsSync9, readFileSync as readFileSync8, writeFileSync as writeFileSync5 } from "fs";
3614
3650
  var MAX_SEEN_MESSAGE_IDS = 2e3;
3615
3651
  var CURSOR_SAVE_DEBOUNCE_MS = 500;
3616
3652
  var AsyncLock = class {
@@ -4323,7 +4359,7 @@ var MochatChannel = class extends BaseChannel {
4323
4359
  return;
4324
4360
  }
4325
4361
  try {
4326
- const raw = readFileSync7(this.cursorPath, "utf-8");
4362
+ const raw = readFileSync8(this.cursorPath, "utf-8");
4327
4363
  const data = JSON.parse(raw);
4328
4364
  const cursors = data.cursors;
4329
4365
  if (cursors && typeof cursors === "object") {
@@ -5279,12 +5315,13 @@ var ChannelManager = class {
5279
5315
  };
5280
5316
 
5281
5317
  // src/config/loader.ts
5282
- import { readFileSync as readFileSync8, writeFileSync as writeFileSync6, existsSync as existsSync10, mkdirSync as mkdirSync5 } from "fs";
5318
+ import { readFileSync as readFileSync9, writeFileSync as writeFileSync6, existsSync as existsSync10, mkdirSync as mkdirSync5 } from "fs";
5283
5319
  import { resolve as resolve5 } from "path";
5284
- import { z as z2 } from "zod";
5320
+ import { z as z3 } from "zod";
5285
5321
 
5286
5322
  // src/config/schema.ts
5287
- import { z } from "zod";
5323
+ import { z as z2 } from "zod";
5324
+ import { zodToJsonSchema } from "zod-to-json-schema";
5288
5325
 
5289
5326
  // src/providers/registry.ts
5290
5327
  var PROVIDERS = [
@@ -5517,117 +5554,419 @@ function providerLabel(spec) {
5517
5554
  return spec.displayName || spec.name;
5518
5555
  }
5519
5556
 
5557
+ // src/config/schema.hints.ts
5558
+ import { z } from "zod";
5559
+
5560
+ // src/config/schema.help.ts
5561
+ var FIELD_HELP = {
5562
+ "agents.defaults.workspace": "Workspace directory for agent context files and memory.",
5563
+ "agents.defaults.model": "Default model identifier used by the agent.",
5564
+ "agents.defaults.maxTokens": "Maximum tokens per response.",
5565
+ "agents.defaults.temperature": "Sampling temperature for model responses.",
5566
+ "agents.defaults.maxToolIterations": "Maximum tool calls per turn.",
5567
+ "agents.context.bootstrap.files": "Files injected into the system prompt at startup.",
5568
+ "agents.context.bootstrap.minimalFiles": "Minimal file set used for low-context runs.",
5569
+ "agents.context.bootstrap.heartbeatFiles": "Files checked periodically for tasks.",
5570
+ "agents.context.bootstrap.perFileChars": "Max chars per bootstrap file.",
5571
+ "agents.context.bootstrap.totalChars": "Max total chars across bootstrap files.",
5572
+ "agents.context.memory.enabled": "Enable memory injection from memory files.",
5573
+ "agents.context.memory.maxChars": "Max characters of memory injected per turn.",
5574
+ "providers.*.apiKey": "API key for this provider.",
5575
+ "providers.*.apiBase": "Override the provider API base URL if needed.",
5576
+ "providers.*.extraHeaders": "Extra headers to send to the provider.",
5577
+ "providers.*.wireApi": "Select API mode (auto/chat/responses) for providers that support it.",
5578
+ "gateway.host": "Bind address for the gateway server.",
5579
+ "gateway.port": "Port for the gateway server.",
5580
+ "ui.enabled": "Enable the built-in UI server.",
5581
+ "ui.host": "Bind address for the UI server.",
5582
+ "ui.port": "Port for the UI server.",
5583
+ "ui.open": "Open the browser when UI starts.",
5584
+ "tools.web.search.apiKey": "API key for the configured web search provider.",
5585
+ "tools.web.search.maxResults": "Default number of search results.",
5586
+ "tools.exec.timeout": "Command execution timeout (seconds).",
5587
+ "tools.restrictToWorkspace": "Restrict tools to the workspace directory.",
5588
+ "channels.whatsapp.bridgeUrl": "WebSocket URL for the WhatsApp bridge.",
5589
+ "channels.telegram.token": "Telegram bot token.",
5590
+ "channels.telegram.proxy": "Optional HTTP proxy for Telegram.",
5591
+ "channels.discord.token": "Discord bot token.",
5592
+ "channels.discord.gatewayUrl": "Override Discord gateway URL.",
5593
+ "channels.discord.intents": "Gateway intents bitmask.",
5594
+ "channels.feishu.appId": "Feishu app ID.",
5595
+ "channels.feishu.appSecret": "Feishu app secret.",
5596
+ "channels.feishu.encryptKey": "Feishu encrypt key (if enabled).",
5597
+ "channels.feishu.verificationToken": "Feishu verification token.",
5598
+ "channels.dingtalk.clientId": "DingTalk client ID.",
5599
+ "channels.dingtalk.clientSecret": "DingTalk client secret.",
5600
+ "channels.mochat.baseUrl": "Base URL for Mochat API.",
5601
+ "channels.mochat.socketUrl": "WebSocket URL for Mochat (optional override).",
5602
+ "channels.mochat.socketPath": "Socket.IO path for Mochat.",
5603
+ "channels.mochat.clawToken": "Mochat claw token.",
5604
+ "channels.mochat.agentUserId": "Mochat agent user id.",
5605
+ "channels.mochat.replyDelayMode": "Reply delay policy for Mochat.",
5606
+ "channels.mochat.replyDelayMs": "Reply delay duration in milliseconds.",
5607
+ "channels.slack.mode": "Slack connection mode (socket or webhook).",
5608
+ "channels.slack.webhookPath": "Slack webhook path when using webhook mode.",
5609
+ "channels.slack.botToken": "Slack bot token.",
5610
+ "channels.slack.appToken": "Slack app-level token.",
5611
+ "channels.slack.groupPolicy": "Group policy for Slack channels.",
5612
+ "channels.slack.dm.policy": "DM policy for Slack.",
5613
+ "channels.email.imapHost": "IMAP server host.",
5614
+ "channels.email.imapPort": "IMAP server port.",
5615
+ "channels.email.imapUsername": "IMAP username.",
5616
+ "channels.email.imapPassword": "IMAP password.",
5617
+ "channels.email.smtpHost": "SMTP server host.",
5618
+ "channels.email.smtpPort": "SMTP server port.",
5619
+ "channels.email.smtpUsername": "SMTP username.",
5620
+ "channels.email.smtpPassword": "SMTP password.",
5621
+ "channels.email.fromAddress": "Default From address for outgoing mail.",
5622
+ "channels.email.pollIntervalSeconds": "Polling interval in seconds.",
5623
+ "channels.email.maxBodyChars": "Max chars to read from incoming emails.",
5624
+ "channels.email.subjectPrefix": "Prefix used for reply subjects.",
5625
+ "channels.qq.appId": "QQ app ID.",
5626
+ "channels.qq.secret": "QQ app secret."
5627
+ };
5628
+
5629
+ // src/config/schema.labels.ts
5630
+ var FIELD_LABELS = {
5631
+ agents: "Agents",
5632
+ "agents.defaults": "Agent Defaults",
5633
+ "agents.defaults.workspace": "Workspace",
5634
+ "agents.defaults.model": "Default Model",
5635
+ "agents.defaults.maxTokens": "Max Tokens",
5636
+ "agents.defaults.temperature": "Temperature",
5637
+ "agents.defaults.maxToolIterations": "Max Tool Iterations",
5638
+ "agents.context": "Context",
5639
+ "agents.context.bootstrap": "Bootstrap Files",
5640
+ "agents.context.bootstrap.files": "Bootstrap Files",
5641
+ "agents.context.bootstrap.minimalFiles": "Minimal Files",
5642
+ "agents.context.bootstrap.heartbeatFiles": "Heartbeat Files",
5643
+ "agents.context.bootstrap.perFileChars": "Max Chars per File",
5644
+ "agents.context.bootstrap.totalChars": "Total Bootstrap Chars",
5645
+ "agents.context.memory": "Memory",
5646
+ "agents.context.memory.enabled": "Enable Memory",
5647
+ "agents.context.memory.maxChars": "Max Memory Chars",
5648
+ providers: "Providers",
5649
+ "providers.*.apiKey": "API Key",
5650
+ "providers.*.apiBase": "API Base URL",
5651
+ "providers.*.extraHeaders": "Extra Headers",
5652
+ "providers.*.wireApi": "Wire API Mode",
5653
+ channels: "Channels",
5654
+ gateway: "Gateway",
5655
+ "gateway.host": "Gateway Host",
5656
+ "gateway.port": "Gateway Port",
5657
+ ui: "UI",
5658
+ "ui.enabled": "Enable UI",
5659
+ "ui.host": "UI Host",
5660
+ "ui.port": "UI Port",
5661
+ "ui.open": "Open Browser",
5662
+ tools: "Tools",
5663
+ "tools.web": "Web Tools",
5664
+ "tools.web.search": "Web Search",
5665
+ "tools.web.search.apiKey": "Search API Key",
5666
+ "tools.web.search.maxResults": "Max Results",
5667
+ "tools.exec": "Command Execution",
5668
+ "tools.exec.timeout": "Exec Timeout (seconds)",
5669
+ "tools.restrictToWorkspace": "Restrict Tools to Workspace",
5670
+ "channels.whatsapp": "WhatsApp",
5671
+ "channels.whatsapp.enabled": "Enabled",
5672
+ "channels.whatsapp.bridgeUrl": "Bridge URL",
5673
+ "channels.whatsapp.allowFrom": "Allow From",
5674
+ "channels.telegram": "Telegram",
5675
+ "channels.telegram.enabled": "Enabled",
5676
+ "channels.telegram.token": "Bot Token",
5677
+ "channels.telegram.allowFrom": "Allow From",
5678
+ "channels.telegram.proxy": "Proxy",
5679
+ "channels.discord": "Discord",
5680
+ "channels.discord.enabled": "Enabled",
5681
+ "channels.discord.token": "Bot Token",
5682
+ "channels.discord.allowFrom": "Allow From",
5683
+ "channels.discord.gatewayUrl": "Gateway URL",
5684
+ "channels.discord.intents": "Gateway Intents",
5685
+ "channels.feishu": "Feishu",
5686
+ "channels.feishu.enabled": "Enabled",
5687
+ "channels.feishu.appId": "App ID",
5688
+ "channels.feishu.appSecret": "App Secret",
5689
+ "channels.feishu.encryptKey": "Encrypt Key",
5690
+ "channels.feishu.verificationToken": "Verification Token",
5691
+ "channels.feishu.allowFrom": "Allow From",
5692
+ "channels.dingtalk": "DingTalk",
5693
+ "channels.dingtalk.enabled": "Enabled",
5694
+ "channels.dingtalk.clientId": "Client ID",
5695
+ "channels.dingtalk.clientSecret": "Client Secret",
5696
+ "channels.dingtalk.allowFrom": "Allow From",
5697
+ "channels.mochat": "Mochat",
5698
+ "channels.mochat.enabled": "Enabled",
5699
+ "channels.mochat.baseUrl": "Base URL",
5700
+ "channels.mochat.socketUrl": "Socket URL",
5701
+ "channels.mochat.socketPath": "Socket Path",
5702
+ "channels.mochat.clawToken": "Claw Token",
5703
+ "channels.mochat.agentUserId": "Agent User ID",
5704
+ "channels.mochat.allowFrom": "Allow From",
5705
+ "channels.mochat.replyDelayMode": "Reply Delay Mode",
5706
+ "channels.mochat.replyDelayMs": "Reply Delay (ms)",
5707
+ "channels.slack": "Slack",
5708
+ "channels.slack.enabled": "Enabled",
5709
+ "channels.slack.mode": "Mode",
5710
+ "channels.slack.webhookPath": "Webhook Path",
5711
+ "channels.slack.botToken": "Bot Token",
5712
+ "channels.slack.appToken": "App Token",
5713
+ "channels.slack.userTokenReadOnly": "User Token Read Only",
5714
+ "channels.slack.groupPolicy": "Group Policy",
5715
+ "channels.slack.groupAllowFrom": "Group Allow From",
5716
+ "channels.slack.dm": "DM",
5717
+ "channels.slack.dm.enabled": "Enabled",
5718
+ "channels.slack.dm.policy": "DM Policy",
5719
+ "channels.slack.dm.allowFrom": "Allow From",
5720
+ "channels.email": "Email",
5721
+ "channels.email.enabled": "Enabled",
5722
+ "channels.email.imapHost": "IMAP Host",
5723
+ "channels.email.imapPort": "IMAP Port",
5724
+ "channels.email.imapUsername": "IMAP Username",
5725
+ "channels.email.imapPassword": "IMAP Password",
5726
+ "channels.email.smtpHost": "SMTP Host",
5727
+ "channels.email.smtpPort": "SMTP Port",
5728
+ "channels.email.smtpUsername": "SMTP Username",
5729
+ "channels.email.smtpPassword": "SMTP Password",
5730
+ "channels.email.fromAddress": "From Address",
5731
+ "channels.email.pollIntervalSeconds": "Poll Interval (seconds)",
5732
+ "channels.email.maxBodyChars": "Max Body Chars",
5733
+ "channels.email.subjectPrefix": "Subject Prefix",
5734
+ "channels.email.allowFrom": "Allow From",
5735
+ "channels.qq": "QQ",
5736
+ "channels.qq.enabled": "Enabled",
5737
+ "channels.qq.appId": "App ID",
5738
+ "channels.qq.secret": "App Secret",
5739
+ "channels.qq.markdownSupport": "Markdown Support",
5740
+ "channels.qq.allowFrom": "Allow From"
5741
+ };
5742
+
5743
+ // src/config/schema.hints.ts
5744
+ var GROUP_LABELS = {
5745
+ agents: "Agents",
5746
+ providers: "Providers",
5747
+ channels: "Channels",
5748
+ tools: "Tools",
5749
+ gateway: "Gateway",
5750
+ ui: "UI"
5751
+ };
5752
+ var GROUP_ORDER = {
5753
+ agents: 20,
5754
+ providers: 30,
5755
+ channels: 40,
5756
+ tools: 50,
5757
+ gateway: 60,
5758
+ ui: 70
5759
+ };
5760
+ var FIELD_PLACEHOLDERS = {
5761
+ "gateway.host": "0.0.0.0",
5762
+ "gateway.port": "18790",
5763
+ "ui.host": "127.0.0.1",
5764
+ "ui.port": "18791",
5765
+ "providers.*.apiBase": "https://api.example.com"
5766
+ };
5767
+ var SENSITIVE_KEY_WHITELIST_SUFFIXES = [
5768
+ "maxtokens",
5769
+ "maxoutputtokens",
5770
+ "maxinputtokens",
5771
+ "maxcompletiontokens",
5772
+ "contexttokens",
5773
+ "totaltokens",
5774
+ "tokencount",
5775
+ "tokenlimit",
5776
+ "tokenbudget"
5777
+ ];
5778
+ var NORMALIZED_SENSITIVE_KEY_WHITELIST_SUFFIXES = SENSITIVE_KEY_WHITELIST_SUFFIXES.map(
5779
+ (suffix) => suffix.toLowerCase()
5780
+ );
5781
+ var SENSITIVE_PATTERNS = [/token$/i, /password/i, /secret/i, /api.?key/i];
5782
+ function isWhitelistedSensitivePath(path) {
5783
+ const lowerPath = path.toLowerCase();
5784
+ return NORMALIZED_SENSITIVE_KEY_WHITELIST_SUFFIXES.some((suffix) => lowerPath.endsWith(suffix));
5785
+ }
5786
+ function matchesSensitivePattern(path) {
5787
+ return SENSITIVE_PATTERNS.some((pattern) => pattern.test(path));
5788
+ }
5789
+ function isSensitiveConfigPath(path) {
5790
+ return !isWhitelistedSensitivePath(path) && matchesSensitivePattern(path);
5791
+ }
5792
+ function buildBaseHints() {
5793
+ const hints = {};
5794
+ for (const [group, label] of Object.entries(GROUP_LABELS)) {
5795
+ hints[group] = {
5796
+ label,
5797
+ group: label,
5798
+ order: GROUP_ORDER[group]
5799
+ };
5800
+ }
5801
+ for (const [path, label] of Object.entries(FIELD_LABELS)) {
5802
+ const current = hints[path];
5803
+ hints[path] = current ? { ...current, label } : { label };
5804
+ }
5805
+ for (const [path, help] of Object.entries(FIELD_HELP)) {
5806
+ const current = hints[path];
5807
+ hints[path] = current ? { ...current, help } : { help };
5808
+ }
5809
+ for (const [path, placeholder] of Object.entries(FIELD_PLACEHOLDERS)) {
5810
+ const current = hints[path];
5811
+ hints[path] = current ? { ...current, placeholder } : { placeholder };
5812
+ }
5813
+ return hints;
5814
+ }
5815
+ function applySensitiveHints(hints) {
5816
+ const next = { ...hints };
5817
+ for (const key of Object.keys(next)) {
5818
+ if (next[key]?.sensitive !== void 0) {
5819
+ continue;
5820
+ }
5821
+ if (isSensitiveConfigPath(key)) {
5822
+ next[key] = { ...next[key], sensitive: true };
5823
+ }
5824
+ }
5825
+ return next;
5826
+ }
5827
+ function isUnwrappable(object) {
5828
+ return !!object && typeof object === "object" && "unwrap" in object && typeof object.unwrap === "function" && !(object instanceof z.ZodArray);
5829
+ }
5830
+ function mapSensitivePaths(schema, path, hints) {
5831
+ let next = { ...hints };
5832
+ let currentSchema = schema;
5833
+ while (isUnwrappable(currentSchema)) {
5834
+ currentSchema = currentSchema.unwrap();
5835
+ }
5836
+ if (path && isSensitiveConfigPath(path) && !next[path]?.sensitive) {
5837
+ next[path] = { ...next[path], sensitive: true };
5838
+ }
5839
+ if (currentSchema instanceof z.ZodObject) {
5840
+ const shape = currentSchema.shape;
5841
+ for (const key in shape) {
5842
+ const nextPath = path ? `${path}.${key}` : key;
5843
+ next = mapSensitivePaths(shape[key], nextPath, next);
5844
+ }
5845
+ } else if (currentSchema instanceof z.ZodArray) {
5846
+ const nextPath = path ? `${path}[]` : "[]";
5847
+ next = mapSensitivePaths(currentSchema.element, nextPath, next);
5848
+ } else if (currentSchema instanceof z.ZodRecord) {
5849
+ const nextPath = path ? `${path}.*` : "*";
5850
+ next = mapSensitivePaths(currentSchema._def.valueType, nextPath, next);
5851
+ } else if (currentSchema instanceof z.ZodUnion || currentSchema instanceof z.ZodDiscriminatedUnion) {
5852
+ for (const option of currentSchema.options) {
5853
+ next = mapSensitivePaths(option, path, next);
5854
+ }
5855
+ }
5856
+ return next;
5857
+ }
5858
+
5520
5859
  // src/config/schema.ts
5521
- var allowFrom = z.array(z.string()).default([]);
5522
- var WhatsAppConfigSchema = z.object({
5523
- enabled: z.boolean().default(false),
5524
- bridgeUrl: z.string().default("ws://localhost:3001"),
5860
+ var allowFrom = z2.array(z2.string()).default([]);
5861
+ var WhatsAppConfigSchema = z2.object({
5862
+ enabled: z2.boolean().default(false),
5863
+ bridgeUrl: z2.string().default("ws://localhost:3001"),
5525
5864
  allowFrom
5526
5865
  });
5527
- var TelegramConfigSchema = z.object({
5528
- enabled: z.boolean().default(false),
5529
- token: z.string().default(""),
5866
+ var TelegramConfigSchema = z2.object({
5867
+ enabled: z2.boolean().default(false),
5868
+ token: z2.string().default(""),
5530
5869
  allowFrom,
5531
- proxy: z.string().nullable().default(null)
5870
+ proxy: z2.string().nullable().default(null)
5532
5871
  });
5533
- var FeishuConfigSchema = z.object({
5534
- enabled: z.boolean().default(false),
5535
- appId: z.string().default(""),
5536
- appSecret: z.string().default(""),
5537
- encryptKey: z.string().default(""),
5538
- verificationToken: z.string().default(""),
5872
+ var FeishuConfigSchema = z2.object({
5873
+ enabled: z2.boolean().default(false),
5874
+ appId: z2.string().default(""),
5875
+ appSecret: z2.string().default(""),
5876
+ encryptKey: z2.string().default(""),
5877
+ verificationToken: z2.string().default(""),
5539
5878
  allowFrom
5540
5879
  });
5541
- var DingTalkConfigSchema = z.object({
5542
- enabled: z.boolean().default(false),
5543
- clientId: z.string().default(""),
5544
- clientSecret: z.string().default(""),
5880
+ var DingTalkConfigSchema = z2.object({
5881
+ enabled: z2.boolean().default(false),
5882
+ clientId: z2.string().default(""),
5883
+ clientSecret: z2.string().default(""),
5545
5884
  allowFrom
5546
5885
  });
5547
- var DiscordConfigSchema = z.object({
5548
- enabled: z.boolean().default(false),
5549
- token: z.string().default(""),
5886
+ var DiscordConfigSchema = z2.object({
5887
+ enabled: z2.boolean().default(false),
5888
+ token: z2.string().default(""),
5550
5889
  allowFrom,
5551
- gatewayUrl: z.string().default("wss://gateway.discord.gg/?v=10&encoding=json"),
5552
- intents: z.number().int().default(37377)
5890
+ gatewayUrl: z2.string().default("wss://gateway.discord.gg/?v=10&encoding=json"),
5891
+ intents: z2.number().int().default(37377)
5553
5892
  });
5554
- var EmailConfigSchema = z.object({
5555
- enabled: z.boolean().default(false),
5556
- consentGranted: z.boolean().default(false),
5557
- imapHost: z.string().default(""),
5558
- imapPort: z.number().int().default(993),
5559
- imapUsername: z.string().default(""),
5560
- imapPassword: z.string().default(""),
5561
- imapMailbox: z.string().default("INBOX"),
5562
- imapUseSsl: z.boolean().default(true),
5563
- smtpHost: z.string().default(""),
5564
- smtpPort: z.number().int().default(587),
5565
- smtpUsername: z.string().default(""),
5566
- smtpPassword: z.string().default(""),
5567
- smtpUseTls: z.boolean().default(true),
5568
- smtpUseSsl: z.boolean().default(false),
5569
- fromAddress: z.string().default(""),
5570
- autoReplyEnabled: z.boolean().default(true),
5571
- pollIntervalSeconds: z.number().int().default(30),
5572
- markSeen: z.boolean().default(true),
5573
- maxBodyChars: z.number().int().default(12e3),
5574
- subjectPrefix: z.string().default("Re: "),
5893
+ var EmailConfigSchema = z2.object({
5894
+ enabled: z2.boolean().default(false),
5895
+ consentGranted: z2.boolean().default(false),
5896
+ imapHost: z2.string().default(""),
5897
+ imapPort: z2.number().int().default(993),
5898
+ imapUsername: z2.string().default(""),
5899
+ imapPassword: z2.string().default(""),
5900
+ imapMailbox: z2.string().default("INBOX"),
5901
+ imapUseSsl: z2.boolean().default(true),
5902
+ smtpHost: z2.string().default(""),
5903
+ smtpPort: z2.number().int().default(587),
5904
+ smtpUsername: z2.string().default(""),
5905
+ smtpPassword: z2.string().default(""),
5906
+ smtpUseTls: z2.boolean().default(true),
5907
+ smtpUseSsl: z2.boolean().default(false),
5908
+ fromAddress: z2.string().default(""),
5909
+ autoReplyEnabled: z2.boolean().default(true),
5910
+ pollIntervalSeconds: z2.number().int().default(30),
5911
+ markSeen: z2.boolean().default(true),
5912
+ maxBodyChars: z2.number().int().default(12e3),
5913
+ subjectPrefix: z2.string().default("Re: "),
5575
5914
  allowFrom
5576
5915
  });
5577
- var MochatMentionSchema = z.object({
5578
- requireInGroups: z.boolean().default(false)
5916
+ var MochatMentionSchema = z2.object({
5917
+ requireInGroups: z2.boolean().default(false)
5579
5918
  });
5580
- var MochatGroupRuleSchema = z.object({
5581
- requireMention: z.boolean().default(false)
5919
+ var MochatGroupRuleSchema = z2.object({
5920
+ requireMention: z2.boolean().default(false)
5582
5921
  });
5583
- var MochatConfigSchema = z.object({
5584
- enabled: z.boolean().default(false),
5585
- baseUrl: z.string().default("https://mochat.io"),
5586
- socketUrl: z.string().default(""),
5587
- socketPath: z.string().default("/socket.io"),
5588
- socketDisableMsgpack: z.boolean().default(false),
5589
- socketReconnectDelayMs: z.number().int().default(1e3),
5590
- socketMaxReconnectDelayMs: z.number().int().default(1e4),
5591
- socketConnectTimeoutMs: z.number().int().default(1e4),
5592
- refreshIntervalMs: z.number().int().default(3e4),
5593
- watchTimeoutMs: z.number().int().default(25e3),
5594
- watchLimit: z.number().int().default(100),
5595
- retryDelayMs: z.number().int().default(500),
5596
- maxRetryAttempts: z.number().int().default(0),
5597
- clawToken: z.string().default(""),
5598
- agentUserId: z.string().default(""),
5599
- sessions: z.array(z.string()).default([]),
5600
- panels: z.array(z.string()).default([]),
5922
+ var MochatConfigSchema = z2.object({
5923
+ enabled: z2.boolean().default(false),
5924
+ baseUrl: z2.string().default("https://mochat.io"),
5925
+ socketUrl: z2.string().default(""),
5926
+ socketPath: z2.string().default("/socket.io"),
5927
+ socketDisableMsgpack: z2.boolean().default(false),
5928
+ socketReconnectDelayMs: z2.number().int().default(1e3),
5929
+ socketMaxReconnectDelayMs: z2.number().int().default(1e4),
5930
+ socketConnectTimeoutMs: z2.number().int().default(1e4),
5931
+ refreshIntervalMs: z2.number().int().default(3e4),
5932
+ watchTimeoutMs: z2.number().int().default(25e3),
5933
+ watchLimit: z2.number().int().default(100),
5934
+ retryDelayMs: z2.number().int().default(500),
5935
+ maxRetryAttempts: z2.number().int().default(0),
5936
+ clawToken: z2.string().default(""),
5937
+ agentUserId: z2.string().default(""),
5938
+ sessions: z2.array(z2.string()).default([]),
5939
+ panels: z2.array(z2.string()).default([]),
5601
5940
  allowFrom,
5602
5941
  mention: MochatMentionSchema.default({}),
5603
- groups: z.record(MochatGroupRuleSchema).default({}),
5604
- replyDelayMode: z.string().default("non-mention"),
5605
- replyDelayMs: z.number().int().default(12e4)
5942
+ groups: z2.record(MochatGroupRuleSchema).default({}),
5943
+ replyDelayMode: z2.string().default("non-mention"),
5944
+ replyDelayMs: z2.number().int().default(12e4)
5606
5945
  });
5607
- var SlackDMSchema = z.object({
5608
- enabled: z.boolean().default(true),
5609
- policy: z.string().default("open"),
5946
+ var SlackDMSchema = z2.object({
5947
+ enabled: z2.boolean().default(true),
5948
+ policy: z2.string().default("open"),
5610
5949
  allowFrom
5611
5950
  });
5612
- var SlackConfigSchema = z.object({
5613
- enabled: z.boolean().default(false),
5614
- mode: z.string().default("socket"),
5615
- webhookPath: z.string().default("/slack/events"),
5616
- botToken: z.string().default(""),
5617
- appToken: z.string().default(""),
5618
- userTokenReadOnly: z.boolean().default(true),
5619
- groupPolicy: z.string().default("mention"),
5951
+ var SlackConfigSchema = z2.object({
5952
+ enabled: z2.boolean().default(false),
5953
+ mode: z2.string().default("socket"),
5954
+ webhookPath: z2.string().default("/slack/events"),
5955
+ botToken: z2.string().default(""),
5956
+ appToken: z2.string().default(""),
5957
+ userTokenReadOnly: z2.boolean().default(true),
5958
+ groupPolicy: z2.string().default("mention"),
5620
5959
  groupAllowFrom: allowFrom,
5621
5960
  dm: SlackDMSchema.default({})
5622
5961
  });
5623
- var QQConfigSchema = z.object({
5624
- enabled: z.boolean().default(false),
5625
- appId: z.string().default(""),
5626
- secret: z.string().default(""),
5627
- markdownSupport: z.boolean().default(false),
5962
+ var QQConfigSchema = z2.object({
5963
+ enabled: z2.boolean().default(false),
5964
+ appId: z2.string().default(""),
5965
+ secret: z2.string().default(""),
5966
+ markdownSupport: z2.boolean().default(false),
5628
5967
  allowFrom
5629
5968
  });
5630
- var ChannelsConfigSchema = z.object({
5969
+ var ChannelsConfigSchema = z2.object({
5631
5970
  whatsapp: WhatsAppConfigSchema.default({}),
5632
5971
  telegram: TelegramConfigSchema.default({}),
5633
5972
  discord: DiscordConfigSchema.default({}),
@@ -5638,15 +5977,15 @@ var ChannelsConfigSchema = z.object({
5638
5977
  slack: SlackConfigSchema.default({}),
5639
5978
  qq: QQConfigSchema.default({})
5640
5979
  });
5641
- var AgentDefaultsSchema = z.object({
5642
- workspace: z.string().default(DEFAULT_WORKSPACE_PATH),
5643
- model: z.string().default("anthropic/claude-opus-4-5"),
5644
- maxTokens: z.number().int().default(8192),
5645
- temperature: z.number().default(0.7),
5646
- maxToolIterations: z.number().int().default(20)
5980
+ var AgentDefaultsSchema = z2.object({
5981
+ workspace: z2.string().default(DEFAULT_WORKSPACE_PATH),
5982
+ model: z2.string().default("anthropic/claude-opus-4-5"),
5983
+ maxTokens: z2.number().int().default(8192),
5984
+ temperature: z2.number().default(0.7),
5985
+ maxToolIterations: z2.number().int().default(20)
5647
5986
  });
5648
- var ContextBootstrapSchema = z.object({
5649
- files: z.array(z.string()).default([
5987
+ var ContextBootstrapSchema = z2.object({
5988
+ files: z2.array(z2.string()).default([
5650
5989
  "AGENTS.md",
5651
5990
  "SOUL.md",
5652
5991
  "USER.md",
@@ -5656,30 +5995,30 @@ var ContextBootstrapSchema = z.object({
5656
5995
  "BOOTSTRAP.md",
5657
5996
  "HEARTBEAT.md"
5658
5997
  ]),
5659
- minimalFiles: z.array(z.string()).default(["AGENTS.md", "SOUL.md", "TOOLS.md", "IDENTITY.md"]),
5660
- heartbeatFiles: z.array(z.string()).default(["HEARTBEAT.md"]),
5661
- perFileChars: z.number().int().default(4e3),
5662
- totalChars: z.number().int().default(12e3)
5998
+ minimalFiles: z2.array(z2.string()).default(["AGENTS.md", "SOUL.md", "TOOLS.md", "IDENTITY.md"]),
5999
+ heartbeatFiles: z2.array(z2.string()).default(["HEARTBEAT.md"]),
6000
+ perFileChars: z2.number().int().default(4e3),
6001
+ totalChars: z2.number().int().default(12e3)
5663
6002
  });
5664
- var ContextMemorySchema = z.object({
5665
- enabled: z.boolean().default(true),
5666
- maxChars: z.number().int().default(8e3)
6003
+ var ContextMemorySchema = z2.object({
6004
+ enabled: z2.boolean().default(true),
6005
+ maxChars: z2.number().int().default(8e3)
5667
6006
  });
5668
- var ContextConfigSchema = z.object({
6007
+ var ContextConfigSchema = z2.object({
5669
6008
  bootstrap: ContextBootstrapSchema.default({}),
5670
6009
  memory: ContextMemorySchema.default({})
5671
6010
  });
5672
- var AgentsConfigSchema = z.object({
6011
+ var AgentsConfigSchema = z2.object({
5673
6012
  defaults: AgentDefaultsSchema.default({}),
5674
6013
  context: ContextConfigSchema.default({})
5675
6014
  });
5676
- var ProviderConfigSchema = z.object({
5677
- apiKey: z.string().default(""),
5678
- apiBase: z.string().nullable().default(null),
5679
- extraHeaders: z.record(z.string()).nullable().default(null),
5680
- wireApi: z.enum(["auto", "chat", "responses"]).default("auto")
6015
+ var ProviderConfigSchema = z2.object({
6016
+ apiKey: z2.string().default(""),
6017
+ apiBase: z2.string().nullable().default(null),
6018
+ extraHeaders: z2.record(z2.string()).nullable().default(null),
6019
+ wireApi: z2.enum(["auto", "chat", "responses"]).default("auto")
5681
6020
  });
5682
- var ProvidersConfigSchema = z.object({
6021
+ var ProvidersConfigSchema = z2.object({
5683
6022
  anthropic: ProviderConfigSchema.default({}),
5684
6023
  openai: ProviderConfigSchema.default({}),
5685
6024
  openrouter: ProviderConfigSchema.default({}),
@@ -5693,32 +6032,32 @@ var ProvidersConfigSchema = z.object({
5693
6032
  minimax: ProviderConfigSchema.default({}),
5694
6033
  aihubmix: ProviderConfigSchema.default({})
5695
6034
  });
5696
- var GatewayConfigSchema = z.object({
5697
- host: z.string().default("0.0.0.0"),
5698
- port: z.number().int().default(18790)
6035
+ var GatewayConfigSchema = z2.object({
6036
+ host: z2.string().default("0.0.0.0"),
6037
+ port: z2.number().int().default(18790)
5699
6038
  });
5700
- var UiConfigSchema = z.object({
5701
- enabled: z.boolean().default(false),
5702
- host: z.string().default("127.0.0.1"),
5703
- port: z.number().int().default(18791),
5704
- open: z.boolean().default(false)
6039
+ var UiConfigSchema = z2.object({
6040
+ enabled: z2.boolean().default(false),
6041
+ host: z2.string().default("127.0.0.1"),
6042
+ port: z2.number().int().default(18791),
6043
+ open: z2.boolean().default(false)
5705
6044
  });
5706
- var WebSearchConfigSchema = z.object({
5707
- apiKey: z.string().default(""),
5708
- maxResults: z.number().int().default(5)
6045
+ var WebSearchConfigSchema = z2.object({
6046
+ apiKey: z2.string().default(""),
6047
+ maxResults: z2.number().int().default(5)
5709
6048
  });
5710
- var WebToolsConfigSchema = z.object({
6049
+ var WebToolsConfigSchema = z2.object({
5711
6050
  search: WebSearchConfigSchema.default({})
5712
6051
  });
5713
- var ExecToolConfigSchema = z.object({
5714
- timeout: z.number().int().default(60)
6052
+ var ExecToolConfigSchema = z2.object({
6053
+ timeout: z2.number().int().default(60)
5715
6054
  });
5716
- var ToolsConfigSchema = z.object({
6055
+ var ToolsConfigSchema = z2.object({
5717
6056
  web: WebToolsConfigSchema.default({}),
5718
6057
  exec: ExecToolConfigSchema.default({}),
5719
- restrictToWorkspace: z.boolean().default(false)
6058
+ restrictToWorkspace: z2.boolean().default(false)
5720
6059
  });
5721
- var ConfigSchema = z.object({
6060
+ var ConfigSchema = z2.object({
5722
6061
  agents: AgentsConfigSchema.default({}),
5723
6062
  channels: ChannelsConfigSchema.default({}),
5724
6063
  providers: ProvidersConfigSchema.default({}),
@@ -5768,6 +6107,22 @@ function getApiBase(config, model) {
5768
6107
  }
5769
6108
  return null;
5770
6109
  }
6110
+ function buildConfigSchema(options) {
6111
+ const schema = zodToJsonSchema(ConfigSchema, {
6112
+ name: "NextClawConfig",
6113
+ target: "jsonSchema7"
6114
+ });
6115
+ if (schema && typeof schema === "object") {
6116
+ schema.title = "NextClawConfig";
6117
+ }
6118
+ const hints = mapSensitivePaths(ConfigSchema, "", buildBaseHints());
6119
+ return {
6120
+ schema,
6121
+ uiHints: hints,
6122
+ version: options?.version ?? getPackageVersion(),
6123
+ generatedAt: (/* @__PURE__ */ new Date()).toISOString()
6124
+ };
6125
+ }
5771
6126
 
5772
6127
  // src/config/loader.ts
5773
6128
  function getConfigPath() {
@@ -5780,12 +6135,12 @@ function loadConfig(configPath) {
5780
6135
  const path = configPath ?? getConfigPath();
5781
6136
  if (existsSync10(path)) {
5782
6137
  try {
5783
- const raw = readFileSync8(path, "utf-8");
6138
+ const raw = readFileSync9(path, "utf-8");
5784
6139
  const data = JSON.parse(raw);
5785
6140
  const migrated = migrateConfig(data);
5786
6141
  return ConfigSchema.parse(migrated);
5787
6142
  } catch (err) {
5788
- const message = err instanceof z2.ZodError ? err.message : String(err);
6143
+ const message = err instanceof z3.ZodError ? err.message : String(err);
5789
6144
  console.warn(`Warning: Failed to load config from ${path}: ${message}`);
5790
6145
  }
5791
6146
  }
@@ -5894,8 +6249,66 @@ function buildReloadPlan(changedPaths) {
5894
6249
  return plan;
5895
6250
  }
5896
6251
 
6252
+ // src/config/redact.ts
6253
+ function matchHint(path, hints) {
6254
+ const direct = hints[path];
6255
+ if (direct) {
6256
+ return direct;
6257
+ }
6258
+ const segments = path.split(".");
6259
+ for (const [hintKey, hint] of Object.entries(hints)) {
6260
+ if (!hintKey.includes("*")) {
6261
+ continue;
6262
+ }
6263
+ const hintSegments = hintKey.split(".");
6264
+ if (hintSegments.length !== segments.length) {
6265
+ continue;
6266
+ }
6267
+ let match = true;
6268
+ for (let i = 0; i < segments.length; i += 1) {
6269
+ if (hintSegments[i] !== "*" && hintSegments[i] !== segments[i]) {
6270
+ match = false;
6271
+ break;
6272
+ }
6273
+ }
6274
+ if (match) {
6275
+ return hint;
6276
+ }
6277
+ }
6278
+ return void 0;
6279
+ }
6280
+ function isSensitivePath(path, hints) {
6281
+ if (hints) {
6282
+ const hint = matchHint(path, hints);
6283
+ if (hint?.sensitive !== void 0) {
6284
+ return Boolean(hint.sensitive);
6285
+ }
6286
+ }
6287
+ return isSensitiveConfigPath(path);
6288
+ }
6289
+ function redactConfigObject(value, hints, prefix = "") {
6290
+ if (Array.isArray(value)) {
6291
+ const nextPath = prefix ? `${prefix}[]` : "[]";
6292
+ return value.map((entry) => redactConfigObject(entry, hints, nextPath));
6293
+ }
6294
+ if (!value || typeof value !== "object") {
6295
+ return value;
6296
+ }
6297
+ const entries = value;
6298
+ const output = {};
6299
+ for (const [key, val] of Object.entries(entries)) {
6300
+ const nextPath = prefix ? `${prefix}.${key}` : key;
6301
+ if (isSensitivePath(nextPath, hints)) {
6302
+ output[key] = val ? "***" : val;
6303
+ continue;
6304
+ }
6305
+ output[key] = redactConfigObject(val, hints, nextPath);
6306
+ }
6307
+ return output;
6308
+ }
6309
+
5897
6310
  // src/cron/service.ts
5898
- import { readFileSync as readFileSync9, writeFileSync as writeFileSync7, existsSync as existsSync11, mkdirSync as mkdirSync6 } from "fs";
6311
+ import { readFileSync as readFileSync10, writeFileSync as writeFileSync7, existsSync as existsSync11, mkdirSync as mkdirSync6 } from "fs";
5899
6312
  import { dirname as dirname2 } from "path";
5900
6313
  import { randomUUID as randomUUID2 } from "crypto";
5901
6314
  import cronParser from "cron-parser";
@@ -5935,7 +6348,7 @@ var CronService = class {
5935
6348
  }
5936
6349
  if (existsSync11(this.storePath)) {
5937
6350
  try {
5938
- const data = JSON.parse(readFileSync9(this.storePath, "utf-8"));
6351
+ const data = JSON.parse(readFileSync10(this.storePath, "utf-8"));
5939
6352
  const jobs = (data.jobs ?? []).map((job) => ({
5940
6353
  id: String(job.id),
5941
6354
  name: String(job.name),
@@ -6138,7 +6551,7 @@ var CronService = class {
6138
6551
  };
6139
6552
 
6140
6553
  // src/heartbeat/service.ts
6141
- import { readFileSync as readFileSync10, existsSync as existsSync12 } from "fs";
6554
+ import { readFileSync as readFileSync11, existsSync as existsSync12 } from "fs";
6142
6555
  import { join as join9 } from "path";
6143
6556
  var DEFAULT_HEARTBEAT_INTERVAL_S = 30 * 60;
6144
6557
  var HEARTBEAT_PROMPT = "Read HEARTBEAT.md in your workspace (if it exists).\nFollow any instructions or tasks listed there.\nIf nothing needs attention, reply with just: HEARTBEAT_OK";
@@ -6172,7 +6585,7 @@ var HeartbeatService = class {
6172
6585
  readHeartbeatFile() {
6173
6586
  if (existsSync12(this.heartbeatFile)) {
6174
6587
  try {
6175
- return readFileSync10(this.heartbeatFile, "utf-8");
6588
+ return readFileSync11(this.heartbeatFile, "utf-8");
6176
6589
  } catch {
6177
6590
  return null;
6178
6591
  }
@@ -6615,6 +7028,9 @@ export {
6615
7028
  WebSearchConfigSchema,
6616
7029
  WebToolsConfigSchema,
6617
7030
  WhatsAppConfigSchema,
7031
+ applySensitiveHints,
7032
+ buildBaseHints,
7033
+ buildConfigSchema,
6618
7034
  buildReloadPlan,
6619
7035
  diffConfigPaths,
6620
7036
  ensureDir,
@@ -6628,17 +7044,21 @@ export {
6628
7044
  getDataDir,
6629
7045
  getDataPath,
6630
7046
  getMemoryPath,
7047
+ getPackageVersion,
6631
7048
  getProvider,
6632
7049
  getProviderName,
6633
7050
  getSessionsPath,
6634
7051
  getSkillsPath,
6635
7052
  getWorkspacePath,
6636
7053
  getWorkspacePathFromConfig,
7054
+ isSensitiveConfigPath,
6637
7055
  loadConfig,
7056
+ mapSensitivePaths,
6638
7057
  matchProvider,
6639
7058
  parseSessionKey,
6640
7059
  probeFeishu,
6641
7060
  providerLabel,
7061
+ redactConfigObject,
6642
7062
  safeFilename,
6643
7063
  saveConfig,
6644
7064
  timestamp,