@horizon_works/banto 0.7.0 → 0.8.0
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/gate_core.js +220 -1
- package/license/client.js +20 -1
- package/package.json +1 -2
- package/server.js +110 -36
- package/payload/compliance/SKILL.md +0 -113
- package/payload/compliance/dict/00_/345/213/225/344/275/234/347/242/272/350/252/215.json +0 -14
- package/payload/compliance/dict/06_house_rules.json +0 -16
- package/payload/compliance/dict/_sample/04_common_keihyo.json +0 -270
- package/payload/compliance/dict/_sample/README.md +0 -72
- package/payload/compliance/lint.js +0 -265
- package/payload/disciplines//347/225/252/351/240/255/343/201/256/344/275/234/346/263/225.md +0 -64
- package/payload/disciplines//350/207/252/345/267/261/345/201/245/350/250/272.md +0 -74
- package/payload/disciplines//351/200/261/346/254/241/343/203/241/343/203/263/343/203/206.md +0 -60
- package/payload/migration/protocol.md +0 -35
- package/payload/skill-maker/SKILL_base_anthropic.md +0 -357
- package/payload/skill-maker/skill-maker-v1.0.md +0 -305
- package/payload/skills/_/343/201/202/343/201/250/343/201/247/350/266/263/343/201/233/343/202/213/343/202/202/343/201/256.md +0 -47
- package/payload/skills/image-gen/SKILL.md +0 -147
- package/payload/skills/image-gen/gen.js +0 -225
- package/payload/skills/remember/SETUP_Google/351/200/243/346/220/272.md +0 -106
- package/payload/skills/remember/SKILL.md +0 -158
- package/payload/skills/remember/handoff.js +0 -149
- package/payload/skills/remember/tasks.js +0 -320
- package/payload/skills/slide-deck/SKILL.md +0 -160
- package/payload/skills/transcribe/README.md +0 -182
- package/payload/skills/transcribe/dict.json +0 -10
- package/payload/skills/transcribe/enroll_speaker.py +0 -76
- package/payload/skills/transcribe/identify_speakers.py +0 -153
- package/payload/skills/transcribe/transcribe.py +0 -126
package/gate_core.js
CHANGED
|
@@ -219,6 +219,221 @@ function summarize(r, dict, targetLabel) {
|
|
|
219
219
|
|
|
220
220
|
// ───────────────────────────────── 記録(学びを溜めて、ルールに落とす)
|
|
221
221
|
// ★入口は絶対に止めない。書きにくくすると、記録そのものが減る。
|
|
222
|
+
// ═══════════════════════════════════════════════════════════════
|
|
223
|
+
// 正本(★同じ役割のものが2つあると、どちらも育たない)
|
|
224
|
+
//
|
|
225
|
+
// 🚨 由来: 2026-08-28。AI番頭キット(FL)をお使いの方が、このMCPも入れると
|
|
226
|
+
// 下書き帳が2つになることに気づいた。★形が違うので、片方の関所が
|
|
227
|
+
// 「数えるのに一覧に出せない」状態になる(=止まるのに閉じられない)。
|
|
228
|
+
//
|
|
229
|
+
// ★★**機械は数えるだけ。同種かどうかの判断はAIがする。**
|
|
230
|
+
// ファイル名だけで決めると、たまたま名前が似たものを潰す。
|
|
231
|
+
// ★**判断した結果は、その場の会話で終わらせずにファイルへ残す。**
|
|
232
|
+
// 残さないと、次のセッションでまた同じことを考える(=考えが積まない)。
|
|
233
|
+
// ★**残したものを、実際に読んで振る舞いを変える。**
|
|
234
|
+
// 読まないルールは、無いのと同じ(未配線の関所)。
|
|
235
|
+
// ═══════════════════════════════════════════════════════════════
|
|
236
|
+
|
|
237
|
+
// ★役割語=「同じ仕事をしていそう」の当たり付け(★これは判定ではない)
|
|
238
|
+
const 役割語 = {
|
|
239
|
+
'下書き帳': ['retain_log', 'bantou-log', 'bantou_log', '気づき', 'notes', 'メモ帳'],
|
|
240
|
+
'記憶の正本': ['subconscious', 'hot_memory', 'ホットメモリ', 'MEMORY.md'],
|
|
241
|
+
'状態': ['state.md', 'status.md', '.state.json'],
|
|
242
|
+
'引き継ぎ': ['handoff', '引き継ぎ', 'pending'],
|
|
243
|
+
'辞書': ['dict', '辞書', 'dictionary'],
|
|
244
|
+
'人物': ['profile', 'プロファイル', '人物'],
|
|
245
|
+
'やること': ['task', 'todo', 'やること', '課題'],
|
|
246
|
+
'ルール': ['rules', 'ルール', 'policy', 'CLAUDE.md', 'GEMINI.md', 'AGENTS.md'],
|
|
247
|
+
};
|
|
248
|
+
|
|
249
|
+
function masterFile(dir) {
|
|
250
|
+
const r = path.join(dir, '_agents', 'rules');
|
|
251
|
+
try { if (fs.existsSync(r)) return path.join(r, '正本.md'); } catch (_) {}
|
|
252
|
+
return path.join(dir, '正本.md');
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
function readMasters(dir) {
|
|
256
|
+
const f = masterFile(dir);
|
|
257
|
+
const out = {};
|
|
258
|
+
let s = '';
|
|
259
|
+
try { s = fs.readFileSync(f, 'utf8'); } catch (_) { return { file: f, decided: out }; }
|
|
260
|
+
// 形: | 役割 | 正本 | 別本(; 区切り) | 決めた日 |
|
|
261
|
+
for (const line of s.split(String.fromCharCode(10))) {
|
|
262
|
+
const m = line.match(/^\|\s*([^|]+?)\s*\|\s*([^|]+?)\s*\|\s*([^|]*?)\s*\|\s*([^|]*?)\s*\|\s*$/);
|
|
263
|
+
if (!m || m[1] === '役割' || /^-+$/.test(m[1])) continue;
|
|
264
|
+
out[m[1]] = { 正本: m[2], 別本: m[3] ? m[3].split(';').map((x) => x.trim()).filter(Boolean) : [], 決めた日: m[4] };
|
|
265
|
+
}
|
|
266
|
+
return { file: f, decided: out };
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
function writeMaster(dir, 役割, 正本, 別本, 理由) {
|
|
270
|
+
const f = masterFile(dir);
|
|
271
|
+
const { decided } = readMasters(dir);
|
|
272
|
+
decided[役割] = { 正本, 別本: 別本 || [], 決めた日: stamp().slice(0, 10), 理由 };
|
|
273
|
+
const nl = String.fromCharCode(10);
|
|
274
|
+
const head = [
|
|
275
|
+
'# 正本(どれが本物か)',
|
|
276
|
+
'',
|
|
277
|
+
'> ★同じ役割のものが2つあると、どちらも育ちません。ここで1つに決めます。',
|
|
278
|
+
'> ★決めたのは番頭とあなたです。機械は候補を出しただけです。',
|
|
279
|
+
'> ★増えたら `check_masters` でもう一度見てください。',
|
|
280
|
+
'',
|
|
281
|
+
'| 役割 | 正本 | 別本(使わない) | 決めた日 |',
|
|
282
|
+
'|---|---|---|---|',
|
|
283
|
+
];
|
|
284
|
+
const rows = Object.entries(decided).map(([k, v]) =>
|
|
285
|
+
'| ' + k + ' | ' + v.正本 + ' | ' + (v.別本 || []).join('; ') + ' | ' + (v.決めた日 || '') + ' |');
|
|
286
|
+
const notes = Object.entries(decided).filter(([, v]) => v.理由)
|
|
287
|
+
.map(([k, v]) => '- **' + k + '**:' + v.理由);
|
|
288
|
+
const body = head.concat(rows, notes.length ? ['', '## なぜそう決めたか', ''].concat(notes) : [], ['']).join(nl);
|
|
289
|
+
fs.mkdirSync(path.dirname(f), { recursive: true });
|
|
290
|
+
fs.writeFileSync(f, body, 'utf8');
|
|
291
|
+
return f;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
/**
|
|
295
|
+
* ★候補を出す。★判定はしない。
|
|
296
|
+
* ★中身はAIへ返さない(ファイル名・行数・更新日・先頭の見出しだけ)。
|
|
297
|
+
*/
|
|
298
|
+
function checkMasters(dir, decide) {
|
|
299
|
+
const d = path.resolve(String(dir || process.cwd()));
|
|
300
|
+
if (!fs.existsSync(d)) throw new Error('見つかりません: ' + d);
|
|
301
|
+
|
|
302
|
+
if (decide && decide.役割 && decide.正本) {
|
|
303
|
+
const f = writeMaster(d, decide.役割, decide.正本, decide.別本, decide.理由);
|
|
304
|
+
return {
|
|
305
|
+
決めました: decide.役割, 正本: decide.正本,
|
|
306
|
+
別本: decide.別本 || [], 書いた先: f,
|
|
307
|
+
note: '★これ以降、記録の道具はこの決めごとを読んで振る舞います。'
|
|
308
|
+
+ '★決め直すときは、もう一度この道具を呼んでください。',
|
|
309
|
+
};
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
// ── 走査(★深すぎない・重くない)
|
|
313
|
+
const 見つけた = [];
|
|
314
|
+
(function w(p, depth) {
|
|
315
|
+
if (depth > 4) return;
|
|
316
|
+
let es = [];
|
|
317
|
+
try { es = fs.readdirSync(p, { withFileTypes: true }); } catch (_) { return; }
|
|
318
|
+
for (const e of es) {
|
|
319
|
+
if (e.name === 'node_modules' || e.name === '_かたづけ' || e.name.startsWith('.')) continue;
|
|
320
|
+
const full = path.join(p, e.name);
|
|
321
|
+
if (e.isDirectory()) { w(full, depth + 1); continue; }
|
|
322
|
+
if (!/\.(md|json|txt)$/i.test(e.name)) continue;
|
|
323
|
+
見つけた.push(full);
|
|
324
|
+
}
|
|
325
|
+
})(d, 0);
|
|
326
|
+
|
|
327
|
+
const { file: mf, decided } = readMasters(d);
|
|
328
|
+
const 組 = [];
|
|
329
|
+
for (const [役割, 語s] of Object.entries(役割語)) {
|
|
330
|
+
const 当たり = 見つけた.filter((f) => {
|
|
331
|
+
const rel = path.relative(d, f).split(String.fromCharCode(92)).join('/');
|
|
332
|
+
return 語s.some((w2) => rel.toLowerCase().indexOf(w2.toLowerCase()) >= 0);
|
|
333
|
+
});
|
|
334
|
+
if (当たり.length < 2) continue;
|
|
335
|
+
組.push({
|
|
336
|
+
役割,
|
|
337
|
+
すでに決めてある: decided[役割] ? decided[役割].正本 : null,
|
|
338
|
+
候補: 当たり.slice(0, 8).map((f) => {
|
|
339
|
+
let 行数 = null, 見出し = null, 更新 = null;
|
|
340
|
+
try {
|
|
341
|
+
const s = fs.readFileSync(f, 'utf8');
|
|
342
|
+
行数 = s.split(String.fromCharCode(10)).length;
|
|
343
|
+
const h = s.split(String.fromCharCode(10)).find((l) => /^#\s/.test(l));
|
|
344
|
+
見出し = h ? h.replace(/^#\s*/, '').slice(0, 40) : null;
|
|
345
|
+
更新 = fs.statSync(f).mtime.toISOString().slice(0, 10);
|
|
346
|
+
} catch (_) {}
|
|
347
|
+
return { path: path.relative(d, f).split(String.fromCharCode(92)).join('/'), 行数, 見出し, 更新 };
|
|
348
|
+
}),
|
|
349
|
+
});
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
return {
|
|
353
|
+
見たフォルダ: d,
|
|
354
|
+
正本の帳面: mf,
|
|
355
|
+
決めてある件数: Object.keys(decided).length,
|
|
356
|
+
確かめてほしい組: 組.length ? 組 : '(役割が重なりそうな組は見つかりませんでした)',
|
|
357
|
+
note:
|
|
358
|
+
'★これは候補です。**同じ役割かどうかは、機械には決められません。**'
|
|
359
|
+
+ 'あなた(番頭)が1組ずつ見て、同じ仕事をしているなら1つに決めてください。'
|
|
360
|
+
+ '★決めたら check_masters を decide 付きで呼ぶと、帳面へ残ります。'
|
|
361
|
+
+ '★中身は返していません(ファイル名・行数・更新日・先頭の見出しだけ)。',
|
|
362
|
+
'★どう決めるか': [
|
|
363
|
+
'同じ仕事をしているか(片方が消えても困らないなら、たぶん別の仕事)',
|
|
364
|
+
'★どちらを残すか=**関所が読んでいるほう**(止める・数える仕組みが見ているファイル)',
|
|
365
|
+
'★形が違うものを1つに混ぜない(読めなくなる)。使わないほうは別本にする',
|
|
366
|
+
],
|
|
367
|
+
};
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
// 🚨★**AI番頭キット(FL)が入っているなら、記録はそちらへ譲る**(2026-08-28)
|
|
371
|
+
// ★由来=FL 2.2〜2.5 をお使いの方が、このMCPも入れた場合。
|
|
372
|
+
// ★FLキットは `second-brain/retain_log.md` に書き、`kit/memory/close.js` が
|
|
373
|
+
// **そのファイルだけ**を読んで「未処理10件で止める」関所を回している。
|
|
374
|
+
// ★★こちらが `bantou-log.md` に書くと、**下書き帳が2つ**になる(=正本割れ)。
|
|
375
|
+
// ★しかも1行の形が違う:
|
|
376
|
+
// FL : `- [ ] 日時 | 0.6 | failure | 本文 〔sig:key〕`(★点数の欄がある)
|
|
377
|
+
// こちら: `- [ ] 日時 | failure 〔key〕 | 本文`
|
|
378
|
+
// ★FLの関所は行頭 `- [ ]` だけを数え、一覧は正規表現で読む。
|
|
379
|
+
// ★→ **数えられるのに一覧に出ない**。止まるのに閉じられない、いちばん悪い形になる。
|
|
380
|
+
// ★→ ★**書く場所を横取りしない。FLの道具へお返しする。**
|
|
381
|
+
// ★型キーを3件で数える働きは、FLの retain.js が同じことをしている。失うものは無い。
|
|
382
|
+
function findKit() {
|
|
383
|
+
let d = process.cwd();
|
|
384
|
+
for (let i = 0; i < 6; i++) {
|
|
385
|
+
const p = path.join(d, 'kit', 'memory', 'retain.js');
|
|
386
|
+
try { if (fs.existsSync(p)) return { script: p, root: d }; } catch (_) {}
|
|
387
|
+
const up = path.dirname(d);
|
|
388
|
+
if (up === d) break;
|
|
389
|
+
d = up;
|
|
390
|
+
}
|
|
391
|
+
return null;
|
|
392
|
+
}
|
|
393
|
+
// ★★**機械は決めない。決めごとがあれば従い、無ければ止めて番頭に聞く。**
|
|
394
|
+
// ★由来(2026-08-28)=「AIが同種の記載をしているかどうか判断して、ルールを作らせるのがいい」。
|
|
395
|
+
// ★私は最初、キットの有無だけで機械が決める形にした。★それだと
|
|
396
|
+
// ①たまたま名前が似ただけのものまで潰す ②決めた理由が残らない
|
|
397
|
+
// ③次のセッションでまた同じことを考える(考えが積まない)。
|
|
398
|
+
// ★順番=① 環境変数の明示 → ② 正本.md の決めごと → ③ 未決なら止めて聞く。
|
|
399
|
+
function logOwner() {
|
|
400
|
+
if (process.env.BANTOU_LOG) return null; // ★人が明示した指定が最優先
|
|
401
|
+
let decided = {};
|
|
402
|
+
try { decided = readMasters(process.cwd()).decided; } catch (_) {}
|
|
403
|
+
const m = decided['下書き帳'];
|
|
404
|
+
if (m) {
|
|
405
|
+
// ★決めてある。こちらが正本なら普通に書く。よそが正本なら譲る
|
|
406
|
+
return /bantou-log/i.test(m.正本) ? null : { 決めてある: true, 正本: m.正本 };
|
|
407
|
+
}
|
|
408
|
+
const k = findKit(); // ★キットが在る=重なる恐れがある
|
|
409
|
+
return k ? { 決めてある: false, キットの場所: k.root } : null;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
function kitNotice(cmd) {
|
|
413
|
+
const o = logOwner();
|
|
414
|
+
if (o && o.決めてある) {
|
|
415
|
+
return {
|
|
416
|
+
verdict: '書いていません',
|
|
417
|
+
理由: '★このフォルダの「下書き帳」の正本は ' + o.正本 + ' と決めてあります(正本.md)。',
|
|
418
|
+
お使いください: 'node kit/memory/' + cmd,
|
|
419
|
+
};
|
|
420
|
+
}
|
|
421
|
+
return {
|
|
422
|
+
verdict: '書いていません',
|
|
423
|
+
理由: '★このフォルダには AI番頭キットが入っていて、記録の置き場が**まだ決まっていません**。',
|
|
424
|
+
'★あなた(番頭)にお願いしたいこと': [
|
|
425
|
+
'1. `check_masters` を呼んで、役割が重なっている組を見てください',
|
|
426
|
+
'2. 同じ仕事をしているか、あなたが判断してください(★機械には決められません)',
|
|
427
|
+
'3. 決めたら `check_masters` を decide 付きで呼んでください。帳面に残り、次から迷いません',
|
|
428
|
+
],
|
|
429
|
+
なぜ止めたか:
|
|
430
|
+
'★下書き帳が2つあると、どちらも育ちません。'
|
|
431
|
+
+ 'しかも形が違うと、片方の関所が「数えるのに一覧に出せない」状態になります'
|
|
432
|
+
+ '(止まるのに閉じられない)。★先に1つ決めてから書きます。',
|
|
433
|
+
キットの場所: o ? o.キットの場所 : null,
|
|
434
|
+
};
|
|
435
|
+
}
|
|
436
|
+
|
|
222
437
|
function logPath() {
|
|
223
438
|
return process.env.BANTOU_LOG || path.join(process.cwd(), 'bantou-log.md');
|
|
224
439
|
}
|
|
@@ -227,6 +442,7 @@ function stamp() {
|
|
|
227
442
|
return `${d.getFullYear()}-${p(d.getMonth()+1)}-${p(d.getDate())} ${p(d.getHours())}:${p(d.getMinutes())}`;
|
|
228
443
|
}
|
|
229
444
|
function remember(text, tag, sig) {
|
|
445
|
+
if (logOwner()) return kitNotice('retain.js add "<本文>" --score 0.5 --tag failure --sig <型キー>');
|
|
230
446
|
const t = String(text || '').trim();
|
|
231
447
|
if (!t) throw new Error('記録する内容がありません');
|
|
232
448
|
const f = logPath();
|
|
@@ -277,6 +493,7 @@ function readLog() {
|
|
|
277
493
|
}
|
|
278
494
|
|
|
279
495
|
function recall(days, onlyOpen) {
|
|
496
|
+
if (logOwner()) return kitNotice('retain.js recall --min 0.5 --days 7');
|
|
280
497
|
const { file, entries } = readLog();
|
|
281
498
|
const since = Date.now() - (Number(days) || 7) * 86400000;
|
|
282
499
|
const out = entries
|
|
@@ -291,6 +508,7 @@ function recall(days, onlyOpen) {
|
|
|
291
508
|
* ★これが FL の山。溜めるのが目的ではなく、ルールか手順に落とすのが目的。
|
|
292
509
|
*/
|
|
293
510
|
function review(record) {
|
|
511
|
+
if (logOwner()) return kitNotice('close.js');
|
|
294
512
|
const { file, entries, ruled } = readLog();
|
|
295
513
|
const ruledBy = {};
|
|
296
514
|
for (const r of ruled) ruledBy[r.sig] = r;
|
|
@@ -353,6 +571,7 @@ function review(record) {
|
|
|
353
571
|
* ruled_to … ルールや手順にしたときの置き場所(sig と一緒に指定)
|
|
354
572
|
*/
|
|
355
573
|
function settle(ids, sig, ruledTo) {
|
|
574
|
+
if (logOwner()) return kitNotice('retain.js promote <行番号...>');
|
|
356
575
|
const { file, lines, entries } = readLog();
|
|
357
576
|
if (!fs.existsSync(file)) throw new Error('まだ記録がありません');
|
|
358
577
|
let closed = 0;
|
|
@@ -620,7 +839,7 @@ function handle(req) {
|
|
|
620
839
|
|
|
621
840
|
// ★このファイルは「正本」。社内版はこれを読み込んで、辞書と鍵を足すだけにする。
|
|
622
841
|
// (検出のロジックを2か所に持たないため)
|
|
623
|
-
module.exports = { inspectText, summarize, report, loadDict, walk, remember, recall, review, settle, checkup, readLog, selfDiagnosis, INTERNAL_PATTERNS, NAMEISH };
|
|
842
|
+
module.exports = { inspectText, summarize, report, loadDict, walk, remember, recall, review, settle, checkup, readLog, selfDiagnosis, INTERNAL_PATTERNS, NAMEISH, checkMasters, readMasters };
|
|
624
843
|
|
|
625
844
|
// 直接起動されたときだけ、MCPサーバーとして動く
|
|
626
845
|
if (require.main === module) {
|
package/license/client.js
CHANGED
|
@@ -22,10 +22,26 @@ const CACHE = path.join(DIR, '.state.json');
|
|
|
22
22
|
const GRACE_DAYS = 7; // 通信できないときに通す日数
|
|
23
23
|
const CHECK_EVERY_H = 24; // 問い合わせの間隔
|
|
24
24
|
|
|
25
|
+
// 🚨★**渡すものを1つにする**(2026-08-28)
|
|
26
|
+
// ★URL と anon キーは★**全員同じ**(★公開情報)なので、★ここに埋める。
|
|
27
|
+
// ★★鍵と署名は `鍵.署名` の形で1つにまとめる(★コピペするので長さは問題にならない)。
|
|
28
|
+
// ★→ ★利用者が入れるのは `BANTO_KEY` 1つだけ。
|
|
29
|
+
// ★★古い4つの環境変数も、★そのまま使える(★既に配った方が止まらないように)。
|
|
30
|
+
const BUILTIN_URL = 'https://ltelwzqclaxmpnbjuoql.supabase.co/rest/v1/rpc/verify_license';
|
|
31
|
+
const BUILTIN_ANON = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Imx0ZWx3enFjbGF4bXBuYmp1b3FsIiwicm9sZSI6ImFub24iLCJpYXQiOjE3ODcxOTg4MDIsImV4cCI6MjEwMjc3NDgwMn0.vOrEj3rFFUdMM1vYGqBwnGkPbFVQHql3_X_DhpA-9L4';
|
|
32
|
+
|
|
25
33
|
function config() {
|
|
26
34
|
const c = { key: process.env.BANTOU_LICENSE_KEY || '', url: process.env.BANTOU_LICENSE_URL || '',
|
|
27
35
|
anonKey: process.env.BANTOU_LICENSE_ANON || '',
|
|
28
36
|
sig: process.env.BANTOU_LICENSE_SIG || '' };
|
|
37
|
+
|
|
38
|
+
// ★新しい形=`BANTO_KEY = 鍵.署名`
|
|
39
|
+
const one = process.env.BANTO_KEY || '';
|
|
40
|
+
if (one && !c.key) {
|
|
41
|
+
const i = one.indexOf('.');
|
|
42
|
+
if (i > 0) { c.key = one.slice(0, i); c.sig = one.slice(i + 1); }
|
|
43
|
+
else { c.key = one; }
|
|
44
|
+
}
|
|
29
45
|
const f = path.join(DIR, 'config.json');
|
|
30
46
|
if (fs.existsSync(f)) {
|
|
31
47
|
try {
|
|
@@ -36,6 +52,9 @@ function config() {
|
|
|
36
52
|
c.sig = c.sig || j.sig || '';
|
|
37
53
|
} catch (_) {}
|
|
38
54
|
}
|
|
55
|
+
// ★どこにも書かれていなければ、★埋め込みを使う
|
|
56
|
+
c.url = c.url || BUILTIN_URL;
|
|
57
|
+
c.anonKey = c.anonKey || BUILTIN_ANON;
|
|
39
58
|
return c;
|
|
40
59
|
}
|
|
41
60
|
|
|
@@ -281,4 +300,4 @@ async function reportStats(version, review) {
|
|
|
281
300
|
}
|
|
282
301
|
}
|
|
283
302
|
|
|
284
|
-
module.exports = { check, reportStats, buildStats, isSharing, setSharing , peek};
|
|
303
|
+
module.exports = { check, reportStats, buildStats, isSharing, setSharing , peek, config};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@horizon_works/banto",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"description": "AI番頭のOS。ルール・記憶・検査を1本のMCPで配ります。中身をクラウドへ渡さずに検査できます。",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"mcp",
|
|
@@ -31,7 +31,6 @@
|
|
|
31
31
|
"env.js",
|
|
32
32
|
"subconscious.js",
|
|
33
33
|
"supplements.json",
|
|
34
|
-
"payload/",
|
|
35
34
|
"license/client.js",
|
|
36
35
|
"README.md"
|
|
37
36
|
],
|
package/server.js
CHANGED
|
@@ -552,6 +552,31 @@ const TOOLS = [
|
|
|
552
552
|
required: ['name'],
|
|
553
553
|
},
|
|
554
554
|
},
|
|
555
|
+
{
|
|
556
|
+
name: 'check_masters',
|
|
557
|
+
description:
|
|
558
|
+
'★同じ役割のものが2つ以上ないかを見ます(正本割れの点検)。'
|
|
559
|
+
+ '★機械は候補を出すだけで、**同じ仕事かどうかは判断しません**。あなたが1組ずつ見て決めてください。'
|
|
560
|
+
+ '★中身は返しません(ファイル名・行数・更新日・先頭の見出しだけ)。'
|
|
561
|
+
+ '決めたら decide を付けて呼ぶと `正本.md` に残り、記録の道具がそれを読んで振る舞います。',
|
|
562
|
+
inputSchema: {
|
|
563
|
+
type: 'object',
|
|
564
|
+
properties: {
|
|
565
|
+
dir: { type: 'string', description: '見るフォルダ(省略すると今の場所)' },
|
|
566
|
+
decide: {
|
|
567
|
+
type: 'object',
|
|
568
|
+
description: '★決まったときだけ付けます',
|
|
569
|
+
properties: {
|
|
570
|
+
役割: { type: 'string', description: '例: 下書き帳 / 記憶の正本 / やること' },
|
|
571
|
+
正本: { type: 'string', description: '本物にするファイル(フォルダからの相対)' },
|
|
572
|
+
別本: { type: 'array', items: { type: 'string' }, description: '使わないほう' },
|
|
573
|
+
理由: { type: 'string', description: '★なぜそう決めたか(次に読む人のため)' },
|
|
574
|
+
},
|
|
575
|
+
required: ['役割', '正本'],
|
|
576
|
+
},
|
|
577
|
+
},
|
|
578
|
+
},
|
|
579
|
+
},
|
|
555
580
|
{
|
|
556
581
|
name: 'tidy',
|
|
557
582
|
description:
|
|
@@ -727,6 +752,44 @@ function resources() {
|
|
|
727
752
|
// 実体・作法 → ここ(利用者のフォルダ)
|
|
728
753
|
// 記憶 → MCP(tools)
|
|
729
754
|
const PAYLOAD = path.join(__dirname, 'payload');
|
|
755
|
+
|
|
756
|
+
// 🚨★**配るものは、パッケージに同梱しない。配布口(サーバー)から取る**(2026-08-28)
|
|
757
|
+
// ★由来=「payload をサーバー側へ移す(sync_core と同じ口にする=鍵が要る形)」。
|
|
758
|
+
// ★★同梱したままだと、鍵で `install` を止めても意味がない。
|
|
759
|
+
// `npm pack @horizon_works/banto` して解凍すれば、**誰でも中身を読める**から。
|
|
760
|
+
// ★鍵の話ではなく、同梱の話だった。
|
|
761
|
+
// ★取り方は `add_supplement` と同じ口(`get_supplement`)に寄せる。★新しい口を作らない。
|
|
762
|
+
// ★鍵が無ければ `supPost` が止める(★入口を1つにしたので、抜け道ができない)。
|
|
763
|
+
// ★手元に控えを残す。取れなかったときは控えで進める(★切れても止まらない)。
|
|
764
|
+
const BASE_NAME = 'base';
|
|
765
|
+
let PAYLOAD_DIR = PAYLOAD; // ★実際に読む場所
|
|
766
|
+
function baseCacheDir() {
|
|
767
|
+
return path.join(process.env.BANTO_HOME || path.join(require('os').homedir(), '.banto'), 'base');
|
|
768
|
+
}
|
|
769
|
+
async function ensurePayload(force) {
|
|
770
|
+
// ★開発中(リポジトリの中)は同梱されたものを使う
|
|
771
|
+
try { if (fs.existsSync(PAYLOAD) && fs.readdirSync(PAYLOAD).length) { PAYLOAD_DIR = PAYLOAD; return; } } catch (_) {}
|
|
772
|
+
const cache = baseCacheDir();
|
|
773
|
+
const 控えがある = (() => { try { return fs.existsSync(cache) && fs.readdirSync(cache).length > 0; } catch (_) { return false; } })();
|
|
774
|
+
if (控えがある && !force) { PAYLOAD_DIR = cache; return; }
|
|
775
|
+
let rows;
|
|
776
|
+
try {
|
|
777
|
+
rows = await supPost('get_supplement', { p_name: BASE_NAME });
|
|
778
|
+
} catch (e) {
|
|
779
|
+
if (控えがある) { PAYLOAD_DIR = cache; return; } // ★取れなくても控えで進む
|
|
780
|
+
throw new Error('配るものを取ってこられませんでした(' + e.message + ')');
|
|
781
|
+
}
|
|
782
|
+
if (!rows.length) {
|
|
783
|
+
if (控えがある) { PAYLOAD_DIR = cache; return; }
|
|
784
|
+
throw new Error('配布口に「' + BASE_NAME + '」がありません');
|
|
785
|
+
}
|
|
786
|
+
for (const r of rows) {
|
|
787
|
+
const dst = path.join(cache, r.path);
|
|
788
|
+
fs.mkdirSync(path.dirname(dst), { recursive: true });
|
|
789
|
+
fs.writeFileSync(dst, r.content, 'utf8');
|
|
790
|
+
}
|
|
791
|
+
PAYLOAD_DIR = cache;
|
|
792
|
+
}
|
|
730
793
|
// ★版は package.json だけに書く(2026-08-21)。
|
|
731
794
|
// コードにも書くと、上げ忘れて**別々の版を名乗る**。正本は1つ。
|
|
732
795
|
const VERSION = (() => {
|
|
@@ -755,13 +818,13 @@ const NEEDS = {
|
|
|
755
818
|
|
|
756
819
|
function listPayload() {
|
|
757
820
|
const out = [];
|
|
758
|
-
if (!fs.existsSync(
|
|
821
|
+
if (!fs.existsSync(PAYLOAD_DIR)) return out;
|
|
759
822
|
(function w(d) {
|
|
760
823
|
for (const e of fs.readdirSync(d)) {
|
|
761
824
|
const p = path.join(d, e);
|
|
762
|
-
fs.statSync(p).isDirectory() ? w(p) : out.push(path.relative(
|
|
825
|
+
fs.statSync(p).isDirectory() ? w(p) : out.push(path.relative(PAYLOAD_DIR, p).split('\\').join('/'));
|
|
763
826
|
}
|
|
764
|
-
})(
|
|
827
|
+
})(PAYLOAD_DIR);
|
|
765
828
|
return out.sort();
|
|
766
829
|
}
|
|
767
830
|
|
|
@@ -782,6 +845,21 @@ function writeManifest(dest, m) {
|
|
|
782
845
|
fs.writeFileSync(path.join(dest, MANIFEST), JSON.stringify(m, null, 2), 'utf8');
|
|
783
846
|
}
|
|
784
847
|
|
|
848
|
+
function listInstallable() {
|
|
849
|
+
const files = listPayload();
|
|
850
|
+
const groups = {};
|
|
851
|
+
for (const f of files) {
|
|
852
|
+
const g = f.split('/').slice(0, 2).join('/').replace(/\.[^.]+$/, '');
|
|
853
|
+
(groups[g] = groups[g] || []).push(f);
|
|
854
|
+
}
|
|
855
|
+
return {
|
|
856
|
+
置けるもの: Object.entries(groups).map(([g, fs2]) => ({ 名前: g, 件数: fs2.length })),
|
|
857
|
+
合計: files.length,
|
|
858
|
+
別に要るもの: Object.entries(NEEDS).map(([k, v]) => k + ':' + v),
|
|
859
|
+
note: '★ルールと番頭の核は、このMCPが持っています。置く必要はありません。',
|
|
860
|
+
};
|
|
861
|
+
}
|
|
862
|
+
|
|
785
863
|
function install(to, overwrite) {
|
|
786
864
|
const files = listPayload();
|
|
787
865
|
if (!files.length) throw new Error('配るものが見つかりません(payload が空です)');
|
|
@@ -791,7 +869,7 @@ function install(to, overwrite) {
|
|
|
791
869
|
const m = readManifest(dest);
|
|
792
870
|
const placed = [], skipped = [], backed = [];
|
|
793
871
|
for (const f of files) {
|
|
794
|
-
const src = path.join(
|
|
872
|
+
const src = path.join(PAYLOAD_DIR, f);
|
|
795
873
|
const dst = path.join(dest, f);
|
|
796
874
|
if (fs.existsSync(dst)) {
|
|
797
875
|
if (!overwrite) { skipped.push(f); continue; }
|
|
@@ -847,7 +925,7 @@ function update(to, apply) {
|
|
|
847
925
|
|
|
848
926
|
const 入れ替える = [], 新しく足す = [], 触らない = [], 変わっていない = [];
|
|
849
927
|
for (const f of files) {
|
|
850
|
-
const src = path.join(
|
|
928
|
+
const src = path.join(PAYLOAD_DIR, f);
|
|
851
929
|
const dst = path.join(dest, f);
|
|
852
930
|
const now = digest(src);
|
|
853
931
|
if (!fs.existsSync(dst)) { 新しく足す.push(f); continue; }
|
|
@@ -884,21 +962,21 @@ function update(to, apply) {
|
|
|
884
962
|
const dst = path.join(dest, f);
|
|
885
963
|
try {
|
|
886
964
|
fs.renameSync(dst, dst + '.bak-' + stampCompact()); // ★消さずに控えへ
|
|
887
|
-
fs.copyFileSync(path.join(
|
|
888
|
-
m.files[f] = digest(path.join(
|
|
965
|
+
fs.copyFileSync(path.join(PAYLOAD_DIR, f), dst);
|
|
966
|
+
m.files[f] = digest(path.join(PAYLOAD_DIR, f));
|
|
889
967
|
done.push(f);
|
|
890
968
|
} catch (_) {}
|
|
891
969
|
}
|
|
892
970
|
for (const f of 新しく足す) {
|
|
893
971
|
const dst = path.join(dest, f);
|
|
894
972
|
fs.mkdirSync(path.dirname(dst), { recursive: true });
|
|
895
|
-
fs.copyFileSync(path.join(
|
|
896
|
-
m.files[f] = digest(path.join(
|
|
973
|
+
fs.copyFileSync(path.join(PAYLOAD_DIR, f), dst);
|
|
974
|
+
m.files[f] = digest(path.join(PAYLOAD_DIR, f));
|
|
897
975
|
added.push(f);
|
|
898
976
|
}
|
|
899
977
|
for (const f of 触らない) {
|
|
900
978
|
// ★元は動かさない。新しい版を隣に置くだけ。見比べて、要るところだけ写してもらう
|
|
901
|
-
try { fs.copyFileSync(path.join(
|
|
979
|
+
try { fs.copyFileSync(path.join(PAYLOAD_DIR, f), path.join(dest, f + '.new')); asNew.push(f + '.new'); } catch (_) {}
|
|
902
980
|
}
|
|
903
981
|
m.version = VERSION;
|
|
904
982
|
try { writeManifest(dest, m); } catch (_) {}
|
|
@@ -1062,20 +1140,22 @@ function supEndpoint(fn) {
|
|
|
1062
1140
|
if (!url) return null;
|
|
1063
1141
|
return url.replace(/\/rpc\/[^/]+$/, '/rpc/' + fn);
|
|
1064
1142
|
}
|
|
1143
|
+
// 🚨★**同じ役割のものを2つ持たない**(2026-08-28)
|
|
1144
|
+
// ★ここには `licenseConfig()` という写しがあった。★`client.js` の `config()` と同じ仕事をしていた。
|
|
1145
|
+
// ★★実際に事故った=`BANTO_KEY`(鍵と署名を1つにまとめた形)の読み取りを client.js にだけ入れ、
|
|
1146
|
+
// ★こちらを直し忘れた。★署名の検査は通るのに、サプリの口だけ「鍵が設定されていません」と言う、
|
|
1147
|
+
// ★**静かなズレ**になった。★2つあると、必ずこうなる。
|
|
1148
|
+
// ★→ ★**写しを消して、正本(client.js の config)を呼ぶ。**
|
|
1149
|
+
// ★client.js が無い構成でも落ちないように、無ければ環境変数だけで組む。
|
|
1065
1150
|
function licenseConfig() {
|
|
1066
|
-
// 🚨★2026-08-28=★sig を足した。★client.js 側だけ直して、★こちらを忘れていた
|
|
1067
|
-
// (★同じ役割のものが2箇所にある=★正本割れ。★片方だけ直すと静かにずれる)
|
|
1068
|
-
const c = { key: process.env.BANTOU_LICENSE_KEY || '', url: process.env.BANTOU_LICENSE_URL || '',
|
|
1069
|
-
anonKey: process.env.BANTOU_LICENSE_ANON || '',
|
|
1070
|
-
sig: process.env.BANTOU_LICENSE_SIG || '' };
|
|
1071
1151
|
try {
|
|
1072
|
-
const
|
|
1073
|
-
|
|
1074
|
-
c.url = c.url || j.url || '';
|
|
1075
|
-
c.anonKey = c.anonKey || j.anonKey || '';
|
|
1076
|
-
c.sig = c.sig || j.sig || '';
|
|
1152
|
+
const LIC = require('./license/client.js');
|
|
1153
|
+
if (LIC && LIC.config) return LIC.config();
|
|
1077
1154
|
} catch (_) {}
|
|
1078
|
-
return
|
|
1155
|
+
return { key: process.env.BANTO_KEY ? String(process.env.BANTO_KEY).split('.')[0] : (process.env.BANTOU_LICENSE_KEY || ''),
|
|
1156
|
+
url: process.env.BANTOU_LICENSE_URL || '',
|
|
1157
|
+
anonKey: process.env.BANTOU_LICENSE_ANON || '',
|
|
1158
|
+
sig: process.env.BANTOU_LICENSE_SIG || '' };
|
|
1079
1159
|
}
|
|
1080
1160
|
|
|
1081
1161
|
function supPost(fn, body) {
|
|
@@ -1442,7 +1522,7 @@ const 変えるかもしれない道具 = new Set([
|
|
|
1442
1522
|
'mask_file', 'unmask_file', 'render', 'make_dict', 'policy',
|
|
1443
1523
|
'tidy', 'tidy_undo', 'set_polaris', 'learn', 'remember', 'settle',
|
|
1444
1524
|
'setup_google', 'connect_google', 'add_task', 'complete_task',
|
|
1445
|
-
'share_stats', 'open_shelf',
|
|
1525
|
+
'share_stats', 'open_shelf', 'check_masters',
|
|
1446
1526
|
]);
|
|
1447
1527
|
|
|
1448
1528
|
function 足あとを残す(name, args) {
|
|
@@ -1550,11 +1630,11 @@ function callTool(name, args) {
|
|
|
1550
1630
|
}
|
|
1551
1631
|
|
|
1552
1632
|
if (name === 'install') {
|
|
1553
|
-
return install(args && args.to, args && args.overwrite);
|
|
1633
|
+
return ensurePayload(true).then(() => install(args && args.to, args && args.overwrite));
|
|
1554
1634
|
}
|
|
1555
1635
|
|
|
1556
1636
|
if (name === 'update') {
|
|
1557
|
-
return update(args && args.to, args && args.apply);
|
|
1637
|
+
return ensurePayload(true).then(() => update(args && args.to, args && args.apply));
|
|
1558
1638
|
}
|
|
1559
1639
|
|
|
1560
1640
|
if (name === 'policy') {
|
|
@@ -1585,6 +1665,10 @@ function callTool(name, args) {
|
|
|
1585
1665
|
return PRIV.render(FREE, args && args.mask_id, args && args.text, args && args.to, args && args.dir);
|
|
1586
1666
|
}
|
|
1587
1667
|
|
|
1668
|
+
if (name === 'check_masters') {
|
|
1669
|
+
return FREE.checkMasters(args && args.dir, args && args.decide);
|
|
1670
|
+
}
|
|
1671
|
+
|
|
1588
1672
|
if (name === 'tidy') return tidy(args && args.dir, args && args.days, args && args.apply);
|
|
1589
1673
|
if (name === 'tidy_undo') return tidyUndo(args && args.dir);
|
|
1590
1674
|
|
|
@@ -1620,18 +1704,8 @@ function callTool(name, args) {
|
|
|
1620
1704
|
if (name === 'remove_supplement') return removeSupplement(args && args.name, args && args.to);
|
|
1621
1705
|
|
|
1622
1706
|
if (name === 'list_installable') {
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
for (const f of files) {
|
|
1626
|
-
const g = f.split('/').slice(0, 2).join('/').replace(/\.[^.]+$/, '');
|
|
1627
|
-
(groups[g] = groups[g] || []).push(f);
|
|
1628
|
-
}
|
|
1629
|
-
return {
|
|
1630
|
-
置けるもの: Object.entries(groups).map(([g, fs2]) => ({ 名前: g, 件数: fs2.length })),
|
|
1631
|
-
合計: files.length,
|
|
1632
|
-
別に要るもの: Object.entries(NEEDS).map(([k, v]) => k + ':' + v),
|
|
1633
|
-
note: '★ルールと番頭の核は、このMCPが持っています。置く必要はありません。',
|
|
1634
|
-
};
|
|
1707
|
+
// ★一覧も配布口から。★控えがあれば通信しない(force を付けない)
|
|
1708
|
+
return ensurePayload(false).then(() => listInstallable());
|
|
1635
1709
|
}
|
|
1636
1710
|
|
|
1637
1711
|
if (name === 'check_text') {
|