clawtool 0.1.2 → 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/loop.js CHANGED
@@ -15,6 +15,7 @@ class DoctorLoop {
15
15
  constructor(callback, deps) {
16
16
  this.state = 'idle';
17
17
  this.stopped = false;
18
+ this.lang = 'en';
18
19
  this.userDescription = '';
19
20
  this.callback = callback;
20
21
  this.deps = { ...defaultDeps, ...deps };
@@ -22,6 +23,9 @@ class DoctorLoop {
22
23
  stop() {
23
24
  this.stopped = true;
24
25
  }
26
+ setLang(lang) {
27
+ this.lang = /^zh/i.test(lang) ? 'zh' : 'en';
28
+ }
25
29
  provideInput(field, value) {
26
30
  if (field === 'userDescription' && this.pendingDescription) {
27
31
  this.pendingDescription(value || '');
@@ -46,12 +50,16 @@ class DoctorLoop {
46
50
  const sessionId = Date.now().toString();
47
51
  this.emit('session_start', { sessionId });
48
52
  this.setState('observing');
49
- this.emit('progress', { message: 'Scanning your local OpenClaw setup...' });
53
+ this.emit('progress', {
54
+ message: this.lang === 'zh'
55
+ ? '正在扫描本地 OpenClaw 环境...'
56
+ : 'Scanning your local OpenClaw setup...'
57
+ });
50
58
  const observation = await this.deps.collectObservation();
51
59
  const findings = this.deps.runRules(observation);
52
60
  if (this.stopped)
53
61
  return;
54
- const steps = this.deps.buildPlanSteps(findings, observation);
62
+ const steps = this.deps.buildPlanSteps(findings, this.lang, observation);
55
63
  this.setState('running');
56
64
  for (let i = 0; i < steps.length; i++) {
57
65
  const step = steps[i];
package/dist/planner.js CHANGED
@@ -7,12 +7,62 @@ const severityWeight = {
7
7
  warning: 2,
8
8
  info: 3,
9
9
  };
10
- function buildPlanSteps(findings) {
11
- const sorted = [...findings].sort((a, b) => severityWeight[a.severity] - severityWeight[b.severity]);
10
+ const findingI18nZh = {
11
+ 'gateway-not-running': {
12
+ title: '网关未运行',
13
+ description: 'OpenClaw 网关进程当前处于停止状态。',
14
+ },
15
+ 'gateway-service-not-installed': {
16
+ title: '网关服务未安装或未加载',
17
+ description: 'LaunchAgent 服务缺失或未加载,网关无法稳定启动。',
18
+ },
19
+ 'gateway-probe-failed': {
20
+ title: '网关 RPC 探测失败',
21
+ description: '网关端点当前无法建立稳定的 RPC 连接。',
22
+ },
23
+ 'config-missing': {
24
+ title: '配置文件缺失',
25
+ description: '未找到 OpenClaw 配置文件。',
26
+ },
27
+ 'config-json5-error': {
28
+ title: '检测到配置解析错误',
29
+ description: '日志显示 JSON5 语法错误,网关无法加载配置。',
30
+ },
31
+ 'proxy-in-plist': {
32
+ title: 'LaunchAgent 中存在代理变量',
33
+ description: '代理环境变量可能导致模型服务连通失败。',
34
+ },
35
+ };
36
+ function localizeFinding(finding, lang) {
37
+ if (lang !== 'zh')
38
+ return finding;
39
+ const mapped = findingI18nZh[finding.id];
40
+ if (!mapped)
41
+ return finding;
42
+ return {
43
+ ...finding,
44
+ title: mapped.title,
45
+ description: mapped.description || finding.description,
46
+ };
47
+ }
48
+ function buildPlanSteps(findings, lang = 'en') {
49
+ const i18n = {
50
+ readGatewayStatus: lang === 'zh' ? '正在检查网关运行状态' : 'Checking gateway runtime status',
51
+ noCriticalFindings: lang === 'zh' ? '未发现关键问题' : 'No critical findings',
52
+ noFixNeededSummary: lang === 'zh'
53
+ ? '未发现需要修复的关键问题,当前本地检查结果正常。'
54
+ : 'No fix was needed. System appears healthy from local checks.',
55
+ planFinished: lang === 'zh' ? '修复计划执行完成' : 'Plan finished',
56
+ appliedSummary: lang === 'zh'
57
+ ? '已根据本地规则执行修复计划。'
58
+ : 'Applied local repair plan based on deterministic findings.',
59
+ };
60
+ const localizedFindings = findings.map((f) => localizeFinding(f, lang));
61
+ const sorted = [...localizedFindings].sort((a, b) => severityWeight[a.severity] - severityWeight[b.severity]);
12
62
  const steps = [
13
63
  {
14
64
  type: 'read',
15
- description: 'Check current gateway status',
65
+ description: i18n.readGatewayStatus,
16
66
  command: 'openclaw gateway status 2>&1',
17
67
  risk: 'low',
18
68
  },
@@ -30,9 +80,9 @@ function buildPlanSteps(findings) {
30
80
  if (sorted.length === 0) {
31
81
  steps.push({
32
82
  type: 'done',
33
- description: 'No critical findings',
83
+ description: i18n.noCriticalFindings,
34
84
  fixed: false,
35
- summary: 'No fix was needed. System appears healthy from local checks.',
85
+ summary: i18n.noFixNeededSummary,
36
86
  warnings: [],
37
87
  problem: null,
38
88
  fix: null,
@@ -41,9 +91,9 @@ function buildPlanSteps(findings) {
41
91
  }
42
92
  steps.push({
43
93
  type: 'done',
44
- description: 'Plan finished',
94
+ description: i18n.planFinished,
45
95
  fixed: true,
46
- summary: 'Applied local repair plan based on deterministic findings.',
96
+ summary: i18n.appliedSummary,
47
97
  warnings: [],
48
98
  problem: sorted[0]?.description || null,
49
99
  fix: sorted[0]?.fix || null,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clawtool",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "description": "Local-first OpenClaw diagnose and repair tool",
5
5
  "type": "commonjs",
6
6
  "main": "dist/index.js",
package/web/index.html CHANGED
@@ -4,7 +4,7 @@
4
4
  <meta charset="UTF-8" />
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1" />
6
6
  <meta name="google" content="notranslate" />
7
- <title>ClawTool</title>
7
+ <title>OpenClaw Repair Assistant</title>
8
8
  <style>
9
9
  :root {
10
10
  --bg: #f5f4ef;
@@ -163,6 +163,7 @@
163
163
  <section id="start" class="card">
164
164
  <div class="hint" id="start-text">No cloud call. No token required. Local execution only.</div>
165
165
  <button id="btn-start" class="btn btn-main">Start Scan</button>
166
+ <button id="btn-rescan" class="btn btn-main hidden" style="margin-top:8px">Scan Again</button>
166
167
  </section>
167
168
 
168
169
  <section id="feed" class="feed hidden"></section>
@@ -180,7 +181,13 @@
180
181
  allow: '执行',
181
182
  skip: '跳过',
182
183
  done: '完成',
183
- error: '发生错误'
184
+ error: '发生错误',
185
+ rescan: '重新扫描',
186
+ badge: {
187
+ read: '检查',
188
+ fix: '修复',
189
+ done: '完成'
190
+ }
184
191
  } : {
185
192
  tag: 'Local-First Diagnose Engine',
186
193
  sub: 'Detect and repair OpenClaw issues locally with explicit fix confirmation.',
@@ -190,7 +197,13 @@
190
197
  allow: 'Allow',
191
198
  skip: 'Skip',
192
199
  done: 'Completed',
193
- error: 'Error occurred'
200
+ error: 'Error occurred',
201
+ rescan: 'Scan Again',
202
+ badge: {
203
+ read: 'read',
204
+ fix: 'fix',
205
+ done: 'done'
206
+ }
194
207
  };
195
208
 
196
209
  const el = {
@@ -199,6 +212,7 @@
199
212
  startText: document.getElementById('start-text'),
200
213
  startWrap: document.getElementById('start'),
201
214
  btnStart: document.getElementById('btn-start'),
215
+ btnRescan: document.getElementById('btn-rescan'),
202
216
  feed: document.getElementById('feed')
203
217
  };
204
218
 
@@ -206,6 +220,7 @@
206
220
  el.sub.textContent = T.sub;
207
221
  el.startText.textContent = T.startText;
208
222
  el.btnStart.textContent = T.start;
223
+ el.btnRescan.textContent = T.rescan;
209
224
 
210
225
  let eventSource = null;
211
226
  let sessionId = null;
@@ -224,6 +239,15 @@
224
239
  return 'b-read';
225
240
  }
226
241
 
242
+ function badgeText(type) {
243
+ const map = {
244
+ read: isZh ? '检查' : 'read',
245
+ fix: isZh ? '修复' : 'fix',
246
+ done: isZh ? '完成' : 'done'
247
+ };
248
+ return map[type] || map.read;
249
+ }
250
+
227
251
  async function postConfirm(confirmed) {
228
252
  if (!sessionId) return;
229
253
  await fetch('/api/confirm', {
@@ -255,9 +279,9 @@
255
279
  const card = addCard(
256
280
  '<div class="step-title">'
257
281
  + '<span>' + (step.description || 'step') + '</span>'
258
- + '<span class="badge ' + typeBadge(step.type) + '">' + (step.type || 'read') + '</span>'
282
+ + '<span class="badge ' + typeBadge(step.type) + '">' + badgeText(step.type || 'read') + '</span>'
259
283
  + '</div>'
260
- + (step.command ? '<div class="mono" translate="no">' + step.command + '</div>' : '')
284
+ + (step.type !== 'read' && step.command ? '<div class="mono" translate="no">' + step.command + '</div>' : '')
261
285
  );
262
286
 
263
287
  if (step.type === 'fix') {
@@ -279,12 +303,14 @@
279
303
 
280
304
  if (type === 'complete') {
281
305
  addCard('<h3 class="ok">' + T.done + '</h3><p>' + (data.summary || '') + '</p>');
306
+ el.btnRescan.classList.remove('hidden');
282
307
  if (eventSource) eventSource.close();
283
308
  return;
284
309
  }
285
310
 
286
311
  if (type === 'error') {
287
312
  addCard('<h3 class="err">' + T.error + '</h3><p>' + (data.message || '') + '</p>');
313
+ el.btnRescan.classList.remove('hidden');
288
314
  if (eventSource) eventSource.close();
289
315
  }
290
316
  };
@@ -293,6 +319,15 @@
293
319
  el.btnStart.addEventListener('click', function () {
294
320
  el.startWrap.classList.add('hidden');
295
321
  el.feed.classList.remove('hidden');
322
+ el.btnRescan.classList.add('hidden');
323
+ connect();
324
+ });
325
+
326
+ el.btnRescan.addEventListener('click', function () {
327
+ el.feed.innerHTML = '';
328
+ el.feed.classList.remove('hidden');
329
+ el.btnRescan.classList.add('hidden');
330
+ if (eventSource) eventSource.close();
296
331
  connect();
297
332
  });
298
333