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
|
@@ -1,15 +1,33 @@
|
|
|
1
|
-
import { mkdtempSync, mkdirSync, readFileSync, readdirSync, rmSync, writeFileSync } from 'node:fs';
|
|
1
|
+
import { existsSync, mkdtempSync, mkdirSync, readFileSync, readdirSync, renameSync, rmSync, statSync, writeFileSync } from 'node:fs';
|
|
2
2
|
import { tmpdir } from 'node:os';
|
|
3
3
|
import { join } from 'node:path';
|
|
4
4
|
import { parse as parseYaml } from 'yaml';
|
|
5
5
|
import { elanousStateRoot } from '../../autopilot/state-paths.js';
|
|
6
|
+
import { debug } from '../../debug/log.js';
|
|
6
7
|
import { defaultGraphsDir, loadGraphTemplatesFrom } from '../../self-implement/graph-templates.js';
|
|
7
8
|
import { loadGraphTemplates } from '../../self-implement/graph-yaml.js';
|
|
9
|
+
import { validateGraphYaml } from './graph-kinds.js';
|
|
10
|
+
import { parseWizardSteps, readWizardSteps, withWizardSteps } from '../../graph-wizard/saved-steps.js';
|
|
11
|
+
import type { WizardNodeStep } from '../../graph-wizard/steps.js';
|
|
8
12
|
import { jsonResponse } from './json-response.js';
|
|
9
13
|
|
|
10
14
|
/** Declared graph ids only. A user string is never joined onto a filesystem path. */
|
|
11
15
|
const GRAPH_ID = /^[a-z0-9-]+$/;
|
|
12
16
|
|
|
17
|
+
/**
|
|
18
|
+
* Sidecar names a graph directory loads specially. `recipes.yaml` next to a graph is what
|
|
19
|
+
* the graph runner executes (`cmd:` recipes), so an HTTP write to `<mine>/recipes.yaml`
|
|
20
|
+
* would be a remote command path. These ids are never created, saved or reverted, and
|
|
21
|
+
* these files are never indexed as graphs.
|
|
22
|
+
*/
|
|
23
|
+
export const RESERVED_GRAPH_IDS: ReadonlySet<string> = new Set(['recipes', 'overlay', 'overlays']);
|
|
24
|
+
|
|
25
|
+
function reserved(id: string): Response | null {
|
|
26
|
+
if (!RESERVED_GRAPH_IDS.has(id)) return null;
|
|
27
|
+
debug.log('nexus.graphs', 'rejected-reserved', { id });
|
|
28
|
+
return jsonResponse({ error: 'reserved-id', id }, 400);
|
|
29
|
+
}
|
|
30
|
+
|
|
13
31
|
export interface GraphCatalogEntry {
|
|
14
32
|
id: string;
|
|
15
33
|
source: 'core' | 'mine';
|
|
@@ -45,6 +63,7 @@ function readDirYaml(dir: string): string[] {
|
|
|
45
63
|
function indexByDeclaredId(dir: string): Map<string, { file: string; text: string }> {
|
|
46
64
|
const found = new Map<string, { file: string; text: string }>();
|
|
47
65
|
for (const file of readDirYaml(dir)) {
|
|
66
|
+
if (RESERVED_GRAPH_IDS.has(file.replace(/\.ya?ml$/, ''))) continue;
|
|
48
67
|
let text: string;
|
|
49
68
|
try { text = readFileSync(join(dir, file), 'utf8'); }
|
|
50
69
|
catch { continue; }
|
|
@@ -92,7 +111,12 @@ function detailOf(id: string, source: 'core' | 'mine', editable: boolean, dir: s
|
|
|
92
111
|
editable,
|
|
93
112
|
entry_node: template.entryNode,
|
|
94
113
|
terminal_nodes: template.terminalNodes,
|
|
95
|
-
|
|
114
|
+
// GRAPH-GROUPS — a node's `group:` tag (when the loader carries it) rides along so the PWA can box/fold units.
|
|
115
|
+
nodes: template.nodes.map((node) => {
|
|
116
|
+
const { nodeId, kind, recipe, maxVisits } = node;
|
|
117
|
+
const group = (node as { group?: unknown }).group;
|
|
118
|
+
return { node_id: nodeId, kind, recipe, max_visits: maxVisits, ...(typeof group === 'string' && group.trim() ? { group: group.trim() } : {}) };
|
|
119
|
+
}),
|
|
96
120
|
edges: spec.edges.map(({ from, to, on, map }) => ({
|
|
97
121
|
from,
|
|
98
122
|
...(to === undefined ? {} : { to }),
|
|
@@ -125,6 +149,21 @@ function validateInTemp(text: string, expectedId: string): { ok: true } | { ok:
|
|
|
125
149
|
}
|
|
126
150
|
}
|
|
127
151
|
|
|
152
|
+
/** Same validator as POST /v1/graphs/validate. Invalid → 422; declared graph_id ≠ id → 400. Null = ok. */
|
|
153
|
+
function rejectGraphYaml(text: string, id: string, version?: string): Response | null {
|
|
154
|
+
const checked = validateGraphYaml('harness', text);
|
|
155
|
+
if (!checked.ok) {
|
|
156
|
+
debug.log('nexus.graphs', 'rejected-invalid', { id, ...(version ? { version } : {}), issues: checked.errors });
|
|
157
|
+
return jsonResponse({ error: 'invalid-graph', errors: checked.errors, ignoredKeys: checked.ignoredKeys }, 422);
|
|
158
|
+
}
|
|
159
|
+
if (checked.graphId !== id) {
|
|
160
|
+
const errors = [{ path: `${id}/graph_id`, message: 'graph_id 가 요청 id 와 다르다' }];
|
|
161
|
+
debug.log('nexus.graphs', 'rejected-invalid', { id, issues: errors });
|
|
162
|
+
return jsonResponse({ error: 'graph-id-mismatch', errors }, 400);
|
|
163
|
+
}
|
|
164
|
+
return null;
|
|
165
|
+
}
|
|
166
|
+
|
|
128
167
|
/** Core graphs are read from the same YAML loader as the harness, never rewritten by this API. */
|
|
129
168
|
export function handleGraphsGet(pathname: string, dir = defaultGraphsDir(), deps: GraphsApiDeps = {}): Response {
|
|
130
169
|
const coreDir = deps.coreDir ?? dir;
|
|
@@ -146,7 +185,8 @@ export function handleGraphsGet(pathname: string, dir = defaultGraphsDir(), deps
|
|
|
146
185
|
const coreHit = core.ok ? indexByDeclaredId(coreDir).get(id) : undefined;
|
|
147
186
|
const hit = mineHit ?? coreHit;
|
|
148
187
|
if (!hit) return jsonResponse({ error: 'not-found', id }, 404);
|
|
149
|
-
|
|
188
|
+
const steps = mineHit ? readWizardSteps(mineDir, id, hit.text) : null;
|
|
189
|
+
return jsonResponse({ id, source: mineHit ? 'mine' : 'core', editable: Boolean(mineHit), yaml: hit.text, ...(steps ? { steps } : {}) });
|
|
150
190
|
}
|
|
151
191
|
|
|
152
192
|
const match = /^\/v1\/graphs\/([^/]+)$/.exec(pathname);
|
|
@@ -168,6 +208,8 @@ export async function handleGraphsPut(pathname: string, req: Request, deps: Grap
|
|
|
168
208
|
if (!match) return jsonResponse({ error: 'not-found' }, 404);
|
|
169
209
|
const id = resolveId(match[1]!);
|
|
170
210
|
if (!id) return jsonResponse({ error: 'bad_request', reason: 'invalid graph id' }, 400);
|
|
211
|
+
const reservedId = reserved(id);
|
|
212
|
+
if (reservedId) return reservedId;
|
|
171
213
|
const coreDir = deps.coreDir ?? defaultGraphsDir();
|
|
172
214
|
const mineDir = deps.mineDir ?? mineGraphsDir();
|
|
173
215
|
const core = coreCatalog(coreDir);
|
|
@@ -175,15 +217,179 @@ export async function handleGraphsPut(pathname: string, req: Request, deps: Grap
|
|
|
175
217
|
const mineOwns = indexByDeclaredId(mineDir).has(id);
|
|
176
218
|
if (coreOwns && !mineOwns) return jsonResponse({ error: 'core-read-only', id }, 403);
|
|
177
219
|
if (!mineOwns) return jsonResponse({ error: 'not-found', id }, 404);
|
|
178
|
-
let body: { yaml?: unknown };
|
|
179
|
-
try { body = await req.json() as { yaml?: unknown }; }
|
|
220
|
+
let body: { yaml?: unknown; steps?: unknown };
|
|
221
|
+
try { body = await req.json() as { yaml?: unknown; steps?: unknown }; }
|
|
180
222
|
catch { return jsonResponse({ error: 'bad_request', reason: 'json body required' }, 400); }
|
|
181
|
-
if (typeof body
|
|
182
|
-
const
|
|
183
|
-
if (
|
|
223
|
+
if (typeof body?.yaml !== 'string') return jsonResponse({ error: 'bad_request', reason: 'yaml string required' }, 400);
|
|
224
|
+
const rejected = rejectGraphYaml(body.yaml, id);
|
|
225
|
+
if (rejected) return rejected;
|
|
226
|
+
const steps = stepsOf(body.steps, body.yaml);
|
|
227
|
+
if (steps instanceof Response) return steps;
|
|
184
228
|
const existing = indexByDeclaredId(mineDir).get(id)!;
|
|
185
|
-
|
|
186
|
-
return jsonResponse({
|
|
229
|
+
// Writes go only to <mine>/<id>.yaml; a hand-placed file under another name is not edited over HTTP.
|
|
230
|
+
if (existing.file !== `${id}.yaml`) return jsonResponse({ error: 'file-name-mismatch', id, file: existing.file }, 409);
|
|
231
|
+
let written: { version: string; previous: string };
|
|
232
|
+
try { written = withWizardSteps(mineDir, id, steps, () => writeWithVersion(mineDir, id, existing, body.yaml as string)); }
|
|
233
|
+
catch (error) { return saveFailed(id, error); }
|
|
234
|
+
const { version, previous } = written;
|
|
235
|
+
debug.log('nexus.graphs', 'saved', { id, version, previous, steps: steps ? Object.keys(steps).length : 0 });
|
|
236
|
+
return jsonResponse({ id, source: 'mine', editable: true, saved: true, version, previous });
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
const VERSION_ID = /^\d{4}-\d{2}-\d{2}T\d{2}-\d{2}-\d{2}-\d{3}Z(?:-\d{4})?$/;
|
|
240
|
+
export const GRAPH_VERSION_CAP = 20;
|
|
241
|
+
|
|
242
|
+
function versionsDir(mineDir: string, id: string): string {
|
|
243
|
+
return join(mineDir, '.versions', id);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/** Version ids are ISO timestamps (`:`/`.` → `-`), so name order is save order. */
|
|
247
|
+
export function listGraphVersions(mineDir: string, id: string): Array<{ version: string; savedAt: string; bytes: number }> {
|
|
248
|
+
const dir = versionsDir(mineDir, id);
|
|
249
|
+
// Only `.yaml` is ever written, read and reverted — a stray `.yml` is not a version.
|
|
250
|
+
return readDirYaml(dir)
|
|
251
|
+
.filter((file) => file.endsWith('.yaml'))
|
|
252
|
+
.map((file) => file.replace(/\.yaml$/, ''))
|
|
253
|
+
.filter((version) => VERSION_ID.test(version))
|
|
254
|
+
.sort()
|
|
255
|
+
.map((version) => {
|
|
256
|
+
const iso = /^(\d{4}-\d{2}-\d{2})T(\d{2})-(\d{2})-(\d{2})-(\d{3})Z/.exec(version);
|
|
257
|
+
let bytes = 0;
|
|
258
|
+
try { bytes = statSync(join(dir, `${version}.yaml`)).size; } catch { /* listed above; raced away */ }
|
|
259
|
+
return { version, savedAt: iso ? `${iso[1]}T${iso[2]}:${iso[3]}:${iso[4]}.${iso[5]}Z` : version, bytes };
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
function recordVersion(mineDir: string, id: string, text: string): string {
|
|
264
|
+
const dir = versionsDir(mineDir, id);
|
|
265
|
+
mkdirSync(dir, { recursive: true });
|
|
266
|
+
const base = new Date().toISOString().replace(/[:.]/g, '-');
|
|
267
|
+
let version = base;
|
|
268
|
+
for (let n = 1; existsSync(join(dir, `${version}.yaml`)); n += 1) version = `${base}-${String(n).padStart(4, '0')}`;
|
|
269
|
+
writeFileSync(join(dir, `${version}.yaml`), text);
|
|
270
|
+
const all = listGraphVersions(mineDir, id);
|
|
271
|
+
for (const old of all.slice(0, Math.max(0, all.length - GRAPH_VERSION_CAP))) {
|
|
272
|
+
rmSync(join(dir, `${old.version}.yaml`), { force: true });
|
|
273
|
+
}
|
|
274
|
+
return version;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* Every saved state is a version. A graph with no history yet (e.g. a fresh clone)
|
|
279
|
+
* first records its current bytes, so `previous` always points at restorable bytes.
|
|
280
|
+
*/
|
|
281
|
+
function writeWithVersion(mineDir: string, id: string, existing: { file: string; text: string }, text: string): { version: string; previous: string } {
|
|
282
|
+
const history = listGraphVersions(mineDir, id);
|
|
283
|
+
const previous = history.at(-1)?.version ?? recordVersion(mineDir, id, existing.text);
|
|
284
|
+
// Version first, then an atomic replace of the current file: a crash in between leaves
|
|
285
|
+
// the old current bytes intact (they are `previous`), never bytes that no version holds.
|
|
286
|
+
const version = recordVersion(mineDir, id, text);
|
|
287
|
+
const target = join(mineDir, `${id}.yaml`);
|
|
288
|
+
const temp = join(mineDir, `.${id}.yaml.${process.pid}.tmp`);
|
|
289
|
+
writeFileSync(temp, text);
|
|
290
|
+
renameSync(temp, target);
|
|
291
|
+
return { version, previous };
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
function mineOwnership(id: string, deps: GraphsApiDeps): { mineDir: string; existing: { file: string; text: string } } | Response {
|
|
295
|
+
const reservedId = reserved(id);
|
|
296
|
+
if (reservedId) return reservedId;
|
|
297
|
+
const coreDir = deps.coreDir ?? defaultGraphsDir();
|
|
298
|
+
const mineDir = deps.mineDir ?? mineGraphsDir();
|
|
299
|
+
const existing = indexByDeclaredId(mineDir).get(id);
|
|
300
|
+
if (!existing) {
|
|
301
|
+
const core = coreCatalog(coreDir);
|
|
302
|
+
if (core.ok && indexByDeclaredId(coreDir).has(id)) return jsonResponse({ error: 'core-read-only', id }, 403);
|
|
303
|
+
return jsonResponse({ error: 'not-found', id }, 404);
|
|
304
|
+
}
|
|
305
|
+
return { mineDir, existing };
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
/** GET /v1/graphs/<id>/versions — only «mine» graphs have history. */
|
|
309
|
+
export function handleGraphVersionsGet(pathname: string, deps: GraphsApiDeps = {}): Response {
|
|
310
|
+
const match = /^\/v1\/graphs\/([^/]+)\/versions$/.exec(pathname);
|
|
311
|
+
if (!match) return jsonResponse({ error: 'not-found' }, 404);
|
|
312
|
+
const id = resolveId(match[1]!);
|
|
313
|
+
if (!id) return jsonResponse({ error: 'bad_request', reason: 'invalid graph id' }, 400);
|
|
314
|
+
const owned = mineOwnership(id, deps);
|
|
315
|
+
if (owned instanceof Response) return owned;
|
|
316
|
+
return jsonResponse({ id, versions: listGraphVersions(owned.mineDir, id) });
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
/** POST /v1/graphs/<id>/revert {version} — restores that version; the restore is itself a new version. */
|
|
320
|
+
export async function handleGraphsRevert(pathname: string, req: Request, deps: GraphsApiDeps = {}): Promise<Response> {
|
|
321
|
+
const match = /^\/v1\/graphs\/([^/]+)\/revert$/.exec(pathname);
|
|
322
|
+
if (!match) return jsonResponse({ error: 'not-found' }, 404);
|
|
323
|
+
const id = resolveId(match[1]!);
|
|
324
|
+
if (!id) return jsonResponse({ error: 'bad_request', reason: 'invalid graph id' }, 400);
|
|
325
|
+
const owned = mineOwnership(id, deps);
|
|
326
|
+
if (owned instanceof Response) return owned;
|
|
327
|
+
if (owned.existing.file !== `${id}.yaml`) return jsonResponse({ error: 'file-name-mismatch', id, file: owned.existing.file }, 409);
|
|
328
|
+
let body: { version?: unknown };
|
|
329
|
+
try { body = await req.json() as { version?: unknown }; }
|
|
330
|
+
catch { return jsonResponse({ error: 'bad_request', reason: 'json body required' }, 400); }
|
|
331
|
+
if (typeof body?.version !== 'string' || !VERSION_ID.test(body.version)) {
|
|
332
|
+
return jsonResponse({ error: 'bad_request', reason: 'version string required' }, 400);
|
|
333
|
+
}
|
|
334
|
+
const target = body.version;
|
|
335
|
+
// Only a listed version id is ever joined onto a path.
|
|
336
|
+
if (!listGraphVersions(owned.mineDir, id).some((entry) => entry.version === target)) {
|
|
337
|
+
return jsonResponse({ error: 'version-not-found', id, version: target }, 404);
|
|
338
|
+
}
|
|
339
|
+
const text = readFileSync(join(versionsDir(owned.mineDir, id), `${target}.yaml`), 'utf8');
|
|
340
|
+
const rejected = rejectGraphYaml(text, id, target);
|
|
341
|
+
if (rejected) return rejected;
|
|
342
|
+
const { version, previous } = writeWithVersion(owned.mineDir, id, owned.existing, text);
|
|
343
|
+
debug.log('nexus.graphs', 'reverted', { id, version, previous, restoredFrom: target });
|
|
344
|
+
return jsonResponse({ id, source: 'mine', editable: true, reverted: true, version, previous, restoredFrom: target });
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
/** GRAPH-WIZARD-SAVE-RECIPES — optional `steps` on save (the wizard's node → library step map). Absent = keep
|
|
348
|
+
* whatever sidecar the graph has; present = validated against the step library and stored as a sidecar. The
|
|
349
|
+
* run builds commands from it server-side (src/graph-wizard/saved-steps.ts) — no client command text is kept. */
|
|
350
|
+
function stepsOf(raw: unknown, yaml: string): Record<string, WizardNodeStep> | null | Response {
|
|
351
|
+
// Only an absent field means «keep what is there»; an explicit null or any other non-object is a bad request.
|
|
352
|
+
if (raw === undefined) return null;
|
|
353
|
+
const parsed = parseWizardSteps(raw, yaml);
|
|
354
|
+
if (!parsed.ok) return jsonResponse({ error: 'bad_request', reason: parsed.reason }, 400);
|
|
355
|
+
return parsed.steps;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
function saveFailed(id: string, error: unknown): Response {
|
|
359
|
+
const reason = error instanceof Error ? error.message.slice(0, 200) : String(error);
|
|
360
|
+
debug.log('nexus.graphs', 'save-failed', { id, reason }, { level: 'warn' });
|
|
361
|
+
return jsonResponse({ error: 'save-failed', id, reason }, 500);
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
/** POST /v1/graphs {id, yaml} — a brand-new «mine» graph (blank canvas → save). Records version v1. */
|
|
365
|
+
export async function handleGraphsCreate(req: Request, deps: GraphsApiDeps = {}): Promise<Response> {
|
|
366
|
+
let body: { id?: unknown; yaml?: unknown; steps?: unknown };
|
|
367
|
+
try { body = await req.json() as { id?: unknown; yaml?: unknown; steps?: unknown }; }
|
|
368
|
+
catch { return jsonResponse({ error: 'bad_request', reason: 'json body required' }, 400); }
|
|
369
|
+
const id = typeof body?.id === 'string' ? resolveId(body.id) : null;
|
|
370
|
+
if (!id) return jsonResponse({ error: 'bad_request', reason: 'id must match [a-z0-9-]' }, 400);
|
|
371
|
+
const reservedId = reserved(id);
|
|
372
|
+
if (reservedId) return reservedId;
|
|
373
|
+
if (typeof body.yaml !== 'string') return jsonResponse({ error: 'bad_request', reason: 'yaml string required' }, 400);
|
|
374
|
+
const coreDir = deps.coreDir ?? defaultGraphsDir();
|
|
375
|
+
const mineDir = deps.mineDir ?? mineGraphsDir();
|
|
376
|
+
if (indexByDeclaredId(coreDir).has(id)) return jsonResponse({ error: 'core-read-only', id }, 403);
|
|
377
|
+
if (indexByDeclaredId(mineDir).has(id) || existsSync(join(mineDir, `${id}.yaml`))) return jsonResponse({ error: 'exists', id }, 409);
|
|
378
|
+
const rejected = rejectGraphYaml(body.yaml, id);
|
|
379
|
+
if (rejected) return rejected;
|
|
380
|
+
const steps = stepsOf(body.steps, body.yaml);
|
|
381
|
+
if (steps instanceof Response) return steps;
|
|
382
|
+
let version: string;
|
|
383
|
+
try {
|
|
384
|
+
version = withWizardSteps(mineDir, id, steps, () => {
|
|
385
|
+
mkdirSync(mineDir, { recursive: true });
|
|
386
|
+
writeFileSync(join(mineDir, `${id}.yaml`), body.yaml as string, { flag: 'wx' });
|
|
387
|
+
try { return recordVersion(mineDir, id, body.yaml as string); }
|
|
388
|
+
catch (error) { rmSync(join(mineDir, `${id}.yaml`), { force: true }); throw error; }
|
|
389
|
+
});
|
|
390
|
+
} catch (error) { return saveFailed(id, error); }
|
|
391
|
+
debug.log('nexus.graphs', 'saved', { id, version, created: true, steps: steps ? Object.keys(steps).length : 0 });
|
|
392
|
+
return jsonResponse({ id, source: 'mine', editable: true, saved: true, version, previous: null }, 201);
|
|
187
393
|
}
|
|
188
394
|
|
|
189
395
|
/** Copy core bytes into «my graphs», changing only the declared graph_id. */
|
|
@@ -198,6 +404,8 @@ export async function handleGraphsClone(pathname: string, req: Request, deps: Gr
|
|
|
198
404
|
if (typeof body.newId !== 'string' || !GRAPH_ID.test(body.newId) || body.newId === id) {
|
|
199
405
|
return jsonResponse({ error: 'bad_request', reason: 'newId must match [a-z0-9-]' }, 400);
|
|
200
406
|
}
|
|
407
|
+
const reservedId = reserved(body.newId);
|
|
408
|
+
if (reservedId) return reservedId;
|
|
201
409
|
const coreDir = deps.coreDir ?? defaultGraphsDir();
|
|
202
410
|
const mineDir = deps.mineDir ?? mineGraphsDir();
|
|
203
411
|
const source = indexByDeclaredId(coreDir).get(id) ?? indexByDeclaredId(mineDir).get(id);
|
|
@@ -215,7 +423,9 @@ export async function handleGraphsClone(pathname: string, req: Request, deps: Gr
|
|
|
215
423
|
}
|
|
216
424
|
|
|
217
425
|
export async function handleGraphsMutation(pathname: string, req: Request, deps: GraphsApiDeps = {}): Promise<Response | null> {
|
|
426
|
+
if (req.method === 'POST' && pathname === '/v1/graphs') return handleGraphsCreate(req, deps);
|
|
218
427
|
if (req.method === 'PUT' && /^\/v1\/graphs\/[^/]+\/yaml$/.test(pathname)) return handleGraphsPut(pathname, req, deps);
|
|
219
428
|
if (req.method === 'POST' && /^\/v1\/graphs\/[^/]+\/clone$/.test(pathname)) return handleGraphsClone(pathname, req, deps);
|
|
429
|
+
if (req.method === 'POST' && /^\/v1\/graphs\/[^/]+\/revert$/.test(pathname)) return handleGraphsRevert(pathname, req, deps);
|
|
220
430
|
return null;
|
|
221
431
|
}
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
// HARNESS-RUN-LIVE-GRAPH (0.2.21) — one harness run's «which node, which outcome, when» for the PWA
|
|
2
|
+
// live/replay graph page (`/live-run/?run=<runId>`).
|
|
3
|
+
//
|
|
4
|
+
// Canonical source = the run's own ledger `~/.elanous/run-ledger/<runId>.jsonl` (federated: pod ledgers synced
|
|
5
|
+
// home are found through the same directories as the running-runs query):
|
|
6
|
+
// · `pipeline-node-entry` — one per node visit (node · round · graphId)
|
|
7
|
+
// · `graph-visit-budget` — the visit count the runtime itself counted
|
|
8
|
+
// · per-node outcome events — implemented · gated · reviewed(+review-reflect) · rework-budget · pre-pr-sync ·
|
|
9
|
+
// gate.postsync · pr-opened · merge-decision
|
|
10
|
+
// · `run-status` — terminal status of the whole run
|
|
11
|
+
// Durations are derived (gap to the next node entry, or to the terminal status); nothing writes a node «end».
|
|
12
|
+
//
|
|
13
|
+
// Node ids are never hardcoded for routing: any node the graph declares and the runtime enters (including the journey
|
|
14
|
+
// nodes HARNESS-FULL-GRAPH adds — entrance, queue, author, dispatch, landing, failure side) shows up as a step. A
|
|
15
|
+
// generic `pipeline-node-exit {node, outcome, detail?}` wins over the per-node heuristics below when present.
|
|
16
|
+
//
|
|
17
|
+
// ⛔ No goal body: the title is the first line of `start.feature`, cut to 80 chars. No artifact paths, no tokens.
|
|
18
|
+
|
|
19
|
+
import { loadFederatedRunLedger, loadRunLedger, runLedgerDir, type RunLedgerEntry } from '../../self-implement/run-ledger.js';
|
|
20
|
+
import { redactSecretText } from '../../debug/log.js';
|
|
21
|
+
|
|
22
|
+
export interface TraversalStepWire {
|
|
23
|
+
node: string;
|
|
24
|
+
round: number | null;
|
|
25
|
+
/** `pass` · `fail` · null (still running). */
|
|
26
|
+
outcome: string | null;
|
|
27
|
+
at: string;
|
|
28
|
+
durationMs: number | null;
|
|
29
|
+
visit: number;
|
|
30
|
+
maxVisits: number | null;
|
|
31
|
+
detail: string | null;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface RunTraversalWire {
|
|
35
|
+
runId: string;
|
|
36
|
+
graphId: string | null;
|
|
37
|
+
title: string | null;
|
|
38
|
+
/** `running` until the ledger carries a terminal `run-status`. */
|
|
39
|
+
status: string;
|
|
40
|
+
stage: string | null;
|
|
41
|
+
startedAt: string | null;
|
|
42
|
+
endedAt: string | null;
|
|
43
|
+
prNumber: number | null;
|
|
44
|
+
substrate: string | null;
|
|
45
|
+
steps: TraversalStepWire[];
|
|
46
|
+
ledgerEvents: number;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const str = (value: unknown): string | null => typeof value === 'string' && value.trim() ? value : null;
|
|
50
|
+
const num = (value: unknown): number | null => typeof value === 'number' && Number.isFinite(value) ? value : null;
|
|
51
|
+
const ms = (iso: string | undefined | null): number => iso ? Date.parse(iso) : Number.NaN;
|
|
52
|
+
|
|
53
|
+
const TERMINAL_FAIL = new Set(['failed', 'abandoned', 'cancelled', 'error', 'aborted', 'stopped']);
|
|
54
|
+
|
|
55
|
+
/** A display title from a goal's first line: path-like tokens shrink to their basename (`a/b/c.tsx` → `c.tsx`),
|
|
56
|
+
* then the line is redacted and cut to 80 chars. Shared with the PWA picker (`apps/pwa/src/lib/live-run-view.ts`). */
|
|
57
|
+
export function displayTitle(text: string | null | undefined, max = 80): string | null {
|
|
58
|
+
const line = text?.split('\n').map((part) => part.trim()).find(Boolean) ?? '';
|
|
59
|
+
const shrunk = line.replace(/\s*\[truncated[^\]]*\]\s*$/, '').replace(/(?:~|\.{0,2})?\/?(?:[\w.@-]+\/)+([\w.@-]+)/g, '$1');
|
|
60
|
+
const cut = shrunk.length > max ? `${shrunk.slice(0, max - 1)}…` : shrunk;
|
|
61
|
+
return cut ? redactSecretText(cut) : null;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function titleOf(entries: readonly RunLedgerEntry[]): string | null {
|
|
65
|
+
return displayTitle(str(entries.find((entry) => entry.event === 'start')?.data.feature));
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/** Outcome + one-line detail of a node visit, read from the events between its entry and the next entry. */
|
|
69
|
+
function judgeVisit(node: string, window: readonly RunLedgerEntry[], lookback: readonly RunLedgerEntry[] = []): { outcome: 'pass' | 'fail' | null; detail: string | null } {
|
|
70
|
+
const last = (event: string) => [...window].reverse().find((entry) => entry.event === event)?.data;
|
|
71
|
+
// Some decisions are written just before the node they belong to is entered (merge-decision lands in the regate
|
|
72
|
+
// window, right before open-pr) — look back to the previous node entry for those.
|
|
73
|
+
const near = (event: string) => last(event) ?? [...lookback].reverse().find((entry) => entry.event === event)?.data;
|
|
74
|
+
const exit = [...window].reverse().find((entry) => entry.event === 'pipeline-node-exit' && entry.data.node === node)?.data;
|
|
75
|
+
const exitOutcome = str(exit?.outcome);
|
|
76
|
+
if (exitOutcome) {
|
|
77
|
+
const failed = TERMINAL_FAIL.has(exitOutcome) || exitOutcome === 'fail';
|
|
78
|
+
const exitDetail = str(exit?.detail)?.replace(/\s+/g, ' ').trim();
|
|
79
|
+
return { outcome: failed ? 'fail' : 'pass', detail: exitDetail ? redactSecretText(exitDetail.slice(0, 120)) : exitOutcome };
|
|
80
|
+
}
|
|
81
|
+
switch (node) {
|
|
82
|
+
case 'implement': {
|
|
83
|
+
const done = last('implemented');
|
|
84
|
+
if (!done) return { outcome: null, detail: null };
|
|
85
|
+
const evidence = last('off-diff-evidence');
|
|
86
|
+
const covered = num(evidence?.coveredEvidence); const required = num(evidence?.requiredEvidence);
|
|
87
|
+
const proof = covered !== null && required !== null ? ` · 증거 ${covered}/${required}` : '';
|
|
88
|
+
return done.ok === false ? { outcome: 'fail', detail: '구현 실패' } : { outcome: 'pass', detail: `구현 완료${proof}` };
|
|
89
|
+
}
|
|
90
|
+
case 'gate': {
|
|
91
|
+
const gated = last('gated');
|
|
92
|
+
if (!gated) return { outcome: null, detail: null };
|
|
93
|
+
return gated.passed === true ? { outcome: 'pass', detail: '게이트 통과' } : { outcome: 'fail', detail: '게이트 실패' };
|
|
94
|
+
}
|
|
95
|
+
case 'regate': {
|
|
96
|
+
const gated = last('gate.postsync');
|
|
97
|
+
if (!gated) return { outcome: null, detail: null };
|
|
98
|
+
return gated.passed === true ? { outcome: 'pass', detail: 'main 동기화 뒤 재게이트 통과' } : { outcome: 'fail', detail: '재게이트 실패' };
|
|
99
|
+
}
|
|
100
|
+
case 'review': {
|
|
101
|
+
const review = last('reviewed');
|
|
102
|
+
if (!review) return { outcome: null, detail: null };
|
|
103
|
+
const verdict = str(review.verdict) ?? '?';
|
|
104
|
+
const mustFix = num(review.mustFix);
|
|
105
|
+
const converged = window.some((entry) => entry.event === 'review-reflect' && entry.data.converged === true);
|
|
106
|
+
const base = `리뷰 ${verdict}${mustFix !== null ? ` · 꼭 고칠 것 ${mustFix}` : ''}`;
|
|
107
|
+
if (converged) return { outcome: 'pass', detail: `${base} · 반박 수렴` };
|
|
108
|
+
return { outcome: verdict === 'fail' ? 'fail' : 'pass', detail: base };
|
|
109
|
+
}
|
|
110
|
+
case 'rework': {
|
|
111
|
+
const budget = last('rework-budget');
|
|
112
|
+
const verdict = str(budget?.verdict);
|
|
113
|
+
return { outcome: 'pass', detail: verdict ? `재작업 예산 ${verdict}` : '재작업' };
|
|
114
|
+
}
|
|
115
|
+
case 'main-sync': {
|
|
116
|
+
const sync = last('pre-pr-sync');
|
|
117
|
+
if (!sync) return { outcome: null, detail: null };
|
|
118
|
+
const status = str(sync.status) ?? '?';
|
|
119
|
+
return { outcome: status === 'merged' || status === 'up-to-date' ? 'pass' : 'fail', detail: `main 동기화 ${status}` };
|
|
120
|
+
}
|
|
121
|
+
case 'open-pr': {
|
|
122
|
+
const opened = last('pr-opened');
|
|
123
|
+
const decision = str(near('merge-decision')?.decision);
|
|
124
|
+
if (!opened) return { outcome: null, detail: decision ? `병합 결정 ${decision}` : null };
|
|
125
|
+
const number = num(opened.number);
|
|
126
|
+
return { outcome: 'pass', detail: `PR${number !== null ? ` #${number}` : ''} 열림${decision ? ` · 병합 결정 ${decision}` : ''}` };
|
|
127
|
+
}
|
|
128
|
+
default:
|
|
129
|
+
return { outcome: null, detail: null };
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
export function buildRunTraversal(runId: string, entries: readonly RunLedgerEntry[]): RunTraversalWire {
|
|
134
|
+
const sorted = [...entries].map((entry, index) => ({ entry, index }))
|
|
135
|
+
.sort((a, b) => (ms(a.entry.timestamp) - ms(b.entry.timestamp)) || a.index - b.index).map(({ entry }) => entry);
|
|
136
|
+
const terminal = [...sorted].reverse().find((entry) => entry.event === 'run-status' && str(entry.data.runStatus));
|
|
137
|
+
const status = terminal ? str(terminal.data.runStatus)! : 'running';
|
|
138
|
+
const entryIndexes = sorted.map((entry, index) => entry.event === 'pipeline-node-entry' && str(entry.data.node) ? index : -1).filter((index) => index >= 0);
|
|
139
|
+
const visits = new Map<string, number>();
|
|
140
|
+
const steps: TraversalStepWire[] = entryIndexes.map((at, position) => {
|
|
141
|
+
const entry = sorted[at]!;
|
|
142
|
+
const node = str(entry.data.node)!;
|
|
143
|
+
const nextAt = entryIndexes[position + 1];
|
|
144
|
+
const window = sorted.slice(at + 1, nextAt ?? sorted.length);
|
|
145
|
+
const budget = [...sorted.slice(Math.max(0, at - 3), at)].reverse()
|
|
146
|
+
.find((candidate) => candidate.event === 'graph-visit-budget' && candidate.data.node === node);
|
|
147
|
+
const visit = num(budget?.data.visits) ?? (visits.get(node) ?? 0) + 1;
|
|
148
|
+
visits.set(node, visit);
|
|
149
|
+
const prevAt = position > 0 ? entryIndexes[position - 1]! : -1;
|
|
150
|
+
let { outcome, detail } = judgeVisit(node, window, sorted.slice(prevAt + 1, at));
|
|
151
|
+
const isLast = nextAt === undefined;
|
|
152
|
+
if (!isLast && outcome === null) outcome = 'pass';
|
|
153
|
+
if (isLast && terminal) outcome = TERMINAL_FAIL.has(status) ? 'fail' : (outcome ?? 'pass');
|
|
154
|
+
const endIso = nextAt !== undefined ? sorted[nextAt]!.timestamp : terminal?.timestamp;
|
|
155
|
+
const duration = ms(endIso) - ms(entry.timestamp);
|
|
156
|
+
return {
|
|
157
|
+
node,
|
|
158
|
+
round: num(entry.data.round),
|
|
159
|
+
outcome,
|
|
160
|
+
at: entry.timestamp ?? '',
|
|
161
|
+
durationMs: Number.isFinite(duration) && duration >= 0 ? duration : null,
|
|
162
|
+
visit,
|
|
163
|
+
maxVisits: num(budget?.data.maxVisits),
|
|
164
|
+
detail,
|
|
165
|
+
};
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
const contract = sorted.find((entry) => entry.event === 'graph-run-contract-resolved')?.data;
|
|
169
|
+
const pr = [...sorted].reverse().find((entry) => entry.event === 'pr-opened')?.data;
|
|
170
|
+
return {
|
|
171
|
+
runId,
|
|
172
|
+
graphId: str(contract?.graphId) ?? str(sorted.find((entry) => entry.event === 'pipeline-node-entry')?.data.graphId),
|
|
173
|
+
title: titleOf(sorted),
|
|
174
|
+
status,
|
|
175
|
+
stage: terminal ? str(terminal.data.stage) : null,
|
|
176
|
+
startedAt: sorted[0]?.timestamp ?? null,
|
|
177
|
+
endedAt: terminal?.timestamp ?? null,
|
|
178
|
+
prNumber: num(pr?.number),
|
|
179
|
+
substrate: str(contract?.actualSubstrate),
|
|
180
|
+
steps,
|
|
181
|
+
ledgerEvents: sorted.length,
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
export interface TraversalDeps {
|
|
186
|
+
loadLedger?: (runId: string) => RunLedgerEntry[] | null;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
const USAGE = 'usage: GET /v1/harness/run-traversal?runId=<runId>';
|
|
190
|
+
|
|
191
|
+
/** GET /v1/harness/run-traversal?runId= — ordered node steps of one harness run. */
|
|
192
|
+
export function handleHarnessRunTraversalGet(req: Request, deps: TraversalDeps = {}): Response {
|
|
193
|
+
const runId = new URL(req.url).searchParams.get('runId')?.trim() ?? '';
|
|
194
|
+
if (!runId || /[\\/]|\.\./.test(runId)) return Response.json({ error: USAGE }, { status: 400 });
|
|
195
|
+
let entries: RunLedgerEntry[] | null;
|
|
196
|
+
try {
|
|
197
|
+
entries = deps.loadLedger
|
|
198
|
+
? deps.loadLedger(runId)
|
|
199
|
+
: loadRunLedger(runId, runLedgerDir()) ?? loadFederatedRunLedger(runId);
|
|
200
|
+
} catch (error) {
|
|
201
|
+
return Response.json({ error: 'ledger-unreadable', reason: error instanceof Error ? error.message.slice(0, 200) : 'unknown' }, { status: 422 });
|
|
202
|
+
}
|
|
203
|
+
if (!entries) return Response.json({ error: 'run-not-found', runId }, { status: 404 });
|
|
204
|
+
return Response.json(buildRunTraversal(runId, entries));
|
|
205
|
+
}
|