@horizon_works/banto 0.8.9 → 0.9.1

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/license/client.js CHANGED
@@ -308,6 +308,28 @@ function buildStats(review) {
308
308
  * 送る(★1日1回)
309
309
  * @returns {Promise<{sent:boolean, reason:string, count:number}>}
310
310
  */
311
+ /**
312
+ * ★記録の数え方だけを取り出す(2026-08-30)
313
+ *
314
+ * ★送るのは**数だけ**。★何を書いたかは入らない。
315
+ * ★書面(banto-monitor v4.0)に先に書いてから足した。
316
+ *
317
+ * ★なぜ要るか=「型が無い」と「型を付けていない」は別。
318
+ * ★型が付いていないと、**3件で鳴る仕組みが一度も動かない**。
319
+ */
320
+ function buildCounts(review) {
321
+ if (!review) return null;
322
+ const n = (v) => (typeof v === 'number' ? v : null);
323
+ const c = {
324
+ total: n(review['総数'] ?? review.total),
325
+ no_sig: n(review['型を付けていないもの'] ?? review.no_sig),
326
+ pending: n(review['未処理'] ?? review.pending),
327
+ };
328
+ // ★1つも数えられていなければ送らない
329
+ if (c.total === null && c.no_sig === null && c.pending === null) return null;
330
+ return c;
331
+ }
332
+
311
333
  async function reportStats(version, review) {
312
334
  const { key, url, anonKey } = config();
313
335
  if (!key || !url || !anonKey) return { sent: false, reason: 'unconfigured', count: 0 };
@@ -315,7 +337,9 @@ async function reportStats(version, review) {
315
337
  if (!isSharing()) return { sent: false, reason: 'opted-out', count: 0 };
316
338
 
317
339
  const stats = buildStats(review);
318
- if (!stats.length) return { sent: false, reason: 'nothing', count: 0 };
340
+ const counts = buildCounts(review);
341
+ // ★型が1つも無くても、数え方があれば送る(★「型を付けていない」を知らせたい)
342
+ if (!stats.length && !counts) return { sent: false, reason: 'nothing', count: 0 };
319
343
 
320
344
  // ★1日1回(同じ日に何度起動しても増えない)
321
345
  const today = new Date().toISOString().slice(0, 10);
@@ -326,7 +350,8 @@ async function reportStats(version, review) {
326
350
  const keyHash = crypto.createHash('sha256').update(key).digest('hex');
327
351
  const rpc = url.replace(/verify_license\/?$/, 'report_stats');
328
352
  try {
329
- await post(rpc, { p_key_hash: keyHash, p_version: version || null, p_stats: stats }, anonKey);
353
+ await post(rpc, { p_key_hash: keyHash, p_version: version || null,
354
+ p_stats: stats, p_counts: counts }, anonKey);
330
355
  try { fs.writeFileSync(STATS_CACHE, JSON.stringify({ on: today, count: stats.length }, null, 2)); } catch (_) {}
331
356
  return { sent: true, reason: 'ok', count: stats.length };
332
357
  } catch (e) {
@@ -335,4 +360,4 @@ async function reportStats(version, review) {
335
360
  }
336
361
  }
337
362
 
338
- module.exports = { check, reportStats, buildStats, isSharing, setSharing , peek, config};
363
+ module.exports = { check, reportStats, buildStats, isSharing, setSharing , peek, config, buildCounts};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@horizon_works/banto",
3
- "version": "0.8.9",
3
+ "version": "0.9.1",
4
4
  "description": "AI番頭のOS。ルール・記憶・検査を1本のMCPで配ります。中身をクラウドへ渡さずに検査できます。",
5
5
  "keywords": [
6
6
  "mcp",
package/server.js CHANGED
@@ -1654,13 +1654,19 @@ function callTool(name, args) {
1654
1654
  if (!LIC || !LIC.setSharing) return { error: 'この構成では使えません(鍵の口がありません)' };
1655
1655
  if (args && typeof args.on === 'boolean') LIC.setSharing(args.on);
1656
1656
  const on = LIC.isSharing();
1657
- const 送られるもの = LIC.buildStats(FREE.review(false));
1657
+ const 見た = FREE.review(false);
1658
+ const 送られるもの = LIC.buildStats(見た);
1659
+ // ★記録の数え方も見せる(★書面に「そのとき送られる中身がそのまま出る」と書いてある)
1660
+ const 数え方 = LIC.buildCounts ? LIC.buildCounts(見た) : null;
1658
1661
  return {
1659
1662
  いまの設定: on ? '送ります' : '★送りません(既定)',
1660
- 送るもの: '型キーの名前と件数だけ',
1663
+ 送るもの: '型キーの名前と件数、そして記録の数え方(★数だけ)',
1661
1664
  送らないもの: ['記録の本文', '検査した文章', '顧客名・事業所名', 'ファイルの中身', 'パソコンの中の何か'],
1662
1665
  回数: '1日に1回まで',
1663
1666
  '★いま送られる中身': 送られるもの.length ? 送られるもの : '(まだ型キーが付いた記録がありません)',
1667
+ '★いま送られる数え方': 数え方
1668
+ ? { 記録がぜんぶで: 数え方.total, うち型を付けていない: 数え方.no_sig, まだ片づけていない: 数え方.pending }
1669
+ : '(まだ記録がありません)',
1664
1670
  切り替え方: 'share_stats を on: true / false で呼んでください。いつでも変えられます',
1665
1671
  note: on
1666
1672
  ? '★これは、同じ失敗が他の方でも起きているかを見て、助言に使うためのものです。中身は読めません。'
@@ -1923,7 +1929,20 @@ let LICENSE_STATE = { allowed: true, status: 'unconfigured', message: '', daysLe
1923
1929
  // ここで送るのは型キーの名前と件数だけ(組み立ては client 側の buildStats に閉じてある)。
1924
1930
  // ★送れなくても、何も止めない。
1925
1931
  if (LICENSE_STATE.allowed && LIC.isSharing && LIC.isSharing()) {
1926
- try { await LIC.reportStats(VERSION, FREE.review(false)); } catch (_) {}
1932
+ // 🚨★失敗を握りつぶさない(2026-09-01)。
1933
+ // ★ここが `catch (_) {}` だったので、「同意しているのに何も届かない」を
1934
+ // ★こちらもお手元も**一度も知ることができなかった**。
1935
+ // ★止めはしない。★手元の控えに1行だけ残す(`checkup` で読める)。
1936
+ try {
1937
+ await LIC.reportStats(VERSION, FREE.review(false));
1938
+ try { fs.writeFileSync(path.join(CORE_HOME, '.stats_last.json'),
1939
+ JSON.stringify({ ok: true, at: new Date().toISOString() }, null, 2), 'utf8'); } catch (_) {}
1940
+ } catch (e) {
1941
+ try { fs.mkdirSync(CORE_HOME, { recursive: true }); } catch (_) {}
1942
+ try { fs.writeFileSync(path.join(CORE_HOME, '.stats_last.json'),
1943
+ JSON.stringify({ ok: false, at: new Date().toISOString(),
1944
+ why: String(e && e.message || e).slice(0, 200) }, null, 2), 'utf8'); } catch (_) {}
1945
+ }
1927
1946
  }
1928
1947
 
1929
1948
  // ★核の新しい版が出ていないかを、1日1回だけ見にいく(★版だけ。中身は取らない)
@@ -1931,6 +1950,18 @@ let LICENSE_STATE = { allowed: true, status: 'unconfigured', message: '', daysLe
1931
1950
  // ★★黙って上書きしない。★出るのは「取ってきますか」まで。
1932
1951
  if (LICENSE_STATE.allowed) {
1933
1952
  try { await 核の版を取りにいく(); } catch (_) {}
1953
+
1954
+ // 🚨★**一度も入っていないときだけ、自動で取る**(2026-09-01)
1955
+ // ★由来=お渡しした方が4日つないで、記録が0件だった。
1956
+ // 調べたら **核そのものが入っていなかった**(`checkup` に「このフォルダには無い」)。
1957
+ // ★`sync_core` は**相手のAIが自分で呼ばないと動かない**作りだった。
1958
+ // → tooling-check.md §1手順5「裁量の出番を残した関所は、経路が増えた瞬間に漏れる」
1959
+ // ★★上の「黙って上書きしない」は**更新の話**。★ここは**初回**で、上書きするものが無い。
1960
+ // だから設計と衝突しない。★更新は今までどおり、お知らせだけ。
1961
+ try {
1962
+ const 初めて = !fs.existsSync(path.join(CORE_HOME, 'instructions.md'));
1963
+ if (初めて) await syncCore();
1964
+ } catch (_) { /* ★取れなくても何も止めない */ }
1934
1965
  }
1935
1966
  } catch (_) {
1936
1967
  // license モジュールが無い構成でも動く