leerness 1.36.95 โ†’ 1.36.97

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/lib/graph.js CHANGED
@@ -8,6 +8,11 @@
8
8
  'use strict';
9
9
  const path = require('path');
10
10
  const { absRoot, exists, read, writeUtf8, log } = require('./io');
11
+ // 1.36.96 (P-0010): ๐Ÿ“„ ๋ฌธ์„œ ํƒญ์˜ ์‹œํฌ๋ฆฟ ๋งˆ์Šคํ‚น์€ dashboard ์™€ **๊ฐ™์€ ์ˆ ์–ด**๋ฅผ ์“ด๋‹ค.
12
+ // ํ‘œ๋ฉด๋งˆ๋‹ค ๋‹ค์‹œ ๊ตฌํ˜„ํ•˜๋ฉด ํ•œ์ชฝ๋งŒ ๊ณ ์ณ์ง€๋Š” ๋ถˆ์ผ์น˜๊ฐ€ ์ƒ๊ธด๋‹ค(1.36.88โ†’1.36.90 ์—์„œ ์ด๋ฏธ ๊ฒช์€ ํด๋ž˜์Šค).
13
+ const _redact = require('./pure-utils').redactSecrets;
14
+ // 1.36.96: ๋งˆ์Šคํ‚น์ด **์™„๋ฒฝํ•ด์•ผ ํ•˜๋Š” ๊ตฌ์กฐ๋ฅผ ๋ฒ„๋ ธ๋‹ค** โ€” ์ž๊ฒฉ์ฆ๋ช… ํ‘œ์ง€๊ฐ€ ์žˆ๋Š” ๋ฌธ์„œ๋Š” ๋ณธ๋ฌธ์„ ์‹ฃ์ง€ ์•Š๋Š”๋‹ค.
15
+ const _hasCred = require('./pure-utils').hasCredentialMarker;
11
16
 
12
17
  // ๊ฒ€์ฆ๋œ ํ”„๋กœํ† ํƒ€์ž… ํ…œํ”Œ๋ฆฟ(Claude Preview ๋ Œ๋”+ํด๋ฆญ์กฐํšŒ ํ™•์ธ). `/*__DATA__*/null` ์ž๋ฆฌํ‘œ์‹œ์ž์— JSON ์ฃผ์ž….
13
18
  // String.raw ํ•„์ˆ˜: ๋‚ด๋ถ€ JS ์˜ `\'` ๊ฐ™์€ escape ๊ฐ€ ์›๋ฌธ ๊ทธ๋Œ€๋กœ ์ถœ๋ ฅ๋ผ ๋ธŒ๋ผ์šฐ์ € JS ์—”์ง„์ด ํ•ด์„ํ•˜๋„๋ก ๋ณด์กด.
@@ -62,10 +67,36 @@ canvas{position:fixed;inset:0;top:46px}
62
67
  .cmd.show{display:flex;align-items:center;gap:10px}
63
68
  .cmd button{background:#1a2230;border:1px solid var(--line);color:var(--txt);border-radius:5px;padding:3px 9px;cursor:pointer;font:inherit;font-size:10px}
64
69
  .note{color:var(--mut);font-size:11px;margin:14px 0 18px;line-height:1.6}
70
+ /* ๐Ÿ“„ ๋ฌธ์„œ ํƒญ (1.36.96, P-0010) */
71
+ .dwrap{display:flex;gap:16px;align-items:flex-start}
72
+ .dnav{flex:0 0 190px;position:sticky;top:70px}
73
+ .dnav div{padding:7px 10px;border-radius:7px;cursor:pointer;font-size:12px;color:var(--mut);user-select:none}
74
+ .dnav div.on{background:#1a2230;color:#fff}
75
+ .dbody{flex:1;min-width:0;border:1px solid var(--line);border-radius:10px;padding:16px 22px;background:var(--panel)}
76
+ .md h1,.md h2,.md h3,.md h4,.md h5,.md h6{color:#fff;line-height:1.35;margin:18px 0 10px}
77
+ .md h1{font-size:20px}.md h2{font-size:17px}.md h3{font-size:15px}.md h4,.md h5,.md h6{font-size:13px}
78
+ .md p{margin:0 0 10px;line-height:1.7;word-break:break-word}
79
+ .md ul,.md ol{margin:0 0 10px;padding-left:22px}
80
+ .md li{margin:3px 0;line-height:1.65}
81
+ .md pre{background:#0b0f14;border:1px solid var(--line);border-radius:8px;padding:12px 14px;overflow:auto;margin:0 0 12px}
82
+ .md code{font-family:var(--mono);font-size:12px;color:#7ee787}
83
+ .md pre code{color:#c9d1d9}
84
+ .md table{border-collapse:collapse;margin:0 0 12px;display:block;overflow-x:auto}
85
+ .md th,.md td{border:1px solid var(--line);padding:6px 10px;font-size:12px;text-align:left}
86
+ .md th{background:#141b23;color:#fff}
87
+ .md a{color:#58a6ff}
88
+ /* ์ข์€ ํ™”๋ฉด์—์„œ 190px ๊ณ ์ • ๋‚ด๋น„๊ฐ€ ์ž๋ฆฌ๋ฅผ ๋‹ค ๋จน์–ด ๋ณธ๋ฌธ์ด ํ•œ ๊ธ€์ž์”ฉ ์„ธ๋กœ๋กœ ํ˜๋ €๋‹ค(์‹ค์ œ ๋ธŒ๋ผ์šฐ์ €์—์„œ ํ™•์ธ).
89
+ ํ…Œ์ŠคํŠธ๋กœ๋Š” ์•ˆ ๋ณด์ด๋Š” ์ข…๋ฅ˜๋ผ ๋ˆˆ์œผ๋กœ ๋ด์•ผ ์žกํžŒ๋‹ค. ์ข์•„์ง€๋ฉด ๋‚ด๋น„๋ฅผ ๊ฐ€๋กœ ์นฉ์œผ๋กœ ์ ‘๋Š”๋‹ค. */
90
+ @media (max-width:760px){
91
+ .dwrap{flex-direction:column}
92
+ .dnav{position:static;flex:0 0 auto;display:flex;flex-wrap:wrap;gap:4px;width:100%}
93
+ .dnav div{border:1px solid var(--line)}
94
+ .dbody{width:100%;padding:14px 16px}
95
+ }
65
96
  </style></head><body>
66
97
  <div id="bar">
67
98
  <div class="ttl"><span class="dot"></span><span id="proj">leerness</span><span style="color:var(--mut);font-weight:400">/ ontology</span></div>
68
- <div id="tabs"><span class="tab on" data-v="graph">๊ทธ๋ž˜ํ”„</span><span class="tab" data-v="roadmap">๋กœ๋“œ๋งต</span><span class="tab" data-v="tech">๐Ÿ›  ๊ธฐ์ˆ </span></div>
99
+ <div id="tabs"><span class="tab on" data-v="graph">๊ทธ๋ž˜ํ”„</span><span class="tab" data-v="roadmap">๋กœ๋“œ๋งต</span><span class="tab" data-v="tech">๐Ÿ›  ๊ธฐ์ˆ </span><span class="tab" data-v="docs">๐Ÿ“„ ๋ฌธ์„œ</span></div>
69
100
  <span class="stat" id="stat"></span>
70
101
  <input id="search" placeholder="search nodesโ€ฆ" autocomplete="off">
71
102
  <div id="chips"></div>
@@ -76,6 +107,7 @@ canvas{position:fixed;inset:0;top:46px}
76
107
  <div id="hint">drag node ยท scroll zoom ยท drag bg pan ยท click node โ†’ details ยท search+Enter jump ยท f / dblclick fit ยท p export PNG ยท Esc close</div>
77
108
  <div id="vroadmap" class="view"></div>
78
109
  <div id="vtech" class="view"></div>
110
+ <div id="vdocs" class="view"></div>
79
111
  <script>
80
112
  var DATA = /*__DATA__*/null;
81
113
  var COLORS={task:'#58a6ff',plan:'#d29922',decision:'#39d0d8',lesson:'#e3b341',rule:'#bc8cff',skill:'#2dd4bf',feature:'#6e7681'};
@@ -172,7 +204,7 @@ window.addEventListener('keydown',function(ev){ if(ev.target&&ev.target.tagName=
172
204
  cv.addEventListener('dblclick',function(ev){ if(!hit(ev.offsetX,ev.offsetY)){_fit=true;fitView();} });
173
205
 
174
206
  // โ”€โ”€ ํƒญ (1.36.53, UR-0062): ๊ทธ๋ž˜ํ”„ | ๋กœ๋“œ๋งต | ๐Ÿ›  ๊ธฐ์ˆ  (ํ† ๊ธ€ ์Šค์œ„์น˜ UI ์ œ๊ฑฐ โ†’ CLI ๊ฐ€์ด๋“œ๋กœ ๋Œ€์ฒด) โ”€โ”€
175
- var _views={roadmap:document.getElementById('vroadmap'),tech:document.getElementById('vtech')};
207
+ var _views={roadmap:document.getElementById('vroadmap'),tech:document.getElementById('vtech'),docs:document.getElementById('vdocs')};
176
208
  document.querySelectorAll('.tab').forEach(function(t){t.onclick=function(){
177
209
  document.querySelectorAll('.tab').forEach(function(x){x.classList.toggle('on',x===t);});
178
210
  var v=t.getAttribute('data-v');
@@ -234,10 +266,222 @@ document.querySelectorAll('.tab').forEach(function(t){t.onclick=function(){
234
266
  +'ยท ์ด ํŒŒ์ผ์€ ์ •์  HTML โ€” ์ƒํƒœ ๋ณ€๊ฒฝ์€ ํ„ฐ๋ฏธ๋„(๋˜๋Š” AI)์—๊ฒŒ ์œ„ ๋ช…๋ น์„ ์‹คํ–‰์‹œํ‚ค์„ธ์š”. AI ๋Š” ํ† ๊ธ€ ์ƒํƒœ๋ฅผ ์ž๋™ ์ค€์ˆ˜ํ•ฉ๋‹ˆ๋‹ค.</div></div>';
235
267
  el.innerHTML=h;
236
268
  })();
269
+
270
+ // โ”€โ”€ ๐Ÿ“„ ๋ฌธ์„œ ํƒญ (1.36.96, P-0010) โ€” .harness ํ•ต์‹ฌ ๋ฌธ์„œ 7์ข…์„ ์ด ํŒŒ์ผ ์•ˆ์—์„œ ๊ทธ๋Œ€๋กœ ์ฝ๋Š”๋‹ค.
271
+ // ๋ Œ๋”๋Ÿฌ๋Š” **์ตœ์†Œ ๋ถ€๋ถ„์ง‘ํ•ฉ**๋งŒ ํ•ด์„ํ•œ๋‹ค: ์ œ๋ชฉ ยท ๋ชฉ๋ก ยท ์ฝ”๋“œํŽœ์Šค ยท ํ‘œ ยท ๋งํฌ ยท ๊ฐ•์กฐ.
272
+ // ๊ทธ ๋ฐ–์˜ ๋ชจ๋“  ๊ฒƒ(์›์‹œ HTML ํฌํ•จ)์€ ์ด์Šค์ผ€์ดํ”„๋œ ํ‰๋ฌธ์œผ๋กœ ๋‚จ๋Š”๋‹ค โ€” ํŒŒ์„œ๋ฅผ ๋„“ํž์ˆ˜๋ก XSS ๋ฉด์ด ๋„“์–ด์ง„๋‹ค.
273
+ // ์ด ํ…œํ”Œ๋ฆฟ์€ String.raw ๋ผ ๋ฆฌํ„ฐ๋Ÿด ๋ฐฑํ‹ฑ์„ ์“ธ ์ˆ˜ ์—†๋‹ค(ํ…œํ”Œ๋ฆฟ์ด ๋Š๊ธด๋‹ค) โ†’ ์ •๊ทœ์‹/๋ฌธ์ž์—ด์—์„œ \x60 ์„ ์“ด๋‹ค.
274
+ function mdEsc(s){return String(s==null?'':s).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/"/g,'&quot;');}
275
+ // URL ํ—ˆ์šฉ: http(s) ยท mailto ยท ํŽ˜์ด์ง€์•ต์ปค ยท ์ƒ๋Œ€๊ฒฝ๋กœ. ๊ทธ ์™ธ **์Šคํ‚ด์ด ๋ถ™์€ ๊ฒƒ์€ ์ „๋ถ€ ๊ฑฐ๋ถ€**(javascript:, data:, vbscript: โ€ฆ).
276
+ // ๋ธŒ๋ผ์šฐ์ € URL ํŒŒ์„œ๋Š” ํƒญ/๊ฐœํ–‰/CR ์„ **์ œ๊ฑฐํ•œ ๋’ค** ์Šคํ‚ด์„ ์ฝ๋Š”๋‹ค โ€” ์‹ค์ธก์œผ๋กœ ํ™•์ธํ–ˆ๋‹ค(java + ํƒญ + script:
277
+ // ๋ฅผ href ์— ๋„ฃ์œผ๋ฉด protocol ์ด javascript: ๋กœ ๋‚˜์˜จ๋‹ค). ์ง€๊ธˆ์€ ๋งํฌ ํ† ํฌ๋‚˜์ด์ €๊ฐ€ ๊ณต๋ฐฑ๋ฅ˜๋ฅผ ๋ฐฐ์ œํ•ด ๊ทธ๋Ÿฐ
278
+ // ์ž…๋ ฅ์ด ์• ์ดˆ์— ๋งํฌ๊ฐ€ ๋˜์ง€ ์•Š์ง€๋งŒ ๊ทธ๊ฑด **์šฐ์—ฐํ•œ ์•ˆ์ „**์ด๋‹ค. ํ† ํฌ๋‚˜์ด์ €๋ฅผ ๋‚˜์ค‘์— ๋„“ํžˆ๋ฉด ๋˜์‚ด์•„๋‚œ๋‹ค.
279
+ // (์ด ์ฃผ์„์— ๋ฐฑํ‹ฑ์„ ์“ฐ๋ฉด String.raw ํ…œํ”Œ๋ฆฟ์ด ๊ฑฐ๊ธฐ์„œ ๋Š๊ธด๋‹ค โ€” ๋ฐ”๋กœ ์œ„์— ์ ์–ด ๋†“๊ณ  ํ•œ ๋ฒˆ ์–ด๊ฒผ๋‹ค.)
280
+ // ํŒ์ • ์ „์— ๋ธŒ๋ผ์šฐ์ €์™€ ๊ฐ™์€ ๋ฌธ์ž๋ฅผ ์ œ๊ฑฐํ•ด, ์šฐ๋ฆฌ๊ฐ€ ๋ณด๋Š” ๊ฒƒ๊ณผ ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ๋ณด๋Š” ๊ฒƒ์„ ์ผ์น˜์‹œํ‚จ๋‹ค.
281
+ // ํƒญยท๊ฐœํ–‰๋ฟ ์•„๋‹ˆ๋ผ **C0 ์ œ์–ด๋ฌธ์ž์™€ DEL ์ „๋ถ€**๋ฅผ ์ œ๊ฑฐํ•œ๋‹ค โ€” ๊ฒ€์ˆ˜ ์‹ค์ธก์—์„œ \x03ยท\x01ยท\x00ยท\x1Fยท\x7F ๋ฅผ
282
+ // ์•ž์ด๋‚˜ ์ค‘๊ฐ„์— ๋ผ์šฐ๋ฉด ์Šคํ‚ด ํŒ์ •์„ ๋น ์ ธ๋‚˜๊ฐ”๋‹ค. ๋ธŒ๋ผ์šฐ์ €๋Š” ๊ทธ๋Ÿฐ ๋ฌธ์ž๋ฅผ URL ์—์„œ ๋ฌด์‹œํ•˜๊ฑฐ๋‚˜ ์ œ๊ฑฐํ•œ๋‹ค.
283
+ function mdUrl(u){var t=String(u||'').replace(/[\x00-\x1F\x7F]/g,'').trim();
284
+ // ๋ธŒ๋ผ์šฐ์ €๋Š” ์—ญ์Šฌ๋ž˜์‹œ๋ฅผ ์Šฌ๋ž˜์‹œ์ฒ˜๋Ÿผ ์ฝ๋Š”๋‹ค โ€” ์—ญ์Šฌ๋ž˜์‹œ 2๊ฐœ๋กœ ์‹œ์ž‘ํ•˜๋Š” ๊ฐ’์€ ํ”„๋กœํ† ์ฝœ ์ƒ๋Œ€ URL ๋กœ ํ•ด์„๋œ๋‹ค
285
+ // (๊ฒ€์ˆ˜ ์‹ค์ธก: file://host/share, HTTPS ํŽ˜์ด์ง€์—์„œ๋Š” https://host/share). ์ข…์ „์—” ์Šฌ๋ž˜์‹œ 2๊ฐœ๋งŒ ๋ง‰์•˜๋‹ค.
286
+ if(/^[\\/]{2}/.test(t))return null;
287
+ if(/^(https?:\/\/|mailto:|#)/i.test(t))return t;
288
+ if(/^[a-z][a-z0-9+.\-]*:/i.test(t))return null;
289
+ return t;}
290
+ function mdInline(s){
291
+ // ์ฝ”๋“œ์ŠคํŒฌ์„ **๋จผ์ €** ๋ฝ‘์•„ ์ž๋ฆฌํ‘œ์‹œ์ž๋กœ ์น˜ํ™˜ํ•œ๋‹ค โ€” ์•ˆ์ชฝ์˜ * ๋‚˜ [](โ€ฆ) ๊ฐ€ ๊ฐ•์กฐ/๋งํฌ๋กœ ํ•ด์„๋˜๋ฉด ์•ˆ ๋œ๋‹ค.
292
+ // ์ž๋ฆฌํ‘œ์‹œ์ž๋Š” U+0001 ๋กœ ๊ฐ์‹ผ๋‹ค(์ด์Šค์ผ€์ดํ”„๋œ ๋ณธ๋ฌธ์— ์ ˆ๋Œ€ ๋‚˜ํƒ€๋‚˜์ง€ ์•Š๋Š” ๋ฌธ์ž). ๊ฐ์‹ธ๋Š” ๋ฌธ์ž๋ฅผ ๋น ๋œจ๋ฆฌ๋ฉด
293
+ // ๋ณต์› ์ •๊ทœ์‹์ด ๋ณธ๋ฌธ์˜ **๋ชจ๋“  ์ˆซ์ž**๋ฅผ ์‚ผํ‚จ๋‹ค โ€” ์ดˆ์•ˆ์—์„œ ์‹ค์ œ๋กœ ๊ทธ๋žฌ๋‹ค.
294
+ // ๋ฌธ์„œ์— U+0001 ์ด ์‹ค์ œ๋กœ ๋“ค์–ด ์žˆ์œผ๋ฉด ์ž๋ฆฌํ‘œ์‹œ์ž์™€ ์ถฉ๋Œํ•œ๋‹ค โ€” ๋จผ์ € ์ง€์šด๋‹ค(ํ‘œ์‹œ์šฉ ํ…์ŠคํŠธ๋ผ ๋ฌดํ•ด).
295
+ var out=mdEsc(s).replace(/[\x01\x02]/g,''),codes=[],links=[];
296
+ out=out.replace(/\x60([^\x60]+)\x60/g,function(m,c){codes.push(c);return '\x01'+(codes.length-1)+'\x01';});
297
+ // ์ˆ˜๋Ÿ‰์ž๋ฅผ **์œ ๊ณ„**๋กœ ๋‘”๋‹ค. ์ข…์ „ [^\]]* ๋Š” ๋‹ซํžˆ์ง€ ์•Š์€ ๋Œ€๊ด„ํ˜ธ๋งˆ๋‹ค ๋‚จ์€ ๋ฌธ์ž์—ด ์ „์ฒด๋ฅผ ๋‹ค์‹œ ํ›‘์–ด O(nยฒ) ์˜€๋‹ค
298
+ // (์‹ค์ธก: 64KB ์ž…๋ ฅ 1,825ms ยท ์ตœ์•… 2,730ms โ€” ๊ทธ๋งŒํผ ๋ธŒ๋ผ์šฐ์ € ๋ฉ”์ธ ์Šค๋ ˆ๋“œ๊ฐ€ ๋ฉˆ์ถ˜๋‹ค).
299
+ // ์ƒํ•œ์„ ๋„˜๋Š” ๋งํฌ๋Š” ๋งํฌ๊ฐ€ ๋˜์ง€ ์•Š๊ณ  ๊ธ€์ž๋กœ ๋‚จ๋Š”๋‹ค(๊ณ„์•ฝ์ƒ ์•ˆ์ „ํ•œ ์ชฝ์œผ๋กœ ์‹คํŒจํ•œ๋‹ค).
300
+ // URL ์ƒํ•œ์€ **์ด์Šค์ผ€์ดํ”„ ํ›„** ๊ธธ์ด์— ๊ฑธ๋ฆฐ๋‹ค โ€” ์›๋ฌธ 796์ž์งœ๋ฆฌ ์ •์ƒ ์ฟผ๋ฆฌ URL ์ด & โ†’ &amp; ํŒฝ์ฐฝ์œผ๋กœ
301
+ // 1,192์ž๊ฐ€ ๋˜์–ด ๊ฑฐ๋ถ€๋๋‹ค(๊ฒ€์ˆ˜ ์‹ค์ธก). ์‹ค์ธก ๋น„๊ต๋กœ ๋ผ๋ฒจ 300 / URL 4000 ์„ ๊ณจ๋ž๋‹ค:
302
+ // ์ตœ์•… 115ms(๋ฌด๊ณ„๋Š” 1,747ms)์ด๊ณ  ๊ทธ URL ์€ ๋งํฌ๋œ๋‹ค.
303
+ out=out.replace(/\[([^\]\n]{0,300})\]\(([^)\s]{1,4000})\)/g,function(m,t,u){var v=mdUrl(u);
304
+ if(v===null)return m;
305
+ // ์™„์„ฑ๋œ <a> ๋„ ์ž๋ฆฌํ‘œ์‹œ์ž๋กœ ๋นผ ๋‘”๋‹ค โ€” ๋’ค๋”ฐ๋ฅด๋Š” ๊ฐ•์กฐ ์ •๊ทœ์‹์ด href ์•ˆ๊นŒ์ง€ ๋‹ค์‹œ ํ•ด์„ํ•ด
306
+ // href="https://a/<b>b</b>/c" ์ฒ˜๋Ÿผ URL ์„ ํ›ผ์†ํ–ˆ๋‹ค(๊ฒ€์ˆ˜ ์‹ค์ธก).
307
+ links.push('<a href="'+v+'" target="_blank" rel="noopener noreferrer">'+t+'</a>');
308
+ return '\x02'+(links.length-1)+'\x02';});
309
+ out=out.replace(/\*\*([^*]+)\*\*/g,'<b>$1</b>');
310
+ out=out.replace(/(^|[^*])\*([^*\n]+)\*/g,'$1<i>$2</i>');
311
+ out=out.replace(/\x02(\d+)\x02/g,function(m,i){return links[i];});
312
+ out=out.replace(/\x01(\d+)\x01/g,function(m,i){return '<code>'+codes[i]+'</code>';});
313
+ return out;}
314
+ function mdRender(src){
315
+ var ls=String(src||'').split(/\r?\n/),h='',i=0,list=null,olNext=0;
316
+ function endList(){if(list){h+='</'+list+'>';list=null;}}
317
+ function cells(r){return r.replace(/^\s*\|/,'').replace(/\|\s*$/,'').split('|').map(function(c){return c.trim();});}
318
+ while(i<ls.length){
319
+ var l=ls[i];
320
+ if(/^\s*\x60{3,}/.test(l)){ // ์ฝ”๋“œํŽœ์Šค โ€” ์•ˆ์ชฝ์€ ์–ด๋–ค ๊ทœ์น™๋„ ์ ์šฉํ•˜์ง€ ์•Š๋Š”๋‹ค
321
+ endList();var buf=[];i++;
322
+ while(i<ls.length&&!/^\s*\x60{3,}\s*$/.test(ls[i])){buf.push(ls[i]);i++;}
323
+ i++;h+='<pre><code>'+mdEsc(buf.join('\n'))+'</code></pre>';continue;}
324
+ var hd=l.match(/^(#{1,6})\s+(.*)$/);
325
+ if(hd){endList();var n=hd[1].length;h+='<h'+n+'>'+mdInline(hd[2])+'</h'+n+'>';i++;continue;}
326
+ if(l.indexOf('|')>=0&&i+1<ls.length&&ls[i+1].indexOf('|')>=0&&/^[\s:|\-]*-[\s:|\-]*$/.test(ls[i+1])){
327
+ endList();
328
+ // ๊ตฌ๋ถ„ํ–‰์˜ ์ฝœ๋ก ์„ ๋ฌธ๋ฒ•์œผ๋กœ๋Š” ๋ฐ›์œผ๋ฉด์„œ ์ •๋ ฌ์„ ๋ฒ„๋ฆฌ๋ฉด ์‚ฌ์šฉ์ž๊ฐ€ ์ง€์ •ํ•œ ์˜๋ฏธ๊ฐ€ ์กฐ์šฉํžˆ ์‚ฌ๋ผ์ง„๋‹ค(๊ฒ€์ˆ˜ ์‹ค์ธก).
329
+ // ๋ฐ›์€ ์ด์ƒ ๋ฐ˜์˜ํ•œ๋‹ค โ€” ์™ผ์ชฝ/๊ฐ€์šด๋ฐ/์˜ค๋ฅธ์ชฝ. (์ด ํ…œํ”Œ๋ฆฟ ์•ˆ ์ฃผ์„์—๋Š” ๋ฐฑํ‹ฑ์„ ์“ธ ์ˆ˜ ์—†๋‹ค.)
330
+ var al=cells(ls[i+1]).map(function(c){
331
+ var lft=c.charAt(0)===':',rgt=c.charAt(c.length-1)===':';
332
+ return lft&&rgt?' style="text-align:center"':(rgt?' style="text-align:right"':(lft?' style="text-align:left"':''));});
333
+ var A=function(k){return al[k]||'';};
334
+ h+='<table><thead><tr>'+cells(l).map(function(c,k){return '<th'+A(k)+'>'+mdInline(c)+'</th>';}).join('')+'</tr></thead><tbody>';
335
+ i+=2;
336
+ while(i<ls.length&&ls[i].indexOf('|')>=0&&ls[i].trim()!==''){
337
+ h+='<tr>'+cells(ls[i]).map(function(c,k){return '<td'+A(k)+'>'+mdInline(c)+'</td>';}).join('')+'</tr>';i++;}
338
+ h+='</tbody></table>';continue;}
339
+ var ul=l.match(/^\s*[-*+]\s+(.*)$/),ol=l.match(/^\s*(\d+)[.)]\s+(.*)$/);
340
+ if(ul||ol){var want=ul?'ul':'ol';
341
+ // ์›๋ฌธ ๋ฒˆํ˜ธ๋ฅผ ์ง€ํ‚ค์ง€ ์•Š์œผ๋ฉด "2. two" ๊ฐ€ ๋ธŒ๋ผ์šฐ์ €์—์„œ "1. two" ๋กœ, 1ยท3 ์ด 1ยท2 ๋กœ ํ‘œ์‹œ๋œ๋‹ค
342
+ // โ€” ๋ฌธ์„œ์˜ ์˜๋ฏธ๊ฐ€ ์กฐ์šฉํžˆ ๋ฐ”๋€๋‹ค(๊ฒ€์ˆ˜ ์‹ค์ธก). ์‹œ์ž‘๊ฐ’์€ ol start, ๊ฑด๋„ˆ๋›ด ๋ฒˆํ˜ธ๋Š” li value ๋กœ ๋ณด์กดํ•œ๋‹ค.
343
+ if(list!==want){endList();h+=want==='ol'?('<ol start="'+ol[1]+'">'):'<ul>';list=want;olNext=ol?parseInt(ol[1],10):0;}
344
+ if(ul){h+='<li>'+mdInline(ul[1])+'</li>';}
345
+ else{var num=parseInt(ol[1],10);h+=(num===olNext?'<li>':'<li value="'+num+'">')+mdInline(ol[2])+'</li>';olNext=num+1;}
346
+ i++;continue;}
347
+ if(!l.trim()){endList();i++;continue;}
348
+ endList();h+='<p>'+mdInline(l)+'</p>';i++;}
349
+ endList();return h;}
350
+
351
+ (function(){
352
+ var dd=(DATA&&DATA.docs)||{files:[]},el=_views.docs,fl=dd.files||[];
353
+ if(!fl.length){el.innerHTML='<div class="note">๋ฌธ์„œ ์—†์Œ โ€” leerness handoff . ๋กœ .harness ๋ฅผ ์ฑ„์šฐ๊ณ  ์žฌ์ƒ์„ฑํ•˜์„ธ์š”.</div>';return;}
354
+ var kb=function(n){return Math.round((n||0)/1024)+'KB';};
355
+ el.innerHTML='<div class="note">์Šค๋ƒ…์ƒท '+esc(String(dd.at||'').slice(0,19).replace('T',' '))+' UTC ยท ์ด ํŒŒ์ผ์€ <b>์ •์  ์‚ฌ๋ณธ</b>์ž…๋‹ˆ๋‹ค โ€” ์ตœ์‹  ๋‚ด์šฉ์€ <b>leerness graph --html</b> ๋กœ ์žฌ์ƒ์„ฑํ•˜์„ธ์š”. ์‹œํฌ๋ฆฟ ๊ฐ’์€ ๊ฐ€๋ ค์ง‘๋‹ˆ๋‹ค(๋ฌธ์„œ๋‹น '+kb(dd.perCap)+' ยท ์ „์ฒด '+kb(dd.totalCap)+' ์ƒํ•œ).</div>'
356
+ +'<div class="dwrap"><div class="dnav">'
357
+ +fl.map(function(f,i){return '<div data-i="'+i+'"'+(i===0?' class="on"':'')+'>'+esc(f.title)+(f.missing?' <span style="color:var(--mut)">(์—†์Œ)</span>':(f.guarded?' <span style="color:var(--mut)">๐Ÿ”’</span>':(f.truncated?' <span style="color:var(--mut)">โœ‚</span>':'')))+'</div>';}).join('')
358
+ +'</div><div class="dbody md" id="dbody"></div></div>';
359
+ var body=document.getElementById('dbody');
360
+ function show(i){
361
+ var f=fl[i]||{},h='<div class="sec" style="margin-top:0">'+esc(f.title||'')+' <span style="color:var(--mut);font-weight:400;font-size:11px">'+esc(f.rel||'')+'</span></div>';
362
+ if(f.missing){h+='<div class="note">์•„์ง ์—†๋Š” ํŒŒ์ผ์ž…๋‹ˆ๋‹ค โ€” <b>leerness handoff .</b> ์‹คํ–‰ ์‹œ ์ƒ์„ฑ๋ฉ๋‹ˆ๋‹ค.</div>';}
363
+ // ์ž๊ฒฉ์ฆ๋ช… ํ‘œ์ง€๊ฐ€ ์žˆ๋Š” ๋ฌธ์„œ๋Š” **๋ณธ๋ฌธ์„ ์‹ฃ์ง€ ์•Š๋Š”๋‹ค** โ€” ๋ถ€๋ถ„ ๋งˆ์Šคํ‚น์˜ ๋นˆํ‹ˆ์œผ๋กœ ์ƒˆ๋Š” ๊ฒƒ์„ ๊ตฌ์กฐ์ ์œผ๋กœ ๋ง‰๋Š”๋‹ค.
364
+ else if(f.guarded){h+='<div class="note">๐Ÿ”’ ์ด ๋ฌธ์„œ์—๋Š” <b>์ž๊ฒฉ์ฆ๋ช…์œผ๋กœ ๋ณด์ด๋Š” ๋‚ด์šฉ</b>์ด ์žˆ์–ด ๋ณธ๋ฌธ์„ ์‹ฃ์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค('+(f.charsTotal||0)+'์ž). ํŒŒ์ผ์„ ์ง์ ‘ ์—ฌ์„ธ์š”: '+esc(f.rel||'')+'<br>์ด ํŒŒ์ผ์€ ๊ณต์œ ๋  ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ, ๋ถ€๋ถ„์ ์œผ๋กœ ๊ฐ€๋ฆฌ๋Š” ๋Œ€์‹  ํ†ต์งธ๋กœ ์ œ์™ธํ•ฉ๋‹ˆ๋‹ค.</div>';}
365
+ else if(f.unreadable){h+='<div class="note">์ฝ์ง€ ๋ชปํ–ˆ์Šต๋‹ˆ๋‹ค(๊ถŒํ•œ/์ธ์ฝ”๋”ฉ). ์›๋ณธ ํŒŒ์ผ์€ ๊ทธ๋Œ€๋กœ์ž…๋‹ˆ๋‹ค โ€” ์ง์ ‘ ์—ฌ์„ธ์š”.</div>';}
366
+ else if(f.skipped){h+='<div class="note">โš  ์ „์ฒด ์ƒํ•œ('+kb(dd.totalCap)+')์— ๋„๋‹ฌํ•ด <b>๋‹ด๊ธฐ์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค</b> โ€” ์›๋ณธ '+(f.charsTotal||0)+'์ž. '+esc(f.rel||'')+' ๋ฅผ ์ง์ ‘ ์—ฌ์„ธ์š”.</div>';}
367
+ else{
368
+ // "์ž" ๋Š” ์ฝ”๋“œํฌ์ธํŠธ๋‹ค โ€” String.length ๋ฅผ ์“ฐ๋ฉด ์ด๋ชจ์ง€ ๋ฌธ์„œ์—์„œ ๋‘ ๋ฐฐ๋กœ ํ‘œ์‹œ๋œ๋‹ค(๊ฒ€์ˆ˜ ์‹ค์ธก).
369
+ if(f.truncated)h+='<div class="note">โš  ์ƒํ•œ์œผ๋กœ ์ž˜๋ ธ์Šต๋‹ˆ๋‹ค โ€” '+(f.chars||0)+'/'+(f.charsTotal||0)+'์ž('+(f.tail?'๋’ท๋ถ€๋ถ„':'์•ž๋ถ€๋ถ„')+'๋งŒ). ์ „์ฒด๋Š” '+esc(f.rel||'')+' ๋ฅผ ์—ฌ์„ธ์š”.</div>';
370
+ h+=(f.lang==='json')?('<pre><code>'+mdEsc(f.text||'')+'</code></pre>'):mdRender(f.text||'');}
371
+ body.innerHTML=h;}
372
+ el.querySelectorAll('.dnav div').forEach(function(n){n.onclick=function(){
373
+ el.querySelectorAll('.dnav div').forEach(function(x){x.classList.toggle('on',x===n);});
374
+ show(parseInt(n.getAttribute('data-i'),10));};});
375
+ show(0);
376
+ })();
237
377
  </script></body></html>`;
238
378
 
239
379
  const _txt = v => (v == null ? '' : String(v));
240
- function _trunc(s, n) { s = _txt(s); return s.length > n ? s.slice(0, n - 1) + 'โ€ฆ' : s; }
380
+ // 1.36.96 (๊ฒ€์ˆ˜ P1): ๋ฌธ์„œ ํƒญ๋งŒ ๊ฐ€๋“œํ–ˆ๋Š”๋ฐ **๊ฐ™์€ ๋‚ด์šฉ์ด nodesยทroadmap ์œผ๋กœ๋„ ์ž„๋ฒ ๋“œ**๋œ๋‹ค โ€”
381
+ // `decision add "Authorization: Bearer <ํ† ํฐ>"` ์ด DATA.nodes[].label / detail ๋กœ ์›๋ฌธ ๊ทธ๋Œ€๋กœ ์‹ค๋ ธ๋‹ค(์‹ค์ธก).
382
+ // ๊ทธ๋ž˜ํ”„ ์ชฝ์—๋Š” ๋งˆ์Šคํ‚น์กฐ์ฐจ ์—†์—ˆ๋‹ค. ์ž„๋ฒ ๋“œ๋˜๋Š” **๋ชจ๋“  ์‚ฌ์šฉ์ž ํ…์ŠคํŠธ**๋ฅผ ๊ฐ™์€ ์ˆ ์–ด๋กœ ํ†ต๊ณผ์‹œํ‚จ๋‹ค.
383
+ // ์ž๊ฒฉ์ฆ๋ช… ํ‘œ์ง€๊ฐ€ ์žˆ์œผ๋ฉด ๊ทธ ํ•ญ๋ชฉ ํ…์ŠคํŠธ๋ฅผ ํ†ต์งธ๋กœ ๋Œ€์ฒดํ•˜๊ณ , ์•„๋‹ˆ๋ฉด 2์ฐจ ๋งˆ์Šคํ‚น๋งŒ ์ ์šฉํ•œ๋‹ค.
384
+ const _GUARD_TXT = '๐Ÿ”’ ์ž๊ฒฉ์ฆ๋ช… ํ‘œ์ง€ โ€” ์ƒ๋žต๋จ';
385
+ const _safe = (v) => { const t = _txt(v); return t && _hasCred(t) ? _GUARD_TXT : _redact(t); };
386
+ function _trunc(s, n) { s = _safe(s); return s.length > n ? s.slice(0, n - 1) + 'โ€ฆ' : s; }
387
+
388
+ // โ”€โ”€ ๐Ÿ“„ ๋ฌธ์„œ ํƒญ ๋ฐ์ดํ„ฐ (1.36.96, P-0010) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
389
+ // ์‚ฌ์šฉ์ž ์š”์ฒญ์€ "exe ์‹คํ–‰๊ธฐ๋กœ .harness ๋งˆํฌ๋‹ค์šด์„ ๋„์šฐ๊ธฐ"์˜€๋‹ค. ์‹คํ–‰๊ธฐ๋Š” ๋Ÿฐํƒ€์ž„ ์˜์กด์„ฑ 0 ยท install script 0
390
+ // ๊ฒฝ๊ณ„๋ฅผ ๊นจ๋ฏ€๋กœ ๋งŒ๋“ค์ง€ ์•Š๋Š”๋‹ค(1.36.90 ์—์„œ ๊ฐ™์€ ๊ฒฝ๊ณ„๋ฅผ ํ…Œ์ŠคํŠธ๋กœ ๋ชป๋ฐ•์•˜๋‹ค). ๊ฐ™์€ ๋ชฉ์ ์„ ์ •์  ๋‹จ์ผ ํŒŒ์ผ๋กœ ์ด๋ฃฌ๋‹ค.
391
+ // `tail: true` ๋Š” **๋ˆ„์  ๋กœ๊ทธ**๋‹ค โ€” ์•ž์„ ๋‚จ๊ธฐ๋ฉด ๊ฐ€์žฅ ์˜ค๋ž˜๋œ ๊ฒƒ๋งŒ ๋ณด์ธ๋‹ค(1.36.95 handoff ์—์„œ ๊ฒช์€ ๊ทธ ๋ฐฉํ–ฅ ์˜ค๋ฅ˜).
392
+ const DOC_FILES = [
393
+ { key: 'plan', title: 'ํ”Œ๋žœ ยท ๋กœ๋“œ๋งต', rel: '.harness/plan.md' },
394
+ { key: 'skills', title: '์Šคํ‚ฌ', rel: '.harness/skill-index.md' },
395
+ { key: 'tech', title: '๊ธฐ์ˆ  ํ”„๋กœํ•„', rel: '.harness/tech-profile.json', lang: 'json' },
396
+ { key: 'handoff', title: '์„ธ์…˜ ํ•ธ๋“œ์˜คํ”„', rel: '.harness/session-handoff.md' },
397
+ { key: 'state', title: 'ํ˜„์žฌ ์ƒํƒœ', rel: '.harness/current-state.md' },
398
+ { key: 'progress', title: '์ง„ํ–‰ ์ถ”์ ', rel: '.harness/progress-tracker.md', tail: true },
399
+ { key: 'decisions', title: '๊ฒฐ์ • ๊ธฐ๋ก', rel: '.harness/decisions.md', tail: true },
400
+ ];
401
+ const DOC_PER_CAP = 64 * 1024;
402
+ const DOC_TOTAL_CAP = 512 * 1024;
403
+ // โš  ์˜ค๋Š˜์˜ ๋ชฉ๋ก(7์ข… ร— 64KB = 448KB)์œผ๋กœ๋Š” ์ „์ฒด ์ƒํ•œ 512KB ์— **๋„๋‹ฌํ•  ์ˆ˜ ์—†๋‹ค** โ€” ๋ชฉ๋ก์ด 8์ข… ์ด์ƒ์œผ๋กœ
404
+ // ๋Š˜ ๋•Œ๋ฅผ ์œ„ํ•œ backstop ์ด๋‹ค. ๊ทธ๋ž˜์„œ caps ๋ฅผ ์ฃผ์ž… ๊ฐ€๋Šฅํ•˜๊ฒŒ ๋’€๋‹ค: ๋„๋‹ฌ ๋ถˆ๊ฐ€๋Šฅํ•œ ๋ถ„๊ธฐ๋Š” "์žˆ๋‹ค"๊ณ ๋งŒ ์ ์œผ๋ฉด
405
+ // ๊ณตํ—ˆํ•œ ๊ฐ€๋“œ๊ฐ€ ๋œ๋‹ค(๊ทธ ํ•จ์ •์„ ์ด ์ €์žฅ์†Œ์—์„œ ๋ฐ˜๋ณตํ•ด์„œ ๋ฐŸ์•˜๋‹ค). ํ…Œ์ŠคํŠธ๊ฐ€ ์ž‘์€ ์ƒํ•œ์œผ๋กœ ์‹ค์ œ๋กœ ๋ฐœํ™”์‹œํ‚จ๋‹ค.
406
+
407
+ // ์ค„ ๊ฒฝ๊ณ„์—์„œ ์ž๋ฅด๊ณ , ํ•œ ์ค„์ด ์ƒํ•œ๋ณด๋‹ค ๊ธธ๋ฉด **์ฝ”๋“œํฌ์ธํŠธ** ๊ฒฝ๊ณ„์—์„œ ์ž๋ฅธ๋‹ค(์„œ๋กœ๊ฒŒ์ดํŠธ ์Œ ๋ถ„ํ•  ๊ธˆ์ง€).
408
+ // 1.36.96 (๊ฒ€์ˆ˜ P2): ์ƒํ•œ ๋‹จ์œ„๋ฅผ **UTF-8 ๋ฐ”์ดํŠธ**๋กœ ๋ฐ”๊ฟจ๋‹ค. ์ข…์ „์—” UTF-16 ์ฝ”๋“œ ๋‹จ์œ„๋ฅผ ์„ธ๋ฉด์„œ "64KB"๋ผ๊ณ 
409
+ // ์ ์—ˆ๊ณ , ํ•œ๊ธ€ ๋ฌธ์„œ์—์„œ๋Š” ๋ฌธ์„œ๋‹น ์‹ค์ œ 192KB ยท ํ•ฉ๊ณ„ 1.38MB ๊นŒ์ง€ ์‹ค๋ ค ์ฃผ์žฅ์ด ์‚ฌ์‹ค๊ณผ ๋‹ฌ๋ž๋‹ค(์‹ค์ธก: 1,152KB).
410
+ const _blen = (x) => Buffer.byteLength(x, 'utf8');
411
+ function _capDoc(s, cap, tail) {
412
+ s = _txt(s);
413
+ if (_blen(s) <= cap) return { text: s, truncated: false };
414
+ const ls = s.split('\n');
415
+ let acc = 0, keep = 0;
416
+ if (tail) {
417
+ for (let i = ls.length - 1; i >= 0; i--) { const add = (keep ? 1 : 0) + _blen(ls[i]); if (acc + add > cap) break; acc += add; keep++; }
418
+ } else {
419
+ for (let i = 0; i < ls.length; i++) { const add = (keep ? 1 : 0) + _blen(ls[i]); if (acc + add > cap) break; acc += add; keep++; }
420
+ }
421
+ // **"๋ฌด์—‡์„ ๋‚ด์šฉ์œผ๋กœ ์…€์ง€"๋ฅผ ํŒ์ •ํ•˜์ง€ ์•Š๋Š”๋‹ค.** ์ด ์ž๋ฆฌ์—์„œ ๋ฐ์ดํ„ฐ ์†Œ์‹ค์ด ์„ธ ๋ฒˆ ์žฌ๋ฐœํ–ˆ๋‹ค:
422
+ // โ‘  `keep > 0` โ†’ ๊ฒฝ๊ณ„์— ๋นˆ ์ค„ ํ•˜๋‚˜๋ฉด 70,000์ž๊ฐ€ 0์ž โ‘ก `acc > 0` โ†’ ๋นˆ ์ค„ ๋‘ ๊ฐœ๋ฉด 1์ž
423
+ // โ‘ข ๋‚ด์šฉ ๋ฐ”์ดํŠธ(content) โ†’ CRLF ์˜ `\r` ๊ณผ ๊ณต๋ฐฑ/ํƒญ์ด ๋‚ด์šฉ์œผ๋กœ ์„ธ์–ด์ ธ ๋‹ค์‹œ 1~3์ž
424
+ // ๋งค๋ฒˆ ์…ˆ์„ ์กฐ๊ธˆ์”ฉ ํ‹€๋ ธ์œผ๋ฏ€๋กœ ์…ˆ์„ ์—†์•ค๋‹ค. ์˜จ์ „ํ•œ ์ค„๋กœ ์ฑ„์šด ๋’ค, **๋‚จ์€ ์˜ˆ์‚ฐ๋งŒํผ ๊ฒฝ๊ณ„ ์ค„์„ ์ฝ”๋“œํฌ์ธํŠธ๋กœ
425
+ // ์ž˜๋ผ ์ด์–ด ๋ถ™์ธ๋‹ค**. ์–ด๋–ค ๊ณต๋ฐฑ ์กฐํ•ฉ์—๋„ ์˜ˆ์‚ฐ์ด ์ฑ„์›Œ์ง€๊ณ , ์ˆœ์„œ๊ฐ€ ์œ ์ง€๋˜๋ฏ€๋กœ "์•ž๋ถ€๋ถ„/๋’ท๋ถ€๋ถ„" ๊ณ ์ง€๊ฐ€ ์ฐธ์ด ๋œ๋‹ค.
426
+ // (ํ•œ๋•Œ ๋‘˜ ์ค‘ ๊ธด ์ชฝ๋งŒ ๊ณจ๋ž๋Š”๋ฐ, ๊ฒฝ๊ณ„ ์ค„์ด ๊ธธ๋ฉด **๋ฌธ์„œ์˜ ์ œ๋ชฉยทํ‘ธํ„ฐ๊ฐ€ ํ†ต์งธ๋กœ ์‚ฌ๋ผ์กŒ๋‹ค** โ€” ๊ฒ€์ˆ˜ ์‹ค์ธก.)
427
+ let packed = tail ? ls.slice(ls.length - keep).join('\n') : ls.slice(0, keep).join('\n');
428
+ // `\n` ์œผ๋กœ๋งŒ ๋‚˜๋ˆ„๋ฏ€๋กœ CRLF ๋ฌธ์„œ์—์„œ๋Š” ๊ฐ ์ค„ ๋์— `\r` ์ด ๋‚จ๋Š”๋‹ค. head ๋ฐฉํ–ฅ์—์„œ ๋‹ค์Œ ์ค„์ด ์•ˆ ๋“ค์–ด๊ฐ€๋ฉด
429
+ // ์žฌ์กฐ๋ฆฝ ๋•Œ ๊ฐœํ–‰์ด ๋ถ™์ง€ ์•Š์•„ **๊ณ ๋ฆฝ๋œ CR** ์ด ๊ผฌ๋ฆฌ์— ๋‚จ๋Š”๋‹ค(๊ฒ€์ˆ˜ ์‹ค์ธก: "A\r"). ํ‘œ์‹œ ๋ฐ์ดํ„ฐ์— ์ œ์–ด๋ฌธ์ž๋ฅผ
430
+ // ๋‚จ๊ธฐ์ง€ ์•Š๋„๋ก ์ž˜๋ผ๋‚ธ๋‹ค โ€” tail ๋ฐฉํ–ฅ์€ ์•ž์— ๊ฐœํ–‰์ด ๋ถ™์œผ๋ฏ€๋กœ ํ•ด๋‹น ์—†์Œ.
431
+ if (!tail) packed = packed.replace(/\r$/, '');
432
+ const line = (tail ? ls[ls.length - 1 - keep] : ls[keep]) || '';
433
+ // ๊ฒฝ๊ณ„ ์ค„์„ **ํ•ญ์ƒ** ์ž˜๋ผ ๋ถ™์ด๋ฉด ํ‰๋ฒ”ํ•œ ๋ฌธ์„œ๊นŒ์ง€ ์ค„ ์ค‘๊ฐ„์—์„œ ์ž˜๋ฆฐ๋‹ค("์ค„ ๊ฒฝ๊ณ„ ์ ˆ๋‹จ" ์†์„ฑ์ด ๊นจ์ง„๋‹ค).
434
+ // ํ˜ผ์ž์„œ๋„ ์ƒํ•œ์„ ๋„˜๋Š” ์ค„, ์ฆ‰ **์–ด๋–ค ์˜ˆ์‚ฐ์œผ๋กœ๋„ ํ†ต์งธ๋กœ๋Š” ๋‹ด๊ธธ ์ˆ˜ ์—†๋Š” ์ค„**์ผ ๋•Œ๋งŒ ์ž˜๋ผ ๋ถ™์ธ๋‹ค.
435
+ const mustCut = _blen(line) > cap;
436
+ const room = mustCut ? cap - acc - (keep && line ? 1 : 0) : 0; // ์ด์–ด ๋ถ™์ผ ๊ฐœํ–‰ ๋ชซ๊นŒ์ง€ ๋บ€๋‹ค
437
+ const cps = Array.from(line);
438
+ let n = 0, len = 0;
439
+ if (room > 0) {
440
+ if (tail) { for (let i = cps.length - 1; i >= 0 && len + _blen(cps[i]) <= room; i--) { len += _blen(cps[i]); n++; } }
441
+ else { for (let i = 0; i < cps.length && len + _blen(cps[i]) <= room; i++) { len += _blen(cps[i]); n++; } }
442
+ }
443
+ const part = n ? (tail ? cps.slice(cps.length - n).join('') : cps.slice(0, n).join('')) : '';
444
+ const text = !part ? packed : (!keep ? part : (tail ? part + '\n' + packed : packed + '\n' + part));
445
+ return { text, truncated: true };
446
+ }
447
+
448
+ function buildDocsData(root, nowIso, caps) {
449
+ const perCap = (caps && caps.perCap) || DOC_PER_CAP;
450
+ const totalCap = (caps && caps.totalCap) || DOC_TOTAL_CAP;
451
+ const minSlice = Math.max(256, Math.min(2048, Math.floor(totalCap / 8)));
452
+ const files = [];
453
+ let spent = 0;
454
+ for (const d of DOC_FILES) {
455
+ const base = { key: d.key, title: d.title, rel: d.rel, lang: d.lang || 'md', tail: !!d.tail };
456
+ const p = path.join(root, d.rel);
457
+ if (!exists(p)) { files.push(Object.assign({}, base, { missing: true, text: '', totalChars: 0, truncated: false })); continue; }
458
+ let raw;
459
+ try { raw = read(p); } catch { files.push(Object.assign({}, base, { unreadable: true, text: '', totalChars: 0, truncated: false })); continue; }
460
+ // **์ž๊ฒฉ์ฆ๋ช… ํ‘œ์ง€๊ฐ€ ์žˆ์œผ๋ฉด ๋ณธ๋ฌธ์„ ์‹ฃ์ง€ ์•Š๋Š”๋‹ค.** ๊ฐ’ ๋‹จ์œ„ ๋งˆ์Šคํ‚น์€ ๋‹ค์„ฏ ๋ผ์šด๋“œ ๋™์•ˆ ๊ณ„์† ์ƒˆ์–ด์„œ
461
+ // ์•ˆ์ „์˜ ์ „์ œ์—์„œ ๋บ๋‹ค(๋งˆ์Šคํ‚น์€ ์•„๋ž˜์—์„œ ๋‘ ๋ฒˆ์งธ ๋ฐฉ์–ด์ธต์œผ๋กœ๋งŒ ๋‚จ๋Š”๋‹ค). ์‹ค์ธก ๋น„์šฉ 0.6%.
462
+ if (_hasCred(raw)) {
463
+ files.push(Object.assign({}, base, {
464
+ guarded: true, text: '', truncated: false,
465
+ totalChars: raw.length, charsTotal: Array.from(raw).length, totalBytes: _blen(raw), bytes: 0, chars: 0,
466
+ }));
467
+ continue;
468
+ }
469
+ const red = _redact(raw); // dashboard ์™€ ๊ฐ™์€ ์ˆ ์–ด(๋‘ ๋ฒˆ์งธ ๋ฐฉ์–ด์ธต)
470
+ const remain = totalCap - spent;
471
+ // ์ž”์—ฌ ์˜ˆ์‚ฐ์ด ์˜๋ฏธ ์—†๋Š” ํฌ๊ธฐ๋ฉด **์กฐ๊ฐ์„ ์‹ฃ์ง€ ์•Š๊ณ  ๊ฑด๋„ˆ๋›ด๋‹ค**. ์ข…์ „์—” remain<=0 ์ผ ๋•Œ๋งŒ ๊ฑด๋„ˆ๋›ฐ์–ด
472
+ // 159์ž์งœ๋ฆฌ ์“ธ๋ชจ์—†๋Š” ์กฐ๊ฐ์ด "์ž˜๋ฆผ"์œผ๋กœ ํ‘œ์‹œ๋๋‹ค(์ธก์ •์œผ๋กœ ํ™•์ธ). ์กฐ๊ฐ๋ณด๋‹ค "์•ˆ ๋‹ด์•˜๋‹ค"๊ฐ€ ์ •ํ™•ํ•˜๋‹ค.
473
+ if (remain < minSlice) { files.push(Object.assign({}, base, { skipped: true, text: '', totalChars: red.length, totalBytes: _blen(red), bytes: 0, truncated: true, chars: 0, charsTotal: Array.from(red).length })); continue; }
474
+ const c = _capDoc(red, Math.min(perCap, remain), !!d.tail);
475
+ spent += _blen(c.text); // ์˜ˆ์‚ฐ๋„ ๋ฐ”์ดํŠธ๋กœ ์„ผ๋‹ค(์ƒํ•œ๊ณผ ๊ฐ™์€ ๋‹จ์œ„)
476
+ // ํ™”๋ฉด ๊ณ ์ง€์˜ "์ž" ๋Š” **์ฝ”๋“œํฌ์ธํŠธ**์—ฌ์•ผ ํ•œ๋‹ค. String.length(UTF-16 ์ฝ”๋“œ ๋‹จ์œ„)๋ฅผ ์“ฐ๋ฉด ์ด๋ชจ์ง€ ๋ฌธ์„œ์—์„œ
477
+ // 32,767/60,001์ž ๋กœ ๋‚˜์˜ค์ง€๋งŒ ์‹ค์ œ๋กœ๋Š” 16,384/30,001์ž๋‹ค(๊ฒ€์ˆ˜ ์‹ค์ธก). ์„ธ์–ด์„œ ํ•จ๊ป˜ ์‹ฃ๋Š”๋‹ค.
478
+ files.push(Object.assign({}, base, {
479
+ text: c.text, totalChars: red.length, totalBytes: _blen(red), bytes: _blen(c.text), truncated: c.truncated,
480
+ chars: Array.from(c.text).length, charsTotal: Array.from(red).length,
481
+ }));
482
+ }
483
+ return { at: nowIso, perCap, totalCap, spent, files };
484
+ }
241
485
 
242
486
  // ํ•˜๋„ค์Šค ํ‘œ๋ฉด โ†’ {project, version, counts, nodes, edges}. deps ๋กœ in-process ๋กœ๋” ์ฃผ์ž…(์…ธ๋ง X).
243
487
  function buildGraphData(root, deps = {}) {
@@ -315,7 +559,45 @@ function buildGraphData(root, deps = {}) {
315
559
  // 1.36.53 (UR-0062): ๊ธฐ์ˆ  ํ”„๋กœํ•„ ์ž„๋ฒ ๋“œ โ€” ๐Ÿ›  ๊ธฐ์ˆ  ํƒญ ๋ฐ์ดํ„ฐ
316
560
  const tech = (typeof deps._loadTechProfile === 'function' ? (deps._loadTechProfile(root) || null) : null) || { current: { languages: [], services: [] }, history: [] };
317
561
  const toggleRegistry = deps._toggleRegistry || {};
318
- return { project: rd.project || path.basename(root), version: rd.version || '', root, counts, nodes, edges, roadmap, toggles, toggleRegistry, tech };
562
+ // ํ•„๋“œ๋งˆ๋‹ค ์†์œผ๋กœ ๊ฐ์‹ธ๋ฉด ๋ฐ˜๋“œ์‹œ ๋น ๋œจ๋ฆฐ๋‹ค(๋ฌธ์„œ ํƒญ๋งŒ ๊ฐ€๋“œํ–ˆ๋‹ค๊ฐ€ nodesยทroadmap ์œผ๋กœ ์ƒœ๋‹ค).
563
+ // **์ž„๋ฒ ๋“œ ์ง์ „ ํ•œ ๊ณณ**์—์„œ ๋ชจ๋“  ๋ฌธ์ž์—ด์„ ํ†ต๊ณผ์‹œํ‚จ๋‹ค โ€” ์ง€๊ธˆ ์žˆ๋Š” ํ‘œ๋ฉด๋„, ๋‚˜์ค‘์— ์ถ”๊ฐ€๋  ํ‘œ๋ฉด๋„ ํ•จ๊ป˜ ๋ฎ์ธ๋‹ค.
564
+ // idยทtype ๊ฐ™์€ ์ž์ฒด ์ƒ์„ฑ ๊ฐ’์€ ํ‘œ์ง€์— ๊ฑธ๋ฆฌ์ง€ ์•Š์œผ๋ฏ€๋กœ ๋ฌดํ•ดํ•˜๋‹ค.
565
+ // **๊นŠ์ด ์ œํ•œ์„ ๋‘๋ฉด ๊ทธ ๋„ˆ๋จธ๊ฐ€ ํ†ต์งธ๋กœ ์šฐํšŒํ•œ๋‹ค.** ์ˆœํ™˜์„ ๋ง‰์œผ๋ ค 12๋‹จ๊ณ„ ์ƒํ•œ์„ ๋’€๋”๋‹ˆ, 12๋‹จ๊ณ„๋ณด๋‹ค ๊นŠ์€
566
+ // ๊ฐ์ฒด๋Š” ๊ทธ๋Œ€๋กœ ๋ฐ˜ํ™˜๋ผ ๊ทธ ์•„๋ž˜ ๋ฌธ์ž์—ด์— ์ •๊ทœํ™”๊ฐ€ ์•„์˜ˆ ๊ฑธ๋ฆฌ์ง€ ์•Š์•˜๋‹ค(๊ฒ€์ˆ˜ ์‹ค์ธก: tech-profile.json ์€
567
+ // ์ž„์˜ JSON ์ด๋ผ ์‹ค CLI ๋กœ ๋„๋‹ฌ ๊ฐ€๋Šฅ). ์ƒํ•œ ๋Œ€์‹  **๋ฐฉ๋ฌธ ์ง‘ํ•ฉ**์œผ๋กœ ์ˆœํ™˜๋งŒ ๋Š๋Š”๋‹ค โ€” ๊นŠ์ด๋Š” ์ œํ•œํ•˜์ง€ ์•Š๋Š”๋‹ค.
568
+ // ๋ฐฉ๋ฌธ ์ง‘ํ•ฉ์„ **์žฌ๊ท€ ์Šคํƒ์ฒ˜๋Ÿผ** ์จ์•ผ ํ•œ๋‹ค. ์ „์—ญ ์ง‘ํ•ฉ์œผ๋กœ ๋‘๋ฉด ๊ฐ™์€ ๊ฐ์ฒด๋ฅผ ๋‘ ํ•„๋“œ๊ฐ€ ๊ณต์œ ํ•  ๋•Œ
569
+ // ๋‘ ๋ฒˆ์งธ๊ฐ€ ์ˆœํ™˜์ด ์•„๋‹Œ๋ฐ๋„ ์ž˜๋ ค ๋‚˜๊ฐ„๋‹ค(๊ฒ€์ˆ˜ ์‹ค์ธก: `{first:X, second:X}` ์˜ second ๊ฐ€ null ์ด ๋๋‹ค).
570
+ // ๋“ค์–ด๊ฐˆ ๋•Œ ๋„ฃ๊ณ  **๋‚˜์˜ฌ ๋•Œ ๋บ€๋‹ค** โ€” ์ง„์งœ ์ˆœํ™˜(์กฐ์ƒ์— ์ž์‹ ์ด ์žˆ๋Š” ๊ฒฝ์šฐ)๋งŒ ๋Š๊ธด๋‹ค.
571
+ // ์žฌ๊ท€ ๊นŠ์ด๋„ ์ œํ•œํ•˜์ง€ ์•Š๋˜, ์Šคํƒ์ด ํ„ฐ์ง€๋ฉด ๊ทธ ๊ฐ€์ง€๋งŒ ํ‘œ์ง€ ๋ฌธ์ž์—ด๋กœ ๋Œ€์ฒดํ•œ๋‹ค(์ƒ์„ฑ ์ž์ฒด๊ฐ€ ์‹คํŒจํ•˜๋ฉด ์•ˆ ๋œ๋‹ค).
572
+ // ๊นŠ์ด ์ƒํ•œ ์ž์ฒด๊ฐ€ ๋ฌธ์ œ๊ฐ€ ์•„๋‹ˆ๋ผ **์ƒํ•œ์„ ๋„˜๊ฒผ์„ ๋•Œ ์›๋ณธ์„ ๊ทธ๋Œ€๋กœ ํ†ต๊ณผ์‹œํ‚จ ๊ฒƒ**์ด ๋ฌธ์ œ์˜€๋‹ค(๊ทธ๊ฒŒ ์šฐํšŒ์˜€๋‹ค).
573
+ // ์ƒํ•œ์„ ๋„˜์œผ๋ฉด **์ž๋ฆฌํ‘œ์‹œ์ž ๋ฌธ์ž์—ด๋กœ ๋Œ€์ฒด**ํ•œ๋‹ค โ€” ์•„๋ฌด๊ฒƒ๋„ ์ƒˆ์ง€ ์•Š๊ณ , ๋’ค์ด์€ JSON.stringify ๋„ ์•ˆ์ „ํ•˜๋‹ค
574
+ // (์ง๋ ฌํ™” ์ž์ฒด๊ฐ€ ์•ฝ 4์ฒœ ๋‹จ๊ณ„์—์„œ ์Šคํƒ์„ ํ„ฐ๋œจ๋ฆฐ๋‹ค โ€” 24KB ์งœ๋ฆฌ ์œ ํšจ JSON ํ•˜๋‚˜๋กœ ์ƒ์„ฑ์ด ์‹คํŒจํ–ˆ๋‹ค).
575
+ const MAXD = 200; // ์ •์ƒ ๋ฐ์ดํ„ฐ๋Š” ์ด ๊ทผ์ฒ˜์—๋„ ์˜ค์ง€ ์•Š๋Š”๋‹ค
576
+ const _deepSafe = (v, path, depth) => {
577
+ if (typeof v === 'string') return _safe(v);
578
+ if (!v || typeof v !== 'object') return v;
579
+ const dep = depth || 0;
580
+ if (dep > MAXD) return _GUARD_TXT; // fail-safe: ์›๋ณธ์ด ์•„๋‹ˆ๋ผ ํ‘œ์‹œ ๋ฌธ์ž์—ด๋กœ ๋Œ€์ฒด
581
+ const p = path || new Set();
582
+ if (p.has(v)) return null; // ์กฐ์ƒ์— ์ž์‹ ์ด ์žˆ๋‹ค = ์ˆœํ™˜ โ†’ ์ž˜๋ผ๋‚ธ๋‹ค
583
+ p.add(v);
584
+ try {
585
+ if (Array.isArray(v)) return v.map(x => _deepSafe(x, p, dep + 1));
586
+ const o = {};
587
+ for (const k of Object.keys(v)) o[k] = _deepSafe(v[k], p, dep + 1);
588
+ return o;
589
+ } finally {
590
+ p.delete(v); // ํ˜•์ œ ๋…ธ๋“œ๊ฐ€ ๊ณต์œ  ์ฐธ์กฐ๋ฅผ ๋‹ค์‹œ ์“ธ ์ˆ˜ ์žˆ๊ฒŒ ๋˜๋Œ๋ฆฐ๋‹ค
591
+ }
592
+ };
593
+ // 1.36.96 (P-0010): ๐Ÿ“„ ๋ฌธ์„œ ํƒญ โ€” .harness ํ•ต์‹ฌ ๋ฌธ์„œ 7์ข… ์Šค๋ƒ…์ƒท(๋งˆ์Šคํ‚น + ์ƒํ•œ + ์ž˜๋ฆผ ๊ณ ์ง€).
594
+ const docs = buildDocsData(root, deps.nowIso || new Date().toISOString());
595
+ // `docs` ๋Š” buildDocsData ์—์„œ **์ด๋ฏธ ๊ฐ™์€ ์ˆ ์–ด๋ฅผ ํ†ต๊ณผํ–ˆ๊ณ  ํšŒ๊ณ„(spent/bytes)๋„ ๊ฑฐ๊ธฐ์„œ ํ™•์ •**๋๋‹ค.
596
+ // ์—ฌ๊ธฐ์„œ ๋‹ค์‹œ ๋ณ€ํ™˜ํ•˜๋ฉด ํ…์ŠคํŠธ๊ฐ€ ๋ฏธ์„ธํ•˜๊ฒŒ ๋ฐ”๋€Œ์–ด "๋ณด๊ณ ํ•œ spent = ์‹ค์ œ ์ž„๋ฒ ๋“œ ๋ฐ”์ดํŠธ" ๋ถˆ๋ณ€์‹์ด ๊นจ์ง„๋‹ค
597
+ // (๊ฒ€์ˆ˜ ์‹ค์ธก: 1๋ฐ”์ดํŠธ ์ฐจ์ด). ๋‘ ๋ฒˆ ์ฒ˜๋ฆฌํ•˜์ง€ ์•Š๋Š”๋‹ค.
598
+ const safe = _deepSafe({ project: rd.project || path.basename(root), version: rd.version || '', root, counts, nodes, edges, roadmap, toggles, toggleRegistry, tech });
599
+ safe.docs = docs;
600
+ return safe;
319
601
  }
320
602
 
321
603
  // `leerness graph --html [path] [--out file] [--json]` ํ•ธ๋“ค๋Ÿฌ.
@@ -338,4 +620,4 @@ function graphHtmlCmd(root, deps = {}, outFile) {
338
620
  return summary;
339
621
  }
340
622
 
341
- module.exports = { graphHtmlCmd, buildGraphData };
623
+ module.exports = { graphHtmlCmd, buildGraphData, buildDocsData, _capDoc, DOC_FILES, DOC_PER_CAP, DOC_TOTAL_CAP };
package/lib/pure-utils.js CHANGED
@@ -24,11 +24,121 @@ const _SECRET_ASSIGN = /\b([A-Za-z0-9_]*(?:TOKEN|KEY|SECRET|PASSWORD|PASSWD|PWD|
24
24
  // (ํ—ค๋” ๋ฐ–์˜ ๋งจ `Bearer <๊ฐ’>` ์€ ์•„๋ž˜ SHAPE ๊ทœ์น™์ด ํ˜•ํƒœ๋กœ ์žก๋Š”๋‹ค โ€” ์‚ฐ๋ฌธ ํ›ผ์† ์œ„ํ—˜์„ ๊ฐ์ˆ˜ํ•˜์ง€ ์•Š๋Š”๋‹ค.)
25
25
  const _SECRET_AUTH_HEADER = /\b((?:proxy-)?authorization)([ \t]*[:=][ \t]*)(?:(Bearer|Basic|Token|ApiKey|Digest)([ \t]+))?([A-Za-z0-9._~+/-]+=*)/gi;
26
26
  const _SECRET_SHAPE = /\b(sk-[A-Za-z0-9_-]{8,}|ghp_[A-Za-z0-9]{16,}|gho_[A-Za-z0-9]{16,}|ghs_[A-Za-z0-9]{16,}|github_pat_[A-Za-z0-9_]{20,}|xox[baprs]-[A-Za-z0-9-]{8,}|AKIA[0-9A-Z]{12,}|ASIA[0-9A-Z]{12,}|AIza[A-Za-z0-9_-]{20,}|eyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{5,})/g;
27
+ // 1.36.96 (๊ฒ€์ˆ˜ P1): ๋งˆ์Šคํ‚น์ด **์Šค์บ๋„ˆ์™€ ๋‹ค๋ฅธ(๋” ์•ฝํ•œ) ๋ชฉ๋ก**์„ ์“ฐ๊ณ  ์žˆ์—ˆ๋‹ค. ์‹ค์ธก์œผ๋กœ 6์ข…์ด ๊ทธ๋Œ€๋กœ ์ƒˆ์–ด
28
+ // ํ™”๋ฉด์— ์‹ค๋ ธ๋‹ค โ€” DB ์ ‘์†๋ฌธ์ž์—ด ๋น„๋ฐ€๋ฒˆํ˜ธ ยท GitLab PAT ยท npm ํ† ํฐ ยท Stripe live ํ‚ค ยท SendGrid ยท ๊ฐœ์ธํ‚ค ๋ธ”๋ก.
29
+ // ์ „๋ถ€ `SECRET_PATTERNS`(catalogs.js)์— **์ด๋ฏธ ์žˆ๋˜** ํŒจํ„ด์ด๋‹ค. ํ•œ์ชฝ๋งŒ ๊ฐ•ํ™”๋œ ์ „ํ˜•์ ์ธ ํ‘œ๋ฉด ๋ถˆ์ผ์น˜๋ผ
30
+ // ๊ฐ™์€ ๋ชฉ๋ก์„ ๊ณต์œ ํ•œ๋‹ค. placeholder/entropy ๊ฐ€๋“œ๋„ ์Šค์บ๋„ˆ์™€ ๋™์ผํ•˜๊ฒŒ ์ ์šฉํ•ด ์˜คํƒ์„ ๋Š˜๋ฆฌ์ง€ ์•Š๋Š”๋‹ค.
31
+ // (catalogs.js ๋Š” require ๊ฐ€ ์—†์–ด ์ˆœํ™˜์ด ์ƒ๊ธฐ์ง€ ์•Š๋Š”๋‹ค. ๊ทธ๋ž˜๋„ ์ง€์—ฐ ๋กœ๋“œ + ์‹คํŒจ ์‹œ ๊ธฐ์กด ๊ทœ์น™๋งŒ ์‚ฌ์šฉ.)
32
+ // ๊ฐœ์ธํ‚ค๋Š” **๋ธ”๋ก ์ „์ฒด**๋ฅผ ์ง€์šด๋‹ค. ์Šค์บ๋„ˆ๋Š” BEGIN ์ค„๋งŒ ํƒ์ง€ํ•˜๋ฉด ์ถฉ๋ถ„ํ•˜์ง€๋งŒ(์žˆ๋‹ค/์—†๋‹ค), ํ™”๋ฉด์— ์‹ฃ๋Š”
33
+ // ํ…์ŠคํŠธ๋Š” ๋ณธ๋ฌธ(base64)์ด ๋‚จ์œผ๋ฉด ํ‚ค๊ฐ€ ๊ทธ๋Œ€๋กœ ๋…ธ์ถœ๋œ๋‹ค โ€” ์‹ค์ธก์—์„œ BEGIN ์ค„๋งŒ *** ๊ฐ€ ๋˜๊ณ  ๋ณธ๋ฌธ์€ ๋‚จ์•˜๋‹ค.
34
+ // `[\s\S]*?` ๋Š” **BEGIN ๊ณผ ๋ฌด๊ด€ํ•œ END ์‚ฌ์ด์˜ ์‚ฐ๋ฌธ๊นŒ์ง€** ์‚ผํ‚จ๋‹ค โ€” ์ข…๋ฃŒ๋˜์ง€ ์•Š์€ ํ‚ค ํ•˜๋‚˜๊ฐ€ ๋ฌธ์„œ ๋’ค์ชฝ์˜
35
+ // END ์ค„๊ณผ ์ง์ง€์–ด์ ธ ๊ทธ ์‚ฌ์ด ์ „๋ถ€๊ฐ€ ์‚ฌ๋ผ์งˆ ์ˆ˜ ์žˆ๋‹ค(๋ณ€์ด ์‹คํ—˜ ์ค‘ ๋ฐœ๊ฒฌ). ์‚ฌ์ด์—๋Š” PEM ํ—ค๋”ยทbase64ยท๋นˆ ์ค„๋งŒ ํ—ˆ์šฉํ•œ๋‹ค.
36
+ // **๋ฌธํ„ฑ๊ณผ ๋ฌธ์ž ์กฐ์„ฑ์œผ๋กœ๋Š” ํ‚ค ๋ณธ๋ฌธ๊ณผ ์‚ฐ๋ฌธ์„ ๊ฐ€๋ฅผ ์ˆ˜ ์—†๋‹ค.** ๋„ค ๋ฒˆ ์‹œ๋„ํ–ˆ๊ณ  ๋งค๋ฒˆ ํ•œ์ชฝ์ด ๊นจ์กŒ๋‹ค:
37
+ // 16์ž โ†’ ์ธ์šฉ ๋ฌธ์„œ์˜ ์‹๋ณ„์ž ์‚ผํ‚ด ยท 40์ž โ†’ ์งง๊ฒŒ ๊ฐ๊ธด ํ‚ค ๋ˆ„์ถœ ยท
38
+ // "์ˆซ์ž/๊ธฐํ˜ธ ํฌํ•จ" โ†’ ์˜๋ฌธ์ž๋งŒ์ธ ์‹ค์ œ ํ‚ค ์ค„ ๋ˆ„์ถœ(๊ฒ€์ˆ˜๊ฐ€ ์ง„์งœ RSA ํ‚ค๋กœ 51์ค„ ๋ˆ„์ถœ์„ ์žฌํ˜„) ยท
39
+ // ๊ทธ ์—ญ โ†’ `PolicyVersion2026Final` ๊ฐ™์€ ์ •์ƒ ์‹๋ณ„์ž ์‚ผํ‚ด.
40
+ // ๊ทธ๋ž˜์„œ ๊ฐ’์ด ์•„๋‹ˆ๋ผ **๋ฌธ๋งฅ๊ณผ ์‹คํŒจ ๋ฐฉํ–ฅ**์œผ๋กœ ์ •ํ•œ๋‹ค. BEGIN ๋งˆ์ปค ๋’ค์˜ **๊ณต๋ฐฑ ์—†๋Š” ํ•œ ์ค„**์€ ํ‚ค ์žฌ๋ฃŒ๋กœ ๋ณธ๋‹ค.
41
+ // ์‚ฐ๋ฌธ์€ ๊ฑฐ์˜ ํ•ญ์ƒ ๊ณต๋ฐฑ์„ ํฌํ•จํ•˜๋ฏ€๋กœ ๋ฌธ๋‹จ์€ ์‚ด์•„๋‚จ๊ณ , ์–ด๋–ค ์กฐ์„ฑ์˜ ํ‚ค ๋ณธ๋ฌธ๋„ ์ƒˆ์ง€ ์•Š๋Š”๋‹ค.
42
+ // ๋Œ€๊ฐ€: BEGIN ์„ ์ธ์šฉํ•œ ๋ฌธ์„œ์—์„œ **๋ฐ”๋กœ ๋’ค์˜ ๋‹จ์ผ ํ† ํฐ ํ•œ ์ค„**์ด ๊ฐ€๋ ค์ง„๋‹ค. ์›๋ณธ ํŒŒ์ผ์€ ๊ทธ๋Œ€๋กœ์ด๊ณ 
43
+ // ์ด๊ฑด ํ‘œ์‹œ์šฉ ์Šค๋ƒ…์ƒท์ด๋ฏ€๋กœ, ๋ˆ„์ถœ๋ณด๋‹ค ์ด ์ชฝ์ด ๋‚ซ๋‹ค โ€” ํ‘œ์‹œ์šฉ ๋งˆ์Šคํ‚น์€ ์•ˆ์ „ํ•œ ์ชฝ์œผ๋กœ ์‹คํŒจํ•ด์•ผ ํ•œ๋‹ค.
44
+ // PGP ๋ธ”๋ก์˜ `=CRC` ์ค„๋„ ์ด ๊ทœ์น™์— ์ž์—ฐํžˆ ํฌํ•จ๋œ๋‹ค.
45
+ const _PEM_HDR = '(?:Proc-Type|DEK-Info|Comment|Subject|Originator|Version|MessageID|Charset|X-[A-Za-z0-9-]+):[^\\r\\n]*';
46
+ // ์ค„ **๋๊นŒ์ง€** ๊ณต๋ฐฑ์ด ์—†์–ด์•ผ ํ•œ๋‹ค โ€” ์•ต์ปค๊ฐ€ ์—†์œผ๋ฉด ์‚ฐ๋ฌธ ์ค„์˜ ์ฒซ ๋‚ฑ๋ง๋งŒ ๋จน๊ณ  ๋‚˜๋จธ์ง€๋ฅผ ๋‚จ๊ธด๋‹ค(์‹ค์ธก).
47
+ const _PEM_BODY = '(?!-----)\\S+(?=[ \\t]*(?:\\r?\\n|$))';
48
+ // 1.36.96: **๊ฐ’ ๋‹จ์œ„ ๋งˆ์Šคํ‚น์ด ์™„๋ฒฝํ•ด์•ผ ํ•˜๋Š” ๊ตฌ์กฐ๋ฅผ ๋ฒ„๋ฆฐ๋‹ค.**
49
+ // ๋ฌธ์„œ ๋ทฐ์–ด์—์„œ ๋‹ค์„ฏ ๋ผ์šด๋“œ ๋™์•ˆ ๋ˆ„์ถœ ๊ฒฝ๋กœ๊ฐ€ ๊ณ„์† ๋‚˜์™”๋‹ค โ€” ์ธ์šฉ๋ฌธ(`> `)์œผ๋กœ ๊ฐ์‹ผ PEM, base64 ์˜ `/`ยท`+`,
50
+ // ์†Œ๋ฌธ์ž `bearer`, placeholder ๋ชจ์–‘ ํ† ํฐ, ์ค„ ์•ž ์ ‘๋‘์–ดโ€ฆ ๊ฐ’์˜ ํ˜•ํƒœ๋กœ ๊ฐ€๋ฅด๋ ค๋Š” ์‹œ๋„๋งˆ๋‹ค ๋ฐ˜๋ก€๊ฐ€ ์žˆ์—ˆ๋‹ค.
51
+ // ๊ทธ๋ž˜์„œ **์ž๊ฒฉ์ฆ๋ช… ํ‘œ์ง€๊ฐ€ ํ•˜๋‚˜๋ผ๋„ ์žˆ์œผ๋ฉด ๊ทธ ๋ฌธ์„œ์˜ ๋ณธ๋ฌธ์„ ์•„์˜ˆ ์‹ฃ์ง€ ์•Š๋Š”๋‹ค.** ๋งˆ์Šคํ‚น์€ ๋ฐฉ์–ด์˜ ๋‘ ๋ฒˆ์งธ ์ธต์œผ๋กœ๋งŒ ๋‚จ๊ณ ,
52
+ // "์™„๋ฒฝํ•œ ๋งˆ์Šคํ‚น"์ด ์•ˆ์ „์˜ ์ „์ œ์—์„œ ๋น ์ง„๋‹ค.
53
+ // ๋น„์šฉ ์‹ค์ธก(์‹ค์ œ ๋ฌธ์„œ 156๊ฐœ): ํ‘œ์ง€๊ฐ€ ๊ฑธ๋ฆฐ ํŒŒ์ผ **1๊ฐœ(0.6%)** โ€” ์‚ฌ์‹ค์ƒ ๊ณต์งœ๋‹ค.
54
+ // ํ‘œ์ง€๋Š” **๊ฐ’์ด ์•„๋‹ˆ๋ผ ํ˜•์‹**์œผ๋กœ๋งŒ ์ •์˜ํ•œ๋‹ค(์ค„ ์•ž ์ ‘๋‘์–ดยท๋Œ€์†Œ๋ฌธ์žยท๋ณธ๋ฌธ ์กฐ์„ฑ์— ์˜์กดํ•˜์ง€ ์•Š๋Š”๋‹ค).
55
+ // ๊ฒ€์ˆ˜๊ฐ€ ํ‘œ์ง€๋ฅผ ๋น ์ ธ๋‚˜๊ฐ€๋Š” ๋„ค ํ˜•ํƒœ๋ฅผ ์‹คํ–‰์œผ๋กœ ๋ณด์˜€๋‹ค โ€” RFC6750 ์˜ ์งง์€(19์ž) Bearer, ์„ธ์…˜ ์ฟ ํ‚ค,
56
+ // compact JWE(5๋ถ„์ ˆ์ด๋ผ `eyJโ€ฆeyJ` ํ˜•ํƒœ๊ฐ€ ์•„๋‹ˆ๋‹ค), ๋งˆํฌ๋‹ค์šด ๊ฐ•์กฐ๋กœ ๊ฐ์‹ผ `` `Bearer` `` .
57
+ // ๋„“ํžˆ๊ธฐ ์ „์— ๋น„์šฉ์„ ์Ÿ€๋‹ค: ์‹ค์ œ ๋ฌธ์„œ 156๊ฐœ์—์„œ **์ถ”๊ฐ€๋กœ ๊ฑธ๋ฆฌ๋Š” ํŒŒ์ผ 0๊ฐœ**. ๊ทธ๋ž˜์„œ ์…‹ ๋‹ค ๋„“ํžŒ๋‹ค.
58
+ const _CRED_MARKERS = [
59
+ /-----BEGIN (?:[A-Z0-9]+ )*PRIVATE KEY(?: BLOCK)?-----/,
60
+ // JWT(3๋ถ„์ ˆ)์™€ JWE(5๋ถ„์ ˆ)๋ฅผ ํ•จ๊ป˜ โ€” ๋‘ ๋ฒˆ์งธ ๋ถ„์ ˆ์ด ๋น„์–ด ์žˆ์„ ์ˆ˜ ์žˆ๋‹ค(dir ์•Œ๊ณ ๋ฆฌ์ฆ˜)
61
+ /\beyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]*\.[A-Za-z0-9_-]{2,}/,
62
+ // ์Šคํ‚ด๊ณผ ๊ฐ’ ์‚ฌ์ด์— ๋งˆํฌ๋‹ค์šด ๊ฐ•์กฐ(`**` ยท `` ` ``)๊ฐ€ ๋‚„ ์ˆ˜ ์žˆ๋‹ค. RFC6750 ํ† ํฐ์€ ์งง์„ ์ˆ˜ ์žˆ์–ด 16์ž๋กœ ์žก๋Š”๋‹ค.
63
+ /\bbearer\b[^A-Za-z0-9\r\n]{0,4}[A-Za-z0-9_\-.=+/~]{16,}/i,
64
+ /\b(?:proxy-)?authorization[ \t]*[:=]/i,
65
+ /\b(?:set-)?cookie[ \t]*[:=]/i,
66
+ /\b(?:gh[pousr]_|github_pat_|sk-|sk_live_|rk_live_|xox[baprs]-|xapp-|AKIA|ASIA|AIza|ya29\.|glpat-|npm_|SG\.)/,
67
+ /\b(?:postgres|postgresql|mysql|mongodb(?:\+srv)?|redis|amqp):\/\/[^:\s/@]+:[^@\s/]+@/i,
68
+ ];
69
+ function hasCredentialMarker(s) {
70
+ const t = String(s == null ? '' : s);
71
+ return _CRED_MARKERS.some(re => re.test(t));
72
+ }
73
+
74
+ // ์นดํƒˆ๋กœ๊ทธ์˜ Bearer ๋ฌธ์ž๊ตฐ์€ `[A-Za-z0-9_\-.=]` ๋ผ base64 ์˜ `/` `+` ์—์„œ ๋Š๊ธด๋‹ค โ€” ์•ž ์กฐ๊ฐ์ด 20์ž ๋ฏธ๋งŒ์ด๋ฉด
75
+ // `Bearer <์‹คํ† ํฐ>` ์ด ํ†ต์งธ๋กœ ๋‚จ๋Š”๋‹ค(์‹ค์ธก: ์•ž 8์ž ๋’ค `/` ์ธ 40์ž ํ† ํฐ์ด ์ „ํ˜€ ๊ฐ€๋ ค์ง€์ง€ ์•Š์•˜๋‹ค).
76
+ // `Authorization:` ํ—ค๋” ํ˜•ํƒœ๋Š” _SECRET_AUTH_HEADER ๊ฐ€ ์žก์ง€๋งŒ ๋งจ `Bearer โ€ฆ` ๋Š” ์ด ๊ทœ์น™์ด ํ•„์š”ํ•˜๋‹ค.
77
+ // ์Šค์บ๋„ˆ ์ชฝ ๋ฌธ์ž๊ตฐ๋„ ๊ฐ™์€ ๊ตฌ๋ฉ์ด ์žˆ๋‹ค(T-0085 ๋กœ ๋‚จ๊ฒผ๋‹ค) โ€” ์—ฌ๊ธฐ์„œ๋Š” ํ‘œ์‹œ ๋ˆ„์ถœ๋ถ€ํ„ฐ ๋ง‰๋Š”๋‹ค.
78
+ const _SECRET_BEARER = /\bBearer\s+[A-Za-z0-9_\-.=+/]{20,}/g;
79
+ const _SECRET_PEM = new RegExp('-----BEGIN (?:[A-Z0-9]+ )*PRIVATE KEY(?: BLOCK)?-----(?:[ \\t]*\\r?\\n[ \\t]*(?:' + _PEM_HDR + '|' + _PEM_BODY + ')?)*[ \\t]*\\r?\\n[ \\t]*-----END (?:[A-Z0-9]+ )*PRIVATE KEY(?: BLOCK)?-----', 'g');
80
+ // END ์ค„์ด ์—†์œผ๋ฉด ์œ„ ๊ทœ์น™์ด ๋งค์น˜๋˜์ง€ ์•Š์•„ BEGIN ์ค„๋งŒ ์ง€์›Œ์ง€๊ณ  **๋ณธ๋ฌธ base64 ๊ฐ€ ๊ทธ๋Œ€๋กœ ๋‚จ์•˜๋‹ค**(๊ฒ€์ˆ˜ ์‹ค์ธก).
81
+ // ๋ถ™์—ฌ๋„ฃ๋‹ค ์ž˜๋ฆฐ ํ‚ค๊ฐ€ ํ”ํ•˜๋ฏ€๋กœ, BEGIN ๋’ค์— ์ด์–ด์ง€๋Š” base64/ํ—ค๋” ์ค„๊นŒ์ง€ ํ•จ๊ป˜ ์ง€์šด๋‹ค.
82
+ // โ‘  ์•”ํ˜ธํ™” ํ‚ค๋Š” `Proc-Type:`/`DEK-Info:` ํ—ค๋” ๋’ค **๋นˆ ์ค„**์ด ์˜ค๊ณ  ๋ณธ๋ฌธ์ด ์‹œ์ž‘ํ•œ๋‹ค โ€” ๋นˆ ์ค„์—์„œ ๋ฉˆ์ถ”๋ฉด ๋ณธ๋ฌธ์ด ์ƒŒ๋‹ค.
83
+ // โ‘ก ๋ฐ˜๋Œ€๋กœ ์•„๋ฌด `Name: value` ์ค„์ด๋‚˜ ๋จน์œผ๋ฉด BEGIN ์„ ์ธ์šฉํ•œ ์ •์ฑ… ๋ฌธ์„œ์˜ ์‚ฐ๋ฌธ์„ ์‚ผํ‚จ๋‹ค(๋‘˜ ๋‹ค ๊ฒ€์ˆ˜ ์‹ค์ธก).
84
+ // ๊ทธ๋ž˜์„œ ํ—ค๋”๋Š” **PEM ํ‘œ์ค€ ์ด๋ฆ„๋งŒ** ํ—ˆ์šฉํ•˜๊ณ , ๋ณธ๋ฌธ base64 ์ค„์ด **์ตœ์†Œ ํ•˜๋‚˜** ์žˆ์„ ๋•Œ๋งŒ ํ™•์žฅํ•œ๋‹ค.
85
+ const _SECRET_PEM_OPEN = new RegExp('-----BEGIN (?:[A-Z0-9]+ )*PRIVATE KEY(?: BLOCK)?-----(?:[ \\t]*\\r?\\n[ \\t]*' + _PEM_HDR + ')*(?:[ \\t]*\\r?\\n[ \\t]*)*(?:[ \\t]*\\r?\\n[ \\t]*(?:' + _PEM_BODY + '))+[ \\t]*\\r?\\n?', 'g');
86
+
87
+ // **ํƒ์ง€์šฉ ๋ฌธ๋งฅ ํœด๋ฆฌ์Šคํ‹ฑ์€ ๊ฐ€๋ฆผ์— ๊ทธ๋Œ€๋กœ ์“ฐ๋ฉด ์•ˆ ๋œ๋‹ค.** ํƒ์ง€์˜ ์˜คํƒ์€ ๊ฒฝ๊ณ  ํ•œ ์ค„์ด์ง€๋งŒ, ๊ฐ€๋ฆผ์˜ ์˜คํƒ์€
88
+ // ์‚ฌ์šฉ์ž๊ฐ€ ๋ณด๋Š” ๋ฌธ์„œ๋ฅผ ์กฐ์šฉํžˆ ํŒŒ๊ดดํ•œ๋‹ค(๋Œ€์‹œ๋ณด๋“œ๋„ ์ด ํ•จ์ˆ˜๋ฅผ ์“ด๋‹ค). ๊ฒ€์ˆ˜ ์‹ค์ธก์œผ๋กœ ํ™•์ธํ•œ ๋‘ ๊ฐ€์ง€:
89
+ // 'AWS secret commit <40์ž ์ปค๋ฐ‹ํ•ด์‹œ> was reverted' โ†’ ๋ฌธ์žฅ ์ „์ฒด๊ฐ€ *** ๋กœ ์‚ฌ๋ผ์กŒ๋‹ค.
90
+ // 'Use Bearer authenticationMiddleware for API requests.' โ†’ 'Use *** for API requests.'
91
+ // ์—”ํŠธ๋กœํ”ผ ๊ฒŒ์ดํŠธ(_looksSecretLike)๋กœ๋Š” ๊ฐˆ๋ฆฌ์ง€ ์•Š์•˜๋‹ค โ€” ์ง„์งœ AWS ํ‚ค๊นŒ์ง€ placeholder ๋กœ ํŒ์ •๋œ๋‹ค(์‹ค์ธก).
92
+ // ๊ทธ๋ž˜์„œ ๋ฌธ๋งฅ ํŒจํ„ด์€ ๊ฐ€๋ฆผ์—์„œ ์ œ์™ธํ•˜๊ณ , Bearer ๋Š” ๊ฐ’์— ์ˆซ์ž๊ฐ€ ์žˆ์„ ๋•Œ๋งŒ ๊ฐ€๋ฆฐ๋‹ค.
93
+ // ๋‚จ๋Š” ๊ตฌ๋ฉ: `Authorization:` ์—†์ด ์ ํžŒ **์˜๋ฌธ์ž๋งŒ์œผ๋กœ ๋œ** Bearer ํ† ํฐ. ํ—ค๋” ํ˜•ํƒœ๋Š” _SECRET_AUTH_HEADER ๊ฐ€,
94
+ // ์‹ค์ œ ํ† ํฐ ํ˜•์‹(JWTยทghp_ยทAKIAยทsk- ๋“ฑ)์€ ํ˜•ํƒœ ํŒจํ„ด์ด ์žก๋Š”๋‹ค.
95
+ // ์ดˆํŒ์€ AWS ๋ฌธ๋งฅ ํŒจํ„ด์„ ํ†ต์งธ๋กœ ์ œ์™ธํ–ˆ๋Š”๋ฐ, ๊ทธ๋Ÿฌ๋ฉด `aws secret access key <40์ž>` ์ฒ˜๋Ÿผ **๊ตฌ๋ถ„์ž ์—†๋Š” ์‹ค๊ฐ’์ด
96
+ // ์ƒŒ๋‹ค**(๊ฒ€์ˆ˜ ์‹ค์ธก). ๋ฌธ์žฅ์„ ์‚ผํ‚ค์ง€ ์•Š์œผ๋ฉด์„œ ๊ฐ’๋งŒ ์ง€์šฐ๋„๋ก, ์ด ํŒจํ„ด์€ **๊ผฌ๋ฆฌ ์ž๊ฒฉ์ฆ๋ช…๋งŒ** ์น˜ํ™˜ํ•œ๋‹ค.
97
+ // commit ํ•ด์‹œ๊ฐ€ ๊ฐ™์€ ํ˜•ํƒœ๋ผ ๊ฐ€๋ ค์ง€๋Š” ๊ฒƒ์€ ๊ฐ์ˆ˜ํ•œ๋‹ค โ€” ๋ฌธ์žฅ ๊ตฌ์กฐ๋Š” ๋‚จ์œผ๋ฏ€๋กœ ํŒŒ๊ดด๊ฐ€ ์•„๋‹ˆ๋‹ค.
98
+ const _REDACT_SKIP = new Set();
99
+ const _REDACT_TAIL = {
100
+ 'AWS Secret Access Key (context)': /(["']?)([A-Za-z0-9/+]{40})(["']?)$/,
101
+ };
102
+ // Bearer ๊ฐ’๋„ ๊ฐ™์€ ๊ฒฐ๋ก ์ด๋‹ค. ๊ธธ์ด ๋ฌธํ„ฑ(32์ž)์€ 20~31์ž ์‹คํ† ํฐ์„ ๋†“์ณค๊ณ , ๋ชจ์–‘ ํŒ๋ณ„(camelCase ์˜ˆ์™ธ)์€
103
+ // `Bearer abcdefghijklmnopqrst` ยท `Bearer CorrectHorseBatteryStaple` ๊ฐ™์€ **์œ ํšจํ•œ ๋ถˆํˆฌ๋ช… ํ† ํฐ์„ ์‚ฐ๋ฌธ์œผ๋กœ
104
+ // ํŒ์ •ํ•ด ๋…ธ์ถœ**ํ–ˆ๋‹ค(๊ฒ€์ˆ˜ ์‹ค์ธก). ๋ถˆํˆฌ๋ช… ํ† ํฐ์ด ์†Œ๋ฌธ์ž๋‚˜ ๋‚ฑ๋ง๊ผด์ด ์•„๋‹ˆ๋ผ๋Š” ๋ณด์žฅ์ด ์—†๋‹ค โ€” ์˜ˆ์™ธ๋ฅผ ์—†์•ค๋‹ค.
105
+ // ๋Œ€๊ฐ€: `Use Bearer authenticationMiddleware for API requests.` ๊ฐ™์€ ๋ฌธ์žฅ์ด `Use *** for โ€ฆ` ๊ฐ€ ๋œ๋‹ค.
106
+ // ์นดํƒˆ๋กœ๊ทธ๊ฐ€ ์ด๋ฏธ 20์ž ์ด์ƒ๋งŒ ํ›„๋ณด๋กœ ์žก์œผ๋ฏ€๋กœ `Bearer ์ธ์ฆ` ๊ฐ™์€ ์งง์€ ํ‘œํ˜„์€ ์˜ํ–ฅ์ด ์—†๋‹ค.
107
+ const _REDACT_GUARD = {};
108
+ let _SECRET_CATALOG = null;
109
+ function _secretCatalog() {
110
+ if (_SECRET_CATALOG) return _SECRET_CATALOG;
111
+ try { _SECRET_CATALOG = require('./catalogs').SECRET_PATTERNS || []; } catch { _SECRET_CATALOG = []; }
112
+ return _SECRET_CATALOG;
113
+ }
27
114
  function redactSecrets(s, max) {
28
115
  let out = String(s == null ? '' : s)
29
116
  .replace(_SECRET_AUTH_HEADER, (m, key, sep, scheme, gap) => `${key}${sep}${scheme ? scheme + (gap || ' ') : ''}***`)
30
117
  .replace(_SECRET_ASSIGN, (m, k) => `${k}=***`)
31
- .replace(_SECRET_SHAPE, '***');
118
+ .replace(_SECRET_SHAPE, '***')
119
+ .replace(_SECRET_PEM, '***')
120
+ .replace(_SECRET_PEM_OPEN, '***\n')
121
+ .replace(_SECRET_BEARER, '***');
122
+ for (const p of _secretCatalog()) {
123
+ const { re, valueGroup, requireSecretLike } = p;
124
+ if (!re || _REDACT_SKIP.has(p.name)) continue;
125
+ const guard = _REDACT_GUARD[p.name];
126
+ try {
127
+ out = out.replace(re, (...args) => {
128
+ const m0 = args[0];
129
+ const val = (valueGroup != null) ? args[valueGroup] : m0;
130
+ if (!val) return m0;
131
+ if (guard && !guard(m0)) return m0; // ๊ฐ€๋ฆผ ์ „์šฉ ์ถ”๊ฐ€ ๊ฐ€๋“œ
132
+ const tailRe = _REDACT_TAIL[p.name]; // ๊ผฌ๋ฆฌ ๊ฐ’๋งŒ ์ง€์šฐ๋Š” ํŒจํ„ด
133
+ if (tailRe) { const t = m0.match(tailRe); return t ? m0.slice(0, m0.length - t[0].length) + t[1] + '***' + t[3] : m0; }
134
+ if (_isPlaceholderSecret(val)) return m0; // .env.example ์˜ ๋”๋ฏธ๊ฐ’
135
+ if (valueGroup != null && requireSecretLike && !_looksSecretLike(val)) return m0;
136
+ if (valueGroup == null) return '***';
137
+ const i = m0.indexOf(val); // ๊ฐ’๋งŒ ์ง€์šฐ๊ณ  ๋งฅ๋ฝ์€ ๋‚จ๊ธด๋‹ค
138
+ return i < 0 ? '***' : m0.slice(0, i) + '***' + m0.slice(i + val.length);
139
+ });
140
+ } catch { /* ํ•œ ํŒจํ„ด์ด ์‹คํŒจํ•ด๋„ ๋‚˜๋จธ์ง€ ๋งˆ์Šคํ‚น์€ ๊ณ„์†ํ•œ๋‹ค */ }
141
+ }
32
142
  if (max && out.length > max) out = out.slice(0, max) + 'โ€ฆ';
33
143
  return out;
34
144
  }
@@ -1321,7 +1431,7 @@ function _replaceMdSection(content, heading, newBodyLines, opts = {}) {
1321
1431
  module.exports = { _decisionBlocksWithOffset, _blocksWithOffset, _lineOfOffset,
1322
1432
  _parseImplExports, _briefUnfilled, _planGoalUnfilled, _mdSectionBody, _draftAnchors, _replaceMdSection,
1323
1433
  _matchTool, _parsePackageJsonDeps, _parseRequirementsTxt, _buildGlossary, _renderGlossaryMd, GLOSSARY_START, GLOSSARY_END,
1324
- _isSecretKey, redactSecrets, compareVer, parseHarnessVersion,
1434
+ _isSecretKey, redactSecrets, hasCredentialMarker, compareVer, parseHarnessVersion,
1325
1435
  _isPlaceholderSecret, _looksSecretLike,
1326
1436
  _mergeLines, _mergeEnvLines, _mergeReadmeSection, _managedMerge, _parseSkillsValue,
1327
1437
  _parseArchiveBlocks, _parseSkillCatalog, _renderTeamsMd, _composeTeamPlan, _teamHandoffReminders, _cadenceAssessment, _teamDeployGate, _renderWorkspaceReferenceGuide, _memorySurface, _renderPulseLine,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "leerness",
3
- "version": "1.36.95",
3
+ "version": "1.36.97",
4
4
  "description": "The AI-coding operations layer that makes \"done\" require evidence โ€” persistent memory, evidence-gated completion checks, and clean handoffs for any AI agent (Claude Code, Codex, Cursor). State lives as plain files in your repo. CLI + MCP, 0 runtime dependencies.",
5
5
  "keywords": [
6
6
  "leerness",