Skip to content

Commit 14c79f6

Browse files
feat: v1.3.0 - massive template expansion and channel improvements
1 parent 0927fa9 commit 14c79f6

12 files changed

Lines changed: 3143 additions & 685 deletions

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "clawwizard",
33
"private": true,
4-
"version": "1.0.0",
4+
"version": "1.3.0",
55
"type": "module",
66
"scripts": {
77
"dev": "concurrently \"vite\" \"node src/server/bridge.js\"",

src/context/WizardContext.jsx

Lines changed: 166 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -21,46 +21,63 @@ const defaultConfig = {
2121
},
2222
models: {},
2323
workspace: '~/.openclaw/workspace',
24+
imageMaxDimensionPx: 1200,
25+
compaction: {
26+
mode: 'default',
27+
},
2428
heartbeat: {
2529
every: '30m',
26-
model: '',
2730
suppressToolErrorWarnings: false,
2831
},
2932
},
3033
list: [],
3134
},
35+
messages: {
36+
queue: {
37+
mode: 'collect',
38+
},
39+
},
3240
channels: {
33-
whatsapp: { enabled: false, dmPolicy: 'pairing', groupPolicy: 'allowlist', allowFrom: [] },
34-
telegram: { enabled: false, botToken: '', dmPolicy: 'pairing', groupPolicy: 'allowlist', allowFrom: [] },
35-
discord: { enabled: false, botToken: '', applicationId: '', dmPolicy: 'pairing', groupPolicy: 'allowlist', allowFrom: [] },
36-
slack: { enabled: false, botToken: '', appToken: '', dmPolicy: 'pairing', groupPolicy: 'allowlist', allowFrom: [] },
37-
signal: { enabled: false, dmPolicy: 'pairing', groupPolicy: 'allowlist' },
38-
imessage: { enabled: false, dmPolicy: 'pairing', groupPolicy: 'allowlist' },
39-
googlechat: { enabled: false, dmPolicy: 'pairing', groupPolicy: 'allowlist' },
40-
mattermost: { enabled: false, url: '', token: '', dmPolicy: 'pairing', groupPolicy: 'allowlist' },
41-
msteams: { enabled: false, dmPolicy: 'pairing', groupPolicy: 'allowlist' },
42-
bluebubbles: { enabled: false, dmPolicy: 'pairing', groupPolicy: 'allowlist' },
43-
matrix: { enabled: false, dmPolicy: 'pairing', groupPolicy: 'allowlist' },
44-
zalo: { enabled: false, dmPolicy: 'pairing', groupPolicy: 'allowlist' },
45-
webchat: { enabled: false, dmPolicy: 'pairing', groupPolicy: 'allowlist' },
41+
whatsapp: { enabled: false, dmPolicy: 'pairing', allowFrom: [] },
42+
telegram: { enabled: false, botToken: '', dmPolicy: 'pairing', allowFrom: [] },
43+
discord: { enabled: false, botToken: '', applicationId: '', dmPolicy: 'pairing', allowFrom: [] },
44+
slack: { enabled: false, botToken: '', appToken: '', dmPolicy: 'pairing', allowFrom: [] },
45+
signal: { enabled: false, dmPolicy: 'pairing' },
46+
imessage: { enabled: false, dmPolicy: 'pairing' },
47+
googlechat: { enabled: false, dmPolicy: 'pairing' },
48+
mattermost: { enabled: false, url: '', token: '', dmPolicy: 'pairing' },
49+
msteams: { enabled: false, dmPolicy: 'pairing' },
50+
bluebubbles: { enabled: false, dmPolicy: 'pairing' },
51+
matrix: { enabled: false, dmPolicy: 'pairing' },
52+
zalo: { enabled: false, dmPolicy: 'pairing' },
53+
webchat: { enabled: false, dmPolicy: 'pairing' },
4654
},
4755
gateway: {
56+
mode: 'local',
4857
port: 18789,
49-
bind: '127.0.0.1',
58+
bind: 'loopback',
5059
auth: {
5160
mode: 'token',
52-
token: '',
5361
},
5462
tailscale: {
5563
mode: 'off',
5664
},
5765
},
66+
session: {
67+
dmScope: 'main',
68+
},
69+
browser: {
70+
enabled: true,
71+
defaultProfile: 'chrome',
72+
headless: false,
73+
ssrfPolicy: {
74+
dangerouslyAllowPrivateNetwork: true,
75+
},
76+
},
5877
env: {},
5978
tools: {
60-
disabled: [],
79+
deny: [],
6180
},
62-
sessionRetention: '7d',
63-
imageMaxDimensionPx: 1024,
6481
}
6582

6683
const initialState = {
@@ -70,6 +87,16 @@ const initialState = {
7087
selectedTemplate: null,
7188
config: { ...defaultConfig },
7289
soulMd: '',
90+
workspaceFiles: {
91+
'AGENTS.md': '',
92+
'BOOT.md': '',
93+
'BOOTSTRAP.md': '',
94+
'HEARTBEAT.md': '',
95+
'IDENTITY': '',
96+
'SOUL.md': '',
97+
'TOOLS.md': '',
98+
'USER': '',
99+
},
73100
provider: 'anthropic',
74101
apiKey: '',
75102
skippedFields: [],
@@ -80,7 +107,7 @@ function loadState() {
80107
const saved = localStorage.getItem(STORAGE_KEY)
81108
if (saved) {
82109
const parsed = JSON.parse(saved)
83-
return { ...initialState, ...parsed }
110+
return deepMerge(initialState, parsed)
84111
}
85112
} catch (e) { /* ignore */ }
86113
return initialState
@@ -104,7 +131,24 @@ function wizardReducer(state, action) {
104131
case 'SET_API_KEY':
105132
return { ...state, apiKey: action.payload }
106133
case 'SET_SOUL_MD':
107-
return { ...state, soulMd: action.payload }
134+
return {
135+
...state,
136+
soulMd: action.payload,
137+
workspaceFiles: { ...state.workspaceFiles, 'SOUL.md': action.payload }
138+
}
139+
case 'SET_WORKSPACE_FILE':
140+
return {
141+
...state,
142+
workspaceFiles: { ...state.workspaceFiles, [action.payload.name]: action.payload.content },
143+
// Keep soulMd in sync if SOUL.md is edited
144+
soulMd: action.payload.name === 'SOUL.md' ? action.payload.content : state.soulMd
145+
}
146+
case 'SET_ALL_WORKSPACE_FILES':
147+
return {
148+
...state,
149+
workspaceFiles: { ...state.workspaceFiles, ...action.payload },
150+
soulMd: action.payload['SOUL.md'] || state.soulMd
151+
}
108152
case 'UPDATE_CONFIG':
109153
return { ...state, config: deepMerge(state.config, action.payload) }
110154
case 'SET_CHANNEL': {
@@ -128,16 +172,6 @@ function wizardReducer(state, action) {
128172
gateway: { ...state.config.gateway, ...action.payload },
129173
},
130174
}
131-
case 'TOGGLE_TOOL_DISABLED': {
132-
const tool = action.payload
133-
const disabled = state.config.tools.disabled.includes(tool)
134-
? state.config.tools.disabled.filter(t => t !== tool)
135-
: [...state.config.tools.disabled, tool]
136-
return {
137-
...state,
138-
config: { ...state.config, tools: { ...state.config.tools, disabled } },
139-
}
140-
}
141175
case 'SKIP_FIELD': {
142176
const set = new Set(state.skippedFields)
143177
set.add(action.payload)
@@ -200,31 +234,98 @@ export function WizardProvider({ children }) {
200234
}
201235

202236
const generateConfig = () => {
203-
const cfg = { ...state.config }
237+
const cfg = JSON.parse(JSON.stringify(state.config))
238+
239+
// ═══ SCHEMA MIGRATION — fix old localStorage values ═══
204240

205-
// Set model
241+
// 1. compaction: must be object { mode: "default"|"safeguard" }, not boolean
242+
if (cfg.agents?.defaults) {
243+
if (typeof cfg.agents.defaults.compaction === 'boolean' || cfg.agents.defaults.compaction === undefined) {
244+
cfg.agents.defaults.compaction = { mode: 'default' }
245+
}
246+
// Remove keys not in official schema
247+
delete cfg.agents.defaults.timeoutSeconds
248+
delete cfg.agents.defaults.blockStreamingDefault
249+
// heartbeat.model empty string is invalid — remove it
250+
if (cfg.agents.defaults.heartbeat?.model === '') {
251+
delete cfg.agents.defaults.heartbeat.model
252+
}
253+
}
254+
255+
// 2. tools: "disabled" key is not valid — migrate to "deny"
256+
if (cfg.tools) {
257+
if (cfg.tools.disabled) {
258+
cfg.tools.deny = cfg.tools.disabled
259+
delete cfg.tools.disabled
260+
}
261+
}
262+
263+
// 3. session.dmScope: valid values are "main"|"per-peer"|"per-channel-peer"|"per-account-channel-peer"
264+
const validDmScopes = ['main', 'per-peer', 'per-channel-peer', 'per-account-channel-peer']
265+
if (cfg.session && !validDmScopes.includes(cfg.session.dmScope)) {
266+
cfg.session.dmScope = 'main'
267+
}
268+
269+
// 4. gateway.bind: valid values are "loopback"|"lan"|"tailnet"|"auto" (not IP addresses)
270+
const validBinds = ['loopback', 'lan', 'tailnet', 'auto']
271+
if (cfg.gateway && !validBinds.includes(cfg.gateway.bind)) {
272+
// Map old IP values to correct enum
273+
if (cfg.gateway.bind === '127.0.0.1' || cfg.gateway.bind === 'localhost') {
274+
cfg.gateway.bind = 'loopback'
275+
} else if (cfg.gateway.bind === '0.0.0.0') {
276+
cfg.gateway.bind = 'lan'
277+
} else {
278+
cfg.gateway.bind = 'loopback'
279+
}
280+
}
281+
// Remove empty auth.token (auto-generated by openclaw)
282+
if (cfg.gateway?.auth?.token === '') {
283+
delete cfg.gateway.auth.token
284+
}
285+
286+
// 5. Remove invalid root-level keys
287+
delete cfg.sessionRetention
288+
delete cfg.imageMaxDimensionPx
289+
290+
// 6. Remove groupPolicy from channels (not a valid key)
291+
for (const ch of Object.values(cfg.channels || {})) {
292+
delete ch.groupPolicy
293+
}
294+
295+
// 7. Remove extra queue keys that aren't in schema
296+
if (cfg.messages?.queue) {
297+
delete cfg.messages.queue.debounceMs
298+
delete cfg.messages.queue.cap
299+
delete cfg.messages.queue.drop
300+
}
301+
302+
// ═══ END MIGRATION ═══
303+
304+
// Set provider API key in env
206305
if (state.apiKey && state.provider) {
207306
const envKey = {
208307
anthropic: 'ANTHROPIC_API_KEY',
209308
openai: 'OPENAI_API_KEY',
210309
openrouter: 'OPENROUTER_API_KEY',
211-
google: 'GOOGLE_API_KEY',
212-
kilocode: 'KILOCODE_API_KEY',
310+
google: 'GEMINI_API_KEY',
311+
kilocode: 'KILO_API_KEY',
213312
groq: 'GROQ_API_KEY',
214313
mistral: 'MISTRAL_API_KEY',
215314
xai: 'XAI_API_KEY',
216315
together: 'TOGETHER_API_KEY',
217316
deepseek: 'DEEPSEEK_API_KEY',
218-
cerebras: 'CEREBRAS_API_KEY',
219317
moonshot: 'MOONSHOT_API_KEY',
220318
minimax: 'MINIMAX_API_KEY',
319+
venice: 'VENICE_API_KEY',
320+
opencode: 'OPENCODE_API_KEY',
321+
synthetic: 'SYNTHETIC_API_KEY',
221322
}[state.provider]
222323
if (envKey) {
223324
cfg.env = { ...cfg.env, [envKey]: state.apiKey }
224325
}
225326
}
226327

227-
// Remove disabled channels
328+
// Remove disabled channels (keep only enabled ones without the 'enabled' flag)
228329
const channels = {}
229330
for (const [key, val] of Object.entries(cfg.channels)) {
230331
if (val.enabled) {
@@ -234,6 +335,34 @@ export function WizardProvider({ children }) {
234335
}
235336
cfg.channels = channels
236337

338+
// Clean up tools: remove empty deny array
339+
if (cfg.tools?.deny?.length === 0) {
340+
delete cfg.tools.deny
341+
}
342+
if (cfg.tools && Object.keys(cfg.tools).length === 0) {
343+
delete cfg.tools
344+
}
345+
346+
// Clean up browser: remove internal 'enabled' flag
347+
if (cfg.browser) {
348+
delete cfg.browser.enabled
349+
}
350+
351+
// Clean up env: remove if empty
352+
if (cfg.env && Object.keys(cfg.env).length === 0) {
353+
delete cfg.env
354+
}
355+
356+
// Clean up empty agents.list
357+
if (cfg.agents?.list?.length === 0) {
358+
delete cfg.agents.list
359+
}
360+
361+
// Clean up empty models
362+
if (cfg.agents?.defaults?.models && Object.keys(cfg.agents.defaults.models).length === 0) {
363+
delete cfg.agents.defaults.models
364+
}
365+
237366
return cfg
238367
}
239368

0 commit comments

Comments
 (0)