elanous 0.2.19 → 0.2.21
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/apps/pwa/out/404/index.html +1 -1
- package/apps/pwa/out/404.html +1 -1
- package/apps/pwa/out/_next/static/MObU8ujkMciAKr6ljAV05/_buildManifest.js +1 -0
- package/apps/pwa/out/_next/static/chunks/088d8e8a.8069543d082bec12.js +160 -0
- package/apps/pwa/out/_next/static/chunks/1109.78a7a3288c0259d9.js +148 -0
- package/apps/pwa/out/_next/static/chunks/115.1c6690b8604f81c4.js +32 -0
- package/apps/pwa/out/_next/static/chunks/119fc678.6f591cdb84ca5791.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1262.76a3f08e67c5f816.js +309 -0
- package/apps/pwa/out/_next/static/chunks/{4323.c5dfb6f503304775.js → 1268.0840ff91e083fcbd.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/1307-c97eba9fb72a9a5e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1358d292.5c87f12cad6eaa91.js +122 -0
- package/apps/pwa/out/_next/static/chunks/1438.27a1a7b8f2308004.js +149 -0
- package/apps/pwa/out/_next/static/chunks/1442.02d85cd88f4c1eac.js +79 -0
- package/apps/pwa/out/_next/static/chunks/1501-1aad662d82ba43f7.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1584.9e2dd53da6e6d4b1.js +24 -0
- package/apps/pwa/out/_next/static/chunks/1586.b989b0614afb77e6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1609.2ad355062870151f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1612-f6df0fc0eed7157b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1826-5d279da64bbbc2de.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1923.37f1d5fe7ebe3860.js +27 -0
- package/apps/pwa/out/_next/static/chunks/1948.5f35b39eef222a94.js +5 -0
- package/apps/pwa/out/_next/static/chunks/195-fb1d7690bf30bab4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1986-33e13d648a9df765.js +1 -0
- package/apps/pwa/out/_next/static/chunks/1993.3fd074a2a246234e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{2977-e38442e86289f1d3.js → 211-9c9090c59e2c5907.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/2140.d2daf9ee96e295d1.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2196-4700653d12a44b40.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2212.0359851457473efc.js +1 -0
- package/apps/pwa/out/_next/static/chunks/224.583e562365a52f74.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2328.0f37465646bafa7d.js +36 -0
- package/apps/pwa/out/_next/static/chunks/2372.bf71372184991937.js +286 -0
- package/apps/pwa/out/_next/static/chunks/2489-ff55a35b202c0f94.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2537-6c289376caa88ca1.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2549-a8515c86f9e8cb96.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2655.72cc6a97276c5acc.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2699-325cc923ba7b8631.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2743-ef6e8c3ced74cce5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2840-5458ad50c49cac6a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/2856.f75d5baef7f49b74.js +1 -0
- package/apps/pwa/out/_next/static/chunks/311.e685738f92e6e2ec.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3194-807b212fb8eae065.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3324-d1d2979821dfa845.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3344.0de4b0ec5a454b7a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/335.a13f339e97893437.js +94 -0
- package/apps/pwa/out/_next/static/chunks/3394.646348f34b2500be.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3443.6276909b53d72d53.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3495.abd4254d808bf2dc.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3522-1de1319d028ac89c.js +25 -0
- package/apps/pwa/out/_next/static/chunks/3558-bbb5ab553a0471d3.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3721.ba5b82db0d9a5572.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3857-995ea9f5ef33d71e.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3916-3d02dd1fc257dd3c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/3919.9ff7dc25afefee33.js +74 -0
- package/apps/pwa/out/_next/static/chunks/3998.bd9a69d9b50e9bd2.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4003.ab6db1dc266576c9.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4144-fad095b48a1862ee.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4216.e8ac94b9fe6b96ab.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4327.c44dc2b55cb43ee5.js +34 -0
- package/apps/pwa/out/_next/static/chunks/4401.94257d497b6d26bc.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4559.17ca2cdbaa8d414f.js +115 -0
- package/apps/pwa/out/_next/static/chunks/4565-3d7e3fdeb43686a8.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4588.ad3364cb7fb97ada.js +86 -0
- package/apps/pwa/out/_next/static/chunks/4706.97e734bd4c9816ca.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4795-f372490b178583fc.js +1 -0
- package/apps/pwa/out/_next/static/chunks/4982.6e44352f751b9a1b.js +77 -0
- package/apps/pwa/out/_next/static/chunks/5342.49c5c5a530e62b50.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5360-08a80abdf3a4da99.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5431.66c58ab1327ee0b7.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5717.bcfcb96e701b82e0.js +90 -0
- package/apps/pwa/out/_next/static/chunks/5755-30d102586789bf7f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5957.3f1a4012388dd962.js +1 -0
- package/apps/pwa/out/_next/static/chunks/5977-59fedea50d13c762.js +1 -0
- package/apps/pwa/out/_next/static/chunks/602.9bf02ac8e9502598.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6020.3a1b347e2d2dfacc.js +8 -0
- package/apps/pwa/out/_next/static/chunks/{4847.56f5bc454fbcc827.js → 6110.1297d4093fdfbb0d.js} +2 -2
- package/apps/pwa/out/_next/static/chunks/6158.566c890adc06a459.js +2 -0
- package/apps/pwa/out/_next/static/chunks/6375.fce1d174ba6d0324.js +64 -0
- package/apps/pwa/out/_next/static/chunks/6546.92a828aef441271f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/658.957fdb0a8c6c6c8e.js +287 -0
- package/apps/pwa/out/_next/static/chunks/6706.323590aabb8b1293.js +226 -0
- package/apps/pwa/out/_next/static/chunks/6747.b67947f293a4c02d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6767.8e6c5f6e744fcd9f.js +38 -0
- package/apps/pwa/out/_next/static/chunks/6868.000b24a3045c5574.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6877-74399f69c7da1433.js +1 -0
- package/apps/pwa/out/_next/static/chunks/6946.bba74e9c96050f30.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7033.a558f1b2f6912a53.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7065.46029c26c53eac50.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7087.6fae577d09249bc1.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7175-350f4ce2873bc038.js +1 -0
- package/apps/pwa/out/_next/static/chunks/722.de8d9801e0f6d414.js +41 -0
- package/apps/pwa/out/_next/static/chunks/{1470.98a6ab61ccb4131c.js → 726.83532d69452ee24b.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/7290-f09c928a06c52e2d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{6062-73e68343a9814f6a.js → 7327-2d69fb321ba73389.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/7497.57981e9d7a75a52a.js +24 -0
- package/apps/pwa/out/_next/static/chunks/7630.2b9a68659053ff4e.js +193 -0
- package/apps/pwa/out/_next/static/chunks/7672.4af122ad892117c7.js +1 -0
- package/apps/pwa/out/_next/static/chunks/771.6ab889a233e7dae7.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7797-df5aa0381fdc078f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/7899.597af4a9996c907f.js +1 -0
- package/apps/pwa/out/_next/static/chunks/79b78388.663188133449d707.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8060-cc008cc6fedfa818.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8085-d065d0393c1760f5.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8096.f7da0776cfde6300.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8194.484f51e273d9abd2.js +217 -0
- package/apps/pwa/out/_next/static/chunks/8401-35e94e9091f5eb8d.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8682-7061dd4fa7bd62ed.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8695-846034ce44852c70.js +1 -0
- package/apps/pwa/out/_next/static/chunks/8769.815ca69879f608b4.js +104 -0
- package/apps/pwa/out/_next/static/chunks/8857-f1762140ef9a96b1.js +1 -0
- package/apps/pwa/out/_next/static/chunks/88729267.9c6c000157c83f2f.js +62 -0
- package/apps/pwa/out/_next/static/chunks/8f835b4f.048eeb02299e3e12.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9068.5f2764fccf86c1b2.js +93 -0
- package/apps/pwa/out/_next/static/chunks/9070.067c106528ec1ae6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9103-60fbef33e66f0a46.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9247-7581825d511a64c0.js +1 -0
- package/apps/pwa/out/_next/static/chunks/{7647.2fcd30d894d5ee30.js → 935.4ddaa99fc1d1126c.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/9451.ebfb0dcc9c02250f.js +23 -0
- package/apps/pwa/out/_next/static/chunks/9497-b0e758d1cd4ae8d7.js +1 -0
- package/apps/pwa/out/_next/static/chunks/9559.e648369b1d57cf9a.js +62 -0
- package/apps/pwa/out/_next/static/chunks/9832.2ab2e818013574c7.js +1 -0
- package/apps/pwa/out/_next/static/chunks/a9f2b54d-40c16bd0efdb04b8.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/_not-found/page-c1a96669158471ec.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-937c92292a4ab4f0.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/autopilot/page-d781be3e3a9cfd74.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/board/page-b0a80997903852d8.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/botlab/page-2b1263165630d9c8.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/bots/page-ea998f7f5d156bea.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ceo/page-33c328b64ca508f8.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/chat/page-e2d6bed48ac20120.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/consult/page-111cabe4d1fd69ae.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/control/page-544dd4bd4847eeb7.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/decisions/page-3ee191319bf97d87.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/design-check/page-e4bcb2c503dab04b.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/editor/page-300897dcbbd5ed76.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/exec/page-a449f1ed472b81c2.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/field/page-9045094d6d3b5cd0.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/healthz/route-c1a96669158471ec.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/inside/page-c06673596849ef45.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/intake/page-103fa3191c1b2e08.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/layout-9f81233f29dcd409.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/live/{page-b70cd005d9169e25.js → page-be8b4615c919e36c.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/app/live-run/page-c685b104e0dfa609.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/loops/page-8d693331cfbb6add.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/market/page-cef4f30a4c22fae1.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-66ea1b54fc83fa15.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/morning/page-7a12f61be4a579d1.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/not-found-c85b0589591a99c9.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-725a4da02dd129eb.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-8cc707bd09a497d9.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/release/page-90b55be1f15ce0e8.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-cb16606e08be65db.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/outputs/page-16dc64b74ed6a40a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/page-21bec4afeb2f0917.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/reflection/page-dc39ee7a36d26385.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/scheduler/page-84ae759f76053672.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-3db58ddb61fe2be9.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-c5315e30f3528dc4.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/settings/page-2fe368efb6de3b6a.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-8d04132dd0ac5e10.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/layout-c1a96669158471ec.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/setup/page-f23484de467c9ea3.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/share/page-c69762987b751174.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-5456692bda39aafb.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-d75f7cd65845ce98.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/term/page-5603f5e568b25ce3.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/today/page-d2cc0f4e89f42028.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/trace/page-a6eb73b8a4840f1c.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/vault/page-c0a3f4d10edb83fa.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-6c0b88068c143f15.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workflows/page-7f8d7fcdf058a845.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/workspace/page-22163556cd38bc88.js +1 -0
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-141436fd26dd56fc.js +1 -0
- package/apps/pwa/out/_next/static/chunks/d70879db.c66c56a95a91efc6.js +66 -0
- package/apps/pwa/out/_next/static/chunks/{8046cadf-211ca3c0e00f2b72.js → f6231284-45726550aba08fca.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/{2ea96958.e8c7e112de50b067.js → f6b7c9ba.64a895b8b2fedbdc.js} +1 -1
- package/apps/pwa/out/_next/static/chunks/f8626309.a4e5d362b1d55e59.js +1 -0
- package/apps/pwa/out/_next/static/chunks/framework-5c169757551134ed.js +1 -0
- package/apps/pwa/out/_next/static/chunks/main-59fe95fed40c7a01.js +1 -0
- package/apps/pwa/out/_next/static/chunks/main-app-4ca74dbbee7057c6.js +1 -0
- package/apps/pwa/out/_next/static/chunks/pages/_app-13a0a886df4d97a1.js +1 -0
- package/apps/pwa/out/_next/static/chunks/pages/_error-6af8591cd3a3f038.js +1 -0
- package/apps/pwa/out/_next/static/chunks/webpack-f2b4016b9b981bc8.js +1 -0
- package/apps/pwa/out/_next/static/css/{d3427996da11e480.css → b9d8151e48998417.css} +1 -1
- package/apps/pwa/out/_next/static/css/e81df11490b4413b.css +1 -0
- package/apps/pwa/out/approvals/index.html +1 -1
- package/apps/pwa/out/approvals/index.txt +23 -23
- package/apps/pwa/out/autopilot/index.html +1 -1
- package/apps/pwa/out/autopilot/index.txt +23 -23
- package/apps/pwa/out/board/index.html +1 -1
- package/apps/pwa/out/board/index.txt +23 -23
- package/apps/pwa/out/botlab/index.html +1 -1
- package/apps/pwa/out/botlab/index.txt +22 -22
- package/apps/pwa/out/bots/index.html +1 -1
- package/apps/pwa/out/bots/index.txt +22 -22
- package/apps/pwa/out/ceo/index.html +1 -1
- package/apps/pwa/out/ceo/index.txt +23 -23
- package/apps/pwa/out/chat/index.html +1 -1
- package/apps/pwa/out/chat/index.txt +22 -22
- package/apps/pwa/out/consult/index.html +1 -1
- package/apps/pwa/out/consult/index.txt +23 -23
- package/apps/pwa/out/control/index.html +1 -1
- package/apps/pwa/out/control/index.txt +23 -23
- package/apps/pwa/out/decisions/index.html +1 -1
- package/apps/pwa/out/decisions/index.txt +22 -22
- package/apps/pwa/out/design-check/index.html +1 -1
- package/apps/pwa/out/design-check/index.txt +23 -23
- package/apps/pwa/out/editor/index.html +1 -1
- package/apps/pwa/out/editor/index.txt +24 -24
- package/apps/pwa/out/exec/index.html +1 -1
- package/apps/pwa/out/exec/index.txt +23 -23
- package/apps/pwa/out/field/index.html +1 -1
- package/apps/pwa/out/field/index.txt +23 -23
- package/apps/pwa/out/healthz +1 -1
- package/apps/pwa/out/index.html +1 -1
- package/apps/pwa/out/index.txt +22 -22
- package/apps/pwa/out/inside/index.html +1 -1
- package/apps/pwa/out/inside/index.txt +23 -23
- package/apps/pwa/out/intake/index.html +1 -1
- package/apps/pwa/out/intake/index.txt +23 -23
- package/apps/pwa/out/live/index.html +1 -1
- package/apps/pwa/out/live/index.txt +23 -23
- package/apps/pwa/out/live-run/index.html +1 -0
- package/apps/pwa/out/live-run/index.txt +35 -0
- package/apps/pwa/out/loops/index.html +1 -1
- package/apps/pwa/out/loops/index.txt +22 -22
- package/apps/pwa/out/market/index.html +1 -1
- package/apps/pwa/out/market/index.txt +22 -22
- package/apps/pwa/out/missions/placeholder/index.html +1 -1
- package/apps/pwa/out/missions/placeholder/index.txt +22 -22
- package/apps/pwa/out/morning/index.html +1 -1
- package/apps/pwa/out/morning/index.txt +23 -23
- package/apps/pwa/out/observatory/index.html +1 -1
- package/apps/pwa/out/observatory/index.txt +23 -23
- package/apps/pwa/out/ops/checklist/index.html +1 -1
- package/apps/pwa/out/ops/checklist/index.txt +22 -22
- package/apps/pwa/out/ops/release/index.html +1 -1
- package/apps/pwa/out/ops/release/index.txt +22 -22
- package/apps/pwa/out/ops/seats/index.html +1 -1
- package/apps/pwa/out/ops/seats/index.txt +22 -22
- package/apps/pwa/out/outputs/index.html +1 -1
- package/apps/pwa/out/outputs/index.txt +22 -22
- package/apps/pwa/out/reflection/index.html +1 -1
- package/apps/pwa/out/reflection/index.txt +23 -23
- package/apps/pwa/out/scheduler/index.html +1 -1
- package/apps/pwa/out/scheduler/index.txt +23 -23
- package/apps/pwa/out/sessions/index.html +1 -1
- package/apps/pwa/out/sessions/index.txt +22 -22
- package/apps/pwa/out/settings/devices/index.html +1 -1
- package/apps/pwa/out/settings/devices/index.txt +23 -23
- package/apps/pwa/out/settings/index.html +1 -1
- package/apps/pwa/out/settings/index.txt +23 -23
- package/apps/pwa/out/setup/done/index.html +1 -1
- package/apps/pwa/out/setup/done/index.txt +23 -23
- package/apps/pwa/out/setup/index.html +1 -1
- package/apps/pwa/out/setup/index.txt +23 -23
- package/apps/pwa/out/share/index.html +1 -1
- package/apps/pwa/out/share/index.txt +23 -23
- package/apps/pwa/out/showroom/index.html +1 -1
- package/apps/pwa/out/showroom/index.txt +22 -22
- package/apps/pwa/out/tasks/index.html +1 -1
- package/apps/pwa/out/tasks/index.txt +23 -23
- package/apps/pwa/out/term/index.html +1 -1
- package/apps/pwa/out/term/index.txt +23 -23
- package/apps/pwa/out/today/index.html +1 -1
- package/apps/pwa/out/today/index.txt +23 -23
- package/apps/pwa/out/trace/index.html +1 -1
- package/apps/pwa/out/trace/index.txt +23 -23
- package/apps/pwa/out/vault/index.html +1 -1
- package/apps/pwa/out/vault/index.txt +23 -23
- package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
- package/apps/pwa/out/workflows/chat-ui/index.txt +23 -23
- package/apps/pwa/out/workflows/index.html +1 -1
- package/apps/pwa/out/workflows/index.txt +23 -23
- package/apps/pwa/out/workspace/index.html +1 -1
- package/apps/pwa/out/workspace/index.txt +23 -23
- package/apps/pwa/out/worktrees/index.html +1 -1
- package/apps/pwa/out/worktrees/index.txt +23 -23
- package/graphs/demo/editor-recipes.yaml +22 -0
- package/graphs/drafts/recipes.yaml +4 -4
- package/graphs/implement-loop.yaml +277 -5
- package/graphs/retro/daily.yaml +25 -0
- package/graphs/retro/recipes.yaml +24 -0
- package/package.json +1 -1
- package/scripts/ask-marker-check.ts +3 -1
- package/scripts/coord-channel-watch.sh +31 -4
- package/scripts/docs-lint.ts +4 -1
- package/scripts/land-global-checks.ts +89 -0
- package/scripts/lib/pch-regress-map.ts +2 -1
- package/scripts/marketing/generate-ax-brief.ts +18 -1
- package/scripts/measure-nl-routing-corpus.ts +11 -3
- package/scripts/release-loop/gate-node.ts +296 -25
- package/scripts/release-loop/gate-timing.ts +116 -7
- package/scripts/release-loop/preflight.ts +6 -4
- package/scripts/release-loop/publish-window.ts +141 -0
- package/scripts/release-loop/release-docs.ts +1 -4
- package/scripts/release-loop/release-universe.ts +38 -0
- package/scripts/release-loop/resume-release.ts +4 -1
- package/scripts/release-loop/shard-plan.ts +50 -5
- package/scripts/release-loop/unattended-release.ts +10 -2
- package/scripts/release-story/draft.ts +5 -1
- package/scripts/retro/shadow-compare.ts +71 -0
- package/scripts/rhythm/daily.ts +8 -2
- package/src/agent-mission/driver.ts +34 -6
- package/src/autopilot/build/llm-conflict-merge.ts +132 -7
- package/src/autopilot/build/merge-intent.ts +135 -7
- package/src/away/release-watch.ts +20 -3
- package/src/browser-cdp/client.ts +10 -5
- package/src/cli/config-drift.ts +513 -0
- package/src/cli/log-event-names.ts +4 -0
- package/src/cli/ops-cli.ts +11 -0
- package/src/cli/ops-overview-cli.ts +50 -0
- package/src/cli/pod-cli.ts +1 -0
- package/src/cli/pr-cli.ts +20 -0
- package/src/cli/release-cli.ts +20 -5
- package/src/cli/retro-cli.ts +78 -0
- package/src/cli/tasks-cli.ts +200 -8
- package/src/dashboard/index.ts +40 -4
- package/src/dashboard/slash-runtime/dashboard-handlers.ts +5 -4
- package/src/dashboard/slash-runtime/help-from-registry.ts +57 -31
- package/src/domains/outbound-log-probe.ts +6 -1
- package/src/esc-abort-gate.ts +3 -1
- package/src/graph-runner/graph-cli.ts +57 -0
- package/src/graph-runner/graph-run-api.ts +218 -0
- package/src/graph-runner/runner.ts +11 -1
- package/src/graph-wizard/archetypes.ts +88 -0
- package/src/graph-wizard/generate.ts +545 -0
- package/src/graph-wizard/saved-steps.ts +120 -0
- package/src/graph-wizard/steps.ts +193 -0
- package/src/harness/harness-cli-command.ts +482 -59
- package/src/harness/harness-queue.ts +315 -25
- package/src/harness/harness-salvage-cli.ts +62 -0
- package/src/hq/fence-outcomes.ts +63 -0
- package/src/hq/fence-wrapper.ts +33 -0
- package/src/hq/hq.ts +11 -1
- package/src/index.ts +76 -10
- package/src/intake-plane/digest.ts +18 -11
- package/src/maturity/feature-maturity.ts +4 -3
- package/src/nexus/api/draft-metrics.ts +114 -0
- package/src/nexus/api/graph-kinds.ts +10 -4
- package/src/nexus/api/graph-wizard-api.ts +53 -0
- package/src/nexus/api/graphs-api.ts +220 -10
- package/src/nexus/api/harness-traversal.ts +205 -0
- package/src/nexus/api/http-server.ts +69 -3
- package/src/nexus/api/loop-edges.ts +4 -2
- package/src/nexus/api/ops-api.ts +22 -7
- package/src/nexus/api/ops-board-route.ts +48 -0
- package/src/nexus/outbound/router.ts +28 -8
- package/src/onboarding/entry-hints.ts +5 -0
- package/src/onboarding/first-run.ts +3 -0
- package/src/onboarding.ts +24 -6
- package/src/ops/overview-collect.ts +345 -0
- package/src/ops/overview-seat-era.ts +90 -0
- package/src/ops/overview.ts +223 -0
- package/src/release-loop/feature-store.ts +14 -11
- package/src/release-loop/gate-shards.ts +163 -0
- package/src/release-loop/release-schedule.ts +6 -2
- package/src/retro/daily.ts +372 -0
- package/src/seat-loop/op-decide.ts +173 -0
- package/src/seat-loop/seat-loop.ts +60 -3
- package/src/seat-loop/seat-report.ts +58 -3
- package/src/self-dev/ask-launch-flow.ts +9 -6
- package/src/self-dev/ask-launch-io.ts +52 -2
- package/src/self-dev/ask-path-candidates.ts +40 -2
- package/src/self-dev/draft-sweep.ts +371 -16
- package/src/self-dev/draft-triage-rules.ts +19 -4
- package/src/self-dev/graph-journey-nodes.ts +157 -0
- package/src/self-dev/graph-journey-route.ts +105 -0
- package/src/self-dev/launch-preflight.ts +43 -3
- package/src/self-dev/pr-preview.ts +84 -16
- package/src/self-dev/release-path-guard.ts +7 -4
- package/src/self-implement/blocked-draft-policy.ts +101 -0
- package/src/self-implement/execution-clarification.ts +11 -0
- package/src/self-implement/frozen-merges.ts +23 -0
- package/src/self-implement/gate-env-retry.ts +83 -0
- package/src/self-implement/goal-author.ts +14 -0
- package/src/self-implement/heal-intake.ts +3 -0
- package/src/self-implement/orchestrator.ts +234 -6
- package/src/self-implement/run-stop.ts +123 -0
- package/src/self-implement/run-ttl.ts +258 -0
- package/src/self-implement/salvage-retention.ts +123 -0
- package/src/self-implement/seams.ts +7 -0
- package/src/self-implement/self-implement-cli.ts +2 -0
- package/src/self-implement/self-implement-runtime.ts +12 -0
- package/src/self-implement/sibling-resync.ts +223 -0
- package/src/self-implement/stop-autoheal.ts +204 -0
- package/src/session-runtime/index.ts +5 -3
- package/src/shell-env-bootstrap.ts +17 -3
- package/src/task-agent/board.ts +1 -1
- package/src/task-agent/card-evidence.ts +105 -10
- package/src/task-agent/judge.ts +7 -0
- package/src/task-agent/live-moves.ts +120 -20
- package/src/task-agent/mission.ts +1 -1
- package/src/task-agent/parent-host.ts +315 -0
- package/src/task-agent/shadow.ts +10 -1
- package/src/task-agent/task-hand.ts +16 -4
- package/src/task-orchestrator/surfaces/pod-command-job.ts +60 -11
- package/src/task-orchestrator/surfaces/pod-failure-reason.ts +4 -0
- package/src/task-orchestrator/surfaces/pod-gate-deps.ts +123 -0
- package/src/task-orchestrator/surfaces/pod-install-slots.ts +89 -0
- package/src/task-orchestrator/surfaces/pod-lease.ts +93 -10
- package/src/task-orchestrator/surfaces/pod-pool.ts +147 -34
- package/src/task-orchestrator/surfaces/self-implement-pod.ts +58 -9
- package/src/telegram-commands.ts +41 -0
- package/src/testing/real-cli-spawn.ts +106 -0
- package/src/tool-runtime/browser-runtime.ts +87 -3
- package/src/tool-runtime/index.ts +1 -0
- package/src/user-config.ts +76 -7
- package/src/version/packed-revision.json +1 -1
- package/apps/pwa/out/_next/static/J5H7BvqgSI8DUjtRRcA_S/_buildManifest.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1105-c473ed46272fba79.js +0 -1
- package/apps/pwa/out/_next/static/chunks/1157-cba0e8553d35a980.js +0 -1
- package/apps/pwa/out/_next/static/chunks/171-23a877ed11720de9.js +0 -1
- package/apps/pwa/out/_next/static/chunks/216-5acd01cbde4acda7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2330-c2f07246c8788af1.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2336-e830c777eb6bce04.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2375-e061bb24cd2b5cde.js +0 -1
- package/apps/pwa/out/_next/static/chunks/2415.ed6428ff334c7ce2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3094.67339318c7f72a3a.js +0 -2
- package/apps/pwa/out/_next/static/chunks/338-ceb956612dcfaa86.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3395-835e4ceaa6335ba4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3403.4bc97b4066945a94.js +0 -5
- package/apps/pwa/out/_next/static/chunks/3441-be82e3595aef3a20.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3689-89905039bf3e7257.js +0 -1
- package/apps/pwa/out/_next/static/chunks/371-4ad6af55dad02ced.js +0 -1
- package/apps/pwa/out/_next/static/chunks/3822-6f268bc26c6a84ac.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4114-3e63117452378807.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4457-d7df81c90f9d0268.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4551-77dadae76f6cbace.js +0 -1
- package/apps/pwa/out/_next/static/chunks/4978.6fd93b974441e87f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5012-f746f6549b869f91.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5609-012bb3557211f07e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/5791-bb82743fa6299562.js +0 -1
- package/apps/pwa/out/_next/static/chunks/62-5bfed1e23790b745.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6320-9bc8561fde416e07.js +0 -1
- package/apps/pwa/out/_next/static/chunks/649-ca96d1c5d09e02b8.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6725-68ab7cc0b1faf00d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6726-85a7db59b288c53f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6735-33267c4630d0d963.js +0 -1
- package/apps/pwa/out/_next/static/chunks/6998-a654fe5c58271600.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7633-141682015873beb6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7673-2eda1507d38dadb8.js +0 -1
- package/apps/pwa/out/_next/static/chunks/7990-e12efba616f46e03.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8067-143791231c99bd02.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8142-744998d65d7bb17e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8430-a10cfba8ef282d4e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/8870-9614a213b990b10b.js +0 -25
- package/apps/pwa/out/_next/static/chunks/920-f7dceb78f1c585ef.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9454-12c2f30dea62291c.js +0 -1
- package/apps/pwa/out/_next/static/chunks/976-4a9b4ebdca7c739a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/9989-15cdf568bafa9fc8.js +0 -1
- package/apps/pwa/out/_next/static/chunks/ae787e4c-e5b1ef00b2dbceca.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/_not-found/page-859cea1570b6227a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/approvals/page-deef5ee191830f14.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/autopilot/page-b193da485f9a14b2.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/board/page-b6e1a79df0e214f4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/botlab/page-e84df4d907f703c7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/bots/page-a5fb9a5db89ae4ea.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ceo/page-1499f481d7fc52d6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/chat/page-c7cf753aeb648790.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/consult/page-4118469e514e10fd.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/control/page-e968f7630654332a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/decisions/page-505915052f947022.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/design-check/page-c2f962fb4a3b06c6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/editor/page-7b5ed0bd8a589ec4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/exec/page-cf8193250dd42dd4.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/field/page-cf3f5dfa35dea8e9.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/healthz/route-859cea1570b6227a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/inside/page-5b29d9b06ceadade.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/intake/page-a15e7fcc283e9ca7.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/layout-d2915e7b70ff3d0e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/loops/page-2595f0eba29f57e1.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/market/page-c776322454156e36.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-77bbd17f4435aa10.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/morning/page-049689e611ecc936.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/not-found-85954a4fb09c44eb.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/observatory/page-e773e552e1068ee6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-5633c4bfa7684b4a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/release/page-d829b92bd01b4994.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-061c317fc771becf.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/outputs/page-3a45ece8865ed4b6.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/page-0be990618c0d56bd.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/reflection/page-bfea4865dc244b73.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/scheduler/page-0d6ba8e337741484.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/sessions/page-76a88b55d816482e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-bba1542df59f61ce.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/settings/page-4185c95689bb372e.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/done/page-b699e1a8d5d34b8b.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/layout-859cea1570b6227a.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/setup/page-c225e338c31bf274.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/share/page-449353f70507ca1f.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/showroom/page-90b889714102476d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/tasks/page-f1ee7fbeb7f96ddc.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/term/page-d59a95e1434dfaa0.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/today/page-71491a23414d1a47.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/trace/page-de7112bdb65fa4fe.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/vault/page-feb4d0d88079660d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-f17d887b40dd6b1d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workflows/page-8da77be2192bcd5d.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/workspace/page-72c3b9a5e9582b29.js +0 -1
- package/apps/pwa/out/_next/static/chunks/app/worktrees/page-c3afe4c8d0c48c39.js +0 -1
- package/apps/pwa/out/_next/static/chunks/framework-ea806bb57f2b8923.js +0 -1
- package/apps/pwa/out/_next/static/chunks/main-8d2302399c2c8f87.js +0 -1
- package/apps/pwa/out/_next/static/chunks/main-app-b9308f2d8a931531.js +0 -1
- package/apps/pwa/out/_next/static/chunks/pages/_app-b10928c60f0b8488.js +0 -1
- package/apps/pwa/out/_next/static/chunks/pages/_error-689039227d443fab.js +0 -1
- package/apps/pwa/out/_next/static/chunks/webpack-6f35f9d892fa630b.js +0 -1
- package/apps/pwa/out/_next/static/css/85f53c6f35d11382.css +0 -1
- /package/apps/pwa/out/_next/static/{J5H7BvqgSI8DUjtRRcA_S → MObU8ujkMciAKr6ljAV05}/_ssgManifest.js +0 -0
- /package/skills/project-onboarding/{skill.md → SKILL.md} +0 -0
|
@@ -0,0 +1,513 @@
|
|
|
1
|
+
// ── CFG-STORE1 · 읽기 전용 «유효 config ⊕ 출처» · 드리프트 표 (0.2.20 · 2026-10-08) ──
|
|
2
|
+
//
|
|
3
|
+
// RFC-universe-by-house-not-tree-and-config-store-2026-10-06 §2·§3.
|
|
4
|
+
//
|
|
5
|
+
// S: 집(운영 `~/.elanous`)과 파생 시험 우주(`<트리>/.elanous-test` · 10-06 실측 161)가 각자
|
|
6
|
+
// `config.json` 사본을 «물질화»해 갖는다. 운영 키를 바꿔도 사본은 그대로다.
|
|
7
|
+
// C: 10-08 밤 — 운영 `harness.podPool` 에서 node-c 를 뺐는데 자리·작업 트리의 시험 우주 사본 36개가
|
|
8
|
+
// 옛 podPool(node-c 포함)을 들고 있어, 그 트리에서 쏜 발사가 계속 node-c 에 Pod 를 놓았다.
|
|
9
|
+
// Q: 「어느 키가 어느 우주에서 운영과 다른가」를 한 명령으로 볼 수 있나.
|
|
10
|
+
// A: `elanous config drift` · `elanous config explain <key>` — 이 모듈이 그 계산이다.
|
|
11
|
+
//
|
|
12
|
+
// ⛔ 불변식
|
|
13
|
+
// • 읽기만 한다 — 레지스트리 prune(readLogInstances)·XDG 이주(userConfigPath)도 부르지 않는다.
|
|
14
|
+
// • 못 읽은 config 는 «못 읽음»으로 따로 센다 — 절대 「같다」로 접지 않는다.
|
|
15
|
+
// • 비밀 값은 찍지 않는다 — 경로의 한 마디라도 token/secret/key/password 면 값을 가린다.
|
|
16
|
+
// • config.json 이 «없는» 우주는 비교에서 빼되 수를 낸다(그 우주는 코드 기본값으로 돈다).
|
|
17
|
+
|
|
18
|
+
import { existsSync, readdirSync, readFileSync, statSync } from 'node:fs';
|
|
19
|
+
import { homedir } from 'node:os';
|
|
20
|
+
import { join, resolve } from 'node:path';
|
|
21
|
+
import { redactSecretText } from '../debug/log.js';
|
|
22
|
+
import { resolveInstanceKind, type LogInstanceEntry } from '../mss/logging/instance-registry.js';
|
|
23
|
+
import { buildTestSafeRawConfig } from './config-test-sync.js';
|
|
24
|
+
import { prodInstanceRoot } from '../instance/resolve.js';
|
|
25
|
+
|
|
26
|
+
export type UniverseOrigin = 'registry' | 'tree-scan';
|
|
27
|
+
export type UniverseStatus = 'ok' | 'no-config' | 'unreadable' | 'gone';
|
|
28
|
+
|
|
29
|
+
export interface UniverseConfig {
|
|
30
|
+
configDir: string;
|
|
31
|
+
configPath: string;
|
|
32
|
+
origins: UniverseOrigin[];
|
|
33
|
+
status: UniverseStatus;
|
|
34
|
+
/** status=unreadable 일 때 사유. */
|
|
35
|
+
error?: string;
|
|
36
|
+
/** status=ok 일 때 파일 원문(raw) — 겹 «파일» 층. */
|
|
37
|
+
raw?: Record<string, unknown>;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export interface DiscoverOptions {
|
|
41
|
+
prodRoot?: string;
|
|
42
|
+
/** 인스턴스 레지스트리(`~/.elanous/logs/instances.json`) — 읽기만. null 이면 안 읽는다. */
|
|
43
|
+
registryPath?: string | null;
|
|
44
|
+
/** 명시 트리 뿌리 — 각 자식 디렉토리의 `.elanous-test` 를 본다. */
|
|
45
|
+
treeScanRoots?: string[];
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// 비밀 «이름» — 대소문자 무시 낱말 ⊕ 「auth」「pass」는 낱말 경계로만(authorOnPod·bypass 는 아니다).
|
|
49
|
+
const SECRET_NAME = /token|secret|key|password|passphrase|credential|authorization|oauth|bearer|cookie|session|dsn/i;
|
|
50
|
+
const SECRET_NAME_CAMEL = /(?:(?:^|[^a-zA-Z])(?:auth|AUTH|pass|PASS)|(?:Auth|Pass))(?![a-z])/;
|
|
51
|
+
// 비밀 «값» — 이름이 무해해도(notify.url) 값 모양으로 가린다.
|
|
52
|
+
const SECRET_VALUE_PATTERNS: readonly RegExp[] = [
|
|
53
|
+
/[a-z][a-z0-9+.-]*:\/\/[^/\s@]+@/i, // URL userinfo (https://user:pass@host)
|
|
54
|
+
/discord(?:app)?\.com\/api\/webhooks|hooks\.slack\.com/i, // Discord·Slack 웹훅
|
|
55
|
+
/^\s*(?:ghp_|gho_|ghs_|github_pat_|sk-|xoxb-|xoxp-|Bearer\s)/, // 토큰 접두
|
|
56
|
+
];
|
|
57
|
+
|
|
58
|
+
/** 키 한 마디가 비밀 이름인가. */
|
|
59
|
+
export function isSecretName(segment: string): boolean {
|
|
60
|
+
return SECRET_NAME.test(segment) || SECRET_NAME_CAMEL.test(segment);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/** 문자열 값이 비밀 모양인가(이름과 무관). */
|
|
64
|
+
export function isSecretValue(value: unknown): boolean {
|
|
65
|
+
return typeof value === 'string' && SECRET_VALUE_PATTERNS.some((re) => re.test(value));
|
|
66
|
+
}
|
|
67
|
+
export const MASK = '<masked>';
|
|
68
|
+
|
|
69
|
+
export function defaultProdRoot(): string {
|
|
70
|
+
return prodInstanceRoot(); // 집 = 운영 뿌리(우주 리졸버 경유 · 격리 게이트)
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export function defaultRegistryPath(prodRoot: string = defaultProdRoot()): string {
|
|
74
|
+
return join(prodRoot, 'logs', 'instances.json');
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** 레지스트리 밖에서 시험 우주가 사는 «알려진» 트리 뿌리(10-08 사건의 세 곳). 명시 목록 — 홈 전체를 훑지 않는다. */
|
|
78
|
+
export function defaultTreeScanRoots(home: string = homedir()): string[] {
|
|
79
|
+
return [join(home, 'elanous-hq', 'seats'), join(home, 'elanous-hq', 'work'), join(home, 'source', 'pilot')];
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/** 읽기 실패 사유 — ⛔ 파서 메시지는 파일 «내용» 조각을 실을 수 있어 그대로 내지 않는다(비밀 노출). 종류·errno 코드만. */
|
|
83
|
+
export function safeReadError(err: unknown): string {
|
|
84
|
+
if (err instanceof SyntaxError) return 'JSON 파싱 실패';
|
|
85
|
+
const code = (err as { code?: unknown } | null)?.code;
|
|
86
|
+
if (typeof code === 'string' && /^[A-Z0-9_]+$/.test(code)) return `읽기 실패(${code})`;
|
|
87
|
+
return '읽기 실패';
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function norm(p: string): string {
|
|
91
|
+
return resolve(p.trim().replace(/\/+$/, ''));
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/** 레지스트리를 «읽기만» 한다(prune 쓰기 없음). 실패는 {ok:false}. */
|
|
95
|
+
export function readRegistryReadOnly(path: string): { ok: boolean; entries: LogInstanceEntry[]; error?: string } {
|
|
96
|
+
try {
|
|
97
|
+
const parsed = JSON.parse(readFileSync(path, 'utf-8')) as { instances?: unknown };
|
|
98
|
+
if (!Array.isArray(parsed.instances)) return { ok: false, entries: [], error: 'instances 배열 없음' };
|
|
99
|
+
const entries = parsed.instances.filter((e): e is LogInstanceEntry => !!e && typeof e === 'object'
|
|
100
|
+
&& typeof (e as LogInstanceEntry).stateDir === 'string');
|
|
101
|
+
return { ok: true, entries };
|
|
102
|
+
} catch (err) {
|
|
103
|
+
return { ok: false, entries: [], error: safeReadError(err) };
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export function readUniverseConfig(configDir: string, origins: UniverseOrigin[]): UniverseConfig {
|
|
108
|
+
const configPath = join(configDir, 'config.json');
|
|
109
|
+
if (!existsSync(configDir)) return { configDir, configPath, origins, status: 'gone' };
|
|
110
|
+
if (!existsSync(configPath)) return { configDir, configPath, origins, status: 'no-config' };
|
|
111
|
+
try {
|
|
112
|
+
const parsed = JSON.parse(readFileSync(configPath, 'utf-8')) as unknown;
|
|
113
|
+
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
|
|
114
|
+
return { configDir, configPath, origins, status: 'unreadable', error: '최상위가 객체가 아님' };
|
|
115
|
+
}
|
|
116
|
+
return { configDir, configPath, origins, status: 'ok', raw: parsed as Record<string, unknown> };
|
|
117
|
+
} catch (err) {
|
|
118
|
+
return { configDir, configPath, origins, status: 'unreadable', error: safeReadError(err) };
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export interface DiscoveryResult {
|
|
123
|
+
prod: UniverseConfig;
|
|
124
|
+
derived: UniverseConfig[];
|
|
125
|
+
registry: { path: string | null; ok: boolean; entries: number; testEntries: number; error?: string };
|
|
126
|
+
treeScanRoots: string[];
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/** 집 ⊕ 파생 시험 우주를 모은다 — 레지스트리(test 항목) ∪ 명시 트리 뿌리. configDir 로 중복 제거. */
|
|
130
|
+
export function discoverUniverses(opts: DiscoverOptions = {}): DiscoveryResult {
|
|
131
|
+
const prodRoot = norm(opts.prodRoot ?? defaultProdRoot());
|
|
132
|
+
const registryPath = opts.registryPath === undefined ? defaultRegistryPath(prodRoot) : opts.registryPath;
|
|
133
|
+
const treeScanRoots = (opts.treeScanRoots ?? defaultTreeScanRoots()).map(norm);
|
|
134
|
+
const found = new Map<string, Set<UniverseOrigin>>();
|
|
135
|
+
const add = (dir: string, origin: UniverseOrigin) => {
|
|
136
|
+
const d = norm(dir);
|
|
137
|
+
if (d === prodRoot) return;
|
|
138
|
+
const set = found.get(d) ?? new Set<UniverseOrigin>();
|
|
139
|
+
set.add(origin);
|
|
140
|
+
found.set(d, set);
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
let registry: DiscoveryResult['registry'] = { path: registryPath, ok: false, entries: 0, testEntries: 0 };
|
|
144
|
+
if (registryPath) {
|
|
145
|
+
const r = readRegistryReadOnly(registryPath);
|
|
146
|
+
let testEntries = 0;
|
|
147
|
+
for (const e of r.entries) {
|
|
148
|
+
if (resolveInstanceKind(e) !== 'test') continue;
|
|
149
|
+
testEntries++;
|
|
150
|
+
add(e.configDir ?? e.stateDir, 'registry');
|
|
151
|
+
}
|
|
152
|
+
registry = { path: registryPath, ok: r.ok, entries: r.entries.length, testEntries, ...(r.error ? { error: r.error } : {}) };
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
for (const root of treeScanRoots) {
|
|
156
|
+
let children: string[] = [];
|
|
157
|
+
try { children = readdirSync(root); } catch { continue; }
|
|
158
|
+
for (const child of children) {
|
|
159
|
+
const tree = join(root, child);
|
|
160
|
+
try { if (!statSync(tree).isDirectory()) continue; } catch { continue; }
|
|
161
|
+
const candidate = join(tree, '.elanous-test');
|
|
162
|
+
if (existsSync(candidate)) add(candidate, 'tree-scan');
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const derived = [...found.entries()]
|
|
167
|
+
.sort(([a], [b]) => a.localeCompare(b))
|
|
168
|
+
.map(([dir, origins]) => readUniverseConfig(dir, [...origins].sort()));
|
|
169
|
+
return { prod: readUniverseConfig(prodRoot, []), derived, registry, treeScanRoots };
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// ── 값 평탄화 · 가림 ──
|
|
173
|
+
|
|
174
|
+
export function isSecretPath(key: string): boolean {
|
|
175
|
+
return key.split('.').some(isSecretName);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/** 객체는 내려가고, 배열·원시값·빈 객체는 잎으로 둔다. */
|
|
179
|
+
export function flattenConfig(raw: Record<string, unknown>, prefix = '', out = new Map<string, unknown>()): Map<string, unknown> {
|
|
180
|
+
for (const [k, v] of Object.entries(raw)) {
|
|
181
|
+
const key = prefix ? `${prefix}.${k}` : k;
|
|
182
|
+
if (v && typeof v === 'object' && !Array.isArray(v) && Object.keys(v).length > 0) {
|
|
183
|
+
flattenConfig(v as Record<string, unknown>, key, out);
|
|
184
|
+
} else {
|
|
185
|
+
out.set(key, v);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
return out;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
function stableStringify(v: unknown): string {
|
|
192
|
+
if (Array.isArray(v)) return `[${v.map(stableStringify).join(',')}]`;
|
|
193
|
+
if (v && typeof v === 'object') {
|
|
194
|
+
return `{${Object.keys(v).sort().map((k) => `${JSON.stringify(k)}:${stableStringify((v as Record<string, unknown>)[k])}`).join(',')}}`;
|
|
195
|
+
}
|
|
196
|
+
return JSON.stringify(v) ?? 'undefined';
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/** 잎 안의 구조(배열 속 객체 등)도 내려가 비밀 이름 키의 값을 가린다 — `telegram.channels[].botToken` 같은 자리. */
|
|
200
|
+
export function maskDeep(value: unknown): unknown {
|
|
201
|
+
if (isSecretValue(value)) return MASK;
|
|
202
|
+
if (Array.isArray(value)) return value.map(maskDeep);
|
|
203
|
+
if (value && typeof value === 'object') {
|
|
204
|
+
return Object.fromEntries(Object.entries(value as Record<string, unknown>)
|
|
205
|
+
.map(([k, v]) => [k, isSecretName(k) ? MASK : maskDeep(v)]));
|
|
206
|
+
}
|
|
207
|
+
return value;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/** 표시용 값 — 비밀 경로면 가린다, 아니면 구조 안 비밀 키 가림 ⊕ 텍스트 축 가림. */
|
|
211
|
+
export function displayValue(key: string, value: unknown, present = true): string {
|
|
212
|
+
if (!present) return '(없음)';
|
|
213
|
+
if (isSecretPath(key) || isSecretValue(value)) return MASK;
|
|
214
|
+
const s = typeof value === 'string' ? JSON.stringify(value) : stableStringify(maskDeep(value));
|
|
215
|
+
const red = redactSecretText(s);
|
|
216
|
+
return red.length > 160 ? `${red.slice(0, 157)}...` : red;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
function matchesKey(key: string, filter?: string): boolean {
|
|
220
|
+
if (!filter) return true;
|
|
221
|
+
return key === filter || key.startsWith(`${filter}.`);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// ── 드리프트 ──
|
|
225
|
+
|
|
226
|
+
export interface DriftValueGroup {
|
|
227
|
+
/** 표시값(가림 적용). 없으면 '(없음)'. */
|
|
228
|
+
value: string;
|
|
229
|
+
present: boolean;
|
|
230
|
+
count: number;
|
|
231
|
+
examples: string[];
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
export interface DriftRow {
|
|
235
|
+
key: string;
|
|
236
|
+
prodPresent: boolean;
|
|
237
|
+
prodValue: string;
|
|
238
|
+
/** 운영과 다른 파생 우주 수(비교 대상 중). */
|
|
239
|
+
differing: number;
|
|
240
|
+
/** 운영엔 있는데 파생엔 없음. */
|
|
241
|
+
missingInDerived: number;
|
|
242
|
+
/** 운영엔 없는데 파생엔 있음. */
|
|
243
|
+
extraInDerived: number;
|
|
244
|
+
/** 둘 다 있는데 값이 다름. */
|
|
245
|
+
valueDiffers: number;
|
|
246
|
+
groups: DriftValueGroup[];
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
export interface DriftReport {
|
|
250
|
+
prodConfigPath: string;
|
|
251
|
+
prodStatus: UniverseStatus;
|
|
252
|
+
prodError?: string;
|
|
253
|
+
keyFilter?: string;
|
|
254
|
+
/** false(기본)면 «의도된 차이»를 드리프트로 안 센다 — `config sync-test` 의 test-safe 변환 결과와 같은 값 ⊕ `_test*` 메타 키. */
|
|
255
|
+
includeExpected: boolean;
|
|
256
|
+
universes: { total: number; compared: number; noConfig: number; unreadable: number; gone: number };
|
|
257
|
+
registry: DiscoveryResult['registry'];
|
|
258
|
+
treeScanRoots: string[];
|
|
259
|
+
unreadable: Array<{ configPath: string; error: string }>;
|
|
260
|
+
rows: DriftRow[];
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
const EXAMPLES = 3;
|
|
264
|
+
|
|
265
|
+
/** 사본 메타 키(`_testSyncedAt`·`_testSyncedFrom`·`_testSecretsStripped`) — 우주마다 다른 게 정상이다. */
|
|
266
|
+
export function isSyncMetaKey(key: string): boolean {
|
|
267
|
+
return key.startsWith('_test');
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
export function computeDrift(d: DiscoveryResult, opts: { key?: string; includeExpected?: boolean } = {}): DriftReport {
|
|
271
|
+
const includeExpected = opts.includeExpected === true;
|
|
272
|
+
const compared = d.derived.filter((u) => u.status === 'ok');
|
|
273
|
+
const unreadable = d.derived.filter((u) => u.status === 'unreadable');
|
|
274
|
+
const report: DriftReport = {
|
|
275
|
+
prodConfigPath: d.prod.configPath,
|
|
276
|
+
prodStatus: d.prod.status,
|
|
277
|
+
...(d.prod.error ? { prodError: d.prod.error } : {}),
|
|
278
|
+
...(opts.key ? { keyFilter: opts.key } : {}),
|
|
279
|
+
includeExpected,
|
|
280
|
+
universes: {
|
|
281
|
+
total: d.derived.length,
|
|
282
|
+
compared: compared.length,
|
|
283
|
+
noConfig: d.derived.filter((u) => u.status === 'no-config').length,
|
|
284
|
+
unreadable: unreadable.length,
|
|
285
|
+
gone: d.derived.filter((u) => u.status === 'gone').length,
|
|
286
|
+
},
|
|
287
|
+
registry: d.registry,
|
|
288
|
+
treeScanRoots: d.treeScanRoots,
|
|
289
|
+
unreadable: unreadable.map((u) => ({ configPath: u.configPath, error: u.error ?? '알 수 없음' })),
|
|
290
|
+
rows: [],
|
|
291
|
+
};
|
|
292
|
+
// ⛔ 운영을 못 읽으면 비교 자체를 하지 않는다 — 「드리프트 0」으로 보이면 안 된다.
|
|
293
|
+
if (d.prod.status !== 'ok' || !d.prod.raw) return report;
|
|
294
|
+
|
|
295
|
+
const prodFlat = flattenConfig(d.prod.raw);
|
|
296
|
+
// 「의도된 차이」의 자 — 오늘 운영 config 로 sync-test 를 돌리면 사본에 박힐 값.
|
|
297
|
+
const safeFlat = includeExpected ? null : flattenConfig(buildTestSafeRawConfig(d.prod.raw));
|
|
298
|
+
const derivedFlat = compared.map((u) => ({ u, flat: flattenConfig(u.raw!) }));
|
|
299
|
+
const keys = new Set<string>([...prodFlat.keys()].filter((k) => matchesKey(k, opts.key)));
|
|
300
|
+
for (const { flat } of derivedFlat) for (const k of flat.keys()) if (matchesKey(k, opts.key)) keys.add(k);
|
|
301
|
+
|
|
302
|
+
for (const key of [...keys].sort()) {
|
|
303
|
+
if (!includeExpected && isSyncMetaKey(key)) continue;
|
|
304
|
+
const prodPresent = prodFlat.has(key);
|
|
305
|
+
const prodSig = prodPresent ? stableStringify(prodFlat.get(key)) : undefined;
|
|
306
|
+
const safePresent = safeFlat?.has(key) ?? false;
|
|
307
|
+
const safeSig = safePresent ? stableStringify(safeFlat!.get(key)) : undefined;
|
|
308
|
+
const groups = new Map<string, DriftValueGroup>();
|
|
309
|
+
let missingInDerived = 0, extraInDerived = 0, valueDiffers = 0;
|
|
310
|
+
for (const { u, flat } of derivedFlat) {
|
|
311
|
+
const present = flat.has(key);
|
|
312
|
+
const sig = present ? stableStringify(flat.get(key)) : undefined;
|
|
313
|
+
if (present === prodPresent && sig === prodSig) continue;
|
|
314
|
+
if (safeFlat && present === safePresent && sig === safeSig) continue; // test-safe 변환 그대로 — 의도된 차이
|
|
315
|
+
if (!present) missingInDerived++;
|
|
316
|
+
else if (!prodPresent) extraInDerived++;
|
|
317
|
+
else valueDiffers++;
|
|
318
|
+
const groupKey = present ? `v:${sig}` : 'absent';
|
|
319
|
+
const g = groups.get(groupKey) ?? {
|
|
320
|
+
// 비밀 경로는 값 그룹마다 번호만 — 서로 다른지는 보이되 값은 안 보인다.
|
|
321
|
+
value: present ? (isSecretPath(key) ? `${MASK}#${groups.size + 1}` : displayValue(key, flat.get(key))) : '(없음)',
|
|
322
|
+
present, count: 0, examples: [],
|
|
323
|
+
};
|
|
324
|
+
g.count++;
|
|
325
|
+
if (g.examples.length < EXAMPLES) g.examples.push(u.configPath);
|
|
326
|
+
groups.set(groupKey, g);
|
|
327
|
+
}
|
|
328
|
+
const differing = missingInDerived + extraInDerived + valueDiffers;
|
|
329
|
+
if (differing === 0) continue;
|
|
330
|
+
report.rows.push({
|
|
331
|
+
key, prodPresent,
|
|
332
|
+
prodValue: displayValue(key, prodFlat.get(key), prodPresent),
|
|
333
|
+
differing, missingInDerived, extraInDerived, valueDiffers,
|
|
334
|
+
groups: [...groups.values()].sort((a, b) => b.count - a.count),
|
|
335
|
+
});
|
|
336
|
+
}
|
|
337
|
+
report.rows.sort((a, b) => b.differing - a.differing || a.key.localeCompare(b.key));
|
|
338
|
+
return report;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
function walk(root: unknown, key: string): { present: boolean; value?: unknown } {
|
|
342
|
+
let cur: unknown = root;
|
|
343
|
+
for (const part of key.split('.').filter(Boolean)) {
|
|
344
|
+
if (cur === null || typeof cur !== 'object' || !(part in (cur as Record<string, unknown>))) return { present: false };
|
|
345
|
+
cur = (cur as Record<string, unknown>)[part];
|
|
346
|
+
}
|
|
347
|
+
return { present: true, value: cur };
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
function shortHome(p: string): string {
|
|
351
|
+
const h = homedir();
|
|
352
|
+
return p.startsWith(`${h}/`) ? `~${p.slice(h.length)}` : p;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
function universeSummaryLine(r: DriftReport): string {
|
|
356
|
+
const u = r.universes;
|
|
357
|
+
return `파생 우주 ${u.total} (비교 ${u.compared} · config 없음 ${u.noConfig} · 못 읽음 ${u.unreadable} · 사라짐 ${u.gone})`
|
|
358
|
+
+ ` · 레지스트리 ${r.registry.path === null ? '끔' : r.registry.ok ? `test ${r.registry.testEntries}/${r.registry.entries}` : `못 읽음${r.registry.error ? `(${r.registry.error})` : ''}`}`
|
|
359
|
+
+ ` · 트리 뿌리 ${r.treeScanRoots.length}`;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
export function renderDrift(r: DriftReport, opts: { limit?: number } = {}): string {
|
|
363
|
+
const lines: string[] = [];
|
|
364
|
+
lines.push(`config drift — 집 ${shortHome(r.prodConfigPath)}${r.keyFilter ? ` · 키 ${r.keyFilter}` : ''}`
|
|
365
|
+
+ (r.includeExpected ? ' · 의도된 차이 포함' : ' · 의도된 차이(test-safe 변환·_test 메타) 제외 — --include-expected'));
|
|
366
|
+
lines.push(` ${universeSummaryLine(r)}`);
|
|
367
|
+
if (r.prodStatus !== 'ok') {
|
|
368
|
+
lines.push(` ⛔ 운영 config 못 읽음(${r.prodStatus}${r.prodError ? `: ${r.prodError}` : ''}) — 비교하지 않았다(「드리프트 0」이 아니다)`);
|
|
369
|
+
}
|
|
370
|
+
if (r.universes.compared === 0 && r.prodStatus === 'ok') lines.push(' ⚠️ 비교한 파생 우주 0 — 「드리프트 0」이 아니라 «잴 대상이 없었다»');
|
|
371
|
+
const limit = opts.limit ?? 50;
|
|
372
|
+
const shown = limit > 0 ? r.rows.slice(0, limit) : r.rows;
|
|
373
|
+
lines.push(` 드리프트 키 ${r.rows.length}`);
|
|
374
|
+
for (const row of shown) {
|
|
375
|
+
const parts: string[] = [];
|
|
376
|
+
if (row.missingInDerived) parts.push(`파생에 없음 ${row.missingInDerived}`);
|
|
377
|
+
if (row.extraInDerived) parts.push(`운영에 없음 ${row.extraInDerived}`);
|
|
378
|
+
if (row.valueDiffers) parts.push(`값 다름 ${row.valueDiffers}`);
|
|
379
|
+
lines.push(`\n ${row.key} ${row.differing}/${r.universes.compared} 우주 다름 (${parts.join(' · ')})`);
|
|
380
|
+
lines.push(` 운영: ${row.prodValue}`);
|
|
381
|
+
for (const g of row.groups.slice(0, 4)) {
|
|
382
|
+
lines.push(` ${g.value} × ${g.count} 예: ${g.examples.map(shortHome).join(', ')}`);
|
|
383
|
+
}
|
|
384
|
+
if (row.groups.length > 4) lines.push(` … 값 묶음 ${row.groups.length - 4}개 더 (--json)`);
|
|
385
|
+
}
|
|
386
|
+
if (shown.length < r.rows.length) lines.push(`\n … ${r.rows.length - shown.length}개 키 더 — --limit 0 또는 --json (⚠️ 잘린 표다)`);
|
|
387
|
+
if (r.unreadable.length) {
|
|
388
|
+
lines.push('\n 못 읽음 (같다고 보지 않음):');
|
|
389
|
+
for (const u of r.unreadable.slice(0, 20)) lines.push(` ${shortHome(u.configPath)} — ${u.error}`);
|
|
390
|
+
if (r.unreadable.length > 20) lines.push(` … ${r.unreadable.length - 20}개 더 (--json)`);
|
|
391
|
+
}
|
|
392
|
+
return lines.join('\n');
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
// ── explain ──
|
|
396
|
+
|
|
397
|
+
export type ProdSourceLayer = 'prod-file' | 'llm-fallback' | 'code-default' | 'unset' | 'prod-unreadable';
|
|
398
|
+
|
|
399
|
+
export interface ExplainReport {
|
|
400
|
+
key: string;
|
|
401
|
+
prod: { configPath: string; layer: ProdSourceLayer; value: string; note?: string };
|
|
402
|
+
codeDefault: { present: boolean; value: string };
|
|
403
|
+
/** false 면 운영을 못 읽어 파생과 «비교하지 않았다» — groups 는 비고 「다름」으로 세지 않는다. */
|
|
404
|
+
comparable?: boolean;
|
|
405
|
+
universes: DriftReport['universes'];
|
|
406
|
+
registry: DiscoveryResult['registry'];
|
|
407
|
+
treeScanRoots: string[];
|
|
408
|
+
/** 파생 우주 값 묶음 — 운영과 같은 것도 포함(sameAsProd). */
|
|
409
|
+
groups: Array<DriftValueGroup & { sameAsProd: boolean }>;
|
|
410
|
+
unreadable: Array<{ configPath: string; error: string }>;
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
export interface ExplainDeps {
|
|
414
|
+
/** 코드 기본값 객체(파일 없이 만든 유효 config). ⛔ 필수 — 없으면 「기본값 없음」과 「모름」이 섞인다. */
|
|
415
|
+
codeDefaults: () => unknown;
|
|
416
|
+
/** `<prodRoot>/llm-fallback.json` 원문. */
|
|
417
|
+
llmFallbackPath?: string;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
/** `buildUserConfig` 의 llm-fallback 적용 조건과 같은 판정 — provider 가 없거나 auto/none 이면 «빈» llm 절. */
|
|
421
|
+
export function isLlmSectionEmptyRaw(llm: unknown): boolean {
|
|
422
|
+
if (!llm || typeof llm !== 'object' || Array.isArray(llm)) return true;
|
|
423
|
+
const provider = (llm as Record<string, unknown>).provider;
|
|
424
|
+
return typeof provider !== 'string' || provider.trim() === '' || provider === 'auto' || provider === 'none';
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
export function computeExplain(d: DiscoveryResult, key: string, deps: ExplainDeps): ExplainReport {
|
|
428
|
+
const defaults = walk(deps.codeDefaults(), key);
|
|
429
|
+
const codeDefault = { present: defaults.present, value: displayValue(key, defaults.value, defaults.present) };
|
|
430
|
+
|
|
431
|
+
let prod: ExplainReport['prod'];
|
|
432
|
+
if (d.prod.status !== 'ok' || !d.prod.raw) {
|
|
433
|
+
prod = { configPath: d.prod.configPath, layer: 'prod-unreadable', value: '(못 읽음)', note: d.prod.error ?? d.prod.status };
|
|
434
|
+
} else {
|
|
435
|
+
const fromFile = walk(d.prod.raw, key);
|
|
436
|
+
if (fromFile.present) {
|
|
437
|
+
prod = { configPath: d.prod.configPath, layer: 'prod-file', value: displayValue(key, fromFile.value) };
|
|
438
|
+
} else {
|
|
439
|
+
let fb: { present: boolean; value?: unknown } = { present: false };
|
|
440
|
+
const fbPath = deps.llmFallbackPath ?? join(d.prod.configDir, 'llm-fallback.json');
|
|
441
|
+
// 폴백 파일은 운영 llm 절이 «비었을 때만» 합쳐진다 — 그 조건이 아닐 땐 출처 층이 아니다.
|
|
442
|
+
if (key.startsWith('llm.') && isLlmSectionEmptyRaw(d.prod.raw.llm) && existsSync(fbPath)) {
|
|
443
|
+
try { fb = walk(JSON.parse(readFileSync(fbPath, 'utf-8')), key.slice(4)); } catch { /* 못 읽으면 폴백 층 아님 */ }
|
|
444
|
+
}
|
|
445
|
+
if (fb.present) {
|
|
446
|
+
prod = { configPath: fbPath, layer: 'llm-fallback', value: displayValue(key, fb.value), note: '운영 llm 절이 비어 폴백 파일이 쓰인다' };
|
|
447
|
+
} else if (defaults.present) {
|
|
448
|
+
prod = { configPath: d.prod.configPath, layer: 'code-default', value: codeDefault.value, note: '파일에 없음 — 코드 기본값' };
|
|
449
|
+
} else {
|
|
450
|
+
prod = { configPath: d.prod.configPath, layer: 'unset', value: '(없음)' };
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
const prodRaw = d.prod.raw ? walk(d.prod.raw, key) : { present: false };
|
|
456
|
+
const prodSig = prodRaw.present ? stableStringify(prodRaw.value) : undefined;
|
|
457
|
+
const groups = new Map<string, DriftValueGroup & { sameAsProd: boolean }>();
|
|
458
|
+
const compared = d.derived.filter((u) => u.status === 'ok');
|
|
459
|
+
const comparable = d.prod.status === 'ok' && !!d.prod.raw;
|
|
460
|
+
for (const u of comparable ? compared : []) {
|
|
461
|
+
const w = walk(u.raw, key);
|
|
462
|
+
const sig = w.present ? stableStringify(w.value) : undefined;
|
|
463
|
+
const gk = w.present ? `v:${sig}` : 'absent';
|
|
464
|
+
const sameAsProd = w.present === prodRaw.present && sig === prodSig;
|
|
465
|
+
const g = groups.get(gk) ?? {
|
|
466
|
+
value: w.present
|
|
467
|
+
? (isSecretPath(key) ? `${MASK}#${groups.size + 1}` : displayValue(key, w.value))
|
|
468
|
+
: codeDefault.present ? `(없음 → 코드 기본값 ${codeDefault.value})` : '(없음 · 코드 기본값도 없음)',
|
|
469
|
+
present: w.present, count: 0, examples: [], sameAsProd,
|
|
470
|
+
};
|
|
471
|
+
g.count++;
|
|
472
|
+
if (g.examples.length < EXAMPLES) g.examples.push(u.configPath);
|
|
473
|
+
groups.set(gk, g);
|
|
474
|
+
}
|
|
475
|
+
const unreadable = d.derived.filter((u) => u.status === 'unreadable');
|
|
476
|
+
return {
|
|
477
|
+
key, prod, codeDefault, comparable,
|
|
478
|
+
universes: {
|
|
479
|
+
total: d.derived.length,
|
|
480
|
+
compared: compared.length,
|
|
481
|
+
noConfig: d.derived.filter((u) => u.status === 'no-config').length,
|
|
482
|
+
unreadable: unreadable.length,
|
|
483
|
+
gone: d.derived.filter((u) => u.status === 'gone').length,
|
|
484
|
+
},
|
|
485
|
+
registry: d.registry,
|
|
486
|
+
treeScanRoots: d.treeScanRoots,
|
|
487
|
+
groups: [...groups.values()].sort((a, b) => Number(a.sameAsProd) - Number(b.sameAsProd) || b.count - a.count),
|
|
488
|
+
unreadable: unreadable.map((u) => ({ configPath: u.configPath, error: u.error ?? '알 수 없음' })),
|
|
489
|
+
};
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
export function renderExplain(r: ExplainReport): string {
|
|
493
|
+
const lines: string[] = [];
|
|
494
|
+
lines.push(`config explain ${r.key}`);
|
|
495
|
+
lines.push(` 운영 값: ${r.prod.value}`);
|
|
496
|
+
lines.push(` 출처 층: ${r.prod.layer} (${shortHome(r.prod.configPath)})${r.prod.note ? ` — ${r.prod.note}` : ''}`);
|
|
497
|
+
lines.push(` 코드 기본값: ${r.codeDefault.value}`);
|
|
498
|
+
lines.push(` ${universeSummaryLine({ universes: r.universes, registry: r.registry, treeScanRoots: r.treeScanRoots } as DriftReport)}`);
|
|
499
|
+
if (r.comparable === false) {
|
|
500
|
+
lines.push(' ⛔ 운영 config 를 못 읽어 파생 우주와 비교하지 않았다(「다름」도 「같음」도 아니다)');
|
|
501
|
+
} else {
|
|
502
|
+
const differing = r.groups.filter((g) => !g.sameAsProd).reduce((s, g) => s + g.count, 0);
|
|
503
|
+
lines.push(` 운영과 다른 우주 ${differing}/${r.universes.compared}`);
|
|
504
|
+
}
|
|
505
|
+
for (const g of r.groups) {
|
|
506
|
+
lines.push(` ${g.sameAsProd ? '= 운영과 같음' : '≠'} ${g.value} × ${g.count} 예: ${g.examples.map(shortHome).join(', ')}`);
|
|
507
|
+
}
|
|
508
|
+
if (r.unreadable.length) {
|
|
509
|
+
lines.push(' 못 읽음 (같다고 보지 않음):');
|
|
510
|
+
for (const u of r.unreadable.slice(0, 20)) lines.push(` ${shortHome(u.configPath)} — ${u.error}`);
|
|
511
|
+
}
|
|
512
|
+
return lines.join('\n');
|
|
513
|
+
}
|
|
@@ -775,7 +775,9 @@ export const KNOWN_LOG_EVENT_NAMES: ReadonlySet<string> = new Set([
|
|
|
775
775
|
"manifest-frame-fail",
|
|
776
776
|
"manual-goal-evidence-requirement",
|
|
777
777
|
"map",
|
|
778
|
+
"mark-failed",
|
|
778
779
|
"mark-read",
|
|
780
|
+
"marked",
|
|
779
781
|
"marker-readback",
|
|
780
782
|
"marker-readback-exhausted",
|
|
781
783
|
"materialized",
|
|
@@ -1217,6 +1219,7 @@ export const KNOWN_LOG_EVENT_NAMES: ReadonlySet<string> = new Set([
|
|
|
1217
1219
|
"self-orchestrate-exposure",
|
|
1218
1220
|
"self-turn",
|
|
1219
1221
|
"send",
|
|
1222
|
+
"send-failed",
|
|
1220
1223
|
"send.encoder.error",
|
|
1221
1224
|
"send.error",
|
|
1222
1225
|
"send.pcm.error",
|
|
@@ -1249,6 +1252,7 @@ export const KNOWN_LOG_EVENT_NAMES: ReadonlySet<string> = new Set([
|
|
|
1249
1252
|
"showMenu.rejected",
|
|
1250
1253
|
"showMenu.resolved",
|
|
1251
1254
|
"shutdown",
|
|
1255
|
+
"sibling-resync-on-merge-failed",
|
|
1252
1256
|
"sigint",
|
|
1253
1257
|
"signal-cleared",
|
|
1254
1258
|
"signal-consumed-cleared",
|
package/src/cli/ops-cli.ts
CHANGED
|
@@ -213,6 +213,17 @@ opsCmd.command('status').description('현재 상태 종합(미션·태스크·
|
|
|
213
213
|
}
|
|
214
214
|
return o.allInstances ? runOpsFleet(o.json === true, o.includeTest === true) : runOps('snapshot', o);
|
|
215
215
|
});
|
|
216
|
+
// OPS-OVERVIEW-CLI (RFC-ops-overview-flow-health-cli-2026-10-08) — 판정 먼저 · 이유 사슬 · 절. 못 잼 = null(0 아님).
|
|
217
|
+
opsCmd.command('overview').description('흐름 한 장 — 저작→구현→착지가 흐르나(판정 먼저·이유 사슬) · 판·일꾼·기계·착지·루프 · 못 잼은 null · exit 0=흐름 정상/저하 3=그 밖 1=수집 실패')
|
|
218
|
+
.option('--json', '칸 꼴 그대로 ⊕ 머리 {verdict, reasons, nextActions, meta.elapsedMs}')
|
|
219
|
+
.option('--watch [seconds]', '주기 재조회(기본 30초)')
|
|
220
|
+
.option('--since <window>', '착지 창(<n>m|h|d · 기본 1h)', '1h')
|
|
221
|
+
.option('--owner <owner>', '일꾼 절 거르기 — seat:TC · TC · unassigned · all')
|
|
222
|
+
.option('--version <v>', '판(기본: 다음 컷)')
|
|
223
|
+
.action(async (o: { json?: boolean; watch?: string | boolean; since?: string; owner?: string; version?: string }) => {
|
|
224
|
+
const { runOpsOverview } = await import('./ops-overview-cli.js');
|
|
225
|
+
process.exitCode = await runOpsOverview(o);
|
|
226
|
+
});
|
|
216
227
|
opsCmd.command('health').description('이상 판정만(blocked·errored·stale)').option('--json')
|
|
217
228
|
.action((o: OpsOpts) => runOps('health', o));
|
|
218
229
|
opsCmd.command('timeline').description('상태 전이 최근순 통합')
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { debug } from '../debug/log.js';
|
|
2
|
+
import { collectOverview, parseSince } from '../ops/overview-collect.js';
|
|
3
|
+
import { computeVerdict, renderOverview, unmeasuredCells, verdictExitCode, VERDICT_LABEL } from '../ops/overview.js';
|
|
4
|
+
import { writeStdoutJson } from './stdout-json.js';
|
|
5
|
+
|
|
6
|
+
export interface OpsOverviewOptions { json?: boolean; watch?: string | boolean; since?: string; owner?: string; version?: string }
|
|
7
|
+
|
|
8
|
+
/** 한 번 모아서 내고 exit code 를 돌려준다(RFC §A4: flowing·degraded 0 · 그 밖 3 · 수집 실패 1). */
|
|
9
|
+
async function once(o: OpsOverviewOptions, previous: string | null): Promise<{ code: number; verdict: string | null }> {
|
|
10
|
+
const started = performance.now();
|
|
11
|
+
let since: { ms: number; label: string };
|
|
12
|
+
try { since = parseSince(o.since); } catch (error) { console.error(String((error as Error).message)); return { code: 1, verdict: null }; }
|
|
13
|
+
try {
|
|
14
|
+
const snapshot = await collectOverview({ sinceMs: since.ms, sinceLabel: since.label, ...(o.owner ? { owner: o.owner } : {}), ...(o.version ? { version: o.version } : {}) });
|
|
15
|
+
const result = computeVerdict(snapshot);
|
|
16
|
+
const elapsedMs = Math.round(performance.now() - started);
|
|
17
|
+
const unmeasured = unmeasuredCells(snapshot);
|
|
18
|
+
debug.log('ops.overview', 'computed', { verdict: result.verdict, elapsedMs, unmeasured });
|
|
19
|
+
debug.flush();
|
|
20
|
+
if (o.json) {
|
|
21
|
+
await writeStdoutJson(JSON.stringify({ verdict: result.verdict, verdictLabel: VERDICT_LABEL[result.verdict], reasons: result.reasons, nextActions: result.nextActions,
|
|
22
|
+
meta: { elapsedMs, source: 'local', unmeasured }, ...snapshot }, null, 2) + '\n');
|
|
23
|
+
} else {
|
|
24
|
+
const text = renderOverview(snapshot, result, { elapsedMs });
|
|
25
|
+
if (previous !== null && previous !== result.verdict) console.log(`🔔 판정 바뀜: ${previous} → ${result.verdict}`);
|
|
26
|
+
console.log(text);
|
|
27
|
+
}
|
|
28
|
+
return { code: verdictExitCode(result.verdict), verdict: result.verdict };
|
|
29
|
+
} catch (error) {
|
|
30
|
+
debug.log('ops.overview', 'failed', { reason: String(error) });
|
|
31
|
+
debug.flush();
|
|
32
|
+
console.error(`ops overview 수집 실패: ${String(error)}`);
|
|
33
|
+
return { code: 1, verdict: null };
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export async function runOpsOverview(o: OpsOverviewOptions): Promise<number> {
|
|
38
|
+
// 독립 CLI 프로세스는 데몬 StoreSink 를 물려받지 않는다 — 등록 없이는 debug.log 가 logs.db 에 안 닿는다(관측 안 한 것).
|
|
39
|
+
try { await (await import('../domains/standalone-log-sink.js')).registerStandaloneLogSink('cli'); } catch { /* fail-open */ }
|
|
40
|
+
if (o.watch === undefined || o.watch === false) return (await once(o, null)).code;
|
|
41
|
+
const seconds = o.watch === true ? 30 : Number(o.watch);
|
|
42
|
+
if (!Number.isFinite(seconds) || seconds < 5) { console.error('--watch 는 5초 이상'); return 1; }
|
|
43
|
+
let previous: string | null = null;
|
|
44
|
+
for (;;) {
|
|
45
|
+
if (!o.json) process.stdout.write('\x1b[2J\x1b[H');
|
|
46
|
+
const r = await once(o, previous);
|
|
47
|
+
previous = r.verdict ?? previous;
|
|
48
|
+
await new Promise((res) => setTimeout(res, seconds * 1000));
|
|
49
|
+
}
|
|
50
|
+
}
|
package/src/cli/pod-cli.ts
CHANGED
|
@@ -151,6 +151,7 @@ export function registerPodCommands(program: Command, deps: PodCliDeps = {}): vo
|
|
|
151
151
|
try {
|
|
152
152
|
const result = await run({
|
|
153
153
|
command: commandArgs,
|
|
154
|
+
returnLogs: true,
|
|
154
155
|
...(opts.pool ? { pool: opts.pool } : {}),
|
|
155
156
|
...(skills.length ? { skills } : {}),
|
|
156
157
|
...(opts.llm === 'grok' ? { llm: 'grok' as const } : {}),
|
package/src/cli/pr-cli.ts
CHANGED
|
@@ -22,6 +22,7 @@ import { exportLeakCheck, publicExportChangedFiles, type ExportLeakCheck } from
|
|
|
22
22
|
import { exportImportCheck, type ExportImportCheck } from '../../scripts/ci-public-export-import-gate.js';
|
|
23
23
|
import { checkCommands, extractElanousCommands, type Finding as DocsCliFinding } from '../../scripts/docs-cli-check.js';
|
|
24
24
|
import { runTestInterferenceGate } from '../../scripts/ci-test-interference-gate.js';
|
|
25
|
+
import { landGlobalChecks, runLandGlobalChecksGate } from '../../scripts/land-global-checks.js';
|
|
25
26
|
import { isGoalDocumentFileName } from '../self-implement/goal-document.js';
|
|
26
27
|
import { queryFederatedUnfinishedRunLedgers, type FederatedUnfinishedRunLedgerEntry, type FederatedUnfinishedRunLedgerQuery } from '../self-implement/run-ledger.js';
|
|
27
28
|
import { queryRunningRuns, type RunningRunsResult } from '../self-implement/running-runs.js';
|
|
@@ -120,6 +121,8 @@ export interface PrLandDeps {
|
|
|
120
121
|
runDocsCliCheck?: (docFiles: readonly string[]) => DocsCliFinding[];
|
|
121
122
|
/** 변경 시험 파일 간 간섭 검사 심(시험 주입용). */
|
|
122
123
|
runTestInterferenceGate?: (out: { log: (message: string) => void; error: (message: string) => void }, changedFiles: readonly string[]) => Promise<number>;
|
|
124
|
+
/** 전역 검사 게이트 심(시험 주입용) — 바뀐 파일 꼴로 끌려온 전역 시험(scripts/land-global-checks.ts)을 돌린다. */
|
|
125
|
+
runLandGlobalChecks?: (out: { log: (message: string) => void; error: (message: string) => void }, changedFiles: readonly string[]) => boolean;
|
|
123
126
|
/** 안드로이드 단위 시험 게이트 심(시험 주입용). */
|
|
124
127
|
runAndroidGate?: (out: { log: (message: string) => void; error: (message: string) => void }, changedFiles: readonly string[]) => boolean;
|
|
125
128
|
/** iOS 시험 게이트 심(시험 주입용). */
|
|
@@ -1471,6 +1474,23 @@ export async function runPrLand(opts: PrLandOpts = {}, deps: PrLandDeps = {}): P
|
|
|
1471
1474
|
out,
|
|
1472
1475
|
);
|
|
1473
1476
|
record('test-interference-gate', true, { measured: testInterference.measured });
|
|
1477
|
+
// LAND-GLOBAL-CHECKS — 바뀐 파일의 경로 꼴이 부르는 «전역 검사»를 끌어와 돌린다(표 = scripts/land-global-checks.ts).
|
|
1478
|
+
// 시험이 실패하면 막는다 · 러너가 못 돌았으면 「검사 못 함」으로 말하고 통과시킨다(gateVerdict).
|
|
1479
|
+
const landGlobal = gateVerdict(
|
|
1480
|
+
'land-global-checks',
|
|
1481
|
+
deps.runLandGlobalChecks
|
|
1482
|
+
? (o) => deps.runLandGlobalChecks!(o, changedPaths)
|
|
1483
|
+
: (o) => runLandGlobalChecksGate({ changedFiles: changedPaths, cwd, log: o.log, error: o.error }),
|
|
1484
|
+
out,
|
|
1485
|
+
);
|
|
1486
|
+
record('land-global-checks', landGlobal.ok, { measured: landGlobal.measured });
|
|
1487
|
+
if (!landGlobal.ok) {
|
|
1488
|
+
out.error('✗ land-global-checks: scripts/land-global-checks.ts blocked pr land — 바뀐 파일이 끌어온 전역 시험이 실패했다.');
|
|
1489
|
+
return 1;
|
|
1490
|
+
}
|
|
1491
|
+
if (landGlobal.measured && landGlobalChecks(changedPaths).length > 0) {
|
|
1492
|
+
out.log(`✓ land-global-checks: scripts/land-global-checks.ts PASS — 끌려온 전역 시험 ${landGlobalChecks(changedPaths).join(' · ')} 실패 0.`);
|
|
1493
|
+
}
|
|
1474
1494
|
|
|
1475
1495
|
const androidGate = gateVerdict(
|
|
1476
1496
|
'android-gate',
|